Skip to content

Commit

Permalink
Merge branches 'pci/iommu' and 'pci/resource' into next
Browse files Browse the repository at this point in the history
* pci/iommu:
  of: Calculate device DMA masks based on DT dma-range size
  arm: dma-mapping: limit IOMMU mapping size
  PCI: Update DMA configuration from DT
  of/pci: Add of_pci_dma_configure() to update DMA configuration
  PCI: Add helper functions pci_get[put]_host_bridge_device()
  of: Fix size when dma-range is not used
  of: Move of_dma_configure() to device.c to help re-use
  of: iommu: Add ptr to OF node arg to of_iommu_configure()

* pci/resource:
  PCI: Fail pci_ioremap_bar() on unassigned resources
  PCI: Show driver, BAR#, and resource on pci_ioremap_bar() failure
  PCI: Mark invalid BARs as unassigned
  PNP: Don't check for overlaps with unassigned PCI BARs
  • Loading branch information
Bjorn Helgaas committed Mar 19, 2015
3 parents 06e5801 + 9a6d729 + 646c028 commit 85e8a0a
Show file tree
Hide file tree
Showing 14 changed files with 164 additions and 67 deletions.
7 changes: 7 additions & 0 deletions arch/arm/mm/dma-mapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -2027,6 +2027,13 @@ static bool arm_setup_iommu_dma_ops(struct device *dev, u64 dma_base, u64 size,
if (!iommu)
return false;

/*
* currently arm_iommu_create_mapping() takes a max of size_t
* for size param. So check this limit for now.
*/
if (size > SIZE_MAX)
return false;

mapping = arm_iommu_create_mapping(dev->bus, dma_base, size);
if (IS_ERR(mapping)) {
pr_warn("Failed to create %llu-byte IOMMU mapping for device %s\n",
Expand Down
10 changes: 8 additions & 2 deletions drivers/iommu/of_iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,19 +133,25 @@ struct iommu_ops *of_iommu_get_ops(struct device_node *np)
return ops;
}

struct iommu_ops *of_iommu_configure(struct device *dev)
struct iommu_ops *of_iommu_configure(struct device *dev,
struct device_node *master_np)
{
struct of_phandle_args iommu_spec;
struct device_node *np;
struct iommu_ops *ops = NULL;
int idx = 0;

if (dev_is_pci(dev)) {
dev_err(dev, "IOMMU is currently not supported for PCI\n");
return NULL;
}

/*
* We don't currently walk up the tree looking for a parent IOMMU.
* See the `Notes:' section of
* Documentation/devicetree/bindings/iommu/iommu.txt
*/
while (!of_parse_phandle_with_args(dev->of_node, "iommus",
while (!of_parse_phandle_with_args(master_np, "iommus",
"#iommu-cells", idx,
&iommu_spec)) {
np = iommu_spec.np;
Expand Down
84 changes: 84 additions & 0 deletions drivers/of/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/of_iommu.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
Expand Down Expand Up @@ -66,6 +69,87 @@ 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 coherent_dma_mask to 32 bit. Drivers are expected to
* setup the correct supported mask.
*/
if (!dev->coherent_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 + 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);
}

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 ");

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);
Expand Down
21 changes: 21 additions & 0 deletions drivers/of/of_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <linux/export.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_pci.h>
#include <linux/slab.h>

Expand Down Expand Up @@ -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
Expand Down
58 changes: 2 additions & 56 deletions drivers/of/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_iommu.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
Expand Down Expand Up @@ -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_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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 14 additions & 0 deletions drivers/pci/host-bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ static struct pci_host_bridge *find_pci_host_bridge(struct pci_bus *bus)
return to_pci_host_bridge(root_bus->bridge);
}

struct device *pci_get_host_bridge_device(struct pci_dev *dev)
{
struct pci_bus *root_bus = find_pci_root_bus(dev->bus);
struct device *bridge = root_bus->bridge;

kobject_get(&bridge->kobj);
return bridge;
}

void pci_put_host_bridge_device(struct device *dev)
{
kobject_put(&dev->kobj);
}

void pci_set_host_bridge_release(struct pci_host_bridge *bridge,
void (*release_fn)(struct pci_host_bridge *),
void *release_data)
Expand Down
9 changes: 5 additions & 4 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,16 @@ EXPORT_SYMBOL_GPL(pci_bus_max_busnr);
#ifdef CONFIG_HAS_IOMEM
void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar)
{
struct resource *res = &pdev->resource[bar];

/*
* Make sure the BAR is actually a memory resource, not an IO resource
*/
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
WARN_ON(1);
if (res->flags & IORESOURCE_UNSET || !(res->flags & IORESOURCE_MEM)) {
dev_warn(&pdev->dev, "can't ioremap BAR %d: %pR\n", bar, res);
return NULL;
}
return ioremap_nocache(pci_resource_start(pdev, bar),
pci_resource_len(pdev, bar));
return ioremap_nocache(res->start, resource_size(res));
}
EXPORT_SYMBOL_GPL(pci_ioremap_bar);
#endif
Expand Down
2 changes: 2 additions & 0 deletions drivers/pci/probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/of_pci.h>
#include <linux/pci_hotplug.h>
#include <linux/slab.h>
#include <linux/module.h>
Expand Down Expand Up @@ -1520,6 +1521,7 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
dev->dev.dma_mask = &dev->dma_mask;
dev->dev.dma_parms = &dev->dma_parms;
dev->dev.coherent_dma_mask = 0xffffffffull;
of_pci_dma_configure(dev);

pci_set_dma_max_seg_size(dev, 65536);
pci_set_dma_seg_boundary(dev, 0xffffffff);
Expand Down
2 changes: 2 additions & 0 deletions drivers/pci/setup-res.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,15 @@ int pci_claim_resource(struct pci_dev *dev, int resource)
if (!root) {
dev_info(&dev->dev, "can't claim BAR %d %pR: no compatible bridge window\n",
resource, res);
res->flags |= IORESOURCE_UNSET;
return -EINVAL;
}

conflict = request_resource_conflict(root, res);
if (conflict) {
dev_info(&dev->dev, "can't claim BAR %d %pR: address conflict with %s %pR\n",
resource, res, conflict->name, conflict);
res->flags |= IORESOURCE_UNSET;
return -EBUSY;
}

Expand Down
9 changes: 6 additions & 3 deletions drivers/pnp/quirks.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,16 @@ static void quirk_system_pci_resources(struct pnp_dev *dev)
*/
for_each_pci_dev(pdev) {
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
unsigned long type;
unsigned long flags, type;

type = pci_resource_flags(pdev, i) &
(IORESOURCE_IO | IORESOURCE_MEM);
flags = pci_resource_flags(pdev, i);
type = flags & (IORESOURCE_IO | IORESOURCE_MEM);
if (!type || pci_resource_len(pdev, i) == 0)
continue;

if (flags & IORESOURCE_UNSET)
continue;

pci_start = pci_resource_start(pdev, i);
pci_end = pci_resource_end(pdev, i);
for (j = 0;
Expand Down
3 changes: 3 additions & 0 deletions include/linux/of_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ static inline struct device_node *of_cpu_device_node_get(int cpu)
return of_node_get(cpu_dev->of_node);
}

void of_dma_configure(struct device *dev, struct device_node *np);
#else /* CONFIG_OF */

static inline int of_driver_match_device(struct device *dev,
Expand Down Expand Up @@ -90,6 +91,8 @@ static inline struct device_node *of_cpu_device_node_get(int cpu)
{
return NULL;
}
static inline void of_dma_configure(struct device *dev, struct device_node *np)
{}
#endif /* CONFIG_OF */

#endif /* _LINUX_OF_DEVICE_H */
6 changes: 4 additions & 2 deletions include/linux/of_iommu.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ extern int of_get_dma_window(struct device_node *dn, const char *prefix,
size_t *size);

extern void of_iommu_init(void);
extern struct iommu_ops *of_iommu_configure(struct device *dev);
extern struct iommu_ops *of_iommu_configure(struct device *dev,
struct device_node *master_np);

#else

Expand All @@ -24,7 +25,8 @@ static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
}

static inline void of_iommu_init(void) { }
static inline struct iommu_ops *of_iommu_configure(struct device *dev)
static inline struct iommu_ops *of_iommu_configure(struct device *dev,
struct device_node *master_np)
{
return NULL;
}
Expand Down
Loading

0 comments on commit 85e8a0a

Please sign in to comment.