|
@@ -1654,3 +1654,169 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
|
|
|
int ret = -EINVAL;
|
|
|
int hwsup = 0;
|
|
|
|
|
|
+ if (!oh)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (!soc_ops.deassert_hardreset)
|
|
|
+ return -ENOSYS;
|
|
|
+
|
|
|
+ ret = _lookup_hardreset(oh, name, &ohri);
|
|
|
+ if (IS_ERR_VALUE(ret))
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if (oh->clkdm) {
|
|
|
+ /*
|
|
|
+ * A clockdomain must be in SW_SUP otherwise reset
|
|
|
+ * might not be completed. The clockdomain can be set
|
|
|
+ * in HW_AUTO only when the module become ready.
|
|
|
+ */
|
|
|
+ hwsup = clkdm_in_hwsup(oh->clkdm);
|
|
|
+ ret = clkdm_hwmod_enable(oh->clkdm, oh);
|
|
|
+ if (ret) {
|
|
|
+ WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
|
|
|
+ oh->name, oh->clkdm->name, ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _enable_clocks(oh);
|
|
|
+ if (soc_ops.enable_module)
|
|
|
+ soc_ops.enable_module(oh);
|
|
|
+
|
|
|
+ ret = soc_ops.deassert_hardreset(oh, &ohri);
|
|
|
+
|
|
|
+ if (soc_ops.disable_module)
|
|
|
+ soc_ops.disable_module(oh);
|
|
|
+ _disable_clocks(oh);
|
|
|
+
|
|
|
+ if (ret == -EBUSY)
|
|
|
+ pr_warning("omap_hwmod: %s: failed to hardreset\n", oh->name);
|
|
|
+
|
|
|
+ if (!ret) {
|
|
|
+ /*
|
|
|
+ * Set the clockdomain to HW_AUTO, assuming that the
|
|
|
+ * previous state was HW_AUTO.
|
|
|
+ */
|
|
|
+ if (oh->clkdm && hwsup)
|
|
|
+ clkdm_allow_idle(oh->clkdm);
|
|
|
+ } else {
|
|
|
+ if (oh->clkdm)
|
|
|
+ clkdm_hwmod_disable(oh->clkdm, oh);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _read_hardreset - read the HW reset line state of submodules
|
|
|
+ * contained in the hwmod module
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ * @name: name of the reset line to look up and read
|
|
|
+ *
|
|
|
+ * Return the state of the reset line. Returns -EINVAL if @oh is
|
|
|
+ * null, -ENOSYS if we have no way of reading the hardreset line
|
|
|
+ * status on the currently-booted SoC, or passes along the return
|
|
|
+ * value from _lookup_hardreset() or the SoC's is_hardreset_asserted
|
|
|
+ * code.
|
|
|
+ */
|
|
|
+static int _read_hardreset(struct omap_hwmod *oh, const char *name)
|
|
|
+{
|
|
|
+ struct omap_hwmod_rst_info ohri;
|
|
|
+ int ret = -EINVAL;
|
|
|
+
|
|
|
+ if (!oh)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (!soc_ops.is_hardreset_asserted)
|
|
|
+ return -ENOSYS;
|
|
|
+
|
|
|
+ ret = _lookup_hardreset(oh, name, &ohri);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return soc_ops.is_hardreset_asserted(oh, &ohri);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _are_all_hardreset_lines_asserted - return true if the @oh is hard-reset
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ *
|
|
|
+ * If all hardreset lines associated with @oh are asserted, then return true.
|
|
|
+ * Otherwise, if part of @oh is out hardreset or if no hardreset lines
|
|
|
+ * associated with @oh are asserted, then return false.
|
|
|
+ * This function is used to avoid executing some parts of the IP block
|
|
|
+ * enable/disable sequence if its hardreset line is set.
|
|
|
+ */
|
|
|
+static bool _are_all_hardreset_lines_asserted(struct omap_hwmod *oh)
|
|
|
+{
|
|
|
+ int i, rst_cnt = 0;
|
|
|
+
|
|
|
+ if (oh->rst_lines_cnt == 0)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ for (i = 0; i < oh->rst_lines_cnt; i++)
|
|
|
+ if (_read_hardreset(oh, oh->rst_lines[i].name) > 0)
|
|
|
+ rst_cnt++;
|
|
|
+
|
|
|
+ if (oh->rst_lines_cnt == rst_cnt)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _are_any_hardreset_lines_asserted - return true if any part of @oh is
|
|
|
+ * hard-reset
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ *
|
|
|
+ * If any hardreset lines associated with @oh are asserted, then
|
|
|
+ * return true. Otherwise, if no hardreset lines associated with @oh
|
|
|
+ * are asserted, or if @oh has no hardreset lines, then return false.
|
|
|
+ * This function is used to avoid executing some parts of the IP block
|
|
|
+ * enable/disable sequence if any hardreset line is set.
|
|
|
+ */
|
|
|
+static bool _are_any_hardreset_lines_asserted(struct omap_hwmod *oh)
|
|
|
+{
|
|
|
+ int rst_cnt = 0;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < oh->rst_lines_cnt && rst_cnt == 0; i++)
|
|
|
+ if (_read_hardreset(oh, oh->rst_lines[i].name) > 0)
|
|
|
+ rst_cnt++;
|
|
|
+
|
|
|
+ return (rst_cnt) ? true : false;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _omap4_disable_module - enable CLKCTRL modulemode on OMAP4
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ *
|
|
|
+ * Disable the PRCM module mode related to the hwmod @oh.
|
|
|
+ * Return EINVAL if the modulemode is not supported and 0 in case of success.
|
|
|
+ */
|
|
|
+static int _omap4_disable_module(struct omap_hwmod *oh)
|
|
|
+{
|
|
|
+ int v;
|
|
|
+
|
|
|
+ if (!oh->clkdm || !oh->prcm.omap4.modulemode)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Since integration code might still be doing something, only
|
|
|
+ * disable if all lines are under hardreset.
|
|
|
+ */
|
|
|
+ if (_are_any_hardreset_lines_asserted(oh))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ pr_debug("omap_hwmod: %s: %s\n", oh->name, __func__);
|
|
|
+
|
|
|
+ omap4_cminst_module_disable(oh->clkdm->prcm_partition,
|
|
|
+ oh->clkdm->cm_inst,
|
|
|
+ oh->clkdm->clkdm_offs,
|
|
|
+ oh->prcm.omap4.clkctrl_offs);
|
|
|
+
|
|
|
+ v = _omap4_wait_target_disable(oh);
|
|
|
+ if (v)
|
|
|
+ pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
|
|
|
+ oh->name);
|
|
|
+
|