author | Suman Anna <s-anna@ti.com> | |
Mon, 18 May 2015 02:03:33 +0000 (21:03 -0500) | ||
committer | Suman Anna <s-anna@ti.com> | |
Mon, 18 May 2015 02:10:28 +0000 (21:10 -0500) |
Merge in the updated iommu feature branch into remoteproc tree to
pull in the suspend/resume support in the OMAP IOMMU driver. The
following are the main changes:
- improvements in the OMAP iommu to perform the enabling &
disabling of the IOMMU from within the runtime pm callbacks
- two new API that needs to be invoked from the OMAP remoteproc
driver to suspend/resume the IOMMU.
- locked TLB save & restore logic
- add needed pdata quirks to all supported IOMMUs
* 'iommu-linux-3.14.y' of git://git.ti.com/rpmsg/iommu:
iommu/omap: add support for runtime auto suspend/resume
iommu/omap: add logic to save/restore locked TLBs
iommu/omap: introduce new API for suspend/resume
iommu/omap: streamline enable/disable through runtime pm callbacks
ARM: OMAP2+: add pdata-quirks for OMAP3 ISP IOMMU
ARM: OMAP2+: Add iommu pdata-quirks for DRA7 DSP EDMA MMUs
ARM: OMAP2+: plug in device_enable/idle ops for IOMMUs
iommu/omap: add pdata ops for omap_device_enable/idle
Signed-off-by: Suman Anna <s-anna@ti.com>
pull in the suspend/resume support in the OMAP IOMMU driver. The
following are the main changes:
- improvements in the OMAP iommu to perform the enabling &
disabling of the IOMMU from within the runtime pm callbacks
- two new API that needs to be invoked from the OMAP remoteproc
driver to suspend/resume the IOMMU.
- locked TLB save & restore logic
- add needed pdata quirks to all supported IOMMUs
* 'iommu-linux-3.14.y' of git://git.ti.com/rpmsg/iommu:
iommu/omap: add support for runtime auto suspend/resume
iommu/omap: add logic to save/restore locked TLBs
iommu/omap: introduce new API for suspend/resume
iommu/omap: streamline enable/disable through runtime pm callbacks
ARM: OMAP2+: add pdata-quirks for OMAP3 ISP IOMMU
ARM: OMAP2+: Add iommu pdata-quirks for DRA7 DSP EDMA MMUs
ARM: OMAP2+: plug in device_enable/idle ops for IOMMUs
iommu/omap: add pdata ops for omap_device_enable/idle
Signed-off-by: Suman Anna <s-anna@ti.com>
index f1fab5684a24b1fc26a20b180305398be5165c65..d6803a41034c1b4f3b1dc2af8aee64f7af8067b3 100644 (file)
pdata->assert_reset = omap_device_assert_hardreset;
pdata->deassert_reset = omap_device_deassert_hardreset;
}
+ pdata->device_enable = omap_device_enable,
+ pdata->device_idle = omap_device_idle,
pdev = omap_device_build("omap-iommu", i, oh, pdata, sizeof(*pdata));
index bce029f1bdcd22d822fafc35ff3881395255cc3c..9bb61bec9251b3fc73577067782ddd6b82ff2a7d 100644 (file)
.reset_name = "mmu",
.assert_reset = omap_device_assert_hardreset,
.deassert_reset = omap_device_deassert_hardreset,
+ .device_enable = omap_device_enable,
+ .device_idle = omap_device_idle,
+};
+
+static struct iommu_platform_data omap3_iommu_isp_pdata = {
+ .device_enable = omap_device_enable,
+ .device_idle = omap_device_idle,
};
static int omap3_sbc_t3730_twl_callback(struct device *dev,
.reset_name = "mmu_cache",
.assert_reset = omap_device_assert_hardreset,
.deassert_reset = omap_device_deassert_hardreset,
+ .device_enable = omap_device_enable,
+ .device_idle = omap_device_idle,
};
static struct omap_rproc_pdata omap4_ipu_pdata = {
#endif
#ifdef CONFIG_SOC_DRA7XX
+static struct iommu_platform_data dra7_dsp_mmu_edma_pdata = {
+ .device_enable = omap_device_enable,
+ .device_idle = omap_device_idle,
+};
+
static struct omap_rproc_pdata dra7_dsp1_pdata = {
.device_enable = omap_rproc_device_enable,
.device_shutdown = omap_rproc_device_shutdown,
OF_DEV_AUXDATA("ti,omap3-padconf", 0x48002a00, "48002a00.pinmux", &pcs_pdata),
OF_DEV_AUXDATA("ti,omap2-iommu", 0x5d000000, "5d000000.mmu",
&omap3_iommu_pdata),
+ OF_DEV_AUXDATA("ti,omap2-iommu", 0x480bd400, "480bd400.mmu",
+ &omap3_iommu_isp_pdata),
/* Only on am3517 */
OF_DEV_AUXDATA("ti,davinci_mdio", 0x5c030000, "davinci_mdio.0", NULL),
OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0",
OF_DEV_AUXDATA("ti,dra7-padconf", 0x4a003400, "4a003400.pinmux", &pcs_pdata),
OF_DEV_AUXDATA("ti,dra7-iommu", 0x40d01000, "40d01000.mmu",
&omap4_iommu_pdata),
+ OF_DEV_AUXDATA("ti,dra7-iommu", 0x40d02000, "40d02000.mmu",
+ &dra7_dsp_mmu_edma_pdata),
OF_DEV_AUXDATA("ti,dra7-iommu", 0x41501000, "41501000.mmu",
&omap4_iommu_pdata),
+ OF_DEV_AUXDATA("ti,dra7-iommu", 0x41502000, "41502000.mmu",
+ &dra7_dsp_mmu_edma_pdata),
OF_DEV_AUXDATA("ti,dra7-iommu", 0x55082000, "55082000.mmu",
&omap4_iommu_pdata),
OF_DEV_AUXDATA("ti,dra7-iommu", 0x58882000, "58882000.mmu",
index 693f23c46a64ed0f69cf057a583a0ba8cc987152..17b7890616b2ce105eacdaea0fb84f72591bb8ce 100644 (file)
/**
* omap_iommu_save_ctx - Save registers for pm off-mode support
* @dev: client device
+ *
+ * This should be treated as an deprecated API. It is preserved only
+ * to maintain existing functionality for OMAP3 ISP driver, newer
+ * SoCs will leverage the newer omap_iommu_domain_suspend() API and
+ * associated runtime_suspend callback.
**/
void omap_iommu_save_ctx(struct device *dev)
{
/**
* omap_iommu_restore_ctx - Restore registers for pm off-mode support
* @dev: client device
+ *
+ * This should be treated as an deprecated API. It is preserved only
+ * to maintain existing functionality for OMAP3 ISP driver, newer
+ * SoCs will leverage the newer omap_iommu_domain_resume() API and
+ * associated runtime_resume callback.
**/
void omap_iommu_restore_ctx(struct device *dev)
{
static int iommu_enable(struct omap_iommu *obj)
{
- int err;
- struct platform_device *pdev = to_platform_device(obj->dev);
- struct iommu_platform_data *pdata = pdev->dev.platform_data;
+ int ret;
if (!arch_iommu)
return -ENODEV;
- if (pdata && pdata->deassert_reset) {
- err = pdata->deassert_reset(pdev, pdata->reset_name);
- if (err) {
- dev_err(obj->dev, "deassert_reset failed: %d\n", err);
- return err;
- }
- }
-
- pm_runtime_get_sync(obj->dev);
-
- err = arch_iommu->enable(obj);
+ ret = pm_runtime_get_sync(obj->dev);
+ if (ret < 0)
+ pm_runtime_put_noidle(obj->dev);
- return err;
+ return ret < 0 ? ret : 0;
}
static void iommu_disable(struct omap_iommu *obj)
{
- struct platform_device *pdev = to_platform_device(obj->dev);
- struct iommu_platform_data *pdata = pdev->dev.platform_data;
-
- arch_iommu->disable(obj);
+ if (!arch_iommu)
+ return;
pm_runtime_put_sync(obj->dev);
-
- if (pdata && pdata->assert_reset)
- pdata->assert_reset(pdev, pdata->reset_name);
}
/*
spin_lock(&obj->iommu_lock);
- iommu_disable(obj);
obj->iopgd = NULL;
+ iommu_disable(obj);
spin_unlock(&obj->iommu_lock);
dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
}
+/**
+ * omap_iommu_domain_suspend - suspend the iommu device
+ * @domain: iommu domain attached to the target iommu device
+ * @auto_suspend: flag to indicate system suspend or runtime suspend
+ *
+ * This API allows the client devices of IOMMU devices to suspend
+ * the IOMMUs they control, after they are idled and suspended all
+ * activity.
+ **/
+int omap_iommu_domain_suspend(struct iommu_domain *domain, bool auto_suspend)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu_device *iommu;
+ struct omap_iommu *oiommu;
+ int i;
+
+ if (!omap_domain->attached)
+ return 0;
+
+ iommu = omap_domain->iommus;
+ iommu += (omap_domain->num_iommus - 1);
+ for (i = 0; i < omap_domain->num_iommus; i++, iommu--) {
+ oiommu = iommu->iommu_dev;
+ if (auto_suspend) {
+ pm_runtime_put_sync(oiommu->dev);
+ } else {
+ pm_runtime_put_noidle(oiommu->dev);
+ pm_generic_runtime_suspend(oiommu->dev);
+ pm_runtime_disable(oiommu->dev);
+ pm_runtime_set_suspended(oiommu->dev);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_domain_suspend);
+
+/**
+ * omap_iommu_domain_resume - resume the iommu device
+ * @domain: iommu domain attached to the target iommu device
+ * @auto_suspend: flag to indicate system suspend or runtime suspend
+ *
+ * This API allows the client devices of IOMMU devices to resume
+ * the IOMMUs they control, before they can resume operations.
+ **/
+int omap_iommu_domain_resume(struct iommu_domain *domain, bool auto_suspend)
+{
+ struct omap_iommu_domain *omap_domain = domain->priv;
+ struct omap_iommu_device *iommu;
+ struct omap_iommu *oiommu;
+ int i;
+
+ if (!omap_domain->attached)
+ return 0;
+
+ iommu = omap_domain->iommus;
+ for (i = 0; i < omap_domain->num_iommus; i++, iommu++) {
+ oiommu = iommu->iommu_dev;
+ if (auto_suspend) {
+ pm_runtime_get_sync(oiommu->dev);
+ } else {
+ pm_runtime_set_active(oiommu->dev);
+ pm_runtime_enable(oiommu->dev);
+ pm_runtime_get_noresume(oiommu->dev);
+ pm_generic_runtime_resume(oiommu->dev);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_domain_resume);
+
+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
+ *
+ * This function performs all that is necessary to disable an
+ * IOMMU device, either during final detachment from a client
+ * 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. This function also saves the context of any
+ * locked TLBs if suspending.
+ **/
+static int omap_iommu_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct iommu_platform_data *pdata = dev_get_platdata(dev);
+ struct omap_iommu *obj = to_iommu(dev);
+
+ /* save the TLBs only during suspend, and not for power down */
+ if (obj->domain && obj->iopgd)
+ omap_iommu_save_tlb_entries(obj);
+
+ if (arch_iommu->disable)
+ arch_iommu->disable(obj);
+
+ if (pdata && pdata->device_idle)
+ pdata->device_idle(pdev);
+
+ if (pdata && pdata->assert_reset)
+ pdata->assert_reset(pdev, pdata->reset_name);
+
+ return 0;
+}
+
+/**
+ * omap_iommu_runtime_resume - enable an iommu device
+ * @dev: iommu device
+ *
+ * This function performs all that is necessary to enable an
+ * IOMMU device, either during initial attachment to a client
+ * 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. The function also restores any locked TLBs if
+ * resuming after a suspend.
+ **/
+static int omap_iommu_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct iommu_platform_data *pdata = dev_get_platdata(dev);
+ struct omap_iommu *obj = to_iommu(dev);
+ int ret = 0;
+
+ if (pdata && pdata->deassert_reset) {
+ ret = pdata->deassert_reset(pdev, pdata->reset_name);
+ if (ret) {
+ dev_err(dev, "deassert_reset failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ 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);
+
+ if (arch_iommu->enable)
+ ret = arch_iommu->enable(obj);
+
+ return ret;
+}
+
static int omap_iommu_dra7_get_dsp_system_cfg(struct platform_device *pdev,
struct omap_iommu *obj,
resource_size_t start)
if (!obj)
return -ENOMEM;
+ /*
+ * self-manage the ordering dependencies between omap_device_enable/idle
+ * and omap_device_assert/deassert_hardreset API
+ */
+ if (pdev->dev.pm_domain) {
+ dev_dbg(&pdev->dev, "device pm_domain is being reset\n");
+ pdev->dev.pm_domain = NULL;
+ }
+
if (of) {
obj->name = dev_name(&pdev->dev);
obj->nr_tlb_entries = 32;
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);
mutex_init(&obj->mmap_lock);
return 0;
}
+static const struct dev_pm_ops omap_iommu_pm_ops = {
+ SET_RUNTIME_PM_OPS(omap_iommu_runtime_suspend,
+ omap_iommu_runtime_resume, NULL)
+};
+
static struct of_device_id omap_iommu_of_match[] = {
{ .compatible = "ti,omap2-iommu" },
{ .compatible = "ti,omap4-iommu" },
.remove = omap_iommu_remove,
.driver = {
.name = "omap-iommu",
+ .pm = &omap_iommu_pm_ops,
.of_match_table = of_match_ptr(omap_iommu_of_match),
},
};
index c1c0d824c79b59cd1c3c1975ce65031bfd3a50f7..4278aa36b77065ac726f2b1892426cc9b6ff89c7 100644 (file)
u32 da_start;
u32 da_end;
+ struct cr_regs *cr_ctx;
+ u32 num_cr_ctx;
+
int has_bus_err_back;
u32 id;
};
index cac78de09c07e0a8bdaeee4015b86577ba79e164..7a3a9c88f1639a215594c67b6a1a49f250f0a54c 100644 (file)
extern void omap_iommu_save_ctx(struct device *dev);
extern void omap_iommu_restore_ctx(struct device *dev);
+int omap_iommu_domain_suspend(struct iommu_domain *domain, bool auto_suspend);
+int omap_iommu_domain_resume(struct iommu_domain *domain, bool auto_suspend);
+
#endif
index 5b429c43a29778ae9b9fb8b545599c2464a736c7..8aec9172822cbbea6fb1069d6fa9a84b7238342c 100644 (file)
int (*assert_reset)(struct platform_device *pdev, const char *name);
int (*deassert_reset)(struct platform_device *pdev, const char *name);
+ int (*device_enable)(struct platform_device *pdev);
+ int (*device_idle)(struct platform_device *pdev);
};