From patchwork Mon Oct 19 17:24:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11844831 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=-12.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, 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 2C638C43457 for ; Mon, 19 Oct 2020 17:27:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B5F032224D for ; Mon, 19 Oct 2020 17:27:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726464AbgJSR1f (ORCPT ); Mon, 19 Oct 2020 13:27:35 -0400 Received: from mailout06.rmx.de ([94.199.90.92]:59946 "EHLO mailout06.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726005AbgJSR1e (ORCPT ); Mon, 19 Oct 2020 13:27:34 -0400 Received: from kdin01.retarus.com (kdin01.dmz1.retloc [172.19.17.48]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout06.rmx.de (Postfix) with ESMTPS id 4CFNxB465Lz9yvf; Mon, 19 Oct 2020 19:27:26 +0200 (CEST) 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 kdin01.retarus.com (Postfix) with ESMTPS id 4CFNvg4m4xz2xGM; Mon, 19 Oct 2020 19:26:07 +0200 (CEST) Received: from N95HX1G2.wgnetz.xx (192.168.54.91) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.408.0; Mon, 19 Oct 2020 19:25:23 +0200 From: Christian Eggers To: Vladimir Oltean , Florian Fainelli , Andrew Lunn , Vivien Didelot , Jakub Kicinski , Rob Herring CC: Helmut Grohne , Paul Barker , Codrin Ciubotariu , George McCollister , Marek Vasut , Tristram Ha , "David S . Miller" , Woojung Huh , "Microchip Linux Driver Support" , Christian Eggers , , , Subject: [RFC PATCH net-next 1/9] dt-bindings: net: dsa: convert ksz bindings document to yaml Date: Mon, 19 Oct 2020 19:24:27 +0200 Message-ID: <20201019172435.4416-2-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201019172435.4416-1-ceggers@arri.de> References: <20201019172435.4416-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.91] X-RMX-ID: 20201019-192611-4CFNvg4m4xz2xGM-0@kdin01 X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC Convert the bindings document for Microchip KSZ Series Ethernet switches from txt to yaml. Signed-off-by: Christian Eggers --- .../devicetree/bindings/net/dsa/ksz.txt | 125 --------------- .../bindings/net/dsa/microchip,ksz.yaml | 147 ++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 148 insertions(+), 126 deletions(-) delete mode 100644 Documentation/devicetree/bindings/net/dsa/ksz.txt create mode 100644 Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml diff --git a/Documentation/devicetree/bindings/net/dsa/ksz.txt b/Documentation/devicetree/bindings/net/dsa/ksz.txt deleted file mode 100644 index 95e91e84151c..000000000000 --- a/Documentation/devicetree/bindings/net/dsa/ksz.txt +++ /dev/null @@ -1,125 +0,0 @@ -Microchip KSZ Series Ethernet switches -================================== - -Required properties: - -- compatible: For external switch chips, compatible string must be exactly one - of the following: - - "microchip,ksz8765" - - "microchip,ksz8794" - - "microchip,ksz8795" - - "microchip,ksz9477" - - "microchip,ksz9897" - - "microchip,ksz9896" - - "microchip,ksz9567" - - "microchip,ksz8565" - - "microchip,ksz9893" - - "microchip,ksz9563" - - "microchip,ksz8563" - -Optional properties: - -- reset-gpios : Should be a gpio specifier for a reset line -- microchip,synclko-125 : Set if the output SYNCLKO frequency should be set to - 125MHz instead of 25MHz. - -See Documentation/devicetree/bindings/net/dsa/dsa.txt for a list of additional -required and optional properties. - -Examples: - -Ethernet switch connected via SPI to the host, CPU port wired to eth0: - - eth0: ethernet@10001000 { - fixed-link { - speed = <1000>; - full-duplex; - }; - }; - - spi1: spi@f8008000 { - pinctrl-0 = <&pinctrl_spi_ksz>; - cs-gpios = <&pioC 25 0>; - id = <1>; - - ksz9477: ksz9477@0 { - compatible = "microchip,ksz9477"; - reg = <0>; - - spi-max-frequency = <44000000>; - spi-cpha; - spi-cpol; - - ports { - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - label = "lan1"; - }; - port@1 { - reg = <1>; - label = "lan2"; - }; - port@2 { - reg = <2>; - label = "lan3"; - }; - port@3 { - reg = <3>; - label = "lan4"; - }; - port@4 { - reg = <4>; - label = "lan5"; - }; - port@5 { - reg = <5>; - label = "cpu"; - ethernet = <ð0>; - fixed-link { - speed = <1000>; - full-duplex; - }; - }; - }; - }; - ksz8565: ksz8565@0 { - compatible = "microchip,ksz8565"; - reg = <0>; - - spi-max-frequency = <44000000>; - spi-cpha; - spi-cpol; - - ports { - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - label = "lan1"; - }; - port@1 { - reg = <1>; - label = "lan2"; - }; - port@2 { - reg = <2>; - label = "lan3"; - }; - port@3 { - reg = <3>; - label = "lan4"; - }; - port@6 { - reg = <6>; - label = "cpu"; - ethernet = <ð0>; - fixed-link { - speed = <1000>; - full-duplex; - }; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml new file mode 100644 index 000000000000..f93c3bdd0b83 --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml @@ -0,0 +1,147 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/dsa/microchip,ksz.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip KSZ Series Ethernet switches + +maintainers: + - Marek Vasut + - Woojung Huh + +properties: + # See Documentation/devicetree/bindings/net/dsa/dsa.yaml for a list of additional + # required and optional properties. + compatible: + enum: + - "microchip,ksz8765" + - "microchip,ksz8794" + - "microchip,ksz8795" + - "microchip,ksz9477" + - "microchip,ksz9897" + - "microchip,ksz9896" + - "microchip,ksz9567" + - "microchip,ksz8565" + - "microchip,ksz9893" + - "microchip,ksz9563" + - "microchip,ksz8563" + + reset-gpios: + description: + Should be a gpio specifier for a reset line. + maxItems: 1 + + microchip,synclko-125: + $ref: /schemas/types.yaml#/definitions/flag + description: + Set if the output SYNCLKO frequency should be set to 125MHz instead of 25MHz. + +required: + - compatible + - reg + +examples: + - | + #include + + // Ethernet switch connected via SPI to the host, CPU port wired to eth0: + eth0 { + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + spi0 { + #address-cells = <1>; + #size-cells = <0>; + + pinctrl-0 = <&pinctrl_spi_ksz>; + cs-gpios = <&pioC 25 0>; + id = <1>; + + ksz9477: ksz9477@0 { + compatible = "microchip,ksz9477"; + reg = <0>; + reset-gpios = <&gpio5 0 GPIO_ACTIVE_LOW>; + + spi-max-frequency = <44000000>; + spi-cpha; + spi-cpol; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + label = "lan1"; + }; + port@1 { + reg = <1>; + label = "lan2"; + }; + port@2 { + reg = <2>; + label = "lan3"; + }; + port@3 { + reg = <3>; + label = "lan4"; + }; + port@4 { + reg = <4>; + label = "lan5"; + }; + port@5 { + reg = <5>; + label = "cpu"; + ethernet = <ð0>; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; + + ksz8565: ksz8565@1 { + compatible = "microchip,ksz8565"; + reg = <1>; + + spi-max-frequency = <44000000>; + spi-cpha; + spi-cpol; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + label = "lan1"; + }; + port@1 { + reg = <1>; + label = "lan2"; + }; + port@2 { + reg = <2>; + label = "lan3"; + }; + port@3 { + reg = <3>; + label = "lan4"; + }; + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <ð0>; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index a4cba74383fa..8f56ced74cb1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11467,7 +11467,7 @@ M: Woojung Huh M: Microchip Linux Driver Support L: netdev@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/net/dsa/ksz.txt +F: Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml F: drivers/net/dsa/microchip/* F: include/linux/platform_data/microchip-ksz.h F: net/dsa/tag_ksz.c From patchwork Mon Oct 19 17:24:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11844843 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=-12.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, 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 41752C433DF for ; Mon, 19 Oct 2020 17:28:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E2001223C6 for ; Mon, 19 Oct 2020 17:28:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727476AbgJSR2q (ORCPT ); Mon, 19 Oct 2020 13:28:46 -0400 Received: from mailout04.rmx.de ([94.199.90.94]:56039 "EHLO mailout04.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725887AbgJSR2q (ORCPT ); Mon, 19 Oct 2020 13:28:46 -0400 Received: from kdin01.retarus.com (kdin01.dmz1.retloc [172.19.17.48]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout04.rmx.de (Postfix) with ESMTPS id 4CFNyb2jzYz3qf83; Mon, 19 Oct 2020 19:28:39 +0200 (CEST) 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 kdin01.retarus.com (Postfix) with ESMTPS id 4CFNxB3hrGz2xDw; Mon, 19 Oct 2020 19:27:26 +0200 (CEST) Received: from N95HX1G2.wgnetz.xx (192.168.54.91) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.408.0; Mon, 19 Oct 2020 19:25:58 +0200 From: Christian Eggers To: Vladimir Oltean , Florian Fainelli , Andrew Lunn , Vivien Didelot , Jakub Kicinski , Rob Herring CC: Helmut Grohne , Paul Barker , Codrin Ciubotariu , George McCollister , Marek Vasut , Tristram Ha , "David S . Miller" , Woojung Huh , "Microchip Linux Driver Support" , Christian Eggers , , , Subject: [RFC PATCH net-next 2/9] net: dsa: microchip: split ksz_common.h Date: Mon, 19 Oct 2020 19:24:28 +0200 Message-ID: <20201019172435.4416-3-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201019172435.4416-1-ceggers@arri.de> References: <20201019172435.4416-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.91] X-RMX-ID: 20201019-192726-4CFNxB3hrGz2xDw-0@kdin01 X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC Parts of ksz_common.h (struct ksz_device) will be required in net/dsa/tag_ksz.c soon. So move the relevant parts into a new header file. Signed-off-by: Christian Eggers --- MAINTAINERS | 1 + drivers/net/dsa/microchip/ksz_common.h | 81 +--------------------- include/linux/dsa/ksz_common.h | 96 ++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 80 deletions(-) create mode 100644 include/linux/dsa/ksz_common.h diff --git a/MAINTAINERS b/MAINTAINERS index 8f56ced74cb1..65bb9847e8e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11469,6 +11469,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/microchip/ksz_common.h F: include/linux/platform_data/microchip-ksz.h F: net/dsa/tag_ksz.c diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index cf866e48ff66..5735374b5bc3 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -7,92 +7,13 @@ #ifndef __KSZ_COMMON_H #define __KSZ_COMMON_H +#include #include -#include -#include -#include -#include -#include struct vlan_table { u32 table[3]; }; -struct ksz_port_mib { - struct mutex cnt_mutex; /* structure access */ - u8 cnt_ptr; - u64 *counters; -}; - -struct ksz_port { - u16 member; - u16 vid_member; - int stp_state; - struct phy_device phydev; - - u32 on:1; /* port is not disabled by hardware */ - u32 phy:1; /* port has a PHY */ - u32 fiber:1; /* port is fiber */ - u32 sgmii:1; /* port is SGMII */ - u32 force:1; - u32 read:1; /* read MIB counters in background */ - u32 freeze:1; /* MIB counter freeze is enabled */ - - struct ksz_port_mib mib; - phy_interface_t interface; -}; - -struct ksz_device { - struct dsa_switch *ds; - struct ksz_platform_data *pdata; - const char *name; - - struct mutex dev_mutex; /* device access */ - struct mutex regmap_mutex; /* regmap access */ - struct mutex alu_mutex; /* ALU access */ - struct mutex vlan_mutex; /* vlan access */ - const struct ksz_dev_ops *dev_ops; - - struct device *dev; - struct regmap *regmap[3]; - - void *priv; - - struct gpio_desc *reset_gpio; /* Optional reset GPIO */ - - /* chip specific data */ - u32 chip_id; - int num_vlans; - int num_alus; - int num_statics; - int cpu_port; /* port connected to CPU */ - int cpu_ports; /* port bitmap can be cpu port */ - int phy_port_cnt; - int port_cnt; - int reg_mib_cnt; - int mib_cnt; - int mib_port_cnt; - int last_port; /* ports after that not used */ - phy_interface_t compat_interface; - u32 regs_size; - bool phy_errata_9477; - bool synclko_125; - - struct vlan_table *vlan_cache; - - struct ksz_port *ports; - struct delayed_work mib_read; - unsigned long mib_read_interval; - u16 br_member; - u16 member; - u16 mirror_rx; - u16 mirror_tx; - u32 features; /* chip specific features */ - u32 overrides; /* chip functions set by user */ - u16 host_mask; - u16 port_mask; -}; - struct alu_struct { /* entry 1 */ u8 is_static:1; diff --git a/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h new file mode 100644 index 000000000000..3b22380d85c5 --- /dev/null +++ b/include/linux/dsa/ksz_common.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* Included by drivers/net/dsa/microchip/ksz_common.h and net/dsa/tag_ksz.c */ + +#ifndef _NET_DSA_KSZ_COMMON_H_ +#define _NET_DSA_KSZ_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct ksz_platform_data; +struct ksz_dev_ops; +struct vlan_table; + +struct ksz_port_mib { + struct mutex cnt_mutex; /* structure access */ + u8 cnt_ptr; + u64 *counters; +}; + +struct ksz_port { + u16 member; + u16 vid_member; + int stp_state; + struct phy_device phydev; + + u32 on:1; /* port is not disabled by hardware */ + u32 phy:1; /* port has a PHY */ + u32 fiber:1; /* port is fiber */ + u32 sgmii:1; /* port is SGMII */ + u32 force:1; + u32 read:1; /* read MIB counters in background */ + u32 freeze:1; /* MIB counter freeze is enabled */ + + struct ksz_port_mib mib; + phy_interface_t interface; +}; + +struct ksz_device { + struct dsa_switch *ds; + struct ksz_platform_data *pdata; + const char *name; + + struct mutex dev_mutex; /* device access */ + struct mutex regmap_mutex; /* regmap access */ + struct mutex alu_mutex; /* ALU access */ + struct mutex vlan_mutex; /* vlan access */ + const struct ksz_dev_ops *dev_ops; + + struct device *dev; + struct regmap *regmap[3]; + + void *priv; + + struct gpio_desc *reset_gpio; /* Optional reset GPIO */ + + /* chip specific data */ + u32 chip_id; + int num_vlans; + int num_alus; + int num_statics; + int cpu_port; /* port connected to CPU */ + int cpu_ports; /* port bitmap can be cpu port */ + int phy_port_cnt; + int port_cnt; + int reg_mib_cnt; + int mib_cnt; + int mib_port_cnt; + int last_port; /* ports after that not used */ + phy_interface_t compat_interface; + u32 regs_size; + bool phy_errata_9477; + bool synclko_125; + + struct vlan_table *vlan_cache; + + struct ksz_port *ports; + struct delayed_work mib_read; + unsigned long mib_read_interval; + u16 br_member; + u16 member; + u16 mirror_rx; + u16 mirror_tx; + u32 features; /* chip specific features */ + u32 overrides; /* chip functions set by user */ + u16 host_mask; + u16 port_mask; +}; + +#endif /* _NET_DSA_KSZ_COMMON_H_ */ From patchwork Mon Oct 19 17:24:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11844849 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=-12.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, 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 270E4C433DF for ; Mon, 19 Oct 2020 17:30:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BC15A2225F for ; Mon, 19 Oct 2020 17:30:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727623AbgJSRaB (ORCPT ); Mon, 19 Oct 2020 13:30:01 -0400 Received: from mailout08.rmx.de ([94.199.90.85]:39633 "EHLO mailout08.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725887AbgJSRaB (ORCPT ); Mon, 19 Oct 2020 13:30:01 -0400 Received: from kdin01.retarus.com (kdin01.dmz1.retloc [172.19.17.48]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout08.rmx.de (Postfix) with ESMTPS id 4CFP041pC8zMvJF; Mon, 19 Oct 2020 19:29:56 +0200 (CEST) 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 kdin01.retarus.com (Postfix) with ESMTPS id 4CFNyb2j6Kz2xFY; Mon, 19 Oct 2020 19:28:39 +0200 (CEST) Received: from N95HX1G2.wgnetz.xx (192.168.54.91) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.408.0; Mon, 19 Oct 2020 19:26:28 +0200 From: Christian Eggers To: Vladimir Oltean , Florian Fainelli , Andrew Lunn , Vivien Didelot , Jakub Kicinski , Rob Herring CC: Helmut Grohne , Paul Barker , Codrin Ciubotariu , George McCollister , Marek Vasut , Tristram Ha , "David S . Miller" , Woojung Huh , "Microchip Linux Driver Support" , Christian Eggers , , , Subject: [RFC PATCH net-next 3/9] net: dsa: microchip: rename ksz9477.c to ksz9477_main.o Date: Mon, 19 Oct 2020 19:24:29 +0200 Message-ID: <20201019172435.4416-4-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201019172435.4416-1-ceggers@arri.de> References: <20201019172435.4416-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.91] X-RMX-ID: 20201019-192845-4CFNyb2j6Kz2xFY-0@kdin01 X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC PTP functionality will be built into a separate source file (ksz9477_ptp.c). Signed-off-by: Christian Eggers --- 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 Mon Oct 19 17:24:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11844851 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=-12.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, 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 C7A1EC433DF for ; Mon, 19 Oct 2020 17:31:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7DAA1223EA for ; Mon, 19 Oct 2020 17:31:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727676AbgJSRb1 (ORCPT ); Mon, 19 Oct 2020 13:31:27 -0400 Received: from mailout06.rmx.de ([94.199.90.92]:34599 "EHLO mailout06.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726354AbgJSRb1 (ORCPT ); Mon, 19 Oct 2020 13:31:27 -0400 X-Greylist: delayed 315 seconds by postgrey-1.27 at vger.kernel.org; Mon, 19 Oct 2020 13:31:25 EDT Received: from kdin01.retarus.com (kdin01.dmz1.retloc [172.19.17.48]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout06.rmx.de (Postfix) with ESMTPS id 4CFP1k0xchz9tny; Mon, 19 Oct 2020 19:31:22 +0200 (CEST) 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 kdin01.retarus.com (Postfix) with ESMTPS id 4CFP0B75hxz2xKS; Mon, 19 Oct 2020 19:30:02 +0200 (CEST) Received: from N95HX1G2.wgnetz.xx (192.168.54.91) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.408.0; Mon, 19 Oct 2020 19:27:03 +0200 From: Christian Eggers To: Vladimir Oltean , Florian Fainelli , Andrew Lunn , Vivien Didelot , Jakub Kicinski , Rob Herring CC: Helmut Grohne , Paul Barker , Codrin Ciubotariu , George McCollister , Marek Vasut , Tristram Ha , "David S . Miller" , Woojung Huh , "Microchip Linux Driver Support" , Christian Eggers , , , Subject: [RFC PATCH net-next 4/9] dt-bindings: net: dsa: microchip,ksz: add interrupt property Date: Mon, 19 Oct 2020 19:24:30 +0200 Message-ID: <20201019172435.4416-5-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201019172435.4416-1-ceggers@arri.de> References: <20201019172435.4416-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.91] X-RMX-ID: 20201019-193007-4CFP0B75hxz2xKS-0@kdin01 X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC The devices have an optional interrupt line. Signed-off-by: Christian Eggers --- .../devicetree/bindings/net/dsa/microchip,ksz.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml index f93c3bdd0b83..be927a3a370b 100644 --- a/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml +++ b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml @@ -32,6 +32,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: @@ -44,6 +49,7 @@ required: examples: - | #include + #include // Ethernet switch connected via SPI to the host, CPU port wired to eth0: eth0 { @@ -65,6 +71,8 @@ examples: compatible = "microchip,ksz9477"; reg = <0>; reset-gpios = <&gpio5 0 GPIO_ACTIVE_LOW>; + interrupt-parent = <&gpio5>; + interrupts = <1 IRQ_TYPE_LEVEL_LOW>; /* INTRP_N line */ spi-max-frequency = <44000000>; spi-cpha; From patchwork Mon Oct 19 17:24:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11844853 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=-12.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, 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 15829C43467 for ; Mon, 19 Oct 2020 17:32:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A7233222C2 for ; Mon, 19 Oct 2020 17:32:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728101AbgJSRcl (ORCPT ); Mon, 19 Oct 2020 13:32:41 -0400 Received: from mailout04.rmx.de ([94.199.90.94]:60621 "EHLO mailout04.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726032AbgJSRcl (ORCPT ); Mon, 19 Oct 2020 13:32:41 -0400 Received: from kdin01.retarus.com (kdin01.dmz1.retloc [172.19.17.48]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout04.rmx.de (Postfix) with ESMTPS id 4CFP3705xTz3qnc1; Mon, 19 Oct 2020 19:32:35 +0200 (CEST) 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 kdin01.retarus.com (Postfix) with ESMTPS id 4CFP1k0rzdz2xD9; Mon, 19 Oct 2020 19:31:22 +0200 (CEST) Received: from N95HX1G2.wgnetz.xx (192.168.54.91) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.408.0; Mon, 19 Oct 2020 19:27:33 +0200 From: Christian Eggers To: Vladimir Oltean , Florian Fainelli , Andrew Lunn , Vivien Didelot , Jakub Kicinski , Rob Herring CC: Helmut Grohne , Paul Barker , Codrin Ciubotariu , George McCollister , Marek Vasut , Tristram Ha , "David S . Miller" , Woojung Huh , "Microchip Linux Driver Support" , Christian Eggers , , , Subject: [RFC PATCH net-next 5/9] net: dsa: microchip: ksz9477: basic interrupt support Date: Mon, 19 Oct 2020 19:24:31 +0200 Message-ID: <20201019172435.4416-6-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201019172435.4416-1-ceggers@arri.de> References: <20201019172435.4416-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.91] X-RMX-ID: 20201019-193122-4CFP1k0rzdz2xD9-0@kdin01 X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC 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 actually enabled nor handled. ksz9477_reset_switch() must be called before requesting the IRQ (in ksz9477_init() instead of ksz9477_setup()). Signed-off-by: Christian Eggers --- drivers/net/dsa/microchip/ksz9477_i2c.c | 2 + drivers/net/dsa/microchip/ksz9477_main.c | 103 +++++++++++++++++++++-- drivers/net/dsa/microchip/ksz9477_spi.c | 2 + include/linux/dsa/ksz_common.h | 1 + 4 files changed, 100 insertions(+), 8 deletions(-) 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 abfd3802bb51..6b5a981fb21f 100644 --- a/drivers/net/dsa/microchip/ksz9477_main.c +++ b/drivers/net/dsa/microchip/ksz9477_main.c @@ -7,7 +7,9 @@ #include #include +#include #include +#include #include #include #include @@ -1345,19 +1347,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); @@ -1535,12 +1530,84 @@ 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; + irqreturn_t result = IRQ_NONE; + + /* Read global port interrupt status register */ + ret = ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data); + if (ret) + return result; + + for (port = 0; port < dev->port_cnt; port++) { + if (data & BIT(port)) { + u8 data8; + + /* Read port interrupt status register */ + ret = ksz_read8(dev, PORT_CTRL_ADDR(port, REG_PORT_INT_STATUS), + &data8); + if (ret) + return result; + + /* ToDo: Add specific handling of port interrupts */ + } + } + + return result; +} + +static int ksz9477_enable_port_interrupts(struct ksz_device *dev) +{ + u32 data; + int ret; + + ret = ksz_read32(dev, REG_SW_PORT_INT_MASK__4, &data); + if (ret) + return ret; + + /* Enable port interrupts (0 means enabled) */ + data &= ~((1 << dev->port_cnt) - 1); + ret = ksz_write32(dev, REG_SW_PORT_INT_MASK__4, data); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_disable_port_interrupts(struct ksz_device *dev) +{ + u32 data; + int ret; + + ret = ksz_read32(dev, REG_SW_PORT_INT_MASK__4, &data); + if (ret) + return ret; + + /* Disable port interrupts (1 means disabled) */ + data |= ((1 << dev->port_cnt) - 1); + ret = ksz_write32(dev, REG_SW_PORT_INT_MASK__4, data); + if (ret) + return ret; + + return 0; +} + 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]; @@ -1584,12 +1651,32 @@ 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) { + unsigned long irqflags = irqd_get_trigger_type(irq_get_irq_data(dev->irq)); + + irqflags |= IRQF_ONESHOT; + ret = devm_request_threaded_irq(dev->dev, dev->irq, NULL, + ksz9477_switch_irq_thread, + irqflags, + dev_name(dev->dev), + dev); + if (ret) { + dev_err(dev->dev, "failed to request IRQ.\n"); + return ret; + } + + ret = ksz9477_enable_port_interrupts(dev); + if (ret) + return ret; + } return 0; } static void ksz9477_switch_exit(struct ksz_device *dev) { + if (dev->irq > 0) + ksz9477_disable_port_interrupts(dev); ksz9477_reset_switch(dev); } diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c index 1142768969c2..d2eea9596e53 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; + ret = ksz9477_switch_register(dev); /* Main DSA driver may not be started yet. */ diff --git a/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h index 3b22380d85c5..bf57ba4b2132 100644 --- a/include/linux/dsa/ksz_common.h +++ b/include/linux/dsa/ksz_common.h @@ -55,6 +55,7 @@ struct ksz_device { struct device *dev; struct regmap *regmap[3]; + int irq; void *priv; From patchwork Mon Oct 19 17:24:32 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11844855 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=-9.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, 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 CC3A8C433E7 for ; Mon, 19 Oct 2020 17:34:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6767C222EC for ; Mon, 19 Oct 2020 17:34:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727395AbgJSRd7 (ORCPT ); Mon, 19 Oct 2020 13:33:59 -0400 Received: from mailout07.rmx.de ([94.199.90.95]:60875 "EHLO mailout07.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726342AbgJSRd6 (ORCPT ); Mon, 19 Oct 2020 13:33:58 -0400 Received: from kdin01.retarus.com (kdin01.dmz1.retloc [172.19.17.48]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout07.rmx.de (Postfix) with ESMTPS id 4CFP4b6MlJzBvZl; Mon, 19 Oct 2020 19:33:51 +0200 (CEST) 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 kdin01.retarus.com (Postfix) with ESMTPS id 4CFP3703j6z2xb4; Mon, 19 Oct 2020 19:32:35 +0200 (CEST) Received: from N95HX1G2.wgnetz.xx (192.168.54.91) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.408.0; Mon, 19 Oct 2020 19:28:08 +0200 From: Christian Eggers To: Vladimir Oltean , Florian Fainelli , Andrew Lunn , Vivien Didelot , Jakub Kicinski , Rob Herring CC: Helmut Grohne , Paul Barker , Codrin Ciubotariu , George McCollister , Marek Vasut , Tristram Ha , "David S . Miller" , Woojung Huh , "Microchip Linux Driver Support" , Christian Eggers , , , Subject: [RFC PATCH net-next 6/9] net: dsa: microchip: ksz9477: add Posix clock support for chip PTP clock Date: Mon, 19 Oct 2020 19:24:32 +0200 Message-ID: <20201019172435.4416-7-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201019172435.4416-1-ceggers@arri.de> References: <20201019172435.4416-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.91] X-RMX-ID: 20201019-193241-4CFP3703j6z2xb4-0@kdin01 X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC Implement routines (adjfine, adjtime, gettime and settime) for manipulating the chip's PTP clock. Signed-off-by: Christian Eggers --- drivers/net/dsa/microchip/Kconfig | 9 + 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 | 301 +++++++++++++++++++++++ drivers/net/dsa/microchip/ksz9477_ptp.h | 27 ++ drivers/net/dsa/microchip/ksz9477_spi.c | 2 +- drivers/net/dsa/microchip/ksz_common.h | 1 + include/linux/dsa/ksz_common.h | 7 + 9 files changed, 365 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..aa33eeaabdbf 100644 --- a/drivers/net/dsa/microchip/Kconfig +++ b/drivers/net/dsa/microchip/Kconfig @@ -24,6 +24,15 @@ 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" + default n + depends on NET_DSA_MICROCHIP_KSZ9477 + imply 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 6b5a981fb21f..7d623400139f 100644 --- a/drivers/net/dsa/microchip/ksz9477_main.c +++ b/drivers/net/dsa/microchip/ksz9477_main.c @@ -18,6 +18,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) @@ -1719,10 +1720,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..44d7bbdea518 --- /dev/null +++ b/drivers/net/dsa/microchip/ksz9477_ptp.c @@ -0,0 +1,301 @@ +// 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; + + if (scaled_ppm) { + /* 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) */ + s64 ppb, adj; + u32 data32; + + /* 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) + return ret; + } + + ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data16); + if (ret) + return ret; + + 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) + return ret; + + return 0; +} + +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 hw */ + 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 -ENOTTY; +} + +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; + ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data); + if (ret) + return ret; + + return 0; +} + +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 d2eea9596e53..e49d581547ac 100644 --- a/drivers/net/dsa/microchip/ksz9477_spi.c +++ b/drivers/net/dsa/microchip/ksz9477_spi.c @@ -66,7 +66,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 5735374b5bc3..96808d2585d9 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -67,6 +67,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); diff --git a/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h index bf57ba4b2132..4d5b6cc9429a 100644 --- a/include/linux/dsa/ksz_common.h +++ b/include/linux/dsa/ksz_common.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +93,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; +#endif }; #endif /* _NET_DSA_KSZ_COMMON_H_ */ From patchwork Mon Oct 19 17:24:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11844857 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=-12.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, 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 239D0C433DF for ; Mon, 19 Oct 2020 17:35:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 95521222EC for ; Mon, 19 Oct 2020 17:35:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727473AbgJSRf0 (ORCPT ); Mon, 19 Oct 2020 13:35:26 -0400 Received: from mailout02.rmx.de ([62.245.148.41]:53287 "EHLO mailout02.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726138AbgJSRf0 (ORCPT ); Mon, 19 Oct 2020 13:35:26 -0400 Received: from kdin01.retarus.com (kdin01.dmz1.retloc [172.19.17.48]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout02.rmx.de (Postfix) with ESMTPS id 4CFP685YRyzNrKW; Mon, 19 Oct 2020 19:35:12 +0200 (CEST) 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 kdin01.retarus.com (Postfix) with ESMTPS id 4CFP4b6F2Sz2xFM; Mon, 19 Oct 2020 19:33:51 +0200 (CEST) Received: from N95HX1G2.wgnetz.xx (192.168.54.91) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.408.0; Mon, 19 Oct 2020 19:28:43 +0200 From: Christian Eggers To: Vladimir Oltean , Florian Fainelli , Andrew Lunn , Vivien Didelot , Jakub Kicinski , Rob Herring CC: Helmut Grohne , Paul Barker , Codrin Ciubotariu , George McCollister , Marek Vasut , Tristram Ha , "David S . Miller" , Woojung Huh , "Microchip Linux Driver Support" , Christian Eggers , , , Subject: [RFC PATCH net-next 7/9] net: dsa: microchip: ksz9477: add hardware time stamping support Date: Mon, 19 Oct 2020 19:24:33 +0200 Message-ID: <20201019172435.4416-8-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201019172435.4416-1-ceggers@arri.de> References: <20201019172435.4416-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.91] X-RMX-ID: 20201019-193353-4CFP4b6F2Sz2xFM-0@kdin01 X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC Add 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. PTP mode is permanently enabled (changes tail tag; depends on CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP).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. linuxptp assumes that the RX time stamp has already been subtracted from the PDelay_Req correction field (as done by the TI PHYTER). linuxptp 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 doesn't support negative values on this field. Of course, the UDP checksums (if any) have to be corrected after this (for both directions). The PTP hardware performs internal detection of PTP frames (likely similar as ptp_classify_raw() and ptp_parse_header()). As these filters cannot be disabled, the current delay mode (E2E/P2P) and the clock mode (master/slave) must be configured via sysfs attributes. Time stamping will only be performed on PTP packets matching the current mode settings. Everything has been tested on a Microchip KSZ9563 switch. Signed-off-by: Christian Eggers --- drivers/net/dsa/microchip/ksz9477_main.c | 9 +- drivers/net/dsa/microchip/ksz9477_ptp.c | 629 +++++++++++++++++++++++ drivers/net/dsa/microchip/ksz9477_ptp.h | 27 + include/linux/dsa/ksz_common.h | 30 ++ net/dsa/tag_ksz.c | 206 +++++++- 5 files changed, 893 insertions(+), 8 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477_main.c b/drivers/net/dsa/microchip/ksz9477_main.c index 7d623400139f..42cd17c8c25d 100644 --- a/drivers/net/dsa/microchip/ksz9477_main.c +++ b/drivers/net/dsa/microchip/ksz9477_main.c @@ -1388,6 +1388,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, @@ -1408,6 +1409,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 = ksz9477_ptp_port_txtstamp, + /* never defer rx delivery, tstamping is done via tail tagging */ + .port_rxtstamp = NULL, }; static u32 ksz9477_get_port_addr(int port, int offset) @@ -1554,7 +1560,8 @@ static irqreturn_t ksz9477_switch_irq_thread(int irq, void *dev_id) if (ret) return result; - /* ToDo: Add specific handling of port interrupts */ + if (data8 & PORT_PTP_INT) + result |= ksz9477_ptp_port_interrupt(dev, port); } } diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.c b/drivers/net/dsa/microchip/ksz9477_ptp.c index 44d7bbdea518..d4bcfff0577e 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" @@ -71,8 +74,10 @@ 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 timespec64 delta64 = ns_to_timespec64(delta); s32 sec, nsec; u16 data16; + unsigned long flags; int ret; mutex_lock(&dev->ptp_mutex); @@ -103,6 +108,10 @@ static int ksz9477_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) if (ret) goto error_return; + spin_lock_irqsave(&dev->ptp_clock_lock, flags); + dev->ptp_clock_time = timespec64_add(dev->ptp_clock_time, delta64); + spin_unlock_irqrestore(&dev->ptp_clock_lock, flags); + error_return: mutex_unlock(&dev->ptp_mutex); return ret; @@ -160,6 +169,7 @@ static int ksz9477_ptp_settime(struct ptp_clock_info *ptp, struct timespec64 con { struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); u16 data16; + unsigned long flags; int ret; mutex_lock(&dev->ptp_mutex); @@ -198,6 +208,10 @@ static int ksz9477_ptp_settime(struct ptp_clock_info *ptp, struct timespec64 con if (ret) goto error_return; + spin_lock_irqsave(&dev->ptp_clock_lock, flags); + dev->ptp_clock_time = *ts; + spin_unlock_irqrestore(&dev->ptp_clock_lock, flags); + error_return: mutex_unlock(&dev->ptp_mutex); return ret; @@ -208,9 +222,27 @@ static int ksz9477_ptp_enable(struct ptp_clock_info *ptp, struct ptp_clock_reque return -ENOTTY; } +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; + unsigned long flags; + + mutex_lock(&dev->ptp_mutex); + _ksz9477_ptp_gettime(dev, &ts); + mutex_unlock(&dev->ptp_mutex); + + spin_lock_irqsave(&dev->ptp_clock_lock, flags); + dev->ptp_clock_time = ts; + spin_unlock_irqrestore(&dev->ptp_clock_lock, flags); + + return HZ; /* reschedule in 1 second */ +} + static int ksz9477_ptp_start_clock(struct ksz_device *dev) { u16 data; + unsigned long flags; int ret; ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data); @@ -230,6 +262,11 @@ static int ksz9477_ptp_start_clock(struct ksz_device *dev) if (ret) return ret; + spin_lock_irqsave(&dev->ptp_clock_lock, flags); + dev->ptp_clock_time.tv_sec = 0; + dev->ptp_clock_time.tv_nsec = 0; + spin_unlock_irqrestore(&dev->ptp_clock_lock, flags); + return 0; } @@ -251,11 +288,349 @@ static int ksz9477_ptp_stop_clock(struct ksz_device *dev) return 0; } +/* Time stamping support */ + +static int ksz9477_ptp_enable_mode(struct ksz_device *dev) +{ + u16 data; + int ret; + + ret = ksz_read16(dev, REG_PTP_MSG_CONF1, &data); + if (ret) + return ret; + + /* Enable PTP mode */ + data |= PTP_ENABLE; + ret = ksz_write16(dev, REG_PTP_MSG_CONF1, data); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_ptp_disable_mode(struct ksz_device *dev) +{ + u16 data; + int ret; + + ret = ksz_read16(dev, REG_PTP_MSG_CONF1, &data); + if (ret) + return ret; + + /* Disable PTP mode */ + data &= ~PTP_ENABLE; + ret = ksz_write16(dev, REG_PTP_MSG_CONF1, data); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_ptp_enable_port_ptp_interrupts(struct ksz_device *dev, int port) +{ + u32 addr = PORT_CTRL_ADDR(port, REG_PORT_INT_MASK); + u8 data; + int ret; + + ret = ksz_read8(dev, addr, &data); + if (ret) + return ret; + + /* Enable port PTP interrupt (0 means enabled) */ + data &= ~PORT_PTP_INT; + ret = ksz_write8(dev, addr, data); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_ptp_disable_port_ptp_interrupts(struct ksz_device *dev, int port) +{ + u32 addr = PORT_CTRL_ADDR(port, REG_PORT_INT_MASK); + u8 data; + int ret; + + ret = ksz_read8(dev, addr, &data); + if (ret) + return ret; + + /* Enable port PTP interrupt (1 means disabled) */ + data |= PORT_PTP_INT; + ret = ksz_write8(dev, addr, data); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_ptp_enable_port_egress_interrupts(struct ksz_device *dev, int port) +{ + 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; + + /* Enable port xdelay egress timestamp interrupt (1 means enabled) */ + data |= PTP_PORT_XDELAY_REQ_INT; + ret = ksz_write16(dev, addr, data); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_ptp_disable_port_egress_interrupts(struct ksz_device *dev, int port) +{ + 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; + + /* Disable port xdelay egress timestamp interrupts (0 means disabled) */ + data &= PTP_PORT_XDELAY_REQ_INT; + ret = ksz_write16(dev, addr, data); + if (ret) + return ret; + + return 0; +} + +static int ksz9477_ptp_port_init(struct ksz_device *dev, int port) +{ + struct ksz_port *prt = &dev->ports[port]; + int ret; + + /* Read rx and tx delay from port registers */ + ret = ksz_read16(dev, PORT_CTRL_ADDR(port, REG_PTP_PORT_RX_DELAY__2), + &prt->tstamp_rx_latency_ns); + if (ret) + return ret; + + ret = ksz_read16(dev, PORT_CTRL_ADDR(port, REG_PTP_PORT_TX_DELAY__2), + &prt->tstamp_tx_latency_ns); + if (ret) + return ret; + + if (port != dev->cpu_port) { + ret = ksz9477_ptp_enable_port_ptp_interrupts(dev, port); + if (ret) + return ret; + + ret = ksz9477_ptp_enable_port_egress_interrupts(dev, port); + if (ret) + goto error_disable_port_ptp_interrupts; + } + + return 0; + +error_disable_port_ptp_interrupts: + if (port != dev->cpu_port) + ksz9477_ptp_disable_port_ptp_interrupts(dev, port); + return ret; +} + +static void ksz9477_ptp_port_deinit(struct ksz_device *dev, int port) +{ + if (port != dev->cpu_port) { + ksz9477_ptp_disable_port_egress_interrupts(dev, port); + ksz9477_ptp_disable_port_ptp_interrupts(dev, port); + } +} + +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: + for (--port; port >= 0; --port) + ksz9477_ptp_port_deinit(dev, port); + return ret; +} + +static void ksz9477_ptp_ports_deinit(struct ksz_device *dev) +{ + int port; + + for (port = dev->port_cnt - 1; port >= 0; --port) + ksz9477_ptp_port_deinit(dev, port); +} + +/* device attributes */ + +enum ksz9477_ptp_tcmode { + KSZ9477_PTP_TCMODE_E2E, + KSZ9477_PTP_TCMODE_P2P, +}; + +static int ksz9477_ptp_tcmode_get(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; + + *tcmode = (data & PTP_TC_P2P) ? KSZ9477_PTP_TCMODE_P2P : KSZ9477_PTP_TCMODE_E2E; + + return 0; +} + +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; + + ret = ksz_write16(dev, REG_PTP_MSG_CONF1, data); + if (ret) + return ret; + + return 0; +} + +static ssize_t tcmode_show(struct device *dev, struct device_attribute *attr __always_unused, + char *buf) +{ + struct ksz_device *ksz = dev_get_drvdata(dev); + enum ksz9477_ptp_tcmode tcmode; + int ret = ksz9477_ptp_tcmode_get(ksz, &tcmode); + + if (ret) + return ret; + + return sprintf(buf, "%s\n", tcmode == KSZ9477_PTP_TCMODE_P2P ? "P2P" : "E2E"); +} + +static ssize_t tcmode_store(struct device *dev, struct device_attribute *attr __always_unused, + char const *buf, size_t count) +{ + struct ksz_device *ksz = dev_get_drvdata(dev); + int ret; + + if (strcasecmp(buf, "E2E") == 0) + ret = ksz9477_ptp_tcmode_set(ksz, KSZ9477_PTP_TCMODE_E2E); + else if (strcasecmp(buf, "P2P") == 0) + ret = ksz9477_ptp_tcmode_set(ksz, KSZ9477_PTP_TCMODE_P2P); + else + return -EINVAL; + + return ret ? ret : (ssize_t)count; +} + +static DEVICE_ATTR_RW(tcmode); + +enum ksz9477_ptp_ocmode { + KSZ9477_PTP_OCMODE_SLAVE, + KSZ9477_PTP_OCMODE_MASTER, +}; + +static int ksz9477_ptp_ocmode_get(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; + + *ocmode = (data & PTP_MASTER) ? KSZ9477_PTP_OCMODE_MASTER : KSZ9477_PTP_OCMODE_SLAVE; + + return 0; +} + +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; + + ret = ksz_write16(dev, REG_PTP_MSG_CONF1, data); + if (ret) + return ret; + + return 0; +} + +static ssize_t ocmode_show(struct device *dev, struct device_attribute *attr __always_unused, + char *buf) +{ + struct ksz_device *ksz = dev_get_drvdata(dev); + enum ksz9477_ptp_ocmode ocmode; + int ret = ksz9477_ptp_ocmode_get(ksz, &ocmode); + + if (ret) + return ret; + + return sprintf(buf, "%s\n", ocmode == KSZ9477_PTP_OCMODE_MASTER ? "master" : "slave"); +} + +static ssize_t ocmode_store(struct device *dev, struct device_attribute *attr __always_unused, + char const *buf, size_t count) +{ + struct ksz_device *ksz = dev_get_drvdata(dev); + int ret; + + if (strcasecmp(buf, "master") == 0) + ret = ksz9477_ptp_ocmode_set(ksz, KSZ9477_PTP_OCMODE_MASTER); + else if (strcasecmp(buf, "slave") == 0) + ret = ksz9477_ptp_ocmode_set(ksz, KSZ9477_PTP_OCMODE_SLAVE); + else + return -EINVAL; + + return ret ? ret : (ssize_t)count; +} + +static DEVICE_ATTR_RW(ocmode); + +static struct attribute *ksz9477_ptp_attrs[] = { + &dev_attr_tcmode.attr, + &dev_attr_ocmode.attr, + NULL, +}; + +static struct attribute_group ksz9477_ptp_attrgrp = { + .attrs = ksz9477_ptp_attrs, +}; + int ksz9477_ptp_init(struct ksz_device *dev) { int ret; mutex_init(&dev->ptp_mutex); + spin_lock_init(&dev->ptp_clock_lock); /* PTP clock properties */ @@ -275,6 +650,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); @@ -287,8 +663,36 @@ int ksz9477_ptp_init(struct ksz_device *dev) goto error_stop_clock; } + /* Enable PTP mode (will affect tail tagging format) */ + ret = ksz9477_ptp_enable_mode(dev); + if (ret) + goto error_unregister_clock; + + /* Init switch ports */ + ret = ksz9477_ptp_ports_init(dev); + if (ret) + goto error_disable_mode; + + /* Init attributes */ + ret = sysfs_create_group(&dev->dev->kobj, &ksz9477_ptp_attrgrp); + if (ret) + goto error_ports_deinit; + + /* Schedule cyclic call of ksz_ptp_do_aux_work() */ + ret = ptp_schedule_worker(dev->ptp_clock, 0); + if (ret) + goto error_device_remove_attrgrp; + return 0; +error_device_remove_attrgrp: + sysfs_remove_group(&dev->dev->kobj, &ksz9477_ptp_attrgrp); +error_ports_deinit: + ksz9477_ptp_ports_deinit(dev); +error_disable_mode: + ksz9477_ptp_disable_mode(dev); +error_unregister_clock: + ptp_clock_unregister(dev->ptp_clock); error_stop_clock: ksz9477_ptp_stop_clock(dev); return ret; @@ -296,6 +700,231 @@ int ksz9477_ptp_init(struct ksz_device *dev) void ksz9477_ptp_deinit(struct ksz_device *dev) { + sysfs_remove_group(&dev->dev->kobj, &ksz9477_ptp_attrgrp); + ksz9477_ptp_ports_deinit(dev); + ksz9477_ptp_disable_mode(dev); 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]; + irqreturn_t result = IRQ_NONE; + u16 data; + int ret; + + ret = ksz_read16(dev, addr, &data); + if (ret) + return IRQ_NONE; + + if ((data & PTP_PORT_XDELAY_REQ_INT) && prt->tstamp_tx_xdelay_skb) { + /* Timestamp for Pdelay_Req / Delay_Req */ + u32 tstamp_raw; + struct skb_shared_hwtstamps shhwtstamps; + struct sk_buff *tmp_skb; + + /* In contrast to the KSZ9563R data sheet, the format of the + * port time stamp registers is 2 bit seconds + 30 bit nano + * seconds (same as in the tail tags). + */ + ret = ksz_read32(dev, PORT_CTRL_ADDR(port, REG_PTP_PORT_XDELAY_TS), &tstamp_raw); + if (ret) + return result; + + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ksz9477_tstamp_to_clock(dev, tstamp_raw, + prt->tstamp_tx_latency_ns); + + /* skb_complete_tx_timestamp() will free up the client to make + * another timestamp-able transmit. We have to be ready for it + * -- by clearing the ps->tx_skb "flag" -- beforehand. + */ + + tmp_skb = prt->tstamp_tx_xdelay_skb; + prt->tstamp_tx_xdelay_skb = NULL; + clear_bit_unlock(KSZ_HWTSTAMP_TX_XDELAY_IN_PROGRESS, &prt->tstamp_state); + skb_complete_tx_timestamp(tmp_skb, &shhwtstamps); + } + + /* 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 __always_unused, + 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]; + bool tstamp_enable = false; + + /* Prevent the TX/RX paths from trying to interact with the + * timestamp hardware while we reconfigure it. + */ + clear_bit_unlock(KSZ_HWTSTAMP_ENABLED, &prt->tstamp_state); + + /* reserved for future extensions */ + if (config->flags) + return -EINVAL; + + switch (config->tx_type) { + case HWTSTAMP_TX_OFF: + tstamp_enable = false; + break; + case HWTSTAMP_TX_ONESTEP_P2P: + tstamp_enable = true; + break; + default: + return -ERANGE; + } + + switch (config->rx_filter) { + case HWTSTAMP_FILTER_NONE: + tstamp_enable = false; + break; + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; /* UDPv4/UDPv6 */ + break; + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; /* 802.3 ether */ + break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; /* UDP / 802.3 */ + break; + case HWTSTAMP_FILTER_ALL: + default: + config->rx_filter = HWTSTAMP_FILTER_NONE; + return -ERANGE; + } + + /* Once hardware has been configured, enable timestamp checks + * in the RX/TX paths. + */ + if (tstamp_enable) + set_bit(KSZ_HWTSTAMP_ENABLED, &prt->tstamp_state); + + return 0; +} + +int ksz9477_ptp_port_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr) +{ + struct ksz_device *dev = ds->priv; + struct hwtstamp_config *port_tstamp_config = &dev->ports[port].tstamp_config; + + return copy_to_user(ifr->ifr_data, + port_tstamp_config, sizeof(*port_tstamp_config)) ? -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 *port_tstamp_config = &dev->ports[port].tstamp_config; + struct hwtstamp_config config; + 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(port_tstamp_config, &config, sizeof(config)); + + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; +} + +/* Returns a pointer to the PTP header if the caller should time stamp, + * or NULL if the caller should not. + */ +static struct ptp_header *ksz9477_ptp_should_tstamp(struct ksz_port *port, struct sk_buff *skb, + unsigned int type) +{ + if (!test_bit(KSZ_HWTSTAMP_ENABLED, &port->tstamp_state)) + return NULL; + + return ptp_parse_header(skb, type); +} + +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]; + enum ksz9477_ptp_event_messages msg_type; + struct ptp_header *hdr; + + /* KSZ9563 supports PTPv2 only */ + if (!(type & PTP_CLASS_V2)) + return false; + + /* Should already been tested in dsa_skb_tx_timestamp()? */ + if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP)) + return false; + + hdr = ksz9477_ptp_should_tstamp(prt, clone, type); + if (!hdr) + return false; + + msg_type = ptp_get_msgtype(hdr, type); + + switch (msg_type) { + /* As the KSZ9563 always performs one step time stamping, only the time + * stamp for Delay_Req and Pdelay_Req are 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. + */ + case PTP_Event_Message_Delay_Req: + case PTP_Event_Message_Pdelay_Req: + if (test_and_set_bit_lock(KSZ_HWTSTAMP_TX_XDELAY_IN_PROGRESS, + &prt->tstamp_state)) + return false; /* free cloned skb */ + + prt->tstamp_tx_xdelay_skb = clone; + break; + + default: + return false; /* free cloned skb */ + } + + return true; /* keep cloned skb */ +} diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.h b/drivers/net/dsa/microchip/ksz9477_ptp.h index 0076538419fa..e8d50a086311 100644 --- a/drivers/net/dsa/microchip/ksz9477_ptp.h +++ b/drivers/net/dsa/microchip/ksz9477_ptp.h @@ -10,6 +10,9 @@ #ifndef DRIVERS_NET_DSA_MICROCHIP_KSZ9477_PTP_H_ #define DRIVERS_NET_DSA_MICROCHIP_KSZ9477_PTP_H_ +#include +#include + #include "ksz_common.h" #if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) @@ -17,11 +20,35 @@ 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; } + +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; } + +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/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h index 4d5b6cc9429a..10b42154ba5a 100644 --- a/include/linux/dsa/ksz_common.h +++ b/include/linux/dsa/ksz_common.h @@ -8,8 +8,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -25,6 +27,12 @@ struct ksz_port_mib { u64 *counters; }; +/* state flags for ksz_port::tstamp_state */ +enum { + KSZ_HWTSTAMP_ENABLED, + KSZ_HWTSTAMP_TX_XDELAY_IN_PROGRESS, +}; + struct ksz_port { u16 member; u16 vid_member; @@ -41,6 +49,13 @@ struct ksz_port { struct ksz_port_mib mib; phy_interface_t interface; +#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) + u16 tstamp_rx_latency_ns; /* rx delay from wire to tstamp unit */ + u16 tstamp_tx_latency_ns; /* tx delay from tstamp unit to wire */ + struct hwtstamp_config tstamp_config; + struct sk_buff *tstamp_tx_xdelay_skb; + unsigned long tstamp_state; +#endif }; struct ksz_device { @@ -98,7 +113,22 @@ struct ksz_device { struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_caps; struct mutex ptp_mutex; + spinlock_t ptp_clock_lock; /* for ptp_clock_time */ + /* approximated current time, read once per second from hardware */ + struct timespec64 ptp_clock_time; #endif }; +/* KSZ9563 will only timestamp event messages */ +enum ksz9477_ptp_event_messages { + PTP_Event_Message_Sync = 0x0, + PTP_Event_Message_Delay_Req = 0x1, + PTP_Event_Message_Pdelay_Req = 0x2, + PTP_Event_Message_Pdelay_Resp = 0x3, +}; + +/* net/dsa/tag_ksz.c */ +ktime_t ksz9477_tstamp_to_clock(struct ksz_device *ksz, u32 tstamp, + int offset_ns); + #endif /* _NET_DSA_KSZ_COMMON_H_ */ diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c index 4820dbcedfa2..9dbe2fde3db0 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,26 +91,210 @@ 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) +#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) +static inline __wsum ksz9477_check_diff8(__be64 old, __be64 new, __wsum oldsum) +{ + __be64 diff[2] = { ~old, new }; + + return csum_partial(diff, sizeof(diff), oldsum); +} + +/* Replace content of PTP header's correction field and update UDP checksum */ +static void ksz9477_update_ptp_correction_field(struct sk_buff *skb, unsigned int type, + struct ptp_header *ptp_header, u64 value) +{ + u8 *ptr = skb_mac_header(skb); + struct udphdr *uhdr = NULL; + __be64 correction_old; + + /* previous correction value is required for checksum update. */ + memcpy(&correction_old, &ptp_header->correction, sizeof(correction_old)); + + /* write new correction value */ + put_unaligned_be64(value, &ptp_header->correction); + + /* locate udp header */ + if (type & PTP_CLASS_VLAN) + ptr += VLAN_HLEN; + + ptr += ETH_HLEN; + + switch (type & PTP_CLASS_PMASK) { + case PTP_CLASS_IPV4: + ptr += ((struct iphdr *)ptr)->ihl << 2; + uhdr = (struct udphdr *)ptr; + break; + case PTP_CLASS_IPV6: + ptr += IP6_HLEN; + uhdr = (struct udphdr *)ptr; + break; + } + + if (uhdr) { + /* update checksum */ + uhdr->check = csum_fold(ksz9477_check_diff8(correction_old, ptp_header->correction, + ~csum_unfold(uhdr->check))); + if (!uhdr->check) + uhdr->check = CSUM_MANGLED_0; + } +} + +/* Time stamp tag is only inserted if PTP is enabled in hardware. */ +static void ksz9477_xmit_timestamp(struct sk_buff *skb) +{ + enum ksz9477_ptp_event_messages msg_type; + struct ptp_header *ptp_hdr; + unsigned int ptp_type; + u32 tstamp_raw = 0; + u64 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, so it must be moved to the time stamp + * field in the tail tag. + */ + + /* Check PTP message type */ + ptp_type = ptp_classify_raw(skb); + + 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; + + msg_type = ptp_get_msgtype(ptp_hdr, ptp_type); + if (msg_type != PTP_Event_Message_Pdelay_Resp) + goto out_put_tag; + + correction = get_unaligned_be64(&ptp_hdr->correction); + if ((s64)correction < 0) { + struct timespec64 ts; + + /* Move ingress time stamp from PTP header's correction field to tail tag. */ + ts = ns_to_timespec64(-((s64)correction)); + tstamp_raw = ((ts.tv_sec & 3) << 30) | ts.tv_nsec; + correction = 0; + ksz9477_update_ptp_correction_field(skb, ptp_type, ptp_hdr, correction); + } + +out_put_tag: + put_unaligned_be32(tstamp_raw, skb_put(skb, KSZ9477_PTP_TAG_LEN)); +} + +ktime_t ksz9477_tstamp_to_clock(struct ksz_device *ksz, u32 tstamp, int offset_ns) +{ + struct timespec64 ptp_clock_time; + /* Split up time stamp, 2 bit seconds, 30 bit nano seconds */ + struct timespec64 ts = { + .tv_sec = tstamp >> 30, + .tv_nsec = tstamp & (BIT_MASK(30) - 1), + }; + struct timespec64 diff; + unsigned long flags; + s64 ns; + + spin_lock_irqsave(&ksz->ptp_clock_lock, flags); + ptp_clock_time = ksz->ptp_clock_time; + spin_unlock_irqrestore(&ksz->ptp_clock_lock, flags); + + /* 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; + + /* Add/remove excess delay between wire and time stamp unit */ + ns = timespec64_to_ns(&ts) + offset_ns; + + return ns_to_ktime(ns); +} +EXPORT_SYMBOL(ksz9477_tstamp_to_clock); + +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); + enum ksz9477_ptp_event_messages msg_type; + struct dsa_switch *ds = dev->dsa_ptr->ds; + struct ksz_device *ksz = ds->priv; + struct ksz_port *prt = &ksz->ports[port]; + u8 *tstamp = tag - KSZ9477_PTP_TAG_LEN; + struct ptp_header *ptp_hdr; + unsigned int ptp_type; + u64 correction; + + /* convert time stamp and write to skb */ + memset(hwtstamps, 0, sizeof(*hwtstamps)); + hwtstamps->hwtstamp = ksz9477_tstamp_to_clock(ksz, get_unaligned_be32(tstamp), + -prt->tstamp_rx_latency_ns); + + /* For PDelay_Req messages, user space (ptp4l) expects that the hardware + * subtracts the egress 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; + + msg_type = ptp_get_msgtype(ptp_hdr, ptp_type); + if (msg_type != PTP_Event_Message_Pdelay_Req) + return; + + correction = get_unaligned_be64(&ptp_hdr->correction); + correction -= hwtstamps->hwtstamp; + ksz9477_update_ptp_correction_field(skb, ptp_type, ptp_hdr, correction); +} +#else /* IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) */ +static void ksz9477_xmit_timestamp(struct sk_buff *skb __maybe_unused) +{ +} + +static void ksz9477_rcv_timestamp(struct sk_buff *skb __maybe_unused, u8 *tag __maybe_unused, + struct net_device *dev __maybe_unused, + unsigned int port __maybe_unused) +{ +} +#endif /* IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ9477_PTP) */ + static struct sk_buff *ksz9477_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -116,6 +304,7 @@ static struct sk_buff *ksz9477_xmit(struct sk_buff *skb, u16 val; /* Tag encoding */ + ksz9477_xmit_timestamp(skb); tag = skb_put(skb, KSZ9477_INGRESS_TAG_LEN); addr = skb_mac_header(skb); @@ -138,8 +327,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 +340,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, }; @@ -167,6 +358,7 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb, u8 *tag; /* Tag encoding */ + ksz9477_xmit_timestamp(skb); tag = skb_put(skb, KSZ_INGRESS_TAG_LEN); addr = skb_mac_header(skb); @@ -183,7 +375,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 Mon Oct 19 17:24:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11844859 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=-12.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, 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 2D6D5C43457 for ; Mon, 19 Oct 2020 17:36:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BBB93222EC for ; Mon, 19 Oct 2020 17:36:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728355AbgJSRge (ORCPT ); Mon, 19 Oct 2020 13:36:34 -0400 Received: from mailout04.rmx.de ([94.199.90.94]:36888 "EHLO mailout04.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727307AbgJSRge (ORCPT ); Mon, 19 Oct 2020 13:36:34 -0400 Received: from kdin01.retarus.com (kdin01.dmz1.retloc [172.19.17.48]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout04.rmx.de (Postfix) with ESMTPS id 4CFP7b4YFkz3qlcZ; Mon, 19 Oct 2020 19:36:27 +0200 (CEST) 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 kdin01.retarus.com (Postfix) with ESMTPS id 4CFP685Wcwz2xFr; Mon, 19 Oct 2020 19:35:12 +0200 (CEST) Received: from N95HX1G2.wgnetz.xx (192.168.54.91) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.408.0; Mon, 19 Oct 2020 19:29:18 +0200 From: Christian Eggers To: Vladimir Oltean , Florian Fainelli , Andrew Lunn , Vivien Didelot , Jakub Kicinski , Rob Herring CC: Helmut Grohne , Paul Barker , Codrin Ciubotariu , George McCollister , Marek Vasut , Tristram Ha , "David S . Miller" , Woojung Huh , "Microchip Linux Driver Support" , Christian Eggers , , , Subject: [RFC PATCH net-next 8/9] net: dsa: microchip: ksz9477: add Pulse Per Second (PPS) support Date: Mon, 19 Oct 2020 19:24:34 +0200 Message-ID: <20201019172435.4416-9-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201019172435.4416-1-ceggers@arri.de> References: <20201019172435.4416-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.91] X-RMX-ID: 20201019-193520-4CFP685Wcwz2xFr-0@kdin01 X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC The KSZ9563 has a Trigger Output Unit (TOU) which can be used to generate periodic signals. After adjusting the PTP clock time, the PPS signal has to be restarted. Tested on a Microchip KSZ9563 switch. Signed-off-by: Christian Eggers --- drivers/net/dsa/microchip/ksz9477_ptp.c | 251 +++++++++++++++++++++++- drivers/net/dsa/microchip/ksz9477_ptp.h | 2 + include/linux/dsa/ksz_common.h | 6 + 3 files changed, 257 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.c b/drivers/net/dsa/microchip/ksz9477_ptp.c index d4bcfff0577e..6f7df077bc72 100644 --- a/drivers/net/dsa/microchip/ksz9477_ptp.c +++ b/drivers/net/dsa/microchip/ksz9477_ptp.c @@ -19,6 +19,126 @@ #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_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) @@ -71,6 +191,8 @@ static int ksz9477_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) return 0; } +static int ksz9477_ptp_enable_pps(struct ksz_device *dev, int on); + static int ksz9477_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); @@ -108,6 +230,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_PPS: + dev_info(dev->dev, "Restarting PPS\n"); + + ret = ksz9477_ptp_enable_pps(dev, 1); + if (ret) + goto error_return; + + break; + } + spin_lock_irqsave(&dev->ptp_clock_lock, flags); dev->ptp_clock_time = timespec64_add(dev->ptp_clock_time, delta64); spin_unlock_irqrestore(&dev->ptp_clock_lock, flags); @@ -208,6 +344,20 @@ static int ksz9477_ptp_settime(struct ptp_clock_info *ptp, struct timespec64 con if (ret) goto error_return; + switch (dev->ptp_tou_mode) { + case KSZ_PTP_TOU_IDLE: + break; + + case KSZ_PTP_TOU_PPS: + dev_info(dev->dev, "Restarting PPS\n"); + + ret = ksz9477_ptp_enable_pps(dev, 1); + if (ret) + goto error_return; + + break; + } + spin_lock_irqsave(&dev->ptp_clock_lock, flags); dev->ptp_clock_time = *ts; spin_unlock_irqrestore(&dev->ptp_clock_lock, flags); @@ -217,9 +367,106 @@ static int ksz9477_ptp_settime(struct ptp_clock_info *ptp, struct timespec64 con return ret; } +#define KSZ9477_PPS_TOU 0 /* currently fixed to trigger output unit 0 */ + +static int ksz9477_ptp_enable_pps(struct ksz_device *dev, int on) +{ + struct timespec64 now, pps_start, diff; + u32 gpio_stat0, trig_ctrl; + int ret; + + if (dev->ptp_tou_mode != KSZ_PTP_TOU_PPS && dev->ptp_tou_mode != KSZ_PTP_TOU_IDLE) + return -EBUSY; + + /* Reset trigger unit 0 */ + ret = ksz9477_ptp_tou_reset(dev, KSZ9477_PPS_TOU); + if (ret) + return ret; + + if (!on) { + dev->ptp_tou_mode = KSZ_PTP_TOU_IDLE; + return 0; /* success */ + } + + /* Enable notify, set rising edge, set periodic pulse 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; + + /* Set cycle width (1 s) */ + ret = ksz9477_ptp_tou_cycle_width_set(dev, NSEC_PER_SEC); + if (ret) + return ret; + + /* Set cycle count (infinite) */ + ksz9477_ptp_tou_cycle_count_set(dev, 0); + if (ret) + return ret; + + /* Set pulse with (125 ms / 8 ns) */ + ret = ksz9477_ptp_tou_pulse_set(dev, 125000000); + if (ret) + return ret; + + /* Read current time */ + ret = _ksz9477_ptp_gettime(dev, &now); + if (ret) + return ret; + + /* Determine and write start time of PPS */ + pps_start.tv_sec = now.tv_sec + 1; + pps_start.tv_nsec = 0; + diff = timespec64_sub(pps_start, now); + + /* Reserve at least 100 ms for programming and activating */ + if (diff.tv_nsec < 100000000) + pps_start.tv_sec++; + + ret = ksz9477_ptp_tou_target_time_set(dev, &pps_start); + 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 << (KSZ9477_PPS_TOU + TRIG_ERROR_S))) { + dev_err(dev->dev, "%s: Trigger unit error!\n", __func__); + ret = -EIO; + /* Unit will be reset on next access */ + return ret; + } + + dev->ptp_tou_mode = KSZ_PTP_TOU_PPS; + return 0; +} + static int ksz9477_ptp_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *req, int on) { - return -ENOTTY; + struct ksz_device *dev = container_of(ptp, struct ksz_device, ptp_caps); + int ret; + + switch (req->type) { + case PTP_CLK_REQ_PPS: + mutex_lock(&dev->ptp_mutex); + ret = ksz9477_ptp_enable_pps(dev, 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) @@ -644,7 +891,7 @@ int ksz9477_ptp_init(struct ksz_device *dev) 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.pps = 1; dev->ptp_caps.adjfine = ksz9477_ptp_adjfine; dev->ptp_caps.adjtime = ksz9477_ptp_adjtime; dev->ptp_caps.gettime64 = ksz9477_ptp_gettime; diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.h b/drivers/net/dsa/microchip/ksz9477_ptp.h index e8d50a086311..03058e795678 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, struct ethtool_ts_info *ts); @@ -33,6 +34,7 @@ bool ksz9477_ptp_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff * 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/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h index 10b42154ba5a..f1014b40d643 100644 --- a/include/linux/dsa/ksz_common.h +++ b/include/linux/dsa/ksz_common.h @@ -58,6 +58,11 @@ struct ksz_port { #endif }; +enum ksz_ptp_tou_mode { + KSZ_PTP_TOU_IDLE, + KSZ_PTP_TOU_PPS, +}; + struct ksz_device { struct dsa_switch *ds; struct ksz_platform_data *pdata; @@ -116,6 +121,7 @@ struct ksz_device { spinlock_t ptp_clock_lock; /* for ptp_clock_time */ /* approximated current time, read once per second from hardware */ struct timespec64 ptp_clock_time; + enum ksz_ptp_tou_mode ptp_tou_mode; #endif }; From patchwork Mon Oct 19 17:24:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Eggers X-Patchwork-Id: 11844867 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=-12.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, 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 CD99AC43457 for ; Mon, 19 Oct 2020 17:37:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6E2192225F for ; Mon, 19 Oct 2020 17:37:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728280AbgJSRhw (ORCPT ); Mon, 19 Oct 2020 13:37:52 -0400 Received: from mailout09.rmx.de ([94.199.88.74]:42930 "EHLO mailout09.rmx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725887AbgJSRhw (ORCPT ); Mon, 19 Oct 2020 13:37:52 -0400 Received: from kdin01.retarus.com (kdin01.dmz1.retloc [172.19.17.48]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout09.rmx.de (Postfix) with ESMTPS id 4CFP963qnCzbhsG; Mon, 19 Oct 2020 19:37:46 +0200 (CEST) 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 kdin01.retarus.com (Postfix) with ESMTPS id 4CFP7b4W46z2xFT; Mon, 19 Oct 2020 19:36:27 +0200 (CEST) Received: from N95HX1G2.wgnetz.xx (192.168.54.91) by mta.arri.de (192.168.100.104) with Microsoft SMTP Server (TLS) id 14.3.408.0; Mon, 19 Oct 2020 19:29:53 +0200 From: Christian Eggers To: Vladimir Oltean , Florian Fainelli , Andrew Lunn , Vivien Didelot , Jakub Kicinski , Rob Herring CC: Helmut Grohne , Paul Barker , Codrin Ciubotariu , George McCollister , Marek Vasut , Tristram Ha , "David S . Miller" , Woojung Huh , "Microchip Linux Driver Support" , Christian Eggers , , , Subject: [RFC PATCH net-next 9/9] net: dsa: microchip: ksz9477: add periodic output support Date: Mon, 19 Oct 2020 19:24:35 +0200 Message-ID: <20201019172435.4416-10-ceggers@arri.de> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201019172435.4416-1-ceggers@arri.de> References: <20201019172435.4416-1-ceggers@arri.de> MIME-Version: 1.0 X-Originating-IP: [192.168.54.91] X-RMX-ID: 20201019-193631-4CFP7b4W46z2xFT-0@kdin01 X-RMX-SOURCE: 217.111.95.66 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC 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 --- drivers/net/dsa/microchip/ksz9477_ptp.c | 244 +++++++++++++++++++++++- include/linux/dsa/ksz_common.h | 6 + 2 files changed, 249 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/microchip/ksz9477_ptp.c b/drivers/net/dsa/microchip/ksz9477_ptp.c index 6f7df077bc72..0ba6c3a5aa92 100644 --- a/drivers/net/dsa/microchip/ksz9477_ptp.c +++ b/drivers/net/dsa/microchip/ksz9477_ptp.c @@ -90,6 +90,20 @@ static int ksz9477_ptp_tou_cycle_count_set(struct ksz_device *dev, u16 count) 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; @@ -191,6 +205,7 @@ static int ksz9477_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) return 0; } +static int ksz9477_ptp_restart_perout(struct ksz_device *dev); static int ksz9477_ptp_enable_pps(struct ksz_device *dev, int on); static int ksz9477_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) @@ -234,6 +249,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"); + + ret = ksz9477_ptp_restart_perout(dev); + if (ret) + goto error_return; + + break; + case KSZ_PTP_TOU_PPS: dev_info(dev->dev, "Restarting PPS\n"); @@ -348,6 +372,15 @@ static int ksz9477_ptp_settime(struct ptp_clock_info *ptp, struct timespec64 con 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; + case KSZ_PTP_TOU_PPS: dev_info(dev->dev, "Restarting PPS\n"); @@ -367,6 +400,159 @@ static int ksz9477_ptp_settime(struct ptp_clock_info *ptp, struct timespec64 con 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) +{ + int ret; + u32 trig_ctrl; + + /* 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; +} + +static int ksz9477_ptp_enable_perout(struct ksz_device *dev, + struct ptp_perout_request const *perout_request, int on) +{ + u32 gpio_stat0; + u64 cycle_width_ns; + int ret; + + 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) { + 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; + } + + 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) +{ + struct timespec64 now; + s64 now_ns, first_ns, period_ns, next_ns; + 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; +} + #define KSZ9477_PPS_TOU 0 /* currently fixed to trigger output unit 0 */ static int ksz9477_ptp_enable_pps(struct ksz_device *dev, int on) @@ -457,6 +643,15 @@ static int ksz9477_ptp_enable(struct ptp_clock_info *ptp, struct ptp_clock_reque 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; + } case PTP_CLK_REQ_PPS: mutex_lock(&dev->ptp_mutex); ret = ksz9477_ptp_enable_pps(dev, on); @@ -862,9 +1057,56 @@ static ssize_t ocmode_store(struct device *dev, struct device_attribute *attr __ static DEVICE_ATTR_RW(ocmode); +static ssize_t pulse_show(struct device *dev, struct device_attribute *attr __always_unused, + char *buf) +{ + struct ksz_device *ksz = dev_get_drvdata(dev); + int ret; + + mutex_lock(&ksz->ptp_mutex); + + ret = sprintf(buf, "%u\n", ksz->ptp_perout_pulse_width_ns); + + mutex_unlock(&ksz->ptp_mutex); + return ret; +} + +static ssize_t pulse_store(struct device *dev, struct device_attribute *attr __always_unused, + char const *buf, size_t count) +{ + struct ksz_device *ksz = dev_get_drvdata(dev); + u32 value; + int ret; + + mutex_lock(&ksz->ptp_mutex); + + if (ksz->ptp_tou_mode != KSZ_PTP_TOU_IDLE) { + ret = -EBUSY; + goto error_return; + } + + ret = kstrtou32(buf, 0, &value); + if (ret) + goto error_return; + + ret = ksz9477_ptp_tou_pulse_verify(value); + if (ret) + goto error_return; + + ksz->ptp_perout_pulse_width_ns = value; + ret = count; /* success */ + +error_return: + mutex_unlock(&ksz->ptp_mutex); + return ret; +} + +static DEVICE_ATTR_RW(pulse); + static struct attribute *ksz9477_ptp_attrs[] = { &dev_attr_tcmode.attr, &dev_attr_ocmode.attr, + &dev_attr_pulse.attr, NULL, }; @@ -890,7 +1132,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 = 1; dev->ptp_caps.adjfine = ksz9477_ptp_adjfine; dev->ptp_caps.adjtime = ksz9477_ptp_adjtime; diff --git a/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h index f1014b40d643..194a0a968889 100644 --- a/include/linux/dsa/ksz_common.h +++ b/include/linux/dsa/ksz_common.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ struct ksz_port { enum ksz_ptp_tou_mode { KSZ_PTP_TOU_IDLE, + KSZ_PTP_TOU_PEROUT, KSZ_PTP_TOU_PPS, }; @@ -122,6 +124,10 @@ struct ksz_device { /* approximated current time, read once per second from hardware */ struct timespec64 ptp_clock_time; 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 };