net: ena: Add PHC support in the ENA driver

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>
This commit is contained in:
David Arinzon
2025-06-17 14:05:37 +03:00
committed by Jakub Kicinski
parent 253833da4e
commit e0ea34158e
12 changed files with 736 additions and 5 deletions

View 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:

View 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)"

View 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

View 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)

View File

@@ -41,6 +41,12 @@
#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;

View 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)

View 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)

View 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 @@ static int ena_restore_device(struct ena_adapter *adapter)
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 @@ static int ena_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
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);

View File

@@ -110,6 +110,8 @@
#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]

View File

@@ -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;
}

View File

@@ -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 */

View 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_ */