===================================================================
@@ -12,7 +12,11 @@
#include <linux/device.h>
struct dev_power_governor {
- bool (*power_down_ok)(struct dev_power_domain *domain);
+ int (*choose_state)(struct dev_power_domain *domain);
+};
+
+struct power_domain_state {
+ void *platform_data;
};
struct generic_power_domain {
@@ -23,12 +27,14 @@ struct generic_power_domain {
struct list_head device_list;
struct mutex lock;
struct dev_power_governor *gov;
- unsigned int in_progress;
+ int nr_states;
+ int current_state;
bool power_is_off;
+ unsigned int in_progress;
unsigned int device_count;
unsigned int suspended_count;
- int (*power_off)(struct dev_power_domain *domain);
- int (*power_on)(struct dev_power_domain *domain);
+ int (*set_state)(struct dev_power_domain *domain, int state);
+ bool (*power_off_state)(struct dev_power_domain *domain, int state);
int (*start_device)(struct device *dev);
int (*stop_device)(struct device *dev);
};
@@ -48,7 +54,8 @@ extern int pm_genpd_add_subdomain(struct
extern int pm_genpd_remove_subdomain(struct generic_power_domain *genpd,
struct generic_power_domain *target);
extern void pm_genpd_init(struct generic_power_domain *genpd,
- struct dev_power_governor *gov, bool is_off);
+ struct dev_power_governor *gov, int nr_states,
+ int cur_state);
#else
static inline int pm_genpd_add_device(struct generic_power_domain *genpd,
struct device *dev)
@@ -71,7 +78,8 @@ static inline int pm_genpd_remove_subdom
return -ENOSYS;
}
static inline void pm_genpd_init(struct generic_power_domain *genpd,
- struct dev_power_governor *gov, bool is_off) {}
+ struct dev_power_governor *gov,
+ int nr_states, int cur_state) {}
#endif
#endif /* _LINUX_PM_DOMAIN_H */
===================================================================
@@ -62,6 +62,7 @@ static int __pm_genpd_poweroff(struct ge
struct generic_power_domain *subdomain;
struct dev_list_entry *dle;
unsigned int not_suspended;
+ int new_state;
int ret;
if (genpd->power_is_off)
@@ -83,9 +84,23 @@ static int __pm_genpd_poweroff(struct ge
return ret;
}
- if (genpd->gov && genpd->gov->power_down_ok) {
- if (!genpd->gov->power_down_ok(&genpd->domain))
- return -EAGAIN;
+ new_state = (genpd->gov && genpd->gov->choose_state) ?
+ genpd->gov->choose_state(&genpd->domain) : 1;
+ if (new_state < 0 || new_state >= genpd->nr_states)
+ return -EAGAIN;
+
+ if (new_state == genpd->current_state)
+ return 0;
+
+ if (genpd->power_off_state
+ && !genpd->power_off_state(&genpd->domain, new_state)) {
+ if (genpd->set_state) {
+ ret = genpd->set_state(&genpd->domain, new_state);
+ if (ret)
+ return ret;
+ }
+ genpd->current_state = new_state;
+ return 0;
}
list_for_each_entry_reverse(dle, &genpd->device_list, node) {
@@ -105,9 +120,13 @@ static int __pm_genpd_poweroff(struct ge
goto err_dev;
}
- if (genpd->power_off)
- genpd->power_off(&genpd->domain);
+ if (genpd->set_state) {
+ ret = genpd->set_state(&genpd->domain, new_state);
+ if (ret)
+ goto err_dev;
+ }
+ genpd->current_state = new_state;
genpd->power_is_off = true;
return 0;
@@ -199,14 +218,17 @@ static int __pm_genpd_poweron(struct gen
{
struct dev_list_entry *dle;
- if (!genpd->power_is_off)
+ if (genpd->current_state == 0)
return 0;
- if (genpd->power_on) {
- int ret = genpd->power_on(&genpd->domain);
+ if (genpd->set_state) {
+ int ret = genpd->set_state(&genpd->domain, 0);
if (ret)
return ret;
}
+ genpd->current_state = 0;
+ if (!genpd->power_is_off)
+ return 0;
genpd->power_is_off = false;
@@ -363,8 +385,8 @@ static int pm_genpd_suspend_noirq(struct
mutex_lock(&genpd->lock);
if (++genpd->suspended_count == genpd->device_count) {
- if (genpd->power_off)
- genpd->power_off(&genpd->domain);
+ if (genpd->set_state)
+ genpd->set_state(&genpd->domain, genpd->nr_states - 1);
}
mutex_unlock(&genpd->lock);
@@ -395,8 +417,8 @@ static int pm_genpd_resume_noirq(struct
mutex_lock(&genpd->lock);
if (genpd->suspended_count == genpd->device_count) {
- if (genpd->power_on) {
- int ret = genpd->power_on(&genpd->domain);
+ if (genpd->set_state) {
+ int ret = genpd->set_state(&genpd->domain, 0);
if (ret) {
mutex_unlock(&genpd->lock);
return ret;
@@ -713,10 +735,12 @@ int pm_genpd_remove_subdomain(struct gen
* pm_genpd_init - Initialize a generic I/O power domain object.
* @genpd: Power domain object to initialize.
* @gov: Power domain governor to associate with the domain (may be NULL).
- * @is_off: Initial value of the domain's power_is_off field.
+ * @nr_states: Number of power domain states (must be greater than 1).
+ * @cur_state: Initial state of the power domain.
*/
void pm_genpd_init(struct generic_power_domain *genpd,
- struct dev_power_governor *gov, bool is_off)
+ struct dev_power_governor *gov, int nr_states,
+ int cur_state)
{
if (IS_ERR_OR_NULL(genpd))
return;
@@ -727,8 +751,14 @@ void pm_genpd_init(struct generic_power_
INIT_LIST_HEAD(&genpd->subdomain_list);
mutex_init(&genpd->lock);
genpd->gov = gov;
+ genpd->nr_states = nr_states > 1 ? nr_states : 2;
+ if (cur_state < 0 || cur_state >= nr_states)
+ cur_state = 0;
+ genpd->current_state = cur_state;
+ genpd->power_is_off = genpd->power_off_state ?
+ genpd->power_off_state(&genpd->domain, cur_state) :
+ (cur_state > 0);
genpd->in_progress = 0;
- genpd->power_is_off = is_off;
genpd->device_count = 0;
genpd->suspended_count = 0;
genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
===================================================================
@@ -60,15 +60,20 @@ static int pd_power_up(struct dev_power_
return 0;
}
+static int pd_set_state(struct dev_power_domain *domain, int state)
+{
+ return state > 0 ? pd_power_down(domain) : pd_power_up(domain);
+}
+
static void sh7372_init_domain(struct generic_power_domain *domain,
struct sh7372_domain_data *pdata)
{
- pm_genpd_init(domain, NULL, false);
domain->domain.platform_data = pdata;
domain->stop_device = pm_runtime_clk_suspend;
domain->start_device = pm_runtime_clk_resume;
- domain->power_off = pd_power_down;
- domain->power_on = pd_power_up;
+ domain->set_state = pd_set_state;
+ domain->power_off_state = NULL;
+ pm_genpd_init(domain, NULL, 2, 0);
}
void sh7372_add_device_to_domain(struct generic_power_domain *domain,