diff options
Diffstat (limited to 'drivers/base/power/main.c')
| -rw-r--r-- | drivers/base/power/main.c | 375 | 
1 files changed, 165 insertions, 210 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index c3d2dfcf438..e2cc3d2e0ec 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -32,6 +32,8 @@  #include "../base.h"  #include "power.h" +typedef int (*pm_callback_t)(struct device *); +  /*   * The entries in the dpm_list list are in a depth first order, simply   * because children are guaranteed to be discovered after parents, and @@ -164,8 +166,9 @@ static ktime_t initcall_debug_start(struct device *dev)  	ktime_t calltime = ktime_set(0, 0);  	if (initcall_debug) { -		pr_info("calling  %s+ @ %i\n", -				dev_name(dev), task_pid_nr(current)); +		pr_info("calling  %s+ @ %i, parent: %s\n", +			dev_name(dev), task_pid_nr(current), +			dev->parent ? dev_name(dev->parent) : "none");  		calltime = ktime_get();  	} @@ -211,151 +214,69 @@ static void dpm_wait_for_children(struct device *dev, bool async)  }  /** - * pm_op - Execute the PM operation appropriate for given PM event. - * @dev: Device to handle. + * pm_op - Return the PM operation appropriate for given PM event.   * @ops: PM operations to choose from.   * @state: PM transition of the system being carried out.   */ -static int pm_op(struct device *dev, -		 const struct dev_pm_ops *ops, -		 pm_message_t state) +static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state)  { -	int error = 0; -	ktime_t calltime; - -	calltime = initcall_debug_start(dev); -  	switch (state.event) {  #ifdef CONFIG_SUSPEND  	case PM_EVENT_SUSPEND: -		if (ops->suspend) { -			error = ops->suspend(dev); -			suspend_report_result(ops->suspend, error); -		} -		break; +		return ops->suspend;  	case PM_EVENT_RESUME: -		if (ops->resume) { -			error = ops->resume(dev); -			suspend_report_result(ops->resume, error); -		} -		break; +		return ops->resume;  #endif /* CONFIG_SUSPEND */  #ifdef CONFIG_HIBERNATE_CALLBACKS  	case PM_EVENT_FREEZE:  	case PM_EVENT_QUIESCE: -		if (ops->freeze) { -			error = ops->freeze(dev); -			suspend_report_result(ops->freeze, error); -		} -		break; +		return ops->freeze;  	case PM_EVENT_HIBERNATE: -		if (ops->poweroff) { -			error = ops->poweroff(dev); -			suspend_report_result(ops->poweroff, error); -		} -		break; +		return ops->poweroff;  	case PM_EVENT_THAW:  	case PM_EVENT_RECOVER: -		if (ops->thaw) { -			error = ops->thaw(dev); -			suspend_report_result(ops->thaw, error); -		} +		return ops->thaw;  		break;  	case PM_EVENT_RESTORE: -		if (ops->restore) { -			error = ops->restore(dev); -			suspend_report_result(ops->restore, error); -		} -		break; +		return ops->restore;  #endif /* CONFIG_HIBERNATE_CALLBACKS */ -	default: -		error = -EINVAL;  	} -	initcall_debug_report(dev, calltime, error); - -	return error; +	return NULL;  }  /** - * pm_noirq_op - Execute the PM operation appropriate for given PM event. - * @dev: Device to handle. + * pm_noirq_op - Return the PM operation appropriate for given PM event.   * @ops: PM operations to choose from.   * @state: PM transition of the system being carried out.   *   * The driver of @dev will not receive interrupts while this function is being   * executed.   */ -static int pm_noirq_op(struct device *dev, -			const struct dev_pm_ops *ops, -			pm_message_t state) +static pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t state)  { -	int error = 0; -	ktime_t calltime = ktime_set(0, 0), delta, rettime; - -	if (initcall_debug) { -		pr_info("calling  %s+ @ %i, parent: %s\n", -				dev_name(dev), task_pid_nr(current), -				dev->parent ? dev_name(dev->parent) : "none"); -		calltime = ktime_get(); -	} -  	switch (state.event) {  #ifdef CONFIG_SUSPEND  	case PM_EVENT_SUSPEND: -		if (ops->suspend_noirq) { -			error = ops->suspend_noirq(dev); -			suspend_report_result(ops->suspend_noirq, error); -		} -		break; +		return ops->suspend_noirq;  	case PM_EVENT_RESUME: -		if (ops->resume_noirq) { -			error = ops->resume_noirq(dev); -			suspend_report_result(ops->resume_noirq, error); -		} -		break; +		return ops->resume_noirq;  #endif /* CONFIG_SUSPEND */  #ifdef CONFIG_HIBERNATE_CALLBACKS  	case PM_EVENT_FREEZE:  	case PM_EVENT_QUIESCE: -		if (ops->freeze_noirq) { -			error = ops->freeze_noirq(dev); -			suspend_report_result(ops->freeze_noirq, error); -		} -		break; +		return ops->freeze_noirq;  	case PM_EVENT_HIBERNATE: -		if (ops->poweroff_noirq) { -			error = ops->poweroff_noirq(dev); -			suspend_report_result(ops->poweroff_noirq, error); -		} -		break; +		return ops->poweroff_noirq;  	case PM_EVENT_THAW:  	case PM_EVENT_RECOVER: -		if (ops->thaw_noirq) { -			error = ops->thaw_noirq(dev); -			suspend_report_result(ops->thaw_noirq, error); -		} -		break; +		return ops->thaw_noirq;  	case PM_EVENT_RESTORE: -		if (ops->restore_noirq) { -			error = ops->restore_noirq(dev); -			suspend_report_result(ops->restore_noirq, error); -		} -		break; +		return ops->restore_noirq;  #endif /* CONFIG_HIBERNATE_CALLBACKS */ -	default: -		error = -EINVAL; -	} - -	if (initcall_debug) { -		rettime = ktime_get(); -		delta = ktime_sub(rettime, calltime); -		printk("initcall %s_i+ returned %d after %Ld usecs\n", -			dev_name(dev), error, -			(unsigned long long)ktime_to_ns(delta) >> 10);  	} -	return error; +	return NULL;  }  static char *pm_verb(int event) @@ -413,6 +334,26 @@ static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info)  		usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC);  } +static int dpm_run_callback(pm_callback_t cb, struct device *dev, +			    pm_message_t state, char *info) +{ +	ktime_t calltime; +	int error; + +	if (!cb) +		return 0; + +	calltime = initcall_debug_start(dev); + +	pm_dev_dbg(dev, state, info); +	error = cb(dev); +	suspend_report_result(cb, error); + +	initcall_debug_report(dev, calltime, error); + +	return error; +} +  /*------------------------- Resume routines -------------------------*/  /** @@ -425,25 +366,34 @@ static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info)   */  static int device_resume_noirq(struct device *dev, pm_message_t state)  { +	pm_callback_t callback = NULL; +	char *info = NULL;  	int error = 0;  	TRACE_DEVICE(dev);  	TRACE_RESUME(0);  	if (dev->pm_domain) { -		pm_dev_dbg(dev, state, "EARLY power domain "); -		error = pm_noirq_op(dev, &dev->pm_domain->ops, state); +		info = "EARLY power domain "; +		callback = pm_noirq_op(&dev->pm_domain->ops, state);  	} else if (dev->type && dev->type->pm) { -		pm_dev_dbg(dev, state, "EARLY type "); -		error = pm_noirq_op(dev, dev->type->pm, state); +		info = "EARLY type "; +		callback = pm_noirq_op(dev->type->pm, state);  	} else if (dev->class && dev->class->pm) { -		pm_dev_dbg(dev, state, "EARLY class "); -		error = pm_noirq_op(dev, dev->class->pm, state); +		info = "EARLY class "; +		callback = pm_noirq_op(dev->class->pm, state);  	} else if (dev->bus && dev->bus->pm) { -		pm_dev_dbg(dev, state, "EARLY "); -		error = pm_noirq_op(dev, dev->bus->pm, state); +		info = "EARLY bus "; +		callback = pm_noirq_op(dev->bus->pm, state);  	} +	if (!callback && dev->driver && dev->driver->pm) { +		info = "EARLY driver "; +		callback = pm_noirq_op(dev->driver->pm, state); +	} + +	error = dpm_run_callback(callback, dev, state, info); +  	TRACE_RESUME(error);  	return error;  } @@ -486,26 +436,6 @@ void dpm_resume_noirq(pm_message_t state)  EXPORT_SYMBOL_GPL(dpm_resume_noirq);  /** - * legacy_resume - Execute a legacy (bus or class) resume callback for device. - * @dev: Device to resume. - * @cb: Resume callback to execute. - */ -static int legacy_resume(struct device *dev, int (*cb)(struct device *dev)) -{ -	int error; -	ktime_t calltime; - -	calltime = initcall_debug_start(dev); - -	error = cb(dev); -	suspend_report_result(cb, error); - -	initcall_debug_report(dev, calltime, error); - -	return error; -} - -/**   * device_resume - Execute "resume" callbacks for given device.   * @dev: Device to handle.   * @state: PM transition of the system being carried out. @@ -513,6 +443,8 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev))   */  static int device_resume(struct device *dev, pm_message_t state, bool async)  { +	pm_callback_t callback = NULL; +	char *info = NULL;  	int error = 0;  	bool put = false; @@ -535,40 +467,48 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)  	put = true;  	if (dev->pm_domain) { -		pm_dev_dbg(dev, state, "power domain "); -		error = pm_op(dev, &dev->pm_domain->ops, state); -		goto End; +		info = "power domain "; +		callback = pm_op(&dev->pm_domain->ops, state); +		goto Driver;  	}  	if (dev->type && dev->type->pm) { -		pm_dev_dbg(dev, state, "type "); -		error = pm_op(dev, dev->type->pm, state); -		goto End; +		info = "type "; +		callback = pm_op(dev->type->pm, state); +		goto Driver;  	}  	if (dev->class) {  		if (dev->class->pm) { -			pm_dev_dbg(dev, state, "class "); -			error = pm_op(dev, dev->class->pm, state); -			goto End; +			info = "class "; +			callback = pm_op(dev->class->pm, state); +			goto Driver;  		} else if (dev->class->resume) { -			pm_dev_dbg(dev, state, "legacy class "); -			error = legacy_resume(dev, dev->class->resume); +			info = "legacy class "; +			callback = dev->class->resume;  			goto End;  		}  	}  	if (dev->bus) {  		if (dev->bus->pm) { -			pm_dev_dbg(dev, state, ""); -			error = pm_op(dev, dev->bus->pm, state); +			info = "bus "; +			callback = pm_op(dev->bus->pm, state);  		} else if (dev->bus->resume) { -			pm_dev_dbg(dev, state, "legacy "); -			error = legacy_resume(dev, dev->bus->resume); +			info = "legacy bus "; +			callback = dev->bus->resume; +			goto End;  		}  	} + Driver: +	if (!callback && dev->driver && dev->driver->pm) { +		info = "driver "; +		callback = pm_op(dev->driver->pm, state); +	} +   End: +	error = dpm_run_callback(callback, dev, state, info);  	dev->power.is_suspended = false;   Unlock: @@ -660,24 +600,33 @@ void dpm_resume(pm_message_t state)   */  static void device_complete(struct device *dev, pm_message_t state)  { +	void (*callback)(struct device *) = NULL; +	char *info = NULL; +  	device_lock(dev);  	if (dev->pm_domain) { -		pm_dev_dbg(dev, state, "completing power domain "); -		if (dev->pm_domain->ops.complete) -			dev->pm_domain->ops.complete(dev); +		info = "completing power domain "; +		callback = dev->pm_domain->ops.complete;  	} else if (dev->type && dev->type->pm) { -		pm_dev_dbg(dev, state, "completing type "); -		if (dev->type->pm->complete) -			dev->type->pm->complete(dev); +		info = "completing type "; +		callback = dev->type->pm->complete;  	} else if (dev->class && dev->class->pm) { -		pm_dev_dbg(dev, state, "completing class "); -		if (dev->class->pm->complete) -			dev->class->pm->complete(dev); +		info = "completing class "; +		callback = dev->class->pm->complete;  	} else if (dev->bus && dev->bus->pm) { -		pm_dev_dbg(dev, state, "completing "); -		if (dev->bus->pm->complete) -			dev->bus->pm->complete(dev); +		info = "completing bus "; +		callback = dev->bus->pm->complete; +	} + +	if (!callback && dev->driver && dev->driver->pm) { +		info = "completing driver "; +		callback = dev->driver->pm->complete; +	} + +	if (callback) { +		pm_dev_dbg(dev, state, info); +		callback(dev);  	}  	device_unlock(dev); @@ -763,31 +712,29 @@ static pm_message_t resume_event(pm_message_t sleep_state)   */  static int device_suspend_noirq(struct device *dev, pm_message_t state)  { -	int error; +	pm_callback_t callback = NULL; +	char *info = NULL;  	if (dev->pm_domain) { -		pm_dev_dbg(dev, state, "LATE power domain "); -		error = pm_noirq_op(dev, &dev->pm_domain->ops, state); -		if (error) -			return error; +		info = "LATE power domain "; +		callback = pm_noirq_op(&dev->pm_domain->ops, state);  	} else if (dev->type && dev->type->pm) { -		pm_dev_dbg(dev, state, "LATE type "); -		error = pm_noirq_op(dev, dev->type->pm, state); -		if (error) -			return error; +		info = "LATE type "; +		callback = pm_noirq_op(dev->type->pm, state);  	} else if (dev->class && dev->class->pm) { -		pm_dev_dbg(dev, state, "LATE class "); -		error = pm_noirq_op(dev, dev->class->pm, state); -		if (error) -			return error; +		info = "LATE class "; +		callback = pm_noirq_op(dev->class->pm, state);  	} else if (dev->bus && dev->bus->pm) { -		pm_dev_dbg(dev, state, "LATE "); -		error = pm_noirq_op(dev, dev->bus->pm, state); -		if (error) -			return error; +		info = "LATE bus "; +		callback = pm_noirq_op(dev->bus->pm, state);  	} -	return 0; +	if (!callback && dev->driver && dev->driver->pm) { +		info = "LATE driver "; +		callback = pm_noirq_op(dev->driver->pm, state); +	} + +	return dpm_run_callback(callback, dev, state, info);  }  /** @@ -864,6 +811,8 @@ static int legacy_suspend(struct device *dev, pm_message_t state,   */  static int __device_suspend(struct device *dev, pm_message_t state, bool async)  { +	pm_callback_t callback = NULL; +	char *info = NULL;  	int error = 0;  	dpm_wait_for_children(dev, async); @@ -884,22 +833,22 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)  	device_lock(dev);  	if (dev->pm_domain) { -		pm_dev_dbg(dev, state, "power domain "); -		error = pm_op(dev, &dev->pm_domain->ops, state); -		goto End; +		info = "power domain "; +		callback = pm_op(&dev->pm_domain->ops, state); +		goto Run;  	}  	if (dev->type && dev->type->pm) { -		pm_dev_dbg(dev, state, "type "); -		error = pm_op(dev, dev->type->pm, state); -		goto End; +		info = "type "; +		callback = pm_op(dev->type->pm, state); +		goto Run;  	}  	if (dev->class) {  		if (dev->class->pm) { -			pm_dev_dbg(dev, state, "class "); -			error = pm_op(dev, dev->class->pm, state); -			goto End; +			info = "class "; +			callback = pm_op(dev->class->pm, state); +			goto Run;  		} else if (dev->class->suspend) {  			pm_dev_dbg(dev, state, "legacy class ");  			error = legacy_suspend(dev, state, dev->class->suspend); @@ -909,14 +858,23 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)  	if (dev->bus) {  		if (dev->bus->pm) { -			pm_dev_dbg(dev, state, ""); -			error = pm_op(dev, dev->bus->pm, state); +			info = "bus "; +			callback = pm_op(dev->bus->pm, state);  		} else if (dev->bus->suspend) { -			pm_dev_dbg(dev, state, "legacy "); +			pm_dev_dbg(dev, state, "legacy bus ");  			error = legacy_suspend(dev, state, dev->bus->suspend); +			goto End;  		}  	} + Run: +	if (!callback && dev->driver && dev->driver->pm) { +		info = "driver "; +		callback = pm_op(dev->driver->pm, state); +	} + +	error = dpm_run_callback(callback, dev, state, info); +   End:  	if (!error) {  		dev->power.is_suspended = true; @@ -1022,6 +980,8 @@ int dpm_suspend(pm_message_t state)   */  static int device_prepare(struct device *dev, pm_message_t state)  { +	int (*callback)(struct device *) = NULL; +	char *info = NULL;  	int error = 0;  	device_lock(dev); @@ -1029,34 +989,29 @@ static int device_prepare(struct device *dev, pm_message_t state)  	dev->power.wakeup_path = device_may_wakeup(dev);  	if (dev->pm_domain) { -		pm_dev_dbg(dev, state, "preparing power domain "); -		if (dev->pm_domain->ops.prepare) -			error = dev->pm_domain->ops.prepare(dev); -		suspend_report_result(dev->pm_domain->ops.prepare, error); -		if (error) -			goto End; +		info = "preparing power domain "; +		callback = dev->pm_domain->ops.prepare;  	} else if (dev->type && dev->type->pm) { -		pm_dev_dbg(dev, state, "preparing type "); -		if (dev->type->pm->prepare) -			error = dev->type->pm->prepare(dev); -		suspend_report_result(dev->type->pm->prepare, error); -		if (error) -			goto End; +		info = "preparing type "; +		callback = dev->type->pm->prepare;  	} else if (dev->class && dev->class->pm) { -		pm_dev_dbg(dev, state, "preparing class "); -		if (dev->class->pm->prepare) -			error = dev->class->pm->prepare(dev); -		suspend_report_result(dev->class->pm->prepare, error); -		if (error) -			goto End; +		info = "preparing class "; +		callback = dev->class->pm->prepare;  	} else if (dev->bus && dev->bus->pm) { -		pm_dev_dbg(dev, state, "preparing "); -		if (dev->bus->pm->prepare) -			error = dev->bus->pm->prepare(dev); -		suspend_report_result(dev->bus->pm->prepare, error); +		info = "preparing bus "; +		callback = dev->bus->pm->prepare; +	} + +	if (!callback && dev->driver && dev->driver->pm) { +		info = "preparing driver "; +		callback = dev->driver->pm->prepare; +	} + +	if (callback) { +		error = callback(dev); +		suspend_report_result(callback, error);  	} - End:  	device_unlock(dev);  	return error;  |