mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-25 10:42:21 +03:00
nvethernet: add PTP support
This takes care of implementing the PTP support which includes PTP V1/V2 over IPV4,IPV6,Ethernet,gPTP. Bug 200524751 Change-Id: Id647db1f60582717a09f24699841e00d7a582a1d Signed-off-by: Narayan Reddy <narayanr@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/2123439 GVS: Gerrit_Virtual_Submit Reviewed-by: Srinivas Ramachandran <srinivasra@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
committed by
Revanth Kumar Uppala
parent
6a3fdf61b3
commit
21dcef7b83
@@ -24,6 +24,7 @@ nvethernet-objs:= ether_linux.o \
|
||||
ethtool.o \
|
||||
sysfs.o \
|
||||
ioctl.o \
|
||||
ptp.o \
|
||||
$(OSI_CORE)/osi_core.o \
|
||||
$(OSI_CORE)/osi_common.o \
|
||||
$(OSI_DMA)/osi_dma.o \
|
||||
|
||||
@@ -925,6 +925,15 @@ static int ether_open(struct net_device *dev)
|
||||
/* Start the MAC */
|
||||
osi_start_mac(pdata->osi_core);
|
||||
|
||||
/* Initialize PTP */
|
||||
ret = ether_ptp_init(pdata);
|
||||
if (ret < 0) {
|
||||
dev_err(pdata->dev,
|
||||
"%s:failed to initialize PTP with reason %d\n",
|
||||
__func__, ret);
|
||||
goto err_hw_init;
|
||||
}
|
||||
|
||||
ether_napi_enable(pdata);
|
||||
|
||||
/* start PHY */
|
||||
@@ -988,6 +997,9 @@ static int ether_close(struct net_device *dev)
|
||||
/* free DMA resources after DMA stop */
|
||||
free_dma_resources(pdata->osi_dma, pdata->dev);
|
||||
|
||||
/* PTP de-init */
|
||||
ether_ptp_remove(pdata);
|
||||
|
||||
/* Stop the MAC */
|
||||
osi_stop_mac(pdata->osi_core);
|
||||
|
||||
@@ -1104,6 +1116,11 @@ static int ether_tx_swcx_alloc(struct device *dev,
|
||||
tx_pkt_cx->flags |= OSI_PKT_CX_VLAN;
|
||||
}
|
||||
|
||||
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
|
||||
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
|
||||
tx_pkt_cx->flags |= OSI_PKT_CX_PTP;
|
||||
}
|
||||
|
||||
if (((tx_pkt_cx->flags & OSI_PKT_CX_VLAN) == OSI_PKT_CX_VLAN) ||
|
||||
((tx_pkt_cx->flags & OSI_PKT_CX_TSO) == OSI_PKT_CX_TSO)) {
|
||||
tx_swcx = tx_ring->tx_swcx + cur_tx_idx;
|
||||
@@ -1577,6 +1594,10 @@ static int ether_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
||||
ret = ether_handle_priv_ioctl(dev, rq);
|
||||
break;
|
||||
|
||||
case SIOCSHWTSTAMP:
|
||||
ret = ether_handle_hwtstamp_ioctl(pdata, rq);
|
||||
break;
|
||||
|
||||
default:
|
||||
netdev_err(dev, "%s: Unsupported ioctl %d\n",
|
||||
__func__, cmd);
|
||||
@@ -2384,6 +2405,15 @@ static int ether_configure_car(struct platform_device *pdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set PTP clock rate*/
|
||||
ret = clk_set_rate(pdata->ptp_ref_clk, pdata->ptp_ref_clock_speed);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to set ptp clk rate\n");
|
||||
return ret;
|
||||
} else {
|
||||
osi_core->ptp_config.ptp_ref_clk_rate = pdata->ptp_ref_clock_speed;
|
||||
}
|
||||
|
||||
ret = ether_enable_clks(pdata);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable clks\n");
|
||||
@@ -2576,20 +2606,27 @@ static void ether_parse_queue_prio(struct ether_priv_data *pdata,
|
||||
*/
|
||||
static int ether_parse_dt(struct ether_priv_data *pdata)
|
||||
{
|
||||
struct device *dev = pdata->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
struct osi_dma_priv_data *osi_dma = pdata->osi_dma;
|
||||
struct device *dev = pdata->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
/* read ptp clock */
|
||||
ret = of_property_read_u32(np, "nvidia,ptp_ref_clock_speed",
|
||||
&pdata->ptp_ref_clock_speed);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "setting default PTP clk rate as 312.5MHz\n");
|
||||
pdata->ptp_ref_clock_speed = ETHER_DFLT_PTP_CLK;
|
||||
}
|
||||
/* Read Pause frame feature support */
|
||||
ret = of_property_read_u32(np, "nvidia,pause_frames",
|
||||
&pdata->osi_core->pause_frames);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read nvida,pause_frames, so"
|
||||
" setting to default support as enable\n");
|
||||
pdata->osi_core->pause_frames = OSI_PAUSE_FRAMES_ENABLE;
|
||||
" setting to default support as disable\n");
|
||||
pdata->osi_core->pause_frames = OSI_PAUSE_FRAMES_DISABLE;
|
||||
}
|
||||
|
||||
/* Check if IOMMU is enabled */
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
#ifndef ETHER_LINUX_H
|
||||
#define ETHER_LINUX_H
|
||||
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/of_gpio.h>
|
||||
@@ -34,6 +36,7 @@
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/ktime.h>
|
||||
|
||||
#include <osi_core.h>
|
||||
#include <osi_dma.h>
|
||||
@@ -45,6 +48,7 @@
|
||||
#define ETHER_QUEUE_PRIO_DEFAULT 0U
|
||||
#define ETHER_QUEUE_PRIO_MAX 7U
|
||||
#define ETHER_QUEUE_PRIO_INVALID 0xFFU
|
||||
#define ETHER_DFLT_PTP_CLK 312500000U
|
||||
|
||||
#define EQOS_CONFIG_FAIL -3
|
||||
#define EQOS_CONFIG_SUCCESS 0
|
||||
@@ -172,6 +176,11 @@ struct ether_rx_napi {
|
||||
* @l3_l4_filter: L3_l4 filter enabled 1: enabled
|
||||
* @vlan_hash_filtering: vlan hash filter 1: hash, 0: perfect
|
||||
* @l2_filtering_mode: l2 filter mode 1: hash 0: perfect
|
||||
* @ptp_clock_ops: PTP clock operations structure.
|
||||
* @ptp_clock: PTP system clock
|
||||
* @ptp_ref_clock_speed: PTP reference clock supported by platform
|
||||
* @hwts_tx_en: HW tx time stamping enable
|
||||
* @hwts_rx_en: HW rx time stamping enable
|
||||
*/
|
||||
struct ether_priv_data {
|
||||
struct osi_core_priv_data *osi_core;
|
||||
@@ -231,9 +240,20 @@ struct ether_priv_data {
|
||||
unsigned int l3_l4_filter;
|
||||
unsigned int vlan_hash_filtering;
|
||||
unsigned int l2_filtering_mode;
|
||||
|
||||
/* for PTP */
|
||||
struct ptp_clock_info ptp_clock_ops;
|
||||
struct ptp_clock *ptp_clock;
|
||||
unsigned int ptp_ref_clock_speed;
|
||||
unsigned int hwts_tx_en;
|
||||
unsigned int hwts_rx_en;
|
||||
};
|
||||
|
||||
void ether_set_ethtool_ops(struct net_device *ndev);
|
||||
int ether_sysfs_register(struct device *dev);
|
||||
void ether_sysfs_unregister(struct device *dev);
|
||||
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);
|
||||
#endif /* ETHER_LINUX_H */
|
||||
|
||||
@@ -128,6 +128,44 @@ static int ether_set_pauseparam(struct net_device *ndev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_get_ts_info: Get HW supported time stamping.
|
||||
* @net: Net device data.
|
||||
* @info: Holds device supported timestamping types
|
||||
*
|
||||
* Algorithm: Function used to query the PTP capabilities for given netdev.
|
||||
*
|
||||
* Dependencies: HW need to support PTP functionality.
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: Return can't be a -ve value.
|
||||
*/
|
||||
|
||||
static int ether_get_ts_info(struct net_device *net,
|
||||
struct ethtool_ts_info *info)
|
||||
{
|
||||
info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
|
||||
SOF_TIMESTAMPING_RX_HARDWARE |
|
||||
SOF_TIMESTAMPING_TX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_RX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_RAW_HARDWARE |
|
||||
SOF_TIMESTAMPING_SOFTWARE;
|
||||
info->phc_index = 0;
|
||||
|
||||
info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON);
|
||||
|
||||
info->rx_filters |= ((1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
|
||||
(1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
|
||||
(1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
|
||||
(1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
|
||||
(1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) |
|
||||
(1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
|
||||
(1 << HWTSTAMP_FILTER_PTP_V2_EVENT) |
|
||||
(1 << HWTSTAMP_FILTER_NONE));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops ether_ethtool_ops = {
|
||||
.get_link = ethtool_op_get_link,
|
||||
@@ -135,6 +173,7 @@ static const struct ethtool_ops ether_ethtool_ops = {
|
||||
.set_link_ksettings = phy_ethtool_set_link_ksettings,
|
||||
.get_pauseparam = ether_get_pauseparam,
|
||||
.set_pauseparam = ether_set_pauseparam,
|
||||
.get_ts_info = ether_get_ts_info,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -212,17 +212,19 @@ static void ether_realloc_rx_skb(struct ether_priv_data *pdata,
|
||||
* Return: None.
|
||||
*/
|
||||
void osd_receive_packet(void *priv, void *rxring, unsigned int chan,
|
||||
unsigned int dma_buf_len, void *rxpkt_cx)
|
||||
unsigned int dma_buf_len, void *rxpkt_cx,
|
||||
void *rx_pkt_swcx)
|
||||
{
|
||||
struct ether_priv_data *pdata = (struct ether_priv_data *)priv;
|
||||
struct ether_rx_napi *rx_napi = pdata->rx_napi[chan];
|
||||
struct osi_rx_ring *rx_ring = (struct osi_rx_ring *)rxring;
|
||||
struct osi_rx_swcx *rx_swcx = rx_ring->rx_swcx + rx_ring->cur_rx_idx;
|
||||
struct osi_rx_swcx *rx_swcx = (struct osi_rx_swcx *)rx_pkt_swcx;
|
||||
struct osi_rx_pkt_cx *rx_pkt_cx = (struct osi_rx_pkt_cx *)rxpkt_cx;
|
||||
struct sk_buff *skb = (struct sk_buff *)rx_swcx->buf_virt_addr;
|
||||
dma_addr_t dma_addr = (dma_addr_t)rx_swcx->buf_phy_addr;
|
||||
struct net_device *ndev = pdata->ndev;
|
||||
struct osi_pkt_err_stats *pkt_err_stat = &pdata->osi_dma->pkt_err_stats;
|
||||
struct skb_shared_hwtstamps *shhwtstamp;
|
||||
|
||||
dma_unmap_single(pdata->dev, dma_addr, dma_buf_len, DMA_FROM_DEVICE);
|
||||
|
||||
@@ -242,6 +244,14 @@ void osd_receive_packet(void *priv, void *rxring, unsigned int chan,
|
||||
rx_pkt_cx->vlan_tag);
|
||||
}
|
||||
|
||||
/* Handle time stamp */
|
||||
if ((rx_pkt_cx->flags & OSI_PKT_CX_PTP) == OSI_PKT_CX_PTP) {
|
||||
shhwtstamp = skb_hwtstamps(skb);
|
||||
memset(shhwtstamp, 0,
|
||||
sizeof(struct skb_shared_hwtstamps));
|
||||
shhwtstamp->hwtstamp = ns_to_ktime(rx_pkt_cx->ns);
|
||||
}
|
||||
|
||||
skb->dev = ndev;
|
||||
skb->protocol = eth_type_trans(skb, ndev);
|
||||
ndev->stats.rx_bytes += skb->len;
|
||||
@@ -259,8 +269,6 @@ void osd_receive_packet(void *priv, void *rxring, unsigned int chan,
|
||||
rx_swcx->buf_virt_addr = NULL;
|
||||
rx_swcx->buf_phy_addr = 0;
|
||||
|
||||
INCR_RX_DESC_INDEX(rx_ring->cur_rx_idx, 1U);
|
||||
|
||||
if (osi_get_refill_rx_desc_cnt(rx_ring) >= 16U)
|
||||
ether_realloc_rx_skb(pdata, rx_ring, chan);
|
||||
}
|
||||
@@ -278,6 +286,7 @@ void osd_receive_packet(void *priv, void *rxring, unsigned int chan,
|
||||
* Algorithm:
|
||||
* 1) Updates stats for linux network stack.
|
||||
* 2) unmap and free the buffer DMA address and buffer.
|
||||
* 3) Time stamp will be update to stack if available.
|
||||
*
|
||||
* Dependencies: Tx completion need to make sure that Tx descriptors
|
||||
* processed properly.
|
||||
@@ -295,6 +304,7 @@ void osd_transmit_complete(void *priv, void *buffer, unsigned long dmaaddr,
|
||||
struct osi_dma_priv_data *osi_dma = pdata->osi_dma;
|
||||
struct sk_buff *skb = (struct sk_buff *)buffer;
|
||||
dma_addr_t dma_addr = (dma_addr_t)dmaaddr;
|
||||
struct skb_shared_hwtstamps shhwtstamp;
|
||||
struct net_device *ndev = pdata->ndev;
|
||||
struct osi_tx_ring *tx_ring;
|
||||
struct netdev_queue *txq;
|
||||
@@ -303,6 +313,13 @@ void osd_transmit_complete(void *priv, void *buffer, unsigned long dmaaddr,
|
||||
ndev->stats.tx_packets++;
|
||||
ndev->stats.tx_bytes += len;
|
||||
|
||||
if ((txdone_pkt_cx->flags & OSI_TXDONE_CX_TS) == OSI_TXDONE_CX_TS) {
|
||||
memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
|
||||
shhwtstamp.hwtstamp = ns_to_ktime(txdone_pkt_cx->ns);
|
||||
/* pass tstamp to stack */
|
||||
skb_tstamp_tx(skb, &shhwtstamp);
|
||||
}
|
||||
|
||||
if (dma_addr) {
|
||||
if ((txdone_pkt_cx->flags & OSI_TXDONE_CX_PAGED_BUF) ==
|
||||
OSI_TXDONE_CX_PAGED_BUF) {
|
||||
|
||||
389
drivers/net/ethernet/nvidia/nvethernet/ptp.c
Normal file
389
drivers/net/ethernet/nvidia/nvethernet/ptp.c
Normal file
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ether_linux.h"
|
||||
|
||||
/**
|
||||
* ether_adjust_time: Adjust hardware time
|
||||
* @ptp: Pointer to ptp_clock_info structure.
|
||||
* @delta: Desired change in nanoseconds.
|
||||
*
|
||||
* Algorithm: This function is used to shift/adjust the time of the
|
||||
* hardware clock.
|
||||
*
|
||||
* Dependencies:
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: 0 - success, negative value - failure.
|
||||
*/
|
||||
static int ether_adjust_time(struct ptp_clock_info *ptp, s64 delta)
|
||||
{
|
||||
struct ether_priv_data *pdata = container_of(ptp,
|
||||
struct ether_priv_data,
|
||||
ptp_clock_ops);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
int ret = -1;
|
||||
|
||||
ret = osi_adjust_time(osi_core, delta);
|
||||
if (ret < 0) {
|
||||
dev_err(pdata->dev,
|
||||
"%s:failed to adjust time with reason %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_adjust_freq: Adjust hardware time
|
||||
* @ptp: Pointer to ptp_clock_info structure.
|
||||
* @ppb: Desired period change in parts per billion.
|
||||
*
|
||||
* Algorithm: This function is used to adjust the frequency of the
|
||||
* hardware clock.
|
||||
*
|
||||
* Dependencies:
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: 0 - success, negative value - failure.
|
||||
*/
|
||||
static int ether_adjust_freq(struct ptp_clock_info *ptp, s32 ppb)
|
||||
{
|
||||
struct ether_priv_data *pdata = container_of(ptp,
|
||||
struct ether_priv_data,
|
||||
ptp_clock_ops);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
int ret = -1;
|
||||
|
||||
ret = osi_adjust_freq(osi_core, ppb);
|
||||
if (ret < 0) {
|
||||
dev_err(pdata->dev,
|
||||
"%s:failed to adjust frequency with reason code %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_get_time: Get current time
|
||||
* @ptp: Pointer to ptp_clock_info structure.
|
||||
* @ts: Pointer to hole time.
|
||||
*
|
||||
* Algorithm: This function is used to read the current time from the
|
||||
* hardware clock
|
||||
*
|
||||
* Dependencies:
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: 0 - success, negative value - failure.
|
||||
*/
|
||||
static int ether_get_time(struct ptp_clock_info *ptp, struct timespec *ts)
|
||||
{
|
||||
struct ether_priv_data *pdata = container_of(ptp,
|
||||
struct ether_priv_data,
|
||||
ptp_clock_ops);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
unsigned int sec, nsec;
|
||||
|
||||
osi_get_systime_from_mac(osi_core, &sec, &nsec);
|
||||
|
||||
ts->tv_sec = sec;
|
||||
ts->tv_nsec = nsec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_set_time: Set current time
|
||||
* @ptp: Pointer to ptp_clock_info structure.
|
||||
* @ts: Time value to set.
|
||||
*
|
||||
* Algorithm: This function is used to set the current time to the
|
||||
* hardware clock.
|
||||
*
|
||||
* Dependencies:
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: 0 - success, negative value - failure.
|
||||
*/
|
||||
static int ether_set_time(struct ptp_clock_info *ptp,
|
||||
const struct timespec *ts)
|
||||
{
|
||||
struct ether_priv_data *pdata = container_of(ptp,
|
||||
struct ether_priv_data,
|
||||
ptp_clock_ops);
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
int ret = -1;
|
||||
|
||||
ret = osi_set_systime_to_mac(osi_core, ts->tv_sec, ts->tv_nsec);
|
||||
if (ret < 0) {
|
||||
dev_err(pdata->dev,
|
||||
"%s:failed to set system time with reason %d\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* structure describing a PTP hardware clock */
|
||||
static struct ptp_clock_info ether_ptp_clock_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ether_ptp_clk",
|
||||
.max_adj = OSI_ETHER_SYSCLOCK,
|
||||
.n_alarm = 0,
|
||||
.n_ext_ts = 0,
|
||||
.n_per_out = 0,
|
||||
.pps = 0,
|
||||
.adjfreq = ether_adjust_freq,
|
||||
.adjtime = ether_adjust_time,
|
||||
.gettime64 = ether_get_time,
|
||||
.settime64 = ether_set_time,
|
||||
};
|
||||
|
||||
/**
|
||||
* ether_ptp_init: Function to register ptp clock driver.
|
||||
* @pdata: Pointer to private data structure.
|
||||
*
|
||||
* Algorithm: This function is used to register the ptp clock
|
||||
* driver to kernel.
|
||||
*
|
||||
* Dependencies: Ethernet driver probe need to be completed successfully
|
||||
* with ethernet network device created.
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: 0 - success, negative value - failure.
|
||||
*/
|
||||
int ether_ptp_init(struct ether_priv_data *pdata)
|
||||
{
|
||||
if (pdata->hw_feat.tsstssel == OSI_DISABLE) {
|
||||
pdata->ptp_clock = NULL;
|
||||
dev_err(pdata->dev, "No PTP supports in HW\n"
|
||||
"Aborting PTP clock driver registration\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdata->ptp_clock_ops = ether_ptp_clock_ops;
|
||||
pdata->ptp_clock = ptp_clock_register(&pdata->ptp_clock_ops,
|
||||
pdata->dev);
|
||||
if (IS_ERR(pdata->ptp_clock)) {
|
||||
pdata->ptp_clock = NULL;
|
||||
dev_err(pdata->dev, "Fail to register PTP clock\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* By default enable nano second accuracy */
|
||||
pdata->osi_core->ptp_config.one_nsec_accuracy = OSI_ENABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_ptp_remove: Function to de register ptp clock driver.
|
||||
* @pdata: Pointer to private data structure.
|
||||
*
|
||||
* Algorithm: This function is used to de register the ptp clock
|
||||
*
|
||||
* Dependencies: PTP clock driver need to be sucessfully registered during
|
||||
* initialization
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: None.
|
||||
*/
|
||||
void ether_ptp_remove(struct ether_priv_data *pdata)
|
||||
{
|
||||
if (pdata->ptp_clock) {
|
||||
ptp_clock_unregister(pdata->ptp_clock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ether_handle_hwtstamp_ioctl: Function to handle PTP settings.
|
||||
* @pdata: Pointer to private data structure.
|
||||
* @ifr: Interface request structure used for socket ioctl
|
||||
*
|
||||
* Algorithm: This function is used to handle the hardware PTP settings.
|
||||
*
|
||||
* Dependencies: PTP clock driver need to be sucessfully registered during
|
||||
* initialization and HW need to support PTP functionality.
|
||||
*
|
||||
* Protection: None.
|
||||
*
|
||||
* Return: None.
|
||||
*/
|
||||
int ether_handle_hwtstamp_ioctl(struct ether_priv_data *pdata,
|
||||
struct ifreq *ifr)
|
||||
{
|
||||
struct osi_core_priv_data *osi_core = pdata->osi_core;
|
||||
struct hwtstamp_config config;
|
||||
unsigned int hwts_rx_en = 1;
|
||||
struct timespec now;
|
||||
|
||||
if (pdata->hw_feat.tsstssel == OSI_DISABLE) {
|
||||
dev_err(pdata->dev, "HW timestamping not available\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (copy_from_user(&config, ifr->ifr_data,
|
||||
sizeof(struct hwtstamp_config))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
dev_err(pdata->dev, "config.flags = %#x, tx_type = %#x,"
|
||||
"rx_filter = %#x\n", config.flags, config.tx_type,
|
||||
config.rx_filter);
|
||||
|
||||
/* reserved for future extensions */
|
||||
if (config.flags) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (config.tx_type) {
|
||||
case HWTSTAMP_TX_OFF:
|
||||
pdata->hwts_tx_en = OSI_DISABLE;
|
||||
break;
|
||||
|
||||
case HWTSTAMP_TX_ON:
|
||||
pdata->hwts_tx_en = OSI_ENABLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(pdata->dev, "tx_type is out of range\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
/* Initialize ptp filter to 0 */
|
||||
osi_core->ptp_config.ptp_filter = 0;
|
||||
|
||||
switch (config.rx_filter) {
|
||||
/* time stamp no incoming packet at all */
|
||||
case HWTSTAMP_FILTER_NONE:
|
||||
hwts_rx_en = 0;
|
||||
break;
|
||||
|
||||
/* PTP v1, UDP, any kind of event packet */
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
||||
osi_core->ptp_config.ptp_filter = OSI_MAC_TCR_SNAPTYPSEL_1 |
|
||||
OSI_MAC_TCR_TSIPV4ENA |
|
||||
OSI_MAC_TCR_TSIPV6ENA;
|
||||
break;
|
||||
|
||||
/* PTP v1, UDP, Sync packet */
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
|
||||
osi_core->ptp_config.ptp_filter = OSI_MAC_TCR_TSEVENTENA |
|
||||
OSI_MAC_TCR_TSIPV4ENA |
|
||||
OSI_MAC_TCR_TSIPV6ENA;
|
||||
break;
|
||||
|
||||
/* PTP v1, UDP, Delay_req packet */
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
|
||||
osi_core->ptp_config.ptp_filter = OSI_MAC_TCR_TSMASTERENA |
|
||||
OSI_MAC_TCR_TSEVENTENA |
|
||||
OSI_MAC_TCR_TSIPV4ENA |
|
||||
OSI_MAC_TCR_TSIPV6ENA;
|
||||
break;
|
||||
|
||||
/* PTP v2, UDP, any kind of event packet */
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
||||
osi_core->ptp_config.ptp_filter = OSI_MAC_TCR_SNAPTYPSEL_1 |
|
||||
OSI_MAC_TCR_TSIPV4ENA |
|
||||
OSI_MAC_TCR_TSIPV6ENA |
|
||||
OSI_MAC_TCR_TSVER2ENA;
|
||||
break;
|
||||
|
||||
/* PTP v2, UDP, Sync packet */
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
||||
osi_core->ptp_config.ptp_filter = OSI_MAC_TCR_TSEVENTENA |
|
||||
OSI_MAC_TCR_TSIPV4ENA |
|
||||
OSI_MAC_TCR_TSIPV6ENA |
|
||||
OSI_MAC_TCR_TSVER2ENA;
|
||||
break;
|
||||
|
||||
/* PTP v2, UDP, Delay_req packet */
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
||||
osi_core->ptp_config.ptp_filter = OSI_MAC_TCR_TSEVENTENA |
|
||||
OSI_MAC_TCR_TSMASTERENA |
|
||||
OSI_MAC_TCR_TSIPV4ENA |
|
||||
OSI_MAC_TCR_TSIPV6ENA |
|
||||
OSI_MAC_TCR_TSVER2ENA;
|
||||
break;
|
||||
|
||||
/* PTP v2/802.AS1, any layer, any kind of event packet */
|
||||
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
||||
osi_core->ptp_config.ptp_filter = OSI_MAC_TCR_SNAPTYPSEL_1 |
|
||||
OSI_MAC_TCR_TSIPV4ENA |
|
||||
OSI_MAC_TCR_TSIPV6ENA |
|
||||
OSI_MAC_TCR_TSVER2ENA |
|
||||
OSI_MAC_TCR_TSIPENA;
|
||||
break;
|
||||
|
||||
/* PTP v2/802.AS1, any layer, Sync packet */
|
||||
case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
||||
osi_core->ptp_config.ptp_filter = OSI_MAC_TCR_TSIPV4ENA |
|
||||
OSI_MAC_TCR_TSIPV6ENA |
|
||||
OSI_MAC_TCR_TSVER2ENA |
|
||||
OSI_MAC_TCR_TSEVENTENA |
|
||||
OSI_MAC_TCR_TSIPENA |
|
||||
OSI_MAC_TCR_AV8021ASMEN;
|
||||
break;
|
||||
|
||||
/* PTP v2/802.AS1, any layer, Delay_req packet */
|
||||
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
||||
osi_core->ptp_config.ptp_filter = OSI_MAC_TCR_TSIPV4ENA |
|
||||
OSI_MAC_TCR_TSIPV6ENA |
|
||||
OSI_MAC_TCR_TSVER2ENA |
|
||||
OSI_MAC_TCR_TSEVENTENA |
|
||||
OSI_MAC_TCR_AV8021ASMEN |
|
||||
OSI_MAC_TCR_TSMASTERENA |
|
||||
OSI_MAC_TCR_TSIPENA;
|
||||
break;
|
||||
|
||||
/* time stamp any incoming packet */
|
||||
case HWTSTAMP_FILTER_ALL:
|
||||
osi_core->ptp_config.ptp_filter = OSI_MAC_TCR_TSENALL;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(pdata->dev, "rx_filter is out of range\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
if (!pdata->hwts_tx_en && !hwts_rx_en) {
|
||||
/* disable the PTP configuration */
|
||||
osi_ptp_configuration(osi_core, OSI_DISABLE);
|
||||
} else {
|
||||
/* Store SYS CLOCK */
|
||||
osi_core->ptp_config.ptp_clock = OSI_ETHER_SYSCLOCK;
|
||||
/* initialize system time */
|
||||
getnstimeofday(&now);
|
||||
/* Store sec and nsec */
|
||||
osi_core->ptp_config.sec = now.tv_sec;
|
||||
osi_core->ptp_config.nsec = now.tv_nsec;
|
||||
/* one nsec accuracy */
|
||||
osi_core->ptp_config.one_nsec_accuracy = OSI_ENABLE;
|
||||
/* Enable the PTP configuration */
|
||||
osi_ptp_configuration(osi_core, OSI_ENABLE);
|
||||
}
|
||||
|
||||
return (copy_to_user(ifr->ifr_data, &config,
|
||||
sizeof(struct hwtstamp_config))) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user