|
@@ -113,7 +113,6 @@ static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm,
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
for (cd = deps; cd->clkdm_name; cd++) {
|
|
|
-
|
|
|
if (!omap_chip_is(cd->omap_chip))
|
|
|
continue;
|
|
|
|
|
@@ -122,7 +121,6 @@ static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm,
|
|
|
|
|
|
if (cd->clkdm == clkdm)
|
|
|
break;
|
|
|
-
|
|
|
}
|
|
|
|
|
|
if (!cd->clkdm_name)
|
|
@@ -254,6 +252,96 @@ static void _omap2_clkdm_set_hwsup(struct clockdomain *clkdm, int enable)
|
|
|
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * _init_wkdep_usecount - initialize wkdep usecounts to match hardware
|
|
|
+ * @clkdm: clockdomain to initialize wkdep usecounts
|
|
|
+ *
|
|
|
+ * Initialize the wakeup dependency usecount variables for clockdomain @clkdm.
|
|
|
+ * If a wakeup dependency is present in the hardware, the usecount will be
|
|
|
+ * set to 1; otherwise, it will be set to 0. Software should clear all
|
|
|
+ * software wakeup dependencies prior to calling this function if it wishes
|
|
|
+ * to ensure that all usecounts start at 0. No return value.
|
|
|
+ */
|
|
|
+static void _init_wkdep_usecount(struct clockdomain *clkdm)
|
|
|
+{
|
|
|
+ u32 v;
|
|
|
+ struct clkdm_dep *cd;
|
|
|
+
|
|
|
+ if (!clkdm->wkdep_srcs)
|
|
|
+ return;
|
|
|
+
|
|
|
+ for (cd = clkdm->wkdep_srcs; cd->clkdm_name; cd++) {
|
|
|
+ if (!omap_chip_is(cd->omap_chip))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!cd->clkdm && cd->clkdm_name)
|
|
|
+ cd->clkdm = _clkdm_lookup(cd->clkdm_name);
|
|
|
+
|
|
|
+ if (!cd->clkdm) {
|
|
|
+ WARN(!cd->clkdm, "clockdomain: %s: wkdep clkdm %s not "
|
|
|
+ "found\n", clkdm->name, cd->clkdm_name);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ v = prm_read_mod_bits_shift(clkdm->pwrdm.ptr->prcm_offs,
|
|
|
+ PM_WKDEP,
|
|
|
+ (1 << cd->clkdm->dep_bit));
|
|
|
+
|
|
|
+ if (v)
|
|
|
+ pr_debug("clockdomain: %s: wakeup dependency already "
|
|
|
+ "set to wake up when %s wakes\n",
|
|
|
+ clkdm->name, cd->clkdm->name);
|
|
|
+
|
|
|
+ atomic_set(&cd->wkdep_usecount, (v) ? 1 : 0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _init_sleepdep_usecount - initialize sleepdep usecounts to match hardware
|
|
|
+ * @clkdm: clockdomain to initialize sleepdep usecounts
|
|
|
+ *
|
|
|
+ * Initialize the sleep dependency usecount variables for clockdomain @clkdm.
|
|
|
+ * If a sleep dependency is present in the hardware, the usecount will be
|
|
|
+ * set to 1; otherwise, it will be set to 0. Software should clear all
|
|
|
+ * software sleep dependencies prior to calling this function if it wishes
|
|
|
+ * to ensure that all usecounts start at 0. No return value.
|
|
|
+ */
|
|
|
+static void _init_sleepdep_usecount(struct clockdomain *clkdm)
|
|
|
+{
|
|
|
+ u32 v;
|
|
|
+ struct clkdm_dep *cd;
|
|
|
+
|
|
|
+ if (!cpu_is_omap34xx())
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (!clkdm->sleepdep_srcs)
|
|
|
+ return;
|
|
|
+
|
|
|
+ for (cd = clkdm->sleepdep_srcs; cd->clkdm_name; cd++) {
|
|
|
+ if (!omap_chip_is(cd->omap_chip))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!cd->clkdm && cd->clkdm_name)
|
|
|
+ cd->clkdm = _clkdm_lookup(cd->clkdm_name);
|
|
|
+
|
|
|
+ if (!cd->clkdm) {
|
|
|
+ WARN(!cd->clkdm, "clockdomain: %s: sleepdep clkdm %s "
|
|
|
+ "not found\n", clkdm->name, cd->clkdm_name);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ v = prm_read_mod_bits_shift(clkdm->pwrdm.ptr->prcm_offs,
|
|
|
+ OMAP3430_CM_SLEEPDEP,
|
|
|
+ (1 << cd->clkdm->dep_bit));
|
|
|
+
|
|
|
+ if (v)
|
|
|
+ pr_debug("clockdomain: %s: sleep dependency already "
|
|
|
+ "set to prevent from idling until %s "
|
|
|
+ "idles\n", clkdm->name, cd->clkdm->name);
|
|
|
+
|
|
|
+ atomic_set(&cd->sleepdep_usecount, (v) ? 1 : 0);
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
/* Public functions */
|
|
|
|
|
@@ -272,6 +360,7 @@ void clkdm_init(struct clockdomain **clkdms,
|
|
|
struct clkdm_autodep *init_autodeps)
|
|
|
{
|
|
|
struct clockdomain **c = NULL;
|
|
|
+ struct clockdomain *clkdm;
|
|
|
struct clkdm_autodep *autodep = NULL;
|
|
|
|
|
|
if (clkdms)
|
|
@@ -282,6 +371,15 @@ void clkdm_init(struct clockdomain **clkdms,
|
|
|
if (autodeps)
|
|
|
for (autodep = autodeps; autodep->clkdm.ptr; autodep++)
|
|
|
_autodep_lookup(autodep);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Ensure that the *dep_usecount registers reflect the current
|
|
|
+ * state of the PRCM.
|
|
|
+ */
|
|
|
+ list_for_each_entry(clkdm, &clkdm_list, node) {
|
|
|
+ _init_wkdep_usecount(clkdm);
|
|
|
+ _init_sleepdep_usecount(clkdm);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -387,11 +485,13 @@ int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
|
|
|
return PTR_ERR(cd);
|
|
|
}
|
|
|
|
|
|
- pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n",
|
|
|
- clkdm1->name, clkdm2->name);
|
|
|
+ if (atomic_inc_return(&cd->wkdep_usecount) == 1) {
|
|
|
+ pr_debug("clockdomain: hardware will wake up %s when %s wakes "
|
|
|
+ "up\n", clkdm1->name, clkdm2->name);
|
|
|
|
|
|
- prm_set_mod_reg_bits((1 << clkdm2->dep_bit),
|
|
|
- clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
|
|
|
+ prm_set_mod_reg_bits((1 << clkdm2->dep_bit),
|
|
|
+ clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -420,11 +520,13 @@ int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
|
|
|
return PTR_ERR(cd);
|
|
|
}
|
|
|
|
|
|
- pr_debug("clockdomain: hardware will no longer wake up %s after %s "
|
|
|
- "wakes up\n", clkdm1->name, clkdm2->name);
|
|
|
+ if (atomic_dec_return(&cd->wkdep_usecount) == 0) {
|
|
|
+ pr_debug("clockdomain: hardware will no longer wake up %s "
|
|
|
+ "after %s wakes up\n", clkdm1->name, clkdm2->name);
|
|
|
|
|
|
- prm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
|
|
|
- clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
|
|
|
+ prm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
|
|
|
+ clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -457,10 +559,43 @@ int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
|
|
|
return PTR_ERR(cd);
|
|
|
}
|
|
|
|
|
|
+ /* XXX It's faster to return the atomic wkdep_usecount */
|
|
|
return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP,
|
|
|
(1 << clkdm2->dep_bit));
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * clkdm_clear_all_wkdeps - remove all wakeup dependencies from target clkdm
|
|
|
+ * @clkdm: struct clockdomain * to remove all wakeup dependencies from
|
|
|
+ *
|
|
|
+ * Remove all inter-clockdomain wakeup dependencies that could cause
|
|
|
+ * @clkdm to wake. Intended to be used during boot to initialize the
|
|
|
+ * PRCM to a known state, after all clockdomains are put into swsup idle
|
|
|
+ * and woken up. Returns -EINVAL if @clkdm pointer is invalid, or
|
|
|
+ * 0 upon success.
|
|
|
+ */
|
|
|
+int clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
|
|
|
+{
|
|
|
+ struct clkdm_dep *cd;
|
|
|
+ u32 mask = 0;
|
|
|
+
|
|
|
+ if (!clkdm)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ for (cd = clkdm->wkdep_srcs; cd && cd->clkdm_name; cd++) {
|
|
|
+ if (!omap_chip_is(cd->omap_chip))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* PRM accesses are slow, so minimize them */
|
|
|
+ mask |= 1 << cd->clkdm->dep_bit;
|
|
|
+ atomic_set(&cd->wkdep_usecount, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs, PM_WKDEP);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1
|
|
|
* @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
|
|
@@ -491,12 +626,14 @@ int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
|
|
|
return PTR_ERR(cd);
|
|
|
}
|
|
|
|
|
|
- pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n",
|
|
|
- clkdm1->name, clkdm2->name);
|
|
|
+ if (atomic_inc_return(&cd->sleepdep_usecount) == 1) {
|
|
|
+ pr_debug("clockdomain: will prevent %s from sleeping if %s "
|
|
|
+ "is active\n", clkdm1->name, clkdm2->name);
|
|
|
|
|
|
- cm_set_mod_reg_bits((1 << clkdm2->dep_bit),
|
|
|
- clkdm1->pwrdm.ptr->prcm_offs,
|
|
|
- OMAP3430_CM_SLEEPDEP);
|
|
|
+ cm_set_mod_reg_bits((1 << clkdm2->dep_bit),
|
|
|
+ clkdm1->pwrdm.ptr->prcm_offs,
|
|
|
+ OMAP3430_CM_SLEEPDEP);
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -531,12 +668,15 @@ int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
|
|
|
return PTR_ERR(cd);
|
|
|
}
|
|
|
|
|
|
- pr_debug("clockdomain: will no longer prevent %s from sleeping if "
|
|
|
- "%s is active\n", clkdm1->name, clkdm2->name);
|
|
|
+ if (atomic_dec_return(&cd->sleepdep_usecount) == 0) {
|
|
|
+ pr_debug("clockdomain: will no longer prevent %s from "
|
|
|
+ "sleeping if %s is active\n", clkdm1->name,
|
|
|
+ clkdm2->name);
|
|
|
|
|
|
- cm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
|
|
|
- clkdm1->pwrdm.ptr->prcm_offs,
|
|
|
- OMAP3430_CM_SLEEPDEP);
|
|
|
+ cm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
|
|
|
+ clkdm1->pwrdm.ptr->prcm_offs,
|
|
|
+ OMAP3430_CM_SLEEPDEP);
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -575,11 +715,47 @@ int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
|
|
|
return PTR_ERR(cd);
|
|
|
}
|
|
|
|
|
|
+ /* XXX It's faster to return the atomic sleepdep_usecount */
|
|
|
return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs,
|
|
|
OMAP3430_CM_SLEEPDEP,
|
|
|
(1 << clkdm2->dep_bit));
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * clkdm_clear_all_sleepdeps - remove all sleep dependencies from target clkdm
|
|
|
+ * @clkdm: struct clockdomain * to remove all sleep dependencies from
|
|
|
+ *
|
|
|
+ * Remove all inter-clockdomain sleep dependencies that could prevent
|
|
|
+ * @clkdm from idling. Intended to be used during boot to initialize the
|
|
|
+ * PRCM to a known state, after all clockdomains are put into swsup idle
|
|
|
+ * and woken up. Returns -EINVAL if @clkdm pointer is invalid, or
|
|
|
+ * 0 upon success.
|
|
|
+ */
|
|
|
+int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
|
|
|
+{
|
|
|
+ struct clkdm_dep *cd;
|
|
|
+ u32 mask = 0;
|
|
|
+
|
|
|
+ if (!cpu_is_omap34xx())
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (!clkdm)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ for (cd = clkdm->sleepdep_srcs; cd && cd->clkdm_name; cd++) {
|
|
|
+ if (!omap_chip_is(cd->omap_chip))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* PRM accesses are slow, so minimize them */
|
|
|
+ mask |= 1 << cd->clkdm->dep_bit;
|
|
|
+ atomic_set(&cd->sleepdep_usecount, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs,
|
|
|
+ OMAP3430_CM_SLEEPDEP);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
/**
|
|
|
* omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode
|