|
@@ -286,3 +286,200 @@ int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs,
|
|
|
* @part: PRCM partition ID that the CM_CLKCTRL register exists in
|
|
|
* @inst: CM instance register offset (*_INST macro)
|
|
|
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
|
|
|
+ * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
|
|
|
+ *
|
|
|
+ * Wait for the module IDLEST to be disabled. Some PRCM transition,
|
|
|
+ * like reset assertion or parent clock de-activation must wait the
|
|
|
+ * module to be fully disabled.
|
|
|
+ */
|
|
|
+int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
|
|
|
+{
|
|
|
+ int i = 0;
|
|
|
+
|
|
|
+ if (!clkctrl_offs)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ omap_test_timeout((_clkctrl_idlest(part, inst, cdoffs, clkctrl_offs) ==
|
|
|
+ CLKCTRL_IDLEST_DISABLED),
|
|
|
+ MAX_MODULE_DISABLE_TIME, i);
|
|
|
+
|
|
|
+ return (i < MAX_MODULE_DISABLE_TIME) ? 0 : -EBUSY;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * omap4_cminst_module_enable - Enable the modulemode inside CLKCTRL
|
|
|
+ * @mode: Module mode (SW or HW)
|
|
|
+ * @part: PRCM partition ID that the CM_CLKCTRL register exists in
|
|
|
+ * @inst: CM instance register offset (*_INST macro)
|
|
|
+ * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
|
|
|
+ * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
|
|
|
+ *
|
|
|
+ * No return value.
|
|
|
+ */
|
|
|
+void omap4_cminst_module_enable(u8 mode, u8 part, u16 inst, s16 cdoffs,
|
|
|
+ u16 clkctrl_offs)
|
|
|
+{
|
|
|
+ u32 v;
|
|
|
+
|
|
|
+ v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs);
|
|
|
+ v &= ~OMAP4430_MODULEMODE_MASK;
|
|
|
+ v |= mode << OMAP4430_MODULEMODE_SHIFT;
|
|
|
+ omap4_cminst_write_inst_reg(v, part, inst, clkctrl_offs);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * omap4_cminst_module_disable - Disable the module inside CLKCTRL
|
|
|
+ * @part: PRCM partition ID that the CM_CLKCTRL register exists in
|
|
|
+ * @inst: CM instance register offset (*_INST macro)
|
|
|
+ * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
|
|
|
+ * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
|
|
|
+ *
|
|
|
+ * No return value.
|
|
|
+ */
|
|
|
+void omap4_cminst_module_disable(u8 part, u16 inst, s16 cdoffs,
|
|
|
+ u16 clkctrl_offs)
|
|
|
+{
|
|
|
+ u32 v;
|
|
|
+
|
|
|
+ v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs);
|
|
|
+ v &= ~OMAP4430_MODULEMODE_MASK;
|
|
|
+ omap4_cminst_write_inst_reg(v, part, inst, clkctrl_offs);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Clockdomain low-level functions
|
|
|
+ */
|
|
|
+
|
|
|
+static int omap4_clkdm_add_wkup_sleep_dep(struct clockdomain *clkdm1,
|
|
|
+ struct clockdomain *clkdm2)
|
|
|
+{
|
|
|
+ omap4_cminst_set_inst_reg_bits((1 << clkdm2->dep_bit),
|
|
|
+ clkdm1->prcm_partition,
|
|
|
+ clkdm1->cm_inst, clkdm1->clkdm_offs +
|
|
|
+ OMAP4_CM_STATICDEP);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int omap4_clkdm_del_wkup_sleep_dep(struct clockdomain *clkdm1,
|
|
|
+ struct clockdomain *clkdm2)
|
|
|
+{
|
|
|
+ omap4_cminst_clear_inst_reg_bits((1 << clkdm2->dep_bit),
|
|
|
+ clkdm1->prcm_partition,
|
|
|
+ clkdm1->cm_inst, clkdm1->clkdm_offs +
|
|
|
+ OMAP4_CM_STATICDEP);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int omap4_clkdm_read_wkup_sleep_dep(struct clockdomain *clkdm1,
|
|
|
+ struct clockdomain *clkdm2)
|
|
|
+{
|
|
|
+ return omap4_cminst_read_inst_reg_bits(clkdm1->prcm_partition,
|
|
|
+ clkdm1->cm_inst,
|
|
|
+ clkdm1->clkdm_offs +
|
|
|
+ OMAP4_CM_STATICDEP,
|
|
|
+ (1 << clkdm2->dep_bit));
|
|
|
+}
|
|
|
+
|
|
|
+static int omap4_clkdm_clear_all_wkup_sleep_deps(struct clockdomain *clkdm)
|
|
|
+{
|
|
|
+ struct clkdm_dep *cd;
|
|
|
+ u32 mask = 0;
|
|
|
+
|
|
|
+ if (!clkdm->prcm_partition)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ for (cd = clkdm->wkdep_srcs; cd && cd->clkdm_name; cd++) {
|
|
|
+ if (!cd->clkdm)
|
|
|
+ continue; /* only happens if data is erroneous */
|
|
|
+
|
|
|
+ mask |= 1 << cd->clkdm->dep_bit;
|
|
|
+ atomic_set(&cd->wkdep_usecount, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ omap4_cminst_clear_inst_reg_bits(mask, clkdm->prcm_partition,
|
|
|
+ clkdm->cm_inst, clkdm->clkdm_offs +
|
|
|
+ OMAP4_CM_STATICDEP);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int omap4_clkdm_sleep(struct clockdomain *clkdm)
|
|
|
+{
|
|
|
+ omap4_cminst_clkdm_enable_hwsup(clkdm->prcm_partition,
|
|
|
+ clkdm->cm_inst, clkdm->clkdm_offs);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int omap4_clkdm_wakeup(struct clockdomain *clkdm)
|
|
|
+{
|
|
|
+ omap4_cminst_clkdm_force_wakeup(clkdm->prcm_partition,
|
|
|
+ clkdm->cm_inst, clkdm->clkdm_offs);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void omap4_clkdm_allow_idle(struct clockdomain *clkdm)
|
|
|
+{
|
|
|
+ omap4_cminst_clkdm_enable_hwsup(clkdm->prcm_partition,
|
|
|
+ clkdm->cm_inst, clkdm->clkdm_offs);
|
|
|
+}
|
|
|
+
|
|
|
+static void omap4_clkdm_deny_idle(struct clockdomain *clkdm)
|
|
|
+{
|
|
|
+ if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
|
|
|
+ omap4_clkdm_wakeup(clkdm);
|
|
|
+ else
|
|
|
+ omap4_cminst_clkdm_disable_hwsup(clkdm->prcm_partition,
|
|
|
+ clkdm->cm_inst,
|
|
|
+ clkdm->clkdm_offs);
|
|
|
+}
|
|
|
+
|
|
|
+static int omap4_clkdm_clk_enable(struct clockdomain *clkdm)
|
|
|
+{
|
|
|
+ if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
|
|
|
+ return omap4_clkdm_wakeup(clkdm);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int omap4_clkdm_clk_disable(struct clockdomain *clkdm)
|
|
|
+{
|
|
|
+ bool hwsup = false;
|
|
|
+
|
|
|
+ if (!clkdm->prcm_partition)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The CLKDM_MISSING_IDLE_REPORTING flag documentation has
|
|
|
+ * more details on the unpleasant problem this is working
|
|
|
+ * around
|
|
|
+ */
|
|
|
+ if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING &&
|
|
|
+ !(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
|
|
|
+ omap4_clkdm_allow_idle(clkdm);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition,
|
|
|
+ clkdm->cm_inst, clkdm->clkdm_offs);
|
|
|
+
|
|
|
+ if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
|
|
|
+ omap4_clkdm_sleep(clkdm);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+struct clkdm_ops omap4_clkdm_operations = {
|
|
|
+ .clkdm_add_wkdep = omap4_clkdm_add_wkup_sleep_dep,
|
|
|
+ .clkdm_del_wkdep = omap4_clkdm_del_wkup_sleep_dep,
|
|
|
+ .clkdm_read_wkdep = omap4_clkdm_read_wkup_sleep_dep,
|
|
|
+ .clkdm_clear_all_wkdeps = omap4_clkdm_clear_all_wkup_sleep_deps,
|
|
|
+ .clkdm_add_sleepdep = omap4_clkdm_add_wkup_sleep_dep,
|
|
|
+ .clkdm_del_sleepdep = omap4_clkdm_del_wkup_sleep_dep,
|
|
|
+ .clkdm_read_sleepdep = omap4_clkdm_read_wkup_sleep_dep,
|
|
|
+ .clkdm_clear_all_sleepdeps = omap4_clkdm_clear_all_wkup_sleep_deps,
|
|
|
+ .clkdm_sleep = omap4_clkdm_sleep,
|
|
|
+ .clkdm_wakeup = omap4_clkdm_wakeup,
|
|
|
+ .clkdm_allow_idle = omap4_clkdm_allow_idle,
|
|
|
+ .clkdm_deny_idle = omap4_clkdm_deny_idle,
|
|
|
+ .clkdm_clk_enable = omap4_clkdm_clk_enable,
|
|
|
+ .clkdm_clk_disable = omap4_clkdm_clk_disable,
|
|
|
+};
|