Skip to content

Commit

Permalink
Merge branch 'ENETC-support-hardware-timestamping'
Browse files Browse the repository at this point in the history
Y.b. Lu says:

====================
ENETC: support hardware timestamping

This patch-set is to support hardware timestamping for ENETC
and also to add ENETC 1588 timer device tree node for ls1028a.

Because the ENETC RX BD ring dynamic allocation has not been
supported and it is too expensive to use extended RX BDs
if timestamping is not used, a Kconfig option is used to
enable extended RX BDs in order to support hardware
timestamping. This option will be removed once RX BD
ring dynamic allocation is implemented.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed May 24, 2019
2 parents dfb569f + 4940100 commit daeceb2
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 6 deletions.
1 change: 1 addition & 0 deletions Documentation/devicetree/bindings/ptp/ptp-qoriq.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ General Properties:

- compatible Should be "fsl,etsec-ptp" for eTSEC
Should be "fsl,fman-ptp-timer" for DPAA FMan
Should be "fsl,enetc-ptp" for ENETC
- reg Offset and length of the register set for the device
- interrupts There should be at least two interrupts. Some devices
have as many as four PTP related interrupts.
Expand Down
6 changes: 6 additions & 0 deletions arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,12 @@
compatible = "fsl,enetc";
reg = <0x000100 0 0 0 0>;
};
ethernet@0,4 {
compatible = "fsl,enetc-ptp";
reg = <0x000400 0 0 0 0>;
clocks = <&clockgen 4 0>;
little-endian;
};
};
};
};
10 changes: 10 additions & 0 deletions drivers/net/ethernet/freescale/enetc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
158 changes: 153 additions & 5 deletions drivers/net/ethernet/freescale/enetc/enetc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);

Expand All @@ -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,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
15 changes: 14 additions & 1 deletion drivers/net/ethernet/freescale/enetc/enetc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand All @@ -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];
Expand All @@ -200,6 +209,9 @@ struct enetc_msg_cmd_set_primary_mac {

#define ENETC_CBDR_TIMEOUT 1000 /* usecs */

/* PTP driver exports */
extern int enetc_phc_index;

/* SI common */
int enetc_pci_probe(struct pci_dev *pdev, const char *name, int sizeof_priv);
void enetc_pci_remove(struct pci_dev *pdev);
Expand All @@ -216,6 +228,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);

Expand Down
31 changes: 31 additions & 0 deletions drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,35 @@ static void enetc_get_ringparam(struct net_device *ndev,
}
}

static int enetc_get_ts_info(struct net_device *ndev,
struct ethtool_ts_info *info)
{
int *phc_idx;

phc_idx = symbol_get(enetc_phc_index);
if (phc_idx) {
info->phc_index = *phc_idx;
symbol_put(enetc_phc_index);
} else {
info->phc_index = -1;
}

#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING
info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;

info->tx_types = (1 << HWTSTAMP_TX_OFF) |
(1 << HWTSTAMP_TX_ON);
info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
(1 << HWTSTAMP_FILTER_ALL);
#else
info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE;
#endif
return 0;
}

static const struct ethtool_ops enetc_pf_ethtool_ops = {
.get_regs_len = enetc_get_reglen,
.get_regs = enetc_get_regs,
Expand All @@ -571,6 +600,7 @@ static const struct ethtool_ops enetc_pf_ethtool_ops = {
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings,
.get_link = ethtool_op_get_link,
.get_ts_info = enetc_get_ts_info,
};

static const struct ethtool_ops enetc_vf_ethtool_ops = {
Expand All @@ -586,6 +616,7 @@ static const struct ethtool_ops enetc_vf_ethtool_ops = {
.set_rxfh = enetc_set_rxfh,
.get_ringparam = enetc_get_ringparam,
.get_link = ethtool_op_get_link,
.get_ts_info = enetc_get_ts_info,
};

void enetc_set_ethtool_ops(struct net_device *ndev)
Expand Down
Loading

0 comments on commit daeceb2

Please sign in to comment.