]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: ena: Add PHC support in the ENA driver
authorDavid Arinzon <darinzon@amazon.com>
Tue, 17 Jun 2025 11:05:37 +0000 (14:05 +0300)
committerJakub Kicinski <kuba@kernel.org>
Thu, 19 Jun 2025 01:57:28 +0000 (18:57 -0700)
The ENA driver will be extended to support the new PHC feature using
ptp_clock interface [1]. this will provide timestamp reference for user
space to allow measuring time offset between the PHC and the system
clock in order to achieve nanosecond accuracy.

[1] - https://www.kernel.org/doc/html/latest/driver-api/ptp.html

Signed-off-by: Amit Bernstein <amitbern@amazon.com>
Signed-off-by: David Arinzon <darinzon@amazon.com>
Link: https://patch.msgid.link/20250617110545.5659-2-darinzon@amazon.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
12 files changed:
Documentation/networking/device_drivers/ethernet/amazon/ena.rst
drivers/net/ethernet/amazon/Kconfig
drivers/net/ethernet/amazon/ena/Makefile
drivers/net/ethernet/amazon/ena/ena_admin_defs.h
drivers/net/ethernet/amazon/ena/ena_com.c
drivers/net/ethernet/amazon/ena/ena_com.h
drivers/net/ethernet/amazon/ena/ena_ethtool.c
drivers/net/ethernet/amazon/ena/ena_netdev.c
drivers/net/ethernet/amazon/ena/ena_netdev.h
drivers/net/ethernet/amazon/ena/ena_phc.c [new file with mode: 0644]
drivers/net/ethernet/amazon/ena/ena_phc.h [new file with mode: 0644]
drivers/net/ethernet/amazon/ena/ena_regs_defs.h

index 4561e8ab9e085a1a19983f881d1c947308b9b05d..98dc62174a40613684ddcc1c73507bf18a5e2a75 100644 (file)
@@ -56,6 +56,7 @@ ena_netdev.[ch]     Main Linux kernel driver.
 ena_ethtool.c       ethtool callbacks.
 ena_xdp.[ch]        XDP files
 ena_pci_id_tbl.h    Supported device IDs.
+ena_phc.[ch]        PTP hardware clock infrastructure (see `PHC`_ for more info)
 =================   ======================================================
 
 Management Interface:
index c37fa393b99ee60d4f80747087d48e49dc3a2e65..8d61bc62e295aee260260b9484e1a83a38a0c323 100644 (file)
@@ -19,6 +19,7 @@ if NET_VENDOR_AMAZON
 config ENA_ETHERNET
        tristate "Elastic Network Adapter (ENA) support"
        depends on PCI_MSI && !CPU_BIG_ENDIAN
+       depends on PTP_1588_CLOCK_OPTIONAL
        select DIMLIB
        help
          This driver supports Elastic Network Adapter (ENA)"
index 6ab615365172e85df316b96fc8d3e6fb7c965210..8c874177468fe70d3376d7d2cb36ab31dcdeedaa 100644 (file)
@@ -5,4 +5,4 @@
 
 obj-$(CONFIG_ENA_ETHERNET) += ena.o
 
-ena-y := ena_netdev.o ena_com.o ena_eth_com.o ena_ethtool.o ena_xdp.o
+ena-y := ena_netdev.o ena_com.o ena_eth_com.o ena_ethtool.o ena_xdp.o ena_phc.o
index 9d9fa655935476e9c756dd26191d2e9cef0ba9e7..562869a0fdbafc44fd93012fe0dd87d0b2f4c5ed 100644 (file)
@@ -60,6 +60,7 @@ enum ena_admin_aq_feature_id {
        ENA_ADMIN_AENQ_CONFIG                       = 26,
        ENA_ADMIN_LINK_CONFIG                       = 27,
        ENA_ADMIN_HOST_ATTR_CONFIG                  = 28,
+       ENA_ADMIN_PHC_CONFIG                        = 29,
        ENA_ADMIN_FEATURES_OPCODE_NUM               = 32,
 };
 
@@ -127,6 +128,14 @@ enum ena_admin_get_stats_scope {
        ENA_ADMIN_ETH_TRAFFIC                       = 1,
 };
 
+enum ena_admin_phc_type {
+       ENA_ADMIN_PHC_TYPE_READLESS                 = 0,
+};
+
+enum ena_admin_phc_error_flags {
+       ENA_ADMIN_PHC_ERROR_FLAG_TIMESTAMP   = BIT(0),
+};
+
 /* ENA SRD configuration for ENI */
 enum ena_admin_ena_srd_flags {
        /* Feature enabled */
@@ -943,7 +952,9 @@ struct ena_admin_host_info {
         * 4 : rss_configurable_function_key
         * 5 : reserved
         * 6 : rx_page_reuse
-        * 31:7 : reserved
+        * 7 : reserved
+        * 8 : phc
+        * 31:9 : reserved
         */
        u32 driver_supported_features;
 };
@@ -1023,6 +1034,43 @@ struct ena_admin_queue_ext_feature_desc {
        };
 };
 
+struct ena_admin_feature_phc_desc {
+       /* PHC type as defined in enum ena_admin_get_phc_type,
+        * used only for GET command.
+        */
+       u8 type;
+
+       /* Reserved - MBZ */
+       u8 reserved1[3];
+
+       /* PHC doorbell address as an offset to PCIe MMIO REG BAR,
+        * used only for GET command.
+        */
+       u32 doorbell_offset;
+
+       /* Max time for valid PHC retrieval, passing this threshold will
+        * fail the get-time request and block PHC requests for
+        * block_timeout_usec, used only for GET command.
+        */
+       u32 expire_timeout_usec;
+
+       /* PHC requests block period, blocking starts if PHC request expired
+        * in order to prevent floods on busy device,
+        * used only for GET command.
+        */
+       u32 block_timeout_usec;
+
+       /* Shared PHC physical address (ena_admin_phc_resp),
+        * used only for SET command.
+        */
+       struct ena_common_mem_addr output_address;
+
+       /* Shared PHC Size (ena_admin_phc_resp),
+        * used only for SET command.
+        */
+       u32 output_length;
+};
+
 struct ena_admin_get_feat_resp {
        struct ena_admin_acq_common_desc acq_common_desc;
 
@@ -1052,6 +1100,8 @@ struct ena_admin_get_feat_resp {
                struct ena_admin_feature_intr_moder_desc intr_moderation;
 
                struct ena_admin_ena_hw_hints hw_hints;
+
+               struct ena_admin_feature_phc_desc phc;
        } u;
 };
 
@@ -1085,6 +1135,9 @@ struct ena_admin_set_feat_cmd {
 
                /* LLQ configuration */
                struct ena_admin_feature_llq_desc llq;
+
+               /* PHC configuration */
+               struct ena_admin_feature_phc_desc phc;
        } u;
 };
 
@@ -1162,6 +1215,23 @@ struct ena_admin_ena_mmio_req_read_less_resp {
        u32 reg_val;
 };
 
+struct ena_admin_phc_resp {
+       /* Request Id, received from DB register */
+       u16 req_id;
+
+       u8 reserved1[6];
+
+       /* PHC timestamp (nsec) */
+       u64 timestamp;
+
+       u8 reserved2[12];
+
+       /* Bit field of enum ena_admin_phc_error_flags */
+       u32 error_flags;
+
+       u8 reserved3[32];
+};
+
 /* aq_common_desc */
 #define ENA_ADMIN_AQ_COMMON_DESC_COMMAND_ID_MASK            GENMASK(11, 0)
 #define ENA_ADMIN_AQ_COMMON_DESC_PHASE_MASK                 BIT(0)
@@ -1260,6 +1330,8 @@ struct ena_admin_ena_mmio_req_read_less_resp {
 #define ENA_ADMIN_HOST_INFO_RSS_CONFIGURABLE_FUNCTION_KEY_MASK BIT(4)
 #define ENA_ADMIN_HOST_INFO_RX_PAGE_REUSE_SHIFT             6
 #define ENA_ADMIN_HOST_INFO_RX_PAGE_REUSE_MASK              BIT(6)
+#define ENA_ADMIN_HOST_INFO_PHC_SHIFT                       8
+#define ENA_ADMIN_HOST_INFO_PHC_MASK                        BIT(8)
 
 /* aenq_common_desc */
 #define ENA_ADMIN_AENQ_COMMON_DESC_PHASE_MASK               BIT(0)
index 66445617fbfbf8dc41449fffbb5c093357db002f..e67b592e569763a28c054832711d3f0e1fd07105 100644 (file)
 
 #define ENA_MAX_ADMIN_POLL_US 5000
 
+/* PHC definitions */
+#define ENA_PHC_DEFAULT_EXPIRE_TIMEOUT_USEC 10
+#define ENA_PHC_DEFAULT_BLOCK_TIMEOUT_USEC 1000
+#define ENA_PHC_REQ_ID_OFFSET 0xDEAD
+#define ENA_PHC_ERROR_FLAGS (ENA_ADMIN_PHC_ERROR_FLAG_TIMESTAMP)
+
 /*****************************************************************************/
 /*****************************************************************************/
 /*****************************************************************************/
@@ -1641,6 +1647,267 @@ void ena_com_set_admin_polling_mode(struct ena_com_dev *ena_dev, bool polling)
        ena_dev->admin_queue.polling = polling;
 }
 
+bool ena_com_phc_supported(struct ena_com_dev *ena_dev)
+{
+       return ena_com_check_supported_feature_id(ena_dev, ENA_ADMIN_PHC_CONFIG);
+}
+
+int ena_com_phc_init(struct ena_com_dev *ena_dev)
+{
+       struct ena_com_phc_info *phc = &ena_dev->phc;
+
+       memset(phc, 0x0, sizeof(*phc));
+
+       /* Allocate shared mem used PHC timestamp retrieved from device */
+       phc->virt_addr = dma_alloc_coherent(ena_dev->dmadev,
+                                           sizeof(*phc->virt_addr),
+                                           &phc->phys_addr,
+                                           GFP_KERNEL);
+       if (unlikely(!phc->virt_addr))
+               return -ENOMEM;
+
+       spin_lock_init(&phc->lock);
+
+       phc->virt_addr->req_id = 0;
+       phc->virt_addr->timestamp = 0;
+
+       return 0;
+}
+
+int ena_com_phc_config(struct ena_com_dev *ena_dev)
+{
+       struct ena_com_phc_info *phc = &ena_dev->phc;
+       struct ena_admin_get_feat_resp get_feat_resp;
+       struct ena_admin_set_feat_resp set_feat_resp;
+       struct ena_admin_set_feat_cmd set_feat_cmd;
+       int ret = 0;
+
+       /* Get device PHC default configuration */
+       ret = ena_com_get_feature(ena_dev,
+                                 &get_feat_resp,
+                                 ENA_ADMIN_PHC_CONFIG,
+                                 0);
+       if (unlikely(ret)) {
+               netdev_err(ena_dev->net_device,
+                          "Failed to get PHC feature configuration, error: %d\n",
+                          ret);
+               return ret;
+       }
+
+       /* Supporting only readless PHC retrieval */
+       if (get_feat_resp.u.phc.type != ENA_ADMIN_PHC_TYPE_READLESS) {
+               netdev_err(ena_dev->net_device,
+                          "Unsupported PHC type, error: %d\n",
+                          -EOPNOTSUPP);
+               return -EOPNOTSUPP;
+       }
+
+       /* Update PHC doorbell offset according to device value,
+        * used to write req_id to PHC bar
+        */
+       phc->doorbell_offset = get_feat_resp.u.phc.doorbell_offset;
+
+       /* Update PHC expire timeout according to device
+        * or default driver value
+        */
+       phc->expire_timeout_usec = (get_feat_resp.u.phc.expire_timeout_usec) ?
+                                   get_feat_resp.u.phc.expire_timeout_usec :
+                                   ENA_PHC_DEFAULT_EXPIRE_TIMEOUT_USEC;
+
+       /* Update PHC block timeout according to device
+        * or default driver value
+        */
+       phc->block_timeout_usec = (get_feat_resp.u.phc.block_timeout_usec) ?
+                                  get_feat_resp.u.phc.block_timeout_usec :
+                                  ENA_PHC_DEFAULT_BLOCK_TIMEOUT_USEC;
+
+       /* Sanity check - expire timeout must not exceed block timeout */
+       if (phc->expire_timeout_usec > phc->block_timeout_usec)
+               phc->expire_timeout_usec = phc->block_timeout_usec;
+
+       /* Prepare PHC feature command */
+       memset(&set_feat_cmd, 0x0, sizeof(set_feat_cmd));
+       set_feat_cmd.aq_common_descriptor.opcode = ENA_ADMIN_SET_FEATURE;
+       set_feat_cmd.feat_common.feature_id = ENA_ADMIN_PHC_CONFIG;
+       set_feat_cmd.u.phc.output_length = sizeof(*phc->virt_addr);
+       ret = ena_com_mem_addr_set(ena_dev,
+                                  &set_feat_cmd.u.phc.output_address,
+                                  phc->phys_addr);
+       if (unlikely(ret)) {
+               netdev_err(ena_dev->net_device,
+                          "Failed setting PHC output address, error: %d\n",
+                          ret);
+               return ret;
+       }
+
+       /* Send PHC feature command to the device */
+       ret = ena_com_execute_admin_command(&ena_dev->admin_queue,
+                                           (struct ena_admin_aq_entry *)&set_feat_cmd,
+                                           sizeof(set_feat_cmd),
+                                           (struct ena_admin_acq_entry *)&set_feat_resp,
+                                           sizeof(set_feat_resp));
+
+       if (unlikely(ret)) {
+               netdev_err(ena_dev->net_device,
+                          "Failed to enable PHC, error: %d\n",
+                          ret);
+               return ret;
+       }
+
+       phc->active = true;
+       netdev_dbg(ena_dev->net_device, "PHC is active in the device\n");
+
+       return ret;
+}
+
+void ena_com_phc_destroy(struct ena_com_dev *ena_dev)
+{
+       struct ena_com_phc_info *phc = &ena_dev->phc;
+       unsigned long flags = 0;
+
+       /* In case PHC is not supported by the device, silently exiting */
+       if (!phc->virt_addr)
+               return;
+
+       spin_lock_irqsave(&phc->lock, flags);
+       phc->active = false;
+       spin_unlock_irqrestore(&phc->lock, flags);
+
+       dma_free_coherent(ena_dev->dmadev,
+                         sizeof(*phc->virt_addr),
+                         phc->virt_addr,
+                         phc->phys_addr);
+       phc->virt_addr = NULL;
+}
+
+int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp)
+{
+       volatile struct ena_admin_phc_resp *resp = ena_dev->phc.virt_addr;
+       const ktime_t zero_system_time = ktime_set(0, 0);
+       struct ena_com_phc_info *phc = &ena_dev->phc;
+       ktime_t expire_time;
+       ktime_t block_time;
+       unsigned long flags = 0;
+       int ret = 0;
+
+       if (!phc->active) {
+               netdev_err(ena_dev->net_device, "PHC feature is not active in the device\n");
+               return -EOPNOTSUPP;
+       }
+
+       spin_lock_irqsave(&phc->lock, flags);
+
+       /* Check if PHC is in blocked state */
+       if (unlikely(ktime_compare(phc->system_time, zero_system_time))) {
+               /* Check if blocking time expired */
+               block_time = ktime_add_us(phc->system_time, phc->block_timeout_usec);
+               if (!ktime_after(ktime_get(), block_time)) {
+                       /* PHC is still in blocked state, skip PHC request */
+                       phc->stats.phc_skp++;
+                       ret = -EBUSY;
+                       goto skip;
+               }
+
+               /* PHC is in active state, update statistics according
+                * to req_id and error_flags
+                */
+               if (READ_ONCE(resp->req_id) != phc->req_id) {
+                       /* Device didn't update req_id during blocking time,
+                        * this indicates on a device error
+                        */
+                       netdev_err(ena_dev->net_device,
+                                  "PHC get time request 0x%x failed (device error)\n",
+                                  phc->req_id);
+                       phc->stats.phc_err_dv++;
+               } else if (resp->error_flags & ENA_PHC_ERROR_FLAGS) {
+                       /* Device updated req_id during blocking time but got
+                        * a PHC error, this occurs if device:
+                        * - exceeded the get time request limit
+                        * - received an invalid timestamp
+                        */
+                       netdev_err(ena_dev->net_device,
+                                  "PHC get time request 0x%x failed (error 0x%x)\n",
+                                  phc->req_id,
+                                  resp->error_flags);
+                       phc->stats.phc_err_ts += !!(resp->error_flags &
+                               ENA_ADMIN_PHC_ERROR_FLAG_TIMESTAMP);
+               } else {
+                       /* Device updated req_id during blocking time
+                        * with valid timestamp
+                        */
+                       phc->stats.phc_exp++;
+               }
+       }
+
+       /* Setting relative timeouts */
+       phc->system_time = ktime_get();
+       block_time = ktime_add_us(phc->system_time, phc->block_timeout_usec);
+       expire_time = ktime_add_us(phc->system_time, phc->expire_timeout_usec);
+
+       /* We expect the device to return this req_id once
+        * the new PHC timestamp is updated
+        */
+       phc->req_id++;
+
+       /* Initialize PHC shared memory with different req_id value
+        * to be able to identify once the device changes it to req_id
+        */
+       resp->req_id = phc->req_id + ENA_PHC_REQ_ID_OFFSET;
+
+       /* Writing req_id to PHC bar */
+       writel(phc->req_id, ena_dev->reg_bar + phc->doorbell_offset);
+
+       /* Stalling until the device updates req_id */
+       while (1) {
+               if (unlikely(ktime_after(ktime_get(), expire_time))) {
+                       /* Gave up waiting for updated req_id, PHC enters into
+                        * blocked state until passing blocking time,
+                        * during this time any get PHC timestamp will fail with
+                        * device busy error
+                        */
+                       ret = -EBUSY;
+                       break;
+               }
+
+               /* Check if req_id was updated by the device */
+               if (READ_ONCE(resp->req_id) != phc->req_id) {
+                       /* req_id was not updated by the device yet,
+                        * check again on next loop
+                        */
+                       continue;
+               }
+
+               /* req_id was updated by the device which indicates that
+                * PHC timestamp and error_flags are updated too,
+                * checking errors before retrieving timestamp
+                */
+               if (unlikely(resp->error_flags & ENA_PHC_ERROR_FLAGS)) {
+                       /* Retrieved invalid PHC timestamp, PHC enters into
+                        * blocked state until passing blocking time,
+                        * during this time any get PHC timestamp requests
+                        * will fail with device busy error
+                        */
+                       ret = -EBUSY;
+                       break;
+               }
+
+               /* PHC timestamp value is returned to the caller */
+               *timestamp = resp->timestamp;
+
+               /* Update statistic on valid PHC timestamp retrieval */
+               phc->stats.phc_cnt++;
+
+               /* This indicates PHC state is active */
+               phc->system_time = zero_system_time;
+               break;
+       }
+
+skip:
+       spin_unlock_irqrestore(&phc->lock, flags);
+
+       return ret;
+}
+
 int ena_com_mmio_reg_read_request_init(struct ena_com_dev *ena_dev)
 {
        struct ena_com_mmio_read *mmio_read = &ena_dev->mmio_read;
index 9414e93d107b3d6f468ea3af15b534869bf09261..64df2c48c9a6aa2907c5c6ae5cb26d3850bca295 100644 (file)
@@ -210,6 +210,14 @@ struct ena_com_stats_admin {
        u64 no_completion;
 };
 
+struct ena_com_stats_phc {
+       u64 phc_cnt;
+       u64 phc_exp;
+       u64 phc_skp;
+       u64 phc_err_dv;
+       u64 phc_err_ts;
+};
+
 struct ena_com_admin_queue {
        void *q_dmadev;
        struct ena_com_dev *ena_dev;
@@ -258,6 +266,47 @@ struct ena_com_mmio_read {
        spinlock_t lock;
 };
 
+/* PTP hardware clock (PHC) MMIO read data info */
+struct ena_com_phc_info {
+       /* Internal PHC statistics */
+       struct ena_com_stats_phc stats;
+
+       /* PHC shared memory - virtual address */
+       struct ena_admin_phc_resp *virt_addr;
+
+       /* System time of last PHC request */
+       ktime_t system_time;
+
+       /* Spin lock to ensure a single outstanding PHC read */
+       spinlock_t lock;
+
+       /* PHC doorbell address as an offset to PCIe MMIO REG BAR */
+       u32 doorbell_offset;
+
+       /* Shared memory read expire timeout (usec)
+        * Max time for valid PHC retrieval, passing this threshold will fail
+        * the get time request and block new PHC requests for block_timeout_usec
+        * in order to prevent floods on busy device
+        */
+       u32 expire_timeout_usec;
+
+       /* Shared memory read abort timeout (usec)
+        * PHC requests block period, blocking starts once PHC request expired
+        * in order to prevent floods on busy device,
+        * any PHC requests during block period will be skipped
+        */
+       u32 block_timeout_usec;
+
+       /* PHC shared memory - physical address */
+       dma_addr_t phys_addr;
+
+       /* Request id sent to the device */
+       u16 req_id;
+
+       /* True if PHC is active in the device */
+       bool active;
+};
+
 struct ena_rss {
        /* Indirect table */
        u16 *host_rss_ind_tbl;
@@ -317,6 +366,7 @@ struct ena_com_dev {
        u32 ena_min_poll_delay_us;
 
        struct ena_com_mmio_read mmio_read;
+       struct ena_com_phc_info phc;
 
        struct ena_rss rss;
        u32 supported_features;
@@ -382,6 +432,40 @@ struct ena_aenq_handlers {
  */
 int ena_com_mmio_reg_read_request_init(struct ena_com_dev *ena_dev);
 
+/* ena_com_phc_init - Allocate and initialize PHC feature
+ * @ena_dev: ENA communication layer struct
+ * @note: This method assumes PHC is supported by the device
+ * @return - 0 on success, negative value on failure
+ */
+int ena_com_phc_init(struct ena_com_dev *ena_dev);
+
+/* ena_com_phc_supported - Return if PHC feature is supported by the device
+ * @ena_dev: ENA communication layer struct
+ * @note: This method must be called after getting supported features
+ * @return - supported or not
+ */
+bool ena_com_phc_supported(struct ena_com_dev *ena_dev);
+
+/* ena_com_phc_config - Configure PHC feature
+ * @ena_dev: ENA communication layer struct
+ * Configure PHC feature in driver and device
+ * @note: This method assumes PHC is supported by the device
+ * @return - 0 on success, negative value on failure
+ */
+int ena_com_phc_config(struct ena_com_dev *ena_dev);
+
+/* ena_com_phc_destroy - Destroy PHC feature
+ * @ena_dev: ENA communication layer struct
+ */
+void ena_com_phc_destroy(struct ena_com_dev *ena_dev);
+
+/* ena_com_phc_get_timestamp - Retrieve PHC timestamp
+ * @ena_dev: ENA communication layer struct
+ * @timestamp: Retrieved PHC timestamp
+ * @return - 0 on success, negative value on failure
+ */
+int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp);
+
 /* ena_com_set_mmio_read_mode - Enable/disable the indirect mmio reg read mechanism
  * @ena_dev: ENA communication layer struct
  * @readless_supported: readless mode (enable/disable)
index 07e8f6b1e8afb3f9fcc6765c9aa7e6591550d30e..a81d3a7a3bb9ae6aef878821363a887558f349cb 100644 (file)
@@ -5,9 +5,11 @@
 
 #include <linux/ethtool.h>
 #include <linux/pci.h>
+#include <linux/net_tstamp.h>
 
 #include "ena_netdev.h"
 #include "ena_xdp.h"
+#include "ena_phc.h"
 
 struct ena_stats {
        char name[ETH_GSTRING_LEN];
@@ -298,6 +300,18 @@ static void ena_get_ethtool_stats(struct net_device *netdev,
        ena_get_stats(adapter, data, true);
 }
 
+static int ena_get_ts_info(struct net_device *netdev,
+                          struct kernel_ethtool_ts_info *info)
+{
+       struct ena_adapter *adapter = netdev_priv(netdev);
+
+       info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE;
+
+       info->phc_index = ena_phc_get_index(adapter);
+
+       return 0;
+}
+
 static int ena_get_sw_stats_count(struct ena_adapter *adapter)
 {
        return adapter->num_io_queues * (ENA_STATS_ARRAY_TX + ENA_STATS_ARRAY_RX)
@@ -1090,7 +1104,7 @@ static const struct ethtool_ops ena_ethtool_ops = {
        .set_channels           = ena_set_channels,
        .get_tunable            = ena_get_tunable,
        .set_tunable            = ena_set_tunable,
-       .get_ts_info            = ethtool_op_get_ts_info,
+       .get_ts_info            = ena_get_ts_info,
 };
 
 void ena_set_ethtool_ops(struct net_device *netdev)
index 86fd08f375df1ceeeb54f06f58d1aedae2917649..14a85916464b6e74b84200f4604a7650d96307ab 100644 (file)
@@ -19,6 +19,8 @@
 #include "ena_pci_id_tbl.h"
 #include "ena_xdp.h"
 
+#include "ena_phc.h"
+
 MODULE_AUTHOR("Amazon.com, Inc. or its affiliates");
 MODULE_DESCRIPTION(DEVICE_NAME);
 MODULE_LICENSE("GPL");
@@ -2743,7 +2745,8 @@ static void ena_config_host_info(struct ena_com_dev *ena_dev, struct pci_dev *pd
                ENA_ADMIN_HOST_INFO_INTERRUPT_MODERATION_MASK |
                ENA_ADMIN_HOST_INFO_RX_BUF_MIRRORING_MASK |
                ENA_ADMIN_HOST_INFO_RSS_CONFIGURABLE_FUNCTION_KEY_MASK |
-               ENA_ADMIN_HOST_INFO_RX_PAGE_REUSE_MASK;
+               ENA_ADMIN_HOST_INFO_RX_PAGE_REUSE_MASK |
+               ENA_ADMIN_HOST_INFO_PHC_MASK;
 
        rc = ena_com_set_host_attributes(ena_dev);
        if (rc) {
@@ -3188,6 +3191,10 @@ static int ena_device_init(struct ena_adapter *adapter, struct pci_dev *pdev,
        if (unlikely(rc))
                goto err_admin_init;
 
+       rc = ena_phc_init(adapter);
+       if (unlikely(rc && (rc != -EOPNOTSUPP)))
+               netdev_err(netdev, "Failed initializing PHC, error: %d\n", rc);
+
        return 0;
 
 err_admin_init:
@@ -3271,6 +3278,8 @@ static int ena_destroy_device(struct ena_adapter *adapter, bool graceful)
 
        ena_com_admin_destroy(ena_dev);
 
+       ena_phc_destroy(adapter);
+
        ena_com_mmio_reg_read_request_destroy(ena_dev);
 
        /* return reset reason to default value */
@@ -3344,6 +3353,7 @@ err_device_destroy:
        ena_com_wait_for_abort_completion(ena_dev);
        ena_com_admin_destroy(ena_dev);
        ena_com_dev_reset(ena_dev, ENA_REGS_RESET_DRIVER_INVALID_STATE);
+       ena_phc_destroy(adapter);
        ena_com_mmio_reg_read_request_destroy(ena_dev);
 err:
        clear_bit(ENA_FLAG_DEVICE_RUNNING, &adapter->flags);
@@ -3932,10 +3942,16 @@ static int ena_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        pci_set_drvdata(pdev, adapter);
 
+       rc = ena_phc_alloc(adapter);
+       if (rc) {
+               netdev_err(netdev, "ena_phc_alloc failed\n");
+               goto err_netdev_destroy;
+       }
+
        rc = ena_com_allocate_customer_metrics_buffer(ena_dev);
        if (rc) {
                netdev_err(netdev, "ena_com_allocate_customer_metrics_buffer failed\n");
-               goto err_netdev_destroy;
+               goto err_free_phc;
        }
 
        rc = ena_map_llq_mem_bar(pdev, ena_dev, bars);
@@ -4072,6 +4088,8 @@ err_device_destroy:
        ena_com_admin_destroy(ena_dev);
 err_metrics_destroy:
        ena_com_delete_customer_metrics_buffer(ena_dev);
+err_free_phc:
+       ena_phc_free(adapter);
 err_netdev_destroy:
        free_netdev(netdev);
 err_free_region:
@@ -4112,6 +4130,8 @@ static void __ena_shutoff(struct pci_dev *pdev, bool shutdown)
        adapter->reset_reason = ENA_REGS_RESET_SHUTDOWN;
        ena_destroy_device(adapter, true);
 
+       ena_phc_free(adapter);
+
        if (shutdown) {
                netif_device_detach(netdev);
                dev_close(netdev);
index 6e12ae3b12e5b90379efad95f73fe493671537b1..7867cd7f25bc1cb9fefa172c21b9514b59e04e29 100644 (file)
 
 #define ENA_MMIO_DISABLE_REG_READ      BIT(0)
 
+struct ena_phc_info;
+
 struct ena_irq {
        irq_handler_t handler;
        void *data;
@@ -348,6 +350,8 @@ struct ena_adapter {
 
        char name[ENA_NAME_MAX_LEN];
 
+       struct ena_phc_info *phc_info;
+
        unsigned long flags;
        /* TX */
        struct ena_ring tx_ring[ENA_MAX_NUM_IO_QUEUES]
diff --git a/drivers/net/ethernet/amazon/ena/ena_phc.c b/drivers/net/ethernet/amazon/ena/ena_phc.c
new file mode 100644 (file)
index 0000000..612cf45
--- /dev/null
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/*
+ * Copyright 2015-2022 Amazon.com, Inc. or its affiliates. All rights reserved.
+ */
+
+#include <linux/pci.h>
+#include "ena_netdev.h"
+#include "ena_phc.h"
+
+static int ena_phc_adjtime(struct ptp_clock_info *clock_info, s64 delta)
+{
+       return -EOPNOTSUPP;
+}
+
+static int ena_phc_adjfine(struct ptp_clock_info *clock_info, long scaled_ppm)
+{
+       return -EOPNOTSUPP;
+}
+
+static int ena_phc_feature_enable(struct ptp_clock_info *clock_info,
+                                 struct ptp_clock_request *rq,
+                                 int on)
+{
+       return -EOPNOTSUPP;
+}
+
+static int ena_phc_gettimex64(struct ptp_clock_info *clock_info,
+                             struct timespec64 *ts,
+                             struct ptp_system_timestamp *sts)
+{
+       struct ena_phc_info *phc_info =
+               container_of(clock_info, struct ena_phc_info, clock_info);
+       unsigned long flags;
+       u64 timestamp_nsec;
+       int rc;
+
+       spin_lock_irqsave(&phc_info->lock, flags);
+
+       ptp_read_system_prets(sts);
+
+       rc = ena_com_phc_get_timestamp(phc_info->adapter->ena_dev,
+                                      &timestamp_nsec);
+
+       ptp_read_system_postts(sts);
+
+       spin_unlock_irqrestore(&phc_info->lock, flags);
+
+       *ts = ns_to_timespec64(timestamp_nsec);
+
+       return rc;
+}
+
+static int ena_phc_settime64(struct ptp_clock_info *clock_info,
+                            const struct timespec64 *ts)
+{
+       return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ena_ptp_clock_info = {
+       .owner          = THIS_MODULE,
+       .n_alarm        = 0,
+       .n_ext_ts       = 0,
+       .n_per_out      = 0,
+       .pps            = 0,
+       .adjtime        = ena_phc_adjtime,
+       .adjfine        = ena_phc_adjfine,
+       .gettimex64     = ena_phc_gettimex64,
+       .settime64      = ena_phc_settime64,
+       .enable         = ena_phc_feature_enable,
+};
+
+/* Enable/Disable PHC by the kernel, affects on the next init flow */
+void ena_phc_enable(struct ena_adapter *adapter, bool enable)
+{
+       struct ena_phc_info *phc_info = adapter->phc_info;
+
+       if (!phc_info) {
+               netdev_err(adapter->netdev, "phc_info is not allocated\n");
+               return;
+       }
+
+       phc_info->enabled = enable;
+}
+
+/* Check if PHC is enabled by the kernel */
+bool ena_phc_is_enabled(struct ena_adapter *adapter)
+{
+       struct ena_phc_info *phc_info = adapter->phc_info;
+
+       return (phc_info && phc_info->enabled);
+}
+
+/* PHC is activated if ptp clock is registered in the kernel */
+bool ena_phc_is_active(struct ena_adapter *adapter)
+{
+       struct ena_phc_info *phc_info = adapter->phc_info;
+
+       return (phc_info && phc_info->clock);
+}
+
+static int ena_phc_register(struct ena_adapter *adapter)
+{
+       struct pci_dev *pdev = adapter->pdev;
+       struct ptp_clock_info *clock_info;
+       struct ena_phc_info *phc_info;
+       int rc = 0;
+
+       phc_info = adapter->phc_info;
+       clock_info = &phc_info->clock_info;
+
+       phc_info->adapter = adapter;
+
+       spin_lock_init(&phc_info->lock);
+
+       /* Fill the ptp_clock_info struct and register PTP clock */
+       *clock_info = ena_ptp_clock_info;
+       snprintf(clock_info->name,
+                sizeof(clock_info->name),
+                "ena-ptp-%02x",
+                PCI_SLOT(pdev->devfn));
+
+       phc_info->clock = ptp_clock_register(clock_info, &pdev->dev);
+       if (IS_ERR(phc_info->clock)) {
+               rc = PTR_ERR(phc_info->clock);
+               netdev_err(adapter->netdev, "Failed registering ptp clock, error: %d\n",
+                          rc);
+               phc_info->clock = NULL;
+       }
+
+       return rc;
+}
+
+static void ena_phc_unregister(struct ena_adapter *adapter)
+{
+       struct ena_phc_info *phc_info = adapter->phc_info;
+
+       if (ena_phc_is_active(adapter)) {
+               ptp_clock_unregister(phc_info->clock);
+               phc_info->clock = NULL;
+       }
+}
+
+int ena_phc_alloc(struct ena_adapter *adapter)
+{
+       /* Allocate driver specific PHC info */
+       adapter->phc_info = vzalloc(sizeof(*adapter->phc_info));
+       if (unlikely(!adapter->phc_info)) {
+               netdev_err(adapter->netdev, "Failed to alloc phc_info\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+void ena_phc_free(struct ena_adapter *adapter)
+{
+       if (adapter->phc_info) {
+               vfree(adapter->phc_info);
+               adapter->phc_info = NULL;
+       }
+}
+
+int ena_phc_init(struct ena_adapter *adapter)
+{
+       struct ena_com_dev *ena_dev = adapter->ena_dev;
+       struct net_device *netdev = adapter->netdev;
+       int rc = -EOPNOTSUPP;
+
+       /* Validate PHC feature is supported in the device */
+       if (!ena_com_phc_supported(ena_dev)) {
+               netdev_dbg(netdev, "PHC feature is not supported by the device\n");
+               goto err_ena_com_phc_init;
+       }
+
+       /* Validate PHC feature is enabled by the kernel */
+       if (!ena_phc_is_enabled(adapter)) {
+               netdev_dbg(netdev, "PHC feature is not enabled by the kernel\n");
+               goto err_ena_com_phc_init;
+       }
+
+       /* Initialize device specific PHC info */
+       rc = ena_com_phc_init(ena_dev);
+       if (unlikely(rc)) {
+               netdev_err(netdev, "Failed to init phc, error: %d\n", rc);
+               goto err_ena_com_phc_init;
+       }
+
+       /* Configure PHC feature in driver and device */
+       rc = ena_com_phc_config(ena_dev);
+       if (unlikely(rc)) {
+               netdev_err(netdev, "Failed to config phc, error: %d\n", rc);
+               goto err_ena_com_phc_config;
+       }
+
+       /* Register to PTP class driver */
+       rc = ena_phc_register(adapter);
+       if (unlikely(rc)) {
+               netdev_err(netdev, "Failed to register phc, error: %d\n", rc);
+               goto err_ena_com_phc_config;
+       }
+
+       return 0;
+
+err_ena_com_phc_config:
+       ena_com_phc_destroy(ena_dev);
+err_ena_com_phc_init:
+       ena_phc_enable(adapter, false);
+       return rc;
+}
+
+void ena_phc_destroy(struct ena_adapter *adapter)
+{
+       ena_phc_unregister(adapter);
+       ena_com_phc_destroy(adapter->ena_dev);
+}
+
+int ena_phc_get_index(struct ena_adapter *adapter)
+{
+       if (ena_phc_is_active(adapter))
+               return ptp_clock_index(adapter->phc_info->clock);
+
+       return -1;
+}
diff --git a/drivers/net/ethernet/amazon/ena/ena_phc.h b/drivers/net/ethernet/amazon/ena/ena_phc.h
new file mode 100644 (file)
index 0000000..7364fe7
--- /dev/null
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/*
+ * Copyright 2015-2022 Amazon.com, Inc. or its affiliates. All rights reserved.
+ */
+
+#ifndef ENA_PHC_H
+#define ENA_PHC_H
+
+#include <linux/ptp_clock_kernel.h>
+
+struct ena_phc_info {
+       /* PTP hardware capabilities */
+       struct ptp_clock_info clock_info;
+
+       /* Registered PTP clock device */
+       struct ptp_clock *clock;
+
+       /* Adapter specific private data structure */
+       struct ena_adapter *adapter;
+
+       /* PHC lock */
+       spinlock_t lock;
+
+       /* Enabled by kernel */
+       bool enabled;
+};
+
+void ena_phc_enable(struct ena_adapter *adapter, bool enable);
+bool ena_phc_is_enabled(struct ena_adapter *adapter);
+bool ena_phc_is_active(struct ena_adapter *adapter);
+int ena_phc_get_index(struct ena_adapter *adapter);
+int ena_phc_init(struct ena_adapter *adapter);
+void ena_phc_destroy(struct ena_adapter *adapter);
+int ena_phc_alloc(struct ena_adapter *adapter);
+void ena_phc_free(struct ena_adapter *adapter);
+
+#endif /* ENA_PHC_H */
index a2efebafd686a7be9f340a7975e4f9987ce903d1..51068dc1cc2aacf0666a9d1ae3f73e673cccf8cd 100644 (file)
@@ -53,6 +53,11 @@ enum ena_regs_reset_reason_types {
 #define ENA_REGS_MMIO_RESP_HI_OFF                           0x64
 #define ENA_REGS_RSS_IND_ENTRY_UPDATE_OFF                   0x68
 
+/* phc_registers offsets */
+
+/* 100 base */
+#define ENA_REGS_PHC_DB_OFF                                 0x100
+
 /* version register */
 #define ENA_REGS_VERSION_MINOR_VERSION_MASK                 0xff
 #define ENA_REGS_VERSION_MAJOR_VERSION_SHIFT                8
@@ -129,4 +134,7 @@ enum ena_regs_reset_reason_types {
 #define ENA_REGS_RSS_IND_ENTRY_UPDATE_CQ_IDX_SHIFT          16
 #define ENA_REGS_RSS_IND_ENTRY_UPDATE_CQ_IDX_MASK           0xffff0000
 
+/* phc_db_req_id register */
+#define ENA_REGS_PHC_DB_REQ_ID_MASK                         0xffff
+
 #endif /* _ENA_REGS_H_ */