From 077a980e55a7ceb6040ad39474d019114bbe82cb Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Wed, 17 Apr 2013 15:54:09 +0300 Subject: dpaa_eth: Add support for hardware timestamping Add Linux conforming support for hardware timestamping. Minor fixes and comments about the FSL_DPA_1588 code. Signed-off-by: Madalin Bucur Change-Id: I6f9cd77cbe157219cb77e87bb8cfd72bee037261 Reviewed-on: http://git.am.freescale.net:8181/1469 Reviewed-by: Radulescu Ruxandra Ioana-B05472 Reviewed-by: Fleming Andrew-AFLEMING Tested-by: Fleming Andrew-AFLEMING diff --git a/drivers/net/ethernet/freescale/dpa/Kconfig b/drivers/net/ethernet/freescale/dpa/Kconfig index 6ef0918..7c4973d 100644 --- a/drivers/net/ethernet/freescale/dpa/Kconfig +++ b/drivers/net/ethernet/freescale/dpa/Kconfig @@ -57,6 +57,11 @@ config FSL_DPA_1588 depends on DPA_ETH default n +config FSL_DPA_TS + tristate "Linux compliant timestamping" + depends on DPA_ETH + default n + choice DPA_ETH_WQ_ASSIGN prompt "WorkQueue assignment scheme for FrameQueues" depends on DPA_ETH diff --git a/drivers/net/ethernet/freescale/dpa/dpaa_eth-common.h b/drivers/net/ethernet/freescale/dpa/dpaa_eth-common.h index e33bf01..25474e7 100644 --- a/drivers/net/ethernet/freescale/dpa/dpaa_eth-common.h +++ b/drivers/net/ethernet/freescale/dpa/dpaa_eth-common.h @@ -40,7 +40,7 @@ #define __hot /* Simple enum of FQ types - used for array indexing */ -enum {RX, TX}; +enum port_type {RX, TX}; /* More detailed FQ types - used for fine-grained WQ assignments */ enum dpa_fq_type { diff --git a/drivers/net/ethernet/freescale/dpa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpa/dpaa_eth.c index ee2e27b..268fc09 100644 --- a/drivers/net/ethernet/freescale/dpa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpa/dpaa_eth.c @@ -51,6 +51,7 @@ #include /* struct udphdr */ #include /* struct tcphdr */ #include /* net_ratelimit() */ +#include /* struct hwtstamp_config */ #include /* ETH_P_IP and ETH_P_IPV6 */ #include #include @@ -912,6 +913,7 @@ struct sk_buff *_dpa_cleanup_tx_fd(const struct dpa_priv_s *priv, { dma_addr_t addr = qm_fd_addr(fd); dma_addr_t sg_addr; + void *vaddr; struct dpa_bp *bp = priv->dpa_bp; struct sk_buff **skbh; struct sk_buff *skb = NULL; @@ -920,8 +922,8 @@ struct sk_buff *_dpa_cleanup_tx_fd(const struct dpa_priv_s *priv, if (unlikely(!addr)) return skb; - - skbh = (struct sk_buff **)phys_to_virt(addr); + vaddr = phys_to_virt(addr); + skbh = (struct sk_buff **)vaddr; if (fd->format == qm_fd_contig) { /* For contiguous frames, just unmap data buffer; @@ -939,7 +941,6 @@ struct sk_buff *_dpa_cleanup_tx_fd(const struct dpa_priv_s *priv, /* For s/g, we need to unmap both the SGT buffer and the * data buffer, and also free the SGT buffer */ struct qm_sg_entry *sg_entry; - void *vaddr = phys_to_virt(addr); /* Unmap first buffer (contains S/G table) */ dma_unmap_single(bp->dev, addr, sgt_buffer_size(priv), @@ -954,9 +955,23 @@ struct sk_buff *_dpa_cleanup_tx_fd(const struct dpa_priv_s *priv, /* Retrieve the skb backpointer */ skb = *skbh; - /* Free first buffer (which was allocated on Tx) */ - kfree(vaddr); } +/* on some error paths this might not be necessary: */ +#ifdef CONFIG_FSL_DPA_TS + if (unlikely(priv->ts_tx_en && + skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { + struct skb_shared_hwtstamps shhwtstamps; + + if (!dpa_get_ts(priv, TX, &shhwtstamps, (void *)skbh)) + skb_tstamp_tx(skb, &shhwtstamps); + } +#endif /* CONFIG_FSL_DPA_TS */ + + /* Free first buffer (which was allocated on Tx) containing the + * skb backpointer and hardware timestamp information + */ + if (fd->format != qm_fd_contig) + kfree(vaddr); return skb; } @@ -1086,25 +1101,159 @@ static void dpa_set_rx_mode(struct net_device *net_dev) netdev_err(net_dev, "mac_dev->set_multi() = %d\n", _errno); } -#ifdef CONFIG_FSL_DPA_1588 +#ifdef CONFIG_FSL_DPA_TS +int dpa_get_ts(const struct dpa_priv_s *priv, enum port_type rx_tx, + struct skb_shared_hwtstamps *shhwtstamps, const void *data) +{ + u64 *ts, ns; + + /* this will be replaced by a new FMD wrapper API */ + ts = FM_PORT_GetBufferTimeStamp( + fm_port_get_handle(priv->mac_dev->port_dev[rx_tx]), + (char *)data); + + memset(shhwtstamps, 0, sizeof(*shhwtstamps)); + + /* was the timestamping performed? */ + if (!ts || *ts == 0) + return -EINVAL; + + /* The timestamp unit was found to be 0.4 ns */ + ns = *ts * 10; + do_div(ns, 4); + shhwtstamps->hwtstamp = ns_to_ktime(ns); + + return 0; +} + +static void dpa_ts_tx_enable(struct net_device *dev) +{ + struct dpa_priv_s *priv = netdev_priv(dev); + struct mac_device *mac_dev = priv->mac_dev; + + if (mac_dev->fm_rtc_enable) + mac_dev->fm_rtc_enable(dev); + if (mac_dev->ptp_enable) + mac_dev->ptp_enable(mac_dev); + + priv->ts_tx_en = TRUE; +} + +static void dpa_ts_tx_disable(struct net_device *dev) +{ + struct dpa_priv_s *priv = netdev_priv(dev); + +#if 0 +/* + * the RTC might be needed by the Rx Ts, cannot disable here + * no separate ptp_disable API for Rx/Tx, cannot disable here + */ + struct mac_device *mac_dev = priv->mac_dev; + + if (mac_dev->fm_rtc_disable) + mac_dev->fm_rtc_disable(dev); + + if (mac_dev->ptp_disable) + mac_dev->ptp_disable(mac_dev); +#endif + + priv->ts_tx_en = FALSE; +} + +static void dpa_ts_rx_enable(struct net_device *dev) +{ + struct dpa_priv_s *priv = netdev_priv(dev); + struct mac_device *mac_dev = priv->mac_dev; + + if (mac_dev->fm_rtc_enable) + mac_dev->fm_rtc_enable(dev); + if (mac_dev->ptp_enable) + mac_dev->ptp_enable(mac_dev); + + priv->ts_rx_en = TRUE; +} + +static void dpa_ts_rx_disable(struct net_device *dev) +{ + struct dpa_priv_s *priv = netdev_priv(dev); + +#if 0 +/* + * the RTC might be needed by the Tx Ts, cannot disable here + * no separate ptp_disable API for Rx/Tx, cannot disable here + */ + struct mac_device *mac_dev = priv->mac_dev; + + if (mac_dev->fm_rtc_disable) + mac_dev->fm_rtc_disable(dev); + + if (mac_dev->ptp_disable) + mac_dev->ptp_disable(mac_dev); +#endif + + priv->ts_rx_en = FALSE; +} + +static int dpa_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct hwtstamp_config config; + + if (copy_from_user(&config, rq->ifr_data, sizeof(config))) + return -EFAULT; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + dpa_ts_tx_disable(dev); + break; + case HWTSTAMP_TX_ON: + dpa_ts_tx_enable(dev); + break; + default: + return -ERANGE; + } + + if (config.rx_filter == HWTSTAMP_FILTER_NONE) + dpa_ts_rx_disable(dev); + else { + dpa_ts_rx_enable(dev); + /* TS is set for all frame types, not only those requested */ + config.rx_filter = HWTSTAMP_FILTER_ALL; + } + + return copy_to_user(rq->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} +#endif /* CONFIG_FSL_DPA_TS */ + static int dpa_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { +#ifdef CONFIG_FSL_DPA_1588 struct dpa_priv_s *priv = netdev_priv(dev); +#endif int ret = 0; +/* at least one timestamping feature must be enabled to proceed */ +#if defined(CONFIG_FSL_DPA_1588) || defined(CONFIG_FSL_DPA_TS) if (!netif_running(dev)) +#endif return -EINVAL; +#ifdef CONFIG_FSL_DPA_TS + if (cmd == SIOCSHWTSTAMP) + return dpa_ts_ioctl(dev, rq, cmd); +#endif /* CONFIG_FSL_DPA_TS */ + +#ifdef CONFIG_FSL_DPA_1588 if ((cmd >= PTP_ENBL_TXTS_IOCTL) && (cmd <= PTP_CLEANUP_TS)) { if (priv->tsu && priv->tsu->valid) ret = dpa_ioctl_1588(dev, rq, cmd); else ret = -ENODEV; } +#endif return ret; } -#endif #ifndef CONFIG_DPAA_ETH_SG_SUPPORT /* @@ -1352,6 +1501,7 @@ void __hot _dpa_rx(struct net_device *net_dev, prefetch(skb_shinfo(skb)); +/* Shouldn't we store the timestamp after we validate the mtu? */ #ifdef CONFIG_FSL_DPA_1588 if (priv->tsu && priv->tsu->valid && priv->tsu->hwts_rx_en_ioctl) dpa_ptp_store_rxstamp(net_dev, skb, fd); @@ -1372,6 +1522,11 @@ void __hot _dpa_rx(struct net_device *net_dev, _dpa_process_parse_results(parse_result, fd, skb, &use_gro, &hdr_size_unused); +#ifdef CONFIG_FSL_DPA_TS + if (priv->ts_rx_en) + dpa_get_ts(priv, RX, skb_hwtstamps(skb), (void *)skbh); +#endif /* CONFIG_FSL_DPA_TS */ + if (use_gro) { gro_result_t gro_result; @@ -1451,6 +1606,8 @@ static void __hot _dpa_tx_conf(struct net_device *net_dev, { struct sk_buff *skb; + /* do we need the timestamp for the error frames? */ + if (unlikely(fd->status & FM_FD_STAT_ERRORS) != 0) { if (netif_msg_hw(priv) && net_ratelimit()) netdev_warn(net_dev, "FD status = 0x%08x\n", @@ -1459,6 +1616,8 @@ static void __hot _dpa_tx_conf(struct net_device *net_dev, percpu_priv->stats.tx_errors++; } + /* hopefully we need not get the timestamp before the hook */ + if (dpaa_eth_hooks.tx_confirm && dpaa_eth_hooks.tx_confirm(net_dev, fd, fqid) == DPAA_ETH_STOLEN) /* it's the hook that must now perform cleanup */ @@ -1497,8 +1656,13 @@ static void dpa_set_buffer_layout(struct dpa_priv_s *priv, struct fm_port *port, DPA_RX_PRIV_DATA_SIZE : DPA_TX_PRIV_DATA_SIZE); layout->parse_results = true; layout->hash_results = true; +#ifdef CONFIG_FSL_DPA_1588 if (priv && priv->tsu && priv->tsu->valid) layout->time_stamp = true; +#endif +#ifdef CONFIG_FSL_DPA_TS + layout->time_stamp = true; +#endif fm_port_get_buff_layout_ext_params(port, ¶ms); layout->manip_extra_space = params.manip_extra_space; @@ -1767,6 +1931,13 @@ static int skb_to_sg_fd(struct dpa_priv_s *priv, sg_entry->addr_hi = upper_32_bits(paddr); sg_entry->addr_lo = lower_32_bits(paddr); +#ifdef CONFIG_FSL_DPA_TS + if (unlikely(priv->ts_tx_en && + skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + } +#endif /* CONFIG_FSL_DPA_TS */ + return 0; } @@ -1836,6 +2007,15 @@ static int skb_to_contig_fd(struct dpa_priv_s *priv, } } +#ifdef CONFIG_FSL_DPA_TS + if (unlikely(priv->ts_tx_en && + skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { + /* we need the fd back to get the timestamp */ + can_recycle = false; + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + } +#endif /* CONFIG_FSL_DPA_TS */ + if (likely(can_recycle)) { /* Buffer will get recycled, setup fd accordingly */ fd->cmd |= FM_FD_CMD_FCO; @@ -1925,6 +2105,11 @@ int __hot dpa_tx(struct sk_buff *skb, struct net_device *net_dev) if (priv->tsu && priv->tsu->valid && priv->tsu->hwts_tx_en_ioctl) fd.cmd |= FM_FD_CMD_UPD; #endif +#ifdef CONFIG_FSL_DPA_TS + if (unlikely(priv->ts_tx_en && + skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) + fd.cmd |= FM_FD_CMD_UPD; +#endif /* CONFIG_FSL_DPA_TS */ /* * We have two paths here: @@ -3319,9 +3504,7 @@ static const struct net_device_ops dpa_private_ops = { .ndo_init = dpa_ndo_init, .ndo_set_features = dpa_set_features, .ndo_fix_features = dpa_fix_features, -#ifdef CONFIG_FSL_DPA_1588 .ndo_do_ioctl = dpa_ioctl, -#endif }; static const struct net_device_ops dpa_shared_ops = { @@ -3340,9 +3523,7 @@ static const struct net_device_ops dpa_shared_ops = { .ndo_init = dpa_ndo_init, .ndo_set_features = dpa_set_features, .ndo_fix_features = dpa_fix_features, -#ifdef CONFIG_FSL_DPA_1588 .ndo_do_ioctl = dpa_ioctl, -#endif }; static u32 rx_pool_channel; diff --git a/drivers/net/ethernet/freescale/dpa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpa/dpaa_eth.h index 9c946aa..abb7f08 100644 --- a/drivers/net/ethernet/freescale/dpa/dpaa_eth.h +++ b/drivers/net/ethernet/freescale/dpa/dpaa_eth.h @@ -366,7 +366,9 @@ struct dpa_priv_s { #endif uint32_t msg_enable; /* net_device message level */ +#ifdef CONFIG_FSL_DPA_1588 struct dpa_ptp_tsu *tsu; +#endif #if defined(CONFIG_DPAA_FMAN_UNIT_TESTS) /* TODO: this is temporary until pcd support is implemented in dpaa */ @@ -390,6 +392,12 @@ struct dpa_priv_s { */ u32 cgr_congested_count; } cgr_data; + +#ifdef CONFIG_FSL_DPA_TS + bool ts_tx_en; /* Tx timestamping enabled */ + bool ts_rx_en; /* Rx timestamping enabled */ +#endif /* CONFIG_FSL_DPA_TS */ + /* * Store here the needed Tx headroom for convenience and speed * (even though it can be computed based on the fields of buf_layout) @@ -630,4 +638,10 @@ static inline void _dpa_assign_wq(struct dpa_fq *fq) skb_get_queue_mapping(skb) #endif +#ifdef CONFIG_FSL_DPA_TS +/* Updates the skb shared hw timestamp from the hardware timestamp */ +int dpa_get_ts(const struct dpa_priv_s *priv, enum port_type rx_tx, + struct skb_shared_hwtstamps *shhwtstamps, const void *data); +#endif /* CONFIG_FSL_DPA_TS */ + #endif /* __DPA_H */ diff --git a/drivers/net/ethernet/freescale/dpa/dpaa_eth_sg.c b/drivers/net/ethernet/freescale/dpa/dpaa_eth_sg.c index 9aec590..b50ccab 100644 --- a/drivers/net/ethernet/freescale/dpa/dpaa_eth_sg.c +++ b/drivers/net/ethernet/freescale/dpa/dpaa_eth_sg.c @@ -249,6 +249,16 @@ struct sk_buff *_dpa_cleanup_tx_fd(const struct dpa_priv_s *priv, */ sgt = phys_to_virt(addr + dpa_fd_offset(fd)); +#ifdef CONFIG_FSL_DPA_TS + if (unlikely(priv->ts_tx_en && + skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { + struct skb_shared_hwtstamps shhwtstamps; + + dpa_get_ts(priv, TX, &shhwtstamps, (void *)skbh); + skb_tstamp_tx(skb, &shhwtstamps); + } +#endif /* CONFIG_FSL_DPA_TS */ + /* sgt[0] is from lowmem, was dma_map_single()-ed */ dma_unmap_single(dpa_bp->dev, sgt[0].addr, sgt[0].length, dma_dir); @@ -269,7 +279,19 @@ struct sk_buff *_dpa_cleanup_tx_fd(const struct dpa_priv_s *priv, /* Free separately the pages that we allocated on Tx */ free_page((unsigned long)phys_to_virt(addr)); -} + } +#ifdef CONFIG_FSL_DPA_TS + else { + /* get the timestamp for non-SG frames */ + if (unlikely(priv->ts_tx_en && + skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { + struct skb_shared_hwtstamps shhwtstamps; + + dpa_get_ts(priv, TX, &shhwtstamps, (void *)skbh); + skb_tstamp_tx(skb, &shhwtstamps); + } + } +#endif /* CONFIG_FSL_DPA_TS */ return skb; } @@ -295,6 +317,7 @@ static void __hot contig_fd_to_skb(const struct dpa_priv_s *priv, vaddr = phys_to_virt(addr); + /* do we need the timestamp for bad frames? */ #ifdef CONFIG_FSL_DPA_1588 if (priv->tsu && priv->tsu->valid && priv->tsu->hwts_rx_en_ioctl) dpa_ptp_store_rxstamp(priv->net_dev, skb, fd); @@ -304,6 +327,11 @@ static void __hot contig_fd_to_skb(const struct dpa_priv_s *priv, parse_results = (const t_FmPrsResult *)(vaddr + DPA_RX_PRIV_DATA_SIZE); _dpa_process_parse_results(parse_results, fd, skb, use_gro, ©_size); +#ifdef CONFIG_FSL_DPA_TS + if (priv->ts_rx_en) + dpa_get_ts(priv, RX, skb_hwtstamps(skb), vaddr); +#endif /* CONFIG_FSL_DPA_TS */ + tailptr = skb_put(skb, copy_size); /* Copy (at least) the headers in the linear portion */ @@ -364,6 +392,11 @@ static void __hot sg_fd_to_skb(const struct dpa_priv_s *priv, /* Inspect the parse results before anything else. */ _dpa_process_parse_results(parse_results, fd, skb, use_gro, ©_size); +#ifdef CONFIG_FSL_DPA_TS + if (priv->ts_rx_en) + dpa_get_ts(priv, RX, skb_hwtstamps(skb), vaddr); +#endif /* CONFIG_FSL_DPA_TS */ + /* * Iterate through the SGT entries and add the data buffers as * skb fragments @@ -708,6 +741,12 @@ int __hot dpa_tx(struct sk_buff *skb, struct net_device *net_dev) if (priv->tsu && priv->tsu->valid && priv->tsu->hwts_tx_en_ioctl) fd.cmd |= FM_FD_CMD_UPD; #endif +#ifdef CONFIG_FSL_DPA_TS + if (unlikely(priv->ts_tx_en && + skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) + fd.cmd |= FM_FD_CMD_UPD; + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; +#endif /* CONFIG_FSL_DPA_TS */ /* * MAX_SKB_FRAGS is larger than our DPA_SGT_MAX_ENTRIES; make sure -- cgit v0.10.2