* [net-next v1] bpf: add bpf_ktime_get_real_ns helper
From: xiangxia.m.yue @ 2022-04-20 12:23 UTC (permalink / raw)
To: netdev, bpf
Cc: Tonghao Zhang, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Jiri Olsa, Dave Marchevsky,
Kuniyuki Iwashima, Joanne Koong, Geliang Tang, David S. Miller,
Jakub Kicinski, Eric Dumazet
From: Tonghao Zhang <xiangxia.m.yue@gmail.com>
This patch introduce a new bpf_ktime_get_real_ns helper, which may
help us to measure the skb latency in the ingress/forwarding path:
HW/SW[1] -> ip_rcv/tcp_rcv_established -> tcp_recvmsg_locked/tcp_update_recv_tstamps
* Insert BPF kprobe into ip_rcv/tcp_rcv_established invoking this helper.
Then we can inspect how long time elapsed since HW/SW.
* If inserting BPF kprobe tcp_update_recv_tstamps invoked by tcp_recvmsg,
we can measure how much latency skb in tcp receive queue. The reason for
this can be application fetch the TCP messages too late.
[1]:
- HW drivers may set skb_hwtstamps(skb)->hwtstamp
- SW __netif_receive_skb_core set skb->tstamp with ktime_get_real()
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Song Liu <songliubraving@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: John Fastabend <john.fastabend@gmail.com>
Cc: KP Singh <kpsingh@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Dave Marchevsky <davemarchevsky@fb.com>
Cc: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
Cc: Joanne Koong <joannekoong@fb.com>
Cc: Geliang Tang <geliang.tang@suse.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Eric Dumazet <edumazet@google.com>
Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
---
include/uapi/linux/bpf.h | 13 +++++++++++++
kernel/bpf/core.c | 1 +
kernel/bpf/helpers.c | 14 ++++++++++++++
tools/include/uapi/linux/bpf.h | 13 +++++++++++++
4 files changed, 41 insertions(+)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index d14b10b85e51..2565c587fe1b 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5143,6 +5143,18 @@ union bpf_attr {
* The **hash_algo** is returned on success,
* **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if
* invalid arguments are passed.
+ *
+ * u64 bpf_ktime_get_real_ns(void)
+ * Description
+ * Return a fine-grained version of the real (i.e., wall-clock) time,
+ * in nanoseconds. This clock is affected by discontinuous jumps in
+ * the system time (e.g., if the system administrator manually changes
+ * the clock), and by the incremental adjustments performed by adjtime(3)
+ * and NTP.
+ * See: **clock_gettime**\ (**CLOCK_REALTIME**)
+ * Return
+ * Current *ktime*.
+ *
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -5339,6 +5351,7 @@ union bpf_attr {
FN(copy_from_user_task), \
FN(skb_set_tstamp), \
FN(ima_file_hash), \
+ FN(ktime_get_real_ns), \
/* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 13e9dbeeedf3..acdf538b1dcd 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -2627,6 +2627,7 @@ const struct bpf_func_proto bpf_get_prandom_u32_proto __weak;
const struct bpf_func_proto bpf_get_smp_processor_id_proto __weak;
const struct bpf_func_proto bpf_get_numa_node_id_proto __weak;
const struct bpf_func_proto bpf_ktime_get_ns_proto __weak;
+const struct bpf_func_proto bpf_ktime_get_real_ns_proto __weak;
const struct bpf_func_proto bpf_ktime_get_boot_ns_proto __weak;
const struct bpf_func_proto bpf_ktime_get_coarse_ns_proto __weak;
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 315053ef6a75..d38548ed292f 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -159,6 +159,18 @@ const struct bpf_func_proto bpf_ktime_get_ns_proto = {
.ret_type = RET_INTEGER,
};
+BPF_CALL_0(bpf_ktime_get_real_ns)
+{
+ /* NMI safe access to clock realtime. */
+ return ktime_get_real_fast_ns();
+}
+
+const struct bpf_func_proto bpf_ktime_get_real_ns_proto = {
+ .func = bpf_ktime_get_real_ns,
+ .gpl_only = false,
+ .ret_type = RET_INTEGER,
+};
+
BPF_CALL_0(bpf_ktime_get_boot_ns)
{
/* NMI safe access to clock boottime */
@@ -1410,6 +1422,8 @@ bpf_base_func_proto(enum bpf_func_id func_id)
return &bpf_ktime_get_ns_proto;
case BPF_FUNC_ktime_get_boot_ns:
return &bpf_ktime_get_boot_ns_proto;
+ case BPF_FUNC_ktime_get_real_ns:
+ return &bpf_ktime_get_real_ns_proto;
case BPF_FUNC_ringbuf_output:
return &bpf_ringbuf_output_proto;
case BPF_FUNC_ringbuf_reserve:
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index d14b10b85e51..2565c587fe1b 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5143,6 +5143,18 @@ union bpf_attr {
* The **hash_algo** is returned on success,
* **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if
* invalid arguments are passed.
+ *
+ * u64 bpf_ktime_get_real_ns(void)
+ * Description
+ * Return a fine-grained version of the real (i.e., wall-clock) time,
+ * in nanoseconds. This clock is affected by discontinuous jumps in
+ * the system time (e.g., if the system administrator manually changes
+ * the clock), and by the incremental adjustments performed by adjtime(3)
+ * and NTP.
+ * See: **clock_gettime**\ (**CLOCK_REALTIME**)
+ * Return
+ * Current *ktime*.
+ *
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -5339,6 +5351,7 @@ union bpf_attr {
FN(copy_from_user_task), \
FN(skb_set_tstamp), \
FN(ima_file_hash), \
+ FN(ktime_get_real_ns), \
/* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
--
2.27.0
^ permalink raw reply related
* Re: [PATCH v3 2/2] net: sysctl: introduce sysctl SYSCTL_THREE
From: Tonghao Zhang @ 2022-04-20 12:43 UTC (permalink / raw)
To: Luis Chamberlain, Jakub Kicinski
Cc: Kees Cook, Iurii Zaikin, David S. Miller, Hideaki YOSHIFUJI,
David Ahern, Simon Horman, Julian Anastasov, Pablo Neira Ayuso,
Jozsef Kadlecsik, Linux Kernel Network Developers,
Florian Westphal, Dmitry Vyukov, Alexei Starovoitov, Eric Dumazet,
Marc Kleine-Budde, Lorenz Bauer, Akhmat Karakotov
In-Reply-To: <20220415163912.26530-3-xiangxia.m.yue@gmail.com>
On Sat, Apr 16, 2022 at 12:40 AM <xiangxia.m.yue@gmail.com> wrote:
>
> From: Tonghao Zhang <xiangxia.m.yue@gmail.com>
>
> This patch introdues the SYSCTL_THREE.
Hi Luis, Jakub
any thoughts? I have fixed v2.
> KUnit:
> [00:10:14] ================ sysctl_test (10 subtests) =================
> [00:10:14] [PASSED] sysctl_test_api_dointvec_null_tbl_data
> [00:10:14] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset
> [00:10:14] [PASSED] sysctl_test_api_dointvec_table_len_is_zero
> [00:10:14] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set
> [00:10:14] [PASSED] sysctl_test_dointvec_read_happy_single_positive
> [00:10:14] [PASSED] sysctl_test_dointvec_read_happy_single_negative
> [00:10:14] [PASSED] sysctl_test_dointvec_write_happy_single_positive
> [00:10:14] [PASSED] sysctl_test_dointvec_write_happy_single_negative
> [00:10:14] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min
> [00:10:14] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max
> [00:10:14] =================== [PASSED] sysctl_test ===================
>
> ./run_kselftest.sh -c sysctl
> ...
> ok 1 selftests: sysctl: sysctl.sh
>
> Cc: Luis Chamberlain <mcgrof@kernel.org>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Iurii Zaikin <yzaikin@google.com>
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: Jakub Kicinski <kuba@kernel.org>
> Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
> Cc: David Ahern <dsahern@kernel.org>
> Cc: Simon Horman <horms@verge.net.au>
> Cc: Julian Anastasov <ja@ssi.bg>
> Cc: Pablo Neira Ayuso <pablo@netfilter.org>
> Cc: Jozsef Kadlecsik <kadlec@netfilter.org>
> Cc: Florian Westphal <fw@strlen.de>
> Cc: Dmitry Vyukov <dvyukov@google.com>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Eric Dumazet <edumazet@google.com>
> Cc: Marc Kleine-Budde <mkl@pengutronix.de>
> Cc: Lorenz Bauer <lmb@cloudflare.com>
> Cc: Akhmat Karakotov <hmukos@yandex-team.ru>
> Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
> ---
> fs/proc/proc_sysctl.c | 2 +-
> include/linux/sysctl.h | 9 +++++----
> net/core/sysctl_net_core.c | 3 +--
> net/ipv4/sysctl_net_ipv4.c | 3 +--
> net/ipv6/sysctl_net_ipv6.c | 3 +--
> net/netfilter/ipvs/ip_vs_ctl.c | 4 +---
> 6 files changed, 10 insertions(+), 14 deletions(-)
>
> diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
> index 7d9cfc730bd4..5851c2a92c0d 100644
> --- a/fs/proc/proc_sysctl.c
> +++ b/fs/proc/proc_sysctl.c
> @@ -26,7 +26,7 @@ static const struct file_operations proc_sys_dir_file_operations;
> static const struct inode_operations proc_sys_dir_operations;
>
> /* shared constants to be used in various sysctls */
> -const int sysctl_vals[] = { -1, 0, 1, 2, 4, 100, 200, 1000, 3000, INT_MAX, 65535 };
> +const int sysctl_vals[] = { 0, 1, 2, 3, 4, 100, 200, 1000, 3000, INT_MAX, 65535, -1 };
> EXPORT_SYMBOL(sysctl_vals);
>
> const unsigned long sysctl_long_vals[] = { 0, 1, LONG_MAX };
> diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
> index 6353d6db69b2..80263f7cdb77 100644
> --- a/include/linux/sysctl.h
> +++ b/include/linux/sysctl.h
> @@ -38,10 +38,10 @@ struct ctl_table_header;
> struct ctl_dir;
>
> /* Keep the same order as in fs/proc/proc_sysctl.c */
> -#define SYSCTL_NEG_ONE ((void *)&sysctl_vals[0])
> -#define SYSCTL_ZERO ((void *)&sysctl_vals[1])
> -#define SYSCTL_ONE ((void *)&sysctl_vals[2])
> -#define SYSCTL_TWO ((void *)&sysctl_vals[3])
> +#define SYSCTL_ZERO ((void *)&sysctl_vals[0])
> +#define SYSCTL_ONE ((void *)&sysctl_vals[1])
> +#define SYSCTL_TWO ((void *)&sysctl_vals[2])
> +#define SYSCTL_THREE ((void *)&sysctl_vals[3])
> #define SYSCTL_FOUR ((void *)&sysctl_vals[4])
> #define SYSCTL_ONE_HUNDRED ((void *)&sysctl_vals[5])
> #define SYSCTL_TWO_HUNDRED ((void *)&sysctl_vals[6])
> @@ -51,6 +51,7 @@ struct ctl_dir;
>
> /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
> #define SYSCTL_MAXOLDUID ((void *)&sysctl_vals[10])
> +#define SYSCTL_NEG_ONE ((void *)&sysctl_vals[11])
>
> extern const int sysctl_vals[];
>
> diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
> index 3a0ce309ffcd..195ca5c28771 100644
> --- a/net/core/sysctl_net_core.c
> +++ b/net/core/sysctl_net_core.c
> @@ -25,7 +25,6 @@
>
> #include "dev.h"
>
> -static int three = 3;
> static int int_3600 = 3600;
> static int min_sndbuf = SOCK_MIN_SNDBUF;
> static int min_rcvbuf = SOCK_MIN_RCVBUF;
> @@ -553,7 +552,7 @@ static struct ctl_table net_core_table[] = {
> .mode = 0644,
> .proc_handler = proc_dointvec_minmax,
> .extra1 = SYSCTL_ZERO,
> - .extra2 = &three,
> + .extra2 = SYSCTL_THREE,
> },
> {
> .procname = "high_order_alloc_disable",
> diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
> index 9ff60a389cd0..cd448cdd3b38 100644
> --- a/net/ipv4/sysctl_net_ipv4.c
> +++ b/net/ipv4/sysctl_net_ipv4.c
> @@ -20,7 +20,6 @@
> #include <net/protocol.h>
> #include <net/netevent.h>
>
> -static int three __maybe_unused = 3;
> static int tcp_retr1_max = 255;
> static int ip_local_port_range_min[] = { 1, 1 };
> static int ip_local_port_range_max[] = { 65535, 65535 };
> @@ -1056,7 +1055,7 @@ static struct ctl_table ipv4_net_table[] = {
> .mode = 0644,
> .proc_handler = proc_fib_multipath_hash_policy,
> .extra1 = SYSCTL_ZERO,
> - .extra2 = &three,
> + .extra2 = SYSCTL_THREE,
> },
> {
> .procname = "fib_multipath_hash_fields",
> diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
> index 560c48d0ddb7..94a0a294c6a1 100644
> --- a/net/ipv6/sysctl_net_ipv6.c
> +++ b/net/ipv6/sysctl_net_ipv6.c
> @@ -23,7 +23,6 @@
> #endif
> #include <linux/ioam6.h>
>
> -static int three = 3;
> static int flowlabel_reflect_max = 0x7;
> static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX;
> static u32 rt6_multipath_hash_fields_all_mask =
> @@ -171,7 +170,7 @@ static struct ctl_table ipv6_table_template[] = {
> .mode = 0644,
> .proc_handler = proc_rt6_multipath_hash_policy,
> .extra1 = SYSCTL_ZERO,
> - .extra2 = &three,
> + .extra2 = SYSCTL_THREE,
> },
> {
> .procname = "fib_multipath_hash_fields",
> diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
> index 7f645328b47f..efab2b06d373 100644
> --- a/net/netfilter/ipvs/ip_vs_ctl.c
> +++ b/net/netfilter/ipvs/ip_vs_ctl.c
> @@ -1767,8 +1767,6 @@ static int ip_vs_zero_all(struct netns_ipvs *ipvs)
>
> #ifdef CONFIG_SYSCTL
>
> -static int three = 3;
> -
> static int
> proc_do_defense_mode(struct ctl_table *table, int write,
> void *buffer, size_t *lenp, loff_t *ppos)
> @@ -1977,7 +1975,7 @@ static struct ctl_table vs_vars[] = {
> .mode = 0644,
> .proc_handler = proc_dointvec_minmax,
> .extra1 = SYSCTL_ZERO,
> - .extra2 = &three,
> + .extra2 = SYSCTL_THREE,
> },
> {
> .procname = "nat_icmp_send",
> --
> 2.27.0
>
--
Best regards, Tonghao
^ permalink raw reply
* Re: [net-next v1] bpf: add bpf_ktime_get_real_ns helper
From: Toke Høiland-Jørgensen @ 2022-04-20 12:53 UTC (permalink / raw)
To: xiangxia.m.yue, netdev, bpf
Cc: Tonghao Zhang, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Jiri Olsa, Dave Marchevsky,
Kuniyuki Iwashima, Joanne Koong, Geliang Tang, David S. Miller,
Jakub Kicinski, Eric Dumazet
In-Reply-To: <20220420122307.5290-1-xiangxia.m.yue@gmail.com>
xiangxia.m.yue@gmail.com writes:
> From: Tonghao Zhang <xiangxia.m.yue@gmail.com>
>
> This patch introduce a new bpf_ktime_get_real_ns helper, which may
> help us to measure the skb latency in the ingress/forwarding path:
>
> HW/SW[1] -> ip_rcv/tcp_rcv_established -> tcp_recvmsg_locked/tcp_update_recv_tstamps
>
> * Insert BPF kprobe into ip_rcv/tcp_rcv_established invoking this helper.
> Then we can inspect how long time elapsed since HW/SW.
> * If inserting BPF kprobe tcp_update_recv_tstamps invoked by tcp_recvmsg,
> we can measure how much latency skb in tcp receive queue. The reason for
> this can be application fetch the TCP messages too late.
Why not just use one of the existing ktime helpers and also add a BPF
probe to set the initial timestamp instead of relying on skb->tstamp?
-Toke
^ permalink raw reply
* Re: [PATCH net 1/1] net: stmmac: add fsleep() in HW Rx timestamp checking loop
From: Richard Cochran @ 2022-04-20 12:54 UTC (permalink / raw)
To: Tan Tee Min
Cc: Jakub Kicinski, Tan Tee Min, Giuseppe Cavallaro, Alexandre Torgue,
Jose Abreu, David S . Miller, Paolo Abeni, Maxime Coquelin,
Rayagond Kokatanur, netdev, linux-stm32, linux-arm-kernel,
linux-kernel, stable, Voon Wei Feng, Wong Vee Khee,
Song Yoong Siang, Alexandre Torgue
In-Reply-To: <20220420051508.GA18173@linux.intel.com>
On Wed, Apr 20, 2022 at 01:15:08PM +0800, Tan Tee Min wrote:
> No. The context descriptor (frame) is possibly still owned by the
> DMA controller in this situation.
So that is a problem. The solution is to postpone this logic until
the driver owns the buffer. Doesn't the HW offer some means of
notification, like an interrupt for example?
Thanks,
Richard
^ permalink raw reply
* [PATCH 5/5] net: stmmac: Use acpi_mdiobus_register() for ACPI based system
From: Kai-Heng Feng @ 2022-04-20 12:40 UTC (permalink / raw)
To: andrew, hkallweit1, linux, peppe.cavallaro, alexandre.torgue,
joabreu, davem, kuba, pabeni
Cc: Kai-Heng Feng, Maxime Coquelin, netdev, linux-stm32,
linux-arm-kernel, linux-kernel
In-Reply-To: <20220420124053.853891-1-kai.heng.feng@canonical.com>
Child nodes of stmmac ACPI node may have additional properties that the
PHY layer can use.
To achieve that, use acpi_mdiobus_register() to find PHY nodes
references via "phy-handle", so the properties of PHY nodes can be used
by the PHY layer.
Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
index a5d150c5f3d8c..37cebd8f2ec5c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
@@ -10,6 +10,8 @@
Maintainer: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#include <linux/acpi.h>
+#include <linux/acpi_mdio.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@@ -445,6 +447,7 @@ int stmmac_mdio_register(struct net_device *ndev)
struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
struct device_node *mdio_node = priv->plat->mdio_node;
struct device *dev = ndev->dev.parent;
+ struct fwnode_handle *fwnode = dev->fwnode;
int addr, found, max_addr;
if (!mdio_bus_data)
@@ -488,7 +491,10 @@ int stmmac_mdio_register(struct net_device *ndev)
new_bus->phy_mask = mdio_bus_data->phy_mask;
new_bus->parent = priv->device;
- err = of_mdiobus_register(new_bus, mdio_node);
+ if (is_acpi_node(fwnode))
+ err = acpi_mdiobus_register(new_bus, fwnode);
+ else
+ err = of_mdiobus_register(new_bus, mdio_node);
if (err != 0) {
dev_err(dev, "Cannot register the MDIO bus\n");
goto bus_register_fail;
--
2.34.1
^ permalink raw reply related
* [PATCH 1/5] net: mdio: Mask PHY only when its ACPI node is present
From: Kai-Heng Feng @ 2022-04-20 12:40 UTC (permalink / raw)
To: andrew, hkallweit1, linux, peppe.cavallaro, alexandre.torgue,
joabreu, davem, kuba, pabeni
Cc: Kai-Heng Feng, netdev, linux-kernel
In-Reply-To: <20220420124053.853891-1-kai.heng.feng@canonical.com>
Not all PHY has an ACPI node, for those nodes auto probing is still
needed.
So only mask those PHYs with ACPI nodes.
Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
---
drivers/net/mdio/acpi_mdio.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/net/mdio/acpi_mdio.c b/drivers/net/mdio/acpi_mdio.c
index d77c987fda9cd..f9369319ada19 100644
--- a/drivers/net/mdio/acpi_mdio.c
+++ b/drivers/net/mdio/acpi_mdio.c
@@ -33,8 +33,15 @@ int acpi_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode)
u32 addr;
int ret;
- /* Mask out all PHYs from auto probing. */
- mdio->phy_mask = GENMASK(31, 0);
+ /* Loop over the child nodes and mask out PHY from auto probing */
+ fwnode_for_each_child_node(fwnode, child) {
+ ret = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &addr);
+ if (ret || addr >= PHY_MAX_ADDR)
+ continue;
+
+ mdio->phy_mask |= BIT(addr);
+ }
+
ret = mdiobus_register(mdio);
if (ret)
return ret;
--
2.34.1
^ permalink raw reply related
* [PATCH 3/5] net: phy: Add helpers to save and restore firmware LED
From: Kai-Heng Feng @ 2022-04-20 12:40 UTC (permalink / raw)
To: andrew, hkallweit1, linux, peppe.cavallaro, alexandre.torgue,
joabreu, davem, kuba, pabeni
Cc: Kai-Heng Feng, netdev, linux-kernel
In-Reply-To: <20220420124053.853891-1-kai.heng.feng@canonical.com>
PHY drivers may set hardcoded LED config in phy_init_hw(), so to
preserve the firmware LED after init or system sleep, it needs to be
saved before init and be restored after.
To do so, create helpers and driver callbacks to access and save LED
config for the need.
Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
---
drivers/net/phy/phy_device.c | 22 ++++++++++++++++++++++
include/linux/phy.h | 4 ++++
2 files changed, 26 insertions(+)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 8406ac739def8..33b402279febe 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1154,6 +1154,24 @@ static int phy_poll_reset(struct phy_device *phydev)
return 0;
}
+static void phy_save_led(struct phy_device *phydev)
+{
+ if (!phydev->use_firmware_led)
+ return;
+
+ if (phydev->drv->get_led_config)
+ phydev->led_config = phydev->drv->get_led_config(phydev);
+}
+
+static void phy_restore_led(struct phy_device *phydev)
+{
+ if (!phydev->use_firmware_led)
+ return;
+
+ if (phydev->drv->set_led_config && phydev->led_config)
+ phydev->drv->set_led_config(phydev, phydev->led_config);
+}
+
int phy_init_hw(struct phy_device *phydev)
{
int ret = 0;
@@ -1463,6 +1481,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
if (dev)
netif_carrier_off(phydev->attached_dev);
+ phy_save_led(phydev);
+
/* Do initial configuration here, now that
* we have certain key parameters
* (dev_flags and interface)
@@ -1803,6 +1823,8 @@ int __phy_resume(struct phy_device *phydev)
if (!ret)
phydev->suspended = false;
+ phy_restore_led(phydev);
+
return ret;
}
EXPORT_SYMBOL(__phy_resume);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 53e693b3430ec..cd9c05ff75ee1 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -657,6 +657,7 @@ struct phy_device {
u32 eee_broken_modes;
bool use_firmware_led;
+ int led_config;
#ifdef CONFIG_LED_TRIGGER_PHY
struct phy_led_trigger *phy_led_triggers;
unsigned int phy_num_led_triggers;
@@ -933,6 +934,9 @@ struct phy_driver {
int (*get_sqi)(struct phy_device *dev);
/** @get_sqi_max: Get the maximum signal quality indication */
int (*get_sqi_max)(struct phy_device *dev);
+
+ int (*get_led_config)(struct phy_device *dev);
+ void (*set_led_config)(struct phy_device *dev, int led_config);
};
#define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
struct phy_driver, mdiodrv)
--
2.34.1
^ permalink raw reply related
* [PATCH 4/5] net: phy: marvell: Add LED accessors for Marvell 88E1510
From: Kai-Heng Feng @ 2022-04-20 12:40 UTC (permalink / raw)
To: andrew, hkallweit1, linux, peppe.cavallaro, alexandre.torgue,
joabreu, davem, kuba, pabeni
Cc: Kai-Heng Feng, netdev, linux-kernel
In-Reply-To: <20220420124053.853891-1-kai.heng.feng@canonical.com>
Implement get_led_config() and set_led_config() callbacks so phy core
can use firmware LED as platform requested.
Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
---
drivers/net/phy/marvell.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 2702faf7b0f60..c5f13e09b0692 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -750,6 +750,30 @@ static int m88e1510_config_aneg(struct phy_device *phydev)
return err;
}
+static int marvell_get_led_config(struct phy_device *phydev)
+{
+ int led;
+
+ led = phy_read_paged(phydev, MII_MARVELL_LED_PAGE, MII_PHY_LED_CTRL);
+ if (led < 0) {
+ phydev_warn(phydev, "Fail to get marvell phy LED.\n");
+ led = 0;
+ }
+
+ return led;
+}
+
+static void marvell_set_led_config(struct phy_device *phydev, int led_config)
+{
+ int err;
+
+ err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE, MII_PHY_LED_CTRL,
+ led_config);
+
+ if (err < 0)
+ phydev_warn(phydev, "Fail to set marvell phy LED.\n");
+}
+
static void marvell_config_led(struct phy_device *phydev)
{
u16 def_config;
@@ -3139,6 +3163,8 @@ static struct phy_driver marvell_drivers[] = {
.cable_test_start = marvell_vct7_cable_test_start,
.cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
.cable_test_get_status = marvell_vct7_cable_test_get_status,
+ .get_led_config = marvell_get_led_config,
+ .set_led_config = marvell_set_led_config,
},
{
.phy_id = MARVELL_PHY_ID_88E1540,
--
2.34.1
^ permalink raw reply related
* [PATCH 2/5] net: mdio: Add "use-firmware-led" firmware property
From: Kai-Heng Feng @ 2022-04-20 12:40 UTC (permalink / raw)
To: andrew, hkallweit1, linux, peppe.cavallaro, alexandre.torgue,
joabreu, davem, kuba, pabeni
Cc: Kai-Heng Feng, netdev, linux-kernel
In-Reply-To: <20220420124053.853891-1-kai.heng.feng@canonical.com>
Some system may prefer preset PHY LED setting instead of the one
hardcoded in the PHY driver, so adding a new firmware
property, "use-firmware-led", to cope with that.
On ACPI based system the ASL looks like this:
Scope (_SB.PC00.OTN0)
{
Device (PHY0)
{
Name (_ADR, One) // _ADR: Address
Name (_DSD, Package (0x02) // _DSD: Device-Specific Data
{
ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
Package (0x01)
{
Package (0x02)
{
"use-firmware-led",
One
}
}
})
}
Name (_DSD, Package (0x02) // _DSD: Device-Specific Data
{
ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
Package (0x01)
{
Package (0x02)
{
"phy-handle",
PHY0
}
}
})
}
Basically use the "phy-handle" reference to read the "use-firmware-led"
boolean.
Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
---
drivers/net/mdio/fwnode_mdio.c | 4 ++++
include/linux/phy.h | 1 +
2 files changed, 5 insertions(+)
diff --git a/drivers/net/mdio/fwnode_mdio.c b/drivers/net/mdio/fwnode_mdio.c
index 1c1584fca6327..bfca67b42164b 100644
--- a/drivers/net/mdio/fwnode_mdio.c
+++ b/drivers/net/mdio/fwnode_mdio.c
@@ -144,6 +144,10 @@ int fwnode_mdiobus_register_phy(struct mii_bus *bus,
*/
if (mii_ts)
phy->mii_ts = mii_ts;
+
+ phy->use_firmware_led =
+ fwnode_property_read_bool(child, "use-firmware-led");
+
return 0;
}
EXPORT_SYMBOL(fwnode_mdiobus_register_phy);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 36ca2b5c22533..53e693b3430ec 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -656,6 +656,7 @@ struct phy_device {
/* Energy efficient ethernet modes which should be prohibited */
u32 eee_broken_modes;
+ bool use_firmware_led;
#ifdef CONFIG_LED_TRIGGER_PHY
struct phy_led_trigger *phy_led_triggers;
unsigned int phy_num_led_triggers;
--
2.34.1
^ permalink raw reply related
* Re: [PATCH 2/5] net: mdio: Add "use-firmware-led" firmware property
From: Andrew Lunn @ 2022-04-20 13:01 UTC (permalink / raw)
To: Kai-Heng Feng
Cc: hkallweit1, linux, peppe.cavallaro, alexandre.torgue, joabreu,
davem, kuba, pabeni, netdev, linux-kernel
In-Reply-To: <20220420124053.853891-3-kai.heng.feng@canonical.com>
On Wed, Apr 20, 2022 at 08:40:49PM +0800, Kai-Heng Feng wrote:
> Some system may prefer preset PHY LED setting instead of the one
> hardcoded in the PHY driver, so adding a new firmware
> property, "use-firmware-led", to cope with that.
>
> On ACPI based system the ASL looks like this:
>
> Scope (_SB.PC00.OTN0)
> {
> Device (PHY0)
> {
> Name (_ADR, One) // _ADR: Address
> Name (_DSD, Package (0x02) // _DSD: Device-Specific Data
> {
> ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
> Package (0x01)
> {
> Package (0x02)
> {
> "use-firmware-led",
> One
> }
> }
> })
> }
>
> Name (_DSD, Package (0x02) // _DSD: Device-Specific Data
> {
> ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
> Package (0x01)
> {
> Package (0x02)
> {
> "phy-handle",
> PHY0
> }
> }
> })
> }
>
> Basically use the "phy-handle" reference to read the "use-firmware-led"
> boolean.
Please update Documentation/firmware-guide/acpi/dsd/phy.rst
Please also Cc: the ACPI list. I have no idea what the naming
convention is for ACPI properties.
>
> Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
> ---
> drivers/net/mdio/fwnode_mdio.c | 4 ++++
> include/linux/phy.h | 1 +
> 2 files changed, 5 insertions(+)
>
> diff --git a/drivers/net/mdio/fwnode_mdio.c b/drivers/net/mdio/fwnode_mdio.c
> index 1c1584fca6327..bfca67b42164b 100644
> --- a/drivers/net/mdio/fwnode_mdio.c
> +++ b/drivers/net/mdio/fwnode_mdio.c
> @@ -144,6 +144,10 @@ int fwnode_mdiobus_register_phy(struct mii_bus *bus,
> */
> if (mii_ts)
> phy->mii_ts = mii_ts;
> +
> + phy->use_firmware_led =
> + fwnode_property_read_bool(child, "use-firmware-led");
> +
This is an ACPI only property. It is not valid in DT. It does not
fulfil the DT naming requirement etc. So please move this up inside
the if (is_acpi_node(child)) clause.
> diff --git a/include/linux/phy.h b/include/linux/phy.h
> index 36ca2b5c22533..53e693b3430ec 100644
> --- a/include/linux/phy.h
> +++ b/include/linux/phy.h
> @@ -656,6 +656,7 @@ struct phy_device {
> /* Energy efficient ethernet modes which should be prohibited */
> u32 eee_broken_modes;
>
> + bool use_firmware_led;
There is probably a two byte hole after mdix_ctrl. So please consider
adding it there. Also, don't forget to update the documentation.
Andrew
^ permalink raw reply
* Re: [PATCH net-next] ixgbe: add xdp frags support to ndo_xdp_xmit
From: Lorenzo Bianconi @ 2022-04-20 13:09 UTC (permalink / raw)
To: anthony.l.nguyen
Cc: netdev, davem, kuba, pabeni, bpf, ast, daniel, andrii,
jesse.brandeburg, anthony.l.nguyen, magnus.karlsson, jbrouer,
toke, intel-wired-lan
In-Reply-To: <6de1d7547b60677ad0b0f7ebcbc7ebc76a31dcf7.1649180962.git.lorenzo@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 5034 bytes --]
> Add the capability to map non-linear xdp frames in XDP_TX and ndo_xdp_xmit
> callback.
Hi Tony,
do you have any feedbacks about this patch?
Regards,
Lorenzo
>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
> drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 99 ++++++++++++-------
> 1 file changed, 63 insertions(+), 36 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> index c4a4954aa317..8b84c9b2eecc 100644
> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> @@ -2344,6 +2344,7 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
> hard_start = page_address(rx_buffer->page) +
> rx_buffer->page_offset - offset;
> xdp_prepare_buff(&xdp, hard_start, offset, size, true);
> + xdp_buff_clear_frags_flag(&xdp);
> #if (PAGE_SIZE > 4096)
> /* At larger PAGE_SIZE, frame_sz depend on len size */
> xdp.frame_sz = ixgbe_rx_frame_truesize(rx_ring, size);
> @@ -8571,57 +8572,83 @@ static u16 ixgbe_select_queue(struct net_device *dev, struct sk_buff *skb,
> int ixgbe_xmit_xdp_ring(struct ixgbe_ring *ring,
> struct xdp_frame *xdpf)
> {
> - struct ixgbe_tx_buffer *tx_buffer;
> - union ixgbe_adv_tx_desc *tx_desc;
> - u32 len, cmd_type;
> - dma_addr_t dma;
> - u16 i;
> -
> - len = xdpf->len;
> + struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf);
> + u8 nr_frags = unlikely(xdp_frame_has_frags(xdpf)) ? sinfo->nr_frags : 0;
> + u16 i = 0, index = ring->next_to_use;
> + struct ixgbe_tx_buffer *tx_head = &ring->tx_buffer_info[index];
> + struct ixgbe_tx_buffer *tx_buff = tx_head;
> + union ixgbe_adv_tx_desc *tx_desc = IXGBE_TX_DESC(ring, index);
> + u32 cmd_type, len = xdpf->len;
> + void *data = xdpf->data;
>
> - if (unlikely(!ixgbe_desc_unused(ring)))
> + if (unlikely(ixgbe_desc_unused(ring) < 1 + nr_frags))
> return IXGBE_XDP_CONSUMED;
>
> - dma = dma_map_single(ring->dev, xdpf->data, len, DMA_TO_DEVICE);
> - if (dma_mapping_error(ring->dev, dma))
> - return IXGBE_XDP_CONSUMED;
> + tx_head->bytecount = xdp_get_frame_len(xdpf);
> + tx_head->gso_segs = 1;
> + tx_head->xdpf = xdpf;
>
> - /* record the location of the first descriptor for this packet */
> - tx_buffer = &ring->tx_buffer_info[ring->next_to_use];
> - tx_buffer->bytecount = len;
> - tx_buffer->gso_segs = 1;
> - tx_buffer->protocol = 0;
> + tx_desc->read.olinfo_status =
> + cpu_to_le32(tx_head->bytecount << IXGBE_ADVTXD_PAYLEN_SHIFT);
>
> - i = ring->next_to_use;
> - tx_desc = IXGBE_TX_DESC(ring, i);
> + for (;;) {
> + dma_addr_t dma;
>
> - dma_unmap_len_set(tx_buffer, len, len);
> - dma_unmap_addr_set(tx_buffer, dma, dma);
> - tx_buffer->xdpf = xdpf;
> + dma = dma_map_single(ring->dev, data, len, DMA_TO_DEVICE);
> + if (dma_mapping_error(ring->dev, dma))
> + goto unmap;
>
> - tx_desc->read.buffer_addr = cpu_to_le64(dma);
> + dma_unmap_len_set(tx_buff, len, len);
> + dma_unmap_addr_set(tx_buff, dma, dma);
> +
> + cmd_type = IXGBE_ADVTXD_DTYP_DATA | IXGBE_ADVTXD_DCMD_DEXT |
> + IXGBE_ADVTXD_DCMD_IFCS | len;
> + tx_desc->read.cmd_type_len = cpu_to_le32(cmd_type);
> + tx_desc->read.buffer_addr = cpu_to_le64(dma);
> + tx_buff->protocol = 0;
> +
> + if (++index == ring->count)
> + index = 0;
> +
> + if (i == nr_frags)
> + break;
> +
> + tx_buff = &ring->tx_buffer_info[index];
> + tx_desc = IXGBE_TX_DESC(ring, index);
> + tx_desc->read.olinfo_status = 0;
>
> + data = skb_frag_address(&sinfo->frags[i]);
> + len = skb_frag_size(&sinfo->frags[i]);
> + i++;
> + }
> /* put descriptor type bits */
> - cmd_type = IXGBE_ADVTXD_DTYP_DATA |
> - IXGBE_ADVTXD_DCMD_DEXT |
> - IXGBE_ADVTXD_DCMD_IFCS;
> - cmd_type |= len | IXGBE_TXD_CMD;
> - tx_desc->read.cmd_type_len = cpu_to_le32(cmd_type);
> - tx_desc->read.olinfo_status =
> - cpu_to_le32(len << IXGBE_ADVTXD_PAYLEN_SHIFT);
> + tx_desc->read.cmd_type_len |= cpu_to_le32(IXGBE_TXD_CMD);
>
> /* Avoid any potential race with xdp_xmit and cleanup */
> smp_wmb();
>
> - /* set next_to_watch value indicating a packet is present */
> - i++;
> - if (i == ring->count)
> - i = 0;
> -
> - tx_buffer->next_to_watch = tx_desc;
> - ring->next_to_use = i;
> + tx_head->next_to_watch = tx_desc;
> + ring->next_to_use = index;
>
> return IXGBE_XDP_TX;
> +
> +unmap:
> + for (;;) {
> + tx_buff = &ring->tx_buffer_info[index];
> + if (dma_unmap_len(tx_buff, len))
> + dma_unmap_page(ring->dev, dma_unmap_addr(tx_buff, dma),
> + dma_unmap_len(tx_buff, len),
> + DMA_TO_DEVICE);
> + dma_unmap_len_set(tx_buff, len, 0);
> + if (tx_buff == tx_head)
> + break;
> +
> + if (!index)
> + index += ring->count;
> + index--;
> + }
> +
> + return IXGBE_XDP_CONSUMED;
> }
>
> netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
> --
> 2.35.1
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH net 2/2] virtio_net: check L3 protocol for VLAN packets
From: Willem de Bruijn @ 2022-04-20 13:12 UTC (permalink / raw)
To: Hangbin Liu
Cc: Willem de Bruijn, Maxim Mikityanskiy, Mike Pattrick,
Michael S . Tsirkin, netdev, Eric Dumazet, virtualization,
Balazs Nemeth, Jakub Kicinski, Paolo Abeni, David S . Miller
In-Reply-To: <Yl9d2L39BzUiLINN@Laptop-X1>
On Tue, Apr 19, 2022 at 9:16 PM Hangbin Liu <liuhangbin@gmail.com> wrote:
>
> Hi Willem,
>
> On Tue, Apr 19, 2022 at 09:52:46AM -0400, Willem de Bruijn wrote:
> > Segmentation offload requires checksum offload. Packets that request
>
> OK, makes sense.
>
> > GSO but not NEEDS_CSUM are an aberration. We had to go out of our way
> > to handle them because the original implementation did not explicitly
> > flag and drop these. But we should not extend that to new types.
>
> So do you mean, the current gso types are enough, we should not extend to
> handle VLAN headers if no NEEDS_CSUM flag. This patch can be dropped, right?
That's right.
> Although I don't understand why we should not extend to support VLAN GSO.
> I'm OK if you think this patch should be dropped when I re-post patch 1/2 to
> net-next.
>
> Thanks
> Hangbin
^ permalink raw reply
* Re: [PATCH net-next 04/12] net: pcs: add Renesas MII converter driver
From: Geert Uytterhoeven @ 2022-04-20 13:25 UTC (permalink / raw)
To: Clément Léger
Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean,
David S . Miller, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Geert Uytterhoeven, Magnus Damm,
Heiner Kallweit, Russell King, Thomas Petazzoni, Herve Codina,
Miquèl Raynal, Milan Stevanovic, Jimmy Lalande,
Linux Kernel Mailing List,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Linux-Renesas, netdev
In-Reply-To: <20220414122250.158113-5-clement.leger@bootlin.com>
Hi Clément,
Thanks for your patch!
Only cosmetic comments from me, as I'm not too familiar with MII.
On Thu, Apr 14, 2022 at 2:24 PM Clément Léger <clement.leger@bootlin.com> wrote:
> Add PCS driver for the MII converter that is present on Renesas RZ/N1
Add a ... on the ...
> SoC. This MII converter is reponsible of converting MII to RMII/RGMII
responsible for
> or act as a MII passtrough. Exposing it as a PCS allows to reuse it
pass-through
> in both the switch driver and the stmmac driver. Currently, this driver
> only allows the PCS to be used by the dual Cortex-A7 subsystem since
> the register locking system is not used.
>
> Signed-off-by: Clément Léger <clement.leger@bootlin.com>
> --- a/drivers/net/pcs/Kconfig
> +++ b/drivers/net/pcs/Kconfig
> @@ -18,4 +18,11 @@ config PCS_LYNX
> This module provides helpers to phylink for managing the Lynx PCS
> which is part of the Layerscape and QorIQ Ethernet SERDES.
>
> +config PCS_RZN1_MIIC
> + tristate "Renesas RZN1 MII converter"
RZ/N1
> + help
> + This module provides a driver for the MII converter that is available
> + on RZN1 SoC. This PCS convert MII to RMII/RGMII or can be in
RZ/N1
> + passthrough mode for MII.
> +
> endmenu
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH net-next] net/af_packet: add VLAN support for AF_PACKET SOCK_RAW GSO
From: Willem de Bruijn @ 2022-04-20 13:45 UTC (permalink / raw)
To: Hangbin Liu
Cc: netdev, Michael S . Tsirkin, Jason Wang, David S . Miller,
Jakub Kicinski, Paolo Abeni, Maxim Mikityanskiy, virtualization,
Balazs Nemeth, Mike Pattrick, Eric Dumazet
In-Reply-To: <20220420082758.581245-1-liuhangbin@gmail.com>
On Wed, Apr 20, 2022 at 4:28 AM Hangbin Liu <liuhangbin@gmail.com> wrote:
>
> Currently, the kernel drops GSO VLAN tagged packet if it's created with
> socket(AF_PACKET, SOCK_RAW, 0) plus virtio_net_hdr.
>
> The reason is AF_PACKET doesn't adjust the skb network header if there is
> a VLAN tag. Then after virtio_net_hdr_set_proto() called, the skb->protocol
> will be set to ETH_P_IP/IPv6. And in later inet/ipv6_gso_segment() the skb
> is dropped as network header position is invalid.
>
> Let's handle VLAN packets by adjusting network header position in
> packet_parse_headers(), and move the function in packet_snd() before
> calling virtio_net_hdr_set_proto().
The network header is set in
skb_reset_network_header(skb);
err = -EINVAL;
if (sock->type == SOCK_DGRAM) {
offset = dev_hard_header(skb, dev, ntohs(proto), addr,
NULL, len);
if (unlikely(offset < 0))
goto out_free;
} else if (reserve) {
skb_reserve(skb, -reserve);
if (len < reserve + sizeof(struct ipv6hdr) &&
dev->min_header_len != dev->hard_header_len)
skb_reset_network_header(skb);
}
If all that is needed is to move the network header beyond an optional
VLAN tag in the case of SOCK_RAW, then this can be done in the else
for Ethernet packets.
It is not safe to increase reserve, as that would eat into the
reserved hlen LL_RESERVED_SPACE(dev), which does not account for
optional VLAN headers.
Instead of setting here first, then patching up again later in
packet_parse_headers.
This change affects all packets with VLAN headers, not just those with
GSO. I imagine that moving the network header is safe for all, but
don't know that code well enough to verify that it does not have
unintended side effects. Does dev_queue_xmit expect the network header
to point to the start of the VLAN headers or after, for instance?
> No need to update tpacket_snd() as it calls packet_parse_headers() in
> tpacket_fill_skb(), which is already before calling virtio_net_hdr_*
> functions.
>
> skb->no_fcs setting is also moved upper to make all skb settings together
> and keep consistence with function packet_sendmsg_spkt().
>
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> ---
> net/packet/af_packet.c | 18 +++++++++++++-----
> 1 file changed, 13 insertions(+), 5 deletions(-)
>
> diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
> index 002d2b9c69dd..cfdbda28ef82 100644
> --- a/net/packet/af_packet.c
> +++ b/net/packet/af_packet.c
> @@ -1924,12 +1924,20 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev,
>
> static void packet_parse_headers(struct sk_buff *skb, struct socket *sock)
> {
> + int depth;
> +
> if ((!skb->protocol || skb->protocol == htons(ETH_P_ALL)) &&
> sock->type == SOCK_RAW) {
> skb_reset_mac_header(skb);
> skb->protocol = dev_parse_header_protocol(skb);
> }
>
> + /* Move network header to the right position for VLAN tagged packets */
> + if (likely(skb->dev->type == ARPHRD_ETHER) &&
> + eth_type_vlan(skb->protocol) &&
> + __vlan_get_protocol(skb, skb->protocol, &depth) != 0)
> + skb_set_network_header(skb, depth);
> +
> skb_probe_transport_header(skb);
> }
>
> @@ -3047,6 +3055,11 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
> skb->mark = sockc.mark;
> skb->tstamp = sockc.transmit_time;
>
> + if (unlikely(extra_len == 4))
> + skb->no_fcs = 1;
> +
> + packet_parse_headers(skb, sock);
> +
> if (has_vnet_hdr) {
> err = virtio_net_hdr_to_skb(skb, &vnet_hdr, vio_le());
> if (err)
> @@ -3055,11 +3068,6 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
> virtio_net_hdr_set_proto(skb, &vnet_hdr);
> }
>
> - packet_parse_headers(skb, sock);
> -
> - if (unlikely(extra_len == 4))
> - skb->no_fcs = 1;
> -
> err = po->xmit(skb);
> if (unlikely(err != 0)) {
> if (err > 0)
> --
> 2.35.1
>
^ permalink raw reply
* Support for IEEE1588 timestamping in the BCM54210PE PHY using the kernel mii_timestamper interface
From: Lasse Johnsen @ 2022-04-20 14:03 UTC (permalink / raw)
To: netdev; +Cc: richardcochran, Gordon Hollingworth, Ahmad Byagowi
[-- Attachment #1: Type: text/plain, Size: 2117 bytes --]
Hello,
The attached set of patches adds support for the IEEE1588 functionality on the BCM54210PE PHY using the Linux Kernel mii_timestamper interface. The BCM54210PE PHY can be found in the Raspberry PI Compute Module 4 and the work has been undertaken by Timebeat.app on behalf of Raspberry PI with help and support from the nice engineers at Broadcom.
Design:
After the ethernet frames are identified using the ptp_classify_raw function, they are parsed through the mii_timestamper interface to the bcm54210pe_rxtstamp and bcm54210pe_txtstamp functions via the skb_defer_rx_timestamp and skb_clone_tx_timestamp kernel functions.
In both cases these functions enqueue the sk_buff ptrs and schedules a work struct thread that attaches timestamps and forward the skbs upstream via the netif_rx_ni and skb_complete_tx_timestamp in the bcm54210pe_run_rx_timestamp_match_thread and bcm54210pe_run_tx_timestamp_match_thread functions in a non-interrupt context.
The driver uses poll style behaviour triggered by the rx or tx of frames, but does not use a formal interrupt handler.
In addition to gettime, settime and adjtime and adjfine, I've implemented the gettimex64 function to provide the best possible sync of the kernel clock from the PHC. However, as the PHY is separated from the MAC by the MDIO bus, I cannot lock and prevent the scheduler from interrupting the 3-timestamp process, but performance is nonetheless reasonable and the kernel clock sees offset variations in the 1-2us range.
Features:
In addition the driver add support for perout and extts functionality using ptp_clock_request structs as provided for by the standard SO_TIMESTAMPING API.
Test & Performance:
We have tested the features and accuracy in our lab and are able as a client to run at -7 intervals without significant performance impact on a Raspberry PI CM4 on an IO board. We are able to maintain synchronisation of the PHC within +/-10ns of its grandmaster PTP source and the system clock within 1-2us of the PHC.
I look forward to receiving your feedback.
All the best,
Lasse
[-- Attachment #2: bcm54210pe-1588.patch.txt --]
[-- Type: text/plain, Size: 51963 bytes --]
From bf2ec31ad461f2213e4f3850649e1d00953cf86d Mon Sep 17 00:00:00 2001
From: Lasse Johnsen <l@ssejohnsen.me>
Date: Sat, 5 Feb 2022 09:34:19 -0500
Subject: [PATCH] Added support for IEEE1588 timestamping for the BCM54210PE
PHY using the kernel mii_timestamper interface
---
arch/arm/configs/bcm2711_defconfig | 1 +
arch/arm64/configs/bcm2711_defconfig | 1 +
.../net/ethernet/broadcom/genet/bcmgenet.c | 16 +-
drivers/net/phy/Makefile | 1 +
drivers/net/phy/bcm54210pe_ptp.c | 1406 +++++++++++++++++
drivers/net/phy/bcm54210pe_ptp.h | 111 ++
drivers/net/phy/broadcom.c | 21 +-
drivers/ptp/Kconfig | 17 +
8 files changed, 1572 insertions(+), 2 deletions(-)
create mode 100755 drivers/net/phy/bcm54210pe_ptp.c
create mode 100755 drivers/net/phy/bcm54210pe_ptp.h
diff --git a/arch/arm/configs/bcm2711_defconfig b/arch/arm/configs/bcm2711_defconfig
index 8f4ae82cade4f..c7f3cce0024b7 100644
--- a/arch/arm/configs/bcm2711_defconfig
+++ b/arch/arm/configs/bcm2711_defconfig
@@ -1402,6 +1402,7 @@ CONFIG_MAXIM_THERMOCOUPLE=m
CONFIG_MAX31856=m
CONFIG_PWM_BCM2835=m
CONFIG_PWM_PCA9685=m
+CONFIG_GENERIC_PHY=y
CONFIG_RPI_AXIPERF=m
CONFIG_NVMEM_RMEM=m
CONFIG_EXT4_FS=y
diff --git a/arch/arm64/configs/bcm2711_defconfig b/arch/arm64/configs/bcm2711_defconfig
index 75333e69ef741..2af6b2fc5dcda 100644
--- a/arch/arm64/configs/bcm2711_defconfig
+++ b/arch/arm64/configs/bcm2711_defconfig
@@ -1409,6 +1409,7 @@ CONFIG_MAXIM_THERMOCOUPLE=m
CONFIG_MAX31856=m
CONFIG_PWM_BCM2835=m
CONFIG_PWM_PCA9685=m
+CONFIG_GENERIC_PHY=y
CONFIG_RPI_AXIPERF=m
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index bd1f419bc47ae..2fa6258103025 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -39,8 +39,11 @@
#include <asm/unaligned.h>
+#include <linux/ptp_classify.h>
+
#include "bcmgenet.h"
+
/* Maximum number of hardware queues, downsized if needed */
#define GENET_MAX_MQ_CNT 4
@@ -2096,7 +2099,18 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev)
}
GENET_CB(skb)->last_cb = tx_cb_ptr;
- skb_tx_timestamp(skb);
+
+ // Timestamping
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
+ {
+ //skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ skb_pull(skb, skb_mac_offset(skb)); // Feels like this pull should really be part of ptp_classify_raw...
+ skb_clone_tx_timestamp(skb);
+ }
+ else if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_SW_TSTAMP))
+ {
+ skb_tstamp_tx(skb, NULL);
+ }
/* Decrement total BD count and advance our write pointer */
ring->free_bds -= nr_frags + 1;
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index a13e402074cf8..528192d59d793 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_BCM84881_PHY) += bcm84881.o
obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o
obj-$(CONFIG_BCM_CYGNUS_PHY) += bcm-cygnus.o
obj-$(CONFIG_BCM_NET_PHYLIB) += bcm-phy-lib.o
+obj-$(CONFIG_BCM54120PE_PHY) += bcm54210pe_ptp.o
obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
obj-$(CONFIG_CICADA_PHY) += cicada.o
obj-$(CONFIG_CORTINA_PHY) += cortina.o
diff --git a/drivers/net/phy/bcm54210pe_ptp.c b/drivers/net/phy/bcm54210pe_ptp.c
new file mode 100755
index 0000000000000..c4882c84229f9
--- /dev/null
+++ b/drivers/net/phy/bcm54210pe_ptp.c
@@ -0,0 +1,1406 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * drivers/net/phy/bcm54210pe_ptp.c
+ *
+ * IEEE1588 (PTP), perout and extts for BCM54210PE PHY
+ *
+ * Authors: Carlos Fernandez, Kyle Judd, Lasse Johnsen
+ * License: GPL
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/ip.h>
+#include <linux/net_tstamp.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/udp.h>
+#include <asm/unaligned.h>
+#include <linux/brcmphy.h>
+#include <linux/irq.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/if_ether.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+
+#include "bcm54210pe_ptp.h"
+#include "bcm-phy-lib.h"
+
+#define PTP_CONTROL_OFFSET 32
+#define PTP_TSMT_OFFSET 0
+#define PTP_SEQUENCE_ID_OFFSET 30
+#define PTP_CLOCK_ID_OFFSET 20
+#define PTP_CLOCK_ID_SIZE 8
+#define PTP_SEQUENCE_PORT_NUMER_OFFSET (PTP_CLOCK_ID_OFFSET + PTP_CLOCK_ID_SIZE)
+
+#define EXT_SELECT_REG 0x17
+#define EXT_DATA_REG 0x15
+
+#define EXT_ENABLE_REG1 0x17
+#define EXT_ENABLE_DATA1 0x0F7E
+#define EXT_ENABLE_REG2 0x15
+#define EXT_ENABLE_DATA2 0x0000
+
+#define EXT_1588_SLICE_REG 0x0810
+#define EXT_1588_SLICE_DATA 0x0101
+
+#define ORIGINAL_TIME_CODE_0 0x0854
+#define ORIGINAL_TIME_CODE_1 0x0855
+#define ORIGINAL_TIME_CODE_2 0x0856
+#define ORIGINAL_TIME_CODE_3 0x0857
+#define ORIGINAL_TIME_CODE_4 0x0858
+
+#define TIME_STAMP_REG_0 0x0889
+#define TIME_STAMP_REG_1 0x088A
+#define TIME_STAMP_REG_2 0x088B
+#define TIME_STAMP_REG_3 0x08C4
+#define TIME_STAMP_INFO_1 0x088C
+#define TIME_STAMP_INFO_2 0x088D
+#define INTERRUPT_STATUS_REG 0x085F
+#define INTERRUPT_MASK_REG 0x085E
+#define EXT_SOFTWARE_RESET 0x0F70
+#define EXT_RESET1 0x0001 //RESET
+#define EXT_RESET2 0x0000 //NORMAL OPERATION
+#define GLOBAL_TIMESYNC_REG 0x0FF5
+
+#define TX_EVENT_MODE_REG 0x0811
+#define RX_EVENT_MODE_REG 0x0819
+#define TX_TSCAPTURE_ENABLE_REG 0x0821
+#define RX_TSCAPTURE_ENABLE_REG 0x0822
+#define TXRX_1588_OPTION_REG 0x0823
+
+#define TX_TS_OFFSET_LSB 0x0834
+#define TX_TS_OFFSET_MSB 0x0835
+#define RX_TS_OFFSET_LSB 0x0844
+#define RX_TS_OFFSET_MSB 0x0845
+#define NSE_DPPL_NCO_1_LSB_REG 0x0873
+#define NSE_DPPL_NCO_1_MSB_REG 0x0874
+
+#define NSE_DPPL_NCO_2_0_REG 0x0875
+#define NSE_DPPL_NCO_2_1_REG 0x0876
+#define NSE_DPPL_NCO_2_2_REG 0x0877
+
+#define NSE_DPPL_NCO_3_0_REG 0x0878
+#define NSE_DPPL_NCO_3_1_REG 0x0879
+#define NSE_DPPL_NCO_3_2_REG 0x087A
+
+#define NSE_DPPL_NCO_4_REG 0x087B
+
+#define NSE_DPPL_NCO_5_0_REG 0x087C
+#define NSE_DPPL_NCO_5_1_REG 0x087D
+#define NSE_DPPL_NCO_5_2_REG 0x087E
+
+#define NSE_DPPL_NCO_6_REG 0x087F
+
+#define NSE_DPPL_NCO_7_0_REG 0x0880
+#define NSE_DPPL_NCO_7_1_REG 0x0881
+
+#define DPLL_SELECT_REG 0x085b
+#define TIMECODE_SEL_REG 0x08C3
+#define SHADOW_REG_CONTROL 0x085C
+#define SHADOW_REG_LOAD 0x085D
+
+#define PTP_INTERRUPT_REG 0x0D0C
+
+#define CTR_DBG_REG 0x088E
+#define HEART_BEAT_REG4 0x08ED
+#define HEART_BEAT_REG3 0x08EC
+#define HEART_BEAT_REG2 0x0888
+#define HEART_BEAT_REG1 0x0887
+#define HEART_BEAT_REG0 0x0886
+
+#define READ_END_REG 0x0885
+
+static bool bcm54210pe_fetch_timestamp(u8 txrx, u8 message_type, u16 seq_id, struct bcm54210pe_private *private, u64 *timestamp)
+{
+ struct bcm54210pe_circular_buffer_item *item;
+ struct list_head *this, *next;
+
+ u8 index = (txrx * 4) + message_type;
+
+ if(index >= CIRCULAR_BUFFER_COUNT)
+ {
+ return false;
+ }
+
+ list_for_each_safe(this, next, &private->circular_buffers[index])
+ {
+ item = list_entry(this, struct bcm54210pe_circular_buffer_item, list);
+
+ if(item->sequence_id == seq_id && item->is_valid)
+ {
+ item->is_valid = false;
+ *timestamp = item->time_stamp;
+ mutex_unlock(&private->timestamp_buffer_lock);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void bcm54210pe_read_sop_time_register(struct bcm54210pe_private *private)
+{
+ struct phy_device *phydev = private->phydev;
+ struct bcm54210pe_circular_buffer_item *item;
+ u16 fifo_info_1, fifo_info_2;
+ u8 tx_or_rx, msg_type, index;
+ u16 sequence_id;
+ u64 timestamp;
+ u16 Time[4];
+
+ mutex_lock(&private->timestamp_buffer_lock);
+
+ while(bcm_phy_read_exp(phydev, INTERRUPT_STATUS_REG) & 2)
+ {
+ mutex_lock(&private->clock_lock);
+
+ // Flush out the FIFO
+ bcm_phy_write_exp(phydev, READ_END_REG, 1);
+
+ Time[3] = bcm_phy_read_exp(phydev, TIME_STAMP_REG_3);
+ Time[2] = bcm_phy_read_exp(phydev, TIME_STAMP_REG_2);
+ Time[1] = bcm_phy_read_exp(phydev, TIME_STAMP_REG_1);
+ Time[0] = bcm_phy_read_exp(phydev, TIME_STAMP_REG_0);
+
+ fifo_info_1 = bcm_phy_read_exp(phydev, TIME_STAMP_INFO_1);
+ fifo_info_2 = bcm_phy_read_exp(phydev, TIME_STAMP_INFO_2);
+
+ bcm_phy_write_exp(phydev, READ_END_REG, 2);
+ bcm_phy_write_exp(phydev, READ_END_REG, 0);
+
+ mutex_unlock(&private->clock_lock);
+
+ msg_type = (u8) ((fifo_info_2 & 0xF000) >> 12);
+ tx_or_rx = (u8) ((fifo_info_2 & 0x0800) >> 11); // 1 = TX, 0 = RX
+ sequence_id = fifo_info_1;
+
+ timestamp = four_u16_to_ns(Time);
+
+ index = (tx_or_rx * 4) + msg_type;
+
+ if(index < CIRCULAR_BUFFER_COUNT)
+ {
+ item = list_first_entry_or_null(&private->circular_buffers[index], struct bcm54210pe_circular_buffer_item, list);
+ }
+
+ if(item == NULL) {
+ continue;
+ }
+
+ list_del_init(&item->list);
+
+ item->msg_type = msg_type;
+ item->sequence_id = sequence_id;
+ item->time_stamp = timestamp;
+ item->is_valid = true;
+
+ list_add_tail(&item->list, &private->circular_buffers[index]);
+ }
+
+ mutex_unlock(&private->timestamp_buffer_lock);
+}
+
+static void bcm54210pe_run_rx_timestamp_match_thread(struct work_struct *w)
+{
+ struct bcm54210pe_private *private =
+ container_of(w, struct bcm54210pe_private, rxts_work);
+
+ struct skb_shared_hwtstamps *shhwtstamps;
+ struct ptp_header *hdr;
+ struct sk_buff *skb;
+
+ u8 msg_type;
+ u16 sequence_id;
+ u64 timestamp;
+ int x, type;
+
+ skb = skb_dequeue(&private->rx_skb_queue);
+
+ while (skb != NULL) {
+
+ // Yes.... skb_defer_rx_timestamp just did this but <ZZZzzz>....
+ skb_push(skb, ETH_HLEN);
+ type = ptp_classify_raw(skb);
+ skb_pull(skb, ETH_HLEN);
+
+ hdr = ptp_parse_header(skb, type);
+
+ if (hdr == NULL) {
+ goto dequeue;
+ }
+
+ msg_type = ptp_get_msgtype(hdr, type);
+ sequence_id = be16_to_cpu(hdr->sequence_id);
+
+ timestamp = 0;
+
+ for (x = 0; x < 10; x++) {
+ bcm54210pe_read_sop_time_register(private);
+ if (bcm54210pe_fetch_timestamp(0, msg_type, sequence_id,
+ private, ×tamp)) {
+ break;
+ }
+
+ udelay(private->fib_sequence[x] *
+ private->fib_factor_rx);
+ }
+
+ shhwtstamps = skb_hwtstamps(skb);
+
+ if (!shhwtstamps) {
+ goto dequeue;
+ }
+
+ memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+ shhwtstamps->hwtstamp = ns_to_ktime(timestamp);
+
+ dequeue:
+ netif_rx_ni(skb);
+ skb = skb_dequeue(&private->rx_skb_queue);
+ }
+}
+
+static void bcm54210pe_run_tx_timestamp_match_thread(struct work_struct *w)
+{
+ struct bcm54210pe_private *private =
+ container_of(w, struct bcm54210pe_private, txts_work);
+
+ struct skb_shared_hwtstamps *shhwtstamps;
+ struct sk_buff *skb;
+
+ struct ptp_header *hdr;
+ u8 msg_type;
+ u16 sequence_id;
+ u64 timestamp;
+ int x;
+
+ timestamp = 0;
+ skb = skb_dequeue(&private->tx_skb_queue);
+
+ while (skb != NULL) {
+ int type = ptp_classify_raw(skb);
+ hdr = ptp_parse_header(skb, type);
+
+ if (!hdr) {
+ return;
+ }
+
+ msg_type = ptp_get_msgtype(hdr, type);
+ sequence_id = be16_to_cpu(hdr->sequence_id);
+
+ for (x = 0; x < 10; x++) {
+ bcm54210pe_read_sop_time_register(private);
+ if (bcm54210pe_fetch_timestamp(1, msg_type, sequence_id,
+ private, ×tamp)) {
+ break;
+ }
+ udelay(private->fib_sequence[x] * private->fib_factor_tx);
+ }
+ shhwtstamps = skb_hwtstamps(skb);
+
+ if (!shhwtstamps) {
+ kfree_skb(skb);
+ goto dequeue;
+ }
+
+ memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+ shhwtstamps->hwtstamp = ns_to_ktime(timestamp);
+
+ skb_complete_tx_timestamp(skb, shhwtstamps);
+
+ dequeue:
+ skb = skb_dequeue(&private->tx_skb_queue);
+ }
+}
+
+static int bcm54210pe_config_1588(struct phy_device *phydev)
+{
+ int err;
+
+ err = bcm_phy_write_exp(phydev, PTP_INTERRUPT_REG, 0x3c02 );
+
+ err |= bcm_phy_write_exp(phydev, GLOBAL_TIMESYNC_REG, 0x0001); //Enable global timesync register.
+ err |= bcm_phy_write_exp(phydev, EXT_1588_SLICE_REG, 0x0101); //ENABLE TX and RX slice 1588
+ err |= bcm_phy_write_exp(phydev, TX_EVENT_MODE_REG, 0xFF00); //Add 80bit timestamp + NO CPU MODE in TX
+ err |= bcm_phy_write_exp(phydev, RX_EVENT_MODE_REG, 0xFF00); //Add 32+32 bits timestamp + NO CPU mode in RX
+ err |= bcm_phy_write_exp(phydev, TIMECODE_SEL_REG, 0x0101); //Select 80 bit counter
+ err |= bcm_phy_write_exp(phydev, TX_TSCAPTURE_ENABLE_REG, 0x0001); //Enable timestamp capture in TX
+ err |= bcm_phy_write_exp(phydev, RX_TSCAPTURE_ENABLE_REG, 0x0001); //Enable timestamp capture in RX
+
+ //Enable shadow register
+ err |= bcm_phy_write_exp(phydev, SHADOW_REG_CONTROL, 0x0000);
+ err |= bcm_phy_write_exp(phydev, SHADOW_REG_LOAD, 0x07c0);
+
+
+ // Set global mode and trigger immediate framesync to load shaddow registers
+ err |= bcm_phy_write_exp(phydev, NSE_DPPL_NCO_6_REG, 0xC020);
+
+ //15, 33 or 41 - experimental
+ printk("DEBUG: GPIO %d IRQ %d\n", 15, gpio_to_irq(41));
+
+ // Enable Interrupt behaviour (eventhough we get no interrupts)
+ err |= bcm54210pe_interrupts_enable(phydev,true, false);
+
+ return err;
+}
+
+static int bcm54210pe_gettimex(struct ptp_clock_info *info,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+
+ struct bcm54210pe_ptp *ptp = container_of(info, struct bcm54210pe_ptp, caps);
+ return bcm54210pe_get80bittime(ptp->chosen, ts, sts);
+}
+
+// Must be called under clock_lock
+static void bcm54210pe_read80bittime_register(struct phy_device *phydev, u64 *time_stamp_80, u64 *time_stamp_48)
+{
+ u16 Time[5];
+ u64 ts[3];
+ u64 cumulative;
+
+ bcm_phy_write_exp(phydev, CTR_DBG_REG, 0x400);
+ Time[4] = bcm_phy_read_exp(phydev, HEART_BEAT_REG4);
+ Time[3] = bcm_phy_read_exp(phydev, HEART_BEAT_REG3);
+ Time[2] = bcm_phy_read_exp(phydev, HEART_BEAT_REG2);
+ Time[1] = bcm_phy_read_exp(phydev, HEART_BEAT_REG1);
+ Time[0] = bcm_phy_read_exp(phydev, HEART_BEAT_REG0);
+
+ // Set read end bit
+ bcm_phy_write_exp(phydev, CTR_DBG_REG, 0x800);
+ bcm_phy_write_exp(phydev, CTR_DBG_REG, 0x000);
+
+ *time_stamp_80 = four_u16_to_ns(Time);
+
+ if (time_stamp_48 != NULL) {
+
+
+ ts[2] = (((u64)Time[2]) << 32);
+ ts[1] = (((u64)Time[1]) << 16);
+ ts[0] = ((u64)Time[0]);
+
+ cumulative = 0;
+ cumulative |= ts[0];
+ cumulative |= ts[1];
+ cumulative |= ts[2];
+
+ *time_stamp_48 = cumulative;
+ }
+}
+
+// Must be called under clock_lock
+static void bcm54210pe_read48bittime_register(struct phy_device *phydev, u64 *time_stamp)
+{
+ u16 Time[3];
+ u64 ts[3];
+ u64 cumulative;
+
+ bcm_phy_write_exp(phydev, CTR_DBG_REG, 0x400);
+ Time[2] = bcm_phy_read_exp(phydev, HEART_BEAT_REG2);
+ Time[1] = bcm_phy_read_exp(phydev, HEART_BEAT_REG1);
+ Time[0] = bcm_phy_read_exp(phydev, HEART_BEAT_REG0);
+
+ // Set read end bit
+ bcm_phy_write_exp(phydev, CTR_DBG_REG, 0x800);
+ bcm_phy_write_exp(phydev, CTR_DBG_REG, 0x000);
+
+
+ ts[2] = (((u64)Time[2]) << 32);
+ ts[1] = (((u64)Time[1]) << 16);
+ ts[0] = ((u64)Time[0]);
+
+ cumulative = 0;
+ cumulative |= ts[0];
+ cumulative |= ts[1];
+ cumulative |= ts[2];
+
+ *time_stamp = cumulative;
+}
+
+static int bcm54210pe_get80bittime(struct bcm54210pe_private *private,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ struct phy_device *phydev;
+ u16 nco_6_register_value;
+ int i;
+ u64 time_stamp_48, time_stamp_80, control_ts;
+
+ phydev = private->phydev;
+
+ // Capture timestamp on next framesync
+ nco_6_register_value = 0x2000;
+
+ // Lock
+ mutex_lock(&private->clock_lock);
+
+ // We share frame sync events with extts, so we need to ensure no event has occurred as we are about to boot the registers, so....
+
+ // If extts is enabled
+ if (private->extts_en) {
+
+ // Halt framesyncs generated by the sync in pin
+ bcm_phy_modify_exp(phydev, NSE_DPPL_NCO_6_REG, 0x003C, 0x0000);
+
+ // Read what's in the 8- bit register
+ bcm54210pe_read48bittime_register(phydev, &control_ts);
+
+ // If it matches neither the last gettime or extts timestamp
+ if (control_ts != private->last_extts_ts && control_ts != private->last_immediate_ts[0]) { // FIXME: This is a bug
+
+ // Odds are this is a extts not yet logged as an event
+ //printk("extts triggered by get80bittime\n");
+ bcm54210pe_trigger_extts_event(private, control_ts);
+ }
+ }
+
+ // Heartbeat register selection. Latch 80 bit Original time coude counter into Heartbeat register
+ // (this is undocumented)
+ bcm_phy_write_exp(phydev, DPLL_SELECT_REG, 0x0040);
+
+ // Amend to base register
+ nco_6_register_value = bcm54210pe_get_base_nco6_reg(private, nco_6_register_value, false);
+
+ // Set the NCO register
+ bcm_phy_write_exp(phydev, NSE_DPPL_NCO_6_REG, nco_6_register_value);
+
+ // Trigger framesync
+ if (sts != NULL) {
+
+ // If we are doing a gettimex call
+ ptp_read_system_prets(sts);
+ bcm_phy_modify_exp(phydev, NSE_DPPL_NCO_6_REG, 0x003C, 0x0020);
+ ptp_read_system_postts(sts);
+
+ } else {
+
+ // or if we are doing a gettime call
+ bcm_phy_modify_exp(phydev, NSE_DPPL_NCO_6_REG, 0x003C, 0x0020);
+ }
+
+ for (i = 0; i < 5;i++) {
+
+ bcm54210pe_read80bittime_register(phydev, &time_stamp_80, &time_stamp_48);
+
+ if (time_stamp_80 != 0) {
+ break;
+ }
+ }
+
+ // Convert to timespec64
+ ns_to_ts(time_stamp_80, ts);
+
+ // If we are using extts
+ if(private->extts_en) {
+
+ // Commit last timestamp
+ private->last_immediate_ts[0] = time_stamp_48;
+ private->last_immediate_ts[1] = time_stamp_80;
+
+ // Heartbeat register selection. Latch 48 bit Original time coude counter into Heartbeat register
+ // (this is undocumented)
+ bcm_phy_write_exp(phydev, DPLL_SELECT_REG, 0x0000);
+
+ // Rearm framesync for sync in pin
+ nco_6_register_value = bcm54210pe_get_base_nco6_reg(private, nco_6_register_value, false);
+ bcm_phy_write_exp(phydev, NSE_DPPL_NCO_6_REG, nco_6_register_value);
+ }
+
+ mutex_unlock(&private->clock_lock);
+
+ return 0;
+}
+
+static int bcm54210pe_gettime(struct ptp_clock_info *info, struct timespec64 *ts)
+{
+ int err;
+ err = bcm54210pe_gettimex(info, ts, NULL);
+ return err;
+}
+
+static int bcm54210pe_get48bittime(struct bcm54210pe_private *private, u64 *timestamp)
+{
+ u16 nco_6_register_value;
+ int i, err;
+
+ struct phy_device *phydev = private->phydev;
+
+ // Capture timestamp on next framesync
+ nco_6_register_value = 0x2000;
+
+ mutex_lock(&private->clock_lock);
+
+ // Heartbeat register selection. Latch 48 bit Original time coude counter into Heartbeat register
+ // (this is undocumented)
+ err = bcm_phy_write_exp(phydev, DPLL_SELECT_REG, 0x0000);
+
+ // Amend to base register
+ nco_6_register_value =
+ bcm54210pe_get_base_nco6_reg(private, nco_6_register_value, false);
+
+ // Set the NCO register
+ err |= bcm_phy_write_exp(phydev, NSE_DPPL_NCO_6_REG, nco_6_register_value);
+
+ // Trigger framesync
+ err |= bcm_phy_modify_exp(phydev, NSE_DPPL_NCO_6_REG, 0x003C, 0x0020);
+
+ for (i = 0; i < 5; i++) {
+
+ bcm54210pe_read48bittime_register(phydev,timestamp);
+
+ if (*timestamp != 0) {
+ break;
+ }
+ }
+ mutex_unlock(&private->clock_lock);
+
+ return err;
+}
+
+static int bcm54210pe_settime(struct ptp_clock_info *info, const struct timespec64 *ts)
+{
+ u16 shadow_load_register, nco_6_register_value;
+ u16 original_time_codes[5], local_time_codes[3];
+ struct bcm54210pe_ptp *ptp;
+ struct phy_device *phydev;
+
+ ptp = container_of(info, struct bcm54210pe_ptp, caps);
+ phydev = ptp->chosen->phydev;
+
+ shadow_load_register = 0;
+ nco_6_register_value = 0;
+
+ // Assign original time codes (80 bit)
+ original_time_codes[4] = (u16) ((ts->tv_sec & 0x0000FFFF00000000) >> 32);
+ original_time_codes[3] = (u16) ((ts->tv_sec & 0x00000000FFFF0000) >> 16);
+ original_time_codes[2] = (u16) (ts->tv_sec & 0x000000000000FFFF);
+ original_time_codes[1] = (u16) ((ts->tv_nsec & 0x00000000FFFF0000) >> 16);
+ original_time_codes[0] = (u16) (ts->tv_nsec & 0x000000000000FFFF);
+
+ // Assign original time codes (48 bit)
+ local_time_codes[2] = 0x4000;
+ local_time_codes[1] = (u16) (ts->tv_nsec >> 20);
+ local_time_codes[0] = (u16) (ts->tv_nsec >> 4);
+
+ // Set Time Code load bit in the shadow load register
+ shadow_load_register |= 0x0400;
+
+ // Set Local Time load bit in the shadow load register
+ shadow_load_register |= 0x0080;
+
+ mutex_lock(&ptp->chosen->clock_lock);
+
+ // Write Original Time Code Register
+ bcm_phy_write_exp(phydev, ORIGINAL_TIME_CODE_0, original_time_codes[0]);
+ bcm_phy_write_exp(phydev, ORIGINAL_TIME_CODE_1, original_time_codes[1]);
+ bcm_phy_write_exp(phydev, ORIGINAL_TIME_CODE_2, original_time_codes[2]);
+ bcm_phy_write_exp(phydev, ORIGINAL_TIME_CODE_3, original_time_codes[3]);
+ bcm_phy_write_exp(phydev, ORIGINAL_TIME_CODE_4, original_time_codes[4]);
+
+ // Write Local Time Code Register
+ bcm_phy_write_exp(phydev, NSE_DPPL_NCO_2_0_REG, local_time_codes[0]);
+ bcm_phy_write_exp(phydev, NSE_DPPL_NCO_2_1_REG, local_time_codes[1]);
+ bcm_phy_write_exp(phydev, NSE_DPPL_NCO_2_2_REG, local_time_codes[2]);
+
+ // Write Shadow register
+ bcm_phy_write_exp(phydev, SHADOW_REG_CONTROL, 0x0000);
+ bcm_phy_write_exp(phydev, SHADOW_REG_LOAD, shadow_load_register);
+
+ // Set global mode and nse_init
+ nco_6_register_value = bcm54210pe_get_base_nco6_reg(ptp->chosen, nco_6_register_value, true);
+
+ // Write to register
+ bcm_phy_write_exp(phydev, NSE_DPPL_NCO_6_REG, nco_6_register_value);
+
+ // Trigger framesync
+ bcm_phy_modify_exp(phydev, NSE_DPPL_NCO_6_REG, 0x003C, 0x0020);
+
+ // Set the second on set
+ ptp->chosen->second_on_set = ts->tv_sec;
+
+ mutex_unlock(&ptp->chosen->clock_lock);
+
+ return 0;
+}
+
+static int bcm54210pe_adjfine(struct ptp_clock_info *info, long scaled_ppm)
+{
+ int err;
+ u16 lo, hi;
+ u32 corrected_8ns_interval, base_8ns_interval;
+ bool negative;
+
+ struct bcm54210pe_ptp *ptp = container_of(info, struct bcm54210pe_ptp, caps);
+ struct phy_device *phydev = ptp->chosen->phydev;
+
+ negative = false;
+ if ( scaled_ppm < 0 ) {
+ negative = true;
+ scaled_ppm = -scaled_ppm;
+ }
+
+ // This is not completely accurate but very fast
+ scaled_ppm >>= 7;
+
+ // Nominal counter increment is 8ns
+ base_8ns_interval = 1 << 31;
+
+ // Add or subtract differential
+ if (negative) {
+ corrected_8ns_interval = base_8ns_interval - scaled_ppm;
+ } else {
+ corrected_8ns_interval = base_8ns_interval + scaled_ppm;
+ }
+
+ // Load up registers
+ hi = (corrected_8ns_interval & 0xFFFF0000) >> 16;
+ lo = (corrected_8ns_interval & 0x0000FFFF);
+
+ mutex_lock(&ptp->chosen->clock_lock);
+
+ // Set freq_mdio_sel to 1
+ bcm_phy_write_exp(phydev, NSE_DPPL_NCO_2_2_REG, 0x4000);
+
+ // Load 125MHz frequency reqcntrl
+ bcm_phy_write_exp(phydev, NSE_DPPL_NCO_1_MSB_REG, hi);
+ bcm_phy_write_exp(phydev, NSE_DPPL_NCO_1_LSB_REG, lo);
+
+ // On next framesync load freq from freqcntrl
+ bcm_phy_write_exp(phydev, SHADOW_REG_LOAD, 0x0040);
+
+ // Trigger framesync
+ err = bcm_phy_modify_exp(phydev, NSE_DPPL_NCO_6_REG, 0x003C, 0x0020);
+
+ mutex_unlock(&ptp->chosen->clock_lock);
+
+ return err;
+}
+
+static int bcm54210pe_adjtime(struct ptp_clock_info *info, s64 delta)
+{
+ int err;
+ struct timespec64 ts;
+ u64 now;
+
+ err = bcm54210pe_gettime(info, &ts);
+ if (err < 0)
+ return err;
+
+ now = ktime_to_ns(timespec64_to_ktime(ts));
+ ts = ns_to_timespec64(now + delta);
+
+ err = bcm54210pe_settime(info, &ts);
+
+ return err;
+}
+
+
+static int bcm54210pe_extts_enable(struct bcm54210pe_private *private, int enable)
+{
+ int err;
+ struct phy_device *phydev;
+ u16 nco_6_register_value;
+
+ phydev = private->phydev;
+
+ if (enable) {
+
+ if (!private->extts_en) {
+
+ // Set enable per_out
+ private->extts_en = true;
+ err = bcm_phy_write_exp(phydev, NSE_DPPL_NCO_4_REG, 0x0001);
+
+ nco_6_register_value = 0;
+ nco_6_register_value = bcm54210pe_get_base_nco6_reg(private, nco_6_register_value, false);
+
+ err = bcm_phy_write_exp(phydev, NSE_DPPL_NCO_7_0_REG, 0x0100);
+ err = bcm_phy_write_exp(phydev, NSE_DPPL_NCO_7_1_REG, 0x0200);
+ err = bcm_phy_write_exp(phydev, NSE_DPPL_NCO_6_REG, nco_6_register_value);
+
+ schedule_delayed_work(&private->extts_ws, msecs_to_jiffies(1));
+ }
+
+ } else {
+ private->extts_en = false;
+ err = bcm_phy_write_exp(phydev, NSE_DPPL_NCO_4_REG, 0x0000);
+
+ }
+
+ return err;
+}
+
+static void bcm54210pe_run_extts_thread(struct work_struct *extts_ws)
+{
+ struct bcm54210pe_private *private;
+ struct phy_device *phydev;
+ u64 interval, time_stamp_48, time_stamp_80;
+
+ private = container_of((struct delayed_work *)extts_ws, struct bcm54210pe_private, extts_ws);
+ phydev = private->phydev;
+
+ interval = 10; // in ms - long after we are gone from this earth, discussions will be had and
+ // songs will be sung about whether this interval is short enough....
+ // Before you complain let me say that in Timebeat.app up to ~150ms allows
+ // single digit ns servo accuracy. If your client / servo is not as cool: Do better :-)
+
+ mutex_lock(&private->clock_lock);
+
+ bcm54210pe_read80bittime_register(phydev, &time_stamp_80, &time_stamp_48);
+
+
+ if (private->last_extts_ts != time_stamp_48 &&
+ private->last_immediate_ts[0] != time_stamp_48 &&
+ private->last_immediate_ts[1] != time_stamp_80) {
+
+ bcm_phy_write_exp(phydev, NSE_DPPL_NCO_6_REG, 0xE000);
+ bcm54210pe_trigger_extts_event(private, time_stamp_48);
+ bcm_phy_write_exp(phydev, NSE_DPPL_NCO_6_REG, 0xE004);
+ }
+
+ mutex_unlock(&private->clock_lock);
+
+ // Do we need to reschedule
+ if (private->extts_en) {
+ schedule_delayed_work(&private->extts_ws, msecs_to_jiffies(interval));
+ }
+}
+
+// Must be called under clock_lock
+static void bcm54210pe_trigger_extts_event(struct bcm54210pe_private *private, u64 time_stamp)
+{
+
+ struct ptp_clock_event event;
+ struct timespec64 ts;
+
+ event.type = PTP_CLOCK_EXTTS;
+ event.timestamp = convert_48bit_to_80bit(private->second_on_set, time_stamp);
+ event.index = 0;
+
+ ptp_clock_event(private->ptp->ptp_clock, &event);
+
+ private->last_extts_ts = time_stamp;
+
+ ns_to_ts(time_stamp, &ts);
+}
+
+static int bcm54210pe_perout_enable(struct bcm54210pe_private *private, s64 period, s64 pulsewidth, int enable)
+{
+ struct phy_device *phydev;
+ u16 nco_6_register_value, frequency_hi, frequency_lo, pulsewidth_reg, pulse_start_hi, pulse_start_lo;
+ int err;
+
+ phydev = private->phydev;
+
+ if (enable) {
+ frequency_hi = 0;
+ frequency_lo = 0;
+ pulsewidth_reg = 0;
+ pulse_start_hi = 0;
+ pulse_start_lo = 0;
+
+ // Convert interval pulse spacing (period) and pulsewidth to 8 ns units
+ period /= 8;
+ pulsewidth /= 8;
+
+ // Mode 2 only: If pulsewidth is not explicitly set with PTP_PEROUT_DUTY_CYCLE
+ if (pulsewidth == 0) {
+ if (period < 2500) {
+ // At a frequency at less than 20us (2500 x 8ns) set pulse length to 1/10th of the interval pulse spacing
+ pulsewidth = period / 10;
+
+ // Where the interval pulse spacing is short, ensure we set a pulse length of 8ns
+ if (pulsewidth == 0) {
+ pulsewidth = 1;
+ }
+
+ } else {
+ // Otherwise set pulse with to 4us (8ns x 500 = 4us)
+ pulsewidth = 500;
+ }
+ }
+
+ if (private->perout_mode == SYNC_OUT_MODE_1) {
+
+ // Set period
+ private->perout_period = period;
+
+ if (!private->perout_en) {
+
+ // Set enable per_out
+ private->perout_en = true;
+ schedule_delayed_work(&private->perout_ws, msecs_to_jiffies(1));
+ }
+
+ err = 0;
+
+ } else if (private->perout_mode == SYNC_OUT_MODE_2) {
+
+ // Set enable per_out
+ private->perout_en = true;
+
+ // Calculate registers
+ frequency_lo = (u16)period; // Lowest 16 bits of 8ns interval pulse spacing [15:0]
+ frequency_hi = (u16) (0x3FFF & (period >> 16)); // Highest 14 bits of 8ns interval pulse spacing [29:16]
+ frequency_hi |= (u16) pulsewidth << 14; // 2 lowest bits of 8ns pulse length [1:0]
+ pulsewidth_reg = (u16) (0x7F & (pulsewidth >> 2)); // 7 highest bit of 8 ns pulse length [8:2]
+
+ // Get base value
+ nco_6_register_value = bcm54210pe_get_base_nco6_reg(
+ private, nco_6_register_value, true);
+
+ mutex_lock(&private->clock_lock);
+
+ // Write to register
+ err = bcm_phy_write_exp(phydev, NSE_DPPL_NCO_6_REG,
+ nco_6_register_value);
+
+ // Set sync out pulse interval spacing and pulse length
+ err |= bcm_phy_write_exp(
+ phydev, NSE_DPPL_NCO_3_0_REG, frequency_lo);
+ err |= bcm_phy_write_exp(
+ phydev, NSE_DPPL_NCO_3_1_REG, frequency_hi);
+ err |= bcm_phy_write_exp(phydev,
+ NSE_DPPL_NCO_3_2_REG,
+ pulsewidth_reg);
+
+ // On next framesync load sync out frequency
+ err |= bcm_phy_write_exp(phydev, SHADOW_REG_LOAD,
+ 0x0200);
+
+ // Trigger immediate framesync framesync
+ err |= bcm_phy_modify_exp(
+ phydev, NSE_DPPL_NCO_6_REG, 0x003C, 0x0020);
+
+ mutex_unlock(&private->clock_lock);
+ }
+ } else {
+
+ // Set disable pps
+ private->perout_en = false;
+
+ // Get base value
+ nco_6_register_value = bcm54210pe_get_base_nco6_reg(private, nco_6_register_value, false);
+
+ mutex_lock(&private->clock_lock);
+
+ // Write to register
+ err = bcm_phy_write_exp(phydev, NSE_DPPL_NCO_6_REG, nco_6_register_value);
+
+ mutex_unlock(&private->clock_lock);
+ }
+
+ return err;
+}
+
+static void bcm54210pe_run_perout_mode_one_thread(struct work_struct *perout_ws)
+{
+ struct bcm54210pe_private *private;
+ u64 local_time_stamp_48bits; //, local_time_stamp_80bits;
+ u64 next_event, time_before_next_pulse, period;
+ u16 nco_6_register_value, pulsewidth_nco3_hack;
+ u64 wait_one, wait_two;
+
+ private = container_of((struct delayed_work *)perout_ws, struct bcm54210pe_private, perout_ws);
+ period = private->perout_period * 8;
+ pulsewidth_nco3_hack = 250; // The BCM chip is broken. It does not respect this in sync out mode 1
+
+ nco_6_register_value = 0;
+
+ // Get base value
+ nco_6_register_value = bcm54210pe_get_base_nco6_reg(private, nco_6_register_value, false);
+
+ // Get 48 bit local time
+ bcm54210pe_get48bittime(private, &local_time_stamp_48bits);
+
+ // Calculate time before next event and next event time
+ time_before_next_pulse = period - (local_time_stamp_48bits % period);
+ next_event = local_time_stamp_48bits + time_before_next_pulse;
+
+ // Lock
+ mutex_lock(&private->clock_lock);
+
+ // Set pulsewidth (test reveal this does not work), but registers need content or no pulse will exist
+ bcm_phy_write_exp(private->phydev, NSE_DPPL_NCO_3_1_REG, pulsewidth_nco3_hack << 14);
+ bcm_phy_write_exp(private->phydev, NSE_DPPL_NCO_3_2_REG, pulsewidth_nco3_hack >> 2);
+
+ // Set sync out pulse interval spacing and pulse length
+ bcm_phy_write_exp(private->phydev, NSE_DPPL_NCO_5_0_REG, next_event & 0xFFF0);
+ bcm_phy_write_exp(private->phydev, NSE_DPPL_NCO_5_1_REG, next_event >> 16);
+ bcm_phy_write_exp(private->phydev, NSE_DPPL_NCO_5_2_REG, next_event >> 32);
+
+ // On next framesync load sync out frequency
+ bcm_phy_write_exp(private->phydev, SHADOW_REG_LOAD, 0x0200);
+
+ // Write to register with mode one set for sync out
+ bcm_phy_write_exp(private->phydev, NSE_DPPL_NCO_6_REG, nco_6_register_value || 0x0001);
+
+ // Trigger immediate framesync framesync
+ bcm_phy_modify_exp(private->phydev, NSE_DPPL_NCO_6_REG, 0x003C, 0x0020);
+
+ // Unlock
+ mutex_unlock(&private->clock_lock);
+
+ // Wait until 1/10 period after the next pulse
+ wait_one = (time_before_next_pulse / 1000000) + (period / 1000000 / 10);
+ mdelay(wait_one);
+
+ // Lock
+ mutex_lock(&private->clock_lock);
+
+ // Clear pulse by bumping sync_out_match to max (this pulls sync out down)
+ bcm_phy_write_exp(private->phydev, NSE_DPPL_NCO_5_0_REG, 0xFFF0);
+ bcm_phy_write_exp(private->phydev, NSE_DPPL_NCO_5_1_REG, 0xFFFF);
+ bcm_phy_write_exp(private->phydev, NSE_DPPL_NCO_5_2_REG, 0xFFFF);
+
+ // On next framesync load sync out frequency
+ bcm_phy_write_exp(private->phydev, SHADOW_REG_LOAD, 0x0200);
+
+ // Trigger immediate framesync framesync
+ bcm_phy_modify_exp(private->phydev, NSE_DPPL_NCO_6_REG, 0x003C, 0x0020);
+
+ // Unlock
+ mutex_unlock(&private->clock_lock);
+
+ // Calculate wait before we reschedule the next pulse
+ wait_two = (period / 1000000) - (2 * (period / 10000000));
+
+ // Do we need to reschedule
+ if (private->perout_en) {
+ schedule_delayed_work(&private->perout_ws, msecs_to_jiffies(wait_two));
+ }
+}
+
+
+bool bcm54210pe_rxtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb, int type)
+{
+ struct bcm54210pe_private *private = container_of(mii_ts, struct bcm54210pe_private, mii_ts);
+
+ if (private->hwts_rx_en) {
+ skb_queue_tail(&private->rx_skb_queue, skb);
+ schedule_work(&private->rxts_work);
+ return true;
+ }
+
+ return false;
+}
+
+void bcm54210pe_txtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb, int type)
+{
+ struct bcm54210pe_private *private = container_of(mii_ts, struct bcm54210pe_private, mii_ts);
+
+ switch (private->hwts_tx_en)
+ {
+ case HWTSTAMP_TX_ON:
+ {
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ skb_queue_tail(&private->tx_skb_queue, skb);
+ schedule_work(&private->txts_work);
+ break;
+ }
+ case HWTSTAMP_TX_OFF:
+ {
+
+ }
+ default:
+ {
+ kfree_skb(skb);
+ break;
+ }
+ }
+}
+
+int bcm54210pe_ts_info(struct mii_timestamper *mii_ts, struct ethtool_ts_info *info)
+{
+ struct bcm54210pe_private *bcm54210pe = container_of(mii_ts, struct bcm54210pe_private, mii_ts);
+
+ info->so_timestamping =
+ SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ info->phc_index = ptp_clock_index(bcm54210pe->ptp->ptp_clock);
+ info->tx_types =
+ (1 << HWTSTAMP_TX_OFF) |
+ (1 << HWTSTAMP_TX_ON) ;
+ info->rx_filters =
+ (1 << HWTSTAMP_FILTER_NONE) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT);
+ return 0;
+}
+
+int bcm54210pe_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr)
+{
+ struct bcm54210pe_private *device = container_of(mii_ts, struct bcm54210pe_private, mii_ts);
+
+ struct hwtstamp_config cfg;
+
+ if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
+ return -EFAULT;
+
+ if (cfg.flags) /* reserved for future extensions */
+ return -EINVAL;
+
+ if (cfg.tx_type < 0 || cfg.tx_type > HWTSTAMP_TX_ONESTEP_SYNC)
+ return -ERANGE;
+
+ device->hwts_tx_en = cfg.tx_type;
+
+ switch (cfg.rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ device->hwts_rx_en = 0;
+ device->layer = 0;
+ device->version = 0;
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ device->hwts_rx_en = 1;
+ device->layer = PTP_CLASS_L4;
+ device->version = PTP_CLASS_V1;
+ cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ device->hwts_rx_en = 1;
+ device->layer = PTP_CLASS_L4;
+ device->version = PTP_CLASS_V2;
+ cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ device->hwts_rx_en = 1;
+ device->layer = PTP_CLASS_L2;
+ device->version = PTP_CLASS_V2;
+ cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ device->hwts_rx_en = 1;
+ device->layer = PTP_CLASS_L4 | PTP_CLASS_L2;
+ device->version = PTP_CLASS_V2;
+ cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
+
+static int bcm54210pe_feature_enable(struct ptp_clock_info *info, struct ptp_clock_request *req, int on)
+{
+ struct bcm54210pe_ptp *ptp = container_of(info, struct bcm54210pe_ptp, caps);
+ s64 period, pulsewidth;
+ struct timespec64 ts;
+
+ switch (req->type) {
+
+ case PTP_CLK_REQ_PEROUT :
+
+ period = 0;
+ pulsewidth = 0;
+
+ // Check if pin func is set correctly
+ if (ptp->chosen->sdp_config[SYNC_OUT_PIN].func != PTP_PF_PEROUT) {
+ return -EOPNOTSUPP;
+ }
+
+ // No other flags supported
+ if (req->perout.flags & ~PTP_PEROUT_DUTY_CYCLE) {
+ return -EOPNOTSUPP;
+ }
+
+ // Check if a specific pulsewidth is set
+ if ((req->perout.flags & PTP_PEROUT_DUTY_CYCLE) > 0) {
+
+ if (ptp->chosen->perout_mode == SYNC_OUT_MODE_1) {
+ return -EOPNOTSUPP;
+ }
+
+ // Extract pulsewidth
+ ts.tv_sec = req->perout.on.sec;
+ ts.tv_nsec = req->perout.on.nsec;
+ pulsewidth = timespec64_to_ns(&ts);
+
+ // 9 bits in 8ns units, so max = 4,088ns
+ if (pulsewidth > 511 * 8) {
+ return -ERANGE;
+ }
+ }
+
+ // Extract pulse spacing interval (period)
+ ts.tv_sec = req->perout.period.sec;
+ ts.tv_nsec = req->perout.period.nsec;
+ period = timespec64_to_ns(&ts);
+
+ // 16ns is minimum pulse spacing interval (a value of 16 will result in 8ns high followed by 8 ns low)
+ if (period != 0 && period < 16) {
+ return -ERANGE;
+ }
+
+ return bcm54210pe_perout_enable(ptp->chosen, period, pulsewidth, on);
+
+ case PTP_CLK_REQ_EXTTS:
+
+ if (ptp->chosen->sdp_config[SYNC_IN_PIN].func != PTP_PF_EXTTS) {
+ return -EOPNOTSUPP;
+ }
+
+ return bcm54210pe_extts_enable(ptp->chosen, on);
+
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+
+static int bcm54210pe_ptp_verify_pin(struct ptp_clock_info *info, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ switch (func) {
+ case PTP_PF_NONE:
+ return 0;
+ break;
+ case PTP_PF_EXTTS:
+ if (pin == SYNC_IN_PIN)
+ return 0;
+ break;
+ case PTP_PF_PEROUT:
+ if (pin == SYNC_OUT_PIN)
+ return 0;
+ break;
+ case PTP_PF_PHYSYNC:
+ break;
+ }
+ return -1;
+}
+
+static const struct ptp_clock_info bcm54210pe_clk_caps = {
+ .owner = THIS_MODULE,
+ .name = "BCM54210PE_PHC",
+ .max_adj = 100000000,
+ .n_alarm = 0,
+ .n_pins = 2,
+ .n_ext_ts = 1,
+ .n_per_out = 1,
+ .pps = 0,
+ .adjtime = &bcm54210pe_adjtime,
+ .adjfine = &bcm54210pe_adjfine,
+ .gettime64 = &bcm54210pe_gettime,
+ .gettimex64 = &bcm54210pe_gettimex,
+ .settime64 = &bcm54210pe_settime,
+ .enable = &bcm54210pe_feature_enable,
+ .verify = &bcm54210pe_ptp_verify_pin,
+};
+
+static int bcm54210pe_interrupts_enable(struct phy_device *phydev, bool fsync_en, bool sop_en)
+{
+ u16 interrupt_mask;
+
+ interrupt_mask = 0;
+
+ if (fsync_en) {
+ interrupt_mask |= 0x0001;
+ }
+
+ if (sop_en) {
+ interrupt_mask |= 0x0002;
+ }
+
+ return bcm_phy_write_exp(phydev, INTERRUPT_MASK_REG, interrupt_mask);
+}
+
+static int bcm54210pe_sw_reset(struct phy_device *phydev)
+{
+ u16 err;
+ u16 aux;
+
+ err = bcm_phy_write_exp(phydev, EXT_SOFTWARE_RESET, EXT_RESET1);
+ err = bcm_phy_read_exp(phydev, EXT_ENABLE_REG1);
+ if (err < 0)
+ return err;
+
+ err = bcm_phy_write_exp(phydev, EXT_SOFTWARE_RESET, EXT_RESET2);
+ aux = bcm_phy_read_exp(phydev, EXT_SOFTWARE_RESET);
+ return err;
+}
+
+int bcm54210pe_probe(struct phy_device *phydev)
+{
+ int x, y;
+ struct bcm54210pe_ptp *ptp;
+ struct bcm54210pe_private *bcm54210pe;
+ struct ptp_pin_desc *sync_in_pin_desc, *sync_out_pin_desc;
+
+ bcm54210pe_sw_reset(phydev);
+ bcm54210pe_config_1588(phydev);
+
+ bcm54210pe = kzalloc(sizeof(struct bcm54210pe_private), GFP_KERNEL);
+ if (!bcm54210pe) {
+ return -ENOMEM;
+ }
+
+ ptp = kzalloc(sizeof(struct bcm54210pe_ptp), GFP_KERNEL);
+ if (!ptp) {
+ return -ENOMEM;
+ }
+
+ bcm54210pe->phydev = phydev;
+ bcm54210pe->ptp = ptp;
+
+ bcm54210pe->mii_ts.rxtstamp = bcm54210pe_rxtstamp;
+ bcm54210pe->mii_ts.txtstamp = bcm54210pe_txtstamp;
+ bcm54210pe->mii_ts.hwtstamp = bcm54210pe_hwtstamp;
+ bcm54210pe->mii_ts.ts_info = bcm54210pe_ts_info;
+
+
+ phydev->mii_ts = &bcm54210pe->mii_ts;
+
+ // Initialisation of work_structs and similar
+ INIT_WORK(&bcm54210pe->txts_work, bcm54210pe_run_tx_timestamp_match_thread);
+ INIT_WORK(&bcm54210pe->rxts_work, bcm54210pe_run_rx_timestamp_match_thread);
+ INIT_DELAYED_WORK(&bcm54210pe->perout_ws, bcm54210pe_run_perout_mode_one_thread);
+ INIT_DELAYED_WORK(&bcm54210pe->extts_ws, bcm54210pe_run_extts_thread);
+
+ // SKB queues
+ skb_queue_head_init(&bcm54210pe->tx_skb_queue);
+ skb_queue_head_init(&bcm54210pe->rx_skb_queue);
+
+ for (x = 0; x < CIRCULAR_BUFFER_COUNT; x++)
+ {
+ INIT_LIST_HEAD(&bcm54210pe->circular_buffers[x]);
+
+ for (y = 0; y < CIRCULAR_BUFFER_ITEM_COUNT; y++)
+ { list_add(&bcm54210pe->circular_buffer_items[x][y].list, &bcm54210pe->circular_buffers[x]); }
+ }
+
+ // Caps
+ memcpy(&bcm54210pe->ptp->caps, &bcm54210pe_clk_caps, sizeof(bcm54210pe_clk_caps));
+ bcm54210pe->ptp->caps.pin_config = bcm54210pe->sdp_config;
+
+ // Mutex
+ mutex_init(&bcm54210pe->clock_lock);
+ mutex_init(&bcm54210pe->timestamp_buffer_lock);
+
+ // Features
+ bcm54210pe->one_step = false;
+ bcm54210pe->extts_en = false;
+ bcm54210pe->perout_en = false;
+ bcm54210pe->perout_mode = SYNC_OUT_MODE_1;
+
+ // Fibonacci RSewoke style progressive backoff scheme
+ bcm54210pe->fib_sequence[0] = 1;
+ bcm54210pe->fib_sequence[1] = 1;
+ bcm54210pe->fib_sequence[2] = 2;
+ bcm54210pe->fib_sequence[3] = 3;
+ bcm54210pe->fib_sequence[4] = 5;
+ bcm54210pe->fib_sequence[5] = 8;
+ bcm54210pe->fib_sequence[6] = 13;
+ bcm54210pe->fib_sequence[7] = 21;
+ bcm54210pe->fib_sequence[8] = 34;
+ bcm54210pe->fib_sequence[9] = 55;
+
+ //bcm54210pe->fib_sequence = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55};
+ bcm54210pe->fib_factor_rx = 10;
+ bcm54210pe->fib_factor_tx = 10;
+
+ // Pin descriptions
+ sync_in_pin_desc = &bcm54210pe->sdp_config[SYNC_IN_PIN];
+ snprintf(sync_in_pin_desc->name, sizeof(sync_in_pin_desc->name), "SYNC_IN");
+ sync_in_pin_desc->index = SYNC_IN_PIN;
+ sync_in_pin_desc->func = PTP_PF_NONE;
+
+ sync_out_pin_desc = &bcm54210pe->sdp_config[SYNC_OUT_PIN];
+ snprintf(sync_out_pin_desc->name, sizeof(sync_out_pin_desc->name), "SYNC_OUT");
+ sync_out_pin_desc->index = SYNC_OUT_PIN;
+ sync_out_pin_desc->func = PTP_PF_NONE;
+
+ ptp->chosen = bcm54210pe;
+ phydev->priv = bcm54210pe;
+ ptp->caps.owner = THIS_MODULE;
+
+ bcm54210pe->ptp->ptp_clock = ptp_clock_register(&bcm54210pe->ptp->caps, &phydev->mdio.dev);
+
+ if (IS_ERR(bcm54210pe->ptp->ptp_clock)) {
+ return PTR_ERR(bcm54210pe->ptp->ptp_clock);
+ }
+
+ return 0;
+}
+
+static u16 bcm54210pe_get_base_nco6_reg(struct bcm54210pe_private *private, u16 val, bool do_nse_init)
+{
+
+ // Set Global mode to CPU system
+ val |= 0xC000;
+
+ // NSE init
+ if (do_nse_init) {
+ val |= 0x1000;
+ }
+
+ if (private->extts_en) {
+ val |= 0x2004;
+ }
+
+ if(private->perout_en) {
+ if (private->perout_mode == SYNC_OUT_MODE_1) {
+ val |= 0x0001;
+ } else if (private->perout_mode == SYNC_OUT_MODE_2) {
+ val |= 0x0002;
+ }
+ }
+
+ return val;
+}
+
+
+static u64 convert_48bit_to_80bit(u64 second_on_set, u64 ts)
+{
+ return (second_on_set * 1000000000) + ts;
+}
+
+static u64 four_u16_to_ns(u16 *four_u16)
+{
+ u32 seconds;
+ u32 nanoseconds;
+ struct timespec64 ts;
+ u16 *ptr;
+
+ nanoseconds = 0;
+ seconds = 0;
+
+
+ ptr = (u16 *)&nanoseconds;
+ *ptr = four_u16[0]; ptr++; *ptr = four_u16[1];
+
+ ptr = (u16 *)&seconds;
+ *ptr = four_u16[2]; ptr++; *ptr = four_u16[3];
+
+ ts.tv_sec = seconds;
+ ts.tv_nsec = nanoseconds;
+
+ return ts_to_ns(&ts);
+}
+
+static u64 ts_to_ns(struct timespec64 *ts)
+{
+ return ((u64)ts->tv_sec * (u64)1000000000) + ts->tv_nsec;
+}
+
+static void ns_to_ts(u64 time_stamp, struct timespec64 *ts)
+{
+ ts->tv_sec = ( (u64)time_stamp / (u64)1000000000 );
+ ts->tv_nsec = ( (u64)time_stamp % (u64)1000000000 );
+}
diff --git a/drivers/net/phy/bcm54210pe_ptp.h b/drivers/net/phy/bcm54210pe_ptp.h
new file mode 100755
index 0000000000000..483dafc2d4514
--- /dev/null
+++ b/drivers/net/phy/bcm54210pe_ptp.h
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * drivers/net/phy/bcm54210pe_ptp.h
+ *
+* IEEE1588 (PTP), perout and extts for BCM54210PE PHY
+ *
+ * Authors: Carlos Fernandez, Kyle Judd, Lasse Johnsen
+ * License: GPL
+ */
+
+#include <linux/ptp_clock_kernel.h>
+#include <linux/list.h>
+
+#define CIRCULAR_BUFFER_COUNT 8
+#define CIRCULAR_BUFFER_ITEM_COUNT 32
+
+#define SYNC_IN_PIN 0
+#define SYNC_OUT_PIN 1
+
+#define SYNC_OUT_MODE_1 1
+#define SYNC_OUT_MODE_2 2
+
+#define DIRECTION_RX 0
+#define DIRECTION_TX 1
+
+struct bcm54210pe_ptp {
+ struct ptp_clock_info caps;
+ struct ptp_clock *ptp_clock;
+ struct bcm54210pe_private *chosen;
+};
+
+struct bcm54210pe_circular_buffer_item {
+ struct list_head list;
+
+ u8 msg_type;
+ u16 sequence_id;
+ u64 time_stamp;
+ bool is_valid;
+};
+
+struct bcm54210pe_private {
+ struct phy_device *phydev;
+ struct bcm54210pe_ptp *ptp;
+ struct mii_timestamper mii_ts;
+ struct ptp_pin_desc sdp_config[2];
+
+ int ts_tx_config;
+ int tx_rx_filter;
+
+ bool one_step;
+ bool perout_en;
+ bool extts_en;
+
+ int second_on_set;
+
+ int perout_mode;
+ int perout_period;
+ int perout_pulsewidth;
+
+ u64 last_extts_ts;
+ u64 last_immediate_ts[2];
+
+ struct sk_buff_head tx_skb_queue;
+ struct sk_buff_head rx_skb_queue;
+
+ struct bcm54210pe_circular_buffer_item
+ circular_buffer_items[CIRCULAR_BUFFER_COUNT]
+ [CIRCULAR_BUFFER_ITEM_COUNT];
+ struct list_head circular_buffers[CIRCULAR_BUFFER_COUNT];
+
+ struct work_struct txts_work, rxts_work;
+ struct delayed_work perout_ws, extts_ws;
+ struct mutex clock_lock, timestamp_buffer_lock;
+
+ int fib_sequence[10];
+
+ int fib_factor_rx;
+ int fib_factor_tx;
+
+ int hwts_tx_en;
+ int hwts_rx_en;
+ int layer;
+ int version;
+};
+
+static bool bcm54210pe_rxtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb, int type);
+static void bcm54210pe_txtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb, int type);
+static void bcm54210pe_run_rx_timestamp_match_thread(struct work_struct *w);
+static void bcm54210pe_run_tx_timestamp_match_thread(struct work_struct *w);
+static void bcm54210pe_read_sop_time_register(struct bcm54210pe_private *private);
+static bool bcm54210pe_fetch_timestamp(u8 txrx, u8 message_type, u16 seq_id, struct bcm54210pe_private *private, u64 *timestamp);
+
+static u16 bcm54210pe_get_base_nco6_reg(struct bcm54210pe_private *private, u16 val, bool do_nse_init);
+static int bcm54210pe_interrupts_enable(struct phy_device *phydev, bool fsync_en, bool sop_en);
+static int bcm54210pe_gettimex(struct ptp_clock_info *info, struct timespec64 *ts, struct ptp_system_timestamp *sts);
+static int bcm54210pe_get80bittime(struct bcm54210pe_private *private, struct timespec64 *ts, struct ptp_system_timestamp *sts);
+static int bcm54210pe_get48bittime(struct bcm54210pe_private *private, u64 *time_stamp);
+static void bcm54210pe_read80bittime_register(struct phy_device *phydev, u64 *time_stamp_80, u64 *time_stamp_48);
+static void bcm54210pe_read48bittime_register(struct phy_device *phydev, u64 *time_stamp);
+
+static int bcm54210pe_perout_enable(struct bcm54210pe_private *private, s64 period, s64 pulsewidth, int on);
+static void bcm54210pe_run_perout_mode_one_thread(struct work_struct *perout_ws);
+
+static int bcm54210pe_extts_enable(struct bcm54210pe_private *private, int enable);
+static void bcm54210pe_run_extts_thread(struct work_struct *extts_ws);
+static void bcm54210pe_trigger_extts_event(struct bcm54210pe_private *private, u64 timestamp);
+
+static u64 convert_48bit_to_80bit(u64 second_on_set, u64 ts);
+static u64 four_u16_to_ns(u16 *four_u16);
+static u64 ts_to_ns(struct timespec64 *ts);
+static void ns_to_ts(u64 time_stamp, struct timespec64 *ts);
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index 8b0ac38742d06..c8b79522cf3ad 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -15,6 +15,11 @@
#include <linux/phy.h>
#include <linux/brcmphy.h>
#include <linux/of.h>
+#include <linux/irq.h>
+
+#if IS_ENABLED (CONFIG_BCM54120PE_PHY)
+extern int bcm54210pe_probe(struct phy_device *phydev);
+#endif
#define BRCM_PHY_MODEL(phydev) \
((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
@@ -778,7 +783,20 @@ static struct phy_driver broadcom_drivers[] = {
.config_init = bcm54xx_config_init,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
-}, {
+},
+
+#if IS_ENABLED (CONFIG_BCM54120PE_PHY)
+{
+ .phy_id = PHY_ID_BCM54213PE,
+ .phy_id_mask = 0xffffffff,
+ .name = "Broadcom BCM54210PE",
+ /* PHY_GBIT_FEATURES */
+ .config_init = bcm54xx_config_init,
+ .ack_interrupt = bcm_phy_ack_intr,
+ .config_intr = bcm_phy_config_intr,
+ .probe = bcm54210pe_probe,
+#elif
+{
.phy_id = PHY_ID_BCM54213PE,
.phy_id_mask = 0xffffffff,
.name = "Broadcom BCM54213PE",
@@ -786,6 +804,7 @@ static struct phy_driver broadcom_drivers[] = {
.config_init = bcm54xx_config_init,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
+#endif
}, {
.phy_id = PHY_ID_BCM5461,
.phy_id_mask = 0xfffffff0,
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 3e377f3c69e5d..975a62286a9c6 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -87,6 +87,23 @@ config PTP_1588_CLOCK_INES
core. This clock is only useful if the MII bus of your MAC
is wired up to the core.
+ config BCM54120PE_PHY
+ tristate "Add suport for ptp in bcm54210pe PHYs"
+ depends on NETWORK_PHY_TIMESTAMPING
+ depends on PHYLIB
+ depends on PTP_1588_CLOCK
+ depends on BCM_NET_PHYLIB
+ select NET_PTP_CLASSIFY
+ help
+ This driver adds support for using the BCM54210PE as a PTP
+ clock. This clock is only useful if your PTP programs are
+ getting hardware time stamps on the PTP Ethernet packets
+ using the SO_TIMESTAMPING API.
+
+ In order for this to work, your MAC driver must also
+ implement the skb_tx_timestamp() function.
+
+
config PTP_1588_CLOCK_PCH
tristate "Intel PCH EG20T as PTP clock"
depends on X86_32 || COMPILE_TEST
^ permalink raw reply related
* [PATCH v3 net-next] dt-bindings: net: mediatek,net: convert to the json-schema
From: Lorenzo Bianconi @ 2022-04-20 14:07 UTC (permalink / raw)
To: netdev; +Cc: nbd, lorenzo.bianconi, devicetree, robh, davem, kuba, pabeni,
john
This patch converts the existing mediatek-net.txt binding file
in yaml format.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
Changes since v2:
- remove additionalItems for clock-names properties
- move mediatek,sgmiisys definition out of the if block
Changes since v1:
- set resets maxItems to 3
- fix cci-control-port usage in example
This patch is based on commits [0] and [1] available in net-next tree but not
in Linus's one yet.
[0] 1dafd0d60703 ("dt-bindings: net: mediatek: add optional properties for the SoC ethernet core")
[1] 4263f77a5144 ("net: ethernet: mtk_eth_soc: use standard property for cci-control-port")
---
.../devicetree/bindings/net/mediatek,net.yaml | 297 ++++++++++++++++++
.../devicetree/bindings/net/mediatek-net.txt | 108 -------
2 files changed, 297 insertions(+), 108 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/mediatek,net.yaml
delete mode 100644 Documentation/devicetree/bindings/net/mediatek-net.txt
diff --git a/Documentation/devicetree/bindings/net/mediatek,net.yaml b/Documentation/devicetree/bindings/net/mediatek,net.yaml
new file mode 100644
index 000000000000..43cc4024ef98
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mediatek,net.yaml
@@ -0,0 +1,297 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/mediatek,net.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Frame Engine Ethernet controller
+
+maintainers:
+ - Lorenzo Bianconi <lorenzo@kernel.org>
+ - Felix Fietkau <nbd@nbd.name>
+
+description:
+ The frame engine ethernet controller can be found on MediaTek SoCs. These SoCs
+ have dual GMAC ports.
+
+properties:
+ compatible:
+ enum:
+ - mediatek,mt2701-eth
+ - mediatek,mt7623-eth
+ - mediatek,mt7622-eth
+ - mediatek,mt7629-eth
+ - ralink,rt5350-eth
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ minItems: 3
+ maxItems: 3
+
+ power-domains:
+ maxItems: 1
+
+ resets:
+ maxItems: 3
+
+ reset-names:
+ items:
+ - const: fe
+ - const: gmac
+ - const: ppe
+
+ mediatek,ethsys:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the syscon node that handles the port setup.
+
+ cci-control-port: true
+
+ mediatek,hifsys:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the mediatek hifsys controller used to provide various clocks
+ and reset to the system.
+
+ mediatek,sgmiisys:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ minItems: 1
+ maxItems: 2
+ items:
+ maxItems: 1
+ description:
+ A list of phandle to the syscon node that handles the SGMII setup which is required for
+ those SoCs equipped with SGMII.
+
+ dma-coherent: true
+
+ mdio-bus:
+ $ref: mdio.yaml#
+ unevaluatedProperties: false
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+allOf:
+ - $ref: "ethernet-controller.yaml#"
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - mediatek,mt2701-eth
+ - mediatek,mt7623-eth
+ then:
+ properties:
+ clocks:
+ minItems: 4
+ maxItems: 4
+
+ clock-names:
+ items:
+ - const: ethif
+ - const: esw
+ - const: gp1
+ - const: gp2
+
+ mediatek,pctl:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the syscon node that handles the ports slew rate and
+ driver current.
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: mediatek,mt7622-eth
+ then:
+ properties:
+ clocks:
+ minItems: 11
+ maxItems: 11
+
+ clock-names:
+ items:
+ - const: ethif
+ - const: esw
+ - const: gp0
+ - const: gp1
+ - const: gp2
+ - const: sgmii_tx250m
+ - const: sgmii_rx250m
+ - const: sgmii_cdr_ref
+ - const: sgmii_cdr_fb
+ - const: sgmii_ck
+ - const: eth2pll
+
+ mediatek,sgmiisys:
+ minItems: 1
+ maxItems: 1
+
+ mediatek,wed:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ minItems: 2
+ maxItems: 2
+ items:
+ maxItems: 1
+ description:
+ List of phandles to wireless ethernet dispatch nodes.
+
+ mediatek,pcie-mirror:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the mediatek pcie-mirror controller.
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: mediatek,mt7629-eth
+ then:
+ properties:
+ clocks:
+ minItems: 17
+ maxItems: 17
+
+ clock-names:
+ items:
+ - const: ethif
+ - const: sgmiitop
+ - const: esw
+ - const: gp0
+ - const: gp1
+ - const: gp2
+ - const: fe
+ - const: sgmii_tx250m
+ - const: sgmii_rx250m
+ - const: sgmii_cdr_ref
+ - const: sgmii_cdr_fb
+ - const: sgmii2_tx250m
+ - const: sgmii2_rx250m
+ - const: sgmii2_cdr_ref
+ - const: sgmii2_cdr_fb
+ - const: sgmii_ck
+ - const: eth2pll
+
+ mediatek,infracfg:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the syscon node that handles the path from GMAC to
+ PHY variants.
+
+ mediatek,sgmiisys:
+ minItems: 2
+ maxItems: 2
+
+patternProperties:
+ "^mac@[0-1]$":
+ type: object
+ additionalProperties: false
+ allOf:
+ - $ref: ethernet-controller.yaml#
+ description:
+ Ethernet MAC node
+ properties:
+ compatible:
+ const: mediatek,eth-mac
+
+ reg:
+ maxItems: 1
+
+ phy-handle: true
+
+ phy-mode: true
+
+ required:
+ - reg
+ - compatible
+ - phy-handle
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - power-domains
+ - mediatek,ethsys
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/clock/mt7622-clk.h>
+ #include <dt-bindings/power/mt7622-power.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ ethernet: ethernet@1b100000 {
+ compatible = "mediatek,mt7622-eth";
+ reg = <0 0x1b100000 0 0x20000>;
+ interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 224 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 225 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&topckgen CLK_TOP_ETH_SEL>,
+ <ðsys CLK_ETH_ESW_EN>,
+ <ðsys CLK_ETH_GP0_EN>,
+ <ðsys CLK_ETH_GP1_EN>,
+ <ðsys CLK_ETH_GP2_EN>,
+ <&sgmiisys CLK_SGMII_TX250M_EN>,
+ <&sgmiisys CLK_SGMII_RX250M_EN>,
+ <&sgmiisys CLK_SGMII_CDR_REF>,
+ <&sgmiisys CLK_SGMII_CDR_FB>,
+ <&topckgen CLK_TOP_SGMIIPLL>,
+ <&apmixedsys CLK_APMIXED_ETH2PLL>;
+ clock-names = "ethif", "esw", "gp0", "gp1", "gp2",
+ "sgmii_tx250m", "sgmii_rx250m",
+ "sgmii_cdr_ref", "sgmii_cdr_fb", "sgmii_ck",
+ "eth2pll";
+ power-domains = <&scpsys MT7622_POWER_DOMAIN_ETHSYS>;
+ mediatek,ethsys = <ðsys>;
+ mediatek,sgmiisys = <&sgmiisys>;
+ cci-control-port = <&cci_control2>;
+ mediatek,pcie-mirror = <&pcie_mirror>;
+ mediatek,hifsys = <&hifsys>;
+ dma-coherent;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mdio0: mdio-bus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phy0: ethernet-phy@0 {
+ reg = <0>;
+ };
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+ };
+
+ gmac0: mac@0 {
+ compatible = "mediatek,eth-mac";
+ phy-mode = "rgmii";
+ phy-handle = <&phy0>;
+ reg = <0>;
+ };
+
+ gmac1: mac@1 {
+ compatible = "mediatek,eth-mac";
+ phy-mode = "rgmii";
+ phy-handle = <&phy1>;
+ reg = <1>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/net/mediatek-net.txt b/Documentation/devicetree/bindings/net/mediatek-net.txt
deleted file mode 100644
index f18d70189375..000000000000
--- a/Documentation/devicetree/bindings/net/mediatek-net.txt
+++ /dev/null
@@ -1,108 +0,0 @@
-MediaTek Frame Engine Ethernet controller
-=========================================
-
-The frame engine ethernet controller can be found on MediaTek SoCs. These SoCs
-have dual GMAC each represented by a child node..
-
-* Ethernet controller node
-
-Required properties:
-- compatible: Should be
- "mediatek,mt2701-eth": for MT2701 SoC
- "mediatek,mt7623-eth", "mediatek,mt2701-eth": for MT7623 SoC
- "mediatek,mt7622-eth": for MT7622 SoC
- "mediatek,mt7629-eth": for MT7629 SoC
- "ralink,rt5350-eth": for Ralink Rt5350F and MT7628/88 SoC
-- reg: Address and length of the register set for the device
-- interrupts: Should contain the three frame engines interrupts in numeric
- order. These are fe_int0, fe_int1 and fe_int2.
-- clocks: the clock used by the core
-- clock-names: the names of the clock listed in the clocks property. These are
- "ethif", "esw", "gp2", "gp1" : For MT2701 and MT7623 SoC
- "ethif", "esw", "gp0", "gp1", "gp2", "sgmii_tx250m", "sgmii_rx250m",
- "sgmii_cdr_ref", "sgmii_cdr_fb", "sgmii_ck", "eth2pll" : For MT7622 SoC
- "ethif", "sgmiitop", "esw", "gp0", "gp1", "gp2", "fe", "sgmii_tx250m",
- "sgmii_rx250m", "sgmii_cdr_ref", "sgmii_cdr_fb", "sgmii2_tx250m",
- "sgmii2_rx250m", "sgmii2_cdr_ref", "sgmii2_cdr_fb", "sgmii_ck",
- "eth2pll" : For MT7629 SoC.
-- power-domains: phandle to the power domain that the ethernet is part of
-- resets: Should contain phandles to the ethsys reset signals
-- reset-names: Should contain the names of reset signal listed in the resets
- property
- These are "fe", "gmac" and "ppe"
-- mediatek,ethsys: phandle to the syscon node that handles the port setup
-- mediatek,infracfg: phandle to the syscon node that handles the path from
- GMAC to PHY variants, which is required for MT7629 SoC.
-- mediatek,sgmiisys: a list of phandles to the syscon node that handles the
- SGMII setup which is required for those SoCs equipped with SGMII such
- as MT7622 and MT7629 SoC. And MT7622 have only one set of SGMII shared
- by GMAC1 and GMAC2; MT7629 have two independent sets of SGMII directed
- to GMAC1 and GMAC2, respectively.
-- mediatek,pctl: phandle to the syscon node that handles the ports slew rate
- and driver current: only for MT2701 and MT7623 SoC
-
-Optional properties:
-- dma-coherent: present if dma operations are coherent
-- mediatek,cci-control: phandle to the cache coherent interconnect node
-- mediatek,hifsys: phandle to the mediatek hifsys controller used to provide
- various clocks and reset to the system.
-- mediatek,wed: a list of phandles to wireless ethernet dispatch nodes for
- MT7622 SoC.
-- mediatek,pcie-mirror: phandle to the mediatek pcie-mirror controller for
- MT7622 SoC.
-
-* Ethernet MAC node
-
-Required properties:
-- compatible: Should be "mediatek,eth-mac"
-- reg: The number of the MAC
-- phy-handle: see ethernet.txt file in the same directory and
- the phy-mode "trgmii" required being provided when reg
- is equal to 0 and the MAC uses fixed-link to connect
- with internal switch such as MT7530.
-
-Example:
-
-eth: ethernet@1b100000 {
- compatible = "mediatek,mt7623-eth";
- reg = <0 0x1b100000 0 0x20000>;
- clocks = <&topckgen CLK_TOP_ETHIF_SEL>,
- <ðsys CLK_ETHSYS_ESW>,
- <ðsys CLK_ETHSYS_GP2>,
- <ðsys CLK_ETHSYS_GP1>;
- clock-names = "ethif", "esw", "gp2", "gp1";
- interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW
- GIC_SPI 199 IRQ_TYPE_LEVEL_LOW
- GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>;
- power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>;
- resets = <ðsys MT2701_ETHSYS_ETH_RST>;
- reset-names = "eth";
- mediatek,ethsys = <ðsys>;
- mediatek,pctl = <&syscfg_pctl_a>;
- #address-cells = <1>;
- #size-cells = <0>;
-
- gmac1: mac@0 {
- compatible = "mediatek,eth-mac";
- reg = <0>;
- phy-handle = <&phy0>;
- };
-
- gmac2: mac@1 {
- compatible = "mediatek,eth-mac";
- reg = <1>;
- phy-handle = <&phy1>;
- };
-
- mdio-bus {
- phy0: ethernet-phy@0 {
- reg = <0>;
- phy-mode = "rgmii";
- };
-
- phy1: ethernet-phy@1 {
- reg = <1>;
- phy-mode = "rgmii";
- };
- };
-};
--
2.35.1
^ permalink raw reply related
* Re: [PATCH net-next 0/6] mlxsw: Line cards status tracking
From: patchwork-bot+netdevbpf @ 2022-04-20 14:10 UTC (permalink / raw)
To: Ido Schimmel; +Cc: netdev, davem, kuba, pabeni, petrm, jiri, vadimp, mlxsw
In-Reply-To: <20220419145431.2991382-1-idosch@nvidia.com>
Hello:
This series was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:
On Tue, 19 Apr 2022 17:54:25 +0300 you wrote:
> When a line card is provisioned, netdevs corresponding to the ports
> found on the line card are registered. User space can then perform
> various logical configurations (e.g., splitting, setting MTU) on these
> netdevs.
>
> However, since the line card is not present / powered on (i.e., it is
> not in 'active' state), user space cannot access the various components
> found on the line card. For example, user space cannot read the
> temperature of gearboxes or transceiver modules found on the line card
> via hwmon / thermal. Similarly, it cannot dump the EEPROM contents of
> these transceiver modules. The above is only possible when the line card
> becomes active.
>
> [...]
Here is the summary with links:
- [net-next,1/6] mlxsw: core_linecards: Introduce ops for linecards status change tracking
https://git.kernel.org/netdev/net-next/c/de28976d2650
- [net-next,2/6] mlxsw: core: Add bus argument to environment init API
https://git.kernel.org/netdev/net-next/c/7b261af9f641
- [net-next,3/6] mlxsw: core_env: Split module power mode setting to a separate function
https://git.kernel.org/netdev/net-next/c/a11e1ec141ea
- [net-next,4/6] mlxsw: core_env: Add interfaces for line card initialization and de-initialization
https://git.kernel.org/netdev/net-next/c/06a0fc43bb10
- [net-next,5/6] mlxsw: core_thermal: Add interfaces for line card initialization and de-initialization
https://git.kernel.org/netdev/net-next/c/f11a323da46c
- [net-next,6/6] mlxsw: core_hwmon: Add interfaces for line card initialization and de-initialization
https://git.kernel.org/netdev/net-next/c/99a03b3193f6
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net 0/2] selftests: mlxsw: Make VXLAN flooding tests more robust
From: patchwork-bot+netdevbpf @ 2022-04-20 14:10 UTC (permalink / raw)
To: Ido Schimmel; +Cc: netdev, davem, kuba, pabeni, petrm, amcohen, mlxsw
In-Reply-To: <20220419135155.2987141-1-idosch@nvidia.com>
Hello:
This series was applied to netdev/net.git (master)
by David S. Miller <davem@davemloft.net>:
On Tue, 19 Apr 2022 16:51:53 +0300 you wrote:
> Make the VXLAN flooding tests (with IPv4 and IPv6 underlay) more robust
> by preventing flooding of unwanted packets. See detailed description of
> the problem and solution in the commit messages.
>
> Ido Schimmel (2):
> selftests: mlxsw: vxlan_flooding: Prevent flooding of unwanted packets
> selftests: mlxsw: vxlan_flooding_ipv6: Prevent flooding of unwanted
> packets
>
> [...]
Here is the summary with links:
- [net,1/2] selftests: mlxsw: vxlan_flooding: Prevent flooding of unwanted packets
https://git.kernel.org/netdev/net/c/044011fdf162
- [net,2/2] selftests: mlxsw: vxlan_flooding_ipv6: Prevent flooding of unwanted packets
https://git.kernel.org/netdev/net/c/5e6242151d7f
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [PATCH net-next] mlxsw: core_linecards: Fix size of array element during ini_files allocation
From: Ido Schimmel @ 2022-04-20 14:20 UTC (permalink / raw)
To: netdev; +Cc: davem, kuba, pabeni, petrm, jiri, mlxsw, Ido Schimmel
From: Jiri Pirko <jiri@nvidia.com>
types_info->ini_files is an array of pointers
to struct mlxsw_linecard_ini_file.
Fix the kmalloc_array() argument to be of a size of a pointer.
Addresses-Coverity: ("Incorrect expression (SIZEOF_MISMATCH)")
Fixes: b217127e5e4e ("mlxsw: core_linecards: Add line card objects and implement provisioning")
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
---
drivers/net/ethernet/mellanox/mlxsw/core_linecards.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c b/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c
index 90e487cc2e2a..5c9869dcf674 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c
@@ -1032,7 +1032,7 @@ static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,
}
types_info->ini_files = kmalloc_array(types_info->count,
- sizeof(struct mlxsw_linecard_ini_file),
+ sizeof(struct mlxsw_linecard_ini_file *),
GFP_KERNEL);
if (!types_info->ini_files) {
err = -ENOMEM;
--
2.33.1
^ permalink raw reply related
* Re: [PATCH 1/2] dt-bindings: mailbox: qcom-ipcc: simplify the example
From: Jassi Brar @ 2022-04-20 14:22 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Manivannan Sadhasivam, Bjorn Andersson, Krzysztof Kozlowski,
Paolo Abeni, linux-arm-msm, Linux Kernel Mailing List,
Devicetree List, Andy Gross, <netdev@vger.kernel.org>,
Rob Herring, Alex Elder, Jakub Kicinski, David S. Miller,
Rob Herring
In-Reply-To: <a3edf0e1-644a-38b2-b23d-30cc01005786@linaro.org>
On Wed, Apr 20, 2022 at 3:42 AM Krzysztof Kozlowski
<krzysztof.kozlowski@linaro.org> wrote:
>
> On 02/04/2022 17:55, Krzysztof Kozlowski wrote:
> > Consumer examples in the bindings of resource providers are trivial,
> > useless and duplicating code. Additionally the incomplete qcom,smp2p
> > example triggers DT schema warnings.
> >
> > Cleanup the example by removing the consumer part and fixing the
> > indentation to DT schema convention.
> >
> > Reported-by: Rob Herring <robh@kernel.org>
> > Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
>
> Jassi,
> Do you plan to pick this mailbox patch?
>
Yes, I do. I am ok too, if you want it through some other tree as a
part of some bigger patchset.
thanks.
^ permalink raw reply
* Re: [PATCH 1/5] net: mdio: Mask PHY only when its ACPI node is present
From: Andrew Lunn @ 2022-04-20 14:47 UTC (permalink / raw)
To: Kai-Heng Feng
Cc: hkallweit1, linux, peppe.cavallaro, alexandre.torgue, joabreu,
davem, kuba, pabeni, netdev, linux-kernel
In-Reply-To: <20220420124053.853891-2-kai.heng.feng@canonical.com>
On Wed, Apr 20, 2022 at 08:40:48PM +0800, Kai-Heng Feng wrote:
> Not all PHY has an ACPI node, for those nodes auto probing is still
> needed.
Why do you need this?
Documentation/firmware-guide/acpi/dsd/phy.rst
There is nothing here about there being PHYs which are not listed in
ACPI. If you have decided to go the ACPI route, you need to list the
PHYs.
Andrew
^ permalink raw reply
* Re: [PATCH net-next] Revert "rtnetlink: return EINVAL when request cannot succeed"
From: Guillaume Nault @ 2022-04-20 14:48 UTC (permalink / raw)
To: Florent Fourcot, David Miller, Jakub Kicinski, Paolo Abeni
Cc: netdev, Stephen Hemminger, Brian Baboch
In-Reply-To: <20220419125151.15589-1-florent.fourcot@wifirst.fr>
On Tue, Apr 19, 2022 at 02:51:51PM +0200, Florent Fourcot wrote:
> This reverts commit b6177d3240a4
>
> ip-link command is testing kernel capability by sending a RTM_NEWLINK
> request, without any argument. It accepts everything in reply, except
> EOPNOTSUPP and EINVAL (functions iplink_have_newlink / accept_msg)
>
> So we must keep compatiblity here, invalid empty message should not
> return EINVAL
"ip link" is currently unusable on net-next without this patch.
Can we please rush this fix in?
Tested-by: Guillaume Nault <gnault@redhat.com>
Fixes: b6177d3240a4 ("rtnetlink: return EINVAL when request cannot succeed")
> Signed-off-by: Florent Fourcot <florent.fourcot@wifirst.fr>
> ---
> net/core/rtnetlink.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
> index b943336908a7..73f2cbc440c9 100644
> --- a/net/core/rtnetlink.c
> +++ b/net/core/rtnetlink.c
> @@ -3457,7 +3457,7 @@ static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
> return rtnl_group_changelink(skb, net,
> nla_get_u32(tb[IFLA_GROUP]),
> ifm, extack, tb);
> - return -EINVAL;
> + return -ENODEV;
> }
>
> if (tb[IFLA_MAP] || tb[IFLA_PROTINFO])
> --
> 2.30.2
>
^ permalink raw reply
* Re: [PATCH net-next 1/2] ipv6: Remove __ipv6_only_sock().
From: David Ahern @ 2022-04-20 14:55 UTC (permalink / raw)
To: Kuniyuki Iwashima, David S. Miller, Jakub Kicinski
Cc: Kuniyuki Iwashima, netdev
In-Reply-To: <20220420015851.50237-2-kuniyu@amazon.co.jp>
On 4/19/22 7:58 PM, Kuniyuki Iwashima wrote:
> Since commit 9fe516ba3fb2 ("inet: move ipv6only in sock_common"),
> ipv6_only_sock() and __ipv6_only_sock() are the same macro. Let's
> remove the one.
>
> Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
> ---
> include/linux/ipv6.h | 4 +---
> net/dccp/ipv6.c | 2 +-
> net/ipv6/datagram.c | 4 ++--
> net/ipv6/tcp_ipv6.c | 2 +-
> net/ipv6/udp.c | 4 ++--
> net/sctp/ipv6.c | 4 ++--
> 6 files changed, 9 insertions(+), 11 deletions(-)
>
Reviewed-by: David Ahern <dsahern@kernel.org>
^ permalink raw reply
* Re: [PATCH net-next 2/2] ipv6: Use ipv6_only_sock() helper in condition.
From: David Ahern @ 2022-04-20 14:56 UTC (permalink / raw)
To: Kuniyuki Iwashima, David S. Miller, Jakub Kicinski
Cc: Kuniyuki Iwashima, netdev
In-Reply-To: <20220420015851.50237-3-kuniyu@amazon.co.jp>
On 4/19/22 7:58 PM, Kuniyuki Iwashima wrote:
> This patch replaces some sk_ipv6only tests with ipv6_only_sock().
>
> Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
> ---
> drivers/net/bonding/bond_main.c | 2 +-
> drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c | 2 +-
> drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c | 2 +-
> drivers/net/ethernet/netronome/nfp/crypto/tls.c | 2 +-
> net/core/filter.c | 2 +-
> net/ipv6/af_inet6.c | 2 +-
> 6 files changed, 6 insertions(+), 6 deletions(-)
>
Reviewed-by: David Ahern <dsahern@kernel.org>
^ permalink raw reply
* Re: [PATCH 4/5] net: phy: marvell: Add LED accessors for Marvell 88E1510
From: Andrew Lunn @ 2022-04-20 15:03 UTC (permalink / raw)
To: Kai-Heng Feng
Cc: hkallweit1, linux, peppe.cavallaro, alexandre.torgue, joabreu,
davem, kuba, pabeni, netdev, linux-kernel
In-Reply-To: <20220420124053.853891-5-kai.heng.feng@canonical.com>
On Wed, Apr 20, 2022 at 08:40:51PM +0800, Kai-Heng Feng wrote:
> Implement get_led_config() and set_led_config() callbacks so phy core
> can use firmware LED as platform requested.
>
> Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
> ---
> drivers/net/phy/marvell.c | 26 ++++++++++++++++++++++++++
> 1 file changed, 26 insertions(+)
>
> diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
> index 2702faf7b0f60..c5f13e09b0692 100644
> --- a/drivers/net/phy/marvell.c
> +++ b/drivers/net/phy/marvell.c
> @@ -750,6 +750,30 @@ static int m88e1510_config_aneg(struct phy_device *phydev)
> return err;
> }
>
> +static int marvell_get_led_config(struct phy_device *phydev)
> +{
> + int led;
> +
> + led = phy_read_paged(phydev, MII_MARVELL_LED_PAGE, MII_PHY_LED_CTRL);
> + if (led < 0) {
> + phydev_warn(phydev, "Fail to get marvell phy LED.\n");
> + led = 0;
> + }
I've said this multiple times, there are three LED registers, The
Function Control register, the Priority Control register and the Timer
control register. It is the combination of all three that defines how
the LEDs work. You need to save all of them.
And you need to make your API generic enough that the PHY driver can
save anywhere from 1 bit to 42 bytes of configuration.
I don't know ACPI, but i'm pretty sure this is not the ACPI way of
doing this. I think you should be defining an ACPI method, which
phylib can call after initialising the hardware to allow the firmware
to configure the LED.
Andrew
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox