All of lore.kernel.org
 help / color / mirror / Atom feed
From: Frank Li <Frank.li@oss.nxp.com>
To: Koichiro Den <den@valinux.co.jp>
Cc: "Frank Li" <Frank.Li@kernel.org>,
	"Manivannan Sadhasivam" <mani@kernel.org>,
	"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
	"Kishon Vijay Abraham I" <kishon@kernel.org>,
	"Jon Mason" <jdmason@kudzu.us>,
	"Dave Jiang" <dave.jiang@intel.com>,
	"Allen Hubbe" <allenbh@gmail.com>,
	"Niklas Cassel" <cassel@kernel.org>,
	"Bjorn Helgaas" <bhelgaas@google.com>,
	"Jonathan Corbet" <corbet@lwn.net>,
	"Shuah Khan" <skhan@linuxfoundation.org>,
	"Randy Dunlap" <rdunlap@infradead.org>,
	"Jingoo Han" <jingoohan1@gmail.com>,
	"Lorenzo Pieralisi" <lpieralisi@kernel.org>,
	"Rob Herring" <robh@kernel.org>,
	"Jerome Brunet" <jbrunet@baylibre.com>,
	linux-pci@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, ntb@lists.linux.dev
Subject: Re: [PATCH v10 09/10] NTB: ntb_hw_epf: Discover vNTB-embedded DMA
Date: Fri, 13 Nov 2026 10:24:09 -0600	[thread overview]
Message-ID: <avc5qUSOUHzJXxR9@SMW015318> (raw)
In-Reply-To: <20261113132935.4152566-10-den@valinux.co.jp>

On Fri, Nov 13, 2026 at 10:29:34PM +0900, Koichiro Den wrote:
> A vNTB PCI function can now expose endpoint-local DMA channels to its
> host. Decode the extension and register the advertised channels before
> the NTB device, so clients of that NTB device can use the DMA
> functionality in the same PCI function. Endpoints without the extension,
> or hosts without support for its DMA type, continue without DMA.
>
> All DMA channels share IRQ slot 1, historically unused by link events
> and doorbells. This keeps the doorbell count unchanged and needs no
> extra vector.
>
> Only DW eDMA is supported for now. Dispatch on the advertised DMA type
> and keep type-specific setup in local helpers. Support for another
> implementation can add a new case and its own helpers.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

> Changes in v10:
>   - Use the historically unused slot 1 for a shared DMA IRQ without
>     taking a doorbell vector. (Sashiko)
>   - Drop R-b tag due to the IRQ allocation changes.
>   - Link to v9: https://lore.kernel.org/r/20261110082459.1798763-10-den@valinux.co.jp/
>
>  drivers/ntb/hw/epf/ntb_hw_epf.c | 259 +++++++++++++++++++++++++++++++-
>  1 file changed, 256 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c
> index ff92c2a61ce7..a70da2b4f187 100644
> --- a/drivers/ntb/hw/epf/ntb_hw_epf.c
> +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c
> @@ -8,6 +8,7 @@
>
>  #include <linux/atomic.h>
>  #include <linux/delay.h>
> +#include <linux/dma/edma.h>
>  #include <linux/io-64-nonatomic-lo-hi.h>
>  #include <linux/module.h>
>  #include <linux/pci.h>
> @@ -18,11 +19,17 @@
>  /* Offsets into the control region, see include/linux/pci-epf-ntb.h. */
>  #define NTB_EPF_CTRL(field)	offsetof(struct epf_ntb_ctrl, field)
>
> +/* Private DMA wire extension produced by pci-epf-vntb. */
> +#define NTB_EPF_DMA(field)	(EPF_NTB_DMA_CTRL_OFFSET + \
> +				 offsetof(struct epf_ntb_dma_ctrl, field))
> +#define NTB_EPF_DMA_REGION(field)	\
> +	offsetof(struct epf_ntb_dma_region_ctrl, field)
> +
>  /*
>   * Legacy doorbell slot layout when paired with pci-epf-*ntb:
>   *
>   *   slot 0 : reserved for link events
> - *   slot 1 : unused (historical extra offset)
> + *   slot 1 : unused, or DMA IRQ if enabled on the host
>   *   slot 2 : DB#0
>   *   slot 3 : DB#1
>   *   ...
> @@ -71,6 +78,23 @@ struct ntb_epf_irq_ctx {
>  	unsigned int irq_no;
>  };
>
> +struct ntb_epf_dma_region {
> +	unsigned int bar;
> +	resource_size_t offset;
> +	resource_size_t size;
> +};
> +
> +struct ntb_epf_dw_edma {
> +	struct dw_edma_chip chip;
> +	struct ntb_epf_dma_region reg;
> +	struct ntb_epf_dma_region ll[EDMA_MAX_RD_CH];
> +};
> +
> +struct ntb_epf_dma {
> +	struct ntb_epf_dma_region submap;
> +	u32 type;
> +};
> +
>  struct ntb_epf_dev {
>  	struct ntb_dev ntb;
>  	struct device *dev;
> @@ -82,6 +106,8 @@ struct ntb_epf_dev {
>  	unsigned int mw_count;
>  	unsigned int spad_count;
>  	unsigned int db_count;
> +	struct ntb_epf_dma dma;
> +	struct ntb_epf_dw_edma dw_edma;
>
>  	void __iomem *ctrl_reg;
>  	void __iomem *db_reg;
> @@ -136,6 +162,209 @@ static int ntb_epf_send_command(struct ntb_epf_dev *ndev, u32 command,
>  	return ret;
>  }
>
> +static bool ntb_epf_dma_region_parse(struct ntb_epf_dev *ndev, u32 base,
> +				     bool optional,
> +				     struct ntb_epf_dma_region *region)
> +{
> +	void __iomem *reg = ndev->ctrl_reg + base;
> +	resource_size_t bar_len;
> +	u32 bar, offset, size;
> +
> +	bar = readl(reg + NTB_EPF_DMA_REGION(bar));
> +	offset = readl(reg + NTB_EPF_DMA_REGION(offset));
> +	size = readl(reg + NTB_EPF_DMA_REGION(size));
> +	region->bar = bar;
> +	region->offset = offset;
> +	region->size = size;
> +	if (!size)
> +		return optional && bar == U32_MAX && !offset;
> +	if (bar > BAR_5)
> +		return false;
> +
> +	bar_len = pci_resource_len(ndev->ntb.pdev, bar);
> +	if (offset > bar_len || size > bar_len - offset)
> +		return false;
> +
> +	return true;
> +}
> +
> +/* DW eDMA */
> +
> +static int ntb_epf_dw_edma_parse(struct ntb_epf_dev *ndev, u32 length)
> +{
> +	struct ntb_epf_dw_edma *edma = &ndev->dw_edma;
> +	struct dw_edma_chip *chip = &edma->chip;
> +	u32 count, chan_offset, chan_size;
> +	unsigned int i;
> +
> +	chan_offset = offsetof(struct epf_ntb_dma_ctrl, chan);
> +	chan_size = sizeof(struct epf_ntb_dma_chan_ctrl);
> +	if (length < chan_offset + chan_size ||
> +	    (length - chan_offset) % chan_size)
> +		return -EINVAL;
> +
> +	count = (length - chan_offset) / chan_size;
> +	if (count > EDMA_MAX_RD_CH)
> +		return -EINVAL;
> +
> +	if (!ntb_epf_dma_region_parse(ndev, NTB_EPF_DMA(reg), false,
> +				      &edma->reg) ||
> +	    !IS_ALIGNED(edma->reg.offset, sizeof(u32)))
> +		return -EINVAL;
> +
> +	chip->mf = EDMA_MF_EDMA_UNROLL;
> +	chip->ll_rd_cnt = count;
> +	chip->func_no = PCI_FUNC(ndev->ntb.pdev->devfn);
> +
> +	for (i = 0; i < count; i++) {
> +		u64 paddr;
> +
> +		if (!ntb_epf_dma_region_parse(ndev, NTB_EPF_DMA(chan[i].desc),
> +					      false, &edma->ll[i]) ||
> +		    !IS_ALIGNED(edma->ll[i].offset, sizeof(u32)))
> +			return -EINVAL;
> +
> +		paddr = readl(ndev->ctrl_reg +
> +			      NTB_EPF_DMA(chan[i].desc_addr_lo));
> +		paddr |= (u64)readl(ndev->ctrl_reg +
> +				    NTB_EPF_DMA(chan[i].desc_addr_hi)) << 32;
> +		if (paddr == U64_MAX)
> +			return -EINVAL;
> +
> +		chip->ll_region_rd[i].sz = edma->ll[i].size;
> +		chip->ll_region_rd[i].paddr = paddr;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ntb_epf_dw_edma_irq_vector(struct device *dev, unsigned int nr)
> +{
> +	return pci_irq_vector(to_pci_dev(dev), EPF_IRQ_RESERVED_DB);
> +}
> +
> +static const struct dw_edma_plat_ops ntb_epf_dw_edma_ops = {
> +	.irq_vector = ntb_epf_dw_edma_irq_vector,
> +};
> +
> +static void __iomem *
> +ntb_epf_dw_edma_map_region(struct pci_dev *pdev,
> +			   const struct ntb_epf_dma_region *region)
> +{
> +	return pcim_iomap_range(pdev, region->bar, region->offset, region->size);
> +}
> +
> +static int ntb_epf_dw_edma_init(struct ntb_epf_dev *ndev)
> +{
> +	struct ntb_epf_dw_edma *edma = &ndev->dw_edma;
> +	struct dw_edma_chip *chip = &edma->chip;
> +	struct pci_dev *pdev = ndev->ntb.pdev;
> +	unsigned int i;
> +	int ret;
> +
> +	/* Install BAR submaps before mapping the advertised DMA regions. */
> +	ret = ntb_epf_send_command(ndev, EPF_NTB_COMMAND_SETUP_DMA_BAR, 0);
> +	if (ret)
> +		return ret;
> +
> +	chip->reg_base = ntb_epf_dw_edma_map_region(pdev, &edma->reg);
> +	if (IS_ERR(chip->reg_base))
> +		return PTR_ERR(chip->reg_base);
> +	for (i = 0; i < chip->ll_rd_cnt; i++) {
> +		chip->ll_region_rd[i].vaddr.io =
> +			ntb_epf_dw_edma_map_region(pdev, &edma->ll[i]);
> +		if (IS_ERR(chip->ll_region_rd[i].vaddr.io))
> +			return PTR_ERR(chip->ll_region_rd[i].vaddr.io);
> +	}
> +
> +	chip->dev = ndev->dev;
> +	chip->nr_irqs = 1;
> +	chip->ops = &ntb_epf_dw_edma_ops;
> +	chip->flags = DW_EDMA_CHIP_PARTIAL;
> +
> +	return dw_edma_probe(chip);
> +}
> +
> +static void ntb_epf_dw_edma_deinit(struct ntb_epf_dev *ndev)
> +{
> +	dw_edma_remove(&ndev->dw_edma.chip);
> +}
> +
> +/* Common endpoint DMA */
> +
> +static int ntb_epf_dma_parse(struct ntb_epf_dev *ndev)
> +{
> +	u32 magic, type, spad_off;
> +	u16 revision, length;
> +	int ret;
> +
> +	/*
> +	 * Legacy endpoints place scratchpads where this extension would begin.
> +	 * Once the extension is present, malformed metadata is fatal.
> +	 */
> +	spad_off = readl(ndev->ctrl_reg + NTB_EPF_CTRL(spad_offset));
> +	if (spad_off < EPF_NTB_DMA_CTRL_OFFSET + EPF_NTB_DMA_CTRL_COMMON_SIZE)
> +		return 0;
> +
> +	magic = readl(ndev->ctrl_reg + NTB_EPF_DMA(magic));
> +	if (!magic)
> +		return 0;
> +	if (magic == U32_MAX)
> +		return -EIO;
> +	if (magic != EPF_NTB_DMA_MAGIC)
> +		return -EINVAL;
> +
> +	revision = readw(ndev->ctrl_reg + NTB_EPF_DMA(revision));
> +	length = readw(ndev->ctrl_reg + NTB_EPF_DMA(length));
> +	if (revision != EPF_NTB_DMA_REVISION ||
> +	    length < EPF_NTB_DMA_CTRL_COMMON_SIZE ||
> +	    spad_off < EPF_NTB_DMA_CTRL_OFFSET + length)
> +		return -EINVAL;
> +	if (!ntb_epf_dma_region_parse(ndev, NTB_EPF_DMA(submap), true,
> +				      &ndev->dma.submap))
> +		return -EINVAL;
> +
> +	type = readl(ndev->ctrl_reg + NTB_EPF_DMA(type));
> +	if (type == U32_MAX)
> +		return -EIO;
> +
> +	switch (type) {
> +	case EPF_NTB_DMA_TYPE_DW_EDMA:
> +		if (!IS_REACHABLE(CONFIG_DW_EDMA))
> +			break;
> +		ret = ntb_epf_dw_edma_parse(ndev, length);
> +		if (!ret)
> +			ndev->dma.type = type;
> +		return ret;
> +	default:
> +		break;
> +	}
> +
> +	dev_warn(ndev->dev,
> +		 "Endpoint DMA type %u unavailable, continuing without DMA\n",
> +		 type);
> +	return 0;
> +}
> +
> +static int ntb_epf_dma_init(struct ntb_epf_dev *ndev)
> +{
> +	switch (ndev->dma.type) {
> +	case EPF_NTB_DMA_TYPE_DW_EDMA:
> +		return ntb_epf_dw_edma_init(ndev);
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static void ntb_epf_dma_deinit(struct ntb_epf_dev *ndev)
> +{
> +	switch (ndev->dma.type) {
> +	case EPF_NTB_DMA_TYPE_DW_EDMA:
> +		ntb_epf_dw_edma_deinit(ndev);
> +		break;
> +	}
> +}
> +
>  static int ntb_epf_mw_to_bar(struct ntb_epf_dev *ndev, int idx)
>  {
>  	struct device *dev = ndev->dev;
> @@ -366,6 +595,10 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
>
>  	ndev->db_count = irq - 1;
>  	for (i = 0; i < irq; i++) {
> +		/* When DMA is enabled, the DMA driver owns slot 1. */
> +		if (ndev->dma.type && i == EPF_IRQ_RESERVED_DB)
> +			continue;
> +
>  		ndev->irq_ctx[i].ndev = ndev;
>  		ndev->irq_ctx[i].irq_no = i;
>  		ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr,
> @@ -386,8 +619,11 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
>  	return 0;
>
>  err_free_irq:
> -	while (i--)
> +	while (i--) {
> +		if (ndev->dma.type && i == EPF_IRQ_RESERVED_DB)
> +			continue;
>  		free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]);
> +	}
>  	pci_free_irq_vectors(pdev);
>
>  	return ret;
> @@ -610,6 +846,11 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
>  		dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count);
>  		return -EINVAL;
>  	}
> +	ret = ntb_epf_dma_parse(ndev);
> +	if (ret) {
> +		dev_err(dev, "Invalid endpoint DMA layout\n");
> +		return ret;
> +	}
>
>  	/* One Link interrupt and rest doorbell interrupt */
>  	ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1,
> @@ -720,8 +961,11 @@ static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev)
>  	ntb_epf_send_command(ndev, EPF_NTB_COMMAND_TEARDOWN_DOORBELL,
>  			     ndev->db_count + 1);
>
> -	for (i = 0; i < ndev->db_count + 1; i++)
> +	for (i = 0; i < ndev->db_count + 1; i++) {
> +		if (ndev->dma.type && i == EPF_IRQ_RESERVED_DB)
> +			continue;
>  		free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]);
> +	}
>  	pci_free_irq_vectors(pdev);
>  }
>
> @@ -760,6 +1004,12 @@ static int ntb_epf_pci_probe(struct pci_dev *pdev,
>  		goto err_init_dev;
>  	}
>
> +	ret = ntb_epf_dma_init(ndev);
> +	if (ret) {
> +		dev_err(dev, "Failed to initialize endpoint DMA\n");
> +		goto err_dma_init;
> +	}
> +
>  	ret = ntb_register_device(&ndev->ntb);
>  	if (ret) {
>  		dev_err(dev, "Failed to register NTB device\n");
> @@ -769,6 +1019,8 @@ static int ntb_epf_pci_probe(struct pci_dev *pdev,
>  	return 0;
>
>  err_register_dev:
> +	ntb_epf_dma_deinit(ndev);
> +err_dma_init:
>  	ntb_epf_cleanup_isr(ndev);
>
>  err_init_dev:
> @@ -782,6 +1034,7 @@ static void ntb_epf_pci_remove(struct pci_dev *pdev)
>  	struct ntb_epf_dev *ndev = pci_get_drvdata(pdev);
>
>  	ntb_unregister_device(&ndev->ntb);
> +	ntb_epf_dma_deinit(ndev);
>  	ntb_epf_cleanup_isr(ndev);
>  	ntb_epf_deinit_pci(ndev);
>  }
> --
> 2.54.0
>

  parent reply	other threads:[~2026-11-13 16:24 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-13 13:29 [PATCH v10 00/10] PCI: endpoint: Remote DMA support via vNTB Koichiro Den
2026-11-13 13:29 ` [PATCH v10 01/10] PCI: endpoint: Add DMA auxiliary resource metadata Koichiro Den
2026-11-13 13:36   ` sashiko-bot
2026-11-13 13:29 ` [PATCH v10 02/10] PCI: dwc: Expose endpoint DMA resources Koichiro Den
2026-11-13 14:01   ` sashiko-bot
2026-11-13 13:29 ` [PATCH v10 03/10] PCI: endpoint: pci-epf-vntb: Move epf_ntb_is_bar_used() up Koichiro Den
2026-11-13 13:33   ` sashiko-bot
2026-11-13 13:29 ` [PATCH v10 04/10] PCI: endpoint: Add pci-epf-ntb.h for the shared NTB control layout Koichiro Den
2026-11-13 13:37   ` sashiko-bot
2026-11-13 13:29 ` [PATCH v10 05/10] PCI: endpoint: pci-epf-vntb: Export endpoint DMA channels Koichiro Den
2026-11-13 14:00   ` sashiko-bot
2026-11-13 13:29 ` [PATCH v10 06/10] PCI: endpoint: pci-epf-vntb: Allow DMA and MW to share a BAR Koichiro Den
2026-11-13 14:00   ` sashiko-bot
2026-11-13 13:29 ` [PATCH v10 07/10] Documentation: PCI: endpoint: Document vNTB DMA export Koichiro Den
2026-11-13 14:03   ` sashiko-bot
2026-11-13 13:29 ` [PATCH v10 08/10] NTB: ntb_hw_epf: Use the shared NTB control layout Koichiro Den
2026-11-13 14:07   ` sashiko-bot
2026-11-13 13:29 ` [PATCH v10 09/10] NTB: ntb_hw_epf: Discover vNTB-embedded DMA Koichiro Den
2026-11-13 14:18   ` sashiko-bot
2026-11-13 16:24   ` Frank Li [this message]
2026-11-13 13:29 ` [PATCH v10 10/10] NTB: ntb_hw_epf: Support DMA and MW sharing a BAR Koichiro Den
2026-11-13 14:12   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=avc5qUSOUHzJXxR9@SMW015318 \
    --to=frank.li@oss.nxp.com \
    --cc=Frank.Li@kernel.org \
    --cc=allenbh@gmail.com \
    --cc=bhelgaas@google.com \
    --cc=cassel@kernel.org \
    --cc=corbet@lwn.net \
    --cc=dave.jiang@intel.com \
    --cc=den@valinux.co.jp \
    --cc=jbrunet@baylibre.com \
    --cc=jdmason@kudzu.us \
    --cc=jingoohan1@gmail.com \
    --cc=kishon@kernel.org \
    --cc=kwilczynski@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=lpieralisi@kernel.org \
    --cc=mani@kernel.org \
    --cc=ntb@lists.linux.dev \
    --cc=rdunlap@infradead.org \
    --cc=robh@kernel.org \
    --cc=skhan@linuxfoundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.