From: <sukhdeeps@marvell.com>
To: <netdev@vger.kernel.org>
Cc: <andrew+netdev@lunn.ch>, <davem@davemloft.net>,
<edumazet@google.com>, <kuba@kernel.org>, <pabeni@redhat.com>,
<linux-kernel@vger.kernel.org>, <horms@kernel.org>,
<vadim.fedorenko@linux.dev>,
Sukhdeep Singh <sukhdeeps@marvell.com>
Subject: [PATCH net-next v3 10/12] net: atlantic: add AQC113 PTP hardware ops in hw_atl2
Date: Wed, 20 May 2026 19:40:43 +0530 [thread overview]
Message-ID: <20260520141046.2151-11-sukhdeeps@marvell.com> (raw)
In-Reply-To: <20260520141046.2151-1-sukhdeeps@marvell.com>
From: Sukhdeep Singh <sukhdeeps@marvell.com>
Add the hardware-layer PTP implementation for AQC113 (Antigua):
- hw_atl2.h/hw_atl2_utils.h/hw_atl2_internal.h: export PTP function
prototypes, per-instance aq_ptp_offset arrays, RX timestamp size
(HW_ATL2_RX_TS_SIZE=8), and reduced HW_ATL2_RXBUF_MAX=172 (AQC113
on-chip RX packet buffer hardware limit for data TCs).
- hw_atl2.c: implement hw_atl2_enable_ptp() to program TSG clocks,
PTP TC receive path and filter ART entries, and re-enable PTP state
after hardware reset.
- hw_atl2.c: implement hw_atl2_adj_sys_clock(), hw_atl2_adj_clock_freq(),
and aq_get_ptp_ts() for TSG clock read/adjust/increment operations.
- hw_atl2.c: implement hw_atl2_gpio_pulse() for PPS output generation
via TSG pulse generator.
- hw_atl2.c: implement hw_atl2_hw_tx_ptp_ring_init() and
hw_atl2_hw_rx_ptp_ring_init() for PTP ring setup.
- hw_atl2.c: implement hw_atl2_hw_ring_tx_ptp_get_ts() to read TX
timestamp from descriptor writeback, and hw_atl2_hw_rx_extract_ts()
to extract RX timestamp from the 8-byte packet trailer.
- hw_atl2.c: add hw_atl2_hw_get_clk_sel() helper.
- Wire all new ops into hw_atl2_ops.
Signed-off-by: Sukhdeep Singh <sukhdeeps@marvell.com>
---
.../aquantia/atlantic/hw_atl2/hw_atl2.c | 202 +++++++++++++++++-
.../aquantia/atlantic/hw_atl2/hw_atl2.h | 12 ++
.../atlantic/hw_atl2/hw_atl2_internal.h | 3 +-
.../aquantia/atlantic/hw_atl2/hw_atl2_utils.h | 10 +
4 files changed, 224 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c
index 39d84576ca51..d6a97a1ec5c6 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c
@@ -7,6 +7,7 @@
#include "aq_hw_utils.h"
#include "aq_ring.h"
#include "aq_nic.h"
+#include "aq_ptp.h"
#include "hw_atl/hw_atl_b0.h"
#include "hw_atl/hw_atl_utils.h"
#include "hw_atl/hw_atl_llh.h"
@@ -20,6 +21,15 @@
static int hw_atl2_act_rslvr_table_set(struct aq_hw_s *self, u8 location,
u32 tag, u32 mask, u32 action);
+static void hw_atl2_enable_ptp(struct aq_hw_s *self,
+ unsigned int param, int enable);
+static int hw_atl2_hw_tx_ptp_ring_init(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring);
+static int hw_atl2_hw_rx_ptp_ring_init(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring);
+static void aq_get_ptp_ts(struct aq_hw_s *self, u64 *stamp);
+static int hw_atl2_adj_clock_freq(struct aq_hw_s *self, s32 ppb);
+
#define DEFAULT_BOARD_BASIC_CAPABILITIES \
.is_64_dma = true, \
.op64bit = true, \
@@ -127,6 +137,7 @@ static u32 hw_atl2_sem_act_rslvr_get(struct aq_hw_s *self)
static int hw_atl2_hw_reset(struct aq_hw_s *self)
{
struct hw_atl2_priv *priv = self->priv;
+ s8 clk_sel;
int err;
int i;
@@ -144,6 +155,13 @@ static int hw_atl2_hw_reset(struct aq_hw_s *self)
priv->l3l4_filters[i].l4_index = -1;
}
+ clk_sel = READ_ONCE(self->clk_select);
+ if (clk_sel != -1)
+ hw_atl2_enable_ptp(self,
+ clk_sel,
+ aq_utils_obj_test(&self->flags, AQ_HW_PTP_AVAILABLE) ?
+ 1 : 0);
+
self->aq_fw_ops->set_state(self, MPI_RESET);
err = aq_hw_err_from_flags(self);
@@ -721,14 +739,32 @@ static int hw_atl2_hw_ring_rx_init(struct aq_hw_s *self,
struct aq_ring_s *aq_ring,
struct aq_ring_param_s *aq_ring_param)
{
- return hw_atl_b0_hw_ring_rx_init(self, aq_ring, aq_ring_param);
+ int res = hw_atl_b0_hw_ring_rx_init(self, aq_ring, aq_ring_param);
+
+ if (!res && aq_ptp_ring(aq_ring->aq_nic, aq_ring)) {
+ int rx_err = hw_atl2_hw_rx_ptp_ring_init(self, aq_ring);
+
+ if (!res)
+ res = rx_err;
+ }
+
+ return res;
}
static int hw_atl2_hw_ring_tx_init(struct aq_hw_s *self,
struct aq_ring_s *aq_ring,
struct aq_ring_param_s *aq_ring_param)
{
- return hw_atl_b0_hw_ring_tx_init(self, aq_ring, aq_ring_param);
+ int res = hw_atl_b0_hw_ring_tx_init(self, aq_ring, aq_ring_param);
+
+ if (!res && aq_ptp_ring(aq_ring->aq_nic, aq_ring)) {
+ int tx_err = hw_atl2_hw_tx_ptp_ring_init(self, aq_ring);
+
+ if (!res)
+ res = tx_err;
+ }
+
+ return res;
}
#define IS_FILTER_ENABLED(_F_) ((packet_filter & (_F_)) ? 1U : 0U)
@@ -888,6 +924,151 @@ static struct aq_stats_s *hw_atl2_utils_get_hw_stats(struct aq_hw_s *self)
return &self->curr_stats;
}
+static void hw_atl2_enable_ptp(struct aq_hw_s *self,
+ unsigned int param, int enable)
+{
+ WRITE_ONCE(self->clk_select, (s8)param);
+
+ /* enable tsg counter */
+ hw_atl2_tsg_clock_reset(self, self->clk_select);
+ hw_atl2_tsg_clock_en(self, !self->clk_select, enable);
+ hw_atl2_tsg_clock_en(self, self->clk_select, enable);
+
+ if (enable)
+ hw_atl2_adj_clock_freq(self, 0);
+
+ hw_atl2_tpb_tps_highest_priority_tc_enable_set(self, enable);
+}
+
+static void aq_get_ptp_ts(struct aq_hw_s *self, u64 *stamp)
+{
+ s8 clk_sel = READ_ONCE(self->clk_select);
+
+ if (clk_sel < 0)
+ return;
+ if (stamp)
+ *stamp = hw_atl2_tsg_clock_read(self, clk_sel);
+}
+
+static u64 hw_atl2_hw_ring_tx_ptp_get_ts(struct aq_ring_s *ring)
+{
+ struct hw_atl2_txts_s *txts;
+ u32 ctrl;
+
+ txts = (struct hw_atl2_txts_s *)&ring->dx_ring[ring->sw_head *
+ HW_ATL2_TXD_SIZE];
+ /* DD + TS_VALID */
+ ctrl = READ_ONCE(txts->ctrl);
+ if ((ctrl & HW_ATL2_TXTS_DD) && (ctrl & HW_ATL2_TXTS_TS_VALID)) {
+ dma_rmb();
+ return le64_to_cpu(txts->ts);
+ }
+
+ return 0;
+}
+
+static u16 hw_atl2_hw_rx_extract_ts(struct aq_hw_s *self, u8 *p,
+ unsigned int len, u64 *timestamp)
+{
+ unsigned int offset = HW_ATL2_RX_TS_SIZE;
+ __le64 ts;
+ u8 *ptr;
+
+ if (len <= offset || !timestamp)
+ return 0;
+
+ ptr = p + (len - offset);
+ memcpy(&ts, ptr, sizeof(ts));
+ *timestamp = le64_to_cpu(ts);
+
+ return HW_ATL2_RX_TS_SIZE;
+}
+
+static int hw_atl2_adj_sys_clock(struct aq_hw_s *self, s64 delta)
+{
+ s8 clk_sel = READ_ONCE(self->clk_select);
+
+ if (clk_sel < 0)
+ return -ENODEV;
+ if (delta >= 0)
+ hw_atl2_tsg_clock_add(self, clk_sel, (u64)delta);
+ else
+ hw_atl2_tsg_clock_sub(self, clk_sel, (u64)(-delta));
+
+ return 0;
+}
+
+static int hw_atl2_adj_clock_freq(struct aq_hw_s *self, s32 ppb)
+{
+ u32 freq = AQ2_HW_PTP_COUNTER_HZ;
+ u64 divisor = 0, base_ns;
+ u32 nsi_frac = 0, nsi;
+ u32 nsi_rem;
+ s8 clk_sel;
+
+ base_ns = div_u64((u64)((s64)ppb + NSEC_PER_SEC) * NSEC_PER_SEC, freq);
+ nsi = (u32)div_u64_rem(base_ns, NSEC_PER_SEC, &nsi_rem);
+ if (nsi_rem != 0) {
+ divisor = div_u64(mul_u32_u32(NSEC_PER_SEC, NSEC_PER_SEC),
+ nsi_rem);
+ nsi_frac = (u32)div64_u64(AQ_FRAC_PER_NS * NSEC_PER_SEC,
+ divisor);
+ }
+
+ clk_sel = READ_ONCE(self->clk_select);
+ if (clk_sel < 0)
+ return -ENODEV;
+ hw_atl2_tsg_clock_increment_set(self, clk_sel, nsi, nsi_frac);
+
+ return 0;
+}
+
+static int hw_atl2_hw_tx_ptp_ring_init(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring)
+{
+ hw_atl2_tdm_tx_desc_timestamp_writeback_en_set(self, true,
+ aq_ring->idx);
+ hw_atl2_tdm_tx_desc_timestamp_en_set(self, true, aq_ring->idx);
+ hw_atl2_tdm_tx_desc_avb_en_set(self, true, aq_ring->idx);
+
+ return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl2_hw_rx_ptp_ring_init(struct aq_hw_s *self,
+ struct aq_ring_s *aq_ring)
+{
+ hw_atl2_rpf_rx_desc_timestamp_req_set(self,
+ self->clk_select == ATL_TSG_CLOCK_SEL_1 ? 2 : 1,
+ aq_ring->idx);
+ return aq_hw_err_from_flags(self);
+}
+
+static u32 hw_atl2_hw_get_clk_sel(struct aq_hw_s *self)
+{
+ return READ_ONCE(self->clk_select);
+}
+
+static int hw_atl2_gpio_pulse(struct aq_hw_s *self, u32 index, u32 clk_sel,
+ u64 start, u32 period, u32 hightime)
+{
+ u32 mode;
+
+ if (index != 1 && index != 3)
+ return -EINVAL;
+
+ if (start == 0)
+ mode = HW_ATL2_GPIO_PIN_SPEC_MODE_GPIO;
+ else if (clk_sel == ATL_TSG_CLOCK_SEL_0)
+ mode = HW_ATL2_GPIO_PIN_SPEC_MODE_TSG0_EVENT_OUTPUT;
+ else
+ mode = HW_ATL2_GPIO_PIN_SPEC_MODE_TSG1_EVENT_OUTPUT;
+
+ hw_atl2_gpio_special_mode_set(self, mode, index);
+ hw_atl2_tsg_ptp_gpio_gen_pulse(self, clk_sel, start, period, hightime);
+
+ return 0;
+}
+
static bool hw_atl2_rxf_l3_is_equal(struct hw_atl2_l3_filter *f1,
struct hw_atl2_l3_filter *f2)
{
@@ -1468,4 +1649,21 @@ const struct aq_hw_ops hw_atl2_ops = {
.hw_set_offload = hw_atl_b0_hw_offload_set,
.hw_set_loopback = hw_atl_b0_set_loopback,
.hw_set_fc = hw_atl_b0_set_fc,
+
+ .hw_ring_hwts_rx_fill = NULL,
+ .hw_ring_hwts_rx_receive = NULL,
+
+ .hw_get_ptp_ts = aq_get_ptp_ts,
+ .hw_adj_clock_freq = hw_atl2_adj_clock_freq,
+ .hw_adj_sys_clock = hw_atl2_adj_sys_clock,
+ .hw_gpio_pulse = hw_atl2_gpio_pulse,
+
+ .enable_ptp = hw_atl2_enable_ptp,
+ .hw_ring_tx_ptp_get_ts = hw_atl2_hw_ring_tx_ptp_get_ts,
+ .rx_extract_ts = hw_atl2_hw_rx_extract_ts,
+ .hw_tx_ptp_ring_init = hw_atl2_hw_tx_ptp_ring_init,
+ .hw_rx_ptp_ring_init = hw_atl2_hw_rx_ptp_ring_init,
+ .hw_get_clk_sel = hw_atl2_hw_get_clk_sel,
+ .extract_hwts = NULL,
+ .hw_extts_gpio_enable = NULL,
};
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h
index 346f0dc9912e..4b905231ae73 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h
@@ -7,6 +7,18 @@
#define HW_ATL2_H
#include "aq_common.h"
+#define HW_ATL2_RX_TS_SIZE 8
+
+#define HW_ATL2_PTP_OFFSET_INGRESS_100 768
+#define HW_ATL2_PTP_OFFSET_EGRESS_100 336
+#define HW_ATL2_PTP_OFFSET_INGRESS_1000 510
+#define HW_ATL2_PTP_OFFSET_EGRESS_1000 105
+#define HW_ATL2_PTP_OFFSET_INGRESS_2500 2447
+#define HW_ATL2_PTP_OFFSET_EGRESS_2500 634
+#define HW_ATL2_PTP_OFFSET_INGRESS_5000 1426
+#define HW_ATL2_PTP_OFFSET_EGRESS_5000 361
+#define HW_ATL2_PTP_OFFSET_INGRESS_10000 997
+#define HW_ATL2_PTP_OFFSET_EGRESS_10000 203
extern const struct aq_hw_caps_s hw_atl2_caps_aqc113;
extern const struct aq_hw_caps_s hw_atl2_caps_aqc115c;
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h
index 31d7cae6641a..8da3262c167d 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h
@@ -29,7 +29,8 @@
#define HW_ATL2_TXBUF_MAX 128U
#define HW_ATL2_PTP_TXBUF_SIZE 8U
-#define HW_ATL2_RXBUF_MAX 192U
+/* AQC113 on-chip RX packet buffer available for data TCs */
+#define HW_ATL2_RXBUF_MAX 172U
#define HW_ATL2_PTP_RXBUF_SIZE 16U
#define HW_ATL2_RSS_REDIRECTION_MAX 64U
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h
index c84955bc14ae..ee7f444f6d07 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h
@@ -8,6 +8,16 @@
#include "aq_hw.h"
+/* Hardware tx launch time descriptor */
+struct hw_atl2_txts_s {
+ __le64 ts;
+ u32 ctrl;
+ u32 reserved;
+};
+
+#define HW_ATL2_TXTS_DD BIT(3)
+#define HW_ATL2_TXTS_TS_VALID BIT(20)
+
/* F W A P I */
struct link_options_s {
--
2.43.0
next prev parent reply other threads:[~2026-05-20 14:13 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-20 14:10 [PATCH net-next v3 0/12] net: atlantic: add PTP support for AQC113 (Antigua) sukhdeeps
2026-05-20 14:10 ` [PATCH net-next v3 1/12] net: atlantic: correct L3L4 filter flow_type masking and IPv6 handling sukhdeeps
2026-05-20 14:10 ` [PATCH net-next v3 2/12] net: atlantic: move active_ipv4/ipv6 bitmap updates after HW write sukhdeeps
2026-05-20 14:10 ` [PATCH net-next v3 3/12] net: atlantic: decouple aq_set_data_fl3l4() from driver internals sukhdeeps
2026-05-20 14:10 ` [PATCH net-next v3 4/12] net: atlantic: add AQC113 hardware register definitions and accessors sukhdeeps
2026-05-20 14:10 ` [PATCH net-next v3 5/12] net: atlantic: add AQC113 filter data structures and firmware query sukhdeeps
2026-05-20 14:10 ` [PATCH net-next v3 6/12] net: atlantic: fix AQC113 HW init: ART sections, L2 filter slot, MAC address sukhdeeps
2026-05-20 14:10 ` [PATCH net-next v3 7/12] net: atlantic: implement AQC113 L2/L3/L4 RX filter ops sukhdeeps
2026-05-20 14:10 ` [PATCH net-next v3 8/12] net: atlantic: add AQC113 PTP traffic class and TX path setup sukhdeeps
2026-05-20 14:10 ` [PATCH net-next v3 9/12] net: atlantic: extend hw_ops and TX descriptor for AQC113 PTP sukhdeeps
2026-05-20 14:10 ` sukhdeeps [this message]
2026-05-20 14:10 ` [PATCH net-next v3 11/12] net: atlantic: add AQC113 TX timestamp polling and PTP TX classification sukhdeeps
2026-05-20 14:10 ` [PATCH net-next v3 12/12] net: atlantic: add AQC113 PTP support in aq_ptp and driver core sukhdeeps
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260520141046.2151-11-sukhdeeps@marvell.com \
--to=sukhdeeps@marvell.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=vadim.fedorenko@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox