|
@@ -240,3 +240,142 @@ int omap2xxx_cm_split_idlest_reg(void __iomem *idlest_reg, s16 *prcm_inst,
|
|
*/
|
|
*/
|
|
|
|
|
|
/**
|
|
/**
|
|
|
|
+ * omap2xxx_cm_wait_module_ready - wait for a module to leave idle or standby
|
|
|
|
+ * @prcm_mod: PRCM module offset
|
|
|
|
+ * @idlest_id: CM_IDLESTx register ID (i.e., x = 1, 2, 3)
|
|
|
|
+ * @idlest_shift: shift of the bit in the CM_IDLEST* register to check
|
|
|
|
+ *
|
|
|
|
+ * Wait for the PRCM to indicate that the module identified by
|
|
|
|
+ * (@prcm_mod, @idlest_id, @idlest_shift) is clocked. Return 0 upon
|
|
|
|
+ * success or -EBUSY if the module doesn't enable in time.
|
|
|
|
+ */
|
|
|
|
+int omap2xxx_cm_wait_module_ready(s16 prcm_mod, u8 idlest_id, u8 idlest_shift)
|
|
|
|
+{
|
|
|
|
+ int ena = 0, i = 0;
|
|
|
|
+ u8 cm_idlest_reg;
|
|
|
|
+ u32 mask;
|
|
|
|
+
|
|
|
|
+ if (!idlest_id || (idlest_id > ARRAY_SIZE(omap2xxx_cm_idlest_offs)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ cm_idlest_reg = omap2xxx_cm_idlest_offs[idlest_id - 1];
|
|
|
|
+
|
|
|
|
+ mask = 1 << idlest_shift;
|
|
|
|
+ ena = mask;
|
|
|
|
+
|
|
|
|
+ omap_test_timeout(((omap2_cm_read_mod_reg(prcm_mod, cm_idlest_reg) &
|
|
|
|
+ mask) == ena), MAX_MODULE_READY_TIME, i);
|
|
|
|
+
|
|
|
|
+ return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Clockdomain low-level functions */
|
|
|
|
+
|
|
|
|
+static void omap2xxx_clkdm_allow_idle(struct clockdomain *clkdm)
|
|
|
|
+{
|
|
|
|
+ if (atomic_read(&clkdm->usecount) > 0)
|
|
|
|
+ _clkdm_add_autodeps(clkdm);
|
|
|
|
+
|
|
|
|
+ omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
|
|
|
|
+ clkdm->clktrctrl_mask);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void omap2xxx_clkdm_deny_idle(struct clockdomain *clkdm)
|
|
|
|
+{
|
|
|
|
+ omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
|
|
|
|
+ clkdm->clktrctrl_mask);
|
|
|
|
+
|
|
|
|
+ if (atomic_read(&clkdm->usecount) > 0)
|
|
|
|
+ _clkdm_del_autodeps(clkdm);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int omap2xxx_clkdm_clk_enable(struct clockdomain *clkdm)
|
|
|
|
+{
|
|
|
|
+ bool hwsup = false;
|
|
|
|
+
|
|
|
|
+ if (!clkdm->clktrctrl_mask)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
|
|
|
|
+ clkdm->clktrctrl_mask);
|
|
|
|
+
|
|
|
|
+ if (hwsup) {
|
|
|
|
+ /* Disable HW transitions when we are changing deps */
|
|
|
|
+ omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
|
|
|
|
+ clkdm->clktrctrl_mask);
|
|
|
|
+ _clkdm_add_autodeps(clkdm);
|
|
|
|
+ omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
|
|
|
|
+ clkdm->clktrctrl_mask);
|
|
|
|
+ } else {
|
|
|
|
+ if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
|
|
|
|
+ omap2xxx_clkdm_wakeup(clkdm);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int omap2xxx_clkdm_clk_disable(struct clockdomain *clkdm)
|
|
|
|
+{
|
|
|
|
+ bool hwsup = false;
|
|
|
|
+
|
|
|
|
+ if (!clkdm->clktrctrl_mask)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
|
|
|
|
+ clkdm->clktrctrl_mask);
|
|
|
|
+
|
|
|
|
+ if (hwsup) {
|
|
|
|
+ /* Disable HW transitions when we are changing deps */
|
|
|
|
+ omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
|
|
|
|
+ clkdm->clktrctrl_mask);
|
|
|
|
+ _clkdm_del_autodeps(clkdm);
|
|
|
|
+ omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
|
|
|
|
+ clkdm->clktrctrl_mask);
|
|
|
|
+ } else {
|
|
|
|
+ if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
|
|
|
|
+ omap2xxx_clkdm_sleep(clkdm);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+struct clkdm_ops omap2_clkdm_operations = {
|
|
|
|
+ .clkdm_add_wkdep = omap2_clkdm_add_wkdep,
|
|
|
|
+ .clkdm_del_wkdep = omap2_clkdm_del_wkdep,
|
|
|
|
+ .clkdm_read_wkdep = omap2_clkdm_read_wkdep,
|
|
|
|
+ .clkdm_clear_all_wkdeps = omap2_clkdm_clear_all_wkdeps,
|
|
|
|
+ .clkdm_sleep = omap2xxx_clkdm_sleep,
|
|
|
|
+ .clkdm_wakeup = omap2xxx_clkdm_wakeup,
|
|
|
|
+ .clkdm_allow_idle = omap2xxx_clkdm_allow_idle,
|
|
|
|
+ .clkdm_deny_idle = omap2xxx_clkdm_deny_idle,
|
|
|
|
+ .clkdm_clk_enable = omap2xxx_clkdm_clk_enable,
|
|
|
|
+ .clkdm_clk_disable = omap2xxx_clkdm_clk_disable,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static struct cm_ll_data omap2xxx_cm_ll_data = {
|
|
|
|
+ .split_idlest_reg = &omap2xxx_cm_split_idlest_reg,
|
|
|
|
+ .wait_module_ready = &omap2xxx_cm_wait_module_ready,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+int __init omap2xxx_cm_init(void)
|
|
|
|
+{
|
|
|
|
+ if (!cpu_is_omap24xx())
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ return cm_register(&omap2xxx_cm_ll_data);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void __exit omap2xxx_cm_exit(void)
|
|
|
|
+{
|
|
|
|
+ if (!cpu_is_omap24xx())
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ /* Should never happen */
|
|
|
|
+ WARN(cm_unregister(&omap2xxx_cm_ll_data),
|
|
|
|
+ "%s: cm_ll_data function pointer mismatch\n", __func__);
|
|
|
|
+}
|
|
|
|
+__exitcall(omap2xxx_cm_exit);
|