]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: atlantic: add AQC113 PTP hardware ops in hw_atl2
authorSukhdeep Singh <sukhdeeps@marvell.com>
Wed, 10 Jun 2026 11:54:46 +0000 (17:24 +0530)
committerJakub Kicinski <kuba@kernel.org>
Mon, 15 Jun 2026 22:38:44 +0000 (15:38 -0700)
Add the hardware-layer PTP implementation for AQC113 (Antigua):

- hw_atl2.h/hw_atl2_utils.h/hw_atl2_internal.h: add PTP offset
  constants, 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 reset and enable TSG
  clocks and set PTP TC scheduling priority 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>
Link: https://patch.msgid.link/20260610115448.272-11-sukhdeeps@marvell.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h

index 39e1b606a75a9dd2749b9d3e57f533f1574334b4..2cd6cca66fcc6fe328ad284e240e6647cabc86c6 100644 (file)
@@ -250,6 +250,7 @@ static int aq_pci_probe(struct pci_dev *pdev,
                goto err_ioremap;
        }
        self->aq_hw->aq_nic_cfg = aq_nic_get_cfg(self);
+       self->aq_hw->clk_select = -1;
        if (self->aq_hw->aq_nic_cfg->aq_hw_caps->priv_data_len) {
                int len = self->aq_hw->aq_nic_cfg->aq_hw_caps->priv_data_len;
 
index 1fb72d83825a681405425812f9bfd55de1b8f067..25dd7150aaaf22e16648240b72c0cf1106892cf1 100644 (file)
@@ -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"
 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);
@@ -716,14 +734,24 @@ 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))
+               res = hw_atl2_hw_rx_ptp_ring_init(self, aq_ring);
+
+       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))
+               res = hw_atl2_hw_tx_ptp_ring_init(self, aq_ring);
+
+       return res;
 }
 
 #define IS_FILTER_ENABLED(_F_) ((packet_filter & (_F_)) ? 1U : 0U)
@@ -885,6 +913,157 @@ 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)
+{
+       s8 sel = (s8)param;
+
+       WRITE_ONCE(self->clk_select, sel);
+       /* enable tsg counter */
+       hw_atl2_tsg_clock_reset(self, sel);
+       hw_atl2_tsg_clock_en(self, !sel, enable);
+       hw_atl2_tsg_clock_en(self, sel, 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) {
+               if (stamp)
+                       *stamp = 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 = le32_to_cpu(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)
+{
+       s8 clk_sel = READ_ONCE(self->clk_select);
+
+       hw_atl2_rpf_rx_desc_timestamp_req_set(self,
+                                             clk_sel == 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)
 {
@@ -1465,4 +1644,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,
 };
index 346f0dc9912e500014d253135ab431f67be8eb3c..4b905231ae7385929bc7fa12f740602336276a6d 100644 (file)
@@ -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;
index e66e0def79e7ff01495d348260e1bb1bc9a6a768..c36127900de38bb1bdfbd57f61832812349e5f8f 100644 (file)
@@ -29,7 +29,8 @@
 #define HW_ATL2_TXBUF_MAX              128U
 #define HW_ATL2_PTP_TXBUF_SIZE           8U
 
-#define HW_ATL2_RXBUF_MAX              192U
+/* hw_atl2 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
 
index bae18c32365be1da8540d12d103d79613fca664a..e02ed522b0045f9c1a36e70a0b0ae1f415dcfea9 100644 (file)
@@ -8,6 +8,16 @@
 
 #include "aq_hw.h"
 
+/* Hardware tx launch time descriptor */
+struct hw_atl2_txts_s {
+       __le64 ts;
+       __le32 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 {