git: f2c300112ea4 - main - if_bnxt: add RX timestamping support
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 09 Oct 2026 10:12:31 UTC
The branch main has been updated by ssaxena:
URL: https://cgit.FreeBSD.org/src/commit/?id=f2c300112ea491d69799c5a1085b57c48acef713
commit f2c300112ea491d69799c5a1085b57c48acef713
Author: Sumit Saxena <sumit.saxena@broadcom.com>
AuthorDate: 2026-08-18 21:00:11 +0000
Commit: Sumit Saxena <ssaxena@FreeBSD.org>
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 <sys/param.h>
+#include <sys/time.h>
+#include <sys/clock.h>
+#include <sys/seqc.h>
+#include <machine/atomic.h>
+#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 <machine/atomic.h>
#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