| // 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, |
| ×tamp_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; |
| } |