From patchwork Thu Dec 3 10:21:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11948331 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EE887C64E8A for ; Thu, 3 Dec 2020 10:23:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8FCDA21D91 for ; Thu, 3 Dec 2020 10:23:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730174AbgLCKXY (ORCPT ); Thu, 3 Dec 2020 05:23:24 -0500 Received: from mailout04.rmx.de ([94.199.90.94]:48002 "EHLO mailout04.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726080AbgLCKXY (ORCPT ); Thu, 3 Dec 2020 05:23:24 -0500 Received: from kdin02.retarus.com (kdin02.dmz1.retloc [172.19.17.49]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout04.rmx.de (Postfix) with ESMTPS id 4CmsNH4pkFz3qq3p; Thu, 3 Dec 2020 11:22:39 +0100 (CET) Received: from mta.arri.de (unknown [217.111.95.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by kdin02.retarus.com (Postfix) with ESMTPS id 4CmsMf15Rwz2TTM0; Thu, 3 Dec 2020 11:22:06 +0100 (CET) Received: from N95HX1G2.wgnetz.xx (192.168.54.174) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.487.0; Thu, 3 Dec 2020 11:22:04 +0100 From: Christian Eggers To: Vladimir Oltean , Jakub Kicinski , Andrew Lunn , Richard Cochran , "Rob Herring" CC: Vivien Didelot , "David S . Miller" , Kurt Kanzenbach , George McCollister , Marek Vasut , Helmut Grohne , Paul Barker , Codrin Ciubotariu , Tristram Ha , Woojung Huh , Microchip Linux Driver Support , Christian Eggers , , , Subject: [PATCH net-next v5 1/9] net: dsa: microchip: rename ksz9477.c to ksz9477_main.c Date: Thu, 3 Dec 2020 11:21:09 +0100 Message-ID: <20201203102117.8995-2-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201203102117.8995-1-ceggers@arri.de> References: <20201203102117.8995-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.174] X-RMX-ID: 20201203-112206-hLMzJfcsjVJf-0@out02.hq X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org PTP functionality will be built into a separate source file (ksz9477_ptp.c). Signed-off-by: Christian Eggers Reviewed-by: Vladimir Oltean --- drivers/net/dsa/microchip/Makefile | 1 + drivers/net/dsa/microchip/{ksz9477.c => ksz9477_main.c} | 0 2 files changed, 1 insertion(+) rename drivers/net/dsa/microchip/{ksz9477.c => ksz9477_main.c} (100%) diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile index 929caa81e782..c5cc1d5dea06 100644 --- a/drivers/net/dsa/microchip/Makefile +++ b/drivers/net/dsa/microchip/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON) += ksz_common.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477) += ksz9477.o +ksz9477-objs := ksz9477_main.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C) += ksz9477_i2c.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI) += ksz9477_spi.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8795) += ksz8795.o diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477_main.c similarity index 100% rename from drivers/net/dsa/microchip/ksz9477.c rename to drivers/net/dsa/microchip/ksz9477_main.c From patchwork Thu Dec 3 10:21:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11948333 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 273C6C64E8A for ; Thu, 3 Dec 2020 10:24:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CB77421D1A for ; Thu, 3 Dec 2020 10:24:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387667AbgLCKYF (ORCPT ); Thu, 3 Dec 2020 05:24:05 -0500 Received: from mailout09.rmx.de ([94.199.88.74]:37432 "EHLO mailout09.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726080AbgLCKYE (ORCPT ); Thu, 3 Dec 2020 05:24:04 -0500 Received: from kdin02.retarus.com (kdin02.dmz1.retloc [172.19.17.49]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout09.rmx.de (Postfix) with ESMTPS id 4CmsP34rnyzbksd; Thu, 3 Dec 2020 11:23:19 +0100 (CET) Received: from mta.arri.de (unknown [217.111.95.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by kdin02.retarus.com (Postfix) with ESMTPS id 4CmsNM4h7Wz2TRlF; Thu, 3 Dec 2020 11:22:43 +0100 (CET) Received: from N95HX1G2.wgnetz.xx (192.168.54.174) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.487.0; Thu, 3 Dec 2020 11:22:34 +0100 From: Christian Eggers To: Vladimir Oltean , Jakub Kicinski , Andrew Lunn , Richard Cochran , "Rob Herring" CC: Vivien Didelot , "David S . Miller" , Kurt Kanzenbach , George McCollister , Marek Vasut , Helmut Grohne , Paul Barker , Codrin Ciubotariu , Tristram Ha , Woojung Huh , Microchip Linux Driver Support , Christian Eggers , , , Subject: [PATCH net-next v5 2/9] dt-bindings: net: dsa: microchip,ksz: add interrupt property Date: Thu, 3 Dec 2020 11:21:10 +0100 Message-ID: <20201203102117.8995-3-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201203102117.8995-1-ceggers@arri.de> References: <20201203102117.8995-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.174] X-RMX-ID: 20201203-112243-dHdwvPqQMJkq-0@out02.hq X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org The devices have an optional interrupt line. Signed-off-by: Christian Eggers Reviewed-by: Rob Herring --- .../devicetree/bindings/net/dsa/microchip,ksz.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml index 9f7d131bbcef..c3235110156a 100644 --- a/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml +++ b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml @@ -35,6 +35,11 @@ properties: Should be a gpio specifier for a reset line. maxItems: 1 + interrupts: + description: + Interrupt specifier for the INTRP_N line from the device. + maxItems: 1 + microchip,synclko-125: $ref: /schemas/types.yaml#/definitions/flag description: @@ -49,6 +54,7 @@ unevaluatedProperties: false examples: - | #include + #include // Ethernet switch connected via SPI to the host, CPU port wired to eth0: eth0 { @@ -70,6 +76,7 @@ examples: compatible = "microchip,ksz9477"; reg = <0>; reset-gpios = <&gpio5 0 GPIO_ACTIVE_LOW>; + interrupts-extended = <&gpio5 1 IRQ_TYPE_LEVEL_LOW>; /* INTRP_N line */ spi-max-frequency = <44000000>; From patchwork Thu Dec 3 10:21:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11948335 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6DDC9C64E8A for ; Thu, 3 Dec 2020 10:25:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D39D621D91 for ; Thu, 3 Dec 2020 10:25:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730244AbgLCKYq (ORCPT ); Thu, 3 Dec 2020 05:24:46 -0500 Received: from mailout12.rmx.de ([94.199.88.78]:51363 "EHLO mailout12.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726080AbgLCKYq (ORCPT ); Thu, 3 Dec 2020 05:24:46 -0500 Received: from kdin02.retarus.com (kdin02.dmz1.retloc [172.19.17.49]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout12.rmx.de (Postfix) with ESMTPS id 4CmsPr5Qh6zRlCw; Thu, 3 Dec 2020 11:24:00 +0100 (CET) Received: from mta.arri.de (unknown [217.111.95.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by kdin02.retarus.com (Postfix) with ESMTPS id 4CmsP34qD9z2TTMK; Thu, 3 Dec 2020 11:23:19 +0100 (CET) Received: from N95HX1G2.wgnetz.xx (192.168.54.174) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.487.0; Thu, 3 Dec 2020 11:23:09 +0100 From: Christian Eggers To: Vladimir Oltean , Jakub Kicinski , Andrew Lunn , Richard Cochran , "Rob Herring" CC: Vivien Didelot , "David S . Miller" , Kurt Kanzenbach , George McCollister , Marek Vasut , Helmut Grohne , Paul Barker , Codrin Ciubotariu , Tristram Ha , Woojung Huh , Microchip Linux Driver Support , Christian Eggers , , , Subject: [PATCH net-next v5 3/9] net: dsa: microchip: ksz9477: move chip reset to ksz9477_switch_init() Date: Thu, 3 Dec 2020 11:21:11 +0100 Message-ID: <20201203102117.8995-4-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201203102117.8995-1-ceggers@arri.de> References: <20201203102117.8995-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.174] X-RMX-ID: 20201203-112319-pWfkCHtyNVld-0@out02.hq X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org The next patch will add basic interrupt support. Chip reset must be performed before requesting the IRQ, so move this from ksz9477_setup() to ksz9477_init(). Signed-off-by: Christian Eggers Reviewed-by: Vladimir Oltean --- drivers/net/dsa/microchip/ksz9477_main.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477_main.c b/drivers/net/dsa/microchip/ksz9477_main.c index 42e647b67abd..681e752919ac 100644 --- a/drivers/net/dsa/microchip/ksz9477_main.c +++ b/drivers/net/dsa/microchip/ksz9477_main.c @@ -1343,19 +1343,12 @@ static void ksz9477_config_cpu_port(struct dsa_switch *ds) static int ksz9477_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; - int ret = 0; dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), dev->num_vlans, GFP_KERNEL); if (!dev->vlan_cache) return -ENOMEM; - ret = ksz9477_reset_switch(dev); - if (ret) { - dev_err(ds->dev, "failed to reset switch\n"); - return ret; - } - /* Required for port partitioning. */ ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, true); @@ -1533,10 +1526,16 @@ static const struct ksz_chip_data ksz9477_switch_chips[] = { static int ksz9477_switch_init(struct ksz_device *dev) { - int i; + int i, ret; dev->ds->ops = &ksz9477_switch_ops; + ret = ksz9477_reset_switch(dev); + if (ret) { + dev_err(dev->dev, "failed to reset switch\n"); + return ret; + } + for (i = 0; i < ARRAY_SIZE(ksz9477_switch_chips); i++) { const struct ksz_chip_data *chip = &ksz9477_switch_chips[i]; From patchwork Thu Dec 3 10:21:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11948337 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1D8EAC64E7B for ; Thu, 3 Dec 2020 10:26:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B20B022244 for ; Thu, 3 Dec 2020 10:26:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730265AbgLCKZz (ORCPT ); Thu, 3 Dec 2020 05:25:55 -0500 Received: from mailout01.rmx.de ([94.199.90.91]:34572 "EHLO mailout01.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730192AbgLCKZy (ORCPT ); Thu, 3 Dec 2020 05:25:54 -0500 Received: from kdin02.retarus.com (kdin02.dmz1.retloc [172.19.17.49]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout01.rmx.de (Postfix) with ESMTPS id 4CmsR81v57z2SX3Q; Thu, 3 Dec 2020 11:25:08 +0100 (CET) Received: from mta.arri.de (unknown [217.111.95.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by kdin02.retarus.com (Postfix) with ESMTPS id 4CmsQJ1jTZz2TTL5; Thu, 3 Dec 2020 11:24:24 +0100 (CET) Received: from N95HX1G2.wgnetz.xx (192.168.54.174) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.487.0; Thu, 3 Dec 2020 11:23:44 +0100 From: Christian Eggers To: Vladimir Oltean , Jakub Kicinski , Andrew Lunn , Richard Cochran , "Rob Herring" CC: Vivien Didelot , "David S . Miller" , Kurt Kanzenbach , George McCollister , Marek Vasut , Helmut Grohne , Paul Barker , Codrin Ciubotariu , Tristram Ha , Woojung Huh , Microchip Linux Driver Support , Christian Eggers , , , Subject: [PATCH net-next v5 4/9] net: dsa: microchip: ksz9477: basic interrupt support Date: Thu, 3 Dec 2020 11:21:12 +0100 Message-ID: <20201203102117.8995-5-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201203102117.8995-1-ceggers@arri.de> References: <20201203102117.8995-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.174] X-RMX-ID: 20201203-112424-NLSCxzcZDpWs-0@out02.hq X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Interrupts are required for TX time stamping. Probably they could also be used for PHY connection status. This patch only adds the basic infrastructure for interrupts, no interrupts are finally enabled nor handled. Signed-off-by: Christian Eggers Reviewed-by: Vladimir Oltean --- Changes in v4: -------------- - s/low active/active low/ - Reviewed-by: Vladimir Oltean drivers/net/dsa/microchip/ksz9477_i2c.c | 2 + drivers/net/dsa/microchip/ksz9477_main.c | 68 ++++++++++++++++++++++++ drivers/net/dsa/microchip/ksz9477_spi.c | 2 + drivers/net/dsa/microchip/ksz_common.h | 1 + 4 files changed, 73 insertions(+) diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index 4e053a25d077..4ed1f503044a 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -41,6 +41,8 @@ static int ksz9477_i2c_probe(struct i2c_client *i2c, if (i2c->dev.platform_data) dev->pdata = i2c->dev.platform_data; + dev->irq = i2c->irq; + ret = ksz9477_switch_register(dev); /* Main DSA driver may not be started yet. */ diff --git a/drivers/net/dsa/microchip/ksz9477_main.c b/drivers/net/dsa/microchip/ksz9477_main.c index 681e752919ac..f869041e3be0 100644 --- a/drivers/net/dsa/microchip/ksz9477_main.c +++ b/drivers/net/dsa/microchip/ksz9477_main.c @@ -5,9 +5,12 @@ * Copyright (C) 2017-2019 Microchip Technology Inc. */ +#include #include #include +#include #include +#include #include #include #include @@ -1524,6 +1527,54 @@ static const struct ksz_chip_data ksz9477_switch_chips[] = { }, }; +static irqreturn_t ksz9477_switch_irq_thread(int irq, void *dev_id) +{ + struct ksz_device *dev = dev_id; + u32 data; + int port; + int ret; + + /* Read global port interrupt status register */ + ret = ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data); + if (ret) + return IRQ_NONE; + + for (port = 0; port < dev->port_cnt; port++) { + u8 data8; + + if (!(data & BIT(port))) + continue; + + /* Read port interrupt status register */ + ret = ksz_read8(dev, PORT_CTRL_ADDR(port, REG_PORT_INT_STATUS), + &data8); + if (ret) + return IRQ_NONE; + + /* ToDo: Add specific handling of port interrupts */ + } + + return IRQ_NONE; +} + +static int ksz9477_enable_port_interrupts(struct ksz_device *dev, bool enable) +{ + u32 data, mask = GENMASK(dev->port_cnt - 1, 0); + int ret; + + ret = ksz_read32(dev, REG_SW_PORT_INT_MASK__4, &data); + if (ret) + return ret; + + /* bits in REG_SW_PORT_INT_MASK__4 are active low */ + if (enable) + data &= ~mask; + else + data |= mask; + + return ksz_write32(dev, REG_SW_PORT_INT_MASK__4, data); +} + static int ksz9477_switch_init(struct ksz_device *dev) { int i, ret; @@ -1579,12 +1630,29 @@ static int ksz9477_switch_init(struct ksz_device *dev) /* set the real number of ports */ dev->ds->num_ports = dev->port_cnt; + if (dev->irq > 0) { + ret = devm_request_threaded_irq(dev->dev, dev->irq, NULL, + ksz9477_switch_irq_thread, + IRQF_ONESHOT | IRQF_SHARED, + dev_name(dev->dev), + dev); + if (ret) { + dev_err(dev->dev, "failed to request IRQ.\n"); + return ret; + } + + ret = ksz9477_enable_port_interrupts(dev, true); + if (ret) + return ret; + } return 0; } static void ksz9477_switch_exit(struct ksz_device *dev) { + if (dev->irq > 0) + ksz9477_enable_port_interrupts(dev, false); ksz9477_reset_switch(dev); } diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c index 15bc11b3cda4..fc0ac9e2c56d 100644 --- a/drivers/net/dsa/microchip/ksz9477_spi.c +++ b/drivers/net/dsa/microchip/ksz9477_spi.c @@ -48,6 +48,8 @@ static int ksz9477_spi_probe(struct spi_device *spi) if (spi->dev.platform_data) dev->pdata = spi->dev.platform_data; + dev->irq = spi->irq; + /* setup spi */ spi->mode = SPI_MODE_3; ret = spi_setup(spi); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 720f22275c84..ac6914d361c8 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -55,6 +55,7 @@ struct ksz_device { struct device *dev; struct regmap *regmap[3]; + int irq; void *priv; From patchwork Thu Dec 3 10:21:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11948339 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BBE38C64E8A for ; Thu, 3 Dec 2020 10:27:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 53F922224A for ; Thu, 3 Dec 2020 10:27:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730273AbgLCK0x (ORCPT ); Thu, 3 Dec 2020 05:26:53 -0500 Received: from mailout04.rmx.de ([94.199.90.94]:56800 "EHLO mailout04.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728638AbgLCK0w (ORCPT ); Thu, 3 Dec 2020 05:26:52 -0500 Received: from kdin02.retarus.com (kdin02.dmz1.retloc [172.19.17.49]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout04.rmx.de (Postfix) with ESMTPS id 4CmsSF0cxhz3qfds; Thu, 3 Dec 2020 11:26:05 +0100 (CET) Received: from mta.arri.de (unknown [217.111.95.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by kdin02.retarus.com (Postfix) with ESMTPS id 4CmsR84zcGz2TTMJ; Thu, 3 Dec 2020 11:25:08 +0100 (CET) Received: from N95HX1G2.wgnetz.xx (192.168.54.174) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.487.0; Thu, 3 Dec 2020 11:24:19 +0100 From: Christian Eggers To: Vladimir Oltean , Jakub Kicinski , Andrew Lunn , Richard Cochran , "Rob Herring" CC: Vivien Didelot , "David S . Miller" , Kurt Kanzenbach , George McCollister , Marek Vasut , Helmut Grohne , Paul Barker , Codrin Ciubotariu , Tristram Ha , Woojung Huh , Microchip Linux Driver Support , Christian Eggers , , , Subject: [PATCH net-next v5 5/9] net: dsa: microchip: ksz9477: add Posix clock support for chip PTP clock Date: Thu, 3 Dec 2020 11:21:13 +0100 Message-ID: <20201203102117.8995-6-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201203102117.8995-1-ceggers@arri.de> References: <20201203102117.8995-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.174] X-RMX-ID: 20201203-112508-qPFcBsZSlwVY-0@out02.hq X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Implement routines (adjfine, adjtime, gettime and settime) for manipulating the chip's PTP clock. Signed-off-by: Christian Eggers Reviewed-by: Vladimir Oltean --- drivers/net/dsa/microchip/Kconfig | 8 + drivers/net/dsa/microchip/Makefile | 1 + drivers/net/dsa/microchip/ksz9477_i2c.c | 2 +- drivers/net/dsa/microchip/ksz9477_main.c | 17 ++ drivers/net/dsa/microchip/ksz9477_ptp.c | 308 +++++++++++++++++++++++ drivers/net/dsa/microchip/ksz9477_ptp.h | 27 ++ drivers/net/dsa/microchip/ksz9477_spi.c | 2 +- drivers/net/dsa/microchip/ksz_common.h | 8 + 8 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 drivers/net/dsa/microchip/ksz9477_ptp.c create mode 100644 drivers/net/dsa/microchip/ksz9477_ptp.h diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig index 4ec6a47b7f72..7a4e06bab238 100644 --- a/drivers/net/dsa/microchip/Kconfig +++ b/drivers/net/dsa/microchip/Kconfig @@ -24,6 +24,14 @@ config NET_DSA_MICROCHIP_KSZ9477_SPI help Select to enable support for registering switches configured through SPI. +config NET_DSA_MICROCHIP_KSZ9477_PTP + bool "PTP support for Microchip KSZ9477 series" + depends on NET_DSA_MICROCHIP_KSZ9477 + depends on PTP_1588_CLOCK + help + Say Y to enable PTP hardware timestamping on Microchip KSZ switch + chips that support it. + menuconfig NET_DSA_MICROCHIP_KSZ8795 tristate "Microchip KSZ8795 series switch support" depends on NET_DSA diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile index c5cc1d5dea06..35c4356bad65 100644 --- a/drivers/net/dsa/microchip/Makefile +++ b/drivers/net/dsa/microchip/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON) += ksz_common.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477) += ksz9477.o ksz9477-objs := ksz9477_main.o +ksz9477-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) += ksz9477_ptp.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C) += ksz9477_i2c.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI) += ksz9477_spi.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8795) += ksz8795.o diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index 4ed1f503044a..315eb24c444d 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -58,7 +58,7 @@ static int ksz9477_i2c_remove(struct i2c_client *i2c) { struct ksz_device *dev = i2c_get_clientdata(i2c); - ksz_switch_remove(dev); + ksz9477_switch_remove(dev); return 0; } diff --git a/drivers/net/dsa/microchip/ksz9477_main.c b/drivers/net/dsa/microchip/ksz9477_main.c index f869041e3be0..2cb33e9beb4c 100644 --- a/drivers/net/dsa/microchip/ksz9477_main.c +++ b/drivers/net/dsa/microchip/ksz9477_main.c @@ -19,6 +19,7 @@ #include "ksz9477_reg.h" #include "ksz_common.h" +#include "ksz9477_ptp.h" /* Used with variable features to indicate capabilities. */ #define GBIT_SUPPORT BIT(0) @@ -1695,10 +1696,26 @@ int ksz9477_switch_register(struct ksz_device *dev) phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Full_BIT); } + + ret = ksz9477_ptp_init(dev); + if (ret) + goto error_switch_unregister; + + return 0; + +error_switch_unregister: + ksz_switch_remove(dev); return ret; } EXPORT_SYMBOL(ksz9477_switch_register); +void ksz9477_switch_remove(struct ksz_device *dev) +{ + ksz9477_ptp_deinit(dev); + ksz_switch_remove(dev); +} +EXPORT_SYMBOL(ksz9477_switch_remove); + MODULE_AUTHOR("Woojung Huh "); MODULE_DESCRIPTION("Microchip KSZ9477 Series Switch DSA Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.c b/drivers/net/dsa/microchip/ksz9477_ptp.c new file mode 100644 index 000000000000..0ffc4504a290 --- /dev/null +++ b/drivers/net/dsa/microchip/ksz9477_ptp.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Microchip KSZ9477 switch driver PTP routines + * + * Author: Christian Eggers + * + * Copyright (c) 2020 ARRI Lighting + */ + +#include + +#include "ksz_common.h" +#include "ksz9477_reg.h" + +#include "ksz9477_ptp.h" + +#define KSZ_PTP_INC_NS 40 /* HW clock is incremented every 40 ns (by 40) */ +#define KSZ_PTP_SUBNS_BITS 32 /* Number of bits in sub-nanoseconds counter */ + +/* Posix clock support */ + +static int ksz9477_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); + u16 data16; + int ret; + + mutex_lock(&dev->ptp_mutex); + + if (scaled_ppm) { + s64 ppb, adj; + u32 data32; + + /* basic calculation: + * s32 ppb = scaled_ppm_to_ppb(scaled_ppm); + * s64 adj = div_s64(((s64)ppb * KSZ_PTP_INC_NS) << KSZ_PTP_SUBNS_BITS, + * NSEC_PER_SEC); + */ + + /* More precise calculation (avoids shifting out precision). + * See scaled_ppm_to_ppb() in ptp_clock.c for details. + */ + ppb = 1 + scaled_ppm; + ppb *= 125; + ppb *= KSZ_PTP_INC_NS; + ppb <<= KSZ_PTP_SUBNS_BITS - 13; + adj = div_s64(ppb, NSEC_PER_SEC); + + data32 = abs(adj); + data32 &= BIT_MASK(30) - 1; + if (adj >= 0) + data32 |= PTP_RATE_DIR; + + ret = ksz_write32(dev, REG_PTP_SUBNANOSEC_RATE, data32); + if (ret) + goto error_return; + } + + ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data16); + if (ret) + goto error_return; + + if (scaled_ppm) + data16 |= PTP_CLK_ADJ_ENABLE; + else + data16 &= ~PTP_CLK_ADJ_ENABLE; + + ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data16); + if (ret) + goto error_return; + +error_return: + mutex_unlock(&dev->ptp_mutex); + return ret; +} + +static int ksz9477_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); + s32 sec, nsec; + u16 data16; + int ret; + + mutex_lock(&dev->ptp_mutex); + + /* Do not use ns_to_timespec64(), both sec and nsec are subtracted by + * hardware. + */ + sec = div_s64_rem(delta, NSEC_PER_SEC, &nsec); + + ret = ksz_write32(dev, REG_PTP_RTC_NANOSEC, abs(nsec)); + if (ret) + goto error_return; + + /* Contradictory to the data sheet, seconds are also considered. */ + ret = ksz_write32(dev, REG_PTP_RTC_SEC, abs(sec)); + if (ret) + goto error_return; + + ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data16); + if (ret) + goto error_return; + + data16 |= PTP_STEP_ADJ; + if (delta < 0) + data16 &= ~PTP_STEP_DIR; /* 0: subtract */ + else + data16 |= PTP_STEP_DIR; /* 1: add */ + + ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data16); + if (ret) + goto error_return; + +error_return: + mutex_unlock(&dev->ptp_mutex); + return ret; +} + +static int _ksz9477_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts) +{ + u32 nanoseconds; + u32 seconds; + u16 data16; + u8 phase; + int ret; + + /* Copy current PTP clock into shadow registers */ + ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data16); + if (ret) + return ret; + + data16 |= PTP_READ_TIME; + + ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data16); + if (ret) + return ret; + + /* Read from shadow registers */ + ret = ksz_read8(dev, REG_PTP_RTC_SUB_NANOSEC__2, &phase); + if (ret) + return ret; + ret = ksz_read32(dev, REG_PTP_RTC_NANOSEC, &nanoseconds); + if (ret) + return ret; + ret = ksz_read32(dev, REG_PTP_RTC_SEC, &seconds); + if (ret) + return ret; + + ts->tv_sec = seconds; + ts->tv_nsec = nanoseconds + phase * 8; + + return 0; +} + +static int ksz9477_ptp_gettime(struct ptp_clock_info *ptp, + struct timespec64 *ts) +{ + struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); + int ret; + + mutex_lock(&dev->ptp_mutex); + ret = _ksz9477_ptp_gettime(dev, ts); + mutex_unlock(&dev->ptp_mutex); + + return ret; +} + +static int ksz9477_ptp_settime(struct ptp_clock_info *ptp, + struct timespec64 const *ts) +{ + struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); + u16 data16; + int ret; + + mutex_lock(&dev->ptp_mutex); + + /* Write to shadow registers */ + + /* clock phase */ + ret = ksz_read16(dev, REG_PTP_RTC_SUB_NANOSEC__2, &data16); + if (ret) + goto error_return; + + data16 &= ~PTP_RTC_SUB_NANOSEC_M; + + ret = ksz_write16(dev, REG_PTP_RTC_SUB_NANOSEC__2, data16); + if (ret) + goto error_return; + + /* nanoseconds */ + ret = ksz_write32(dev, REG_PTP_RTC_NANOSEC, ts->tv_nsec); + if (ret) + goto error_return; + + /* seconds */ + ret = ksz_write32(dev, REG_PTP_RTC_SEC, ts->tv_sec); + if (ret) + goto error_return; + + /* Load PTP clock from shadow registers */ + ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data16); + if (ret) + goto error_return; + + data16 |= PTP_LOAD_TIME; + + ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data16); + if (ret) + goto error_return; + +error_return: + mutex_unlock(&dev->ptp_mutex); + return ret; +} + +static int ksz9477_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *req, int on) +{ + return -EOPNOTSUPP; +} + +static int ksz9477_ptp_start_clock(struct ksz_device *dev) +{ + u16 data; + int ret; + + ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data); + if (ret) + return ret; + + /* Perform PTP clock reset */ + data |= PTP_CLK_RESET; + ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data); + if (ret) + return ret; + data &= ~PTP_CLK_RESET; + + /* Enable PTP clock */ + data |= PTP_CLK_ENABLE; + ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_ptp_stop_clock(struct ksz_device *dev) +{ + u16 data; + int ret; + + ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data); + if (ret) + return ret; + + /* Disable PTP clock */ + data &= ~PTP_CLK_ENABLE; + return ksz_write16(dev, REG_PTP_CLK_CTRL, data); +} + +int ksz9477_ptp_init(struct ksz_device *dev) +{ + int ret; + + mutex_init(&dev->ptp_mutex); + + /* PTP clock properties */ + + dev->ptp_caps.owner = THIS_MODULE; + snprintf(dev->ptp_caps.name, sizeof(dev->ptp_caps.name), + dev_name(dev->dev)); + + /* Sub-nanoseconds-adj,max * sub-nanoseconds / 40ns * 1ns + * = (2^30-1) * (2 ^ 32) / 40 ns * 1 ns = 6249999 + */ + dev->ptp_caps.max_adj = 6249999; + dev->ptp_caps.n_alarm = 0; + dev->ptp_caps.n_ext_ts = 0; /* currently not implemented */ + dev->ptp_caps.n_per_out = 0; + dev->ptp_caps.pps = 0; + dev->ptp_caps.adjfine = ksz9477_ptp_adjfine; + dev->ptp_caps.adjtime = ksz9477_ptp_adjtime; + dev->ptp_caps.gettime64 = ksz9477_ptp_gettime; + dev->ptp_caps.settime64 = ksz9477_ptp_settime; + dev->ptp_caps.enable = ksz9477_ptp_enable; + + /* Start hardware counter (will overflow after 136 years) */ + ret = ksz9477_ptp_start_clock(dev); + if (ret) + return ret; + + dev->ptp_clock = ptp_clock_register(&dev->ptp_caps, dev->dev); + if (IS_ERR(dev->ptp_clock)) { + ret = PTR_ERR(dev->ptp_clock); + goto error_stop_clock; + } + + return 0; + +error_stop_clock: + ksz9477_ptp_stop_clock(dev); + return ret; +} + +void ksz9477_ptp_deinit(struct ksz_device *dev) +{ + ptp_clock_unregister(dev->ptp_clock); + ksz9477_ptp_stop_clock(dev); +} diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.h b/drivers/net/dsa/microchip/ksz9477_ptp.h new file mode 100644 index 000000000000..0076538419fa --- /dev/null +++ b/drivers/net/dsa/microchip/ksz9477_ptp.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Microchip KSZ9477 switch driver PTP routines + * + * Author: Christian Eggers + * + * Copyright (c) 2020 ARRI Lighting + */ + +#ifndef DRIVERS_NET_DSA_MICROCHIP_KSZ9477_PTP_H_ +#define DRIVERS_NET_DSA_MICROCHIP_KSZ9477_PTP_H_ + +#include "ksz_common.h" + +#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) + +int ksz9477_ptp_init(struct ksz_device *dev); +void ksz9477_ptp_deinit(struct ksz_device *dev); + +#else + +static inline int ksz9477_ptp_init(struct ksz_device *dev) { return 0; } +static inline void ksz9477_ptp_deinit(struct ksz_device *dev) {} + +#endif + +#endif /* DRIVERS_NET_DSA_MICROCHIP_KSZ9477_PTP_H_ */ diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c index fc0ac9e2c56d..8cd825a02bba 100644 --- a/drivers/net/dsa/microchip/ksz9477_spi.c +++ b/drivers/net/dsa/microchip/ksz9477_spi.c @@ -72,7 +72,7 @@ static int ksz9477_spi_remove(struct spi_device *spi) struct ksz_device *dev = spi_get_drvdata(spi); if (dev) - ksz_switch_remove(dev); + ksz9477_switch_remove(dev); return 0; } diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index ac6914d361c8..f70a45c591d8 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -90,6 +91,12 @@ struct ksz_device { u32 overrides; /* chip functions set by user */ u16 host_mask; u16 port_mask; + +#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_caps; + struct mutex ptp_mutex; /* protects PTP related hardware */ +#endif }; struct alu_struct { @@ -145,6 +152,7 @@ void ksz_switch_remove(struct ksz_device *dev); int ksz8795_switch_register(struct ksz_device *dev); int ksz9477_switch_register(struct ksz_device *dev); +void ksz9477_switch_remove(struct ksz_device *dev); void ksz_update_port_member(struct ksz_device *dev, int port); void ksz_init_mib_timer(struct ksz_device *dev); From patchwork Thu Dec 3 10:21:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11948341 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C4DBFC83012 for ; Thu, 3 Dec 2020 10:27:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6F18821D1A for ; Thu, 3 Dec 2020 10:27:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388562AbgLCK1K (ORCPT ); Thu, 3 Dec 2020 05:27:10 -0500 Received: from mailout01.rmx.de ([94.199.90.91]:36903 "EHLO mailout01.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730157AbgLCK1K (ORCPT ); Thu, 3 Dec 2020 05:27:10 -0500 Received: from kdin02.retarus.com (kdin02.dmz1.retloc [172.19.17.49]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout01.rmx.de (Postfix) with ESMTPS id 4CmsSc1zMyz2SVJw; Thu, 3 Dec 2020 11:26:24 +0100 (CET) Received: from mta.arri.de (unknown [217.111.95.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by kdin02.retarus.com (Postfix) with ESMTPS id 4CmsRx5GH9z2TSDZ; Thu, 3 Dec 2020 11:25:49 +0100 (CET) Received: from N95HX1G2.wgnetz.xx (192.168.54.174) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.487.0; Thu, 3 Dec 2020 11:24:54 +0100 From: Christian Eggers To: Vladimir Oltean , Jakub Kicinski , Andrew Lunn , Richard Cochran , "Rob Herring" CC: Vivien Didelot , "David S . Miller" , Kurt Kanzenbach , George McCollister , Marek Vasut , Helmut Grohne , Paul Barker , Codrin Ciubotariu , Tristram Ha , Woojung Huh , Microchip Linux Driver Support , Christian Eggers , , , Subject: [PATCH net-next v5 6/9] net: ptp: add helper for one-step P2P clocks Date: Thu, 3 Dec 2020 11:21:14 +0100 Message-ID: <20201203102117.8995-7-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201203102117.8995-1-ceggers@arri.de> References: <20201203102117.8995-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.174] X-RMX-ID: 20201203-112549-CBxkhgyqMZSR-0@out02.hq X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org For P2P delay measurement, the ingress time stamp of the PDelay_Req is required for the correction field of the PDelay_Resp. The application echoes back the correction field of the PDelay_Req when sending the PDelay_Resp. Some hardware (like the ZHAW InES PTP time stamping IP core) subtracts the ingress timestamp autonomously from the correction field, so that the hardware only needs to add the egress timestamp on tx. Other hardware (like the Microchip KSZ9563) reports the ingress time stamp via an interrupt and requires that the software provides this time stamp via tail-tag on tx. In order to avoid introducing a further application interface for this, the driver can simply emulate the behavior of the InES device and subtract the ingress time stamp in software from the correction field. On egress, the correction field can either be kept as it is (and the time stamp field in the tail-tag is set to zero) or move the value from the correction field back to the tail-tag. Changing the correction field requires updating the UDP checksum (if UDP is used as transport). Signed-off-by: Christian Eggers --- include/linux/ptp_classify.h | 73 ++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h index cc0da0b134a4..f19f2f6a9475 100644 --- a/include/linux/ptp_classify.h +++ b/include/linux/ptp_classify.h @@ -10,8 +10,12 @@ #ifndef _PTP_CLASSIFY_H_ #define _PTP_CLASSIFY_H_ +#include #include +#include #include +#include +#include #define PTP_CLASS_NONE 0x00 /* not a PTP event message */ #define PTP_CLASS_V1 0x01 /* protocol version 1 */ @@ -123,6 +127,67 @@ static inline u8 ptp_get_msgtype(const struct ptp_header *hdr, return msgtype; } +/** + * ptp_check_diff8 - Computes new checksum (when altering a 64-bit field) + * @old: old field value + * @new: new field value + * @oldsum: previous checksum + * + * This function can be used to calculate a new checksum when only a single + * field is changed. Similar as ip_vs_check_diff*() in ip_vs.h. + * + * Return: Updated checksum + */ +static inline __wsum ptp_check_diff8(__be64 old, __be64 new, __wsum oldsum) +{ + __be64 diff[2] = { ~old, new }; + + return csum_partial(diff, sizeof(diff), oldsum); +} + +/** + * ptp_header_update_correction - Update PTP header's correction field + * @skb: packet buffer + * @type: type of the packet (see ptp_classify_raw()) + * @hdr: ptp header + * @correction: new correction value + * + * This updates the correction field of a PTP header and updates the UDP + * checksum (if UDP is used as transport). It is needed for hardware capable of + * one-step P2P that does not already modify the correction field of Pdelay_Req + * event messages on ingress. + */ +static inline +void ptp_header_update_correction(struct sk_buff *skb, unsigned int type, + struct ptp_header *hdr, s64 correction) +{ + __be64 correction_old; + struct udphdr *uhdr; + + /* previous correction value is required for checksum update. */ + memcpy(&correction_old, &hdr->correction, sizeof(correction_old)); + + /* write new correction value */ + put_unaligned_be64((u64)correction, &hdr->correction); + + switch (type & PTP_CLASS_PMASK) { + case PTP_CLASS_IPV4: + case PTP_CLASS_IPV6: + /* locate udp header */ + uhdr = (struct udphdr *)((char *)hdr - sizeof(struct udphdr)); + break; + default: + return; + } + + /* update checksum */ + uhdr->check = csum_fold(ptp_check_diff8(correction_old, + hdr->correction, + ~csum_unfold(uhdr->check))); + if (!uhdr->check) + uhdr->check = CSUM_MANGLED_0; +} + void __init ptp_classifier_init(void); #else static inline void ptp_classifier_init(void) @@ -145,5 +210,13 @@ static inline u8 ptp_get_msgtype(const struct ptp_header *hdr, */ return PTP_MSGTYPE_SYNC; } + +static inline +void ptp_onestep_p2p_move_t2_to_correction(struct sk_buff *skb, + unsigned int type, + struct ptp_header *hdr, + ktime_t t2) +{ +} #endif #endif /* _PTP_CLASSIFY_H_ */ From patchwork Thu Dec 3 10:21:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11948343 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BCCC3C64E7B for ; Thu, 3 Dec 2020 10:28:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 74FF021D1A for ; Thu, 3 Dec 2020 10:28:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387927AbgLCK1x (ORCPT ); Thu, 3 Dec 2020 05:27:53 -0500 Received: from mailout11.rmx.de ([94.199.88.76]:60411 "EHLO mailout11.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727431AbgLCK1w (ORCPT ); Thu, 3 Dec 2020 05:27:52 -0500 Received: from kdin02.retarus.com (kdin02.dmz1.retloc [172.19.17.49]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout11.rmx.de (Postfix) with ESMTPS id 4CmsTQ29tWz3yvS; Thu, 3 Dec 2020 11:27:06 +0100 (CET) Received: from mta.arri.de (unknown [217.111.95.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by kdin02.retarus.com (Postfix) with ESMTPS id 4CmsSh58npz2TTBH; Thu, 3 Dec 2020 11:26:28 +0100 (CET) Received: from N95HX1G2.wgnetz.xx (192.168.54.174) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.487.0; Thu, 3 Dec 2020 11:25:24 +0100 From: Christian Eggers To: Vladimir Oltean , Jakub Kicinski , Andrew Lunn , Richard Cochran , "Rob Herring" CC: Vivien Didelot , "David S . Miller" , Kurt Kanzenbach , George McCollister , Marek Vasut , Helmut Grohne , Paul Barker , Codrin Ciubotariu , Tristram Ha , Woojung Huh , Microchip Linux Driver Support , Christian Eggers , , , Subject: [PATCH net-next v5 7/9] net: dsa: microchip: ksz9477: initial hardware time stamping support Date: Thu, 3 Dec 2020 11:21:15 +0100 Message-ID: <20201203102117.8995-8-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201203102117.8995-1-ceggers@arri.de> References: <20201203102117.8995-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.174] X-RMX-ID: 20201203-112628-yPyzlRqhmtFB-0@out02.hq X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Add control routines required for TX hardware time stamping. The KSZ9563 only supports one step time stamping (HWTSTAMP_TX_ONESTEP_P2P), which requires linuxptp-2.0 or later. Currently, only P2P delay measurement is supported. See patchwork discussion and comments in ksz9477_ptp_init() for details: https://patchwork.ozlabs.org/project/netdev/patch/20201019172435.4416-8-ceggers@arri.de/ Signed-off-by: Christian Eggers Reviewed-by: Vladimir Oltean --- Changes in v4: -------------- - Remove useless case statement - Reviewed-by: Vladimir Oltean drivers/net/dsa/microchip/ksz9477_main.c | 6 + drivers/net/dsa/microchip/ksz9477_ptp.c | 186 +++++++++++++++++++++++ drivers/net/dsa/microchip/ksz9477_ptp.h | 21 +++ drivers/net/dsa/microchip/ksz_common.h | 4 + 4 files changed, 217 insertions(+) diff --git a/drivers/net/dsa/microchip/ksz9477_main.c b/drivers/net/dsa/microchip/ksz9477_main.c index 2cb33e9beb4c..0ade40bf27c7 100644 --- a/drivers/net/dsa/microchip/ksz9477_main.c +++ b/drivers/net/dsa/microchip/ksz9477_main.c @@ -1387,6 +1387,7 @@ static const struct dsa_switch_ops ksz9477_switch_ops = { .phy_read = ksz9477_phy_read16, .phy_write = ksz9477_phy_write16, .phylink_mac_link_down = ksz_mac_link_down, + .get_ts_info = ksz9477_ptp_get_ts_info, .port_enable = ksz_enable_port, .get_strings = ksz9477_get_strings, .get_ethtool_stats = ksz_get_ethtool_stats, @@ -1407,6 +1408,11 @@ static const struct dsa_switch_ops ksz9477_switch_ops = { .port_mdb_del = ksz9477_port_mdb_del, .port_mirror_add = ksz9477_port_mirror_add, .port_mirror_del = ksz9477_port_mirror_del, + .port_hwtstamp_get = ksz9477_ptp_port_hwtstamp_get, + .port_hwtstamp_set = ksz9477_ptp_port_hwtstamp_set, + .port_txtstamp = NULL, + /* never defer rx delivery, tstamping is done via tail tagging */ + .port_rxtstamp = NULL, }; static u32 ksz9477_get_port_addr(int port, int offset) diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.c b/drivers/net/dsa/microchip/ksz9477_ptp.c index 0ffc4504a290..a1ca1923ec0c 100644 --- a/drivers/net/dsa/microchip/ksz9477_ptp.c +++ b/drivers/net/dsa/microchip/ksz9477_ptp.c @@ -218,6 +218,18 @@ static int ksz9477_ptp_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } +static long ksz9477_ptp_do_aux_work(struct ptp_clock_info *ptp) +{ + struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); + struct timespec64 ts; + + mutex_lock(&dev->ptp_mutex); + _ksz9477_ptp_gettime(dev, &ts); + mutex_unlock(&dev->ptp_mutex); + + return HZ; /* reschedule in 1 second */ +} + static int ksz9477_ptp_start_clock(struct ksz_device *dev) { u16 data; @@ -257,6 +269,54 @@ static int ksz9477_ptp_stop_clock(struct ksz_device *dev) return ksz_write16(dev, REG_PTP_CLK_CTRL, data); } +/* device attributes */ + +enum ksz9477_ptp_tcmode { + KSZ9477_PTP_TCMODE_E2E, + KSZ9477_PTP_TCMODE_P2P, +}; + +static int ksz9477_ptp_tcmode_set(struct ksz_device *dev, + enum ksz9477_ptp_tcmode tcmode) +{ + u16 data; + int ret; + + ret = ksz_read16(dev, REG_PTP_MSG_CONF1, &data); + if (ret) + return ret; + + if (tcmode == KSZ9477_PTP_TCMODE_P2P) + data |= PTP_TC_P2P; + else + data &= ~PTP_TC_P2P; + + return ksz_write16(dev, REG_PTP_MSG_CONF1, data); +} + +enum ksz9477_ptp_ocmode { + KSZ9477_PTP_OCMODE_SLAVE, + KSZ9477_PTP_OCMODE_MASTER, +}; + +static int ksz9477_ptp_ocmode_set(struct ksz_device *dev, + enum ksz9477_ptp_ocmode ocmode) +{ + u16 data; + int ret; + + ret = ksz_read16(dev, REG_PTP_MSG_CONF1, &data); + if (ret) + return ret; + + if (ocmode == KSZ9477_PTP_OCMODE_MASTER) + data |= PTP_MASTER; + else + data &= ~PTP_MASTER; + + return ksz_write16(dev, REG_PTP_MSG_CONF1, data); +} + int ksz9477_ptp_init(struct ksz_device *dev) { int ret; @@ -282,6 +342,7 @@ int ksz9477_ptp_init(struct ksz_device *dev) dev->ptp_caps.gettime64 = ksz9477_ptp_gettime; dev->ptp_caps.settime64 = ksz9477_ptp_settime; dev->ptp_caps.enable = ksz9477_ptp_enable; + dev->ptp_caps.do_aux_work = ksz9477_ptp_do_aux_work; /* Start hardware counter (will overflow after 136 years) */ ret = ksz9477_ptp_start_clock(dev); @@ -294,8 +355,31 @@ int ksz9477_ptp_init(struct ksz_device *dev) goto error_stop_clock; } + /* Currently, only P2P delay measurement is supported. Setting ocmode + * to slave will work independently of actually being master or slave. + * For E2E delay measurement, switching between master and slave would + * be required, as the KSZ devices filters out PTP messages depending on + * the ocmode setting: + * - in slave mode, DelayReq messages are filtered out + * - in master mode, Sync messages are filtered out + * Currently (and probably also in future) there is no interface in the + * kernel which allows switching between master and slave mode. For + * this reason, E2E cannot be supported. See patchwork for full + * discussion: + * https://patchwork.ozlabs.org/project/netdev/patch/20201019172435.4416-8-ceggers@arri.de/ + */ + ksz9477_ptp_tcmode_set(dev, KSZ9477_PTP_TCMODE_P2P); + ksz9477_ptp_ocmode_set(dev, KSZ9477_PTP_OCMODE_SLAVE); + + /* Schedule cyclic call of ksz_ptp_do_aux_work() */ + ret = ptp_schedule_worker(dev->ptp_clock, 0); + if (ret) + goto error_unregister_clock; + return 0; +error_unregister_clock: + ptp_clock_unregister(dev->ptp_clock); error_stop_clock: ksz9477_ptp_stop_clock(dev); return ret; @@ -306,3 +390,105 @@ void ksz9477_ptp_deinit(struct ksz_device *dev) ptp_clock_unregister(dev->ptp_clock); ksz9477_ptp_stop_clock(dev); } + +/* DSA PTP operations */ + +int ksz9477_ptp_get_ts_info(struct dsa_switch *ds, int port, + struct ethtool_ts_info *ts) +{ + struct ksz_device *dev = ds->priv; + + ts->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + ts->phc_index = ptp_clock_index(dev->ptp_clock); + + ts->tx_types = BIT(HWTSTAMP_TX_OFF) | + BIT(HWTSTAMP_TX_ONESTEP_P2P); + + ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | + BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_EVENT); + + return 0; +} + +static int ksz9477_set_hwtstamp_config(struct ksz_device *dev, int port, + struct hwtstamp_config *config) +{ + struct ksz_port *prt = &dev->ports[port]; + + /* reserved for future extensions */ + if (config->flags) + return -EINVAL; + + switch (config->tx_type) { + case HWTSTAMP_TX_OFF: + prt->hwts_tx_en = false; + break; + case HWTSTAMP_TX_ONESTEP_P2P: + prt->hwts_tx_en = true; + break; + default: + return -ERANGE; + } + + switch (config->rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; + break; + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + break; + default: + config->rx_filter = HWTSTAMP_FILTER_NONE; + return -ERANGE; + } + + return 0; +} + +int ksz9477_ptp_port_hwtstamp_get(struct dsa_switch *ds, int port, + struct ifreq *ifr) +{ + struct ksz_device *dev = ds->priv; + unsigned long bytes_copied; + + bytes_copied = copy_to_user(ifr->ifr_data, + &dev->ports[port].tstamp_config, + sizeof(dev->ports[port].tstamp_config)); + + return bytes_copied ? -EFAULT : 0; +} + +int ksz9477_ptp_port_hwtstamp_set(struct dsa_switch *ds, int port, + struct ifreq *ifr) +{ + struct ksz_device *dev = ds->priv; + struct hwtstamp_config config; + unsigned long bytes_copied; + int err; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + err = ksz9477_set_hwtstamp_config(dev, port, &config); + if (err) + return err; + + /* Save the chosen configuration to be returned later. */ + memcpy(&dev->ports[port].tstamp_config, &config, sizeof(config)); + bytes_copied = copy_to_user(ifr->ifr_data, &config, sizeof(config)); + + return bytes_copied ? -EFAULT : 0; +} diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.h b/drivers/net/dsa/microchip/ksz9477_ptp.h index 0076538419fa..b599401812ae 100644 --- a/drivers/net/dsa/microchip/ksz9477_ptp.h +++ b/drivers/net/dsa/microchip/ksz9477_ptp.h @@ -10,6 +10,8 @@ #ifndef DRIVERS_NET_DSA_MICROCHIP_KSZ9477_PTP_H_ #define DRIVERS_NET_DSA_MICROCHIP_KSZ9477_PTP_H_ +#include + #include "ksz_common.h" #if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) @@ -17,11 +19,30 @@ int ksz9477_ptp_init(struct ksz_device *dev); void ksz9477_ptp_deinit(struct ksz_device *dev); +int ksz9477_ptp_get_ts_info(struct dsa_switch *ds, int port, + struct ethtool_ts_info *ts); +int ksz9477_ptp_port_hwtstamp_get(struct dsa_switch *ds, int port, + struct ifreq *ifr); +int ksz9477_ptp_port_hwtstamp_set(struct dsa_switch *ds, int port, + struct ifreq *ifr); + #else static inline int ksz9477_ptp_init(struct ksz_device *dev) { return 0; } static inline void ksz9477_ptp_deinit(struct ksz_device *dev) {} +static inline int ksz9477_ptp_get_ts_info(struct dsa_switch *ds, int port, + struct ethtool_ts_info *ts) +{ return -EOPNOTSUPP; } + +static inline int ksz9477_ptp_port_hwtstamp_get(struct dsa_switch *ds, int port, + struct ifreq *ifr) +{ return -EOPNOTSUPP; } + +static inline int ksz9477_ptp_port_hwtstamp_set(struct dsa_switch *ds, int port, + struct ifreq *ifr) +{ return -EOPNOTSUPP; } + #endif #endif /* DRIVERS_NET_DSA_MICROCHIP_KSZ9477_PTP_H_ */ diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index f70a45c591d8..e1235d854977 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -41,6 +41,10 @@ struct ksz_port { struct ksz_port_mib mib; phy_interface_t interface; +#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) + struct hwtstamp_config tstamp_config; + bool hwts_tx_en; +#endif }; struct ksz_device { From patchwork Thu Dec 3 10:21:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11948345 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,HK_SCAM,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BE2BEC64E7B for ; Thu, 3 Dec 2020 10:28:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6196C22245 for ; Thu, 3 Dec 2020 10:28:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388689AbgLCK2k (ORCPT ); Thu, 3 Dec 2020 05:28:40 -0500 Received: from mailout07.rmx.de ([94.199.90.95]:58146 "EHLO mailout07.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727431AbgLCK2j (ORCPT ); Thu, 3 Dec 2020 05:28:39 -0500 Received: from kdin02.retarus.com (kdin02.dmz1.retloc [172.19.17.49]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout07.rmx.de (Postfix) with ESMTPS id 4CmsVD71gMzBvJV; Thu, 3 Dec 2020 11:27:48 +0100 (CET) Received: from mta.arri.de (unknown [217.111.95.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by kdin02.retarus.com (Postfix) with ESMTPS id 4CmsTQ22KDz2TT8N; Thu, 3 Dec 2020 11:27:06 +0100 (CET) Received: from N95HX1G2.wgnetz.xx (192.168.54.174) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.487.0; Thu, 3 Dec 2020 11:25:59 +0100 From: Christian Eggers To: Vladimir Oltean , Jakub Kicinski , Andrew Lunn , Richard Cochran , "Rob Herring" CC: Vivien Didelot , "David S . Miller" , Kurt Kanzenbach , George McCollister , Marek Vasut , Helmut Grohne , Paul Barker , Codrin Ciubotariu , Tristram Ha , Woojung Huh , Microchip Linux Driver Support , Christian Eggers , , , Subject: [PATCH net-next v5 8/9] net: dsa: microchip: ksz9477: remaining hardware time stamping support Date: Thu, 3 Dec 2020 11:21:16 +0100 Message-ID: <20201203102117.8995-9-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201203102117.8995-1-ceggers@arri.de> References: <20201203102117.8995-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.174] X-RMX-ID: 20201203-112706-nxPrhMsspQQF-0@out02.hq X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Add data path routines required for TX hardware time stamping. PTP mode is enabled depending on the filter setup (changes tail tag). TX time stamps are reported via an interrupt / device registers whilst RX time stamps are reported via an additional tail tag. One step TX time stamping of PDelay_Resp requires the RX time stamp from the associated PDelay_Req message. The user space PTP stack assumes that the RX time stamp has already been subtracted from the PDelay_Req correction field (as done by the ZHAW InES PTP time stamping core). It will echo back the value of the correction field in the PDelay_Resp message. In order to be compatible to this already established interface, the KSZ9563 code emulates this behavior. When processing the PDelay_Resp message, the time stamp is moved back from the correction field to the tail tag, as the hardware generates an invalid UDP checksum if this field is negative. Of course, the UDP checksums (if any) have to be corrected after this (for both directions). Everything has been tested on a Microchip KSZ9563 switch. Signed-off-by: Christian Eggers --- Changes in v5: -------------- - Fix compile error reported by kernel test robot (NET_DSA_TAG_KSZ must select NET_PTP_CLASSIFY) Changes in v4: -------------- - s/low active/active low/ - 80 chars per line - Use IEEE 802.1AS mode (to suppress forwarding of PDelay messages) - Enable/disable hardware timestaping at runtime (port_hwtstamp_set) - Use mutex in port_hwtstamp_set - Don't use port specific struct hwtstamp_config - removed #ifdefs from tag_ksz.c - Set port's tx_latency and rx_latency to 0 - added include/linux/dsa/ksz_common.h to MAINTAINERS On Saturday, 21 November 2020, 02:26:11 CET, Vladimir Oltean wrote: > If you don't like the #ifdef's, I am not in love with them either. But > maybe Christian is just optimizing too aggressively, and doesn't actually > need to put those #ifdef's there and provide stub implementations, but > could actually just leave the ksz9477_rcv_timestamp and ksz9477_xmit_timestamp > always compiled-in, and "dead at runtime" in the case there is no PTP. I removed the #ifdefs. > [...] > The thing is, ptp4l already has ingressLatency and egressLatency > settings, and I would not be surprised if those config options would get > extended to cover values at multiple link speeds. > > In the general case, the ksz9477 MAC could be attached to any external > PHY, having its own propagation delay characteristics, or any number of > other things that cause clock domain crossings. I'm not sure how feasible > it is for the kernel to abstract this away completely, and adjust > timestamps automatically based on any and all combinations of MAC and > PHY. Maybe this is just wishful thinking. > > Oh, and by the way, Christian, I'm not even sure if you aren't in fact > just beating around the bush with these tstamp_rx_latency_ns and > tstamp_tx_latency_ns values? I mean, the switch adds the latency value > to the timestamps. And you, from the driver, read the value of the > register, so you can subtract the value from the timestamp, to > compensate for its correction. So, all in all, there is no net latency > compensation seen by the outside world?! If that is the case, can't you > just set the latency registers to zero, do your compensation from the > application stack and call it a day? At first I thought that I have to move these values to ptp4l.conf. But after setting the hardware registers to zero, it turned out, that I also have to use zero values in ptp4l.conf. So you are right. On Monday, 23 November 2020, 13:09:38 CET, Vladimir Oltean wrote: > On Mon, Nov 23, 2020 at 12:32:33PM +0100, Christian Eggers wrote: > > please let me know, how I shall finally implement this. Enabling the PTP mode > > on the switch and sending the extra 4 byte tail on tx must be done in sync. > > Currently, both simply depends on the PTP define. > > I, too, would prefer that the reconfiguration is done at ioctl time. > Distributions typically enable whatever kernel config options they can. > However, for users, the behavior should not change. Therefore the tail > tag should remain small even though the PTP kernel config option is > enabled, as long as hardware timestamping has not been explicitly > enabled. I moved this to port_hwtstamp_set. But I am not sure whether enabling PTP mode should depend on tx_type or rx_filter. > [...] > When forwarding what packet? What profile are you testing with? > What commands do you run? > A P2P capable switch should not forward Peer delay messages. With the 802.1AS settings, no SYNC/Announce messages are forwarded anymore. Peer delay messages have never been forwarded. MAINTAINERS | 1 + drivers/net/dsa/microchip/ksz9477_main.c | 12 +- drivers/net/dsa/microchip/ksz9477_ptp.c | 391 ++++++++++++++++++++++- drivers/net/dsa/microchip/ksz9477_ptp.h | 14 + drivers/net/dsa/microchip/ksz_common.h | 9 +- include/linux/dsa/ksz_common.h | 70 ++++ net/dsa/Kconfig | 1 + net/dsa/tag_ksz.c | 205 +++++++++++- 8 files changed, 679 insertions(+), 24 deletions(-) create mode 100644 include/linux/dsa/ksz_common.h diff --git a/MAINTAINERS b/MAINTAINERS index 331fa2594f00..7ed88627c8f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11491,6 +11491,7 @@ L: netdev@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml F: drivers/net/dsa/microchip/* +F: include/linux/dsa/ksz_common.h F: include/linux/platform_data/microchip-ksz.h F: net/dsa/tag_ksz.c diff --git a/drivers/net/dsa/microchip/ksz9477_main.c b/drivers/net/dsa/microchip/ksz9477_main.c index 0ade40bf27c7..a7ec86873277 100644 --- a/drivers/net/dsa/microchip/ksz9477_main.c +++ b/drivers/net/dsa/microchip/ksz9477_main.c @@ -1410,7 +1410,7 @@ static const struct dsa_switch_ops ksz9477_switch_ops = { .port_mirror_del = ksz9477_port_mirror_del, .port_hwtstamp_get = ksz9477_ptp_port_hwtstamp_get, .port_hwtstamp_set = ksz9477_ptp_port_hwtstamp_set, - .port_txtstamp = NULL, + .port_txtstamp = ksz9477_ptp_port_txtstamp, /* never defer rx delivery, tstamping is done via tail tagging */ .port_rxtstamp = NULL, }; @@ -1537,6 +1537,7 @@ static const struct ksz_chip_data ksz9477_switch_chips[] = { static irqreturn_t ksz9477_switch_irq_thread(int irq, void *dev_id) { struct ksz_device *dev = dev_id; + irqreturn_t result = IRQ_NONE; u32 data; int port; int ret; @@ -1556,12 +1557,15 @@ static irqreturn_t ksz9477_switch_irq_thread(int irq, void *dev_id) ret = ksz_read8(dev, PORT_CTRL_ADDR(port, REG_PORT_INT_STATUS), &data8); if (ret) - return IRQ_NONE; + return result; - /* ToDo: Add specific handling of port interrupts */ + if (data8 & PORT_PTP_INT) { + if (ksz9477_ptp_port_interrupt(dev, port) != IRQ_NONE) + result = IRQ_HANDLED; + } } - return IRQ_NONE; + return result; } static int ksz9477_enable_port_interrupts(struct ksz_device *dev, bool enable) diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.c b/drivers/net/dsa/microchip/ksz9477_ptp.c index a1ca1923ec0c..e68c9fc8e679 100644 --- a/drivers/net/dsa/microchip/ksz9477_ptp.c +++ b/drivers/net/dsa/microchip/ksz9477_ptp.c @@ -6,7 +6,10 @@ * Copyright (c) 2020 ARRI Lighting */ +#include +#include #include +#include #include "ksz_common.h" #include "ksz9477_reg.h" @@ -76,6 +79,8 @@ static int ksz9477_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) static int ksz9477_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); + struct ksz_device_ptp_shared *ptp_shared = &dev->ptp_shared; + struct timespec64 delta64 = ns_to_timespec64(delta); s32 sec, nsec; u16 data16; int ret; @@ -110,6 +115,11 @@ static int ksz9477_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) if (ret) goto error_return; + spin_lock_bh(&ptp_shared->ptp_clock_lock); + ptp_shared->ptp_clock_time = timespec64_add(ptp_shared->ptp_clock_time, + delta64); + spin_unlock_bh(&ptp_shared->ptp_clock_lock); + error_return: mutex_unlock(&dev->ptp_mutex); return ret; @@ -168,6 +178,7 @@ static int ksz9477_ptp_settime(struct ptp_clock_info *ptp, struct timespec64 const *ts) { struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); + struct ksz_device_ptp_shared *ptp_shared = &dev->ptp_shared; u16 data16; int ret; @@ -207,6 +218,10 @@ static int ksz9477_ptp_settime(struct ptp_clock_info *ptp, if (ret) goto error_return; + spin_lock_bh(&ptp_shared->ptp_clock_lock); + ptp_shared->ptp_clock_time = *ts; + spin_unlock_bh(&ptp_shared->ptp_clock_lock); + error_return: mutex_unlock(&dev->ptp_mutex); return ret; @@ -221,17 +236,23 @@ static int ksz9477_ptp_enable(struct ptp_clock_info *ptp, static long ksz9477_ptp_do_aux_work(struct ptp_clock_info *ptp) { struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); + struct ksz_device_ptp_shared *ptp_shared = &dev->ptp_shared; struct timespec64 ts; mutex_lock(&dev->ptp_mutex); _ksz9477_ptp_gettime(dev, &ts); mutex_unlock(&dev->ptp_mutex); + spin_lock_bh(&ptp_shared->ptp_clock_lock); + ptp_shared->ptp_clock_time = ts; + spin_unlock_bh(&ptp_shared->ptp_clock_lock); + return HZ; /* reschedule in 1 second */ } static int ksz9477_ptp_start_clock(struct ksz_device *dev) { + struct ksz_device_ptp_shared *ptp_shared = &dev->ptp_shared; u16 data; int ret; @@ -252,6 +273,11 @@ static int ksz9477_ptp_start_clock(struct ksz_device *dev) if (ret) return ret; + spin_lock_bh(&ptp_shared->ptp_clock_lock); + ptp_shared->ptp_clock_time.tv_sec = 0; + ptp_shared->ptp_clock_time.tv_nsec = 0; + spin_unlock_bh(&ptp_shared->ptp_clock_lock); + return 0; } @@ -269,6 +295,236 @@ static int ksz9477_ptp_stop_clock(struct ksz_device *dev) return ksz_write16(dev, REG_PTP_CLK_CTRL, data); } +/* Time stamping support */ + +static int ksz9477_ptp_enable_mode(struct ksz_device *dev, bool enable) +{ + u16 data; + int ret; + + ret = ksz_read16(dev, REG_PTP_MSG_CONF1, &data); + if (ret) + return ret; + + /* Setting the PTP_802_1AS bit disables forwarding of PDelay_Req and + * PDelay_Resp messages. These messages must not be forwarded in + * Boundary Clock mode. + */ + if (enable) + data |= PTP_ENABLE | PTP_802_1AS; + else + data &= ~PTP_ENABLE; + + ret = ksz_write16(dev, REG_PTP_MSG_CONF1, data); + if (ret) + return ret; + + if (enable) { + /* Schedule cyclic call of ksz_ptp_do_aux_work() */ + ret = ptp_schedule_worker(dev->ptp_clock, 0); + if (ret) + goto error_disable_mode; + } else { + ptp_cancel_worker_sync(dev->ptp_clock); + } + + return 0; + +error_disable_mode: + ksz_write16(dev, REG_PTP_MSG_CONF1, data & ~PTP_ENABLE); + return ret; +} + +static int ksz9477_ptp_enable_port_ptp_interrupts(struct ksz_device *dev, + int port, bool enable) +{ + u32 addr = PORT_CTRL_ADDR(port, REG_PORT_INT_MASK); + u8 data; + int ret; + + ret = ksz_read8(dev, addr, &data); + if (ret) + return ret; + + /* PORT_PTP_INT bit is active low */ + if (enable) + data &= ~PORT_PTP_INT; + else + data |= PORT_PTP_INT; + + return ksz_write8(dev, addr, data); +} + +static int ksz9477_ptp_enable_port_egress_interrupts(struct ksz_device *dev, + int port, bool enable) +{ + u32 addr = PORT_CTRL_ADDR(port, REG_PTP_PORT_TX_INT_ENABLE__2); + u16 data; + int ret; + + ret = ksz_read16(dev, addr, &data); + if (ret) + return ret; + + /* PTP_PORT_XDELAY_REQ_INT is high active */ + if (enable) + data |= PTP_PORT_XDELAY_REQ_INT; + else + data &= PTP_PORT_XDELAY_REQ_INT; + + return ksz_write16(dev, addr, data); +} + +static void ksz9477_ptp_txtstamp_skb(struct ksz_device *dev, + struct ksz_port *prt, struct sk_buff *skb) +{ + struct skb_shared_hwtstamps hwtstamps = {}; + int ret; + + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + + /* timeout must include tstamp latency, IRQ latency and time for + * reading the time stamp via I2C. + */ + ret = wait_for_completion_timeout(&prt->tstamp_completion, + msecs_to_jiffies(100)); + if (!ret) { + dev_err(dev->dev, "timeout waiting for time stamp\n"); + return; + } + hwtstamps.hwtstamp = prt->tstamp_xdelay; + skb_complete_tx_timestamp(skb, &hwtstamps); +} + +#define work_to_port(work) \ + container_of((work), struct ksz_port_ptp_shared, xmit_work) +#define ptp_shared_to_ksz_port(t) \ + container_of((t), struct ksz_port, ptp_shared) +#define ptp_shared_to_ksz_device(t) \ + container_of((t), struct ksz_device, ptp_shared) + +/* Deferred work is necessary for time stamped PDelay_Req messages. This cannot + * be done from atomic context as we have to wait for the hardware interrupt. + */ +static void ksz9477_port_deferred_xmit(struct kthread_work *work) +{ + struct ksz_port_ptp_shared *prt_ptp_shared = work_to_port(work); + struct ksz_port *prt = ptp_shared_to_ksz_port(prt_ptp_shared); + struct ksz_device_ptp_shared *ptp_shared = prt_ptp_shared->dev; + struct ksz_device *dev = ptp_shared_to_ksz_device(ptp_shared); + int port = prt - dev->ports; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&prt_ptp_shared->xmit_queue)) != NULL) { + struct sk_buff *clone = DSA_SKB_CB(skb)->clone; + + reinit_completion(&prt->tstamp_completion); + + /* Transfer skb to the host port. */ + dsa_enqueue_skb(skb, dsa_to_port(dev->ds, port)->slave); + + ksz9477_ptp_txtstamp_skb(dev, prt, clone); + } +} + +static int ksz9477_ptp_port_init(struct ksz_device *dev, int port) +{ + struct ksz_port *prt = &dev->ports[port]; + struct ksz_port_ptp_shared *ptp_shared = &prt->ptp_shared; + struct dsa_port *dp = dsa_to_port(dev->ds, port); + int ret; + + if (port == dev->cpu_port) + return 0; + + /* Set rx and tx latency to 0 (will be handled by user space) */ + ret = ksz_write16(dev, PORT_CTRL_ADDR(port, REG_PTP_PORT_RX_DELAY__2), + 0); + if (ret) + return ret; + + ret = ksz_write16(dev, PORT_CTRL_ADDR(port, REG_PTP_PORT_TX_DELAY__2), + 0); + if (ret) + return ret; + + ret = ksz9477_ptp_enable_port_ptp_interrupts(dev, port, true); + if (ret) + return ret; + + ret = ksz9477_ptp_enable_port_egress_interrupts(dev, port, true); + if (ret) + goto error_disable_port_ptp_interrupts; + + /* ksz_port::ptp_shared is used in tagging driver */ + ptp_shared->dev = &dev->ptp_shared; + dp->priv = ptp_shared; + + /* PDelay_Req messages require deferred transmit as the time + * stamp unit provides no sequenceId or similar. So we must + * wait for the time stamp interrupt. + */ + init_completion(&prt->tstamp_completion); + kthread_init_work(&ptp_shared->xmit_work, + ksz9477_port_deferred_xmit); + ptp_shared->xmit_worker = kthread_create_worker(0, "%s_xmit", + dp->slave->name); + if (IS_ERR(ptp_shared->xmit_worker)) { + ret = PTR_ERR(ptp_shared->xmit_worker); + dev_err(dev->dev, + "failed to create deferred xmit thread: %d\n", ret); + goto error_disable_port_egress_interrupts; + } + skb_queue_head_init(&ptp_shared->xmit_queue); + + return 0; + +error_disable_port_egress_interrupts: + ksz9477_ptp_enable_port_egress_interrupts(dev, port, false); +error_disable_port_ptp_interrupts: + ksz9477_ptp_enable_port_ptp_interrupts(dev, port, false); + return ret; +} + +static void ksz9477_ptp_port_deinit(struct ksz_device *dev, int port) +{ + struct ksz_port_ptp_shared *ptp_shared = &dev->ports[port].ptp_shared; + + if (port == dev->cpu_port) + return; + + kthread_destroy_worker(ptp_shared->xmit_worker); + ksz9477_ptp_enable_port_egress_interrupts(dev, port, false); + ksz9477_ptp_enable_port_ptp_interrupts(dev, port, false); +} + +static int ksz9477_ptp_ports_init(struct ksz_device *dev) +{ + int port; + int ret; + + for (port = 0; port < dev->port_cnt; port++) { + ret = ksz9477_ptp_port_init(dev, port); + if (ret) + goto error_deinit; + } + + return 0; + +error_deinit: + while (port-- > 0) + ksz9477_ptp_port_deinit(dev, port); + return ret; +} + +static void ksz9477_ptp_ports_deinit(struct ksz_device *dev) +{ + int port; + + for (port = 0; port < dev->port_cnt; port++) + ksz9477_ptp_port_deinit(dev, port); +} + /* device attributes */ enum ksz9477_ptp_tcmode { @@ -322,6 +578,7 @@ int ksz9477_ptp_init(struct ksz_device *dev) int ret; mutex_init(&dev->ptp_mutex); + spin_lock_init(&dev->ptp_shared.ptp_clock_lock); /* PTP clock properties */ @@ -355,6 +612,11 @@ int ksz9477_ptp_init(struct ksz_device *dev) goto error_stop_clock; } + /* Init switch ports */ + ret = ksz9477_ptp_ports_init(dev); + if (ret) + goto error_unregister_clock; + /* Currently, only P2P delay measurement is supported. Setting ocmode * to slave will work independently of actually being master or slave. * For E2E delay measurement, switching between master and slave would @@ -371,11 +633,6 @@ int ksz9477_ptp_init(struct ksz_device *dev) ksz9477_ptp_tcmode_set(dev, KSZ9477_PTP_TCMODE_P2P); ksz9477_ptp_ocmode_set(dev, KSZ9477_PTP_OCMODE_SLAVE); - /* Schedule cyclic call of ksz_ptp_do_aux_work() */ - ret = ptp_schedule_worker(dev->ptp_clock, 0); - if (ret) - goto error_unregister_clock; - return 0; error_unregister_clock: @@ -387,10 +644,53 @@ int ksz9477_ptp_init(struct ksz_device *dev) void ksz9477_ptp_deinit(struct ksz_device *dev) { + ksz9477_ptp_ports_deinit(dev); + ksz9477_ptp_enable_mode(dev, false); ptp_clock_unregister(dev->ptp_clock); ksz9477_ptp_stop_clock(dev); } +irqreturn_t ksz9477_ptp_port_interrupt(struct ksz_device *dev, int port) +{ + u32 addr = PORT_CTRL_ADDR(port, REG_PTP_PORT_TX_INT_STATUS__2); + struct ksz_port *prt = &dev->ports[port]; + u16 data; + int ret; + + ret = ksz_read16(dev, addr, &data); + if (ret) + return IRQ_NONE; + + if (data & PTP_PORT_XDELAY_REQ_INT) { + /* Timestamp for Pdelay_Req / Delay_Req */ + struct ksz_device_ptp_shared *ptp_shared = &dev->ptp_shared; + u32 tstamp_raw; + ktime_t tstamp; + + /* In contrast to the KSZ9563R data sheet, the format of the + * port time stamp registers is also 2 bit seconds + 30 bit + * nanoseconds (same as in the tail tags). + */ + ret = ksz_read32(dev, + PORT_CTRL_ADDR(port, REG_PTP_PORT_XDELAY_TS), + &tstamp_raw); + if (ret) + return IRQ_NONE; + + tstamp = ksz9477_decode_tstamp(tstamp_raw); + prt->tstamp_xdelay = ksz9477_tstamp_reconstruct(ptp_shared, + tstamp); + complete(&prt->tstamp_completion); + } + + /* Clear interrupt(s) (W1C) */ + ret = ksz_write16(dev, addr, data); + if (ret) + return IRQ_NONE; + + return IRQ_HANDLED; +} + /* DSA PTP operations */ int ksz9477_ptp_get_ts_info(struct dsa_switch *ds, int port, @@ -418,7 +718,9 @@ int ksz9477_ptp_get_ts_info(struct dsa_switch *ds, int port, static int ksz9477_set_hwtstamp_config(struct ksz_device *dev, int port, struct hwtstamp_config *config) { + struct ksz_device_ptp_shared *ptp_shared = &dev->ptp_shared; struct ksz_port *prt = &dev->ports[port]; + bool on = true; /* reserved for future extensions */ if (config->flags) @@ -437,6 +739,7 @@ static int ksz9477_set_hwtstamp_config(struct ksz_device *dev, int port, switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE: + on = false; break; case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: @@ -452,9 +755,25 @@ static int ksz9477_set_hwtstamp_config(struct ksz_device *dev, int port, break; default: config->rx_filter = HWTSTAMP_FILTER_NONE; + on = false; return -ERANGE; } + if (on != test_bit(KSZ9477_HWTS_EN, &ptp_shared->state)) { + int ret = 0; + + clear_bit(KSZ9477_HWTS_EN, &ptp_shared->state); + + ret = ksz9477_ptp_enable_mode(dev, on); + if (ret) { + dev_err(dev->dev, + "Failed to change timestamping: %d\n", ret); + return ret; + } + if (on) + set_bit(KSZ9477_HWTS_EN, &ptp_shared->state); + } + return 0; } @@ -464,9 +783,8 @@ int ksz9477_ptp_port_hwtstamp_get(struct dsa_switch *ds, int port, struct ksz_device *dev = ds->priv; unsigned long bytes_copied; - bytes_copied = copy_to_user(ifr->ifr_data, - &dev->ports[port].tstamp_config, - sizeof(dev->ports[port].tstamp_config)); + bytes_copied = copy_to_user(ifr->ifr_data, &dev->tstamp_config, + sizeof(dev->tstamp_config)); return bytes_copied ? -EFAULT : 0; } @@ -482,13 +800,66 @@ int ksz9477_ptp_port_hwtstamp_set(struct dsa_switch *ds, int port, if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) return -EFAULT; + mutex_lock(&dev->ptp_mutex); + err = ksz9477_set_hwtstamp_config(dev, port, &config); - if (err) + if (err) { + mutex_unlock(&dev->ptp_mutex); return err; + } /* Save the chosen configuration to be returned later. */ - memcpy(&dev->ports[port].tstamp_config, &config, sizeof(config)); + memcpy(&dev->tstamp_config, &config, sizeof(config)); bytes_copied = copy_to_user(ifr->ifr_data, &config, sizeof(config)); + mutex_unlock(&dev->ptp_mutex); + return bytes_copied ? -EFAULT : 0; } + +bool ksz9477_ptp_port_txtstamp(struct dsa_switch *ds, int port, + struct sk_buff *clone, unsigned int type) +{ + struct ksz_device *dev = ds->priv; + struct ksz_port *prt = &dev->ports[port]; + struct ptp_header *hdr; + u8 ptp_msg_type; + + /* Should already been tested in dsa_skb_tx_timestamp()? */ + if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP)) + return false; + + if (!prt->hwts_tx_en) + return false; + + hdr = ptp_parse_header(clone, type); + if (!hdr) + return false; + + ptp_msg_type = ptp_get_msgtype(hdr, type); + switch (ptp_msg_type) { + /* As the KSZ9563 always performs one step time stamping, only the time + * stamp for Pdelay_Req is reported to the application via socket error + * queue. Time stamps for Sync and Pdelay_resp will be applied directly + * to the outgoing message (e.g. correction field), but will NOT be + * reported to the socket. + * Delay_Req is not time stamped as E2E is currently not supported by + * this driver. See ksz9477_ptp_init() for details. + */ + case PTP_MSGTYPE_PDELAY_REQ: + case PTP_MSGTYPE_PDELAY_RESP: + break; + default: + return false; + } + + /* ptp_type will be reused in ksz9477_xmit_timestamp(). ptp_msg_type + * will be reused in ksz9477_defer_xmit(). For PDelay_Resp, the cloned + * skb will not be passed to skb_complete_tx_timestamp() and has to be + * freed manually in ksz9477_defer_xmit(). + */ + KSZ9477_SKB_CB(clone)->ptp_type = type; + KSZ9477_SKB_CB(clone)->ptp_msg_type = ptp_msg_type; + + return true; /* keep cloned skb */ +} diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.h b/drivers/net/dsa/microchip/ksz9477_ptp.h index b599401812ae..2f7c4fa0753a 100644 --- a/drivers/net/dsa/microchip/ksz9477_ptp.h +++ b/drivers/net/dsa/microchip/ksz9477_ptp.h @@ -10,6 +10,7 @@ #ifndef DRIVERS_NET_DSA_MICROCHIP_KSZ9477_PTP_H_ #define DRIVERS_NET_DSA_MICROCHIP_KSZ9477_PTP_H_ +#include #include #include "ksz_common.h" @@ -19,18 +20,26 @@ int ksz9477_ptp_init(struct ksz_device *dev); void ksz9477_ptp_deinit(struct ksz_device *dev); +irqreturn_t ksz9477_ptp_port_interrupt(struct ksz_device *dev, int port); + int ksz9477_ptp_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *ts); int ksz9477_ptp_port_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr); int ksz9477_ptp_port_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr); +bool ksz9477_ptp_port_txtstamp(struct dsa_switch *ds, int port, + struct sk_buff *clone, unsigned int type); #else static inline int ksz9477_ptp_init(struct ksz_device *dev) { return 0; } static inline void ksz9477_ptp_deinit(struct ksz_device *dev) {} +static inline irqreturn_t ksz9477_ptp_port_interrupt(struct ksz_device *dev, + int port) +{ return IRQ_NONE; } + static inline int ksz9477_ptp_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *ts) { return -EOPNOTSUPP; } @@ -43,6 +52,11 @@ static inline int ksz9477_ptp_port_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr) { return -EOPNOTSUPP; } +static inline bool ksz9477_ptp_port_txtstamp(struct dsa_switch *ds, int port, + struct sk_buff *clone, + unsigned int type) +{ return false; } + #endif #endif /* DRIVERS_NET_DSA_MICROCHIP_KSZ9477_PTP_H_ */ diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index e1235d854977..18bae8c0de9a 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -7,8 +7,11 @@ #ifndef __KSZ_COMMON_H #define __KSZ_COMMON_H +#include +#include #include #include +#include #include #include #include @@ -42,7 +45,9 @@ struct ksz_port { struct ksz_port_mib mib; phy_interface_t interface; #if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) - struct hwtstamp_config tstamp_config; + struct ksz_port_ptp_shared ptp_shared; + ktime_t tstamp_xdelay; + struct completion tstamp_completion; bool hwts_tx_en; #endif }; @@ -99,7 +104,9 @@ struct ksz_device { #if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_caps; + struct hwtstamp_config tstamp_config; struct mutex ptp_mutex; /* protects PTP related hardware */ + struct ksz_device_ptp_shared ptp_shared; #endif }; diff --git a/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h new file mode 100644 index 000000000000..a9b4720cc842 --- /dev/null +++ b/include/linux/dsa/ksz_common.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Routines shared between drivers/net/dsa/microchip/ksz9477_ptp.h and + * net/dsa/tag_ksz.c + * + * Copyright (C) 2020 ARRI Lighting + */ + +#ifndef _NET_DSA_KSZ_COMMON_H_ +#define _NET_DSA_KSZ_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* All time stamps from the KSZ consist of 2 bits for seconds and 30 bits for + * nanoseconds. This is NOT the same as 32 bits for nanoseconds. + */ +#define KSZ_TSTAMP_SEC_MASK GENMASK(31, 30) +#define KSZ_TSTAMP_NSEC_MASK GENMASK(29, 0) + +#define KSZ9477_HWTS_EN 0 + +struct ksz_device_ptp_shared { + /* protects ptp_clock_time (user space (various syscalls) + * vs. softirq in ksz9477_rcv_timestamp()). + */ + spinlock_t ptp_clock_lock; + /* approximated current time, read once per second from hardware */ + struct timespec64 ptp_clock_time; + unsigned long state; +}; + +struct ksz_port_ptp_shared { + struct ksz_device_ptp_shared *dev; + struct kthread_worker *xmit_worker; + struct kthread_work xmit_work; + struct sk_buff_head xmit_queue; +}; + +/* net/dsa/tag_ksz.c */ +static inline ktime_t ksz9477_decode_tstamp(u32 tstamp) +{ + u64 ns = FIELD_GET(KSZ_TSTAMP_SEC_MASK, tstamp) * NSEC_PER_SEC + + FIELD_GET(KSZ_TSTAMP_NSEC_MASK, tstamp); + + return ns_to_ktime(ns); +} + +ktime_t ksz9477_tstamp_reconstruct(struct ksz_device_ptp_shared *ksz, + ktime_t tstamp); + +struct ksz9477_skb_cb { + unsigned int ptp_type; + /* Do not cache pointer to PTP header between ksz9477_ptp_port_txtstamp + * and ksz9xxx_xmit() (will become invalid during dsa_realloc_skb()). + */ + u8 ptp_msg_type; +}; + +#define KSZ9477_SKB_CB(skb) \ + ((struct ksz9477_skb_cb *)DSA_SKB_CB_PRIV(skb)) + +#endif /* _NET_DSA_KSZ_COMMON_H_ */ diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index dfecd7b22fd7..ad07ad216305 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -93,6 +93,7 @@ config NET_DSA_TAG_MTK config NET_DSA_TAG_KSZ tristate "Tag driver for Microchip 8795/9477/9893 families of switches" + select NET_PTP_CLASSIFY help Say Y if you want to enable support for tagging frames for the Microchip 8795/9477/9893 families of switches. diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c index 4820dbcedfa2..0d324367ebec 100644 --- a/net/dsa/tag_ksz.c +++ b/net/dsa/tag_ksz.c @@ -4,10 +4,14 @@ * Copyright (c) 2017 Microchip Technology */ +#include +#include #include #include +#include #include #include +#include #include "dsa_priv.h" /* Typically only one byte is used for tail tag. */ @@ -87,35 +91,212 @@ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795); /* * For Ingress (Host -> KSZ9477), 2 bytes are added before FCS. * --------------------------------------------------------------------------- - * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|tag1(1byte)|FCS(4bytes) + * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|ts(4bytes)|tag0(1byte)|tag1(1byte)|FCS(4bytes) * --------------------------------------------------------------------------- + * ts : time stamp (only present if PTP is enabled on the hardware). * tag0 : Prioritization (not used now) * tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5) * - * For Egress (KSZ9477 -> Host), 1 byte is added before FCS. + * For Egress (KSZ9477 -> Host), 1/4 bytes are added before FCS. * --------------------------------------------------------------------------- - * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes) + * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|ts(4bytes)|tag0(1byte)|FCS(4bytes) * --------------------------------------------------------------------------- + * ts : time stamp (only present of bit 7 in tag0 is set * tag0 : zero-based value represents port * (eg, 0x00=port1, 0x02=port3, 0x06=port7) */ #define KSZ9477_INGRESS_TAG_LEN 2 #define KSZ9477_PTP_TAG_LEN 4 -#define KSZ9477_PTP_TAG_INDICATION 0x80 +#define KSZ9477_PTP_TAG_INDICATION BIT(7) #define KSZ9477_TAIL_TAG_OVERRIDE BIT(9) #define KSZ9477_TAIL_TAG_LOOKUP BIT(10) +/* Time stamp tag is only inserted if PTP is enabled in hardware. */ +static void ksz9477_xmit_timestamp(struct sk_buff *skb) +{ + struct sk_buff *clone = DSA_SKB_CB(skb)->clone; + struct ptp_header *ptp_hdr; + unsigned int ptp_type; + u32 tstamp_raw = 0; + u8 ptp_msg_type; + s64 correction; + + if (!clone) + goto out_put_tag; + + /* Use cached PTP type from ksz9477_ptp_port_txtstamp(). */ + ptp_type = KSZ9477_SKB_CB(clone)->ptp_type; + if (ptp_type == PTP_CLASS_NONE) + goto out_put_tag; + + ptp_hdr = ptp_parse_header(skb, ptp_type); + if (!ptp_hdr) + goto out_put_tag; + + ptp_msg_type = KSZ9477_SKB_CB(clone)->ptp_msg_type; + if (ptp_msg_type != PTP_MSGTYPE_PDELAY_RESP) + goto out_put_tag; + + correction = (s64)get_unaligned_be64(&ptp_hdr->correction); + + /* For PDelay_Resp messages we will likely have a negative value in the + * correction field (see ksz9477_rcv()). The switch hardware cannot + * correctly update such values (produces an off by one error in the UDP + * checksum), so it must be moved to the time stamp field in the tail + * tag. + */ + if (correction < 0) { + struct timespec64 ts; + + /* Move ingress time stamp from PTP header's correction field to + * tail tag. Format of the correction filed is 48 bit ns + 16 + * bit fractional ns. + */ + ts = ns_to_timespec64(-correction >> 16); + tstamp_raw = ((ts.tv_sec & 3) << 30) | ts.tv_nsec; + + /* Set correction field to 0 and update UDP checksum. */ + ptp_header_update_correction(skb, ptp_type, ptp_hdr, 0); + } + + /* For PDelay_Resp messages, the clone is not required in + * skb_complete_tx_timestamp() and should be freed here. + */ + kfree_skb(clone); + DSA_SKB_CB(skb)->clone = NULL; + +out_put_tag: + put_unaligned_be32(tstamp_raw, skb_put(skb, KSZ9477_PTP_TAG_LEN)); +} + +/* Defer transmit if waiting for egress time stamp is required. */ +static struct sk_buff *ksz9477_defer_xmit(struct dsa_port *dp, + struct sk_buff *skb) +{ + struct ksz_port_ptp_shared *ptp_shared = dp->priv; + struct sk_buff *clone = DSA_SKB_CB(skb)->clone; + u8 ptp_msg_type; + + if (!clone) + return skb; /* no deferred xmit for this packet */ + + /* Use cached PTP msg type from ksz9477_ptp_port_txtstamp(). */ + ptp_msg_type = KSZ9477_SKB_CB(clone)->ptp_msg_type; + if (ptp_msg_type != PTP_MSGTYPE_PDELAY_REQ) + goto out_free_clone; /* only PDelay_Req is deferred */ + + /* Increase refcount so the kfree_skb in dsa_slave_xmit + * won't really free the packet. + */ + skb_queue_tail(&ptp_shared->xmit_queue, skb_get(skb)); + kthread_queue_work(ptp_shared->xmit_worker, &ptp_shared->xmit_work); + + return NULL; + +out_free_clone: + kfree_skb(clone); + DSA_SKB_CB(skb)->clone = NULL; + return skb; +} + +ktime_t ksz9477_tstamp_reconstruct(struct ksz_device_ptp_shared *ksz, + ktime_t tstamp) +{ + struct timespec64 ts = ktime_to_timespec64(tstamp); + struct timespec64 ptp_clock_time; + struct timespec64 diff; + + spin_lock_bh(&ksz->ptp_clock_lock); + ptp_clock_time = ksz->ptp_clock_time; + spin_unlock_bh(&ksz->ptp_clock_lock); + + /* calculate full time from partial time stamp */ + ts.tv_sec = (ptp_clock_time.tv_sec & ~3) | ts.tv_sec; + + /* find nearest possible point in time */ + diff = timespec64_sub(ts, ptp_clock_time); + if (diff.tv_sec > 2) + ts.tv_sec -= 4; + else if (diff.tv_sec < -2) + ts.tv_sec += 4; + + return timespec64_to_ktime(ts); +} +EXPORT_SYMBOL(ksz9477_tstamp_reconstruct); + +static void ksz9477_rcv_timestamp(struct sk_buff *skb, u8 *tag, + struct net_device *dev, unsigned int port) +{ + struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); + struct dsa_switch *ds = dev->dsa_ptr->ds; + struct ksz_port_ptp_shared *port_ptp_shared; + u8 *tstamp_raw = tag - KSZ9477_PTP_TAG_LEN; + struct ptp_header *ptp_hdr; + unsigned int ptp_type; + u8 ptp_msg_type; + ktime_t tstamp; + s64 correction; + + port_ptp_shared = dsa_to_port(ds, port)->priv; + if (!port_ptp_shared) + return; + + /* convert time stamp and write to skb */ + tstamp = ksz9477_decode_tstamp(get_unaligned_be32(tstamp_raw)); + memset(hwtstamps, 0, sizeof(*hwtstamps)); + hwtstamps->hwtstamp = ksz9477_tstamp_reconstruct(port_ptp_shared->dev, + tstamp); + + /* For PDelay_Req messages, user space (ptp4l) expects that the hardware + * subtracts the ingress time stamp from the correction field. The + * separate hw time stamp from the sk_buff struct will not be used in + * this case. + */ + + if (skb_headroom(skb) < ETH_HLEN) + return; + + __skb_push(skb, ETH_HLEN); + ptp_type = ptp_classify_raw(skb); + __skb_pull(skb, ETH_HLEN); + + if (ptp_type == PTP_CLASS_NONE) + return; + + ptp_hdr = ptp_parse_header(skb, ptp_type); + if (!ptp_hdr) + return; + + ptp_msg_type = ptp_get_msgtype(ptp_hdr, ptp_type); + if (ptp_msg_type != PTP_MSGTYPE_PDELAY_REQ) + return; + + /* Only subtract the partial time stamp from the correction field. When + * the hardware adds the egress time stamp to the correction field of + * the PDelay_Resp message on tx, also only the partial time stamp will + * be added. + */ + correction = (s64)get_unaligned_be64(&ptp_hdr->correction); + correction -= ktime_to_ns(tstamp) << 16; + + ptp_header_update_correction(skb, ptp_type, ptp_hdr, correction); +} + static struct sk_buff *ksz9477_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_port *dp = dsa_slave_to_port(dev); + struct ksz_port_ptp_shared *port_ptp_shared = dp->priv; + struct ksz_device_ptp_shared *ptp_shared = port_ptp_shared->dev; __be16 *tag; u8 *addr; u16 val; /* Tag encoding */ + if (test_bit(KSZ9477_HWTS_EN, &ptp_shared->state)) + ksz9477_xmit_timestamp(skb); tag = skb_put(skb, KSZ9477_INGRESS_TAG_LEN); addr = skb_mac_header(skb); @@ -126,7 +307,7 @@ static struct sk_buff *ksz9477_xmit(struct sk_buff *skb, *tag = cpu_to_be16(val); - return skb; + return ksz9477_defer_xmit(dp, skb); } static struct sk_buff *ksz9477_rcv(struct sk_buff *skb, struct net_device *dev, @@ -138,8 +319,10 @@ static struct sk_buff *ksz9477_rcv(struct sk_buff *skb, struct net_device *dev, unsigned int len = KSZ_EGRESS_TAG_LEN; /* Extra 4-bytes PTP timestamp */ - if (tag[0] & KSZ9477_PTP_TAG_INDICATION) + if (tag[0] & KSZ9477_PTP_TAG_INDICATION) { + ksz9477_rcv_timestamp(skb, tag, dev, port); len += KSZ9477_PTP_TAG_LEN; + } return ksz_common_rcv(skb, dev, port, len); } @@ -149,7 +332,7 @@ static const struct dsa_device_ops ksz9477_netdev_ops = { .proto = DSA_TAG_PROTO_KSZ9477, .xmit = ksz9477_xmit, .rcv = ksz9477_rcv, - .overhead = KSZ9477_INGRESS_TAG_LEN, + .overhead = KSZ9477_INGRESS_TAG_LEN + KSZ9477_PTP_TAG_LEN, .tail_tag = true, }; @@ -163,10 +346,14 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_port *dp = dsa_slave_to_port(dev); + struct ksz_port_ptp_shared *port_ptp_shared = dp->priv; + struct ksz_device_ptp_shared *ptp_shared = port_ptp_shared->dev; u8 *addr; u8 *tag; /* Tag encoding */ + if (test_bit(KSZ9477_HWTS_EN, &ptp_shared->state)) + ksz9477_xmit_timestamp(skb); tag = skb_put(skb, KSZ_INGRESS_TAG_LEN); addr = skb_mac_header(skb); @@ -175,7 +362,7 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb, if (is_link_local_ether_addr(addr)) *tag |= KSZ9893_TAIL_TAG_OVERRIDE; - return skb; + return ksz9477_defer_xmit(dp, skb); } static const struct dsa_device_ops ksz9893_netdev_ops = { @@ -183,7 +370,7 @@ static const struct dsa_device_ops ksz9893_netdev_ops = { .proto = DSA_TAG_PROTO_KSZ9893, .xmit = ksz9893_xmit, .rcv = ksz9477_rcv, - .overhead = KSZ_INGRESS_TAG_LEN, + .overhead = KSZ_INGRESS_TAG_LEN + KSZ9477_PTP_TAG_LEN, .tail_tag = true, }; From patchwork Thu Dec 3 10:21:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11948347 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 03DF6C83012 for ; Thu, 3 Dec 2020 10:29:24 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9FB2521527 for ; Thu, 3 Dec 2020 10:29:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388730AbgLCK3T (ORCPT ); Thu, 3 Dec 2020 05:29:19 -0500 Received: from mailout04.rmx.de ([94.199.90.94]:34452 "EHLO mailout04.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388696AbgLCK3T (ORCPT ); Thu, 3 Dec 2020 05:29:19 -0500 Received: from kdin02.retarus.com (kdin02.dmz1.retloc [172.19.17.49]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout04.rmx.de (Postfix) with ESMTPS id 4CmsW20D4Rz3qfQJ; Thu, 3 Dec 2020 11:28:30 +0100 (CET) Received: from mta.arri.de (unknown [217.111.95.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by kdin02.retarus.com (Postfix) with ESMTPS id 4CmsVD6yp9z2TSCL; Thu, 3 Dec 2020 11:27:48 +0100 (CET) Received: from N95HX1G2.wgnetz.xx (192.168.54.174) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.487.0; Thu, 3 Dec 2020 11:26:34 +0100 From: Christian Eggers To: Vladimir Oltean , Jakub Kicinski , Andrew Lunn , Richard Cochran , "Rob Herring" CC: Vivien Didelot , "David S . Miller" , Kurt Kanzenbach , George McCollister , Marek Vasut , Helmut Grohne , Paul Barker , Codrin Ciubotariu , Tristram Ha , Woojung Huh , Microchip Linux Driver Support , Christian Eggers , , , Subject: [PATCH net-next v5 9/9] net: dsa: microchip: ksz9477: add periodic output support Date: Thu, 3 Dec 2020 11:21:17 +0100 Message-ID: <20201203102117.8995-10-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201203102117.8995-1-ceggers@arri.de> References: <20201203102117.8995-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.174] X-RMX-ID: 20201203-112748-fktPMvdYzHSN-0@out02.hq X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org The KSZ9563 has a Trigger Output Unit (TOU) which can be used to generate periodic signals. The pulse length can be altered via a device attribute. Tested on a Microchip KSZ9563 switch. Signed-off-by: Christian Eggers --- Changes in v4: -------------- - 80 chars per line - reverse christmas tree - Set default pulse width for perout pulse to 50% (max. 125ms) - reject unsupported flags for perout_request On Saturday, 21 November 2020, 00:48:08 CET, Vladimir Oltean wrote: > On Wed, Nov 18, 2020 at 09:30:13PM +0100, Christian Eggers wrote: > > static int ksz9477_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) > > @@ -241,6 +256,15 @@ static int ksz9477_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) > > case KSZ_PTP_TOU_IDLE: > > break; > > > > + case KSZ_PTP_TOU_PEROUT: > > + dev_info(dev->dev, "Restarting periodic output signal\n"); > > Isn't this a bit too verbose, or is there something for the user to be > concerned about? In my setup this message appears only once after ptp4l has been started. Shall I remove it? > Watch out for 80 characters, please! A few long line are left (for instance ksz9477_ptp_enable_perout). Please let me know how I shall wrap them. > > + if (perout_request->flags & PTP_PEROUT_DUTY_CYCLE) { > > + u64 value = perout_request->on.sec * NSEC_PER_SEC + > > + perout_request->on.nsec; > > + > > + ret = ksz9477_ptp_tou_pulse_verify(value); > > + if (ret) > > + return ret; > > + > > + dev->ptp_perout_pulse_width_ns = value; > > + } > > It is not guaranteed that user space will set this flag. Shouldn't you > assign a default value for the pulse width? I don't know, half the > period should be a good default. Yes I should. The hardware support a maximum "duty cycle" of a little bit more than 125 ms. As PPS signals often have a 125 ms high period, I use this duration as the maximum. > > + /* Check error flag: > > + * - the ACTIVE flag is NOT cleared an error! > > + */ > > + ret = ksz_read32(dev, REG_PTP_TRIG_STATUS__4, &gpio_stat0); > > + if (ret) > > + return ret; > > + > > + if (gpio_stat0 & (1 << (0 + TRIG_ERROR_S))) { > > What is the role of this "0 +" term here? > > > + dev_err(dev->dev, "%s: Trigger unit0 error!\n", __func__); This is the index of the used trigger unit. drivers/net/dsa/microchip/ksz9477_ptp.c | 357 +++++++++++++++++++++++- drivers/net/dsa/microchip/ksz9477_ptp.h | 4 + drivers/net/dsa/microchip/ksz_common.h | 10 + 3 files changed, 369 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.c b/drivers/net/dsa/microchip/ksz9477_ptp.c index e68c9fc8e679..c3a0275e444a 100644 --- a/drivers/net/dsa/microchip/ksz9477_ptp.c +++ b/drivers/net/dsa/microchip/ksz9477_ptp.c @@ -19,6 +19,140 @@ #define KSZ_PTP_INC_NS 40 /* HW clock is incremented every 40 ns (by 40) */ #define KSZ_PTP_SUBNS_BITS 32 /* Number of bits in sub-nanoseconds counter */ +/* Shared register access routines (Trigger Output Unit) */ + +static int ksz9477_ptp_tou_reset(struct ksz_device *dev, unsigned int unit) +{ + u32 ctrl_stat, data; + int ret; + + /* Reset trigger unit (clears TRIGGER_EN, but not GPIOSTATx) */ + ret = ksz_read32(dev, REG_PTP_CTRL_STAT__4, &ctrl_stat); + if (ret) + return ret; + + ctrl_stat |= TRIG_RESET; + + ret = ksz_write32(dev, REG_PTP_CTRL_STAT__4, ctrl_stat); + if (ret) + return ret; + + /* Clear DONE */ + data = 1 << (unit + TRIG_DONE_S); + ret = ksz_write32(dev, REG_PTP_TRIG_STATUS__4, data); + if (ret) + return ret; + + /* Clear IRQ */ + data = 1 << (unit + TRIG_INT_S); + ret = ksz_write32(dev, REG_PTP_INT_STATUS__4, data); + if (ret) + return ret; + + /* Clear reset and set GPIO direction */ + ctrl_stat &= ~TRIG_ENABLE; /* clear cached bit :-) */ + ctrl_stat &= ~TRIG_RESET; + + ret = ksz_write32(dev, REG_PTP_CTRL_STAT__4, ctrl_stat); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_ptp_tou_cycle_width_set(struct ksz_device *dev, u32 width_ns) +{ + int ret; + + ret = ksz_write32(dev, REG_TRIG_CYCLE_WIDTH, width_ns); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_ptp_tou_cycle_count_set(struct ksz_device *dev, u16 count) +{ + u32 data; + int ret; + + ret = ksz_read32(dev, REG_TRIG_CYCLE_CNT, &data); + if (ret) + return ret; + + data &= ~(TRIG_CYCLE_CNT_M << TRIG_CYCLE_CNT_S); + data |= (count & TRIG_CYCLE_CNT_M) << TRIG_CYCLE_CNT_S; + + ret = ksz_write32(dev, REG_TRIG_CYCLE_CNT, data); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_ptp_tou_pulse_verify(u64 pulse_ns) +{ + u32 data; + + if (pulse_ns & 0x3) + return -EINVAL; + + data = (pulse_ns / 8); + if (data != (data & TRIG_PULSE_WIDTH_M)) + return -ERANGE; + + return 0; +} + +static int ksz9477_ptp_tou_pulse_set(struct ksz_device *dev, u32 pulse_ns) +{ + u32 data; + + data = (pulse_ns / 8); + + return ksz_write32(dev, REG_TRIG_PULSE_WIDTH__4, data); +} + +static int ksz9477_ptp_tou_target_time_set(struct ksz_device *dev, struct timespec64 const *ts) +{ + int ret; + + /* Hardware has only 32 bit */ + if ((ts->tv_sec & 0xffffffff) != ts->tv_sec) + return -EINVAL; + + ret = ksz_write32(dev, REG_TRIG_TARGET_NANOSEC, ts->tv_nsec); + if (ret) + return ret; + + ret = ksz_write32(dev, REG_TRIG_TARGET_SEC, ts->tv_sec); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_ptp_tou_start(struct ksz_device *dev, u32 *ctrl_stat_) +{ + u32 ctrl_stat; + int ret; + + ret = ksz_read32(dev, REG_PTP_CTRL_STAT__4, &ctrl_stat); + if (ret) + return ret; + + ctrl_stat |= TRIG_ENABLE; + + ret = ksz_write32(dev, REG_PTP_CTRL_STAT__4, ctrl_stat); + if (ret) + return ret; + + if (ctrl_stat_) + *ctrl_stat_ = ctrl_stat; + + return 0; +} + /* Posix clock support */ static int ksz9477_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) @@ -76,6 +210,8 @@ static int ksz9477_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) return ret; } +static int ksz9477_ptp_restart_perout(struct ksz_device *dev); + static int ksz9477_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); @@ -115,6 +251,20 @@ static int ksz9477_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) if (ret) goto error_return; + switch (dev->ptp_tou_mode) { + case KSZ_PTP_TOU_IDLE: + break; + + case KSZ_PTP_TOU_PEROUT: + dev_info(dev->dev, "Restarting periodic output signal\n"); + + ret = ksz9477_ptp_restart_perout(dev); + if (ret) + goto error_return; + + break; + } + spin_lock_bh(&ptp_shared->ptp_clock_lock); ptp_shared->ptp_clock_time = timespec64_add(ptp_shared->ptp_clock_time, delta64); @@ -218,6 +368,20 @@ static int ksz9477_ptp_settime(struct ptp_clock_info *ptp, if (ret) goto error_return; + switch (dev->ptp_tou_mode) { + case KSZ_PTP_TOU_IDLE: + break; + + case KSZ_PTP_TOU_PEROUT: + dev_info(dev->dev, "Restarting periodic output signal\n"); + + ret = ksz9477_ptp_restart_perout(dev); + if (ret) + goto error_return; + + break; + } + spin_lock_bh(&ptp_shared->ptp_clock_lock); ptp_shared->ptp_clock_time = *ts; spin_unlock_bh(&ptp_shared->ptp_clock_lock); @@ -227,10 +391,199 @@ static int ksz9477_ptp_settime(struct ptp_clock_info *ptp, return ret; } +static int ksz9477_ptp_configure_perout(struct ksz_device *dev, + u32 cycle_width_ns, u16 cycle_count, + u32 pulse_width_ns, + struct timespec64 const *target_time) +{ + u32 trig_ctrl; + int ret; + + /* Enable notify, set rising edge, set periodic pattern */ + trig_ctrl = TRIG_NOTIFY | (TRIG_POS_PERIOD << TRIG_PATTERN_S); + ret = ksz_write32(dev, REG_TRIG_CTRL__4, trig_ctrl); + if (ret) + return ret; + + ret = ksz9477_ptp_tou_cycle_width_set(dev, cycle_width_ns); + if (ret) + return ret; + + ksz9477_ptp_tou_cycle_count_set(dev, cycle_count); + if (ret) + return ret; + + ret = ksz9477_ptp_tou_pulse_set(dev, pulse_width_ns); + if (ret) + return ret; + + ret = ksz9477_ptp_tou_target_time_set(dev, target_time); + if (ret) + return ret; + + return 0; +} + +#define KSZ9477_PEROUT_VALID_FLAGS ( \ + PTP_PEROUT_DUTY_CYCLE \ +) + +static int ksz9477_ptp_enable_perout(struct ksz_device *dev, + struct ptp_perout_request const *perout_request, + int on) +{ + u64 cycle_width_ns; + u64 pulse_width_ns; + u32 gpio_stat0; + int ret; + + if (perout_request->flags & ~KSZ9477_PEROUT_VALID_FLAGS) + return -EINVAL; + + if (dev->ptp_tou_mode != KSZ_PTP_TOU_PEROUT && + dev->ptp_tou_mode != KSZ_PTP_TOU_IDLE) + return -EBUSY; + + ret = ksz9477_ptp_tou_reset(dev, 0); + if (ret) + return ret; + + if (!on) { + dev->ptp_tou_mode = KSZ_PTP_TOU_IDLE; + return 0; /* success */ + } + + dev->ptp_perout_target_time_first.tv_sec = perout_request->start.sec; + dev->ptp_perout_target_time_first.tv_nsec = perout_request->start.nsec; + + dev->ptp_perout_period.tv_sec = perout_request->period.sec; + dev->ptp_perout_period.tv_nsec = perout_request->period.nsec; + + cycle_width_ns = timespec64_to_ns(&dev->ptp_perout_period); + if ((cycle_width_ns & GENMASK(31, 0)) != cycle_width_ns) + return -EINVAL; + + if (perout_request->flags & PTP_PEROUT_DUTY_CYCLE) + pulse_width_ns = perout_request->on.sec * NSEC_PER_SEC + + perout_request->on.nsec; + + else + /* Use a duty cycle of 50%. Maximum pulse width supported by the + * hardware is a little bit more than 125 ms. + */ + pulse_width_ns = min_t(u64, + (perout_request->period.sec * NSEC_PER_SEC + + perout_request->period.nsec) / 2 + / 8 * 8, + 125000000LL); + + ret = ksz9477_ptp_tou_pulse_verify(pulse_width_ns); + if (ret) + return ret; + + dev->ptp_perout_pulse_width_ns = pulse_width_ns; + + ret = ksz9477_ptp_configure_perout(dev, cycle_width_ns, + dev->ptp_perout_cycle_count, + dev->ptp_perout_pulse_width_ns, + &dev->ptp_perout_target_time_first); + if (ret) + return ret; + + /* Activate trigger unit */ + ret = ksz9477_ptp_tou_start(dev, NULL); + if (ret) + return ret; + + /* Check error flag: + * - the ACTIVE flag is NOT cleared an error! + */ + ret = ksz_read32(dev, REG_PTP_TRIG_STATUS__4, &gpio_stat0); + if (ret) + return ret; + + if (gpio_stat0 & (1 << (0 + TRIG_ERROR_S))) { + dev_err(dev->dev, "%s: Trigger unit0 error!\n", __func__); + ret = -EIO; + /* Unit will be reset on next access */ + return ret; + } + + dev->ptp_tou_mode = KSZ_PTP_TOU_PEROUT; + return 0; +} + +static int ksz9477_ptp_restart_perout(struct ksz_device *dev) +{ + s64 now_ns, first_ns, period_ns, next_ns; + struct timespec64 now; + unsigned int count; + int ret; + + ret = _ksz9477_ptp_gettime(dev, &now); + if (ret) + return ret; + + now_ns = timespec64_to_ns(&now); + first_ns = timespec64_to_ns(&dev->ptp_perout_target_time_first); + + /* Calculate next perout event based on start time and period */ + period_ns = timespec64_to_ns(&dev->ptp_perout_period); + + if (first_ns < now_ns) { + count = div_u64(now_ns - first_ns, period_ns); + next_ns = first_ns + count * period_ns; + } else { + next_ns = first_ns; + } + + /* Ensure 100 ms guard time prior next event */ + while (next_ns < now_ns + 100000000) + next_ns += period_ns; + + /* Restart periodic output signal */ + { + struct timespec64 next = ns_to_timespec64(next_ns); + struct ptp_perout_request perout_request = { + .start = { + .sec = next.tv_sec, + .nsec = next.tv_nsec + }, + .period = { + .sec = dev->ptp_perout_period.tv_sec, + .nsec = dev->ptp_perout_period.tv_nsec + }, + .index = 0, + .flags = 0, /* keep current values */ + }; + ret = ksz9477_ptp_enable_perout(dev, &perout_request, 1); + if (ret) + return ret; + } + + return 0; +} + static int ksz9477_ptp_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *req, int on) { - return -EOPNOTSUPP; + struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); + int ret; + + switch (req->type) { + case PTP_CLK_REQ_PEROUT: { + struct ptp_perout_request const *perout_request = &req->perout; + + mutex_lock(&dev->ptp_mutex); + ret = ksz9477_ptp_enable_perout(dev, perout_request, on); + mutex_unlock(&dev->ptp_mutex); + return ret; + } + default: + return -EINVAL; + } + + return 0; } static long ksz9477_ptp_do_aux_work(struct ptp_clock_info *ptp) @@ -592,7 +945,7 @@ int ksz9477_ptp_init(struct ksz_device *dev) dev->ptp_caps.max_adj = 6249999; dev->ptp_caps.n_alarm = 0; dev->ptp_caps.n_ext_ts = 0; /* currently not implemented */ - dev->ptp_caps.n_per_out = 0; + dev->ptp_caps.n_per_out = 1; dev->ptp_caps.pps = 0; dev->ptp_caps.adjfine = ksz9477_ptp_adjfine; dev->ptp_caps.adjtime = ksz9477_ptp_adjtime; diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.h b/drivers/net/dsa/microchip/ksz9477_ptp.h index 2f7c4fa0753a..4d20decf0ad7 100644 --- a/drivers/net/dsa/microchip/ksz9477_ptp.h +++ b/drivers/net/dsa/microchip/ksz9477_ptp.h @@ -20,6 +20,7 @@ int ksz9477_ptp_init(struct ksz_device *dev); void ksz9477_ptp_deinit(struct ksz_device *dev); +irqreturn_t ksz9477_ptp_interrupt(struct ksz_device *dev); irqreturn_t ksz9477_ptp_port_interrupt(struct ksz_device *dev, int port); int ksz9477_ptp_get_ts_info(struct dsa_switch *ds, int port, @@ -36,6 +37,9 @@ bool ksz9477_ptp_port_txtstamp(struct dsa_switch *ds, int port, static inline int ksz9477_ptp_init(struct ksz_device *dev) { return 0; } static inline void ksz9477_ptp_deinit(struct ksz_device *dev) {} +static inline irqreturn_t ksz9477_ptp_interrupt(struct ksz_device *dev) +{ return IRQ_NONE; } + static inline irqreturn_t ksz9477_ptp_port_interrupt(struct ksz_device *dev, int port) { return IRQ_NONE; } diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 18bae8c0de9a..626f56570c0b 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -52,6 +52,11 @@ struct ksz_port { #endif }; +enum ksz_ptp_tou_mode { + KSZ_PTP_TOU_IDLE, + KSZ_PTP_TOU_PEROUT, +}; + struct ksz_device { struct dsa_switch *ds; struct ksz_platform_data *pdata; @@ -107,6 +112,11 @@ struct ksz_device { struct hwtstamp_config tstamp_config; struct mutex ptp_mutex; /* protects PTP related hardware */ struct ksz_device_ptp_shared ptp_shared; + enum ksz_ptp_tou_mode ptp_tou_mode; + struct timespec64 ptp_perout_target_time_first; /* start of first perout pulse */ + struct timespec64 ptp_perout_period; + u32 ptp_perout_pulse_width_ns; + u16 ptp_perout_cycle_count; #endif };