msm: iommu: Synchronize access to IOMMU cfg port
Add remote spinlock that allows CPU and GPU to
synchronize access to IOMMU hardware.
Add usage of remote spinlock to iommu driver and
add depenency on SFPB hardware mutex being enabled.`
This feature is not using SFPB hardware mutex. However,
SFPB hardware mutex must be enabled since the remote
spinlock implementation is making use of shared memory
that is normally used when SFPB hardware mutex is not enabled.
Change-Id: Idc622f3484062e0721493be3cbbfb8889ed9d800
Signed-off-by: Olav Haugan <ohaugan@codeaurora.org>
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index 85b78fb..e929189 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -124,6 +124,59 @@
irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id);
irqreturn_t msm_iommu_fault_handler_v2(int irq, void *dev_id);
+enum {
+ PROC_APPS,
+ PROC_GPU,
+ PROC_MAX
+};
+
+/* Expose structure to allow kgsl iommu driver to use the same structure to
+ * communicate to GPU the addresses of the flag and turn variables.
+ */
+struct remote_iommu_petersons_spinlock {
+ uint32_t flag[PROC_MAX];
+ uint32_t turn;
+};
+
+#ifdef CONFIG_MSM_IOMMU
+void *msm_iommu_lock_initialize(void);
+void msm_iommu_mutex_lock(void);
+void msm_iommu_mutex_unlock(void);
+#else
+static inline void *msm_iommu_lock_initialize(void)
+{
+ return NULL;
+}
+static inline void msm_iommu_mutex_lock(void) { }
+static inline void msm_iommu_mutex_unlock(void) { }
+#endif
+
+#ifdef CONFIG_MSM_IOMMU_GPU_SYNC
+void msm_iommu_remote_p0_spin_lock(void);
+void msm_iommu_remote_p0_spin_unlock(void);
+
+#define msm_iommu_remote_lock_init() _msm_iommu_remote_spin_lock_init()
+#define msm_iommu_remote_spin_lock() msm_iommu_remote_p0_spin_lock()
+#define msm_iommu_remote_spin_unlock() msm_iommu_remote_p0_spin_unlock()
+#else
+#define msm_iommu_remote_lock_init()
+#define msm_iommu_remote_spin_lock()
+#define msm_iommu_remote_spin_unlock()
+#endif
+
+/* Allows kgsl iommu driver to acquire lock */
+#define msm_iommu_lock() \
+ do { \
+ msm_iommu_mutex_lock(); \
+ msm_iommu_remote_spin_lock(); \
+ } while (0)
+
+#define msm_iommu_unlock() \
+ do { \
+ msm_iommu_remote_spin_unlock(); \
+ msm_iommu_mutex_unlock(); \
+ } while (0)
+
#ifdef CONFIG_MSM_IOMMU
/*
* Look up an IOMMU context device by its context name. NULL if none found.
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index f867dcb..b1befa0 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -25,6 +25,17 @@
If unsure, say N here.
+# MSM IOMMU CPU-GPU sync programming support
+config MSM_IOMMU_GPU_SYNC
+ bool "MSM IOMMU CPU-GPU Sync Support"
+ depends on (ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064 || ARCH_MSM8930) && MSM_IOMMU && MSM_REMOTE_SPINLOCK_SFPB
+ help
+ Say Y here if you want to synchronize access to IOMMU configuration
+ port between CPU and GPU. CPU will grab a remote spinlock before
+ accessing IOMMU configuration registers and GPU will do the same.
+
+ If unsure, say N here.
+
config IOMMU_PGTABLES_L2
bool "Allow SMMU page tables in the L2 cache (Experimental)"
depends on MSM_IOMMU && MMU && SMP && CPU_DCACHE_DISABLE=n
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index e17e1f8..10c110b 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,6 +29,7 @@
#include <mach/iommu_hw-8xxx.h>
#include <mach/iommu.h>
+#include <mach/msm_smsm.h>
#define MRC(reg, processor, op1, crn, crm, op2) \
__asm__ __volatile__ ( \
@@ -63,6 +64,69 @@
DEFINE_MUTEX(msm_iommu_lock);
+/**
+ * Remote spinlock implementation based on Peterson's algorithm to be used
+ * to synchronize IOMMU config port access between CPU and GPU.
+ * This implements Process 0 of the spin lock algorithm. GPU implements
+ * Process 1. Flag and turn is stored in shared memory to allow GPU to
+ * access these.
+ */
+struct msm_iommu_remote_lock {
+ int initialized;
+ struct remote_iommu_petersons_spinlock *lock;
+};
+
+static struct msm_iommu_remote_lock msm_iommu_remote_lock;
+
+#ifdef CONFIG_MSM_IOMMU_GPU_SYNC
+static void _msm_iommu_remote_spin_lock_init(void)
+{
+ msm_iommu_remote_lock.lock = smem_alloc(SMEM_SPINLOCK_ARRAY, 32);
+ memset(msm_iommu_remote_lock.lock, 0,
+ sizeof(*msm_iommu_remote_lock.lock));
+}
+
+void msm_iommu_remote_p0_spin_lock(void)
+{
+ msm_iommu_remote_lock.lock->flag[PROC_APPS] = 1;
+ msm_iommu_remote_lock.lock->turn = 1;
+
+ smp_mb();
+
+ while (msm_iommu_remote_lock.lock->flag[PROC_GPU] == 1 &&
+ msm_iommu_remote_lock.lock->turn == 1)
+ cpu_relax();
+}
+
+void msm_iommu_remote_p0_spin_unlock(void)
+{
+ smp_mb();
+
+ msm_iommu_remote_lock.lock->flag[PROC_APPS] = 0;
+}
+#endif
+
+inline void msm_iommu_mutex_lock(void)
+{
+ mutex_lock(&msm_iommu_lock);
+}
+
+inline void msm_iommu_mutex_unlock(void)
+{
+ mutex_unlock(&msm_iommu_lock);
+}
+
+void *msm_iommu_lock_initialize(void)
+{
+ mutex_lock(&msm_iommu_lock);
+ if (!msm_iommu_remote_lock.initialized) {
+ msm_iommu_remote_lock_init();
+ msm_iommu_remote_lock.initialized = 1;
+ }
+ mutex_unlock(&msm_iommu_lock);
+ return msm_iommu_remote_lock.lock;
+}
+
struct msm_priv {
unsigned long *pgtable;
int redirect;
@@ -113,12 +177,17 @@
if (ret)
goto fail;
+ msm_iommu_remote_spin_lock();
+
asid = GET_CONTEXTIDR_ASID(iommu_drvdata->base,
ctx_drvdata->num);
SET_TLBIVA(iommu_drvdata->base, ctx_drvdata->num,
asid | (va & TLBIVA_VA));
mb();
+
+ msm_iommu_remote_spin_unlock();
+
__disable_clocks(iommu_drvdata);
}
fail:
@@ -145,11 +214,16 @@
if (ret)
goto fail;
+ msm_iommu_remote_spin_lock();
+
asid = GET_CONTEXTIDR_ASID(iommu_drvdata->base,
ctx_drvdata->num);
SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num, asid);
mb();
+
+ msm_iommu_remote_spin_unlock();
+
__disable_clocks(iommu_drvdata);
}
fail:
@@ -186,6 +260,9 @@
{
unsigned int prrr, nmrr;
int i, j, found;
+
+ msm_iommu_remote_spin_lock();
+
__reset_context(base, ctx);
/* Set up HTW mode */
@@ -275,6 +352,8 @@
/* Enable the MMU */
SET_M(base, ctx, 1);
mb();
+
+ msm_iommu_remote_spin_unlock();
}
static int msm_iommu_domain_init(struct iommu_domain *domain, int flags)
@@ -414,10 +493,15 @@
if (ret)
goto fail;
+ msm_iommu_remote_spin_lock();
+
SET_TLBIASID(iommu_drvdata->base, ctx_dev->num,
GET_CONTEXTIDR_ASID(iommu_drvdata->base, ctx_dev->num));
__reset_context(iommu_drvdata->base, ctx_dev->num);
+
+ msm_iommu_remote_spin_unlock();
+
__disable_clocks(iommu_drvdata);
list_del_init(&ctx_drvdata->attached_elm);
ctx_drvdata->attached_domain = NULL;
@@ -917,6 +1001,8 @@
if (ret)
goto fail;
+ msm_iommu_remote_spin_lock();
+
SET_V2PPR(base, ctx, va & V2Pxx_VA);
mb();
@@ -931,6 +1017,8 @@
if (GET_FAULT(base, ctx))
ret = 0;
+ msm_iommu_remote_spin_unlock();
+
__disable_clocks(iommu_drvdata);
fail:
mutex_unlock(&msm_iommu_lock);
@@ -991,6 +1079,8 @@
if (ret)
goto fail;
+ msm_iommu_remote_spin_lock();
+
fsr = GET_FSR(base, num);
if (fsr) {
@@ -1017,6 +1107,8 @@
} else
ret = IRQ_NONE;
+ msm_iommu_remote_spin_unlock();
+
__disable_clocks(drvdata);
fail:
mutex_unlock(&msm_iommu_lock);
@@ -1087,6 +1179,8 @@
if (!msm_soc_version_supports_iommu_v1())
return -ENODEV;
+ msm_iommu_lock_initialize();
+
setup_iommu_tex_classes();
bus_set_iommu(&platform_bus_type, &msm_iommu_ops);
return 0;