linux/drivers/net/ethernet/amazon/ena/ena_phc.c
David Arinzon 816b52624c net: ena: Control PHC enable through devlink
Add the capability to set parameters through the devlink framework.

The parameter used for controlling PHC (enable/disable) details
are as follows:
- Name: enable_phc
- Type: Boolean (true - enable/false - disable)
- Mode: DEVLINK_PARAM_CMODE_DRIVERINIT
- Effect: Changes take place during driver initialization,
          any changes require a devlink reload to take effect.

Signed-off-by: David Arinzon <darinzon@amazon.com>
Link: https://patch.msgid.link/20250617110545.5659-7-darinzon@amazon.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-06-18 18:57:29 -07:00

233 lines
5.5 KiB
C

// 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"
#include "ena_devlink.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 may already be registered in case of a reset */
if (ena_phc_is_active(adapter))
return 0;
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;
/* During reset flow, PHC must stay registered
* to keep kernel's PHC index
*/
if (ena_phc_is_active(adapter) &&
!test_bit(ENA_FLAG_TRIGGER_RESET, &adapter->flags)) {
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);
ena_devlink_disable_phc_param(adapter->devlink);
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;
}