]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - rpmsg/rpmsg.git/commitdiff
Merge branch 'iommu-linux-4.19.y' of git://git.ti.com/rpmsg/iommu into rproc-linux...
authorSuman Anna <s-anna@ti.com>
Mon, 4 Mar 2019 16:15:55 +0000 (10:15 -0600)
committerSuman Anna <s-anna@ti.com>
Mon, 4 Mar 2019 16:16:18 +0000 (10:16 -0600)
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
   - system suspend/resume support through late dev_pm_ops
   - two new API that needs to be invoked from the OMAP remoteproc
     driver to runtime suspend/resume the IOMMU
   - locked TLB save & restore logic
   - add needed pdata quirks to all supported IOMMUs

Suspend/resume support in the OMAP mailbox driver is already supported
in baseline upstream kernel.

* 'iommu-linux-4.19.y' of git://git.ti.com/rpmsg/iommu:
  iommu/omap: introduce new API for runtime suspend/resume control
  iommu/omap: Add system suspend/resume support
  iommu/omap: add logic to save/restore locked TLBs
  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>
arch/arm/mach-omap2/pdata-quirks.c
drivers/iommu/omap-iommu.c
drivers/iommu/omap-iommu.h
include/linux/omap-iommu.h
include/linux/platform_data/iommu-omap.h

index 2271add0a6619e50648598e5a4e8843921b680ed..9b6aec1fef88ac37300a6daf8226a0e019195a01 100644 (file)
@@ -104,6 +104,13 @@ static struct iommu_platform_data omap3_iommu_pdata = {
        .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,
@@ -438,6 +445,8 @@ static struct iommu_platform_data omap4_iommu_pdata = {
        .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,
 };
 #endif
 
@@ -466,9 +475,16 @@ static struct iommu_platform_data dra7_ipu1_dsp_iommu_pdata = {
        .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,
        .set_pwrdm_constraint = omap_iommu_set_pwrdm_constraint,
 };
 
+static struct iommu_platform_data dra7_dsp_mmu_edma_pdata = {
+       .device_enable = omap_device_enable,
+       .device_idle = omap_device_idle,
+};
+
 static struct omap_hsmmc_platform_data dra7_hsmmc_data_mmc1;
 static struct omap_hsmmc_platform_data dra7_hsmmc_data_mmc2;
 static struct omap_hsmmc_platform_data dra7_hsmmc_data_mmc3;
@@ -585,6 +601,8 @@ static struct of_dev_auxdata omap_auxdata_lookup[] = {
 #ifdef CONFIG_ARCH_OMAP3
        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),
        OF_DEV_AUXDATA("ti,omap3-smartreflex-core", 0x480cb000,
                       "480cb000.smartreflex", &omap_sr_pdata[OMAP_SR_CORE]),
        OF_DEV_AUXDATA("ti,omap3-smartreflex-mpu-iva", 0x480c9000,
@@ -644,6 +662,10 @@ static struct of_dev_auxdata omap_auxdata_lookup[] = {
                       &dra7_ipu1_dsp_iommu_pdata),
        OF_DEV_AUXDATA("ti,dra7-dsp-iommu", 0x41501000, "41501000.mmu",
                       &dra7_ipu1_dsp_iommu_pdata),
+       OF_DEV_AUXDATA("ti,dra7-dsp-iommu", 0x40d02000, "40d02000.mmu",
+                      &dra7_dsp_mmu_edma_pdata),
+       OF_DEV_AUXDATA("ti,dra7-dsp-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 f623f76378a0d2ceab6ef84318211ce878bd7d1f..c69d8690052073e841062de9cd8558809630628d 100644 (file)
@@ -69,6 +69,9 @@ static struct omap_iommu_domain *to_omap_domain(struct iommu_domain *dom)
 /**
  * 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.
  **/
 void omap_iommu_save_ctx(struct device *dev)
 {
@@ -96,6 +99,9 @@ EXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
 /**
  * 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.
  **/
 void omap_iommu_restore_ctx(struct device *dev)
 {
@@ -190,53 +196,18 @@ static void omap2_iommu_disable(struct omap_iommu *obj)
 
 static int iommu_enable(struct omap_iommu *obj)
 {
-       int err;
-       struct platform_device *pdev = to_platform_device(obj->dev);
-       struct iommu_platform_data *pdata = dev_get_platdata(&pdev->dev);
-
-       if (pdata && pdata->set_pwrdm_constraint) {
-               err = pdata->set_pwrdm_constraint(pdev, true, &obj->pwrst);
-               if (err) {
-                       dev_warn(obj->dev, "pwrdm_constraint failed to be set, status = %d\n",
-                                err);
-               }
-       }
-
-       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);
+       int ret;
 
-       err = omap2_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 = dev_get_platdata(&pdev->dev);
-       int ret;
-
-       omap2_iommu_disable(obj);
-
        pm_runtime_put_sync(obj->dev);
-
-       if (pdata && pdata->assert_reset)
-               pdata->assert_reset(pdev, pdata->reset_name);
-
-       if (pdata && pdata->set_pwrdm_constraint) {
-               ret = pdata->set_pwrdm_constraint(pdev, false, &obj->pwrst);
-               if (ret) {
-                       dev_warn(obj->dev, "pwrdm_constraint failed to be reset, status = %d\n",
-                                ret);
-               }
-       }
 }
 
 /*
@@ -922,15 +893,219 @@ 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_domain_deactivate - deactivate attached iommu devices
+ * @domain: iommu domain attached to the target iommu device
+ *
+ * This API allows the client devices of IOMMU devices to suspend
+ * the IOMMUs they control at runtime, after they are idled and
+ * suspended all activity. System Suspend will leverage the PM
+ * driver late callbacks.
+ **/
+int omap_iommu_domain_deactivate(struct iommu_domain *domain)
+{
+       struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
+       struct omap_iommu_device *iommu;
+       struct omap_iommu *oiommu;
+       int i;
+
+       if (!omap_domain->dev)
+               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;
+               pm_runtime_put_sync(oiommu->dev);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_domain_deactivate);
+
+/**
+ * omap_iommu_domain_activate - activate attached iommu devices
+ * @domain: iommu domain attached to the target iommu device
+ *
+ * This API allows the client devices of IOMMU devices to resume the
+ * IOMMUs they control at runtime, before they can resume operations.
+ * System Resume will leverage the PM driver late callbacks.
+ **/
+int omap_iommu_domain_activate(struct iommu_domain *domain)
+{
+       struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
+       struct omap_iommu_device *iommu;
+       struct omap_iommu *oiommu;
+       int i;
+
+       if (!omap_domain->dev)
+               return 0;
+
+       iommu = omap_domain->iommus;
+       for (i = 0; i < omap_domain->num_iommus; i++, iommu++) {
+               oiommu = iommu->iommu_dev;
+               pm_runtime_get_sync(oiommu->dev);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(omap_iommu_domain_activate);
+
+/**
+ * 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);
+       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)
+               pdata->device_idle(pdev);
+
+       if (pdata && pdata->assert_reset)
+               pdata->assert_reset(pdev, pdata->reset_name);
+
+       if (pdata && pdata->set_pwrdm_constraint) {
+               ret = pdata->set_pwrdm_constraint(pdev, false, &obj->pwrst);
+               if (ret) {
+                       dev_warn(obj->dev, "pwrdm_constraint failed to be reset, status = %d\n",
+                                ret);
+               }
+       }
+
+       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->set_pwrdm_constraint) {
+               ret = pdata->set_pwrdm_constraint(pdev, true, &obj->pwrst);
+               if (ret) {
+                       dev_warn(obj->dev, "pwrdm_constraint failed to be set, status = %d\n",
+                                ret);
+               }
+       }
+
+       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);
+
+       ret = omap2_iommu_enable(obj);
+
+       return ret;
+}
+
+/**
+ * omap_iommu_suspend_prepare - prepare() dev_pm_ops implementation
+ * @dev:       iommu device
+ *
+ * This function performs the necessary checks to determine if the IOMMU
+ * device needs suspending or not. The function checks if the runtime_pm
+ * status of the device is suspended, and returns 1 in that case. This
+ * results in the PM core to skip invoking any of the Sleep PM callbacks
+ * (suspend, suspend_late, resume, resume_early etc).
+ */
+static int omap_iommu_prepare(struct device *dev)
+{
+       if (pm_runtime_status_suspended(dev))
+               return 1;
+       return 0;
+}
+
 static bool omap_iommu_can_register(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
@@ -1005,6 +1180,15 @@ static int omap_iommu_probe(struct platform_device *pdev)
        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;
+       }
+
        obj->name = dev_name(&pdev->dev);
        obj->nr_tlb_entries = 32;
        err = of_property_read_u32(of, "ti,#tlb-entries", &obj->nr_tlb_entries);
@@ -1017,6 +1201,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);
@@ -1093,6 +1282,14 @@ static int omap_iommu_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct dev_pm_ops omap_iommu_pm_ops = {
+       .prepare = omap_iommu_prepare,
+       SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                                    pm_runtime_force_resume)
+       SET_RUNTIME_PM_OPS(omap_iommu_runtime_suspend,
+                          omap_iommu_runtime_resume, NULL)
+};
+
 static const struct of_device_id omap_iommu_of_match[] = {
        { .compatible = "ti,omap2-iommu" },
        { .compatible = "ti,omap4-iommu" },
@@ -1106,6 +1303,7 @@ static struct platform_driver omap_iommu_driver = {
        .remove = omap_iommu_remove,
        .driver = {
                .name   = "omap-iommu",
+               .pm     = &omap_iommu_pm_ops,
                .of_match_table = of_match_ptr(omap_iommu_of_match),
        },
 };
index 7766d5a4f3f1f888e9688882079fc1d75ed97906..ca07fbf287d0910f19188ab815b5a6998b5c8bf1 100644 (file)
@@ -76,6 +76,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;
 
index ce1b7c6283eecdfd1d4d7943a34dd60dd70eb93f..5b66325bc63c1ccccb4b13f1c43ee30272b13546 100644 (file)
 #ifndef _OMAP_IOMMU_H_
 #define _OMAP_IOMMU_H_
 
+struct iommu_domain;
+
 #ifdef CONFIG_OMAP_IOMMU
 extern void omap_iommu_save_ctx(struct device *dev);
 extern void omap_iommu_restore_ctx(struct device *dev);
+
+int omap_iommu_domain_deactivate(struct iommu_domain *domain);
+int omap_iommu_domain_activate(struct iommu_domain *domain);
 #else
 static inline void omap_iommu_save_ctx(struct device *dev) {}
 static inline void omap_iommu_restore_ctx(struct device *dev) {}
+
+static inline int omap_iommu_domain_deactivate(struct iommu_domain *domain)
+{
+       return -ENOTSUP;
+}
+
+static inline int omap_iommu_domain_activate(struct iommu_domain *domain)
+{
+       return -ENOTSUP;
+}
 #endif
 
 #endif
index 1a0aa46a5ad6459e0ff31e8ab406fd7aae32f6f2..6669dc9394daafd33547d2fcc4e4a750687630ac 100644 (file)
@@ -16,6 +16,8 @@ struct iommu_platform_data {
        const char *reset_name;
        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);
        int (*set_pwrdm_constraint)(struct platform_device *pdev, bool request,
                                    u8 *pwrst);
 };