From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 75A193B5F5D; Fri, 9 Oct 2026 17:12:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791565946; cv=none; b=pn3mBs5n94gJYbLMI+21UbVGkz2HQIGId7hxkcODLfR5MjGb0XJPApEl9/loUxxZrSrDvEjTpCR2tTJsuHOT5yCshi6hky5cEhU3hgSNQip7UDeFjtCii/Syo8f3gHbT6pzfgHtxEvFKMcx//D2PObRglt4Z3XCqSIagiKn2nd0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791565946; c=relaxed/simple; bh=nRnourIruJSJL0uorlJaUN32dN7FfbD0U/OMr+KnyUs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=E6AicXkW5SaLgwEwFhSHES0NywuvM7GqfVMk3zzBmNaBMm1FVIjKQr8Vitt6hdLZVPeuUdnj53wcGj/OjPy8hh8TnBTNDZeh0d3A48lZ39sE0Tq0zhrwafH7z8Okm+iLQ7nKvc27m1sVs4JMB/yQA/KtYVhl2TGsW1CoF0Bbvxw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=uZv51dww; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="uZv51dww" Received: by smtp.kernel.org (Postfix) with ESMTPS id 3C8ABC4AF0E; Fri, 9 Oct 2026 17:12:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1791565946; bh=nRnourIruJSJL0uorlJaUN32dN7FfbD0U/OMr+KnyUs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=uZv51dwwknfPGqeAkwkTemBeHgrfE5Pk/Ix9/16bv4mrZrePNqqSI3Dlh1y6cyEth 2WSw0R/PCSbj0eTKdHK/GCTK+lrQOlwBoH6aGt8ykJ5+aOM05hVS09obOc5H9xMtAO H0sPvsEs+WZt1M07qmtUXtroMvH4609qiYVDxfttvEg8RSarcsTRZNMsyvJDkePfYp 9TdzOld1R/ne1SREHrzHubvtZ2mzj3sKnMXgKnRSMXGk+ve/7/i/t6GNx17aBUfQo4 3hfokPOfkBYarHfMltGRJKC3yvuOhekR7ntRiDxurNHpM6CNFQSSnnobBn6wmHKdq+ RqEpeQSxzHSRw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2996CCA9EBE; Fri, 9 Oct 2026 17:12:26 +0000 (UTC) From: Selvamani Rajagopal via B4 Relay Date: Fri, 09 Oct 2026 10:12:25 -0700 Subject: [PATCH net-next v9 05/11] net: ethernet: oa_tc6: Support for hardware timestamp Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit Message-Id: <20261009-s2500-mac-phy-support-v9-5-dcefe1d0bf0d@onsemi.com> References: <20261009-s2500-mac-phy-support-v9-0-dcefe1d0bf0d@onsemi.com> In-Reply-To: <20261009-s2500-mac-phy-support-v9-0-dcefe1d0bf0d@onsemi.com> To: Selva Rajagopal , Andrew Lunn , "David S. Miller" , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Parthiban Veerasooran , Simon Horman , Jonathan Corbet , Shuah Khan , Randy Dunlap , Richard Cochran , Andrew Lunn , Heiner Kallweit , Russell King , Eric Dumazet , Piergiorgio Beruto , Eric Dumazet Cc: Selva Rajagopal , netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Jerry Ray , Ciprian Regus X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1791565943; l=36188; i=Selvamani.Rajagopal@onsemi.com; s=20260531; h=from:subject:message-id; bh=jIudcLB9vw151W34Sf2DQieBGk/2/4jUziYz/bjQN54=; b=7YGX74SxOaVbizVFNPMWeW/VrWNEn6qJswyjrwtgdBnrsbnLFkibo/0obuxGrHPx3BpI1O+3P KmbUa98Yus7DiTp03TER3TVyHXAo8s/6L3L0Sb4c6KgkngCdqaI8ETa X-Developer-Key: i=Selvamani.Rajagopal@onsemi.com; a=ed25519; pk=5QRdM0HS/LGWWcUZZ9hVfZ+qbPQGZCumcTXOiN7Fyug= X-Endpoint-Received: by B4 Relay for Selvamani.Rajagopal@onsemi.com/20260531 with auth_id=803 X-Original-From: Selvamani Rajagopal Reply-To: Selvamani.Rajagopal@onsemi.com From: Selvamani Rajagopal PTP register/unregister calls are implemented in oa_tc6_ptp.c. The APIs that work with the hardware for timestamp is provided by vendor code as it may be vendor dependent. Interface for ndo_hwtstamp_set/get, control and status callback for ethtool are provided to support hardware timestamp feature. ioctl interface passes the request to PHY. Hardware timestamp functions that handle the traffic are generally placed in oa_tc6.c. Rest of the timestamp functionalities, mostly helper functions are in oa_tc6_tstamp.c. When all the three timestamp capture registers are busy and not available, driver drops the incoming TX SKB, if it comes with the request for hardware timestamping. Normal traffic without timestamp request is not affected. Practically, application using hardware timestamp is not expected to hit this scenario as three timestamp capture registers are plenty considering the amount of traffic expected in such applications. Signed-off-by: Selvamani Rajagopal --- changes in v9 - Moved the vend1_mms initialization to the correct patch - Fixed the confusing goto statements to return true or false - Changed the magic number offset to index into array to names. - Checking valid tsc instead of relying on SKBTX_IN_PROGRESS as life of this flag may not be 100% internal to the driver changes in v8 - Moved few timestamp defintions to header file from source file. - Improved logic in handling timestamp capture register events. - timestamp skb queue is protected inside spinlock - Removed unnecessary ioctl callback changes in v7 - Fixed the parameter name mismatch in function protoype and definition - CONFIG0 is set with 64 bit timestamp support by default. - Added information about return value for API documentation. changes in v6 - Fixed the issue of function parameter in oa_tc6_get_ts_stats not described in comments section for documentation. - Avoided typecasting __be32 as u32 changes in v5 - As subtracting skb len by FCS size is considered bug, changes are removed. Will be fixed in stable branch (net repo) changes in v4 - Fixed the condition check for subtracting the FCS size from skb len. changes in v3 - Replaced warning printk with ratelimited printk - Checking the hardware register before enabling hardware timestamp changes in v1 - Added hardware timestamp support to the OA TC6 framework. --- MAINTAINERS | 1 + drivers/net/ethernet/adi/Kconfig | 1 + drivers/net/ethernet/microchip/lan865x/Kconfig | 1 + drivers/net/ethernet/oa_tc6/Kconfig | 1 + drivers/net/ethernet/oa_tc6/Makefile | 2 +- drivers/net/ethernet/oa_tc6/oa_tc6.c | 312 +++++++++++++++++++++++-- drivers/net/ethernet/oa_tc6/oa_tc6_ptp.c | 65 ++++++ drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h | 46 ++++ drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c | 224 ++++++++++++++++++ include/linux/oa_tc6.h | 23 ++ 10 files changed, 659 insertions(+), 17 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 6c1deaf5643b..1d1e0563b922 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20467,6 +20467,7 @@ F: drivers/rtc/rtc-optee.c OPEN ALLIANCE 10BASE-T1S MACPHY SERIAL INTERFACE FRAMEWORK M: Parthiban Veerasooran +M: Selva Rajagopal (timestamp support) L: netdev@vger.kernel.org S: Maintained F: Documentation/networking/oa-tc6-framework.rst diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig index bdb8ff7d15da..120a52e6b283 100644 --- a/drivers/net/ethernet/adi/Kconfig +++ b/drivers/net/ethernet/adi/Kconfig @@ -29,6 +29,7 @@ config ADIN1110 config ADIN1140 tristate "Analog Devices ADIN1140 MAC-PHY" depends on SPI + depends on PTP_1588_CLOCK_OPTIONAL select ADIN1140_PHY select OA_TC6 help diff --git a/drivers/net/ethernet/microchip/lan865x/Kconfig b/drivers/net/ethernet/microchip/lan865x/Kconfig index 7f2a4e7e1915..342172cb8837 100644 --- a/drivers/net/ethernet/microchip/lan865x/Kconfig +++ b/drivers/net/ethernet/microchip/lan865x/Kconfig @@ -8,6 +8,7 @@ if NET_VENDOR_MICROCHIP config LAN865X tristate "LAN865x support" depends on SPI + depends on PTP_1588_CLOCK_OPTIONAL select OA_TC6 help Support for the Microchip LAN8650/1 Rev.B0/B1 MACPHY Ethernet chip. It diff --git a/drivers/net/ethernet/oa_tc6/Kconfig b/drivers/net/ethernet/oa_tc6/Kconfig index 657567276030..2c8c881d5b99 100644 --- a/drivers/net/ethernet/oa_tc6/Kconfig +++ b/drivers/net/ethernet/oa_tc6/Kconfig @@ -6,6 +6,7 @@ config OA_TC6 tristate "OPEN Alliance TC6 10BASE-T1x MAC-PHY support" if COMPILE_TEST depends on SPI + depends on PTP_1588_CLOCK_OPTIONAL select PHYLIB help This library implements OPEN Alliance TC6 10BASE-T1x MAC-PHY diff --git a/drivers/net/ethernet/oa_tc6/Makefile b/drivers/net/ethernet/oa_tc6/Makefile index f24aae852ef2..964f668efc2d 100644 --- a/drivers/net/ethernet/oa_tc6/Makefile +++ b/drivers/net/ethernet/oa_tc6/Makefile @@ -4,4 +4,4 @@ # obj-$(CONFIG_OA_TC6) := oa_tc6_mod.o -oa_tc6_mod-objs := oa_tc6.o +oa_tc6_mod-objs := oa_tc6.o oa_tc6_ptp.o oa_tc6_tstamp.o diff --git a/drivers/net/ethernet/oa_tc6/oa_tc6.c b/drivers/net/ethernet/oa_tc6/oa_tc6.c index f8e9309a72f1..0b6e216de8a1 100644 --- a/drivers/net/ethernet/oa_tc6/oa_tc6.c +++ b/drivers/net/ethernet/oa_tc6/oa_tc6.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,16 @@ #include "oa_tc6_std_def.h" +struct oa_tc6_ts_info_rx { + bool rtsa; + bool rtsp; +}; + +struct oa_tc6_ts_info_tx { + ktime_t hwtstamp; + u8 tsc; +}; + static int oa_tc6_spi_transfer(struct oa_tc6 *tc6, enum oa_tc6_header_type header_type, u16 length) { @@ -50,6 +61,193 @@ static int oa_tc6_get_parity(u32 p) return !((p >> 28) & 1); } +static struct oa_tc6_ts_info_tx *oa_tc6_tsinfo_tx(struct sk_buff *skb) +{ + return (struct oa_tc6_ts_info_tx *)(skb->cb); +} + +static struct oa_tc6_ts_info_rx *oa_tc6_tsinfo_rx(struct sk_buff *skb) +{ + return (struct oa_tc6_ts_info_rx *)(skb->cb); +} + +static u8 oa_tc6_next_tsc(u8 tsc) +{ + return tsc < OA_TC6_TTSCC_REG_ID ? tsc + 1 : OA_TC6_TTSCA_REG_ID; +} + +static bool oa_tc6_req_tx_hwtstamp(struct oa_tc6 *tc6, struct sk_buff *skb) +{ + u8 tsc; + u8 i; + + lockdep_assert_held(&tc6->tx_skb_lock); + + if (!skb || !(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) + return false; + + if (!tc6->hw_tstamp_enabled) + return false; + + if (tc6->ts_config.tx_type != HWTSTAMP_TX_ON) { + tc6->tx_hwtstamp_lost++; + return false; + } + + tsc = tc6->ttsc_current_id; + for (i = 0; i < OA_TC6_TTSCC_REG_ID; i++) { + if (!(tc6->ts_ttsc_pending & + BIT(tsc - OA_TC6_TTSCA_REG_ID))) + goto slot_found; + tsc = oa_tc6_next_tsc(tsc); + } + + /* All the three slots are waiting for its event. This skb + * can't request timestamp. Marking it to be dropped. + */ + tc6->tx_hwtstamp_lost++; + return true; + +slot_found: + tc6->ts_ttsc_pending |= BIT(tsc - OA_TC6_TTSCA_REG_ID); + tc6->ttsc_current_id = oa_tc6_next_tsc(tsc); + oa_tc6_tsinfo_tx(skb)->tsc = tsc; + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + return false; +} + +static void oa_tc6_events_handle(struct oa_tc6 *tc6, u32 val, const u32 *ts) +{ + struct skb_shared_hwtstamps tstamp; + struct oa_tc6_ts_info_tx *ski; + struct sk_buff *skb, *tmp; + LIST_HEAD(deliver); + LIST_HEAD(drop); + u8 tsc, bit_num; + u32 handled = 0; + u32 event; + + spin_lock_bh(&tc6->tx_skb_lock); + list_for_each_entry_safe(skb, tmp, &tc6->tx_ts_skb_q, list) { + ski = oa_tc6_tsinfo_tx(skb); + tsc = ski->tsc; + bit_num = tsc - OA_TC6_TTSCA_REG_ID; + event = OA_TC6_STATUS0_TTSCAA << bit_num; + if (!(val & event)) + continue; + if (handled & event) { + dev_warn_ratelimited(&tc6->spi->dev, + "Multiple skbs\n"); + tc6->tx_hwtstamp_err++; + list_move_tail(&skb->list, &drop); + continue; + } + + handled |= event; + tc6->ts_ttsc_pending &= ~BIT(bit_num); + + ski->hwtstamp = ktime_set(ts[2 * bit_num], + ts[2 * bit_num + 1]); + tc6->tx_hwtstamp_pkts++; + list_move_tail(&skb->list, &deliver); + } + spin_unlock_bh(&tc6->tx_skb_lock); + + /* skb_tstamp_tx() clones the skb and wakes the owning socket, so keep + * it out of the lock that oa_tc6_start_xmit() takes. + */ + list_for_each_entry_safe(skb, tmp, &deliver, list) { + list_del(&skb->list); + memset(&tstamp, 0, sizeof(tstamp)); + tstamp.hwtstamp = oa_tc6_tsinfo_tx(skb)->hwtstamp; + skb_tstamp_tx(skb, &tstamp); + dev_kfree_skb(skb); + } + + list_for_each_entry_safe(skb, tmp, &drop, list) { + list_del(&skb->list); + dev_kfree_skb(skb); + } + + if (val & ~handled) { + tc6->tx_hwtstamp_err += hweight32(val & ~handled); + dev_warn_ratelimited(&tc6->spi->dev, + "Unexpected event: 0x%x\n", + val & ~handled); + } +} + +void oa_tc6_cleanup_tx_tstamp_skbs(struct oa_tc6 *tc6) +{ + struct sk_buff *skb, *tmp; + + spin_lock_bh(&tc6->tx_skb_lock); + list_for_each_entry_safe(skb, tmp, &tc6->tx_ts_skb_q, list) { + list_del(&skb->list); + tc6->ts_ttsc_pending &= ~BIT(oa_tc6_tsinfo_tx(skb)->tsc - + OA_TC6_TTSCA_REG_ID); + dev_kfree_skb(skb); + + /* The frame was transmitted, only its timestamp is lost. */ + tc6->tx_hwtstamp_lost++; + } + spin_unlock_bh(&tc6->tx_skb_lock); +} + +static int oa_tc6_update_ts_in_rx_skb(struct oa_tc6 *tc6) +{ + unsigned int expected_len = ETH_HLEN; + struct sk_buff *skb = tc6->rx_skb; + struct oa_tc6_ts_info_rx *ski; + __be32 ts_val[2]; + u32 ts[2]; + + ski = oa_tc6_tsinfo_rx(skb); + if (ski->rtsa) + expected_len += OA_TC6_TSTAMP_SZ; + + if (skb->len < expected_len) { + tc6->netdev->stats.rx_length_errors++; + return -EINVAL; + } + + if (!ski->rtsa) + return 0; + + memcpy(&ts_val[0], skb->data, 4); + memcpy(&ts_val[1], (u32 *)skb->data + 1, 4); + + ts[0] = be32_to_cpu(ts_val[0]); + ts[1] = be32_to_cpu(ts_val[1]); + + /* Check parity */ + if ((oa_tc6_get_parity(ts[0]) ^ oa_tc6_get_parity(ts[1])) == + !ski->rtsp) { + struct skb_shared_hwtstamps *hw_ts; + + /* Report timestamp to the upper layers */ + hw_ts = skb_hwtstamps(skb); + memset(hw_ts, 0, sizeof(*hw_ts)); + hw_ts->hwtstamp = ktime_set(ts[0], ts[1]); + } + skb_pull(skb, sizeof(ts)); + + return 0; +} + +static int oa_tc6_update_standard_capability(struct oa_tc6 *tc6) +{ + u32 regval = 0; + int ret; + + ret = oa_tc6_read_register(tc6, OA_TC6_REG_STDCAP, ®val); + if (ret) + return ret; + if (regval & OA_TC6_STDCAP_TIMESTAMP_CAPABILITY) + tc6->hw_tstamp_supported = true; + return 0; +} + static __be32 oa_tc6_prepare_ctrl_header(u32 addr, u8 length, enum oa_tc6_register_op reg_op) { @@ -625,6 +823,16 @@ static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6) static void oa_tc6_cleanup_ongoing_tx_skb(struct oa_tc6 *tc6) { + if (tc6->ongoing_tx_skb && + oa_tc6_tsinfo_tx(tc6->ongoing_tx_skb)->tsc) { + u8 tsc = oa_tc6_tsinfo_tx(tc6->ongoing_tx_skb)->tsc; + + spin_lock_bh(&tc6->tx_skb_lock); + tc6->ts_ttsc_pending &= ~BIT(tsc - OA_TC6_TTSCA_REG_ID); + spin_unlock_bh(&tc6->tx_skb_lock); + } + + tc6->tx_skb_offset = 0; oa_tc6_drop_tx_skb(tc6, tc6->ongoing_tx_skb); tc6->ongoing_tx_skb = NULL; } @@ -650,6 +858,7 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6) { oa_tc6_free_ongoing_skbs(tc6); oa_tc6_cleanup_waiting_tx_skb(tc6); + oa_tc6_cleanup_tx_tstamp_skbs(tc6); } static void oa_tc6_look_for_new_frame(struct oa_tc6 *tc6) @@ -678,6 +887,8 @@ static void oa_tc6_disable_traffic(struct oa_tc6 *tc6) netif_tx_disable(tc6->netdev); oa_tc6_drop_tx_skb(tc6, skb); oa_tc6_free_ongoing_skbs(tc6); + oa_tc6_cleanup_tx_tstamp_skbs(tc6); + oa_tc6_write_register(tc6, OA_TC6_REG_INT_MASK0, regval); oa_tc6_read_register(tc6, OA_TC6_REG_STATUS0, ®val); oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, regval); @@ -686,15 +897,28 @@ static void oa_tc6_disable_traffic(struct oa_tc6 *tc6) static int oa_tc6_process_extended_status(struct oa_tc6 *tc6) { + u32 regs[OA_TC6_STATUS0_TTSC_REG_COUNT]; + bool ts_valid = !!tc6->ptp_clock; u32 value; int ret; - ret = oa_tc6_read_register(tc6, OA_TC6_REG_STATUS0, &value); + /* When timestamp is disabled, there is no behavior change + * as it reads only STATUS0 register. When enabled, + * TTSCA_HIGH..TTSCC_LOW are fetched together with STATUS0 + * to avoid having to make second SPI transaction. Reading few + * extra registers, even it may not be needed every time this + * function is called, it is more efficient than making second + * SPI transaction, when needed. + */ + ret = oa_tc6_read_registers(tc6, OA_TC6_REG_STATUS0, regs, + ts_valid ? + OA_TC6_STATUS0_TTSC_REG_COUNT : 1); if (ret) { netdev_err(tc6->netdev, "STATUS0 register read failed: %d\n", ret); return ret; } + value = regs[0]; /* This function is called for each chunk received in a given SPI * transaction. In case, extended status bit is set in more than @@ -703,6 +927,11 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6) if (!value) return 0; + if ((value & OA_TC6_STATUS0_TTSCA_MASK) != 0) + oa_tc6_events_handle(tc6, value & + OA_TC6_STATUS0_TTSCA_MASK, + ®s[OA_TC6_TTSC_REG_OFFSET]); + /* Clear the error interrupts status */ ret = oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, value); if (ret) { @@ -788,6 +1017,13 @@ static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6) tc6->rx_skb->len > ETH_FCS_LEN) skb_trim(tc6->rx_skb, tc6->rx_skb->len - ETH_FCS_LEN); + if (oa_tc6_update_ts_in_rx_skb(tc6)) { + tc6->netdev->stats.rx_dropped++; + dev_kfree_skb_any(tc6->rx_skb); + tc6->rx_skb = NULL; + return; + } + tc6->rx_skb->protocol = eth_type_trans(tc6->rx_skb, tc6->netdev); tc6->netdev->stats.rx_packets++; tc6->netdev->stats.rx_bytes += tc6->rx_skb->len; @@ -823,25 +1059,32 @@ static int oa_tc6_update_rx_skb(struct oa_tc6 *tc6, u8 *payload, u8 length) * buffer pointer. We should release, if a valid pointer is found before * allocating a new one. */ -static int oa_tc6_allocate_rx_skb(struct oa_tc6 *tc6) +static int oa_tc6_allocate_rx_skb(struct oa_tc6 *tc6, u32 footer) { + struct oa_tc6_ts_info_rx *ski; + oa_tc6_cleanup_ongoing_rx_skb(tc6); - tc6->rx_skb = netdev_alloc_skb_ip_align(tc6->netdev, tc6->netdev->mtu + - ETH_HLEN + ETH_FCS_LEN); + tc6->rx_skb = netdev_alloc_skb_ip_align(tc6->netdev, + tc6->netdev->mtu + + ETH_HLEN + ETH_FCS_LEN + + OA_TC6_TSTAMP_SZ); if (!tc6->rx_skb) { tc6->netdev->stats.rx_dropped++; return -ENOMEM; } + ski = oa_tc6_tsinfo_rx(tc6->rx_skb); + ski->rtsa = FIELD_GET(OA_TC6_DATA_FOOTER_RTSA_VALID, footer); + ski->rtsp = FIELD_GET(OA_TC6_DATA_FOOTER_RTSP_VALID, footer); return 0; } static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload, - u16 size) + u16 size, u32 footer) { int ret; - ret = oa_tc6_allocate_rx_skb(tc6); + ret = oa_tc6_allocate_rx_skb(tc6, footer); if (ret) return ret; @@ -854,11 +1097,11 @@ static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload, return 0; } -static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size) +static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size, u32 footer) { int ret; - ret = oa_tc6_allocate_rx_skb(tc6); + ret = oa_tc6_allocate_rx_skb(tc6, footer); if (ret) return ret; @@ -905,7 +1148,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, size = end_byte_offset + 1 - start_byte_offset; return oa_tc6_prcs_complete_rx_frame(tc6, &data[start_byte_offset], - size); + size, footer); } /* Process the chunk with only rx frame start */ @@ -913,7 +1156,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset; return oa_tc6_prcs_rx_frame_start(tc6, &data[start_byte_offset], - size); + size, footer); } /* Process the chunk with only rx frame end */ @@ -946,7 +1189,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset; return oa_tc6_prcs_rx_frame_start(tc6, &data[start_byte_offset], - size); + size, footer); } /* Process the chunk with ongoing rx frame data */ @@ -1025,13 +1268,15 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length) } static __be32 oa_tc6_prepare_data_header(bool data_valid, bool start_valid, - bool end_valid, u8 end_byte_offset) + bool end_valid, u8 end_byte_offset, + u8 tsc) { u32 header = FIELD_PREP(OA_TC6_DATA_HEADER_DATA_NOT_CTRL, OA_TC6_DATA_HEADER) | FIELD_PREP(OA_TC6_DATA_HEADER_DATA_VALID, data_valid) | FIELD_PREP(OA_TC6_DATA_HEADER_START_VALID, start_valid) | FIELD_PREP(OA_TC6_DATA_HEADER_END_VALID, end_valid) | + FIELD_PREP(OA_TC6_DATA_HEADER_TSC_OFFSET, tsc) | FIELD_PREP(OA_TC6_DATA_HEADER_END_BYTE_OFFSET, end_byte_offset); @@ -1050,6 +1295,7 @@ static void oa_tc6_add_tx_skb_to_spi_buf(struct oa_tc6 *tc6) enum oa_tc6_data_start_valid_info start_valid; u8 end_byte_offset = 0; u16 length_to_copy; + u8 tsc = 0; /* Initial value is assigned here to avoid more than 80 characters in * the declaration place. @@ -1059,8 +1305,10 @@ static void oa_tc6_add_tx_skb_to_spi_buf(struct oa_tc6 *tc6) /* Set start valid if the current tx chunk contains the start of the tx * ethernet frame. */ - if (!tc6->tx_skb_offset) + if (!tc6->tx_skb_offset) { start_valid = OA_TC6_DATA_START_VALID; + tsc = oa_tc6_tsinfo_tx(tc6->ongoing_tx_skb)->tsc; + } /* If the remaining tx skb length is more than the chunk payload size of * 64 bytes then copy only 64 bytes and leave the ongoing tx skb for @@ -1081,17 +1329,29 @@ static void oa_tc6_add_tx_skb_to_spi_buf(struct oa_tc6 *tc6) tc6->tx_skb_offset = 0; tc6->netdev->stats.tx_bytes += tc6->ongoing_tx_skb->len; tc6->netdev->stats.tx_packets++; - kfree_skb(tc6->ongoing_tx_skb); + + /* Free the ones that are not saved for later processing, + * like timestamping. + */ + if (!oa_tc6_tsinfo_tx(tc6->ongoing_tx_skb)->tsc) { + kfree_skb(tc6->ongoing_tx_skb); + } else { + spin_lock_bh(&tc6->tx_skb_lock); + list_add_tail(&tc6->ongoing_tx_skb->list, + &tc6->tx_ts_skb_q); + spin_unlock_bh(&tc6->tx_skb_lock); + } tc6->ongoing_tx_skb = NULL; } *tx_buf = oa_tc6_prepare_data_header(OA_TC6_DATA_VALID, start_valid, - end_valid, end_byte_offset); + end_valid, end_byte_offset, tsc); tc6->spi_data_tx_buf_offset += OA_TC6_CHUNK_SIZE; } static u16 oa_tc6_prepare_spi_tx_buf_for_tx_skbs(struct oa_tc6 *tc6) { + struct sk_buff *drop_skb; u16 used_tx_credits; /* Get tx skbs and convert them into tx chunks based on the tx credits @@ -1100,10 +1360,21 @@ static u16 oa_tc6_prepare_spi_tx_buf_for_tx_skbs(struct oa_tc6 *tc6) for (used_tx_credits = 0; used_tx_credits < tc6->tx_credits; used_tx_credits++) { if (!tc6->ongoing_tx_skb) { + drop_skb = NULL; spin_lock_bh(&tc6->tx_skb_lock); tc6->ongoing_tx_skb = tc6->waiting_tx_skb; tc6->waiting_tx_skb = NULL; + if (oa_tc6_req_tx_hwtstamp(tc6, + tc6->ongoing_tx_skb)) { + drop_skb = tc6->ongoing_tx_skb; + tc6->ongoing_tx_skb = NULL; + if (!tc6->disable_traffic && + netif_running(tc6->netdev) && + netif_queue_stopped(tc6->netdev)) + netif_wake_queue(tc6->netdev); + } spin_unlock_bh(&tc6->tx_skb_lock); + oa_tc6_drop_tx_skb(tc6, drop_skb); } if (!tc6->ongoing_tx_skb) break; @@ -1120,7 +1391,7 @@ static void oa_tc6_add_empty_chunks_to_spi_buf(struct oa_tc6 *tc6, header = oa_tc6_prepare_data_header(OA_TC6_DATA_INVALID, OA_TC6_DATA_START_INVALID, - OA_TC6_DATA_END_INVALID, 0); + OA_TC6_DATA_END_INVALID, 0, 0); while (needed_empty_chunks--) { __be32 *tx_buf = tc6->spi_data_tx_buf + @@ -1312,6 +1583,7 @@ netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb) return NETDEV_TX_OK; } + oa_tc6_tsinfo_tx(skb)->tsc = 0; spin_lock_bh(&tc6->tx_skb_lock); if (tc6->waiting_tx_skb) { netif_stop_queue(tc6->netdev); @@ -1371,6 +1643,8 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev, SET_NETDEV_DEV(netdev, &spi->dev); mutex_init(&tc6->spi_ctrl_lock); spin_lock_init(&tc6->tx_skb_lock); + tc6->ttsc_current_id = OA_TC6_TTSCA_REG_ID; + INIT_LIST_HEAD(&tc6->tx_ts_skb_q); if (quirks) tc6->quirk_flags = quirks->quirk_flags; @@ -1464,6 +1738,12 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev, goto phy_exit; } + ret = oa_tc6_update_standard_capability(tc6); + if (ret) { + dev_err(&tc6->spi->dev, "Failed to read capability\n"); + goto phy_exit; + } + ret = devm_request_threaded_irq(&tc6->spi->dev, tc6->spi->irq, oa_tc6_macphy_isr, oa_tc6_macphy_threaded_irq, diff --git a/drivers/net/ethernet/oa_tc6/oa_tc6_ptp.c b/drivers/net/ethernet/oa_tc6/oa_tc6_ptp.c new file mode 100644 index 000000000000..1b515615819b --- /dev/null +++ b/drivers/net/ethernet/oa_tc6/oa_tc6_ptp.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Support for hardware timestamping feature for OPEN Alliance + * 10BASE‑T1x MAC‑PHY Serial Interface framework + * + * Author: Selva Rajagopal + */ + +#include +#include +#include +#include +#include +#include + +#include "oa_tc6_std_def.h" + +/** + * oa_tc6_ptp_register - Registers clock related callbacks + * @tc6: oa_tc6 struct. + * @info: Describes a PTP hardware clock + * + * Description: Vendors are expected to set the hardware timestamp + * related callbacks before calling this function. + * + * Return: 0 on success otherwise failed. + * + */ +int oa_tc6_ptp_register(struct oa_tc6 *tc6, struct ptp_clock_info *info) +{ + int ret = 0; + + /* Not supporting hardware timestamp isn't an error */ + if (!tc6->hw_tstamp_supported) + return ret; + + snprintf(info->name, sizeof(info->name), "OA TC6 PTP %s", + dev_name(&tc6->spi->dev)); + tc6->ptp_clock = ptp_clock_register(info, &tc6->spi->dev); + if (IS_ERR(tc6->ptp_clock)) { + ret = PTR_ERR(tc6->ptp_clock); + tc6->ptp_clock = NULL; + dev_err(&tc6->spi->dev, "Registration of %s failed\n", + info->name); + } + + if (tc6->ptp_clock) + dev_info(&tc6->spi->dev, "%s registered. index %d\n", + info->name, ptp_clock_index(tc6->ptp_clock)); + return ret; +} +EXPORT_SYMBOL_GPL(oa_tc6_ptp_register); + +/** + * oa_tc6_ptp_unregister - Unregisters clock related callbacks + * @tc6: oa_tc6 struct. + */ +void oa_tc6_ptp_unregister(struct oa_tc6 *tc6) +{ + if (tc6->ptp_clock) { + ptp_clock_unregister(tc6->ptp_clock); + tc6->ptp_clock = NULL; + } +} +EXPORT_SYMBOL_GPL(oa_tc6_ptp_unregister); diff --git a/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h b/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h index 403a9c22b5f1..92e936a18490 100644 --- a/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h +++ b/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h @@ -18,6 +18,9 @@ #include #include +/* Tx timestamp capture register A (high) */ +#define OA_TC6_REG_TTSCA_HIGH (0x10) + /* Control command header */ #define OA_TC6_CTRL_HEADER_DATA_NOT_CTRL BIT(31) #define OA_TC6_CTRL_HEADER_WRITE_NOT_READ BIT(29) @@ -33,6 +36,7 @@ #define OA_TC6_DATA_HEADER_START_WORD_OFFSET GENMASK(19, 16) #define OA_TC6_DATA_HEADER_END_VALID BIT(14) #define OA_TC6_DATA_HEADER_END_BYTE_OFFSET GENMASK(13, 8) +#define OA_TC6_DATA_HEADER_TSC_OFFSET GENMASK(7, 6) #define OA_TC6_DATA_HEADER_PARITY BIT(0) /* Data footer */ @@ -44,6 +48,8 @@ #define OA_TC6_DATA_FOOTER_START_VALID BIT(20) #define OA_TC6_DATA_FOOTER_START_WORD_OFFSET GENMASK(19, 16) #define OA_TC6_DATA_FOOTER_END_VALID BIT(14) +#define OA_TC6_DATA_FOOTER_RTSA_VALID BIT(7) +#define OA_TC6_DATA_FOOTER_RTSP_VALID BIT(6) #define OA_TC6_DATA_FOOTER_END_BYTE_OFFSET GENMASK(13, 8) #define OA_TC6_DATA_FOOTER_TX_CREDITS GENMASK(5, 1) @@ -70,6 +76,33 @@ #define OA_TC6_REG_MMS_MASK GENMASK(19, 16) +#define OA_TC6_TSTAMP_SZ 8 + +#define OA_TC6_TTSCA_REG_ID 1 +#define OA_TC6_TTSCB_REG_ID 2 +#define OA_TC6_TTSCC_REG_ID 3 + +/* STATUS0 and the TTSCA/B/C capture registers are contiguous in MMS 0, so + * they are fetched with a single control transaction while timestamping is + * available. + */ +#define OA_TC6_TTSC_REG_OFFSET (OA_TC6_REG_TTSCA_HIGH - \ + OA_TC6_REG_STATUS0) +#define OA_TC6_TTSC_REG_COUNT (2 * OA_TC6_TTSCC_REG_ID) +#define OA_TC6_STATUS0_TTSC_REG_COUNT (OA_TC6_TTSC_REG_OFFSET + \ + OA_TC6_TTSC_REG_COUNT) + +/* Useful for reading all the registers between CONFIG0 and INT_MASK0 + * in a single SPI transaction + */ +#define CFG0_TO_IMASK0_REG_COUNT (OA_TC6_REG_INT_MASK0 - \ + OA_TC6_REG_CONFIG0 + 1) +#define CFG0_VAL_OFFSET (0) +#define STATUS0_VAL_OFFSET (OA_TC6_REG_STATUS0 - \ + OA_TC6_REG_CONFIG0) +#define IRQM_VAL_OFFSET (OA_TC6_REG_INT_MASK0 - \ + OA_TC6_REG_CONFIG0) + /* Internal structure for MAC-PHY drivers */ struct oa_tc6 { struct net_device *netdev; @@ -95,6 +128,16 @@ struct oa_tc6 { bool prot_ctrl; enum oa_tc6_quirk_flag quirk_flags; struct gpio_desc *reset_gpio; + struct hwtstamp_config ts_config; + struct list_head tx_ts_skb_q; + struct ptp_clock *ptp_clock; + bool hw_tstamp_supported; + bool hw_tstamp_enabled; + u64 tx_hwtstamp_pkts; + u64 tx_hwtstamp_lost; + u64 tx_hwtstamp_err; + u8 ts_ttsc_pending; + u8 ttsc_current_id; }; enum oa_tc6_header_type { @@ -121,5 +164,8 @@ enum oa_tc6_data_end_valid_info { OA_TC6_DATA_END_INVALID, OA_TC6_DATA_END_VALID, }; + +void oa_tc6_cleanup_tx_tstamp_skbs(struct oa_tc6 *tc6); + #endif /* OA_TC6_STD_DEF_H */ diff --git a/drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c b/drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c new file mode 100644 index 000000000000..539cbd36a1d0 --- /dev/null +++ b/drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface framework + * + * Author: Selva Rajagopal + */ + +#include +#include +#include +#include +#include + +#include "oa_tc6_std_def.h" + +static int oa_tc6_set_hwtstamp_settings(struct oa_tc6 *tc6, + const struct hwtstamp_config *ts_cfg) +{ + u32 regs[CFG0_TO_IMASK0_REG_COUNT]; + u32 cfg0, irqm, status0; + bool disabled; + int ret; + + if (tc6->ts_config.flags == ts_cfg->flags && + tc6->ts_config.tx_type == ts_cfg->tx_type && + tc6->ts_config.rx_filter == ts_cfg->rx_filter) + return 0; + + spin_lock_bh(&tc6->tx_skb_lock); + disabled = tc6->disable_traffic; + spin_unlock_bh(&tc6->tx_skb_lock); + if (disabled) + return -EIO; + + /* CONFIG0, STATUS0 and INT_MASK0 span one register block, so a + * single control transaction covers all three. + */ + ret = oa_tc6_read_registers(tc6, OA_TC6_REG_CONFIG0, regs, + ARRAY_SIZE(regs)); + if (ret) { + dev_err(&tc6->spi->dev, "Failed to read CFG0 block\n"); + goto out; + } + + cfg0 = regs[CFG0_VAL_OFFSET]; + status0 = regs[STATUS0_VAL_OFFSET]; + irqm = regs[IRQM_VAL_OFFSET]; + + /* Hardware timestamp feature supports 64-bit timestamp only */ + if (ts_cfg->tx_type == HWTSTAMP_TX_ON || + ts_cfg->rx_filter == HWTSTAMP_FILTER_ALL) + cfg0 |= (OA_TC6_CONFIG0_FTSE_ENABLE | + OA_TC6_CONFIG0_FTSS_64BIT_TSTAMP); + else + cfg0 &= ~OA_TC6_CONFIG0_FTSE_ENABLE; + + if (ts_cfg->tx_type == HWTSTAMP_TX_ON) + irqm &= ~OA_TC6_INT_MASK0_TTSCA_MASK; + else + irqm |= OA_TC6_INT_MASK0_TTSCA_MASK; + + /* STATUS0 is write-one-to-clear, so this acknowledges a capture + * without delivering its timestamp. Skip it while TX timestamping + * stays enabled, where a capture may still be outstanding. + */ + status0 &= OA_TC6_STATUS0_TTSCA_MASK; + if (status0 && (tc6->ts_config.tx_type != HWTSTAMP_TX_ON || + ts_cfg->tx_type != HWTSTAMP_TX_ON)) { + ret = oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, + status0); + if (ret) { + dev_err(&tc6->spi->dev, + "failed to write STATUS0 register\n"); + goto out; + } + } + + ret = oa_tc6_write_register(tc6, OA_TC6_REG_INT_MASK0, irqm); + if (ret) { + dev_err(&tc6->spi->dev, "failed to write IRQM register\n"); + goto out; + } + + ret = oa_tc6_write_register(tc6, OA_TC6_REG_CONFIG0, cfg0); + if (ret) { + dev_err(&tc6->spi->dev, "failed to write CFG0 register\n"); + goto out; + } + spin_lock_bh(&tc6->tx_skb_lock); + tc6->ts_config = *ts_cfg; + if (cfg0 & OA_TC6_CONFIG0_FTSE_ENABLE) + tc6->hw_tstamp_enabled = true; + else + tc6->hw_tstamp_enabled = false; + spin_unlock_bh(&tc6->tx_skb_lock); + + /* The TX timestamp completion interrupt is masked now. Time + * to release the queued skbs. + */ + if (ts_cfg->tx_type != HWTSTAMP_TX_ON) + oa_tc6_cleanup_tx_tstamp_skbs(tc6); +out: + return ret; +} + +/** + * oa_tc6_hwtstamp_get - gets hardware timestamp config + * @tc6: oa_tc6 struct. + * @cfg: kernel copy of hardware timestamp config + */ +void oa_tc6_hwtstamp_get(struct oa_tc6 *tc6, + struct kernel_hwtstamp_config *cfg) +{ + hwtstamp_config_to_kernel(cfg, &tc6->ts_config); +} +EXPORT_SYMBOL_GPL(oa_tc6_hwtstamp_get); + +/** + * oa_tc6_hwtstamp_set - sets hardware timestamp config + * @tc6: oa_tc6 struct. + * @cfg: kernel copy of hardware timestamp config + * + * Return: 0 on success otherwise failed. + */ +int oa_tc6_hwtstamp_set(struct oa_tc6 *tc6, + struct kernel_hwtstamp_config *cfg) +{ + struct hwtstamp_config ts_cfg; + int ret; + + if (!netif_running(tc6->netdev)) + return -EIO; + + /* Set only when STDCAP advertised the capability and the PHC + * registered, which is what ethtool -T reports. + */ + if (!tc6->ptp_clock) + return -EOPNOTSUPP; + + switch (cfg->tx_type) { + case HWTSTAMP_TX_OFF: + case HWTSTAMP_TX_ON: + break; + default: + return -ERANGE; + } + + switch (cfg->rx_filter) { + case HWTSTAMP_FILTER_NONE: + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_SOME: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_NTP_ALL: + break; + default: + return -ERANGE; + } + + hwtstamp_config_from_kernel(&ts_cfg, cfg); + + /* Supports timestamping all traffic */ + if (cfg->rx_filter != HWTSTAMP_FILTER_NONE || + cfg->tx_type == HWTSTAMP_TX_ON) + ts_cfg.rx_filter = HWTSTAMP_FILTER_ALL; + + ret = oa_tc6_set_hwtstamp_settings(tc6, &ts_cfg); + if (ret) + return ret; + + /* Report what was programmed, not asked. */ + cfg->rx_filter = ts_cfg.rx_filter; + + return 0; +} +EXPORT_SYMBOL_GPL(oa_tc6_hwtstamp_set); + +/** + * oa_tc6_get_ts_stats - Provides timestamping stats + * @tc6: oa_tc6 struct. + * @stats: ethtool data structure to fill in + */ +void oa_tc6_get_ts_stats(struct oa_tc6 *tc6, + struct ethtool_ts_stats *stats) +{ + stats->pkts = tc6->tx_hwtstamp_pkts; + stats->err = tc6->tx_hwtstamp_err; + stats->lost = tc6->tx_hwtstamp_lost; +} +EXPORT_SYMBOL_GPL(oa_tc6_get_ts_stats); + +/** + * oa_tc6_get_ts_info - Provides timestamp info for ethtool + * @tc6: oa_tc6 struct. + * @info: ethtool timestamping info structure + * + * Return: 0 on success otherwise failed. + */ +int oa_tc6_get_ts_info(struct oa_tc6 *tc6, + struct kernel_ethtool_ts_info *info) +{ + if (!tc6->ptp_clock) + return ethtool_op_get_ts_info(tc6->netdev, info); + + info->so_timestamping = SOF_TIMESTAMPING_RAW_HARDWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE; + info->phc_index = ptp_clock_index(tc6->ptp_clock); + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | + BIT(HWTSTAMP_FILTER_ALL); + return 0; +} +EXPORT_SYMBOL_GPL(oa_tc6_get_ts_info); diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h index 27f652d4920b..365abdbf60d3 100644 --- a/include/linux/oa_tc6.h +++ b/include/linux/oa_tc6.h @@ -7,13 +7,18 @@ * Author: Parthiban Veerasooran */ +#ifndef _LINUX_OA_TC6_H +#define _LINUX_OA_TC6_H + #include #include +#include /* OPEN Alliance TC6 registers */ /* Standard Capabilities Register */ #define OA_TC6_REG_STDCAP 0x0002 #define OA_TC6_STDCAP_DIRECT_PHY_REG_ACCESS BIT(8) +#define OA_TC6_STDCAP_TIMESTAMP_CAPABILITY BIT(6) /* Reset Control and Status Register */ #define OA_TC6_REG_RESET 0x0003 @@ -23,6 +28,8 @@ #define OA_TC6_REG_CONFIG0 0x0004 #define OA_TC6_CONFIG0_SYNC BIT(15) #define OA_TC6_CONFIG0_ZARFE_ENABLE BIT(12) +#define OA_TC6_CONFIG0_FTSE_ENABLE BIT(7) +#define OA_TC6_CONFIG0_FTSS_64BIT_TSTAMP BIT(6) #define OA_TC6_CONFIG0_PROTE BIT(5) /* Configuration Register #2 */ @@ -30,6 +37,10 @@ /* Status Register #0 */ #define OA_TC6_REG_STATUS0 0x0008 +#define OA_TC6_STATUS0_TTSCAC BIT(10) +#define OA_TC6_STATUS0_TTSCAB BIT(9) +#define OA_TC6_STATUS0_TTSCAA BIT(8) +#define OA_TC6_STATUS0_TTSCA_MASK GENMASK(10, 8) #define OA_TC6_STATUS0_RESETC BIT(6) /* Reset Complete */ #define OA_TC6_STATUS0_HEADER_ERROR BIT(5) #define OA_TC6_STATUS0_LOSS_OF_FRAME_ERROR BIT(4) @@ -43,6 +54,7 @@ /* Interrupt Mask Register #0 */ #define OA_TC6_REG_INT_MASK0 0x000C +#define OA_TC6_INT_MASK0_TTSCA_MASK GENMASK(10, 8) #define OA_TC6_INT_MASK0_HEADER_ERR_MASK BIT(5) #define OA_TC6_INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4) #define OA_TC6_INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3) @@ -93,3 +105,14 @@ int oa_tc6_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum); int oa_tc6_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum, int regnum, u16 val); +int oa_tc6_ptp_register(struct oa_tc6 *tc6, struct ptp_clock_info *info); +int oa_tc6_get_ts_info(struct oa_tc6 *tc6, + struct kernel_ethtool_ts_info *info); +void oa_tc6_hwtstamp_get(struct oa_tc6 *tc6, + struct kernel_hwtstamp_config *cfg); +void oa_tc6_get_ts_stats(struct oa_tc6 *tc6, + struct ethtool_ts_stats *stats); +int oa_tc6_hwtstamp_set(struct oa_tc6 *tc6, + struct kernel_hwtstamp_config *cfg); +void oa_tc6_ptp_unregister(struct oa_tc6 *tc6); +#endif /* _LINUX_OA_TC6_H */ -- 2.43.0