msm: kgsl: disable use of iommu TTBR1
The v1 iommu only supports splitting between TTBR0 and TTBR1
on a power of two boundary. Cutting off the userspace address
at 2G (0x80000000) is inconvienient, as the GPU userspace
address space should align with the CPU address space.
This requires changing how global allocations are managed,
since there is no longer a separate pagetable for TTBR1.
The default pagetable is still the master of these allocations
and maintains the gen_pool for allocating global addresses.
But now, these regions are mapped into each process pagetable
by calling kgsl_setup_pt(). This requires kgsl_mmu_map
and kgsl_mmu_unmap to be able to handle mapping without
virtual address allocation.
Change-Id: I94e2d63dc7e6a7ef576f993770725b6b7ba14228
Signed-off-by: Jeremy Gebben <jgebben@codeaurora.org>
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index d20f3d2..bb0ef29 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -917,6 +917,70 @@
}
}
+/*
+ * kgsl_iommu_setup_regs - map iommu registers into a pagetable
+ * @mmu: Pointer to mmu structure
+ * @pt: the pagetable
+ *
+ * To do pagetable switches from the GPU command stream, the IOMMU
+ * registers need to be mapped into the GPU's pagetable. This function
+ * is used differently on different targets. On 8960, the registers
+ * are mapped into every pagetable during kgsl_setup_pt(). On
+ * all other targets, the registers are mapped only into the second
+ * context bank.
+ *
+ * Return - 0 on success else error code
+ */
+static int kgsl_iommu_setup_regs(struct kgsl_mmu *mmu,
+ struct kgsl_pagetable *pt)
+{
+ int status;
+ int i = 0;
+ struct kgsl_iommu *iommu = mmu->priv;
+
+ if (!msm_soc_version_supports_iommu_v1())
+ return 0;
+
+ for (i = 0; i < iommu->unit_count; i++) {
+ iommu->iommu_units[i].reg_map.priv |= KGSL_MEMDESC_GLOBAL;
+ status = kgsl_mmu_map(pt,
+ &(iommu->iommu_units[i].reg_map),
+ GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+ if (status) {
+ iommu->iommu_units[i].reg_map.priv &=
+ ~KGSL_MEMDESC_GLOBAL;
+ goto err;
+ }
+ }
+ return 0;
+err:
+ for (i--; i >= 0; i--) {
+ kgsl_mmu_unmap(pt,
+ &(iommu->iommu_units[i].reg_map));
+ iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMDESC_GLOBAL;
+ }
+ return status;
+}
+
+/*
+ * kgsl_iommu_cleanup_regs - unmap iommu registers from a pagetable
+ * @mmu: Pointer to mmu structure
+ * @pt: the pagetable
+ *
+ * Removes mappings created by kgsl_iommu_setup_regs().
+ *
+ * Return - 0 on success else error code
+ */
+static void kgsl_iommu_cleanup_regs(struct kgsl_mmu *mmu,
+ struct kgsl_pagetable *pt)
+{
+ struct kgsl_iommu *iommu = mmu->priv;
+ int i;
+ for (i = 0; i < iommu->unit_count; i++)
+ kgsl_mmu_unmap(pt, &(iommu->iommu_units[i].reg_map));
+}
+
+
static int kgsl_iommu_init(struct kgsl_mmu *mmu)
{
/*
@@ -959,6 +1023,15 @@
KGSL_IOMMU_SETSTATE_NOP_OFFSET,
cp_nop_packet(1));
+ if (cpu_is_msm8960()) {
+ /*
+ * 8960 doesn't have a second context bank, so the IOMMU
+ * registers must be mapped into every pagetable.
+ */
+ iommu_ops.mmu_setup_pt = kgsl_iommu_setup_regs;
+ iommu_ops.mmu_cleanup_pt = kgsl_iommu_cleanup_regs;
+ }
+
dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
__func__);
done:
@@ -981,9 +1054,6 @@
static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
{
int status = 0;
- int i = 0;
- struct kgsl_iommu *iommu = mmu->priv;
- struct kgsl_pagetable *pagetable = NULL;
/* If chip is not 8960 then we use the 2nd context bank for pagetable
* switching on the 3D side for which a separate table is allocated */
@@ -994,6 +1064,9 @@
status = -ENOMEM;
goto err;
}
+ status = kgsl_iommu_setup_regs(mmu, mmu->priv_bank_table);
+ if (status)
+ goto err;
}
mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
/* Return error if the default pagetable doesn't exist */
@@ -1001,31 +1074,10 @@
status = -ENOMEM;
goto err;
}
- pagetable = mmu->priv_bank_table ? mmu->priv_bank_table :
- mmu->defaultpagetable;
- /* Map the IOMMU regsiters to only defaultpagetable */
- if (msm_soc_version_supports_iommu_v1()) {
- for (i = 0; i < iommu->unit_count; i++) {
- iommu->iommu_units[i].reg_map.priv |=
- KGSL_MEMDESC_GLOBAL;
- status = kgsl_mmu_map(pagetable,
- &(iommu->iommu_units[i].reg_map),
- GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
- if (status) {
- iommu->iommu_units[i].reg_map.priv &=
- ~KGSL_MEMDESC_GLOBAL;
- goto err;
- }
- }
- }
return status;
err:
- for (i--; i >= 0; i--) {
- kgsl_mmu_unmap(pagetable,
- &(iommu->iommu_units[i].reg_map));
- iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMDESC_GLOBAL;
- }
if (mmu->priv_bank_table) {
+ kgsl_iommu_cleanup_regs(mmu, mmu->priv_bank_table);
kgsl_mmu_putpagetable(mmu->priv_bank_table);
mmu->priv_bank_table = NULL;
}
@@ -1061,10 +1113,12 @@
* a225, hence we still keep the MMU active on 8960 */
if (cpu_is_msm8960()) {
struct kgsl_mh *mh = &(mmu->device->mh);
+ BUG_ON(iommu->iommu_units[0].reg_map.gpuaddr != 0 &&
+ mh->mpu_base > iommu->iommu_units[0].reg_map.gpuaddr);
kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001);
+
kgsl_regwrite(mmu->device, MH_MMU_MPU_END,
- mh->mpu_base +
- iommu->iommu_units[0].reg_map.gpuaddr);
+ mh->mpu_base + mh->mpu_range);
} else {
kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
}
@@ -1137,14 +1191,12 @@
"with err: %d\n", iommu_pt->domain, gpuaddr,
range, ret);
-#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
/*
* Flushing only required if per process pagetables are used. With
* global case, flushing will happen inside iommu_map function
*/
- if (!ret && msm_soc_version_supports_iommu_v1())
+ if (!ret && kgsl_mmu_is_perprocess())
*tlb_flags = UINT_MAX;
-#endif
return 0;
}
@@ -1225,22 +1277,23 @@
{
struct kgsl_iommu *iommu = mmu->priv;
int i;
- for (i = 0; i < iommu->unit_count; i++) {
- struct kgsl_pagetable *pagetable = (mmu->priv_bank_table ?
- mmu->priv_bank_table : mmu->defaultpagetable);
- if (iommu->iommu_units[i].reg_map.gpuaddr)
- kgsl_mmu_unmap(pagetable,
- &(iommu->iommu_units[i].reg_map));
- if (iommu->iommu_units[i].reg_map.hostptr)
- iounmap(iommu->iommu_units[i].reg_map.hostptr);
- kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
- iommu->iommu_units[i].reg_map.sglen);
+
+ if (mmu->priv_bank_table != NULL) {
+ kgsl_iommu_cleanup_regs(mmu, mmu->priv_bank_table);
+ kgsl_mmu_putpagetable(mmu->priv_bank_table);
}
- if (mmu->priv_bank_table)
- kgsl_mmu_putpagetable(mmu->priv_bank_table);
- if (mmu->defaultpagetable)
+ if (mmu->defaultpagetable != NULL)
kgsl_mmu_putpagetable(mmu->defaultpagetable);
+
+ for (i = 0; i < iommu->unit_count; i++) {
+ struct kgsl_memdesc *reg_map = &iommu->iommu_units[i].reg_map;
+
+ if (reg_map->hostptr)
+ iounmap(reg_map->hostptr);
+ kgsl_sg_free(reg_map->sg, reg_map->sglen);
+ }
+
kfree(iommu);
return 0;
@@ -1385,6 +1438,9 @@
.mmu_get_pt_base_addr = kgsl_iommu_get_pt_base_addr,
.mmu_sync_lock = kgsl_iommu_sync_lock,
.mmu_sync_unlock = kgsl_iommu_sync_unlock,
+ /* These callbacks will be set on some chipsets */
+ .mmu_setup_pt = NULL,
+ .mmu_cleanup_pt = NULL,
};
struct kgsl_mmu_pt_ops iommu_pt_ops = {