From 21dcef7b8367a6bf14e3ba5558033f9f66f62307 Mon Sep 17 00:00:00 2001 From: Narayan Reddy Date: Wed, 15 May 2019 23:36:32 +0530 Subject: [PATCH] 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 Reviewed-on: https://git-master.nvidia.com/r/2123439 GVS: Gerrit_Virtual_Submit Reviewed-by: Srinivas Ramachandran Reviewed-by: mobile promotions Tested-by: mobile promotions --- .../net/ethernet/nvidia/nvethernet/Makefile | 1 + .../ethernet/nvidia/nvethernet/ether_linux.c | 45 +- .../ethernet/nvidia/nvethernet/ether_linux.h | 20 + .../net/ethernet/nvidia/nvethernet/ethtool.c | 39 ++ drivers/net/ethernet/nvidia/nvethernet/osd.c | 25 +- drivers/net/ethernet/nvidia/nvethernet/ptp.c | 389 ++++++++++++++++++ 6 files changed, 511 insertions(+), 8 deletions(-) create mode 100644 drivers/net/ethernet/nvidia/nvethernet/ptp.c diff --git a/drivers/net/ethernet/nvidia/nvethernet/Makefile b/drivers/net/ethernet/nvidia/nvethernet/Makefile index aba3dbf6..bac45ac9 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/Makefile +++ b/drivers/net/ethernet/nvidia/nvethernet/Makefile @@ -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 \ diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index 43e35ae1..08031464 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -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 */ diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index 97c9400c..79bf51de 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -17,8 +17,10 @@ #ifndef ETHER_LINUX_H #define ETHER_LINUX_H +#include #include #include +#include #include #include #include @@ -34,6 +36,7 @@ #include #include #include +#include #include #include @@ -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 */ diff --git a/drivers/net/ethernet/nvidia/nvethernet/ethtool.c b/drivers/net/ethernet/nvidia/nvethernet/ethtool.c index 3dc1303c..f9d2e633 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ethtool.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ethtool.c @@ -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, }; /** diff --git a/drivers/net/ethernet/nvidia/nvethernet/osd.c b/drivers/net/ethernet/nvidia/nvethernet/osd.c index 34bc8c42..2b8975ab 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/osd.c +++ b/drivers/net/ethernet/nvidia/nvethernet/osd.c @@ -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) { diff --git a/drivers/net/ethernet/nvidia/nvethernet/ptp.c b/drivers/net/ethernet/nvidia/nvethernet/ptp.c new file mode 100644 index 00000000..a98f5a08 --- /dev/null +++ b/drivers/net/ethernet/nvidia/nvethernet/ptp.c @@ -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 . + */ + +#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; +} +