diff options
Diffstat (limited to 'drivers/base')
| -rw-r--r-- | drivers/base/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/base/base.h | 2 | ||||
| -rw-r--r-- | drivers/base/core.c | 7 | ||||
| -rw-r--r-- | drivers/base/dd.c | 18 | ||||
| -rw-r--r-- | drivers/base/firmware_class.c | 5 | ||||
| -rw-r--r-- | drivers/base/memory.c | 39 | ||||
| -rw-r--r-- | drivers/base/node.c | 14 | ||||
| -rw-r--r-- | drivers/base/platform.c | 178 | ||||
| -rw-r--r-- | drivers/base/power/Makefile | 4 | ||||
| -rw-r--r-- | drivers/base/power/clock_ops.c | 431 | ||||
| -rw-r--r-- | drivers/base/power/generic_ops.c | 39 | ||||
| -rw-r--r-- | drivers/base/power/main.c | 108 | ||||
| -rw-r--r-- | drivers/base/power/runtime.c | 29 | ||||
| -rw-r--r-- | drivers/base/power/sysfs.c | 4 | ||||
| -rw-r--r-- | drivers/base/power/wakeup.c | 1 | ||||
| -rw-r--r-- | drivers/base/sys.c | 202 | ||||
| -rw-r--r-- | drivers/base/syscore.c | 8 | 
17 files changed, 641 insertions, 455 deletions
| diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index e9e5238f3106..d57e8d0fb823 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -168,11 +168,4 @@ config SYS_HYPERVISOR  	bool  	default n -config ARCH_NO_SYSDEV_OPS -	bool -	---help--- -	  To be selected by architectures that don't use sysdev class or -	  sysdev driver power management (suspend/resume) and shutdown -	  operations. -  endmenu diff --git a/drivers/base/base.h b/drivers/base/base.h index 19f49e41ce5d..a34dca0ad041 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -111,8 +111,6 @@ static inline int driver_match_device(struct device_driver *drv,  	return drv->bus->match ? drv->bus->match(dev, drv) : 1;  } -extern void sysdev_shutdown(void); -  extern char *make_class_name(const char *name, struct kobject *kobj);  extern int devres_release_all(struct device *dev); diff --git a/drivers/base/core.c b/drivers/base/core.c index 81b78ede37c4..bc8729d603a7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -400,7 +400,7 @@ static void device_remove_groups(struct device *dev,  static int device_add_attrs(struct device *dev)  {  	struct class *class = dev->class; -	struct device_type *type = dev->type; +	const struct device_type *type = dev->type;  	int error;  	if (class) { @@ -440,7 +440,7 @@ static int device_add_attrs(struct device *dev)  static void device_remove_attrs(struct device *dev)  {  	struct class *class = dev->class; -	struct device_type *type = dev->type; +	const struct device_type *type = dev->type;  	device_remove_groups(dev, dev->groups); @@ -1314,8 +1314,7 @@ EXPORT_SYMBOL_GPL(put_device);  EXPORT_SYMBOL_GPL(device_create_file);  EXPORT_SYMBOL_GPL(device_remove_file); -struct root_device -{ +struct root_device {  	struct device dev;  	struct module *owner;  }; diff --git a/drivers/base/dd.c b/drivers/base/dd.c index da57ee9d63fe..6658da743c3a 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -245,6 +245,10 @@ int device_attach(struct device *dev)  	device_lock(dev);  	if (dev->driver) { +		if (klist_node_attached(&dev->p->knode_driver)) { +			ret = 1; +			goto out_unlock; +		}  		ret = device_bind_driver(dev);  		if (ret == 0)  			ret = 1; @@ -257,6 +261,7 @@ int device_attach(struct device *dev)  		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);  		pm_runtime_put_sync(dev);  	} +out_unlock:  	device_unlock(dev);  	return ret;  } @@ -316,8 +321,7 @@ static void __device_release_driver(struct device *dev)  	drv = dev->driver;  	if (drv) { -		pm_runtime_get_noresume(dev); -		pm_runtime_barrier(dev); +		pm_runtime_get_sync(dev);  		driver_sysfs_remove(dev); @@ -326,6 +330,8 @@ static void __device_release_driver(struct device *dev)  						     BUS_NOTIFY_UNBIND_DRIVER,  						     dev); +		pm_runtime_put_sync(dev); +  		if (dev->bus && dev->bus->remove)  			dev->bus->remove(dev);  		else if (drv->remove) @@ -338,7 +344,6 @@ static void __device_release_driver(struct device *dev)  						     BUS_NOTIFY_UNBOUND_DRIVER,  						     dev); -		pm_runtime_put_sync(dev);  	}  } @@ -408,17 +413,16 @@ void *dev_get_drvdata(const struct device *dev)  }  EXPORT_SYMBOL(dev_get_drvdata); -void dev_set_drvdata(struct device *dev, void *data) +int dev_set_drvdata(struct device *dev, void *data)  {  	int error; -	if (!dev) -		return;  	if (!dev->p) {  		error = device_private_init(dev);  		if (error) -			return; +			return error;  	}  	dev->p->driver_data = data; +	return 0;  }  EXPORT_SYMBOL(dev_set_drvdata); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 8c798ef7f13f..bbb03e6f7255 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -521,6 +521,11 @@ static int _request_firmware(const struct firmware **firmware_p,  	if (!firmware_p)  		return -EINVAL; +	if (WARN_ON(usermodehelper_is_disabled())) { +		dev_err(device, "firmware: %s will not be loaded\n", name); +		return -EBUSY; +	} +  	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);  	if (!firmware) {  		dev_err(device, "%s: kmalloc(struct firmware) failed\n", diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 3da6a43b7756..45d7c8fc73bd 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -30,7 +30,6 @@  static DEFINE_MUTEX(mem_sysfs_mutex);  #define MEMORY_CLASS_NAME	"memory" -#define MIN_MEMORY_BLOCK_SIZE	(1 << SECTION_SIZE_BITS)  static int sections_per_block; @@ -48,7 +47,8 @@ static const char *memory_uevent_name(struct kset *kset, struct kobject *kobj)  	return MEMORY_CLASS_NAME;  } -static int memory_uevent(struct kset *kset, struct kobject *obj, struct kobj_uevent_env *env) +static int memory_uevent(struct kset *kset, struct kobject *obj, +			struct kobj_uevent_env *env)  {  	int retval = 0; @@ -228,10 +228,11 @@ int memory_isolate_notify(unsigned long val, void *v)   * OK to have direct references to sparsemem variables in here.   */  static int -memory_section_action(unsigned long phys_index, unsigned long action) +memory_block_action(unsigned long phys_index, unsigned long action)  {  	int i;  	unsigned long start_pfn, start_paddr; +	unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;  	struct page *first_page;  	int ret; @@ -243,7 +244,7 @@ memory_section_action(unsigned long phys_index, unsigned long action)  	 * that way.  	 */  	if (action == MEM_ONLINE) { -		for (i = 0; i < PAGES_PER_SECTION; i++) { +		for (i = 0; i < nr_pages; i++) {  			if (PageReserved(first_page+i))  				continue; @@ -257,12 +258,12 @@ memory_section_action(unsigned long phys_index, unsigned long action)  	switch (action) {  		case MEM_ONLINE:  			start_pfn = page_to_pfn(first_page); -			ret = online_pages(start_pfn, PAGES_PER_SECTION); +			ret = online_pages(start_pfn, nr_pages);  			break;  		case MEM_OFFLINE:  			start_paddr = page_to_pfn(first_page) << PAGE_SHIFT;  			ret = remove_memory(start_paddr, -					    PAGES_PER_SECTION << PAGE_SHIFT); +					    nr_pages << PAGE_SHIFT);  			break;  		default:  			WARN(1, KERN_WARNING "%s(%ld, %ld) unknown action: " @@ -276,7 +277,7 @@ memory_section_action(unsigned long phys_index, unsigned long action)  static int memory_block_change_state(struct memory_block *mem,  		unsigned long to_state, unsigned long from_state_req)  { -	int i, ret = 0; +	int ret = 0;  	mutex_lock(&mem->state_mutex); @@ -288,20 +289,11 @@ static int memory_block_change_state(struct memory_block *mem,  	if (to_state == MEM_OFFLINE)  		mem->state = MEM_GOING_OFFLINE; -	for (i = 0; i < sections_per_block; i++) { -		ret = memory_section_action(mem->start_section_nr + i, -					    to_state); -		if (ret) -			break; -	} - -	if (ret) { -		for (i = 0; i < sections_per_block; i++) -			memory_section_action(mem->start_section_nr + i, -					      from_state_req); +	ret = memory_block_action(mem->start_section_nr, to_state); +	if (ret)  		mem->state = from_state_req; -	} else +	else  		mem->state = to_state;  out: @@ -396,15 +388,14 @@ memory_probe_store(struct class *class, struct class_attribute *attr,  		ret = add_memory(nid, phys_addr,  				 PAGES_PER_SECTION << PAGE_SHIFT);  		if (ret) -			break; +			goto out;  		phys_addr += MIN_MEMORY_BLOCK_SIZE;  	} -	if (ret) -		count = ret; - -	return count; +	ret = count; +out: +	return ret;  }  static CLASS_ATTR(probe, S_IWUSR, NULL, memory_probe_store); diff --git a/drivers/base/node.c b/drivers/base/node.c index b3b72d64e805..793f796c4da3 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -7,6 +7,7 @@  #include <linux/init.h>  #include <linux/mm.h>  #include <linux/memory.h> +#include <linux/vmstat.h>  #include <linux/node.h>  #include <linux/hugetlb.h>  #include <linux/compaction.h> @@ -179,11 +180,14 @@ static ssize_t node_read_vmstat(struct sys_device *dev,  				struct sysdev_attribute *attr, char *buf)  {  	int nid = dev->id; -	return sprintf(buf, -		"nr_written %lu\n" -		"nr_dirtied %lu\n", -		node_page_state(nid, NR_WRITTEN), -		node_page_state(nid, NR_DIRTIED)); +	int i; +	int n = 0; + +	for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) +		n += sprintf(buf+n, "%s %lu\n", vmstat_text[i], +			     node_page_state(nid, i)); + +	return n;  }  static SYSDEV_ATTR(vmstat, S_IRUGO, node_read_vmstat, NULL); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 9e0e4fc24c46..6040717b62bb 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -192,18 +192,18 @@ EXPORT_SYMBOL_GPL(platform_device_alloc);  int platform_device_add_resources(struct platform_device *pdev,  				  const struct resource *res, unsigned int num)  { -	struct resource *r; +	struct resource *r = NULL; -	if (!res) -		return 0; - -	r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL); -	if (r) { -		pdev->resource = r; -		pdev->num_resources = num; -		return 0; +	if (res) { +		r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL); +		if (!r) +			return -ENOMEM;  	} -	return -ENOMEM; + +	kfree(pdev->resource); +	pdev->resource = r; +	pdev->num_resources = num; +	return 0;  }  EXPORT_SYMBOL_GPL(platform_device_add_resources); @@ -220,17 +220,17 @@ EXPORT_SYMBOL_GPL(platform_device_add_resources);  int platform_device_add_data(struct platform_device *pdev, const void *data,  			     size_t size)  { -	void *d; +	void *d = NULL; -	if (!data) -		return 0; - -	d = kmemdup(data, size, GFP_KERNEL); -	if (d) { -		pdev->dev.platform_data = d; -		return 0; +	if (data) { +		d = kmemdup(data, size, GFP_KERNEL); +		if (!d) +			return -ENOMEM;  	} -	return -ENOMEM; + +	kfree(pdev->dev.platform_data); +	pdev->dev.platform_data = d; +	return 0;  }  EXPORT_SYMBOL_GPL(platform_device_add_data); @@ -367,7 +367,7 @@ EXPORT_SYMBOL_GPL(platform_device_unregister);   *   * Returns &struct platform_device pointer on success, or ERR_PTR() on error.   */ -struct platform_device *__init_or_module platform_device_register_resndata( +struct platform_device *platform_device_register_resndata(  		struct device *parent,  		const char *name, int id,  		const struct resource *res, unsigned int num, @@ -667,7 +667,7 @@ static int platform_legacy_resume(struct device *dev)  	return ret;  } -static int platform_pm_prepare(struct device *dev) +int platform_pm_prepare(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -678,7 +678,7 @@ static int platform_pm_prepare(struct device *dev)  	return ret;  } -static void platform_pm_complete(struct device *dev) +void platform_pm_complete(struct device *dev)  {  	struct device_driver *drv = dev->driver; @@ -686,16 +686,11 @@ static void platform_pm_complete(struct device *dev)  		drv->pm->complete(dev);  } -#else /* !CONFIG_PM_SLEEP */ - -#define platform_pm_prepare		NULL -#define platform_pm_complete		NULL - -#endif /* !CONFIG_PM_SLEEP */ +#endif /* CONFIG_PM_SLEEP */  #ifdef CONFIG_SUSPEND -int __weak platform_pm_suspend(struct device *dev) +int platform_pm_suspend(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -713,7 +708,7 @@ int __weak platform_pm_suspend(struct device *dev)  	return ret;  } -int __weak platform_pm_suspend_noirq(struct device *dev) +int platform_pm_suspend_noirq(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -729,7 +724,7 @@ int __weak platform_pm_suspend_noirq(struct device *dev)  	return ret;  } -int __weak platform_pm_resume(struct device *dev) +int platform_pm_resume(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -747,7 +742,7 @@ int __weak platform_pm_resume(struct device *dev)  	return ret;  } -int __weak platform_pm_resume_noirq(struct device *dev) +int platform_pm_resume_noirq(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -763,18 +758,11 @@ int __weak platform_pm_resume_noirq(struct device *dev)  	return ret;  } -#else /* !CONFIG_SUSPEND */ - -#define platform_pm_suspend		NULL -#define platform_pm_resume		NULL -#define platform_pm_suspend_noirq	NULL -#define platform_pm_resume_noirq	NULL - -#endif /* !CONFIG_SUSPEND */ +#endif /* CONFIG_SUSPEND */  #ifdef CONFIG_HIBERNATE_CALLBACKS -static int platform_pm_freeze(struct device *dev) +int platform_pm_freeze(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -792,7 +780,7 @@ static int platform_pm_freeze(struct device *dev)  	return ret;  } -static int platform_pm_freeze_noirq(struct device *dev) +int platform_pm_freeze_noirq(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -808,7 +796,7 @@ static int platform_pm_freeze_noirq(struct device *dev)  	return ret;  } -static int platform_pm_thaw(struct device *dev) +int platform_pm_thaw(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -826,7 +814,7 @@ static int platform_pm_thaw(struct device *dev)  	return ret;  } -static int platform_pm_thaw_noirq(struct device *dev) +int platform_pm_thaw_noirq(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -842,7 +830,7 @@ static int platform_pm_thaw_noirq(struct device *dev)  	return ret;  } -static int platform_pm_poweroff(struct device *dev) +int platform_pm_poweroff(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -860,7 +848,7 @@ static int platform_pm_poweroff(struct device *dev)  	return ret;  } -static int platform_pm_poweroff_noirq(struct device *dev) +int platform_pm_poweroff_noirq(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -876,7 +864,7 @@ static int platform_pm_poweroff_noirq(struct device *dev)  	return ret;  } -static int platform_pm_restore(struct device *dev) +int platform_pm_restore(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -894,7 +882,7 @@ static int platform_pm_restore(struct device *dev)  	return ret;  } -static int platform_pm_restore_noirq(struct device *dev) +int platform_pm_restore_noirq(struct device *dev)  {  	struct device_driver *drv = dev->driver;  	int ret = 0; @@ -910,62 +898,13 @@ static int platform_pm_restore_noirq(struct device *dev)  	return ret;  } -#else /* !CONFIG_HIBERNATE_CALLBACKS */ - -#define platform_pm_freeze		NULL -#define platform_pm_thaw		NULL -#define platform_pm_poweroff		NULL -#define platform_pm_restore		NULL -#define platform_pm_freeze_noirq	NULL -#define platform_pm_thaw_noirq		NULL -#define platform_pm_poweroff_noirq	NULL -#define platform_pm_restore_noirq	NULL - -#endif /* !CONFIG_HIBERNATE_CALLBACKS */ - -#ifdef CONFIG_PM_RUNTIME - -int __weak platform_pm_runtime_suspend(struct device *dev) -{ -	return pm_generic_runtime_suspend(dev); -}; - -int __weak platform_pm_runtime_resume(struct device *dev) -{ -	return pm_generic_runtime_resume(dev); -}; - -int __weak platform_pm_runtime_idle(struct device *dev) -{ -	return pm_generic_runtime_idle(dev); -}; - -#else /* !CONFIG_PM_RUNTIME */ - -#define platform_pm_runtime_suspend NULL -#define platform_pm_runtime_resume NULL -#define platform_pm_runtime_idle NULL - -#endif /* !CONFIG_PM_RUNTIME */ +#endif /* CONFIG_HIBERNATE_CALLBACKS */  static const struct dev_pm_ops platform_dev_pm_ops = { -	.prepare = platform_pm_prepare, -	.complete = platform_pm_complete, -	.suspend = platform_pm_suspend, -	.resume = platform_pm_resume, -	.freeze = platform_pm_freeze, -	.thaw = platform_pm_thaw, -	.poweroff = platform_pm_poweroff, -	.restore = platform_pm_restore, -	.suspend_noirq = platform_pm_suspend_noirq, -	.resume_noirq = platform_pm_resume_noirq, -	.freeze_noirq = platform_pm_freeze_noirq, -	.thaw_noirq = platform_pm_thaw_noirq, -	.poweroff_noirq = platform_pm_poweroff_noirq, -	.restore_noirq = platform_pm_restore_noirq, -	.runtime_suspend = platform_pm_runtime_suspend, -	.runtime_resume = platform_pm_runtime_resume, -	.runtime_idle = platform_pm_runtime_idle, +	.runtime_suspend = pm_generic_runtime_suspend, +	.runtime_resume = pm_generic_runtime_resume, +	.runtime_idle = pm_generic_runtime_idle, +	USE_PLATFORM_PM_SLEEP_OPS  };  struct bus_type platform_bus_type = { @@ -977,41 +916,6 @@ struct bus_type platform_bus_type = {  };  EXPORT_SYMBOL_GPL(platform_bus_type); -/** - * platform_bus_get_pm_ops() - return pointer to busses dev_pm_ops - * - * This function can be used by platform code to get the current - * set of dev_pm_ops functions used by the platform_bus_type. - */ -const struct dev_pm_ops * __init platform_bus_get_pm_ops(void) -{ -	return platform_bus_type.pm; -} - -/** - * platform_bus_set_pm_ops() - update dev_pm_ops for the platform_bus_type - * - * @pm: pointer to new dev_pm_ops struct to be used for platform_bus_type - * - * Platform code can override the dev_pm_ops methods of - * platform_bus_type by using this function.  It is expected that - * platform code will first do a platform_bus_get_pm_ops(), then - * kmemdup it, then customize selected methods and pass a pointer to - * the new struct dev_pm_ops to this function. - * - * Since platform-specific code is customizing methods for *all* - * devices (not just platform-specific devices) it is expected that - * any custom overrides of these functions will keep existing behavior - * and simply extend it.  For example, any customization of the - * runtime PM methods should continue to call the pm_generic_* - * functions as the default ones do in addition to the - * platform-specific behavior. - */ -void __init platform_bus_set_pm_ops(const struct dev_pm_ops *pm) -{ -	platform_bus_type.pm = pm; -} -  int __init platform_bus_init(void)  {  	int error; diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 118c1b92a511..3647e114d0e7 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_PM_SLEEP)	+= main.o wakeup.o  obj-$(CONFIG_PM_RUNTIME)	+= runtime.o  obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o  obj-$(CONFIG_PM_OPP)	+= opp.o +obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o -ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG -ccflags-$(CONFIG_PM_VERBOSE)   += -DDEBUG +ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c new file mode 100644 index 000000000000..ad367c4139b1 --- /dev/null +++ b/drivers/base/power/clock_ops.c @@ -0,0 +1,431 @@ +/* + * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks + * + * Copyright (c) 2011 Rafael J. Wysocki <[email protected]>, Renesas Electronics Corp. + * + * This file is released under the GPLv2. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/err.h> + +#ifdef CONFIG_PM_RUNTIME + +struct pm_runtime_clk_data { +	struct list_head clock_list; +	struct mutex lock; +}; + +enum pce_status { +	PCE_STATUS_NONE = 0, +	PCE_STATUS_ACQUIRED, +	PCE_STATUS_ENABLED, +	PCE_STATUS_ERROR, +}; + +struct pm_clock_entry { +	struct list_head node; +	char *con_id; +	struct clk *clk; +	enum pce_status status; +}; + +static struct pm_runtime_clk_data *__to_prd(struct device *dev) +{ +	return dev ? dev->power.subsys_data : NULL; +} + +/** + * pm_runtime_clk_add - Start using a device clock for runtime PM. + * @dev: Device whose clock is going to be used for runtime PM. + * @con_id: Connection ID of the clock. + * + * Add the clock represented by @con_id to the list of clocks used for + * the runtime PM of @dev. + */ +int pm_runtime_clk_add(struct device *dev, const char *con_id) +{ +	struct pm_runtime_clk_data *prd = __to_prd(dev); +	struct pm_clock_entry *ce; + +	if (!prd) +		return -EINVAL; + +	ce = kzalloc(sizeof(*ce), GFP_KERNEL); +	if (!ce) { +		dev_err(dev, "Not enough memory for clock entry.\n"); +		return -ENOMEM; +	} + +	if (con_id) { +		ce->con_id = kstrdup(con_id, GFP_KERNEL); +		if (!ce->con_id) { +			dev_err(dev, +				"Not enough memory for clock connection ID.\n"); +			kfree(ce); +			return -ENOMEM; +		} +	} + +	mutex_lock(&prd->lock); +	list_add_tail(&ce->node, &prd->clock_list); +	mutex_unlock(&prd->lock); +	return 0; +} + +/** + * __pm_runtime_clk_remove - Destroy runtime PM clock entry. + * @ce: Runtime PM clock entry to destroy. + * + * This routine must be called under the mutex protecting the runtime PM list + * of clocks corresponding the the @ce's device. + */ +static void __pm_runtime_clk_remove(struct pm_clock_entry *ce) +{ +	if (!ce) +		return; + +	list_del(&ce->node); + +	if (ce->status < PCE_STATUS_ERROR) { +		if (ce->status == PCE_STATUS_ENABLED) +			clk_disable(ce->clk); + +		if (ce->status >= PCE_STATUS_ACQUIRED) +			clk_put(ce->clk); +	} + +	if (ce->con_id) +		kfree(ce->con_id); + +	kfree(ce); +} + +/** + * pm_runtime_clk_remove - Stop using a device clock for runtime PM. + * @dev: Device whose clock should not be used for runtime PM any more. + * @con_id: Connection ID of the clock. + * + * Remove the clock represented by @con_id from the list of clocks used for + * the runtime PM of @dev. + */ +void pm_runtime_clk_remove(struct device *dev, const char *con_id) +{ +	struct pm_runtime_clk_data *prd = __to_prd(dev); +	struct pm_clock_entry *ce; + +	if (!prd) +		return; + +	mutex_lock(&prd->lock); + +	list_for_each_entry(ce, &prd->clock_list, node) { +		if (!con_id && !ce->con_id) { +			__pm_runtime_clk_remove(ce); +			break; +		} else if (!con_id || !ce->con_id) { +			continue; +		} else if (!strcmp(con_id, ce->con_id)) { +			__pm_runtime_clk_remove(ce); +			break; +		} +	} + +	mutex_unlock(&prd->lock); +} + +/** + * pm_runtime_clk_init - Initialize a device's list of runtime PM clocks. + * @dev: Device to initialize the list of runtime PM clocks for. + * + * Allocate a struct pm_runtime_clk_data object, initialize its lock member and + * make the @dev's power.subsys_data field point to it. + */ +int pm_runtime_clk_init(struct device *dev) +{ +	struct pm_runtime_clk_data *prd; + +	prd = kzalloc(sizeof(*prd), GFP_KERNEL); +	if (!prd) { +		dev_err(dev, "Not enough memory fo runtime PM data.\n"); +		return -ENOMEM; +	} + +	INIT_LIST_HEAD(&prd->clock_list); +	mutex_init(&prd->lock); +	dev->power.subsys_data = prd; +	return 0; +} + +/** + * pm_runtime_clk_destroy - Destroy a device's list of runtime PM clocks. + * @dev: Device to destroy the list of runtime PM clocks for. + * + * Clear the @dev's power.subsys_data field, remove the list of clock entries + * from the struct pm_runtime_clk_data object pointed to by it before and free + * that object. + */ +void pm_runtime_clk_destroy(struct device *dev) +{ +	struct pm_runtime_clk_data *prd = __to_prd(dev); +	struct pm_clock_entry *ce, *c; + +	if (!prd) +		return; + +	dev->power.subsys_data = NULL; + +	mutex_lock(&prd->lock); + +	list_for_each_entry_safe_reverse(ce, c, &prd->clock_list, node) +		__pm_runtime_clk_remove(ce); + +	mutex_unlock(&prd->lock); + +	kfree(prd); +} + +/** + * pm_runtime_clk_acquire - Acquire a device clock. + * @dev: Device whose clock is to be acquired. + * @con_id: Connection ID of the clock. + */ +static void pm_runtime_clk_acquire(struct device *dev, +				    struct pm_clock_entry *ce) +{ +	ce->clk = clk_get(dev, ce->con_id); +	if (IS_ERR(ce->clk)) { +		ce->status = PCE_STATUS_ERROR; +	} else { +		ce->status = PCE_STATUS_ACQUIRED; +		dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id); +	} +} + +/** + * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list. + * @dev: Device to disable the clocks for. + */ +int pm_runtime_clk_suspend(struct device *dev) +{ +	struct pm_runtime_clk_data *prd = __to_prd(dev); +	struct pm_clock_entry *ce; + +	dev_dbg(dev, "%s()\n", __func__); + +	if (!prd) +		return 0; + +	mutex_lock(&prd->lock); + +	list_for_each_entry_reverse(ce, &prd->clock_list, node) { +		if (ce->status == PCE_STATUS_NONE) +			pm_runtime_clk_acquire(dev, ce); + +		if (ce->status < PCE_STATUS_ERROR) { +			clk_disable(ce->clk); +			ce->status = PCE_STATUS_ACQUIRED; +		} +	} + +	mutex_unlock(&prd->lock); + +	return 0; +} + +/** + * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list. + * @dev: Device to enable the clocks for. + */ +int pm_runtime_clk_resume(struct device *dev) +{ +	struct pm_runtime_clk_data *prd = __to_prd(dev); +	struct pm_clock_entry *ce; + +	dev_dbg(dev, "%s()\n", __func__); + +	if (!prd) +		return 0; + +	mutex_lock(&prd->lock); + +	list_for_each_entry(ce, &prd->clock_list, node) { +		if (ce->status == PCE_STATUS_NONE) +			pm_runtime_clk_acquire(dev, ce); + +		if (ce->status < PCE_STATUS_ERROR) { +			clk_enable(ce->clk); +			ce->status = PCE_STATUS_ENABLED; +		} +	} + +	mutex_unlock(&prd->lock); + +	return 0; +} + +/** + * pm_runtime_clk_notify - Notify routine for device addition and removal. + * @nb: Notifier block object this function is a member of. + * @action: Operation being carried out by the caller. + * @data: Device the routine is being run for. + * + * For this function to work, @nb must be a member of an object of type + * struct pm_clk_notifier_block containing all of the requisite data. + * Specifically, the pwr_domain member of that object is copied to the device's + * pwr_domain field and its con_ids member is used to populate the device's list + * of runtime PM clocks, depending on @action. + * + * If the device's pwr_domain field is already populated with a value different + * from the one stored in the struct pm_clk_notifier_block object, the function + * does nothing. + */ +static int pm_runtime_clk_notify(struct notifier_block *nb, +				 unsigned long action, void *data) +{ +	struct pm_clk_notifier_block *clknb; +	struct device *dev = data; +	char **con_id; +	int error; + +	dev_dbg(dev, "%s() %ld\n", __func__, action); + +	clknb = container_of(nb, struct pm_clk_notifier_block, nb); + +	switch (action) { +	case BUS_NOTIFY_ADD_DEVICE: +		if (dev->pwr_domain) +			break; + +		error = pm_runtime_clk_init(dev); +		if (error) +			break; + +		dev->pwr_domain = clknb->pwr_domain; +		if (clknb->con_ids[0]) { +			for (con_id = clknb->con_ids; *con_id; con_id++) +				pm_runtime_clk_add(dev, *con_id); +		} else { +			pm_runtime_clk_add(dev, NULL); +		} + +		break; +	case BUS_NOTIFY_DEL_DEVICE: +		if (dev->pwr_domain != clknb->pwr_domain) +			break; + +		dev->pwr_domain = NULL; +		pm_runtime_clk_destroy(dev); +		break; +	} + +	return 0; +} + +#else /* !CONFIG_PM_RUNTIME */ + +/** + * enable_clock - Enable a device clock. + * @dev: Device whose clock is to be enabled. + * @con_id: Connection ID of the clock. + */ +static void enable_clock(struct device *dev, const char *con_id) +{ +	struct clk *clk; + +	clk = clk_get(dev, con_id); +	if (!IS_ERR(clk)) { +		clk_enable(clk); +		clk_put(clk); +		dev_info(dev, "Runtime PM disabled, clock forced on.\n"); +	} +} + +/** + * disable_clock - Disable a device clock. + * @dev: Device whose clock is to be disabled. + * @con_id: Connection ID of the clock. + */ +static void disable_clock(struct device *dev, const char *con_id) +{ +	struct clk *clk; + +	clk = clk_get(dev, con_id); +	if (!IS_ERR(clk)) { +		clk_disable(clk); +		clk_put(clk); +		dev_info(dev, "Runtime PM disabled, clock forced off.\n"); +	} +} + +/** + * pm_runtime_clk_notify - Notify routine for device addition and removal. + * @nb: Notifier block object this function is a member of. + * @action: Operation being carried out by the caller. + * @data: Device the routine is being run for. + * + * For this function to work, @nb must be a member of an object of type + * struct pm_clk_notifier_block containing all of the requisite data. + * Specifically, the con_ids member of that object is used to enable or disable + * the device's clocks, depending on @action. + */ +static int pm_runtime_clk_notify(struct notifier_block *nb, +				 unsigned long action, void *data) +{ +	struct pm_clk_notifier_block *clknb; +	struct device *dev = data; +	char **con_id; + +	dev_dbg(dev, "%s() %ld\n", __func__, action); + +	clknb = container_of(nb, struct pm_clk_notifier_block, nb); + +	switch (action) { +	case BUS_NOTIFY_BIND_DRIVER: +		if (clknb->con_ids[0]) { +			for (con_id = clknb->con_ids; *con_id; con_id++) +				enable_clock(dev, *con_id); +		} else { +			enable_clock(dev, NULL); +		} +		break; +	case BUS_NOTIFY_UNBOUND_DRIVER: +		if (clknb->con_ids[0]) { +			for (con_id = clknb->con_ids; *con_id; con_id++) +				disable_clock(dev, *con_id); +		} else { +			disable_clock(dev, NULL); +		} +		break; +	} + +	return 0; +} + +#endif /* !CONFIG_PM_RUNTIME */ + +/** + * pm_runtime_clk_add_notifier - Add bus type notifier for runtime PM clocks. + * @bus: Bus type to add the notifier to. + * @clknb: Notifier to be added to the given bus type. + * + * The nb member of @clknb is not expected to be initialized and its + * notifier_call member will be replaced with pm_runtime_clk_notify().  However, + * the remaining members of @clknb should be populated prior to calling this + * routine. + */ +void pm_runtime_clk_add_notifier(struct bus_type *bus, +				 struct pm_clk_notifier_block *clknb) +{ +	if (!bus || !clknb) +		return; + +	clknb->nb.notifier_call = pm_runtime_clk_notify; +	bus_register_notifier(bus, &clknb->nb); +} diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index 42f97f925629..cb3bb368681c 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -74,6 +74,23 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_resume);  #ifdef CONFIG_PM_SLEEP  /** + * pm_generic_prepare - Generic routine preparing a device for power transition. + * @dev: Device to prepare. + * + * Prepare a device for a system-wide power transition. + */ +int pm_generic_prepare(struct device *dev) +{ +	struct device_driver *drv = dev->driver; +	int ret = 0; + +	if (drv && drv->pm && drv->pm->prepare) +		ret = drv->pm->prepare(dev); + +	return ret; +} + +/**   * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.   * @dev: Device to handle.   * @event: PM transition of the system under way. @@ -213,16 +230,38 @@ int pm_generic_restore(struct device *dev)  	return __pm_generic_resume(dev, PM_EVENT_RESTORE);  }  EXPORT_SYMBOL_GPL(pm_generic_restore); + +/** + * pm_generic_complete - Generic routine competing a device power transition. + * @dev: Device to handle. + * + * Complete a device power transition during a system-wide power transition. + */ +void pm_generic_complete(struct device *dev) +{ +	struct device_driver *drv = dev->driver; + +	if (drv && drv->pm && drv->pm->complete) +		drv->pm->complete(dev); + +	/* +	 * Let runtime PM try to suspend devices that haven't been in use before +	 * going into the system-wide sleep state we're resuming from. +	 */ +	pm_runtime_idle(dev); +}  #endif /* CONFIG_PM_SLEEP */  struct dev_pm_ops generic_subsys_pm_ops = {  #ifdef CONFIG_PM_SLEEP +	.prepare = pm_generic_prepare,  	.suspend = pm_generic_suspend,  	.resume = pm_generic_resume,  	.freeze = pm_generic_freeze,  	.thaw = pm_generic_thaw,  	.poweroff = pm_generic_poweroff,  	.restore = pm_generic_restore, +	.complete = pm_generic_complete,  #endif  #ifdef CONFIG_PM_RUNTIME  	.runtime_suspend = pm_generic_runtime_suspend, diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index abe3ab709e87..06f09bf89cb2 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -57,7 +57,8 @@ static int async_error;   */  void device_pm_init(struct device *dev)  { -	dev->power.in_suspend = false; +	dev->power.is_prepared = false; +	dev->power.is_suspended = false;  	init_completion(&dev->power.completion);  	complete_all(&dev->power.completion);  	dev->power.wakeup = NULL; @@ -91,7 +92,7 @@ void device_pm_add(struct device *dev)  	pr_debug("PM: Adding info for %s:%s\n",  		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));  	mutex_lock(&dpm_list_mtx); -	if (dev->parent && dev->parent->power.in_suspend) +	if (dev->parent && dev->parent->power.is_prepared)  		dev_warn(dev, "parent %s should not be sleeping\n",  			dev_name(dev->parent));  	list_add_tail(&dev->power.entry, &dpm_list); @@ -426,10 +427,8 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)  	if (dev->pwr_domain) {  		pm_dev_dbg(dev, state, "EARLY power domain "); -		pm_noirq_op(dev, &dev->pwr_domain->ops, state); -	} - -	if (dev->type && dev->type->pm) { +		error = pm_noirq_op(dev, &dev->pwr_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);  	} else if (dev->class && dev->class->pm) { @@ -513,11 +512,19 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)  	dpm_wait(dev->parent, async);  	device_lock(dev); -	dev->power.in_suspend = false; +	/* +	 * This is a fib.  But we'll allow new children to be added below +	 * a resumed device, even if the device hasn't been completed yet. +	 */ +	dev->power.is_prepared = false; + +	if (!dev->power.is_suspended) +		goto Unlock;  	if (dev->pwr_domain) {  		pm_dev_dbg(dev, state, "power domain "); -		pm_op(dev, &dev->pwr_domain->ops, state); +		error = pm_op(dev, &dev->pwr_domain->ops, state); +		goto End;  	}  	if (dev->type && dev->type->pm) { @@ -549,6 +556,9 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)  	}   End: +	dev->power.is_suspended = false; + + Unlock:  	device_unlock(dev);  	complete_all(&dev->power.completion); @@ -580,11 +590,13 @@ static bool is_async(struct device *dev)   * Execute the appropriate "resume" callback for all devices whose status   * indicates that they are suspended.   */ -static void dpm_resume(pm_message_t state) +void dpm_resume(pm_message_t state)  {  	struct device *dev;  	ktime_t starttime = ktime_get(); +	might_sleep(); +  	mutex_lock(&dpm_list_mtx);  	pm_transition = state;  	async_error = 0; @@ -629,12 +641,11 @@ static void device_complete(struct device *dev, pm_message_t state)  {  	device_lock(dev); -	if (dev->pwr_domain && dev->pwr_domain->ops.complete) { +	if (dev->pwr_domain) {  		pm_dev_dbg(dev, state, "completing power domain "); -		dev->pwr_domain->ops.complete(dev); -	} - -	if (dev->type && dev->type->pm) { +		if (dev->pwr_domain->ops.complete) +			dev->pwr_domain->ops.complete(dev); +	} else if (dev->type && dev->type->pm) {  		pm_dev_dbg(dev, state, "completing type ");  		if (dev->type->pm->complete)  			dev->type->pm->complete(dev); @@ -658,17 +669,19 @@ static void device_complete(struct device *dev, pm_message_t state)   * Execute the ->complete() callbacks for all devices whose PM status is not   * DPM_ON (this allows new devices to be registered).   */ -static void dpm_complete(pm_message_t state) +void dpm_complete(pm_message_t state)  {  	struct list_head list; +	might_sleep(); +  	INIT_LIST_HEAD(&list);  	mutex_lock(&dpm_list_mtx);  	while (!list_empty(&dpm_prepared_list)) {  		struct device *dev = to_device(dpm_prepared_list.prev);  		get_device(dev); -		dev->power.in_suspend = false; +		dev->power.is_prepared = false;  		list_move(&dev->power.entry, &list);  		mutex_unlock(&dpm_list_mtx); @@ -690,7 +703,6 @@ static void dpm_complete(pm_message_t state)   */  void dpm_resume_end(pm_message_t state)  { -	might_sleep();  	dpm_resume(state);  	dpm_complete(state);  } @@ -732,7 +744,12 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)  {  	int error; -	if (dev->type && dev->type->pm) { +	if (dev->pwr_domain) { +		pm_dev_dbg(dev, state, "LATE power domain "); +		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state); +		if (error) +			return error; +	} 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) @@ -749,11 +766,6 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)  			return error;  	} -	if (dev->pwr_domain) { -		pm_dev_dbg(dev, state, "LATE power domain "); -		pm_noirq_op(dev, &dev->pwr_domain->ops, state); -	} -  	return 0;  } @@ -834,28 +846,34 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)  	device_lock(dev);  	if (async_error) -		goto End; +		goto Unlock;  	if (pm_wakeup_pending()) {  		async_error = -EBUSY; +		goto Unlock; +	} + +	if (dev->pwr_domain) { +		pm_dev_dbg(dev, state, "power domain "); +		error = pm_op(dev, &dev->pwr_domain->ops, state);  		goto End;  	}  	if (dev->type && dev->type->pm) {  		pm_dev_dbg(dev, state, "type ");  		error = pm_op(dev, dev->type->pm, state); -		goto Domain; +		goto End;  	}  	if (dev->class) {  		if (dev->class->pm) {  			pm_dev_dbg(dev, state, "class ");  			error = pm_op(dev, dev->class->pm, state); -			goto Domain; +			goto End;  		} else if (dev->class->suspend) {  			pm_dev_dbg(dev, state, "legacy class ");  			error = legacy_suspend(dev, state, dev->class->suspend); -			goto Domain; +			goto End;  		}  	} @@ -869,13 +887,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)  		}  	} - Domain: -	if (!error && dev->pwr_domain) { -		pm_dev_dbg(dev, state, "power domain "); -		pm_op(dev, &dev->pwr_domain->ops, state); -	} -   End: +	dev->power.is_suspended = !error; + + Unlock:  	device_unlock(dev);  	complete_all(&dev->power.completion); @@ -914,11 +929,13 @@ static int device_suspend(struct device *dev)   * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.   * @state: PM transition of the system being carried out.   */ -static int dpm_suspend(pm_message_t state) +int dpm_suspend(pm_message_t state)  {  	ktime_t starttime = ktime_get();  	int error = 0; +	might_sleep(); +  	mutex_lock(&dpm_list_mtx);  	pm_transition = state;  	async_error = 0; @@ -965,7 +982,14 @@ static int device_prepare(struct device *dev, pm_message_t state)  	device_lock(dev); -	if (dev->type && dev->type->pm) { +	if (dev->pwr_domain) { +		pm_dev_dbg(dev, state, "preparing power domain "); +		if (dev->pwr_domain->ops.prepare) +			error = dev->pwr_domain->ops.prepare(dev); +		suspend_report_result(dev->pwr_domain->ops.prepare, error); +		if (error) +			goto End; +	} 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); @@ -984,13 +1008,6 @@ static int device_prepare(struct device *dev, pm_message_t state)  		if (dev->bus->pm->prepare)  			error = dev->bus->pm->prepare(dev);  		suspend_report_result(dev->bus->pm->prepare, error); -		if (error) -			goto End; -	} - -	if (dev->pwr_domain && dev->pwr_domain->ops.prepare) { -		pm_dev_dbg(dev, state, "preparing power domain "); -		dev->pwr_domain->ops.prepare(dev);  	}   End: @@ -1005,10 +1022,12 @@ static int device_prepare(struct device *dev, pm_message_t state)   *   * Execute the ->prepare() callback(s) for all devices.   */ -static int dpm_prepare(pm_message_t state) +int dpm_prepare(pm_message_t state)  {  	int error = 0; +	might_sleep(); +  	mutex_lock(&dpm_list_mtx);  	while (!list_empty(&dpm_list)) {  		struct device *dev = to_device(dpm_list.next); @@ -1037,7 +1056,7 @@ static int dpm_prepare(pm_message_t state)  			put_device(dev);  			break;  		} -		dev->power.in_suspend = true; +		dev->power.is_prepared = true;  		if (!list_empty(&dev->power.entry))  			list_move_tail(&dev->power.entry, &dpm_prepared_list);  		put_device(dev); @@ -1057,7 +1076,6 @@ int dpm_suspend_start(pm_message_t state)  {  	int error; -	might_sleep();  	error = dpm_prepare(state);  	if (!error)  		error = dpm_suspend(state); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 3172c60d23a9..0d4587b15c55 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -168,7 +168,6 @@ static int rpm_check_suspend_allowed(struct device *dev)  static int rpm_idle(struct device *dev, int rpmflags)  {  	int (*callback)(struct device *); -	int (*domain_callback)(struct device *);  	int retval;  	retval = rpm_check_suspend_allowed(dev); @@ -214,7 +213,9 @@ static int rpm_idle(struct device *dev, int rpmflags)  	dev->power.idle_notification = true; -	if (dev->type && dev->type->pm) +	if (dev->pwr_domain) +		callback = dev->pwr_domain->ops.runtime_idle; +	else if (dev->type && dev->type->pm)  		callback = dev->type->pm->runtime_idle;  	else if (dev->class && dev->class->pm)  		callback = dev->class->pm->runtime_idle; @@ -223,19 +224,10 @@ static int rpm_idle(struct device *dev, int rpmflags)  	else  		callback = NULL; -	if (dev->pwr_domain) -		domain_callback = dev->pwr_domain->ops.runtime_idle; -	else -		domain_callback = NULL; - -	if (callback || domain_callback) { +	if (callback) {  		spin_unlock_irq(&dev->power.lock); -		if (domain_callback) -			retval = domain_callback(dev); - -		if (!retval && callback) -			callback(dev); +		callback(dev);  		spin_lock_irq(&dev->power.lock);  	} @@ -382,7 +374,9 @@ static int rpm_suspend(struct device *dev, int rpmflags)  	__update_runtime_status(dev, RPM_SUSPENDING); -	if (dev->type && dev->type->pm) +	if (dev->pwr_domain) +		callback = dev->pwr_domain->ops.runtime_suspend; +	else if (dev->type && dev->type->pm)  		callback = dev->type->pm->runtime_suspend;  	else if (dev->class && dev->class->pm)  		callback = dev->class->pm->runtime_suspend; @@ -400,8 +394,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)  		else  			pm_runtime_cancel_pending(dev);  	} else { -		if (dev->pwr_domain) -			rpm_callback(dev->pwr_domain->ops.runtime_suspend, dev);   no_callback:  		__update_runtime_status(dev, RPM_SUSPENDED);  		pm_runtime_deactivate_timer(dev); @@ -582,9 +574,8 @@ static int rpm_resume(struct device *dev, int rpmflags)  	__update_runtime_status(dev, RPM_RESUMING);  	if (dev->pwr_domain) -		rpm_callback(dev->pwr_domain->ops.runtime_resume, dev); - -	if (dev->type && dev->type->pm) +		callback = dev->pwr_domain->ops.runtime_resume; +	else if (dev->type && dev->type->pm)  		callback = dev->type->pm->runtime_resume;  	else if (dev->class && dev->class->pm)  		callback = dev->class->pm->runtime_resume; diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index fff49bee781d..a9f5b8979611 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -212,8 +212,9 @@ static ssize_t autosuspend_delay_ms_store(struct device *dev,  static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show,  		autosuspend_delay_ms_store); -#endif +#endif /* CONFIG_PM_RUNTIME */ +#ifdef CONFIG_PM_SLEEP  static ssize_t  wake_show(struct device * dev, struct device_attribute *attr, char * buf)  { @@ -248,7 +249,6 @@ wake_store(struct device * dev, struct device_attribute *attr,  static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); -#ifdef CONFIG_PM_SLEEP  static ssize_t wakeup_count_show(struct device *dev,  				struct device_attribute *attr, char *buf)  { diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index abbbd33e8d8a..84f7c7d5a098 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -110,7 +110,6 @@ void wakeup_source_add(struct wakeup_source *ws)  	spin_lock_irq(&events_lock);  	list_add_rcu(&ws->entry, &wakeup_sources);  	spin_unlock_irq(&events_lock); -	synchronize_rcu();  }  EXPORT_SYMBOL_GPL(wakeup_source_add); diff --git a/drivers/base/sys.c b/drivers/base/sys.c index acde9b5ee131..9dff77bfe1e3 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -328,203 +328,8 @@ void sysdev_unregister(struct sys_device *sysdev)  	kobject_put(&sysdev->kobj);  } - -#ifndef CONFIG_ARCH_NO_SYSDEV_OPS -/** - *	sysdev_shutdown - Shut down all system devices. - * - *	Loop over each class of system devices, and the devices in each - *	of those classes. For each device, we call the shutdown method for - *	each driver registered for the device - the auxiliaries, - *	and the class driver. - * - *	Note: The list is iterated in reverse order, so that we shut down - *	child devices before we shut down their parents. The list ordering - *	is guaranteed by virtue of the fact that child devices are registered - *	after their parents. - */ -void sysdev_shutdown(void) -{ -	struct sysdev_class *cls; - -	pr_debug("Shutting Down System Devices\n"); - -	mutex_lock(&sysdev_drivers_lock); -	list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) { -		struct sys_device *sysdev; - -		pr_debug("Shutting down type '%s':\n", -			 kobject_name(&cls->kset.kobj)); - -		list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { -			struct sysdev_driver *drv; -			pr_debug(" %s\n", kobject_name(&sysdev->kobj)); - -			/* Call auxiliary drivers first */ -			list_for_each_entry(drv, &cls->drivers, entry) { -				if (drv->shutdown) -					drv->shutdown(sysdev); -			} - -			/* Now call the generic one */ -			if (cls->shutdown) -				cls->shutdown(sysdev); -		} -	} -	mutex_unlock(&sysdev_drivers_lock); -} - -static void __sysdev_resume(struct sys_device *dev) -{ -	struct sysdev_class *cls = dev->cls; -	struct sysdev_driver *drv; - -	/* First, call the class-specific one */ -	if (cls->resume) -		cls->resume(dev); -	WARN_ONCE(!irqs_disabled(), -		"Interrupts enabled after %pF\n", cls->resume); - -	/* Call auxiliary drivers next. */ -	list_for_each_entry(drv, &cls->drivers, entry) { -		if (drv->resume) -			drv->resume(dev); -		WARN_ONCE(!irqs_disabled(), -			"Interrupts enabled after %pF\n", drv->resume); -	} -} - -/** - *	sysdev_suspend - Suspend all system devices. - *	@state:		Power state to enter. - * - *	We perform an almost identical operation as sysdev_shutdown() - *	above, though calling ->suspend() instead. Interrupts are disabled - *	when this called. Devices are responsible for both saving state and - *	quiescing or powering down the device. - * - *	This is only called by the device PM core, so we let them handle - *	all synchronization. - */ -int sysdev_suspend(pm_message_t state) -{ -	struct sysdev_class *cls; -	struct sys_device *sysdev, *err_dev; -	struct sysdev_driver *drv, *err_drv; -	int ret; - -	pr_debug("Checking wake-up interrupts\n"); - -	/* Return error code if there are any wake-up interrupts pending */ -	ret = check_wakeup_irqs(); -	if (ret) -		return ret; - -	WARN_ONCE(!irqs_disabled(), -		"Interrupts enabled while suspending system devices\n"); - -	pr_debug("Suspending System Devices\n"); - -	list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) { -		pr_debug("Suspending type '%s':\n", -			 kobject_name(&cls->kset.kobj)); - -		list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { -			pr_debug(" %s\n", kobject_name(&sysdev->kobj)); - -			/* Call auxiliary drivers first */ -			list_for_each_entry(drv, &cls->drivers, entry) { -				if (drv->suspend) { -					ret = drv->suspend(sysdev, state); -					if (ret) -						goto aux_driver; -				} -				WARN_ONCE(!irqs_disabled(), -					"Interrupts enabled after %pF\n", -					drv->suspend); -			} - -			/* Now call the generic one */ -			if (cls->suspend) { -				ret = cls->suspend(sysdev, state); -				if (ret) -					goto cls_driver; -				WARN_ONCE(!irqs_disabled(), -					"Interrupts enabled after %pF\n", -					cls->suspend); -			} -		} -	} -	return 0; -	/* resume current sysdev */ -cls_driver: -	drv = NULL; -	printk(KERN_ERR "Class suspend failed for %s: %d\n", -		kobject_name(&sysdev->kobj), ret); - -aux_driver: -	if (drv) -		printk(KERN_ERR "Class driver suspend failed for %s: %d\n", -				kobject_name(&sysdev->kobj), ret); -	list_for_each_entry(err_drv, &cls->drivers, entry) { -		if (err_drv == drv) -			break; -		if (err_drv->resume) -			err_drv->resume(sysdev); -	} - -	/* resume other sysdevs in current class */ -	list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) { -		if (err_dev == sysdev) -			break; -		pr_debug(" %s\n", kobject_name(&err_dev->kobj)); -		__sysdev_resume(err_dev); -	} - -	/* resume other classes */ -	list_for_each_entry_continue(cls, &system_kset->list, kset.kobj.entry) { -		list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) { -			pr_debug(" %s\n", kobject_name(&err_dev->kobj)); -			__sysdev_resume(err_dev); -		} -	} -	return ret; -} -EXPORT_SYMBOL_GPL(sysdev_suspend); - -/** - *	sysdev_resume - Bring system devices back to life. - * - *	Similar to sysdev_suspend(), but we iterate the list forwards - *	to guarantee that parent devices are resumed before their children. - * - *	Note: Interrupts are disabled when called. - */ -int sysdev_resume(void) -{ -	struct sysdev_class *cls; - -	WARN_ONCE(!irqs_disabled(), -		"Interrupts enabled while resuming system devices\n"); - -	pr_debug("Resuming System Devices\n"); - -	list_for_each_entry(cls, &system_kset->list, kset.kobj.entry) { -		struct sys_device *sysdev; - -		pr_debug("Resuming type '%s':\n", -			 kobject_name(&cls->kset.kobj)); - -		list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { -			pr_debug(" %s\n", kobject_name(&sysdev->kobj)); - -			__sysdev_resume(sysdev); -		} -	} -	return 0; -} -EXPORT_SYMBOL_GPL(sysdev_resume); -#endif /* CONFIG_ARCH_NO_SYSDEV_OPS */ +EXPORT_SYMBOL_GPL(sysdev_register); +EXPORT_SYMBOL_GPL(sysdev_unregister);  int __init system_bus_init(void)  { @@ -534,9 +339,6 @@ int __init system_bus_init(void)  	return 0;  } -EXPORT_SYMBOL_GPL(sysdev_register); -EXPORT_SYMBOL_GPL(sysdev_unregister); -  #define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr)  ssize_t sysdev_store_ulong(struct sys_device *sysdev, diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c index c126db3cb7d1..e8d11b6630ee 100644 --- a/drivers/base/syscore.c +++ b/drivers/base/syscore.c @@ -9,6 +9,7 @@  #include <linux/syscore_ops.h>  #include <linux/mutex.h>  #include <linux/module.h> +#include <linux/interrupt.h>  static LIST_HEAD(syscore_ops_list);  static DEFINE_MUTEX(syscore_ops_lock); @@ -48,6 +49,13 @@ int syscore_suspend(void)  	struct syscore_ops *ops;  	int ret = 0; +	pr_debug("Checking wakeup interrupts\n"); + +	/* Return error code if there are any wakeup interrupts pending. */ +	ret = check_wakeup_irqs(); +	if (ret) +		return ret; +  	WARN_ONCE(!irqs_disabled(),  		"Interrupts enabled before system core suspend.\n"); |