kernel: copy kernel 4.19 code to 5.4
No changes were done to the patches while coping them. Currently they do not apply on top of kernel 5.4. Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
		 Hauke Mehrtens
					Hauke Mehrtens
				
			
				
					committed by
					
						 Koen Vandeputte
						Koen Vandeputte
					
				
			
			
				
	
			
			
			 Koen Vandeputte
						Koen Vandeputte
					
				
			
						parent
						
							955634b473
						
					
				
				
					commit
					c16517d26d
				
			| @@ -0,0 +1,30 @@ | |||||||
|  | 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,6 +1,7 @@ | ||||||
|  | -#!/usr/bin/awk -f | ||||||
|  | +#!/bin/sh | ||||||
|  |  # SPDX-License-Identifier: GPL-2.0 | ||||||
|  |  # extract linker version number from stdin and turn into single number | ||||||
|  | +exec awk ' | ||||||
|  |  	{ | ||||||
|  |  	gsub(".*\\)", ""); | ||||||
|  |  	gsub(".*version ", ""); | ||||||
|  | @@ -9,3 +10,4 @@ | ||||||
|  |  	print a[1]*100000000 + a[2]*1000000 + a[3]*10000; | ||||||
|  |  	exit | ||||||
|  |  	} | ||||||
|  | +' | ||||||
| @@ -0,0 +1,23 @@ | |||||||
|  | From 173019b66dcc9d68ad9333aa744dad1e369b5aa8 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Sun, 9 Jul 2017 00:26:53 +0200 | ||||||
|  | Subject: [PATCH 34/34] kernel: add compile fix for linux 4.9 on x86 | ||||||
|  |  | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  Makefile | 4 ++-- | ||||||
|  |  1 file changed, 2 insertions(+), 2 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/Makefile | ||||||
|  | +++ b/Makefile | ||||||
|  | @@ -432,8 +432,8 @@ KBUILD_LDFLAGS := | ||||||
|  |  GCC_PLUGINS_CFLAGS := | ||||||
|  |  CLANG_FLAGS := | ||||||
|  |   | ||||||
|  | -export ARCH SRCARCH CONFIG_SHELL HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE AS LD CC | ||||||
|  | -export CPP AR NM STRIP OBJCOPY OBJDUMP KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS | ||||||
|  | +export ARCH SRCARCH SUBARCH CONFIG_SHELL HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE AS LD | ||||||
|  | +export CC CPP AR NM STRIP OBJCOPY OBJDUMP KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS | ||||||
|  |  export MAKE LEX YACC AWK GENKSYMS INSTALLKERNEL PERL PYTHON PYTHON2 PYTHON3 UTS_MACHINE | ||||||
|  |  export HOSTCXX KBUILD_HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS | ||||||
|  |   | ||||||
| @@ -0,0 +1,558 @@ | |||||||
|  | From 1bb0c3ec899827cfa4668bb63a08713a40744d21 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         | 428 +++++++++++++++++++++++++++ | ||||||
|  |  5 files changed, 481 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 | ||||||
|  | @@ -28,6 +28,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, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | @@ -40,6 +43,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 | ||||||
|  | @@ -135,6 +135,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 | ||||||
|  | @@ -25,6 +25,9 @@ obj-$(CONFIG_NETFILTER_NETLINK_OSF) += n | ||||||
|  |  # connection tracking | ||||||
|  |  obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o | ||||||
|  |   | ||||||
|  | +# optional conntrack route cache extension | ||||||
|  | +obj-$(CONFIG_NF_CONNTRACK_RTCACHE) += nf_conntrack_rtcache.o | ||||||
|  | + | ||||||
|  |  obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o | ||||||
|  |   | ||||||
|  |  # netlink interface for nf_conntrack | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/net/netfilter/nf_conntrack_rtcache.c | ||||||
|  | @@ -0,0 +1,428 @@ | ||||||
|  | +/* 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) | ||||||
|  | +{ | ||||||
|  | +	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->from && rt->from->fib6_node) | ||||||
|  | +			return (u32)rt->from->fib6_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(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 __net_init rtcache_net_init(struct net *net) | ||||||
|  | +{ | ||||||
|  | +	return nf_register_net_hooks(net, rtcache_ops, ARRAY_SIZE(rtcache_ops)); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void __net_exit rtcache_net_exit(struct net *net) | ||||||
|  | +{ | ||||||
|  | +	/* remove hooks so no new connections get rtcache extension */ | ||||||
|  | +	nf_unregister_net_hooks(net, rtcache_ops, ARRAY_SIZE(rtcache_ops)); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static struct pernet_operations rtcache_ops_net_ops = { | ||||||
|  | +	.init	= rtcache_net_init, | ||||||
|  | +	.exit	= rtcache_net_exit, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +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 = register_pernet_subsys(&rtcache_ops_net_ops); | ||||||
|  | +	if (ret) { | ||||||
|  | +		nf_ct_extend_unregister(&rtcache_extend); | ||||||
|  | +		return ret; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	ret = register_netdevice_notifier(&nf_rtcache_notifier); | ||||||
|  | +	if (ret) { | ||||||
|  | +		nf_ct_extend_unregister(&rtcache_extend); | ||||||
|  | +		unregister_pernet_subsys(&rtcache_ops_net_ops); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	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; | ||||||
|  | + | ||||||
|  | +	synchronize_net(); | ||||||
|  | + | ||||||
|  | +	unregister_netdevice_notifier(&nf_rtcache_notifier); | ||||||
|  | +	unregister_pernet_subsys(&rtcache_ops_net_ops); | ||||||
|  | + | ||||||
|  | +	synchronize_net(); | ||||||
|  | + | ||||||
|  | +	rtnl_lock(); | ||||||
|  | + | ||||||
|  | +	/* zap all conntracks with rtcache extension */ | ||||||
|  | +	for_each_net(net) | ||||||
|  | +		nf_ct_iterate_cleanup_net(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"); | ||||||
| @@ -0,0 +1,58 @@ | |||||||
|  | From 1186af457cc186c5ed01708da71b1ffbdf0a2638 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||||
|  | Date: Tue, 20 Nov 2018 09:55:45 +0100 | ||||||
|  | Subject: [PATCH] mtd: keep original flags for every struct mtd_info | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | When allocating a new partition mtd subsystem runs internal tests in the | ||||||
|  | allocate_partition(). They may result in modifying specified flags (e.g. | ||||||
|  | dropping some /features/ like write access). | ||||||
|  |  | ||||||
|  | Those constraints don't have to be necessary true for subpartitions. It | ||||||
|  | may happen parent partition isn't block aligned (effectively disabling | ||||||
|  | write access) while subpartition may fit blocks nicely. In such case all | ||||||
|  | checks should be run again (starting with original flags value). | ||||||
|  |  | ||||||
|  | Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||||
|  | Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> | ||||||
|  | --- | ||||||
|  |  drivers/mtd/mtdcore.c   | 2 ++ | ||||||
|  |  drivers/mtd/mtdpart.c   | 3 ++- | ||||||
|  |  include/linux/mtd/mtd.h | 1 + | ||||||
|  |  3 files changed, 5 insertions(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/mtd/mtdcore.c | ||||||
|  | +++ b/drivers/mtd/mtdcore.c | ||||||
|  | @@ -665,6 +665,8 @@ static void mtd_set_dev_defaults(struct | ||||||
|  |  	} else { | ||||||
|  |  		pr_debug("mtd device won't show a device symlink in sysfs\n"); | ||||||
|  |  	} | ||||||
|  | + | ||||||
|  | +	mtd->orig_flags = mtd->flags; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | --- a/drivers/mtd/mtdpart.c | ||||||
|  | +++ b/drivers/mtd/mtdpart.c | ||||||
|  | @@ -346,7 +346,8 @@ static struct mtd_part *allocate_partiti | ||||||
|  |   | ||||||
|  |  	/* set up the MTD object for this partition */ | ||||||
|  |  	slave->mtd.type = parent->type; | ||||||
|  | -	slave->mtd.flags = parent->flags & ~part->mask_flags; | ||||||
|  | +	slave->mtd.flags = parent->orig_flags & ~part->mask_flags; | ||||||
|  | +	slave->mtd.orig_flags = slave->mtd.flags; | ||||||
|  |  	slave->mtd.size = part->size; | ||||||
|  |  	slave->mtd.writesize = parent->writesize; | ||||||
|  |  	slave->mtd.writebufsize = parent->writebufsize; | ||||||
|  | --- a/include/linux/mtd/mtd.h | ||||||
|  | +++ b/include/linux/mtd/mtd.h | ||||||
|  | @@ -207,6 +207,7 @@ struct mtd_debug_info { | ||||||
|  |  struct mtd_info { | ||||||
|  |  	u_char type; | ||||||
|  |  	uint32_t flags; | ||||||
|  | +	uint32_t orig_flags; /* Flags as before running mtd checks */ | ||||||
|  |  	uint64_t size;	 // Total size of the MTD | ||||||
|  |   | ||||||
|  |  	/* "Major" erase size for the device. Naïve users may take this | ||||||
| @@ -0,0 +1,55 @@ | |||||||
|  | From 6750f61a13a0197c40e4a40739117493b15f19e8 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||||
|  | Date: Tue, 20 Nov 2018 10:24:09 +0100 | ||||||
|  | Subject: [PATCH] mtd: improve calculating partition boundaries when checking | ||||||
|  |  for alignment | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | When checking for alignment mtd should check absolute offsets. It's | ||||||
|  | important for subpartitions as it doesn't make sense to check their | ||||||
|  | relative addresses. | ||||||
|  |  | ||||||
|  | Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||||
|  | Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> | ||||||
|  | --- | ||||||
|  |  drivers/mtd/mtdpart.c | 13 +++++++++++-- | ||||||
|  |  1 file changed, 11 insertions(+), 2 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/mtd/mtdpart.c | ||||||
|  | +++ b/drivers/mtd/mtdpart.c | ||||||
|  | @@ -61,6 +61,15 @@ static inline struct mtd_part *mtd_to_pa | ||||||
|  |  	return container_of(mtd, struct mtd_part, mtd); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static u64 part_absolute_offset(struct mtd_info *mtd) | ||||||
|  | +{ | ||||||
|  | +	struct mtd_part *part = mtd_to_part(mtd); | ||||||
|  | + | ||||||
|  | +	if (!mtd_is_partition(mtd)) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	return part_absolute_offset(part->parent) + part->offset; | ||||||
|  | +} | ||||||
|  |   | ||||||
|  |  /* | ||||||
|  |   * MTD methods which simply translate the effective address and pass through | ||||||
|  | @@ -518,7 +527,7 @@ static struct mtd_part *allocate_partiti | ||||||
|  |  	if (!(slave->mtd.flags & MTD_NO_ERASE)) | ||||||
|  |  		wr_alignment = slave->mtd.erasesize; | ||||||
|  |   | ||||||
|  | -	tmp = slave->offset; | ||||||
|  | +	tmp = part_absolute_offset(parent) + 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 */ | ||||||
|  | @@ -529,7 +538,7 @@ static struct mtd_part *allocate_partiti | ||||||
|  |  			part->name); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	tmp = slave->mtd.size; | ||||||
|  | +	tmp = part_absolute_offset(parent) + slave->mtd.size; | ||||||
|  |  	remainder = do_div(tmp, wr_alignment); | ||||||
|  |  	if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) { | ||||||
|  |  		slave->mtd.flags &= ~MTD_WRITEABLE; | ||||||
| @@ -0,0 +1,199 @@ | |||||||
|  | From 5a1c18b761ddb299a06746948b9ec2814b04fa92 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||||
|  | Date: Wed, 2 Jan 2019 00:00:01 +0100 | ||||||
|  | Subject: [PATCH] bcma: keep a direct pointer to the struct device | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | Accessing struct device is pretty useful/common so having a direct | ||||||
|  | pointer: | ||||||
|  | 1) Simplifies some code | ||||||
|  | 2) Makes bcma_bus_get_host_dev() unneeded | ||||||
|  | 3) Allows further improvements like using dev_* printing helpers | ||||||
|  |  | ||||||
|  | Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||||
|  | Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||||
|  | --- | ||||||
|  |  drivers/bcma/bcma_private.h |  1 - | ||||||
|  |  drivers/bcma/driver_gpio.c  |  2 +- | ||||||
|  |  drivers/bcma/host_pci.c     |  2 ++ | ||||||
|  |  drivers/bcma/host_soc.c     |  4 ++-- | ||||||
|  |  drivers/bcma/main.c         | 45 +++++++++---------------------------- | ||||||
|  |  include/linux/bcma/bcma.h   | 11 +++------ | ||||||
|  |  6 files changed, 18 insertions(+), 47 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/bcma/bcma_private.h | ||||||
|  | +++ b/drivers/bcma/bcma_private.h | ||||||
|  | @@ -33,7 +33,6 @@ int __init bcma_bus_early_register(struc | ||||||
|  |  int bcma_bus_suspend(struct bcma_bus *bus); | ||||||
|  |  int bcma_bus_resume(struct bcma_bus *bus); | ||||||
|  |  #endif | ||||||
|  | -struct device *bcma_bus_get_host_dev(struct bcma_bus *bus); | ||||||
|  |   | ||||||
|  |  /* scan.c */ | ||||||
|  |  void bcma_detect_chip(struct bcma_bus *bus); | ||||||
|  | --- a/drivers/bcma/driver_gpio.c | ||||||
|  | +++ b/drivers/bcma/driver_gpio.c | ||||||
|  | @@ -183,7 +183,7 @@ int bcma_gpio_init(struct bcma_drv_cc *c | ||||||
|  |  	chip->direction_input	= bcma_gpio_direction_input; | ||||||
|  |  	chip->direction_output	= bcma_gpio_direction_output; | ||||||
|  |  	chip->owner		= THIS_MODULE; | ||||||
|  | -	chip->parent		= bcma_bus_get_host_dev(bus); | ||||||
|  | +	chip->parent		= bus->dev; | ||||||
|  |  #if IS_BUILTIN(CONFIG_OF) | ||||||
|  |  	chip->of_node		= cc->core->dev.of_node; | ||||||
|  |  #endif | ||||||
|  | --- a/drivers/bcma/host_pci.c | ||||||
|  | +++ b/drivers/bcma/host_pci.c | ||||||
|  | @@ -196,6 +196,8 @@ static int bcma_host_pci_probe(struct pc | ||||||
|  |  		goto err_pci_release_regions; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | +	bus->dev = &dev->dev; | ||||||
|  | + | ||||||
|  |  	/* Map MMIO */ | ||||||
|  |  	err = -ENOMEM; | ||||||
|  |  	bus->mmio = pci_iomap(dev, 0, ~0UL); | ||||||
|  | --- a/drivers/bcma/host_soc.c | ||||||
|  | +++ b/drivers/bcma/host_soc.c | ||||||
|  | @@ -179,7 +179,6 @@ int __init bcma_host_soc_register(struct | ||||||
|  |  	/* Host specific */ | ||||||
|  |  	bus->hosttype = BCMA_HOSTTYPE_SOC; | ||||||
|  |  	bus->ops = &bcma_host_soc_ops; | ||||||
|  | -	bus->host_pdev = NULL; | ||||||
|  |   | ||||||
|  |  	/* Initialize struct, detect chip */ | ||||||
|  |  	bcma_init_bus(bus); | ||||||
|  | @@ -213,6 +212,8 @@ static int bcma_host_soc_probe(struct pl | ||||||
|  |  	if (!bus) | ||||||
|  |  		return -ENOMEM; | ||||||
|  |   | ||||||
|  | +	bus->dev = dev; | ||||||
|  | + | ||||||
|  |  	/* Map MMIO */ | ||||||
|  |  	bus->mmio = of_iomap(np, 0); | ||||||
|  |  	if (!bus->mmio) | ||||||
|  | @@ -221,7 +222,6 @@ static int bcma_host_soc_probe(struct pl | ||||||
|  |  	/* Host specific */ | ||||||
|  |  	bus->hosttype = BCMA_HOSTTYPE_SOC; | ||||||
|  |  	bus->ops = &bcma_host_soc_ops; | ||||||
|  | -	bus->host_pdev = pdev; | ||||||
|  |   | ||||||
|  |  	/* Initialize struct, detect chip */ | ||||||
|  |  	bcma_init_bus(bus); | ||||||
|  | --- a/drivers/bcma/main.c | ||||||
|  | +++ b/drivers/bcma/main.c | ||||||
|  | @@ -223,8 +223,8 @@ unsigned int bcma_core_irq(struct bcma_d | ||||||
|  |  			mips_irq = bcma_core_mips_irq(core); | ||||||
|  |  			return mips_irq <= 4 ? mips_irq + 2 : 0; | ||||||
|  |  		} | ||||||
|  | -		if (bus->host_pdev) | ||||||
|  | -			return bcma_of_get_irq(&bus->host_pdev->dev, core, num); | ||||||
|  | +		if (bus->dev) | ||||||
|  | +			return bcma_of_get_irq(bus->dev, core, num); | ||||||
|  |  		return 0; | ||||||
|  |  	case BCMA_HOSTTYPE_SDIO: | ||||||
|  |  		return 0; | ||||||
|  | @@ -239,18 +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); | ||||||
|  | +	core->dev.parent = bus->dev; | ||||||
|  | +	if (bus->dev) | ||||||
|  | +		bcma_of_fill_device(bus->dev, core); | ||||||
|  |   | ||||||
|  |  	switch (bus->hosttype) { | ||||||
|  |  	case BCMA_HOSTTYPE_PCI: | ||||||
|  | -		core->dma_dev = &bus->host_pci->dev; | ||||||
|  | +		core->dma_dev = bus->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; | ||||||
|  | +		if (IS_ENABLED(CONFIG_OF) && bus->dev) { | ||||||
|  | +			core->dma_dev = bus->dev; | ||||||
|  |  		} else { | ||||||
|  |  			core->dev.dma_mask = &core->dev.coherent_dma_mask; | ||||||
|  |  			core->dma_dev = &core->dev; | ||||||
|  | @@ -261,28 +261,6 @@ void bcma_prepare_core(struct bcma_bus * | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -struct device *bcma_bus_get_host_dev(struct bcma_bus *bus) | ||||||
|  | -{ | ||||||
|  | -	switch (bus->hosttype) { | ||||||
|  | -	case BCMA_HOSTTYPE_PCI: | ||||||
|  | -		if (bus->host_pci) | ||||||
|  | -			return &bus->host_pci->dev; | ||||||
|  | -		else | ||||||
|  | -			return NULL; | ||||||
|  | -	case BCMA_HOSTTYPE_SOC: | ||||||
|  | -		if (bus->host_pdev) | ||||||
|  | -			return &bus->host_pdev->dev; | ||||||
|  | -		else | ||||||
|  | -			return NULL; | ||||||
|  | -	case BCMA_HOSTTYPE_SDIO: | ||||||
|  | -		if (bus->host_sdio) | ||||||
|  | -			return &bus->host_sdio->dev; | ||||||
|  | -		else | ||||||
|  | -			return NULL; | ||||||
|  | -	} | ||||||
|  | -	return NULL; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  |  void bcma_init_bus(struct bcma_bus *bus) | ||||||
|  |  { | ||||||
|  |  	mutex_lock(&bcma_buses_mutex); | ||||||
|  | @@ -402,7 +380,6 @@ int bcma_bus_register(struct bcma_bus *b | ||||||
|  |  { | ||||||
|  |  	int err; | ||||||
|  |  	struct bcma_device *core; | ||||||
|  | -	struct device *dev; | ||||||
|  |   | ||||||
|  |  	/* Scan for devices (cores) */ | ||||||
|  |  	err = bcma_bus_scan(bus); | ||||||
|  | @@ -425,10 +402,8 @@ int bcma_bus_register(struct bcma_bus *b | ||||||
|  |  		bcma_core_pci_early_init(&bus->drv_pci[0]); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	dev = bcma_bus_get_host_dev(bus); | ||||||
|  | -	if (dev) { | ||||||
|  | -		of_platform_default_populate(dev->of_node, NULL, dev); | ||||||
|  | -	} | ||||||
|  | +	if (bus->dev) | ||||||
|  | +		of_platform_default_populate(bus->dev->of_node, NULL, bus->dev); | ||||||
|  |   | ||||||
|  |  	/* Cores providing flash access go before SPROM init */ | ||||||
|  |  	list_for_each_entry(core, &bus->cores, list) { | ||||||
|  | --- a/include/linux/bcma/bcma.h | ||||||
|  | +++ b/include/linux/bcma/bcma.h | ||||||
|  | @@ -332,6 +332,8 @@ extern int bcma_arch_register_fallback_s | ||||||
|  |  		struct ssb_sprom *out)); | ||||||
|  |   | ||||||
|  |  struct bcma_bus { | ||||||
|  | +	struct device *dev; | ||||||
|  | + | ||||||
|  |  	/* The MMIO area. */ | ||||||
|  |  	void __iomem *mmio; | ||||||
|  |   | ||||||
|  | @@ -339,14 +341,7 @@ struct bcma_bus { | ||||||
|  |   | ||||||
|  |  	enum bcma_hosttype hosttype; | ||||||
|  |  	bool host_is_pcie2; /* Used for BCMA_HOSTTYPE_PCI only */ | ||||||
|  | -	union { | ||||||
|  | -		/* Pointer to the PCI bus (only for BCMA_HOSTTYPE_PCI) */ | ||||||
|  | -		struct pci_dev *host_pci; | ||||||
|  | -		/* Pointer to the SDIO device (only for BCMA_HOSTTYPE_SDIO) */ | ||||||
|  | -		struct sdio_func *host_sdio; | ||||||
|  | -		/* Pointer to platform device (only for BCMA_HOSTTYPE_SOC) */ | ||||||
|  | -		struct platform_device *host_pdev; | ||||||
|  | -	}; | ||||||
|  | +	struct pci_dev *host_pci; /* PCI bus pointer (BCMA_HOSTTYPE_PCI only) */ | ||||||
|  |   | ||||||
|  |  	struct bcma_chipinfo chipinfo; | ||||||
|  |   | ||||||
| @@ -0,0 +1,36 @@ | |||||||
|  | From 777bc4801a6868fcbff09ffb6e30f023e7c5ed38 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||||
|  | Date: Wed, 2 Jan 2019 00:00:02 +0100 | ||||||
|  | Subject: [PATCH] bcma: use dev_* printing functions | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | It provides more meaningful messages. | ||||||
|  |  | ||||||
|  | Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||||
|  | Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||||
|  | --- | ||||||
|  |  drivers/bcma/bcma_private.h | 8 ++++---- | ||||||
|  |  1 file changed, 4 insertions(+), 4 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/bcma/bcma_private.h | ||||||
|  | +++ b/drivers/bcma/bcma_private.h | ||||||
|  | @@ -10,13 +10,13 @@ | ||||||
|  |  #include <linux/delay.h> | ||||||
|  |   | ||||||
|  |  #define bcma_err(bus, fmt, ...) \ | ||||||
|  | -	pr_err("bus%d: " fmt, (bus)->num, ##__VA_ARGS__) | ||||||
|  | +	dev_err((bus)->dev, "bus%d: " fmt, (bus)->num, ##__VA_ARGS__) | ||||||
|  |  #define bcma_warn(bus, fmt, ...) \ | ||||||
|  | -	pr_warn("bus%d: " fmt, (bus)->num, ##__VA_ARGS__) | ||||||
|  | +	dev_warn((bus)->dev, "bus%d: " fmt, (bus)->num, ##__VA_ARGS__) | ||||||
|  |  #define bcma_info(bus, fmt, ...) \ | ||||||
|  | -	pr_info("bus%d: " fmt, (bus)->num, ##__VA_ARGS__) | ||||||
|  | +	dev_info((bus)->dev, "bus%d: " fmt, (bus)->num, ##__VA_ARGS__) | ||||||
|  |  #define bcma_debug(bus, fmt, ...) \ | ||||||
|  | -	pr_debug("bus%d: " fmt, (bus)->num, ##__VA_ARGS__) | ||||||
|  | +	dev_dbg((bus)->dev, "bus%d: " fmt, (bus)->num, ##__VA_ARGS__) | ||||||
|  |   | ||||||
|  |  struct bcma_bus; | ||||||
|  |   | ||||||
| @@ -0,0 +1,79 @@ | |||||||
|  | From 46bf067870156abd61fe24d14c2486d15b8b502c Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Dave Taht <dave@taht.net> | ||||||
|  | Date: Fri, 14 Dec 2018 18:38:40 +0000 | ||||||
|  | Subject: [PATCH 1/1] Allow class-e address assignment in ifconfig and early | ||||||
|  |  boot | ||||||
|  |  | ||||||
|  | While the linux kernel became mostly "class-e clean" a decade ago, | ||||||
|  | and most distributions long ago switched to the iproute2 suite | ||||||
|  | of utilities, which allow class-e (240.0.0.0/4) address assignment, | ||||||
|  | distributions relying on busybox, toybox and other forms of | ||||||
|  | ifconfig cannot assign class-e addresses without this kernel patch. | ||||||
|  |  | ||||||
|  | With this patch, also, a boot command line on these addresses is feasible: | ||||||
|  | (ip=248.0.1.2::248.0.1.1:255.255.255.0). | ||||||
|  |  | ||||||
|  | While CIDR has been obsolete for 2 decades, and a survey of all the | ||||||
|  | userspace open source code in the world shows most IN_whatever macros | ||||||
|  | are also obsolete... rather than obsolete CIDR from this ioctl entirely, | ||||||
|  | this patch merely enables class-e assignment, sanely. | ||||||
|  |  | ||||||
|  | H/T to Vince Fuller and his original patch here: | ||||||
|  |     https://lkml.org/lkml/2008/1/7/370 | ||||||
|  |  | ||||||
|  | Signed-off-by: Dave Taht <dave.taht@gmail.com> | ||||||
|  | Reviewed-by: John Gilmore <gnu@toad.com> | ||||||
|  | --- | ||||||
|  |  include/uapi/linux/in.h | 8 ++++++-- | ||||||
|  |  net/ipv4/devinet.c      | 4 +++- | ||||||
|  |  net/ipv4/ipconfig.c     | 2 ++ | ||||||
|  |  3 files changed, 11 insertions(+), 3 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/include/uapi/linux/in.h | ||||||
|  | +++ b/include/uapi/linux/in.h | ||||||
|  | @@ -268,8 +268,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 | ||||||
|  | @@ -949,7 +949,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); | ||||||
|  | @@ -960,6 +960,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 | ||||||
|  | @@ -429,6 +429,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); | ||||||
| @@ -0,0 +1,46 @@ | |||||||
|  | From 4cc30de79d293f1e8c5f50ae3a9c005def9564a0 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Koen Vandeputte <koen.vandeputte@ncentric.com> | ||||||
|  | Date: Mon, 7 Jan 2019 14:14:27 +0100 | ||||||
|  | Subject: [PATCH 2/2] arm: cns3xxx: use actual size reads for PCIe | ||||||
|  |  | ||||||
|  | commit 802b7c06adc7 ("ARM: cns3xxx: Convert PCI to use generic config accessors") | ||||||
|  | reimplemented cns3xxx_pci_read_config() using pci_generic_config_read32(), | ||||||
|  | which preserved the property of only doing 32-bit reads. | ||||||
|  |  | ||||||
|  | It also replaced cns3xxx_pci_write_config() with pci_generic_config_write(), | ||||||
|  | so it changed writes from always being 32 bits to being the actual size, | ||||||
|  | which works just fine. | ||||||
|  |  | ||||||
|  | Due to: | ||||||
|  | - The documentation does not mention that only 32 bit access is allowed. | ||||||
|  | - Writes are already executed using the actual size | ||||||
|  | - Extensive testing shows that 8b, 16b and 32b reads work as intended | ||||||
|  |  | ||||||
|  | It makes perfectly sense to also swap 32 bit reading in favor of actual size. | ||||||
|  |  | ||||||
|  | Fixes: 802b7c06adc7 ("ARM: cns3xxx: Convert PCI to use generic config accessors") | ||||||
|  | Suggested-by: Bjorn Helgaas <bhelgaas@google.com> | ||||||
|  | Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com> | ||||||
|  | CC: Arnd Bergmann <arnd@arndb.de> | ||||||
|  | CC: Krzysztof Halasa <khalasa@piap.pl> | ||||||
|  | CC: Olof Johansson <olof@lixom.net> | ||||||
|  | CC: Robin Leblon <robin.leblon@ncentric.com> | ||||||
|  | CC: Rob Herring <robh@kernel.org> | ||||||
|  | CC: Russell King <linux@armlinux.org.uk> | ||||||
|  | CC: Tim Harvey <tharvey@gateworks.com> | ||||||
|  | CC: stable@vger.kernel.org # v4.0+ | ||||||
|  | --- | ||||||
|  |  arch/arm/mach-cns3xxx/pcie.c | 2 +- | ||||||
|  |  1 file changed, 1 insertion(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/arch/arm/mach-cns3xxx/pcie.c | ||||||
|  | +++ b/arch/arm/mach-cns3xxx/pcie.c | ||||||
|  | @@ -93,7 +93,7 @@ static int cns3xxx_pci_read_config(struc | ||||||
|  |  	u32 mask = (0x1ull << (size * 8)) - 1; | ||||||
|  |  	int shift = (where % 4) * 8; | ||||||
|  |   | ||||||
|  | -	ret = pci_generic_config_read32(bus, devfn, where, size, val); | ||||||
|  | +	ret = pci_generic_config_read(bus, devfn, where, size, val); | ||||||
|  |   | ||||||
|  |  	if (ret == PCIBIOS_SUCCESSFUL && !bus->number && !devfn && | ||||||
|  |  	    (where & 0xffc) == PCI_CLASS_REVISION) | ||||||
| @@ -0,0 +1,63 @@ | |||||||
|  | From 28b5c129ca6e585ec95c160ec4297bc6c6360b6f Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Minas Harutyunyan <minas.harutyunyan@synopsys.com> | ||||||
|  | Date: Mon, 4 Mar 2019 17:08:07 +0400 | ||||||
|  | Subject: usb: dwc2: Set lpm mode parameters depend on HW configuration | ||||||
|  |  | ||||||
|  | If core not supported lpm, i.e. BCM2835 then confusing warnings seen | ||||||
|  | in log. | ||||||
|  |  | ||||||
|  | To avoid these warnings, added function dwc2_set_param_lpm() to set | ||||||
|  | lpm and other lpm related parameters based on lpm support by core. | ||||||
|  |  | ||||||
|  | Signed-off-by: Minas Harutyunyan <hminas@synopsys.com> | ||||||
|  | Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com> | ||||||
|  | --- | ||||||
|  |  drivers/usb/dwc2/params.c | 23 ++++++++++++++++++----- | ||||||
|  |  1 file changed, 18 insertions(+), 5 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/usb/dwc2/params.c | ||||||
|  | +++ b/drivers/usb/dwc2/params.c | ||||||
|  | @@ -273,6 +273,23 @@ static void dwc2_set_param_power_down(st | ||||||
|  |  	hsotg->params.power_down = val; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static void dwc2_set_param_lpm(struct dwc2_hsotg *hsotg) | ||||||
|  | +{ | ||||||
|  | +	struct dwc2_core_params *p = &hsotg->params; | ||||||
|  | + | ||||||
|  | +	p->lpm = hsotg->hw_params.lpm_mode; | ||||||
|  | +	if (p->lpm) { | ||||||
|  | +		p->lpm_clock_gating = true; | ||||||
|  | +		p->besl = true; | ||||||
|  | +		p->hird_threshold_en = true; | ||||||
|  | +		p->hird_threshold = 4; | ||||||
|  | +	} else { | ||||||
|  | +		p->lpm_clock_gating = false; | ||||||
|  | +		p->besl = false; | ||||||
|  | +		p->hird_threshold_en = false; | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  /** | ||||||
|  |   * dwc2_set_default_params() - Set all core parameters to their | ||||||
|  |   * auto-detected default values. | ||||||
|  | @@ -291,6 +308,7 @@ static void dwc2_set_default_params(stru | ||||||
|  |  	dwc2_set_param_speed(hsotg); | ||||||
|  |  	dwc2_set_param_phy_utmi_width(hsotg); | ||||||
|  |  	dwc2_set_param_power_down(hsotg); | ||||||
|  | +	dwc2_set_param_lpm(hsotg); | ||||||
|  |  	p->phy_ulpi_ddr = false; | ||||||
|  |  	p->phy_ulpi_ext_vbus = false; | ||||||
|  |   | ||||||
|  | @@ -303,11 +321,6 @@ static void dwc2_set_default_params(stru | ||||||
|  |  	p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a); | ||||||
|  |  	p->uframe_sched = true; | ||||||
|  |  	p->external_id_pin_ctl = false; | ||||||
|  | -	p->lpm = true; | ||||||
|  | -	p->lpm_clock_gating = true; | ||||||
|  | -	p->besl = true; | ||||||
|  | -	p->hird_threshold_en = true; | ||||||
|  | -	p->hird_threshold = 4; | ||||||
|  |  	p->ipg_isoc_en = false; | ||||||
|  |  	p->max_packet_count = hw->max_packet_count; | ||||||
|  |  	p->max_transfer_size = hw->max_transfer_size; | ||||||
| @@ -0,0 +1,280 @@ | |||||||
|  | From 9966a05c7b80f075f2bc7e48dbb108d3f2927234 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Dave Martin <Dave.Martin@arm.com> | ||||||
|  | Date: Fri, 4 Jan 2019 13:09:51 +0000 | ||||||
|  | Subject: [PATCH] arm64/sve: Disentangle <uapi/asm/ptrace.h> from | ||||||
|  |  <uapi/asm/sigcontext.h> | ||||||
|  |  | ||||||
|  | Currently, <uapi/asm/sigcontext.h> provides common definitions for | ||||||
|  | describing SVE context structures that are also used by the ptrace | ||||||
|  | definitions in <uapi/asm/ptrace.h>. | ||||||
|  |  | ||||||
|  | For this reason, a #include of <asm/sigcontext.h> was added in | ||||||
|  | ptrace.h, but it this turns out that this can interact badly with | ||||||
|  | userspace code that tries to include ptrace.h on top of the libc | ||||||
|  | headers (which may provide their own shadow definitions for | ||||||
|  | sigcontext.h). | ||||||
|  |  | ||||||
|  | To make the headers easier for userspace to consume, this patch | ||||||
|  | bounces the common definitions into an __SVE_* namespace and moves | ||||||
|  | them to a backend header <uapi/asm/sve_context.h> that can be | ||||||
|  | included by the other headers as appropriate.  This should allow | ||||||
|  | ptrace.h to be used alongside libc's sigcontext.h (if any) without | ||||||
|  | ill effects. | ||||||
|  |  | ||||||
|  | This should make the situation unambiguous: <asm/sigcontext.h> is | ||||||
|  | the header to include for the sigframe-specific definitions, while | ||||||
|  | <asm/ptrace.h> is the header to include for ptrace-specific | ||||||
|  | definitions. | ||||||
|  |  | ||||||
|  | To avoid conflicting with existing usage, <asm/sigcontext.h> | ||||||
|  | remains the canonical way to get the common definitions for | ||||||
|  | SVE_VQ_MIN, sve_vq_from_vl() etc., both in userspace and in the | ||||||
|  | kernel: relying on these being defined as a side effect of | ||||||
|  | including just <asm/ptrace.h> was never intended to be safe. | ||||||
|  |  | ||||||
|  | Signed-off-by: Dave Martin <Dave.Martin@arm.com> | ||||||
|  | Signed-off-by: Will Deacon <will.deacon@arm.com> | ||||||
|  | --- | ||||||
|  |  arch/arm64/include/uapi/asm/ptrace.h      | 39 ++++++++-------- | ||||||
|  |  arch/arm64/include/uapi/asm/sigcontext.h  | 56 +++++++++++------------ | ||||||
|  |  arch/arm64/include/uapi/asm/sve_context.h | 53 +++++++++++++++++++++ | ||||||
|  |  3 files changed, 99 insertions(+), 49 deletions(-) | ||||||
|  |  create mode 100644 arch/arm64/include/uapi/asm/sve_context.h | ||||||
|  |  | ||||||
|  | --- a/arch/arm64/include/uapi/asm/ptrace.h | ||||||
|  | +++ b/arch/arm64/include/uapi/asm/ptrace.h | ||||||
|  | @@ -23,7 +23,7 @@ | ||||||
|  |  #include <linux/types.h> | ||||||
|  |   | ||||||
|  |  #include <asm/hwcap.h> | ||||||
|  | -#include <asm/sigcontext.h> | ||||||
|  | +#include <asm/sve_context.h> | ||||||
|  |   | ||||||
|  |   | ||||||
|  |  /* | ||||||
|  | @@ -129,9 +129,9 @@ struct user_sve_header { | ||||||
|  |   */ | ||||||
|  |   | ||||||
|  |  /* Offset from the start of struct user_sve_header to the register data */ | ||||||
|  | -#define SVE_PT_REGS_OFFSET					\ | ||||||
|  | -	((sizeof(struct user_sve_header) + (SVE_VQ_BYTES - 1))	\ | ||||||
|  | -		/ SVE_VQ_BYTES * SVE_VQ_BYTES) | ||||||
|  | +#define SVE_PT_REGS_OFFSET						\ | ||||||
|  | +	((sizeof(struct user_sve_header) + (__SVE_VQ_BYTES - 1))	\ | ||||||
|  | +		/ __SVE_VQ_BYTES * __SVE_VQ_BYTES) | ||||||
|  |   | ||||||
|  |  /* | ||||||
|  |   * The register data content and layout depends on the value of the | ||||||
|  | @@ -177,39 +177,36 @@ struct user_sve_header { | ||||||
|  |   * Additional data might be appended in the future. | ||||||
|  |   */ | ||||||
|  |   | ||||||
|  | -#define SVE_PT_SVE_ZREG_SIZE(vq)	SVE_SIG_ZREG_SIZE(vq) | ||||||
|  | -#define SVE_PT_SVE_PREG_SIZE(vq)	SVE_SIG_PREG_SIZE(vq) | ||||||
|  | -#define SVE_PT_SVE_FFR_SIZE(vq)		SVE_SIG_FFR_SIZE(vq) | ||||||
|  | +#define SVE_PT_SVE_ZREG_SIZE(vq)	__SVE_ZREG_SIZE(vq) | ||||||
|  | +#define SVE_PT_SVE_PREG_SIZE(vq)	__SVE_PREG_SIZE(vq) | ||||||
|  | +#define SVE_PT_SVE_FFR_SIZE(vq)		__SVE_FFR_SIZE(vq) | ||||||
|  |  #define SVE_PT_SVE_FPSR_SIZE		sizeof(__u32) | ||||||
|  |  #define SVE_PT_SVE_FPCR_SIZE		sizeof(__u32) | ||||||
|  |   | ||||||
|  | -#define __SVE_SIG_TO_PT(offset) \ | ||||||
|  | -	((offset) - SVE_SIG_REGS_OFFSET + SVE_PT_REGS_OFFSET) | ||||||
|  | - | ||||||
|  |  #define SVE_PT_SVE_OFFSET		SVE_PT_REGS_OFFSET | ||||||
|  |   | ||||||
|  |  #define SVE_PT_SVE_ZREGS_OFFSET \ | ||||||
|  | -	__SVE_SIG_TO_PT(SVE_SIG_ZREGS_OFFSET) | ||||||
|  | +	(SVE_PT_REGS_OFFSET + __SVE_ZREGS_OFFSET) | ||||||
|  |  #define SVE_PT_SVE_ZREG_OFFSET(vq, n) \ | ||||||
|  | -	__SVE_SIG_TO_PT(SVE_SIG_ZREG_OFFSET(vq, n)) | ||||||
|  | +	(SVE_PT_REGS_OFFSET + __SVE_ZREG_OFFSET(vq, n)) | ||||||
|  |  #define SVE_PT_SVE_ZREGS_SIZE(vq) \ | ||||||
|  | -	(SVE_PT_SVE_ZREG_OFFSET(vq, SVE_NUM_ZREGS) - SVE_PT_SVE_ZREGS_OFFSET) | ||||||
|  | +	(SVE_PT_SVE_ZREG_OFFSET(vq, __SVE_NUM_ZREGS) - SVE_PT_SVE_ZREGS_OFFSET) | ||||||
|  |   | ||||||
|  |  #define SVE_PT_SVE_PREGS_OFFSET(vq) \ | ||||||
|  | -	__SVE_SIG_TO_PT(SVE_SIG_PREGS_OFFSET(vq)) | ||||||
|  | +	(SVE_PT_REGS_OFFSET + __SVE_PREGS_OFFSET(vq)) | ||||||
|  |  #define SVE_PT_SVE_PREG_OFFSET(vq, n) \ | ||||||
|  | -	__SVE_SIG_TO_PT(SVE_SIG_PREG_OFFSET(vq, n)) | ||||||
|  | +	(SVE_PT_REGS_OFFSET + __SVE_PREG_OFFSET(vq, n)) | ||||||
|  |  #define SVE_PT_SVE_PREGS_SIZE(vq) \ | ||||||
|  | -	(SVE_PT_SVE_PREG_OFFSET(vq, SVE_NUM_PREGS) - \ | ||||||
|  | +	(SVE_PT_SVE_PREG_OFFSET(vq, __SVE_NUM_PREGS) - \ | ||||||
|  |  		SVE_PT_SVE_PREGS_OFFSET(vq)) | ||||||
|  |   | ||||||
|  |  #define SVE_PT_SVE_FFR_OFFSET(vq) \ | ||||||
|  | -	__SVE_SIG_TO_PT(SVE_SIG_FFR_OFFSET(vq)) | ||||||
|  | +	(SVE_PT_REGS_OFFSET + __SVE_FFR_OFFSET(vq)) | ||||||
|  |   | ||||||
|  |  #define SVE_PT_SVE_FPSR_OFFSET(vq)				\ | ||||||
|  |  	((SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq) +	\ | ||||||
|  | -			(SVE_VQ_BYTES - 1))			\ | ||||||
|  | -		/ SVE_VQ_BYTES * SVE_VQ_BYTES) | ||||||
|  | +			(__SVE_VQ_BYTES - 1))			\ | ||||||
|  | +		/ __SVE_VQ_BYTES * __SVE_VQ_BYTES) | ||||||
|  |  #define SVE_PT_SVE_FPCR_OFFSET(vq) \ | ||||||
|  |  	(SVE_PT_SVE_FPSR_OFFSET(vq) + SVE_PT_SVE_FPSR_SIZE) | ||||||
|  |   | ||||||
|  | @@ -220,8 +217,8 @@ struct user_sve_header { | ||||||
|  |   | ||||||
|  |  #define SVE_PT_SVE_SIZE(vq, flags)					\ | ||||||
|  |  	((SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE		\ | ||||||
|  | -			- SVE_PT_SVE_OFFSET + (SVE_VQ_BYTES - 1))	\ | ||||||
|  | -		/ SVE_VQ_BYTES * SVE_VQ_BYTES) | ||||||
|  | +			- SVE_PT_SVE_OFFSET + (__SVE_VQ_BYTES - 1))	\ | ||||||
|  | +		/ __SVE_VQ_BYTES * __SVE_VQ_BYTES) | ||||||
|  |   | ||||||
|  |  #define SVE_PT_SIZE(vq, flags)						\ | ||||||
|  |  	 (((flags) & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE ?		\ | ||||||
|  | --- a/arch/arm64/include/uapi/asm/sigcontext.h | ||||||
|  | +++ b/arch/arm64/include/uapi/asm/sigcontext.h | ||||||
|  | @@ -130,6 +130,8 @@ struct sve_context { | ||||||
|  |   | ||||||
|  |  #endif /* !__ASSEMBLY__ */ | ||||||
|  |   | ||||||
|  | +#include <asm/sve_context.h> | ||||||
|  | + | ||||||
|  |  /* | ||||||
|  |   * The SVE architecture leaves space for future expansion of the | ||||||
|  |   * vector length beyond its initial architectural limit of 2048 bits | ||||||
|  | @@ -138,21 +140,20 @@ struct sve_context { | ||||||
|  |   * See linux/Documentation/arm64/sve.txt for a description of the VL/VQ | ||||||
|  |   * terminology. | ||||||
|  |   */ | ||||||
|  | -#define SVE_VQ_BYTES		16	/* number of bytes per quadword */ | ||||||
|  | +#define SVE_VQ_BYTES		__SVE_VQ_BYTES	/* bytes per quadword */ | ||||||
|  |   | ||||||
|  | -#define SVE_VQ_MIN		1 | ||||||
|  | -#define SVE_VQ_MAX		512 | ||||||
|  | +#define SVE_VQ_MIN		__SVE_VQ_MIN | ||||||
|  | +#define SVE_VQ_MAX		__SVE_VQ_MAX | ||||||
|  |   | ||||||
|  | -#define SVE_VL_MIN		(SVE_VQ_MIN * SVE_VQ_BYTES) | ||||||
|  | -#define SVE_VL_MAX		(SVE_VQ_MAX * SVE_VQ_BYTES) | ||||||
|  | +#define SVE_VL_MIN		__SVE_VL_MIN | ||||||
|  | +#define SVE_VL_MAX		__SVE_VL_MAX | ||||||
|  |   | ||||||
|  | -#define SVE_NUM_ZREGS		32 | ||||||
|  | -#define SVE_NUM_PREGS		16 | ||||||
|  | +#define SVE_NUM_ZREGS		__SVE_NUM_ZREGS | ||||||
|  | +#define SVE_NUM_PREGS		__SVE_NUM_PREGS | ||||||
|  |   | ||||||
|  | -#define sve_vl_valid(vl) \ | ||||||
|  | -	((vl) % SVE_VQ_BYTES == 0 && (vl) >= SVE_VL_MIN && (vl) <= SVE_VL_MAX) | ||||||
|  | -#define sve_vq_from_vl(vl)	((vl) / SVE_VQ_BYTES) | ||||||
|  | -#define sve_vl_from_vq(vq)	((vq) * SVE_VQ_BYTES) | ||||||
|  | +#define sve_vl_valid(vl)	__sve_vl_valid(vl) | ||||||
|  | +#define sve_vq_from_vl(vl)	__sve_vq_from_vl(vl) | ||||||
|  | +#define sve_vl_from_vq(vq)	__sve_vl_from_vq(vq) | ||||||
|  |   | ||||||
|  |  /* | ||||||
|  |   * If the SVE registers are currently live for the thread at signal delivery, | ||||||
|  | @@ -205,34 +206,33 @@ struct sve_context { | ||||||
|  |   * Additional data might be appended in the future. | ||||||
|  |   */ | ||||||
|  |   | ||||||
|  | -#define SVE_SIG_ZREG_SIZE(vq)	((__u32)(vq) * SVE_VQ_BYTES) | ||||||
|  | -#define SVE_SIG_PREG_SIZE(vq)	((__u32)(vq) * (SVE_VQ_BYTES / 8)) | ||||||
|  | -#define SVE_SIG_FFR_SIZE(vq)	SVE_SIG_PREG_SIZE(vq) | ||||||
|  | +#define SVE_SIG_ZREG_SIZE(vq)	__SVE_ZREG_SIZE(vq) | ||||||
|  | +#define SVE_SIG_PREG_SIZE(vq)	__SVE_PREG_SIZE(vq) | ||||||
|  | +#define SVE_SIG_FFR_SIZE(vq)	__SVE_FFR_SIZE(vq) | ||||||
|  |   | ||||||
|  |  #define SVE_SIG_REGS_OFFSET					\ | ||||||
|  | -	((sizeof(struct sve_context) + (SVE_VQ_BYTES - 1))	\ | ||||||
|  | -		/ SVE_VQ_BYTES * SVE_VQ_BYTES) | ||||||
|  | +	((sizeof(struct sve_context) + (__SVE_VQ_BYTES - 1))	\ | ||||||
|  | +		/ __SVE_VQ_BYTES * __SVE_VQ_BYTES) | ||||||
|  |   | ||||||
|  | -#define SVE_SIG_ZREGS_OFFSET	SVE_SIG_REGS_OFFSET | ||||||
|  | +#define SVE_SIG_ZREGS_OFFSET \ | ||||||
|  | +		(SVE_SIG_REGS_OFFSET + __SVE_ZREGS_OFFSET) | ||||||
|  |  #define SVE_SIG_ZREG_OFFSET(vq, n) \ | ||||||
|  | -	(SVE_SIG_ZREGS_OFFSET + SVE_SIG_ZREG_SIZE(vq) * (n)) | ||||||
|  | -#define SVE_SIG_ZREGS_SIZE(vq) \ | ||||||
|  | -	(SVE_SIG_ZREG_OFFSET(vq, SVE_NUM_ZREGS) - SVE_SIG_ZREGS_OFFSET) | ||||||
|  | +		(SVE_SIG_REGS_OFFSET + __SVE_ZREG_OFFSET(vq, n)) | ||||||
|  | +#define SVE_SIG_ZREGS_SIZE(vq) __SVE_ZREGS_SIZE(vq) | ||||||
|  |   | ||||||
|  |  #define SVE_SIG_PREGS_OFFSET(vq) \ | ||||||
|  | -	(SVE_SIG_ZREGS_OFFSET + SVE_SIG_ZREGS_SIZE(vq)) | ||||||
|  | +		(SVE_SIG_REGS_OFFSET + __SVE_PREGS_OFFSET(vq)) | ||||||
|  |  #define SVE_SIG_PREG_OFFSET(vq, n) \ | ||||||
|  | -	(SVE_SIG_PREGS_OFFSET(vq) + SVE_SIG_PREG_SIZE(vq) * (n)) | ||||||
|  | -#define SVE_SIG_PREGS_SIZE(vq) \ | ||||||
|  | -	(SVE_SIG_PREG_OFFSET(vq, SVE_NUM_PREGS) - SVE_SIG_PREGS_OFFSET(vq)) | ||||||
|  | +		(SVE_SIG_REGS_OFFSET + __SVE_PREG_OFFSET(vq, n)) | ||||||
|  | +#define SVE_SIG_PREGS_SIZE(vq) __SVE_PREGS_SIZE(vq) | ||||||
|  |   | ||||||
|  |  #define SVE_SIG_FFR_OFFSET(vq) \ | ||||||
|  | -	(SVE_SIG_PREGS_OFFSET(vq) + SVE_SIG_PREGS_SIZE(vq)) | ||||||
|  | +		(SVE_SIG_REGS_OFFSET + __SVE_FFR_OFFSET(vq)) | ||||||
|  |   | ||||||
|  |  #define SVE_SIG_REGS_SIZE(vq) \ | ||||||
|  | -	(SVE_SIG_FFR_OFFSET(vq) + SVE_SIG_FFR_SIZE(vq) - SVE_SIG_REGS_OFFSET) | ||||||
|  | - | ||||||
|  | -#define SVE_SIG_CONTEXT_SIZE(vq) (SVE_SIG_REGS_OFFSET + SVE_SIG_REGS_SIZE(vq)) | ||||||
|  | +		(__SVE_FFR_OFFSET(vq) + __SVE_FFR_SIZE(vq)) | ||||||
|  |   | ||||||
|  | +#define SVE_SIG_CONTEXT_SIZE(vq) \ | ||||||
|  | +		(SVE_SIG_REGS_OFFSET + SVE_SIG_REGS_SIZE(vq)) | ||||||
|  |   | ||||||
|  |  #endif /* _UAPI__ASM_SIGCONTEXT_H */ | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/arch/arm64/include/uapi/asm/sve_context.h | ||||||
|  | @@ -0,0 +1,53 @@ | ||||||
|  | +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||||||
|  | +/* Copyright (C) 2017-2018 ARM Limited */ | ||||||
|  | + | ||||||
|  | +/* | ||||||
|  | + * For use by other UAPI headers only. | ||||||
|  | + * Do not make direct use of header or its definitions. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#ifndef _UAPI__ASM_SVE_CONTEXT_H | ||||||
|  | +#define _UAPI__ASM_SVE_CONTEXT_H | ||||||
|  | + | ||||||
|  | +#include <linux/types.h> | ||||||
|  | + | ||||||
|  | +#define __SVE_VQ_BYTES		16	/* number of bytes per quadword */ | ||||||
|  | + | ||||||
|  | +#define __SVE_VQ_MIN		1 | ||||||
|  | +#define __SVE_VQ_MAX		512 | ||||||
|  | + | ||||||
|  | +#define __SVE_VL_MIN		(__SVE_VQ_MIN * __SVE_VQ_BYTES) | ||||||
|  | +#define __SVE_VL_MAX		(__SVE_VQ_MAX * __SVE_VQ_BYTES) | ||||||
|  | + | ||||||
|  | +#define __SVE_NUM_ZREGS		32 | ||||||
|  | +#define __SVE_NUM_PREGS		16 | ||||||
|  | + | ||||||
|  | +#define __sve_vl_valid(vl)			\ | ||||||
|  | +	((vl) % __SVE_VQ_BYTES == 0 &&		\ | ||||||
|  | +	 (vl) >= __SVE_VL_MIN &&		\ | ||||||
|  | +	 (vl) <= __SVE_VL_MAX) | ||||||
|  | + | ||||||
|  | +#define __sve_vq_from_vl(vl)	((vl) / __SVE_VQ_BYTES) | ||||||
|  | +#define __sve_vl_from_vq(vq)	((vq) * __SVE_VQ_BYTES) | ||||||
|  | + | ||||||
|  | +#define __SVE_ZREG_SIZE(vq)	((__u32)(vq) * __SVE_VQ_BYTES) | ||||||
|  | +#define __SVE_PREG_SIZE(vq)	((__u32)(vq) * (__SVE_VQ_BYTES / 8)) | ||||||
|  | +#define __SVE_FFR_SIZE(vq)	__SVE_PREG_SIZE(vq) | ||||||
|  | + | ||||||
|  | +#define __SVE_ZREGS_OFFSET	0 | ||||||
|  | +#define __SVE_ZREG_OFFSET(vq, n) \ | ||||||
|  | +	(__SVE_ZREGS_OFFSET + __SVE_ZREG_SIZE(vq) * (n)) | ||||||
|  | +#define __SVE_ZREGS_SIZE(vq) \ | ||||||
|  | +	(__SVE_ZREG_OFFSET(vq, __SVE_NUM_ZREGS) - __SVE_ZREGS_OFFSET) | ||||||
|  | + | ||||||
|  | +#define __SVE_PREGS_OFFSET(vq) \ | ||||||
|  | +	(__SVE_ZREGS_OFFSET + __SVE_ZREGS_SIZE(vq)) | ||||||
|  | +#define __SVE_PREG_OFFSET(vq, n) \ | ||||||
|  | +	(__SVE_PREGS_OFFSET(vq) + __SVE_PREG_SIZE(vq) * (n)) | ||||||
|  | +#define __SVE_PREGS_SIZE(vq) \ | ||||||
|  | +	(__SVE_PREG_OFFSET(vq, __SVE_NUM_PREGS) - __SVE_PREGS_OFFSET(vq)) | ||||||
|  | + | ||||||
|  | +#define __SVE_FFR_OFFSET(vq) \ | ||||||
|  | +	(__SVE_PREGS_OFFSET(vq) + __SVE_PREGS_SIZE(vq)) | ||||||
|  | + | ||||||
|  | +#endif /* ! _UAPI__ASM_SVE_CONTEXT_H */ | ||||||
| @@ -0,0 +1,99 @@ | |||||||
|  | From: Pablo Neira Ayuso <pablo@netfilter.org> | ||||||
|  | Date: Thu, 25 Jan 2018 12:58:55 +0100 | ||||||
|  | Subject: [PATCH] netfilter: nft_flow_offload: handle netdevice events from | ||||||
|  |  nf_flow_table | ||||||
|  |  | ||||||
|  | Move the code that deals with device events to the core. | ||||||
|  |  | ||||||
|  | Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | --- a/net/netfilter/nf_flow_table_core.c | ||||||
|  | +++ b/net/netfilter/nf_flow_table_core.c | ||||||
|  | @@ -535,5 +535,35 @@ void nf_flow_table_free(struct nf_flowta | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL_GPL(nf_flow_table_free); | ||||||
|  |   | ||||||
|  | +static int nf_flow_table_netdev_event(struct notifier_block *this, | ||||||
|  | +				      unsigned long event, void *ptr) | ||||||
|  | +{ | ||||||
|  | +	struct net_device *dev = netdev_notifier_info_to_dev(ptr); | ||||||
|  | + | ||||||
|  | +	if (event != NETDEV_DOWN) | ||||||
|  | +		return NOTIFY_DONE; | ||||||
|  | + | ||||||
|  | +	nf_flow_table_cleanup(dev_net(dev), dev); | ||||||
|  | + | ||||||
|  | +	return NOTIFY_DONE; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static struct notifier_block flow_offload_netdev_notifier = { | ||||||
|  | +	.notifier_call	= nf_flow_table_netdev_event, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int __init nf_flow_table_module_init(void) | ||||||
|  | +{ | ||||||
|  | +	return register_netdevice_notifier(&flow_offload_netdev_notifier); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void __exit nf_flow_table_module_exit(void) | ||||||
|  | +{ | ||||||
|  | +	unregister_netdevice_notifier(&flow_offload_netdev_notifier); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +module_init(nf_flow_table_module_init); | ||||||
|  | +module_exit(nf_flow_table_module_exit); | ||||||
|  | + | ||||||
|  |  MODULE_LICENSE("GPL"); | ||||||
|  |  MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); | ||||||
|  | --- a/net/netfilter/nft_flow_offload.c | ||||||
|  | +++ b/net/netfilter/nft_flow_offload.c | ||||||
|  | @@ -216,47 +216,14 @@ static struct nft_expr_type nft_flow_off | ||||||
|  |  	.owner		= THIS_MODULE, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | -static int flow_offload_netdev_event(struct notifier_block *this, | ||||||
|  | -				     unsigned long event, void *ptr) | ||||||
|  | -{ | ||||||
|  | -	struct net_device *dev = netdev_notifier_info_to_dev(ptr); | ||||||
|  | - | ||||||
|  | -	if (event != NETDEV_DOWN) | ||||||
|  | -		return NOTIFY_DONE; | ||||||
|  | - | ||||||
|  | -	nf_flow_table_cleanup(dev_net(dev), dev); | ||||||
|  | - | ||||||
|  | -	return NOTIFY_DONE; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static struct notifier_block flow_offload_netdev_notifier = { | ||||||
|  | -	.notifier_call	= flow_offload_netdev_event, | ||||||
|  | -}; | ||||||
|  | - | ||||||
|  |  static int __init nft_flow_offload_module_init(void) | ||||||
|  |  { | ||||||
|  | -	int err; | ||||||
|  | - | ||||||
|  | -	err = register_netdevice_notifier(&flow_offload_netdev_notifier); | ||||||
|  | -	if (err) | ||||||
|  | -		goto err; | ||||||
|  | - | ||||||
|  | -	err = nft_register_expr(&nft_flow_offload_type); | ||||||
|  | -	if (err < 0) | ||||||
|  | -		goto register_expr; | ||||||
|  | - | ||||||
|  | -	return 0; | ||||||
|  | - | ||||||
|  | -register_expr: | ||||||
|  | -	unregister_netdevice_notifier(&flow_offload_netdev_notifier); | ||||||
|  | -err: | ||||||
|  | -	return err; | ||||||
|  | +	return nft_register_expr(&nft_flow_offload_type); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void __exit nft_flow_offload_module_exit(void) | ||||||
|  |  { | ||||||
|  |  	nft_unregister_expr(&nft_flow_offload_type); | ||||||
|  | -	unregister_netdevice_notifier(&flow_offload_netdev_notifier); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  module_init(nft_flow_offload_module_init); | ||||||
| @@ -0,0 +1,112 @@ | |||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Wed, 13 Jun 2018 12:33:39 +0200 | ||||||
|  | Subject: [PATCH] netfilter: nf_flow_table: fix offloaded connection timeout | ||||||
|  |  corner case | ||||||
|  |  | ||||||
|  | The full teardown of offloaded flows is deferred to a gc work item, | ||||||
|  | however processing of packets by netfilter needs to happen immediately | ||||||
|  | after a teardown is requested, because the conntrack state needs to be | ||||||
|  | fixed up. | ||||||
|  |  | ||||||
|  | Since the IPS_OFFLOAD_BIT is still kept until the teardown is complete, | ||||||
|  | the netfilter conntrack gc can accidentally bump the timeout of a | ||||||
|  | connection where offload was just stopped, causing a conntrack entry | ||||||
|  | leak. | ||||||
|  |  | ||||||
|  | Fix this by moving the conntrack timeout bumping from conntrack core to | ||||||
|  | the nf_flow_offload and add a check to prevent bogus timeout bumps. | ||||||
|  |  | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | --- a/net/netfilter/nf_conntrack_core.c | ||||||
|  | +++ b/net/netfilter/nf_conntrack_core.c | ||||||
|  | @@ -1178,18 +1178,6 @@ static bool gc_worker_can_early_drop(con | ||||||
|  |  	return false; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -#define	DAY	(86400 * HZ) | ||||||
|  | - | ||||||
|  | -/* Set an arbitrary timeout large enough not to ever expire, this save | ||||||
|  | - * us a check for the IPS_OFFLOAD_BIT from the packet path via | ||||||
|  | - * nf_ct_is_expired(). | ||||||
|  | - */ | ||||||
|  | -static void nf_ct_offload_timeout(struct nf_conn *ct) | ||||||
|  | -{ | ||||||
|  | -	if (nf_ct_expires(ct) < DAY / 2) | ||||||
|  | -		ct->timeout = nfct_time_stamp + DAY; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  |  static void gc_worker(struct work_struct *work) | ||||||
|  |  { | ||||||
|  |  	unsigned int min_interval = max(HZ / GC_MAX_BUCKETS_DIV, 1u); | ||||||
|  | @@ -1226,10 +1214,8 @@ static void gc_worker(struct work_struct | ||||||
|  |  			tmp = nf_ct_tuplehash_to_ctrack(h); | ||||||
|  |   | ||||||
|  |  			scanned++; | ||||||
|  | -			if (test_bit(IPS_OFFLOAD_BIT, &tmp->status)) { | ||||||
|  | -				nf_ct_offload_timeout(tmp); | ||||||
|  | +			if (test_bit(IPS_OFFLOAD_BIT, &tmp->status)) | ||||||
|  |  				continue; | ||||||
|  | -			} | ||||||
|  |   | ||||||
|  |  			if (nf_ct_is_expired(tmp)) { | ||||||
|  |  				nf_ct_gc_expired(tmp); | ||||||
|  | --- a/net/netfilter/nf_flow_table_core.c | ||||||
|  | +++ b/net/netfilter/nf_flow_table_core.c | ||||||
|  | @@ -183,10 +183,29 @@ static const struct rhashtable_params nf | ||||||
|  |  	.automatic_shrinking	= true, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +#define        DAY     (86400 * HZ) | ||||||
|  | + | ||||||
|  | +/* Set an arbitrary timeout large enough not to ever expire, this save | ||||||
|  | + * us a check for the IPS_OFFLOAD_BIT from the packet path via | ||||||
|  | + * nf_ct_is_expired(). | ||||||
|  | + */ | ||||||
|  | +static void nf_ct_offload_timeout(struct flow_offload *flow) | ||||||
|  | +{ | ||||||
|  | +	struct flow_offload_entry *entry; | ||||||
|  | +	struct nf_conn *ct; | ||||||
|  | + | ||||||
|  | +	entry = container_of(flow, struct flow_offload_entry, flow); | ||||||
|  | +	ct = entry->ct; | ||||||
|  | + | ||||||
|  | +	if (nf_ct_expires(ct) < DAY / 2) | ||||||
|  | +		ct->timeout = nfct_time_stamp + DAY; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) | ||||||
|  |  { | ||||||
|  |  	int err; | ||||||
|  |   | ||||||
|  | +	nf_ct_offload_timeout(flow); | ||||||
|  |  	flow->timeout = (u32)jiffies + NF_FLOW_TIMEOUT; | ||||||
|  |   | ||||||
|  |  	err = rhashtable_insert_fast(&flow_table->rhashtable, | ||||||
|  | @@ -317,6 +336,8 @@ static int nf_flow_offload_gc_step(struc | ||||||
|  |  	rhashtable_walk_start(&hti); | ||||||
|  |   | ||||||
|  |  	while ((tuplehash = rhashtable_walk_next(&hti))) { | ||||||
|  | +		bool teardown; | ||||||
|  | + | ||||||
|  |  		if (IS_ERR(tuplehash)) { | ||||||
|  |  			err = PTR_ERR(tuplehash); | ||||||
|  |  			if (err != -EAGAIN) | ||||||
|  | @@ -329,9 +350,13 @@ static int nf_flow_offload_gc_step(struc | ||||||
|  |   | ||||||
|  |  		flow = container_of(tuplehash, struct flow_offload, tuplehash[0]); | ||||||
|  |   | ||||||
|  | -		if (nf_flow_has_expired(flow) || | ||||||
|  | -		    (flow->flags & (FLOW_OFFLOAD_DYING | | ||||||
|  | -				    FLOW_OFFLOAD_TEARDOWN))) | ||||||
|  | +		teardown = flow->flags & (FLOW_OFFLOAD_DYING | | ||||||
|  | +					  FLOW_OFFLOAD_TEARDOWN); | ||||||
|  | + | ||||||
|  | +		if (!teardown) | ||||||
|  | +			nf_ct_offload_timeout(flow); | ||||||
|  | + | ||||||
|  | +		if (nf_flow_has_expired(flow) || teardown) | ||||||
|  |  			flow_offload_del(flow_table, flow); | ||||||
|  |  	} | ||||||
|  |  out: | ||||||
| @@ -0,0 +1,24 @@ | |||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Thu, 14 Jun 2018 11:20:09 +0200 | ||||||
|  | Subject: [PATCH] netfilter: nf_flow_table: fix up ct state of flows after | ||||||
|  |  timeout | ||||||
|  |  | ||||||
|  | If a connection simply times out instead of being torn down, it is left | ||||||
|  | active with a long timeout. Fix this by calling flow_offload_fixup_ct_state | ||||||
|  | here as well. | ||||||
|  |  | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | --- a/net/netfilter/nf_flow_table_core.c | ||||||
|  | +++ b/net/netfilter/nf_flow_table_core.c | ||||||
|  | @@ -243,6 +243,9 @@ static void flow_offload_del(struct nf_f | ||||||
|  |  	e = container_of(flow, struct flow_offload_entry, flow); | ||||||
|  |  	clear_bit(IPS_OFFLOAD_BIT, &e->ct->status); | ||||||
|  |   | ||||||
|  | +	if (!(flow->flags & FLOW_OFFLOAD_TEARDOWN)) | ||||||
|  | +		flow_offload_fixup_ct_state(e->ct); | ||||||
|  | + | ||||||
|  |  	flow_offload_free(flow); | ||||||
|  |  } | ||||||
|  |   | ||||||
| @@ -0,0 +1,670 @@ | |||||||
|  | From d129a72f465dab2d9fc8f1580c38600a8b808327 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> | ||||||
|  | Date: Wed, 13 Mar 2019 20:54:49 +0000 | ||||||
|  | Subject: [PATCH] net: sched: Backport Introduce act_ctinfo action | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | ctinfo is a new tc filter action module.  It is designed to restore | ||||||
|  | information contained in firewall conntrack marks to other packet fields | ||||||
|  | and is typically used on packet ingress paths.  At present it has two | ||||||
|  | independent sub-functions or operating modes, DSCP restoration mode & | ||||||
|  | skb mark restoration mode. | ||||||
|  |  | ||||||
|  | The DSCP restore mode: | ||||||
|  |  | ||||||
|  | This mode copies DSCP values that have been placed in the firewall | ||||||
|  | conntrack mark back into the IPv4/v6 diffserv fields of relevant | ||||||
|  | packets. | ||||||
|  |  | ||||||
|  | The DSCP restoration is intended for use and has been found useful for | ||||||
|  | restoring ingress classifications based on egress classifications across | ||||||
|  | links that bleach or otherwise change DSCP, typically home ISP Internet | ||||||
|  | links.  Restoring DSCP on ingress on the WAN link allows qdiscs such as | ||||||
|  | but by no means limited to CAKE to shape inbound packets according to | ||||||
|  | policies that are easier to set & mark on egress. | ||||||
|  |  | ||||||
|  | Ingress classification is traditionally a challenging task since | ||||||
|  | iptables rules haven't yet run and tc filter/eBPF programs are pre-NAT | ||||||
|  | lookups, hence are unable to see internal IPv4 addresses as used on the | ||||||
|  | typical home masquerading gateway.  Thus marking the connection in some | ||||||
|  | manner on egress for later restoration of classification on ingress is | ||||||
|  | easier to implement. | ||||||
|  |  | ||||||
|  | Parameters related to DSCP restore mode: | ||||||
|  |  | ||||||
|  | dscpmask - a 32 bit mask of 6 contiguous bits and indicate bits of the | ||||||
|  | conntrack mark field contain the DSCP value to be restored. | ||||||
|  |  | ||||||
|  | statemask - a 32 bit mask of (usually) 1 bit length, outside the area | ||||||
|  | specified by dscpmask.  This represents a conditional operation flag | ||||||
|  | whereby the DSCP is only restored if the flag is set.  This is useful to | ||||||
|  | implement a 'one shot' iptables based classification where the | ||||||
|  | 'complicated' iptables rules are only run once to classify the | ||||||
|  | connection on initial (egress) packet and subsequent packets are all | ||||||
|  | marked/restored with the same DSCP.  A mask of zero disables the | ||||||
|  | conditional behaviour ie. the conntrack mark DSCP bits are always | ||||||
|  | restored to the ip diffserv field (assuming the conntrack entry is found | ||||||
|  | & the skb is an ipv4/ipv6 type) | ||||||
|  |  | ||||||
|  | e.g. dscpmask 0xfc000000 statemask 0x01000000 | ||||||
|  |  | ||||||
|  | |----0xFC----conntrack mark----000000---| | ||||||
|  | | Bits 31-26 | bit 25 | bit24 |~~~ Bit 0| | ||||||
|  | | DSCP       | unused | flag  |unused   | | ||||||
|  | |-----------------------0x01---000000---| | ||||||
|  |       |                   | | ||||||
|  |       |                   | | ||||||
|  |       ---|             Conditional flag | ||||||
|  |          v             only restore if set | ||||||
|  | |-ip diffserv-| | ||||||
|  | | 6 bits      | | ||||||
|  | |-------------| | ||||||
|  |  | ||||||
|  | The skb mark restore mode (cpmark): | ||||||
|  |  | ||||||
|  | This mode copies the firewall conntrack mark to the skb's mark field. | ||||||
|  | It is completely the functional equivalent of the existing act_connmark | ||||||
|  | action with the additional feature of being able to apply a mask to the | ||||||
|  | restored value. | ||||||
|  |  | ||||||
|  | Parameters related to skb mark restore mode: | ||||||
|  |  | ||||||
|  | mask - a 32 bit mask applied to the firewall conntrack mark to mask out | ||||||
|  | bits unwanted for restoration.  This can be useful where the conntrack | ||||||
|  | mark is being used for different purposes by different applications.  If | ||||||
|  | not specified and by default the whole mark field is copied (i.e. | ||||||
|  | default mask of 0xffffffff) | ||||||
|  |  | ||||||
|  | e.g. mask 0x00ffffff to mask out the top 8 bits being used by the | ||||||
|  | aforementioned DSCP restore mode. | ||||||
|  |  | ||||||
|  | |----0x00----conntrack mark----ffffff---| | ||||||
|  | | Bits 31-24 |                          | | ||||||
|  | | DSCP & flag|      some value here     | | ||||||
|  | |---------------------------------------| | ||||||
|  | 			| | ||||||
|  | 			| | ||||||
|  | 			v | ||||||
|  | |------------skb mark-------------------| | ||||||
|  | |            |                          | | ||||||
|  | |  zeroed    |                          | | ||||||
|  | |---------------------------------------| | ||||||
|  |  | ||||||
|  | Overall parameters: | ||||||
|  |  | ||||||
|  | zone - conntrack zone | ||||||
|  |  | ||||||
|  | control - action related control (reclassify | pipe | drop | continue | | ||||||
|  | ok | goto chain <CHAIN_INDEX>) | ||||||
|  |  | ||||||
|  | Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> | ||||||
|  | Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com> | ||||||
|  | Acked-by: Cong Wang <xiyou.wangcong@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  |  | ||||||
|  | Backport | ||||||
|  | Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> | ||||||
|  | --- | ||||||
|  |  include/net/tc_act/tc_ctinfo.h            |  33 ++ | ||||||
|  |  include/uapi/linux/pkt_cls.h              |   3 +- | ||||||
|  |  include/uapi/linux/tc_act/tc_ctinfo.h     |  29 ++ | ||||||
|  |  net/sched/Kconfig                         |  17 + | ||||||
|  |  net/sched/Makefile                        |   1 + | ||||||
|  |  net/sched/act_ctinfo.c                    | 420 ++++++++++++++++++++++ | ||||||
|  |  tools/testing/selftests/tc-testing/config |   1 + | ||||||
|  |  7 files changed, 503 insertions(+), 1 deletion(-) | ||||||
|  |  create mode 100644 include/net/tc_act/tc_ctinfo.h | ||||||
|  |  create mode 100644 include/uapi/linux/tc_act/tc_ctinfo.h | ||||||
|  |  create mode 100644 net/sched/act_ctinfo.c | ||||||
|  |  | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/include/net/tc_act/tc_ctinfo.h | ||||||
|  | @@ -0,0 +1,33 @@ | ||||||
|  | +/* SPDX-License-Identifier: GPL-2.0 */ | ||||||
|  | +#ifndef __NET_TC_CTINFO_H | ||||||
|  | +#define __NET_TC_CTINFO_H | ||||||
|  | + | ||||||
|  | +#include <net/act_api.h> | ||||||
|  | + | ||||||
|  | +struct tcf_ctinfo_params { | ||||||
|  | +	struct rcu_head rcu; | ||||||
|  | +	struct net *net; | ||||||
|  | +	u32 dscpmask; | ||||||
|  | +	u32 dscpstatemask; | ||||||
|  | +	u32 cpmarkmask; | ||||||
|  | +	u16 zone; | ||||||
|  | +	u8 mode; | ||||||
|  | +	u8 dscpmaskshift; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +struct tcf_ctinfo { | ||||||
|  | +	struct tc_action common; | ||||||
|  | +	struct tcf_ctinfo_params __rcu *params; | ||||||
|  | +	u64 stats_dscp_set; | ||||||
|  | +	u64 stats_dscp_error; | ||||||
|  | +	u64 stats_cpmark_set; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +enum { | ||||||
|  | +	CTINFO_MODE_DSCP	= BIT(0), | ||||||
|  | +	CTINFO_MODE_CPMARK	= BIT(1) | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +#define to_ctinfo(a) ((struct tcf_ctinfo *)a) | ||||||
|  | + | ||||||
|  | +#endif /* __NET_TC_CTINFO_H */ | ||||||
|  | --- a/include/uapi/linux/pkt_cls.h | ||||||
|  | +++ b/include/uapi/linux/pkt_cls.h | ||||||
|  | @@ -68,7 +68,8 @@ enum { | ||||||
|  |  	TCA_ID_UNSPEC=0, | ||||||
|  |  	TCA_ID_POLICE=1, | ||||||
|  |  	/* other actions go here */ | ||||||
|  | -	__TCA_ID_MAX=255 | ||||||
|  | +	TCA_ID_CTINFO, | ||||||
|  | +	__TCA_ID_MAX = 255 | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  #define TCA_ID_MAX __TCA_ID_MAX | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/include/uapi/linux/tc_act/tc_ctinfo.h | ||||||
|  | @@ -0,0 +1,29 @@ | ||||||
|  | +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||||||
|  | +#ifndef __UAPI_TC_CTINFO_H | ||||||
|  | +#define __UAPI_TC_CTINFO_H | ||||||
|  | + | ||||||
|  | +#include <linux/types.h> | ||||||
|  | +#include <linux/pkt_cls.h> | ||||||
|  | + | ||||||
|  | +struct tc_ctinfo { | ||||||
|  | +	tc_gen; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +enum { | ||||||
|  | +	TCA_CTINFO_UNSPEC, | ||||||
|  | +	TCA_CTINFO_PAD, | ||||||
|  | +	TCA_CTINFO_TM, | ||||||
|  | +	TCA_CTINFO_ACT, | ||||||
|  | +	TCA_CTINFO_ZONE, | ||||||
|  | +	TCA_CTINFO_PARMS_DSCP_MASK, | ||||||
|  | +	TCA_CTINFO_PARMS_DSCP_STATEMASK, | ||||||
|  | +	TCA_CTINFO_PARMS_CPMARK_MASK, | ||||||
|  | +	TCA_CTINFO_STATS_DSCP_SET, | ||||||
|  | +	TCA_CTINFO_STATS_DSCP_ERROR, | ||||||
|  | +	TCA_CTINFO_STATS_CPMARK_SET, | ||||||
|  | +	__TCA_CTINFO_MAX | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +#define TCA_CTINFO_MAX (__TCA_CTINFO_MAX - 1) | ||||||
|  | + | ||||||
|  | +#endif | ||||||
|  | --- a/net/sched/Kconfig | ||||||
|  | +++ b/net/sched/Kconfig | ||||||
|  | @@ -866,6 +866,23 @@ config NET_ACT_CONNMARK | ||||||
|  |  	  To compile this code as a module, choose M here: the | ||||||
|  |  	  module will be called act_connmark. | ||||||
|  |   | ||||||
|  | +config NET_ACT_CTINFO | ||||||
|  | +        tristate "Netfilter Connection Mark Actions" | ||||||
|  | +        depends on NET_CLS_ACT && NETFILTER && IP_NF_IPTABLES | ||||||
|  | +        depends on NF_CONNTRACK && NF_CONNTRACK_MARK | ||||||
|  | +        help | ||||||
|  | +	  Say Y here to allow transfer of a connmark stored information. | ||||||
|  | +	  Current actions transfer connmark stored DSCP into | ||||||
|  | +	  ipv4/v6 diffserv and/or to transfer connmark to packet | ||||||
|  | +	  mark.  Both are useful for restoring egress based marks | ||||||
|  | +	  back onto ingress connections for qdisc priority mapping | ||||||
|  | +	  purposes. | ||||||
|  | + | ||||||
|  | +	  If unsure, say N. | ||||||
|  | + | ||||||
|  | +	  To compile this code as a module, choose M here: the | ||||||
|  | +	  module will be called act_ctinfo. | ||||||
|  | + | ||||||
|  |  config NET_ACT_SKBMOD | ||||||
|  |          tristate "skb data modification action" | ||||||
|  |          depends on NET_CLS_ACT | ||||||
|  | --- a/net/sched/Makefile | ||||||
|  | +++ b/net/sched/Makefile | ||||||
|  | @@ -21,6 +21,7 @@ obj-$(CONFIG_NET_ACT_CSUM)	+= act_csum.o | ||||||
|  |  obj-$(CONFIG_NET_ACT_VLAN)	+= act_vlan.o | ||||||
|  |  obj-$(CONFIG_NET_ACT_BPF)	+= act_bpf.o | ||||||
|  |  obj-$(CONFIG_NET_ACT_CONNMARK)	+= act_connmark.o | ||||||
|  | +obj-$(CONFIG_NET_ACT_CTINFO)	+= act_ctinfo.o | ||||||
|  |  obj-$(CONFIG_NET_ACT_SKBMOD)	+= act_skbmod.o | ||||||
|  |  obj-$(CONFIG_NET_ACT_IFE)	+= act_ife.o | ||||||
|  |  obj-$(CONFIG_NET_IFE_SKBMARK)	+= act_meta_mark.o | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/net/sched/act_ctinfo.c | ||||||
|  | @@ -0,0 +1,420 @@ | ||||||
|  | +// SPDX-License-Identifier: GPL-2.0+ | ||||||
|  | +/* net/sched/act_ctinfo.c  netfilter ctinfo connmark actions | ||||||
|  | + * | ||||||
|  | + * Copyright (c) 2019 Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#include <linux/module.h> | ||||||
|  | +#include <linux/init.h> | ||||||
|  | +#include <linux/kernel.h> | ||||||
|  | +#include <linux/skbuff.h> | ||||||
|  | +#include <linux/rtnetlink.h> | ||||||
|  | +#include <linux/pkt_cls.h> | ||||||
|  | +#include <linux/ip.h> | ||||||
|  | +#include <linux/ipv6.h> | ||||||
|  | +#include <net/netlink.h> | ||||||
|  | +#include <net/pkt_sched.h> | ||||||
|  | +#include <net/act_api.h> | ||||||
|  | +#include <net/pkt_cls.h> | ||||||
|  | +#include <uapi/linux/tc_act/tc_ctinfo.h> | ||||||
|  | +#include <net/tc_act/tc_ctinfo.h> | ||||||
|  | + | ||||||
|  | +#include <net/netfilter/nf_conntrack.h> | ||||||
|  | +#include <net/netfilter/nf_conntrack_core.h> | ||||||
|  | +#include <net/netfilter/nf_conntrack_ecache.h> | ||||||
|  | +#include <net/netfilter/nf_conntrack_zones.h> | ||||||
|  | + | ||||||
|  | +static struct tc_action_ops act_ctinfo_ops; | ||||||
|  | +static unsigned int ctinfo_net_id; | ||||||
|  | + | ||||||
|  | +static void tcf_ctinfo_dscp_set(struct nf_conn *ct, struct tcf_ctinfo *ca, | ||||||
|  | +				struct tcf_ctinfo_params *cp, | ||||||
|  | +				struct sk_buff *skb, int wlen, int proto) | ||||||
|  | +{ | ||||||
|  | +	u8 dscp, newdscp; | ||||||
|  | + | ||||||
|  | +	newdscp = (((ct->mark & cp->dscpmask) >> cp->dscpmaskshift) << 2) & | ||||||
|  | +		     ~INET_ECN_MASK; | ||||||
|  | + | ||||||
|  | +	switch (proto) { | ||||||
|  | +	case NFPROTO_IPV4: | ||||||
|  | +		dscp = ipv4_get_dsfield(ip_hdr(skb)) & ~INET_ECN_MASK; | ||||||
|  | +		if (dscp != newdscp) { | ||||||
|  | +			if (likely(!skb_try_make_writable(skb, wlen))) { | ||||||
|  | +				ipv4_change_dsfield(ip_hdr(skb), | ||||||
|  | +						    INET_ECN_MASK, | ||||||
|  | +						    newdscp); | ||||||
|  | +				ca->stats_dscp_set++; | ||||||
|  | +			} else { | ||||||
|  | +				ca->stats_dscp_error++; | ||||||
|  | +			} | ||||||
|  | +		} | ||||||
|  | +		break; | ||||||
|  | +	case NFPROTO_IPV6: | ||||||
|  | +		dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & ~INET_ECN_MASK; | ||||||
|  | +		if (dscp != newdscp) { | ||||||
|  | +			if (likely(!skb_try_make_writable(skb, wlen))) { | ||||||
|  | +				ipv6_change_dsfield(ipv6_hdr(skb), | ||||||
|  | +						    INET_ECN_MASK, | ||||||
|  | +						    newdscp); | ||||||
|  | +				ca->stats_dscp_set++; | ||||||
|  | +			} else { | ||||||
|  | +				ca->stats_dscp_error++; | ||||||
|  | +			} | ||||||
|  | +		} | ||||||
|  | +		break; | ||||||
|  | +	default: | ||||||
|  | +		break; | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void tcf_ctinfo_cpmark_set(struct nf_conn *ct, struct tcf_ctinfo *ca, | ||||||
|  | +				  struct tcf_ctinfo_params *cp, | ||||||
|  | +				  struct sk_buff *skb) | ||||||
|  | +{ | ||||||
|  | +	ca->stats_cpmark_set++; | ||||||
|  | +	skb->mark = ct->mark & cp->cpmarkmask; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int tcf_ctinfo_act(struct sk_buff *skb, const struct tc_action *a, | ||||||
|  | +			  struct tcf_result *res) | ||||||
|  | +{ | ||||||
|  | +	const struct nf_conntrack_tuple_hash *thash = NULL; | ||||||
|  | +	struct tcf_ctinfo *ca = to_ctinfo(a); | ||||||
|  | +	struct nf_conntrack_tuple tuple; | ||||||
|  | +	struct nf_conntrack_zone zone; | ||||||
|  | +	enum ip_conntrack_info ctinfo; | ||||||
|  | +	struct tcf_ctinfo_params *cp; | ||||||
|  | +	struct nf_conn *ct; | ||||||
|  | +	int proto, wlen; | ||||||
|  | +	int action; | ||||||
|  | + | ||||||
|  | +	cp = rcu_dereference_bh(ca->params); | ||||||
|  | + | ||||||
|  | +	tcf_lastuse_update(&ca->tcf_tm); | ||||||
|  | +	bstats_update(&ca->tcf_bstats, skb); | ||||||
|  | +	action = READ_ONCE(ca->tcf_action); | ||||||
|  | + | ||||||
|  | +	wlen = skb_network_offset(skb); | ||||||
|  | +	if (tc_skb_protocol(skb) == htons(ETH_P_IP)) { | ||||||
|  | +		wlen += sizeof(struct iphdr); | ||||||
|  | +		if (!pskb_may_pull(skb, wlen)) | ||||||
|  | +			goto out; | ||||||
|  | + | ||||||
|  | +		proto = NFPROTO_IPV4; | ||||||
|  | +	} else if (tc_skb_protocol(skb) == htons(ETH_P_IPV6)) { | ||||||
|  | +		wlen += sizeof(struct ipv6hdr); | ||||||
|  | +		if (!pskb_may_pull(skb, wlen)) | ||||||
|  | +			goto out; | ||||||
|  | + | ||||||
|  | +		proto = NFPROTO_IPV6; | ||||||
|  | +	} else { | ||||||
|  | +		goto out; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	ct = nf_ct_get(skb, &ctinfo); | ||||||
|  | +	if (!ct) { /* look harder, usually ingress */ | ||||||
|  | +		if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), | ||||||
|  | +				       proto, cp->net, &tuple)) | ||||||
|  | +			goto out; | ||||||
|  | +		zone.id = cp->zone; | ||||||
|  | +		zone.dir = NF_CT_DEFAULT_ZONE_DIR; | ||||||
|  | + | ||||||
|  | +		thash = nf_conntrack_find_get(cp->net, &zone, &tuple); | ||||||
|  | +		if (!thash) | ||||||
|  | +			goto out; | ||||||
|  | + | ||||||
|  | +		ct = nf_ct_tuplehash_to_ctrack(thash); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (cp->mode & CTINFO_MODE_DSCP) | ||||||
|  | +		if (!cp->dscpstatemask || (ct->mark & cp->dscpstatemask)) | ||||||
|  | +			tcf_ctinfo_dscp_set(ct, ca, cp, skb, wlen, proto); | ||||||
|  | + | ||||||
|  | +	if (cp->mode & CTINFO_MODE_CPMARK) | ||||||
|  | +		tcf_ctinfo_cpmark_set(ct, ca, cp, skb); | ||||||
|  | + | ||||||
|  | +	if (thash) | ||||||
|  | +		nf_ct_put(ct); | ||||||
|  | +out: | ||||||
|  | +	return action; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct nla_policy ctinfo_policy[TCA_CTINFO_MAX + 1] = { | ||||||
|  | +	[TCA_CTINFO_ACT]		  = { .len = sizeof(struct | ||||||
|  | +							    tc_ctinfo) }, | ||||||
|  | +	[TCA_CTINFO_ZONE]		  = { .type = NLA_U16 }, | ||||||
|  | +	[TCA_CTINFO_PARMS_DSCP_MASK]	  = { .type = NLA_U32 }, | ||||||
|  | +	[TCA_CTINFO_PARMS_DSCP_STATEMASK] = { .type = NLA_U32 }, | ||||||
|  | +	[TCA_CTINFO_PARMS_CPMARK_MASK]	  = { .type = NLA_U32 }, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int tcf_ctinfo_init(struct net *net, struct nlattr *nla, | ||||||
|  | +			   struct nlattr *est, struct tc_action **a, | ||||||
|  | +			   int ovr, int bind, bool rtnl_held, | ||||||
|  | +			   struct netlink_ext_ack *extack) | ||||||
|  | +{ | ||||||
|  | +	struct tc_action_net *tn = net_generic(net, ctinfo_net_id); | ||||||
|  | +	u32 dscpmask = 0, dscpstatemask, index; | ||||||
|  | +	struct nlattr *tb[TCA_CTINFO_MAX + 1]; | ||||||
|  | +	struct tcf_ctinfo_params *cp_new; | ||||||
|  | +/*	struct tcf_chain *goto_ch = NULL; */ | ||||||
|  | +	struct tc_ctinfo *actparm; | ||||||
|  | +	struct tcf_ctinfo *ci; | ||||||
|  | +	u8 dscpmaskshift; | ||||||
|  | +	int ret = 0, err; | ||||||
|  | + | ||||||
|  | +	if (!nla) { | ||||||
|  | +		NL_SET_ERR_MSG_MOD(extack, "ctinfo requires attributes to be passed"); | ||||||
|  | +		return -EINVAL; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	err = nla_parse_nested(tb, TCA_CTINFO_MAX, nla, ctinfo_policy, extack); | ||||||
|  | +	if (err < 0) | ||||||
|  | +		return err; | ||||||
|  | + | ||||||
|  | +	if (!tb[TCA_CTINFO_ACT]) { | ||||||
|  | +		NL_SET_ERR_MSG_MOD(extack, | ||||||
|  | +				   "Missing required TCA_CTINFO_ACT attribute"); | ||||||
|  | +		return -EINVAL; | ||||||
|  | +	} | ||||||
|  | +	actparm = nla_data(tb[TCA_CTINFO_ACT]); | ||||||
|  | + | ||||||
|  | +	/* do some basic validation here before dynamically allocating things */ | ||||||
|  | +	/* that we would otherwise have to clean up.			      */ | ||||||
|  | +	if (tb[TCA_CTINFO_PARMS_DSCP_MASK]) { | ||||||
|  | +		dscpmask = nla_get_u32(tb[TCA_CTINFO_PARMS_DSCP_MASK]); | ||||||
|  | +		/* need contiguous 6 bit mask */ | ||||||
|  | +		dscpmaskshift = dscpmask ? __ffs(dscpmask) : 0; | ||||||
|  | +		if ((~0 & (dscpmask >> dscpmaskshift)) != 0x3f) { | ||||||
|  | +			NL_SET_ERR_MSG_ATTR(extack, | ||||||
|  | +					    tb[TCA_CTINFO_PARMS_DSCP_MASK], | ||||||
|  | +					    "dscp mask must be 6 contiguous bits"); | ||||||
|  | +			return -EINVAL; | ||||||
|  | +		} | ||||||
|  | +		dscpstatemask = tb[TCA_CTINFO_PARMS_DSCP_STATEMASK] ? | ||||||
|  | +			nla_get_u32(tb[TCA_CTINFO_PARMS_DSCP_STATEMASK]) : 0; | ||||||
|  | +		/* mask & statemask must not overlap */ | ||||||
|  | +		if (dscpmask & dscpstatemask) { | ||||||
|  | +			NL_SET_ERR_MSG_ATTR(extack, | ||||||
|  | +					    tb[TCA_CTINFO_PARMS_DSCP_STATEMASK], | ||||||
|  | +					    "dscp statemask must not overlap dscp mask"); | ||||||
|  | +			return -EINVAL; | ||||||
|  | +		} | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	/* done the validation:now to the actual action allocation */ | ||||||
|  | +	index = actparm->index; | ||||||
|  | +	err = tcf_idr_check_alloc(tn, &index, a, bind); | ||||||
|  | +	if (!err) { | ||||||
|  | +		ret = tcf_idr_create(tn, index, est, a, | ||||||
|  | +				     &act_ctinfo_ops, bind, false); | ||||||
|  | +		if (ret) { | ||||||
|  | +			tcf_idr_cleanup(tn, index); | ||||||
|  | +			return ret; | ||||||
|  | +		} | ||||||
|  | +		ret = ACT_P_CREATED; | ||||||
|  | +	} else if (err > 0) { | ||||||
|  | +		if (bind) /* don't override defaults */ | ||||||
|  | +			return 0; | ||||||
|  | +		if (!ovr) { | ||||||
|  | +			tcf_idr_release(*a, bind); | ||||||
|  | +			return -EEXIST; | ||||||
|  | +		} | ||||||
|  | +	} else { | ||||||
|  | +		return err; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +/*	err = tcf_action_check_ctrlact(actparm->action, tp, &goto_ch, extack); | ||||||
|  | +	if (err < 0) | ||||||
|  | +		goto release_idr; | ||||||
|  | +		*/ | ||||||
|  | + | ||||||
|  | +	ci = to_ctinfo(*a); | ||||||
|  | + | ||||||
|  | +	cp_new = kzalloc(sizeof(*cp_new), GFP_KERNEL); | ||||||
|  | +	if (unlikely(!cp_new)) { | ||||||
|  | +		err = -ENOMEM; | ||||||
|  | +		goto put_chain; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	cp_new->net = net; | ||||||
|  | +	cp_new->zone = tb[TCA_CTINFO_ZONE] ? | ||||||
|  | +			nla_get_u16(tb[TCA_CTINFO_ZONE]) : 0; | ||||||
|  | +	if (dscpmask) { | ||||||
|  | +		cp_new->dscpmask = dscpmask; | ||||||
|  | +		cp_new->dscpmaskshift = dscpmaskshift; | ||||||
|  | +		cp_new->dscpstatemask = dscpstatemask; | ||||||
|  | +		cp_new->mode |= CTINFO_MODE_DSCP; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (tb[TCA_CTINFO_PARMS_CPMARK_MASK]) { | ||||||
|  | +		cp_new->cpmarkmask = | ||||||
|  | +				nla_get_u32(tb[TCA_CTINFO_PARMS_CPMARK_MASK]); | ||||||
|  | +		cp_new->mode |= CTINFO_MODE_CPMARK; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	spin_lock_bh(&ci->tcf_lock); | ||||||
|  | +/*	goto_ch = tcf_action_set_ctrlact(*a, actparm->action, goto_ch); */ | ||||||
|  | +	ci->tcf_action = actparm->action; | ||||||
|  | +	rcu_swap_protected(ci->params, cp_new, | ||||||
|  | +			   lockdep_is_held(&ci->tcf_lock)); | ||||||
|  | +	spin_unlock_bh(&ci->tcf_lock); | ||||||
|  | + | ||||||
|  | +/*	if (goto_ch) | ||||||
|  | +		tcf_chain_put_by_act(goto_ch); */ | ||||||
|  | +	if (cp_new) | ||||||
|  | +		kfree_rcu(cp_new, rcu); | ||||||
|  | + | ||||||
|  | +	if (ret == ACT_P_CREATED) | ||||||
|  | +		tcf_idr_insert(tn, *a); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | + | ||||||
|  | +put_chain: | ||||||
|  | +/*	if (goto_ch) | ||||||
|  | +		tcf_chain_put_by_act(goto_ch); | ||||||
|  | +release_idr: */ | ||||||
|  | +	tcf_idr_release(*a, bind); | ||||||
|  | +	return err; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int tcf_ctinfo_dump(struct sk_buff *skb, struct tc_action *a, | ||||||
|  | +			   int bind, int ref) | ||||||
|  | +{ | ||||||
|  | +	struct tcf_ctinfo *ci = to_ctinfo(a); | ||||||
|  | +	struct tc_ctinfo opt = { | ||||||
|  | +		.index   = ci->tcf_index, | ||||||
|  | +		.refcnt  = refcount_read(&ci->tcf_refcnt) - ref, | ||||||
|  | +		.bindcnt = atomic_read(&ci->tcf_bindcnt) - bind, | ||||||
|  | +	}; | ||||||
|  | +	unsigned char *b = skb_tail_pointer(skb); | ||||||
|  | +	struct tcf_ctinfo_params *cp; | ||||||
|  | +	struct tcf_t t; | ||||||
|  | + | ||||||
|  | +	spin_lock_bh(&ci->tcf_lock); | ||||||
|  | +	cp = rcu_dereference_protected(ci->params, | ||||||
|  | +				       lockdep_is_held(&ci->tcf_lock)); | ||||||
|  | + | ||||||
|  | +	tcf_tm_dump(&t, &ci->tcf_tm); | ||||||
|  | +	if (nla_put_64bit(skb, TCA_CTINFO_TM, sizeof(t), &t, TCA_CTINFO_PAD)) | ||||||
|  | +		goto nla_put_failure; | ||||||
|  | + | ||||||
|  | +	opt.action = ci->tcf_action; | ||||||
|  | +	if (nla_put(skb, TCA_CTINFO_ACT, sizeof(opt), &opt)) | ||||||
|  | +		goto nla_put_failure; | ||||||
|  | + | ||||||
|  | +	if (nla_put_u16(skb, TCA_CTINFO_ZONE, cp->zone)) | ||||||
|  | +		goto nla_put_failure; | ||||||
|  | + | ||||||
|  | +	if (cp->mode & CTINFO_MODE_DSCP) { | ||||||
|  | +		if (nla_put_u32(skb, TCA_CTINFO_PARMS_DSCP_MASK, | ||||||
|  | +				cp->dscpmask)) | ||||||
|  | +			goto nla_put_failure; | ||||||
|  | +		if (nla_put_u32(skb, TCA_CTINFO_PARMS_DSCP_STATEMASK, | ||||||
|  | +				cp->dscpstatemask)) | ||||||
|  | +			goto nla_put_failure; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (cp->mode & CTINFO_MODE_CPMARK) { | ||||||
|  | +		if (nla_put_u32(skb, TCA_CTINFO_PARMS_CPMARK_MASK, | ||||||
|  | +				cp->cpmarkmask)) | ||||||
|  | +			goto nla_put_failure; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_DSCP_SET, | ||||||
|  | +			      ci->stats_dscp_set, TCA_CTINFO_PAD)) | ||||||
|  | +		goto nla_put_failure; | ||||||
|  | + | ||||||
|  | +	if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_DSCP_ERROR, | ||||||
|  | +			      ci->stats_dscp_error, TCA_CTINFO_PAD)) | ||||||
|  | +		goto nla_put_failure; | ||||||
|  | + | ||||||
|  | +	if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_CPMARK_SET, | ||||||
|  | +			      ci->stats_cpmark_set, TCA_CTINFO_PAD)) | ||||||
|  | +		goto nla_put_failure; | ||||||
|  | + | ||||||
|  | +	spin_unlock_bh(&ci->tcf_lock); | ||||||
|  | +	return skb->len; | ||||||
|  | + | ||||||
|  | +nla_put_failure: | ||||||
|  | +	spin_unlock_bh(&ci->tcf_lock); | ||||||
|  | +	nlmsg_trim(skb, b); | ||||||
|  | +	return -1; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int tcf_ctinfo_walker(struct net *net, struct sk_buff *skb, | ||||||
|  | +			     struct netlink_callback *cb, int type, | ||||||
|  | +			     const struct tc_action_ops *ops, | ||||||
|  | +			     struct netlink_ext_ack *extack) | ||||||
|  | +{ | ||||||
|  | +	struct tc_action_net *tn = net_generic(net, ctinfo_net_id); | ||||||
|  | + | ||||||
|  | +	return tcf_generic_walker(tn, skb, cb, type, ops, extack); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int tcf_ctinfo_search(struct net *net, struct tc_action **a, u32 index, | ||||||
|  | +			     struct netlink_ext_ack *extack) | ||||||
|  | +{ | ||||||
|  | +	struct tc_action_net *tn = net_generic(net, ctinfo_net_id); | ||||||
|  | + | ||||||
|  | +	return tcf_idr_search(tn, a, index); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void tcf_ctinfo_cleanup(struct tc_action *a) | ||||||
|  | +{ | ||||||
|  | +	struct tcf_ctinfo *ci = to_ctinfo(a); | ||||||
|  | +	struct tcf_ctinfo_params *cp; | ||||||
|  | + | ||||||
|  | +	cp = rcu_dereference_protected(ci->params, 1); | ||||||
|  | +	if (cp) | ||||||
|  | +		kfree_rcu(cp, rcu); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static struct tc_action_ops act_ctinfo_ops = { | ||||||
|  | +	.kind	= "ctinfo", | ||||||
|  | +	.type	= TCA_ID_CTINFO, | ||||||
|  | +	.owner	= THIS_MODULE, | ||||||
|  | +	.act	= tcf_ctinfo_act, | ||||||
|  | +	.dump	= tcf_ctinfo_dump, | ||||||
|  | +	.init	= tcf_ctinfo_init, | ||||||
|  | +	.walk	= tcf_ctinfo_walker, | ||||||
|  | +	.cleanup= tcf_ctinfo_cleanup, | ||||||
|  | +	.lookup	= tcf_ctinfo_search, | ||||||
|  | +	.size	= sizeof(struct tcf_ctinfo), | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static __net_init int ctinfo_init_net(struct net *net) | ||||||
|  | +{ | ||||||
|  | +	struct tc_action_net *tn = net_generic(net, ctinfo_net_id); | ||||||
|  | + | ||||||
|  | +	return tc_action_net_init(net, tn, &act_ctinfo_ops); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void __net_exit ctinfo_exit_net(struct list_head *net_list) | ||||||
|  | +{ | ||||||
|  | +	tc_action_net_exit(net_list, ctinfo_net_id); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static struct pernet_operations ctinfo_net_ops = { | ||||||
|  | +	.init		= ctinfo_init_net, | ||||||
|  | +	.exit_batch	= ctinfo_exit_net, | ||||||
|  | +	.id		= &ctinfo_net_id, | ||||||
|  | +	.size		= sizeof(struct tc_action_net), | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int __init ctinfo_init_module(void) | ||||||
|  | +{ | ||||||
|  | +	return tcf_register_action(&act_ctinfo_ops, &ctinfo_net_ops); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void __exit ctinfo_cleanup_module(void) | ||||||
|  | +{ | ||||||
|  | +	tcf_unregister_action(&act_ctinfo_ops, &ctinfo_net_ops); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +module_init(ctinfo_init_module); | ||||||
|  | +module_exit(ctinfo_cleanup_module); | ||||||
|  | +MODULE_AUTHOR("Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>"); | ||||||
|  | +MODULE_DESCRIPTION("Connection tracking mark actions"); | ||||||
|  | +MODULE_LICENSE("GPL"); | ||||||
|  | --- a/tools/testing/selftests/tc-testing/config | ||||||
|  | +++ b/tools/testing/selftests/tc-testing/config | ||||||
|  | @@ -38,6 +38,7 @@ CONFIG_NET_ACT_CSUM=m | ||||||
|  |  CONFIG_NET_ACT_VLAN=m | ||||||
|  |  CONFIG_NET_ACT_BPF=m | ||||||
|  |  CONFIG_NET_ACT_CONNMARK=m | ||||||
|  | +CONFIG_NET_ACT_CONNCTINFO=m | ||||||
|  |  CONFIG_NET_ACT_SKBMOD=m | ||||||
|  |  CONFIG_NET_ACT_IFE=m | ||||||
|  |  CONFIG_NET_ACT_TUNNEL_KEY=m | ||||||
| @@ -0,0 +1,37 @@ | |||||||
|  | From 9a4d83074769d6ecf1f5c3fef0f183b09abf3726 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Robert Marko <robimarko@gmail.com> | ||||||
|  | Date: Sat, 6 Oct 2018 17:36:42 +0200 | ||||||
|  | Subject: [PATCH 1/8] mtd: spinand: winbond: Add support for W25N01GV | ||||||
|  |  | ||||||
|  | W25N01GV is a single die version of the already supported | ||||||
|  | W25M02GV with half the capacity. Everything else is the | ||||||
|  | same so introduce support for W25N01GV. | ||||||
|  |  | ||||||
|  | Datasheet:http://www.winbond.com/resource-files/w25n01gv%20revl%20050918%20unsecured.pdf | ||||||
|  |  | ||||||
|  | Tested on 8devices Jalapeno dev board under OpenWrt running 4.19-rc5. | ||||||
|  |  | ||||||
|  | Signed-off-by: Robert Marko <robimarko@gmail.com> | ||||||
|  | Reviewed-by: Boris Brezillon <boris.brezillon@bootlin.com> | ||||||
|  | Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||||
|  | --- | ||||||
|  |  drivers/mtd/nand/spi/winbond.c | 8 ++++++++ | ||||||
|  |  1 file changed, 8 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/mtd/nand/spi/winbond.c | ||||||
|  | +++ b/drivers/mtd/nand/spi/winbond.c | ||||||
|  | @@ -84,6 +84,14 @@ static const struct spinand_info winbond | ||||||
|  |  		     0, | ||||||
|  |  		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), | ||||||
|  |  		     SPINAND_SELECT_TARGET(w25m02gv_select_target)), | ||||||
|  | +	SPINAND_INFO("W25N01GV", 0xAA, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(1, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  /** | ||||||
| @@ -0,0 +1,188 @@ | |||||||
|  | From 10949af1681d5bb5cdbcc012815c6e40eec17d02 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Schrempf Frieder <frieder.schrempf@kontron.De> | ||||||
|  | Date: Thu, 8 Nov 2018 08:32:11 +0000 | ||||||
|  | Subject: [PATCH 2/8] mtd: spinand: Add initial support for Toshiba TC58CVG2S0H | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | Add minimal support for the Toshiba TC58CVG2S0H SPI NAND chip. | ||||||
|  |  | ||||||
|  | Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de> | ||||||
|  | Acked-by: Clément Péron <peron.clem@gmail.com> | ||||||
|  | Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||||
|  | --- | ||||||
|  |  drivers/mtd/nand/spi/Makefile  |   2 +- | ||||||
|  |  drivers/mtd/nand/spi/core.c    |   1 + | ||||||
|  |  drivers/mtd/nand/spi/toshiba.c | 137 +++++++++++++++++++++++++++++++++ | ||||||
|  |  include/linux/mtd/spinand.h    |   1 + | ||||||
|  |  4 files changed, 140 insertions(+), 1 deletion(-) | ||||||
|  |  create mode 100644 drivers/mtd/nand/spi/toshiba.c | ||||||
|  |  | ||||||
|  | --- a/drivers/mtd/nand/spi/Makefile | ||||||
|  | +++ b/drivers/mtd/nand/spi/Makefile | ||||||
|  | @@ -1,3 +1,3 @@ | ||||||
|  |  # SPDX-License-Identifier: GPL-2.0 | ||||||
|  | -spinand-objs := core.o macronix.o micron.o winbond.o | ||||||
|  | +spinand-objs := core.o macronix.o micron.o toshiba.o winbond.o | ||||||
|  |  obj-$(CONFIG_MTD_SPI_NAND) += spinand.o | ||||||
|  | --- a/drivers/mtd/nand/spi/core.c | ||||||
|  | +++ b/drivers/mtd/nand/spi/core.c | ||||||
|  | @@ -764,6 +764,7 @@ static const struct nand_ops spinand_ops | ||||||
|  |  static const struct spinand_manufacturer *spinand_manufacturers[] = { | ||||||
|  |  	¯onix_spinand_manufacturer, | ||||||
|  |  	µn_spinand_manufacturer, | ||||||
|  | +	&toshiba_spinand_manufacturer, | ||||||
|  |  	&winbond_spinand_manufacturer, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/drivers/mtd/nand/spi/toshiba.c | ||||||
|  | @@ -0,0 +1,137 @@ | ||||||
|  | +// SPDX-License-Identifier: GPL-2.0 | ||||||
|  | +/* | ||||||
|  | + * Copyright (c) 2018 exceet electronics GmbH | ||||||
|  | + * Copyright (c) 2018 Kontron Electronics GmbH | ||||||
|  | + * | ||||||
|  | + * Author: Frieder Schrempf <frieder.schrempf@kontron.de> | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#include <linux/device.h> | ||||||
|  | +#include <linux/kernel.h> | ||||||
|  | +#include <linux/mtd/spinand.h> | ||||||
|  | + | ||||||
|  | +#define SPINAND_MFR_TOSHIBA		0x98 | ||||||
|  | +#define TOSH_STATUS_ECC_HAS_BITFLIPS_T	(3 << 4) | ||||||
|  | + | ||||||
|  | +static SPINAND_OP_VARIANTS(read_cache_variants, | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); | ||||||
|  | + | ||||||
|  | +static SPINAND_OP_VARIANTS(write_cache_variants, | ||||||
|  | +		SPINAND_PROG_LOAD(true, 0, NULL, 0)); | ||||||
|  | + | ||||||
|  | +static SPINAND_OP_VARIANTS(update_cache_variants, | ||||||
|  | +		SPINAND_PROG_LOAD(false, 0, NULL, 0)); | ||||||
|  | + | ||||||
|  | +static int tc58cvg2s0h_ooblayout_ecc(struct mtd_info *mtd, int section, | ||||||
|  | +				     struct mtd_oob_region *region) | ||||||
|  | +{ | ||||||
|  | +	if (section > 7) | ||||||
|  | +		return -ERANGE; | ||||||
|  | + | ||||||
|  | +	region->offset = 128 + 16 * section; | ||||||
|  | +	region->length = 16; | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int tc58cvg2s0h_ooblayout_free(struct mtd_info *mtd, int section, | ||||||
|  | +				      struct mtd_oob_region *region) | ||||||
|  | +{ | ||||||
|  | +	if (section > 0) | ||||||
|  | +		return -ERANGE; | ||||||
|  | + | ||||||
|  | +	/* 2 bytes reserved for BBM */ | ||||||
|  | +	region->offset = 2; | ||||||
|  | +	region->length = 126; | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct mtd_ooblayout_ops tc58cvg2s0h_ooblayout = { | ||||||
|  | +	.ecc = tc58cvg2s0h_ooblayout_ecc, | ||||||
|  | +	.free = tc58cvg2s0h_ooblayout_free, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int tc58cvg2s0h_ecc_get_status(struct spinand_device *spinand, | ||||||
|  | +				      u8 status) | ||||||
|  | +{ | ||||||
|  | +	struct nand_device *nand = spinand_to_nand(spinand); | ||||||
|  | +	u8 mbf = 0; | ||||||
|  | +	struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf); | ||||||
|  | + | ||||||
|  | +	switch (status & STATUS_ECC_MASK) { | ||||||
|  | +	case STATUS_ECC_NO_BITFLIPS: | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	case STATUS_ECC_UNCOR_ERROR: | ||||||
|  | +		return -EBADMSG; | ||||||
|  | + | ||||||
|  | +	case STATUS_ECC_HAS_BITFLIPS: | ||||||
|  | +	case TOSH_STATUS_ECC_HAS_BITFLIPS_T: | ||||||
|  | +		/* | ||||||
|  | +		 * Let's try to retrieve the real maximum number of bitflips | ||||||
|  | +		 * in order to avoid forcing the wear-leveling layer to move | ||||||
|  | +		 * data around if it's not necessary. | ||||||
|  | +		 */ | ||||||
|  | +		if (spi_mem_exec_op(spinand->spimem, &op)) | ||||||
|  | +			return nand->eccreq.strength; | ||||||
|  | + | ||||||
|  | +		mbf >>= 4; | ||||||
|  | + | ||||||
|  | +		if (WARN_ON(mbf > nand->eccreq.strength || !mbf)) | ||||||
|  | +			return nand->eccreq.strength; | ||||||
|  | + | ||||||
|  | +		return mbf; | ||||||
|  | + | ||||||
|  | +	default: | ||||||
|  | +		break; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return -EINVAL; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct spinand_info toshiba_spinand_table[] = { | ||||||
|  | +	SPINAND_INFO("TC58CVG2S0H", 0xCD, | ||||||
|  | +		     NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     SPINAND_HAS_QE_BIT, | ||||||
|  | +		     SPINAND_ECCINFO(&tc58cvg2s0h_ooblayout, | ||||||
|  | +				     tc58cvg2s0h_ecc_get_status)), | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int toshiba_spinand_detect(struct spinand_device *spinand) | ||||||
|  | +{ | ||||||
|  | +	u8 *id = spinand->id.data; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	/* | ||||||
|  | +	 * Toshiba SPI NAND read ID needs a dummy byte, | ||||||
|  | +	 * so the first byte in id is garbage. | ||||||
|  | +	 */ | ||||||
|  | +	if (id[1] != SPINAND_MFR_TOSHIBA) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	ret = spinand_match_and_init(spinand, toshiba_spinand_table, | ||||||
|  | +				     ARRAY_SIZE(toshiba_spinand_table), | ||||||
|  | +				     id[2]); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	return 1; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { | ||||||
|  | +	.detect = toshiba_spinand_detect, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +const struct spinand_manufacturer toshiba_spinand_manufacturer = { | ||||||
|  | +	.id = SPINAND_MFR_TOSHIBA, | ||||||
|  | +	.name = "Toshiba", | ||||||
|  | +	.ops = &toshiba_spinand_manuf_ops, | ||||||
|  | +}; | ||||||
|  | --- a/include/linux/mtd/spinand.h | ||||||
|  | +++ b/include/linux/mtd/spinand.h | ||||||
|  | @@ -196,6 +196,7 @@ struct spinand_manufacturer { | ||||||
|  |  /* SPI NAND manufacturers */ | ||||||
|  |  extern const struct spinand_manufacturer macronix_spinand_manufacturer; | ||||||
|  |  extern const struct spinand_manufacturer micron_spinand_manufacturer; | ||||||
|  | +extern const struct spinand_manufacturer toshiba_spinand_manufacturer; | ||||||
|  |  extern const struct spinand_manufacturer winbond_spinand_manufacturer; | ||||||
|  |   | ||||||
|  |  /** | ||||||
| @@ -0,0 +1,196 @@ | |||||||
|  | From c93c613214ac70c87beab5422a60077bf126b855 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Chuanhong Guo <gch981213@gmail.com> | ||||||
|  | Date: Wed, 28 Nov 2018 21:07:25 +0800 | ||||||
|  | Subject: [PATCH 3/8] mtd: spinand: add support for GigaDevice GD5FxGQ4xA | ||||||
|  |  | ||||||
|  | Add support for GigaDevice GD5F1G/2G/4GQ4xA SPI NAND. | ||||||
|  |  | ||||||
|  | Signed-off-by: Chuanhong Guo <gch981213@gmail.com> | ||||||
|  | Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de> | ||||||
|  | Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||||
|  | --- | ||||||
|  |  drivers/mtd/nand/spi/Makefile     |   2 +- | ||||||
|  |  drivers/mtd/nand/spi/core.c       |   1 + | ||||||
|  |  drivers/mtd/nand/spi/gigadevice.c | 148 ++++++++++++++++++++++++++++++ | ||||||
|  |  include/linux/mtd/spinand.h       |   1 + | ||||||
|  |  4 files changed, 151 insertions(+), 1 deletion(-) | ||||||
|  |  create mode 100644 drivers/mtd/nand/spi/gigadevice.c | ||||||
|  |  | ||||||
|  | --- a/drivers/mtd/nand/spi/Makefile | ||||||
|  | +++ b/drivers/mtd/nand/spi/Makefile | ||||||
|  | @@ -1,3 +1,3 @@ | ||||||
|  |  # SPDX-License-Identifier: GPL-2.0 | ||||||
|  | -spinand-objs := core.o macronix.o micron.o toshiba.o winbond.o | ||||||
|  | +spinand-objs := core.o gigadevice.o macronix.o micron.o toshiba.o winbond.o | ||||||
|  |  obj-$(CONFIG_MTD_SPI_NAND) += spinand.o | ||||||
|  | --- a/drivers/mtd/nand/spi/core.c | ||||||
|  | +++ b/drivers/mtd/nand/spi/core.c | ||||||
|  | @@ -762,6 +762,7 @@ static const struct nand_ops spinand_ops | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static const struct spinand_manufacturer *spinand_manufacturers[] = { | ||||||
|  | +	&gigadevice_spinand_manufacturer, | ||||||
|  |  	¯onix_spinand_manufacturer, | ||||||
|  |  	µn_spinand_manufacturer, | ||||||
|  |  	&toshiba_spinand_manufacturer, | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/drivers/mtd/nand/spi/gigadevice.c | ||||||
|  | @@ -0,0 +1,148 @@ | ||||||
|  | +// SPDX-License-Identifier: GPL-2.0 | ||||||
|  | +/* | ||||||
|  | + * Author: | ||||||
|  | + *	Chuanhong Guo <gch981213@gmail.com> | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#include <linux/device.h> | ||||||
|  | +#include <linux/kernel.h> | ||||||
|  | +#include <linux/mtd/spinand.h> | ||||||
|  | + | ||||||
|  | +#define SPINAND_MFR_GIGADEVICE			0xC8 | ||||||
|  | +#define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS	(1 << 4) | ||||||
|  | +#define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS	(3 << 4) | ||||||
|  | + | ||||||
|  | +static SPINAND_OP_VARIANTS(read_cache_variants, | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); | ||||||
|  | + | ||||||
|  | +static SPINAND_OP_VARIANTS(write_cache_variants, | ||||||
|  | +		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), | ||||||
|  | +		SPINAND_PROG_LOAD(true, 0, NULL, 0)); | ||||||
|  | + | ||||||
|  | +static SPINAND_OP_VARIANTS(update_cache_variants, | ||||||
|  | +		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), | ||||||
|  | +		SPINAND_PROG_LOAD(false, 0, NULL, 0)); | ||||||
|  | + | ||||||
|  | +static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section, | ||||||
|  | +				  struct mtd_oob_region *region) | ||||||
|  | +{ | ||||||
|  | +	if (section > 3) | ||||||
|  | +		return -ERANGE; | ||||||
|  | + | ||||||
|  | +	region->offset = (16 * section) + 8; | ||||||
|  | +	region->length = 8; | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section, | ||||||
|  | +				   struct mtd_oob_region *region) | ||||||
|  | +{ | ||||||
|  | +	if (section > 3) | ||||||
|  | +		return -ERANGE; | ||||||
|  | + | ||||||
|  | +	if (section) { | ||||||
|  | +		region->offset = 16 * section; | ||||||
|  | +		region->length = 8; | ||||||
|  | +	} else { | ||||||
|  | +		/* section 0 has one byte reserved for bad block mark */ | ||||||
|  | +		region->offset = 1; | ||||||
|  | +		region->length = 7; | ||||||
|  | +	} | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, | ||||||
|  | +					 u8 status) | ||||||
|  | +{ | ||||||
|  | +	switch (status & STATUS_ECC_MASK) { | ||||||
|  | +	case STATUS_ECC_NO_BITFLIPS: | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: | ||||||
|  | +		/* 1-7 bits are flipped. return the maximum. */ | ||||||
|  | +		return 7; | ||||||
|  | + | ||||||
|  | +	case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: | ||||||
|  | +		return 8; | ||||||
|  | + | ||||||
|  | +	case STATUS_ECC_UNCOR_ERROR: | ||||||
|  | +		return -EBADMSG; | ||||||
|  | + | ||||||
|  | +	default: | ||||||
|  | +		break; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return -EINVAL; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { | ||||||
|  | +	.ecc = gd5fxgq4xa_ooblayout_ecc, | ||||||
|  | +	.free = gd5fxgq4xa_ooblayout_free, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static const struct spinand_info gigadevice_spinand_table[] = { | ||||||
|  | +	SPINAND_INFO("GD5F1GQ4xA", 0xF1, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, | ||||||
|  | +				     gd5fxgq4xa_ecc_get_status)), | ||||||
|  | +	SPINAND_INFO("GD5F2GQ4xA", 0xF2, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, | ||||||
|  | +				     gd5fxgq4xa_ecc_get_status)), | ||||||
|  | +	SPINAND_INFO("GD5F4GQ4xA", 0xF4, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, | ||||||
|  | +				     gd5fxgq4xa_ecc_get_status)), | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int gigadevice_spinand_detect(struct spinand_device *spinand) | ||||||
|  | +{ | ||||||
|  | +	u8 *id = spinand->id.data; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	/* | ||||||
|  | +	 * For GD NANDs, There is an address byte needed to shift in before IDs | ||||||
|  | +	 * are read out, so the first byte in raw_id is dummy. | ||||||
|  | +	 */ | ||||||
|  | +	if (id[1] != SPINAND_MFR_GIGADEVICE) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	ret = spinand_match_and_init(spinand, gigadevice_spinand_table, | ||||||
|  | +				     ARRAY_SIZE(gigadevice_spinand_table), | ||||||
|  | +				     id[2]); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	return 1; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { | ||||||
|  | +	.detect = gigadevice_spinand_detect, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +const struct spinand_manufacturer gigadevice_spinand_manufacturer = { | ||||||
|  | +	.id = SPINAND_MFR_GIGADEVICE, | ||||||
|  | +	.name = "GigaDevice", | ||||||
|  | +	.ops = &gigadevice_spinand_manuf_ops, | ||||||
|  | +}; | ||||||
|  | --- a/include/linux/mtd/spinand.h | ||||||
|  | +++ b/include/linux/mtd/spinand.h | ||||||
|  | @@ -194,6 +194,7 @@ struct spinand_manufacturer { | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  /* SPI NAND manufacturers */ | ||||||
|  | +extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; | ||||||
|  |  extern const struct spinand_manufacturer macronix_spinand_manufacturer; | ||||||
|  |  extern const struct spinand_manufacturer micron_spinand_manufacturer; | ||||||
|  |  extern const struct spinand_manufacturer toshiba_spinand_manufacturer; | ||||||
| @@ -0,0 +1,136 @@ | |||||||
|  | From db214513f62fd13c0a9af3bd5c5d634dba37e65d Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Yoshio Furuyama <tmcmc-mb-yfuruyama7@ml.toshiba.co.jp> | ||||||
|  | Date: Wed, 16 Jan 2019 14:53:19 +0900 | ||||||
|  | Subject: [PATCH 7/8] mtd: spinand: Add support for all Toshiba Memory products | ||||||
|  |  | ||||||
|  | Add device table for Toshiba Memory products. | ||||||
|  | Also, generalize OOB layout structure and function names. | ||||||
|  |  | ||||||
|  | Signed-off-by: Yoshio Furuyama <tmcmc-mb-yfuruyama7@ml.toshiba.co.jp> | ||||||
|  | Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de> | ||||||
|  | Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||||
|  | --- | ||||||
|  |  drivers/mtd/nand/spi/toshiba.c | 79 ++++++++++++++++++++++++++++------ | ||||||
|  |  1 file changed, 65 insertions(+), 14 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/mtd/nand/spi/toshiba.c | ||||||
|  | +++ b/drivers/mtd/nand/spi/toshiba.c | ||||||
|  | @@ -25,19 +25,19 @@ static SPINAND_OP_VARIANTS(write_cache_v | ||||||
|  |  static SPINAND_OP_VARIANTS(update_cache_variants, | ||||||
|  |  		SPINAND_PROG_LOAD(false, 0, NULL, 0)); | ||||||
|  |   | ||||||
|  | -static int tc58cvg2s0h_ooblayout_ecc(struct mtd_info *mtd, int section, | ||||||
|  | +static int tc58cxgxsx_ooblayout_ecc(struct mtd_info *mtd, int section, | ||||||
|  |  				     struct mtd_oob_region *region) | ||||||
|  |  { | ||||||
|  | -	if (section > 7) | ||||||
|  | +	if (section > 0) | ||||||
|  |  		return -ERANGE; | ||||||
|  |   | ||||||
|  | -	region->offset = 128 + 16 * section; | ||||||
|  | -	region->length = 16; | ||||||
|  | +	region->offset = mtd->oobsize / 2; | ||||||
|  | +	region->length = mtd->oobsize / 2; | ||||||
|  |   | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int tc58cvg2s0h_ooblayout_free(struct mtd_info *mtd, int section, | ||||||
|  | +static int tc58cxgxsx_ooblayout_free(struct mtd_info *mtd, int section, | ||||||
|  |  				      struct mtd_oob_region *region) | ||||||
|  |  { | ||||||
|  |  	if (section > 0) | ||||||
|  | @@ -45,17 +45,17 @@ static int tc58cvg2s0h_ooblayout_free(st | ||||||
|  |   | ||||||
|  |  	/* 2 bytes reserved for BBM */ | ||||||
|  |  	region->offset = 2; | ||||||
|  | -	region->length = 126; | ||||||
|  | +	region->length = (mtd->oobsize / 2) - 2; | ||||||
|  |   | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static const struct mtd_ooblayout_ops tc58cvg2s0h_ooblayout = { | ||||||
|  | -	.ecc = tc58cvg2s0h_ooblayout_ecc, | ||||||
|  | -	.free = tc58cvg2s0h_ooblayout_free, | ||||||
|  | +static const struct mtd_ooblayout_ops tc58cxgxsx_ooblayout = { | ||||||
|  | +	.ecc = tc58cxgxsx_ooblayout_ecc, | ||||||
|  | +	.free = tc58cxgxsx_ooblayout_free, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | -static int tc58cvg2s0h_ecc_get_status(struct spinand_device *spinand, | ||||||
|  | +static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand, | ||||||
|  |  				      u8 status) | ||||||
|  |  { | ||||||
|  |  	struct nand_device *nand = spinand_to_nand(spinand); | ||||||
|  | @@ -94,15 +94,66 @@ static int tc58cvg2s0h_ecc_get_status(st | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static const struct spinand_info toshiba_spinand_table[] = { | ||||||
|  | -	SPINAND_INFO("TC58CVG2S0H", 0xCD, | ||||||
|  | +	/* 3.3V 1Gb */ | ||||||
|  | +	SPINAND_INFO("TC58CVG0S3", 0xC2, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, | ||||||
|  | +				     tc58cxgxsx_ecc_get_status)), | ||||||
|  | +	/* 3.3V 2Gb */ | ||||||
|  | +	SPINAND_INFO("TC58CVG1S3", 0xCB, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, | ||||||
|  | +				     tc58cxgxsx_ecc_get_status)), | ||||||
|  | +	/* 3.3V 4Gb */ | ||||||
|  | +	SPINAND_INFO("TC58CVG2S0", 0xCD, | ||||||
|  | +		     NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, | ||||||
|  | +				     tc58cxgxsx_ecc_get_status)), | ||||||
|  | +	/* 1.8V 1Gb */ | ||||||
|  | +	SPINAND_INFO("TC58CYG0S3", 0xB2, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, | ||||||
|  | +				     tc58cxgxsx_ecc_get_status)), | ||||||
|  | +	/* 1.8V 2Gb */ | ||||||
|  | +	SPINAND_INFO("TC58CYG1S3", 0xBB, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, | ||||||
|  | +				     tc58cxgxsx_ecc_get_status)), | ||||||
|  | +	/* 1.8V 4Gb */ | ||||||
|  | +	SPINAND_INFO("TC58CYG2S0", 0xBD, | ||||||
|  |  		     NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), | ||||||
|  |  		     NAND_ECCREQ(8, 512), | ||||||
|  |  		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  |  					      &write_cache_variants, | ||||||
|  |  					      &update_cache_variants), | ||||||
|  | -		     SPINAND_HAS_QE_BIT, | ||||||
|  | -		     SPINAND_ECCINFO(&tc58cvg2s0h_ooblayout, | ||||||
|  | -				     tc58cvg2s0h_ecc_get_status)), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, | ||||||
|  | +				     tc58cxgxsx_ecc_get_status)), | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static int toshiba_spinand_detect(struct spinand_device *spinand) | ||||||
| @@ -0,0 +1,129 @@ | |||||||
|  | From c40c7a990a46e5102a1cc4190557bf315d32d80d Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Stefan Roese <sr@denx.de> | ||||||
|  | Date: Thu, 24 Jan 2019 13:48:06 +0100 | ||||||
|  | Subject: [PATCH 8/8] mtd: spinand: Add support for GigaDevice GD5F1GQ4UExxG | ||||||
|  |  | ||||||
|  | Add support for GigaDevice GD5F1GQ4UExxG SPI NAND chip. | ||||||
|  |  | ||||||
|  | Signed-off-by: Stefan Roese <sr@denx.de> | ||||||
|  | Cc: Chuanhong Guo <gch981213@gmail.com> | ||||||
|  | Cc: Frieder Schrempf <frieder.schrempf@kontron.de> | ||||||
|  | Cc: Miquel Raynal <miquel.raynal@bootlin.com> | ||||||
|  | Cc: Boris Brezillon <bbrezillon@kernel.org> | ||||||
|  | Reviewed-by: Boris Brezillon <bbrezillon@kernel.org> | ||||||
|  | Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||||
|  | --- | ||||||
|  |  drivers/mtd/nand/spi/gigadevice.c | 83 +++++++++++++++++++++++++++++++ | ||||||
|  |  1 file changed, 83 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/mtd/nand/spi/gigadevice.c | ||||||
|  | +++ b/drivers/mtd/nand/spi/gigadevice.c | ||||||
|  | @@ -12,6 +12,8 @@ | ||||||
|  |  #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS	(1 << 4) | ||||||
|  |  #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS	(3 << 4) | ||||||
|  |   | ||||||
|  | +#define GD5FXGQ4UEXXG_REG_STATUS2		0xf0 | ||||||
|  | + | ||||||
|  |  static SPINAND_OP_VARIANTS(read_cache_variants, | ||||||
|  |  		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), | ||||||
|  |  		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), | ||||||
|  | @@ -81,11 +83,83 @@ static int gd5fxgq4xa_ecc_get_status(str | ||||||
|  |  	return -EINVAL; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static int gd5fxgq4uexxg_ooblayout_ecc(struct mtd_info *mtd, int section, | ||||||
|  | +				       struct mtd_oob_region *region) | ||||||
|  | +{ | ||||||
|  | +	if (section) | ||||||
|  | +		return -ERANGE; | ||||||
|  | + | ||||||
|  | +	region->offset = 64; | ||||||
|  | +	region->length = 64; | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int gd5fxgq4uexxg_ooblayout_free(struct mtd_info *mtd, int section, | ||||||
|  | +					struct mtd_oob_region *region) | ||||||
|  | +{ | ||||||
|  | +	if (section) | ||||||
|  | +		return -ERANGE; | ||||||
|  | + | ||||||
|  | +	/* Reserve 1 bytes for the BBM. */ | ||||||
|  | +	region->offset = 1; | ||||||
|  | +	region->length = 63; | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, | ||||||
|  | +					u8 status) | ||||||
|  | +{ | ||||||
|  | +	u8 status2; | ||||||
|  | +	struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2, | ||||||
|  | +						      &status2); | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	switch (status & STATUS_ECC_MASK) { | ||||||
|  | +	case STATUS_ECC_NO_BITFLIPS: | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: | ||||||
|  | +		/* | ||||||
|  | +		 * Read status2 register to determine a more fine grained | ||||||
|  | +		 * bit error status | ||||||
|  | +		 */ | ||||||
|  | +		ret = spi_mem_exec_op(spinand->spimem, &op); | ||||||
|  | +		if (ret) | ||||||
|  | +			return ret; | ||||||
|  | + | ||||||
|  | +		/* | ||||||
|  | +		 * 4 ... 7 bits are flipped (1..4 can't be detected, so | ||||||
|  | +		 * report the maximum of 4 in this case | ||||||
|  | +		 */ | ||||||
|  | +		/* bits sorted this way (3...0): ECCS1,ECCS0,ECCSE1,ECCSE0 */ | ||||||
|  | +		return ((status & STATUS_ECC_MASK) >> 2) | | ||||||
|  | +			((status2 & STATUS_ECC_MASK) >> 4); | ||||||
|  | + | ||||||
|  | +	case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: | ||||||
|  | +		return 8; | ||||||
|  | + | ||||||
|  | +	case STATUS_ECC_UNCOR_ERROR: | ||||||
|  | +		return -EBADMSG; | ||||||
|  | + | ||||||
|  | +	default: | ||||||
|  | +		break; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return -EINVAL; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { | ||||||
|  |  	.ecc = gd5fxgq4xa_ooblayout_ecc, | ||||||
|  |  	.free = gd5fxgq4xa_ooblayout_free, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +static const struct mtd_ooblayout_ops gd5fxgq4uexxg_ooblayout = { | ||||||
|  | +	.ecc = gd5fxgq4uexxg_ooblayout_ecc, | ||||||
|  | +	.free = gd5fxgq4uexxg_ooblayout_free, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  static const struct spinand_info gigadevice_spinand_table[] = { | ||||||
|  |  	SPINAND_INFO("GD5F1GQ4xA", 0xF1, | ||||||
|  |  		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), | ||||||
|  | @@ -114,6 +188,15 @@ static const struct spinand_info gigadev | ||||||
|  |  		     0, | ||||||
|  |  		     SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, | ||||||
|  |  				     gd5fxgq4xa_ecc_get_status)), | ||||||
|  | +	SPINAND_INFO("GD5F1GQ4UExxG", 0xd1, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&gd5fxgq4uexxg_ooblayout, | ||||||
|  | +				     gd5fxgq4uexxg_ecc_get_status)), | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static int gigadevice_spinand_detect(struct spinand_device *spinand) | ||||||
| @@ -0,0 +1,25 @@ | |||||||
|  | From 81554171373018b83f3554b9e725d2b5bf1844a5 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Alexander Sverdlin <alexander.sverdlin@nokia.com> | ||||||
|  | Date: Fri, 13 Jul 2018 15:06:46 +0200 | ||||||
|  | Subject: [PATCH] mtd: spi-nor: Add support for mx25u12835f | ||||||
|  |  | ||||||
|  | This chip supports dual and quad read and uniform 4K-byte erase. | ||||||
|  |  | ||||||
|  | Signed-off-by: Alexander Sverdlin <alexander.sverdlin@nokia.com> | ||||||
|  | Reviewed-by: Tudor Ambarus <tudor.ambarus@microchip.com> | ||||||
|  | Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> | ||||||
|  | --- | ||||||
|  |  drivers/mtd/spi-nor/spi-nor.c | 2 ++ | ||||||
|  |  1 file changed, 2 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/mtd/spi-nor/spi-nor.c | ||||||
|  | +++ b/drivers/mtd/spi-nor/spi-nor.c | ||||||
|  | @@ -1088,6 +1088,8 @@ static const struct flash_info spi_nor_i | ||||||
|  |  	{ "mx25u6435f",  INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, | ||||||
|  |  	{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, | ||||||
|  |  	{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, | ||||||
|  | +	{ "mx25u12835f", INFO(0xc22538, 0, 64 * 1024, 256, | ||||||
|  | +			 SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||||
|  |  	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, | ||||||
|  |  	{ "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) }, | ||||||
|  |  	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, | ||||||
| @@ -0,0 +1,81 @@ | |||||||
|  | From d014717d50b1efd011a3a028ce92563a4dc9bae5 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Jeff Kletsky <git-commits@allycomm.com> | ||||||
|  | Date: Wed, 22 May 2019 15:05:53 -0700 | ||||||
|  | Subject: [PATCH 1/3] mtd: spinand: Define macros for page-read ops with | ||||||
|  |  three-byte addresses | ||||||
|  |  | ||||||
|  | The GigaDevice GD5F1GQ4UFxxG SPI NAND utilizes three-byte addresses | ||||||
|  | for its page-read ops. | ||||||
|  |  | ||||||
|  | http://www.gigadevice.com/datasheet/gd5f1gq4xfxxg/ | ||||||
|  |  | ||||||
|  | Signed-off-by: Jeff Kletsky <git-commits@allycomm.com> | ||||||
|  | Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de> | ||||||
|  | Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||||
|  | --- | ||||||
|  |  include/linux/mtd/spinand.h | 30 ++++++++++++++++++++++++++++++ | ||||||
|  |  1 file changed, 30 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/include/linux/mtd/spinand.h | ||||||
|  | +++ b/include/linux/mtd/spinand.h | ||||||
|  | @@ -68,30 +68,60 @@ | ||||||
|  |  		   SPI_MEM_OP_DUMMY(ndummy, 1),				\ | ||||||
|  |  		   SPI_MEM_OP_DATA_IN(len, buf, 1)) | ||||||
|  |   | ||||||
|  | +#define SPINAND_PAGE_READ_FROM_CACHE_OP_3A(fast, addr, ndummy, buf, len) \ | ||||||
|  | +	SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1),		\ | ||||||
|  | +		   SPI_MEM_OP_ADDR(3, addr, 1),				\ | ||||||
|  | +		   SPI_MEM_OP_DUMMY(ndummy, 1),				\ | ||||||
|  | +		   SPI_MEM_OP_DATA_IN(len, buf, 1)) | ||||||
|  | + | ||||||
|  |  #define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len)	\ | ||||||
|  |  	SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1),				\ | ||||||
|  |  		   SPI_MEM_OP_ADDR(2, addr, 1),				\ | ||||||
|  |  		   SPI_MEM_OP_DUMMY(ndummy, 1),				\ | ||||||
|  |  		   SPI_MEM_OP_DATA_IN(len, buf, 2)) | ||||||
|  |   | ||||||
|  | +#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(addr, ndummy, buf, len)	\ | ||||||
|  | +	SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1),				\ | ||||||
|  | +		   SPI_MEM_OP_ADDR(3, addr, 1),				\ | ||||||
|  | +		   SPI_MEM_OP_DUMMY(ndummy, 1),				\ | ||||||
|  | +		   SPI_MEM_OP_DATA_IN(len, buf, 2)) | ||||||
|  | + | ||||||
|  |  #define SPINAND_PAGE_READ_FROM_CACHE_X4_OP(addr, ndummy, buf, len)	\ | ||||||
|  |  	SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1),				\ | ||||||
|  |  		   SPI_MEM_OP_ADDR(2, addr, 1),				\ | ||||||
|  |  		   SPI_MEM_OP_DUMMY(ndummy, 1),				\ | ||||||
|  |  		   SPI_MEM_OP_DATA_IN(len, buf, 4)) | ||||||
|  |   | ||||||
|  | +#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(addr, ndummy, buf, len)	\ | ||||||
|  | +	SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1),				\ | ||||||
|  | +		   SPI_MEM_OP_ADDR(3, addr, 1),				\ | ||||||
|  | +		   SPI_MEM_OP_DUMMY(ndummy, 1),				\ | ||||||
|  | +		   SPI_MEM_OP_DATA_IN(len, buf, 4)) | ||||||
|  | + | ||||||
|  |  #define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(addr, ndummy, buf, len)	\ | ||||||
|  |  	SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1),				\ | ||||||
|  |  		   SPI_MEM_OP_ADDR(2, addr, 2),				\ | ||||||
|  |  		   SPI_MEM_OP_DUMMY(ndummy, 2),				\ | ||||||
|  |  		   SPI_MEM_OP_DATA_IN(len, buf, 2)) | ||||||
|  |   | ||||||
|  | +#define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP_3A(addr, ndummy, buf, len) \ | ||||||
|  | +	SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1),				\ | ||||||
|  | +		   SPI_MEM_OP_ADDR(3, addr, 2),				\ | ||||||
|  | +		   SPI_MEM_OP_DUMMY(ndummy, 2),				\ | ||||||
|  | +		   SPI_MEM_OP_DATA_IN(len, buf, 2)) | ||||||
|  | + | ||||||
|  |  #define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(addr, ndummy, buf, len)	\ | ||||||
|  |  	SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1),				\ | ||||||
|  |  		   SPI_MEM_OP_ADDR(2, addr, 4),				\ | ||||||
|  |  		   SPI_MEM_OP_DUMMY(ndummy, 4),				\ | ||||||
|  |  		   SPI_MEM_OP_DATA_IN(len, buf, 4)) | ||||||
|  |   | ||||||
|  | +#define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP_3A(addr, ndummy, buf, len) \ | ||||||
|  | +	SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1),				\ | ||||||
|  | +		   SPI_MEM_OP_ADDR(3, addr, 4),				\ | ||||||
|  | +		   SPI_MEM_OP_DUMMY(ndummy, 4),				\ | ||||||
|  | +		   SPI_MEM_OP_DATA_IN(len, buf, 4)) | ||||||
|  | + | ||||||
|  |  #define SPINAND_PROG_EXEC_OP(addr)					\ | ||||||
|  |  	SPI_MEM_OP(SPI_MEM_OP_CMD(0x10, 1),				\ | ||||||
|  |  		   SPI_MEM_OP_ADDR(3, addr, 1),				\ | ||||||
| @@ -0,0 +1,48 @@ | |||||||
|  | From 53dd94a79d3bfdaae30e5a4ebf474ea1af1d572e Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Jeff Kletsky <git-commits@allycomm.com> | ||||||
|  | Date: Wed, 22 May 2019 15:05:54 -0700 | ||||||
|  | Subject: [PATCH 2/3] mtd: spinand: Add support for two-byte device IDs | ||||||
|  |  | ||||||
|  | The GigaDevice GD5F1GQ4UFxxG SPI NAND utilizes two-byte device IDs. | ||||||
|  |  | ||||||
|  | http://www.gigadevice.com/datasheet/gd5f1gq4xfxxg/ | ||||||
|  |  | ||||||
|  | Signed-off-by: Jeff Kletsky <git-commits@allycomm.com> | ||||||
|  | Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de> | ||||||
|  | Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||||
|  | --- | ||||||
|  |  drivers/mtd/nand/spi/core.c | 2 +- | ||||||
|  |  include/linux/mtd/spinand.h | 4 ++-- | ||||||
|  |  2 files changed, 3 insertions(+), 3 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/mtd/nand/spi/core.c | ||||||
|  | +++ b/drivers/mtd/nand/spi/core.c | ||||||
|  | @@ -853,7 +853,7 @@ spinand_select_op_variant(struct spinand | ||||||
|  |   */ | ||||||
|  |  int spinand_match_and_init(struct spinand_device *spinand, | ||||||
|  |  			   const struct spinand_info *table, | ||||||
|  | -			   unsigned int table_size, u8 devid) | ||||||
|  | +			   unsigned int table_size, u16 devid) | ||||||
|  |  { | ||||||
|  |  	struct nand_device *nand = spinand_to_nand(spinand); | ||||||
|  |  	unsigned int i; | ||||||
|  | --- a/include/linux/mtd/spinand.h | ||||||
|  | +++ b/include/linux/mtd/spinand.h | ||||||
|  | @@ -290,7 +290,7 @@ struct spinand_ecc_info { | ||||||
|  |   */ | ||||||
|  |  struct spinand_info { | ||||||
|  |  	const char *model; | ||||||
|  | -	u8 devid; | ||||||
|  | +	u16 devid; | ||||||
|  |  	u32 flags; | ||||||
|  |  	struct nand_memory_organization memorg; | ||||||
|  |  	struct nand_ecc_req eccreq; | ||||||
|  | @@ -445,7 +445,7 @@ static inline void spinand_set_of_node(s | ||||||
|  |   | ||||||
|  |  int spinand_match_and_init(struct spinand_device *dev, | ||||||
|  |  			   const struct spinand_info *table, | ||||||
|  | -			   unsigned int table_size, u8 devid); | ||||||
|  | +			   unsigned int table_size, u16 devid); | ||||||
|  |   | ||||||
|  |  int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); | ||||||
|  |  int spinand_select_target(struct spinand_device *spinand, unsigned int target); | ||||||
| @@ -0,0 +1,197 @@ | |||||||
|  |  | ||||||
|  | IMPORTANT NOTE | ||||||
|  | ============== | ||||||
|  |  | ||||||
|  | The content of this patch has been adapted for Linux 4.19 | ||||||
|  |  | ||||||
|  | Changes were made in Linux 5.x to add the bad-block limit | ||||||
|  | to the metadata available to the driver, adding a parameter | ||||||
|  |  | ||||||
|  | NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), | ||||||
|  |                                      ^- New bad-block limit | ||||||
|  |  | ||||||
|  | This patch omits that parameter from the upstream patch | ||||||
|  | for compatibility with the Linux 4.19 driver. | ||||||
|  |  | ||||||
|  | ===== | ||||||
|  |  | ||||||
|  | From 049df13c4e63884fe6634db5568e08f65922256e Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Jeff Kletsky <git-commits@allycomm.com> | ||||||
|  | Date: Wed, 22 May 2019 15:05:55 -0700 | ||||||
|  | Subject: [PATCH 3/3] mtd: spinand: Add support for GigaDevice GD5F1GQ4UFxxG | ||||||
|  |  | ||||||
|  | The GigaDevice GD5F1GQ4UFxxG SPI NAND is in current production devices | ||||||
|  | and, while it has the same logical layout as the E-series devices, | ||||||
|  | it differs in the SPI interfacing in significant ways. | ||||||
|  |  | ||||||
|  | This support is contingent on previous commits to: | ||||||
|  |  | ||||||
|  |   * Add support for two-byte device IDs | ||||||
|  |   * Define macros for page-read ops with three-byte addresses | ||||||
|  |  | ||||||
|  | http://www.gigadevice.com/datasheet/gd5f1gq4xfxxg/ | ||||||
|  |  | ||||||
|  | Signed-off-by: Jeff Kletsky <git-commits@allycomm.com> | ||||||
|  | Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de> | ||||||
|  | Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||||
|  | --- | ||||||
|  |  drivers/mtd/nand/spi/gigadevice.c | 79 +++++++++++++++++++++++++------ | ||||||
|  |  1 file changed, 64 insertions(+), 15 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/mtd/nand/spi/gigadevice.c | ||||||
|  | +++ b/drivers/mtd/nand/spi/gigadevice.c | ||||||
|  | @@ -9,11 +9,17 @@ | ||||||
|  |  #include <linux/mtd/spinand.h> | ||||||
|  |   | ||||||
|  |  #define SPINAND_MFR_GIGADEVICE			0xC8 | ||||||
|  | + | ||||||
|  |  #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS	(1 << 4) | ||||||
|  |  #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS	(3 << 4) | ||||||
|  |   | ||||||
|  |  #define GD5FXGQ4UEXXG_REG_STATUS2		0xf0 | ||||||
|  |   | ||||||
|  | +#define GD5FXGQ4UXFXXG_STATUS_ECC_MASK		(7 << 4) | ||||||
|  | +#define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS	(0 << 4) | ||||||
|  | +#define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS	(1 << 4) | ||||||
|  | +#define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR	(7 << 4) | ||||||
|  | + | ||||||
|  |  static SPINAND_OP_VARIANTS(read_cache_variants, | ||||||
|  |  		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), | ||||||
|  |  		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), | ||||||
|  | @@ -22,6 +28,14 @@ static SPINAND_OP_VARIANTS(read_cache_va | ||||||
|  |  		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), | ||||||
|  |  		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); | ||||||
|  |   | ||||||
|  | +static SPINAND_OP_VARIANTS(read_cache_variants_f, | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0)); | ||||||
|  | + | ||||||
|  |  static SPINAND_OP_VARIANTS(write_cache_variants, | ||||||
|  |  		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), | ||||||
|  |  		SPINAND_PROG_LOAD(true, 0, NULL, 0)); | ||||||
|  | @@ -59,6 +73,11 @@ static int gd5fxgq4xa_ooblayout_free(str | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { | ||||||
|  | +	.ecc = gd5fxgq4xa_ooblayout_ecc, | ||||||
|  | +	.free = gd5fxgq4xa_ooblayout_free, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, | ||||||
|  |  					 u8 status) | ||||||
|  |  { | ||||||
|  | @@ -83,7 +102,7 @@ static int gd5fxgq4xa_ecc_get_status(str | ||||||
|  |  	return -EINVAL; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int gd5fxgq4uexxg_ooblayout_ecc(struct mtd_info *mtd, int section, | ||||||
|  | +static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, | ||||||
|  |  				       struct mtd_oob_region *region) | ||||||
|  |  { | ||||||
|  |  	if (section) | ||||||
|  | @@ -95,7 +114,7 @@ static int gd5fxgq4uexxg_ooblayout_ecc(s | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int gd5fxgq4uexxg_ooblayout_free(struct mtd_info *mtd, int section, | ||||||
|  | +static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section, | ||||||
|  |  					struct mtd_oob_region *region) | ||||||
|  |  { | ||||||
|  |  	if (section) | ||||||
|  | @@ -108,6 +127,11 @@ static int gd5fxgq4uexxg_ooblayout_free( | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = { | ||||||
|  | +	.ecc = gd5fxgq4_variant2_ooblayout_ecc, | ||||||
|  | +	.free = gd5fxgq4_variant2_ooblayout_free, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, | ||||||
|  |  					u8 status) | ||||||
|  |  { | ||||||
|  | @@ -150,15 +174,25 @@ static int gd5fxgq4uexxg_ecc_get_status( | ||||||
|  |  	return -EINVAL; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { | ||||||
|  | -	.ecc = gd5fxgq4xa_ooblayout_ecc, | ||||||
|  | -	.free = gd5fxgq4xa_ooblayout_free, | ||||||
|  | -}; | ||||||
|  | +static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, | ||||||
|  | +					u8 status) | ||||||
|  | +{ | ||||||
|  | +	switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) { | ||||||
|  | +	case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS: | ||||||
|  | +		return 0; | ||||||
|  |   | ||||||
|  | -static const struct mtd_ooblayout_ops gd5fxgq4uexxg_ooblayout = { | ||||||
|  | -	.ecc = gd5fxgq4uexxg_ooblayout_ecc, | ||||||
|  | -	.free = gd5fxgq4uexxg_ooblayout_free, | ||||||
|  | -}; | ||||||
|  | +	case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS: | ||||||
|  | +		return 3; | ||||||
|  | + | ||||||
|  | +	case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR: | ||||||
|  | +		return -EBADMSG; | ||||||
|  | + | ||||||
|  | +	default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */ | ||||||
|  | +		return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return -EINVAL; | ||||||
|  | +} | ||||||
|  |   | ||||||
|  |  static const struct spinand_info gigadevice_spinand_table[] = { | ||||||
|  |  	SPINAND_INFO("GD5F1GQ4xA", 0xF1, | ||||||
|  | @@ -195,25 +229,40 @@ static const struct spinand_info gigadev | ||||||
|  |  					      &write_cache_variants, | ||||||
|  |  					      &update_cache_variants), | ||||||
|  |  		     0, | ||||||
|  | -		     SPINAND_ECCINFO(&gd5fxgq4uexxg_ooblayout, | ||||||
|  | +		     SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, | ||||||
|  |  				     gd5fxgq4uexxg_ecc_get_status)), | ||||||
|  | +	SPINAND_INFO("GD5F1GQ4UFxxG", 0xb148, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, | ||||||
|  | +				     gd5fxgq4ufxxg_ecc_get_status)), | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static int gigadevice_spinand_detect(struct spinand_device *spinand) | ||||||
|  |  { | ||||||
|  |  	u8 *id = spinand->id.data; | ||||||
|  | +	u16 did; | ||||||
|  |  	int ret; | ||||||
|  |   | ||||||
|  |  	/* | ||||||
|  | -	 * For GD NANDs, There is an address byte needed to shift in before IDs | ||||||
|  | -	 * are read out, so the first byte in raw_id is dummy. | ||||||
|  | +	 * Earlier GDF5-series devices (A,E) return [0][MID][DID] | ||||||
|  | +	 * Later (F) devices return [MID][DID1][DID2] | ||||||
|  |  	 */ | ||||||
|  | -	if (id[1] != SPINAND_MFR_GIGADEVICE) | ||||||
|  | + | ||||||
|  | +	if (id[0] == SPINAND_MFR_GIGADEVICE) | ||||||
|  | +		did = (id[1] << 8) + id[2]; | ||||||
|  | +	else if (id[0] == 0 && id[1] == SPINAND_MFR_GIGADEVICE) | ||||||
|  | +		did = id[2]; | ||||||
|  | +	else | ||||||
|  |  		return 0; | ||||||
|  |   | ||||||
|  |  	ret = spinand_match_and_init(spinand, gigadevice_spinand_table, | ||||||
|  |  				     ARRAY_SIZE(gigadevice_spinand_table), | ||||||
|  | -				     id[2]); | ||||||
|  | +				     did); | ||||||
|  |  	if (ret) | ||||||
|  |  		return ret; | ||||||
|  |   | ||||||
| @@ -0,0 +1,203 @@ | |||||||
|  | From 3552691616c940a7c4125c2678ba816653cd725e Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Jeff Kletsky <git-commits@allycomm.com> | ||||||
|  | Date: Tue, 18 Jun 2019 10:08:05 -0700 | ||||||
|  | Subject: [PATCH] mtd: spinand: Add initial support for Paragon PN26G0xA | ||||||
|  |  | ||||||
|  | Add initial support for Paragon Technology | ||||||
|  | PN26G01Axxxxx and PN26G02Axxxxx SPI NAND | ||||||
|  |  | ||||||
|  | Datasheets available at | ||||||
|  | http://www.xtxtech.com/upfile/2016082517274590.pdf | ||||||
|  | http://www.xtxtech.com/upfile/2016082517282329.pdf | ||||||
|  |  | ||||||
|  | Signed-off-by: Jeff Kletsky <git-commits@allycomm.com> | ||||||
|  | Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de> | ||||||
|  | Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||||
|  |  | ||||||
|  | ADOPTED FROM UPSTREAM	due to upstream commit 377e517b5fa5 in Linux 5.2 | ||||||
|  | 			which added another parameter to NAND_MEMORG | ||||||
|  | --- | ||||||
|  |  drivers/mtd/nand/spi/Makefile  |   2 +- | ||||||
|  |  drivers/mtd/nand/spi/core.c    |   1 + | ||||||
|  |  drivers/mtd/nand/spi/paragon.c | 147 +++++++++++++++++++++++++++++++++ | ||||||
|  |  include/linux/mtd/spinand.h    |   1 + | ||||||
|  |  4 files changed, 150 insertions(+), 1 deletion(-) | ||||||
|  |  create mode 100644 drivers/mtd/nand/spi/paragon.c | ||||||
|  |  | ||||||
|  | --- a/drivers/mtd/nand/spi/Makefile | ||||||
|  | +++ b/drivers/mtd/nand/spi/Makefile | ||||||
|  | @@ -1,3 +1,3 @@ | ||||||
|  |  # SPDX-License-Identifier: GPL-2.0 | ||||||
|  | -spinand-objs := core.o gigadevice.o macronix.o micron.o toshiba.o winbond.o | ||||||
|  | +spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o | ||||||
|  |  obj-$(CONFIG_MTD_SPI_NAND) += spinand.o | ||||||
|  | --- a/drivers/mtd/nand/spi/core.c | ||||||
|  | +++ b/drivers/mtd/nand/spi/core.c | ||||||
|  | @@ -765,6 +765,7 @@ static const struct spinand_manufacturer | ||||||
|  |  	&gigadevice_spinand_manufacturer, | ||||||
|  |  	¯onix_spinand_manufacturer, | ||||||
|  |  	µn_spinand_manufacturer, | ||||||
|  | +	¶gon_spinand_manufacturer, | ||||||
|  |  	&toshiba_spinand_manufacturer, | ||||||
|  |  	&winbond_spinand_manufacturer, | ||||||
|  |  }; | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/drivers/mtd/nand/spi/paragon.c | ||||||
|  | @@ -0,0 +1,147 @@ | ||||||
|  | +// SPDX-License-Identifier: GPL-2.0 | ||||||
|  | +/* | ||||||
|  | + * Copyright (C) 2019 Jeff Kletsky | ||||||
|  | + * | ||||||
|  | + * Author: Jeff Kletsky <git-commits@allycomm.com> | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#include <linux/device.h> | ||||||
|  | +#include <linux/kernel.h> | ||||||
|  | +#include <linux/mtd/spinand.h> | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +#define SPINAND_MFR_PARAGON	0xa1 | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +#define PN26G0XA_STATUS_ECC_BITMASK		(3 << 4) | ||||||
|  | + | ||||||
|  | +#define PN26G0XA_STATUS_ECC_NONE_DETECTED	(0 << 4) | ||||||
|  | +#define PN26G0XA_STATUS_ECC_1_7_CORRECTED	(1 << 4) | ||||||
|  | +#define PN26G0XA_STATUS_ECC_ERRORED		(2 << 4) | ||||||
|  | +#define PN26G0XA_STATUS_ECC_8_CORRECTED		(3 << 4) | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +static SPINAND_OP_VARIANTS(read_cache_variants, | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), | ||||||
|  | +		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); | ||||||
|  | + | ||||||
|  | +static SPINAND_OP_VARIANTS(write_cache_variants, | ||||||
|  | +		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), | ||||||
|  | +		SPINAND_PROG_LOAD(true, 0, NULL, 0)); | ||||||
|  | + | ||||||
|  | +static SPINAND_OP_VARIANTS(update_cache_variants, | ||||||
|  | +		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), | ||||||
|  | +		SPINAND_PROG_LOAD(false, 0, NULL, 0)); | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +static int pn26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, | ||||||
|  | +				   struct mtd_oob_region *region) | ||||||
|  | +{ | ||||||
|  | +	if (section > 3) | ||||||
|  | +		return -ERANGE; | ||||||
|  | + | ||||||
|  | +	region->offset = 6 + (15 * section); /* 4 BBM + 2 user bytes */ | ||||||
|  | +	region->length = 13; | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int pn26g0xa_ooblayout_free(struct mtd_info *mtd, int section, | ||||||
|  | +				   struct mtd_oob_region *region) | ||||||
|  | +{ | ||||||
|  | +	if (section > 4) | ||||||
|  | +		return -ERANGE; | ||||||
|  | + | ||||||
|  | +	if (section == 4) { | ||||||
|  | +		region->offset = 64; | ||||||
|  | +		region->length = 64; | ||||||
|  | +	} else { | ||||||
|  | +		region->offset = 4 + (15 * section); | ||||||
|  | +		region->length = 2; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int pn26g0xa_ecc_get_status(struct spinand_device *spinand, | ||||||
|  | +				   u8 status) | ||||||
|  | +{ | ||||||
|  | +	switch (status & PN26G0XA_STATUS_ECC_BITMASK) { | ||||||
|  | +	case PN26G0XA_STATUS_ECC_NONE_DETECTED: | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	case PN26G0XA_STATUS_ECC_1_7_CORRECTED: | ||||||
|  | +		return 7;	/* Return upper limit by convention */ | ||||||
|  | + | ||||||
|  | +	case PN26G0XA_STATUS_ECC_8_CORRECTED: | ||||||
|  | +		return 8; | ||||||
|  | + | ||||||
|  | +	case PN26G0XA_STATUS_ECC_ERRORED: | ||||||
|  | +		return -EBADMSG; | ||||||
|  | + | ||||||
|  | +	default: | ||||||
|  | +		break; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return -EINVAL; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct mtd_ooblayout_ops pn26g0xa_ooblayout = { | ||||||
|  | +	.ecc = pn26g0xa_ooblayout_ecc, | ||||||
|  | +	.free = pn26g0xa_ooblayout_free, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +static const struct spinand_info paragon_spinand_table[] = { | ||||||
|  | +	SPINAND_INFO("PN26G01A", 0xe1, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&pn26g0xa_ooblayout, | ||||||
|  | +				     pn26g0xa_ecc_get_status)), | ||||||
|  | +	SPINAND_INFO("PN26G02A", 0xe2, | ||||||
|  | +		     NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), | ||||||
|  | +		     NAND_ECCREQ(8, 512), | ||||||
|  | +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||||
|  | +					      &write_cache_variants, | ||||||
|  | +					      &update_cache_variants), | ||||||
|  | +		     0, | ||||||
|  | +		     SPINAND_ECCINFO(&pn26g0xa_ooblayout, | ||||||
|  | +				     pn26g0xa_ecc_get_status)), | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int paragon_spinand_detect(struct spinand_device *spinand) | ||||||
|  | +{ | ||||||
|  | +	u8 *id = spinand->id.data; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	/* Read ID returns [0][MID][DID] */ | ||||||
|  | + | ||||||
|  | +	if (id[1] != SPINAND_MFR_PARAGON) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	ret = spinand_match_and_init(spinand, paragon_spinand_table, | ||||||
|  | +				     ARRAY_SIZE(paragon_spinand_table), | ||||||
|  | +				     id[2]); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	return 1; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct spinand_manufacturer_ops paragon_spinand_manuf_ops = { | ||||||
|  | +	.detect = paragon_spinand_detect, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +const struct spinand_manufacturer paragon_spinand_manufacturer = { | ||||||
|  | +	.id = SPINAND_MFR_PARAGON, | ||||||
|  | +	.name = "Paragon", | ||||||
|  | +	.ops = ¶gon_spinand_manuf_ops, | ||||||
|  | +}; | ||||||
|  | --- a/include/linux/mtd/spinand.h | ||||||
|  | +++ b/include/linux/mtd/spinand.h | ||||||
|  | @@ -227,6 +227,7 @@ struct spinand_manufacturer { | ||||||
|  |  extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; | ||||||
|  |  extern const struct spinand_manufacturer macronix_spinand_manufacturer; | ||||||
|  |  extern const struct spinand_manufacturer micron_spinand_manufacturer; | ||||||
|  | +extern const struct spinand_manufacturer paragon_spinand_manufacturer; | ||||||
|  |  extern const struct spinand_manufacturer toshiba_spinand_manufacturer; | ||||||
|  |  extern const struct spinand_manufacturer winbond_spinand_manufacturer; | ||||||
|  |   | ||||||
| @@ -0,0 +1,44 @@ | |||||||
|  | From 6f3ea4e5b1f0867ec217f6101fcb89783ed905d7 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Sat, 9 Feb 2019 18:23:26 +0000 | ||||||
|  | Subject: [PATCH] net: phylink: only call mac_config() during resolve | ||||||
|  |  when link is up | ||||||
|  |  | ||||||
|  | There's little point calling mac_config() when the link is down. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c | 11 +++++++++-- | ||||||
|  |  1 file changed, 9 insertions(+), 2 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -339,6 +339,13 @@ static void phylink_mac_config(struct ph | ||||||
|  |  	pl->ops->mac_config(pl->netdev, pl->link_an_mode, state); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static void phylink_mac_config_up(struct phylink *pl, | ||||||
|  | +				  const struct phylink_link_state *state) | ||||||
|  | +{ | ||||||
|  | +	if (state->link) | ||||||
|  | +		phylink_mac_config(pl, state); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  static void phylink_mac_an_restart(struct phylink *pl) | ||||||
|  |  { | ||||||
|  |  	if (pl->link_config.an_enabled && | ||||||
|  | @@ -442,12 +449,12 @@ static void phylink_resolve(struct work_ | ||||||
|  |  		case MLO_AN_PHY: | ||||||
|  |  			link_state = pl->phy_state; | ||||||
|  |  			phylink_resolve_flow(pl, &link_state); | ||||||
|  | -			phylink_mac_config(pl, &link_state); | ||||||
|  | +			phylink_mac_config_up(pl, &link_state); | ||||||
|  |  			break; | ||||||
|  |   | ||||||
|  |  		case MLO_AN_FIXED: | ||||||
|  |  			phylink_get_fixed_state(pl, &link_state); | ||||||
|  | -			phylink_mac_config(pl, &link_state); | ||||||
|  | +			phylink_mac_config_up(pl, &link_state); | ||||||
|  |  			break; | ||||||
|  |   | ||||||
|  |  		case MLO_AN_INBAND: | ||||||
| @@ -0,0 +1,59 @@ | |||||||
|  | From 72f973f292b3eaaf451ebcd3253900d41f4ef24a Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 25 Jan 2019 17:42:51 +0000 | ||||||
|  | Subject: [PATCH] net: phylink: ensure inband AN works correctly | ||||||
|  |  | ||||||
|  | Do not update the link interface mode while the link is down to avoid | ||||||
|  | spurious link interface changes. | ||||||
|  |  | ||||||
|  | Always call mac_config if we have a PHY to propagate the pause mode | ||||||
|  | settings to the MAC. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c | 37 +++++++++++++++---------------------- | ||||||
|  |  1 file changed, 15 insertions(+), 22 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -459,28 +459,21 @@ static void phylink_resolve(struct work_ | ||||||
|  |   | ||||||
|  |  		case MLO_AN_INBAND: | ||||||
|  |  			phylink_get_mac_state(pl, &link_state); | ||||||
|  | -			if (pl->phydev) { | ||||||
|  | -				bool changed = false; | ||||||
|  |   | ||||||
|  | -				link_state.link = link_state.link && | ||||||
|  | -						  pl->phy_state.link; | ||||||
|  | +			/* If we have a phy, the "up" state is the union of | ||||||
|  | +			 * both the PHY and the MAC */ | ||||||
|  | +			if (pl->phydev) | ||||||
|  | +				link_state.link &= pl->phy_state.link; | ||||||
|  |   | ||||||
|  | -				if (pl->phy_state.interface != | ||||||
|  | -				    link_state.interface) { | ||||||
|  | -					link_state.interface = pl->phy_state.interface; | ||||||
|  | -					changed = true; | ||||||
|  | -				} | ||||||
|  | +			/* Only update if the PHY link is up */ | ||||||
|  | +			if (pl->phydev && pl->phy_state.link) { | ||||||
|  | +				link_state.interface = pl->phy_state.interface; | ||||||
|  |   | ||||||
|  | -				/* Propagate the flow control from the PHY | ||||||
|  | -				 * to the MAC. Also propagate the interface | ||||||
|  | -				 * if changed. | ||||||
|  | -				 */ | ||||||
|  | -				if (pl->phy_state.link || changed) { | ||||||
|  | -					link_state.pause |= pl->phy_state.pause; | ||||||
|  | -					phylink_resolve_flow(pl, &link_state); | ||||||
|  | - | ||||||
|  | -					phylink_mac_config(pl, &link_state); | ||||||
|  | -				} | ||||||
|  | +				/* If we have a PHY, we need to update with | ||||||
|  | +				 * the pause mode bits. */ | ||||||
|  | +				link_state.pause |= pl->phy_state.pause; | ||||||
|  | +				phylink_resolve_flow(pl, &link_state); | ||||||
|  | +				phylink_mac_config(pl, &link_state); | ||||||
|  |  			} | ||||||
|  |  			break; | ||||||
|  |  		} | ||||||
| @@ -0,0 +1,49 @@ | |||||||
|  | From 1da223db3a0c522300b519ecbe1dc45927e28088 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Andrew Lunn <andrew@lunn.ch> | ||||||
|  | Date: Wed, 12 Sep 2018 01:53:15 +0200 | ||||||
|  | Subject: [PATCH 600/660] net: ethernet: Add helper for MACs which support asym | ||||||
|  |  pause | ||||||
|  |  | ||||||
|  | Rather than have the MAC drivers manipulate phydev members to indicate | ||||||
|  | they support Asym Pause, add a helper function. | ||||||
|  |  | ||||||
|  | Signed-off-by: Andrew Lunn <andrew@lunn.ch> | ||||||
|  | Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phy_device.c | 13 +++++++++++++ | ||||||
|  |  include/linux/phy.h          |  1 + | ||||||
|  |  2 files changed, 14 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phy_device.c | ||||||
|  | +++ b/drivers/net/phy/phy_device.c | ||||||
|  | @@ -1777,6 +1777,19 @@ int phy_set_max_speed(struct phy_device | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL(phy_set_max_speed); | ||||||
|  |   | ||||||
|  | +/** | ||||||
|  | + * phy_support_asym_pause - Enable support of asym pause | ||||||
|  | + * @phydev: target phy_device struct | ||||||
|  | + * | ||||||
|  | + * Description: Called by the MAC to indicate is supports Asym Pause. | ||||||
|  | + */ | ||||||
|  | +void phy_support_asym_pause(struct phy_device *phydev) | ||||||
|  | +{ | ||||||
|  | +	phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; | ||||||
|  | +	phydev->advertising = phydev->supported; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL(phy_support_asym_pause); | ||||||
|  | + | ||||||
|  |  static void of_set_phy_supported(struct phy_device *phydev) | ||||||
|  |  { | ||||||
|  |  	struct device_node *node = phydev->mdio.dev.of_node; | ||||||
|  | --- a/include/linux/phy.h | ||||||
|  | +++ b/include/linux/phy.h | ||||||
|  | @@ -1049,6 +1049,7 @@ int phy_mii_ioctl(struct phy_device *phy | ||||||
|  |  int phy_start_interrupts(struct phy_device *phydev); | ||||||
|  |  void phy_print_status(struct phy_device *phydev); | ||||||
|  |  int phy_set_max_speed(struct phy_device *phydev, u32 max_speed); | ||||||
|  | +void phy_support_asym_pause(struct phy_device *phydev); | ||||||
|  |   | ||||||
|  |  int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, | ||||||
|  |  		       int (*run)(struct phy_device *)); | ||||||
| @@ -0,0 +1,66 @@ | |||||||
|  | From ce825df56e0480a2cbb296e38976babafb57e503 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Andrew Lunn <andrew@lunn.ch> | ||||||
|  | Date: Wed, 12 Sep 2018 01:53:17 +0200 | ||||||
|  | Subject: [PATCH 601/660] net: ethernet: Add helper for set_pauseparam for Asym | ||||||
|  |  Pause | ||||||
|  |  | ||||||
|  | ethtool can be used to enable/disable pause. Add a helper to configure | ||||||
|  | the PHY when asym pause is supported. | ||||||
|  |  | ||||||
|  | Signed-off-by: Andrew Lunn <andrew@lunn.ch> | ||||||
|  | Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phy_device.c | 30 ++++++++++++++++++++++++++++++ | ||||||
|  |  include/linux/phy.h          |  1 + | ||||||
|  |  2 files changed, 31 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phy_device.c | ||||||
|  | +++ b/drivers/net/phy/phy_device.c | ||||||
|  | @@ -1790,6 +1790,36 @@ void phy_support_asym_pause(struct phy_d | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL(phy_support_asym_pause); | ||||||
|  |   | ||||||
|  | +/** | ||||||
|  | + * phy_set_asym_pause - Configure Pause and Asym Pause | ||||||
|  | + * @phydev: target phy_device struct | ||||||
|  | + * @rx: Receiver Pause is supported | ||||||
|  | + * @tx: Transmit Pause is supported | ||||||
|  | + * | ||||||
|  | + * Description: Configure advertised Pause support depending on if | ||||||
|  | + * transmit and receiver pause is supported. If there has been a | ||||||
|  | + * change in adverting, trigger a new autoneg. Generally called from | ||||||
|  | + * the set_pauseparam .ndo. | ||||||
|  | + */ | ||||||
|  | +void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx) | ||||||
|  | +{ | ||||||
|  | +	u16 oldadv = phydev->advertising; | ||||||
|  | +	u16 newadv = oldadv &= ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause); | ||||||
|  | + | ||||||
|  | +	if (rx) | ||||||
|  | +		newadv |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; | ||||||
|  | +	if (tx) | ||||||
|  | +		newadv ^= SUPPORTED_Asym_Pause; | ||||||
|  | + | ||||||
|  | +	if (oldadv != newadv) { | ||||||
|  | +		phydev->advertising = newadv; | ||||||
|  | + | ||||||
|  | +		if (phydev->autoneg) | ||||||
|  | +			phy_start_aneg(phydev); | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL(phy_set_asym_pause); | ||||||
|  | + | ||||||
|  |  static void of_set_phy_supported(struct phy_device *phydev) | ||||||
|  |  { | ||||||
|  |  	struct device_node *node = phydev->mdio.dev.of_node; | ||||||
|  | --- a/include/linux/phy.h | ||||||
|  | +++ b/include/linux/phy.h | ||||||
|  | @@ -1050,6 +1050,7 @@ int phy_start_interrupts(struct phy_devi | ||||||
|  |  void phy_print_status(struct phy_device *phydev); | ||||||
|  |  int phy_set_max_speed(struct phy_device *phydev, u32 max_speed); | ||||||
|  |  void phy_support_asym_pause(struct phy_device *phydev); | ||||||
|  | +void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx); | ||||||
|  |   | ||||||
|  |  int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, | ||||||
|  |  		       int (*run)(struct phy_device *)); | ||||||
| @@ -0,0 +1,40 @@ | |||||||
|  | From 1541649a9dd79e9b941d399de564475e426a2d0b Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Florian Fainelli <f.fainelli@gmail.com> | ||||||
|  | Date: Tue, 25 Sep 2018 11:28:45 -0700 | ||||||
|  | Subject: [PATCH 602/660] net: phy: Stop with excessive soft reset | ||||||
|  |  | ||||||
|  | While consolidating the PHY reset in phy_init_hw() an unconditionaly | ||||||
|  | BMCR soft-reset I became quite trigger happy with those. This was later | ||||||
|  | on deactivated for the Generic PHY driver on the premise that a prior | ||||||
|  | software entity (e.g: bootloader) might have applied workarounds in | ||||||
|  | commit 0878fff1f42c ("net: phy: Do not perform software reset for | ||||||
|  | Generic PHY"). | ||||||
|  |  | ||||||
|  | Since we have a hook to wire-up a soft_reset callback, just use that and | ||||||
|  | get rid of the call to genphy_soft_reset() entirely. This speeds up | ||||||
|  | initialization and link establishment for most PHYs out there that do | ||||||
|  | not require a reset. | ||||||
|  |  | ||||||
|  | Fixes: 87aa9f9c61ad ("net: phy: consolidate PHY reset in phy_init_hw()") | ||||||
|  | Tested-by: Wang, Dongsheng <dongsheng.wang@hxt-semitech.com> | ||||||
|  | Tested-by: Chris Healy <cphealy@gmail.com> | ||||||
|  | Tested-by: Andrew Lunn <andrew@lunn.ch> | ||||||
|  | Tested-by: Clemens Gruber <clemens.gruber@pqgruber.com> | ||||||
|  | Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phy_device.c | 2 -- | ||||||
|  |  1 file changed, 2 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phy_device.c | ||||||
|  | +++ b/drivers/net/phy/phy_device.c | ||||||
|  | @@ -886,8 +886,6 @@ int phy_init_hw(struct phy_device *phyde | ||||||
|  |   | ||||||
|  |  	if (phydev->drv->soft_reset) | ||||||
|  |  		ret = phydev->drv->soft_reset(phydev); | ||||||
|  | -	else | ||||||
|  | -		ret = genphy_soft_reset(phydev); | ||||||
|  |   | ||||||
|  |  	if (ret < 0) | ||||||
|  |  		return ret; | ||||||
| @@ -0,0 +1,375 @@ | |||||||
|  | From 80758d9542205cd2e9fa730067bc3888d4f5a096 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Nikita Yushchenko <nikita.yoush@cogentembedded.com> | ||||||
|  | Date: Wed, 6 Feb 2019 07:36:40 +0100 | ||||||
|  | Subject: [PATCH 603/660] net: phy: provide full set of accessor functions to | ||||||
|  |  MMD registers | ||||||
|  |  | ||||||
|  | This adds full set of locked and unlocked accessor functions to read and | ||||||
|  | write PHY MMD registers and/or bitfields. | ||||||
|  |  | ||||||
|  | Set of functions exactly matches what is already available for PHY | ||||||
|  | legacy registers. | ||||||
|  |  | ||||||
|  | Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com> | ||||||
|  | Signed-off-by: Andrew Lunn <andrew@lunn.ch> | ||||||
|  | Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phy-core.c | 116 ++++++++++++++++++++++++++++---- | ||||||
|  |  include/linux/phy.h        | 134 ++++++++++++++++++++++++++++++------- | ||||||
|  |  2 files changed, 214 insertions(+), 36 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phy-core.c | ||||||
|  | +++ b/drivers/net/phy/phy-core.c | ||||||
|  | @@ -247,15 +247,15 @@ static void mmd_phy_indirect(struct mii_ | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | - * phy_read_mmd - Convenience function for reading a register | ||||||
|  | + * __phy_read_mmd - Convenience function for reading a register | ||||||
|  |   * from an MMD on a given PHY. | ||||||
|  |   * @phydev: The phy_device struct | ||||||
|  |   * @devad: The MMD to read from (0..31) | ||||||
|  |   * @regnum: The register on the MMD to read (0..65535) | ||||||
|  |   * | ||||||
|  | - * Same rules as for phy_read(); | ||||||
|  | + * Same rules as for __phy_read(); | ||||||
|  |   */ | ||||||
|  | -int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) | ||||||
|  | +int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) | ||||||
|  |  { | ||||||
|  |  	int val; | ||||||
|  |   | ||||||
|  | @@ -267,33 +267,52 @@ int phy_read_mmd(struct phy_device *phyd | ||||||
|  |  	} else if (phydev->is_c45) { | ||||||
|  |  		u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); | ||||||
|  |   | ||||||
|  | -		val = mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); | ||||||
|  | +		val = __mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); | ||||||
|  |  	} else { | ||||||
|  |  		struct mii_bus *bus = phydev->mdio.bus; | ||||||
|  |  		int phy_addr = phydev->mdio.addr; | ||||||
|  |   | ||||||
|  | -		mutex_lock(&bus->mdio_lock); | ||||||
|  |  		mmd_phy_indirect(bus, phy_addr, devad, regnum); | ||||||
|  |   | ||||||
|  |  		/* Read the content of the MMD's selected register */ | ||||||
|  |  		val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA); | ||||||
|  | -		mutex_unlock(&bus->mdio_lock); | ||||||
|  |  	} | ||||||
|  |  	return val; | ||||||
|  |  } | ||||||
|  | +EXPORT_SYMBOL(__phy_read_mmd); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * phy_read_mmd - Convenience function for reading a register | ||||||
|  | + * from an MMD on a given PHY. | ||||||
|  | + * @phydev: The phy_device struct | ||||||
|  | + * @devad: The MMD to read from | ||||||
|  | + * @regnum: The register on the MMD to read | ||||||
|  | + * | ||||||
|  | + * Same rules as for phy_read(); | ||||||
|  | + */ | ||||||
|  | +int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&phydev->mdio.bus->mdio_lock); | ||||||
|  | +	ret = __phy_read_mmd(phydev, devad, regnum); | ||||||
|  | +	mutex_unlock(&phydev->mdio.bus->mdio_lock); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  |  EXPORT_SYMBOL(phy_read_mmd); | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | - * phy_write_mmd - Convenience function for writing a register | ||||||
|  | + * __phy_write_mmd - Convenience function for writing a register | ||||||
|  |   * on an MMD on a given PHY. | ||||||
|  |   * @phydev: The phy_device struct | ||||||
|  |   * @devad: The MMD to read from | ||||||
|  |   * @regnum: The register on the MMD to read | ||||||
|  |   * @val: value to write to @regnum | ||||||
|  |   * | ||||||
|  | - * Same rules as for phy_write(); | ||||||
|  | + * Same rules as for __phy_write(); | ||||||
|  |   */ | ||||||
|  | -int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) | ||||||
|  | +int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) | ||||||
|  |  { | ||||||
|  |  	int ret; | ||||||
|  |   | ||||||
|  | @@ -305,23 +324,43 @@ int phy_write_mmd(struct phy_device *phy | ||||||
|  |  	} else if (phydev->is_c45) { | ||||||
|  |  		u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); | ||||||
|  |   | ||||||
|  | -		ret = mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, | ||||||
|  | -				    addr, val); | ||||||
|  | +		ret = __mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, | ||||||
|  | +				      addr, val); | ||||||
|  |  	} else { | ||||||
|  |  		struct mii_bus *bus = phydev->mdio.bus; | ||||||
|  |  		int phy_addr = phydev->mdio.addr; | ||||||
|  |   | ||||||
|  | -		mutex_lock(&bus->mdio_lock); | ||||||
|  |  		mmd_phy_indirect(bus, phy_addr, devad, regnum); | ||||||
|  |   | ||||||
|  |  		/* Write the data into MMD's selected register */ | ||||||
|  |  		__mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); | ||||||
|  | -		mutex_unlock(&bus->mdio_lock); | ||||||
|  |   | ||||||
|  |  		ret = 0; | ||||||
|  |  	} | ||||||
|  |  	return ret; | ||||||
|  |  } | ||||||
|  | +EXPORT_SYMBOL(__phy_write_mmd); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * phy_write_mmd - Convenience function for writing a register | ||||||
|  | + * on an MMD on a given PHY. | ||||||
|  | + * @phydev: The phy_device struct | ||||||
|  | + * @devad: The MMD to read from | ||||||
|  | + * @regnum: The register on the MMD to read | ||||||
|  | + * @val: value to write to @regnum | ||||||
|  | + * | ||||||
|  | + * Same rules as for phy_write(); | ||||||
|  | + */ | ||||||
|  | +int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&phydev->mdio.bus->mdio_lock); | ||||||
|  | +	ret = __phy_write_mmd(phydev, devad, regnum, val); | ||||||
|  | +	mutex_unlock(&phydev->mdio.bus->mdio_lock); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  |  EXPORT_SYMBOL(phy_write_mmd); | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | @@ -371,6 +410,57 @@ int phy_modify(struct phy_device *phydev | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL_GPL(phy_modify); | ||||||
|  |   | ||||||
|  | +/** | ||||||
|  | + * __phy_modify_mmd - Convenience function for modifying a register on MMD | ||||||
|  | + * @phydev: the phy_device struct | ||||||
|  | + * @devad: the MMD containing register to modify | ||||||
|  | + * @regnum: register number to modify | ||||||
|  | + * @mask: bit mask of bits to clear | ||||||
|  | + * @set: new value of bits set in mask to write to @regnum | ||||||
|  | + * | ||||||
|  | + * Unlocked helper function which allows a MMD register to be modified as | ||||||
|  | + * new register value = (old register value & ~mask) | set | ||||||
|  | + */ | ||||||
|  | +int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | +		     u16 mask, u16 set) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	ret = __phy_read_mmd(phydev, devad, regnum); | ||||||
|  | +	if (ret < 0) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	ret = __phy_write_mmd(phydev, devad, regnum, (ret & ~mask) | set); | ||||||
|  | + | ||||||
|  | +	return ret < 0 ? ret : 0; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL_GPL(__phy_modify_mmd); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * phy_modify_mmd - Convenience function for modifying a register on MMD | ||||||
|  | + * @phydev: the phy_device struct | ||||||
|  | + * @devad: the MMD containing register to modify | ||||||
|  | + * @regnum: register number to modify | ||||||
|  | + * @mask: bit mask of bits to clear | ||||||
|  | + * @set: new value of bits set in mask to write to @regnum | ||||||
|  | + * | ||||||
|  | + * NOTE: MUST NOT be called from interrupt context, | ||||||
|  | + * because the bus read/write functions may wait for an interrupt | ||||||
|  | + * to conclude the operation. | ||||||
|  | + */ | ||||||
|  | +int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | +		   u16 mask, u16 set) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&phydev->mdio.bus->mdio_lock); | ||||||
|  | +	ret = __phy_modify_mmd(phydev, devad, regnum, mask, set); | ||||||
|  | +	mutex_unlock(&phydev->mdio.bus->mdio_lock); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL_GPL(phy_modify_mmd); | ||||||
|  | + | ||||||
|  |  static int __phy_read_page(struct phy_device *phydev) | ||||||
|  |  { | ||||||
|  |  	return phydev->drv->read_page(phydev); | ||||||
|  | --- a/include/linux/phy.h | ||||||
|  | +++ b/include/linux/phy.h | ||||||
|  | @@ -695,17 +695,6 @@ size_t phy_speeds(unsigned int *speeds, | ||||||
|  |  void phy_resolve_aneg_linkmode(struct phy_device *phydev); | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | - * phy_read_mmd - Convenience function for reading a register | ||||||
|  | - * from an MMD on a given PHY. | ||||||
|  | - * @phydev: The phy_device struct | ||||||
|  | - * @devad: The MMD to read from | ||||||
|  | - * @regnum: The register on the MMD to read | ||||||
|  | - * | ||||||
|  | - * Same rules as for phy_read(); | ||||||
|  | - */ | ||||||
|  | -int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum); | ||||||
|  | - | ||||||
|  | -/** | ||||||
|  |   * phy_read - Convenience function for reading a given PHY register | ||||||
|  |   * @phydev: the phy_device struct | ||||||
|  |   * @regnum: register number to read | ||||||
|  | @@ -760,9 +749,60 @@ static inline int __phy_write(struct phy | ||||||
|  |  			       val); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +/** | ||||||
|  | + * phy_read_mmd - Convenience function for reading a register | ||||||
|  | + * from an MMD on a given PHY. | ||||||
|  | + * @phydev: The phy_device struct | ||||||
|  | + * @devad: The MMD to read from | ||||||
|  | + * @regnum: The register on the MMD to read | ||||||
|  | + * | ||||||
|  | + * Same rules as for phy_read(); | ||||||
|  | + */ | ||||||
|  | +int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * __phy_read_mmd - Convenience function for reading a register | ||||||
|  | + * from an MMD on a given PHY. | ||||||
|  | + * @phydev: The phy_device struct | ||||||
|  | + * @devad: The MMD to read from | ||||||
|  | + * @regnum: The register on the MMD to read | ||||||
|  | + * | ||||||
|  | + * Same rules as for __phy_read(); | ||||||
|  | + */ | ||||||
|  | +int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * phy_write_mmd - Convenience function for writing a register | ||||||
|  | + * on an MMD on a given PHY. | ||||||
|  | + * @phydev: The phy_device struct | ||||||
|  | + * @devad: The MMD to write to | ||||||
|  | + * @regnum: The register on the MMD to read | ||||||
|  | + * @val: value to write to @regnum | ||||||
|  | + * | ||||||
|  | + * Same rules as for phy_write(); | ||||||
|  | + */ | ||||||
|  | +int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * __phy_write_mmd - Convenience function for writing a register | ||||||
|  | + * on an MMD on a given PHY. | ||||||
|  | + * @phydev: The phy_device struct | ||||||
|  | + * @devad: The MMD to write to | ||||||
|  | + * @regnum: The register on the MMD to read | ||||||
|  | + * @val: value to write to @regnum | ||||||
|  | + * | ||||||
|  | + * Same rules as for __phy_write(); | ||||||
|  | + */ | ||||||
|  | +int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val); | ||||||
|  | + | ||||||
|  |  int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set); | ||||||
|  |  int phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set); | ||||||
|  |   | ||||||
|  | +int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | +		u16 mask, u16 set); | ||||||
|  | +int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | +		u16 mask, u16 set); | ||||||
|  | + | ||||||
|  |  /** | ||||||
|  |   * __phy_set_bits - Convenience function for setting bits in a PHY register | ||||||
|  |   * @phydev: the phy_device struct | ||||||
|  | @@ -813,6 +853,66 @@ static inline int phy_clear_bits(struct | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | + * __phy_set_bits_mmd - Convenience function for setting bits in a register | ||||||
|  | + * on MMD | ||||||
|  | + * @phydev: the phy_device struct | ||||||
|  | + * @devad: the MMD containing register to modify | ||||||
|  | + * @regnum: register number to modify | ||||||
|  | + * @val: bits to set | ||||||
|  | + * | ||||||
|  | + * The caller must have taken the MDIO bus lock. | ||||||
|  | + */ | ||||||
|  | +static inline int __phy_set_bits_mmd(struct phy_device *phydev, int devad, | ||||||
|  | +		u32 regnum, u16 val) | ||||||
|  | +{ | ||||||
|  | +	return __phy_modify_mmd(phydev, devad, regnum, 0, val); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * __phy_clear_bits_mmd - Convenience function for clearing bits in a register | ||||||
|  | + * on MMD | ||||||
|  | + * @phydev: the phy_device struct | ||||||
|  | + * @devad: the MMD containing register to modify | ||||||
|  | + * @regnum: register number to modify | ||||||
|  | + * @val: bits to clear | ||||||
|  | + * | ||||||
|  | + * The caller must have taken the MDIO bus lock. | ||||||
|  | + */ | ||||||
|  | +static inline int __phy_clear_bits_mmd(struct phy_device *phydev, int devad, | ||||||
|  | +		u32 regnum, u16 val) | ||||||
|  | +{ | ||||||
|  | +	return __phy_modify_mmd(phydev, devad, regnum, val, 0); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * phy_set_bits_mmd - Convenience function for setting bits in a register | ||||||
|  | + * on MMD | ||||||
|  | + * @phydev: the phy_device struct | ||||||
|  | + * @devad: the MMD containing register to modify | ||||||
|  | + * @regnum: register number to modify | ||||||
|  | + * @val: bits to set | ||||||
|  | + */ | ||||||
|  | +static inline int phy_set_bits_mmd(struct phy_device *phydev, int devad, | ||||||
|  | +		u32 regnum, u16 val) | ||||||
|  | +{ | ||||||
|  | +	return phy_modify_mmd(phydev, devad, regnum, 0, val); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * phy_clear_bits_mmd - Convenience function for clearing bits in a register | ||||||
|  | + * on MMD | ||||||
|  | + * @phydev: the phy_device struct | ||||||
|  | + * @devad: the MMD containing register to modify | ||||||
|  | + * @regnum: register number to modify | ||||||
|  | + * @val: bits to clear | ||||||
|  | + */ | ||||||
|  | +static inline int phy_clear_bits_mmd(struct phy_device *phydev, int devad, | ||||||
|  | +		u32 regnum, u16 val) | ||||||
|  | +{ | ||||||
|  | +	return phy_modify_mmd(phydev, devad, regnum, val, 0); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  |   * phy_interrupt_is_valid - Convenience function for testing a given PHY irq | ||||||
|  |   * @phydev: the phy_device struct | ||||||
|  |   * | ||||||
|  | @@ -888,18 +988,6 @@ static inline bool phy_is_pseudo_fixed_l | ||||||
|  |  	return phydev->is_pseudo_fixed_link; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -/** | ||||||
|  | - * phy_write_mmd - Convenience function for writing a register | ||||||
|  | - * on an MMD on a given PHY. | ||||||
|  | - * @phydev: The phy_device struct | ||||||
|  | - * @devad: The MMD to read from | ||||||
|  | - * @regnum: The register on the MMD to read | ||||||
|  | - * @val: value to write to @regnum | ||||||
|  | - * | ||||||
|  | - * Same rules as for phy_write(); | ||||||
|  | - */ | ||||||
|  | -int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val); | ||||||
|  | - | ||||||
|  |  int phy_save_page(struct phy_device *phydev); | ||||||
|  |  int phy_select_page(struct phy_device *phydev, int page); | ||||||
|  |  int phy_restore_page(struct phy_device *phydev, int oldpage, int ret); | ||||||
| @@ -0,0 +1,217 @@ | |||||||
|  | From c1e3f753f6b85d7636024159bb78f764e09492f1 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Heiner Kallweit <hkallweit1@gmail.com> | ||||||
|  | Date: Sun, 10 Feb 2019 19:57:56 +0100 | ||||||
|  | Subject: [PATCH 604/660] net: phy: add register modifying helpers returning 1 | ||||||
|  |  on change | ||||||
|  |  | ||||||
|  | When modifying registers there are scenarios where we need to know | ||||||
|  | whether the register content actually changed. This patch adds | ||||||
|  | new helpers to not break users of the current ones, phy_modify() etc. | ||||||
|  |  | ||||||
|  | Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> | ||||||
|  | Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||||
|  | Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phy-core.c | 127 ++++++++++++++++++++++++++++++++++--- | ||||||
|  |  include/linux/phy.h        |  12 +++- | ||||||
|  |  2 files changed, 128 insertions(+), 11 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phy-core.c | ||||||
|  | +++ b/drivers/net/phy/phy-core.c | ||||||
|  | @@ -364,7 +364,7 @@ int phy_write_mmd(struct phy_device *phy | ||||||
|  |  EXPORT_SYMBOL(phy_write_mmd); | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | - * __phy_modify() - Convenience function for modifying a PHY register | ||||||
|  | + * __phy_modify_changed() - Convenience function for modifying a PHY register | ||||||
|  |   * @phydev: a pointer to a &struct phy_device | ||||||
|  |   * @regnum: register number | ||||||
|  |   * @mask: bit mask of bits to clear | ||||||
|  | @@ -372,16 +372,69 @@ EXPORT_SYMBOL(phy_write_mmd); | ||||||
|  |   * | ||||||
|  |   * Unlocked helper function which allows a PHY register to be modified as | ||||||
|  |   * new register value = (old register value & ~mask) | set | ||||||
|  | + * | ||||||
|  | + * Returns negative errno, 0 if there was no change, and 1 in case of change | ||||||
|  |   */ | ||||||
|  | -int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set) | ||||||
|  | +int __phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask, | ||||||
|  | +			 u16 set) | ||||||
|  |  { | ||||||
|  | -	int ret; | ||||||
|  | +	int new, ret; | ||||||
|  |   | ||||||
|  |  	ret = __phy_read(phydev, regnum); | ||||||
|  |  	if (ret < 0) | ||||||
|  |  		return ret; | ||||||
|  |   | ||||||
|  | -	ret = __phy_write(phydev, regnum, (ret & ~mask) | set); | ||||||
|  | +	new = (ret & ~mask) | set; | ||||||
|  | +	if (new == ret) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	ret = __phy_write(phydev, regnum, new); | ||||||
|  | + | ||||||
|  | +	return ret < 0 ? ret : 1; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL_GPL(__phy_modify_changed); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * phy_modify_changed - Function for modifying a PHY register | ||||||
|  | + * @phydev: the phy_device struct | ||||||
|  | + * @regnum: register number to modify | ||||||
|  | + * @mask: bit mask of bits to clear | ||||||
|  | + * @set: new value of bits set in mask to write to @regnum | ||||||
|  | + * | ||||||
|  | + * NOTE: MUST NOT be called from interrupt context, | ||||||
|  | + * because the bus read/write functions may wait for an interrupt | ||||||
|  | + * to conclude the operation. | ||||||
|  | + * | ||||||
|  | + * Returns negative errno, 0 if there was no change, and 1 in case of change | ||||||
|  | + */ | ||||||
|  | +int phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask, u16 set) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&phydev->mdio.bus->mdio_lock); | ||||||
|  | +	ret = __phy_modify_changed(phydev, regnum, mask, set); | ||||||
|  | +	mutex_unlock(&phydev->mdio.bus->mdio_lock); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL_GPL(phy_modify_changed); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * __phy_modify - Convenience function for modifying a PHY register | ||||||
|  | + * @phydev: the phy_device struct | ||||||
|  | + * @regnum: register number to modify | ||||||
|  | + * @mask: bit mask of bits to clear | ||||||
|  | + * @set: new value of bits set in mask to write to @regnum | ||||||
|  | + * | ||||||
|  | + * NOTE: MUST NOT be called from interrupt context, | ||||||
|  | + * because the bus read/write functions may wait for an interrupt | ||||||
|  | + * to conclude the operation. | ||||||
|  | + */ | ||||||
|  | +int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	ret = __phy_modify_changed(phydev, regnum, mask, set); | ||||||
|  |   | ||||||
|  |  	return ret < 0 ? ret : 0; | ||||||
|  |  } | ||||||
|  | @@ -411,7 +464,7 @@ int phy_modify(struct phy_device *phydev | ||||||
|  |  EXPORT_SYMBOL_GPL(phy_modify); | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | - * __phy_modify_mmd - Convenience function for modifying a register on MMD | ||||||
|  | + * __phy_modify_mmd_changed - Function for modifying a register on MMD | ||||||
|  |   * @phydev: the phy_device struct | ||||||
|  |   * @devad: the MMD containing register to modify | ||||||
|  |   * @regnum: register number to modify | ||||||
|  | @@ -420,17 +473,73 @@ EXPORT_SYMBOL_GPL(phy_modify); | ||||||
|  |   * | ||||||
|  |   * Unlocked helper function which allows a MMD register to be modified as | ||||||
|  |   * new register value = (old register value & ~mask) | set | ||||||
|  | + * | ||||||
|  | + * Returns negative errno, 0 if there was no change, and 1 in case of change | ||||||
|  |   */ | ||||||
|  | -int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | -		     u16 mask, u16 set) | ||||||
|  | +int __phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | +			     u16 mask, u16 set) | ||||||
|  |  { | ||||||
|  | -	int ret; | ||||||
|  | +	int new, ret; | ||||||
|  |   | ||||||
|  |  	ret = __phy_read_mmd(phydev, devad, regnum); | ||||||
|  |  	if (ret < 0) | ||||||
|  |  		return ret; | ||||||
|  |   | ||||||
|  | -	ret = __phy_write_mmd(phydev, devad, regnum, (ret & ~mask) | set); | ||||||
|  | +	new = (ret & ~mask) | set; | ||||||
|  | +	if (new == ret) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	ret = __phy_write_mmd(phydev, devad, regnum, new); | ||||||
|  | + | ||||||
|  | +	return ret < 0 ? ret : 1; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL_GPL(__phy_modify_mmd_changed); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * phy_modify_mmd_changed - Function for modifying a register on MMD | ||||||
|  | + * @phydev: the phy_device struct | ||||||
|  | + * @devad: the MMD containing register to modify | ||||||
|  | + * @regnum: register number to modify | ||||||
|  | + * @mask: bit mask of bits to clear | ||||||
|  | + * @set: new value of bits set in mask to write to @regnum | ||||||
|  | + * | ||||||
|  | + * NOTE: MUST NOT be called from interrupt context, | ||||||
|  | + * because the bus read/write functions may wait for an interrupt | ||||||
|  | + * to conclude the operation. | ||||||
|  | + * | ||||||
|  | + * Returns negative errno, 0 if there was no change, and 1 in case of change | ||||||
|  | + */ | ||||||
|  | +int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | +			   u16 mask, u16 set) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&phydev->mdio.bus->mdio_lock); | ||||||
|  | +	ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set); | ||||||
|  | +	mutex_unlock(&phydev->mdio.bus->mdio_lock); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL_GPL(phy_modify_mmd_changed); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * __phy_modify_mmd - Convenience function for modifying a register on MMD | ||||||
|  | + * @phydev: the phy_device struct | ||||||
|  | + * @devad: the MMD containing register to modify | ||||||
|  | + * @regnum: register number to modify | ||||||
|  | + * @mask: bit mask of bits to clear | ||||||
|  | + * @set: new value of bits set in mask to write to @regnum | ||||||
|  | + * | ||||||
|  | + * NOTE: MUST NOT be called from interrupt context, | ||||||
|  | + * because the bus read/write functions may wait for an interrupt | ||||||
|  | + * to conclude the operation. | ||||||
|  | + */ | ||||||
|  | +int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | +		     u16 mask, u16 set) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set); | ||||||
|  |   | ||||||
|  |  	return ret < 0 ? ret : 0; | ||||||
|  |  } | ||||||
|  | --- a/include/linux/phy.h | ||||||
|  | +++ b/include/linux/phy.h | ||||||
|  | @@ -795,13 +795,21 @@ int phy_write_mmd(struct phy_device *phy | ||||||
|  |   */ | ||||||
|  |  int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val); | ||||||
|  |   | ||||||
|  | +int __phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask, | ||||||
|  | +			 u16 set); | ||||||
|  | +int phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask, | ||||||
|  | +		       u16 set); | ||||||
|  |  int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set); | ||||||
|  |  int phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set); | ||||||
|  |   | ||||||
|  | +int __phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | +			     u16 mask, u16 set); | ||||||
|  | +int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | +			   u16 mask, u16 set); | ||||||
|  |  int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | -		u16 mask, u16 set); | ||||||
|  | +		     u16 mask, u16 set); | ||||||
|  |  int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, | ||||||
|  | -		u16 mask, u16 set); | ||||||
|  | +		   u16 mask, u16 set); | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  |   * __phy_set_bits - Convenience function for setting bits in a PHY register | ||||||
| @@ -0,0 +1,64 @@ | |||||||
|  | From 2c3db705737cf52d7d24c993f0889b25b956c718 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Heiner Kallweit <hkallweit1@gmail.com> | ||||||
|  | Date: Mon, 18 Feb 2019 21:27:18 +0100 | ||||||
|  | Subject: [PATCH 605/660] net: phy: add genphy_c45_check_and_restart_aneg | ||||||
|  |  | ||||||
|  | This function will be used by config_aneg callback implementations of | ||||||
|  | PHY drivers and allows to reduce boilerplate code. | ||||||
|  |  | ||||||
|  | Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phy-c45.c | 30 ++++++++++++++++++++++++++++++ | ||||||
|  |  include/linux/phy.h       |  1 + | ||||||
|  |  2 files changed, 31 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phy-c45.c | ||||||
|  | +++ b/drivers/net/phy/phy-c45.c | ||||||
|  | @@ -110,6 +110,36 @@ int genphy_c45_restart_aneg(struct phy_d | ||||||
|  |  EXPORT_SYMBOL_GPL(genphy_c45_restart_aneg); | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | + * genphy_c45_check_and_restart_aneg - Enable and restart auto-negotiation | ||||||
|  | + * @phydev: target phy_device struct | ||||||
|  | + * @restart: whether aneg restart is requested | ||||||
|  | + * | ||||||
|  | + * This assumes that the auto-negotiation MMD is present. | ||||||
|  | + * | ||||||
|  | + * Check, and restart auto-negotiation if needed. | ||||||
|  | + */ | ||||||
|  | +int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart) | ||||||
|  | +{ | ||||||
|  | +	int ret = 0; | ||||||
|  | + | ||||||
|  | +	if (!restart) { | ||||||
|  | +		/* Configure and restart aneg if it wasn't set before */ | ||||||
|  | +		ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); | ||||||
|  | +		if (ret < 0) | ||||||
|  | +			return ret; | ||||||
|  | + | ||||||
|  | +		if (!(ret & MDIO_AN_CTRL1_ENABLE)) | ||||||
|  | +			restart = true; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (restart) | ||||||
|  | +		ret = genphy_c45_restart_aneg(phydev); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL_GPL(genphy_c45_check_and_restart_aneg); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  |   * genphy_c45_aneg_done - return auto-negotiation complete status | ||||||
|  |   * @phydev: target phy_device struct | ||||||
|  |   * | ||||||
|  | --- a/include/linux/phy.h | ||||||
|  | +++ b/include/linux/phy.h | ||||||
|  | @@ -1098,6 +1098,7 @@ int genphy_write_mmd_unsupported(struct | ||||||
|  |   | ||||||
|  |  /* Clause 45 PHY */ | ||||||
|  |  int genphy_c45_restart_aneg(struct phy_device *phydev); | ||||||
|  | +int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart); | ||||||
|  |  int genphy_c45_aneg_done(struct phy_device *phydev); | ||||||
|  |  int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask); | ||||||
|  |  int genphy_c45_read_lpa(struct phy_device *phydev); | ||||||
| @@ -0,0 +1,59 @@ | |||||||
|  | From 4c4323084e9a67210c8d269dceba1be99356c414 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 28 May 2019 10:57:18 +0100 | ||||||
|  | Subject: [PATCH 606/660] net: phylink: remove netdev from phylink mii ioctl | ||||||
|  |  emulation | ||||||
|  |  | ||||||
|  | The netdev used in the phylink ioctl emulation is never used, so let's | ||||||
|  | remove it. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c | 12 ++++-------- | ||||||
|  |  1 file changed, 4 insertions(+), 8 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -1360,8 +1360,8 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_set_ee | ||||||
|  |   * | ||||||
|  |   * FIXME: should deal with negotiation state too. | ||||||
|  |   */ | ||||||
|  | -static int phylink_mii_emul_read(struct net_device *ndev, unsigned int reg, | ||||||
|  | -				 struct phylink_link_state *state, bool aneg) | ||||||
|  | +static int phylink_mii_emul_read(unsigned int reg, | ||||||
|  | +				 struct phylink_link_state *state) | ||||||
|  |  { | ||||||
|  |  	struct fixed_phy_status fs; | ||||||
|  |  	int val; | ||||||
|  | @@ -1376,8 +1376,6 @@ static int phylink_mii_emul_read(struct | ||||||
|  |  	if (reg == MII_BMSR) { | ||||||
|  |  		if (!state->an_complete) | ||||||
|  |  			val &= ~BMSR_ANEGCOMPLETE; | ||||||
|  | -		if (!aneg) | ||||||
|  | -			val &= ~BMSR_ANEGCAPABLE; | ||||||
|  |  	} | ||||||
|  |  	return val; | ||||||
|  |  } | ||||||
|  | @@ -1473,8 +1471,7 @@ static int phylink_mii_read(struct phyli | ||||||
|  |  	case MLO_AN_FIXED: | ||||||
|  |  		if (phy_id == 0) { | ||||||
|  |  			phylink_get_fixed_state(pl, &state); | ||||||
|  | -			val = phylink_mii_emul_read(pl->netdev, reg, &state, | ||||||
|  | -						    true); | ||||||
|  | +			val = phylink_mii_emul_read(reg, &state); | ||||||
|  |  		} | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  | @@ -1487,8 +1484,7 @@ static int phylink_mii_read(struct phyli | ||||||
|  |  			if (val < 0) | ||||||
|  |  				return val; | ||||||
|  |   | ||||||
|  | -			val = phylink_mii_emul_read(pl->netdev, reg, &state, | ||||||
|  | -						    true); | ||||||
|  | +			val = phylink_mii_emul_read(reg, &state); | ||||||
|  |  		} | ||||||
|  |  		break; | ||||||
|  |  	} | ||||||
| @@ -0,0 +1,90 @@ | |||||||
|  | From cba0aba37d2228556e0d1f776d403435868cdbfa Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 28 May 2019 10:57:23 +0100 | ||||||
|  | Subject: [PATCH 607/660] net: phylink: support for link gpio interrupt | ||||||
|  |  | ||||||
|  | Add support for using GPIO interrupts with a fixed-link GPIO rather than | ||||||
|  | polling the GPIO every second and invoking the phylink resolution.  This | ||||||
|  | avoids unnecessary calls to mac_config(). | ||||||
|  |  | ||||||
|  | Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c | 36 ++++++++++++++++++++++++++++++++---- | ||||||
|  |  1 file changed, 32 insertions(+), 4 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -59,6 +59,7 @@ struct phylink { | ||||||
|  |  	phy_interface_t cur_interface; | ||||||
|  |   | ||||||
|  |  	struct gpio_desc *link_gpio; | ||||||
|  | +	unsigned int link_irq; | ||||||
|  |  	struct timer_list link_poll; | ||||||
|  |  	void (*get_fixed_state)(struct net_device *dev, | ||||||
|  |  				struct phylink_link_state *s); | ||||||
|  | @@ -645,7 +646,7 @@ void phylink_destroy(struct phylink *pl) | ||||||
|  |  { | ||||||
|  |  	if (pl->sfp_bus) | ||||||
|  |  		sfp_unregister_upstream(pl->sfp_bus); | ||||||
|  | -	if (!IS_ERR_OR_NULL(pl->link_gpio)) | ||||||
|  | +	if (pl->link_gpio) | ||||||
|  |  		gpiod_put(pl->link_gpio); | ||||||
|  |   | ||||||
|  |  	cancel_work_sync(&pl->resolve); | ||||||
|  | @@ -912,6 +913,15 @@ void phylink_mac_change(struct phylink * | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL_GPL(phylink_mac_change); | ||||||
|  |   | ||||||
|  | +static irqreturn_t phylink_link_handler(int irq, void *data) | ||||||
|  | +{ | ||||||
|  | +	struct phylink *pl = data; | ||||||
|  | + | ||||||
|  | +	phylink_run_resolve(pl); | ||||||
|  | + | ||||||
|  | +	return IRQ_HANDLED; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  /** | ||||||
|  |   * phylink_start() - start a phylink instance | ||||||
|  |   * @pl: a pointer to a &struct phylink returned from phylink_create() | ||||||
|  | @@ -947,7 +957,22 @@ void phylink_start(struct phylink *pl) | ||||||
|  |  	clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); | ||||||
|  |  	phylink_run_resolve(pl); | ||||||
|  |   | ||||||
|  | -	if (pl->link_an_mode == MLO_AN_FIXED && !IS_ERR(pl->link_gpio)) | ||||||
|  | +	if (pl->link_an_mode == MLO_AN_FIXED && pl->link_gpio) { | ||||||
|  | +		int irq = gpiod_to_irq(pl->link_gpio); | ||||||
|  | + | ||||||
|  | +		if (irq > 0) { | ||||||
|  | +			if (!request_irq(irq, phylink_link_handler, | ||||||
|  | +					 IRQF_TRIGGER_RISING | | ||||||
|  | +					 IRQF_TRIGGER_FALLING, | ||||||
|  | +					 "netdev link", pl)) | ||||||
|  | +				pl->link_irq = irq; | ||||||
|  | +			else | ||||||
|  | +				irq = 0; | ||||||
|  | +		} | ||||||
|  | +		if (irq <= 0) | ||||||
|  | +			mod_timer(&pl->link_poll, jiffies + HZ); | ||||||
|  | +	} | ||||||
|  | +	if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) | ||||||
|  |  		mod_timer(&pl->link_poll, jiffies + HZ); | ||||||
|  |  	if (pl->sfp_bus) | ||||||
|  |  		sfp_upstream_start(pl->sfp_bus); | ||||||
|  | @@ -973,8 +998,11 @@ void phylink_stop(struct phylink *pl) | ||||||
|  |  		phy_stop(pl->phydev); | ||||||
|  |  	if (pl->sfp_bus) | ||||||
|  |  		sfp_upstream_stop(pl->sfp_bus); | ||||||
|  | -	if (pl->link_an_mode == MLO_AN_FIXED && !IS_ERR(pl->link_gpio)) | ||||||
|  | -		del_timer_sync(&pl->link_poll); | ||||||
|  | +	del_timer_sync(&pl->link_poll); | ||||||
|  | +	if (pl->link_irq) { | ||||||
|  | +		free_irq(pl->link_irq, pl); | ||||||
|  | +		pl->link_irq = 0; | ||||||
|  | +	} | ||||||
|  |   | ||||||
|  |  	phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED); | ||||||
|  |  } | ||||||
| @@ -0,0 +1,77 @@ | |||||||
|  | From eb5df3d026824832831376bbdf04e01a52776eea Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 28 May 2019 10:57:29 +0100 | ||||||
|  | Subject: [PATCH 608/660] net: phy: allow Clause 45 access via mii ioctl | ||||||
|  |  | ||||||
|  | Allow userspace to generate Clause 45 MII access cycles via phylib. | ||||||
|  | This is useful for tools such as mii-diag to be able to inspect Clause | ||||||
|  | 45 PHYs. | ||||||
|  |  | ||||||
|  | Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phy.c | 33 ++++++++++++++++++++++++--------- | ||||||
|  |  1 file changed, 24 insertions(+), 9 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phy.c | ||||||
|  | +++ b/drivers/net/phy/phy.c | ||||||
|  | @@ -397,6 +397,7 @@ int phy_mii_ioctl(struct phy_device *phy | ||||||
|  |  	struct mii_ioctl_data *mii_data = if_mii(ifr); | ||||||
|  |  	u16 val = mii_data->val_in; | ||||||
|  |  	bool change_autoneg = false; | ||||||
|  | +	int prtad, devad; | ||||||
|  |   | ||||||
|  |  	switch (cmd) { | ||||||
|  |  	case SIOCGMIIPHY: | ||||||
|  | @@ -404,14 +405,29 @@ int phy_mii_ioctl(struct phy_device *phy | ||||||
|  |  		/* fall through */ | ||||||
|  |   | ||||||
|  |  	case SIOCGMIIREG: | ||||||
|  | -		mii_data->val_out = mdiobus_read(phydev->mdio.bus, | ||||||
|  | -						 mii_data->phy_id, | ||||||
|  | -						 mii_data->reg_num); | ||||||
|  | +		if (mdio_phy_id_is_c45(mii_data->phy_id)) { | ||||||
|  | +			prtad = mdio_phy_id_prtad(mii_data->phy_id); | ||||||
|  | +			devad = mdio_phy_id_devad(mii_data->phy_id); | ||||||
|  | +			devad = MII_ADDR_C45 | devad << 16 | mii_data->reg_num; | ||||||
|  | +		} else { | ||||||
|  | +			prtad = mii_data->phy_id; | ||||||
|  | +			devad = mii_data->reg_num; | ||||||
|  | +		} | ||||||
|  | +		mii_data->val_out = mdiobus_read(phydev->mdio.bus, prtad, | ||||||
|  | +						 devad); | ||||||
|  |  		return 0; | ||||||
|  |   | ||||||
|  |  	case SIOCSMIIREG: | ||||||
|  | -		if (mii_data->phy_id == phydev->mdio.addr) { | ||||||
|  | -			switch (mii_data->reg_num) { | ||||||
|  | +		if (mdio_phy_id_is_c45(mii_data->phy_id)) { | ||||||
|  | +			prtad = mdio_phy_id_prtad(mii_data->phy_id); | ||||||
|  | +			devad = mdio_phy_id_devad(mii_data->phy_id); | ||||||
|  | +			devad = MII_ADDR_C45 | devad << 16 | mii_data->reg_num; | ||||||
|  | +		} else { | ||||||
|  | +			prtad = mii_data->phy_id; | ||||||
|  | +			devad = mii_data->reg_num; | ||||||
|  | +		} | ||||||
|  | +		if (prtad == phydev->mdio.addr) { | ||||||
|  | +			switch (devad) { | ||||||
|  |  			case MII_BMCR: | ||||||
|  |  				if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) { | ||||||
|  |  					if (phydev->autoneg == AUTONEG_ENABLE) | ||||||
|  | @@ -443,11 +459,10 @@ int phy_mii_ioctl(struct phy_device *phy | ||||||
|  |  			} | ||||||
|  |  		} | ||||||
|  |   | ||||||
|  | -		mdiobus_write(phydev->mdio.bus, mii_data->phy_id, | ||||||
|  | -			      mii_data->reg_num, val); | ||||||
|  | +		mdiobus_write(phydev->mdio.bus, prtad, devad, val); | ||||||
|  |   | ||||||
|  | -		if (mii_data->phy_id == phydev->mdio.addr && | ||||||
|  | -		    mii_data->reg_num == MII_BMCR && | ||||||
|  | +		if (prtad == phydev->mdio.addr && | ||||||
|  | +		    devad == MII_BMCR && | ||||||
|  |  		    val & BMCR_RESET) | ||||||
|  |  			return phy_init_hw(phydev); | ||||||
|  |   | ||||||
| @@ -0,0 +1,94 @@ | |||||||
|  | From aeabfaa63285470e81fa341e14f92d68880aa160 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 28 May 2019 10:57:34 +0100 | ||||||
|  | Subject: [PATCH 609/660] net: sfp: add mandatory attach/detach methods for sfp | ||||||
|  |  buses | ||||||
|  |  | ||||||
|  | Add attach and detach methods for SFP buses, which will allow us to get | ||||||
|  | rid of the netdev storage in sfp-bus. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c | 16 ++++++++++++++++ | ||||||
|  |  drivers/net/phy/sfp-bus.c |  4 ++-- | ||||||
|  |  include/linux/sfp.h       |  6 ++++++ | ||||||
|  |  3 files changed, 24 insertions(+), 2 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -1615,6 +1615,20 @@ int phylink_mii_ioctl(struct phylink *pl | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL_GPL(phylink_mii_ioctl); | ||||||
|  |   | ||||||
|  | +static void phylink_sfp_attach(void *upstream, struct sfp_bus *bus) | ||||||
|  | +{ | ||||||
|  | +	struct phylink *pl = upstream; | ||||||
|  | + | ||||||
|  | +	pl->netdev->sfp_bus = bus; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) | ||||||
|  | +{ | ||||||
|  | +	struct phylink *pl = upstream; | ||||||
|  | + | ||||||
|  | +	pl->netdev->sfp_bus = NULL; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  static int phylink_sfp_module_insert(void *upstream, | ||||||
|  |  				     const struct sfp_eeprom_id *id) | ||||||
|  |  { | ||||||
|  | @@ -1733,6 +1747,8 @@ static void phylink_sfp_disconnect_phy(v | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static const struct sfp_upstream_ops sfp_phylink_ops = { | ||||||
|  | +	.attach = phylink_sfp_attach, | ||||||
|  | +	.detach = phylink_sfp_detach, | ||||||
|  |  	.module_insert = phylink_sfp_module_insert, | ||||||
|  |  	.link_up = phylink_sfp_link_up, | ||||||
|  |  	.link_down = phylink_sfp_link_down, | ||||||
|  | --- a/drivers/net/phy/sfp-bus.c | ||||||
|  | +++ b/drivers/net/phy/sfp-bus.c | ||||||
|  | @@ -350,7 +350,7 @@ static int sfp_register_bus(struct sfp_b | ||||||
|  |  	bus->socket_ops->attach(bus->sfp); | ||||||
|  |  	if (bus->started) | ||||||
|  |  		bus->socket_ops->start(bus->sfp); | ||||||
|  | -	bus->netdev->sfp_bus = bus; | ||||||
|  | +	bus->upstream_ops->attach(bus->upstream, bus); | ||||||
|  |  	bus->registered = true; | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  | @@ -359,8 +359,8 @@ static void sfp_unregister_bus(struct sf | ||||||
|  |  { | ||||||
|  |  	const struct sfp_upstream_ops *ops = bus->upstream_ops; | ||||||
|  |   | ||||||
|  | -	bus->netdev->sfp_bus = NULL; | ||||||
|  |  	if (bus->registered) { | ||||||
|  | +		bus->upstream_ops->detach(bus->upstream, bus); | ||||||
|  |  		if (bus->started) | ||||||
|  |  			bus->socket_ops->stop(bus->sfp); | ||||||
|  |  		bus->socket_ops->detach(bus->sfp); | ||||||
|  | --- a/include/linux/sfp.h | ||||||
|  | +++ b/include/linux/sfp.h | ||||||
|  | @@ -469,6 +469,10 @@ struct sfp_bus; | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  |   * struct sfp_upstream_ops - upstream operations structure | ||||||
|  | + * @attach: called when the sfp socket driver is bound to the upstream | ||||||
|  | + *   (mandatory). | ||||||
|  | + * @detach: called when the sfp socket driver is unbound from the upstream | ||||||
|  | + *   (mandatory). | ||||||
|  |   * @module_insert: called after a module has been detected to determine | ||||||
|  |   *   whether the module is supported for the upstream device. | ||||||
|  |   * @module_remove: called after the module has been removed. | ||||||
|  | @@ -481,6 +485,8 @@ struct sfp_bus; | ||||||
|  |   *   been removed. | ||||||
|  |   */ | ||||||
|  |  struct sfp_upstream_ops { | ||||||
|  | +	void (*attach)(void *priv, struct sfp_bus *bus); | ||||||
|  | +	void (*detach)(void *priv, struct sfp_bus *bus); | ||||||
|  |  	int (*module_insert)(void *priv, const struct sfp_eeprom_id *id); | ||||||
|  |  	void (*module_remove)(void *priv); | ||||||
|  |  	void (*link_down)(void *priv); | ||||||
| @@ -0,0 +1,118 @@ | |||||||
|  | From 60d756717d772be90d07a07cd2cc140c76da3e4a Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 28 May 2019 10:57:39 +0100 | ||||||
|  | Subject: [PATCH 610/660] net: sfp: remove sfp-bus use of netdevs | ||||||
|  |  | ||||||
|  | The sfp-bus code now no longer has any use for the network device | ||||||
|  | structure, so remove its use. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c |  3 +-- | ||||||
|  |  drivers/net/phy/sfp-bus.c | 10 +++------- | ||||||
|  |  include/linux/sfp.h       |  6 ++---- | ||||||
|  |  3 files changed, 6 insertions(+), 13 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -555,8 +555,7 @@ static int phylink_register_sfp(struct p | ||||||
|  |  		return ret; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	pl->sfp_bus = sfp_register_upstream(ref.fwnode, pl->netdev, pl, | ||||||
|  | -					    &sfp_phylink_ops); | ||||||
|  | +	pl->sfp_bus = sfp_register_upstream(ref.fwnode, pl, &sfp_phylink_ops); | ||||||
|  |  	if (!pl->sfp_bus) | ||||||
|  |  		return -ENOMEM; | ||||||
|  |   | ||||||
|  | --- a/drivers/net/phy/sfp-bus.c | ||||||
|  | +++ b/drivers/net/phy/sfp-bus.c | ||||||
|  | @@ -23,7 +23,6 @@ struct sfp_bus { | ||||||
|  |   | ||||||
|  |  	const struct sfp_upstream_ops *upstream_ops; | ||||||
|  |  	void *upstream; | ||||||
|  | -	struct net_device *netdev; | ||||||
|  |  	struct phy_device *phydev; | ||||||
|  |   | ||||||
|  |  	bool registered; | ||||||
|  | @@ -442,13 +441,11 @@ static void sfp_upstream_clear(struct sf | ||||||
|  |  { | ||||||
|  |  	bus->upstream_ops = NULL; | ||||||
|  |  	bus->upstream = NULL; | ||||||
|  | -	bus->netdev = NULL; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  |   * sfp_register_upstream() - Register the neighbouring device | ||||||
|  |   * @fwnode: firmware node for the SFP bus | ||||||
|  | - * @ndev: network device associated with the interface | ||||||
|  |   * @upstream: the upstream private data | ||||||
|  |   * @ops: the upstream's &struct sfp_upstream_ops | ||||||
|  |   * | ||||||
|  | @@ -459,7 +456,7 @@ static void sfp_upstream_clear(struct sf | ||||||
|  |   * On error, returns %NULL. | ||||||
|  |   */ | ||||||
|  |  struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode, | ||||||
|  | -				      struct net_device *ndev, void *upstream, | ||||||
|  | +				      void *upstream, | ||||||
|  |  				      const struct sfp_upstream_ops *ops) | ||||||
|  |  { | ||||||
|  |  	struct sfp_bus *bus = sfp_bus_get(fwnode); | ||||||
|  | @@ -469,7 +466,6 @@ struct sfp_bus *sfp_register_upstream(st | ||||||
|  |  		rtnl_lock(); | ||||||
|  |  		bus->upstream_ops = ops; | ||||||
|  |  		bus->upstream = upstream; | ||||||
|  | -		bus->netdev = ndev; | ||||||
|  |   | ||||||
|  |  		if (bus->sfp) { | ||||||
|  |  			ret = sfp_register_bus(bus); | ||||||
|  | @@ -591,7 +587,7 @@ struct sfp_bus *sfp_register_socket(stru | ||||||
|  |  		bus->sfp = sfp; | ||||||
|  |  		bus->socket_ops = ops; | ||||||
|  |   | ||||||
|  | -		if (bus->netdev) { | ||||||
|  | +		if (bus->upstream_ops) { | ||||||
|  |  			ret = sfp_register_bus(bus); | ||||||
|  |  			if (ret) | ||||||
|  |  				sfp_socket_clear(bus); | ||||||
|  | @@ -611,7 +607,7 @@ EXPORT_SYMBOL_GPL(sfp_register_socket); | ||||||
|  |  void sfp_unregister_socket(struct sfp_bus *bus) | ||||||
|  |  { | ||||||
|  |  	rtnl_lock(); | ||||||
|  | -	if (bus->netdev) | ||||||
|  | +	if (bus->upstream_ops) | ||||||
|  |  		sfp_unregister_bus(bus); | ||||||
|  |  	sfp_socket_clear(bus); | ||||||
|  |  	rtnl_unlock(); | ||||||
|  | --- a/include/linux/sfp.h | ||||||
|  | +++ b/include/linux/sfp.h | ||||||
|  | @@ -464,7 +464,6 @@ enum { | ||||||
|  |  struct fwnode_handle; | ||||||
|  |  struct ethtool_eeprom; | ||||||
|  |  struct ethtool_modinfo; | ||||||
|  | -struct net_device; | ||||||
|  |  struct sfp_bus; | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | @@ -510,7 +509,7 @@ int sfp_get_module_eeprom(struct sfp_bus | ||||||
|  |  void sfp_upstream_start(struct sfp_bus *bus); | ||||||
|  |  void sfp_upstream_stop(struct sfp_bus *bus); | ||||||
|  |  struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode, | ||||||
|  | -				      struct net_device *ndev, void *upstream, | ||||||
|  | +				      void *upstream, | ||||||
|  |  				      const struct sfp_upstream_ops *ops); | ||||||
|  |  void sfp_unregister_upstream(struct sfp_bus *bus); | ||||||
|  |  #else | ||||||
|  | @@ -555,8 +554,7 @@ static inline void sfp_upstream_stop(str | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static inline struct sfp_bus *sfp_register_upstream( | ||||||
|  | -	struct fwnode_handle *fwnode, | ||||||
|  | -	struct net_device *ndev, void *upstream, | ||||||
|  | +	struct fwnode_handle *fwnode, void *upstream, | ||||||
|  |  	const struct sfp_upstream_ops *ops) | ||||||
|  |  { | ||||||
|  |  	return (struct sfp_bus *)-1; | ||||||
| @@ -0,0 +1,84 @@ | |||||||
|  | From 8ac1d3e5cf7d277769ba3403d99f643fab1e3fae Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Sat, 23 Nov 2019 14:19:54 +0000 | ||||||
|  | Subject: [PATCH 611/660] net: phylink: avoid reducing support mask | ||||||
|  |  | ||||||
|  | Avoid reducing the support mask as a result of the interface type | ||||||
|  | selected for SFP modules, or when setting the link settings through | ||||||
|  | ethtool - this should only change when the supported link modes of | ||||||
|  | the hardware combination change. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c | 13 +++++++++---- | ||||||
|  |  1 file changed, 9 insertions(+), 4 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -1137,6 +1137,7 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_ksetti | ||||||
|  |  int phylink_ethtool_ksettings_set(struct phylink *pl, | ||||||
|  |  				  const struct ethtool_link_ksettings *kset) | ||||||
|  |  { | ||||||
|  | +	__ETHTOOL_DECLARE_LINK_MODE_MASK(support); | ||||||
|  |  	struct ethtool_link_ksettings our_kset; | ||||||
|  |  	struct phylink_link_state config; | ||||||
|  |  	int ret; | ||||||
|  | @@ -1147,11 +1148,12 @@ int phylink_ethtool_ksettings_set(struct | ||||||
|  |  	    kset->base.autoneg != AUTONEG_ENABLE) | ||||||
|  |  		return -EINVAL; | ||||||
|  |   | ||||||
|  | +	linkmode_copy(support, pl->supported); | ||||||
|  |  	config = pl->link_config; | ||||||
|  |   | ||||||
|  |  	/* Mask out unsupported advertisements */ | ||||||
|  |  	linkmode_and(config.advertising, kset->link_modes.advertising, | ||||||
|  | -		     pl->supported); | ||||||
|  | +		     support); | ||||||
|  |   | ||||||
|  |  	/* FIXME: should we reject autoneg if phy/mac does not support it? */ | ||||||
|  |  	if (kset->base.autoneg == AUTONEG_DISABLE) { | ||||||
|  | @@ -1161,7 +1163,7 @@ int phylink_ethtool_ksettings_set(struct | ||||||
|  |  		 * duplex. | ||||||
|  |  		 */ | ||||||
|  |  		s = phy_lookup_setting(kset->base.speed, kset->base.duplex, | ||||||
|  | -				       pl->supported, | ||||||
|  | +				       support, | ||||||
|  |  				       __ETHTOOL_LINK_MODE_MASK_NBITS, false); | ||||||
|  |  		if (!s) | ||||||
|  |  			return -EINVAL; | ||||||
|  | @@ -1191,7 +1193,7 @@ int phylink_ethtool_ksettings_set(struct | ||||||
|  |  		__set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	if (phylink_validate(pl, pl->supported, &config)) | ||||||
|  | +	if (phylink_validate(pl, support, &config)) | ||||||
|  |  		return -EINVAL; | ||||||
|  |   | ||||||
|  |  	/* If autonegotiation is enabled, we must have an advertisement */ | ||||||
|  | @@ -1633,6 +1635,7 @@ static int phylink_sfp_module_insert(voi | ||||||
|  |  { | ||||||
|  |  	struct phylink *pl = upstream; | ||||||
|  |  	__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; | ||||||
|  | +	__ETHTOOL_DECLARE_LINK_MODE_MASK(support1); | ||||||
|  |  	struct phylink_link_state config; | ||||||
|  |  	phy_interface_t iface; | ||||||
|  |  	int ret = 0; | ||||||
|  | @@ -1660,6 +1663,8 @@ static int phylink_sfp_module_insert(voi | ||||||
|  |  		return ret; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | +	linkmode_copy(support1, support); | ||||||
|  | + | ||||||
|  |  	iface = sfp_select_interface(pl->sfp_bus, id, config.advertising); | ||||||
|  |  	if (iface == PHY_INTERFACE_MODE_NA) { | ||||||
|  |  		netdev_err(pl->netdev, | ||||||
|  | @@ -1669,7 +1674,7 @@ static int phylink_sfp_module_insert(voi | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	config.interface = iface; | ||||||
|  | -	ret = phylink_validate(pl, support, &config); | ||||||
|  | +	ret = phylink_validate(pl, support1, &config); | ||||||
|  |  	if (ret) { | ||||||
|  |  		netdev_err(pl->netdev, "validation of %s/%s with support %*pb failed: %d\n", | ||||||
|  |  			   phylink_an_mode_str(MLO_AN_INBAND), | ||||||
| @@ -0,0 +1,94 @@ | |||||||
|  | From 254236a22109efa84c9e9f5a9c76a1719439e309 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Robert Hancock <hancock@sedsystems.ca> | ||||||
|  | Date: Fri, 7 Jun 2019 10:42:35 -0600 | ||||||
|  | Subject: [PATCH 612/660] net: sfp: Stop SFP polling and interrupt handling | ||||||
|  |  during shutdown | ||||||
|  |  | ||||||
|  | SFP device polling can cause problems during the shutdown process if the | ||||||
|  | parent devices of the network controller have been shut down already. | ||||||
|  | This problem was seen on the iMX6 platform with PCIe devices, where | ||||||
|  | accessing the device after the bus is shut down causes a hang. | ||||||
|  |  | ||||||
|  | Free any acquired GPIO interrupts and stop all delayed work in the SFP | ||||||
|  | driver during the shutdown process, so that we ensure that no pending | ||||||
|  | operations are still occurring after the SFP shutdown completes. | ||||||
|  |  | ||||||
|  | Signed-off-by: Robert Hancock <hancock@sedsystems.ca> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 31 ++++++++++++++++++++++++++----- | ||||||
|  |  1 file changed, 26 insertions(+), 5 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -183,6 +183,7 @@ struct sfp { | ||||||
|  |  	int (*write)(struct sfp *, bool, u8, void *, size_t); | ||||||
|  |   | ||||||
|  |  	struct gpio_desc *gpio[GPIO_MAX]; | ||||||
|  | +	int gpio_irq[GPIO_MAX]; | ||||||
|  |   | ||||||
|  |  	bool attached; | ||||||
|  |  	struct mutex st_mutex;			/* Protects state */ | ||||||
|  | @@ -1803,7 +1804,7 @@ static int sfp_probe(struct platform_dev | ||||||
|  |  	const struct sff_data *sff; | ||||||
|  |  	struct sfp *sfp; | ||||||
|  |  	bool poll = false; | ||||||
|  | -	int irq, err, i; | ||||||
|  | +	int err, i; | ||||||
|  |   | ||||||
|  |  	sfp = sfp_alloc(&pdev->dev); | ||||||
|  |  	if (IS_ERR(sfp)) | ||||||
|  | @@ -1885,19 +1886,22 @@ static int sfp_probe(struct platform_dev | ||||||
|  |  		if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i]) | ||||||
|  |  			continue; | ||||||
|  |   | ||||||
|  | -		irq = gpiod_to_irq(sfp->gpio[i]); | ||||||
|  | -		if (!irq) { | ||||||
|  | +		sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]); | ||||||
|  | +		if (!sfp->gpio_irq[i]) { | ||||||
|  |  			poll = true; | ||||||
|  |  			continue; | ||||||
|  |  		} | ||||||
|  |   | ||||||
|  | -		err = devm_request_threaded_irq(sfp->dev, irq, NULL, sfp_irq, | ||||||
|  | +		err = devm_request_threaded_irq(sfp->dev, sfp->gpio_irq[i], | ||||||
|  | +						NULL, sfp_irq, | ||||||
|  |  						IRQF_ONESHOT | | ||||||
|  |  						IRQF_TRIGGER_RISING | | ||||||
|  |  						IRQF_TRIGGER_FALLING, | ||||||
|  |  						dev_name(sfp->dev), sfp); | ||||||
|  | -		if (err) | ||||||
|  | +		if (err) { | ||||||
|  | +			sfp->gpio_irq[i] = 0; | ||||||
|  |  			poll = true; | ||||||
|  | +		} | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	if (poll) | ||||||
|  | @@ -1928,9 +1932,26 @@ static int sfp_remove(struct platform_de | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static void sfp_shutdown(struct platform_device *pdev) | ||||||
|  | +{ | ||||||
|  | +	struct sfp *sfp = platform_get_drvdata(pdev); | ||||||
|  | +	int i; | ||||||
|  | + | ||||||
|  | +	for (i = 0; i < GPIO_MAX; i++) { | ||||||
|  | +		if (!sfp->gpio_irq[i]) | ||||||
|  | +			continue; | ||||||
|  | + | ||||||
|  | +		devm_free_irq(sfp->dev, sfp->gpio_irq[i], sfp); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	cancel_delayed_work_sync(&sfp->poll); | ||||||
|  | +	cancel_delayed_work_sync(&sfp->timeout); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  static struct platform_driver sfp_driver = { | ||||||
|  |  	.probe = sfp_probe, | ||||||
|  |  	.remove = sfp_remove, | ||||||
|  | +	.shutdown = sfp_shutdown, | ||||||
|  |  	.driver = { | ||||||
|  |  		.name = "sfp", | ||||||
|  |  		.of_match_table = sfp_of_match, | ||||||
| @@ -0,0 +1,141 @@ | |||||||
|  | From b8803113537a1c1f457eba6270d46e3af305031f Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Arseny Solokha <asolokha@kb.kras.ru> | ||||||
|  | Date: Wed, 24 Jul 2019 20:31:39 +0700 | ||||||
|  | Subject: [PATCH 613/660] net: phylink: don't start and stop SGMII PHYs in SFP | ||||||
|  |  modules twice | ||||||
|  |  | ||||||
|  | SFP modules connected using the SGMII interface have their own PHYs which | ||||||
|  | are handled by the struct phylink's phydev field. On the other hand, for | ||||||
|  | the modules connected using 1000Base-X interface that field is not set. | ||||||
|  |  | ||||||
|  | Since commit ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network | ||||||
|  | devices and sfp cages") phylink_start() ends up setting the phydev field | ||||||
|  | using the sfp-bus infrastructure, which eventually calls phy_start() on it, | ||||||
|  | and then calling phy_start() again on the same phydev from phylink_start() | ||||||
|  | itself. Similar call sequence holds for phylink_stop(), only in the reverse | ||||||
|  | order. This results in WARNs during network interface bringup and shutdown | ||||||
|  | when a copper SFP module is connected, as phy_start() and phy_stop() are | ||||||
|  | called twice in a row for the same phy_device: | ||||||
|  |  | ||||||
|  |   % ip link set up dev eth0 | ||||||
|  |   ------------[ cut here ]------------ | ||||||
|  |   called from state UP | ||||||
|  |   WARNING: CPU: 1 PID: 155 at drivers/net/phy/phy.c:895 phy_start+0x74/0xc0 | ||||||
|  |   Modules linked in: | ||||||
|  |   CPU: 1 PID: 155 Comm: backend Not tainted 5.2.0+ #1 | ||||||
|  |   NIP:  c0227bf0 LR: c0227bf0 CTR: c004d224 | ||||||
|  |   REGS: df547720 TRAP: 0700   Not tainted  (5.2.0+) | ||||||
|  |   MSR:  00029000 <CE,EE,ME>  CR: 24002822  XER: 00000000 | ||||||
|  |  | ||||||
|  |   GPR00: c0227bf0 df5477d8 df5d7080 00000014 df9d2370 df9d5ac4 1f4eb000 00000001 | ||||||
|  |   GPR08: c061fe58 00000000 00000000 df5477d8 0000003c 100c8768 00000000 00000000 | ||||||
|  |   GPR16: df486a00 c046f1c8 c046eea0 00000000 c046e904 c0239604 db68449c 00000000 | ||||||
|  |   GPR24: e9083204 00000000 00000001 db684460 e9083404 00000000 db6dce00 db6dcc00 | ||||||
|  |   NIP [c0227bf0] phy_start+0x74/0xc0 | ||||||
|  |   LR [c0227bf0] phy_start+0x74/0xc0 | ||||||
|  |   Call Trace: | ||||||
|  |   [df5477d8] [c0227bf0] phy_start+0x74/0xc0 (unreliable) | ||||||
|  |   [df5477e8] [c023cad0] startup_gfar+0x398/0x3f4 | ||||||
|  |   [df547828] [c023cf08] gfar_enet_open+0x364/0x374 | ||||||
|  |   [df547898] [c029d870] __dev_open+0xe4/0x140 | ||||||
|  |   [df5478c8] [c029db70] __dev_change_flags+0xf0/0x188 | ||||||
|  |   [df5478f8] [c029dc28] dev_change_flags+0x20/0x54 | ||||||
|  |   [df547918] [c02ae304] do_setlink+0x310/0x818 | ||||||
|  |   [df547a08] [c02b1eb8] __rtnl_newlink+0x384/0x6b0 | ||||||
|  |   [df547c28] [c02b222c] rtnl_newlink+0x48/0x68 | ||||||
|  |   [df547c48] [c02ad7c8] rtnetlink_rcv_msg+0x240/0x27c | ||||||
|  |   [df547c98] [c02cc068] netlink_rcv_skb+0x8c/0xf0 | ||||||
|  |   [df547cd8] [c02cba3c] netlink_unicast+0x114/0x19c | ||||||
|  |   [df547d08] [c02cbd74] netlink_sendmsg+0x2b0/0x2c0 | ||||||
|  |   [df547d58] [c027b668] sock_sendmsg_nosec+0x20/0x40 | ||||||
|  |   [df547d68] [c027d080] ___sys_sendmsg+0x17c/0x1dc | ||||||
|  |   [df547e98] [c027df7c] __sys_sendmsg+0x68/0x84 | ||||||
|  |   [df547ef8] [c027e430] sys_socketcall+0x1a0/0x204 | ||||||
|  |   [df547f38] [c000d1d8] ret_from_syscall+0x0/0x38 | ||||||
|  |   --- interrupt: c01 at 0xfd4e030 | ||||||
|  |       LR = 0xfd4e010 | ||||||
|  |   Instruction dump: | ||||||
|  |   813f0188 38800000 2b890005 419d0014 3d40c046 5529103a 394aa208 7c8a482e | ||||||
|  |   3c60c046 3863a1b8 4cc63182 4be009a1 <0fe00000> 48000030 3c60c046 3863a1d0 | ||||||
|  |   ---[ end trace d4c095aeaf6ea998 ]--- | ||||||
|  |  | ||||||
|  | and | ||||||
|  |  | ||||||
|  |   % ip link set down dev eth0 | ||||||
|  |   ------------[ cut here ]------------ | ||||||
|  |   called from state HALTED | ||||||
|  |   WARNING: CPU: 1 PID: 184 at drivers/net/phy/phy.c:858 phy_stop+0x3c/0x88 | ||||||
|  |  | ||||||
|  |   <...> | ||||||
|  |  | ||||||
|  |   Call Trace: | ||||||
|  |   [df581788] [c0228450] phy_stop+0x3c/0x88 (unreliable) | ||||||
|  |   [df581798] [c022d548] sfp_sm_phy_detach+0x1c/0x44 | ||||||
|  |   [df5817a8] [c022e8cc] sfp_sm_event+0x4b0/0x87c | ||||||
|  |   [df581848] [c022f04c] sfp_upstream_stop+0x34/0x44 | ||||||
|  |   [df581858] [c0225608] phylink_stop+0x7c/0xe4 | ||||||
|  |   [df581868] [c023c57c] stop_gfar+0x7c/0x94 | ||||||
|  |   [df581888] [c023c5b8] gfar_close+0x24/0x94 | ||||||
|  |   [df5818a8] [c0298688] __dev_close_many+0xdc/0xf8 | ||||||
|  |   [df5818c8] [c029db58] __dev_change_flags+0xd8/0x188 | ||||||
|  |   [df5818f8] [c029dc28] dev_change_flags+0x20/0x54 | ||||||
|  |   [df581918] [c02ae304] do_setlink+0x310/0x818 | ||||||
|  |   [df581a08] [c02b1eb8] __rtnl_newlink+0x384/0x6b0 | ||||||
|  |   [df581c28] [c02b222c] rtnl_newlink+0x48/0x68 | ||||||
|  |   [df581c48] [c02ad7c8] rtnetlink_rcv_msg+0x240/0x27c | ||||||
|  |   [df581c98] [c02cc068] netlink_rcv_skb+0x8c/0xf0 | ||||||
|  |   [df581cd8] [c02cba3c] netlink_unicast+0x114/0x19c | ||||||
|  |   [df581d08] [c02cbd74] netlink_sendmsg+0x2b0/0x2c0 | ||||||
|  |   [df581d58] [c027b668] sock_sendmsg_nosec+0x20/0x40 | ||||||
|  |   [df581d68] [c027d080] ___sys_sendmsg+0x17c/0x1dc | ||||||
|  |   [df581e98] [c027df7c] __sys_sendmsg+0x68/0x84 | ||||||
|  |   [df581ef8] [c027e430] sys_socketcall+0x1a0/0x204 | ||||||
|  |   [df581f38] [c000d1d8] ret_from_syscall+0x0/0x38 | ||||||
|  |  | ||||||
|  |   <...> | ||||||
|  |  | ||||||
|  |   ---[ end trace d4c095aeaf6ea999 ]--- | ||||||
|  |  | ||||||
|  | SFP modules with the 1000Base-X interface are not affected. | ||||||
|  |  | ||||||
|  | Place explicit calls to phy_start() and phy_stop() before enabling or after | ||||||
|  | disabling an attached SFP module, where phydev is not yet set (or is | ||||||
|  | already unset), so they will be made only from the inside of sfp-bus, if | ||||||
|  | needed. | ||||||
|  |  | ||||||
|  | Fixes: 217962615662 ("net: phy: warn if phy_start is called from invalid state") | ||||||
|  | Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru> | ||||||
|  | Acked-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c | 8 ++++---- | ||||||
|  |  1 file changed, 4 insertions(+), 4 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -973,10 +973,10 @@ void phylink_start(struct phylink *pl) | ||||||
|  |  	} | ||||||
|  |  	if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) | ||||||
|  |  		mod_timer(&pl->link_poll, jiffies + HZ); | ||||||
|  | -	if (pl->sfp_bus) | ||||||
|  | -		sfp_upstream_start(pl->sfp_bus); | ||||||
|  |  	if (pl->phydev) | ||||||
|  |  		phy_start(pl->phydev); | ||||||
|  | +	if (pl->sfp_bus) | ||||||
|  | +		sfp_upstream_start(pl->sfp_bus); | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL_GPL(phylink_start); | ||||||
|  |   | ||||||
|  | @@ -993,10 +993,10 @@ void phylink_stop(struct phylink *pl) | ||||||
|  |  { | ||||||
|  |  	ASSERT_RTNL(); | ||||||
|  |   | ||||||
|  | -	if (pl->phydev) | ||||||
|  | -		phy_stop(pl->phydev); | ||||||
|  |  	if (pl->sfp_bus) | ||||||
|  |  		sfp_upstream_stop(pl->sfp_bus); | ||||||
|  | +	if (pl->phydev) | ||||||
|  | +		phy_stop(pl->phydev); | ||||||
|  |  	del_timer_sync(&pl->link_poll); | ||||||
|  |  	if (pl->link_irq) { | ||||||
|  |  		free_irq(pl->link_irq, pl); | ||||||
| @@ -0,0 +1,179 @@ | |||||||
|  | From 4054955f0da08c81d42220cb445820d474f1ac92 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Sat, 14 Sep 2019 14:21:22 +0100 | ||||||
|  | Subject: [PATCH 614/660] net: sfp: move fwnode parsing into sfp-bus layer | ||||||
|  |  | ||||||
|  | Rather than parsing the sfp firmware node in phylink, parse it in the | ||||||
|  | sfp-bus code, so we can re-use this code for PHYs without having to | ||||||
|  | duplicate the parsing. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c | 21 ++++--------- | ||||||
|  |  drivers/net/phy/sfp-bus.c | 65 +++++++++++++++++++++++++-------------- | ||||||
|  |  include/linux/sfp.h       | 10 +++--- | ||||||
|  |  3 files changed, 53 insertions(+), 43 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -538,26 +538,17 @@ static const struct sfp_upstream_ops sfp | ||||||
|  |  static int phylink_register_sfp(struct phylink *pl, | ||||||
|  |  				struct fwnode_handle *fwnode) | ||||||
|  |  { | ||||||
|  | -	struct fwnode_reference_args ref; | ||||||
|  | +	struct sfp_bus *bus; | ||||||
|  |  	int ret; | ||||||
|  |   | ||||||
|  | -	if (!fwnode) | ||||||
|  | -		return 0; | ||||||
|  | - | ||||||
|  | -	ret = fwnode_property_get_reference_args(fwnode, "sfp", NULL, | ||||||
|  | -						 0, 0, &ref); | ||||||
|  | -	if (ret < 0) { | ||||||
|  | -		if (ret == -ENOENT) | ||||||
|  | -			return 0; | ||||||
|  | - | ||||||
|  | -		netdev_err(pl->netdev, "unable to parse \"sfp\" node: %d\n", | ||||||
|  | -			   ret); | ||||||
|  | +	bus = sfp_register_upstream_node(fwnode, pl, &sfp_phylink_ops); | ||||||
|  | +	if (IS_ERR(bus)) { | ||||||
|  | +		ret = PTR_ERR(bus); | ||||||
|  | +		netdev_err(pl->netdev, "unable to attach SFP bus: %d\n", ret); | ||||||
|  |  		return ret; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	pl->sfp_bus = sfp_register_upstream(ref.fwnode, pl, &sfp_phylink_ops); | ||||||
|  | -	if (!pl->sfp_bus) | ||||||
|  | -		return -ENOMEM; | ||||||
|  | +	pl->sfp_bus = bus; | ||||||
|  |   | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  | --- a/drivers/net/phy/sfp-bus.c | ||||||
|  | +++ b/drivers/net/phy/sfp-bus.c | ||||||
|  | @@ -3,6 +3,7 @@ | ||||||
|  |  #include <linux/list.h> | ||||||
|  |  #include <linux/mutex.h> | ||||||
|  |  #include <linux/phylink.h> | ||||||
|  | +#include <linux/property.h> | ||||||
|  |  #include <linux/rtnetlink.h> | ||||||
|  |  #include <linux/slab.h> | ||||||
|  |   | ||||||
|  | @@ -444,45 +445,63 @@ static void sfp_upstream_clear(struct sf | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | - * sfp_register_upstream() - Register the neighbouring device | ||||||
|  | - * @fwnode: firmware node for the SFP bus | ||||||
|  | + * sfp_register_upstream_node() - parse and register the neighbouring device | ||||||
|  | + * @fwnode: firmware node for the parent device (MAC or PHY) | ||||||
|  |   * @upstream: the upstream private data | ||||||
|  |   * @ops: the upstream's &struct sfp_upstream_ops | ||||||
|  |   * | ||||||
|  | - * Register the upstream device (eg, PHY) with the SFP bus. MAC drivers | ||||||
|  | - * should use phylink, which will call this function for them. Returns | ||||||
|  | - * a pointer to the allocated &struct sfp_bus. | ||||||
|  | + * Parse the parent device's firmware node for a SFP bus, and register the | ||||||
|  | + * SFP bus using sfp_register_upstream(). | ||||||
|  |   * | ||||||
|  | - * On error, returns %NULL. | ||||||
|  | + * Returns: on success, a pointer to the sfp_bus structure, | ||||||
|  | + *	    %NULL if no SFP is specified, | ||||||
|  | + * 	    on failure, an error pointer value: | ||||||
|  | + * 		corresponding to the errors detailed for | ||||||
|  | + * 		fwnode_property_get_reference_args(). | ||||||
|  | + * 	        %-ENOMEM if we failed to allocate the bus. | ||||||
|  | + *		an error from the upstream's connect_phy() method. | ||||||
|  |   */ | ||||||
|  | -struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode, | ||||||
|  | -				      void *upstream, | ||||||
|  | -				      const struct sfp_upstream_ops *ops) | ||||||
|  | -{ | ||||||
|  | -	struct sfp_bus *bus = sfp_bus_get(fwnode); | ||||||
|  | -	int ret = 0; | ||||||
|  | - | ||||||
|  | -	if (bus) { | ||||||
|  | -		rtnl_lock(); | ||||||
|  | -		bus->upstream_ops = ops; | ||||||
|  | -		bus->upstream = upstream; | ||||||
|  | +struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode, | ||||||
|  | +					   void *upstream, | ||||||
|  | +					   const struct sfp_upstream_ops *ops) | ||||||
|  | +{ | ||||||
|  | +	struct fwnode_reference_args ref; | ||||||
|  | +	struct sfp_bus *bus; | ||||||
|  | +	int ret; | ||||||
|  |   | ||||||
|  | -		if (bus->sfp) { | ||||||
|  | -			ret = sfp_register_bus(bus); | ||||||
|  | -			if (ret) | ||||||
|  | -				sfp_upstream_clear(bus); | ||||||
|  | -		} | ||||||
|  | -		rtnl_unlock(); | ||||||
|  | +	ret = fwnode_property_get_reference_args(fwnode, "sfp", NULL, | ||||||
|  | +						 0, 0, &ref); | ||||||
|  | +	if (ret == -ENOENT) | ||||||
|  | +		return NULL; | ||||||
|  | +	else if (ret < 0) | ||||||
|  | +		return ERR_PTR(ret); | ||||||
|  | + | ||||||
|  | +	bus = sfp_bus_get(ref.fwnode); | ||||||
|  | +	fwnode_handle_put(ref.fwnode); | ||||||
|  | +	if (!bus) | ||||||
|  | +		return ERR_PTR(-ENOMEM); | ||||||
|  | + | ||||||
|  | +	rtnl_lock(); | ||||||
|  | +	bus->upstream_ops = ops; | ||||||
|  | +	bus->upstream = upstream; | ||||||
|  | + | ||||||
|  | +	if (bus->sfp) { | ||||||
|  | +		ret = sfp_register_bus(bus); | ||||||
|  | +		if (ret) | ||||||
|  | +			sfp_upstream_clear(bus); | ||||||
|  | +	} else { | ||||||
|  | +		ret = 0; | ||||||
|  |  	} | ||||||
|  | +	rtnl_unlock(); | ||||||
|  |   | ||||||
|  |  	if (ret) { | ||||||
|  |  		sfp_bus_put(bus); | ||||||
|  | -		bus = NULL; | ||||||
|  | +		bus = ERR_PTR(ret); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	return bus; | ||||||
|  |  } | ||||||
|  | -EXPORT_SYMBOL_GPL(sfp_register_upstream); | ||||||
|  | +EXPORT_SYMBOL_GPL(sfp_register_upstream_node); | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  |   * sfp_unregister_upstream() - Unregister sfp bus | ||||||
|  | --- a/include/linux/sfp.h | ||||||
|  | +++ b/include/linux/sfp.h | ||||||
|  | @@ -508,9 +508,9 @@ int sfp_get_module_eeprom(struct sfp_bus | ||||||
|  |  			  u8 *data); | ||||||
|  |  void sfp_upstream_start(struct sfp_bus *bus); | ||||||
|  |  void sfp_upstream_stop(struct sfp_bus *bus); | ||||||
|  | -struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode, | ||||||
|  | -				      void *upstream, | ||||||
|  | -				      const struct sfp_upstream_ops *ops); | ||||||
|  | +struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode, | ||||||
|  | +					   void *upstream, | ||||||
|  | +					   const struct sfp_upstream_ops *ops); | ||||||
|  |  void sfp_unregister_upstream(struct sfp_bus *bus); | ||||||
|  |  #else | ||||||
|  |  static inline int sfp_parse_port(struct sfp_bus *bus, | ||||||
|  | @@ -553,11 +553,11 @@ static inline void sfp_upstream_stop(str | ||||||
|  |  { | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static inline struct sfp_bus *sfp_register_upstream( | ||||||
|  | +static inline struct sfp_bus *sfp_register_upstream_node( | ||||||
|  |  	struct fwnode_handle *fwnode, void *upstream, | ||||||
|  |  	const struct sfp_upstream_ops *ops) | ||||||
|  |  { | ||||||
|  | -	return (struct sfp_bus *)-1; | ||||||
|  | +	return NULL; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static inline void sfp_unregister_upstream(struct sfp_bus *bus) | ||||||
| @@ -0,0 +1,254 @@ | |||||||
|  | From 863b5b6941f9f43b924393b6ba2b36647e7dee42 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Thu, 7 Nov 2019 17:06:08 +0000 | ||||||
|  | Subject: [PATCH 615/660] net: sfp: rework upstream interface | ||||||
|  |  | ||||||
|  | The current upstream interface is an all-or-nothing, which is | ||||||
|  | sub-optimal for future changes, as it doesn't allow the upstream driver | ||||||
|  | to prepare for the SFP module becoming available, as it is at boot. | ||||||
|  |  | ||||||
|  | Switch to a find-sfp-bus, add-upstream, del-upstream, put-sfp-bus | ||||||
|  | interface structure instead, which allows the upstream driver to | ||||||
|  | prepare for a module being available as soon as add-upstream is called. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c | 10 +++-- | ||||||
|  |  drivers/net/phy/sfp-bus.c | 92 +++++++++++++++++++++++++++------------ | ||||||
|  |  include/linux/sfp.h       | 25 +++++++---- | ||||||
|  |  3 files changed, 88 insertions(+), 39 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -541,7 +541,7 @@ static int phylink_register_sfp(struct p | ||||||
|  |  	struct sfp_bus *bus; | ||||||
|  |  	int ret; | ||||||
|  |   | ||||||
|  | -	bus = sfp_register_upstream_node(fwnode, pl, &sfp_phylink_ops); | ||||||
|  | +	bus = sfp_bus_find_fwnode(fwnode); | ||||||
|  |  	if (IS_ERR(bus)) { | ||||||
|  |  		ret = PTR_ERR(bus); | ||||||
|  |  		netdev_err(pl->netdev, "unable to attach SFP bus: %d\n", ret); | ||||||
|  | @@ -550,7 +550,10 @@ static int phylink_register_sfp(struct p | ||||||
|  |   | ||||||
|  |  	pl->sfp_bus = bus; | ||||||
|  |   | ||||||
|  | -	return 0; | ||||||
|  | +	ret = sfp_bus_add_upstream(bus, pl, &sfp_phylink_ops); | ||||||
|  | +	sfp_bus_put(bus); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | @@ -634,8 +637,7 @@ EXPORT_SYMBOL_GPL(phylink_create); | ||||||
|  |   */ | ||||||
|  |  void phylink_destroy(struct phylink *pl) | ||||||
|  |  { | ||||||
|  | -	if (pl->sfp_bus) | ||||||
|  | -		sfp_unregister_upstream(pl->sfp_bus); | ||||||
|  | +	sfp_bus_del_upstream(pl->sfp_bus); | ||||||
|  |  	if (pl->link_gpio) | ||||||
|  |  		gpiod_put(pl->link_gpio); | ||||||
|  |   | ||||||
|  | --- a/drivers/net/phy/sfp-bus.c | ||||||
|  | +++ b/drivers/net/phy/sfp-bus.c | ||||||
|  | @@ -328,10 +328,19 @@ static void sfp_bus_release(struct kref | ||||||
|  |  	kfree(bus); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static void sfp_bus_put(struct sfp_bus *bus) | ||||||
|  | +/** | ||||||
|  | + * sfp_bus_put() - put a reference on the &struct sfp_bus | ||||||
|  | + * bus: the &struct sfp_bus found via sfp_bus_find_fwnode() | ||||||
|  | + * | ||||||
|  | + * Put a reference on the &struct sfp_bus and free the underlying structure | ||||||
|  | + * if this was the last reference. | ||||||
|  | + */ | ||||||
|  | +void sfp_bus_put(struct sfp_bus *bus) | ||||||
|  |  { | ||||||
|  | -	kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); | ||||||
|  | +	if (bus) | ||||||
|  | +		kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); | ||||||
|  |  } | ||||||
|  | +EXPORT_SYMBOL_GPL(sfp_bus_put); | ||||||
|  |   | ||||||
|  |  static int sfp_register_bus(struct sfp_bus *bus) | ||||||
|  |  { | ||||||
|  | @@ -347,11 +356,11 @@ static int sfp_register_bus(struct sfp_b | ||||||
|  |  				return ret; | ||||||
|  |  		} | ||||||
|  |  	} | ||||||
|  | +	bus->registered = true; | ||||||
|  |  	bus->socket_ops->attach(bus->sfp); | ||||||
|  |  	if (bus->started) | ||||||
|  |  		bus->socket_ops->start(bus->sfp); | ||||||
|  |  	bus->upstream_ops->attach(bus->upstream, bus); | ||||||
|  | -	bus->registered = true; | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | @@ -445,13 +454,12 @@ static void sfp_upstream_clear(struct sf | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | - * sfp_register_upstream_node() - parse and register the neighbouring device | ||||||
|  | + * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode | ||||||
|  |   * @fwnode: firmware node for the parent device (MAC or PHY) | ||||||
|  | - * @upstream: the upstream private data | ||||||
|  | - * @ops: the upstream's &struct sfp_upstream_ops | ||||||
|  |   * | ||||||
|  | - * Parse the parent device's firmware node for a SFP bus, and register the | ||||||
|  | - * SFP bus using sfp_register_upstream(). | ||||||
|  | + * Parse the parent device's firmware node for a SFP bus, and locate | ||||||
|  | + * the sfp_bus structure, incrementing its reference count.  This must | ||||||
|  | + * be put via sfp_bus_put() when done. | ||||||
|  |   * | ||||||
|  |   * Returns: on success, a pointer to the sfp_bus structure, | ||||||
|  |   *	    %NULL if no SFP is specified, | ||||||
|  | @@ -461,9 +469,7 @@ static void sfp_upstream_clear(struct sf | ||||||
|  |   * 	        %-ENOMEM if we failed to allocate the bus. | ||||||
|  |   *		an error from the upstream's connect_phy() method. | ||||||
|  |   */ | ||||||
|  | -struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode, | ||||||
|  | -					   void *upstream, | ||||||
|  | -					   const struct sfp_upstream_ops *ops) | ||||||
|  | +struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) | ||||||
|  |  { | ||||||
|  |  	struct fwnode_reference_args ref; | ||||||
|  |  	struct sfp_bus *bus; | ||||||
|  | @@ -481,7 +487,39 @@ struct sfp_bus *sfp_register_upstream_no | ||||||
|  |  	if (!bus) | ||||||
|  |  		return ERR_PTR(-ENOMEM); | ||||||
|  |   | ||||||
|  | +	return bus; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL_GPL(sfp_bus_find_fwnode); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * sfp_bus_add_upstream() - parse and register the neighbouring device | ||||||
|  | + * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode() | ||||||
|  | + * @upstream: the upstream private data | ||||||
|  | + * @ops: the upstream's &struct sfp_upstream_ops | ||||||
|  | + * | ||||||
|  | + * Add upstream driver for the SFP bus, and if the bus is complete, register | ||||||
|  | + * the SFP bus using sfp_register_upstream().  This takes a reference on the | ||||||
|  | + * bus, so it is safe to put the bus after this call. | ||||||
|  | + * | ||||||
|  | + * Returns: on success, a pointer to the sfp_bus structure, | ||||||
|  | + *	    %NULL if no SFP is specified, | ||||||
|  | + * 	    on failure, an error pointer value: | ||||||
|  | + * 		corresponding to the errors detailed for | ||||||
|  | + * 		fwnode_property_get_reference_args(). | ||||||
|  | + * 	        %-ENOMEM if we failed to allocate the bus. | ||||||
|  | + *		an error from the upstream's connect_phy() method. | ||||||
|  | + */ | ||||||
|  | +int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, | ||||||
|  | +			 const struct sfp_upstream_ops *ops) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	/* If no bus, return success */ | ||||||
|  | +	if (!bus) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  |  	rtnl_lock(); | ||||||
|  | +	kref_get(&bus->kref); | ||||||
|  |  	bus->upstream_ops = ops; | ||||||
|  |  	bus->upstream = upstream; | ||||||
|  |   | ||||||
|  | @@ -494,33 +532,33 @@ struct sfp_bus *sfp_register_upstream_no | ||||||
|  |  	} | ||||||
|  |  	rtnl_unlock(); | ||||||
|  |   | ||||||
|  | -	if (ret) { | ||||||
|  | +	if (ret) | ||||||
|  |  		sfp_bus_put(bus); | ||||||
|  | -		bus = ERR_PTR(ret); | ||||||
|  | -	} | ||||||
|  |   | ||||||
|  | -	return bus; | ||||||
|  | +	return ret; | ||||||
|  |  } | ||||||
|  | -EXPORT_SYMBOL_GPL(sfp_register_upstream_node); | ||||||
|  | +EXPORT_SYMBOL_GPL(sfp_bus_add_upstream); | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | - * sfp_unregister_upstream() - Unregister sfp bus | ||||||
|  | + * sfp_bus_del_upstream() - Delete a sfp bus | ||||||
|  |   * @bus: a pointer to the &struct sfp_bus structure for the sfp module | ||||||
|  |   * | ||||||
|  | - * Unregister a previously registered upstream connection for the SFP | ||||||
|  | - * module. @bus is returned from sfp_register_upstream(). | ||||||
|  | + * Delete a previously registered upstream connection for the SFP | ||||||
|  | + * module. @bus should have been added by sfp_bus_add_upstream(). | ||||||
|  |   */ | ||||||
|  | -void sfp_unregister_upstream(struct sfp_bus *bus) | ||||||
|  | +void sfp_bus_del_upstream(struct sfp_bus *bus) | ||||||
|  |  { | ||||||
|  | -	rtnl_lock(); | ||||||
|  | -	if (bus->sfp) | ||||||
|  | -		sfp_unregister_bus(bus); | ||||||
|  | -	sfp_upstream_clear(bus); | ||||||
|  | -	rtnl_unlock(); | ||||||
|  | +	if (bus) { | ||||||
|  | +		rtnl_lock(); | ||||||
|  | +		if (bus->sfp) | ||||||
|  | +			sfp_unregister_bus(bus); | ||||||
|  | +		sfp_upstream_clear(bus); | ||||||
|  | +		rtnl_unlock(); | ||||||
|  |   | ||||||
|  | -	sfp_bus_put(bus); | ||||||
|  | +		sfp_bus_put(bus); | ||||||
|  | +	} | ||||||
|  |  } | ||||||
|  | -EXPORT_SYMBOL_GPL(sfp_unregister_upstream); | ||||||
|  | +EXPORT_SYMBOL_GPL(sfp_bus_del_upstream); | ||||||
|  |   | ||||||
|  |  /* Socket driver entry points */ | ||||||
|  |  int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev) | ||||||
|  | --- a/include/linux/sfp.h | ||||||
|  | +++ b/include/linux/sfp.h | ||||||
|  | @@ -508,10 +508,11 @@ int sfp_get_module_eeprom(struct sfp_bus | ||||||
|  |  			  u8 *data); | ||||||
|  |  void sfp_upstream_start(struct sfp_bus *bus); | ||||||
|  |  void sfp_upstream_stop(struct sfp_bus *bus); | ||||||
|  | -struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode, | ||||||
|  | -					   void *upstream, | ||||||
|  | -					   const struct sfp_upstream_ops *ops); | ||||||
|  | -void sfp_unregister_upstream(struct sfp_bus *bus); | ||||||
|  | +void sfp_bus_put(struct sfp_bus *bus); | ||||||
|  | +struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode); | ||||||
|  | +int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, | ||||||
|  | +			 const struct sfp_upstream_ops *ops); | ||||||
|  | +void sfp_bus_del_upstream(struct sfp_bus *bus); | ||||||
|  |  #else | ||||||
|  |  static inline int sfp_parse_port(struct sfp_bus *bus, | ||||||
|  |  				 const struct sfp_eeprom_id *id, | ||||||
|  | @@ -553,14 +554,22 @@ static inline void sfp_upstream_stop(str | ||||||
|  |  { | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static inline struct sfp_bus *sfp_register_upstream_node( | ||||||
|  | -	struct fwnode_handle *fwnode, void *upstream, | ||||||
|  | -	const struct sfp_upstream_ops *ops) | ||||||
|  | +static inline void sfp_bus_put(struct sfp_bus *bus) | ||||||
|  | +{ | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static inline struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) | ||||||
|  |  { | ||||||
|  |  	return NULL; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static inline void sfp_unregister_upstream(struct sfp_bus *bus) | ||||||
|  | +static int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, | ||||||
|  | +				const struct sfp_upstream_ops *ops) | ||||||
|  | +{ | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static inline void sfp_bus_del_upstream(struct sfp_bus *bus) | ||||||
|  |  { | ||||||
|  |  } | ||||||
|  |  #endif | ||||||
| @@ -0,0 +1,27 @@ | |||||||
|  | From ea7bfd81921827d334c2a23bd11ef0e4e2abafd2 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Sat, 9 Nov 2019 08:13:50 +0000 | ||||||
|  | Subject: [PATCH 616/660] net: sfp: fix sfp_bus_put() kernel documentation | ||||||
|  |  | ||||||
|  | The kbuild test robot found a problem with htmldocs with the recent | ||||||
|  | change to the SFP interfaces.  Fix the kernel documentation for | ||||||
|  | sfp_bus_put() which was missing an '@' before the argument name | ||||||
|  | description. | ||||||
|  |  | ||||||
|  | Fixes: 727b3668b730 ("net: sfp: rework upstream interface") | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp-bus.c | 2 +- | ||||||
|  |  1 file changed, 1 insertion(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp-bus.c | ||||||
|  | +++ b/drivers/net/phy/sfp-bus.c | ||||||
|  | @@ -330,7 +330,7 @@ static void sfp_bus_release(struct kref | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  |   * sfp_bus_put() - put a reference on the &struct sfp_bus | ||||||
|  | - * bus: the &struct sfp_bus found via sfp_bus_find_fwnode() | ||||||
|  | + * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode() | ||||||
|  |   * | ||||||
|  |   * Put a reference on the &struct sfp_bus and free the underlying structure | ||||||
|  |   * if this was the last reference. | ||||||
| @@ -0,0 +1,27 @@ | |||||||
|  | From f76d84cd85f8bd3f083495f7ca723822cba8abc9 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Mon, 11 Nov 2019 10:23:35 +0000 | ||||||
|  | Subject: [PATCH 617/660] net: sfp: fix sfp_bus_add_upstream() warning | ||||||
|  |  | ||||||
|  | When building with SFP disabled, the stub for sfp_bus_add_upstream() | ||||||
|  | missed "inline".  Add it. | ||||||
|  |  | ||||||
|  | Fixes: 727b3668b730 ("net: sfp: rework upstream interface") | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  include/linux/sfp.h | 4 ++-- | ||||||
|  |  1 file changed, 2 insertions(+), 2 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/include/linux/sfp.h | ||||||
|  | +++ b/include/linux/sfp.h | ||||||
|  | @@ -563,8 +563,8 @@ static inline struct sfp_bus *sfp_bus_fi | ||||||
|  |  	return NULL; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, | ||||||
|  | -				const struct sfp_upstream_ops *ops) | ||||||
|  | +static inline int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, | ||||||
|  | +				       const struct sfp_upstream_ops *ops) | ||||||
|  |  { | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
| @@ -0,0 +1,124 @@ | |||||||
|  | From b9d6ed5cdb67533feda7f221eb06f2f9f1ff5047 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 11 Oct 2019 19:33:58 +0100 | ||||||
|  | Subject: [PATCH 618/660] net: sfp: move sfp sub-state machines into separate | ||||||
|  |  functions | ||||||
|  |  | ||||||
|  | Move the SFP sub-state machines out of the main state machine function, | ||||||
|  | in preparation for it doing a bit more with the device state.  By doing | ||||||
|  | so, we ensure that our debug after the main state machine is always | ||||||
|  | printed. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 74 +++++++++++++++++++++++++------------------ | ||||||
|  |  1 file changed, 43 insertions(+), 31 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -1479,19 +1479,34 @@ static void sfp_sm_mod_remove(struct sfp | ||||||
|  |  	dev_info(sfp->dev, "module removed\n"); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static void sfp_sm_event(struct sfp *sfp, unsigned int event) | ||||||
|  | +/* This state machine tracks the netdev up/down state */ | ||||||
|  | +static void sfp_sm_device(struct sfp *sfp, unsigned int event) | ||||||
|  |  { | ||||||
|  | -	mutex_lock(&sfp->sm_mutex); | ||||||
|  | +	switch (sfp->sm_dev_state) { | ||||||
|  | +	default: | ||||||
|  | +		if (event == SFP_E_DEV_UP) | ||||||
|  | +			sfp->sm_dev_state = SFP_DEV_UP; | ||||||
|  | +		break; | ||||||
|  |   | ||||||
|  | -	dev_dbg(sfp->dev, "SM: enter %s:%s:%s event %s\n", | ||||||
|  | -		mod_state_to_str(sfp->sm_mod_state), | ||||||
|  | -		dev_state_to_str(sfp->sm_dev_state), | ||||||
|  | -		sm_state_to_str(sfp->sm_state), | ||||||
|  | -		event_to_str(event)); | ||||||
|  | +	case SFP_DEV_UP: | ||||||
|  | +		if (event == SFP_E_DEV_DOWN) { | ||||||
|  | +			/* If the module has a PHY, avoid raising TX disable | ||||||
|  | +			 * as this resets the PHY. Otherwise, raise it to | ||||||
|  | +			 * turn the laser off. | ||||||
|  | +			 */ | ||||||
|  | +			if (!sfp->mod_phy) | ||||||
|  | +				sfp_module_tx_disable(sfp); | ||||||
|  | +			sfp->sm_dev_state = SFP_DEV_DOWN; | ||||||
|  | +		} | ||||||
|  | +		break; | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  |   | ||||||
|  | -	/* This state machine tracks the insert/remove state of | ||||||
|  | -	 * the module, and handles probing the on-board EEPROM. | ||||||
|  | -	 */ | ||||||
|  | +/* This state machine tracks the insert/remove state of | ||||||
|  | + * the module, and handles probing the on-board EEPROM. | ||||||
|  | + */ | ||||||
|  | +static void sfp_sm_module(struct sfp *sfp, unsigned int event) | ||||||
|  | +{ | ||||||
|  |  	switch (sfp->sm_mod_state) { | ||||||
|  |  	default: | ||||||
|  |  		if (event == SFP_E_INSERT && sfp->attached) { | ||||||
|  | @@ -1531,27 +1546,10 @@ static void sfp_sm_event(struct sfp *sfp | ||||||
|  |  		} | ||||||
|  |  		break; | ||||||
|  |  	} | ||||||
|  | +} | ||||||
|  |   | ||||||
|  | -	/* This state machine tracks the netdev up/down state */ | ||||||
|  | -	switch (sfp->sm_dev_state) { | ||||||
|  | -	default: | ||||||
|  | -		if (event == SFP_E_DEV_UP) | ||||||
|  | -			sfp->sm_dev_state = SFP_DEV_UP; | ||||||
|  | -		break; | ||||||
|  | - | ||||||
|  | -	case SFP_DEV_UP: | ||||||
|  | -		if (event == SFP_E_DEV_DOWN) { | ||||||
|  | -			/* If the module has a PHY, avoid raising TX disable | ||||||
|  | -			 * as this resets the PHY. Otherwise, raise it to | ||||||
|  | -			 * turn the laser off. | ||||||
|  | -			 */ | ||||||
|  | -			if (!sfp->mod_phy) | ||||||
|  | -				sfp_module_tx_disable(sfp); | ||||||
|  | -			sfp->sm_dev_state = SFP_DEV_DOWN; | ||||||
|  | -		} | ||||||
|  | -		break; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | +static void sfp_sm_main(struct sfp *sfp, unsigned int event) | ||||||
|  | +{ | ||||||
|  |  	/* Some events are global */ | ||||||
|  |  	if (sfp->sm_state != SFP_S_DOWN && | ||||||
|  |  	    (sfp->sm_mod_state != SFP_MOD_PRESENT || | ||||||
|  | @@ -1562,7 +1560,6 @@ static void sfp_sm_event(struct sfp *sfp | ||||||
|  |  		if (sfp->mod_phy) | ||||||
|  |  			sfp_sm_phy_detach(sfp); | ||||||
|  |  		sfp_sm_next(sfp, SFP_S_DOWN, 0); | ||||||
|  | -		mutex_unlock(&sfp->sm_mutex); | ||||||
|  |  		return; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | @@ -1617,6 +1614,21 @@ static void sfp_sm_event(struct sfp *sfp | ||||||
|  |  	case SFP_S_TX_DISABLE: | ||||||
|  |  		break; | ||||||
|  |  	} | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void sfp_sm_event(struct sfp *sfp, unsigned int event) | ||||||
|  | +{ | ||||||
|  | +	mutex_lock(&sfp->sm_mutex); | ||||||
|  | + | ||||||
|  | +	dev_dbg(sfp->dev, "SM: enter %s:%s:%s event %s\n", | ||||||
|  | +		mod_state_to_str(sfp->sm_mod_state), | ||||||
|  | +		dev_state_to_str(sfp->sm_dev_state), | ||||||
|  | +		sm_state_to_str(sfp->sm_state), | ||||||
|  | +		event_to_str(event)); | ||||||
|  | + | ||||||
|  | +	sfp_sm_module(sfp, event); | ||||||
|  | +	sfp_sm_device(sfp, event); | ||||||
|  | +	sfp_sm_main(sfp, event); | ||||||
|  |   | ||||||
|  |  	dev_dbg(sfp->dev, "SM: exit %s:%s:%s\n", | ||||||
|  |  		mod_state_to_str(sfp->sm_mod_state), | ||||||
| @@ -0,0 +1,41 @@ | |||||||
|  | From 7e89b737c97a9e7a81dd1584000bc136b92f12fd Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 11 Oct 2019 22:14:47 +0100 | ||||||
|  | Subject: [PATCH 619/660] net: sfp: move tx disable on device down to main | ||||||
|  |  state machine | ||||||
|  |  | ||||||
|  | Move the tx disable assertion on device down to the main state | ||||||
|  | machine. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 10 ++-------- | ||||||
|  |  1 file changed, 2 insertions(+), 8 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -1489,15 +1489,8 @@ static void sfp_sm_device(struct sfp *sf | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_DEV_UP: | ||||||
|  | -		if (event == SFP_E_DEV_DOWN) { | ||||||
|  | -			/* If the module has a PHY, avoid raising TX disable | ||||||
|  | -			 * as this resets the PHY. Otherwise, raise it to | ||||||
|  | -			 * turn the laser off. | ||||||
|  | -			 */ | ||||||
|  | -			if (!sfp->mod_phy) | ||||||
|  | -				sfp_module_tx_disable(sfp); | ||||||
|  | +		if (event == SFP_E_DEV_DOWN) | ||||||
|  |  			sfp->sm_dev_state = SFP_DEV_DOWN; | ||||||
|  | -		} | ||||||
|  |  		break; | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  | @@ -1559,6 +1552,7 @@ static void sfp_sm_main(struct sfp *sfp, | ||||||
|  |  			sfp_sm_link_down(sfp); | ||||||
|  |  		if (sfp->mod_phy) | ||||||
|  |  			sfp_sm_phy_detach(sfp); | ||||||
|  | +		sfp_module_tx_disable(sfp); | ||||||
|  |  		sfp_sm_next(sfp, SFP_S_DOWN, 0); | ||||||
|  |  		return; | ||||||
|  |  	} | ||||||
| @@ -0,0 +1,71 @@ | |||||||
|  | From f2a1ccfc4ad4f97c98c3cc18eb32992151ce089a Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 11 Oct 2019 22:27:21 +0100 | ||||||
|  | Subject: [PATCH 620/660] net: sfp: rename sfp_sm_ins_next() as | ||||||
|  |  sfp_sm_mod_next() | ||||||
|  |  | ||||||
|  | sfp_sm_ins_next() modifies the module state machine.  Change it's name | ||||||
|  | to reflect this. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 16 ++++++++-------- | ||||||
|  |  1 file changed, 8 insertions(+), 8 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -1180,7 +1180,7 @@ static void sfp_sm_next(struct sfp *sfp, | ||||||
|  |  	sfp_sm_set_timer(sfp, timeout); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static void sfp_sm_ins_next(struct sfp *sfp, unsigned int state, | ||||||
|  | +static void sfp_sm_mod_next(struct sfp *sfp, unsigned int state, | ||||||
|  |  			    unsigned int timeout) | ||||||
|  |  { | ||||||
|  |  	sfp->sm_mod_state = state; | ||||||
|  | @@ -1504,22 +1504,22 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  	default: | ||||||
|  |  		if (event == SFP_E_INSERT && sfp->attached) { | ||||||
|  |  			sfp_module_tx_disable(sfp); | ||||||
|  | -			sfp_sm_ins_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT); | ||||||
|  | +			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT); | ||||||
|  |  		} | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_MOD_PROBE: | ||||||
|  |  		if (event == SFP_E_REMOVE) { | ||||||
|  | -			sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  | +			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  |  		} else if (event == SFP_E_TIMEOUT) { | ||||||
|  |  			int val = sfp_sm_mod_probe(sfp); | ||||||
|  |   | ||||||
|  |  			if (val == 0) | ||||||
|  | -				sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0); | ||||||
|  | +				sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0); | ||||||
|  |  			else if (val > 0) | ||||||
|  | -				sfp_sm_ins_next(sfp, SFP_MOD_HPOWER, val); | ||||||
|  | +				sfp_sm_mod_next(sfp, SFP_MOD_HPOWER, val); | ||||||
|  |  			else if (val != -EAGAIN) | ||||||
|  | -				sfp_sm_ins_next(sfp, SFP_MOD_ERROR, 0); | ||||||
|  | +				sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); | ||||||
|  |  			else | ||||||
|  |  				sfp_sm_set_timer(sfp, T_PROBE_RETRY); | ||||||
|  |  		} | ||||||
|  | @@ -1527,7 +1527,7 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |   | ||||||
|  |  	case SFP_MOD_HPOWER: | ||||||
|  |  		if (event == SFP_E_TIMEOUT) { | ||||||
|  | -			sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0); | ||||||
|  | +			sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0); | ||||||
|  |  			break; | ||||||
|  |  		} | ||||||
|  |  		/* fallthrough */ | ||||||
|  | @@ -1535,7 +1535,7 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  	case SFP_MOD_ERROR: | ||||||
|  |  		if (event == SFP_E_REMOVE) { | ||||||
|  |  			sfp_sm_mod_remove(sfp); | ||||||
|  | -			sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  | +			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  |  		} | ||||||
|  |  		break; | ||||||
|  |  	} | ||||||
| @@ -0,0 +1,53 @@ | |||||||
|  | From d2591ea5520e2ee8fa557f96bb64c23cafac4b20 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 15 Oct 2019 10:33:13 +0100 | ||||||
|  | Subject: [PATCH 621/660] net: sfp: handle module remove outside state machine | ||||||
|  |  | ||||||
|  | Removing a module resets the module state machine back to its initial | ||||||
|  | state.  Rather than explicitly handling this in every state, handle it | ||||||
|  | early on outside of the state machine. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 16 +++++++++------- | ||||||
|  |  1 file changed, 9 insertions(+), 7 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -1500,6 +1500,14 @@ static void sfp_sm_device(struct sfp *sf | ||||||
|  |   */ | ||||||
|  |  static void sfp_sm_module(struct sfp *sfp, unsigned int event) | ||||||
|  |  { | ||||||
|  | +	/* Handle remove event globally, it resets this state machine */ | ||||||
|  | +	if (event == SFP_E_REMOVE) { | ||||||
|  | +		if (sfp->sm_mod_state > SFP_MOD_PROBE) | ||||||
|  | +			sfp_sm_mod_remove(sfp); | ||||||
|  | +		sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  | +		return; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  |  	switch (sfp->sm_mod_state) { | ||||||
|  |  	default: | ||||||
|  |  		if (event == SFP_E_INSERT && sfp->attached) { | ||||||
|  | @@ -1509,9 +1517,7 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_MOD_PROBE: | ||||||
|  | -		if (event == SFP_E_REMOVE) { | ||||||
|  | -			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  | -		} else if (event == SFP_E_TIMEOUT) { | ||||||
|  | +		if (event == SFP_E_TIMEOUT) { | ||||||
|  |  			int val = sfp_sm_mod_probe(sfp); | ||||||
|  |   | ||||||
|  |  			if (val == 0) | ||||||
|  | @@ -1533,10 +1539,6 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  		/* fallthrough */ | ||||||
|  |  	case SFP_MOD_PRESENT: | ||||||
|  |  	case SFP_MOD_ERROR: | ||||||
|  | -		if (event == SFP_E_REMOVE) { | ||||||
|  | -			sfp_sm_mod_remove(sfp); | ||||||
|  | -			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  | -		} | ||||||
|  |  		break; | ||||||
|  |  	} | ||||||
|  |  } | ||||||
| @@ -0,0 +1,51 @@ | |||||||
|  | From 615090acb3c0b41691f3a03522ea38350387c0e4 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 15 Oct 2019 10:54:15 +0100 | ||||||
|  | Subject: [PATCH 622/660] net: sfp: rename T_PROBE_WAIT to T_SERIAL | ||||||
|  |  | ||||||
|  | SFF-8472 rev 12.2 defines the time for the serial bus to become ready | ||||||
|  | using t_serial.  Use this as our identifier for this timeout to make | ||||||
|  | it clear what we are referring to. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 13 ++++++------- | ||||||
|  |  1 file changed, 6 insertions(+), 7 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -147,11 +147,10 @@ static const enum gpiod_flags gpio_flags | ||||||
|  |   * the same length on the PCB, which means it's possible for MOD DEF 0 to | ||||||
|  |   * connect before the I2C bus on MOD DEF 1/2. | ||||||
|  |   * | ||||||
|  | - * The SFP MSA specifies 300ms as t_init (the time taken for TX_FAULT to | ||||||
|  | - * be deasserted) but makes no mention of the earliest time before we can | ||||||
|  | - * access the I2C EEPROM.  However, Avago modules require 300ms. | ||||||
|  | + * The SFF-8472 specifies t_serial ("Time from power on until module is | ||||||
|  | + * ready for data transmission over the two wire serial bus.") as 300ms. | ||||||
|  |   */ | ||||||
|  | -#define T_PROBE_INIT	msecs_to_jiffies(300) | ||||||
|  | +#define T_SERIAL	msecs_to_jiffies(300) | ||||||
|  |  #define T_HPOWER_LEVEL	msecs_to_jiffies(300) | ||||||
|  |  #define T_PROBE_RETRY	msecs_to_jiffies(100) | ||||||
|  |   | ||||||
|  | @@ -1495,8 +1494,8 @@ static void sfp_sm_device(struct sfp *sf | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -/* This state machine tracks the insert/remove state of | ||||||
|  | - * the module, and handles probing the on-board EEPROM. | ||||||
|  | +/* This state machine tracks the insert/remove state of the module, probes | ||||||
|  | + * the on-board EEPROM, and sets up the power level. | ||||||
|  |   */ | ||||||
|  |  static void sfp_sm_module(struct sfp *sfp, unsigned int event) | ||||||
|  |  { | ||||||
|  | @@ -1512,7 +1511,7 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  	default: | ||||||
|  |  		if (event == SFP_E_INSERT && sfp->attached) { | ||||||
|  |  			sfp_module_tx_disable(sfp); | ||||||
|  | -			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT); | ||||||
|  | +			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_SERIAL); | ||||||
|  |  		} | ||||||
|  |  		break; | ||||||
|  |   | ||||||
| @@ -0,0 +1,115 @@ | |||||||
|  | From d4b8746219e8c0361e5ed6e440ab3a8a600d1f76 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 11 Oct 2019 17:24:40 +0100 | ||||||
|  | Subject: [PATCH 623/660] net: sfp: parse SFP power requirement earlier | ||||||
|  |  | ||||||
|  | Parse the SFP power requirement earlier, in preparation for moving the | ||||||
|  | power level setup code. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 42 +++++++++++++++++++++++++++++------------- | ||||||
|  |  1 file changed, 29 insertions(+), 13 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -196,6 +196,8 @@ struct sfp { | ||||||
|  |  	unsigned int sm_retries; | ||||||
|  |   | ||||||
|  |  	struct sfp_eeprom_id id; | ||||||
|  | +	unsigned int module_power_mW; | ||||||
|  | + | ||||||
|  |  #if IS_ENABLED(CONFIG_HWMON) | ||||||
|  |  	struct sfp_diag diag; | ||||||
|  |  	struct device *hwmon_dev; | ||||||
|  | @@ -1309,17 +1311,14 @@ static void sfp_sm_mod_init(struct sfp * | ||||||
|  |  		sfp_sm_probe_phy(sfp); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int sfp_sm_mod_hpower(struct sfp *sfp) | ||||||
|  | +static int sfp_module_parse_power(struct sfp *sfp) | ||||||
|  |  { | ||||||
|  | -	u32 power; | ||||||
|  | -	u8 val; | ||||||
|  | -	int err; | ||||||
|  | +	u32 power_mW = 1000; | ||||||
|  |   | ||||||
|  | -	power = 1000; | ||||||
|  |  	if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL)) | ||||||
|  | -		power = 1500; | ||||||
|  | +		power_mW = 1500; | ||||||
|  |  	if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL)) | ||||||
|  | -		power = 2000; | ||||||
|  | +		power_mW = 2000; | ||||||
|  |   | ||||||
|  |  	if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE && | ||||||
|  |  	    (sfp->id.ext.diagmon & (SFP_DIAGMON_DDM | SFP_DIAGMON_ADDRMODE)) != | ||||||
|  | @@ -1328,23 +1327,33 @@ static int sfp_sm_mod_hpower(struct sfp | ||||||
|  |  		 * or requires an address change sequence, so assume that | ||||||
|  |  		 * the module powers up in the indicated power mode. | ||||||
|  |  		 */ | ||||||
|  | -		if (power > sfp->max_power_mW) { | ||||||
|  | +		if (power_mW > sfp->max_power_mW) { | ||||||
|  |  			dev_err(sfp->dev, | ||||||
|  |  				"Host does not support %u.%uW modules\n", | ||||||
|  | -				power / 1000, (power / 100) % 10); | ||||||
|  | +				power_mW / 1000, (power_mW / 100) % 10); | ||||||
|  |  			return -EINVAL; | ||||||
|  |  		} | ||||||
|  |  		return 0; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	if (power > sfp->max_power_mW) { | ||||||
|  | +	if (power_mW > sfp->max_power_mW) { | ||||||
|  |  		dev_warn(sfp->dev, | ||||||
|  |  			 "Host does not support %u.%uW modules, module left in power mode 1\n", | ||||||
|  | -			 power / 1000, (power / 100) % 10); | ||||||
|  | +			 power_mW / 1000, (power_mW / 100) % 10); | ||||||
|  |  		return 0; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	if (power <= 1000) | ||||||
|  | +	sfp->module_power_mW = power_mW; | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int sfp_sm_mod_hpower(struct sfp *sfp) | ||||||
|  | +{ | ||||||
|  | +	u8 val; | ||||||
|  | +	int err; | ||||||
|  | + | ||||||
|  | +	if (sfp->module_power_mW <= 1000) | ||||||
|  |  		return 0; | ||||||
|  |   | ||||||
|  |  	err = sfp_read(sfp, true, SFP_EXT_STATUS, &val, sizeof(val)); | ||||||
|  | @@ -1364,7 +1373,8 @@ static int sfp_sm_mod_hpower(struct sfp | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	dev_info(sfp->dev, "Module switched to %u.%uW power level\n", | ||||||
|  | -		 power / 1000, (power / 100) % 10); | ||||||
|  | +		 sfp->module_power_mW / 1000, | ||||||
|  | +		 (sfp->module_power_mW / 100) % 10); | ||||||
|  |  	return T_HPOWER_LEVEL; | ||||||
|  |   | ||||||
|  |  err: | ||||||
|  | @@ -1451,6 +1461,11 @@ static int sfp_sm_mod_probe(struct sfp * | ||||||
|  |  		dev_warn(sfp->dev, | ||||||
|  |  			 "module address swap to access page 0xA2 is not supported.\n"); | ||||||
|  |   | ||||||
|  | +	/* Parse the module power requirement */ | ||||||
|  | +	ret = sfp_module_parse_power(sfp); | ||||||
|  | +	if (ret < 0) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  |  	ret = sfp_hwmon_insert(sfp); | ||||||
|  |  	if (ret < 0) | ||||||
|  |  		return ret; | ||||||
|  | @@ -1474,6 +1489,7 @@ static void sfp_sm_mod_remove(struct sfp | ||||||
|  |  	sfp_module_tx_disable(sfp); | ||||||
|  |   | ||||||
|  |  	memset(&sfp->id, 0, sizeof(sfp->id)); | ||||||
|  | +	sfp->module_power_mW = 0; | ||||||
|  |   | ||||||
|  |  	dev_info(sfp->dev, "module removed\n"); | ||||||
|  |  } | ||||||
| @@ -0,0 +1,65 @@ | |||||||
|  | From dca678b8838945572cf50584cb33a7199c1fd397 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Thu, 17 Oct 2019 00:24:18 +0100 | ||||||
|  | Subject: [PATCH 624/660] net: sfp: avoid power switch on address-change | ||||||
|  |  modules | ||||||
|  |  | ||||||
|  | If the module indicates that it requires an address change sequence to | ||||||
|  | switch between address 0x50 and 0x51, which we don't support, we can't | ||||||
|  | write to the register that controls the power mode to switch to high | ||||||
|  | power mode.  Warn the user that the module may not be functional in | ||||||
|  | this case, and don't try to change the power mode. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 31 ++++++++++++++++++++----------- | ||||||
|  |  1 file changed, 20 insertions(+), 11 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -1320,25 +1320,34 @@ static int sfp_module_parse_power(struct | ||||||
|  |  	if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL)) | ||||||
|  |  		power_mW = 2000; | ||||||
|  |   | ||||||
|  | -	if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE && | ||||||
|  | -	    (sfp->id.ext.diagmon & (SFP_DIAGMON_DDM | SFP_DIAGMON_ADDRMODE)) != | ||||||
|  | -	    SFP_DIAGMON_DDM) { | ||||||
|  | -		/* The module appears not to implement bus address 0xa2, | ||||||
|  | -		 * or requires an address change sequence, so assume that | ||||||
|  | -		 * the module powers up in the indicated power mode. | ||||||
|  | -		 */ | ||||||
|  | -		if (power_mW > sfp->max_power_mW) { | ||||||
|  | +	if (power_mW > sfp->max_power_mW) { | ||||||
|  | +		/* Module power specification exceeds the allowed maximum. */ | ||||||
|  | +		if (sfp->id.ext.sff8472_compliance == | ||||||
|  | +			SFP_SFF8472_COMPLIANCE_NONE && | ||||||
|  | +		    !(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) { | ||||||
|  | +			/* The module appears not to implement bus address | ||||||
|  | +			 * 0xa2, so assume that the module powers up in the | ||||||
|  | +			 * indicated mode. | ||||||
|  | +			 */ | ||||||
|  |  			dev_err(sfp->dev, | ||||||
|  |  				"Host does not support %u.%uW modules\n", | ||||||
|  |  				power_mW / 1000, (power_mW / 100) % 10); | ||||||
|  |  			return -EINVAL; | ||||||
|  | +		} else { | ||||||
|  | +			dev_warn(sfp->dev, | ||||||
|  | +				 "Host does not support %u.%uW modules, module left in power mode 1\n", | ||||||
|  | +				 power_mW / 1000, (power_mW / 100) % 10); | ||||||
|  | +			return 0; | ||||||
|  |  		} | ||||||
|  | -		return 0; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	if (power_mW > sfp->max_power_mW) { | ||||||
|  | +	/* If the module requires a higher power mode, but also requires | ||||||
|  | +	 * an address change sequence, warn the user that the module may | ||||||
|  | +	 * not be functional. | ||||||
|  | +	 */ | ||||||
|  | +	if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE && power_mW > 1000) { | ||||||
|  |  		dev_warn(sfp->dev, | ||||||
|  | -			 "Host does not support %u.%uW modules, module left in power mode 1\n", | ||||||
|  | +			 "Address Change Sequence not supported but module requies %u.%uW, module may not be functional\n", | ||||||
|  |  			 power_mW / 1000, (power_mW / 100) % 10); | ||||||
|  |  		return 0; | ||||||
|  |  	} | ||||||
| @@ -0,0 +1,52 @@ | |||||||
|  | From df5c4d93c5a59cba0f7479a4cd4e22b50726ce88 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Thu, 17 Oct 2019 11:12:42 +0100 | ||||||
|  | Subject: [PATCH 625/660] net: sfp: control TX_DISABLE and phy only from main | ||||||
|  |  state machine | ||||||
|  |  | ||||||
|  | We initialise TX_DISABLE when the sfp cage is probed, and then | ||||||
|  | maintain its state in the main state machine.  However, the module | ||||||
|  | state machine: | ||||||
|  | - negates it when detecting a newly inserted module when it's already | ||||||
|  |   guaranteed to be negated. | ||||||
|  | - negates it when the module is removed, but the main state machine | ||||||
|  |   will do this anyway. | ||||||
|  |  | ||||||
|  | Make TX_DISABLE entirely controlled by the main state machine. | ||||||
|  |  | ||||||
|  | The main state machine also probes the module for a PHY, and removes | ||||||
|  | the PHY when the the module is removed.  Hence, removing the PHY in | ||||||
|  | sfp_sm_module_remove() is also redundant, and is a left-over from | ||||||
|  | when we tried to probe for the PHY from the module state machine. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 9 +-------- | ||||||
|  |  1 file changed, 1 insertion(+), 8 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -1492,11 +1492,6 @@ static void sfp_sm_mod_remove(struct sfp | ||||||
|  |   | ||||||
|  |  	sfp_hwmon_remove(sfp); | ||||||
|  |   | ||||||
|  | -	if (sfp->mod_phy) | ||||||
|  | -		sfp_sm_phy_detach(sfp); | ||||||
|  | - | ||||||
|  | -	sfp_module_tx_disable(sfp); | ||||||
|  | - | ||||||
|  |  	memset(&sfp->id, 0, sizeof(sfp->id)); | ||||||
|  |  	sfp->module_power_mW = 0; | ||||||
|  |   | ||||||
|  | @@ -1534,10 +1529,8 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |   | ||||||
|  |  	switch (sfp->sm_mod_state) { | ||||||
|  |  	default: | ||||||
|  | -		if (event == SFP_E_INSERT && sfp->attached) { | ||||||
|  | -			sfp_module_tx_disable(sfp); | ||||||
|  | +		if (event == SFP_E_INSERT && sfp->attached) | ||||||
|  |  			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_SERIAL); | ||||||
|  | -		} | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_MOD_PROBE: | ||||||
| @@ -0,0 +1,53 @@ | |||||||
|  | From 5ed0bd49b2d3ac4439c2d7f44e5a82b7cf6f409a Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 18 Oct 2019 10:09:02 +0100 | ||||||
|  | Subject: [PATCH 626/660] net: sfp: split the PHY probe from sfp_sm_mod_init() | ||||||
|  |  | ||||||
|  | Move the PHY probe into a separate function, splitting it from | ||||||
|  | sfp_sm_mod_init().  This will allow us to eliminate the 50ms mdelay() | ||||||
|  | inside the state machine. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 21 +++++++++++++-------- | ||||||
|  |  1 file changed, 13 insertions(+), 8 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -1288,14 +1288,10 @@ static void sfp_sm_fault(struct sfp *sfp | ||||||
|  |  static void sfp_sm_mod_init(struct sfp *sfp) | ||||||
|  |  { | ||||||
|  |  	sfp_module_tx_enable(sfp); | ||||||
|  | +} | ||||||
|  |   | ||||||
|  | -	/* Wait t_init before indicating that the link is up, provided the | ||||||
|  | -	 * current state indicates no TX_FAULT.  If TX_FAULT clears before | ||||||
|  | -	 * this time, that's fine too. | ||||||
|  | -	 */ | ||||||
|  | -	sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES); | ||||||
|  | -	sfp->sm_retries = 5; | ||||||
|  | - | ||||||
|  | +static void sfp_sm_probe_for_phy(struct sfp *sfp) | ||||||
|  | +{ | ||||||
|  |  	/* Setting the serdes link mode is guesswork: there's no | ||||||
|  |  	 * field in the EEPROM which indicates what mode should | ||||||
|  |  	 * be used. | ||||||
|  | @@ -1580,8 +1576,17 @@ static void sfp_sm_main(struct sfp *sfp, | ||||||
|  |  	switch (sfp->sm_state) { | ||||||
|  |  	case SFP_S_DOWN: | ||||||
|  |  		if (sfp->sm_mod_state == SFP_MOD_PRESENT && | ||||||
|  | -		    sfp->sm_dev_state == SFP_DEV_UP) | ||||||
|  | +		    sfp->sm_dev_state == SFP_DEV_UP) { | ||||||
|  |  			sfp_sm_mod_init(sfp); | ||||||
|  | +			sfp_sm_probe_for_phy(sfp); | ||||||
|  | + | ||||||
|  | +			/* Wait t_init before indicating that the link is up, | ||||||
|  | +			 * provided the current state indicates no TX_FAULT. If | ||||||
|  | +			 * TX_FAULT clears before this time, that's fine too. | ||||||
|  | +			 */ | ||||||
|  | +			sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES); | ||||||
|  | +			sfp->sm_retries = 5; | ||||||
|  | +		} | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_S_INIT: | ||||||
| @@ -0,0 +1,130 @@ | |||||||
|  | From 0fe72afaa31f98ebd71bd6683fc47021105d0157 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 18 Oct 2019 10:21:46 +0100 | ||||||
|  | Subject: [PATCH 627/660] net: sfp: eliminate mdelay() from PHY probe | ||||||
|  |  | ||||||
|  | Rather than using mdelay() to wait before probing the PHY (which holds | ||||||
|  | several locks, including the rtnl lock), add an extra wait state to | ||||||
|  | the state machine to introduce the 50ms delay without holding any | ||||||
|  | locks. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 52 +++++++++++++++++++++++++++++++++---------- | ||||||
|  |  1 file changed, 40 insertions(+), 12 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -52,6 +52,7 @@ enum { | ||||||
|  |  	SFP_DEV_UP, | ||||||
|  |   | ||||||
|  |  	SFP_S_DOWN = 0, | ||||||
|  | +	SFP_S_WAIT, | ||||||
|  |  	SFP_S_INIT, | ||||||
|  |  	SFP_S_WAIT_LOS, | ||||||
|  |  	SFP_S_LINK_UP, | ||||||
|  | @@ -108,6 +109,7 @@ static const char *event_to_str(unsigned | ||||||
|  |   | ||||||
|  |  static const char * const sm_state_strings[] = { | ||||||
|  |  	[SFP_S_DOWN] = "down", | ||||||
|  | +	[SFP_S_WAIT] = "wait", | ||||||
|  |  	[SFP_S_INIT] = "init", | ||||||
|  |  	[SFP_S_WAIT_LOS] = "wait_los", | ||||||
|  |  	[SFP_S_LINK_UP] = "link_up", | ||||||
|  | @@ -139,6 +141,7 @@ static const enum gpiod_flags gpio_flags | ||||||
|  |  	GPIOD_ASIS, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +#define T_WAIT		msecs_to_jiffies(50) | ||||||
|  |  #define T_INIT_JIFFIES	msecs_to_jiffies(300) | ||||||
|  |  #define T_RESET_US	10 | ||||||
|  |  #define T_FAULT_RECOVER	msecs_to_jiffies(1000) | ||||||
|  | @@ -159,9 +162,6 @@ static const enum gpiod_flags gpio_flags | ||||||
|  |   */ | ||||||
|  |  #define SFP_PHY_ADDR	22 | ||||||
|  |   | ||||||
|  | -/* Give this long for the PHY to reset. */ | ||||||
|  | -#define T_PHY_RESET_MS	50 | ||||||
|  | - | ||||||
|  |  struct sff_data { | ||||||
|  |  	unsigned int gpios; | ||||||
|  |  	bool (*module_supported)(const struct sfp_eeprom_id *id); | ||||||
|  | @@ -1202,8 +1202,6 @@ static void sfp_sm_probe_phy(struct sfp | ||||||
|  |  	struct phy_device *phy; | ||||||
|  |  	int err; | ||||||
|  |   | ||||||
|  | -	msleep(T_PHY_RESET_MS); | ||||||
|  | - | ||||||
|  |  	phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR); | ||||||
|  |  	if (phy == ERR_PTR(-ENODEV)) { | ||||||
|  |  		dev_info(sfp->dev, "no PHY detected\n"); | ||||||
|  | @@ -1558,6 +1556,8 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |   | ||||||
|  |  static void sfp_sm_main(struct sfp *sfp, unsigned int event) | ||||||
|  |  { | ||||||
|  | +	unsigned long timeout; | ||||||
|  | + | ||||||
|  |  	/* Some events are global */ | ||||||
|  |  	if (sfp->sm_state != SFP_S_DOWN && | ||||||
|  |  	    (sfp->sm_mod_state != SFP_MOD_PRESENT || | ||||||
|  | @@ -1575,17 +1575,45 @@ static void sfp_sm_main(struct sfp *sfp, | ||||||
|  |  	/* The main state machine */ | ||||||
|  |  	switch (sfp->sm_state) { | ||||||
|  |  	case SFP_S_DOWN: | ||||||
|  | -		if (sfp->sm_mod_state == SFP_MOD_PRESENT && | ||||||
|  | -		    sfp->sm_dev_state == SFP_DEV_UP) { | ||||||
|  | -			sfp_sm_mod_init(sfp); | ||||||
|  | -			sfp_sm_probe_for_phy(sfp); | ||||||
|  | +		if (sfp->sm_mod_state != SFP_MOD_PRESENT || | ||||||
|  | +		    sfp->sm_dev_state != SFP_DEV_UP) | ||||||
|  | +			break; | ||||||
|  | + | ||||||
|  | +		sfp_sm_mod_init(sfp); | ||||||
|  | + | ||||||
|  | +		/* Initialise the fault clearance retries */ | ||||||
|  | +		sfp->sm_retries = 5; | ||||||
|  | + | ||||||
|  | +		/* We need to check the TX_FAULT state, which is not defined | ||||||
|  | +		 * while TX_DISABLE is asserted. The earliest we want to do | ||||||
|  | +		 * anything (such as probe for a PHY) is 50ms. | ||||||
|  | +		 */ | ||||||
|  | +		sfp_sm_next(sfp, SFP_S_WAIT, T_WAIT); | ||||||
|  | +		break; | ||||||
|  | + | ||||||
|  | +	case SFP_S_WAIT: | ||||||
|  | +		if (event != SFP_E_TIMEOUT) | ||||||
|  | +			break; | ||||||
|  | + | ||||||
|  | +		sfp_sm_probe_for_phy(sfp); | ||||||
|  |   | ||||||
|  | +		if (sfp->state & SFP_F_TX_FAULT) { | ||||||
|  |  			/* Wait t_init before indicating that the link is up, | ||||||
|  |  			 * provided the current state indicates no TX_FAULT. If | ||||||
|  |  			 * TX_FAULT clears before this time, that's fine too. | ||||||
|  |  			 */ | ||||||
|  | -			sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES); | ||||||
|  | -			sfp->sm_retries = 5; | ||||||
|  | +			timeout = T_INIT_JIFFIES; | ||||||
|  | +			if (timeout > T_WAIT) | ||||||
|  | +				timeout -= T_WAIT; | ||||||
|  | +			else | ||||||
|  | +				timeout = 1; | ||||||
|  | + | ||||||
|  | +			sfp_sm_next(sfp, SFP_S_INIT, timeout); | ||||||
|  | +		} else { | ||||||
|  | +			/* TX_FAULT is not asserted, assume the module has | ||||||
|  | +			 * finished initialising. | ||||||
|  | +			 */ | ||||||
|  | +			goto init_done; | ||||||
|  |  		} | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  | @@ -1593,7 +1621,7 @@ static void sfp_sm_main(struct sfp *sfp, | ||||||
|  |  		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) | ||||||
|  |  			sfp_sm_fault(sfp, true); | ||||||
|  |  		else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) | ||||||
|  | -			sfp_sm_link_check_los(sfp); | ||||||
|  | +	init_done:	sfp_sm_link_check_los(sfp); | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_S_WAIT_LOS: | ||||||
| @@ -0,0 +1,69 @@ | |||||||
|  | From 2aa424ee7fbe43e2cd24e28c2f6388c4e1796bd2 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 18 Oct 2019 09:58:33 +0100 | ||||||
|  | Subject: [PATCH 628/660] net: sfp: allow fault processing to transition to | ||||||
|  |  other states | ||||||
|  |  | ||||||
|  | Add the next state to sfp_sm_fault() so that it can branch to other | ||||||
|  | states. This will be necessary to improve the initialisation path. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 12 ++++++------ | ||||||
|  |  1 file changed, 6 insertions(+), 6 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -1269,7 +1269,7 @@ static bool sfp_los_event_inactive(struc | ||||||
|  |  		event == SFP_E_LOS_LOW); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static void sfp_sm_fault(struct sfp *sfp, bool warn) | ||||||
|  | +static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn) | ||||||
|  |  { | ||||||
|  |  	if (sfp->sm_retries && !--sfp->sm_retries) { | ||||||
|  |  		dev_err(sfp->dev, | ||||||
|  | @@ -1279,7 +1279,7 @@ static void sfp_sm_fault(struct sfp *sfp | ||||||
|  |  		if (warn) | ||||||
|  |  			dev_err(sfp->dev, "module transmit fault indicated\n"); | ||||||
|  |   | ||||||
|  | -		sfp_sm_next(sfp, SFP_S_TX_FAULT, T_FAULT_RECOVER); | ||||||
|  | +		sfp_sm_next(sfp, next_state, T_FAULT_RECOVER); | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | @@ -1619,14 +1619,14 @@ static void sfp_sm_main(struct sfp *sfp, | ||||||
|  |   | ||||||
|  |  	case SFP_S_INIT: | ||||||
|  |  		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) | ||||||
|  | -			sfp_sm_fault(sfp, true); | ||||||
|  | +			sfp_sm_fault(sfp, SFP_S_TX_FAULT, true); | ||||||
|  |  		else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) | ||||||
|  |  	init_done:	sfp_sm_link_check_los(sfp); | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_S_WAIT_LOS: | ||||||
|  |  		if (event == SFP_E_TX_FAULT) | ||||||
|  | -			sfp_sm_fault(sfp, true); | ||||||
|  | +			sfp_sm_fault(sfp, SFP_S_TX_FAULT, true); | ||||||
|  |  		else if (sfp_los_event_inactive(sfp, event)) | ||||||
|  |  			sfp_sm_link_up(sfp); | ||||||
|  |  		break; | ||||||
|  | @@ -1634,7 +1634,7 @@ static void sfp_sm_main(struct sfp *sfp, | ||||||
|  |  	case SFP_S_LINK_UP: | ||||||
|  |  		if (event == SFP_E_TX_FAULT) { | ||||||
|  |  			sfp_sm_link_down(sfp); | ||||||
|  | -			sfp_sm_fault(sfp, true); | ||||||
|  | +			sfp_sm_fault(sfp, SFP_S_TX_FAULT, true); | ||||||
|  |  		} else if (sfp_los_event_active(sfp, event)) { | ||||||
|  |  			sfp_sm_link_down(sfp); | ||||||
|  |  			sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0); | ||||||
|  | @@ -1650,7 +1650,7 @@ static void sfp_sm_main(struct sfp *sfp, | ||||||
|  |   | ||||||
|  |  	case SFP_S_REINIT: | ||||||
|  |  		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { | ||||||
|  | -			sfp_sm_fault(sfp, false); | ||||||
|  | +			sfp_sm_fault(sfp, SFP_S_TX_FAULT, false); | ||||||
|  |  		} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { | ||||||
|  |  			dev_info(sfp->dev, "module transmit fault recovered\n"); | ||||||
|  |  			sfp_sm_link_check_los(sfp); | ||||||
| @@ -0,0 +1,80 @@ | |||||||
|  | From 38b62a12231be4b86fc5ca5477579d29831c02a5 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 18 Oct 2019 10:31:07 +0100 | ||||||
|  | Subject: [PATCH 629/660] net: sfp: ensure TX_FAULT has deasserted before | ||||||
|  |  probing the PHY | ||||||
|  |  | ||||||
|  | TX_FAULT should be deasserted to indicate that the module has completed | ||||||
|  | its initialisation.  This may include the on-board PHY, so wait until | ||||||
|  | the module has deasserted TX_FAULT before probing the PHY. | ||||||
|  |  | ||||||
|  | This means that we need an extra state to handle a TX_FAULT that | ||||||
|  | remains set for longer than t_init, since using the existing handling | ||||||
|  | state would bypass the PHY probe. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 31 +++++++++++++++++++++++++------ | ||||||
|  |  1 file changed, 25 insertions(+), 6 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -54,6 +54,7 @@ enum { | ||||||
|  |  	SFP_S_DOWN = 0, | ||||||
|  |  	SFP_S_WAIT, | ||||||
|  |  	SFP_S_INIT, | ||||||
|  | +	SFP_S_INIT_TX_FAULT, | ||||||
|  |  	SFP_S_WAIT_LOS, | ||||||
|  |  	SFP_S_LINK_UP, | ||||||
|  |  	SFP_S_TX_FAULT, | ||||||
|  | @@ -111,6 +112,7 @@ static const char * const sm_state_strin | ||||||
|  |  	[SFP_S_DOWN] = "down", | ||||||
|  |  	[SFP_S_WAIT] = "wait", | ||||||
|  |  	[SFP_S_INIT] = "init", | ||||||
|  | +	[SFP_S_INIT_TX_FAULT] = "init_tx_fault", | ||||||
|  |  	[SFP_S_WAIT_LOS] = "wait_los", | ||||||
|  |  	[SFP_S_LINK_UP] = "link_up", | ||||||
|  |  	[SFP_S_TX_FAULT] = "tx_fault", | ||||||
|  | @@ -1595,8 +1597,6 @@ static void sfp_sm_main(struct sfp *sfp, | ||||||
|  |  		if (event != SFP_E_TIMEOUT) | ||||||
|  |  			break; | ||||||
|  |   | ||||||
|  | -		sfp_sm_probe_for_phy(sfp); | ||||||
|  | - | ||||||
|  |  		if (sfp->state & SFP_F_TX_FAULT) { | ||||||
|  |  			/* Wait t_init before indicating that the link is up, | ||||||
|  |  			 * provided the current state indicates no TX_FAULT. If | ||||||
|  | @@ -1618,10 +1618,29 @@ static void sfp_sm_main(struct sfp *sfp, | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_S_INIT: | ||||||
|  | -		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) | ||||||
|  | -			sfp_sm_fault(sfp, SFP_S_TX_FAULT, true); | ||||||
|  | -		else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) | ||||||
|  | -	init_done:	sfp_sm_link_check_los(sfp); | ||||||
|  | +		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { | ||||||
|  | +			/* TX_FAULT is still asserted after t_init, so assume | ||||||
|  | +			 * there is a fault. | ||||||
|  | +			 */ | ||||||
|  | +			sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT, | ||||||
|  | +				     sfp->sm_retries == 5); | ||||||
|  | +		} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { | ||||||
|  | +	init_done:	/* TX_FAULT deasserted or we timed out with TX_FAULT | ||||||
|  | +			 * clear.  Probe for the PHY and check the LOS state. | ||||||
|  | +			 */ | ||||||
|  | +			sfp_sm_probe_for_phy(sfp); | ||||||
|  | +			sfp_sm_link_check_los(sfp); | ||||||
|  | + | ||||||
|  | +			/* Reset the fault retry count */ | ||||||
|  | +			sfp->sm_retries = 5; | ||||||
|  | +		} | ||||||
|  | +		break; | ||||||
|  | + | ||||||
|  | +	case SFP_S_INIT_TX_FAULT: | ||||||
|  | +		if (event == SFP_E_TIMEOUT) { | ||||||
|  | +			sfp_module_tx_fault_reset(sfp); | ||||||
|  | +			sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES); | ||||||
|  | +		} | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_S_WAIT_LOS: | ||||||
| @@ -0,0 +1,153 @@ | |||||||
|  | From ec6036a58f979c66bbd5cd9d0d1c783a98c2c644 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 5 Nov 2019 12:57:40 +0000 | ||||||
|  | Subject: [PATCH 630/660] net: sfp: track upstream's attachment state in state | ||||||
|  |  machine | ||||||
|  |  | ||||||
|  | Track the upstream's attachment state in the state machine rather than | ||||||
|  | maintaining a boolean, which ensures that we have a strict order of | ||||||
|  | ATTACH followed by an UP event - we can never believe that a newly | ||||||
|  | attached upstream will be anything but down. | ||||||
|  |  | ||||||
|  | Rearrange the order of state machines so we run the module state | ||||||
|  | machine after the upstream device's state machine, so the module state | ||||||
|  | machine can check the current state of the device and take action to | ||||||
|  | e.g. reset back to empty state when the upstream is detached. | ||||||
|  |  | ||||||
|  | This is to allow the module detection to run independently of the | ||||||
|  | network device becoming available. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 42 +++++++++++++++++++++++++++++------------- | ||||||
|  |  1 file changed, 29 insertions(+), 13 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -34,6 +34,8 @@ enum { | ||||||
|  |   | ||||||
|  |  	SFP_E_INSERT = 0, | ||||||
|  |  	SFP_E_REMOVE, | ||||||
|  | +	SFP_E_DEV_ATTACH, | ||||||
|  | +	SFP_E_DEV_DETACH, | ||||||
|  |  	SFP_E_DEV_DOWN, | ||||||
|  |  	SFP_E_DEV_UP, | ||||||
|  |  	SFP_E_TX_FAULT, | ||||||
|  | @@ -48,7 +50,8 @@ enum { | ||||||
|  |  	SFP_MOD_PRESENT, | ||||||
|  |  	SFP_MOD_ERROR, | ||||||
|  |   | ||||||
|  | -	SFP_DEV_DOWN = 0, | ||||||
|  | +	SFP_DEV_DETACHED = 0, | ||||||
|  | +	SFP_DEV_DOWN, | ||||||
|  |  	SFP_DEV_UP, | ||||||
|  |   | ||||||
|  |  	SFP_S_DOWN = 0, | ||||||
|  | @@ -78,6 +81,7 @@ static const char *mod_state_to_str(unsi | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static const char * const dev_state_strings[] = { | ||||||
|  | +	[SFP_DEV_DETACHED] = "detached", | ||||||
|  |  	[SFP_DEV_DOWN] = "down", | ||||||
|  |  	[SFP_DEV_UP] = "up", | ||||||
|  |  }; | ||||||
|  | @@ -92,6 +96,8 @@ static const char *dev_state_to_str(unsi | ||||||
|  |  static const char * const event_strings[] = { | ||||||
|  |  	[SFP_E_INSERT] = "insert", | ||||||
|  |  	[SFP_E_REMOVE] = "remove", | ||||||
|  | +	[SFP_E_DEV_ATTACH] = "dev_attach", | ||||||
|  | +	[SFP_E_DEV_DETACH] = "dev_detach", | ||||||
|  |  	[SFP_E_DEV_DOWN] = "dev_down", | ||||||
|  |  	[SFP_E_DEV_UP] = "dev_up", | ||||||
|  |  	[SFP_E_TX_FAULT] = "tx_fault", | ||||||
|  | @@ -186,7 +192,6 @@ struct sfp { | ||||||
|  |  	struct gpio_desc *gpio[GPIO_MAX]; | ||||||
|  |  	int gpio_irq[GPIO_MAX]; | ||||||
|  |   | ||||||
|  | -	bool attached; | ||||||
|  |  	struct mutex st_mutex;			/* Protects state */ | ||||||
|  |  	unsigned int state; | ||||||
|  |  	struct delayed_work poll; | ||||||
|  | @@ -1494,17 +1499,26 @@ static void sfp_sm_mod_remove(struct sfp | ||||||
|  |  	dev_info(sfp->dev, "module removed\n"); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -/* This state machine tracks the netdev up/down state */ | ||||||
|  | +/* This state machine tracks the upstream's state */ | ||||||
|  |  static void sfp_sm_device(struct sfp *sfp, unsigned int event) | ||||||
|  |  { | ||||||
|  |  	switch (sfp->sm_dev_state) { | ||||||
|  |  	default: | ||||||
|  | -		if (event == SFP_E_DEV_UP) | ||||||
|  | +		if (event == SFP_E_DEV_ATTACH) | ||||||
|  | +			sfp->sm_dev_state = SFP_DEV_DOWN; | ||||||
|  | +		break; | ||||||
|  | + | ||||||
|  | +	case SFP_DEV_DOWN: | ||||||
|  | +		if (event == SFP_E_DEV_DETACH) | ||||||
|  | +			sfp->sm_dev_state = SFP_DEV_DETACHED; | ||||||
|  | +		else if (event == SFP_E_DEV_UP) | ||||||
|  |  			sfp->sm_dev_state = SFP_DEV_UP; | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_DEV_UP: | ||||||
|  | -		if (event == SFP_E_DEV_DOWN) | ||||||
|  | +		if (event == SFP_E_DEV_DETACH) | ||||||
|  | +			sfp->sm_dev_state = SFP_DEV_DETACHED; | ||||||
|  | +		else if (event == SFP_E_DEV_DOWN) | ||||||
|  |  			sfp->sm_dev_state = SFP_DEV_DOWN; | ||||||
|  |  		break; | ||||||
|  |  	} | ||||||
|  | @@ -1515,17 +1529,20 @@ static void sfp_sm_device(struct sfp *sf | ||||||
|  |   */ | ||||||
|  |  static void sfp_sm_module(struct sfp *sfp, unsigned int event) | ||||||
|  |  { | ||||||
|  | -	/* Handle remove event globally, it resets this state machine */ | ||||||
|  | -	if (event == SFP_E_REMOVE) { | ||||||
|  | +	/* Handle remove event globally, it resets this state machine. | ||||||
|  | +	 * Also deal with upstream detachment. | ||||||
|  | +	 */ | ||||||
|  | +	if (event == SFP_E_REMOVE || sfp->sm_dev_state < SFP_DEV_DOWN) { | ||||||
|  |  		if (sfp->sm_mod_state > SFP_MOD_PROBE) | ||||||
|  |  			sfp_sm_mod_remove(sfp); | ||||||
|  | -		sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  | +		if (sfp->sm_mod_state != SFP_MOD_EMPTY) | ||||||
|  | +			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  |  		return; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	switch (sfp->sm_mod_state) { | ||||||
|  |  	default: | ||||||
|  | -		if (event == SFP_E_INSERT && sfp->attached) | ||||||
|  | +		if (event == SFP_E_INSERT) | ||||||
|  |  			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_SERIAL); | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  | @@ -1691,8 +1708,8 @@ static void sfp_sm_event(struct sfp *sfp | ||||||
|  |  		sm_state_to_str(sfp->sm_state), | ||||||
|  |  		event_to_str(event)); | ||||||
|  |   | ||||||
|  | -	sfp_sm_module(sfp, event); | ||||||
|  |  	sfp_sm_device(sfp, event); | ||||||
|  | +	sfp_sm_module(sfp, event); | ||||||
|  |  	sfp_sm_main(sfp, event); | ||||||
|  |   | ||||||
|  |  	dev_dbg(sfp->dev, "SM: exit %s:%s:%s\n", | ||||||
|  | @@ -1705,15 +1722,14 @@ static void sfp_sm_event(struct sfp *sfp | ||||||
|  |   | ||||||
|  |  static void sfp_attach(struct sfp *sfp) | ||||||
|  |  { | ||||||
|  | -	sfp->attached = true; | ||||||
|  | +	sfp_sm_event(sfp, SFP_E_DEV_ATTACH); | ||||||
|  |  	if (sfp->state & SFP_F_PRESENT) | ||||||
|  |  		sfp_sm_event(sfp, SFP_E_INSERT); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void sfp_detach(struct sfp *sfp) | ||||||
|  |  { | ||||||
|  | -	sfp->attached = false; | ||||||
|  | -	sfp_sm_event(sfp, SFP_E_REMOVE); | ||||||
|  | +	sfp_sm_event(sfp, SFP_E_DEV_DETACH); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void sfp_start(struct sfp *sfp) | ||||||
| @@ -0,0 +1,184 @@ | |||||||
|  | From fdff863a4ce3677907f64396e34c45025abb6600 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 5 Nov 2019 12:59:36 +0000 | ||||||
|  | Subject: [PATCH 631/660] net: sfp: split power mode switching from probe | ||||||
|  |  | ||||||
|  | Switch the power mode switching from the probe, so that we don't | ||||||
|  | repeatedly re-probe the SFP device if there is a problem accessing | ||||||
|  | the registers at I2C address 0x51. | ||||||
|  |  | ||||||
|  | In splitting this out, we can also fix a bug where we leave the module | ||||||
|  | in high-power mode when the upstream device is detached but the module | ||||||
|  | is still inserted. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 101 ++++++++++++++++++++++++++---------------- | ||||||
|  |  1 file changed, 64 insertions(+), 37 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -47,6 +47,7 @@ enum { | ||||||
|  |  	SFP_MOD_EMPTY = 0, | ||||||
|  |  	SFP_MOD_PROBE, | ||||||
|  |  	SFP_MOD_HPOWER, | ||||||
|  | +	SFP_MOD_WAITPWR, | ||||||
|  |  	SFP_MOD_PRESENT, | ||||||
|  |  	SFP_MOD_ERROR, | ||||||
|  |   | ||||||
|  | @@ -69,6 +70,7 @@ static const char  * const mod_state_str | ||||||
|  |  	[SFP_MOD_EMPTY] = "empty", | ||||||
|  |  	[SFP_MOD_PROBE] = "probe", | ||||||
|  |  	[SFP_MOD_HPOWER] = "hpower", | ||||||
|  | +	[SFP_MOD_WAITPWR] = "waitpwr", | ||||||
|  |  	[SFP_MOD_PRESENT] = "present", | ||||||
|  |  	[SFP_MOD_ERROR] = "error", | ||||||
|  |  }; | ||||||
|  | @@ -1358,37 +1360,34 @@ static int sfp_module_parse_power(struct | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int sfp_sm_mod_hpower(struct sfp *sfp) | ||||||
|  | +static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable) | ||||||
|  |  { | ||||||
|  |  	u8 val; | ||||||
|  |  	int err; | ||||||
|  |   | ||||||
|  | -	if (sfp->module_power_mW <= 1000) | ||||||
|  | -		return 0; | ||||||
|  | - | ||||||
|  |  	err = sfp_read(sfp, true, SFP_EXT_STATUS, &val, sizeof(val)); | ||||||
|  |  	if (err != sizeof(val)) { | ||||||
|  |  		dev_err(sfp->dev, "Failed to read EEPROM: %d\n", err); | ||||||
|  | -		err = -EAGAIN; | ||||||
|  | -		goto err; | ||||||
|  | +		return -EAGAIN; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	val |= BIT(0); | ||||||
|  | +	if (enable) | ||||||
|  | +		val |= BIT(0); | ||||||
|  | +	else | ||||||
|  | +		val &= ~BIT(0); | ||||||
|  |   | ||||||
|  |  	err = sfp_write(sfp, true, SFP_EXT_STATUS, &val, sizeof(val)); | ||||||
|  |  	if (err != sizeof(val)) { | ||||||
|  |  		dev_err(sfp->dev, "Failed to write EEPROM: %d\n", err); | ||||||
|  | -		err = -EAGAIN; | ||||||
|  | -		goto err; | ||||||
|  | +		return -EAGAIN; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	dev_info(sfp->dev, "Module switched to %u.%uW power level\n", | ||||||
|  | -		 sfp->module_power_mW / 1000, | ||||||
|  | -		 (sfp->module_power_mW / 100) % 10); | ||||||
|  | -	return T_HPOWER_LEVEL; | ||||||
|  | +	if (enable) | ||||||
|  | +		dev_info(sfp->dev, "Module switched to %u.%uW power level\n", | ||||||
|  | +			 sfp->module_power_mW / 1000, | ||||||
|  | +			 (sfp->module_power_mW / 100) % 10); | ||||||
|  |   | ||||||
|  | -err: | ||||||
|  | -	return err; | ||||||
|  | +	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int sfp_sm_mod_probe(struct sfp *sfp) | ||||||
|  | @@ -1484,7 +1483,7 @@ static int sfp_sm_mod_probe(struct sfp * | ||||||
|  |  	if (ret < 0) | ||||||
|  |  		return ret; | ||||||
|  |   | ||||||
|  | -	return sfp_sm_mod_hpower(sfp); | ||||||
|  | +	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void sfp_sm_mod_remove(struct sfp *sfp) | ||||||
|  | @@ -1529,13 +1528,22 @@ static void sfp_sm_device(struct sfp *sf | ||||||
|  |   */ | ||||||
|  |  static void sfp_sm_module(struct sfp *sfp, unsigned int event) | ||||||
|  |  { | ||||||
|  | -	/* Handle remove event globally, it resets this state machine. | ||||||
|  | -	 * Also deal with upstream detachment. | ||||||
|  | -	 */ | ||||||
|  | -	if (event == SFP_E_REMOVE || sfp->sm_dev_state < SFP_DEV_DOWN) { | ||||||
|  | +	int err; | ||||||
|  | + | ||||||
|  | +	/* Handle remove event globally, it resets this state machine */ | ||||||
|  | +	if (event == SFP_E_REMOVE) { | ||||||
|  |  		if (sfp->sm_mod_state > SFP_MOD_PROBE) | ||||||
|  |  			sfp_sm_mod_remove(sfp); | ||||||
|  | -		if (sfp->sm_mod_state != SFP_MOD_EMPTY) | ||||||
|  | +		sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  | +		return; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	/* Handle device detach globally */ | ||||||
|  | +	if (sfp->sm_dev_state < SFP_DEV_DOWN) { | ||||||
|  | +		if (sfp->module_power_mW > 1000 && | ||||||
|  | +		    sfp->sm_mod_state > SFP_MOD_HPOWER) | ||||||
|  | +			sfp_sm_mod_hpower(sfp, false); | ||||||
|  | +		if (sfp->sm_mod_state > SFP_MOD_EMPTY) | ||||||
|  |  			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  |  		return; | ||||||
|  |  	} | ||||||
|  | @@ -1547,26 +1555,45 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_MOD_PROBE: | ||||||
|  | -		if (event == SFP_E_TIMEOUT) { | ||||||
|  | -			int val = sfp_sm_mod_probe(sfp); | ||||||
|  | +		if (event != SFP_E_TIMEOUT) | ||||||
|  | +			break; | ||||||
|  |   | ||||||
|  | -			if (val == 0) | ||||||
|  | -				sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0); | ||||||
|  | -			else if (val > 0) | ||||||
|  | -				sfp_sm_mod_next(sfp, SFP_MOD_HPOWER, val); | ||||||
|  | -			else if (val != -EAGAIN) | ||||||
|  | -				sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); | ||||||
|  | -			else | ||||||
|  | -				sfp_sm_set_timer(sfp, T_PROBE_RETRY); | ||||||
|  | +		err = sfp_sm_mod_probe(sfp); | ||||||
|  | +		if (err == -EAGAIN) { | ||||||
|  | +			sfp_sm_set_timer(sfp, T_PROBE_RETRY); | ||||||
|  | +			break; | ||||||
|  |  		} | ||||||
|  | -		break; | ||||||
|  | +		if (err < 0) { | ||||||
|  | +			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); | ||||||
|  | +			break; | ||||||
|  | +		} | ||||||
|  | + | ||||||
|  | +		/* If this is a power level 1 module, we are done */ | ||||||
|  | +		if (sfp->module_power_mW <= 1000) | ||||||
|  | +			goto insert; | ||||||
|  |   | ||||||
|  | +		sfp_sm_mod_next(sfp, SFP_MOD_HPOWER, 0); | ||||||
|  | +		/* fall through */ | ||||||
|  |  	case SFP_MOD_HPOWER: | ||||||
|  | -		if (event == SFP_E_TIMEOUT) { | ||||||
|  | -			sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0); | ||||||
|  | +		/* Enable high power mode */ | ||||||
|  | +		err = sfp_sm_mod_hpower(sfp, true); | ||||||
|  | +		if (err == 0) | ||||||
|  | +			sfp_sm_mod_next(sfp, SFP_MOD_WAITPWR, T_HPOWER_LEVEL); | ||||||
|  | +		else if (err != -EAGAIN) | ||||||
|  | +			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); | ||||||
|  | +		else | ||||||
|  | +			sfp_sm_set_timer(sfp, T_PROBE_RETRY); | ||||||
|  | +		break; | ||||||
|  | + | ||||||
|  | +	case SFP_MOD_WAITPWR: | ||||||
|  | +		/* Wait for T_HPOWER_LEVEL to time out */ | ||||||
|  | +		if (event != SFP_E_TIMEOUT) | ||||||
|  |  			break; | ||||||
|  | -		} | ||||||
|  | -		/* fallthrough */ | ||||||
|  | + | ||||||
|  | +	insert: | ||||||
|  | +		sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0); | ||||||
|  | +		break; | ||||||
|  | + | ||||||
|  |  	case SFP_MOD_PRESENT: | ||||||
|  |  	case SFP_MOD_ERROR: | ||||||
|  |  		break; | ||||||
| @@ -0,0 +1,159 @@ | |||||||
|  | From 57cbf7453551db1df619b79410d79fc418d862d5 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 5 Nov 2019 13:00:45 +0000 | ||||||
|  | Subject: [PATCH 632/660] net: sfp: move module insert reporting out of probe | ||||||
|  |  | ||||||
|  | Move the module insertion reporting out of the probe handling, but | ||||||
|  | after we have detected that the upstream has attached (since that is | ||||||
|  | whom we are reporting insertion to.) | ||||||
|  |  | ||||||
|  | Only report module removal if we had previously reported a module | ||||||
|  | insertion. | ||||||
|  |  | ||||||
|  | This gives cleaner semantics, and means we can probe the module before | ||||||
|  | we have an upstream attached. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 58 +++++++++++++++++++++++++++++-------------- | ||||||
|  |  1 file changed, 40 insertions(+), 18 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -45,11 +45,12 @@ enum { | ||||||
|  |  	SFP_E_TIMEOUT, | ||||||
|  |   | ||||||
|  |  	SFP_MOD_EMPTY = 0, | ||||||
|  | +	SFP_MOD_ERROR, | ||||||
|  |  	SFP_MOD_PROBE, | ||||||
|  | +	SFP_MOD_WAITDEV, | ||||||
|  |  	SFP_MOD_HPOWER, | ||||||
|  |  	SFP_MOD_WAITPWR, | ||||||
|  |  	SFP_MOD_PRESENT, | ||||||
|  | -	SFP_MOD_ERROR, | ||||||
|  |   | ||||||
|  |  	SFP_DEV_DETACHED = 0, | ||||||
|  |  	SFP_DEV_DOWN, | ||||||
|  | @@ -68,11 +69,12 @@ enum { | ||||||
|  |   | ||||||
|  |  static const char  * const mod_state_strings[] = { | ||||||
|  |  	[SFP_MOD_EMPTY] = "empty", | ||||||
|  | +	[SFP_MOD_ERROR] = "error", | ||||||
|  |  	[SFP_MOD_PROBE] = "probe", | ||||||
|  | +	[SFP_MOD_WAITDEV] = "waitdev", | ||||||
|  |  	[SFP_MOD_HPOWER] = "hpower", | ||||||
|  |  	[SFP_MOD_WAITPWR] = "waitpwr", | ||||||
|  |  	[SFP_MOD_PRESENT] = "present", | ||||||
|  | -	[SFP_MOD_ERROR] = "error", | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static const char *mod_state_to_str(unsigned short mod_state) | ||||||
|  | @@ -1479,16 +1481,13 @@ static int sfp_sm_mod_probe(struct sfp * | ||||||
|  |  	if (ret < 0) | ||||||
|  |  		return ret; | ||||||
|  |   | ||||||
|  | -	ret = sfp_module_insert(sfp->sfp_bus, &sfp->id); | ||||||
|  | -	if (ret < 0) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void sfp_sm_mod_remove(struct sfp *sfp) | ||||||
|  |  { | ||||||
|  | -	sfp_module_remove(sfp->sfp_bus); | ||||||
|  | +	if (sfp->sm_mod_state > SFP_MOD_WAITDEV) | ||||||
|  | +		sfp_module_remove(sfp->sfp_bus); | ||||||
|  |   | ||||||
|  |  	sfp_hwmon_remove(sfp); | ||||||
|  |   | ||||||
|  | @@ -1539,12 +1538,12 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	/* Handle device detach globally */ | ||||||
|  | -	if (sfp->sm_dev_state < SFP_DEV_DOWN) { | ||||||
|  | +	if (sfp->sm_dev_state < SFP_DEV_DOWN && | ||||||
|  | +	    sfp->sm_mod_state > SFP_MOD_WAITDEV) { | ||||||
|  |  		if (sfp->module_power_mW > 1000 && | ||||||
|  |  		    sfp->sm_mod_state > SFP_MOD_HPOWER) | ||||||
|  |  			sfp_sm_mod_hpower(sfp, false); | ||||||
|  | -		if (sfp->sm_mod_state > SFP_MOD_EMPTY) | ||||||
|  | -			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0); | ||||||
|  | +		sfp_sm_mod_next(sfp, SFP_MOD_WAITDEV, 0); | ||||||
|  |  		return; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | @@ -1555,6 +1554,7 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_MOD_PROBE: | ||||||
|  | +		/* Wait for T_PROBE_INIT to time out */ | ||||||
|  |  		if (event != SFP_E_TIMEOUT) | ||||||
|  |  			break; | ||||||
|  |   | ||||||
|  | @@ -1568,6 +1568,20 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  			break; | ||||||
|  |  		} | ||||||
|  |   | ||||||
|  | +		sfp_sm_mod_next(sfp, SFP_MOD_WAITDEV, 0); | ||||||
|  | +		/* fall through */ | ||||||
|  | +	case SFP_MOD_WAITDEV: | ||||||
|  | +		/* Ensure that the device is attached before proceeding */ | ||||||
|  | +		if (sfp->sm_dev_state < SFP_DEV_DOWN) | ||||||
|  | +			break; | ||||||
|  | + | ||||||
|  | +		/* Report the module insertion to the upstream device */ | ||||||
|  | +		err = sfp_module_insert(sfp->sfp_bus, &sfp->id); | ||||||
|  | +		if (err < 0) { | ||||||
|  | +			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); | ||||||
|  | +			break; | ||||||
|  | +		} | ||||||
|  | + | ||||||
|  |  		/* If this is a power level 1 module, we are done */ | ||||||
|  |  		if (sfp->module_power_mW <= 1000) | ||||||
|  |  			goto insert; | ||||||
|  | @@ -1577,12 +1591,17 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  	case SFP_MOD_HPOWER: | ||||||
|  |  		/* Enable high power mode */ | ||||||
|  |  		err = sfp_sm_mod_hpower(sfp, true); | ||||||
|  | -		if (err == 0) | ||||||
|  | -			sfp_sm_mod_next(sfp, SFP_MOD_WAITPWR, T_HPOWER_LEVEL); | ||||||
|  | -		else if (err != -EAGAIN) | ||||||
|  | -			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); | ||||||
|  | -		else | ||||||
|  | -			sfp_sm_set_timer(sfp, T_PROBE_RETRY); | ||||||
|  | +		if (err < 0) { | ||||||
|  | +			if (err != -EAGAIN) { | ||||||
|  | +				sfp_module_remove(sfp->sfp_bus); | ||||||
|  | +				sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); | ||||||
|  | +			} else { | ||||||
|  | +				sfp_sm_set_timer(sfp, T_PROBE_RETRY); | ||||||
|  | +			} | ||||||
|  | +			break; | ||||||
|  | +		} | ||||||
|  | + | ||||||
|  | +		sfp_sm_mod_next(sfp, SFP_MOD_WAITPWR, T_HPOWER_LEVEL); | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_MOD_WAITPWR: | ||||||
|  | @@ -1750,8 +1769,6 @@ static void sfp_sm_event(struct sfp *sfp | ||||||
|  |  static void sfp_attach(struct sfp *sfp) | ||||||
|  |  { | ||||||
|  |  	sfp_sm_event(sfp, SFP_E_DEV_ATTACH); | ||||||
|  | -	if (sfp->state & SFP_F_PRESENT) | ||||||
|  | -		sfp_sm_event(sfp, SFP_E_INSERT); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void sfp_detach(struct sfp *sfp) | ||||||
|  | @@ -2001,6 +2018,11 @@ static int sfp_probe(struct platform_dev | ||||||
|  |  		sfp->state |= SFP_F_RATE_SELECT; | ||||||
|  |  	sfp_set_state(sfp, sfp->state); | ||||||
|  |  	sfp_module_tx_disable(sfp); | ||||||
|  | +	if (sfp->state & SFP_F_PRESENT) { | ||||||
|  | +		rtnl_lock(); | ||||||
|  | +		sfp_sm_event(sfp, SFP_E_INSERT); | ||||||
|  | +		rtnl_unlock(); | ||||||
|  | +	} | ||||||
|  |   | ||||||
|  |  	for (i = 0; i < GPIO_MAX; i++) { | ||||||
|  |  		if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i]) | ||||||
| @@ -0,0 +1,110 @@ | |||||||
|  | From fb56cd08880aff8fb030e684fa4311bef712a499 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 5 Nov 2019 13:02:30 +0000 | ||||||
|  | Subject: [PATCH 633/660] net: sfp: allow sfp to probe slow to initialise GPON | ||||||
|  |  modules | ||||||
|  |  | ||||||
|  | Some GPON modules (e.g. Huawei MA5671A) take a significant amount of | ||||||
|  | time to start responding on the I2C bus, contary to the SFF | ||||||
|  | specifications. | ||||||
|  |  | ||||||
|  | Work around this by implementing a two-level timeout strategy, where | ||||||
|  | we initially quickly retry for the module, and then use a slower retry | ||||||
|  | after we exceed a maximum number of quick attempts. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 38 ++++++++++++++++++++++++++++---------- | ||||||
|  |  1 file changed, 28 insertions(+), 10 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -165,9 +165,12 @@ static const enum gpiod_flags gpio_flags | ||||||
|  |   * The SFF-8472 specifies t_serial ("Time from power on until module is | ||||||
|  |   * ready for data transmission over the two wire serial bus.") as 300ms. | ||||||
|  |   */ | ||||||
|  | -#define T_SERIAL	msecs_to_jiffies(300) | ||||||
|  | -#define T_HPOWER_LEVEL	msecs_to_jiffies(300) | ||||||
|  | -#define T_PROBE_RETRY	msecs_to_jiffies(100) | ||||||
|  | +#define T_SERIAL		msecs_to_jiffies(300) | ||||||
|  | +#define T_HPOWER_LEVEL		msecs_to_jiffies(300) | ||||||
|  | +#define T_PROBE_RETRY_INIT	msecs_to_jiffies(100) | ||||||
|  | +#define R_PROBE_RETRY_INIT	10 | ||||||
|  | +#define T_PROBE_RETRY_SLOW	msecs_to_jiffies(5000) | ||||||
|  | +#define R_PROBE_RETRY_SLOW	12 | ||||||
|  |   | ||||||
|  |  /* SFP modules appear to always have their PHY configured for bus address | ||||||
|  |   * 0x56 (which with mdio-i2c, translates to a PHY address of 22). | ||||||
|  | @@ -202,6 +205,8 @@ struct sfp { | ||||||
|  |  	struct delayed_work timeout; | ||||||
|  |  	struct mutex sm_mutex;			/* Protects state machine */ | ||||||
|  |  	unsigned char sm_mod_state; | ||||||
|  | +	unsigned char sm_mod_tries_init; | ||||||
|  | +	unsigned char sm_mod_tries; | ||||||
|  |  	unsigned char sm_dev_state; | ||||||
|  |  	unsigned short sm_state; | ||||||
|  |  	unsigned int sm_retries; | ||||||
|  | @@ -1392,7 +1397,7 @@ static int sfp_sm_mod_hpower(struct sfp | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int sfp_sm_mod_probe(struct sfp *sfp) | ||||||
|  | +static int sfp_sm_mod_probe(struct sfp *sfp, bool report) | ||||||
|  |  { | ||||||
|  |  	/* SFP module inserted - read I2C data */ | ||||||
|  |  	struct sfp_eeprom_id id; | ||||||
|  | @@ -1402,7 +1407,8 @@ static int sfp_sm_mod_probe(struct sfp * | ||||||
|  |   | ||||||
|  |  	ret = sfp_read(sfp, false, 0, &id, sizeof(id)); | ||||||
|  |  	if (ret < 0) { | ||||||
|  | -		dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret); | ||||||
|  | +		if (report) | ||||||
|  | +			dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret); | ||||||
|  |  		return -EAGAIN; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | @@ -1549,8 +1555,11 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |   | ||||||
|  |  	switch (sfp->sm_mod_state) { | ||||||
|  |  	default: | ||||||
|  | -		if (event == SFP_E_INSERT) | ||||||
|  | +		if (event == SFP_E_INSERT) { | ||||||
|  |  			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_SERIAL); | ||||||
|  | +			sfp->sm_mod_tries_init = R_PROBE_RETRY_INIT; | ||||||
|  | +			sfp->sm_mod_tries = R_PROBE_RETRY_SLOW; | ||||||
|  | +		} | ||||||
|  |  		break; | ||||||
|  |   | ||||||
|  |  	case SFP_MOD_PROBE: | ||||||
|  | @@ -1558,10 +1567,19 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  		if (event != SFP_E_TIMEOUT) | ||||||
|  |  			break; | ||||||
|  |   | ||||||
|  | -		err = sfp_sm_mod_probe(sfp); | ||||||
|  | +		err = sfp_sm_mod_probe(sfp, sfp->sm_mod_tries == 1); | ||||||
|  |  		if (err == -EAGAIN) { | ||||||
|  | -			sfp_sm_set_timer(sfp, T_PROBE_RETRY); | ||||||
|  | -			break; | ||||||
|  | +			if (sfp->sm_mod_tries_init && | ||||||
|  | +			   --sfp->sm_mod_tries_init) { | ||||||
|  | +				sfp_sm_set_timer(sfp, T_PROBE_RETRY_INIT); | ||||||
|  | +				break; | ||||||
|  | +			} else if (sfp->sm_mod_tries && --sfp->sm_mod_tries) { | ||||||
|  | +				if (sfp->sm_mod_tries == R_PROBE_RETRY_SLOW - 1) | ||||||
|  | +					dev_warn(sfp->dev, | ||||||
|  | +						 "please wait, module slow to respond\n"); | ||||||
|  | +				sfp_sm_set_timer(sfp, T_PROBE_RETRY_SLOW); | ||||||
|  | +				break; | ||||||
|  | +			} | ||||||
|  |  		} | ||||||
|  |  		if (err < 0) { | ||||||
|  |  			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); | ||||||
|  | @@ -1596,7 +1614,7 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  				sfp_module_remove(sfp->sfp_bus); | ||||||
|  |  				sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); | ||||||
|  |  			} else { | ||||||
|  | -				sfp_sm_set_timer(sfp, T_PROBE_RETRY); | ||||||
|  | +				sfp_sm_set_timer(sfp, T_PROBE_RETRY_INIT); | ||||||
|  |  			} | ||||||
|  |  			break; | ||||||
|  |  		} | ||||||
| @@ -0,0 +1,198 @@ | |||||||
|  | From 559391fc20fae506adcb311b904cc544c76436c0 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Thu, 7 Nov 2019 18:52:07 +0000 | ||||||
|  | Subject: [PATCH 634/660] net: sfp: allow modules with slow diagnostics to | ||||||
|  |  probe | ||||||
|  |  | ||||||
|  | When a module is inserted, we attempt to read read the ID from address | ||||||
|  | 0x50.  Once we are able to read the ID, we immediately attempt to | ||||||
|  | initialise the hwmon support by reading from address 0x51.  If this | ||||||
|  | fails, then we fall into error state, and assume that the module is | ||||||
|  | not usable. | ||||||
|  |  | ||||||
|  | Modules such as the ALCATELLUCENT 3FE46541AA use a real EEPROM for | ||||||
|  | I2C address 0x50, which responds immediately.  However, address 0x51 | ||||||
|  | is an emulated, which only becomes available once the on-board firmware | ||||||
|  | has booted.  This prompts us to fall into the error state. | ||||||
|  |  | ||||||
|  | Since the module may be usable without diagnostics, arrange for the | ||||||
|  | hwmon probe independent of the rest of the SFP itself, retrying every | ||||||
|  | 5s for up to about 60s for the monitoring to become available, and | ||||||
|  | print an error message if it doesn't become available. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 96 +++++++++++++++++++++++++++++++++---------- | ||||||
|  |  1 file changed, 74 insertions(+), 22 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -216,6 +216,8 @@ struct sfp { | ||||||
|  |   | ||||||
|  |  #if IS_ENABLED(CONFIG_HWMON) | ||||||
|  |  	struct sfp_diag diag; | ||||||
|  | +	struct delayed_work hwmon_probe; | ||||||
|  | +	unsigned int hwmon_tries; | ||||||
|  |  	struct device *hwmon_dev; | ||||||
|  |  	char *hwmon_name; | ||||||
|  |  #endif | ||||||
|  | @@ -1094,29 +1096,27 @@ static const struct hwmon_chip_info sfp_ | ||||||
|  |  	.info = sfp_hwmon_info, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | -static int sfp_hwmon_insert(struct sfp *sfp) | ||||||
|  | +static void sfp_hwmon_probe(struct work_struct *work) | ||||||
|  |  { | ||||||
|  | +	struct sfp *sfp = container_of(work, struct sfp, hwmon_probe.work); | ||||||
|  |  	int err, i; | ||||||
|  |   | ||||||
|  | -	if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE) | ||||||
|  | -		return 0; | ||||||
|  | - | ||||||
|  | -	if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) | ||||||
|  | -		return 0; | ||||||
|  | - | ||||||
|  | -	if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE) | ||||||
|  | -		/* This driver in general does not support address | ||||||
|  | -		 * change. | ||||||
|  | -		 */ | ||||||
|  | -		return 0; | ||||||
|  | - | ||||||
|  |  	err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag)); | ||||||
|  | -	if (err < 0) | ||||||
|  | -		return err; | ||||||
|  | +	if (err < 0) { | ||||||
|  | +		if (sfp->hwmon_tries--) { | ||||||
|  | +			mod_delayed_work(system_wq, &sfp->hwmon_probe, | ||||||
|  | +					 T_PROBE_RETRY_SLOW); | ||||||
|  | +		} else { | ||||||
|  | +			dev_warn(sfp->dev, "hwmon probe failed: %d\n", err); | ||||||
|  | +		} | ||||||
|  | +		return; | ||||||
|  | +	} | ||||||
|  |   | ||||||
|  |  	sfp->hwmon_name = kstrdup(dev_name(sfp->dev), GFP_KERNEL); | ||||||
|  | -	if (!sfp->hwmon_name) | ||||||
|  | -		return -ENODEV; | ||||||
|  | +	if (!sfp->hwmon_name) { | ||||||
|  | +		dev_err(sfp->dev, "out of memory for hwmon name\n"); | ||||||
|  | +		return; | ||||||
|  | +	} | ||||||
|  |   | ||||||
|  |  	for (i = 0; sfp->hwmon_name[i]; i++) | ||||||
|  |  		if (hwmon_is_bad_char(sfp->hwmon_name[i])) | ||||||
|  | @@ -1126,18 +1126,52 @@ static int sfp_hwmon_insert(struct sfp * | ||||||
|  |  							 sfp->hwmon_name, sfp, | ||||||
|  |  							 &sfp_hwmon_chip_info, | ||||||
|  |  							 NULL); | ||||||
|  | +	if (IS_ERR(sfp->hwmon_dev)) | ||||||
|  | +		dev_err(sfp->dev, "failed to register hwmon device: %ld\n", | ||||||
|  | +			PTR_ERR(sfp->hwmon_dev)); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int sfp_hwmon_insert(struct sfp *sfp) | ||||||
|  | +{ | ||||||
|  | +	if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE) | ||||||
|  | +		/* This driver in general does not support address | ||||||
|  | +		 * change. | ||||||
|  | +		 */ | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	mod_delayed_work(system_wq, &sfp->hwmon_probe, 1); | ||||||
|  | +	sfp->hwmon_tries = R_PROBE_RETRY_SLOW; | ||||||
|  |   | ||||||
|  | -	return PTR_ERR_OR_ZERO(sfp->hwmon_dev); | ||||||
|  | +	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void sfp_hwmon_remove(struct sfp *sfp) | ||||||
|  |  { | ||||||
|  | +	cancel_delayed_work_sync(&sfp->hwmon_probe); | ||||||
|  |  	if (!IS_ERR_OR_NULL(sfp->hwmon_dev)) { | ||||||
|  |  		hwmon_device_unregister(sfp->hwmon_dev); | ||||||
|  |  		sfp->hwmon_dev = NULL; | ||||||
|  |  		kfree(sfp->hwmon_name); | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  | + | ||||||
|  | +static int sfp_hwmon_init(struct sfp *sfp) | ||||||
|  | +{ | ||||||
|  | +	INIT_DELAYED_WORK(&sfp->hwmon_probe, sfp_hwmon_probe); | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void sfp_hwmon_exit(struct sfp *sfp) | ||||||
|  | +{ | ||||||
|  | +	cancel_delayed_work_sync(&sfp->hwmon_probe); | ||||||
|  | +} | ||||||
|  |  #else | ||||||
|  |  static int sfp_hwmon_insert(struct sfp *sfp) | ||||||
|  |  { | ||||||
|  | @@ -1147,6 +1181,15 @@ static int sfp_hwmon_insert(struct sfp * | ||||||
|  |  static void sfp_hwmon_remove(struct sfp *sfp) | ||||||
|  |  { | ||||||
|  |  } | ||||||
|  | + | ||||||
|  | +static int sfp_hwmon_init(struct sfp *sfp) | ||||||
|  | +{ | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void sfp_hwmon_exit(struct sfp *sfp) | ||||||
|  | +{ | ||||||
|  | +} | ||||||
|  |  #endif | ||||||
|  |   | ||||||
|  |  /* Helpers */ | ||||||
|  | @@ -1483,10 +1526,6 @@ static int sfp_sm_mod_probe(struct sfp * | ||||||
|  |  	if (ret < 0) | ||||||
|  |  		return ret; | ||||||
|  |   | ||||||
|  | -	ret = sfp_hwmon_insert(sfp); | ||||||
|  | -	if (ret < 0) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | @@ -1635,6 +1674,15 @@ static void sfp_sm_module(struct sfp *sf | ||||||
|  |  	case SFP_MOD_ERROR: | ||||||
|  |  		break; | ||||||
|  |  	} | ||||||
|  | + | ||||||
|  | +#if IS_ENABLED(CONFIG_HWMON) | ||||||
|  | +	if (sfp->sm_mod_state >= SFP_MOD_WAITDEV && | ||||||
|  | +	    IS_ERR_OR_NULL(sfp->hwmon_dev)) { | ||||||
|  | +		err = sfp_hwmon_insert(sfp); | ||||||
|  | +		if (err) | ||||||
|  | +			dev_warn(sfp->dev, "hwmon probe failed: %d\n", err); | ||||||
|  | +	} | ||||||
|  | +#endif | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void sfp_sm_main(struct sfp *sfp, unsigned int event) | ||||||
|  | @@ -1936,6 +1984,8 @@ static struct sfp *sfp_alloc(struct devi | ||||||
|  |  	INIT_DELAYED_WORK(&sfp->poll, sfp_poll); | ||||||
|  |  	INIT_DELAYED_WORK(&sfp->timeout, sfp_timeout); | ||||||
|  |   | ||||||
|  | +	sfp_hwmon_init(sfp); | ||||||
|  | + | ||||||
|  |  	return sfp; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | @@ -1943,6 +1993,8 @@ static void sfp_cleanup(void *data) | ||||||
|  |  { | ||||||
|  |  	struct sfp *sfp = data; | ||||||
|  |   | ||||||
|  | +	sfp_hwmon_exit(sfp); | ||||||
|  | + | ||||||
|  |  	cancel_delayed_work_sync(&sfp->poll); | ||||||
|  |  	cancel_delayed_work_sync(&sfp->timeout); | ||||||
|  |  	if (sfp->i2c_mii) { | ||||||
| @@ -0,0 +1,183 @@ | |||||||
|  | From eb156db588ac583cdae7b91eaac9c0ad3a358e63 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Sun, 15 Sep 2019 20:05:34 +0100 | ||||||
|  | Subject: [PATCH 635/660] net: phy: add core phylib sfp support | ||||||
|  |  | ||||||
|  | Add core phylib help for supporting SFP sockets on PHYs.  This provides | ||||||
|  | a mechanism to inform the SFP layer about PHY up/down events, and also | ||||||
|  | unregister the SFP bus when the PHY is going away. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phy.c        |  7 ++++ | ||||||
|  |  drivers/net/phy/phy_device.c | 66 ++++++++++++++++++++++++++++++++++++ | ||||||
|  |  include/linux/phy.h          | 11 ++++++ | ||||||
|  |  3 files changed, 84 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phy.c | ||||||
|  | +++ b/drivers/net/phy/phy.c | ||||||
|  | @@ -30,6 +30,7 @@ | ||||||
|  |  #include <linux/ethtool.h> | ||||||
|  |  #include <linux/phy.h> | ||||||
|  |  #include <linux/phy_led_triggers.h> | ||||||
|  | +#include <linux/sfp.h> | ||||||
|  |  #include <linux/workqueue.h> | ||||||
|  |  #include <linux/mdio.h> | ||||||
|  |  #include <linux/io.h> | ||||||
|  | @@ -871,6 +872,9 @@ void phy_stop(struct phy_device *phydev) | ||||||
|  |  	if (phy_interrupt_is_valid(phydev)) | ||||||
|  |  		phy_disable_interrupts(phydev); | ||||||
|  |   | ||||||
|  | +	if (phydev->sfp_bus) | ||||||
|  | +		sfp_upstream_stop(phydev->sfp_bus); | ||||||
|  | + | ||||||
|  |  	phydev->state = PHY_HALTED; | ||||||
|  |   | ||||||
|  |  out_unlock: | ||||||
|  | @@ -899,6 +903,9 @@ void phy_start(struct phy_device *phydev | ||||||
|  |   | ||||||
|  |  	mutex_lock(&phydev->lock); | ||||||
|  |   | ||||||
|  | +	if (phydev->sfp_bus) | ||||||
|  | +		sfp_upstream_start(phydev->sfp_bus); | ||||||
|  | + | ||||||
|  |  	switch (phydev->state) { | ||||||
|  |  	case PHY_STARTING: | ||||||
|  |  		phydev->state = PHY_PENDING; | ||||||
|  | --- a/drivers/net/phy/phy_device.c | ||||||
|  | +++ b/drivers/net/phy/phy_device.c | ||||||
|  | @@ -31,6 +31,7 @@ | ||||||
|  |  #include <linux/ethtool.h> | ||||||
|  |  #include <linux/phy.h> | ||||||
|  |  #include <linux/phy_led_triggers.h> | ||||||
|  | +#include <linux/sfp.h> | ||||||
|  |  #include <linux/mdio.h> | ||||||
|  |  #include <linux/io.h> | ||||||
|  |  #include <linux/uaccess.h> | ||||||
|  | @@ -944,6 +945,65 @@ void phy_attached_print(struct phy_devic | ||||||
|  |  EXPORT_SYMBOL(phy_attached_print); | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | + * phy_sfp_attach - attach the SFP bus to the PHY upstream network device | ||||||
|  | + * @upstream: pointer to the phy device | ||||||
|  | + * @bus: sfp bus representing cage being attached | ||||||
|  | + * | ||||||
|  | + * This is used to fill in the sfp_upstream_ops .attach member. | ||||||
|  | + */ | ||||||
|  | +void phy_sfp_attach(void *upstream, struct sfp_bus *bus) | ||||||
|  | +{ | ||||||
|  | +	struct phy_device *phydev = upstream; | ||||||
|  | + | ||||||
|  | +	if (phydev->attached_dev) | ||||||
|  | +		phydev->attached_dev->sfp_bus = bus; | ||||||
|  | +	phydev->sfp_bus_attached = true; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL(phy_sfp_attach); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * phy_sfp_detach - detach the SFP bus from the PHY upstream network device | ||||||
|  | + * @upstream: pointer to the phy device | ||||||
|  | + * @bus: sfp bus representing cage being attached | ||||||
|  | + * | ||||||
|  | + * This is used to fill in the sfp_upstream_ops .detach member. | ||||||
|  | + */ | ||||||
|  | +void phy_sfp_detach(void *upstream, struct sfp_bus *bus) | ||||||
|  | +{ | ||||||
|  | +	struct phy_device *phydev = upstream; | ||||||
|  | + | ||||||
|  | +	if (phydev->attached_dev) | ||||||
|  | +		phydev->attached_dev->sfp_bus = NULL; | ||||||
|  | +	phydev->sfp_bus_attached = false; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL(phy_sfp_detach); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * phy_sfp_probe - probe for a SFP cage attached to this PHY device | ||||||
|  | + * @phydev: Pointer to phy_device | ||||||
|  | + * @ops: SFP's upstream operations | ||||||
|  | + */ | ||||||
|  | +int phy_sfp_probe(struct phy_device *phydev, | ||||||
|  | +		  const struct sfp_upstream_ops *ops) | ||||||
|  | +{ | ||||||
|  | +	struct sfp_bus *bus; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	if (phydev->mdio.dev.fwnode) { | ||||||
|  | +		bus = sfp_bus_find_fwnode(phydev->mdio.dev.fwnode); | ||||||
|  | +		if (IS_ERR(bus)) | ||||||
|  | +			return PTR_ERR(bus); | ||||||
|  | + | ||||||
|  | +		phydev->sfp_bus = bus; | ||||||
|  | + | ||||||
|  | +		ret = sfp_bus_add_upstream(bus, phydev, ops); | ||||||
|  | +		sfp_bus_put(bus); | ||||||
|  | +	} | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL(phy_sfp_probe); | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  |   * phy_attach_direct - attach a network device to a given PHY device pointer | ||||||
|  |   * @dev: network device to attach | ||||||
|  |   * @phydev: Pointer to phy_device to attach | ||||||
|  | @@ -1016,6 +1076,9 @@ int phy_attach_direct(struct net_device | ||||||
|  |  	phydev->attached_dev = dev; | ||||||
|  |  	dev->phydev = phydev; | ||||||
|  |   | ||||||
|  | +	if (phydev->sfp_bus_attached) | ||||||
|  | +		dev->sfp_bus = phydev->sfp_bus; | ||||||
|  | + | ||||||
|  |  	/* Some Ethernet drivers try to connect to a PHY device before | ||||||
|  |  	 * calling register_netdevice() -> netdev_register_kobject() and | ||||||
|  |  	 * does the dev->dev.kobj initialization. Here we only check for | ||||||
|  | @@ -1950,6 +2013,9 @@ static int phy_remove(struct device *dev | ||||||
|  |  	phydev->state = PHY_DOWN; | ||||||
|  |  	mutex_unlock(&phydev->lock); | ||||||
|  |   | ||||||
|  | +	sfp_bus_del_upstream(phydev->sfp_bus); | ||||||
|  | +	phydev->sfp_bus = NULL; | ||||||
|  | + | ||||||
|  |  	if (phydev->drv && phydev->drv->remove) { | ||||||
|  |  		phydev->drv->remove(phydev); | ||||||
|  |   | ||||||
|  | --- a/include/linux/phy.h | ||||||
|  | +++ b/include/linux/phy.h | ||||||
|  | @@ -184,6 +184,8 @@ static inline const char *phy_modes(phy_ | ||||||
|  |   | ||||||
|  |  struct device; | ||||||
|  |  struct phylink; | ||||||
|  | +struct sfp_bus; | ||||||
|  | +struct sfp_upstream_ops; | ||||||
|  |  struct sk_buff; | ||||||
|  |   | ||||||
|  |  /* | ||||||
|  | @@ -382,6 +384,8 @@ struct phy_c45_device_ids { | ||||||
|  |   * irq: IRQ number of the PHY's interrupt (-1 if none) | ||||||
|  |   * phy_timer: The timer for handling the state machine | ||||||
|  |   * phy_queue: A work_queue for the phy_mac_interrupt | ||||||
|  | + * sfp_bus_attached: flag indicating whether the SFP bus has been attached | ||||||
|  | + * sfp_bus: SFP bus attached to this PHY's fiber port | ||||||
|  |   * attached_dev: The attached enet driver's device instance ptr | ||||||
|  |   * adjust_link: Callback for the enet controller to respond to | ||||||
|  |   * changes in the link state. | ||||||
|  | @@ -471,6 +475,9 @@ struct phy_device { | ||||||
|  |   | ||||||
|  |  	struct mutex lock; | ||||||
|  |   | ||||||
|  | +	/* This may be modified under the rtnl lock */ | ||||||
|  | +	bool sfp_bus_attached; | ||||||
|  | +	struct sfp_bus *sfp_bus; | ||||||
|  |  	struct phylink *phylink; | ||||||
|  |  	struct net_device *attached_dev; | ||||||
|  |   | ||||||
|  | @@ -1031,6 +1038,10 @@ int phy_suspend(struct phy_device *phyde | ||||||
|  |  int phy_resume(struct phy_device *phydev); | ||||||
|  |  int __phy_resume(struct phy_device *phydev); | ||||||
|  |  int phy_loopback(struct phy_device *phydev, bool enable); | ||||||
|  | +void phy_sfp_attach(void *upstream, struct sfp_bus *bus); | ||||||
|  | +void phy_sfp_detach(void *upstream, struct sfp_bus *bus); | ||||||
|  | +int phy_sfp_probe(struct phy_device *phydev, | ||||||
|  | +	          const struct sfp_upstream_ops *ops); | ||||||
|  |  struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, | ||||||
|  |  			      phy_interface_t interface); | ||||||
|  |  struct phy_device *phy_find_first(struct mii_bus *bus); | ||||||
| @@ -0,0 +1,67 @@ | |||||||
|  | From 0836d9fb41ed90090ef4af0d7abe784ee7706f80 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 14 Apr 2017 14:21:25 +0100 | ||||||
|  | Subject: [PATCH 636/660] net: phy: marvell10g: add SFP+ support | ||||||
|  |  | ||||||
|  | Add support for SFP+ cages to the Marvell 10G PHY driver. This is | ||||||
|  | slightly complicated by the way phylib works in that we need to use | ||||||
|  | a multi-step process to attach the SFP bus, and we also need to track | ||||||
|  | the phylink state machine to know when the module's transmit disable | ||||||
|  | signal should change state. | ||||||
|  |  | ||||||
|  | With appropriate DT changes, this allows the SFP+ canges on the | ||||||
|  | Macchiatobin platform to be functional. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/marvell10g.c | 25 ++++++++++++++++++++++++- | ||||||
|  |  1 file changed, 24 insertions(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/marvell10g.c | ||||||
|  | +++ b/drivers/net/phy/marvell10g.c | ||||||
|  | @@ -25,6 +25,7 @@ | ||||||
|  |  #include <linux/hwmon.h> | ||||||
|  |  #include <linux/marvell_phy.h> | ||||||
|  |  #include <linux/phy.h> | ||||||
|  | +#include <linux/sfp.h> | ||||||
|  |   | ||||||
|  |  enum { | ||||||
|  |  	MV_PMA_BOOT		= 0xc050, | ||||||
|  | @@ -219,6 +220,28 @@ static int mv3310_hwmon_probe(struct phy | ||||||
|  |  } | ||||||
|  |  #endif | ||||||
|  |   | ||||||
|  | +static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) | ||||||
|  | +{ | ||||||
|  | +	struct phy_device *phydev = upstream; | ||||||
|  | +	__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; | ||||||
|  | +	phy_interface_t iface; | ||||||
|  | + | ||||||
|  | +	sfp_parse_support(phydev->sfp_bus, id, support); | ||||||
|  | +	iface = sfp_select_interface(phydev->sfp_bus, id, support); | ||||||
|  | + | ||||||
|  | +	if (iface != PHY_INTERFACE_MODE_10GKR) { | ||||||
|  | +		dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); | ||||||
|  | +		return -EINVAL; | ||||||
|  | +	} | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct sfp_upstream_ops mv3310_sfp_ops = { | ||||||
|  | +	.attach = phy_sfp_attach, | ||||||
|  | +	.detach = phy_sfp_detach, | ||||||
|  | +	.module_insert = mv3310_sfp_insert, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  static int mv3310_probe(struct phy_device *phydev) | ||||||
|  |  { | ||||||
|  |  	struct mv3310_priv *priv; | ||||||
|  | @@ -249,7 +272,7 @@ static int mv3310_probe(struct phy_devic | ||||||
|  |  	if (ret) | ||||||
|  |  		return ret; | ||||||
|  |   | ||||||
|  | -	return 0; | ||||||
|  | +	return phy_sfp_probe(phydev, &mv3310_sfp_ops); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int mv3310_suspend(struct phy_device *phydev) | ||||||
| @@ -0,0 +1,45 @@ | |||||||
|  | From 09d7d8395ec61fba4392b35baa6f71c4e36489df Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 8 Nov 2019 15:18:02 +0000 | ||||||
|  | Subject: [PATCH 637/660] net: phylink: update to use phy_support_asym_pause() | ||||||
|  |  | ||||||
|  | Use phy_support_asym_pause() rather than open-coding it. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c | 17 +++++++---------- | ||||||
|  |  1 file changed, 7 insertions(+), 10 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -678,12 +678,6 @@ static int phylink_bringup_phy(struct ph | ||||||
|  |  	u32 advertising; | ||||||
|  |  	int ret; | ||||||
|  |   | ||||||
|  | -	memset(&config, 0, sizeof(config)); | ||||||
|  | -	ethtool_convert_legacy_u32_to_link_mode(supported, phy->supported); | ||||||
|  | -	ethtool_convert_legacy_u32_to_link_mode(config.advertising, | ||||||
|  | -						phy->advertising); | ||||||
|  | -	config.interface = pl->link_config.interface; | ||||||
|  | - | ||||||
|  |  	/* | ||||||
|  |  	 * This is the new way of dealing with flow control for PHYs, | ||||||
|  |  	 * as described by Timur Tabi in commit 529ed1275263 ("net: phy: | ||||||
|  | @@ -691,10 +685,13 @@ static int phylink_bringup_phy(struct ph | ||||||
|  |  	 * using our validate call to the MAC, we rely upon the MAC | ||||||
|  |  	 * clearing the bits from both supported and advertising fields. | ||||||
|  |  	 */ | ||||||
|  | -	if (phylink_test(supported, Pause)) | ||||||
|  | -		phylink_set(config.advertising, Pause); | ||||||
|  | -	if (phylink_test(supported, Asym_Pause)) | ||||||
|  | -		phylink_set(config.advertising, Asym_Pause); | ||||||
|  | +	phy_support_asym_pause(phy); | ||||||
|  | + | ||||||
|  | +	memset(&config, 0, sizeof(config)); | ||||||
|  | +	ethtool_convert_legacy_u32_to_link_mode(supported, phy->supported); | ||||||
|  | +	ethtool_convert_legacy_u32_to_link_mode(config.advertising, | ||||||
|  | +						phy->advertising); | ||||||
|  | +	config.interface = pl->link_config.interface; | ||||||
|  |   | ||||||
|  |  	ret = phylink_validate(pl, supported, &config); | ||||||
|  |  	if (ret) | ||||||
| @@ -0,0 +1,63 @@ | |||||||
|  | From 1be8018db381200c24854e0c299206c557f76fe0 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Mon, 11 Nov 2019 11:58:09 +0000 | ||||||
|  | Subject: [PATCH 638/660] net: phy: avoid matching all-ones clause 45 PHY IDs | ||||||
|  |  | ||||||
|  | We currently match clause 45 PHYs using any ID read from a MMD marked | ||||||
|  | as present in the "Devices in package" registers 5 and 6.  However, | ||||||
|  | this is incorrect.  45.2 says: | ||||||
|  |  | ||||||
|  |   "The definition of the term package is vendor specific and could be | ||||||
|  |    a chip, module, or other similar entity." | ||||||
|  |  | ||||||
|  | so a package could be more or less than the whole PHY - a PHY could be | ||||||
|  | made up of several modules instantiated onto a single chip such as the | ||||||
|  | Marvell 88x3310, or some of the MMDs could be disabled according to | ||||||
|  | chip configuration, such as the Broadcom 84881. | ||||||
|  |  | ||||||
|  | In the case of Broadcom 84881, the "Devices in package" registers | ||||||
|  | contain 0xc000009b, meaning that there is a PHYXS present in the | ||||||
|  | package, but all registers in MMD 4 return 0xffff.  This leads to our | ||||||
|  | matching code incorrectly binding this PHY to one of our generic PHY | ||||||
|  | drivers. | ||||||
|  |  | ||||||
|  | This patch changes the way we determine whether to attempt to match a | ||||||
|  | MMD identifier, or use it to request a module - if the identifier is | ||||||
|  | all-ones, then we skip over it. When reading the identifiers, we | ||||||
|  | initialise phydev->c45_ids.device_ids to all-ones, only reading the | ||||||
|  | device ID if the "Devices in package" registers indicates we should. | ||||||
|  |  | ||||||
|  | This avoids the generic drivers incorrectly matching on a PHY ID of | ||||||
|  | 0xffffffff. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phy_device.c | 7 +++++-- | ||||||
|  |  1 file changed, 5 insertions(+), 2 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phy_device.c | ||||||
|  | +++ b/drivers/net/phy/phy_device.c | ||||||
|  | @@ -335,7 +335,7 @@ static int phy_bus_match(struct device * | ||||||
|  |   | ||||||
|  |  	if (phydev->is_c45) { | ||||||
|  |  		for (i = 1; i < num_ids; i++) { | ||||||
|  | -			if (!(phydev->c45_ids.devices_in_package & (1 << i))) | ||||||
|  | +			if (phydev->c45_ids.device_ids[i] == 0xffffffff) | ||||||
|  |  				continue; | ||||||
|  |   | ||||||
|  |  			if ((phydrv->phy_id & phydrv->phy_id_mask) == | ||||||
|  | @@ -623,10 +623,13 @@ static int get_phy_id(struct mii_bus *bu | ||||||
|  |   */ | ||||||
|  |  struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) | ||||||
|  |  { | ||||||
|  | -	struct phy_c45_device_ids c45_ids = {0}; | ||||||
|  | +	struct phy_c45_device_ids c45_ids; | ||||||
|  |  	u32 phy_id = 0; | ||||||
|  |  	int r; | ||||||
|  |   | ||||||
|  | +	c45_ids.devices_in_package = 0; | ||||||
|  | +	memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids)); | ||||||
|  | + | ||||||
|  |  	r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids); | ||||||
|  |  	if (r) | ||||||
|  |  		return ERR_PTR(r); | ||||||
| @@ -0,0 +1,66 @@ | |||||||
|  | From 4c9633f75dc35abe1b9261e0415d77802f35741d Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Tue, 5 Nov 2019 11:58:00 +0000 | ||||||
|  | Subject: [PATCH 639/660] net: phylink: fix link mode modification in PHY mode | ||||||
|  |  | ||||||
|  | Modifying the link settings via phylink_ethtool_ksettings_set() and | ||||||
|  | phylink_ethtool_set_pauseparam() didn't always work as intended for | ||||||
|  | PHY based setups, as calling phylink_mac_config() would result in the | ||||||
|  | unresolved configuration being committed to the MAC, rather than the | ||||||
|  | configuration with the speed and duplex setting. | ||||||
|  |  | ||||||
|  | This would work fine if the update caused the link to renegotiate, | ||||||
|  | but if no settings have changed, phylib won't trigger a renegotiation | ||||||
|  | cycle, and the MAC will be left incorrectly configured. | ||||||
|  |  | ||||||
|  | Avoid calling phylink_mac_config() unless we are using an inband mode | ||||||
|  | in phylink_ethtool_ksettings_set(), and use phy_set_asym_pause() as | ||||||
|  | introduced in 4.20 to set the PHY settings in | ||||||
|  | phylink_ethtool_set_pauseparam(). | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/phylink.c | 24 ++++++++++++++++-------- | ||||||
|  |  1 file changed, 16 insertions(+), 8 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/phylink.c | ||||||
|  | +++ b/drivers/net/phy/phylink.c | ||||||
|  | @@ -1210,7 +1210,13 @@ int phylink_ethtool_ksettings_set(struct | ||||||
|  |  	pl->link_config.duplex = our_kset.base.duplex; | ||||||
|  |  	pl->link_config.an_enabled = our_kset.base.autoneg != AUTONEG_DISABLE; | ||||||
|  |   | ||||||
|  | -	if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { | ||||||
|  | +	/* If we have a PHY, phylib will call our link state function if the | ||||||
|  | +	 * mode has changed, which will trigger a resolve and update the MAC | ||||||
|  | +	 * configuration. For a fixed link, this isn't able to change any | ||||||
|  | +	 * parameters, which just leaves inband mode. | ||||||
|  | +	 */ | ||||||
|  | +	if (pl->link_an_mode == MLO_AN_INBAND && | ||||||
|  | +	    !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { | ||||||
|  |  		phylink_mac_config(pl, &pl->link_config); | ||||||
|  |  		phylink_mac_an_restart(pl); | ||||||
|  |  	} | ||||||
|  | @@ -1290,14 +1296,16 @@ int phylink_ethtool_set_pauseparam(struc | ||||||
|  |  	if (pause->tx_pause) | ||||||
|  |  		config->pause |= MLO_PAUSE_TX; | ||||||
|  |   | ||||||
|  | -	if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { | ||||||
|  | +	/* If we have a PHY, phylib will call our link state function if the | ||||||
|  | +	 * mode has changed, which will trigger a resolve and update the MAC | ||||||
|  | +	 * configuration. | ||||||
|  | +	 */ | ||||||
|  | +	if (pl->phydev) { | ||||||
|  | +		phy_set_asym_pause(pl->phydev, pause->rx_pause, | ||||||
|  | +				   pause->tx_pause); | ||||||
|  | +	} else if (!test_bit(PHYLINK_DISABLE_STOPPED, | ||||||
|  | +			     &pl->phylink_disable_state)) { | ||||||
|  |  		switch (pl->link_an_mode) { | ||||||
|  | -		case MLO_AN_PHY: | ||||||
|  | -			/* Silently mark the carrier down, and then trigger a resolve */ | ||||||
|  | -			netif_carrier_off(pl->netdev); | ||||||
|  | -			phylink_run_resolve(pl); | ||||||
|  | -			break; | ||||||
|  | - | ||||||
|  |  		case MLO_AN_FIXED: | ||||||
|  |  			/* Should we allow fixed links to change against the config? */ | ||||||
|  |  			phylink_resolve_flow(pl, config); | ||||||
| @@ -0,0 +1,111 @@ | |||||||
|  | From 8df5dd55cef48c0769379e04dbc085a899b106d4 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 8 Mar 2019 14:02:25 +0000 | ||||||
|  | Subject: [PATCH 640/660] net: sfp: add support for module quirks | ||||||
|  |  | ||||||
|  | Add support for applying module quirks to the list of supported | ||||||
|  | ethtool link modes. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp-bus.c | 54 +++++++++++++++++++++++++++++++++++++++ | ||||||
|  |  1 file changed, 54 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp-bus.c | ||||||
|  | +++ b/drivers/net/phy/sfp-bus.c | ||||||
|  | @@ -9,6 +9,12 @@ | ||||||
|  |   | ||||||
|  |  #include "sfp.h" | ||||||
|  |   | ||||||
|  | +struct sfp_quirk { | ||||||
|  | +	const char *vendor; | ||||||
|  | +	const char *part; | ||||||
|  | +	void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  /** | ||||||
|  |   * struct sfp_bus - internal representation of a sfp bus | ||||||
|  |   */ | ||||||
|  | @@ -21,6 +27,7 @@ struct sfp_bus { | ||||||
|  |  	const struct sfp_socket_ops *socket_ops; | ||||||
|  |  	struct device *sfp_dev; | ||||||
|  |  	struct sfp *sfp; | ||||||
|  | +	const struct sfp_quirk *sfp_quirk; | ||||||
|  |   | ||||||
|  |  	const struct sfp_upstream_ops *upstream_ops; | ||||||
|  |  	void *upstream; | ||||||
|  | @@ -30,6 +37,46 @@ struct sfp_bus { | ||||||
|  |  	bool started; | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +static const struct sfp_quirk sfp_quirks[] = { | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static size_t sfp_strlen(const char *str, size_t maxlen) | ||||||
|  | +{ | ||||||
|  | +	size_t size, i; | ||||||
|  | + | ||||||
|  | +	/* Trailing characters should be filled with space chars */ | ||||||
|  | +	for (i = 0, size = 0; i < maxlen; i++) | ||||||
|  | +		if (str[i] != ' ') | ||||||
|  | +			size = i + 1; | ||||||
|  | + | ||||||
|  | +	return size; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static bool sfp_match(const char *qs, const char *str, size_t len) | ||||||
|  | +{ | ||||||
|  | +	if (!qs) | ||||||
|  | +		return true; | ||||||
|  | +	if (strlen(qs) != len) | ||||||
|  | +		return false; | ||||||
|  | +	return !strncmp(qs, str, len); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) | ||||||
|  | +{ | ||||||
|  | +	const struct sfp_quirk *q; | ||||||
|  | +	unsigned int i; | ||||||
|  | +	size_t vs, ps; | ||||||
|  | + | ||||||
|  | +	vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); | ||||||
|  | +	ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); | ||||||
|  | + | ||||||
|  | +	for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) | ||||||
|  | +		if (sfp_match(q->vendor, id->base.vendor_name, vs) && | ||||||
|  | +		    sfp_match(q->part, id->base.vendor_pn, ps)) | ||||||
|  | +			return q; | ||||||
|  | + | ||||||
|  | +	return NULL; | ||||||
|  | +} | ||||||
|  |  /** | ||||||
|  |   * sfp_parse_port() - Parse the EEPROM base ID, setting the port type | ||||||
|  |   * @bus: a pointer to the &struct sfp_bus structure for the sfp module | ||||||
|  | @@ -233,6 +280,9 @@ void sfp_parse_support(struct sfp_bus *b | ||||||
|  |  			phylink_set(modes, 1000baseX_Full); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | +	if (bus->sfp_quirk) | ||||||
|  | +		bus->sfp_quirk->modes(id, modes); | ||||||
|  | + | ||||||
|  |  	bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS); | ||||||
|  |   | ||||||
|  |  	phylink_set(support, Autoneg); | ||||||
|  | @@ -609,6 +659,8 @@ int sfp_module_insert(struct sfp_bus *bu | ||||||
|  |  	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); | ||||||
|  |  	int ret = 0; | ||||||
|  |   | ||||||
|  | +	bus->sfp_quirk = sfp_lookup_quirk(id); | ||||||
|  | + | ||||||
|  |  	if (ops && ops->module_insert) | ||||||
|  |  		ret = ops->module_insert(bus->upstream, id); | ||||||
|  |   | ||||||
|  | @@ -622,6 +674,8 @@ void sfp_module_remove(struct sfp_bus *b | ||||||
|  |   | ||||||
|  |  	if (ops && ops->module_remove) | ||||||
|  |  		ops->module_remove(bus->upstream); | ||||||
|  | + | ||||||
|  | +	bus->sfp_quirk = NULL; | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL_GPL(sfp_module_remove); | ||||||
|  |   | ||||||
| @@ -0,0 +1,52 @@ | |||||||
|  | From ecaa542cfed078dbc356dadff0bad4b6a8e704a0 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 17 May 2019 10:14:45 +0100 | ||||||
|  | Subject: [PATCH 641/660] net: sfp: add some quirks for GPON modules | ||||||
|  |  | ||||||
|  | Marc Micalizzi reports that Huawei MA5671A and Alcatel/Lucent G-010S-P | ||||||
|  | modules are capable of 2500base-X, but incorrectly report their | ||||||
|  | capabilities in the EEPROM.  It seems rather common that GPON modules | ||||||
|  | mis-report. | ||||||
|  |  | ||||||
|  | Let's fix these modules by adding some quirks. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp-bus.c | 25 +++++++++++++++++++++++++ | ||||||
|  |  1 file changed, 25 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp-bus.c | ||||||
|  | +++ b/drivers/net/phy/sfp-bus.c | ||||||
|  | @@ -37,7 +37,32 @@ struct sfp_bus { | ||||||
|  |  	bool started; | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, | ||||||
|  | +				unsigned long *modes) | ||||||
|  | +{ | ||||||
|  | +	phylink_set(modes, 2500baseX_Full); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  static const struct sfp_quirk sfp_quirks[] = { | ||||||
|  | +	{ | ||||||
|  | +		// Alcatel Lucent G-010S-P can operate at 2500base-X, but | ||||||
|  | +		// incorrectly report 2500MBd NRZ in their EEPROM | ||||||
|  | +		.vendor = "ALCATELLUCENT", | ||||||
|  | +		.part = "G010SP", | ||||||
|  | +		.modes = sfp_quirk_2500basex, | ||||||
|  | +	}, { | ||||||
|  | +		// Alcatel Lucent G-010S-A can operate at 2500base-X, but | ||||||
|  | +		// report 3.2GBd NRZ in their EEPROM | ||||||
|  | +		.vendor = "ALCATELLUCENT", | ||||||
|  | +		.part = "3FE46541AA", | ||||||
|  | +		.modes = sfp_quirk_2500basex, | ||||||
|  | +	}, { | ||||||
|  | +		// Huawei MA5671A can operate at 2500base-X, but report 1.2GBd | ||||||
|  | +		// NRZ in their EEPROM | ||||||
|  | +		.vendor = "HUAWEI", | ||||||
|  | +		.part = "MA5671A", | ||||||
|  | +		.modes = sfp_quirk_2500basex, | ||||||
|  | +	}, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static size_t sfp_strlen(const char *str, size_t maxlen) | ||||||
| @@ -0,0 +1,225 @@ | |||||||
|  | From 40e0b3b15f7da92e6b065292b14af7b9bfb1c6e0 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | Date: Fri, 13 Sep 2019 23:00:35 +0100 | ||||||
|  | Subject: [PATCH 642/660] net: sfp: soft status and control support | ||||||
|  |  | ||||||
|  | Add support for the soft status and control register, which allows | ||||||
|  | TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be set.  We | ||||||
|  | make use of this when the board does not support GPIOs for these | ||||||
|  | signals. | ||||||
|  |  | ||||||
|  | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/sfp.c | 110 ++++++++++++++++++++++++++++++++++-------- | ||||||
|  |  include/linux/sfp.h   |   4 ++ | ||||||
|  |  2 files changed, 94 insertions(+), 20 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/sfp.c | ||||||
|  | +++ b/drivers/net/phy/sfp.c | ||||||
|  | @@ -199,7 +199,10 @@ struct sfp { | ||||||
|  |  	struct gpio_desc *gpio[GPIO_MAX]; | ||||||
|  |  	int gpio_irq[GPIO_MAX]; | ||||||
|  |   | ||||||
|  | +	bool need_poll; | ||||||
|  | + | ||||||
|  |  	struct mutex st_mutex;			/* Protects state */ | ||||||
|  | +	unsigned int state_soft_mask; | ||||||
|  |  	unsigned int state; | ||||||
|  |  	struct delayed_work poll; | ||||||
|  |  	struct delayed_work timeout; | ||||||
|  | @@ -393,24 +396,90 @@ static int sfp_i2c_configure(struct sfp | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  /* Interface */ | ||||||
|  | -static unsigned int sfp_get_state(struct sfp *sfp) | ||||||
|  | +static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) | ||||||
|  |  { | ||||||
|  | -	return sfp->get_state(sfp); | ||||||
|  | +	return sfp->read(sfp, a2, addr, buf, len); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static void sfp_set_state(struct sfp *sfp, unsigned int state) | ||||||
|  | +static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) | ||||||
|  |  { | ||||||
|  | -	sfp->set_state(sfp, state); | ||||||
|  | +	return sfp->write(sfp, a2, addr, buf, len); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) | ||||||
|  | +static unsigned int sfp_soft_get_state(struct sfp *sfp) | ||||||
|  |  { | ||||||
|  | -	return sfp->read(sfp, a2, addr, buf, len); | ||||||
|  | +	unsigned int state = 0; | ||||||
|  | +	u8 status; | ||||||
|  | + | ||||||
|  | +	if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) == | ||||||
|  | +		     sizeof(status)) { | ||||||
|  | +		if (status & SFP_STATUS_RX_LOS) | ||||||
|  | +			state |= SFP_F_LOS; | ||||||
|  | +		if (status & SFP_STATUS_TX_FAULT) | ||||||
|  | +			state |= SFP_F_TX_FAULT; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return state & sfp->state_soft_mask; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) | ||||||
|  | +static void sfp_soft_set_state(struct sfp *sfp, unsigned int state) | ||||||
|  |  { | ||||||
|  | -	return sfp->write(sfp, a2, addr, buf, len); | ||||||
|  | +	u8 status; | ||||||
|  | + | ||||||
|  | +	if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) == | ||||||
|  | +		     sizeof(status)) { | ||||||
|  | +		if (state & SFP_F_TX_DISABLE) | ||||||
|  | +			status |= SFP_STATUS_TX_DISABLE_FORCE; | ||||||
|  | +		else | ||||||
|  | +			status &= ~SFP_STATUS_TX_DISABLE_FORCE; | ||||||
|  | + | ||||||
|  | +		sfp_write(sfp, true, SFP_STATUS, &status, sizeof(status)); | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void sfp_soft_start_poll(struct sfp *sfp) | ||||||
|  | +{ | ||||||
|  | +	const struct sfp_eeprom_id *id = &sfp->id; | ||||||
|  | + | ||||||
|  | +	sfp->state_soft_mask = 0; | ||||||
|  | +	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE && | ||||||
|  | +	    !sfp->gpio[GPIO_TX_DISABLE]) | ||||||
|  | +		sfp->state_soft_mask |= SFP_F_TX_DISABLE; | ||||||
|  | +	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT && | ||||||
|  | +	    !sfp->gpio[GPIO_TX_FAULT]) | ||||||
|  | +		sfp->state_soft_mask |= SFP_F_TX_FAULT; | ||||||
|  | +	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS && | ||||||
|  | +	    !sfp->gpio[GPIO_LOS]) | ||||||
|  | +		sfp->state_soft_mask |= SFP_F_LOS; | ||||||
|  | + | ||||||
|  | +	if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) && | ||||||
|  | +	    !sfp->need_poll) | ||||||
|  | +		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void sfp_soft_stop_poll(struct sfp *sfp) | ||||||
|  | +{ | ||||||
|  | +	sfp->state_soft_mask = 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static unsigned int sfp_get_state(struct sfp *sfp) | ||||||
|  | +{ | ||||||
|  | +	unsigned int state = sfp->get_state(sfp); | ||||||
|  | + | ||||||
|  | +	if (state & SFP_F_PRESENT && | ||||||
|  | +	    sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT)) | ||||||
|  | +		state |= sfp_soft_get_state(sfp); | ||||||
|  | + | ||||||
|  | +	return state; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void sfp_set_state(struct sfp *sfp, unsigned int state) | ||||||
|  | +{ | ||||||
|  | +	sfp->set_state(sfp, state); | ||||||
|  | + | ||||||
|  | +	if (state & SFP_F_PRESENT && | ||||||
|  | +	    sfp->state_soft_mask & SFP_F_TX_DISABLE) | ||||||
|  | +		sfp_soft_set_state(sfp, state); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static unsigned int sfp_check(void *buf, size_t len) | ||||||
|  | @@ -1342,11 +1411,6 @@ static void sfp_sm_fault(struct sfp *sfp | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static void sfp_sm_mod_init(struct sfp *sfp) | ||||||
|  | -{ | ||||||
|  | -	sfp_module_tx_enable(sfp); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  |  static void sfp_sm_probe_for_phy(struct sfp *sfp) | ||||||
|  |  { | ||||||
|  |  	/* Setting the serdes link mode is guesswork: there's no | ||||||
|  | @@ -1509,7 +1573,7 @@ static int sfp_sm_mod_probe(struct sfp * | ||||||
|  |  		 (int)sizeof(id.ext.datecode), id.ext.datecode); | ||||||
|  |   | ||||||
|  |  	/* Check whether we support this module */ | ||||||
|  | -	if (!sfp->type->module_supported(&sfp->id)) { | ||||||
|  | +	if (!sfp->type->module_supported(&id)) { | ||||||
|  |  		dev_err(sfp->dev, | ||||||
|  |  			"module is not supported - phys id 0x%02x 0x%02x\n", | ||||||
|  |  			sfp->id.base.phys_id, sfp->id.base.phys_ext_id); | ||||||
|  | @@ -1699,6 +1763,7 @@ static void sfp_sm_main(struct sfp *sfp, | ||||||
|  |  		if (sfp->mod_phy) | ||||||
|  |  			sfp_sm_phy_detach(sfp); | ||||||
|  |  		sfp_module_tx_disable(sfp); | ||||||
|  | +		sfp_soft_stop_poll(sfp); | ||||||
|  |  		sfp_sm_next(sfp, SFP_S_DOWN, 0); | ||||||
|  |  		return; | ||||||
|  |  	} | ||||||
|  | @@ -1710,7 +1775,10 @@ static void sfp_sm_main(struct sfp *sfp, | ||||||
|  |  		    sfp->sm_dev_state != SFP_DEV_UP) | ||||||
|  |  			break; | ||||||
|  |   | ||||||
|  | -		sfp_sm_mod_init(sfp); | ||||||
|  | +		if (!(sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE)) | ||||||
|  | +			sfp_soft_start_poll(sfp); | ||||||
|  | + | ||||||
|  | +		sfp_module_tx_enable(sfp); | ||||||
|  |   | ||||||
|  |  		/* Initialise the fault clearance retries */ | ||||||
|  |  		sfp->sm_retries = 5; | ||||||
|  | @@ -1966,7 +2034,10 @@ static void sfp_poll(struct work_struct | ||||||
|  |  	struct sfp *sfp = container_of(work, struct sfp, poll.work); | ||||||
|  |   | ||||||
|  |  	sfp_check_state(sfp); | ||||||
|  | -	mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); | ||||||
|  | + | ||||||
|  | +	if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) || | ||||||
|  | +	    sfp->need_poll) | ||||||
|  | +		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static struct sfp *sfp_alloc(struct device *dev) | ||||||
|  | @@ -2010,7 +2081,6 @@ static int sfp_probe(struct platform_dev | ||||||
|  |  { | ||||||
|  |  	const struct sff_data *sff; | ||||||
|  |  	struct sfp *sfp; | ||||||
|  | -	bool poll = false; | ||||||
|  |  	int err, i; | ||||||
|  |   | ||||||
|  |  	sfp = sfp_alloc(&pdev->dev); | ||||||
|  | @@ -2100,7 +2170,7 @@ static int sfp_probe(struct platform_dev | ||||||
|  |   | ||||||
|  |  		sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]); | ||||||
|  |  		if (!sfp->gpio_irq[i]) { | ||||||
|  | -			poll = true; | ||||||
|  | +			sfp->need_poll = true; | ||||||
|  |  			continue; | ||||||
|  |  		} | ||||||
|  |   | ||||||
|  | @@ -2112,11 +2182,11 @@ static int sfp_probe(struct platform_dev | ||||||
|  |  						dev_name(sfp->dev), sfp); | ||||||
|  |  		if (err) { | ||||||
|  |  			sfp->gpio_irq[i] = 0; | ||||||
|  | -			poll = true; | ||||||
|  | +			sfp->need_poll = true; | ||||||
|  |  		} | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	if (poll) | ||||||
|  | +	if (sfp->need_poll) | ||||||
|  |  		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); | ||||||
|  |   | ||||||
|  |  	/* We could have an issue in cases no Tx disable pin is available or | ||||||
|  | --- a/include/linux/sfp.h | ||||||
|  | +++ b/include/linux/sfp.h | ||||||
|  | @@ -428,6 +428,10 @@ enum { | ||||||
|  |  	SFP_TEC_CUR			= 0x6c, | ||||||
|  |   | ||||||
|  |  	SFP_STATUS			= 0x6e, | ||||||
|  | +	SFP_STATUS_TX_DISABLE		= BIT(7), | ||||||
|  | +	SFP_STATUS_TX_DISABLE_FORCE	= BIT(6), | ||||||
|  | +	SFP_STATUS_TX_FAULT		= BIT(2), | ||||||
|  | +	SFP_STATUS_RX_LOS		= BIT(1), | ||||||
|  |  	SFP_ALARM0			= 0x70, | ||||||
|  |  	SFP_ALARM0_TEMP_HIGH		= BIT(7), | ||||||
|  |  	SFP_ALARM0_TEMP_LOW		= BIT(6), | ||||||
							
								
								
									
										6101
									
								
								target/linux/generic/config-5.4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6101
									
								
								target/linux/generic/config-5.4
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										206
									
								
								target/linux/generic/hack-5.4/204-module_strip.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								target/linux/generic/hack-5.4/204-module_strip.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,206 @@ | |||||||
|  | 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 | ||||||
|  | @@ -160,6 +160,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) | ||||||
|  | @@ -203,12 +204,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 typeof(name) __mod_##type##__##name##_device_table		\ | ||||||
|  | @@ -235,7 +236,9 @@ extern typeof(name) __mod_##type##__##na | ||||||
|  |   */ | ||||||
|  |   | ||||||
|  |  #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 = {	\ | ||||||
|  | @@ -257,7 +260,7 @@ extern typeof(name) __mod_##type##__##na | ||||||
|  |  /* 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 | ||||||
|  | @@ -17,6 +17,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)[]					  \ | ||||||
|  | @@ -24,8 +34,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) | ||||||
|  | @@ -33,7 +42,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 | ||||||
|  | @@ -1997,6 +1997,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 | ||||||
|  | @@ -3029,9 +3029,11 @@ static int setup_load_info(struct load_i | ||||||
|  |   | ||||||
|  |  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; | ||||||
|  |   | ||||||
|  | @@ -3052,6 +3054,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 | ||||||
|  | @@ -1983,7 +1983,9 @@ static void read_symbols(const char *mod | ||||||
|  |  		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) || vmlinux_section_warnings) | ||||||
|  |  		check_sec_ref(mod, modname, &info); | ||||||
|  | @@ -2146,8 +2148,10 @@ static void add_header(struct buffer *b, | ||||||
|  |  	buf_printf(b, "\n"); | ||||||
|  |  	buf_printf(b, "BUILD_SALT;\n"); | ||||||
|  |  	buf_printf(b, "\n"); | ||||||
|  | +#ifndef CONFIG_MODULE_STRIPPED | ||||||
|  |  	buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n"); | ||||||
|  |  	buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\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"); | ||||||
|  | @@ -2164,8 +2168,10 @@ 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 */ | ||||||
|  | @@ -2178,8 +2184,10 @@ static void add_retpoline(struct buffer | ||||||
|  |   | ||||||
|  |  static void add_staging_flag(struct buffer *b, const char *name) | ||||||
|  |  { | ||||||
|  | +#ifndef CONFIG_MODULE_STRIPPED | ||||||
|  |  	if (strstarts(name, "drivers/staging")) | ||||||
|  |  		buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); | ||||||
|  | +#endif | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | @@ -2278,11 +2286,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) | ||||||
|  | @@ -2519,7 +2529,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); | ||||||
							
								
								
									
										44
									
								
								target/linux/generic/hack-5.4/207-disable-modorder.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								target/linux/generic/hack-5.4/207-disable-modorder.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | 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 | ||||||
|  | @@ -1232,7 +1232,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 | ||||||
|  |   | ||||||
|  | @@ -1261,7 +1260,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 | ||||||
|  | @@ -78,7 +78,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) | ||||||
|  |  	@: | ||||||
|  |   | ||||||
							
								
								
									
										3053
									
								
								target/linux/generic/hack-5.4/210-darwin_scripts_include.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3053
									
								
								target/linux/generic/hack-5.4/210-darwin_scripts_include.patch
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | From 48232d3d931c95953ce2ddfe7da7bb164aef6a73 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Fri, 7 Jul 2017 17:03:16 +0200 | ||||||
|  | Subject: linux-3.6: fix portability of some includes files in tools/ used on the host | ||||||
|  |  | ||||||
|  | lede-commit: 6040b1d29ab1f047c5e49b748abcb6a3196add28 | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  tools/include/tools/be_byteshift.h |  4 ++++ | ||||||
|  |  tools/include/tools/le_byteshift.h |  4 ++++ | ||||||
|  |  tools/include/tools/linux_types.h  | 22 ++++++++++++++++++++++ | ||||||
|  |  3 files changed, 30 insertions(+) | ||||||
|  |  create mode 100644 tools/include/tools/linux_types.h | ||||||
|  |  | ||||||
|  | --- a/tools/include/tools/be_byteshift.h | ||||||
|  | +++ b/tools/include/tools/be_byteshift.h | ||||||
|  | @@ -2,6 +2,10 @@ | ||||||
|  |  #ifndef _TOOLS_BE_BYTESHIFT_H | ||||||
|  |  #define _TOOLS_BE_BYTESHIFT_H | ||||||
|  |   | ||||||
|  | +#ifndef __linux__ | ||||||
|  | +#include "linux_types.h" | ||||||
|  | +#endif | ||||||
|  | + | ||||||
|  |  #include <stdint.h> | ||||||
|  |   | ||||||
|  |  static inline uint16_t __get_unaligned_be16(const uint8_t *p) | ||||||
|  | --- a/tools/include/tools/le_byteshift.h | ||||||
|  | +++ b/tools/include/tools/le_byteshift.h | ||||||
|  | @@ -2,6 +2,10 @@ | ||||||
|  |  #ifndef _TOOLS_LE_BYTESHIFT_H | ||||||
|  |  #define _TOOLS_LE_BYTESHIFT_H | ||||||
|  |   | ||||||
|  | +#ifndef __linux__ | ||||||
|  | +#include "linux_types.h" | ||||||
|  | +#endif | ||||||
|  | + | ||||||
|  |  #include <stdint.h> | ||||||
|  |   | ||||||
|  |  static inline uint16_t __get_unaligned_le16(const uint8_t *p) | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/tools/include/tools/linux_types.h | ||||||
|  | @@ -0,0 +1,22 @@ | ||||||
|  | +#ifndef __LINUX_TYPES_H | ||||||
|  | +#define __LINUX_TYPES_H | ||||||
|  | + | ||||||
|  | +#include <stdint.h> | ||||||
|  | + | ||||||
|  | +typedef uint8_t __u8; | ||||||
|  | +typedef uint8_t __be8; | ||||||
|  | +typedef uint8_t __le8; | ||||||
|  | + | ||||||
|  | +typedef uint16_t __u16; | ||||||
|  | +typedef uint16_t __be16; | ||||||
|  | +typedef uint16_t __le16; | ||||||
|  | + | ||||||
|  | +typedef uint32_t __u32; | ||||||
|  | +typedef uint32_t __be32; | ||||||
|  | +typedef uint32_t __le32; | ||||||
|  | + | ||||||
|  | +typedef uint64_t __u64; | ||||||
|  | +typedef uint64_t __be64; | ||||||
|  | +typedef uint64_t __le64; | ||||||
|  | + | ||||||
|  | +#endif | ||||||
							
								
								
									
										24
									
								
								target/linux/generic/hack-5.4/214-spidev_h_portability.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								target/linux/generic/hack-5.4/214-spidev_h_portability.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | From be9be95ff10e16a5b4ad36f903978d0cc5747024 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Fri, 7 Jul 2017 17:04:08 +0200 | ||||||
|  | Subject: kernel: fix linux/spi/spidev.h portability issues with musl | ||||||
|  |  | ||||||
|  | Felix will try to get this define included into musl | ||||||
|  |  | ||||||
|  | lede-commit: 795e7cf60de19e7a076a46874fab7bb88b43bbff | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  include/uapi/linux/spi/spidev.h | 2 +- | ||||||
|  |  1 file changed, 1 insertion(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/include/uapi/linux/spi/spidev.h | ||||||
|  | +++ b/include/uapi/linux/spi/spidev.h | ||||||
|  | @@ -113,7 +113,7 @@ struct spi_ioc_transfer { | ||||||
|  |   | ||||||
|  |  /* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */ | ||||||
|  |  #define SPI_MSGSIZE(N) \ | ||||||
|  | -	((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \ | ||||||
|  | +	((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << 13)) \ | ||||||
|  |  		? ((N)*(sizeof (struct spi_ioc_transfer))) : 0) | ||||||
|  |  #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) | ||||||
|  |   | ||||||
							
								
								
									
										192
									
								
								target/linux/generic/hack-5.4/220-gc_sections.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								target/linux/generic/hack-5.4/220-gc_sections.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | |||||||
|  | From e3d8676f5722b7622685581e06e8f53e6138e3ab Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Sat, 15 Jul 2017 23:42:36 +0200 | ||||||
|  | Subject: use -ffunction-sections, -fdata-sections and --gc-sections | ||||||
|  |  | ||||||
|  | In combination with kernel symbol export stripping this significantly reduces | ||||||
|  | the kernel image size. Used on both ARM and MIPS architectures. | ||||||
|  |  | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Signed-off-by: Jonas Gorski <jogo@openwrt.org> | ||||||
|  | Signed-off-by: Gabor Juhos <juhosg@openwrt.org> | ||||||
|  | --- | ||||||
|  |  Makefile                          | 10 +++---- | ||||||
|  |  arch/arm/Kconfig                  |  1 + | ||||||
|  |  arch/arm/boot/compressed/Makefile |  1 + | ||||||
|  |  arch/arm/kernel/vmlinux.lds.S     | 26 ++++++++-------- | ||||||
|  |  arch/mips/Kconfig                 |  1 + | ||||||
|  |  arch/mips/kernel/vmlinux.lds.S    |  4 +-- | ||||||
|  |  include/asm-generic/vmlinux.lds.h | 63 ++++++++++++++++++++------------------- | ||||||
|  |  7 files changed, 55 insertions(+), 51 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/Makefile | ||||||
|  | +++ b/Makefile | ||||||
|  | @@ -294,6 +294,11 @@ else | ||||||
|  |  scripts/Kbuild.include: ; | ||||||
|  |  include scripts/Kbuild.include | ||||||
|  |   | ||||||
|  | +ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION | ||||||
|  | +KBUILD_CFLAGS_KERNEL += -ffunction-sections -fdata-sections | ||||||
|  | +LDFLAGS_vmlinux += --gc-sections | ||||||
|  | +endif | ||||||
|  | + | ||||||
|  |  # Read KERNELRELEASE from include/config/kernel.release (if it exists) | ||||||
|  |  KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null) | ||||||
|  |  KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION) | ||||||
|  | @@ -782,11 +787,6 @@ ifdef CONFIG_DEBUG_SECTION_MISMATCH | ||||||
|  |  KBUILD_CFLAGS += $(call cc-option, -fno-inline-functions-called-once) | ||||||
|  |  endif | ||||||
|  |   | ||||||
|  | -ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION | ||||||
|  | -KBUILD_CFLAGS_KERNEL += -ffunction-sections -fdata-sections | ||||||
|  | -LDFLAGS_vmlinux += --gc-sections | ||||||
|  | -endif | ||||||
|  | - | ||||||
|  |  # arch Makefile may override CC so keep this after arch Makefile is included | ||||||
|  |  NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) | ||||||
|  |   | ||||||
|  | --- a/arch/arm/Kconfig | ||||||
|  | +++ b/arch/arm/Kconfig | ||||||
|  | @@ -98,6 +98,7 @@ config ARM | ||||||
|  |  	select HAVE_UID16 | ||||||
|  |  	select HAVE_VIRT_CPU_ACCOUNTING_GEN | ||||||
|  |  	select IRQ_FORCED_THREADING | ||||||
|  | +	select HAVE_LD_DEAD_CODE_DATA_ELIMINATION | ||||||
|  |  	select MODULES_USE_ELF_REL | ||||||
|  |  	select NEED_DMA_MAP_STATE | ||||||
|  |  	select NO_BOOTMEM | ||||||
|  | --- a/arch/arm/boot/compressed/Makefile | ||||||
|  | +++ b/arch/arm/boot/compressed/Makefile | ||||||
|  | @@ -106,6 +106,7 @@ ifeq ($(CONFIG_FUNCTION_TRACER),y) | ||||||
|  |  ORIG_CFLAGS := $(KBUILD_CFLAGS) | ||||||
|  |  KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS)) | ||||||
|  |  endif | ||||||
|  | +KBUILD_CFLAGS_KERNEL := $(patsubst -f%-sections,,$(KBUILD_CFLAGS_KERNEL)) | ||||||
|  |   | ||||||
|  |  # -fstack-protector-strong triggers protection checks in this code, | ||||||
|  |  # but it is being used too early to link to meaningful stack_chk logic. | ||||||
|  | --- a/arch/arm/kernel/vmlinux.lds.S | ||||||
|  | +++ b/arch/arm/kernel/vmlinux.lds.S | ||||||
|  | @@ -100,24 +100,24 @@ SECTIONS | ||||||
|  |  	} | ||||||
|  |  	.init.arch.info : { | ||||||
|  |  		__arch_info_begin = .; | ||||||
|  | -		*(.arch.info.init) | ||||||
|  | +		KEEP(*(.arch.info.init)) | ||||||
|  |  		__arch_info_end = .; | ||||||
|  |  	} | ||||||
|  |  	.init.tagtable : { | ||||||
|  |  		__tagtable_begin = .; | ||||||
|  | -		*(.taglist.init) | ||||||
|  | +		KEEP(*(.taglist.init)) | ||||||
|  |  		__tagtable_end = .; | ||||||
|  |  	} | ||||||
|  |  #ifdef CONFIG_SMP_ON_UP | ||||||
|  |  	.init.smpalt : { | ||||||
|  |  		__smpalt_begin = .; | ||||||
|  | -		*(.alt.smp.init) | ||||||
|  | +		KEEP(*(.alt.smp.init)) | ||||||
|  |  		__smpalt_end = .; | ||||||
|  |  	} | ||||||
|  |  #endif | ||||||
|  |  	.init.pv_table : { | ||||||
|  |  		__pv_table_begin = .; | ||||||
|  | -		*(.pv_table) | ||||||
|  | +		KEEP(*(.pv_table)) | ||||||
|  |  		__pv_table_end = .; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | --- a/arch/arm/kernel/vmlinux.lds.h | ||||||
|  | +++ b/arch/arm/kernel/vmlinux.lds.h | ||||||
|  | @@ -22,13 +22,13 @@ | ||||||
|  |  #define ARM_MMU_DISCARD(x) | ||||||
|  |  #else | ||||||
|  |  #define ARM_MMU_KEEP(x) | ||||||
|  | -#define ARM_MMU_DISCARD(x)	x | ||||||
|  | +#define ARM_MMU_DISCARD(x)	KEEP(x) | ||||||
|  |  #endif | ||||||
|  |   | ||||||
|  |  #define PROC_INFO							\ | ||||||
|  |  		. = ALIGN(4);						\ | ||||||
|  |  		__proc_info_begin = .;					\ | ||||||
|  | -		*(.proc.info.init)					\ | ||||||
|  | +		KEEP(*(.proc.info.init))				\ | ||||||
|  |  		__proc_info_end = .; | ||||||
|  |   | ||||||
|  |  #define HYPERVISOR_TEXT							\ | ||||||
|  | @@ -39,11 +39,11 @@ | ||||||
|  |  #define IDMAP_TEXT							\ | ||||||
|  |  		ALIGN_FUNCTION();					\ | ||||||
|  |  		__idmap_text_start = .;					\ | ||||||
|  | -		*(.idmap.text)						\ | ||||||
|  | +		KEEP(*(.idmap.text))					\ | ||||||
|  |  		__idmap_text_end = .;					\ | ||||||
|  |  		. = ALIGN(PAGE_SIZE);					\ | ||||||
|  |  		__hyp_idmap_text_start = .;				\ | ||||||
|  | -		*(.hyp.idmap.text)					\ | ||||||
|  | +		KEEP(*(.hyp.idmap.text))				\ | ||||||
|  |  		__hyp_idmap_text_end = .; | ||||||
|  |   | ||||||
|  |  #define ARM_DISCARD							\ | ||||||
|  | @@ -86,12 +86,12 @@ | ||||||
|  |  	. = ALIGN(8);							\ | ||||||
|  |  	.ARM.unwind_idx : {						\ | ||||||
|  |  		__start_unwind_idx = .;					\ | ||||||
|  | -		*(.ARM.exidx*)						\ | ||||||
|  | +		KEEP(*(.ARM.exidx*))					\ | ||||||
|  |  		__stop_unwind_idx = .;					\ | ||||||
|  |  	}								\ | ||||||
|  |  	.ARM.unwind_tab : {						\ | ||||||
|  |  		__start_unwind_tab = .;					\ | ||||||
|  | -		*(.ARM.extab*)						\ | ||||||
|  | +		KEEP(*(.ARM.extab*))					\ | ||||||
|  |  		__stop_unwind_tab = .;					\ | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | @@ -102,14 +102,14 @@ | ||||||
|  |  #define ARM_VECTORS							\ | ||||||
|  |  	__vectors_start = .;						\ | ||||||
|  |  	.vectors 0xffff0000 : AT(__vectors_start) {			\ | ||||||
|  | -		*(.vectors)						\ | ||||||
|  | +		KEEP(*(.vectors))					\ | ||||||
|  |  	}								\ | ||||||
|  |  	. = __vectors_start + SIZEOF(.vectors);				\ | ||||||
|  |  	__vectors_end = .;						\ | ||||||
|  |  									\ | ||||||
|  |  	__stubs_start = .;						\ | ||||||
|  |  	.stubs ADDR(.vectors) + 0x1000 : AT(__stubs_start) {		\ | ||||||
|  | -		*(.stubs)						\ | ||||||
|  | +		KEEP(*(.stubs))						\ | ||||||
|  |  	}								\ | ||||||
|  |  	. = __stubs_start + SIZEOF(.stubs);				\ | ||||||
|  |  	__stubs_end = .;						\ | ||||||
|  | --- a/arch/mips/Kconfig | ||||||
|  | +++ b/arch/mips/Kconfig | ||||||
|  | @@ -43,6 +43,7 @@ config MIPS | ||||||
|  |  	select HAVE_ARCH_TRANSPARENT_HUGEPAGE if CPU_SUPPORTS_HUGEPAGES && 64BIT | ||||||
|  |  	select HAVE_CBPF_JIT if (!64BIT && !CPU_MICROMIPS) | ||||||
|  |  	select HAVE_EBPF_JIT if (64BIT && !CPU_MICROMIPS) | ||||||
|  | +	select HAVE_LD_DEAD_CODE_DATA_ELIMINATION | ||||||
|  |  	select HAVE_CONTEXT_TRACKING | ||||||
|  |  	select HAVE_COPY_THREAD_TLS | ||||||
|  |  	select HAVE_C_RECORDMCOUNT | ||||||
|  | --- a/arch/mips/kernel/vmlinux.lds.S | ||||||
|  | +++ b/arch/mips/kernel/vmlinux.lds.S | ||||||
|  | @@ -72,7 +72,7 @@ SECTIONS | ||||||
|  |  	/* Exception table for data bus errors */ | ||||||
|  |  	__dbe_table : { | ||||||
|  |  		__start___dbe_table = .; | ||||||
|  | -		*(__dbe_table) | ||||||
|  | +		KEEP(*(__dbe_table)) | ||||||
|  |  		__stop___dbe_table = .; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | @@ -123,7 +123,7 @@ SECTIONS | ||||||
|  |  	. = ALIGN(4); | ||||||
|  |  	.mips.machines.init : AT(ADDR(.mips.machines.init) - LOAD_OFFSET) { | ||||||
|  |  		__mips_machines_start = .; | ||||||
|  | -		*(.mips.machines.init) | ||||||
|  | +		KEEP(*(.mips.machines.init)) | ||||||
|  |  		__mips_machines_end = .; | ||||||
|  |  	} | ||||||
|  |   | ||||||
							
								
								
									
										101
									
								
								target/linux/generic/hack-5.4/221-module_exports.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								target/linux/generic/hack-5.4/221-module_exports.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | |||||||
|  | From b14784e7883390c20ed3ff904892255404a5914b Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Fri, 7 Jul 2017 17:05:53 +0200 | ||||||
|  | Subject: add an optional config option for stripping all unnecessary symbol exports from the kernel image | ||||||
|  |  | ||||||
|  | lede-commit: bb5a40c64b7c4f4848509fa0a6625055fc9e66cc | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  include/asm-generic/vmlinux.lds.h | 18 +++++++++++++++--- | ||||||
|  |  include/linux/export.h            |  9 ++++++++- | ||||||
|  |  scripts/Makefile.build            |  2 +- | ||||||
|  |  3 files changed, 24 insertions(+), 5 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/include/asm-generic/vmlinux.lds.h | ||||||
|  | +++ b/include/asm-generic/vmlinux.lds.h | ||||||
|  | @@ -54,6 +54,16 @@ | ||||||
|  |  #define LOAD_OFFSET 0 | ||||||
|  |  #endif | ||||||
|  |   | ||||||
|  | +#ifndef SYMTAB_KEEP | ||||||
|  | +#define SYMTAB_KEEP KEEP(*(SORT(___ksymtab+*))) | ||||||
|  | +#define SYMTAB_KEEP_GPL KEEP(*(SORT(___ksymtab_gpl+*))) | ||||||
|  | +#endif | ||||||
|  | + | ||||||
|  | +#ifndef SYMTAB_DISCARD | ||||||
|  | +#define SYMTAB_DISCARD | ||||||
|  | +#define SYMTAB_DISCARD_GPL | ||||||
|  | +#endif | ||||||
|  | + | ||||||
|  |  /* Align . to a 8 byte boundary equals to maximum function alignment. */ | ||||||
|  |  #define ALIGN_FUNCTION()  . = ALIGN(8) | ||||||
|  |   | ||||||
|  | @@ -372,14 +382,14 @@ | ||||||
|  |  	/* Kernel symbol table: Normal symbols */			\ | ||||||
|  |  	__ksymtab         : AT(ADDR(__ksymtab) - LOAD_OFFSET) {		\ | ||||||
|  |  		__start___ksymtab = .;					\ | ||||||
|  | -		KEEP(*(SORT(___ksymtab+*)))				\ | ||||||
|  | +		SYMTAB_KEEP						\ | ||||||
|  |  		__stop___ksymtab = .;					\ | ||||||
|  |  	}								\ | ||||||
|  |  									\ | ||||||
|  |  	/* Kernel symbol table: GPL-only symbols */			\ | ||||||
|  |  	__ksymtab_gpl     : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) {	\ | ||||||
|  |  		__start___ksymtab_gpl = .;				\ | ||||||
|  | -		KEEP(*(SORT(___ksymtab_gpl+*)))				\ | ||||||
|  | +		SYMTAB_KEEP_GPL						\ | ||||||
|  |  		__stop___ksymtab_gpl = .;				\ | ||||||
|  |  	}								\ | ||||||
|  |  									\ | ||||||
|  | @@ -441,7 +451,7 @@ | ||||||
|  |  									\ | ||||||
|  |  	/* Kernel symbol table: strings */				\ | ||||||
|  |          __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) {	\ | ||||||
|  | -		*(__ksymtab_strings)					\ | ||||||
|  | +		*(__ksymtab_strings+*)					\ | ||||||
|  |  	}								\ | ||||||
|  |  									\ | ||||||
|  |  	/* __*init sections */						\ | ||||||
|  | @@ -841,6 +851,8 @@ | ||||||
|  |  	EXIT_TEXT							\ | ||||||
|  |  	EXIT_DATA							\ | ||||||
|  |  	EXIT_CALL							\ | ||||||
|  | +	SYMTAB_DISCARD							\ | ||||||
|  | +	SYMTAB_DISCARD_GPL						\ | ||||||
|  |  	*(.discard)							\ | ||||||
|  |  	*(.discard.*)							\ | ||||||
|  |  	} | ||||||
|  | --- a/include/linux/export.h | ||||||
|  | +++ b/include/linux/export.h | ||||||
|  | @@ -74,12 +74,19 @@ struct kernel_symbol { | ||||||
|  |  }; | ||||||
|  |  #endif | ||||||
|  |   | ||||||
|  | +#ifdef MODULE | ||||||
|  | +#define __EXPORT_SUFFIX(sym) | ||||||
|  | +#else | ||||||
|  | +#define __EXPORT_SUFFIX(sym) "+" #sym | ||||||
|  | +#endif | ||||||
|  | + | ||||||
|  |  /* For every exported symbol, place a struct in the __ksymtab section */ | ||||||
|  |  #define ___EXPORT_SYMBOL(sym, sec)					\ | ||||||
|  |  	extern typeof(sym) sym;						\ | ||||||
|  |  	__CRC_SYMBOL(sym, sec)						\ | ||||||
|  |  	static const char __kstrtab_##sym[]				\ | ||||||
|  | -	__attribute__((section("__ksymtab_strings"), used, aligned(1)))	\ | ||||||
|  | +	__attribute__((section("__ksymtab_strings"			\ | ||||||
|  | +	  __EXPORT_SUFFIX(sym)), used, aligned(1)))			\ | ||||||
|  |  	= #sym;								\ | ||||||
|  |  	__KSYMTAB_ENTRY(sym, sec) | ||||||
|  |   | ||||||
|  | --- a/scripts/Makefile.build | ||||||
|  | +++ b/scripts/Makefile.build | ||||||
|  | @@ -408,7 +408,7 @@ targets += $(extra-y) $(MAKECMDGOALS) $( | ||||||
|  |  # Linker scripts preprocessor (.lds.S -> .lds) | ||||||
|  |  # --------------------------------------------------------------------------- | ||||||
|  |  quiet_cmd_cpp_lds_S = LDS     $@ | ||||||
|  | -      cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -U$(ARCH) \ | ||||||
|  | +      cmd_cpp_lds_S = $(CPP) $(EXTRA_LDSFLAGS) $(cpp_flags) -P -U$(ARCH) \ | ||||||
|  |  	                     -D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $< | ||||||
|  |   | ||||||
|  |  $(obj)/%.lds: $(src)/%.lds.S FORCE | ||||||
							
								
								
									
										71
									
								
								target/linux/generic/hack-5.4/230-openwrt_lzma_options.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								target/linux/generic/hack-5.4/230-openwrt_lzma_options.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | |||||||
|  | From b3d00b452467f621317953d9e4c6f9ae8dcfd271 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Imre Kaloz <kaloz@openwrt.org> | ||||||
|  | Date: Fri, 7 Jul 2017 17:06:55 +0200 | ||||||
|  | Subject: use the openwrt lzma options for now | ||||||
|  |  | ||||||
|  | lede-commit: 548de949f392049420a6a1feeef118b30ab8ea8c | ||||||
|  | Signed-off-by: Imre Kaloz <kaloz@openwrt.org> | ||||||
|  | --- | ||||||
|  |  lib/decompress.c              |  1 + | ||||||
|  |  scripts/Makefile.lib          |  2 +- | ||||||
|  |  usr/gen_initramfs_list.sh | 10 +++++----- | ||||||
|  |  3 files changed, 7 insertions(+), 6 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/lib/decompress.c | ||||||
|  | +++ b/lib/decompress.c | ||||||
|  | @@ -49,6 +49,7 @@ static const struct compress_format comp | ||||||
|  |  	{ {0x1f, 0x9e}, "gzip", gunzip }, | ||||||
|  |  	{ {0x42, 0x5a}, "bzip2", bunzip2 }, | ||||||
|  |  	{ {0x5d, 0x00}, "lzma", unlzma }, | ||||||
|  | +	{ {0x6d, 0x00}, "lzma-openwrt", unlzma }, | ||||||
|  |  	{ {0xfd, 0x37}, "xz", unxz }, | ||||||
|  |  	{ {0x89, 0x4c}, "lzo", unlzo }, | ||||||
|  |  	{ {0x02, 0x21}, "lz4", unlz4 }, | ||||||
|  | --- a/scripts/Makefile.lib | ||||||
|  | +++ b/scripts/Makefile.lib | ||||||
|  | @@ -325,7 +325,7 @@ cmd_bzip2 = (cat $(filter-out FORCE,$^) | ||||||
|  |   | ||||||
|  |  quiet_cmd_lzma = LZMA    $@ | ||||||
|  |  cmd_lzma = (cat $(filter-out FORCE,$^) | \ | ||||||
|  | -	lzma -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ || \ | ||||||
|  | +	lzma e -d20 -lc1 -lp2 -pb2 -eos -si -so && $(call size_append, $(filter-out FORCE,$^))) > $@ || \ | ||||||
|  |  	(rm -f $@ ; false) | ||||||
|  |   | ||||||
|  |  quiet_cmd_lzo = LZO     $@ | ||||||
|  | --- a/usr/gen_initramfs_list.sh | ||||||
|  | +++ b/usr/gen_initramfs_list.sh | ||||||
|  | @@ -229,7 +229,7 @@ cpio_list= | ||||||
|  |  output="/dev/stdout" | ||||||
|  |  output_file="" | ||||||
|  |  is_cpio_compressed= | ||||||
|  | -compr="gzip -n -9 -f" | ||||||
|  | +compr="gzip -n -9 -f -" | ||||||
|  |   | ||||||
|  |  arg="$1" | ||||||
|  |  case "$arg" in | ||||||
|  | @@ -245,13 +245,13 @@ case "$arg" in | ||||||
|  |  		output=${cpio_list} | ||||||
|  |  		echo "$output_file" | grep -q "\.gz$" \ | ||||||
|  |                  && [ -x "`which gzip 2> /dev/null`" ] \ | ||||||
|  | -                && compr="gzip -n -9 -f" | ||||||
|  | +                && compr="gzip -n -9 -f -" | ||||||
|  |  		echo "$output_file" | grep -q "\.bz2$" \ | ||||||
|  |                  && [ -x "`which bzip2 2> /dev/null`" ] \ | ||||||
|  | -                && compr="bzip2 -9 -f" | ||||||
|  | +                && compr="bzip2 -9 -f -" | ||||||
|  |  		echo "$output_file" | grep -q "\.lzma$" \ | ||||||
|  |                  && [ -x "`which lzma 2> /dev/null`" ] \ | ||||||
|  | -                && compr="lzma -9 -f" | ||||||
|  | +                && compr="lzma e -d20 -lc1 -lp2 -pb2 -eos -si -so" | ||||||
|  |  		echo "$output_file" | grep -q "\.xz$" \ | ||||||
|  |                  && [ -x "`which xz 2> /dev/null`" ] \ | ||||||
|  |                  && compr="xz --check=crc32 --lzma2=dict=1MiB" | ||||||
|  | @@ -320,7 +320,7 @@ if [ ! -z ${output_file} ]; then | ||||||
|  |  	if [ "${is_cpio_compressed}" = "compressed" ]; then | ||||||
|  |  		cat ${cpio_tfile} > ${output_file} | ||||||
|  |  	else | ||||||
|  | -		(cat ${cpio_tfile} | ${compr}  - > ${output_file}) \ | ||||||
|  | +		(cat ${cpio_tfile} | ${compr} > ${output_file}) \ | ||||||
|  |  		|| (rm -f ${output_file} ; false) | ||||||
|  |  	fi | ||||||
|  |  	[ -z ${cpio_file} ] && rm ${cpio_tfile} | ||||||
							
								
								
									
										27
									
								
								target/linux/generic/hack-5.4/250-netfilter_depends.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								target/linux/generic/hack-5.4/250-netfilter_depends.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Subject: hack: net: remove bogus netfilter dependencies | ||||||
|  |  | ||||||
|  | lede-commit: 589d2a377dee27d206fc3725325309cf649e4df6 | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  net/netfilter/Kconfig | 2 -- | ||||||
|  |  1 file changed, 2 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/net/netfilter/Kconfig | ||||||
|  | +++ b/net/netfilter/Kconfig | ||||||
|  | @@ -241,7 +241,6 @@ config NF_CONNTRACK_FTP | ||||||
|  |   | ||||||
|  |  config NF_CONNTRACK_H323 | ||||||
|  |  	tristate "H.323 protocol support" | ||||||
|  | -	depends on IPV6 || IPV6=n | ||||||
|  |  	depends on NETFILTER_ADVANCED | ||||||
|  |  	help | ||||||
|  |  	  H.323 is a VoIP signalling protocol from ITU-T. As one of the most | ||||||
|  | @@ -1077,7 +1076,6 @@ config NETFILTER_XT_TARGET_SECMARK | ||||||
|  |   | ||||||
|  |  config NETFILTER_XT_TARGET_TCPMSS | ||||||
|  |  	tristate '"TCPMSS" target support' | ||||||
|  | -	depends on IPV6 || IPV6=n | ||||||
|  |  	default m if NETFILTER_ADVANCED=n | ||||||
|  |  	---help--- | ||||||
|  |  	  This option adds a `TCPMSS' target, which allows you to alter the | ||||||
							
								
								
									
										197
									
								
								target/linux/generic/hack-5.4/251-sound_kconfig.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								target/linux/generic/hack-5.4/251-sound_kconfig.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | |||||||
|  | From da3c50704f14132f4adf80d48e9a4cd5d46e54c9 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: John Crispin <john@phrozen.org> | ||||||
|  | Date: Fri, 7 Jul 2017 17:09:21 +0200 | ||||||
|  | Subject: kconfig: owrt specifc dependencies | ||||||
|  |  | ||||||
|  | Signed-off-by: John Crispin <john@phrozen.org> | ||||||
|  | --- | ||||||
|  |  crypto/Kconfig        | 10 +++++----- | ||||||
|  |  drivers/bcma/Kconfig  |  1 + | ||||||
|  |  drivers/ssb/Kconfig   |  3 ++- | ||||||
|  |  lib/Kconfig           |  8 ++++---- | ||||||
|  |  net/netfilter/Kconfig |  2 +- | ||||||
|  |  net/wireless/Kconfig  | 17 ++++++++++------- | ||||||
|  |  sound/core/Kconfig    |  4 ++-- | ||||||
|  |  7 files changed, 25 insertions(+), 20 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/crypto/Kconfig | ||||||
|  | +++ b/crypto/Kconfig | ||||||
|  | @@ -33,7 +33,7 @@ config CRYPTO_FIPS | ||||||
|  |  	  this is. | ||||||
|  |   | ||||||
|  |  config CRYPTO_ALGAPI | ||||||
|  | -	tristate | ||||||
|  | +	tristate "ALGAPI" | ||||||
|  |  	select CRYPTO_ALGAPI2 | ||||||
|  |  	help | ||||||
|  |  	  This option provides the API for cryptographic algorithms. | ||||||
|  | @@ -42,7 +42,7 @@ config CRYPTO_ALGAPI2 | ||||||
|  |  	tristate | ||||||
|  |   | ||||||
|  |  config CRYPTO_AEAD | ||||||
|  | -	tristate | ||||||
|  | +	tristate "AEAD" | ||||||
|  |  	select CRYPTO_AEAD2 | ||||||
|  |  	select CRYPTO_ALGAPI | ||||||
|  |   | ||||||
|  | @@ -53,7 +53,7 @@ config CRYPTO_AEAD2 | ||||||
|  |  	select CRYPTO_RNG2 | ||||||
|  |   | ||||||
|  |  config CRYPTO_BLKCIPHER | ||||||
|  | -	tristate | ||||||
|  | +	tristate "BLKCIPHER" | ||||||
|  |  	select CRYPTO_BLKCIPHER2 | ||||||
|  |  	select CRYPTO_ALGAPI | ||||||
|  |   | ||||||
|  | @@ -64,7 +64,7 @@ config CRYPTO_BLKCIPHER2 | ||||||
|  |  	select CRYPTO_WORKQUEUE | ||||||
|  |   | ||||||
|  |  config CRYPTO_HASH | ||||||
|  | -	tristate | ||||||
|  | +	tristate "HASH" | ||||||
|  |  	select CRYPTO_HASH2 | ||||||
|  |  	select CRYPTO_ALGAPI | ||||||
|  |   | ||||||
|  | @@ -73,7 +73,7 @@ config CRYPTO_HASH2 | ||||||
|  |  	select CRYPTO_ALGAPI2 | ||||||
|  |   | ||||||
|  |  config CRYPTO_RNG | ||||||
|  | -	tristate | ||||||
|  | +	tristate "RNG" | ||||||
|  |  	select CRYPTO_RNG2 | ||||||
|  |  	select CRYPTO_ALGAPI | ||||||
|  |   | ||||||
|  | --- a/drivers/bcma/Kconfig | ||||||
|  | +++ b/drivers/bcma/Kconfig | ||||||
|  | @@ -16,6 +16,7 @@ if BCMA | ||||||
|  |  # Support for Block-I/O. SELECT this from the driver that needs it. | ||||||
|  |  config BCMA_BLOCKIO | ||||||
|  |  	bool | ||||||
|  | +	default y | ||||||
|  |   | ||||||
|  |  config BCMA_HOST_PCI_POSSIBLE | ||||||
|  |  	bool | ||||||
|  | --- a/drivers/ssb/Kconfig | ||||||
|  | +++ b/drivers/ssb/Kconfig | ||||||
|  | @@ -28,6 +28,7 @@ config SSB_SPROM | ||||||
|  |  config SSB_BLOCKIO | ||||||
|  |  	bool | ||||||
|  |  	depends on SSB | ||||||
|  | +	default y | ||||||
|  |   | ||||||
|  |  config SSB_PCIHOST_POSSIBLE | ||||||
|  |  	bool | ||||||
|  | @@ -48,7 +49,7 @@ config SSB_PCIHOST | ||||||
|  |  config SSB_B43_PCI_BRIDGE | ||||||
|  |  	bool | ||||||
|  |  	depends on SSB_PCIHOST | ||||||
|  | -	default n | ||||||
|  | +	default y | ||||||
|  |   | ||||||
|  |  config SSB_PCMCIAHOST_POSSIBLE | ||||||
|  |  	bool | ||||||
|  | --- a/lib/Kconfig | ||||||
|  | +++ b/lib/Kconfig | ||||||
|  | @@ -377,16 +377,16 @@ config BCH_CONST_T | ||||||
|  |  # Textsearch support is select'ed if needed | ||||||
|  |  # | ||||||
|  |  config TEXTSEARCH | ||||||
|  | -	bool | ||||||
|  | +	bool "Textsearch support" | ||||||
|  |   | ||||||
|  |  config TEXTSEARCH_KMP | ||||||
|  | -	tristate | ||||||
|  | +	tristate "Textsearch KMP" | ||||||
|  |   | ||||||
|  |  config TEXTSEARCH_BM | ||||||
|  | -	tristate | ||||||
|  | +	tristate "Textsearch BM" | ||||||
|  |   | ||||||
|  |  config TEXTSEARCH_FSM | ||||||
|  | -	tristate | ||||||
|  | +	tristate "Textsearch FSM" | ||||||
|  |   | ||||||
|  |  config BTREE | ||||||
|  |  	bool | ||||||
|  | --- a/net/netfilter/Kconfig | ||||||
|  | +++ b/net/netfilter/Kconfig | ||||||
|  | @@ -10,7 +10,7 @@ config NETFILTER_INGRESS | ||||||
|  |  	  infrastructure. | ||||||
|  |   | ||||||
|  |  config NETFILTER_NETLINK | ||||||
|  | -	tristate | ||||||
|  | +	tristate "Netfilter NFNETLINK interface" | ||||||
|  |   | ||||||
|  |  config NETFILTER_FAMILY_BRIDGE | ||||||
|  |  	bool | ||||||
|  | --- a/net/wireless/Kconfig | ||||||
|  | +++ b/net/wireless/Kconfig | ||||||
|  | @@ -1,5 +1,5 @@ | ||||||
|  |  config WIRELESS_EXT | ||||||
|  | -	bool | ||||||
|  | +	bool "Wireless extensions" | ||||||
|  |   | ||||||
|  |  config WEXT_CORE | ||||||
|  |  	def_bool y | ||||||
|  | @@ -11,10 +11,10 @@ config WEXT_PROC | ||||||
|  |  	depends on WEXT_CORE | ||||||
|  |   | ||||||
|  |  config WEXT_SPY | ||||||
|  | -	bool | ||||||
|  | +	bool "WEXT_SPY" | ||||||
|  |   | ||||||
|  |  config WEXT_PRIV | ||||||
|  | -	bool | ||||||
|  | +	bool "WEXT_PRIV" | ||||||
|  |   | ||||||
|  |  config CFG80211 | ||||||
|  |  	tristate "cfg80211 - wireless configuration API" | ||||||
|  | @@ -202,7 +202,7 @@ config CFG80211_WEXT_EXPORT | ||||||
|  |  endif # CFG80211 | ||||||
|  |   | ||||||
|  |  config LIB80211 | ||||||
|  | -	tristate | ||||||
|  | +	tristate "LIB80211" | ||||||
|  |  	default n | ||||||
|  |  	help | ||||||
|  |  	  This options enables a library of common routines used | ||||||
|  | @@ -211,13 +211,16 @@ config LIB80211 | ||||||
|  |  	  Drivers should select this themselves if needed. | ||||||
|  |   | ||||||
|  |  config LIB80211_CRYPT_WEP | ||||||
|  | -	tristate | ||||||
|  | +	tristate "LIB80211_CRYPT_WEP" | ||||||
|  | +	select LIB80211 | ||||||
|  |   | ||||||
|  |  config LIB80211_CRYPT_CCMP | ||||||
|  | -	tristate | ||||||
|  | +	tristate "LIB80211_CRYPT_CCMP" | ||||||
|  | +	select LIB80211 | ||||||
|  |   | ||||||
|  |  config LIB80211_CRYPT_TKIP | ||||||
|  | -	tristate | ||||||
|  | +	tristate "LIB80211_CRYPT_TKIP" | ||||||
|  | +	select LIB80211 | ||||||
|  |   | ||||||
|  |  config LIB80211_DEBUG | ||||||
|  |  	bool "lib80211 debugging messages" | ||||||
|  | --- a/sound/core/Kconfig | ||||||
|  | +++ b/sound/core/Kconfig | ||||||
|  | @@ -16,7 +16,7 @@ config SND_DMAENGINE_PCM | ||||||
|  |  	tristate | ||||||
|  |   | ||||||
|  |  config SND_HWDEP | ||||||
|  | -	tristate | ||||||
|  | +	tristate "Sound hardware support" | ||||||
|  |   | ||||||
|  |  config SND_SEQ_DEVICE | ||||||
|  |  	tristate | ||||||
|  | @@ -26,7 +26,7 @@ config SND_RAWMIDI | ||||||
|  |  	select SND_SEQ_DEVICE if SND_SEQUENCER != n | ||||||
|  |   | ||||||
|  |  config SND_COMPRESS_OFFLOAD | ||||||
|  | -	tristate | ||||||
|  | +	tristate "Compression offloading support" | ||||||
|  |   | ||||||
|  |  config SND_JACK | ||||||
|  |  	bool | ||||||
							
								
								
									
										110
									
								
								target/linux/generic/hack-5.4/259-regmap_dynamic.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								target/linux/generic/hack-5.4/259-regmap_dynamic.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | From 811d9e2268a62b830cfe93cd8bc929afcb8b198b Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Sat, 15 Jul 2017 21:12:38 +0200 | ||||||
|  | Subject: kernel: move regmap bloat out of the kernel image if it is only being used in modules | ||||||
|  |  | ||||||
|  | lede-commit: 96f39119815028073583e4fca3a9c5fe9141e998 | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  drivers/base/regmap/Kconfig  | 15 ++++++++++----- | ||||||
|  |  drivers/base/regmap/Makefile | 12 ++++++++---- | ||||||
|  |  drivers/base/regmap/regmap.c |  3 +++ | ||||||
|  |  include/linux/regmap.h       |  2 +- | ||||||
|  |  4 files changed, 22 insertions(+), 10 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/base/regmap/Kconfig | ||||||
|  | +++ b/drivers/base/regmap/Kconfig | ||||||
|  | @@ -4,9 +4,8 @@ | ||||||
|  |  # subsystems should select the appropriate symbols. | ||||||
|  |   | ||||||
|  |  config REGMAP | ||||||
|  | -	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ) | ||||||
|  |  	select IRQ_DOMAIN if REGMAP_IRQ | ||||||
|  | -	bool | ||||||
|  | +	tristate | ||||||
|  |   | ||||||
|  |  config REGCACHE_COMPRESSED | ||||||
|  |  	select LZO_COMPRESS | ||||||
|  | @@ -18,6 +17,7 @@ config REGMAP_AC97 | ||||||
|  |   | ||||||
|  |  config REGMAP_I2C | ||||||
|  |  	tristate | ||||||
|  | +	select REGMAP | ||||||
|  |  	depends on I2C | ||||||
|  |   | ||||||
|  |  config REGMAP_SLIMBUS | ||||||
|  | @@ -26,20 +26,26 @@ config REGMAP_SLIMBUS | ||||||
|  |   | ||||||
|  |  config REGMAP_SPI | ||||||
|  |  	tristate | ||||||
|  | +	select REGMAP | ||||||
|  | +	depends on SPI_MASTER | ||||||
|  |  	depends on SPI | ||||||
|  |   | ||||||
|  |  config REGMAP_SPMI | ||||||
|  | +	select REGMAP | ||||||
|  |  	tristate | ||||||
|  |  	depends on SPMI | ||||||
|  |   | ||||||
|  |  config REGMAP_W1 | ||||||
|  | +	select REGMAP | ||||||
|  |  	tristate | ||||||
|  |  	depends on W1 | ||||||
|  |   | ||||||
|  |  config REGMAP_MMIO | ||||||
|  |  	tristate | ||||||
|  | +	select REGMAP | ||||||
|  |   | ||||||
|  |  config REGMAP_IRQ | ||||||
|  | +	select REGMAP | ||||||
|  |  	bool | ||||||
|  |   | ||||||
|  |  config REGMAP_SOUNDWIRE | ||||||
|  | --- a/drivers/base/regmap/Makefile | ||||||
|  | +++ b/drivers/base/regmap/Makefile | ||||||
|  | @@ -2,10 +2,14 @@ | ||||||
|  |  # For include/trace/define_trace.h to include trace.h | ||||||
|  |  CFLAGS_regmap.o := -I$(src) | ||||||
|  |   | ||||||
|  | -obj-$(CONFIG_REGMAP) += regmap.o regcache.o | ||||||
|  | -obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o | ||||||
|  | -obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o | ||||||
|  | -obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o | ||||||
|  | +regmap-core-objs = regmap.o regcache.o regcache-rbtree.o regcache-flat.o | ||||||
|  | +ifdef CONFIG_DEBUG_FS | ||||||
|  | +regmap-core-objs += regmap-debugfs.o | ||||||
|  | +endif | ||||||
|  | +ifdef CONFIG_REGCACHE_COMPRESSED | ||||||
|  | +regmap-core-objs += regcache-lzo.o | ||||||
|  | +endif | ||||||
|  | +obj-$(CONFIG_REGMAP) += regmap-core.o | ||||||
|  |  obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o | ||||||
|  |  obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o | ||||||
|  |  obj-$(CONFIG_REGMAP_SLIMBUS) += regmap-slimbus.o | ||||||
|  | --- a/drivers/base/regmap/regmap.c | ||||||
|  | +++ b/drivers/base/regmap/regmap.c | ||||||
|  | @@ -13,6 +13,7 @@ | ||||||
|  |  #include <linux/device.h> | ||||||
|  |  #include <linux/slab.h> | ||||||
|  |  #include <linux/export.h> | ||||||
|  | +#include <linux/module.h> | ||||||
|  |  #include <linux/mutex.h> | ||||||
|  |  #include <linux/err.h> | ||||||
|  |  #include <linux/of.h> | ||||||
|  | @@ -3039,3 +3040,5 @@ static int __init regmap_initcall(void) | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |  postcore_initcall(regmap_initcall); | ||||||
|  | + | ||||||
|  | +MODULE_LICENSE("GPL"); | ||||||
|  | --- a/include/linux/regmap.h | ||||||
|  | +++ b/include/linux/regmap.h | ||||||
|  | @@ -187,7 +187,7 @@ struct reg_sequence { | ||||||
|  |  	pollret ?: ((cond) ? 0 : -ETIMEDOUT); \ | ||||||
|  |  }) | ||||||
|  |   | ||||||
|  | -#ifdef CONFIG_REGMAP | ||||||
|  | +#if IS_REACHABLE(CONFIG_REGMAP) | ||||||
|  |   | ||||||
|  |  enum regmap_endian { | ||||||
|  |  	/* Unspecified -> 0 -> Backwards compatible default */ | ||||||
| @@ -0,0 +1,60 @@ | |||||||
|  | From fd1799b0bf5efa46dd3e6dfbbf3955564807e508 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Fri, 7 Jul 2017 17:12:51 +0200 | ||||||
|  | Subject: kernel: prevent cryptomgr from pulling in useless extra dependencies for tests that are not run | ||||||
|  |  | ||||||
|  | Reduces kernel size after LZMA by about 5k on MIPS | ||||||
|  |  | ||||||
|  | lede-commit: 044c316167e076479a344c59905e5b435b84a77f | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  crypto/Kconfig   | 13 ++++++------- | ||||||
|  |  crypto/algboss.c |  4 ++++ | ||||||
|  |  2 files changed, 10 insertions(+), 7 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/crypto/Kconfig | ||||||
|  | +++ b/crypto/Kconfig | ||||||
|  | @@ -144,13 +144,13 @@ config CRYPTO_MANAGER | ||||||
|  |  	  cbc(aes). | ||||||
|  |   | ||||||
|  |  config CRYPTO_MANAGER2 | ||||||
|  | -	def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y) | ||||||
|  | -	select CRYPTO_AEAD2 | ||||||
|  | -	select CRYPTO_HASH2 | ||||||
|  | -	select CRYPTO_BLKCIPHER2 | ||||||
|  | -	select CRYPTO_AKCIPHER2 | ||||||
|  | -	select CRYPTO_KPP2 | ||||||
|  | -	select CRYPTO_ACOMP2 | ||||||
|  | +	def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y && !CRYPTO_MANAGER_DISABLE_TESTS) | ||||||
|  | +	select CRYPTO_AEAD2 if !CRYPTO_MANAGER_DISABLE_TESTS | ||||||
|  | +	select CRYPTO_HASH2 if !CRYPTO_MANAGER_DISABLE_TESTS | ||||||
|  | +	select CRYPTO_BLKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS | ||||||
|  | +	select CRYPTO_AKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS | ||||||
|  | +	select CRYPTO_KPP2 if !CRYPTO_MANAGER_DISABLE_TESTS | ||||||
|  | +	select CRYPTO_ACOMP2 if !CRYPTO_MANAGER_DISABLE_TESTS | ||||||
|  |   | ||||||
|  |  config CRYPTO_USER | ||||||
|  |  	tristate "Userspace cryptographic algorithm configuration" | ||||||
|  | @@ -163,7 +163,6 @@ config CRYPTO_USER | ||||||
|  |  config CRYPTO_MANAGER_DISABLE_TESTS | ||||||
|  |  	bool "Disable run-time self tests" | ||||||
|  |  	default y | ||||||
|  | -	depends on CRYPTO_MANAGER2 | ||||||
|  |  	help | ||||||
|  |  	  Disable run-time self tests that normally take place at | ||||||
|  |  	  algorithm registration. | ||||||
|  | --- a/crypto/algboss.c | ||||||
|  | +++ b/crypto/algboss.c | ||||||
|  | @@ -247,8 +247,12 @@ static int cryptomgr_schedule_test(struc | ||||||
|  |  	type = alg->cra_flags; | ||||||
|  |   | ||||||
|  |  	/* Do not test internal algorithms. */ | ||||||
|  | +#ifdef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS | ||||||
|  | +	type |= CRYPTO_ALG_TESTED; | ||||||
|  | +#else | ||||||
|  |  	if (type & CRYPTO_ALG_INTERNAL) | ||||||
|  |  		type |= CRYPTO_ALG_TESTED; | ||||||
|  | +#endif | ||||||
|  |   | ||||||
|  |  	param->type = type; | ||||||
|  |   | ||||||
							
								
								
									
										84
									
								
								target/linux/generic/hack-5.4/280-rfkill-stubs.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								target/linux/generic/hack-5.4/280-rfkill-stubs.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | From 236c1acdfef5958010ac9814a9872e0a46fd78ee Mon Sep 17 00:00:00 2001 | ||||||
|  | From: John Crispin <john@phrozen.org> | ||||||
|  | Date: Fri, 7 Jul 2017 17:13:44 +0200 | ||||||
|  | Subject: rfkill: add fake rfkill support | ||||||
|  |  | ||||||
|  | allow building of modules depending on RFKILL even if RFKILL is not enabled. | ||||||
|  |  | ||||||
|  | Signed-off-by: John Crispin <john@phrozen.org> | ||||||
|  | --- | ||||||
|  |  include/linux/rfkill.h |  2 +- | ||||||
|  |  net/Makefile           |  2 +- | ||||||
|  |  net/rfkill/Kconfig     | 14 +++++++++----- | ||||||
|  |  net/rfkill/Makefile    |  2 +- | ||||||
|  |  4 files changed, 12 insertions(+), 8 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/include/linux/rfkill.h | ||||||
|  | +++ b/include/linux/rfkill.h | ||||||
|  | @@ -64,7 +64,7 @@ struct rfkill_ops { | ||||||
|  |  	int	(*set_block)(void *data, bool blocked); | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) | ||||||
|  | +#if defined(CONFIG_RFKILL_FULL) || defined(CONFIG_RFKILL_FULL_MODULE) | ||||||
|  |  /** | ||||||
|  |   * rfkill_alloc - Allocate rfkill structure | ||||||
|  |   * @name: name of the struct -- the string is not copied internally | ||||||
|  | --- a/net/Makefile | ||||||
|  | +++ b/net/Makefile | ||||||
|  | @@ -53,7 +53,7 @@ obj-$(CONFIG_TIPC)		+= tipc/ | ||||||
|  |  obj-$(CONFIG_NETLABEL)		+= netlabel/ | ||||||
|  |  obj-$(CONFIG_IUCV)		+= iucv/ | ||||||
|  |  obj-$(CONFIG_SMC)		+= smc/ | ||||||
|  | -obj-$(CONFIG_RFKILL)		+= rfkill/ | ||||||
|  | +obj-$(CONFIG_RFKILL_FULL)	+= rfkill/ | ||||||
|  |  obj-$(CONFIG_NET_9P)		+= 9p/ | ||||||
|  |  obj-$(CONFIG_CAIF)		+= caif/ | ||||||
|  |  ifneq ($(CONFIG_DCB),) | ||||||
|  | --- a/net/rfkill/Kconfig | ||||||
|  | +++ b/net/rfkill/Kconfig | ||||||
|  | @@ -1,7 +1,11 @@ | ||||||
|  |  # | ||||||
|  |  # RF switch subsystem configuration | ||||||
|  |  # | ||||||
|  | -menuconfig RFKILL | ||||||
|  | +config RFKILL | ||||||
|  | +	bool | ||||||
|  | +	default y | ||||||
|  | + | ||||||
|  | +menuconfig RFKILL_FULL | ||||||
|  |  	tristate "RF switch subsystem support" | ||||||
|  |  	help | ||||||
|  |  	  Say Y here if you want to have control over RF switches | ||||||
|  | @@ -13,19 +17,19 @@ menuconfig RFKILL | ||||||
|  |  # LED trigger support | ||||||
|  |  config RFKILL_LEDS | ||||||
|  |  	bool | ||||||
|  | -	depends on RFKILL | ||||||
|  | +	depends on RFKILL_FULL | ||||||
|  |  	depends on LEDS_TRIGGERS = y || RFKILL = LEDS_TRIGGERS | ||||||
|  |  	default y | ||||||
|  |   | ||||||
|  |  config RFKILL_INPUT | ||||||
|  |  	bool "RF switch input support" if EXPERT | ||||||
|  | -	depends on RFKILL | ||||||
|  | +	depends on RFKILL_FULL | ||||||
|  |  	depends on INPUT = y || RFKILL = INPUT | ||||||
|  |  	default y if !EXPERT | ||||||
|  |   | ||||||
|  |  config RFKILL_GPIO | ||||||
|  |  	tristate "GPIO RFKILL driver" | ||||||
|  | -	depends on RFKILL | ||||||
|  | +	depends on RFKILL_FULL | ||||||
|  |  	depends on GPIOLIB || COMPILE_TEST | ||||||
|  |  	default n | ||||||
|  |  	help | ||||||
|  | --- a/net/rfkill/Makefile | ||||||
|  | +++ b/net/rfkill/Makefile | ||||||
|  | @@ -4,5 +4,5 @@ | ||||||
|  |   | ||||||
|  |  rfkill-y			+= core.o | ||||||
|  |  rfkill-$(CONFIG_RFKILL_INPUT)	+= input.o | ||||||
|  | -obj-$(CONFIG_RFKILL)		+= rfkill.o | ||||||
|  | +obj-$(CONFIG_RFKILL_FULL)	+= rfkill.o | ||||||
|  |  obj-$(CONFIG_RFKILL_GPIO)	+= rfkill-gpio.o | ||||||
| @@ -0,0 +1,33 @@ | |||||||
|  | From 2579d9b982c7232f9354bcca5262e26a84c38799 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Hauke Mehrtens <hauke@hauke-m.de> | ||||||
|  | Date: Fri, 2 Nov 2018 17:40:32 +0100 | ||||||
|  | Subject: [PATCH] nvmem: make CONFIG_NVMEM tristate again | ||||||
|  |  | ||||||
|  | Only build it in when OF_NET is selected and make it possible to build | ||||||
|  | it as module otherwise. | ||||||
|  |  | ||||||
|  | Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> | ||||||
|  | --- | ||||||
|  |  drivers/nvmem/Kconfig | 2 +- | ||||||
|  |  drivers/of/Kconfig    | 1 + | ||||||
|  |  2 files changed, 2 insertions(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/nvmem/Kconfig | ||||||
|  | +++ b/drivers/nvmem/Kconfig | ||||||
|  | @@ -1,5 +1,5 @@ | ||||||
|  |  menuconfig NVMEM | ||||||
|  | -	bool "NVMEM Support" | ||||||
|  | +	tristate "NVMEM Support" | ||||||
|  |  	help | ||||||
|  |  	  Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... | ||||||
|  |   | ||||||
|  | --- a/drivers/of/Kconfig | ||||||
|  | +++ b/drivers/of/Kconfig | ||||||
|  | @@ -71,6 +71,7 @@ config OF_IRQ | ||||||
|  |   | ||||||
|  |  config OF_NET | ||||||
|  |  	depends on NETDEVICES | ||||||
|  | +	select NVMEM | ||||||
|  |  	def_bool y | ||||||
|  |   | ||||||
|  |  config OF_MDIO | ||||||
| @@ -0,0 +1,66 @@ | |||||||
|  | From: Ben Menchaca <ben.menchaca@qca.qualcomm.com> | ||||||
|  | Date: Fri, 7 Jun 2013 18:35:22 -0500 | ||||||
|  | Subject: MIPS: r4k_cache: use more efficient cache blast | ||||||
|  |  | ||||||
|  | Optimize the compiler output for larger cache blast cases that are | ||||||
|  | common for DMA-based networking. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ben Menchaca <ben.menchaca@qca.qualcomm.com> | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  | --- a/arch/mips/include/asm/r4kcache.h | ||||||
|  | +++ b/arch/mips/include/asm/r4kcache.h | ||||||
|  | @@ -683,16 +683,48 @@ static inline void prot##extra##blast_## | ||||||
|  |  						    unsigned long end)	\ | ||||||
|  |  {									\ | ||||||
|  |  	unsigned long lsize = cpu_##desc##_line_size();			\ | ||||||
|  | +	unsigned long lsize_2 = lsize * 2;				\ | ||||||
|  | +	unsigned long lsize_3 = lsize * 3;				\ | ||||||
|  | +	unsigned long lsize_4 = lsize * 4;				\ | ||||||
|  | +	unsigned long lsize_5 = lsize * 5;				\ | ||||||
|  | +	unsigned long lsize_6 = lsize * 6;				\ | ||||||
|  | +	unsigned long lsize_7 = lsize * 7;				\ | ||||||
|  | +	unsigned long lsize_8 = lsize * 8;				\ | ||||||
|  |  	unsigned long addr = start & ~(lsize - 1);			\ | ||||||
|  | -	unsigned long aend = (end - 1) & ~(lsize - 1);			\ | ||||||
|  | +	unsigned long aend = (end + lsize - 1) & ~(lsize - 1);		\ | ||||||
|  | +	int lines = (aend - addr) / lsize;				\ | ||||||
|  |  									\ | ||||||
|  |  	__##pfx##flush_prologue						\ | ||||||
|  |  									\ | ||||||
|  | -	while (1) {							\ | ||||||
|  | +	while (lines >= 8) {						\ | ||||||
|  | +		prot##cache_op(hitop, addr);				\ | ||||||
|  | +		prot##cache_op(hitop, addr + lsize);			\ | ||||||
|  | +		prot##cache_op(hitop, addr + lsize_2);			\ | ||||||
|  | +		prot##cache_op(hitop, addr + lsize_3);			\ | ||||||
|  | +		prot##cache_op(hitop, addr + lsize_4);			\ | ||||||
|  | +		prot##cache_op(hitop, addr + lsize_5);			\ | ||||||
|  | +		prot##cache_op(hitop, addr + lsize_6);			\ | ||||||
|  | +		prot##cache_op(hitop, addr + lsize_7);			\ | ||||||
|  | +		addr += lsize_8;					\ | ||||||
|  | +		lines -= 8;						\ | ||||||
|  | +	}								\ | ||||||
|  | +									\ | ||||||
|  | +	if (lines & 0x4) {						\ | ||||||
|  | +		prot##cache_op(hitop, addr);				\ | ||||||
|  | +		prot##cache_op(hitop, addr + lsize);			\ | ||||||
|  | +		prot##cache_op(hitop, addr + lsize_2);			\ | ||||||
|  | +		prot##cache_op(hitop, addr + lsize_3);			\ | ||||||
|  | +		addr += lsize_4;					\ | ||||||
|  | +	}								\ | ||||||
|  | +									\ | ||||||
|  | +	if (lines & 0x2) {						\ | ||||||
|  | +		prot##cache_op(hitop, addr);				\ | ||||||
|  | +		prot##cache_op(hitop, addr + lsize);			\ | ||||||
|  | +		addr += lsize_2;					\ | ||||||
|  | +	}								\ | ||||||
|  | +									\ | ||||||
|  | +	if (lines & 0x1) {						\ | ||||||
|  |  		prot##cache_op(hitop, addr);				\ | ||||||
|  | -		if (addr == aend)					\ | ||||||
|  | -			break;						\ | ||||||
|  | -		addr += lsize;						\ | ||||||
|  |  	}								\ | ||||||
|  |  									\ | ||||||
|  |  	__##pfx##flush_epilogue						\ | ||||||
| @@ -0,0 +1,38 @@ | |||||||
|  | From: John Crispin <john@phrozen.org> | ||||||
|  | Subject: hack: kernel: add generic image_cmdline hack to MIPS targets | ||||||
|  |  | ||||||
|  | lede-commit: d59f5b3a987a48508257a0ddbaeadc7909f9f976 | ||||||
|  | Signed-off-by: Gabor Juhos <juhosg@openwrt.org> | ||||||
|  | --- | ||||||
|  |  arch/mips/Kconfig       | 4 ++++ | ||||||
|  |  arch/mips/kernel/head.S | 6 ++++++ | ||||||
|  |  2 files changed, 10 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/arch/mips/Kconfig | ||||||
|  | +++ b/arch/mips/Kconfig | ||||||
|  | @@ -1144,6 +1144,10 @@ config SYNC_R4K | ||||||
|  |  config MIPS_MACHINE | ||||||
|  |  	def_bool n | ||||||
|  |   | ||||||
|  | +config IMAGE_CMDLINE_HACK | ||||||
|  | +	bool "OpenWrt specific image command line hack" | ||||||
|  | +	default n | ||||||
|  | + | ||||||
|  |  config NO_IOPORT_MAP | ||||||
|  |  	def_bool n | ||||||
|  |   | ||||||
|  | --- a/arch/mips/kernel/head.S | ||||||
|  | +++ b/arch/mips/kernel/head.S | ||||||
|  | @@ -79,6 +79,12 @@ FEXPORT(__kernel_entry) | ||||||
|  |  	j	kernel_entry | ||||||
|  |  #endif | ||||||
|  |   | ||||||
|  | +#ifdef CONFIG_IMAGE_CMDLINE_HACK | ||||||
|  | +	.ascii	"CMDLINE:" | ||||||
|  | +EXPORT(__image_cmdline) | ||||||
|  | +	.fill	0x400 | ||||||
|  | +#endif /* CONFIG_IMAGE_CMDLINE_HACK */ | ||||||
|  | + | ||||||
|  |  	__REF | ||||||
|  |   | ||||||
|  |  NESTED(kernel_entry, 16, sp)			# kernel entry point | ||||||
| @@ -0,0 +1,39 @@ | |||||||
|  | From 107c0964cb8db7ca28ac5199426414fdab3c274d Mon Sep 17 00:00:00 2001 | ||||||
|  | From: "Alexandros C. Couloumbis" <alex@ozo.com> | ||||||
|  | Date: Fri, 7 Jul 2017 17:14:51 +0200 | ||||||
|  | Subject: hack: arch: powerpc: drop register save/restore library from modules | ||||||
|  |  | ||||||
|  | Upstream GCC uses a libgcc function for saving/restoring registers. This | ||||||
|  | makes the code bigger, and upstream kernels need to carry that function | ||||||
|  | for every single kernel module. Our GCC is patched to avoid those | ||||||
|  | references, so we can drop the extra bloat for modules. | ||||||
|  |  | ||||||
|  | lede-commit: e8e1084654f50904e6bf77b70b2de3f137d7b3ec | ||||||
|  | Signed-off-by: Alexandros C. Couloumbis <alex@ozo.com> | ||||||
|  | --- | ||||||
|  |  arch/powerpc/Makefile | 1 - | ||||||
|  |  1 file changed, 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/arch/powerpc/Makefile | ||||||
|  | +++ b/arch/powerpc/Makefile | ||||||
|  | @@ -60,20 +60,6 @@ machine-$(CONFIG_PPC64) += 64 | ||||||
|  |  machine-$(CONFIG_CPU_LITTLE_ENDIAN) += le | ||||||
|  |  UTS_MACHINE := $(subst $(space),,$(machine-y)) | ||||||
|  |   | ||||||
|  | -# XXX This needs to be before we override LD below | ||||||
|  | -ifdef CONFIG_PPC32 | ||||||
|  | -KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o | ||||||
|  | -else | ||||||
|  | -KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/powerpc/kernel/module.lds | ||||||
|  | -ifeq ($(call ld-ifversion, -ge, 225000000, y),y) | ||||||
|  | -# Have the linker provide sfpr if possible. | ||||||
|  | -# There is a corresponding test in arch/powerpc/lib/Makefile | ||||||
|  | -KBUILD_LDFLAGS_MODULE += --save-restore-funcs | ||||||
|  | -else | ||||||
|  | -KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o | ||||||
|  | -endif | ||||||
|  | -endif | ||||||
|  | - | ||||||
|  |  ifdef CONFIG_CPU_LITTLE_ENDIAN | ||||||
|  |  KBUILD_CFLAGS	+= -mlittle-endian | ||||||
|  |  KBUILD_LDFLAGS	+= -EL | ||||||
							
								
								
									
										1040
									
								
								target/linux/generic/hack-5.4/531-debloat_lzma.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1040
									
								
								target/linux/generic/hack-5.4/531-debloat_lzma.patch
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | From 2e864386e62e702a343be2507062ee08d5dfc810 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Evan Green <evgreen@chromium.org> | ||||||
|  | Date: Thu, 14 Nov 2019 15:50:07 -0800 | ||||||
|  | Subject: loop: Report EOPNOTSUPP properly | ||||||
|  |  | ||||||
|  | Properly plumb out EOPNOTSUPP from loop driver operations, which may | ||||||
|  | get returned when for instance a discard operation is attempted but not | ||||||
|  | supported by the underlying block device. Before this change, everything | ||||||
|  | was reported in the log as an I/O error, which is scary and not | ||||||
|  | helpful in debugging. | ||||||
|  |  | ||||||
|  | Signed-off-by: Evan Green <evgreen@chromium.org> | ||||||
|  | Reviewed-by: Gwendal Grignou <gwendal@chromium.org> | ||||||
|  | Reviewed-by: Bart Van Assche <bvanassche@acm.org> | ||||||
|  | --- | ||||||
|  |  drivers/block/loop.c | 7 +++++-- | ||||||
|  |  1 file changed, 5 insertions(+), 2 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/block/loop.c | ||||||
|  | +++ b/drivers/block/loop.c | ||||||
|  | @@ -460,7 +460,7 @@ static void lo_complete_rq(struct reques | ||||||
|  |  	if (!cmd->use_aio || cmd->ret < 0 || cmd->ret == blk_rq_bytes(rq) || | ||||||
|  |  	    req_op(rq) != REQ_OP_READ) { | ||||||
|  |  		if (cmd->ret < 0) | ||||||
|  | -			ret = BLK_STS_IOERR; | ||||||
|  | +			ret = errno_to_blk_status(cmd->ret); | ||||||
|  |  		goto end_io; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | @@ -1904,7 +1904,10 @@ static void loop_handle_cmd(struct loop_ | ||||||
|  |   failed: | ||||||
|  |  	/* complete non-aio request */ | ||||||
|  |  	if (!cmd->use_aio || ret) { | ||||||
|  | -		cmd->ret = ret ? -EIO : 0; | ||||||
|  | +		if (ret == -EOPNOTSUPP) | ||||||
|  | +			cmd->ret = ret; | ||||||
|  | +		else | ||||||
|  | +			cmd->ret = ret ? -EIO : 0; | ||||||
|  |  		blk_mq_complete_request(rq); | ||||||
|  |  	} | ||||||
|  |  } | ||||||
| @@ -0,0 +1,101 @@ | |||||||
|  | From 3117c3f45edbcc269baaebd3d13f39b7bf884aa6 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Evan Green <evgreen@chromium.org> | ||||||
|  | Date: Thu, 14 Nov 2019 15:50:08 -0800 | ||||||
|  | Subject: loop: Better discard support for block devices | ||||||
|  |  | ||||||
|  | If the backing device for a loop device is itself a block device, | ||||||
|  | then mirror the "write zeroes" capabilities of the underlying | ||||||
|  | block device into the loop device. Copy this capability into both | ||||||
|  | max_write_zeroes_sectors and max_discard_sectors of the loop device. | ||||||
|  |  | ||||||
|  | The reason for this is that REQ_OP_DISCARD on a loop device translates | ||||||
|  | into blkdev_issue_zeroout(), rather than blkdev_issue_discard(). This | ||||||
|  | presents a consistent interface for loop devices (that discarded data | ||||||
|  | is zeroed), regardless of the backing device type of the loop device. | ||||||
|  | There should be no behavior change for loop devices backed by regular | ||||||
|  | files. | ||||||
|  |  | ||||||
|  | This change fixes blktest block/003, and removes an extraneous | ||||||
|  | error print in block/013 when testing on a loop device backed | ||||||
|  | by a block device that does not support discard. | ||||||
|  |  | ||||||
|  | Signed-off-by: Evan Green <evgreen@chromium.org> | ||||||
|  | Reviewed-by: Gwendal Grignou <gwendal@chromium.org> | ||||||
|  | Reviewed-by: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com> | ||||||
|  | --- | ||||||
|  |  drivers/block/loop.c | 40 +++++++++++++++++++++++++++++----------- | ||||||
|  |  1 file changed, 29 insertions(+), 11 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/block/loop.c | ||||||
|  | +++ b/drivers/block/loop.c | ||||||
|  | @@ -426,11 +426,12 @@ static int lo_fallocate(struct loop_devi | ||||||
|  |  	 * information. | ||||||
|  |  	 */ | ||||||
|  |  	struct file *file = lo->lo_backing_file; | ||||||
|  | +	struct request_queue *q = lo->lo_queue; | ||||||
|  |  	int ret; | ||||||
|  |   | ||||||
|  |  	mode |= FALLOC_FL_KEEP_SIZE; | ||||||
|  |   | ||||||
|  | -	if ((!file->f_op->fallocate) || lo->lo_encrypt_key_size) { | ||||||
|  | +	if (!blk_queue_discard(q)) { | ||||||
|  |  		ret = -EOPNOTSUPP; | ||||||
|  |  		goto out; | ||||||
|  |  	} | ||||||
|  | @@ -863,6 +864,21 @@ static void loop_config_discard(struct l | ||||||
|  |  	struct file *file = lo->lo_backing_file; | ||||||
|  |  	struct inode *inode = file->f_mapping->host; | ||||||
|  |  	struct request_queue *q = lo->lo_queue; | ||||||
|  | +	struct request_queue *backingq; | ||||||
|  | + | ||||||
|  | +	/* | ||||||
|  | +	 * If the backing device is a block device, mirror its zeroing | ||||||
|  | +	 * capability. REQ_OP_DISCARD translates to a zero-out even when backed | ||||||
|  | +	 * by block devices to keep consistent behavior with file-backed loop | ||||||
|  | +	 * devices. | ||||||
|  | +	 */ | ||||||
|  | +	if (S_ISBLK(inode->i_mode) && !lo->lo_encrypt_key_size) { | ||||||
|  | +		backingq = bdev_get_queue(inode->i_bdev); | ||||||
|  | +		blk_queue_max_discard_sectors(q, | ||||||
|  | +			backingq->limits.max_write_zeroes_sectors); | ||||||
|  | + | ||||||
|  | +		blk_queue_max_write_zeroes_sectors(q, | ||||||
|  | +			backingq->limits.max_write_zeroes_sectors); | ||||||
|  |   | ||||||
|  |  	/* | ||||||
|  |  	 * We use punch hole to reclaim the free space used by the | ||||||
|  | @@ -870,22 +886,24 @@ static void loop_config_discard(struct l | ||||||
|  |  	 * encryption is enabled, because it may give an attacker | ||||||
|  |  	 * useful information. | ||||||
|  |  	 */ | ||||||
|  | -	if ((!file->f_op->fallocate) || | ||||||
|  | -	    lo->lo_encrypt_key_size) { | ||||||
|  | +	} else if ((!file->f_op->fallocate) || lo->lo_encrypt_key_size) { | ||||||
|  |  		q->limits.discard_granularity = 0; | ||||||
|  |  		q->limits.discard_alignment = 0; | ||||||
|  |  		blk_queue_max_discard_sectors(q, 0); | ||||||
|  |  		blk_queue_max_write_zeroes_sectors(q, 0); | ||||||
|  | -		blk_queue_flag_clear(QUEUE_FLAG_DISCARD, q); | ||||||
|  | -		return; | ||||||
|  | -	} | ||||||
|  |   | ||||||
|  | -	q->limits.discard_granularity = inode->i_sb->s_blocksize; | ||||||
|  | -	q->limits.discard_alignment = 0; | ||||||
|  | +	} else { | ||||||
|  | +		q->limits.discard_granularity = inode->i_sb->s_blocksize; | ||||||
|  | +		q->limits.discard_alignment = 0; | ||||||
|  |   | ||||||
|  | -	blk_queue_max_discard_sectors(q, UINT_MAX >> 9); | ||||||
|  | -	blk_queue_max_write_zeroes_sectors(q, UINT_MAX >> 9); | ||||||
|  | -	blk_queue_flag_set(QUEUE_FLAG_DISCARD, q); | ||||||
|  | +		blk_queue_max_discard_sectors(q, UINT_MAX >> 9); | ||||||
|  | +		blk_queue_max_write_zeroes_sectors(q, UINT_MAX >> 9); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (q->limits.max_write_zeroes_sectors) | ||||||
|  | +		blk_queue_flag_set(QUEUE_FLAG_DISCARD, q); | ||||||
|  | +	else | ||||||
|  | +		blk_queue_flag_clear(QUEUE_FLAG_DISCARD, q); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void loop_unprepare_queue(struct loop_device *lo) | ||||||
| @@ -0,0 +1,82 @@ | |||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Fri, 7 Jul 2017 17:18:54 +0200 | ||||||
|  | Subject: bridge: only accept EAP locally | ||||||
|  |  | ||||||
|  | When bridging, do not forward EAP frames to other ports, only deliver | ||||||
|  | them locally, regardless of the state. | ||||||
|  |  | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | [add disable_eap_hack sysfs attribute] | ||||||
|  | Signed-off-by: Etienne Champetier <champetier.etienne@gmail.com> | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | --- a/net/bridge/br_input.c | ||||||
|  | +++ b/net/bridge/br_input.c | ||||||
|  | @@ -108,10 +108,14 @@ int br_handle_frame_finish(struct net *n | ||||||
|  |  		} | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | +	BR_INPUT_SKB_CB(skb)->brdev = br->dev; | ||||||
|  | + | ||||||
|  | +	if (skb->protocol == htons(ETH_P_PAE) && !br->disable_eap_hack) | ||||||
|  | +		return br_pass_frame_up(skb); | ||||||
|  | + | ||||||
|  |  	if (p->state == BR_STATE_LEARNING) | ||||||
|  |  		goto drop; | ||||||
|  |   | ||||||
|  | -	BR_INPUT_SKB_CB(skb)->brdev = br->dev; | ||||||
|  |  	BR_INPUT_SKB_CB(skb)->src_port_isolated = !!(p->flags & BR_ISOLATED); | ||||||
|  |   | ||||||
|  |  	if (IS_ENABLED(CONFIG_INET) && | ||||||
|  | --- a/net/bridge/br_private.h | ||||||
|  | +++ b/net/bridge/br_private.h | ||||||
|  | @@ -337,6 +337,8 @@ struct net_bridge { | ||||||
|  |  	u16				group_fwd_mask; | ||||||
|  |  	u16				group_fwd_mask_required; | ||||||
|  |   | ||||||
|  | +	bool				disable_eap_hack; | ||||||
|  | + | ||||||
|  |  	/* STP */ | ||||||
|  |  	bridge_id			designated_root; | ||||||
|  |  	bridge_id			bridge_id; | ||||||
|  | --- a/net/bridge/br_sysfs_br.c | ||||||
|  | +++ b/net/bridge/br_sysfs_br.c | ||||||
|  | @@ -170,6 +170,30 @@ static ssize_t group_fwd_mask_store(stru | ||||||
|  |  } | ||||||
|  |  static DEVICE_ATTR_RW(group_fwd_mask); | ||||||
|  |   | ||||||
|  | +static ssize_t disable_eap_hack_show(struct device *d, | ||||||
|  | +				   struct device_attribute *attr, | ||||||
|  | +				   char *buf) | ||||||
|  | +{ | ||||||
|  | +	struct net_bridge *br = to_bridge(d); | ||||||
|  | +	return sprintf(buf, "%u\n", br->disable_eap_hack); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int set_disable_eap_hack(struct net_bridge *br, unsigned long val) | ||||||
|  | +{ | ||||||
|  | +	br->disable_eap_hack = !!val; | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static ssize_t disable_eap_hack_store(struct device *d, | ||||||
|  | +				    struct device_attribute *attr, | ||||||
|  | +				    const char *buf, | ||||||
|  | +				    size_t len) | ||||||
|  | +{ | ||||||
|  | +	return store_bridge_parm(d, buf, len, set_disable_eap_hack); | ||||||
|  | +} | ||||||
|  | +static DEVICE_ATTR_RW(disable_eap_hack); | ||||||
|  | + | ||||||
|  |  static ssize_t priority_show(struct device *d, struct device_attribute *attr, | ||||||
|  |  			     char *buf) | ||||||
|  |  { | ||||||
|  | @@ -810,6 +834,7 @@ static struct attribute *bridge_attrs[] | ||||||
|  |  	&dev_attr_ageing_time.attr, | ||||||
|  |  	&dev_attr_stp_state.attr, | ||||||
|  |  	&dev_attr_group_fwd_mask.attr, | ||||||
|  | +	&dev_attr_disable_eap_hack.attr, | ||||||
|  |  	&dev_attr_priority.attr, | ||||||
|  |  	&dev_attr_bridge_id.attr, | ||||||
|  |  	&dev_attr_root_id.attr, | ||||||
| @@ -0,0 +1,212 @@ | |||||||
|  | From eda40b8c8c82e0f2789d6bc8bf63846dce2e8f32 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> | ||||||
|  | Date: Sat, 23 Mar 2019 09:29:49 +0000 | ||||||
|  | Subject: [PATCH] netfilter: connmark: introduce set-dscpmark | ||||||
|  |  | ||||||
|  | set-dscpmark is a method of storing the DSCP of an ip packet into | ||||||
|  | conntrack mark.  In combination with a suitable tc filter action | ||||||
|  | (act_ctinfo) DSCP values are able to be stored in the mark on egress and | ||||||
|  | restored on ingress across links that otherwise alter or bleach DSCP. | ||||||
|  |  | ||||||
|  | This is useful for qdiscs such as CAKE which are able to shape according | ||||||
|  | to policies based on DSCP. | ||||||
|  |  | ||||||
|  | Ingress classification is traditionally a challenging task since | ||||||
|  | iptables rules haven't yet run and tc filter/eBPF programs are pre-NAT | ||||||
|  | lookups, hence are unable to see internal IPv4 addresses as used on the | ||||||
|  | typical home masquerading gateway. | ||||||
|  |  | ||||||
|  | x_tables CONNMARK set-dscpmark target solves the problem of storing the | ||||||
|  | DSCP to the conntrack mark in a way suitable for the new act_ctinfo tc | ||||||
|  | action to restore. | ||||||
|  |  | ||||||
|  | The set-dscpmark option accepts 2 parameters, a 32bit 'dscpmask' and a | ||||||
|  | 32bit 'statemask'.  The dscp mask must be 6 contiguous bits and | ||||||
|  | represents the area where the DSCP will be stored in the connmark.  The | ||||||
|  | state mask is a minimum 1 bit length mask that must not overlap with the | ||||||
|  | dscpmask.  It represents a flag which is set when the DSCP has been | ||||||
|  | stored in the conntrack mark. This is useful to implement a 'one shot' | ||||||
|  | iptables based classification where the 'complicated' iptables rules are | ||||||
|  | only run once to classify the connection on initial (egress) packet and | ||||||
|  | subsequent packets are all marked/restored with the same DSCP.  A state | ||||||
|  | mask of zero disables the setting of a status bit/s. | ||||||
|  |  | ||||||
|  | example syntax with a suitably modified iptables user space application: | ||||||
|  |  | ||||||
|  | iptables -A QOS_MARK_eth0 -t mangle -j CONNMARK --set-dscpmark 0xfc000000/0x01000000 | ||||||
|  |  | ||||||
|  | Would store the DSCP in the top 6 bits of the 32bit mark field, and use | ||||||
|  | the LSB of the top byte as the 'DSCP has been stored' marker. | ||||||
|  |  | ||||||
|  | |----0xFC----conntrack mark----000000---| | ||||||
|  | | Bits 31-26 | bit 25 | bit24 |~~~ Bit 0| | ||||||
|  | | DSCP       | unused | flag  |unused   | | ||||||
|  | |-----------------------0x01---000000---| | ||||||
|  |       ^                   ^ | ||||||
|  |       |                   | | ||||||
|  |       ---|             Conditional flag | ||||||
|  |          |             set this when dscp | ||||||
|  | |-ip diffserv-|        stored in mark | ||||||
|  | | 6 bits      | | ||||||
|  | |-------------| | ||||||
|  |  | ||||||
|  | an identically configured tc action to restore looks like: | ||||||
|  |  | ||||||
|  | tc filter show dev eth0 ingress | ||||||
|  | filter parent ffff: protocol all pref 10 u32 chain 0 | ||||||
|  | filter parent ffff: protocol all pref 10 u32 chain 0 fh 800: ht divisor 1 | ||||||
|  | filter parent ffff: protocol all pref 10 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1: not_in_hw | ||||||
|  |   match 00000000/00000000 at 0 | ||||||
|  | 	action order 1: ctinfo zone 0 pipe | ||||||
|  | 	 index 2 ref 1 bind 1 dscp 0xfc000000/0x1000000 | ||||||
|  |  | ||||||
|  | 	action order 2: mirred (Egress Redirect to device ifb4eth0) stolen | ||||||
|  | 	index 1 ref 1 bind 1 | ||||||
|  |  | ||||||
|  | |----0xFC----conntrack mark----000000---| | ||||||
|  | | Bits 31-26 | bit 25 | bit24 |~~~ Bit 0| | ||||||
|  | | DSCP       | unused | flag  |unused   | | ||||||
|  | |-----------------------0x01---000000---| | ||||||
|  |       |                   | | ||||||
|  |       |                   | | ||||||
|  |       ---|             Conditional flag | ||||||
|  |          v             only restore if set | ||||||
|  | |-ip diffserv-| | ||||||
|  | | 6 bits      | | ||||||
|  | |-------------| | ||||||
|  |  | ||||||
|  | Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> | ||||||
|  | --- | ||||||
|  |  include/uapi/linux/netfilter/xt_connmark.h | 10 ++++ | ||||||
|  |  net/netfilter/xt_connmark.c                | 55 ++++++++++++++++++---- | ||||||
|  |  2 files changed, 57 insertions(+), 8 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/include/uapi/linux/netfilter/xt_connmark.h | ||||||
|  | +++ b/include/uapi/linux/netfilter/xt_connmark.h | ||||||
|  | @@ -20,6 +20,11 @@ enum { | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  enum { | ||||||
|  | +	XT_CONNMARK_VALUE = BIT(0), | ||||||
|  | +	XT_CONNMARK_DSCP = BIT(1) | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +enum { | ||||||
|  |  	D_SHIFT_LEFT = 0, | ||||||
|  |  	D_SHIFT_RIGHT, | ||||||
|  |  }; | ||||||
|  | @@ -34,6 +39,11 @@ struct xt_connmark_tginfo2 { | ||||||
|  |  	__u8 shift_dir, shift_bits, mode; | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +struct xt_connmark_tginfo3 { | ||||||
|  | +	__u32 ctmark, ctmask, nfmask; | ||||||
|  | +	__u8 shift_dir, shift_bits, mode, func; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  struct xt_connmark_mtinfo1 { | ||||||
|  |  	__u32 mark, mask; | ||||||
|  |  	__u8 invert; | ||||||
|  | --- a/net/netfilter/xt_connmark.c | ||||||
|  | +++ b/net/netfilter/xt_connmark.c | ||||||
|  | @@ -36,12 +36,13 @@ MODULE_ALIAS("ipt_connmark"); | ||||||
|  |  MODULE_ALIAS("ip6t_connmark"); | ||||||
|  |   | ||||||
|  |  static unsigned int | ||||||
|  | -connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info) | ||||||
|  | +connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo3 *info) | ||||||
|  |  { | ||||||
|  |  	enum ip_conntrack_info ctinfo; | ||||||
|  |  	u_int32_t new_targetmark; | ||||||
|  |  	struct nf_conn *ct; | ||||||
|  |  	u_int32_t newmark; | ||||||
|  | +	u_int8_t dscp; | ||||||
|  |   | ||||||
|  |  	ct = nf_ct_get(skb, &ctinfo); | ||||||
|  |  	if (ct == NULL) | ||||||
|  | @@ -49,12 +50,24 @@ connmark_tg_shift(struct sk_buff *skb, c | ||||||
|  |   | ||||||
|  |  	switch (info->mode) { | ||||||
|  |  	case XT_CONNMARK_SET: | ||||||
|  | -		newmark = (ct->mark & ~info->ctmask) ^ info->ctmark; | ||||||
|  | -		if (info->shift_dir == D_SHIFT_RIGHT) | ||||||
|  | -			newmark >>= info->shift_bits; | ||||||
|  | -		else | ||||||
|  | -			newmark <<= info->shift_bits; | ||||||
|  | +		newmark = ct->mark; | ||||||
|  | +		if (info->func & XT_CONNMARK_VALUE) { | ||||||
|  | +			newmark = (newmark & ~info->ctmask) ^ info->ctmark; | ||||||
|  | +			if (info->shift_dir == D_SHIFT_RIGHT) | ||||||
|  | +				newmark >>= info->shift_bits; | ||||||
|  | +			else | ||||||
|  | +				newmark <<= info->shift_bits; | ||||||
|  | +		} else if (info->func & XT_CONNMARK_DSCP) { | ||||||
|  | +			if (skb->protocol == htons(ETH_P_IP)) | ||||||
|  | +				dscp = ipv4_get_dsfield(ip_hdr(skb)) >> 2; | ||||||
|  | +			else if (skb->protocol == htons(ETH_P_IPV6)) | ||||||
|  | +				dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; | ||||||
|  | +			else	/* protocol doesn't have diffserv */ | ||||||
|  | +				break; | ||||||
|  |   | ||||||
|  | +			newmark = (newmark & ~info->ctmark) | | ||||||
|  | +				  (info->ctmask | (dscp << info->shift_bits)); | ||||||
|  | +		} | ||||||
|  |  		if (ct->mark != newmark) { | ||||||
|  |  			ct->mark = newmark; | ||||||
|  |  			nf_conntrack_event_cache(IPCT_MARK, ct); | ||||||
|  | @@ -93,20 +106,36 @@ static unsigned int | ||||||
|  |  connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) | ||||||
|  |  { | ||||||
|  |  	const struct xt_connmark_tginfo1 *info = par->targinfo; | ||||||
|  | -	const struct xt_connmark_tginfo2 info2 = { | ||||||
|  | +	const struct xt_connmark_tginfo3 info3 = { | ||||||
|  |  		.ctmark	= info->ctmark, | ||||||
|  |  		.ctmask	= info->ctmask, | ||||||
|  |  		.nfmask	= info->nfmask, | ||||||
|  |  		.mode	= info->mode, | ||||||
|  | +		.func	= XT_CONNMARK_VALUE | ||||||
|  |  	}; | ||||||
|  |   | ||||||
|  | -	return connmark_tg_shift(skb, &info2); | ||||||
|  | +	return connmark_tg_shift(skb, &info3); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static unsigned int | ||||||
|  |  connmark_tg_v2(struct sk_buff *skb, const struct xt_action_param *par) | ||||||
|  |  { | ||||||
|  |  	const struct xt_connmark_tginfo2 *info = par->targinfo; | ||||||
|  | +	const struct xt_connmark_tginfo3 info3 = { | ||||||
|  | +		.ctmark	= info->ctmark, | ||||||
|  | +		.ctmask	= info->ctmask, | ||||||
|  | +		.nfmask	= info->nfmask, | ||||||
|  | +		.mode	= info->mode, | ||||||
|  | +		.func	= XT_CONNMARK_VALUE | ||||||
|  | +	}; | ||||||
|  | + | ||||||
|  | +	return connmark_tg_shift(skb, &info3); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static unsigned int | ||||||
|  | +connmark_tg_v3(struct sk_buff *skb, const struct xt_action_param *par) | ||||||
|  | +{ | ||||||
|  | +	const struct xt_connmark_tginfo3 *info = par->targinfo; | ||||||
|  |   | ||||||
|  |  	return connmark_tg_shift(skb, info); | ||||||
|  |  } | ||||||
|  | @@ -177,6 +206,16 @@ static struct xt_target connmark_tg_reg[ | ||||||
|  |  		.targetsize     = sizeof(struct xt_connmark_tginfo2), | ||||||
|  |  		.destroy        = connmark_tg_destroy, | ||||||
|  |  		.me             = THIS_MODULE, | ||||||
|  | +	}, | ||||||
|  | +	{ | ||||||
|  | +		.name           = "CONNMARK", | ||||||
|  | +		.revision       = 3, | ||||||
|  | +		.family         = NFPROTO_UNSPEC, | ||||||
|  | +		.checkentry     = connmark_tg_check, | ||||||
|  | +		.target         = connmark_tg_v3, | ||||||
|  | +		.targetsize     = sizeof(struct xt_connmark_tginfo3), | ||||||
|  | +		.destroy        = connmark_tg_destroy, | ||||||
|  | +		.me             = THIS_MODULE, | ||||||
|  |  	} | ||||||
|  |  }; | ||||||
|  |   | ||||||
							
								
								
									
										70
									
								
								target/linux/generic/hack-5.4/647-netfilter-flow-acct.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								target/linux/generic/hack-5.4/647-netfilter-flow-acct.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | |||||||
|  | --- a/include/net/netfilter/nf_flow_table.h | ||||||
|  | +++ b/include/net/netfilter/nf_flow_table.h | ||||||
|  | @@ -163,6 +163,8 @@ struct nf_flow_table_hw { | ||||||
|  |  int nf_flow_table_hw_register(const struct nf_flow_table_hw *offload); | ||||||
|  |  void nf_flow_table_hw_unregister(const struct nf_flow_table_hw *offload); | ||||||
|  |   | ||||||
|  | +void nf_flow_table_acct(struct flow_offload *flow, struct sk_buff *skb, int dir); | ||||||
|  | + | ||||||
|  |  extern struct work_struct nf_flow_offload_hw_work; | ||||||
|  |   | ||||||
|  |  #define MODULE_ALIAS_NF_FLOWTABLE(family)	\ | ||||||
|  | --- a/net/netfilter/nf_flow_table_core.c | ||||||
|  | +++ b/net/netfilter/nf_flow_table_core.c | ||||||
|  | @@ -11,6 +11,7 @@ | ||||||
|  |  #include <net/netfilter/nf_conntrack.h> | ||||||
|  |  #include <net/netfilter/nf_conntrack_core.h> | ||||||
|  |  #include <net/netfilter/nf_conntrack_tuple.h> | ||||||
|  | +#include <net/netfilter/nf_conntrack_acct.h> | ||||||
|  |   | ||||||
|  |  struct flow_offload_entry { | ||||||
|  |  	struct flow_offload	flow; | ||||||
|  | @@ -149,6 +150,22 @@ void flow_offload_free(struct flow_offlo | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL_GPL(flow_offload_free); | ||||||
|  |   | ||||||
|  | +void nf_flow_table_acct(struct flow_offload *flow, struct sk_buff *skb, int dir) | ||||||
|  | +{ | ||||||
|  | +	struct flow_offload_entry *entry; | ||||||
|  | +	struct nf_conn_acct *acct; | ||||||
|  | + | ||||||
|  | +	entry = container_of(flow, struct flow_offload_entry, flow); | ||||||
|  | +	acct = nf_conn_acct_find(entry->ct); | ||||||
|  | +	if (acct) { | ||||||
|  | +		struct nf_conn_counter *counter = acct->counter; | ||||||
|  | + | ||||||
|  | +		atomic64_inc(&counter[dir].packets); | ||||||
|  | +		atomic64_add(skb->len, &counter[dir].bytes); | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL_GPL(nf_flow_table_acct); | ||||||
|  | + | ||||||
|  |  static u32 flow_offload_hash(const void *data, u32 len, u32 seed) | ||||||
|  |  { | ||||||
|  |  	const struct flow_offload_tuple *tuple = data; | ||||||
|  | --- a/net/netfilter/nf_flow_table_ip.c | ||||||
|  | +++ b/net/netfilter/nf_flow_table_ip.c | ||||||
|  | @@ -11,6 +11,7 @@ | ||||||
|  |  #include <net/ip6_route.h> | ||||||
|  |  #include <net/neighbour.h> | ||||||
|  |  #include <net/netfilter/nf_flow_table.h> | ||||||
|  | + | ||||||
|  |  /* For layer 4 checksum field offset. */ | ||||||
|  |  #include <linux/tcp.h> | ||||||
|  |  #include <linux/udp.h> | ||||||
|  | @@ -267,6 +268,7 @@ nf_flow_offload_ip_hook(void *priv, stru | ||||||
|  |  	skb->dev = outdev; | ||||||
|  |  	nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr); | ||||||
|  |  	skb_dst_set_noref(skb, &rt->dst); | ||||||
|  | +	nf_flow_table_acct(flow, skb, dir); | ||||||
|  |  	neigh_xmit(NEIGH_ARP_TABLE, outdev, &nexthop, skb); | ||||||
|  |   | ||||||
|  |  	return NF_STOLEN; | ||||||
|  | @@ -487,6 +489,7 @@ nf_flow_offload_ipv6_hook(void *priv, st | ||||||
|  |  	skb->dev = outdev; | ||||||
|  |  	nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6); | ||||||
|  |  	skb_dst_set_noref(skb, &rt->dst); | ||||||
|  | +	nf_flow_table_acct(flow, skb, dir); | ||||||
|  |  	neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb); | ||||||
|  |   | ||||||
|  |  	return NF_STOLEN; | ||||||
| @@ -0,0 +1,553 @@ | |||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Tue, 20 Feb 2018 15:56:02 +0100 | ||||||
|  | Subject: [PATCH] netfilter: add xt_OFFLOAD target | ||||||
|  |  | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  create mode 100644 net/netfilter/xt_OFFLOAD.c | ||||||
|  |  | ||||||
|  | --- a/net/ipv4/netfilter/Kconfig | ||||||
|  | +++ b/net/ipv4/netfilter/Kconfig | ||||||
|  | @@ -63,8 +63,6 @@ config NF_TABLES_ARP | ||||||
|  |  	help | ||||||
|  |  	  This option enables the ARP support for nf_tables. | ||||||
|  |   | ||||||
|  | -endif # NF_TABLES | ||||||
|  | - | ||||||
|  |  config NF_FLOW_TABLE_IPV4 | ||||||
|  |  	tristate "Netfilter flow table IPv4 module" | ||||||
|  |  	depends on NF_FLOW_TABLE | ||||||
|  | @@ -73,6 +71,8 @@ config NF_FLOW_TABLE_IPV4 | ||||||
|  |   | ||||||
|  |  	  To compile it as a module, choose M here. | ||||||
|  |   | ||||||
|  | +endif # NF_TABLES | ||||||
|  | + | ||||||
|  |  config NF_DUP_IPV4 | ||||||
|  |  	tristate "Netfilter IPv4 packet duplication to alternate destination" | ||||||
|  |  	depends on !NF_CONNTRACK || NF_CONNTRACK | ||||||
|  | --- a/net/ipv6/netfilter/Kconfig | ||||||
|  | +++ b/net/ipv6/netfilter/Kconfig | ||||||
|  | @@ -80,7 +80,6 @@ config NFT_FIB_IPV6 | ||||||
|  |  	  multicast or blackhole. | ||||||
|  |   | ||||||
|  |  endif # NF_TABLES_IPV6 | ||||||
|  | -endif # NF_TABLES | ||||||
|  |   | ||||||
|  |  config NF_FLOW_TABLE_IPV6 | ||||||
|  |  	tristate "Netfilter flow table IPv6 module" | ||||||
|  | @@ -90,6 +89,8 @@ config NF_FLOW_TABLE_IPV6 | ||||||
|  |   | ||||||
|  |  	  To compile it as a module, choose M here. | ||||||
|  |   | ||||||
|  | +endif # NF_TABLES | ||||||
|  | + | ||||||
|  |  config NF_DUP_IPV6 | ||||||
|  |  	tristate "Netfilter IPv6 packet duplication to alternate destination" | ||||||
|  |  	depends on !NF_CONNTRACK || NF_CONNTRACK | ||||||
|  | --- a/net/netfilter/Kconfig | ||||||
|  | +++ b/net/netfilter/Kconfig | ||||||
|  | @@ -693,8 +693,6 @@ config NFT_FIB_NETDEV | ||||||
|  |   | ||||||
|  |  endif # NF_TABLES_NETDEV | ||||||
|  |   | ||||||
|  | -endif # NF_TABLES | ||||||
|  | - | ||||||
|  |  config NF_FLOW_TABLE_INET | ||||||
|  |  	tristate "Netfilter flow table mixed IPv4/IPv6 module" | ||||||
|  |  	depends on NF_FLOW_TABLE | ||||||
|  | @@ -703,11 +701,12 @@ config NF_FLOW_TABLE_INET | ||||||
|  |   | ||||||
|  |  	  To compile it as a module, choose M here. | ||||||
|  |   | ||||||
|  | +endif # NF_TABLES | ||||||
|  | + | ||||||
|  |  config NF_FLOW_TABLE | ||||||
|  |  	tristate "Netfilter flow table module" | ||||||
|  |  	depends on NETFILTER_INGRESS | ||||||
|  |  	depends on NF_CONNTRACK | ||||||
|  | -	depends on NF_TABLES | ||||||
|  |  	help | ||||||
|  |  	  This option adds the flow table core infrastructure. | ||||||
|  |   | ||||||
|  | @@ -996,6 +995,15 @@ config NETFILTER_XT_TARGET_NOTRACK | ||||||
|  |  	depends on NETFILTER_ADVANCED | ||||||
|  |  	select NETFILTER_XT_TARGET_CT | ||||||
|  |   | ||||||
|  | +config NETFILTER_XT_TARGET_FLOWOFFLOAD | ||||||
|  | +	tristate '"FLOWOFFLOAD" target support' | ||||||
|  | +	depends on NF_FLOW_TABLE | ||||||
|  | +	depends on NETFILTER_INGRESS | ||||||
|  | +	help | ||||||
|  | +	  This option adds a `FLOWOFFLOAD' target, which uses the nf_flow_offload | ||||||
|  | +	  module to speed up processing of packets by bypassing the usual | ||||||
|  | +	  netfilter chains | ||||||
|  | + | ||||||
|  |  config NETFILTER_XT_TARGET_RATEEST | ||||||
|  |  	tristate '"RATEEST" target support' | ||||||
|  |  	depends on NETFILTER_ADVANCED | ||||||
|  | --- a/net/netfilter/Makefile | ||||||
|  | +++ b/net/netfilter/Makefile | ||||||
|  | @@ -144,6 +144,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIF | ||||||
|  |  obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o | ||||||
|  |  obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o | ||||||
|  |  obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o | ||||||
|  | +obj-$(CONFIG_NETFILTER_XT_TARGET_FLOWOFFLOAD) += xt_FLOWOFFLOAD.o | ||||||
|  |  obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o | ||||||
|  |  obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o | ||||||
|  |  obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/net/netfilter/xt_FLOWOFFLOAD.c | ||||||
|  | @@ -0,0 +1,422 @@ | ||||||
|  | +/* | ||||||
|  | + * Copyright (C) 2018 Felix Fietkau <nbd@nbd.name> | ||||||
|  | + * | ||||||
|  | + * 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/init.h> | ||||||
|  | +#include <linux/netfilter.h> | ||||||
|  | +#include <linux/netfilter/xt_FLOWOFFLOAD.h> | ||||||
|  | +#include <net/ip.h> | ||||||
|  | +#include <net/netfilter/nf_conntrack.h> | ||||||
|  | +#include <net/netfilter/nf_conntrack_extend.h> | ||||||
|  | +#include <net/netfilter/nf_conntrack_helper.h> | ||||||
|  | +#include <net/netfilter/nf_flow_table.h> | ||||||
|  | + | ||||||
|  | +static struct nf_flowtable nf_flowtable; | ||||||
|  | +static HLIST_HEAD(hooks); | ||||||
|  | +static DEFINE_SPINLOCK(hooks_lock); | ||||||
|  | +static struct delayed_work hook_work; | ||||||
|  | + | ||||||
|  | +struct xt_flowoffload_hook { | ||||||
|  | +	struct hlist_node list; | ||||||
|  | +	struct nf_hook_ops ops; | ||||||
|  | +	struct net *net; | ||||||
|  | +	bool registered; | ||||||
|  | +	bool used; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static unsigned int | ||||||
|  | +xt_flowoffload_net_hook(void *priv, struct sk_buff *skb, | ||||||
|  | +			  const struct nf_hook_state *state) | ||||||
|  | +{ | ||||||
|  | +	switch (skb->protocol) { | ||||||
|  | +	case htons(ETH_P_IP): | ||||||
|  | +		return nf_flow_offload_ip_hook(priv, skb, state); | ||||||
|  | +	case htons(ETH_P_IPV6): | ||||||
|  | +		return nf_flow_offload_ipv6_hook(priv, skb, state); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return NF_ACCEPT; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int | ||||||
|  | +xt_flowoffload_create_hook(struct net_device *dev) | ||||||
|  | +{ | ||||||
|  | +	struct xt_flowoffload_hook *hook; | ||||||
|  | +	struct nf_hook_ops *ops; | ||||||
|  | + | ||||||
|  | +	hook = kzalloc(sizeof(*hook), GFP_ATOMIC); | ||||||
|  | +	if (!hook) | ||||||
|  | +		return -ENOMEM; | ||||||
|  | + | ||||||
|  | +	ops = &hook->ops; | ||||||
|  | +	ops->pf = NFPROTO_NETDEV; | ||||||
|  | +	ops->hooknum = NF_NETDEV_INGRESS; | ||||||
|  | +	ops->priority = 10; | ||||||
|  | +	ops->priv = &nf_flowtable; | ||||||
|  | +	ops->hook = xt_flowoffload_net_hook; | ||||||
|  | +	ops->dev = dev; | ||||||
|  | + | ||||||
|  | +	hlist_add_head(&hook->list, &hooks); | ||||||
|  | +	mod_delayed_work(system_power_efficient_wq, &hook_work, 0); | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static struct xt_flowoffload_hook * | ||||||
|  | +flow_offload_lookup_hook(struct net_device *dev) | ||||||
|  | +{ | ||||||
|  | +	struct xt_flowoffload_hook *hook; | ||||||
|  | + | ||||||
|  | +	hlist_for_each_entry(hook, &hooks, list) { | ||||||
|  | +		if (hook->ops.dev == dev) | ||||||
|  | +			return hook; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return NULL; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void | ||||||
|  | +xt_flowoffload_check_device(struct net_device *dev) | ||||||
|  | +{ | ||||||
|  | +	struct xt_flowoffload_hook *hook; | ||||||
|  | + | ||||||
|  | +	spin_lock_bh(&hooks_lock); | ||||||
|  | +	hook = flow_offload_lookup_hook(dev); | ||||||
|  | +	if (hook) | ||||||
|  | +		hook->used = true; | ||||||
|  | +	else | ||||||
|  | +		xt_flowoffload_create_hook(dev); | ||||||
|  | +	spin_unlock_bh(&hooks_lock); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void | ||||||
|  | +xt_flowoffload_register_hooks(void) | ||||||
|  | +{ | ||||||
|  | +	struct xt_flowoffload_hook *hook; | ||||||
|  | + | ||||||
|  | +restart: | ||||||
|  | +	hlist_for_each_entry(hook, &hooks, list) { | ||||||
|  | +		if (hook->registered) | ||||||
|  | +			continue; | ||||||
|  | + | ||||||
|  | +		hook->registered = true; | ||||||
|  | +		hook->net = dev_net(hook->ops.dev); | ||||||
|  | +		spin_unlock_bh(&hooks_lock); | ||||||
|  | +		nf_register_net_hook(hook->net, &hook->ops); | ||||||
|  | +		spin_lock_bh(&hooks_lock); | ||||||
|  | +		goto restart; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void | ||||||
|  | +xt_flowoffload_cleanup_hooks(void) | ||||||
|  | +{ | ||||||
|  | +	struct xt_flowoffload_hook *hook; | ||||||
|  | + | ||||||
|  | +restart: | ||||||
|  | +	hlist_for_each_entry(hook, &hooks, list) { | ||||||
|  | +		if (hook->used || !hook->registered) | ||||||
|  | +			continue; | ||||||
|  | + | ||||||
|  | +		hlist_del(&hook->list); | ||||||
|  | +		spin_unlock_bh(&hooks_lock); | ||||||
|  | +		nf_unregister_net_hook(hook->net, &hook->ops); | ||||||
|  | +		kfree(hook); | ||||||
|  | +		spin_lock_bh(&hooks_lock); | ||||||
|  | +		goto restart; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void | ||||||
|  | +xt_flowoffload_check_hook(struct flow_offload *flow, void *data) | ||||||
|  | +{ | ||||||
|  | +	struct flow_offload_tuple *tuple = &flow->tuplehash[0].tuple; | ||||||
|  | +	struct xt_flowoffload_hook *hook; | ||||||
|  | +	bool *found = data; | ||||||
|  | + | ||||||
|  | +	spin_lock_bh(&hooks_lock); | ||||||
|  | +	hlist_for_each_entry(hook, &hooks, list) { | ||||||
|  | +		if (hook->ops.dev->ifindex != tuple->iifidx && | ||||||
|  | +		    hook->ops.dev->ifindex != tuple->oifidx) | ||||||
|  | +			continue; | ||||||
|  | + | ||||||
|  | +		hook->used = true; | ||||||
|  | +		*found = true; | ||||||
|  | +	} | ||||||
|  | +	spin_unlock_bh(&hooks_lock); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void | ||||||
|  | +xt_flowoffload_hook_work(struct work_struct *work) | ||||||
|  | +{ | ||||||
|  | +	struct xt_flowoffload_hook *hook; | ||||||
|  | +	bool found = false; | ||||||
|  | +	int err; | ||||||
|  | + | ||||||
|  | +	spin_lock_bh(&hooks_lock); | ||||||
|  | +	xt_flowoffload_register_hooks(); | ||||||
|  | +	hlist_for_each_entry(hook, &hooks, list) | ||||||
|  | +		hook->used = false; | ||||||
|  | +	spin_unlock_bh(&hooks_lock); | ||||||
|  | + | ||||||
|  | +	err = nf_flow_table_iterate(&nf_flowtable, xt_flowoffload_check_hook, | ||||||
|  | +				    &found); | ||||||
|  | +	if (err && err != -EAGAIN) | ||||||
|  | +	    goto out; | ||||||
|  | + | ||||||
|  | +	spin_lock_bh(&hooks_lock); | ||||||
|  | +	xt_flowoffload_cleanup_hooks(); | ||||||
|  | +	spin_unlock_bh(&hooks_lock); | ||||||
|  | + | ||||||
|  | +out: | ||||||
|  | +	if (found) | ||||||
|  | +		queue_delayed_work(system_power_efficient_wq, &hook_work, HZ); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static bool | ||||||
|  | +xt_flowoffload_skip(struct sk_buff *skb, int family) | ||||||
|  | +{ | ||||||
|  | +	if (skb_sec_path(skb)) | ||||||
|  | +		return true; | ||||||
|  | + | ||||||
|  | +	if (family == NFPROTO_IPV4) { | ||||||
|  | +		const struct ip_options *opt = &(IPCB(skb)->opt); | ||||||
|  | + | ||||||
|  | +		if (unlikely(opt->optlen)) | ||||||
|  | +			return true; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return false; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static struct dst_entry * | ||||||
|  | +xt_flowoffload_dst(const struct nf_conn *ct, enum ip_conntrack_dir dir, | ||||||
|  | +		   const struct xt_action_param *par, int ifindex) | ||||||
|  | +{ | ||||||
|  | +	struct dst_entry *dst = NULL; | ||||||
|  | +	struct flowi fl; | ||||||
|  | + | ||||||
|  | +	memset(&fl, 0, sizeof(fl)); | ||||||
|  | +	switch (xt_family(par)) { | ||||||
|  | +	case NFPROTO_IPV4: | ||||||
|  | +		fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip; | ||||||
|  | +		fl.u.ip4.flowi4_oif = ifindex; | ||||||
|  | +		break; | ||||||
|  | +	case NFPROTO_IPV6: | ||||||
|  | +		fl.u.ip6.saddr = ct->tuplehash[dir].tuple.dst.u3.in6; | ||||||
|  | +		fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6; | ||||||
|  | +		fl.u.ip6.flowi6_oif = ifindex; | ||||||
|  | +		break; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	nf_route(xt_net(par), &dst, &fl, false, xt_family(par)); | ||||||
|  | + | ||||||
|  | +	return dst; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int | ||||||
|  | +xt_flowoffload_route(struct sk_buff *skb, const struct nf_conn *ct, | ||||||
|  | +		   const struct xt_action_param *par, | ||||||
|  | +		   struct nf_flow_route *route, enum ip_conntrack_dir dir) | ||||||
|  | +{ | ||||||
|  | +	struct dst_entry *this_dst, *other_dst; | ||||||
|  | + | ||||||
|  | +	this_dst = xt_flowoffload_dst(ct, !dir, par, xt_out(par)->ifindex); | ||||||
|  | +	other_dst = xt_flowoffload_dst(ct, dir, par, xt_in(par)->ifindex); | ||||||
|  | + | ||||||
|  | +	route->tuple[dir].dst		= this_dst; | ||||||
|  | +	route->tuple[!dir].dst		= other_dst; | ||||||
|  | + | ||||||
|  | +	if (!this_dst || !other_dst) | ||||||
|  | +		return -ENOENT; | ||||||
|  | + | ||||||
|  | +	if (dst_xfrm(this_dst) || dst_xfrm(other_dst)) | ||||||
|  | +		return -EINVAL; | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static unsigned int | ||||||
|  | +flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par) | ||||||
|  | +{ | ||||||
|  | +	const struct xt_flowoffload_target_info *info = par->targinfo; | ||||||
|  | +	struct tcphdr _tcph, *tcph = NULL; | ||||||
|  | +	enum ip_conntrack_info ctinfo; | ||||||
|  | +	enum ip_conntrack_dir dir; | ||||||
|  | +	struct nf_flow_route route; | ||||||
|  | +	struct flow_offload *flow = NULL; | ||||||
|  | +	struct nf_conn *ct; | ||||||
|  | +	struct net *net; | ||||||
|  | + | ||||||
|  | +	if (xt_flowoffload_skip(skb, xt_family(par))) | ||||||
|  | +		return XT_CONTINUE; | ||||||
|  | + | ||||||
|  | +	ct = nf_ct_get(skb, &ctinfo); | ||||||
|  | +	if (ct == NULL) | ||||||
|  | +		return XT_CONTINUE; | ||||||
|  | + | ||||||
|  | +	switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) { | ||||||
|  | +	case IPPROTO_TCP: | ||||||
|  | +		if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) | ||||||
|  | +			return XT_CONTINUE; | ||||||
|  | + | ||||||
|  | +		tcph = skb_header_pointer(skb, par->thoff, | ||||||
|  | +					  sizeof(_tcph), &_tcph); | ||||||
|  | +		if (unlikely(!tcph || tcph->fin || tcph->rst)) | ||||||
|  | +			return XT_CONTINUE; | ||||||
|  | +		break; | ||||||
|  | +	case IPPROTO_UDP: | ||||||
|  | +		break; | ||||||
|  | +	default: | ||||||
|  | +		return XT_CONTINUE; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) || | ||||||
|  | +	    ct->status & IPS_SEQ_ADJUST) | ||||||
|  | +		return XT_CONTINUE; | ||||||
|  | + | ||||||
|  | +	if (!nf_ct_is_confirmed(ct)) | ||||||
|  | +		return XT_CONTINUE; | ||||||
|  | + | ||||||
|  | +	if (!xt_in(par) || !xt_out(par)) | ||||||
|  | +		return XT_CONTINUE; | ||||||
|  | + | ||||||
|  | +	if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status)) | ||||||
|  | +		return XT_CONTINUE; | ||||||
|  | + | ||||||
|  | +	dir = CTINFO2DIR(ctinfo); | ||||||
|  | + | ||||||
|  | +	if (xt_flowoffload_route(skb, ct, par, &route, dir) == 0) | ||||||
|  | +		flow = flow_offload_alloc(ct, &route); | ||||||
|  | + | ||||||
|  | +	dst_release(route.tuple[dir].dst); | ||||||
|  | +	dst_release(route.tuple[!dir].dst); | ||||||
|  | + | ||||||
|  | +	if (!flow) | ||||||
|  | +		goto err_flow_route; | ||||||
|  | + | ||||||
|  | +	if (tcph) { | ||||||
|  | +		ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; | ||||||
|  | +		ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (flow_offload_add(&nf_flowtable, flow) < 0) | ||||||
|  | +		goto err_flow_add; | ||||||
|  | + | ||||||
|  | +	xt_flowoffload_check_device(xt_in(par)); | ||||||
|  | +	xt_flowoffload_check_device(xt_out(par)); | ||||||
|  | + | ||||||
|  | +	net = read_pnet(&nf_flowtable.ft_net); | ||||||
|  | +	if (!net) | ||||||
|  | +		write_pnet(&nf_flowtable.ft_net, xt_net(par)); | ||||||
|  | + | ||||||
|  | +	if (info->flags & XT_FLOWOFFLOAD_HW) | ||||||
|  | +		nf_flow_offload_hw_add(xt_net(par), flow, ct); | ||||||
|  | + | ||||||
|  | +	return XT_CONTINUE; | ||||||
|  | + | ||||||
|  | +err_flow_add: | ||||||
|  | +	flow_offload_free(flow); | ||||||
|  | +err_flow_route: | ||||||
|  | +	clear_bit(IPS_OFFLOAD_BIT, &ct->status); | ||||||
|  | +	return XT_CONTINUE; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +static int flowoffload_chk(const struct xt_tgchk_param *par) | ||||||
|  | +{ | ||||||
|  | +	struct xt_flowoffload_target_info *info = par->targinfo; | ||||||
|  | + | ||||||
|  | +	if (info->flags & ~XT_FLOWOFFLOAD_MASK) | ||||||
|  | +		return -EINVAL; | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static struct xt_target offload_tg_reg __read_mostly = { | ||||||
|  | +	.family		= NFPROTO_UNSPEC, | ||||||
|  | +	.name		= "FLOWOFFLOAD", | ||||||
|  | +	.revision	= 0, | ||||||
|  | +	.targetsize	= sizeof(struct xt_flowoffload_target_info), | ||||||
|  | +	.usersize	= sizeof(struct xt_flowoffload_target_info), | ||||||
|  | +	.checkentry	= flowoffload_chk, | ||||||
|  | +	.target		= flowoffload_tg, | ||||||
|  | +	.me		= THIS_MODULE, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int xt_flowoffload_table_init(struct nf_flowtable *table) | ||||||
|  | +{ | ||||||
|  | +	table->flags = NF_FLOWTABLE_F_HW; | ||||||
|  | +	nf_flow_table_init(table); | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void xt_flowoffload_table_cleanup(struct nf_flowtable *table) | ||||||
|  | +{ | ||||||
|  | +	nf_flow_table_free(table); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int flow_offload_netdev_event(struct notifier_block *this, | ||||||
|  | +				     unsigned long event, void *ptr) | ||||||
|  | +{ | ||||||
|  | +	struct xt_flowoffload_hook *hook = NULL; | ||||||
|  | +	struct net_device *dev = netdev_notifier_info_to_dev(ptr); | ||||||
|  | + | ||||||
|  | +	if (event != NETDEV_UNREGISTER) | ||||||
|  | +		return NOTIFY_DONE; | ||||||
|  | + | ||||||
|  | +	spin_lock_bh(&hooks_lock); | ||||||
|  | +	hook = flow_offload_lookup_hook(dev); | ||||||
|  | +	if (hook) { | ||||||
|  | +		hlist_del(&hook->list); | ||||||
|  | +	} | ||||||
|  | +	spin_unlock_bh(&hooks_lock); | ||||||
|  | +	if (hook) { | ||||||
|  | +		nf_unregister_net_hook(hook->net, &hook->ops); | ||||||
|  | +		kfree(hook); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	nf_flow_table_cleanup(dev_net(dev), dev); | ||||||
|  | + | ||||||
|  | +	return NOTIFY_DONE; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static struct notifier_block flow_offload_netdev_notifier = { | ||||||
|  | +	.notifier_call	= flow_offload_netdev_event, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int __init xt_flowoffload_tg_init(void) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	register_netdevice_notifier(&flow_offload_netdev_notifier); | ||||||
|  | + | ||||||
|  | +	INIT_DELAYED_WORK(&hook_work, xt_flowoffload_hook_work); | ||||||
|  | + | ||||||
|  | +	ret = xt_flowoffload_table_init(&nf_flowtable); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	ret = xt_register_target(&offload_tg_reg); | ||||||
|  | +	if (ret) | ||||||
|  | +		xt_flowoffload_table_cleanup(&nf_flowtable); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void __exit xt_flowoffload_tg_exit(void) | ||||||
|  | +{ | ||||||
|  | +	xt_unregister_target(&offload_tg_reg); | ||||||
|  | +	xt_flowoffload_table_cleanup(&nf_flowtable); | ||||||
|  | +	unregister_netdevice_notifier(&flow_offload_netdev_notifier); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +MODULE_LICENSE("GPL"); | ||||||
|  | +module_init(xt_flowoffload_tg_init); | ||||||
|  | +module_exit(xt_flowoffload_tg_exit); | ||||||
|  | --- a/net/netfilter/nf_flow_table_core.c | ||||||
|  | +++ b/net/netfilter/nf_flow_table_core.c | ||||||
|  | @@ -6,7 +6,6 @@ | ||||||
|  |  #include <linux/netdevice.h> | ||||||
|  |  #include <net/ip.h> | ||||||
|  |  #include <net/ip6_route.h> | ||||||
|  | -#include <net/netfilter/nf_tables.h> | ||||||
|  |  #include <net/netfilter/nf_flow_table.h> | ||||||
|  |  #include <net/netfilter/nf_conntrack.h> | ||||||
|  |  #include <net/netfilter/nf_conntrack_core.h> | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h | ||||||
|  | @@ -0,0 +1,17 @@ | ||||||
|  | +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||||||
|  | +#ifndef _XT_FLOWOFFLOAD_H | ||||||
|  | +#define _XT_FLOWOFFLOAD_H | ||||||
|  | + | ||||||
|  | +#include <linux/types.h> | ||||||
|  | + | ||||||
|  | +enum { | ||||||
|  | +	XT_FLOWOFFLOAD_HW	= 1 << 0, | ||||||
|  | + | ||||||
|  | +	XT_FLOWOFFLOAD_MASK	= XT_FLOWOFFLOAD_HW | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +struct xt_flowoffload_target_info { | ||||||
|  | +	__u32 flags; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +#endif /* _XT_FLOWOFFLOAD_H */ | ||||||
							
								
								
									
										24
									
								
								target/linux/generic/hack-5.4/651-wireless_mesh_header.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								target/linux/generic/hack-5.4/651-wireless_mesh_header.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | From 6d3bc769657b0ee7c7506dad9911111c4226a7ea Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Imre Kaloz <kaloz@openwrt.org> | ||||||
|  | Date: Fri, 7 Jul 2017 17:21:05 +0200 | ||||||
|  | Subject: mac80211: increase wireless mesh header size | ||||||
|  |  | ||||||
|  | lede-commit 3d4466cfd8f75f717efdb1f96fdde3c70d865fc1 | ||||||
|  | Signed-off-by: Imre Kaloz <kaloz@openwrt.org> | ||||||
|  | --- | ||||||
|  |  include/linux/netdevice.h | 4 ++-- | ||||||
|  |  1 file changed, 2 insertions(+), 2 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/include/linux/netdevice.h | ||||||
|  | +++ b/include/linux/netdevice.h | ||||||
|  | @@ -140,8 +140,8 @@ static inline bool dev_xmit_complete(int | ||||||
|  |   | ||||||
|  |  #if defined(CONFIG_HYPERV_NET) | ||||||
|  |  # define LL_MAX_HEADER 128 | ||||||
|  | -#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) | ||||||
|  | -# if defined(CONFIG_MAC80211_MESH) | ||||||
|  | +#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) || 1 | ||||||
|  | +# if defined(CONFIG_MAC80211_MESH) || 1 | ||||||
|  |  #  define LL_MAX_HEADER 128 | ||||||
|  |  # else | ||||||
|  |  #  define LL_MAX_HEADER 96 | ||||||
							
								
								
									
										27
									
								
								target/linux/generic/hack-5.4/660-fq_codel_defaults.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								target/linux/generic/hack-5.4/660-fq_codel_defaults.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | From a6ccb238939b25851474a279b20367fd24a0e816 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Fri, 7 Jul 2017 17:21:53 +0200 | ||||||
|  | Subject:  hack: net: fq_codel: tune defaults for small devices | ||||||
|  |  | ||||||
|  | Assume that x86_64 devices always have a big memory and do not need this  | ||||||
|  | optimization compared to devices with only 32 MB or 64 MB RAM. | ||||||
|  |  | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  net/sched/sch_fq_codel.c | 2 +- | ||||||
|  |  1 file changed, 1 insertion(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/net/sched/sch_fq_codel.c | ||||||
|  | +++ b/net/sched/sch_fq_codel.c | ||||||
|  | @@ -474,7 +474,11 @@ static int fq_codel_init(struct Qdisc *s | ||||||
|  |   | ||||||
|  |  	sch->limit = 10*1024; | ||||||
|  |  	q->flows_cnt = 1024; | ||||||
|  | +#ifdef CONFIG_X86_64 | ||||||
|  |  	q->memory_limit = 32 << 20; /* 32 MBytes */ | ||||||
|  | +#else | ||||||
|  | +	q->memory_limit = 4 << 20; /* 4 MBytes */ | ||||||
|  | +#endif | ||||||
|  |  	q->drop_batch_size = 64; | ||||||
|  |  	q->quantum = psched_mtu(qdisc_dev(sch)); | ||||||
|  |  	INIT_LIST_HEAD(&q->new_flows); | ||||||
| @@ -0,0 +1,94 @@ | |||||||
|  | From 1d418f7e88035ed7a94073f6354246c66e9193e9 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Fri, 7 Jul 2017 17:22:58 +0200 | ||||||
|  | Subject: fq_codel: switch default qdisc from pfifo_fast to fq_codel and remove pfifo_fast | ||||||
|  |  | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  include/net/sch_generic.h | 3 ++- | ||||||
|  |  net/sched/Kconfig         | 3 ++- | ||||||
|  |  net/sched/sch_api.c       | 2 +- | ||||||
|  |  net/sched/sch_fq_codel.c  | 3 ++- | ||||||
|  |  net/sched/sch_generic.c   | 4 ++-- | ||||||
|  |  5 files changed, 9 insertions(+), 6 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/include/net/sch_generic.h | ||||||
|  | +++ b/include/net/sch_generic.h | ||||||
|  | @@ -487,12 +487,13 @@ extern struct Qdisc_ops noop_qdisc_ops; | ||||||
|  |  extern struct Qdisc_ops pfifo_fast_ops; | ||||||
|  |  extern struct Qdisc_ops mq_qdisc_ops; | ||||||
|  |  extern struct Qdisc_ops noqueue_qdisc_ops; | ||||||
|  | +extern struct Qdisc_ops fq_codel_qdisc_ops; | ||||||
|  |  extern const struct Qdisc_ops *default_qdisc_ops; | ||||||
|  |  static inline const struct Qdisc_ops * | ||||||
|  |  get_default_qdisc_ops(const struct net_device *dev, int ntx) | ||||||
|  |  { | ||||||
|  |  	return ntx < dev->real_num_tx_queues ? | ||||||
|  | -			default_qdisc_ops : &pfifo_fast_ops; | ||||||
|  | +			default_qdisc_ops : &fq_codel_qdisc_ops; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  struct Qdisc_class_common { | ||||||
|  | --- a/net/sched/Kconfig | ||||||
|  | +++ b/net/sched/Kconfig | ||||||
|  | @@ -3,8 +3,9 @@ | ||||||
|  |  # | ||||||
|  |   | ||||||
|  |  menuconfig NET_SCHED | ||||||
|  | -	bool "QoS and/or fair queueing" | ||||||
|  | +	def_bool y | ||||||
|  |  	select NET_SCH_FIFO | ||||||
|  | +	select NET_SCH_FQ_CODEL | ||||||
|  |  	---help--- | ||||||
|  |  	  When the kernel has several packets to send out over a network | ||||||
|  |  	  device, it has to decide which ones to send first, which ones to | ||||||
|  | --- a/net/sched/sch_api.c | ||||||
|  | +++ b/net/sched/sch_api.c | ||||||
|  | @@ -2162,7 +2162,7 @@ static int __init pktsched_init(void) | ||||||
|  |  		return err; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	register_qdisc(&pfifo_fast_ops); | ||||||
|  | +	register_qdisc(&fq_codel_qdisc_ops); | ||||||
|  |  	register_qdisc(&pfifo_qdisc_ops); | ||||||
|  |  	register_qdisc(&bfifo_qdisc_ops); | ||||||
|  |  	register_qdisc(&pfifo_head_drop_qdisc_ops); | ||||||
|  | --- a/net/sched/sch_fq_codel.c | ||||||
|  | +++ b/net/sched/sch_fq_codel.c | ||||||
|  | @@ -714,7 +714,7 @@ static const struct Qdisc_class_ops fq_c | ||||||
|  |  	.walk		=	fq_codel_walk, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | -static struct Qdisc_ops fq_codel_qdisc_ops __read_mostly = { | ||||||
|  | +struct Qdisc_ops fq_codel_qdisc_ops __read_mostly = { | ||||||
|  |  	.cl_ops		=	&fq_codel_class_ops, | ||||||
|  |  	.id		=	"fq_codel", | ||||||
|  |  	.priv_size	=	sizeof(struct fq_codel_sched_data), | ||||||
|  | @@ -729,6 +729,7 @@ static struct Qdisc_ops fq_codel_qdisc_o | ||||||
|  |  	.dump_stats =	fq_codel_dump_stats, | ||||||
|  |  	.owner		=	THIS_MODULE, | ||||||
|  |  }; | ||||||
|  | +EXPORT_SYMBOL(fq_codel_qdisc_ops); | ||||||
|  |   | ||||||
|  |  static int __init fq_codel_module_init(void) | ||||||
|  |  { | ||||||
|  | --- a/net/sched/sch_generic.c | ||||||
|  | +++ b/net/sched/sch_generic.c | ||||||
|  | @@ -35,7 +35,7 @@ | ||||||
|  |  #include <net/xfrm.h> | ||||||
|  |   | ||||||
|  |  /* Qdisc to use by default */ | ||||||
|  | -const struct Qdisc_ops *default_qdisc_ops = &pfifo_fast_ops; | ||||||
|  | +const struct Qdisc_ops *default_qdisc_ops = &fq_codel_qdisc_ops; | ||||||
|  |  EXPORT_SYMBOL(default_qdisc_ops); | ||||||
|  |   | ||||||
|  |  /* Main transmission queue. */ | ||||||
|  | @@ -1025,7 +1025,7 @@ static void attach_one_default_qdisc(str | ||||||
|  |  				     void *_unused) | ||||||
|  |  { | ||||||
|  |  	struct Qdisc *qdisc; | ||||||
|  | -	const struct Qdisc_ops *ops = default_qdisc_ops; | ||||||
|  | +	const struct Qdisc_ops *ops = &fq_codel_qdisc_ops; | ||||||
|  |   | ||||||
|  |  	if (dev->priv_flags & IFF_NO_QUEUE) | ||||||
|  |  		ops = &noqueue_qdisc_ops; | ||||||
							
								
								
									
										220
									
								
								target/linux/generic/hack-5.4/662-remove_pfifo_fast.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								target/linux/generic/hack-5.4/662-remove_pfifo_fast.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,220 @@ | |||||||
|  | From b531d492d5ef1cf9dba0f4888eb5fd8624a6d762 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Fri, 7 Jul 2017 17:23:42 +0200 | ||||||
|  | Subject: net: sched: switch default qdisc from pfifo_fast to fq_codel and remove pfifo_fast | ||||||
|  |  | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  net/sched/sch_generic.c | 140 ------------------------------------------------ | ||||||
|  |  1 file changed, 140 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/net/sched/sch_generic.c | ||||||
|  | +++ b/net/sched/sch_generic.c | ||||||
|  | @@ -612,207 +612,6 @@ struct Qdisc_ops noqueue_qdisc_ops __rea | ||||||
|  |  	.owner		=	THIS_MODULE, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | -static const u8 prio2band[TC_PRIO_MAX + 1] = { | ||||||
|  | -	1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 | ||||||
|  | -}; | ||||||
|  | - | ||||||
|  | -/* 3-band FIFO queue: old style, but should be a bit faster than | ||||||
|  | -   generic prio+fifo combination. | ||||||
|  | - */ | ||||||
|  | - | ||||||
|  | -#define PFIFO_FAST_BANDS 3 | ||||||
|  | - | ||||||
|  | -/* | ||||||
|  | - * Private data for a pfifo_fast scheduler containing: | ||||||
|  | - *	- rings for priority bands | ||||||
|  | - */ | ||||||
|  | -struct pfifo_fast_priv { | ||||||
|  | -	struct skb_array q[PFIFO_FAST_BANDS]; | ||||||
|  | -}; | ||||||
|  | - | ||||||
|  | -static inline struct skb_array *band2list(struct pfifo_fast_priv *priv, | ||||||
|  | -					  int band) | ||||||
|  | -{ | ||||||
|  | -	return &priv->q[band]; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc *qdisc, | ||||||
|  | -			      struct sk_buff **to_free) | ||||||
|  | -{ | ||||||
|  | -	int band = prio2band[skb->priority & TC_PRIO_MAX]; | ||||||
|  | -	struct pfifo_fast_priv *priv = qdisc_priv(qdisc); | ||||||
|  | -	struct skb_array *q = band2list(priv, band); | ||||||
|  | -	unsigned int pkt_len = qdisc_pkt_len(skb); | ||||||
|  | -	int err; | ||||||
|  | - | ||||||
|  | -	err = skb_array_produce(q, skb); | ||||||
|  | - | ||||||
|  | -	if (unlikely(err)) | ||||||
|  | -		return qdisc_drop_cpu(skb, qdisc, to_free); | ||||||
|  | - | ||||||
|  | -	qdisc_qstats_atomic_qlen_inc(qdisc); | ||||||
|  | -	/* Note: skb can not be used after skb_array_produce(), | ||||||
|  | -	 * so we better not use qdisc_qstats_cpu_backlog_inc() | ||||||
|  | -	 */ | ||||||
|  | -	this_cpu_add(qdisc->cpu_qstats->backlog, pkt_len); | ||||||
|  | -	return NET_XMIT_SUCCESS; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc) | ||||||
|  | -{ | ||||||
|  | -	struct pfifo_fast_priv *priv = qdisc_priv(qdisc); | ||||||
|  | -	struct sk_buff *skb = NULL; | ||||||
|  | -	int band; | ||||||
|  | - | ||||||
|  | -	for (band = 0; band < PFIFO_FAST_BANDS && !skb; band++) { | ||||||
|  | -		struct skb_array *q = band2list(priv, band); | ||||||
|  | - | ||||||
|  | -		if (__skb_array_empty(q)) | ||||||
|  | -			continue; | ||||||
|  | - | ||||||
|  | -		skb = __skb_array_consume(q); | ||||||
|  | -	} | ||||||
|  | -	if (likely(skb)) { | ||||||
|  | -		qdisc_qstats_cpu_backlog_dec(qdisc, skb); | ||||||
|  | -		qdisc_bstats_cpu_update(qdisc, skb); | ||||||
|  | -		qdisc_qstats_atomic_qlen_dec(qdisc); | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	return skb; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static struct sk_buff *pfifo_fast_peek(struct Qdisc *qdisc) | ||||||
|  | -{ | ||||||
|  | -	struct pfifo_fast_priv *priv = qdisc_priv(qdisc); | ||||||
|  | -	struct sk_buff *skb = NULL; | ||||||
|  | -	int band; | ||||||
|  | - | ||||||
|  | -	for (band = 0; band < PFIFO_FAST_BANDS && !skb; band++) { | ||||||
|  | -		struct skb_array *q = band2list(priv, band); | ||||||
|  | - | ||||||
|  | -		skb = __skb_array_peek(q); | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	return skb; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static void pfifo_fast_reset(struct Qdisc *qdisc) | ||||||
|  | -{ | ||||||
|  | -	int i, band; | ||||||
|  | -	struct pfifo_fast_priv *priv = qdisc_priv(qdisc); | ||||||
|  | - | ||||||
|  | -	for (band = 0; band < PFIFO_FAST_BANDS; band++) { | ||||||
|  | -		struct skb_array *q = band2list(priv, band); | ||||||
|  | -		struct sk_buff *skb; | ||||||
|  | - | ||||||
|  | -		/* NULL ring is possible if destroy path is due to a failed | ||||||
|  | -		 * skb_array_init() in pfifo_fast_init() case. | ||||||
|  | -		 */ | ||||||
|  | -		if (!q->ring.queue) | ||||||
|  | -			continue; | ||||||
|  | - | ||||||
|  | -		while ((skb = __skb_array_consume(q)) != NULL) | ||||||
|  | -			kfree_skb(skb); | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	for_each_possible_cpu(i) { | ||||||
|  | -		struct gnet_stats_queue *q = per_cpu_ptr(qdisc->cpu_qstats, i); | ||||||
|  | - | ||||||
|  | -		q->backlog = 0; | ||||||
|  | -	} | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int pfifo_fast_dump(struct Qdisc *qdisc, struct sk_buff *skb) | ||||||
|  | -{ | ||||||
|  | -	struct tc_prio_qopt opt = { .bands = PFIFO_FAST_BANDS }; | ||||||
|  | - | ||||||
|  | -	memcpy(&opt.priomap, prio2band, TC_PRIO_MAX + 1); | ||||||
|  | -	if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt)) | ||||||
|  | -		goto nla_put_failure; | ||||||
|  | -	return skb->len; | ||||||
|  | - | ||||||
|  | -nla_put_failure: | ||||||
|  | -	return -1; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int pfifo_fast_init(struct Qdisc *qdisc, struct nlattr *opt, | ||||||
|  | -			   struct netlink_ext_ack *extack) | ||||||
|  | -{ | ||||||
|  | -	unsigned int qlen = qdisc_dev(qdisc)->tx_queue_len; | ||||||
|  | -	struct pfifo_fast_priv *priv = qdisc_priv(qdisc); | ||||||
|  | -	int prio; | ||||||
|  | - | ||||||
|  | -	/* guard against zero length rings */ | ||||||
|  | -	if (!qlen) | ||||||
|  | -		return -EINVAL; | ||||||
|  | - | ||||||
|  | -	for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) { | ||||||
|  | -		struct skb_array *q = band2list(priv, prio); | ||||||
|  | -		int err; | ||||||
|  | - | ||||||
|  | -		err = skb_array_init(q, qlen, GFP_KERNEL); | ||||||
|  | -		if (err) | ||||||
|  | -			return -ENOMEM; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	/* Can by-pass the queue discipline */ | ||||||
|  | -	qdisc->flags |= TCQ_F_CAN_BYPASS; | ||||||
|  | -	return 0; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static void pfifo_fast_destroy(struct Qdisc *sch) | ||||||
|  | -{ | ||||||
|  | -	struct pfifo_fast_priv *priv = qdisc_priv(sch); | ||||||
|  | -	int prio; | ||||||
|  | - | ||||||
|  | -	for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) { | ||||||
|  | -		struct skb_array *q = band2list(priv, prio); | ||||||
|  | - | ||||||
|  | -		/* NULL ring is possible if destroy path is due to a failed | ||||||
|  | -		 * skb_array_init() in pfifo_fast_init() case. | ||||||
|  | -		 */ | ||||||
|  | -		if (!q->ring.queue) | ||||||
|  | -			continue; | ||||||
|  | -		/* Destroy ring but no need to kfree_skb because a call to | ||||||
|  | -		 * pfifo_fast_reset() has already done that work. | ||||||
|  | -		 */ | ||||||
|  | -		ptr_ring_cleanup(&q->ring, NULL); | ||||||
|  | -	} | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int pfifo_fast_change_tx_queue_len(struct Qdisc *sch, | ||||||
|  | -					  unsigned int new_len) | ||||||
|  | -{ | ||||||
|  | -	struct pfifo_fast_priv *priv = qdisc_priv(sch); | ||||||
|  | -	struct skb_array *bands[PFIFO_FAST_BANDS]; | ||||||
|  | -	int prio; | ||||||
|  | - | ||||||
|  | -	for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) { | ||||||
|  | -		struct skb_array *q = band2list(priv, prio); | ||||||
|  | - | ||||||
|  | -		bands[prio] = q; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	return skb_array_resize_multiple(bands, PFIFO_FAST_BANDS, new_len, | ||||||
|  | -					 GFP_KERNEL); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -struct Qdisc_ops pfifo_fast_ops __read_mostly = { | ||||||
|  | -	.id		=	"pfifo_fast", | ||||||
|  | -	.priv_size	=	sizeof(struct pfifo_fast_priv), | ||||||
|  | -	.enqueue	=	pfifo_fast_enqueue, | ||||||
|  | -	.dequeue	=	pfifo_fast_dequeue, | ||||||
|  | -	.peek		=	pfifo_fast_peek, | ||||||
|  | -	.init		=	pfifo_fast_init, | ||||||
|  | -	.destroy	=	pfifo_fast_destroy, | ||||||
|  | -	.reset		=	pfifo_fast_reset, | ||||||
|  | -	.dump		=	pfifo_fast_dump, | ||||||
|  | -	.change_tx_queue_len =  pfifo_fast_change_tx_queue_len, | ||||||
|  | -	.owner		=	THIS_MODULE, | ||||||
|  | -	.static_flags	=	TCQ_F_NOLOCK | TCQ_F_CPUSTATS, | ||||||
|  | -}; | ||||||
|  | -EXPORT_SYMBOL(pfifo_fast_ops); | ||||||
|  | - | ||||||
|  |  static struct lock_class_key qdisc_tx_busylock; | ||||||
|  |  static struct lock_class_key qdisc_running_key; | ||||||
|  |   | ||||||
							
								
								
									
										140
									
								
								target/linux/generic/hack-5.4/700-swconfig_switch_drivers.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								target/linux/generic/hack-5.4/700-swconfig_switch_drivers.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | |||||||
|  | From 36e516290611e613aa92996cb4339561452695b4 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Fri, 7 Jul 2017 17:24:23 +0200 | ||||||
|  | Subject: net: swconfig: adds openwrt switch layer | ||||||
|  |  | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  drivers/net/phy/Kconfig   | 83 +++++++++++++++++++++++++++++++++++++++++++++++ | ||||||
|  |  drivers/net/phy/Makefile  | 15 +++++++++ | ||||||
|  |  include/uapi/linux/Kbuild |  1 + | ||||||
|  |  3 files changed, 99 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/Kconfig | ||||||
|  | +++ b/drivers/net/phy/Kconfig | ||||||
|  | @@ -209,6 +209,89 @@ config LED_TRIGGER_PHY | ||||||
|  |  		for any speed known to the PHY. | ||||||
|  |   | ||||||
|  |   | ||||||
|  | +comment "Switch configuration API + drivers" | ||||||
|  | + | ||||||
|  | +config SWCONFIG | ||||||
|  | +	tristate "Switch configuration API" | ||||||
|  | +	---help--- | ||||||
|  | +	  Switch configuration API using netlink. This allows | ||||||
|  | +	  you to configure the VLAN features of certain switches. | ||||||
|  | + | ||||||
|  | +config SWCONFIG_LEDS | ||||||
|  | +	bool "Switch LED trigger support" | ||||||
|  | +	depends on (SWCONFIG && LEDS_TRIGGERS) | ||||||
|  | + | ||||||
|  | +config ADM6996_PHY | ||||||
|  | +	tristate "Driver for ADM6996 switches" | ||||||
|  | +	select SWCONFIG | ||||||
|  | +	---help--- | ||||||
|  | +	  Currently supports the ADM6996FC and ADM6996M switches. | ||||||
|  | +	  Support for FC is very limited. | ||||||
|  | + | ||||||
|  | +config AR8216_PHY | ||||||
|  | +	tristate "Driver for Atheros AR8216 switches" | ||||||
|  | +	select ETHERNET_PACKET_MANGLE | ||||||
|  | +	select SWCONFIG | ||||||
|  | + | ||||||
|  | +config AR8216_PHY_LEDS | ||||||
|  | +	bool "Atheros AR8216 switch LED support" | ||||||
|  | +	depends on (AR8216_PHY && LEDS_CLASS) | ||||||
|  | + | ||||||
|  | +source "drivers/net/phy/b53/Kconfig" | ||||||
|  | + | ||||||
|  | +config IP17XX_PHY | ||||||
|  | +	tristate "Driver for IC+ IP17xx switches" | ||||||
|  | +	select SWCONFIG | ||||||
|  | + | ||||||
|  | +config MVSWITCH_PHY | ||||||
|  | +	tristate "Driver for Marvell 88E6060 switches" | ||||||
|  | +	select ETHERNET_PACKET_MANGLE | ||||||
|  | + | ||||||
|  | +config MVSW61XX_PHY | ||||||
|  | +	tristate "Driver for Marvell 88E6171/6172 switches" | ||||||
|  | +	select SWCONFIG | ||||||
|  | + | ||||||
|  | +config PSB6970_PHY | ||||||
|  | +	tristate "Lantiq XWAY Tantos (PSB6970) Ethernet switch" | ||||||
|  | +	select SWCONFIG | ||||||
|  | +	select ETHERNET_PACKET_MANGLE | ||||||
|  | + | ||||||
|  | +config RTL8306_PHY | ||||||
|  | +	tristate "Driver for Realtek RTL8306S switches" | ||||||
|  | +	select SWCONFIG | ||||||
|  | + | ||||||
|  | +config RTL8366_SMI | ||||||
|  | +	tristate "Driver for the RTL8366 SMI interface" | ||||||
|  | +	depends on GPIOLIB | ||||||
|  | +	---help--- | ||||||
|  | +	  This module implements the SMI interface protocol which is used | ||||||
|  | +	  by some RTL8366 ethernet switch devices via the generic GPIO API. | ||||||
|  | + | ||||||
|  | +if RTL8366_SMI | ||||||
|  | + | ||||||
|  | +config RTL8366_SMI_DEBUG_FS | ||||||
|  | +	bool "RTL8366 SMI interface debugfs support" | ||||||
|  | +        depends on DEBUG_FS | ||||||
|  | +        default n | ||||||
|  | + | ||||||
|  | +config RTL8366S_PHY | ||||||
|  | +	tristate "Driver for the Realtek RTL8366S switch" | ||||||
|  | +	select SWCONFIG | ||||||
|  | + | ||||||
|  | +config RTL8366RB_PHY | ||||||
|  | +	tristate "Driver for the Realtek RTL8366RB switch" | ||||||
|  | +	select SWCONFIG | ||||||
|  | + | ||||||
|  | +config RTL8367_PHY | ||||||
|  | +	tristate "Driver for the Realtek RTL8367R/M switches" | ||||||
|  | +	select SWCONFIG | ||||||
|  | + | ||||||
|  | +config RTL8367B_PHY | ||||||
|  | +	tristate "Driver fot the Realtek RTL8367R-VB switch" | ||||||
|  | +	select SWCONFIG | ||||||
|  | + | ||||||
|  | +endif # RTL8366_SMI | ||||||
|  | + | ||||||
|  |  comment "MII PHY device drivers" | ||||||
|  |   | ||||||
|  |  config SFP | ||||||
|  | --- a/drivers/net/phy/Makefile | ||||||
|  | +++ b/drivers/net/phy/Makefile | ||||||
|  | @@ -22,6 +22,21 @@ libphy-$(CONFIG_LED_TRIGGER_PHY)	+= phy_ | ||||||
|  |  obj-$(CONFIG_PHYLINK)		+= phylink.o | ||||||
|  |  obj-$(CONFIG_PHYLIB)		+= libphy.o | ||||||
|  |   | ||||||
|  | +obj-$(CONFIG_SWCONFIG)		+= swconfig.o | ||||||
|  | +obj-$(CONFIG_ADM6996_PHY)	+= adm6996.o | ||||||
|  | +obj-$(CONFIG_AR8216_PHY)	+= ar8216.o ar8327.o | ||||||
|  | +obj-$(CONFIG_SWCONFIG_B53)	+= b53/ | ||||||
|  | +obj-$(CONFIG_IP17XX_PHY)	+= ip17xx.o | ||||||
|  | +obj-$(CONFIG_MVSWITCH_PHY)	+= mvswitch.o | ||||||
|  | +obj-$(CONFIG_MVSW61XX_PHY)	+= mvsw61xx.o | ||||||
|  | +obj-$(CONFIG_PSB6970_PHY)	+= psb6970.o | ||||||
|  | +obj-$(CONFIG_RTL8306_PHY)	+= rtl8306.o | ||||||
|  | +obj-$(CONFIG_RTL8366_SMI)	+= rtl8366_smi.o | ||||||
|  | +obj-$(CONFIG_RTL8366S_PHY)	+= rtl8366s.o | ||||||
|  | +obj-$(CONFIG_RTL8366RB_PHY)	+= rtl8366rb.o | ||||||
|  | +obj-$(CONFIG_RTL8367_PHY)	+= rtl8367.o | ||||||
|  | +obj-$(CONFIG_RTL8367B_PHY)	+= rtl8367b.o | ||||||
|  | + | ||||||
|  |  obj-$(CONFIG_MDIO_BCM_IPROC)	+= mdio-bcm-iproc.o | ||||||
|  |  obj-$(CONFIG_MDIO_BCM_UNIMAC)	+= mdio-bcm-unimac.o | ||||||
|  |  obj-$(CONFIG_MDIO_BITBANG)	+= mdio-bitbang.o | ||||||
|  | --- a/include/linux/platform_data/b53.h | ||||||
|  | +++ b/include/linux/platform_data/b53.h | ||||||
|  | @@ -29,6 +29,9 @@ struct b53_platform_data { | ||||||
|  |  	u32 chip_id; | ||||||
|  |  	u16 enabled_ports; | ||||||
|  |   | ||||||
|  | +	/* allow to specify an ethX alias */ | ||||||
|  | +	const char *alias; | ||||||
|  | + | ||||||
|  |  	/* only used by MMAP'd driver */ | ||||||
|  |  	unsigned big_endian:1; | ||||||
|  |  	void __iomem *regs; | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user