|
@@ -408,3 +408,150 @@ static int _set_softreset(struct omap_hwmod *oh, u32 *v)
|
|
|
if (!oh->class->sysc ||
|
|
|
!(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET))
|
|
|
return -EINVAL;
|
|
|
+
|
|
|
+ if (!oh->class->sysc->sysc_fields) {
|
|
|
+ WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ softrst_mask = (0x1 << oh->class->sysc->sysc_fields->srst_shift);
|
|
|
+
|
|
|
+ *v |= softrst_mask;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _wait_softreset_complete - wait for an OCP softreset to complete
|
|
|
+ * @oh: struct omap_hwmod * to wait on
|
|
|
+ *
|
|
|
+ * Wait until the IP block represented by @oh reports that its OCP
|
|
|
+ * softreset is complete. This can be triggered by software (see
|
|
|
+ * _ocp_softreset()) or by hardware upon returning from off-mode (one
|
|
|
+ * example is HSMMC). Waits for up to MAX_MODULE_SOFTRESET_WAIT
|
|
|
+ * microseconds. Returns the number of microseconds waited.
|
|
|
+ */
|
|
|
+static int _wait_softreset_complete(struct omap_hwmod *oh)
|
|
|
+{
|
|
|
+ struct omap_hwmod_class_sysconfig *sysc;
|
|
|
+ u32 softrst_mask;
|
|
|
+ int c = 0;
|
|
|
+
|
|
|
+ sysc = oh->class->sysc;
|
|
|
+
|
|
|
+ if (sysc->sysc_flags & SYSS_HAS_RESET_STATUS)
|
|
|
+ omap_test_timeout((omap_hwmod_read(oh, sysc->syss_offs)
|
|
|
+ & SYSS_RESETDONE_MASK),
|
|
|
+ MAX_MODULE_SOFTRESET_WAIT, c);
|
|
|
+ else if (sysc->sysc_flags & SYSC_HAS_RESET_STATUS) {
|
|
|
+ softrst_mask = (0x1 << sysc->sysc_fields->srst_shift);
|
|
|
+ omap_test_timeout(!(omap_hwmod_read(oh, sysc->sysc_offs)
|
|
|
+ & softrst_mask),
|
|
|
+ MAX_MODULE_SOFTRESET_WAIT, c);
|
|
|
+ }
|
|
|
+
|
|
|
+ return c;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _set_dmadisable: set OCP_SYSCONFIG.DMADISABLE bit in @v
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ *
|
|
|
+ * The DMADISABLE bit is a semi-automatic bit present in sysconfig register
|
|
|
+ * of some modules. When the DMA must perform read/write accesses, the
|
|
|
+ * DMADISABLE bit is cleared by the hardware. But when the DMA must stop
|
|
|
+ * for power management, software must set the DMADISABLE bit back to 1.
|
|
|
+ *
|
|
|
+ * Set the DMADISABLE bit in @v for hwmod @oh. Returns -EINVAL upon
|
|
|
+ * error or 0 upon success.
|
|
|
+ */
|
|
|
+static int _set_dmadisable(struct omap_hwmod *oh)
|
|
|
+{
|
|
|
+ u32 v;
|
|
|
+ u32 dmadisable_mask;
|
|
|
+
|
|
|
+ if (!oh->class->sysc ||
|
|
|
+ !(oh->class->sysc->sysc_flags & SYSC_HAS_DMADISABLE))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (!oh->class->sysc->sysc_fields) {
|
|
|
+ WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* clocks must be on for this operation */
|
|
|
+ if (oh->_state != _HWMOD_STATE_ENABLED) {
|
|
|
+ pr_warn("omap_hwmod: %s: dma can be disabled only from enabled state\n", oh->name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_debug("omap_hwmod: %s: setting DMADISABLE\n", oh->name);
|
|
|
+
|
|
|
+ v = oh->_sysc_cache;
|
|
|
+ dmadisable_mask =
|
|
|
+ (0x1 << oh->class->sysc->sysc_fields->dmadisable_shift);
|
|
|
+ v |= dmadisable_mask;
|
|
|
+ _write_sysconfig(v, oh);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _set_module_autoidle: set the OCP_SYSCONFIG AUTOIDLE field in @v
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ * @autoidle: desired AUTOIDLE bitfield value (0 or 1)
|
|
|
+ * @v: pointer to register contents to modify
|
|
|
+ *
|
|
|
+ * Update the module autoidle bit in @v to be @autoidle for the @oh
|
|
|
+ * hwmod. The autoidle bit controls whether the module can gate
|
|
|
+ * internal clocks automatically when it isn't doing anything; the
|
|
|
+ * exact function of this bit varies on a per-module basis. This
|
|
|
+ * function does not write to the hardware. Returns -EINVAL upon
|
|
|
+ * error or 0 upon success.
|
|
|
+ */
|
|
|
+static int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle,
|
|
|
+ u32 *v)
|
|
|
+{
|
|
|
+ u32 autoidle_mask;
|
|
|
+ u8 autoidle_shift;
|
|
|
+
|
|
|
+ if (!oh->class->sysc ||
|
|
|
+ !(oh->class->sysc->sysc_flags & SYSC_HAS_AUTOIDLE))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (!oh->class->sysc->sysc_fields) {
|
|
|
+ WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ autoidle_shift = oh->class->sysc->sysc_fields->autoidle_shift;
|
|
|
+ autoidle_mask = (0x1 << autoidle_shift);
|
|
|
+
|
|
|
+ *v &= ~autoidle_mask;
|
|
|
+ *v |= autoidle << autoidle_shift;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _set_idle_ioring_wakeup - enable/disable IO pad wakeup on hwmod idle for mux
|
|
|
+ * @oh: struct omap_hwmod *
|
|
|
+ * @set_wake: bool value indicating to set (true) or clear (false) wakeup enable
|
|
|
+ *
|
|
|
+ * Set or clear the I/O pad wakeup flag in the mux entries for the
|
|
|
+ * hwmod @oh. This function changes the @oh->mux->pads_dynamic array
|
|
|
+ * in memory. If the hwmod is currently idled, and the new idle
|
|
|
+ * values don't match the previous ones, this function will also
|
|
|
+ * update the SCM PADCTRL registers. Otherwise, if the hwmod is not
|
|
|
+ * currently idled, this function won't touch the hardware: the new
|
|
|
+ * mux settings are written to the SCM PADCTRL registers when the
|
|
|
+ * hwmod is idled. No return value.
|
|
|
+ */
|
|
|
+static void _set_idle_ioring_wakeup(struct omap_hwmod *oh, bool set_wake)
|
|
|
+{
|
|
|
+ struct omap_device_pad *pad;
|
|
|
+ bool change = false;
|
|
|
+ u16 prev_idle;
|
|
|
+ int j;
|
|
|
+
|
|
|
+ if (!oh->mux || !oh->mux->enabled)
|