|
@@ -2,6 +2,7 @@
|
|
* omap iommu: tlb and pagetable primitives
|
|
* omap iommu: tlb and pagetable primitives
|
|
*
|
|
*
|
|
* Copyright (C) 2008-2010 Nokia Corporation
|
|
* Copyright (C) 2008-2010 Nokia Corporation
|
|
|
|
+ * Copyright (C) 2013-2017 Texas Instruments Incorporated - http://www.ti.com/
|
|
*
|
|
*
|
|
* Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>,
|
|
* Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>,
|
|
* Paul Mundt and Toshihiro Kobayashi
|
|
* Paul Mundt and Toshihiro Kobayashi
|
|
@@ -71,13 +72,23 @@ static struct omap_iommu_domain *to_omap_domain(struct iommu_domain *dom)
|
|
**/
|
|
**/
|
|
void omap_iommu_save_ctx(struct device *dev)
|
|
void omap_iommu_save_ctx(struct device *dev)
|
|
{
|
|
{
|
|
- struct omap_iommu *obj = dev_to_omap_iommu(dev);
|
|
|
|
- u32 *p = obj->ctx;
|
|
|
|
|
|
+ struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
|
|
|
+ struct omap_iommu *obj;
|
|
|
|
+ u32 *p;
|
|
int i;
|
|
int i;
|
|
|
|
|
|
- for (i = 0; i < (MMU_REG_SIZE / sizeof(u32)); i++) {
|
|
|
|
- p[i] = iommu_read_reg(obj, i * sizeof(u32));
|
|
|
|
- dev_dbg(obj->dev, "%s\t[%02d] %08x\n", __func__, i, p[i]);
|
|
|
|
|
|
+ if (!arch_data)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ while (arch_data->iommu_dev) {
|
|
|
|
+ obj = arch_data->iommu_dev;
|
|
|
|
+ p = obj->ctx;
|
|
|
|
+ for (i = 0; i < (MMU_REG_SIZE / sizeof(u32)); i++) {
|
|
|
|
+ p[i] = iommu_read_reg(obj, i * sizeof(u32));
|
|
|
|
+ dev_dbg(obj->dev, "%s\t[%02d] %08x\n", __func__, i,
|
|
|
|
+ p[i]);
|
|
|
|
+ }
|
|
|
|
+ arch_data++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
|
|
EXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
|
|
@@ -88,13 +99,23 @@ EXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
|
|
**/
|
|
**/
|
|
void omap_iommu_restore_ctx(struct device *dev)
|
|
void omap_iommu_restore_ctx(struct device *dev)
|
|
{
|
|
{
|
|
- struct omap_iommu *obj = dev_to_omap_iommu(dev);
|
|
|
|
- u32 *p = obj->ctx;
|
|
|
|
|
|
+ struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
|
|
|
+ struct omap_iommu *obj;
|
|
|
|
+ u32 *p;
|
|
int i;
|
|
int i;
|
|
|
|
|
|
- for (i = 0; i < (MMU_REG_SIZE / sizeof(u32)); i++) {
|
|
|
|
- iommu_write_reg(obj, p[i], i * sizeof(u32));
|
|
|
|
- dev_dbg(obj->dev, "%s\t[%02d] %08x\n", __func__, i, p[i]);
|
|
|
|
|
|
+ if (!arch_data)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ while (arch_data->iommu_dev) {
|
|
|
|
+ obj = arch_data->iommu_dev;
|
|
|
|
+ p = obj->ctx;
|
|
|
|
+ for (i = 0; i < (MMU_REG_SIZE / sizeof(u32)); i++) {
|
|
|
|
+ iommu_write_reg(obj, p[i], i * sizeof(u32));
|
|
|
|
+ dev_dbg(obj->dev, "%s\t[%02d] %08x\n", __func__, i,
|
|
|
|
+ p[i]);
|
|
|
|
+ }
|
|
|
|
+ arch_data++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(omap_iommu_restore_ctx);
|
|
EXPORT_SYMBOL_GPL(omap_iommu_restore_ctx);
|
|
@@ -893,6 +914,24 @@ static void omap_iommu_detach(struct omap_iommu *obj)
|
|
dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
|
|
dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static bool omap_iommu_can_register(struct platform_device *pdev)
|
|
|
|
+{
|
|
|
|
+ struct device_node *np = pdev->dev.of_node;
|
|
|
|
+
|
|
|
|
+ if (!of_device_is_compatible(np, "ti,dra7-dsp-iommu"))
|
|
|
|
+ return true;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * restrict IOMMU core registration only for processor-port MDMA MMUs
|
|
|
|
+ * on DRA7 DSPs
|
|
|
|
+ */
|
|
|
|
+ if ((!strcmp(dev_name(&pdev->dev), "40d01000.mmu")) ||
|
|
|
|
+ (!strcmp(dev_name(&pdev->dev), "41501000.mmu")))
|
|
|
|
+ return true;
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
static int omap_iommu_dra7_get_dsp_system_cfg(struct platform_device *pdev,
|
|
static int omap_iommu_dra7_get_dsp_system_cfg(struct platform_device *pdev,
|
|
struct omap_iommu *obj)
|
|
struct omap_iommu *obj)
|
|
{
|
|
{
|
|
@@ -984,19 +1023,22 @@ static int omap_iommu_probe(struct platform_device *pdev)
|
|
return err;
|
|
return err;
|
|
platform_set_drvdata(pdev, obj);
|
|
platform_set_drvdata(pdev, obj);
|
|
|
|
|
|
- obj->group = iommu_group_alloc();
|
|
|
|
- if (IS_ERR(obj->group))
|
|
|
|
- return PTR_ERR(obj->group);
|
|
|
|
|
|
+ if (omap_iommu_can_register(pdev)) {
|
|
|
|
+ obj->group = iommu_group_alloc();
|
|
|
|
+ if (IS_ERR(obj->group))
|
|
|
|
+ return PTR_ERR(obj->group);
|
|
|
|
|
|
- err = iommu_device_sysfs_add(&obj->iommu, obj->dev, NULL, obj->name);
|
|
|
|
- if (err)
|
|
|
|
- goto out_group;
|
|
|
|
|
|
+ err = iommu_device_sysfs_add(&obj->iommu, obj->dev, NULL,
|
|
|
|
+ obj->name);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out_group;
|
|
|
|
|
|
- iommu_device_set_ops(&obj->iommu, &omap_iommu_ops);
|
|
|
|
|
|
+ iommu_device_set_ops(&obj->iommu, &omap_iommu_ops);
|
|
|
|
|
|
- err = iommu_device_register(&obj->iommu);
|
|
|
|
- if (err)
|
|
|
|
- goto out_sysfs;
|
|
|
|
|
|
+ err = iommu_device_register(&obj->iommu);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out_sysfs;
|
|
|
|
+ }
|
|
|
|
|
|
pm_runtime_irq_safe(obj->dev);
|
|
pm_runtime_irq_safe(obj->dev);
|
|
pm_runtime_enable(obj->dev);
|
|
pm_runtime_enable(obj->dev);
|
|
@@ -1018,11 +1060,13 @@ static int omap_iommu_remove(struct platform_device *pdev)
|
|
{
|
|
{
|
|
struct omap_iommu *obj = platform_get_drvdata(pdev);
|
|
struct omap_iommu *obj = platform_get_drvdata(pdev);
|
|
|
|
|
|
- iommu_group_put(obj->group);
|
|
|
|
- obj->group = NULL;
|
|
|
|
|
|
+ if (obj->group) {
|
|
|
|
+ iommu_group_put(obj->group);
|
|
|
|
+ obj->group = NULL;
|
|
|
|
|
|
- iommu_device_sysfs_remove(&obj->iommu);
|
|
|
|
- iommu_device_unregister(&obj->iommu);
|
|
|
|
|
|
+ iommu_device_sysfs_remove(&obj->iommu);
|
|
|
|
+ iommu_device_unregister(&obj->iommu);
|
|
|
|
+ }
|
|
|
|
|
|
omap_iommu_debugfs_remove(obj);
|
|
omap_iommu_debugfs_remove(obj);
|
|
|
|
|
|
@@ -1068,11 +1112,13 @@ static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
|
|
phys_addr_t pa, size_t bytes, int prot)
|
|
phys_addr_t pa, size_t bytes, int prot)
|
|
{
|
|
{
|
|
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
|
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
|
- struct omap_iommu *oiommu = omap_domain->iommu_dev;
|
|
|
|
- struct device *dev = oiommu->dev;
|
|
|
|
|
|
+ struct device *dev = omap_domain->dev;
|
|
|
|
+ struct omap_iommu_device *iommu;
|
|
|
|
+ struct omap_iommu *oiommu;
|
|
struct iotlb_entry e;
|
|
struct iotlb_entry e;
|
|
int omap_pgsz;
|
|
int omap_pgsz;
|
|
- u32 ret;
|
|
|
|
|
|
+ u32 ret = -EINVAL;
|
|
|
|
+ int i;
|
|
|
|
|
|
omap_pgsz = bytes_to_iopgsz(bytes);
|
|
omap_pgsz = bytes_to_iopgsz(bytes);
|
|
if (omap_pgsz < 0) {
|
|
if (omap_pgsz < 0) {
|
|
@@ -1084,9 +1130,24 @@ static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
|
|
|
|
|
|
iotlb_init_entry(&e, da, pa, omap_pgsz);
|
|
iotlb_init_entry(&e, da, pa, omap_pgsz);
|
|
|
|
|
|
- ret = omap_iopgtable_store_entry(oiommu, &e);
|
|
|
|
- if (ret)
|
|
|
|
- dev_err(dev, "omap_iopgtable_store_entry failed: %d\n", ret);
|
|
|
|
|
|
+ iommu = omap_domain->iommus;
|
|
|
|
+ for (i = 0; i < omap_domain->num_iommus; i++, iommu++) {
|
|
|
|
+ oiommu = iommu->iommu_dev;
|
|
|
|
+ ret = omap_iopgtable_store_entry(oiommu, &e);
|
|
|
|
+ if (ret) {
|
|
|
|
+ dev_err(dev, "omap_iopgtable_store_entry failed: %d\n",
|
|
|
|
+ ret);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (ret) {
|
|
|
|
+ while (i--) {
|
|
|
|
+ iommu--;
|
|
|
|
+ oiommu = iommu->iommu_dev;
|
|
|
|
+ iopgtable_clear_entry(oiommu, da);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -1095,12 +1156,90 @@ static size_t omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
|
|
size_t size)
|
|
size_t size)
|
|
{
|
|
{
|
|
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
|
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
|
- struct omap_iommu *oiommu = omap_domain->iommu_dev;
|
|
|
|
- struct device *dev = oiommu->dev;
|
|
|
|
|
|
+ struct device *dev = omap_domain->dev;
|
|
|
|
+ struct omap_iommu_device *iommu;
|
|
|
|
+ struct omap_iommu *oiommu;
|
|
|
|
+ bool error = false;
|
|
|
|
+ size_t bytes = 0;
|
|
|
|
+ int i;
|
|
|
|
|
|
dev_dbg(dev, "unmapping da 0x%lx size %u\n", da, size);
|
|
dev_dbg(dev, "unmapping da 0x%lx size %u\n", da, size);
|
|
|
|
|
|
- return iopgtable_clear_entry(oiommu, da);
|
|
|
|
|
|
+ iommu = omap_domain->iommus;
|
|
|
|
+ for (i = 0; i < omap_domain->num_iommus; i++, iommu++) {
|
|
|
|
+ oiommu = iommu->iommu_dev;
|
|
|
|
+ bytes = iopgtable_clear_entry(oiommu, da);
|
|
|
|
+ if (!bytes)
|
|
|
|
+ error = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * simplify return - we are only checking if any of the iommus
|
|
|
|
+ * reported an error, but not if all of them are unmapping the
|
|
|
|
+ * same number of entries. This should not occur due to the
|
|
|
|
+ * mirror programming.
|
|
|
|
+ */
|
|
|
|
+ return error ? 0 : bytes;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int omap_iommu_count(struct device *dev)
|
|
|
|
+{
|
|
|
|
+ struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
|
|
|
+ int count = 0;
|
|
|
|
+
|
|
|
|
+ while (arch_data->iommu_dev) {
|
|
|
|
+ count++;
|
|
|
|
+ arch_data++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return count;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* caller should call cleanup if this function fails */
|
|
|
|
+static int omap_iommu_attach_init(struct device *dev,
|
|
|
|
+ struct omap_iommu_domain *odomain)
|
|
|
|
+{
|
|
|
|
+ struct omap_iommu_device *iommu;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ odomain->num_iommus = omap_iommu_count(dev);
|
|
|
|
+ if (!odomain->num_iommus)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ odomain->iommus = kcalloc(odomain->num_iommus, sizeof(*iommu),
|
|
|
|
+ GFP_ATOMIC);
|
|
|
|
+ if (!odomain->iommus)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ iommu = odomain->iommus;
|
|
|
|
+ for (i = 0; i < odomain->num_iommus; i++, iommu++) {
|
|
|
|
+ iommu->pgtable = kzalloc(IOPGD_TABLE_SIZE, GFP_ATOMIC);
|
|
|
|
+ if (!iommu->pgtable)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * should never fail, but please keep this around to ensure
|
|
|
|
+ * we keep the hardware happy
|
|
|
|
+ */
|
|
|
|
+ if (WARN_ON(!IS_ALIGNED((long)iommu->pgtable,
|
|
|
|
+ IOPGD_TABLE_SIZE)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void omap_iommu_detach_fini(struct omap_iommu_domain *odomain)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+ struct omap_iommu_device *iommu = odomain->iommus;
|
|
|
|
+
|
|
|
|
+ for (i = 0; iommu && i < odomain->num_iommus; i++, iommu++)
|
|
|
|
+ kfree(iommu->pgtable);
|
|
|
|
+
|
|
|
|
+ kfree(odomain->iommus);
|
|
|
|
+ odomain->num_iommus = 0;
|
|
|
|
+ odomain->iommus = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
static int
|
|
@@ -1108,8 +1247,10 @@ omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
|
{
|
|
{
|
|
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
|
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
|
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
|
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
|
|
|
+ struct omap_iommu_device *iommu;
|
|
struct omap_iommu *oiommu;
|
|
struct omap_iommu *oiommu;
|
|
int ret = 0;
|
|
int ret = 0;
|
|
|
|
+ int i;
|
|
|
|
|
|
if (!arch_data || !arch_data->iommu_dev) {
|
|
if (!arch_data || !arch_data->iommu_dev) {
|
|
dev_err(dev, "device doesn't have an associated iommu\n");
|
|
dev_err(dev, "device doesn't have an associated iommu\n");
|
|
@@ -1125,19 +1266,42 @@ omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
- oiommu = arch_data->iommu_dev;
|
|
|
|
-
|
|
|
|
- /* get a handle to and enable the omap iommu */
|
|
|
|
- ret = omap_iommu_attach(oiommu, omap_domain->pgtable);
|
|
|
|
|
|
+ ret = omap_iommu_attach_init(dev, omap_domain);
|
|
if (ret) {
|
|
if (ret) {
|
|
- dev_err(dev, "can't get omap iommu: %d\n", ret);
|
|
|
|
- goto out;
|
|
|
|
|
|
+ dev_err(dev, "failed to allocate required iommu data %d\n",
|
|
|
|
+ ret);
|
|
|
|
+ goto init_fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ iommu = omap_domain->iommus;
|
|
|
|
+ for (i = 0; i < omap_domain->num_iommus; i++, iommu++, arch_data++) {
|
|
|
|
+ /* configure and enable the omap iommu */
|
|
|
|
+ oiommu = arch_data->iommu_dev;
|
|
|
|
+ ret = omap_iommu_attach(oiommu, iommu->pgtable);
|
|
|
|
+ if (ret) {
|
|
|
|
+ dev_err(dev, "can't get omap iommu: %d\n", ret);
|
|
|
|
+ goto attach_fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ oiommu->domain = domain;
|
|
|
|
+ iommu->iommu_dev = oiommu;
|
|
}
|
|
}
|
|
|
|
|
|
- omap_domain->iommu_dev = oiommu;
|
|
|
|
omap_domain->dev = dev;
|
|
omap_domain->dev = dev;
|
|
- oiommu->domain = domain;
|
|
|
|
|
|
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+attach_fail:
|
|
|
|
+ while (i--) {
|
|
|
|
+ iommu--;
|
|
|
|
+ arch_data--;
|
|
|
|
+ oiommu = iommu->iommu_dev;
|
|
|
|
+ omap_iommu_detach(oiommu);
|
|
|
|
+ iommu->iommu_dev = NULL;
|
|
|
|
+ oiommu->domain = NULL;
|
|
|
|
+ }
|
|
|
|
+init_fail:
|
|
|
|
+ omap_iommu_detach_fini(omap_domain);
|
|
out:
|
|
out:
|
|
spin_unlock(&omap_domain->lock);
|
|
spin_unlock(&omap_domain->lock);
|
|
return ret;
|
|
return ret;
|
|
@@ -1146,7 +1310,10 @@ out:
|
|
static void _omap_iommu_detach_dev(struct omap_iommu_domain *omap_domain,
|
|
static void _omap_iommu_detach_dev(struct omap_iommu_domain *omap_domain,
|
|
struct device *dev)
|
|
struct device *dev)
|
|
{
|
|
{
|
|
- struct omap_iommu *oiommu = dev_to_omap_iommu(dev);
|
|
|
|
|
|
+ struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
|
|
|
+ struct omap_iommu_device *iommu = omap_domain->iommus;
|
|
|
|
+ struct omap_iommu *oiommu;
|
|
|
|
+ int i;
|
|
|
|
|
|
if (!omap_domain->dev) {
|
|
if (!omap_domain->dev) {
|
|
dev_err(dev, "domain has no attached device\n");
|
|
dev_err(dev, "domain has no attached device\n");
|
|
@@ -1159,13 +1326,24 @@ static void _omap_iommu_detach_dev(struct omap_iommu_domain *omap_domain,
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- iopgtable_clear_entry_all(oiommu);
|
|
|
|
|
|
+ /*
|
|
|
|
+ * cleanup in the reverse order of attachment - this addresses
|
|
|
|
+ * any h/w dependencies between multiple instances, if any
|
|
|
|
+ */
|
|
|
|
+ iommu += (omap_domain->num_iommus - 1);
|
|
|
|
+ arch_data += (omap_domain->num_iommus - 1);
|
|
|
|
+ for (i = 0; i < omap_domain->num_iommus; i++, iommu--, arch_data--) {
|
|
|
|
+ oiommu = iommu->iommu_dev;
|
|
|
|
+ iopgtable_clear_entry_all(oiommu);
|
|
|
|
+
|
|
|
|
+ omap_iommu_detach(oiommu);
|
|
|
|
+ iommu->iommu_dev = NULL;
|
|
|
|
+ oiommu->domain = NULL;
|
|
|
|
+ }
|
|
|
|
|
|
- omap_iommu_detach(oiommu);
|
|
|
|
|
|
+ omap_iommu_detach_fini(omap_domain);
|
|
|
|
|
|
- omap_domain->iommu_dev = NULL;
|
|
|
|
omap_domain->dev = NULL;
|
|
omap_domain->dev = NULL;
|
|
- oiommu->domain = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static void omap_iommu_detach_dev(struct iommu_domain *domain,
|
|
static void omap_iommu_detach_dev(struct iommu_domain *domain,
|
|
@@ -1187,18 +1365,7 @@ static struct iommu_domain *omap_iommu_domain_alloc(unsigned type)
|
|
|
|
|
|
omap_domain = kzalloc(sizeof(*omap_domain), GFP_KERNEL);
|
|
omap_domain = kzalloc(sizeof(*omap_domain), GFP_KERNEL);
|
|
if (!omap_domain)
|
|
if (!omap_domain)
|
|
- goto out;
|
|
|
|
-
|
|
|
|
- omap_domain->pgtable = kzalloc(IOPGD_TABLE_SIZE, GFP_KERNEL);
|
|
|
|
- if (!omap_domain->pgtable)
|
|
|
|
- goto fail_nomem;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * should never fail, but please keep this around to ensure
|
|
|
|
- * we keep the hardware happy
|
|
|
|
- */
|
|
|
|
- if (WARN_ON(!IS_ALIGNED((long)omap_domain->pgtable, IOPGD_TABLE_SIZE)))
|
|
|
|
- goto fail_align;
|
|
|
|
|
|
+ return NULL;
|
|
|
|
|
|
spin_lock_init(&omap_domain->lock);
|
|
spin_lock_init(&omap_domain->lock);
|
|
|
|
|
|
@@ -1207,13 +1374,6 @@ static struct iommu_domain *omap_iommu_domain_alloc(unsigned type)
|
|
omap_domain->domain.geometry.force_aperture = true;
|
|
omap_domain->domain.geometry.force_aperture = true;
|
|
|
|
|
|
return &omap_domain->domain;
|
|
return &omap_domain->domain;
|
|
-
|
|
|
|
-fail_align:
|
|
|
|
- kfree(omap_domain->pgtable);
|
|
|
|
-fail_nomem:
|
|
|
|
- kfree(omap_domain);
|
|
|
|
-out:
|
|
|
|
- return NULL;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static void omap_iommu_domain_free(struct iommu_domain *domain)
|
|
static void omap_iommu_domain_free(struct iommu_domain *domain)
|
|
@@ -1227,7 +1387,6 @@ static void omap_iommu_domain_free(struct iommu_domain *domain)
|
|
if (omap_domain->dev)
|
|
if (omap_domain->dev)
|
|
_omap_iommu_detach_dev(omap_domain, omap_domain->dev);
|
|
_omap_iommu_detach_dev(omap_domain, omap_domain->dev);
|
|
|
|
|
|
- kfree(omap_domain->pgtable);
|
|
|
|
kfree(omap_domain);
|
|
kfree(omap_domain);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1235,11 +1394,16 @@ static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
|
|
dma_addr_t da)
|
|
dma_addr_t da)
|
|
{
|
|
{
|
|
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
|
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
|
- struct omap_iommu *oiommu = omap_domain->iommu_dev;
|
|
|
|
|
|
+ struct omap_iommu_device *iommu = omap_domain->iommus;
|
|
|
|
+ struct omap_iommu *oiommu = iommu->iommu_dev;
|
|
struct device *dev = oiommu->dev;
|
|
struct device *dev = oiommu->dev;
|
|
u32 *pgd, *pte;
|
|
u32 *pgd, *pte;
|
|
phys_addr_t ret = 0;
|
|
phys_addr_t ret = 0;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * all the iommus within the domain will have identical programming,
|
|
|
|
+ * so perform the lookup using just the first iommu
|
|
|
|
+ */
|
|
iopgtable_lookup_entry(oiommu, da, &pgd, &pte);
|
|
iopgtable_lookup_entry(oiommu, da, &pgd, &pte);
|
|
|
|
|
|
if (pte) {
|
|
if (pte) {
|
|
@@ -1265,11 +1429,12 @@ static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
|
|
|
|
|
|
static int omap_iommu_add_device(struct device *dev)
|
|
static int omap_iommu_add_device(struct device *dev)
|
|
{
|
|
{
|
|
- struct omap_iommu_arch_data *arch_data;
|
|
|
|
|
|
+ struct omap_iommu_arch_data *arch_data, *tmp;
|
|
struct omap_iommu *oiommu;
|
|
struct omap_iommu *oiommu;
|
|
struct iommu_group *group;
|
|
struct iommu_group *group;
|
|
struct device_node *np;
|
|
struct device_node *np;
|
|
struct platform_device *pdev;
|
|
struct platform_device *pdev;
|
|
|
|
+ int num_iommus, i;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1281,36 +1446,57 @@ static int omap_iommu_add_device(struct device *dev)
|
|
if (!dev->of_node)
|
|
if (!dev->of_node)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- np = of_parse_phandle(dev->of_node, "iommus", 0);
|
|
|
|
- if (!np)
|
|
|
|
|
|
+ /*
|
|
|
|
+ * retrieve the count of IOMMU nodes using phandle size as element size
|
|
|
|
+ * since #iommu-cells = 0 for OMAP
|
|
|
|
+ */
|
|
|
|
+ num_iommus = of_property_count_elems_of_size(dev->of_node, "iommus",
|
|
|
|
+ sizeof(phandle));
|
|
|
|
+ if (num_iommus < 0)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- pdev = of_find_device_by_node(np);
|
|
|
|
- if (WARN_ON(!pdev)) {
|
|
|
|
- of_node_put(np);
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
|
|
+ arch_data = kzalloc((num_iommus + 1) * sizeof(*arch_data), GFP_KERNEL);
|
|
|
|
+ if (!arch_data)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
|
|
- oiommu = platform_get_drvdata(pdev);
|
|
|
|
- if (!oiommu) {
|
|
|
|
- of_node_put(np);
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
|
|
+ for (i = 0, tmp = arch_data; i < num_iommus; i++, tmp++) {
|
|
|
|
+ np = of_parse_phandle(dev->of_node, "iommus", i);
|
|
|
|
+ if (!np) {
|
|
|
|
+ kfree(arch_data);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pdev = of_find_device_by_node(np);
|
|
|
|
+ if (WARN_ON(!pdev)) {
|
|
|
|
+ of_node_put(np);
|
|
|
|
+ kfree(arch_data);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ oiommu = platform_get_drvdata(pdev);
|
|
|
|
+ if (!oiommu) {
|
|
|
|
+ of_node_put(np);
|
|
|
|
+ kfree(arch_data);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ tmp->iommu_dev = oiommu;
|
|
|
|
|
|
- arch_data = kzalloc(sizeof(*arch_data), GFP_KERNEL);
|
|
|
|
- if (!arch_data) {
|
|
|
|
of_node_put(np);
|
|
of_node_put(np);
|
|
- return -ENOMEM;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * use the first IOMMU alone for the sysfs device linking.
|
|
|
|
+ * TODO: Evaluate if a single iommu_group needs to be
|
|
|
|
+ * maintained for both IOMMUs
|
|
|
|
+ */
|
|
|
|
+ oiommu = arch_data->iommu_dev;
|
|
ret = iommu_device_link(&oiommu->iommu, dev);
|
|
ret = iommu_device_link(&oiommu->iommu, dev);
|
|
if (ret) {
|
|
if (ret) {
|
|
kfree(arch_data);
|
|
kfree(arch_data);
|
|
- of_node_put(np);
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
- arch_data->iommu_dev = oiommu;
|
|
|
|
dev->archdata.iommu = arch_data;
|
|
dev->archdata.iommu = arch_data;
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1326,8 +1512,6 @@ static int omap_iommu_add_device(struct device *dev)
|
|
}
|
|
}
|
|
iommu_group_put(group);
|
|
iommu_group_put(group);
|
|
|
|
|
|
- of_node_put(np);
|
|
|
|
-
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|