From nobody Fri Oct 09 10:12:31 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4j1N2l5db5z6vQwW for ; Fri, 09 Oct 2026 10:12:31 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4j1N2l4LMzz4XZX for ; Fri, 09 Oct 2026 10:12:31 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791540751; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Q+yNx9CbSF6VfCSPwXAG+7b6TvTBVpQAi/BeX1iiVKQ=; b=kYommo81IFbhTB0eDA3t1xCowUDTtoUV6GKzrtnQ1+zHtZmDnsp4rVFF4/9pvhz6aloAZs ZO0+7hnozkmBNOH0W6QZEqzLyFLtu4VW7w7MzD22gfHu61Piu/tV86xIHgrOou8sopeFnt a9xUeC3Ji8efNYiYapERB0GfXCg+pLyhlQNddHA7fhtZ+UONmYF+r9gf5Vjx1gXOWhXg+o OZ6ikeu5CPCgIq4hPsqFnwuZ9JuZYoM2JW28vWu4cFmsjsnccnM34ECLuU8UOEXrKDUA5j W4er1z8Q8Li2a6BFvSbmtInPQAIkkn9LoRl3bA6+4Xk1dZ7GIdcyfZq6mpOmjg== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1791540751; b=kOLpsQextaEspLJT7T33IgM+pLba3bqMWGHvcDAd+d+F+p+F88nk48rehJL3I3siDFechY uDQgbzAKRlC2QNf5LWY5olLcjffsxgd9eZ9sL4BGOlhY8EYyj2xwIn0WIN2e6r3hYdOsjT JeyZZSJoMtrpnROtOyNJMaV+mlBWJqAo1SkeMcHa58J1K2uLfE10DT7hAC8dCTQed42YVS xBGeRCpUQ/HGlw9qPjgmcvFGNAndk+JfCuRQcx/223MbmEBpf9lmE4JJUn5+V6Aq6e1e01 xk9jcP/XRwgmBIohLpjoSmWPtuzX/noD9WWLRJhxj81oBST4fBoP5Yo7J38Kog== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791540751; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Q+yNx9CbSF6VfCSPwXAG+7b6TvTBVpQAi/BeX1iiVKQ=; b=FcJrfdrD7iVD8cLOmT4Yf+eJN1OgDLEsGf/RCtzTshJ1dQXFX/W8Z2T2M8NZKpAm93o1xY uvSZeYqyLQ882MKBpvofOtk55Nge32x7F6kd9pM94wUHOTs1PVn5zLBmX13AvlJk4DZaeF epwAaeNL9ifbzcnwj9qFPYtvQ38400ALHTzvU03VlNIDfocWH4+uYwAFpttHD1Ku1gZdgq yIDYfWB/mnx+k4mjs80RCaVj1zBlS/wGLBO4mGiw7g42HbY/FImqy/6lrhVN/+nFDm6sMr LSnDZruy42hFCuRRlQy/dKMwnSRKzplsA+y4bIq9A/IsEs0CPSE4TluE19fWrQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4j1N2l3Dx7z1LRt for ; Fri, 09 Oct 2026 10:12:31 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 43131 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Fri, 09 Oct 2026 10:12:31 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: Sumit Saxena From: Sumit Saxena Subject: git: f2c300112ea4 - main - if_bnxt: add RX timestamping support List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: ssaxena X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: f2c300112ea491d69799c5a1085b57c48acef713 Auto-Submitted: auto-generated Date: Fri, 09 Oct 2026 10:12:31 +0000 Message-Id: <6ac8be0f.43131.2b96810e@gitrepo.freebsd.org> The branch main has been updated by ssaxena: URL: https://cgit.FreeBSD.org/src/commit/?id=f2c300112ea491d69799c5a1085b57c48acef713 commit f2c300112ea491d69799c5a1085b57c48acef713 Author: Sumit Saxena AuthorDate: 2026-08-18 21:00:11 +0000 Commit: Sumit Saxena CommitDate: 2026-10-09 08:24:42 +0000 if_bnxt: add RX timestamping support Add PHC register mapping, RTC initialization, refclk read with FW-reset serialization, and sbinuptime-based calibration to convert the raw hardware timestamp carried in RX completions into an mbuf timestamp. Advertise IFCAP_HWRXTSTMP, configure the RX timestamp filter in bnxt_init(), and drain the calibration callout on stop. Check HWRM_FUNC_QCAPS_OUTPUT_FLAGS_PTP_SUPPORTED in bnxt_hwrm_func_qcaps() so the capability bit PTP is gated on actually gets set, without which PTP never armed on real hardware. Reviewed by: gallatin MFC after: 2 weeks Sponsored by: Broadcom Inc. Differential Revision: https://reviews.freebsd.org/D58596 --- sys/conf/files | 1 + sys/dev/bnxt/bnxt_en/bnxt.h | 5 + sys/dev/bnxt/bnxt_en/bnxt_hwrm.c | 2 + sys/dev/bnxt/bnxt_en/bnxt_ptp.c | 392 ++++++++++++++++++++++++++++++++++++++ sys/dev/bnxt/bnxt_en/bnxt_ptp.h | 88 +++++++++ sys/dev/bnxt/bnxt_en/bnxt_txrx.c | 22 +++ sys/dev/bnxt/bnxt_en/if_bnxt.c | 26 +++ sys/modules/bnxt/bnxt_en/Makefile | 1 + 8 files changed, 537 insertions(+) diff --git a/sys/conf/files b/sys/conf/files index 7450d65bf8b7..73a5d5fa8408 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1332,6 +1332,7 @@ dev/bnxt/bnxt_en/bnxt_ulp.c optional bnxt iflib pci compile-with "${BNXT_C}" dev/bnxt/bnxt_en/bnxt_log.c optional bnxt iflib pci compile-with "${BNXT_C}" dev/bnxt/bnxt_en/bnxt_log_data.c optional bnxt iflib pci compile-with "${BNXT_C}" dev/bnxt/bnxt_en/bnxt_coredump.c optional bnxt iflib pci compile-with "${BNXT_C}" +dev/bnxt/bnxt_en/bnxt_ptp.c optional bnxt iflib pci compile-with "${BNXT_C}" dev/bnxt/bnxt_en/if_bnxt.c optional bnxt iflib pci compile-with "${BNXT_C}" dev/bwi/bwimac.c optional bwi dev/bwi/bwiphy.c optional bwi diff --git a/sys/dev/bnxt/bnxt_en/bnxt.h b/sys/dev/bnxt/bnxt_en/bnxt.h index 34afad28377d..98f8c1d3f5fa 100644 --- a/sys/dev/bnxt/bnxt_en/bnxt.h +++ b/sys/dev/bnxt/bnxt_en/bnxt.h @@ -1324,6 +1324,7 @@ struct bnxt_softc { #define BNXT_FW_CAP_SW_MAX_RESOURCE_LIMITS BIT_ULL(61) #define BNXT_SW_RES_LMT(bp) ((bp)->fw_cap & BNXT_FW_CAP_SW_MAX_RESOURCE_LIMITS) + #define BNXT_FW_CAP_PTP BIT_ULL(62) uint32_t lpi_tmr_lo; uint32_t lpi_tmr_hi; @@ -1419,6 +1420,9 @@ struct bnxt_softc { #define MAX_NUM_DMA_INDICATIONS 10 struct iflib_dma_info mgmt_dma_data[MAX_NUM_DMA_INDICATIONS]; + + struct bnxt_ptp_cfg *ptp_cfg; + bool rx_ts_enabled; }; struct bnxt_filter_info { @@ -1480,6 +1484,7 @@ int bnxt_alloc_ctx_pg_tbls(struct bnxt_softc *softc, struct bnxt_ctx_pg_info *ctx_pg, uint32_t mem_size, uint8_t depth, struct bnxt_ctx_mem_type *ctxm); +int bnxt_hwrm_ptp_qcfg(struct bnxt_softc *bp); static inline u32 readl_fbsd(struct bnxt_softc *bp, u32 reg_off, u8 bar_idx) diff --git a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c index a6742b6ca37c..11d40a1a528a 100644 --- a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c +++ b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c @@ -1265,6 +1265,8 @@ bnxt_hwrm_func_qcaps(struct bnxt_softc *softc) softc->fw_cap |= BNXT_FW_CAP_PTP_PTM; if (flags_ext & HWRM_FUNC_QCAPS_OUTPUT_FLAGS_EXT_PTP_64BIT_RTC_SUPPORTED) softc->fw_cap |= BNXT_FW_CAP_PTP_RTC; + if (BNXT_CHIP_P7(softc) && (flags & HWRM_FUNC_QCAPS_OUTPUT_FLAGS_PTP_SUPPORTED)) + softc->fw_cap |= BNXT_FW_CAP_PTP; if (BNXT_PF(softc) && (flags_ext & HWRM_FUNC_QCAPS_OUTPUT_FLAGS_EXT_HOT_RESET_IF_SUPPORT)) softc->fw_cap |= BNXT_FW_CAP_HOT_RESET_IF; if (BNXT_PF(softc) && (flags_ext & HWRM_FUNC_QCAPS_OUTPUT_FLAGS_EXT_FW_LIVEPATCH_SUPPORTED)) diff --git a/sys/dev/bnxt/bnxt_en/bnxt_ptp.c b/sys/dev/bnxt/bnxt_en/bnxt_ptp.c new file mode 100644 index 000000000000..73d0c24c63f1 --- /dev/null +++ b/sys/dev/bnxt/bnxt_en/bnxt_ptp.c @@ -0,0 +1,392 @@ +/* + * Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2026 Broadcom, All Rights Reserved. + * The term Broadcom refers to Broadcom Limited and/or its subsidiaries + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include "bnxt_ptp.h" +#include "bnxt_hwrm.h" + +static int +bnxt_map_ptp_regs(struct bnxt_softc *bp) +{ + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + uint32_t *reg_arr; + int i; + + reg_arr = ptp->refclk_regs; + for (i = 0; i < 2; i++) { + if (reg_arr[i] & BNXT_GRC_BASE_MASK) + return (EINVAL); + ptp->refclk_mapped_regs[i] = ptp->refclk_regs[i]; + } + + return (0); +} + +static int +bnxt_ptp_cfg_settime(struct bnxt_softc *bp, uint64_t time) +{ + struct hwrm_func_ptp_cfg_input req = {0}; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_PTP_CFG); + + req.enables = htole16(HWRM_FUNC_PTP_CFG_INPUT_ENABLES_PTP_SET_TIME); + req.ptp_set_time = htole64(time); + return (hwrm_send_message(bp, &req, sizeof(req))); +} + +static int +bnxt_ptp_init_rtc(struct bnxt_softc *bp, bool phc_cfg) +{ + struct timespec ts; + uint64_t ns; + int rc; + + if (!bp->ptp_cfg || !BNXT_PTP_USE_RTC(bp)) + return (ENODEV); + + if (!phc_cfg) { + nanotime(&ts); + ns = (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec; + rc = bnxt_ptp_cfg_settime(bp, ns); + if (rc) + return (rc); + } + + return (0); +} + +static int +__bnxt_refclk_read(struct bnxt_softc *bp, uint64_t *ns) +{ + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + uint32_t high_before, high_now, low; + + if (test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) + return (EIO); + + /* Refclk registers live in the HWRM BAR, not the doorbell BAR. */ + high_before = readl_fbsd(bp, ptp->refclk_mapped_regs[1], BNXT_HWRM_BAR_IDX); + low = readl_fbsd(bp, ptp->refclk_mapped_regs[0], BNXT_HWRM_BAR_IDX); + high_now = readl_fbsd(bp, ptp->refclk_mapped_regs[1], BNXT_HWRM_BAR_IDX); + if (high_now != high_before) + low = readl_fbsd(bp, ptp->refclk_mapped_regs[0], BNXT_HWRM_BAR_IDX); + *ns = (((uint64_t)high_now) << 32) | ((uint64_t)low); + + return (0); +} + +static int +bnxt_refclk_read(struct bnxt_softc *bp, uint64_t *ns) +{ + int rc; + + /* Serializes reg access vs FW reset. Must stay a spin mutex: the + * calibration callout runs C_DIRECT_EXEC, sometimes from interrupt context. */ + mtx_lock_spin(&bp->ptp_cfg->ptp_lock); + rc = __bnxt_refclk_read(bp, ns); + mtx_unlock_spin(&bp->ptp_cfg->ptp_lock); + return (rc); +} + +uint64_t +bnxt_ptp_hwtstamp_to_ns(struct bnxt_ptp_cfg *priv, uint64_t hw_tstmp) +{ + struct bnxt_clbr_point *cp, dcp; + sbintime_t sbt_cur_to_prev, sbt; + uint64_t hw_clocks; + uint64_t hw_clk_div; + seqc_t gen; + + for (;;) { + cp = &priv->clbr_points[priv->clbr_curr]; + /* Not seqc_read(): a permanently-invalidated slot stays odd + * forever, which would spin here indefinitely. Bail out instead. */ + gen = seqc_read_any(&cp->gen_ts); + if (seqc_in_modify(gen)) + return (0); + dcp = *cp; + if (seqc_consistent(&cp->gen_ts, gen)) + break; + } + + /* Interpolate: (hw_tstmp - hw_prev) * (cur_time - prev_time) / + * (hw_cur - hw_prev) + prev_time, using integer math to avoid float/overflow. */ + if (hw_tstmp < dcp.hw_prev_ts) + return (0); + + hw_clocks = hw_tstmp - dcp.hw_prev_ts; + sbt_cur_to_prev = (dcp.sbt_cur_ts - dcp.sbt_prev_ts); + hw_clk_div = dcp.hw_curr_ts - dcp.hw_prev_ts; + + if (!hw_clk_div) + return (0); + + /* Reject stale calibration intervals; also bounds the math below. */ + if (hw_clk_div > BNXT_PTP_MAX_CLBR_INTERVAL_NS) + return (0); + + /* Allow extrapolating up to one interval past hw_curr_ts; most RX + * timestamps land there, not inside [hw_prev_ts, hw_curr_ts]. */ + if (hw_clocks > 2 * hw_clk_div) + return (0); + + /* Split the divide so hw_clocks * remainder can't overflow 64 bits. */ + sbt = hw_clocks * (sbt_cur_to_prev / hw_clk_div) + + hw_clocks * (sbt_cur_to_prev % hw_clk_div) / hw_clk_div + + dcp.sbt_prev_ts; + return (sbttons(sbt)); +} + +static void +bnxt_ptp_get_current_time(struct bnxt_softc *bp) +{ + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + + if (!ptp) + return; + + atomic_store_64((volatile uint64_t *)&ptp->old_time, + ptp->current_time); + bnxt_refclk_read(bp, &ptp->current_time); +} + +static void +bnxt_calibration_callout(void *arg) +{ + struct bnxt_ptp_cfg *priv; + struct bnxt_clbr_point *next, *curr; + int clbr_curr_next; + sbintime_t sbt; + uint64_t nxt_clbr_hw_curr; + + priv = arg; + curr = &priv->clbr_points[priv->clbr_curr]; + clbr_curr_next = priv->clbr_curr + 1; + if (clbr_curr_next >= nitems(priv->clbr_points)) + clbr_curr_next = 0; + next = &priv->clbr_points[clbr_curr_next]; + + bnxt_ptp_get_current_time(priv->bp); + sbt = sbinuptime(); + nxt_clbr_hw_curr = priv->current_time; + + if (priv->clbr_ticks && + ((nxt_clbr_hw_curr - curr->hw_curr_ts) >> BNXT_TSTMP_PREC) == 0) { + /* A stalled tick isn't necessarily a dead counter (HW may briefly + * not count right after a reset); only disable after MAX_CLBR_STALLS. */ + if (++priv->clbr_stall_cnt < BNXT_PTP_MAX_CLBR_STALLS) { + bnxt_reset_calibration_callout(priv); + return; + } + device_printf(priv->bp->dev, + "HW failed tstmp frozen, current %#jx prev %#jx, disabling\n", + (uintmax_t)nxt_clbr_hw_curr, (uintmax_t)curr->hw_curr_ts); + priv->clbr_ticks = 0; + priv->clbr_stall_cnt = 0; + /* Permanently invalidate by leaving gen_ts odd (seqcount's in-modify + * state); not via seqc_write_begin() since we never pair it with _end(). */ + atomic_store_rel_int(&curr->gen_ts, curr->gen_ts + SEQC_MOD); + return; + } + + priv->clbr_stall_cnt = 0; + seqc_write_begin(&next->gen_ts); + next->sbt_prev_ts = curr->sbt_cur_ts; + next->hw_prev_ts = curr->hw_curr_ts; + next->hw_curr_ts = (priv->current_time) & BNXT_TIMER_MASK; + next->sbt_cur_ts = sbt; + priv->clbr_curr = clbr_curr_next; + seqc_write_end(&next->gen_ts); + + priv->clbr_ticks++; + bnxt_reset_calibration_callout(priv); +} + +void +bnxt_reset_calibration_callout(struct bnxt_ptp_cfg *priv) +{ + if (priv->clbr_ticks == 0) + bnxt_calibration_callout(priv); + else + callout_reset_sbt_curcpu(&priv->tstmp_clbr, SBT_1S, 0, + bnxt_calibration_callout, priv, C_DIRECT_EXEC); +} + +void +bnxt_ptp_stop_calibration(struct bnxt_ptp_cfg *priv) +{ + /* Reset clbr_ticks so the next bnxt_reset_calibration_callout() calibrates + * immediately; clbr_points is stale after a down/up cycle. */ + priv->clbr_ticks = 0; + priv->clbr_stall_cnt = 0; + memset(priv->clbr_points, 0, sizeof(priv->clbr_points)); +} + +void +bnxt_ptp_free(struct bnxt_softc *bp) +{ + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + + if (!ptp) + return; + + /* ptp_lock and tstmp_clbr are set up together, so drain the callout + * before destroying the lock it takes, in case one is still in flight. */ + if (mtx_initialized(&ptp->ptp_lock)) { + callout_drain(&ptp->tstmp_clbr); + mtx_destroy(&ptp->ptp_lock); + } + free(ptp, M_DEVBUF); + bp->ptp_cfg = NULL; +} + +static int +bnxt_ptp_init(struct bnxt_softc *bp) +{ + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + int rc; + + if (!ptp) + return (0); + + rc = bnxt_map_ptp_regs(bp); + if (rc) + return (rc); + + if (BNXT_PTP_USE_RTC(bp)) { + rc = bnxt_ptp_init_rtc(bp, ptp->rtc_configured); + if (rc) + goto out; + } + + mtx_init(&ptp->ptp_lock, "BNXT PTP LOCK", NULL, + MTX_SPIN | MTX_NOWITNESS); + /* Init unconditionally so bnxt_ptp_free() can always safely drain it + * whenever ptp_lock is initialized. */ + callout_init(&ptp->tstmp_clbr, 1); + + bnxt_refclk_read(bp, &ptp->current_time); + atomic_store_64((volatile uint64_t *)&ptp->old_time, ptp->current_time); + + return (0); +out: + /* Nothing to unmap: bnxt_map_ptp_regs() rejects GRC-window regs before mapping. */ + return (rc); +} + +int +bnxt_hwrm_ptp_qcfg(struct bnxt_softc *bp) +{ + struct hwrm_port_mac_ptp_qcfg_output *resp = + (void *)bp->hwrm_cmd_resp.idi_vaddr; + struct hwrm_port_mac_ptp_qcfg_input req = {0}; + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + uint8_t flags; + int rc; + + if (!BNXT_CHIP_P7(bp)) { + rc = ENODEV; + goto no_ptp; + } + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_MAC_PTP_QCFG); + + req.port_id = htole16(bp->pf.port_id); + rc = hwrm_send_message(bp, &req, sizeof(req)); + if (rc) + goto exit; + + flags = resp->flags; + if (!ptp) + ptp = malloc(sizeof(*ptp), M_DEVBUF, M_NOWAIT | M_ZERO); + + if (!ptp) { + rc = ENOMEM; + goto exit; + } + + if (flags & + (HWRM_PORT_MAC_PTP_QCFG_OUTPUT_FLAGS_PARTIAL_DIRECT_ACCESS_REF_CLOCK | + HWRM_PORT_MAC_PTP_QCFG_OUTPUT_FLAGS_64B_PHC_TIME)) { + ptp->refclk_regs[0] = le32toh(resp->ts_ref_clock_reg_lower); + ptp->refclk_regs[1] = le32toh(resp->ts_ref_clock_reg_upper); + } + + ptp->bp = bp; + bp->ptp_cfg = ptp; + + ptp->rtc_configured = + (flags & HWRM_PORT_MAC_PTP_QCFG_OUTPUT_FLAGS_RTC_CONFIGURED) != 0; + rc = bnxt_ptp_init(bp); + if (rc) + device_printf(bp->dev, "PTP initialization failed.\n"); +exit: + if (!rc) + return (0); + +no_ptp: + /* Use bnxt_ptp_free(), not free(): ptp may already have a mutex/callout + * set up from a prior call, which a plain free() would leak. */ + bnxt_ptp_free(bp); + return (rc); +} + +int +bnxt_ptp_cfg_tstamp_filters(struct bnxt_softc *bp) +{ + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + struct hwrm_port_mac_cfg_input req = {0}; + int rc; + + if (!ptp) + return (EOPNOTSUPP); + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_MAC_CFG); + + if (!(bp->fw_cap & BNXT_FW_CAP_RX_ALL_PKT_TS)) { + device_printf(bp->dev, + "Unsupported FW for all RX pkts timestamp filter\n"); + return (EOPNOTSUPP); + } + + req.flags = + htole32(HWRM_PORT_MAC_CFG_INPUT_FLAGS_ALL_RX_TS_CAPTURE_ENABLE); + req.enables = + htole32(HWRM_PORT_MAC_CFG_INPUT_ENABLES_RX_TS_CAPTURE_PTP_MSG_TYPE); + + rc = hwrm_send_message(bp, &req, sizeof(req)); + if (rc) + device_printf(bp->dev, + "Failed to configure HW packet timestamp filters\n"); + + return (rc); +} diff --git a/sys/dev/bnxt/bnxt_en/bnxt_ptp.h b/sys/dev/bnxt/bnxt_en/bnxt_ptp.h new file mode 100644 index 000000000000..81a40800a2a6 --- /dev/null +++ b/sys/dev/bnxt/bnxt_en/bnxt_ptp.h @@ -0,0 +1,88 @@ +/* + * Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2026 Broadcom, All Rights Reserved. + * The term Broadcom refers to Broadcom Limited and/or its subsidiaries + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BNXT_PTP_H +#define BNXT_PTP_H + +#include "bnxt.h" + +/* + * BNXT_GRCPF_REG_CHIMP_COMM, BNXT_GRCPF_REG_CHIMP_COMM_TRIGGER, + * BNXT_GRCPF_REG_WINDOW_BASE_OUT, BNXT_GRC_BASE_MASK, and + * BNXT_GRC_OFFSET_MASK are already defined in bnxt.h, included above. + */ + +#define BNXT_TIMER_MASK 0x0000ffffffffffffUL + +#define BNXT_LO_TIMER_MASK 0x0000ffffffffUL +#define BNXT_HI_TIMER_MASK 0xffff00000000UL + +#define BNXT_PTP_USE_RTC(bp) ((bp)->fw_cap & BNXT_FW_CAP_PTP_RTC) + +#define BNXT_TSTMP_PREC 10 + +/* 1s cadence plus callout jitter; wider gaps are too stale to use. */ +#define BNXT_PTP_MAX_CLBR_INTERVAL_NS 2000000000ULL + +/* Consecutive stalled 1s ticks required before declaring the counter frozen; + * a single stall right after a reset isn't necessarily dead. */ +#define BNXT_PTP_MAX_CLBR_STALLS 3 + +struct bnxt_clbr_point { + uint64_t hw_curr_ts; + uint64_t hw_prev_ts; + sbintime_t sbt_cur_ts; + sbintime_t sbt_prev_ts; + seqc_t gen_ts; +}; + +struct bnxt_ptp_cfg { + uint64_t current_time; + uint64_t old_time; + struct bnxt_softc *bp; + uint8_t rtc_configured:1; + + uint32_t refclk_regs[2]; + uint32_t refclk_mapped_regs[2]; + + struct callout tstmp_clbr; + int clbr_ticks; + int clbr_curr; + int clbr_stall_cnt; + struct bnxt_clbr_point clbr_points[2]; + + struct mtx ptp_lock; +}; + +void bnxt_reset_calibration_callout(struct bnxt_ptp_cfg *priv); +void bnxt_ptp_stop_calibration(struct bnxt_ptp_cfg *priv); +int bnxt_ptp_cfg_tstamp_filters(struct bnxt_softc *bp); +uint64_t bnxt_ptp_hwtstamp_to_ns(struct bnxt_ptp_cfg *priv, uint64_t hw_tstmp); +void bnxt_ptp_free(struct bnxt_softc *bp); + +#endif /* BNXT_PTP_H */ diff --git a/sys/dev/bnxt/bnxt_en/bnxt_txrx.c b/sys/dev/bnxt/bnxt_en/bnxt_txrx.c index 3e867454de8a..8e95cca12a65 100644 --- a/sys/dev/bnxt/bnxt_en/bnxt_txrx.c +++ b/sys/dev/bnxt/bnxt_en/bnxt_txrx.c @@ -38,7 +38,9 @@ #include "opt_inet6.h" #include "opt_rss.h" +#include #include "bnxt.h" +#include "bnxt_ptp.h" /* * Function prototypes @@ -495,9 +497,11 @@ bnxt_pkt_get_l2(struct bnxt_softc *softc, if_rxd_info_t ri, { struct rx_pkt_cmpl *rcp; struct rx_pkt_cmpl_hi *rcph; + struct rx_pkt_v2_cmpl_hi *rcph_v2; struct rx_abuf_cmpl *acp; uint32_t flags2; uint32_t errors; + uint64_t ts, ts_lo, ts_poll; uint8_t ags; int i; @@ -551,6 +555,24 @@ bnxt_pkt_get_l2(struct bnxt_softc *softc, if_rxd_info_t ri, } } + if (softc->rx_ts_enabled && softc->ptp_cfg != NULL) { + rcph_v2 = (struct rx_pkt_v2_cmpl_hi *)rcph; + ts_poll = atomic_load_acq_64( + (volatile uint64_t *)&softc->ptp_cfg->old_time); + ts_lo = le32toh(rcph_v2->timestamp); + ts = (ts_poll & BNXT_HI_TIMER_MASK) | ts_lo; + if (ts_lo < (ts_poll & BNXT_LO_TIMER_MASK)) { + ts += BNXT_LO_TIMER_MASK + 1; + } + ts = bnxt_ptp_hwtstamp_to_ns(softc->ptp_cfg, ts); +#ifdef IFLIB_IRI_VALID_FLAGS + if (ts != 0) { + ri->iri_rcv_tstmp = ts; + ri->iri_flags |= M_TSTMP; + } +#endif + } + /* And finally the ag ring stuff. */ for (i=1; i < ri->iri_nfrags; i++) { NEXT_CP_CONS_V(&cpr->ring, cpr->cons, cpr->v_bit); diff --git a/sys/dev/bnxt/bnxt_en/if_bnxt.c b/sys/dev/bnxt/bnxt_en/if_bnxt.c index 205d98585a1e..044ab619c399 100644 --- a/sys/dev/bnxt/bnxt_en/if_bnxt.c +++ b/sys/dev/bnxt/bnxt_en/if_bnxt.c @@ -75,6 +75,7 @@ #include "bnxt_log.h" #include "bnxt_log_data.h" #include "bnxt_coredump.h" +#include "bnxt_ptp.h" /* * PCI Device ID Table @@ -2901,6 +2902,17 @@ bnxt_attach_pre(if_ctx_t ctx) if (rc) goto failed; + if (softc->fw_cap & BNXT_FW_CAP_PTP) { + rc = bnxt_hwrm_ptp_qcfg(softc); + if (!rc) { + scctx->isc_capabilities |= IFCAP_HWRXTSTMP; + scctx->isc_capenable |= IFCAP_HWRXTSTMP; + } else { + /* PTP is an optional capability; do not fail attach. */ + rc = 0; + } + } + set_bit(BNXT_STATE_OPEN, &softc->state); INIT_WORK(&softc->sp_task, bnxt_sp_task); INIT_DELAYED_WORK(&softc->fw_reset_task, bnxt_fw_reset_task); @@ -3005,6 +3017,7 @@ bnxt_detach(if_ctx_t ctx) bnxt_num_pfs--; bnxt_wol_config(ctx); bnxt_do_disable_intr(&softc->def_cp_ring); + bnxt_ptp_free(softc); bnxt_free_sysctl_ctx(softc); bnxt_free_crash_dump_mem(softc); bnxt_hwrm_func_reset(softc); @@ -3313,6 +3326,14 @@ bnxt_init_hw(if_ctx_t ctx) if (rc) goto fail; skip_def_cp_ring: + if (softc->scctx->isc_capenable & IFCAP_HWRXTSTMP) { + if (!bnxt_ptp_cfg_tstamp_filters(softc)) { + softc->rx_ts_enabled = true; + /* tstmp_clbr is callout_init()'d in bnxt_ptp_init(). */ + bnxt_reset_calibration_callout(softc->ptp_cfg); + } + } + for (i = 0; i < softc->nrxqsets; i++) { /* Allocate the statistics context */ rc = bnxt_hwrm_stat_ctx_alloc(softc, &softc->rx_cp_rings[i], @@ -3480,6 +3501,11 @@ bnxt_stop(if_ctx_t ctx) { struct bnxt_softc *softc = iflib_get_softc(ctx); + if (softc->rx_ts_enabled) { + callout_drain(&softc->ptp_cfg->tstmp_clbr); + bnxt_ptp_stop_calibration(softc->ptp_cfg); + softc->rx_ts_enabled = false; + } softc->is_dev_init = false; bnxt_do_disable_intr(&softc->def_cp_ring); bnxt_func_reset(softc); diff --git a/sys/modules/bnxt/bnxt_en/Makefile b/sys/modules/bnxt/bnxt_en/Makefile index e3cc3e79377c..67bd2646fa4d 100644 --- a/sys/modules/bnxt/bnxt_en/Makefile +++ b/sys/modules/bnxt/bnxt_en/Makefile @@ -14,6 +14,7 @@ SRCS += bnxt_ulp.c bnxt_ulp.h SRCS += bnxt_log.c bnxt_log.h SRCS += bnxt_log_data.c bnxt_log_data.h SRCS += bnxt_coredump.c bnxt_coredump.h +SRCS += bnxt_ptp.c bnxt_ptp.h SRCS += ${LINUXKPI_GENSRCS} CFLAGS+= -DIFLIB