summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuman Anna <s-anna@ti.com>2019-08-07 10:26:48 +0200
committerJoerg Roedel <jroedel@suse.de>2019-08-09 17:37:09 +0200
commitc3b44a063d3b4d9b5afabaf5ab2e3fc11518dc84 (patch)
tree296204d13222485fe52819ab85ce7ba4129c8200
parentiommu/omap: streamline enable/disable through runtime pm callbacks (diff)
downloadlinux-c3b44a063d3b4d9b5afabaf5ab2e3fc11518dc84.tar.xz
linux-c3b44a063d3b4d9b5afabaf5ab2e3fc11518dc84.zip
iommu/omap: add logic to save/restore locked TLBs
The MMUs provide a mechanism to lock TLB entries to avoid eviction and fetching of frequently used page table entries. These TLBs lose context when the MMUs are turned OFF. Add the logic to save and restore these locked TLBS during suspend and resume respectively. There are no locked TLBs during initial power ON, and they need not be saved during final shutdown. Signed-off-by: Suman Anna <s-anna@ti.com> Signed-off-by: Joerg Roedel <jroedel@suse.de>
-rw-r--r--drivers/iommu/omap-iommu.c61
-rw-r--r--drivers/iommu/omap-iommu.h3
2 files changed, 61 insertions, 3 deletions
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index fbceae3c2ee7..7640f2bd7c81 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -883,15 +883,55 @@ static void omap_iommu_detach(struct omap_iommu *obj)
dma_unmap_single(obj->dev, obj->pd_dma, IOPGD_TABLE_SIZE,
DMA_TO_DEVICE);
- iommu_disable(obj);
obj->pd_dma = 0;
obj->iopgd = NULL;
+ iommu_disable(obj);
spin_unlock(&obj->iommu_lock);
dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
}
+static void omap_iommu_save_tlb_entries(struct omap_iommu *obj)
+{
+ struct iotlb_lock lock;
+ struct cr_regs cr;
+ struct cr_regs *tmp;
+ int i;
+
+ /* check if there are any locked tlbs to save */
+ iotlb_lock_get(obj, &lock);
+ obj->num_cr_ctx = lock.base;
+ if (!obj->num_cr_ctx)
+ return;
+
+ tmp = obj->cr_ctx;
+ for_each_iotlb_cr(obj, obj->num_cr_ctx, i, cr)
+ * tmp++ = cr;
+}
+
+static void omap_iommu_restore_tlb_entries(struct omap_iommu *obj)
+{
+ struct iotlb_lock l;
+ struct cr_regs *tmp;
+ int i;
+
+ /* no locked tlbs to restore */
+ if (!obj->num_cr_ctx)
+ return;
+
+ l.base = 0;
+ tmp = obj->cr_ctx;
+ for (i = 0; i < obj->num_cr_ctx; i++, tmp++) {
+ l.vict = i;
+ iotlb_lock_set(obj, &l);
+ iotlb_load_cr(obj, tmp);
+ }
+ l.base = obj->num_cr_ctx;
+ l.vict = i;
+ iotlb_lock_set(obj, &l);
+}
+
/**
* omap_iommu_runtime_suspend - disable an iommu device
* @dev: iommu device
@@ -901,7 +941,8 @@ static void omap_iommu_detach(struct omap_iommu *obj)
* device, or during system/runtime suspend of the device. This
* includes programming all the appropriate IOMMU registers, and
* managing the associated omap_hwmod's state and the device's
- * reset line.
+ * reset line. This function also saves the context of any
+ * locked TLBs if suspending.
**/
static int omap_iommu_runtime_suspend(struct device *dev)
{
@@ -910,6 +951,10 @@ static int omap_iommu_runtime_suspend(struct device *dev)
struct omap_iommu *obj = to_iommu(dev);
int ret;
+ /* save the TLBs only during suspend, and not for power down */
+ if (obj->domain && obj->iopgd)
+ omap_iommu_save_tlb_entries(obj);
+
omap2_iommu_disable(obj);
if (pdata && pdata->device_idle)
@@ -938,7 +983,8 @@ static int omap_iommu_runtime_suspend(struct device *dev)
* device, or during system/runtime resume of the device. This
* includes programming all the appropriate IOMMU registers, and
* managing the associated omap_hwmod's state and the device's
- * reset line.
+ * reset line. The function also restores any locked TLBs if
+ * resuming after a suspend.
**/
static int omap_iommu_runtime_resume(struct device *dev)
{
@@ -966,6 +1012,10 @@ static int omap_iommu_runtime_resume(struct device *dev)
if (pdata && pdata->device_enable)
pdata->device_enable(pdev);
+ /* restore the TLBs only during resume, and not for power up */
+ if (obj->domain)
+ omap_iommu_restore_tlb_entries(obj);
+
ret = omap2_iommu_enable(obj);
return ret;
@@ -1066,6 +1116,11 @@ static int omap_iommu_probe(struct platform_device *pdev)
obj->dev = &pdev->dev;
obj->ctx = (void *)obj + sizeof(*obj);
+ obj->cr_ctx = devm_kzalloc(&pdev->dev,
+ sizeof(*obj->cr_ctx) * obj->nr_tlb_entries,
+ GFP_KERNEL);
+ if (!obj->cr_ctx)
+ return -ENOMEM;
spin_lock_init(&obj->iommu_lock);
spin_lock_init(&obj->page_table_lock);
diff --git a/drivers/iommu/omap-iommu.h b/drivers/iommu/omap-iommu.h
index aac1ca65ef9d..1d15aa857634 100644
--- a/drivers/iommu/omap-iommu.h
+++ b/drivers/iommu/omap-iommu.h
@@ -73,6 +73,9 @@ struct omap_iommu {
void *ctx; /* iommu context: registres saved area */
+ struct cr_regs *cr_ctx;
+ u32 num_cr_ctx;
+
int has_bus_err_back;
u32 id;