aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobin Murphy <robin.murphy@arm.com>2016-09-13 05:54:14 -0400
committerWill Deacon <will.deacon@arm.com>2016-09-16 04:34:15 -0400
commit57f98d2f61e191ef9d06863c9ce3f8621f3671ef (patch)
treefd6d52282bcada86b0893b9972eaa5b8a5f219e1
parentb996444cf35e736621855e73f9d0762bd49f41f2 (diff)
iommu: Introduce iommu_fwspec
Introduce a common structure to hold the per-device firmware data that most IOMMU drivers need to keep track of. This enables us to configure much of that data from common firmware code, and consolidate a lot of the equivalent implementations, device look-up tables, etc. which are currently strewn across IOMMU drivers. This will also be enable us to address the outstanding "multiple IOMMUs on the platform bus" problem by tweaking IOMMU API calls to prefer dev->fwspec->ops before falling back to dev->bus->iommu_ops, and thus gracefully handle those troublesome systems which we currently cannot. As the first user, hook up the OF IOMMU configuration mechanism. The driver-defined nature of DT cells means that we still need the drivers to translate and add the IDs themselves, but future users such as the much less free-form ACPI IORT will be much simpler and self-contained. CC: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Suggested-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Robin Murphy <robin.murphy@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--drivers/iommu/iommu.c58
-rw-r--r--drivers/iommu/of_iommu.c8
-rw-r--r--include/linux/device.h3
-rw-r--r--include/linux/iommu.h39
4 files changed, 106 insertions, 2 deletions
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b06d93594436..9a2f1960873b 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -31,6 +31,7 @@
31#include <linux/err.h> 31#include <linux/err.h>
32#include <linux/pci.h> 32#include <linux/pci.h>
33#include <linux/bitops.h> 33#include <linux/bitops.h>
34#include <linux/property.h>
34#include <trace/events/iommu.h> 35#include <trace/events/iommu.h>
35 36
36static struct kset *iommu_group_kset; 37static struct kset *iommu_group_kset;
@@ -1613,3 +1614,60 @@ out:
1613 1614
1614 return ret; 1615 return ret;
1615} 1616}
1617
1618int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
1619 const struct iommu_ops *ops)
1620{
1621 struct iommu_fwspec *fwspec = dev->iommu_fwspec;
1622
1623 if (fwspec)
1624 return ops == fwspec->ops ? 0 : -EINVAL;
1625
1626 fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
1627 if (!fwspec)
1628 return -ENOMEM;
1629
1630 of_node_get(to_of_node(iommu_fwnode));
1631 fwspec->iommu_fwnode = iommu_fwnode;
1632 fwspec->ops = ops;
1633 dev->iommu_fwspec = fwspec;
1634 return 0;
1635}
1636EXPORT_SYMBOL_GPL(iommu_fwspec_init);
1637
1638void iommu_fwspec_free(struct device *dev)
1639{
1640 struct iommu_fwspec *fwspec = dev->iommu_fwspec;
1641
1642 if (fwspec) {
1643 fwnode_handle_put(fwspec->iommu_fwnode);
1644 kfree(fwspec);
1645 dev->iommu_fwspec = NULL;
1646 }
1647}
1648EXPORT_SYMBOL_GPL(iommu_fwspec_free);
1649
1650int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
1651{
1652 struct iommu_fwspec *fwspec = dev->iommu_fwspec;
1653 size_t size;
1654 int i;
1655
1656 if (!fwspec)
1657 return -EINVAL;
1658
1659 size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
1660 if (size > sizeof(*fwspec)) {
1661 fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL);
1662 if (!fwspec)
1663 return -ENOMEM;
1664 }
1665
1666 for (i = 0; i < num_ids; i++)
1667 fwspec->ids[fwspec->num_ids + i] = ids[i];
1668
1669 fwspec->num_ids += num_ids;
1670 dev->iommu_fwspec = fwspec;
1671 return 0;
1672}
1673EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 19e1e8f2f871..5b82862f571f 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -167,7 +167,9 @@ static const struct iommu_ops
167 return NULL; 167 return NULL;
168 168
169 ops = of_iommu_get_ops(iommu_spec.np); 169 ops = of_iommu_get_ops(iommu_spec.np);
170 if (!ops || !ops->of_xlate || ops->of_xlate(&pdev->dev, &iommu_spec)) 170 if (!ops || !ops->of_xlate ||
171 iommu_fwspec_init(&pdev->dev, &iommu_spec.np->fwnode, ops) ||
172 ops->of_xlate(&pdev->dev, &iommu_spec))
171 ops = NULL; 173 ops = NULL;
172 174
173 of_node_put(iommu_spec.np); 175 of_node_put(iommu_spec.np);
@@ -196,7 +198,9 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
196 np = iommu_spec.np; 198 np = iommu_spec.np;
197 ops = of_iommu_get_ops(np); 199 ops = of_iommu_get_ops(np);
198 200
199 if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec)) 201 if (!ops || !ops->of_xlate ||
202 iommu_fwspec_init(dev, &np->fwnode, ops) ||
203 ops->of_xlate(dev, &iommu_spec))
200 goto err_put_node; 204 goto err_put_node;
201 205
202 of_node_put(np); 206 of_node_put(np);
diff --git a/include/linux/device.h b/include/linux/device.h
index 38f02814d53a..bc41e87a969b 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -41,6 +41,7 @@ struct device_node;
41struct fwnode_handle; 41struct fwnode_handle;
42struct iommu_ops; 42struct iommu_ops;
43struct iommu_group; 43struct iommu_group;
44struct iommu_fwspec;
44 45
45struct bus_attribute { 46struct bus_attribute {
46 struct attribute attr; 47 struct attribute attr;
@@ -765,6 +766,7 @@ struct device_dma_parameters {
765 * gone away. This should be set by the allocator of the 766 * gone away. This should be set by the allocator of the
766 * device (i.e. the bus driver that discovered the device). 767 * device (i.e. the bus driver that discovered the device).
767 * @iommu_group: IOMMU group the device belongs to. 768 * @iommu_group: IOMMU group the device belongs to.
769 * @iommu_fwspec: IOMMU-specific properties supplied by firmware.
768 * 770 *
769 * @offline_disabled: If set, the device is permanently online. 771 * @offline_disabled: If set, the device is permanently online.
770 * @offline: Set after successful invocation of bus type's .offline(). 772 * @offline: Set after successful invocation of bus type's .offline().
@@ -849,6 +851,7 @@ struct device {
849 851
850 void (*release)(struct device *dev); 852 void (*release)(struct device *dev);
851 struct iommu_group *iommu_group; 853 struct iommu_group *iommu_group;
854 struct iommu_fwspec *iommu_fwspec;
852 855
853 bool offline_disabled:1; 856 bool offline_disabled:1;
854 bool offline:1; 857 bool offline:1;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index a35fb8b42e1a..436dc21318af 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -331,10 +331,32 @@ extern struct iommu_group *pci_device_group(struct device *dev);
331/* Generic device grouping function */ 331/* Generic device grouping function */
332extern struct iommu_group *generic_device_group(struct device *dev); 332extern struct iommu_group *generic_device_group(struct device *dev);
333 333
334/**
335 * struct iommu_fwspec - per-device IOMMU instance data
336 * @ops: ops for this device's IOMMU
337 * @iommu_fwnode: firmware handle for this device's IOMMU
338 * @iommu_priv: IOMMU driver private data for this device
339 * @num_ids: number of associated device IDs
340 * @ids: IDs which this device may present to the IOMMU
341 */
342struct iommu_fwspec {
343 const struct iommu_ops *ops;
344 struct fwnode_handle *iommu_fwnode;
345 void *iommu_priv;
346 unsigned int num_ids;
347 u32 ids[1];
348};
349
350int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
351 const struct iommu_ops *ops);
352void iommu_fwspec_free(struct device *dev);
353int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
354
334#else /* CONFIG_IOMMU_API */ 355#else /* CONFIG_IOMMU_API */
335 356
336struct iommu_ops {}; 357struct iommu_ops {};
337struct iommu_group {}; 358struct iommu_group {};
359struct iommu_fwspec {};
338 360
339static inline bool iommu_present(struct bus_type *bus) 361static inline bool iommu_present(struct bus_type *bus)
340{ 362{
@@ -541,6 +563,23 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link)
541{ 563{
542} 564}
543 565
566static inline int iommu_fwspec_init(struct device *dev,
567 struct fwnode_handle *iommu_fwnode,
568 const struct iommu_ops *ops)
569{
570 return -ENODEV;
571}
572
573static inline void iommu_fwspec_free(struct device *dev)
574{
575}
576
577static inline int iommu_fwspec_add_ids(struct device *dev, u32 *ids,
578 int num_ids)
579{
580 return -ENODEV;
581}
582
544#endif /* CONFIG_IOMMU_API */ 583#endif /* CONFIG_IOMMU_API */
545 584
546#endif /* __LINUX_IOMMU_H */ 585#endif /* __LINUX_IOMMU_H */