diff options
-rw-r--r-- | drivers/net/ethernet/freescale/enetc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc.c | 158 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc.h | 12 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc_hw.h | 13 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc_pf.c | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc_vf.c | 1 |
6 files changed, 189 insertions, 6 deletions
diff --git a/drivers/net/ethernet/freescale/enetc/Kconfig b/drivers/net/ethernet/freescale/enetc/Kconfig index 8429f5c1d810..ed0d010c7cf2 100644 --- a/drivers/net/ethernet/freescale/enetc/Kconfig +++ b/drivers/net/ethernet/freescale/enetc/Kconfig @@ -29,3 +29,13 @@ config FSL_ENETC_PTP_CLOCK packets using the SO_TIMESTAMPING API. If compiled as module (M), the module name is fsl-enetc-ptp. + +config FSL_ENETC_HW_TIMESTAMPING + bool "ENETC hardware timestamping support" + depends on FSL_ENETC || FSL_ENETC_VF + help + Enable hardware timestamping support on the Ethernet packets + using the SO_TIMESTAMPING API. Because the RX BD ring dynamic + allocation has not been supported and it is too expensive to use + extended RX BDs if timestamping is not used, this option enables + extended RX BDs in order to support hardware timestamping. diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index 491475d87736..d2ace299bed0 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -13,7 +13,8 @@ #define ENETC_MAX_SKB_FRAGS 13 #define ENETC_TXBDS_MAX_NEEDED ENETC_TXBDS_NEEDED(ENETC_MAX_SKB_FRAGS + 1) -static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb); +static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb, + int active_offloads); netdev_tx_t enetc_xmit(struct sk_buff *skb, struct net_device *ndev) { @@ -33,7 +34,7 @@ netdev_tx_t enetc_xmit(struct sk_buff *skb, struct net_device *ndev) return NETDEV_TX_BUSY; } - count = enetc_map_tx_buffs(tx_ring, skb); + count = enetc_map_tx_buffs(tx_ring, skb, priv->active_offloads); if (unlikely(!count)) goto drop_packet_err; @@ -105,7 +106,8 @@ static void enetc_free_tx_skb(struct enetc_bdr *tx_ring, } } -static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb) +static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb, + int active_offloads) { struct enetc_tx_swbd *tx_swbd; struct skb_frag_struct *frag; @@ -137,7 +139,10 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb) count++; do_vlan = skb_vlan_tag_present(skb); - do_tstamp = skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP; + do_tstamp = (active_offloads & ENETC_F_TX_TSTAMP) && + (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP); + tx_swbd->do_tstamp = do_tstamp; + tx_swbd->check_wb = tx_swbd->do_tstamp; if (do_vlan || do_tstamp) flags |= ENETC_TXBD_FLAGS_EX; @@ -299,24 +304,69 @@ static int enetc_bd_ready_count(struct enetc_bdr *tx_ring, int ci) return pi >= ci ? pi - ci : tx_ring->bd_count - ci + pi; } +static void enetc_get_tx_tstamp(struct enetc_hw *hw, union enetc_tx_bd *txbd, + u64 *tstamp) +{ + u32 lo, hi; + + lo = enetc_rd(hw, ENETC_SICTR0); + hi = enetc_rd(hw, ENETC_SICTR1); + if (lo <= txbd->wb.tstamp) + hi -= 1; + *tstamp = (u64)hi << 32 | txbd->wb.tstamp; +} + +static void enetc_tstamp_tx(struct sk_buff *skb, u64 tstamp) +{ + struct skb_shared_hwtstamps shhwtstamps; + + if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) { + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ns_to_ktime(tstamp); + skb_tstamp_tx(skb, &shhwtstamps); + } +} + static bool enetc_clean_tx_ring(struct enetc_bdr *tx_ring, int napi_budget) { struct net_device *ndev = tx_ring->ndev; int tx_frm_cnt = 0, tx_byte_cnt = 0; struct enetc_tx_swbd *tx_swbd; int i, bds_to_clean; + bool do_tstamp; + u64 tstamp = 0; i = tx_ring->next_to_clean; tx_swbd = &tx_ring->tx_swbd[i]; bds_to_clean = enetc_bd_ready_count(tx_ring, i); + do_tstamp = false; + while (bds_to_clean && tx_frm_cnt < ENETC_DEFAULT_TX_WORK) { bool is_eof = !!tx_swbd->skb; + if (unlikely(tx_swbd->check_wb)) { + struct enetc_ndev_priv *priv = netdev_priv(ndev); + union enetc_tx_bd *txbd; + + txbd = ENETC_TXBD(*tx_ring, i); + + if (txbd->flags & ENETC_TXBD_FLAGS_W && + tx_swbd->do_tstamp) { + enetc_get_tx_tstamp(&priv->si->hw, txbd, + &tstamp); + do_tstamp = true; + } + } + if (likely(tx_swbd->dma)) enetc_unmap_tx_buff(tx_ring, tx_swbd); if (is_eof) { + if (unlikely(do_tstamp)) { + enetc_tstamp_tx(tx_swbd->skb, tstamp); + do_tstamp = false; + } napi_consume_skb(tx_swbd->skb, napi_budget); tx_swbd->skb = NULL; } @@ -425,10 +475,37 @@ static int enetc_refill_rx_ring(struct enetc_bdr *rx_ring, const int buff_cnt) return j; } +#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING +static void enetc_get_rx_tstamp(struct net_device *ndev, + union enetc_rx_bd *rxbd, + struct sk_buff *skb) +{ + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct enetc_hw *hw = &priv->si->hw; + u32 lo, hi; + u64 tstamp; + + if (rxbd->r.flags & ENETC_RXBD_FLAG_TSTMP) { + lo = enetc_rd(hw, ENETC_SICTR0); + hi = enetc_rd(hw, ENETC_SICTR1); + if (lo <= rxbd->r.tstamp) + hi -= 1; + + tstamp = (u64)hi << 32 | rxbd->r.tstamp; + memset(shhwtstamps, 0, sizeof(*shhwtstamps)); + shhwtstamps->hwtstamp = ns_to_ktime(tstamp); + } +} +#endif + static void enetc_get_offloads(struct enetc_bdr *rx_ring, union enetc_rx_bd *rxbd, struct sk_buff *skb) { - /* TODO: add tstamp, hashing */ +#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING + struct enetc_ndev_priv *priv = netdev_priv(rx_ring->ndev); +#endif + /* TODO: hashing */ if (rx_ring->ndev->features & NETIF_F_RXCSUM) { u16 inet_csum = le16_to_cpu(rxbd->r.inet_csum); @@ -442,6 +519,10 @@ static void enetc_get_offloads(struct enetc_bdr *rx_ring, if (le16_to_cpu(rxbd->r.flags) & ENETC_RXBD_FLAG_VLAN) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), le16_to_cpu(rxbd->r.vlan_opt)); +#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING + if (priv->active_offloads & ENETC_F_RX_TSTAMP) + enetc_get_rx_tstamp(rx_ring->ndev, rxbd, skb); +#endif } static void enetc_process_skb(struct enetc_bdr *rx_ring, @@ -1074,6 +1155,9 @@ static void enetc_setup_rxbdr(struct enetc_hw *hw, struct enetc_bdr *rx_ring) enetc_rxbdr_wr(hw, idx, ENETC_RBICIR0, ENETC_RBICIR0_ICEN | 0x1); rbmr = ENETC_RBMR_EN; +#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING + rbmr |= ENETC_RBMR_BDS; +#endif if (rx_ring->ndev->features & NETIF_F_HW_VLAN_CTAG_RX) rbmr |= ENETC_RBMR_VTE; @@ -1396,6 +1480,70 @@ int enetc_set_features(struct net_device *ndev, return 0; } +#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING +static int enetc_hwtstamp_set(struct net_device *ndev, struct ifreq *ifr) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct hwtstamp_config config; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + priv->active_offloads &= ~ENETC_F_TX_TSTAMP; + break; + case HWTSTAMP_TX_ON: + priv->active_offloads |= ENETC_F_TX_TSTAMP; + break; + default: + return -ERANGE; + } + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + priv->active_offloads &= ~ENETC_F_RX_TSTAMP; + break; + default: + priv->active_offloads |= ENETC_F_RX_TSTAMP; + config.rx_filter = HWTSTAMP_FILTER_ALL; + } + + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} + +static int enetc_hwtstamp_get(struct net_device *ndev, struct ifreq *ifr) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct hwtstamp_config config; + + config.flags = 0; + + if (priv->active_offloads & ENETC_F_TX_TSTAMP) + config.tx_type = HWTSTAMP_TX_ON; + else + config.tx_type = HWTSTAMP_TX_OFF; + + config.rx_filter = (priv->active_offloads & ENETC_F_RX_TSTAMP) ? + HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE; + + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} +#endif + +int enetc_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) +{ +#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING + if (cmd == SIOCSHWTSTAMP) + return enetc_hwtstamp_set(ndev, rq); + if (cmd == SIOCGHWTSTAMP) + return enetc_hwtstamp_get(ndev, rq); +#endif + return -EINVAL; +} + int enetc_alloc_msix(struct enetc_ndev_priv *priv) { struct pci_dev *pdev = priv->si->pdev; diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h index b274135c5103..281bb4368b98 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.h +++ b/drivers/net/ethernet/freescale/enetc/enetc.h @@ -21,7 +21,9 @@ struct enetc_tx_swbd { struct sk_buff *skb; dma_addr_t dma; u16 len; - u16 is_dma_page; + u8 is_dma_page:1; + u8 check_wb:1; + u8 do_tstamp:1; }; #define ENETC_RX_MAXFRM_SIZE ENETC_MAC_MAXFRM_SIZE @@ -167,6 +169,12 @@ struct enetc_cls_rule { #define ENETC_MAX_BDR_INT 2 /* fixed to max # of available cpus */ +/* TODO: more hardware offloads */ +enum enetc_active_offloads { + ENETC_F_RX_TSTAMP = BIT(0), + ENETC_F_TX_TSTAMP = BIT(1), +}; + struct enetc_ndev_priv { struct net_device *ndev; struct device *dev; /* dma-mapping device */ @@ -178,6 +186,7 @@ struct enetc_ndev_priv { u16 rx_bd_count, tx_bd_count; u16 msg_enable; + int active_offloads; struct enetc_bdr *tx_ring[16]; struct enetc_bdr *rx_ring[16]; @@ -216,6 +225,7 @@ netdev_tx_t enetc_xmit(struct sk_buff *skb, struct net_device *ndev); struct net_device_stats *enetc_get_stats(struct net_device *ndev); int enetc_set_features(struct net_device *ndev, netdev_features_t features); +int enetc_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd); /* ethtool */ void enetc_set_ethtool_ops(struct net_device *ndev); diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h index df8eb8882d92..6559cef4b07d 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h @@ -361,6 +361,12 @@ union enetc_tx_bd { u8 e_flags; u8 flags; } ext; /* Tx BD extension */ + struct { + __le32 tstamp; + u8 reserved[10]; + u8 status; + u8 flags; + } wb; /* writeback descriptor */ }; #define ENETC_TXBD_FLAGS_L4CS BIT(0) @@ -399,6 +405,9 @@ union enetc_rx_bd { struct { __le64 addr; u8 reserved[8]; +#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING + u8 reserved1[16]; +#endif } w; struct { __le16 inet_csum; @@ -413,6 +422,10 @@ union enetc_rx_bd { }; __le32 lstatus; }; +#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING + __le32 tstamp; + u8 reserved[12]; +#endif } r; }; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index 78287c517095..d78ec8d43c39 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -702,6 +702,7 @@ static const struct net_device_ops enetc_ndev_ops = { .ndo_set_vf_vlan = enetc_pf_set_vf_vlan, .ndo_set_vf_spoofchk = enetc_pf_set_vf_spoofchk, .ndo_set_features = enetc_pf_set_features, + .ndo_do_ioctl = enetc_ioctl, }; static void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev, diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c index 72c3ea887bcf..17f72644c5a1 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c @@ -111,6 +111,7 @@ static const struct net_device_ops enetc_ndev_ops = { .ndo_get_stats = enetc_get_stats, .ndo_set_mac_address = enetc_vf_set_mac_addr, .ndo_set_features = enetc_vf_set_features, + .ndo_do_ioctl = enetc_ioctl, }; static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev, |