From: Bjorn Helgaas <helgaas@kernel.org>
To: marek.vasut@gmail.com
Cc: linux-pci@vger.kernel.org, Oza Pawandeep <oza.oza@broadcom.com>,
Marek Vasut <marek.vasut+renesas@gmail.com>,
Arnd Bergmann <arnd@arndb.de>,
Geert Uytterhoeven <geert+renesas@glider.be>,
Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>,
Robin Murphy <robin.murphy@arm.com>,
Simon Horman <horms+renesas@verge.net.au>,
Wolfram Sang <wsa@the-dreams.de>,
linux-renesas-soc@vger.kernel.org
Subject: Re: [PATCH 2/2] PCI/of fix of_dma_get_range; get PCI specific dma-ranges
Date: Mon, 30 Sep 2019 16:50:16 -0500 [thread overview]
Message-ID: <20190930215016.GA201709@google.com> (raw)
In-Reply-To: <20190809173449.20126-2-marek.vasut@gmail.com>
On Fri, Aug 09, 2019 at 07:34:49PM +0200, marek.vasut@gmail.com wrote:
> From: Oza Pawandeep <oza.oza@broadcom.com>
>
> current device framework and OF framework integration assumes
> dma-ranges in a way where memory-mapped devices define their
> dma-ranges. (child-bus-address, parent-bus-address, length).
"Memory-mapped" means device memory and registers are mapped into the
CPU address space so reads/writes from the CPU can access the device.
IIUC, the issue you're solving is for DMA, which is the other
direction -- DMA reads/writes from the *device* can access system
memory.
> of_dma_configure is specifically written to take care of memory
> mapped devices. but no implementation exists for pci to take
> care of pcie based memory ranges.
s/pci/PCI/
There's nothing PCIe-specific here; the same issue could occur with
either PCI or PCIe, so just say "PCI".
> for e.g. iproc based SOCs and other SOCs(such as rcar) have PCI
> world dma-ranges.
> dma-ranges = <0x43000000 0x00 0x00 0x00 0x00 0x80 0x00>;
>
> this patch fixes the following problems of_dma_get_range.
> 1) return of wrong size as 0.
> 2) not handling absence of dma-ranges which is valid for PCI master.
> 3) not handling multipe inbound windows.
s/multipe/multiple/
> 4) in order to get largest possible dma_mask. this patch also
> retuns the largest possible size based on dma-ranges,
>
> for e.g.
> dma-ranges = <0x43000000 0x00 0x00 0x00 0x00 0x80 0x00>;
> we should get dev->coherent_dma_mask=0x7fffffffff.
>
> based on which IOVA allocation space will honour PCI host
> bridge limitations.
>
> the implementation hooks bus specific callbacks for getting
> dma-ranges.
>
> Signed-off-by: Oza Pawandeep <oza.oza@broadcom.com>
> Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Geert Uytterhoeven <geert+renesas@glider.be>
> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> Cc: Robin Murphy <robin.murphy@arm.com>
> Cc: Simon Horman <horms+renesas@verge.net.au>
> Cc: Wolfram Sang <wsa@the-dreams.de>
> Cc: linux-renesas-soc@vger.kernel.org
> To: linux-pci@vger.kernel.org
> ---
> drivers/of/address.c | 220 +++++++++++++++++++++++++++++--------------
> 1 file changed, 150 insertions(+), 70 deletions(-)
>
> diff --git a/drivers/of/address.c b/drivers/of/address.c
> index 55a4eb7786ca..ae2819e148b8 100644
> --- a/drivers/of/address.c
> +++ b/drivers/of/address.c
> @@ -8,6 +8,7 @@
> #include <linux/logic_pio.h>
> #include <linux/module.h>
> #include <linux/of_address.h>
> +#include <linux/of_pci.h>
> #include <linux/pci.h>
> #include <linux/pci_regs.h>
> #include <linux/sizes.h>
> @@ -48,6 +49,8 @@ struct of_bus {
> int na, int ns, int pna);
> int (*translate)(__be32 *addr, u64 offset, int na);
> unsigned int (*get_flags)(const __be32 *addr);
> + int (*get_dma_ranges)(struct device_node *np,
> + u64 *dma_addr, u64 *paddr, u64 *size);
> };
>
> /*
> @@ -174,6 +177,143 @@ static int of_bus_pci_translate(__be32 *addr, u64 offset, int na)
> return of_bus_default_translate(addr + 1, offset, na - 1);
> }
>
> +static int of_bus_pci_get_dma_ranges(struct device_node *np, u64 *dma_addr,
> + u64 *paddr, u64 *size)
> +{
> + struct device_node *node = of_node_get(np);
> + int ret = 0;
> + struct resource_entry *window;
> + LIST_HEAD(res);
> +
> + if (!node)
> + return -EINVAL;
> +
> + *size = 0;
> + /*
> + * PCI dma-ranges is not mandatory property.
> + * many devices do no need to have it, since
> + * host bridge does not require inbound memory
> + * configuration or rather have design limitations.
> + * so we look for dma-ranges, if missing we
> + * just return the caller full size, and also
> + * no dma-ranges suggests that, host bridge allows
> + * whatever comes in, so we set dma_addr to 0.
Wrap to fill the usual line length in this file.
> + */
> + ret = of_pci_get_dma_ranges(np, &res);
> + if (!ret) {
> + resource_list_for_each_entry(window, &res) {
> + struct resource *res_dma = window->res;
> +
> + if (*size < resource_size(res_dma)) {
> + *dma_addr = res_dma->start - window->offset;
> + *paddr = res_dma->start;
> + *size = resource_size(res_dma);
> + }
> + }
> + }
> + pci_free_resource_list(&res);
> +
> + /*
> + * return the largest possible size,
> + * since PCI master allows everything.
Wrap.
> + */
> + if (*size == 0) {
> + pr_debug("empty/zero size dma-ranges found for node(%s)\n",
> + np->full_name);
> + *size = DMA_BIT_MASK(sizeof(dma_addr_t) * 8) - 1;
> + *dma_addr = *paddr = 0;
> + ret = 0;
> + }
> +
> + pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n",
> + *dma_addr, *paddr, *size);
> +
> + of_node_put(node);
> +
> + return ret;
> +}
> +
> +static int get_dma_ranges(struct device_node *np, u64 *dma_addr,
> + u64 *paddr, u64 *size)
> +{
> + struct device_node *node = of_node_get(np);
> + const __be32 *ranges = NULL;
> + int len, naddr, nsize, pna;
> + int ret = 0;
> + u64 dmaaddr;
> +
> + if (!node)
> + return -EINVAL;
> +
> + while (1) {
> + naddr = of_n_addr_cells(node);
> + nsize = of_n_size_cells(node);
> + node = of_get_next_parent(node);
> + if (!node)
> + break;
> +
> + ranges = of_get_property(node, "dma-ranges", &len);
> +
> + /* Ignore empty ranges, they imply no translation required */
> + if (ranges && len > 0)
> + break;
> +
> + /*
> + * At least empty ranges has to be defined for parent node if
> + * DMA is supported
> + */
> + if (!ranges)
> + break;
> + }
> +
> + if (!ranges) {
> + pr_debug("no dma-ranges found for node(%s)\n", np->full_name);
> + ret = -ENODEV;
> + goto out;
> + }
> +
> + len /= sizeof(u32);
> +
> + pna = of_n_addr_cells(node);
> +
> + /* dma-ranges format:
> + * DMA addr : naddr cells
> + * CPU addr : pna cells
> + * size : nsize cells
> + */
> + dmaaddr = of_read_number(ranges, naddr);
> + *paddr = of_translate_dma_address(np, ranges);
> + if (*paddr == OF_BAD_ADDR) {
> + pr_err("translation of DMA address(%pad) to CPU address failed node(%s)\n",
> + dma_addr, np->full_name);
The old code used %pOF; why the change to %s?
> + ret = -EINVAL;
> + goto out;
> + }
> + *dma_addr = dmaaddr;
> +
> + *size = of_read_number(ranges + naddr + pna, nsize);
> +
> + pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n",
> + *dma_addr, *paddr, *size);
I'm a little confused about the usage of %pad above and %llx here.
Are these printing different things, or could they both be done the
same way? (There are other occurrences of %llx earlier, too.)
> +
> +out:
> + of_node_put(node);
> +
> + return ret;
> +}
> +
> +static int of_bus_isa_get_dma_ranges(struct device_node *np, u64 *dma_addr,
> + u64 *paddr, u64 *size)
> +{
> + return get_dma_ranges(np, dma_addr, paddr, size);
> +}
> +
> +static int of_bus_default_get_dma_ranges(struct device_node *np, u64 *dma_addr,
> + u64 *paddr, u64 *size)
> +{
> + return get_dma_ranges(np, dma_addr, paddr, size);
> +}
> +
> const __be32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size,
> unsigned int *flags)
> {
> @@ -438,6 +578,7 @@ static struct of_bus of_busses[] = {
> .map = of_bus_pci_map,
> .translate = of_bus_pci_translate,
> .get_flags = of_bus_pci_get_flags,
> + .get_dma_ranges = of_bus_pci_get_dma_ranges,
> },
> #endif /* CONFIG_PCI */
> /* ISA */
> @@ -449,6 +590,7 @@ static struct of_bus of_busses[] = {
> .map = of_bus_isa_map,
> .translate = of_bus_isa_translate,
> .get_flags = of_bus_isa_get_flags,
> + .get_dma_ranges = of_bus_isa_get_dma_ranges,
> },
> /* Default */
> {
> @@ -459,6 +601,7 @@ static struct of_bus of_busses[] = {
> .map = of_bus_default_map,
> .translate = of_bus_default_translate,
> .get_flags = of_bus_default_get_flags,
> + .get_dma_ranges = of_bus_default_get_dma_ranges,
> },
> };
>
> @@ -917,80 +1060,17 @@ EXPORT_SYMBOL(of_io_request_and_map);
> * size : nsize cells
> *
> * It returns -ENODEV if "dma-ranges" property was not found
> - * for this device in DT.
> + * for this device in DT, except if PCI device then, dma-ranges
> + * can be optional property, and in that case returns size with
> + * entire host memory.
> */
> int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size)
> {
> - struct device_node *node = of_node_get(np);
> - const __be32 *ranges = NULL;
> - int len, naddr, nsize, pna;
> - int ret = 0;
> - u64 dmaaddr;
> -
> - if (!node)
> - return -EINVAL;
> -
> - while (1) {
> - struct device_node *parent;
> -
> - naddr = of_n_addr_cells(node);
> - nsize = of_n_size_cells(node);
> -
> - parent = __of_get_dma_parent(node);
> - of_node_put(node);
> -
> - node = parent;
> - if (!node)
> - break;
> -
> - ranges = of_get_property(node, "dma-ranges", &len);
> -
> - /* Ignore empty ranges, they imply no translation required */
> - if (ranges && len > 0)
> - break;
> -
> - /*
> - * At least empty ranges has to be defined for parent node if
> - * DMA is supported
> - */
> - if (!ranges)
> - break;
> - }
> -
> - if (!ranges) {
> - pr_debug("no dma-ranges found for node(%pOF)\n", np);
> - ret = -ENODEV;
> - goto out;
> - }
> -
> - len /= sizeof(u32);
> -
> - pna = of_n_addr_cells(node);
> -
> - /* dma-ranges format:
> - * DMA addr : naddr cells
> - * CPU addr : pna cells
> - * size : nsize cells
> - */
> - dmaaddr = of_read_number(ranges, naddr);
> - *paddr = of_translate_dma_address(np, ranges);
> - if (*paddr == OF_BAD_ADDR) {
> - pr_err("translation of DMA address(%pad) to CPU address failed node(%pOF)\n",
> - dma_addr, np);
> - ret = -EINVAL;
> - goto out;
> - }
> - *dma_addr = dmaaddr;
> -
> - *size = of_read_number(ranges + naddr + pna, nsize);
> -
> - pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n",
> - *dma_addr, *paddr, *size);
> -
> -out:
> - of_node_put(node);
> + struct of_bus *bus;
>
> - return ret;
> + /* get bus specific dma-ranges. */
> + bus = of_match_bus(np);
> + return bus->get_dma_ranges(np, dma_addr, paddr, size);
> }
> EXPORT_SYMBOL_GPL(of_dma_get_range);
>
> --
> 2.20.1
>
next prev parent reply other threads:[~2019-09-30 21:50 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-08-09 17:34 [PATCH 1/2] OF/PCI: Export inbound memory interface to PCI RC drivers marek.vasut
2019-08-09 17:34 ` [PATCH 2/2] PCI/of fix of_dma_get_range; get PCI specific dma-ranges marek.vasut
2019-08-09 20:59 ` Simon Horman
2019-09-30 21:50 ` Bjorn Helgaas [this message]
2019-08-09 20:56 ` [PATCH 1/2] OF/PCI: Export inbound memory interface to PCI RC drivers Simon Horman
2019-08-09 21:22 ` Marek Vasut
2019-09-30 21:40 ` Bjorn Helgaas
2019-09-30 21:46 ` Marek Vasut
2019-10-01 12:27 ` Bjorn Helgaas
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=20190930215016.GA201709@google.com \
--to=helgaas@kernel.org \
--cc=arnd@arndb.de \
--cc=geert+renesas@glider.be \
--cc=horms+renesas@verge.net.au \
--cc=linux-pci@vger.kernel.org \
--cc=linux-renesas-soc@vger.kernel.org \
--cc=lorenzo.pieralisi@arm.com \
--cc=marek.vasut+renesas@gmail.com \
--cc=marek.vasut@gmail.com \
--cc=oza.oza@broadcom.com \
--cc=robin.murphy@arm.com \
--cc=wsa@the-dreams.de \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).