diff options
Diffstat (limited to 'target/linux/generic/backport-5.4')
70 files changed, 8250 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.4/010-Kbuild-don-t-hardcode-path-to-awk-in-scripts-ld-vers.patch b/target/linux/generic/backport-5.4/010-Kbuild-don-t-hardcode-path-to-awk-in-scripts-ld-vers.patch new file mode 100644 index 0000000000..7ac4f9d240 --- /dev/null +++ b/target/linux/generic/backport-5.4/010-Kbuild-don-t-hardcode-path-to-awk-in-scripts-ld-vers.patch @@ -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 + } ++' diff --git a/target/linux/generic/backport-5.4/011-kbuild-export-SUBARCH.patch b/target/linux/generic/backport-5.4/011-kbuild-export-SUBARCH.patch new file mode 100644 index 0000000000..a42c720446 --- /dev/null +++ b/target/linux/generic/backport-5.4/011-kbuild-export-SUBARCH.patch @@ -0,0 +1,23 @@ +From 173019b66dcc9d68ad9333aa744dad1e369b5aa8 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Sun, 9 Jul 2017 00:26:53 +0200 +Subject: [PATCH 34/34] kernel: add compile fix for linux 4.9 on x86 + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + Makefile | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/Makefile ++++ b/Makefile +@@ -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 + diff --git a/target/linux/generic/backport-5.4/020-backport_netfilter_rtcache.patch b/target/linux/generic/backport-5.4/020-backport_netfilter_rtcache.patch new file mode 100644 index 0000000000..c02b8f39e0 --- /dev/null +++ b/target/linux/generic/backport-5.4/020-backport_netfilter_rtcache.patch @@ -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"); diff --git a/target/linux/generic/backport-5.4/047-v4.21-mtd-keep-original-flags-for-every-struct-mtd_info.patch b/target/linux/generic/backport-5.4/047-v4.21-mtd-keep-original-flags-for-every-struct-mtd_info.patch new file mode 100644 index 0000000000..d587c9ec07 --- /dev/null +++ b/target/linux/generic/backport-5.4/047-v4.21-mtd-keep-original-flags-for-every-struct-mtd_info.patch @@ -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 diff --git a/target/linux/generic/backport-5.4/048-v4.21-mtd-improve-calculating-partition-boundaries-when-ch.patch b/target/linux/generic/backport-5.4/048-v4.21-mtd-improve-calculating-partition-boundaries-when-ch.patch new file mode 100644 index 0000000000..02296276b0 --- /dev/null +++ b/target/linux/generic/backport-5.4/048-v4.21-mtd-improve-calculating-partition-boundaries-when-ch.patch @@ -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; diff --git a/target/linux/generic/backport-5.4/080-v5.1-0001-bcma-keep-a-direct-pointer-to-the-struct-device.patch b/target/linux/generic/backport-5.4/080-v5.1-0001-bcma-keep-a-direct-pointer-to-the-struct-device.patch new file mode 100644 index 0000000000..cc32aee17b --- /dev/null +++ b/target/linux/generic/backport-5.4/080-v5.1-0001-bcma-keep-a-direct-pointer-to-the-struct-device.patch @@ -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; + diff --git a/target/linux/generic/backport-5.4/080-v5.1-0002-bcma-use-dev_-printing-functions.patch b/target/linux/generic/backport-5.4/080-v5.1-0002-bcma-use-dev_-printing-functions.patch new file mode 100644 index 0000000000..7ce8ba8967 --- /dev/null +++ b/target/linux/generic/backport-5.4/080-v5.1-0002-bcma-use-dev_-printing-functions.patch @@ -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; + diff --git a/target/linux/generic/backport-5.4/095-Allow-class-e-address-assignment-via-ifconfig-ioctl.patch b/target/linux/generic/backport-5.4/095-Allow-class-e-address-assignment-via-ifconfig-ioctl.patch new file mode 100644 index 0000000000..bf6d9aca34 --- /dev/null +++ b/target/linux/generic/backport-5.4/095-Allow-class-e-address-assignment-via-ifconfig-ioctl.patch @@ -0,0 +1,79 @@ +From 46bf067870156abd61fe24d14c2486d15b8b502c Mon Sep 17 00:00:00 2001 +From: Dave Taht <dave@taht.net> +Date: Fri, 14 Dec 2018 18:38:40 +0000 +Subject: [PATCH 1/1] Allow class-e address assignment in ifconfig and early + boot + +While the linux kernel became mostly "class-e clean" a decade ago, +and most distributions long ago switched to the iproute2 suite +of utilities, which allow class-e (240.0.0.0/4) address assignment, +distributions relying on busybox, toybox and other forms of +ifconfig cannot assign class-e addresses without this kernel patch. + +With this patch, also, a boot command line on these addresses is feasible: +(ip=248.0.1.2::248.0.1.1:255.255.255.0). + +While CIDR has been obsolete for 2 decades, and a survey of all the +userspace open source code in the world shows most IN_whatever macros +are also obsolete... rather than obsolete CIDR from this ioctl entirely, +this patch merely enables class-e assignment, sanely. + +H/T to Vince Fuller and his original patch here: + https://lkml.org/lkml/2008/1/7/370 + +Signed-off-by: Dave Taht <dave.taht@gmail.com> +Reviewed-by: John Gilmore <gnu@toad.com> +--- + include/uapi/linux/in.h | 8 ++++++-- + net/ipv4/devinet.c | 4 +++- + net/ipv4/ipconfig.c | 2 ++ + 3 files changed, 11 insertions(+), 3 deletions(-) + +--- a/include/uapi/linux/in.h ++++ b/include/uapi/linux/in.h +@@ -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); diff --git a/target/linux/generic/backport-5.4/101-arm-cns3xxx-use-actual-size-reads-for-PCIe.patch b/target/linux/generic/backport-5.4/101-arm-cns3xxx-use-actual-size-reads-for-PCIe.patch new file mode 100644 index 0000000000..2b3384391a --- /dev/null +++ b/target/linux/generic/backport-5.4/101-arm-cns3xxx-use-actual-size-reads-for-PCIe.patch @@ -0,0 +1,46 @@ +From 4cc30de79d293f1e8c5f50ae3a9c005def9564a0 Mon Sep 17 00:00:00 2001 +From: Koen Vandeputte <koen.vandeputte@ncentric.com> +Date: Mon, 7 Jan 2019 14:14:27 +0100 +Subject: [PATCH 2/2] arm: cns3xxx: use actual size reads for PCIe + +commit 802b7c06adc7 ("ARM: cns3xxx: Convert PCI to use generic config accessors") +reimplemented cns3xxx_pci_read_config() using pci_generic_config_read32(), +which preserved the property of only doing 32-bit reads. + +It also replaced cns3xxx_pci_write_config() with pci_generic_config_write(), +so it changed writes from always being 32 bits to being the actual size, +which works just fine. + +Due to: +- The documentation does not mention that only 32 bit access is allowed. +- Writes are already executed using the actual size +- Extensive testing shows that 8b, 16b and 32b reads work as intended + +It makes perfectly sense to also swap 32 bit reading in favor of actual size. + +Fixes: 802b7c06adc7 ("ARM: cns3xxx: Convert PCI to use generic config accessors") +Suggested-by: Bjorn Helgaas <bhelgaas@google.com> +Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com> +CC: Arnd Bergmann <arnd@arndb.de> +CC: Krzysztof Halasa <khalasa@piap.pl> +CC: Olof Johansson <olof@lixom.net> +CC: Robin Leblon <robin.leblon@ncentric.com> +CC: Rob Herring <robh@kernel.org> +CC: Russell King <linux@armlinux.org.uk> +CC: Tim Harvey <tharvey@gateworks.com> +CC: stable@vger.kernel.org # v4.0+ +--- + arch/arm/mach-cns3xxx/pcie.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm/mach-cns3xxx/pcie.c ++++ b/arch/arm/mach-cns3xxx/pcie.c +@@ -93,7 +93,7 @@ static int cns3xxx_pci_read_config(struc + u32 mask = (0x1ull << (size * 8)) - 1; + int shift = (where % 4) * 8; + +- ret = pci_generic_config_read32(bus, devfn, where, size, val); ++ ret = pci_generic_config_read(bus, devfn, where, size, val); + + if (ret == PCIBIOS_SUCCESSFUL && !bus->number && !devfn && + (where & 0xffc) == PCI_CLASS_REVISION) diff --git a/target/linux/generic/backport-5.4/200-v5.2-usb-dwc2-Set-lpm-mode-parameters-depend-on-HW-configuration.patch b/target/linux/generic/backport-5.4/200-v5.2-usb-dwc2-Set-lpm-mode-parameters-depend-on-HW-configuration.patch new file mode 100644 index 0000000000..ed4aadf2be --- /dev/null +++ b/target/linux/generic/backport-5.4/200-v5.2-usb-dwc2-Set-lpm-mode-parameters-depend-on-HW-configuration.patch @@ -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; diff --git a/target/linux/generic/backport-5.4/210-arm64-sve-Disentangle-uapi-asm-ptrace.h-from-uapi-as.patch b/target/linux/generic/backport-5.4/210-arm64-sve-Disentangle-uapi-asm-ptrace.h-from-uapi-as.patch new file mode 100644 index 0000000000..7c574fd343 --- /dev/null +++ b/target/linux/generic/backport-5.4/210-arm64-sve-Disentangle-uapi-asm-ptrace.h-from-uapi-as.patch @@ -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 */ diff --git a/target/linux/generic/backport-5.4/343-netfilter-nft_flow_offload-handle-netdevice-events-f.patch b/target/linux/generic/backport-5.4/343-netfilter-nft_flow_offload-handle-netdevice-events-f.patch new file mode 100644 index 0000000000..577f2d3df6 --- /dev/null +++ b/target/linux/generic/backport-5.4/343-netfilter-nft_flow_offload-handle-netdevice-events-f.patch @@ -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); diff --git a/target/linux/generic/backport-5.4/370-netfilter-nf_flow_table-fix-offloaded-connection-tim.patch b/target/linux/generic/backport-5.4/370-netfilter-nf_flow_table-fix-offloaded-connection-tim.patch new file mode 100644 index 0000000000..7c10f6f189 --- /dev/null +++ b/target/linux/generic/backport-5.4/370-netfilter-nf_flow_table-fix-offloaded-connection-tim.patch @@ -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: diff --git a/target/linux/generic/backport-5.4/371-netfilter-nf_flow_table-fix-up-ct-state-of-flows-aft.patch b/target/linux/generic/backport-5.4/371-netfilter-nf_flow_table-fix-up-ct-state-of-flows-aft.patch new file mode 100644 index 0000000000..2e25066499 --- /dev/null +++ b/target/linux/generic/backport-5.4/371-netfilter-nf_flow_table-fix-up-ct-state-of-flows-aft.patch @@ -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); + } + diff --git a/target/linux/generic/backport-5.4/380-v5.3-net-sched-Introduce-act_ctinfo-action.patch b/target/linux/generic/backport-5.4/380-v5.3-net-sched-Introduce-act_ctinfo-action.patch new file mode 100644 index 0000000000..a680402f26 --- /dev/null +++ b/target/linux/generic/backport-5.4/380-v5.3-net-sched-Introduce-act_ctinfo-action.patch @@ -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 diff --git a/target/linux/generic/backport-5.4/450-v5.0-mtd-spinand-winbond-Add-support-for-W25N01GV.patch b/target/linux/generic/backport-5.4/450-v5.0-mtd-spinand-winbond-Add-support-for-W25N01GV.patch new file mode 100644 index 0000000000..2024577b8f --- /dev/null +++ b/target/linux/generic/backport-5.4/450-v5.0-mtd-spinand-winbond-Add-support-for-W25N01GV.patch @@ -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)), + }; + + /** diff --git a/target/linux/generic/backport-5.4/451-v5.0-mtd-spinand-Add-initial-support-for-Toshiba-TC58CVG2.patch b/target/linux/generic/backport-5.4/451-v5.0-mtd-spinand-Add-initial-support-for-Toshiba-TC58CVG2.patch new file mode 100644 index 0000000000..ed42f0024b --- /dev/null +++ b/target/linux/generic/backport-5.4/451-v5.0-mtd-spinand-Add-initial-support-for-Toshiba-TC58CVG2.patch @@ -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; + + /** diff --git a/target/linux/generic/backport-5.4/452-v5.0-mtd-spinand-add-support-for-GigaDevice-GD5FxGQ4xA.patch b/target/linux/generic/backport-5.4/452-v5.0-mtd-spinand-add-support-for-GigaDevice-GD5FxGQ4xA.patch new file mode 100644 index 0000000000..4e6f18a4cb --- /dev/null +++ b/target/linux/generic/backport-5.4/452-v5.0-mtd-spinand-add-support-for-GigaDevice-GD5FxGQ4xA.patch @@ -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; diff --git a/target/linux/generic/backport-5.4/455-v5.1-mtd-spinand-Add-support-for-all-Toshiba-Memory-produ.patch b/target/linux/generic/backport-5.4/455-v5.1-mtd-spinand-Add-support-for-all-Toshiba-Memory-produ.patch new file mode 100644 index 0000000000..aad82dcf7a --- /dev/null +++ b/target/linux/generic/backport-5.4/455-v5.1-mtd-spinand-Add-support-for-all-Toshiba-Memory-produ.patch @@ -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) diff --git a/target/linux/generic/backport-5.4/456-v5.1-mtd-spinand-Add-support-for-GigaDevice-GD5F1GQ4UExxG.patch b/target/linux/generic/backport-5.4/456-v5.1-mtd-spinand-Add-support-for-GigaDevice-GD5F1GQ4UExxG.patch new file mode 100644 index 0000000000..8e48debf43 --- /dev/null +++ b/target/linux/generic/backport-5.4/456-v5.1-mtd-spinand-Add-support-for-GigaDevice-GD5F1GQ4UExxG.patch @@ -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) diff --git a/target/linux/generic/backport-5.4/460-v5.0-mtd-spi-nor-Add-support-for-mx25u12835f.patch b/target/linux/generic/backport-5.4/460-v5.0-mtd-spi-nor-Add-support-for-mx25u12835f.patch new file mode 100644 index 0000000000..1bdf35b987 --- /dev/null +++ b/target/linux/generic/backport-5.4/460-v5.0-mtd-spi-nor-Add-support-for-mx25u12835f.patch @@ -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) }, diff --git a/target/linux/generic/backport-5.4/460-v5.3-mtd-spinand-Define-macros-for-page-read-ops-with-thr.patch b/target/linux/generic/backport-5.4/460-v5.3-mtd-spinand-Define-macros-for-page-read-ops-with-thr.patch new file mode 100644 index 0000000000..c28ae1d797 --- /dev/null +++ b/target/linux/generic/backport-5.4/460-v5.3-mtd-spinand-Define-macros-for-page-read-ops-with-thr.patch @@ -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), \ diff --git a/target/linux/generic/backport-5.4/461-v5.3-mtd-spinand-Add-support-for-two-byte-device-IDs.patch b/target/linux/generic/backport-5.4/461-v5.3-mtd-spinand-Add-support-for-two-byte-device-IDs.patch new file mode 100644 index 0000000000..fcbecbb762 --- /dev/null +++ b/target/linux/generic/backport-5.4/461-v5.3-mtd-spinand-Add-support-for-two-byte-device-IDs.patch @@ -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); diff --git a/target/linux/generic/backport-5.4/462-v5.3-mtd-spinand-Add-support-for-GigaDevice-GD5F1GQ4UFxxG.patch b/target/linux/generic/backport-5.4/462-v5.3-mtd-spinand-Add-support-for-GigaDevice-GD5F1GQ4UFxxG.patch new file mode 100644 index 0000000000..06d87ba5e0 --- /dev/null +++ b/target/linux/generic/backport-5.4/462-v5.3-mtd-spinand-Add-support-for-GigaDevice-GD5F1GQ4UFxxG.patch @@ -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; + diff --git a/target/linux/generic/backport-5.4/463-v5.3-mtd-spinand-Add-initial-support-for-Paragon-PN26G0xA.patch b/target/linux/generic/backport-5.4/463-v5.3-mtd-spinand-Add-initial-support-for-Paragon-PN26G0xA.patch new file mode 100644 index 0000000000..17b8e779bc --- /dev/null +++ b/target/linux/generic/backport-5.4/463-v5.3-mtd-spinand-Add-initial-support-for-Paragon-PN26G0xA.patch @@ -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; + diff --git a/target/linux/generic/backport-5.4/700-v5.1-net-phylink-only-call-mac_config-during-resolve-when.patch b/target/linux/generic/backport-5.4/700-v5.1-net-phylink-only-call-mac_config-during-resolve-when.patch new file mode 100644 index 0000000000..0e272ea891 --- /dev/null +++ b/target/linux/generic/backport-5.4/700-v5.1-net-phylink-only-call-mac_config-during-resolve-when.patch @@ -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: diff --git a/target/linux/generic/backport-5.4/701-v5.2-net-phylink-ensure-inband-AN-works-correctly.patch b/target/linux/generic/backport-5.4/701-v5.2-net-phylink-ensure-inband-AN-works-correctly.patch new file mode 100644 index 0000000000..746aca2e5a --- /dev/null +++ b/target/linux/generic/backport-5.4/701-v5.2-net-phylink-ensure-inband-AN-works-correctly.patch @@ -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; + } diff --git a/target/linux/generic/backport-5.4/702-v4.20-net-ethernet-Add-helper-for-MACs-which-support-asym-.patch b/target/linux/generic/backport-5.4/702-v4.20-net-ethernet-Add-helper-for-MACs-which-support-asym-.patch new file mode 100644 index 0000000000..3ada51636b --- /dev/null +++ b/target/linux/generic/backport-5.4/702-v4.20-net-ethernet-Add-helper-for-MACs-which-support-asym-.patch @@ -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 *)); diff --git a/target/linux/generic/backport-5.4/703-v4.20-net-ethernet-Add-helper-for-set_pauseparam-for-Asym-.patch b/target/linux/generic/backport-5.4/703-v4.20-net-ethernet-Add-helper-for-set_pauseparam-for-Asym-.patch new file mode 100644 index 0000000000..4ad3bf698a --- /dev/null +++ b/target/linux/generic/backport-5.4/703-v4.20-net-ethernet-Add-helper-for-set_pauseparam-for-Asym-.patch @@ -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 *)); diff --git a/target/linux/generic/backport-5.4/704-v4.20-net-phy-Stop-with-excessive-soft-reset.patch b/target/linux/generic/backport-5.4/704-v4.20-net-phy-Stop-with-excessive-soft-reset.patch new file mode 100644 index 0000000000..00ef6b7a1d --- /dev/null +++ b/target/linux/generic/backport-5.4/704-v4.20-net-phy-Stop-with-excessive-soft-reset.patch @@ -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; diff --git a/target/linux/generic/backport-5.4/705-v5.1-net-phy-provide-full-set-of-accessor-functions-to-MM.patch b/target/linux/generic/backport-5.4/705-v5.1-net-phy-provide-full-set-of-accessor-functions-to-MM.patch new file mode 100644 index 0000000000..9a587ad3a6 --- /dev/null +++ b/target/linux/generic/backport-5.4/705-v5.1-net-phy-provide-full-set-of-accessor-functions-to-MM.patch @@ -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); diff --git a/target/linux/generic/backport-5.4/706-v5.1-net-phy-add-register-modifying-helpers-returning-1-o.patch b/target/linux/generic/backport-5.4/706-v5.1-net-phy-add-register-modifying-helpers-returning-1-o.patch new file mode 100644 index 0000000000..dddfcc2018 --- /dev/null +++ b/target/linux/generic/backport-5.4/706-v5.1-net-phy-add-register-modifying-helpers-returning-1-o.patch @@ -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 diff --git a/target/linux/generic/backport-5.4/707-v5.1-net-phy-add-genphy_c45_check_and_restart_aneg.patch b/target/linux/generic/backport-5.4/707-v5.1-net-phy-add-genphy_c45_check_and_restart_aneg.patch new file mode 100644 index 0000000000..6d47fa9634 --- /dev/null +++ b/target/linux/generic/backport-5.4/707-v5.1-net-phy-add-genphy_c45_check_and_restart_aneg.patch @@ -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); diff --git a/target/linux/generic/backport-5.4/708-v5.3-net-phylink-remove-netdev-from-phylink-mii-ioctl-emu.patch b/target/linux/generic/backport-5.4/708-v5.3-net-phylink-remove-netdev-from-phylink-mii-ioctl-emu.patch new file mode 100644 index 0000000000..1c3f1cc01a --- /dev/null +++ b/target/linux/generic/backport-5.4/708-v5.3-net-phylink-remove-netdev-from-phylink-mii-ioctl-emu.patch @@ -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; + } diff --git a/target/linux/generic/backport-5.4/709-v5.3-net-phylink-support-for-link-gpio-interrupt.patch b/target/linux/generic/backport-5.4/709-v5.3-net-phylink-support-for-link-gpio-interrupt.patch new file mode 100644 index 0000000000..84a1c853d1 --- /dev/null +++ b/target/linux/generic/backport-5.4/709-v5.3-net-phylink-support-for-link-gpio-interrupt.patch @@ -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); + } diff --git a/target/linux/generic/backport-5.4/710-v5.3-net-phy-allow-Clause-45-access-via-mii-ioctl.patch b/target/linux/generic/backport-5.4/710-v5.3-net-phy-allow-Clause-45-access-via-mii-ioctl.patch new file mode 100644 index 0000000000..3a601c65b0 --- /dev/null +++ b/target/linux/generic/backport-5.4/710-v5.3-net-phy-allow-Clause-45-access-via-mii-ioctl.patch @@ -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); + diff --git a/target/linux/generic/backport-5.4/711-v5.3-net-sfp-add-mandatory-attach-detach-methods-for-sfp-.patch b/target/linux/generic/backport-5.4/711-v5.3-net-sfp-add-mandatory-attach-detach-methods-for-sfp-.patch new file mode 100644 index 0000000000..74dc39fa7f --- /dev/null +++ b/target/linux/generic/backport-5.4/711-v5.3-net-sfp-add-mandatory-attach-detach-methods-for-sfp-.patch @@ -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); diff --git a/target/linux/generic/backport-5.4/712-v5.3-net-sfp-remove-sfp-bus-use-of-netdevs.patch b/target/linux/generic/backport-5.4/712-v5.3-net-sfp-remove-sfp-bus-use-of-netdevs.patch new file mode 100644 index 0000000000..8f0c37e092 --- /dev/null +++ b/target/linux/generic/backport-5.4/712-v5.3-net-sfp-remove-sfp-bus-use-of-netdevs.patch @@ -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; diff --git a/target/linux/generic/backport-5.4/713-v5.2-net-phylink-avoid-reducing-support-mask.patch b/target/linux/generic/backport-5.4/713-v5.2-net-phylink-avoid-reducing-support-mask.patch new file mode 100644 index 0000000000..3aa8d9e571 --- /dev/null +++ b/target/linux/generic/backport-5.4/713-v5.2-net-phylink-avoid-reducing-support-mask.patch @@ -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), diff --git a/target/linux/generic/backport-5.4/714-v5.3-net-sfp-Stop-SFP-polling-and-interrupt-handling-duri.patch b/target/linux/generic/backport-5.4/714-v5.3-net-sfp-Stop-SFP-polling-and-interrupt-handling-duri.patch new file mode 100644 index 0000000000..0509950296 --- /dev/null +++ b/target/linux/generic/backport-5.4/714-v5.3-net-sfp-Stop-SFP-polling-and-interrupt-handling-duri.patch @@ -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, diff --git a/target/linux/generic/backport-5.4/715-v5.3-net-phylink-don-t-start-and-stop-SGMII-PHYs-in-SFP-m.patch b/target/linux/generic/backport-5.4/715-v5.3-net-phylink-don-t-start-and-stop-SGMII-PHYs-in-SFP-m.patch new file mode 100644 index 0000000000..94fb2fb3e4 --- /dev/null +++ b/target/linux/generic/backport-5.4/715-v5.3-net-phylink-don-t-start-and-stop-SGMII-PHYs-in-SFP-m.patch @@ -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); diff --git a/target/linux/generic/backport-5.4/716-v5.4-net-sfp-move-fwnode-parsing-into-sfp-bus-layer.patch b/target/linux/generic/backport-5.4/716-v5.4-net-sfp-move-fwnode-parsing-into-sfp-bus-layer.patch new file mode 100644 index 0000000000..27ab78f3ee --- /dev/null +++ b/target/linux/generic/backport-5.4/716-v5.4-net-sfp-move-fwnode-parsing-into-sfp-bus-layer.patch @@ -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) diff --git a/target/linux/generic/backport-5.4/717-v5.5-net-sfp-rework-upstream-interface.patch b/target/linux/generic/backport-5.4/717-v5.5-net-sfp-rework-upstream-interface.patch new file mode 100644 index 0000000000..f7dd187c16 --- /dev/null +++ b/target/linux/generic/backport-5.4/717-v5.5-net-sfp-rework-upstream-interface.patch @@ -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 diff --git a/target/linux/generic/backport-5.4/718-v5.5-net-sfp-fix-sfp_bus_put-kernel-documentation.patch b/target/linux/generic/backport-5.4/718-v5.5-net-sfp-fix-sfp_bus_put-kernel-documentation.patch new file mode 100644 index 0000000000..59d2ce588c --- /dev/null +++ b/target/linux/generic/backport-5.4/718-v5.5-net-sfp-fix-sfp_bus_put-kernel-documentation.patch @@ -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. diff --git a/target/linux/generic/backport-5.4/719-v5.5-net-sfp-fix-sfp_bus_add_upstream-warning.patch b/target/linux/generic/backport-5.4/719-v5.5-net-sfp-fix-sfp_bus_add_upstream-warning.patch new file mode 100644 index 0000000000..9528049e1b --- /dev/null +++ b/target/linux/generic/backport-5.4/719-v5.5-net-sfp-fix-sfp_bus_add_upstream-warning.patch @@ -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; + } diff --git a/target/linux/generic/backport-5.4/720-v5.5-net-sfp-move-sfp-sub-state-machines-into-separate-fu.patch b/target/linux/generic/backport-5.4/720-v5.5-net-sfp-move-sfp-sub-state-machines-into-separate-fu.patch new file mode 100644 index 0000000000..2f90b00438 --- /dev/null +++ b/target/linux/generic/backport-5.4/720-v5.5-net-sfp-move-sfp-sub-state-machines-into-separate-fu.patch @@ -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), diff --git a/target/linux/generic/backport-5.4/721-v5.5-net-sfp-move-tx-disable-on-device-down-to-main-state.patch b/target/linux/generic/backport-5.4/721-v5.5-net-sfp-move-tx-disable-on-device-down-to-main-state.patch new file mode 100644 index 0000000000..5b3caaf0fd --- /dev/null +++ b/target/linux/generic/backport-5.4/721-v5.5-net-sfp-move-tx-disable-on-device-down-to-main-state.patch @@ -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; + } diff --git a/target/linux/generic/backport-5.4/722-v5.5-net-sfp-rename-sfp_sm_ins_next-as-sfp_sm_mod_next.patch b/target/linux/generic/backport-5.4/722-v5.5-net-sfp-rename-sfp_sm_ins_next-as-sfp_sm_mod_next.patch new file mode 100644 index 0000000000..ed84e76fdf --- /dev/null +++ b/target/linux/generic/backport-5.4/722-v5.5-net-sfp-rename-sfp_sm_ins_next-as-sfp_sm_mod_next.patch @@ -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; + } diff --git a/target/linux/generic/backport-5.4/723-v5.5-net-sfp-handle-module-remove-outside-state-machine.patch b/target/linux/generic/backport-5.4/723-v5.5-net-sfp-handle-module-remove-outside-state-machine.patch new file mode 100644 index 0000000000..542aeaea77 --- /dev/null +++ b/target/linux/generic/backport-5.4/723-v5.5-net-sfp-handle-module-remove-outside-state-machine.patch @@ -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; + } + } diff --git a/target/linux/generic/backport-5.4/724-v5.5-net-sfp-rename-T_PROBE_WAIT-to-T_SERIAL.patch b/target/linux/generic/backport-5.4/724-v5.5-net-sfp-rename-T_PROBE_WAIT-to-T_SERIAL.patch new file mode 100644 index 0000000000..e0c35feea3 --- /dev/null +++ b/target/linux/generic/backport-5.4/724-v5.5-net-sfp-rename-T_PROBE_WAIT-to-T_SERIAL.patch @@ -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; + diff --git a/target/linux/generic/backport-5.4/725-v5.5-net-sfp-parse-SFP-power-requirement-earlier.patch b/target/linux/generic/backport-5.4/725-v5.5-net-sfp-parse-SFP-power-requirement-earlier.patch new file mode 100644 index 0000000000..0b358f86d2 --- /dev/null +++ b/target/linux/generic/backport-5.4/725-v5.5-net-sfp-parse-SFP-power-requirement-earlier.patch @@ -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"); + } diff --git a/target/linux/generic/backport-5.4/726-v5.5-net-sfp-avoid-power-switch-on-address-change-modules.patch b/target/linux/generic/backport-5.4/726-v5.5-net-sfp-avoid-power-switch-on-address-change-modules.patch new file mode 100644 index 0000000000..2ddd4c4d02 --- /dev/null +++ b/target/linux/generic/backport-5.4/726-v5.5-net-sfp-avoid-power-switch-on-address-change-modules.patch @@ -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; + } diff --git a/target/linux/generic/backport-5.4/727-v5.5-net-sfp-control-TX_DISABLE-and-phy-only-from-main-st.patch b/target/linux/generic/backport-5.4/727-v5.5-net-sfp-control-TX_DISABLE-and-phy-only-from-main-st.patch new file mode 100644 index 0000000000..0eac6abb88 --- /dev/null +++ b/target/linux/generic/backport-5.4/727-v5.5-net-sfp-control-TX_DISABLE-and-phy-only-from-main-st.patch @@ -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: diff --git a/target/linux/generic/backport-5.4/728-v5.5-net-sfp-split-the-PHY-probe-from-sfp_sm_mod_init.patch b/target/linux/generic/backport-5.4/728-v5.5-net-sfp-split-the-PHY-probe-from-sfp_sm_mod_init.patch new file mode 100644 index 0000000000..7830b9268f --- /dev/null +++ b/target/linux/generic/backport-5.4/728-v5.5-net-sfp-split-the-PHY-probe-from-sfp_sm_mod_init.patch @@ -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: diff --git a/target/linux/generic/backport-5.4/729-v5.5-net-sfp-eliminate-mdelay-from-PHY-probe.patch b/target/linux/generic/backport-5.4/729-v5.5-net-sfp-eliminate-mdelay-from-PHY-probe.patch new file mode 100644 index 0000000000..5dc92bd10e --- /dev/null +++ b/target/linux/generic/backport-5.4/729-v5.5-net-sfp-eliminate-mdelay-from-PHY-probe.patch @@ -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: diff --git a/target/linux/generic/backport-5.4/730-v5.5-net-sfp-allow-fault-processing-to-transition-to-othe.patch b/target/linux/generic/backport-5.4/730-v5.5-net-sfp-allow-fault-processing-to-transition-to-othe.patch new file mode 100644 index 0000000000..6e34e6cc0f --- /dev/null +++ b/target/linux/generic/backport-5.4/730-v5.5-net-sfp-allow-fault-processing-to-transition-to-othe.patch @@ -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); diff --git a/target/linux/generic/backport-5.4/731-v5.5-net-sfp-ensure-TX_FAULT-has-deasserted-before-probin.patch b/target/linux/generic/backport-5.4/731-v5.5-net-sfp-ensure-TX_FAULT-has-deasserted-before-probin.patch new file mode 100644 index 0000000000..03415fb6e7 --- /dev/null +++ b/target/linux/generic/backport-5.4/731-v5.5-net-sfp-ensure-TX_FAULT-has-deasserted-before-probin.patch @@ -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: diff --git a/target/linux/generic/backport-5.4/732-v5.5-net-sfp-track-upstream-s-attachment-state-in-state-m.patch b/target/linux/generic/backport-5.4/732-v5.5-net-sfp-track-upstream-s-attachment-state-in-state-m.patch new file mode 100644 index 0000000000..5ee44dbf0c --- /dev/null +++ b/target/linux/generic/backport-5.4/732-v5.5-net-sfp-track-upstream-s-attachment-state-in-state-m.patch @@ -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) diff --git a/target/linux/generic/backport-5.4/733-v5.5-net-sfp-split-power-mode-switching-from-probe.patch b/target/linux/generic/backport-5.4/733-v5.5-net-sfp-split-power-mode-switching-from-probe.patch new file mode 100644 index 0000000000..06b3cb5428 --- /dev/null +++ b/target/linux/generic/backport-5.4/733-v5.5-net-sfp-split-power-mode-switching-from-probe.patch @@ -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; diff --git a/target/linux/generic/backport-5.4/734-v5.5-net-sfp-move-module-insert-reporting-out-of-probe.patch b/target/linux/generic/backport-5.4/734-v5.5-net-sfp-move-module-insert-reporting-out-of-probe.patch new file mode 100644 index 0000000000..26b6a6b0e4 --- /dev/null +++ b/target/linux/generic/backport-5.4/734-v5.5-net-sfp-move-module-insert-reporting-out-of-probe.patch @@ -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]) diff --git a/target/linux/generic/backport-5.4/735-v5.5-net-sfp-allow-sfp-to-probe-slow-to-initialise-GPON-m.patch b/target/linux/generic/backport-5.4/735-v5.5-net-sfp-allow-sfp-to-probe-slow-to-initialise-GPON-m.patch new file mode 100644 index 0000000000..6cbd0864f7 --- /dev/null +++ b/target/linux/generic/backport-5.4/735-v5.5-net-sfp-allow-sfp-to-probe-slow-to-initialise-GPON-m.patch @@ -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; + } diff --git a/target/linux/generic/backport-5.4/736-v5.5-net-sfp-allow-modules-with-slow-diagnostics-to-probe.patch b/target/linux/generic/backport-5.4/736-v5.5-net-sfp-allow-modules-with-slow-diagnostics-to-probe.patch new file mode 100644 index 0000000000..fe1e6c4822 --- /dev/null +++ b/target/linux/generic/backport-5.4/736-v5.5-net-sfp-allow-modules-with-slow-diagnostics-to-probe.patch @@ -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) { diff --git a/target/linux/generic/backport-5.4/737-v5.5-net-phy-add-core-phylib-sfp-support.patch b/target/linux/generic/backport-5.4/737-v5.5-net-phy-add-core-phylib-sfp-support.patch new file mode 100644 index 0000000000..9b34d40a0f --- /dev/null +++ b/target/linux/generic/backport-5.4/737-v5.5-net-phy-add-core-phylib-sfp-support.patch @@ -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); diff --git a/target/linux/generic/backport-5.4/738-v5.5-net-phy-marvell10g-add-SFP-support.patch b/target/linux/generic/backport-5.4/738-v5.5-net-phy-marvell10g-add-SFP-support.patch new file mode 100644 index 0000000000..a67431ce5c --- /dev/null +++ b/target/linux/generic/backport-5.4/738-v5.5-net-phy-marvell10g-add-SFP-support.patch @@ -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) diff --git a/target/linux/generic/backport-5.4/739-v5.5-net-phylink-update-to-use-phy_support_asym_pause.patch b/target/linux/generic/backport-5.4/739-v5.5-net-phylink-update-to-use-phy_support_asym_pause.patch new file mode 100644 index 0000000000..15db0abc96 --- /dev/null +++ b/target/linux/generic/backport-5.4/739-v5.5-net-phylink-update-to-use-phy_support_asym_pause.patch @@ -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) diff --git a/target/linux/generic/backport-5.4/740-v5.5-net-phy-avoid-matching-all-ones-clause-45-PHY-IDs.patch b/target/linux/generic/backport-5.4/740-v5.5-net-phy-avoid-matching-all-ones-clause-45-PHY-IDs.patch new file mode 100644 index 0000000000..81f161e9b1 --- /dev/null +++ b/target/linux/generic/backport-5.4/740-v5.5-net-phy-avoid-matching-all-ones-clause-45-PHY-IDs.patch @@ -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); diff --git a/target/linux/generic/backport-5.4/741-v5.5-net-phylink-fix-link-mode-modification-in-PHY-mode.patch b/target/linux/generic/backport-5.4/741-v5.5-net-phylink-fix-link-mode-modification-in-PHY-mode.patch new file mode 100644 index 0000000000..596ecc8272 --- /dev/null +++ b/target/linux/generic/backport-5.4/741-v5.5-net-phylink-fix-link-mode-modification-in-PHY-mode.patch @@ -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); diff --git a/target/linux/generic/backport-5.4/742-v5.5-net-sfp-add-support-for-module-quirks.patch b/target/linux/generic/backport-5.4/742-v5.5-net-sfp-add-support-for-module-quirks.patch new file mode 100644 index 0000000000..5068bd468c --- /dev/null +++ b/target/linux/generic/backport-5.4/742-v5.5-net-sfp-add-support-for-module-quirks.patch @@ -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); + diff --git a/target/linux/generic/backport-5.4/743-v5.5-net-sfp-add-some-quirks-for-GPON-modules.patch b/target/linux/generic/backport-5.4/743-v5.5-net-sfp-add-some-quirks-for-GPON-modules.patch new file mode 100644 index 0000000000..10c34881d8 --- /dev/null +++ b/target/linux/generic/backport-5.4/743-v5.5-net-sfp-add-some-quirks-for-GPON-modules.patch @@ -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) diff --git a/target/linux/generic/backport-5.4/744-v5.5-net-sfp-soft-status-and-control-support.patch b/target/linux/generic/backport-5.4/744-v5.5-net-sfp-soft-status-and-control-support.patch new file mode 100644 index 0000000000..163d8072e6 --- /dev/null +++ b/target/linux/generic/backport-5.4/744-v5.5-net-sfp-soft-status-and-control-support.patch @@ -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), |