From ed748621031c2a205749997421e59fb4dfb1e909 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Tue, 3 Mar 2015 12:52:08 -0500 Subject: of: iommu: Add ptr to OF node arg to of_iommu_configure() of_iommu_configure() is called from of_dma_configure() to setup iommu ops using DT property. This API is currently used for platform devices for which DMA configuration (including IOMMU ops) may come from the device's parent. To extend this functionality for PCI devices, this API needs to take a parent node ptr as an argument instead of assuming device's parent. This is needed since for PCI, the DMA configuration may be defined in the DT node of the root bus bridge's parent device. Currently only dma-range is used for PCI and IOMMU is not supported. Return error if the device is PCI. Add "parent" parameter (a struct device_node *) to of_iommu_configure(). Tested-by: Suravee Suthikulpanit (AMD Seattle) Signed-off-by: Murali Karicheri Signed-off-by: Bjorn Helgaas Reviewed-by: Catalin Marinas Acked-by: Rob Herring Acked-by: Will Deacon CC: Joerg Roedel CC: Grant Likely CC: Russell King CC: Arnd Bergmann --- drivers/of/platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/of') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index b189733a1539..667c6f13f12b 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -196,7 +196,7 @@ static void of_dma_configure(struct device *dev) dev_dbg(dev, "device is%sdma coherent\n", coherent ? " " : " not "); - iommu = of_iommu_configure(dev); + iommu = of_iommu_configure(dev, dev->of_node); dev_dbg(dev, "device is%sbehind an iommu\n", iommu ? " " : " not "); -- cgit v1.2.3 From 1f5c69aa51f9c7c8d2a5c2e4dc339f6c7d5c15cd Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Tue, 3 Mar 2015 12:52:09 -0500 Subject: of: Move of_dma_configure() to device.c to help re-use Move of_dma_configure() to device.c so it can be re-used for PCI devices to obtain DMA configuration from DT. Also add a second argument so that for PCI, the DT node of root bus host bridge can be used to obtain the DMA configuration for the slave PCI device. Tested-by: Suravee Suthikulpanit (AMD Seattle) Signed-off-by: Murali Karicheri Signed-off-by: Bjorn Helgaas Reviewed-by: Catalin Marinas Acked-by: Will Deacon Acked-by: Rob Herring CC: Joerg Roedel CC: Grant Likely CC: Russell King CC: Arnd Bergmann --- drivers/of/device.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/of/platform.c | 58 ++------------------------------------------------ 2 files changed, 61 insertions(+), 56 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/device.c b/drivers/of/device.c index 46d6c75c1404..31a7875b34a4 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -2,6 +2,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -66,6 +69,62 @@ int of_device_add(struct platform_device *ofdev) return device_add(&ofdev->dev); } +/** + * of_dma_configure - Setup DMA configuration + * @dev: Device to apply DMA configuration + * @np: Pointer to OF node having DMA configuration + * + * Try to get devices's DMA configuration from DT and update it + * accordingly. + * + * If platform code needs to use its own special DMA configuration, it + * can use a platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE events + * to fix up DMA configuration. + */ +void of_dma_configure(struct device *dev, struct device_node *np) +{ + u64 dma_addr, paddr, size; + int ret; + bool coherent; + unsigned long offset; + struct iommu_ops *iommu; + + /* + * Set default dma-mask to 32 bit. Drivers are expected to setup + * the correct supported dma_mask. + */ + dev->coherent_dma_mask = DMA_BIT_MASK(32); + + /* + * Set it to coherent_dma_mask by default if the architecture + * code has not set it. + */ + if (!dev->dma_mask) + dev->dma_mask = &dev->coherent_dma_mask; + + ret = of_dma_get_range(np, &dma_addr, &paddr, &size); + if (ret < 0) { + dma_addr = offset = 0; + size = dev->coherent_dma_mask; + } else { + offset = PFN_DOWN(paddr - dma_addr); + dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); + } + + dev->dma_pfn_offset = offset; + + coherent = of_dma_is_coherent(np); + dev_dbg(dev, "device is%sdma coherent\n", + coherent ? " " : " not "); + + iommu = of_iommu_configure(dev, np); + dev_dbg(dev, "device is%sbehind an iommu\n", + iommu ? " " : " not "); + + arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent); +} +EXPORT_SYMBOL_GPL(of_dma_configure); + int of_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 667c6f13f12b..a01f57c9e34e 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -150,59 +149,6 @@ struct platform_device *of_device_alloc(struct device_node *np, } EXPORT_SYMBOL(of_device_alloc); -/** - * of_dma_configure - Setup DMA configuration - * @dev: Device to apply DMA configuration - * - * Try to get devices's DMA configuration from DT and update it - * accordingly. - * - * In case if platform code need to use own special DMA configuration,it - * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event - * to fix up DMA configuration. - */ -static void of_dma_configure(struct device *dev) -{ - u64 dma_addr, paddr, size; - int ret; - bool coherent; - unsigned long offset; - struct iommu_ops *iommu; - - /* - * Set default dma-mask to 32 bit. Drivers are expected to setup - * the correct supported dma_mask. - */ - dev->coherent_dma_mask = DMA_BIT_MASK(32); - - /* - * Set it to coherent_dma_mask by default if the architecture - * code has not set it. - */ - if (!dev->dma_mask) - dev->dma_mask = &dev->coherent_dma_mask; - - ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size); - if (ret < 0) { - dma_addr = offset = 0; - size = dev->coherent_dma_mask; - } else { - offset = PFN_DOWN(paddr - dma_addr); - dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); - } - dev->dma_pfn_offset = offset; - - coherent = of_dma_is_coherent(dev->of_node); - dev_dbg(dev, "device is%sdma coherent\n", - coherent ? " " : " not "); - - iommu = of_iommu_configure(dev, dev->of_node); - dev_dbg(dev, "device is%sbehind an iommu\n", - iommu ? " " : " not "); - - arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent); -} - static void of_dma_deconfigure(struct device *dev) { arch_teardown_dma_ops(dev); @@ -236,7 +182,7 @@ static struct platform_device *of_platform_device_create_pdata( dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data; - of_dma_configure(&dev->dev); + of_dma_configure(&dev->dev, dev->dev.of_node); if (of_device_add(dev) != 0) { of_dma_deconfigure(&dev->dev); @@ -299,7 +245,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node, dev_set_name(&dev->dev, "%s", bus_id); else of_device_make_bus_id(&dev->dev); - of_dma_configure(&dev->dev); + of_dma_configure(&dev->dev, dev->dev.of_node); /* Allow the HW Peripheral ID to be overridden */ prop = of_get_property(node, "arm,primecell-periphid", NULL); -- cgit v1.2.3 From 0c79c81c779fe54e67c0ce8c1fda551ca57d8a35 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Tue, 3 Mar 2015 12:52:10 -0500 Subject: of: Fix size when dma-range is not used Fix the dma-range size when the DT attribute is missing, i.e., set size to dev->coherent_dma_mask + 1 instead of dev->coherent_dma_mask. Also add code to check invalid values of size configured in DT and log error. Tested-by: Suravee Suthikulpanit (AMD Seattle) Signed-off-by: Murali Karicheri Signed-off-by: Bjorn Helgaas Reviewed-by: Catalin Marinas Acked-by: Will Deacon CC: Joerg Roedel CC: Grant Likely CC: Rob Herring CC: Russell King CC: Arnd Bergmann --- drivers/of/device.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/of') diff --git a/drivers/of/device.c b/drivers/of/device.c index 31a7875b34a4..28e743888402 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -105,9 +105,24 @@ void of_dma_configure(struct device *dev, struct device_node *np) ret = of_dma_get_range(np, &dma_addr, &paddr, &size); if (ret < 0) { dma_addr = offset = 0; - size = dev->coherent_dma_mask; + size = dev->coherent_dma_mask + 1; } else { offset = PFN_DOWN(paddr - dma_addr); + + /* + * Add a work around to treat the size as mask + 1 in case + * it is defined in DT as a mask. + */ + if (size & 1) { + dev_warn(dev, "Invalid size 0x%llx for dma-range\n", + size); + size = size + 1; + } + + if (!size) { + dev_err(dev, "Adjusted size 0x%llx invalid\n", size); + return; + } dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); } -- cgit v1.2.3 From c49b8fc26e115a37eca6f7bcef1847eb80f2a4fd Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Tue, 3 Mar 2015 12:52:12 -0500 Subject: of/pci: Add of_pci_dma_configure() to update DMA configuration Add of_pci_dma_configure() to allow updating the DMA configuration of the PCI device using the configuration from DT of the parent of the root bridge device. Use the newly added APIs pci_get/put_host_bridge_device() for implementing this. [bhelgaas: fold in fix for host bridges with no parent OF device] Tested-by: Suravee Suthikulpanit (AMD Seattle) Signed-off-by: Murali Karicheri Signed-off-by: Bjorn Helgaas Reviewed-by: Catalin Marinas Acked-by: Rob Herring Acked-by: Will Deacon CC: Joerg Roedel CC: Grant Likely CC: Russell King CC: Arnd Bergmann --- drivers/of/of_pci.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/of') diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 62426d81a4d6..5751dc5b6494 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -116,6 +117,26 @@ int of_get_pci_domain_nr(struct device_node *node) } EXPORT_SYMBOL_GPL(of_get_pci_domain_nr); +/** + * of_pci_dma_configure - Setup DMA configuration + * @dev: ptr to pci_dev struct of the PCI device + * + * Function to update PCI devices's DMA configuration using the same + * info from the OF node of host bridge's parent (if any). + */ +void of_pci_dma_configure(struct pci_dev *pci_dev) +{ + struct device *dev = &pci_dev->dev; + struct device *bridge = pci_get_host_bridge_device(pci_dev); + + if (!bridge->parent) + return; + + of_dma_configure(dev, bridge->parent->of_node); + pci_put_host_bridge_device(bridge); +} +EXPORT_SYMBOL_GPL(of_pci_dma_configure); + #if defined(CONFIG_OF_ADDRESS) /** * of_pci_get_host_bridge_resources - Parse PCI host bridge resources from DT -- cgit v1.2.3 From 9a6d7298b0833614c411f774c46514efb1bd5651 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Tue, 3 Mar 2015 14:44:57 -0600 Subject: of: Calculate device DMA masks based on DT dma-range size Calculate the dma_mask and coherent_dma_mask based on the dma-range values set in DT for the device. Limit the mask to lower of the default mask and mask calculated. Signed-off-by: Murali Karicheri Signed-off-by: Bjorn Helgaas Reviewed-by: Catalin Marinas CC: Joerg Roedel CC: Grant Likely CC: Rob Herring CC: Will Deacon CC: Russell King CC: Arnd Bergmann CC: Suravee Suthikulpanit --- drivers/of/device.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/device.c b/drivers/of/device.c index 28e743888402..20c1332a0018 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -90,10 +90,11 @@ void of_dma_configure(struct device *dev, struct device_node *np) struct iommu_ops *iommu; /* - * Set default dma-mask to 32 bit. Drivers are expected to setup - * the correct supported dma_mask. + * Set default coherent_dma_mask to 32 bit. Drivers are expected to + * setup the correct supported mask. */ - dev->coherent_dma_mask = DMA_BIT_MASK(32); + if (!dev->coherent_dma_mask) + dev->coherent_dma_mask = DMA_BIT_MASK(32); /* * Set it to coherent_dma_mask by default if the architecture @@ -128,6 +129,15 @@ void of_dma_configure(struct device *dev, struct device_node *np) dev->dma_pfn_offset = offset; + /* + * Limit coherent and dma mask based on size and default mask + * set by the driver. + */ + dev->coherent_dma_mask = min(dev->coherent_dma_mask, + DMA_BIT_MASK(ilog2(dma_addr + size))); + *dev->dma_mask = min((*dev->dma_mask), + DMA_BIT_MASK(ilog2(dma_addr + size))); + coherent = of_dma_is_coherent(np); dev_dbg(dev, "device is%sdma coherent\n", coherent ? " " : " not "); -- cgit v1.2.3