|
@@ -2190,3 +2190,147 @@ static int _idle(struct omap_hwmod *oh)
|
|
|
|
|
|
if (_are_all_hardreset_lines_asserted(oh))
|
|
|
return 0;
|
|
|
+
|
|
|
+ if (oh->class->sysc)
|
|
|
+ _idle_sysc(oh);
|
|
|
+ _del_initiator_dep(oh, mpu_oh);
|
|
|
+
|
|
|
+ if (soc_ops.disable_module)
|
|
|
+ soc_ops.disable_module(oh);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The module must be in idle mode before disabling any parents
|
|
|
+ * clocks. Otherwise, the parent clock might be disabled before
|
|
|
+ * the module transition is done, and thus will prevent the
|
|
|
+ * transition to complete properly.
|
|
|
+ */
|
|
|
+ _disable_clocks(oh);
|
|
|
+ if (oh->clkdm)
|
|
|
+ clkdm_hwmod_disable(oh->clkdm, oh);
|
|
|
+
|
|
|
+ /* Mux pins for device idle if populated */
|
|
|
+ if (oh->mux && oh->mux->pads_dynamic) {
|
|
|
+ omap_hwmod_mux(oh->mux, _HWMOD_STATE_IDLE);
|
|
|
+ _reconfigure_io_chain();
|
|
|
+ }
|
|
|
+
|
|
|
+ oh->_state = _HWMOD_STATE_IDLE;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * omap_hwmod_set_ocp_autoidle - set the hwmod's OCP autoidle bit
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ * @autoidle: desired AUTOIDLE bitfield value (0 or 1)
|
|
|
+ *
|
|
|
+ * Sets the IP block's OCP autoidle bit in hardware, and updates our
|
|
|
+ * local copy. Intended to be used by drivers that require
|
|
|
+ * direct manipulation of the AUTOIDLE bits.
|
|
|
+ * Returns -EINVAL if @oh is null or is not in the ENABLED state, or passes
|
|
|
+ * along the return value from _set_module_autoidle().
|
|
|
+ *
|
|
|
+ * Any users of this function should be scrutinized carefully.
|
|
|
+ */
|
|
|
+int omap_hwmod_set_ocp_autoidle(struct omap_hwmod *oh, u8 autoidle)
|
|
|
+{
|
|
|
+ u32 v;
|
|
|
+ int retval = 0;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ if (!oh || oh->_state != _HWMOD_STATE_ENABLED)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&oh->_lock, flags);
|
|
|
+
|
|
|
+ v = oh->_sysc_cache;
|
|
|
+
|
|
|
+ retval = _set_module_autoidle(oh, autoidle, &v);
|
|
|
+
|
|
|
+ if (!retval)
|
|
|
+ _write_sysconfig(v, oh);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&oh->_lock, flags);
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _shutdown - shutdown an omap_hwmod
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ *
|
|
|
+ * Shut down an omap_hwmod @oh. This should be called when the driver
|
|
|
+ * used for the hwmod is removed or unloaded or if the driver is not
|
|
|
+ * used by the system. Returns -EINVAL if the hwmod is in the wrong
|
|
|
+ * state or returns 0.
|
|
|
+ */
|
|
|
+static int _shutdown(struct omap_hwmod *oh)
|
|
|
+{
|
|
|
+ int ret, i;
|
|
|
+ u8 prev_state;
|
|
|
+
|
|
|
+ if (oh->_state != _HWMOD_STATE_IDLE &&
|
|
|
+ oh->_state != _HWMOD_STATE_ENABLED) {
|
|
|
+ WARN(1, "omap_hwmod: %s: disabled state can only be entered from idle, or enabled state\n",
|
|
|
+ oh->name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_are_all_hardreset_lines_asserted(oh))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ pr_debug("omap_hwmod: %s: disabling\n", oh->name);
|
|
|
+
|
|
|
+ if (oh->class->pre_shutdown) {
|
|
|
+ prev_state = oh->_state;
|
|
|
+ if (oh->_state == _HWMOD_STATE_IDLE)
|
|
|
+ _enable(oh);
|
|
|
+ ret = oh->class->pre_shutdown(oh);
|
|
|
+ if (ret) {
|
|
|
+ if (prev_state == _HWMOD_STATE_IDLE)
|
|
|
+ _idle(oh);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (oh->class->sysc) {
|
|
|
+ if (oh->_state == _HWMOD_STATE_IDLE)
|
|
|
+ _enable(oh);
|
|
|
+ _shutdown_sysc(oh);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* clocks and deps are already disabled in idle */
|
|
|
+ if (oh->_state == _HWMOD_STATE_ENABLED) {
|
|
|
+ _del_initiator_dep(oh, mpu_oh);
|
|
|
+ /* XXX what about the other system initiators here? dma, dsp */
|
|
|
+ if (soc_ops.disable_module)
|
|
|
+ soc_ops.disable_module(oh);
|
|
|
+ _disable_clocks(oh);
|
|
|
+ if (oh->clkdm)
|
|
|
+ clkdm_hwmod_disable(oh->clkdm, oh);
|
|
|
+ }
|
|
|
+ /* XXX Should this code also force-disable the optional clocks? */
|
|
|
+
|
|
|
+ for (i = 0; i < oh->rst_lines_cnt; i++)
|
|
|
+ _assert_hardreset(oh, oh->rst_lines[i].name);
|
|
|
+
|
|
|
+ /* Mux pins to safe mode or use populated off mode values */
|
|
|
+ if (oh->mux)
|
|
|
+ omap_hwmod_mux(oh->mux, _HWMOD_STATE_DISABLED);
|
|
|
+
|
|
|
+ oh->_state = _HWMOD_STATE_DISABLED;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _init_mpu_rt_base - populate the virtual address for a hwmod
|
|
|
+ * @oh: struct omap_hwmod * to locate the virtual address
|
|
|
+ *
|
|
|
+ * Cache the virtual address used by the MPU to access this IP block's
|
|
|
+ * registers. This address is needed early so the OCP registers that
|
|
|
+ * are part of the device's address space can be ioremapped properly.
|
|
|
+ * No return value.
|
|
|
+ */
|
|
|
+static void __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data)
|
|
|
+{
|