if (omap_rev() == OMAP4430_REV_ES1_0)
return -ENXIO;
+ /* Use the achievable power state for the domain */
+ power_state = pwrdm_get_valid_lp_state(pm_info->pwrdm,
+ false, power_state);
+
if (power_state == PWRDM_POWER_OFF)
cpu_state = 1;
struct power_state {
struct powerdomain *pwrdm;
u32 next_state;
+ u32 next_logic_state;
#ifdef CONFIG_SUSPEND
u32 saved_state;
u32 saved_logic_state;
/* Set targeted power domain states by suspend */
list_for_each_entry(pwrst, &pwrst_list, node) {
omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
- pwrdm_set_logic_retst(pwrst->pwrdm, PWRDM_POWER_OFF);
+ pwrdm_set_logic_retst(pwrst->pwrdm, pwrst->next_logic_state);
}
/*
return -ENOMEM;
pwrst->pwrdm = pwrdm;
- pwrst->next_state = PWRDM_POWER_RET;
+ pwrst->next_state = pwrdm_get_valid_lp_state(pwrdm, false,
+ PWRDM_POWER_RET);
+ pwrst->next_logic_state = pwrdm_get_valid_lp_state(pwrdm, true,
+ PWRDM_POWER_OFF);
+
list_add(&pwrst->node, &pwrst_list);
return omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
return -EINVAL;
for (i = 0; i < PWRDM_MAX_CLKDMS && !ret; i++)
- ret = (*fn)(pwrdm, pwrdm->pwrdm_clkdms[i]);
+ if (pwrdm->pwrdm_clkdms[i])
+ ret = (*fn)(pwrdm, pwrdm->pwrdm_clkdms[i]);
return ret;
}
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
#define PWRSTS_OFF_RET (PWRSTS_OFF | PWRSTS_RET)
#define PWRSTS_RET_ON (PWRSTS_RET | PWRSTS_ON)
#define PWRSTS_OFF_RET_ON (PWRSTS_OFF_RET | PWRSTS_ON)
+#define PWRSTS_INA_ON (PWRSTS_INACTIVE | PWRSTS_ON)
/*
int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm);
+u8 pwrdm_get_valid_lp_state(struct powerdomain *pwrdm,
+ bool is_logic_state, u8 req_state);
+
int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst);
int pwrdm_read_next_pwrst(struct powerdomain *pwrdm);
int pwrdm_read_pwrst(struct powerdomain *pwrdm);
.prcm_offs = OMAP54XX_PRM_CORE_INST,
.prcm_partition = OMAP54XX_PRM_PARTITION,
.pwrsts = PWRSTS_RET_ON,
- .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .pwrsts_logic_ret = PWRSTS_RET,
.banks = 5,
.pwrsts_mem_ret = {
[0] = PWRSTS_OFF_RET, /* core_nret_bank */
.voltdm = { .name = "mpu" },
.prcm_offs = OMAP54XX_PRCM_MPU_PRM_C0_INST,
.prcm_partition = OMAP54XX_PRCM_MPU_PARTITION,
- .pwrsts = PWRSTS_OFF_RET_ON,
- .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .pwrsts = PWRSTS_RET_ON,
+ .pwrsts_logic_ret = PWRSTS_RET,
.banks = 1,
.pwrsts_mem_ret = {
[0] = PWRSTS_OFF_RET, /* cpu0_l1 */
.voltdm = { .name = "mpu" },
.prcm_offs = OMAP54XX_PRCM_MPU_PRM_C1_INST,
.prcm_partition = OMAP54XX_PRCM_MPU_PARTITION,
- .pwrsts = PWRSTS_OFF_RET_ON,
- .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .pwrsts = PWRSTS_RET_ON,
+ .pwrsts_logic_ret = PWRSTS_RET,
.banks = 1,
.pwrsts_mem_ret = {
[0] = PWRSTS_OFF_RET, /* cpu1_l1 */
.prcm_offs = OMAP54XX_PRM_MPU_INST,
.prcm_partition = OMAP54XX_PRM_PARTITION,
.pwrsts = PWRSTS_RET_ON,
- .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .pwrsts_logic_ret = PWRSTS_RET,
.banks = 2,
.pwrsts_mem_ret = {
[0] = PWRSTS_OFF_RET, /* mpu_l2 */
.name = "core_pwrdm",
.prcm_offs = DRA7XX_PRM_CORE_INST,
.prcm_partition = DRA7XX_PRM_PARTITION,
- .pwrsts = PWRSTS_RET_ON,
- .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .pwrsts = PWRSTS_INA_ON,
+ .pwrsts_logic_ret = PWRSTS_RET,
.banks = 5,
.pwrsts_mem_ret = {
[0] = PWRSTS_OFF_RET, /* core_nret_bank */
.name = "cpu0_pwrdm",
.prcm_offs = DRA7XX_MPU_PRCM_PRM_C0_INST,
.prcm_partition = DRA7XX_MPU_PRCM_PARTITION,
- .pwrsts = PWRSTS_OFF_RET_ON,
- .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .pwrsts = PWRSTS_RET_ON,
+ .pwrsts_logic_ret = PWRSTS_RET,
.banks = 1,
.pwrsts_mem_ret = {
[0] = PWRSTS_OFF_RET, /* cpu0_l1 */
.name = "cpu1_pwrdm",
.prcm_offs = DRA7XX_MPU_PRCM_PRM_C1_INST,
.prcm_partition = DRA7XX_MPU_PRCM_PARTITION,
- .pwrsts = PWRSTS_OFF_RET_ON,
- .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .pwrsts = PWRSTS_RET_ON,
+ .pwrsts_logic_ret = PWRSTS_RET,
.banks = 1,
.pwrsts_mem_ret = {
[0] = PWRSTS_OFF_RET, /* cpu1_l1 */
.prcm_offs = DRA7XX_PRM_MPU_INST,
.prcm_partition = DRA7XX_PRM_PARTITION,
.pwrsts = PWRSTS_RET_ON,
- .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .pwrsts_logic_ret = PWRSTS_RET,
.banks = 2,
.pwrsts_mem_ret = {
[0] = PWRSTS_OFF_RET, /* mpu_l2 */