diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index 917cf7f5..9641eb91 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -1962,6 +1962,10 @@ static int ether_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ret = ether_handle_priv_ioctl(dev, rq); break; + case ETHER_PRV_TS_IOCTL: + ret = ether_handle_priv_ts_ioctl(pdata, rq); + break; + case SIOCSHWTSTAMP: ret = ether_handle_hwtstamp_ioctl(pdata, rq); break; diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index 38fe5139..25da219a 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -186,6 +186,7 @@ struct ether_rx_napi { * @hwts_tx_en: HW tx time stamping enable * @hwts_rx_en: HW rx time stamping enable * @max_platform_mtu: Max MTU supported by platform + * @ptp_lock: Lock for accessing PTP registers */ struct ether_priv_data { struct osi_core_priv_data *osi_core; @@ -254,6 +255,7 @@ struct ether_priv_data { unsigned int hwts_tx_en; unsigned int hwts_rx_en; unsigned int max_platform_mtu; + raw_spinlock_t ptp_lock; }; void ether_set_ethtool_ops(struct net_device *ndev); @@ -263,4 +265,6 @@ int ether_ptp_init(struct ether_priv_data *pdata); void ether_ptp_remove(struct ether_priv_data *pdata); int ether_handle_hwtstamp_ioctl(struct ether_priv_data *pdata, struct ifreq *ifr); +int ether_handle_priv_ts_ioctl(struct ether_priv_data *pdata, + struct ifreq *ifr); #endif /* ETHER_LINUX_H */ diff --git a/drivers/net/ethernet/nvidia/nvethernet/ioctl.h b/drivers/net/ethernet/nvidia/nvethernet/ioctl.h index b1dc9aef..09de4e7f 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ioctl.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ioctl.h @@ -21,6 +21,7 @@ #define MAX_IP_ADDR_BYTE 0xFF /* Remote wakeup filter */ #define EQOS_RWK_FILTER_LENGTH 8 +#define ETHER_PRV_TS_IOCTL (SIOCDEVPRIVATE + 1) /* private ioctl number*/ #define ETHER_AVB_ALGORITHM 27 @@ -85,8 +86,19 @@ struct arp_offload_param { unsigned char ip_addr[NUM_BYTES_IN_IPADDR]; }; -/* Private ioctl handler function */ +/** + * struct ifr_data_timestamp_struct - common data structure between + * driver and application for sharing info through private TS ioctl + * @clockid: Clock ID + * @kernel_ts: Store kernel time + * @hw_ptp_ts: Store HW time + */ +struct ifr_data_timestamp_struct { + clockid_t clockid; + struct timespec kernel_ts; + struct timespec hw_ptp_ts; +}; + int ether_handle_priv_ioctl(struct net_device *ndev, struct ifreq *ifr); #endif - diff --git a/drivers/net/ethernet/nvidia/nvethernet/ptp.c b/drivers/net/ethernet/nvidia/nvethernet/ptp.c index 0645c040..f1eca30b 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ptp.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ptp.c @@ -15,6 +15,39 @@ */ #include "ether_linux.h" +#include + +/* raw spinlock to get HW PTP time and kernel time atomically */ +static DEFINE_RAW_SPINLOCK(ether_ts_lock); + +/** + * ether_get_ptptime get PTP time + * @data: OSI core private data structure + * + * Return: nano seconds + */ +static inline u64 ether_get_ptptime(void *data) +{ + struct ether_priv_data *pdata = data; + struct osi_core_priv_data *osi_core = pdata->osi_core; + unsigned long flags; + unsigned int sec, nsec; + int ret = -1; + + raw_spin_lock_irqsave(&pdata->ptp_lock, flags); + + ret = osi_get_systime_from_mac(osi_core, &sec, &nsec); + if (ret != 0) { + dev_err(pdata->dev, "%s: Failed to read systime from MAC %d\n", + __func__, ret); + raw_spin_unlock_irqrestore(&pdata->ptp_lock, flags); + return ret; + } + + raw_spin_unlock_irqrestore(&pdata->ptp_lock, flags); + + return (u64)nsec; +} /** * ether_adjust_time: Adjust hardware time @@ -38,6 +71,8 @@ static int ether_adjust_time(struct ptp_clock_info *ptp, s64 delta) struct osi_core_priv_data *osi_core = pdata->osi_core; int ret = -1; + raw_spin_lock(&pdata->ptp_lock); + ret = osi_adjust_time(osi_core, delta); if (ret < 0) { dev_err(pdata->dev, @@ -45,6 +80,8 @@ static int ether_adjust_time(struct ptp_clock_info *ptp, s64 delta) __func__, ret); } + raw_spin_unlock(&pdata->ptp_lock); + return ret; } @@ -70,6 +107,8 @@ static int ether_adjust_freq(struct ptp_clock_info *ptp, s32 ppb) struct osi_core_priv_data *osi_core = pdata->osi_core; int ret = -1; + raw_spin_lock(&pdata->ptp_lock); + ret = osi_adjust_freq(osi_core, ppb); if (ret < 0) { dev_err(pdata->dev, @@ -77,6 +116,8 @@ static int ether_adjust_freq(struct ptp_clock_info *ptp, s32 ppb) __func__, ret); } + raw_spin_unlock(&pdata->ptp_lock); + return ret; } @@ -102,8 +143,12 @@ static int ether_get_time(struct ptp_clock_info *ptp, struct timespec *ts) struct osi_core_priv_data *osi_core = pdata->osi_core; unsigned int sec, nsec; + raw_spin_lock(&pdata->ptp_lock); + osi_get_systime_from_mac(osi_core, &sec, &nsec); + raw_spin_unlock(&pdata->ptp_lock); + ts->tv_sec = sec; ts->tv_nsec = nsec; @@ -133,6 +178,8 @@ static int ether_set_time(struct ptp_clock_info *ptp, struct osi_core_priv_data *osi_core = pdata->osi_core; int ret = -1; + raw_spin_lock(&pdata->ptp_lock); + ret = osi_set_systime_to_mac(osi_core, ts->tv_sec, ts->tv_nsec); if (ret < 0) { dev_err(pdata->dev, @@ -140,6 +187,8 @@ static int ether_set_time(struct ptp_clock_info *ptp, __func__, ret); } + raw_spin_unlock(&pdata->ptp_lock); + return ret; } @@ -181,6 +230,8 @@ int ether_ptp_init(struct ether_priv_data *pdata) return -1; } + raw_spin_lock_init(&pdata->ptp_lock); + pdata->ptp_clock_ops = ether_ptp_clock_ops; pdata->ptp_clock = ptp_clock_register(&pdata->ptp_clock_ops, pdata->dev); @@ -381,9 +432,81 @@ int ether_handle_hwtstamp_ioctl(struct ether_priv_data *pdata, osi_core->ptp_config.one_nsec_accuracy = OSI_ENABLE; /* Enable the PTP configuration */ osi_ptp_configuration(osi_core, OSI_ENABLE); + /* Register broadcasting MAC timestamp to clients */ + tegra_register_hwtime_source(ether_get_ptptime, pdata); } return (copy_to_user(ifr->ifr_data, &config, sizeof(struct hwtstamp_config))) ? -EFAULT : 0; } +/** + * ether_handle_priv_ts_ioctl: Function to handle PTP priv IOCTL + * @pdata: Pointer to private data structure. + * @ifr: Interface request structure used for socket ioctl + * + * Algorithm: This function is used to query hardware time and + * the kernel time simultaneously. + * + * Dependencies: PTP clock driver need to be successfully registered during + * initialization and HW need to support PTP functionality. + * + * Return: 0 on success, negative value on failure. + */ + +int ether_handle_priv_ts_ioctl(struct ether_priv_data *pdata, + struct ifreq *ifr) +{ + struct ifr_data_timestamp_struct req; + struct osi_core_priv_data *osi_core = pdata->osi_core; + unsigned long flags; + int ret = -1; + + if (ifr->ifr_data == NULL) { + dev_err(pdata->dev, "%s: Invalid data for priv ioctl\n", + __func__); + return -EFAULT; + } + + if (copy_from_user(&req, ifr->ifr_data, sizeof(req))) { + dev_err(pdata->dev, "%s: Data copy from user failed\n", + __func__); + return -EFAULT; + } + + raw_spin_lock_irqsave(ðer_ts_lock, flags); + switch (req.clockid) { + case CLOCK_REALTIME: + ktime_get_real_ts(&req.kernel_ts); + break; + + case CLOCK_MONOTONIC: + ktime_get_ts(&req.kernel_ts); + break; + + default: + dev_err(pdata->dev, "Unsupported clockid\n"); + } + + ret = osi_get_systime_from_mac(osi_core, + (unsigned int *)&req.hw_ptp_ts.tv_sec, + (unsigned int *)&req.hw_ptp_ts.tv_nsec); + if (ret != 0) { + dev_err(pdata->dev, "%s: Failed to read systime from MAC %d\n", + __func__, ret); + raw_spin_unlock_irqrestore(ðer_ts_lock, flags); + return ret; + } + raw_spin_unlock_irqrestore(ðer_ts_lock, flags); + + dev_dbg(pdata->dev, "tv_sec = %ld, tv_nsec = %ld\n", + req.hw_ptp_ts.tv_sec, req.hw_ptp_ts.tv_nsec); + + if (copy_to_user(ifr->ifr_data, &req, sizeof(req))) { + dev_err(pdata->dev, "%s: Data copy to user failed\n", + __func__); + return -EFAULT; + } + + return ret; +}