|
@@ -436,3 +436,148 @@ struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
|
|
|
return NULL;
|
|
|
|
|
|
return clkdm->pwrdm.ptr;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* Hardware clockdomain control */
|
|
|
+
|
|
|
+/**
|
|
|
+ * clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1
|
|
|
+ * @clkdm1: wake this struct clockdomain * up (dependent)
|
|
|
+ * @clkdm2: when this struct clockdomain * wakes up (source)
|
|
|
+ *
|
|
|
+ * When the clockdomain represented by @clkdm2 wakes up, wake up
|
|
|
+ * @clkdm1. Implemented in hardware on the OMAP, this feature is
|
|
|
+ * designed to reduce wakeup latency of the dependent clockdomain @clkdm1.
|
|
|
+ * Returns -EINVAL if presented with invalid clockdomain pointers,
|
|
|
+ * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon
|
|
|
+ * success.
|
|
|
+ */
|
|
|
+int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
|
|
|
+{
|
|
|
+ struct clkdm_dep *cd;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!clkdm1 || !clkdm2)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
|
|
|
+ if (IS_ERR(cd))
|
|
|
+ ret = PTR_ERR(cd);
|
|
|
+
|
|
|
+ if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep)
|
|
|
+ ret = -EINVAL;
|
|
|
+
|
|
|
+ if (ret) {
|
|
|
+ pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
|
|
|
+ clkdm1->name, clkdm2->name);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+ ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * clkdm_del_wkdep - remove a wakeup dependency from clkdm2 to clkdm1
|
|
|
+ * @clkdm1: wake this struct clockdomain * up (dependent)
|
|
|
+ * @clkdm2: when this struct clockdomain * wakes up (source)
|
|
|
+ *
|
|
|
+ * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2
|
|
|
+ * wakes up. Returns -EINVAL if presented with invalid clockdomain
|
|
|
+ * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or
|
|
|
+ * 0 upon success.
|
|
|
+ */
|
|
|
+int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
|
|
|
+{
|
|
|
+ struct clkdm_dep *cd;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!clkdm1 || !clkdm2)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
|
|
|
+ if (IS_ERR(cd))
|
|
|
+ ret = PTR_ERR(cd);
|
|
|
+
|
|
|
+ if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep)
|
|
|
+ ret = -EINVAL;
|
|
|
+
|
|
|
+ if (ret) {
|
|
|
+ pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
|
|
|
+ clkdm1->name, clkdm2->name);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+ ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * clkdm_read_wkdep - read wakeup dependency state from clkdm2 to clkdm1
|
|
|
+ * @clkdm1: wake this struct clockdomain * up (dependent)
|
|
|
+ * @clkdm2: when this struct clockdomain * wakes up (source)
|
|
|
+ *
|
|
|
+ * Return 1 if a hardware wakeup dependency exists wherein @clkdm1 will be
|
|
|
+ * awoken when @clkdm2 wakes up; 0 if dependency is not set; -EINVAL
|
|
|
+ * if either clockdomain pointer is invalid; or -ENOENT if the hardware
|
|
|
+ * is incapable.
|
|
|
+ *
|
|
|
+ * REVISIT: Currently this function only represents software-controllable
|
|
|
+ * wakeup dependencies. Wakeup dependencies fixed in hardware are not
|
|
|
+ * yet handled here.
|
|
|
+ */
|
|
|
+int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
|
|
|
+{
|
|
|
+ struct clkdm_dep *cd;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!clkdm1 || !clkdm2)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
|
|
|
+ if (IS_ERR(cd))
|
|
|
+ ret = PTR_ERR(cd);
|
|
|
+
|
|
|
+ if (!arch_clkdm || !arch_clkdm->clkdm_read_wkdep)
|
|
|
+ ret = -EINVAL;
|
|
|
+
|
|
|
+ if (ret) {
|
|
|
+ pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
|
|
|
+ clkdm1->name, clkdm2->name);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* XXX It's faster to return the atomic wkdep_usecount */
|
|
|
+ return arch_clkdm->clkdm_read_wkdep(clkdm1, clkdm2);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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)
|
|
|
+{
|
|
|
+ if (!clkdm)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (!arch_clkdm || !arch_clkdm->clkdm_clear_all_wkdeps)
|
|
|
+ return -EINVAL;
|