ARM: OMAP2+: powerdomain: introduce logic for finding valid power domain
[cascardo/linux.git] / arch / arm / mach-omap2 / powerdomain.c
index f391948..7fb033e 100644 (file)
@@ -1080,6 +1080,82 @@ int pwrdm_post_transition(struct powerdomain *pwrdm)
        return 0;
 }
 
+/**
+ * pwrdm_get_valid_lp_state() - Find best match deep power state
+ * @pwrdm:     power domain for which we want to find best match
+ * @is_logic_state: Are we looking for logic state match here? Should
+ *                 be one of PWRDM_xxx macro values
+ * @req_state: requested power state
+ *
+ * Returns: closest match for requested power state. default fallback
+ * is RET for logic state and ON for power state.
+ *
+ * This does a search from the power domain data looking for the
+ * closest valid power domain state that the hardware can achieve.
+ * PRCM definitions for PWRSTCTRL allows us to program whatever
+ * configuration we'd like, and PRCM will actually attempt such
+ * a transition, however if the powerdomain does not actually support it,
+ * we endup with a hung system. The valid power domain states are already
+ * available in our powerdomain data files. So this function tries to do
+ * the following:
+ * a) find if we have an exact match to the request - no issues.
+ * b) else find if a deeper power state is possible.
+ * c) failing which, it tries to find closest higher power state for the
+ * request.
+ */
+u8 pwrdm_get_valid_lp_state(struct powerdomain *pwrdm,
+                           bool is_logic_state, u8 req_state)
+{
+       u8 pwrdm_states = is_logic_state ? pwrdm->pwrsts_logic_ret :
+                       pwrdm->pwrsts;
+       /* For logic, ret is highest and others, ON is highest */
+       u8 default_pwrst = is_logic_state ? PWRDM_POWER_RET : PWRDM_POWER_ON;
+       u8 new_pwrst;
+       bool found;
+
+       /* If it is already supported, nothing to search */
+       if (pwrdm_states & BIT(req_state))
+               return req_state;
+
+       if (!req_state)
+               goto up_search;
+
+       /*
+        * So, we dont have a exact match
+        * Can we get a deeper power state match?
+        */
+       new_pwrst = req_state - 1;
+       found = true;
+       while (!(pwrdm_states & BIT(new_pwrst))) {
+               /* No match even at OFF? Not available */
+               if (new_pwrst == PWRDM_POWER_OFF) {
+                       found = false;
+                       break;
+               }
+               new_pwrst--;
+       }
+
+       if (found)
+               goto done;
+
+up_search:
+       /* OK, no deeper ones, can we get a higher match? */
+       new_pwrst = req_state + 1;
+       while (!(pwrdm_states & BIT(new_pwrst))) {
+               if (new_pwrst > PWRDM_POWER_ON) {
+                       WARN(1, "powerdomain: %s: Fix max powerstate to ON\n",
+                            pwrdm->name);
+                       return PWRDM_POWER_ON;
+               }
+
+               if (new_pwrst == default_pwrst)
+                       break;
+               new_pwrst++;
+       }
+done:
+       return new_pwrst;
+}
+
 /**
  * omap_set_pwrdm_state - change a powerdomain's current power state
  * @pwrdm: struct powerdomain * to change the power state of