summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/freescale/dpa/Kconfig5
-rw-r--r--drivers/net/ethernet/freescale/dpa/dpaa_eth-common.h2
-rw-r--r--drivers/net/ethernet/freescale/dpa/dpaa_eth.c203
-rw-r--r--drivers/net/ethernet/freescale/dpa/dpaa_eth.h14
-rw-r--r--drivers/net/ethernet/freescale/dpa/dpaa_eth_sg.c41
5 files changed, 252 insertions, 13 deletions
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 <linux/udp.h> /* struct udphdr */
#include <linux/tcp.h> /* struct tcphdr */
#include <linux/net.h> /* net_ratelimit() */
+#include <linux/net_tstamp.h> /* struct hwtstamp_config */
#include <linux/if_ether.h> /* ETH_P_IP and ETH_P_IPV6 */
#include <linux/highmem.h>
#include <linux/percpu.h>
@@ -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, &params);
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, &copy_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, &copy_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