static void omap2_iommu_disable(struct omap_iommu *obj) { struct omap_hwmod *oh; u32 l; oh = omap_hwmod_lookup(obj->name); if (!oh) return; /* * IPU and DSP iommus are not directly connected to the processor * instead they are behind a shared MMU. Therefore in the case of * a mmu fault and the mmu fault was not handled, even if the processor * is under reset, the shared MMU will try to translation the address * again causing that the status flag cannot be clear and therefore * as soon as the clkdm wants to go to idle the clkdm will be stuck * in transition state. The only way to reset the shared MMU is doing * a hardreset of the L2 iommu which shared the reset line with the * shared MMU. That way we can clean the status bit and turn off * the iommu without any issue. */ if (!strcmp(obj->name, "ipu") || !strcmp(obj->name, "dsp")) { omap_hwmod_assert_hardreset(oh, oh->rst_lines->name); omap_hwmod_deassert_hardreset(oh, oh->rst_lines->name); goto out; } l = iommu_read_reg(obj, MMU_IRQSTATUS); iommu_write_reg(obj, l, MMU_IRQSTATUS); l = iommu_read_reg(obj, MMU_CNTL); l &= ~MMU_CNTL_MASK; iommu_write_reg(obj, l, MMU_CNTL); out: clkdm_allow_idle(oh->clkdm); dev_dbg(obj->dev, "%s is shutting down\n", obj->name); }
/** * omap_device_deassert_hardreset - release a device's hardreset line * @pdev: struct platform_device * to reset * @name: const char * name of the reset line * * Release the hardreset line identified by @name on the IP blocks * associated with the hwmods backing the platform_device @pdev. All * of the hwmods associated with @pdev must have the same hardreset * line linked to them for this to work. Passes along the return * value of omap_hwmod_deassert_hardreset() in the event of any * failure, or returns 0 upon success. */ int omap_device_deassert_hardreset(struct platform_device *pdev, const char *name) { struct omap_device *od = to_omap_device(pdev); int ret = 0; int i; for (i = 0; i < od->hwmods_cnt; i++) { ret = omap_hwmod_deassert_hardreset(od->hwmods[i], name); if (ret) break; } return ret; }
/** * omap_device_build_from_dt - build an omap_device with multiple hwmods * @pdev_name: name of the platform_device driver to use * @pdev_id: this platform_device's connection ID * @oh: ptr to the single omap_hwmod that backs this omap_device * @pdata: platform_data ptr to associate with the platform_device * @pdata_len: amount of memory pointed to by @pdata * * Function for building an omap_device already registered from device-tree * * Returns 0 or PTR_ERR() on error. */ static int omap_device_build_from_dt(struct platform_device *pdev) { struct omap_hwmod **hwmods; struct omap_device *od; struct omap_hwmod *oh; struct device_node *node = pdev->dev.of_node; const char *oh_name, *rst_name; int oh_cnt, dstr_cnt, i, ret = 0; bool device_active = false; oh_cnt = of_property_count_strings(node, "ti,hwmods"); if (oh_cnt <= 0) { dev_dbg(&pdev->dev, "No 'hwmods' to build omap_device\n"); return -ENODEV; } hwmods = kzalloc(sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL); if (!hwmods) { ret = -ENOMEM; goto odbfd_exit; } for (i = 0; i < oh_cnt; i++) { of_property_read_string_index(node, "ti,hwmods", i, &oh_name); oh = omap_hwmod_lookup(oh_name); if (!oh) { dev_err(&pdev->dev, "Cannot lookup hwmod '%s'\n", oh_name); ret = -EINVAL; goto odbfd_exit1; } hwmods[i] = oh; if (oh->flags & HWMOD_INIT_NO_IDLE) device_active = true; } od = omap_device_alloc(pdev, hwmods, oh_cnt); if (IS_ERR(od)) { dev_err(&pdev->dev, "Cannot allocate omap_device for :%s\n", oh_name); ret = PTR_ERR(od); goto odbfd_exit1; } /* Fix up missing resource names */ for (i = 0; i < pdev->num_resources; i++) { struct resource *r = &pdev->resource[i]; if (r->name == NULL) r->name = dev_name(&pdev->dev); } pdev->dev.pm_domain = &omap_device_pm_domain; if (device_active) { omap_device_enable(pdev); pm_runtime_set_active(&pdev->dev); } dstr_cnt = of_property_count_strings(node, "ti,deassert-hard-reset"); if (dstr_cnt > 0) { for (i = 0; i < dstr_cnt; i += 2) { of_property_read_string_index( node, "ti,deassert-hard-reset", i, &oh_name); of_property_read_string_index( node, "ti,deassert-hard-reset", i+1, &rst_name); oh = omap_hwmod_lookup(oh_name); if (!oh) { dev_warn(&pdev->dev, "Cannot parse deassert property for '%s'\n", oh_name); break; } omap_hwmod_deassert_hardreset(oh, rst_name); } } odbfd_exit1: kfree(hwmods); odbfd_exit: /* if data/we are at fault.. load up a fail handler */ if (ret) pdev->dev.pm_domain = &omap_device_fail_pm_domain; return ret; }
/** * omap_device_build_from_dt - build an omap_device with multiple hwmods * @pdev_name: name of the platform_device driver to use * @pdev_id: this platform_device's connection ID * @oh: ptr to the single omap_hwmod that backs this omap_device * @pdata: platform_data ptr to associate with the platform_device * @pdata_len: amount of memory pointed to by @pdata * @pm_lats: pointer to a omap_device_pm_latency array for this device * @pm_lats_cnt: ARRAY_SIZE() of @pm_lats * @is_early_device: should the device be registered as an early device or not * * Function for building an omap_device already registered from device-tree * * Returns 0 or PTR_ERR() on error. */ static int omap_device_build_from_dt(struct platform_device *pdev) { struct omap_hwmod **hwmods; struct omap_device *od; struct omap_hwmod *oh; struct device_node *node = pdev->dev.of_node; const char *oh_name, *rst_name; int oh_cnt, dstr_cnt, i, ret = 0; oh_cnt = of_property_count_strings(node, "ti,hwmods"); if (!oh_cnt || IS_ERR_VALUE(oh_cnt)) { dev_dbg(&pdev->dev, "No 'hwmods' to build omap_device\n"); return -ENODEV; } hwmods = kzalloc(sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL); if (!hwmods) { ret = -ENOMEM; goto odbfd_exit; } for (i = 0; i < oh_cnt; i++) { of_property_read_string_index(node, "ti,hwmods", i, &oh_name); oh = omap_hwmod_lookup(oh_name); if (!oh) { dev_err(&pdev->dev, "Cannot lookup hwmod '%s'\n", oh_name); ret = -EINVAL; goto odbfd_exit1; } hwmods[i] = oh; } od = omap_device_alloc(pdev, hwmods, oh_cnt, NULL, 0); if (!od) { dev_err(&pdev->dev, "Cannot allocate omap_device for :%s\n", oh_name); ret = PTR_ERR(od); goto odbfd_exit1; } /* Fix up missing resource names */ for (i = 0; i < pdev->num_resources; i++) { struct resource *r = &pdev->resource[i]; if (r->name == NULL) r->name = dev_name(&pdev->dev); } if (of_get_property(node, "ti,no_idle_on_suspend", NULL)) omap_device_disable_idle_on_suspend(pdev); dstr_cnt = of_property_count_strings(node, "ti,deassert-hard-reset"); if (dstr_cnt > 0) { for (i = 0; i < dstr_cnt; i += 2) { of_property_read_string_index( node, "ti,deassert-hard-reset", i, &oh_name); of_property_read_string_index( node, "ti,deassert-hard-reset", i+1, &rst_name); oh = omap_hwmod_lookup(oh_name); if (!oh) { dev_warn(&pdev->dev, "Cannot parse deassert property for '%s'\n", oh_name); break; } omap_hwmod_deassert_hardreset(oh, rst_name); } } pdev->dev.pm_domain = &omap_device_pm_domain; odbfd_exit1: kfree(hwmods); odbfd_exit: return ret; }
/* Initiliaze WKUP_M3, load the binary blob and let it run */ static int wkup_m3_init(void) { struct clk *m3_clk; struct omap_hwmod *wkup_m3_oh; const struct firmware *firmware; int ret = 0; wkup_m3_oh = omap_hwmod_lookup("wkup_m3"); if (!wkup_m3_oh) { pr_err("%s: could not find omap_hwmod\n", __func__); ret = -ENODEV; goto exit; } ipc_regs = ioremap(A8_M3_IPC_REGS, 0x4*8); if (!ipc_regs) { pr_err("Could not ioremap the IPC area\b"); ret = -ENOMEM; goto exit; } m3_eoi = ioremap(M3_TXEV_EOI, 0x4); if (!m3_eoi) { pr_err("Could not ioremap the EOI register\n"); ret = -ENOMEM; goto err1; } /* Reserve the MBOX for sending messages to M3 */ m3_mbox = omap_mbox_get("wkup_m3", &wkup_m3_mbox_notifier); if (IS_ERR(m3_mbox)) { pr_err("Could not reserve mailbox for A8->M3 IPC\n"); ret = -ENODEV; goto err2; } /* Enable access to the M3 code and data area from A8 */ m3_clk = clk_get(NULL, "wkup_m3_fck"); if (IS_ERR(m3_clk)) { pr_err("%s failed to enable WKUP_M3 clock\n", __func__); goto err3; } if (clk_enable(m3_clk)) { pr_err("%s WKUP_M3: clock enable Failed\n", __func__); goto err4; } m3_code = ioremap(M3_UMEM, SZ_16K); if (!m3_code) { pr_err("%s Could not ioremap M3 code space\n", __func__); ret = -ENOMEM; goto err5; } pr_info("Trying to load am335x-pm-firmware.bin (60 secs timeout)\n"); ret = request_firmware(&firmware, "am335x-pm-firmware.bin", mpu_dev); if (ret < 0) { dev_err(mpu_dev, "request_firmware failed\n"); goto err6; } else { memcpy(m3_code, firmware->data, firmware->size); pr_info("Copied the M3 firmware to UMEM\n"); } ret = request_irq(AM33XX_IRQ_M3_M3SP_TXEV, wkup_m3_txev_handler, IRQF_DISABLED, "wkup_m3_txev", NULL); if (ret) { pr_err("%s request_irq failed for 0x%x\n", __func__, AM33XX_IRQ_M3_M3SP_TXEV); goto err6; } m3_state = M3_STATE_RESET; ret = omap_hwmod_deassert_hardreset(wkup_m3_oh, "wkup_m3"); if (ret) { pr_err("Could not deassert the reset for WKUP_M3\n"); goto err6; } else { return 0; } err6: release_firmware(firmware); iounmap(m3_code); err5: clk_disable(m3_clk); err4: clk_put(m3_clk); err3: omap_mbox_put(m3_mbox, &wkup_m3_mbox_notifier); err2: iounmap(m3_eoi); err1: iounmap(ipc_regs); exit: return ret; }