summaryrefslogtreecommitdiffstats
path: root/drivers/iommu/iommu.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-10-30 15:46:19 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2011-10-30 15:46:19 -0700
commit0cfdc724390fb9370f27bb9a133eadf69114dd21 (patch)
tree2abfb0112c46c837c6b42007eadfc389293b7710 /drivers/iommu/iommu.c
parentb48aeab65e9fc4b0c9757c5fbc1d722544eb8786 (diff)
parent1abb4ba596a91a839f82e0c9c837b777d574e83d (diff)
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (33 commits) iommu/core: Remove global iommu_ops and register_iommu iommu/msm: Use bus_set_iommu instead of register_iommu iommu/omap: Use bus_set_iommu instead of register_iommu iommu/vt-d: Use bus_set_iommu instead of register_iommu iommu/amd: Use bus_set_iommu instead of register_iommu iommu/core: Use bus->iommu_ops in the iommu-api iommu/core: Convert iommu_found to iommu_present iommu/core: Add bus_type parameter to iommu_domain_alloc Driver core: Add iommu_ops to bus_type iommu/core: Define iommu_ops and register_iommu only with CONFIG_IOMMU_API iommu/amd: Fix wrong shift direction iommu/omap: always provide iommu debug code iommu/core: let drivers know if an iommu fault handler isn't installed iommu/core: export iommu_set_fault_handler() iommu/omap: Fix build error with !IOMMU_SUPPORT iommu/omap: Migrate to the generic fault report mechanism iommu/core: Add fault reporting mechanism iommu/core: Use PAGE_SIZE instead of hard-coded value iommu/core: use the existing IS_ALIGNED macro iommu/msm: ->unmap() should return order of unmapped page ... Fixup trivial conflicts in drivers/iommu/Makefile: "move omap iommu to dedicated iommu folder" vs "Rename the DMAR and INTR_REMAP config options" just happened to touch lines next to each other.
Diffstat (limited to 'drivers/iommu/iommu.c')
-rw-r--r--drivers/iommu/iommu.c114
1 files changed, 89 insertions, 25 deletions
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 6e6b6a11b3c..2fb2963df55 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -16,6 +16,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/device.h>
+#include <linux/kernel.h>
#include <linux/bug.h>
#include <linux/types.h>
#include <linux/module.h>
@@ -23,32 +25,78 @@
#include <linux/errno.h>
#include <linux/iommu.h>
-static struct iommu_ops *iommu_ops;
+static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops)
+{
+}
-void register_iommu(struct iommu_ops *ops)
+/**
+ * bus_set_iommu - set iommu-callbacks for the bus
+ * @bus: bus.
+ * @ops: the callbacks provided by the iommu-driver
+ *
+ * This function is called by an iommu driver to set the iommu methods
+ * used for a particular bus. Drivers for devices on that bus can use
+ * the iommu-api after these ops are registered.
+ * This special function is needed because IOMMUs are usually devices on
+ * the bus itself, so the iommu drivers are not initialized when the bus
+ * is set up. With this function the iommu-driver can set the iommu-ops
+ * afterwards.
+ */
+int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops)
{
- if (iommu_ops)
- BUG();
+ if (bus->iommu_ops != NULL)
+ return -EBUSY;
+
+ bus->iommu_ops = ops;
+
+ /* Do IOMMU specific setup for this bus-type */
+ iommu_bus_init(bus, ops);
- iommu_ops = ops;
+ return 0;
}
+EXPORT_SYMBOL_GPL(bus_set_iommu);
-bool iommu_found(void)
+bool iommu_present(struct bus_type *bus)
{
- return iommu_ops != NULL;
+ return bus->iommu_ops != NULL;
}
-EXPORT_SYMBOL_GPL(iommu_found);
+EXPORT_SYMBOL_GPL(iommu_present);
-struct iommu_domain *iommu_domain_alloc(void)
+/**
+ * iommu_set_fault_handler() - set a fault handler for an iommu domain
+ * @domain: iommu domain
+ * @handler: fault handler
+ *
+ * This function should be used by IOMMU users which want to be notified
+ * whenever an IOMMU fault happens.
+ *
+ * The fault handler itself should return 0 on success, and an appropriate
+ * error code otherwise.
+ */
+void iommu_set_fault_handler(struct iommu_domain *domain,
+ iommu_fault_handler_t handler)
+{
+ BUG_ON(!domain);
+
+ domain->handler = handler;
+}
+EXPORT_SYMBOL_GPL(iommu_set_fault_handler);
+
+struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
{
struct iommu_domain *domain;
int ret;
+ if (bus == NULL || bus->iommu_ops == NULL)
+ return NULL;
+
domain = kmalloc(sizeof(*domain), GFP_KERNEL);
if (!domain)
return NULL;
- ret = iommu_ops->domain_init(domain);
+ domain->ops = bus->iommu_ops;
+
+ ret = domain->ops->domain_init(domain);
if (ret)
goto out_free;
@@ -63,62 +111,78 @@ EXPORT_SYMBOL_GPL(iommu_domain_alloc);
void iommu_domain_free(struct iommu_domain *domain)
{
- iommu_ops->domain_destroy(domain);
+ if (likely(domain->ops->domain_destroy != NULL))
+ domain->ops->domain_destroy(domain);
+
kfree(domain);
}
EXPORT_SYMBOL_GPL(iommu_domain_free);
int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
{
- return iommu_ops->attach_dev(domain, dev);
+ if (unlikely(domain->ops->attach_dev == NULL))
+ return -ENODEV;
+
+ return domain->ops->attach_dev(domain, dev);
}
EXPORT_SYMBOL_GPL(iommu_attach_device);
void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
{
- iommu_ops->detach_dev(domain, dev);
+ if (unlikely(domain->ops->detach_dev == NULL))
+ return;
+
+ domain->ops->detach_dev(domain, dev);
}
EXPORT_SYMBOL_GPL(iommu_detach_device);
phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
unsigned long iova)
{
- return iommu_ops->iova_to_phys(domain, iova);
+ if (unlikely(domain->ops->iova_to_phys == NULL))
+ return 0;
+
+ return domain->ops->iova_to_phys(domain, iova);
}
EXPORT_SYMBOL_GPL(iommu_iova_to_phys);
int iommu_domain_has_cap(struct iommu_domain *domain,
unsigned long cap)
{
- return iommu_ops->domain_has_cap(domain, cap);
+ if (unlikely(domain->ops->domain_has_cap == NULL))
+ return 0;
+
+ return domain->ops->domain_has_cap(domain, cap);
}
EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
int iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, int gfp_order, int prot)
{
- unsigned long invalid_mask;
size_t size;
- size = 0x1000UL << gfp_order;
- invalid_mask = size - 1;
+ if (unlikely(domain->ops->map == NULL))
+ return -ENODEV;
- BUG_ON((iova | paddr) & invalid_mask);
+ size = PAGE_SIZE << gfp_order;
- return iommu_ops->map(domain, iova, paddr, gfp_order, prot);
+ BUG_ON(!IS_ALIGNED(iova | paddr, size));
+
+ return domain->ops->map(domain, iova, paddr, gfp_order, prot);
}
EXPORT_SYMBOL_GPL(iommu_map);
int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order)
{
- unsigned long invalid_mask;
size_t size;
- size = 0x1000UL << gfp_order;
- invalid_mask = size - 1;
+ if (unlikely(domain->ops->unmap == NULL))
+ return -ENODEV;
+
+ size = PAGE_SIZE << gfp_order;
- BUG_ON(iova & invalid_mask);
+ BUG_ON(!IS_ALIGNED(iova, size));
- return iommu_ops->unmap(domain, iova, gfp_order);
+ return domain->ops->unmap(domain, iova, gfp_order);
}
EXPORT_SYMBOL_GPL(iommu_unmap);