Commit a9c12174 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-renesas-rswitch-improve-tx-timestamp-accuracy'

Yoshihiro Shimoda says:

====================
net: renesas: rswitch: Improve TX timestamp accuracy

The patch [[123]/4] are minor refacoring for readability.
The patch [4/4] is for improving TX timestamp accuracy.
To improve the accuracy, it requires refactoring so that this is not
a fixed patch.

v2: https://lore.kernel.org/all/20230208235721.2336249-1-yoshihiro.shimoda.uh@renesas.com/
v1: https://lore.kernel.org/all/20230208073445.2317192-1-yoshihiro.shimoda.uh@renesas.com/
====================

Link: https://lore.kernel.org/r/20230209081741.2536034-1-yoshihiro.shimoda.uh@renesas.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 183514f7 33f5d733
Loading
Loading
Loading
Loading
+213 −82
Original line number Diff line number Diff line
@@ -123,13 +123,6 @@ static void rswitch_fwd_init(struct rswitch_private *priv)
	iowrite32(GENMASK(RSWITCH_NUM_PORTS - 1, 0), priv->addr + FWPBFC(priv->gwca.index));
}

/* gPTP timer (gPTP) */
static void rswitch_get_timestamp(struct rswitch_private *priv,
				  struct timespec64 *ts)
{
	priv->ptp_priv->info.gettime64(&priv->ptp_priv->info, ts);
}

/* Gateway CPU agent block (GWCA) */
static int rswitch_gwca_change_mode(struct rswitch_private *priv,
				    enum rswitch_gwca_mode mode)
@@ -240,7 +233,7 @@ static int rswitch_get_num_cur_queues(struct rswitch_gwca_queue *gq)

static bool rswitch_is_queue_rxed(struct rswitch_gwca_queue *gq)
{
	struct rswitch_ext_ts_desc *desc = &gq->ts_ring[gq->dirty];
	struct rswitch_ext_ts_desc *desc = &gq->rx_ring[gq->dirty];

	if ((desc->desc.die_dt & DT_MASK) != DT_FEMPTY)
		return true;
@@ -280,36 +273,43 @@ static void rswitch_gwca_queue_free(struct net_device *ndev,
{
	int i;

	if (gq->gptp) {
	if (!gq->dir_tx) {
		dma_free_coherent(ndev->dev.parent,
				  sizeof(struct rswitch_ext_ts_desc) *
				  (gq->ring_size + 1), gq->ts_ring, gq->ring_dma);
		gq->ts_ring = NULL;
	} else {
		dma_free_coherent(ndev->dev.parent,
				  sizeof(struct rswitch_ext_desc) *
				  (gq->ring_size + 1), gq->ring, gq->ring_dma);
		gq->ring = NULL;
	}
				  (gq->ring_size + 1), gq->rx_ring, gq->ring_dma);
		gq->rx_ring = NULL;

	if (!gq->dir_tx) {
		for (i = 0; i < gq->ring_size; i++)
			dev_kfree_skb(gq->skbs[i]);
	} else {
		dma_free_coherent(ndev->dev.parent,
				  sizeof(struct rswitch_ext_desc) *
				  (gq->ring_size + 1), gq->tx_ring, gq->ring_dma);
		gq->tx_ring = NULL;
	}

	kfree(gq->skbs);
	gq->skbs = NULL;
}

static void rswitch_gwca_ts_queue_free(struct rswitch_private *priv)
{
	struct rswitch_gwca_queue *gq = &priv->gwca.ts_queue;

	dma_free_coherent(&priv->pdev->dev,
			  sizeof(struct rswitch_ts_desc) * (gq->ring_size + 1),
			  gq->ts_ring, gq->ring_dma);
	gq->ts_ring = NULL;
}

static int rswitch_gwca_queue_alloc(struct net_device *ndev,
				    struct rswitch_private *priv,
				    struct rswitch_gwca_queue *gq,
				    bool dir_tx, bool gptp, int ring_size)
				    bool dir_tx, int ring_size)
{
	int i, bit;

	gq->dir_tx = dir_tx;
	gq->gptp = gptp;
	gq->ring_size = ring_size;
	gq->ndev = ndev;

@@ -317,18 +317,19 @@ static int rswitch_gwca_queue_alloc(struct net_device *ndev,
	if (!gq->skbs)
		return -ENOMEM;

	if (!dir_tx)
	if (!dir_tx) {
		rswitch_gwca_queue_alloc_skb(gq, 0, gq->ring_size);

	if (gptp)
		gq->ts_ring = dma_alloc_coherent(ndev->dev.parent,
		gq->rx_ring = dma_alloc_coherent(ndev->dev.parent,
						 sizeof(struct rswitch_ext_ts_desc) *
						 (gq->ring_size + 1), &gq->ring_dma, GFP_KERNEL);
	else
		gq->ring = dma_alloc_coherent(ndev->dev.parent,
	} else {
		gq->tx_ring = dma_alloc_coherent(ndev->dev.parent,
						 sizeof(struct rswitch_ext_desc) *
						 (gq->ring_size + 1), &gq->ring_dma, GFP_KERNEL);
	if (!gq->ts_ring && !gq->ring)
	}

	if (!gq->rx_ring && !gq->tx_ring)
		goto out;

	i = gq->index / 32;
@@ -346,6 +347,17 @@ static int rswitch_gwca_queue_alloc(struct net_device *ndev,
	return -ENOMEM;
}

static int rswitch_gwca_ts_queue_alloc(struct rswitch_private *priv)
{
	struct rswitch_gwca_queue *gq = &priv->gwca.ts_queue;

	gq->ring_size = TS_RING_SIZE;
	gq->ts_ring = dma_alloc_coherent(&priv->pdev->dev,
					 sizeof(struct rswitch_ts_desc) *
					 (gq->ring_size + 1), &gq->ring_dma, GFP_KERNEL);
	return !gq->ts_ring ? -ENOMEM : 0;
}

static void rswitch_desc_set_dptr(struct rswitch_desc *desc, dma_addr_t addr)
{
	desc->dptrl = cpu_to_le32(lower_32_bits(addr));
@@ -361,14 +373,14 @@ static int rswitch_gwca_queue_format(struct net_device *ndev,
				     struct rswitch_private *priv,
				     struct rswitch_gwca_queue *gq)
{
	int tx_ring_size = sizeof(struct rswitch_ext_desc) * gq->ring_size;
	int ring_size = sizeof(struct rswitch_ext_desc) * gq->ring_size;
	struct rswitch_ext_desc *desc;
	struct rswitch_desc *linkfix;
	dma_addr_t dma_addr;
	int i;

	memset(gq->ring, 0, tx_ring_size);
	for (i = 0, desc = gq->ring; i < gq->ring_size; i++, desc++) {
	memset(gq->tx_ring, 0, ring_size);
	for (i = 0, desc = gq->tx_ring; i < gq->ring_size; i++, desc++) {
		if (!gq->dir_tx) {
			dma_addr = dma_map_single(ndev->dev.parent,
						  gq->skbs[i]->data, PKT_BUF_SZ,
@@ -386,7 +398,7 @@ static int rswitch_gwca_queue_format(struct net_device *ndev,
	rswitch_desc_set_dptr(&desc->desc, gq->ring_dma);
	desc->desc.die_dt = DT_LINKFIX;

	linkfix = &priv->linkfix_table[gq->index];
	linkfix = &priv->gwca.linkfix_table[gq->index];
	linkfix->die_dt = DT_LINKFIX;
	rswitch_desc_set_dptr(linkfix, gq->ring_dma);

@@ -397,7 +409,7 @@ static int rswitch_gwca_queue_format(struct net_device *ndev,

err:
	if (!gq->dir_tx) {
		for (i--, desc = gq->ring; i >= 0; i--, desc++) {
		for (i--, desc = gq->tx_ring; i >= 0; i--, desc++) {
			dma_addr = rswitch_desc_get_dptr(&desc->desc);
			dma_unmap_single(ndev->dev.parent, dma_addr, PKT_BUF_SZ,
					 DMA_FROM_DEVICE);
@@ -407,7 +419,21 @@ static int rswitch_gwca_queue_format(struct net_device *ndev,
	return -ENOMEM;
}

static int rswitch_gwca_queue_ts_fill(struct net_device *ndev,
static void rswitch_gwca_ts_queue_fill(struct rswitch_private *priv,
				       int start_index, int num)
{
	struct rswitch_gwca_queue *gq = &priv->gwca.ts_queue;
	struct rswitch_ts_desc *desc;
	int i, index;

	for (i = 0; i < num; i++) {
		index = (i + start_index) % gq->ring_size;
		desc = &gq->ts_ring[index];
		desc->desc.die_dt = DT_FEMPTY_ND | DIE;
	}
}

static int rswitch_gwca_queue_ext_ts_fill(struct net_device *ndev,
					  struct rswitch_gwca_queue *gq,
					  int start_index, int num)
{
@@ -418,7 +444,7 @@ static int rswitch_gwca_queue_ts_fill(struct net_device *ndev,

	for (i = 0; i < num; i++) {
		index = (i + start_index) % gq->ring_size;
		desc = &gq->ts_ring[index];
		desc = &gq->rx_ring[index];
		if (!gq->dir_tx) {
			dma_addr = dma_map_single(ndev->dev.parent,
						  gq->skbs[index]->data, PKT_BUF_SZ,
@@ -442,7 +468,7 @@ static int rswitch_gwca_queue_ts_fill(struct net_device *ndev,
	if (!gq->dir_tx) {
		for (i--; i >= 0; i--) {
			index = (i + start_index) % gq->ring_size;
			desc = &gq->ts_ring[index];
			desc = &gq->rx_ring[index];
			dma_addr = rswitch_desc_get_dptr(&desc->desc);
			dma_unmap_single(ndev->dev.parent, dma_addr, PKT_BUF_SZ,
					 DMA_FROM_DEVICE);
@@ -452,25 +478,25 @@ static int rswitch_gwca_queue_ts_fill(struct net_device *ndev,
	return -ENOMEM;
}

static int rswitch_gwca_queue_ts_format(struct net_device *ndev,
static int rswitch_gwca_queue_ext_ts_format(struct net_device *ndev,
					    struct rswitch_private *priv,
					    struct rswitch_gwca_queue *gq)
{
	int tx_ts_ring_size = sizeof(struct rswitch_ext_ts_desc) * gq->ring_size;
	int ring_size = sizeof(struct rswitch_ext_ts_desc) * gq->ring_size;
	struct rswitch_ext_ts_desc *desc;
	struct rswitch_desc *linkfix;
	int err;

	memset(gq->ts_ring, 0, tx_ts_ring_size);
	err = rswitch_gwca_queue_ts_fill(ndev, gq, 0, gq->ring_size);
	memset(gq->rx_ring, 0, ring_size);
	err = rswitch_gwca_queue_ext_ts_fill(ndev, gq, 0, gq->ring_size);
	if (err < 0)
		return err;

	desc = &gq->ts_ring[gq->ring_size];	/* Last */
	desc = &gq->rx_ring[gq->ring_size];	/* Last */
	rswitch_desc_set_dptr(&desc->desc, gq->ring_dma);
	desc->desc.die_dt = DT_LINKFIX;

	linkfix = &priv->linkfix_table[gq->index];
	linkfix = &priv->gwca.linkfix_table[gq->index];
	linkfix->die_dt = DT_LINKFIX;
	rswitch_desc_set_dptr(linkfix, gq->ring_dma);

@@ -480,28 +506,31 @@ static int rswitch_gwca_queue_ts_format(struct net_device *ndev,
	return 0;
}

static int rswitch_gwca_desc_alloc(struct rswitch_private *priv)
static int rswitch_gwca_linkfix_alloc(struct rswitch_private *priv)
{
	int i, num_queues = priv->gwca.num_queues;
	struct rswitch_gwca *gwca = &priv->gwca;
	struct device *dev = &priv->pdev->dev;

	priv->linkfix_table_size = sizeof(struct rswitch_desc) * num_queues;
	priv->linkfix_table = dma_alloc_coherent(dev, priv->linkfix_table_size,
						 &priv->linkfix_table_dma, GFP_KERNEL);
	if (!priv->linkfix_table)
	gwca->linkfix_table_size = sizeof(struct rswitch_desc) * num_queues;
	gwca->linkfix_table = dma_alloc_coherent(dev, gwca->linkfix_table_size,
						 &gwca->linkfix_table_dma, GFP_KERNEL);
	if (!gwca->linkfix_table)
		return -ENOMEM;
	for (i = 0; i < num_queues; i++)
		priv->linkfix_table[i].die_dt = DT_EOS;
		gwca->linkfix_table[i].die_dt = DT_EOS;

	return 0;
}

static void rswitch_gwca_desc_free(struct rswitch_private *priv)
static void rswitch_gwca_linkfix_free(struct rswitch_private *priv)
{
	if (priv->linkfix_table)
		dma_free_coherent(&priv->pdev->dev, priv->linkfix_table_size,
				  priv->linkfix_table, priv->linkfix_table_dma);
	priv->linkfix_table = NULL;
	struct rswitch_gwca *gwca = &priv->gwca;

	if (gwca->linkfix_table)
		dma_free_coherent(&priv->pdev->dev, gwca->linkfix_table_size,
				  gwca->linkfix_table, gwca->linkfix_table_dma);
	gwca->linkfix_table = NULL;
}

static struct rswitch_gwca_queue *rswitch_gwca_get(struct rswitch_private *priv)
@@ -536,8 +565,7 @@ static int rswitch_txdmac_alloc(struct net_device *ndev)
	if (!rdev->tx_queue)
		return -EBUSY;

	err = rswitch_gwca_queue_alloc(ndev, priv, rdev->tx_queue, true, false,
				       TX_RING_SIZE);
	err = rswitch_gwca_queue_alloc(ndev, priv, rdev->tx_queue, true, TX_RING_SIZE);
	if (err < 0) {
		rswitch_gwca_put(priv, rdev->tx_queue);
		return err;
@@ -571,8 +599,7 @@ static int rswitch_rxdmac_alloc(struct net_device *ndev)
	if (!rdev->rx_queue)
		return -EBUSY;

	err = rswitch_gwca_queue_alloc(ndev, priv, rdev->rx_queue, false, true,
				       RX_RING_SIZE);
	err = rswitch_gwca_queue_alloc(ndev, priv, rdev->rx_queue, false, RX_RING_SIZE);
	if (err < 0) {
		rswitch_gwca_put(priv, rdev->rx_queue);
		return err;
@@ -594,7 +621,7 @@ static int rswitch_rxdmac_init(struct rswitch_private *priv, int index)
	struct rswitch_device *rdev = priv->rdev[index];
	struct net_device *ndev = rdev->ndev;

	return rswitch_gwca_queue_ts_format(ndev, priv, rdev->rx_queue);
	return rswitch_gwca_queue_ext_ts_format(ndev, priv, rdev->rx_queue);
}

static int rswitch_gwca_hw_init(struct rswitch_private *priv)
@@ -617,8 +644,11 @@ static int rswitch_gwca_hw_init(struct rswitch_private *priv)

	iowrite32(GWVCC_VEM_SC_TAG, priv->addr + GWVCC);
	iowrite32(0, priv->addr + GWTTFC);
	iowrite32(lower_32_bits(priv->linkfix_table_dma), priv->addr + GWDCBAC1);
	iowrite32(upper_32_bits(priv->linkfix_table_dma), priv->addr + GWDCBAC0);
	iowrite32(lower_32_bits(priv->gwca.linkfix_table_dma), priv->addr + GWDCBAC1);
	iowrite32(upper_32_bits(priv->gwca.linkfix_table_dma), priv->addr + GWDCBAC0);
	iowrite32(lower_32_bits(priv->gwca.ts_queue.ring_dma), priv->addr + GWTDCAC10);
	iowrite32(upper_32_bits(priv->gwca.ts_queue.ring_dma), priv->addr + GWTDCAC00);
	iowrite32(GWCA_TS_IRQ_BIT, priv->addr + GWTSDCC0);
	rswitch_gwca_set_rate_limit(priv, priv->gwca.speed);

	for (i = 0; i < RSWITCH_NUM_PORTS; i++) {
@@ -675,7 +705,7 @@ static bool rswitch_rx(struct net_device *ndev, int *quota)
	boguscnt = min_t(int, gq->ring_size, *quota);
	limit = boguscnt;

	desc = &gq->ts_ring[gq->cur];
	desc = &gq->rx_ring[gq->cur];
	while ((desc->desc.die_dt & DT_MASK) != DT_FEMPTY) {
		if (--boguscnt < 0)
			break;
@@ -703,14 +733,14 @@ static bool rswitch_rx(struct net_device *ndev, int *quota)
		rdev->ndev->stats.rx_bytes += pkt_len;

		gq->cur = rswitch_next_queue_index(gq, true, 1);
		desc = &gq->ts_ring[gq->cur];
		desc = &gq->rx_ring[gq->cur];
	}

	num = rswitch_get_num_cur_queues(gq);
	ret = rswitch_gwca_queue_alloc_skb(gq, gq->dirty, num);
	if (ret < 0)
		goto err;
	ret = rswitch_gwca_queue_ts_fill(ndev, gq, gq->dirty, num);
	ret = rswitch_gwca_queue_ext_ts_fill(ndev, gq, gq->dirty, num);
	if (ret < 0)
		goto err;
	gq->dirty = rswitch_next_queue_index(gq, false, num);
@@ -737,7 +767,7 @@ static int rswitch_tx_free(struct net_device *ndev, bool free_txed_only)

	for (; rswitch_get_num_cur_queues(gq) > 0;
	     gq->dirty = rswitch_next_queue_index(gq, false, 1)) {
		desc = &gq->ring[gq->dirty];
		desc = &gq->tx_ring[gq->dirty];
		if (free_txed_only && (desc->desc.die_dt & DT_MASK) != DT_FEMPTY)
			break;

@@ -745,15 +775,6 @@ static int rswitch_tx_free(struct net_device *ndev, bool free_txed_only)
		size = le16_to_cpu(desc->desc.info_ds) & TX_DS;
		skb = gq->skbs[gq->dirty];
		if (skb) {
			if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
				struct skb_shared_hwtstamps shhwtstamps;
				struct timespec64 ts;

				rswitch_get_timestamp(rdev->priv, &ts);
				memset(&shhwtstamps, 0, sizeof(shhwtstamps));
				shhwtstamps.hwtstamp = timespec64_to_ktime(ts);
				skb_tstamp_tx(skb, &shhwtstamps);
			}
			dma_addr = rswitch_desc_get_dptr(&desc->desc);
			dma_unmap_single(ndev->dev.parent, dma_addr,
					 size, DMA_TO_DEVICE);
@@ -879,6 +900,73 @@ static int rswitch_gwca_request_irqs(struct rswitch_private *priv)
	return 0;
}

static void rswitch_ts(struct rswitch_private *priv)
{
	struct rswitch_gwca_queue *gq = &priv->gwca.ts_queue;
	struct rswitch_gwca_ts_info *ts_info, *ts_info2;
	struct skb_shared_hwtstamps shhwtstamps;
	struct rswitch_ts_desc *desc;
	struct timespec64 ts;
	u32 tag, port;
	int num;

	desc = &gq->ts_ring[gq->cur];
	while ((desc->desc.die_dt & DT_MASK) != DT_FEMPTY_ND) {
		dma_rmb();

		port = TS_DESC_DPN(__le32_to_cpu(desc->desc.dptrl));
		tag = TS_DESC_TSUN(__le32_to_cpu(desc->desc.dptrl));

		list_for_each_entry_safe(ts_info, ts_info2, &priv->gwca.ts_info_list, list) {
			if (!(ts_info->port == port && ts_info->tag == tag))
				continue;

			memset(&shhwtstamps, 0, sizeof(shhwtstamps));
			ts.tv_sec = __le32_to_cpu(desc->ts_sec);
			ts.tv_nsec = __le32_to_cpu(desc->ts_nsec & cpu_to_le32(0x3fffffff));
			shhwtstamps.hwtstamp = timespec64_to_ktime(ts);
			skb_tstamp_tx(ts_info->skb, &shhwtstamps);
			dev_consume_skb_irq(ts_info->skb);
			list_del(&ts_info->list);
			kfree(ts_info);
			break;
		}

		gq->cur = rswitch_next_queue_index(gq, true, 1);
		desc = &gq->ts_ring[gq->cur];
	}

	num = rswitch_get_num_cur_queues(gq);
	rswitch_gwca_ts_queue_fill(priv, gq->dirty, num);
	gq->dirty = rswitch_next_queue_index(gq, false, num);
}

static irqreturn_t rswitch_gwca_ts_irq(int irq, void *dev_id)
{
	struct rswitch_private *priv = dev_id;

	if (ioread32(priv->addr + GWTSDIS) & GWCA_TS_IRQ_BIT) {
		iowrite32(GWCA_TS_IRQ_BIT, priv->addr + GWTSDIS);
		rswitch_ts(priv);

		return IRQ_HANDLED;
	}

	return IRQ_NONE;
}

static int rswitch_gwca_ts_request_irqs(struct rswitch_private *priv)
{
	int irq;

	irq = platform_get_irq_byname(priv->pdev, GWCA_TS_IRQ_RESOURCE_NAME);
	if (irq < 0)
		return irq;

	return devm_request_irq(&priv->pdev->dev, irq, rswitch_gwca_ts_irq,
				0, GWCA_TS_IRQ_NAME, priv);
}

/* Ethernet TSN Agent block (ETHA) and Ethernet MAC IP block (RMAC) */
static int rswitch_etha_change_mode(struct rswitch_etha *etha,
				    enum rswitch_etha_mode mode)
@@ -1349,15 +1437,28 @@ static int rswitch_open(struct net_device *ndev)
	rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, true);
	rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, true);

	iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDIE);

	return 0;
};

static int rswitch_stop(struct net_device *ndev)
{
	struct rswitch_device *rdev = netdev_priv(ndev);
	struct rswitch_gwca_ts_info *ts_info, *ts_info2;

	netif_tx_stop_all_queues(ndev);

	iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDID);

	list_for_each_entry_safe(ts_info, ts_info2, &rdev->priv->gwca.ts_info_list, list) {
		if (ts_info->port != rdev->port)
			continue;
		dev_kfree_skb_irq(ts_info->skb);
		list_del(&ts_info->list);
		kfree(ts_info);
	}

	rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false);
	rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false);

@@ -1390,17 +1491,31 @@ static netdev_tx_t rswitch_start_xmit(struct sk_buff *skb, struct net_device *nd
	}

	gq->skbs[gq->cur] = skb;
	desc = &gq->ring[gq->cur];
	desc = &gq->tx_ring[gq->cur];
	rswitch_desc_set_dptr(&desc->desc, dma_addr);
	desc->desc.info_ds = cpu_to_le16(skb->len);

	desc->info1 = cpu_to_le64(INFO1_DV(BIT(rdev->etha->index)) | INFO1_FMT);
	if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
		struct rswitch_gwca_ts_info *ts_info;

		ts_info = kzalloc(sizeof(*ts_info), GFP_ATOMIC);
		if (!ts_info) {
			dma_unmap_single(ndev->dev.parent, dma_addr, skb->len, DMA_TO_DEVICE);
			return -ENOMEM;
		}

		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
		rdev->ts_tag++;
		desc->info1 |= cpu_to_le64(INFO1_TSUN(rdev->ts_tag) | INFO1_TXC);
	}

		ts_info->skb = skb_get(skb);
		ts_info->port = rdev->port;
		ts_info->tag = rdev->ts_tag;
		list_add_tail(&ts_info->list, &rdev->priv->gwca.ts_info_list);

		skb_tx_timestamp(skb);
	}

	dma_wmb();

@@ -1650,10 +1765,17 @@ static int rswitch_init(struct rswitch_private *priv)
	if (err < 0)
		return err;

	err = rswitch_gwca_desc_alloc(priv);
	err = rswitch_gwca_linkfix_alloc(priv);
	if (err < 0)
		return -ENOMEM;

	err = rswitch_gwca_ts_queue_alloc(priv);
	if (err < 0)
		goto err_ts_queue_alloc;

	rswitch_gwca_ts_queue_fill(priv, 0, TS_RING_SIZE);
	INIT_LIST_HEAD(&priv->gwca.ts_info_list);

	for (i = 0; i < RSWITCH_NUM_PORTS; i++) {
		err = rswitch_device_alloc(priv, i);
		if (err < 0) {
@@ -1674,6 +1796,10 @@ static int rswitch_init(struct rswitch_private *priv)
	if (err < 0)
		goto err_gwca_request_irq;

	err = rswitch_gwca_ts_request_irqs(priv);
	if (err < 0)
		goto err_gwca_ts_request_irq;

	err = rswitch_gwca_hw_init(priv);
	if (err < 0)
		goto err_gwca_hw_init;
@@ -1704,6 +1830,7 @@ static int rswitch_init(struct rswitch_private *priv)
	rswitch_gwca_hw_deinit(priv);

err_gwca_hw_init:
err_gwca_ts_request_irq:
err_gwca_request_irq:
	rcar_gen4_ptp_unregister(priv->ptp_priv);

@@ -1712,7 +1839,10 @@ static int rswitch_init(struct rswitch_private *priv)
		rswitch_device_free(priv, i);

err_device_alloc:
	rswitch_gwca_desc_free(priv);
	rswitch_gwca_ts_queue_free(priv);

err_ts_queue_alloc:
	rswitch_gwca_linkfix_free(priv);

	return err;
}
@@ -1791,7 +1921,8 @@ static void rswitch_deinit(struct rswitch_private *priv)
		rswitch_device_free(priv, i);
	}

	rswitch_gwca_desc_free(priv);
	rswitch_gwca_ts_queue_free(priv);
	rswitch_gwca_linkfix_free(priv);

	rswitch_clock_disable(priv);
}
+35 −11
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@

#define TX_RING_SIZE		1024
#define RX_RING_SIZE		1024
#define TS_RING_SIZE		(TX_RING_SIZE * RSWITCH_NUM_PORTS)

#define PKT_BUF_SZ		1584
#define RSWITCH_ALIGN		128
@@ -49,6 +50,10 @@
#define AGENT_INDEX_GWCA	3
#define GWRO			RSWITCH_GWCA0_OFFSET

#define GWCA_TS_IRQ_RESOURCE_NAME	"gwca0_rxts0"
#define GWCA_TS_IRQ_NAME		"rswitch: gwca0_rxts0"
#define GWCA_TS_IRQ_BIT			BIT(0)

#define FWRO	0
#define TPRO	RSWITCH_TOP_OFFSET
#define CARO	RSWITCH_COMA_OFFSET
@@ -831,7 +836,7 @@ enum DIE_DT {
	DT_FSINGLE	= 0x80,
	DT_FSTART	= 0x90,
	DT_FMID		= 0xa0,
	DT_FEND		= 0xb8,
	DT_FEND		= 0xb0,

	/* Chain control */
	DT_LEMPTY	= 0xc0,
@@ -843,7 +848,7 @@ enum DIE_DT {
	DT_FEMPTY	= 0x40,
	DT_FEMPTY_IS	= 0x10,
	DT_FEMPTY_IC	= 0x20,
	DT_FEMPTY_ND	= 0x38,
	DT_FEMPTY_ND	= 0x30,
	DT_FEMPTY_START	= 0x50,
	DT_FEMPTY_MID	= 0x60,
	DT_FEMPTY_END	= 0x70,
@@ -865,6 +870,12 @@ enum DIE_DT {
/* For reception */
#define INFO1_SPN(port)		((u64)(port) << 36ULL)

/* For timestamp descriptor in dptrl (Byte 4 to 7) */
#define TS_DESC_TSUN(dptrl)	((dptrl) & GENMASK(7, 0))
#define TS_DESC_SPN(dptrl)	(((dptrl) & GENMASK(10, 8)) >> 8)
#define TS_DESC_DPN(dptrl)	(((dptrl) & GENMASK(17, 16)) >> 16)
#define TS_DESC_TN(dptrl)	((dptrl) & BIT(24))

struct rswitch_desc {
	__le16 info_ds;	/* Descriptor size */
	u8 die_dt;	/* Descriptor interrupt enable and type */
@@ -911,27 +922,43 @@ struct rswitch_etha {
 * name, this driver calls "queue".
 */
struct rswitch_gwca_queue {
	int index;
	bool dir_tx;
	bool gptp;
	union {
		struct rswitch_ext_desc *ring;
		struct rswitch_ext_ts_desc *ts_ring;
		struct rswitch_ext_desc *tx_ring;
		struct rswitch_ext_ts_desc *rx_ring;
		struct rswitch_ts_desc *ts_ring;
	};

	/* Common */
	dma_addr_t ring_dma;
	int ring_size;
	int cur;
	int dirty;
	struct sk_buff **skbs;

	/* For [rt]_ring */
	int index;
	bool dir_tx;
	struct sk_buff **skbs;
	struct net_device *ndev;	/* queue to ndev for irq */
};

struct rswitch_gwca_ts_info {
	struct sk_buff *skb;
	struct list_head list;

	int port;
	u8 tag;
};

#define RSWITCH_NUM_IRQ_REGS	(RSWITCH_MAX_NUM_QUEUES / BITS_PER_TYPE(u32))
struct rswitch_gwca {
	int index;
	struct rswitch_desc *linkfix_table;
	dma_addr_t linkfix_table_dma;
	u32 linkfix_table_size;
	struct rswitch_gwca_queue *queues;
	int num_queues;
	struct rswitch_gwca_queue ts_queue;
	struct list_head ts_info_list;
	DECLARE_BITMAP(used, RSWITCH_MAX_NUM_QUEUES);
	u32 tx_irq_bits[RSWITCH_NUM_IRQ_REGS];
	u32 rx_irq_bits[RSWITCH_NUM_IRQ_REGS];
@@ -969,9 +996,6 @@ struct rswitch_private {
	struct platform_device *pdev;
	void __iomem *addr;
	struct rcar_gen4_ptp_private *ptp_priv;
	struct rswitch_desc *linkfix_table;
	dma_addr_t linkfix_table_dma;
	u32 linkfix_table_size;

	struct rswitch_device *rdev[RSWITCH_NUM_PORTS];