From da8bbea503c778aff8e39962efb65a8908a526bd Mon Sep 17 00:00:00 2001 From: Narayan Reddy Date: Thu, 25 Jul 2019 16:06:56 +0530 Subject: [PATCH] nvethernet: IOCTL and broadcasting MAC timestamp support 1) Register broadcasting MAC timestamp to clients so that client can make use of the ptp callback for getting the timestamp 2) Add private ioctl to read Timestamp information of both HW PTP time and kernel time Bug 200512422 Change-Id: I03509cc02f28571108a1061c739840c86f960af4 Signed-off-by: Narayan Reddy Reviewed-on: https://git-master.nvidia.com/r/2161172 Reviewed-by: mobile promotions Tested-by: mobile promotions --- .../ethernet/nvidia/nvethernet/ether_linux.c | 4 + .../ethernet/nvidia/nvethernet/ether_linux.h | 4 + .../net/ethernet/nvidia/nvethernet/ioctl.h | 16 ++- drivers/net/ethernet/nvidia/nvethernet/ptp.c | 123 ++++++++++++++++++ 4 files changed, 145 insertions(+), 2 deletions(-) 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; +}