diff options
Diffstat (limited to 'drivers/base')
| -rw-r--r-- | drivers/base/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/base/core.c | 2 | ||||
| -rw-r--r-- | drivers/base/dma-coherent.c | 5 | ||||
| -rw-r--r-- | drivers/base/dma-contiguous.c | 29 | ||||
| -rw-r--r-- | drivers/base/firmware_class.c | 266 | ||||
| -rw-r--r-- | drivers/base/platform.c | 33 | ||||
| -rw-r--r-- | drivers/base/power/clock_ops.c | 6 | ||||
| -rw-r--r-- | drivers/base/power/domain.c | 16 | ||||
| -rw-r--r-- | drivers/base/power/opp.c | 44 | ||||
| -rw-r--r-- | drivers/base/power/power.h | 6 | ||||
| -rw-r--r-- | drivers/base/power/qos.c | 323 | ||||
| -rw-r--r-- | drivers/base/power/sysfs.c | 94 | ||||
| -rw-r--r-- | drivers/base/regmap/Kconfig | 2 | 
13 files changed, 592 insertions, 236 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 08b4c520938..b34b5cda5ae 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -236,7 +236,7 @@ config CMA_SIZE_PERCENTAGE  choice  	prompt "Selected region size" -	default CMA_SIZE_SEL_ABSOLUTE +	default CMA_SIZE_SEL_MBYTES  config CMA_SIZE_SEL_MBYTES  	bool "Use mega bytes value only" diff --git a/drivers/base/core.c b/drivers/base/core.c index abea76c36a4..150a41580fa 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1180,7 +1180,6 @@ void device_del(struct device *dev)  	if (dev->bus)  		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,  					     BUS_NOTIFY_DEL_DEVICE, dev); -	device_pm_remove(dev);  	dpm_sysfs_remove(dev);  	if (parent)  		klist_del(&dev->p->knode_parent); @@ -1205,6 +1204,7 @@ void device_del(struct device *dev)  	device_remove_file(dev, &uevent_attr);  	device_remove_attrs(dev);  	bus_remove_device(dev); +	device_pm_remove(dev);  	driver_deferred_probe_del(dev);  	/* Notify the platform of the removal, in case they diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index 560a7173f81..bc256b64102 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -191,9 +191,8 @@ EXPORT_SYMBOL(dma_release_from_coherent);   * This checks whether the memory was allocated from the per-device   * coherent memory pool and if so, maps that memory to the provided vma.   * - * Returns 1 if we correctly mapped the memory, or 0 if - * dma_release_coherent() should proceed with mapping memory from - * generic pools. + * Returns 1 if we correctly mapped the memory, or 0 if the caller should + * proceed with mapping memory from generic pools.   */  int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma,  			   void *vaddr, size_t size, int *ret) diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 9a1469474f5..0ca54421ce9 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -27,15 +27,12 @@  #include <linux/mm.h>  #include <linux/mutex.h>  #include <linux/page-isolation.h> +#include <linux/sizes.h>  #include <linux/slab.h>  #include <linux/swap.h>  #include <linux/mm_types.h>  #include <linux/dma-contiguous.h> -#ifndef SZ_1M -#define SZ_1M (1 << 20) -#endif -  struct cma {  	unsigned long	base_pfn;  	unsigned long	count; @@ -60,8 +57,8 @@ struct cma *dma_contiguous_default_area;   * Users, who want to set the size of global CMA area for their system   * should use cma= kernel parameter.   */ -static const unsigned long size_bytes = CMA_SIZE_MBYTES * SZ_1M; -static long size_cmdline = -1; +static const phys_addr_t size_bytes = CMA_SIZE_MBYTES * SZ_1M; +static phys_addr_t size_cmdline = -1;  static int __init early_cma(char *p)  { @@ -73,7 +70,7 @@ early_param("cma", early_cma);  #ifdef CONFIG_CMA_SIZE_PERCENTAGE -static unsigned long __init __maybe_unused cma_early_percent_memory(void) +static phys_addr_t __init __maybe_unused cma_early_percent_memory(void)  {  	struct memblock_region *reg;  	unsigned long total_pages = 0; @@ -91,7 +88,7 @@ static unsigned long __init __maybe_unused cma_early_percent_memory(void)  #else -static inline __maybe_unused unsigned long cma_early_percent_memory(void) +static inline __maybe_unused phys_addr_t cma_early_percent_memory(void)  {  	return 0;  } @@ -109,7 +106,7 @@ static inline __maybe_unused unsigned long cma_early_percent_memory(void)   */  void __init dma_contiguous_reserve(phys_addr_t limit)  { -	unsigned long selected_size = 0; +	phys_addr_t selected_size = 0;  	pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit); @@ -129,7 +126,7 @@ void __init dma_contiguous_reserve(phys_addr_t limit)  	if (selected_size) {  		pr_debug("%s: reserving %ld MiB for global area\n", __func__, -			 selected_size / SZ_1M); +			 (unsigned long)selected_size / SZ_1M);  		dma_declare_contiguous(NULL, selected_size, 0, limit);  	} @@ -230,11 +227,11 @@ core_initcall(cma_init_reserved_areas);   * called by board specific code when early allocator (memblock or bootmem)   * is still activate.   */ -int __init dma_declare_contiguous(struct device *dev, unsigned long size, +int __init dma_declare_contiguous(struct device *dev, phys_addr_t size,  				  phys_addr_t base, phys_addr_t limit)  {  	struct cma_reserved *r = &cma_reserved[cma_reserved_count]; -	unsigned long alignment; +	phys_addr_t alignment;  	pr_debug("%s(size %lx, base %08lx, limit %08lx)\n", __func__,  		 (unsigned long)size, (unsigned long)base, @@ -271,10 +268,6 @@ int __init dma_declare_contiguous(struct device *dev, unsigned long size,  		if (!addr) {  			base = -ENOMEM;  			goto err; -		} else if (addr + size > ~(unsigned long)0) { -			memblock_free(addr, size); -			base = -EINVAL; -			goto err;  		} else {  			base = addr;  		} @@ -288,14 +281,14 @@ int __init dma_declare_contiguous(struct device *dev, unsigned long size,  	r->size = size;  	r->dev = dev;  	cma_reserved_count++; -	pr_info("CMA: reserved %ld MiB at %08lx\n", size / SZ_1M, +	pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M,  		(unsigned long)base);  	/* Architecture specific contiguous memory fixup. */  	dma_contiguous_early_fixup(base, size);  	return 0;  err: -	pr_err("CMA: failed to reserve %ld MiB\n", size / SZ_1M); +	pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M);  	return base;  } diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 81541452887..8945f4e489e 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -36,68 +36,6 @@ MODULE_AUTHOR("Manuel Estrada Sainz");  MODULE_DESCRIPTION("Multi purpose firmware loading support");  MODULE_LICENSE("GPL"); -static const char *fw_path[] = { -	"/lib/firmware/updates/" UTS_RELEASE, -	"/lib/firmware/updates", -	"/lib/firmware/" UTS_RELEASE, -	"/lib/firmware" -}; - -/* Don't inline this: 'struct kstat' is biggish */ -static noinline long fw_file_size(struct file *file) -{ -	struct kstat st; -	if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st)) -		return -1; -	if (!S_ISREG(st.mode)) -		return -1; -	if (st.size != (long)st.size) -		return -1; -	return st.size; -} - -static bool fw_read_file_contents(struct file *file, struct firmware *fw) -{ -	long size; -	char *buf; - -	size = fw_file_size(file); -	if (size < 0) -		return false; -	buf = vmalloc(size); -	if (!buf) -		return false; -	if (kernel_read(file, 0, buf, size) != size) { -		vfree(buf); -		return false; -	} -	fw->data = buf; -	fw->size = size; -	return true; -} - -static bool fw_get_filesystem_firmware(struct firmware *fw, const char *name) -{ -	int i; -	bool success = false; -	char *path = __getname(); - -	for (i = 0; i < ARRAY_SIZE(fw_path); i++) { -		struct file *file; -		snprintf(path, PATH_MAX, "%s/%s", fw_path[i], name); - -		file = filp_open(path, O_RDONLY, 0); -		if (IS_ERR(file)) -			continue; -		success = fw_read_file_contents(file, fw); -		fput(file); -		if (success) -			break; -	} -	__putname(path); -	return success; -} -  /* Builtin firmware support */  #ifdef CONFIG_FW_LOADER @@ -150,6 +88,11 @@ enum {  	FW_STATUS_ABORT,  }; +enum fw_buf_fmt { +	VMALLOC_BUF,	/* used in direct loading */ +	PAGE_BUF,	/* used in loading via userspace */ +}; +  static int loading_timeout = 60;	/* In seconds */  static inline long firmware_loading_timeout(void) @@ -173,8 +116,6 @@ struct firmware_cache {  	spinlock_t name_lock;  	struct list_head fw_names; -	wait_queue_head_t wait_queue; -	int cnt;  	struct delayed_work work;  	struct notifier_block   pm_notify; @@ -187,6 +128,7 @@ struct firmware_buf {  	struct completion completion;  	struct firmware_cache *fwc;  	unsigned long status; +	enum fw_buf_fmt fmt;  	void *data;  	size_t size;  	struct page **pages; @@ -240,6 +182,7 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name,  	strcpy(buf->fw_id, fw_name);  	buf->fwc = fwc;  	init_completion(&buf->completion); +	buf->fmt = VMALLOC_BUF;  	pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); @@ -307,10 +250,14 @@ static void __fw_free_buf(struct kref *ref)  	list_del(&buf->list);  	spin_unlock(&fwc->lock); -	vunmap(buf->data); -	for (i = 0; i < buf->nr_pages; i++) -		__free_page(buf->pages[i]); -	kfree(buf->pages); + +	if (buf->fmt == PAGE_BUF) { +		vunmap(buf->data); +		for (i = 0; i < buf->nr_pages; i++) +			__free_page(buf->pages[i]); +		kfree(buf->pages); +	} else +		vfree(buf->data);  	kfree(buf);  } @@ -319,6 +266,69 @@ static void fw_free_buf(struct firmware_buf *buf)  	kref_put(&buf->ref, __fw_free_buf);  } +/* direct firmware loading support */ +static const char *fw_path[] = { +	"/lib/firmware/updates/" UTS_RELEASE, +	"/lib/firmware/updates", +	"/lib/firmware/" UTS_RELEASE, +	"/lib/firmware" +}; + +/* Don't inline this: 'struct kstat' is biggish */ +static noinline long fw_file_size(struct file *file) +{ +	struct kstat st; +	if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st)) +		return -1; +	if (!S_ISREG(st.mode)) +		return -1; +	if (st.size != (long)st.size) +		return -1; +	return st.size; +} + +static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf) +{ +	long size; +	char *buf; + +	size = fw_file_size(file); +	if (size < 0) +		return false; +	buf = vmalloc(size); +	if (!buf) +		return false; +	if (kernel_read(file, 0, buf, size) != size) { +		vfree(buf); +		return false; +	} +	fw_buf->data = buf; +	fw_buf->size = size; +	return true; +} + +static bool fw_get_filesystem_firmware(struct firmware_buf *buf) +{ +	int i; +	bool success = false; +	char *path = __getname(); + +	for (i = 0; i < ARRAY_SIZE(fw_path); i++) { +		struct file *file; +		snprintf(path, PATH_MAX, "%s/%s", fw_path[i], buf->fw_id); + +		file = filp_open(path, O_RDONLY, 0); +		if (IS_ERR(file)) +			continue; +		success = fw_read_file_contents(file, buf); +		fput(file); +		if (success) +			break; +	} +	__putname(path); +	return success; +} +  static struct firmware_priv *to_firmware_priv(struct device *dev)  {  	return container_of(dev, struct firmware_priv, dev); @@ -423,6 +433,21 @@ static void firmware_free_data(const struct firmware *fw)  #ifndef PAGE_KERNEL_RO  #define PAGE_KERNEL_RO PAGE_KERNEL  #endif + +/* one pages buffer should be mapped/unmapped only once */ +static int fw_map_pages_buf(struct firmware_buf *buf) +{ +	if (buf->fmt != PAGE_BUF) +		return 0; + +	if (buf->data) +		vunmap(buf->data); +	buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); +	if (!buf->data) +		return -ENOMEM; +	return 0; +} +  /**   * firmware_loading_store - set value in the 'loading' control file   * @dev: device pointer @@ -467,6 +492,14 @@ static ssize_t firmware_loading_store(struct device *dev,  		if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {  			set_bit(FW_STATUS_DONE, &fw_buf->status);  			clear_bit(FW_STATUS_LOADING, &fw_buf->status); + +			/* +			 * Several loading requests may be pending on +			 * one same firmware buf, so let all requests +			 * see the mapped 'buf->data' once the loading +			 * is completed. +			 * */ +			fw_map_pages_buf(fw_buf);  			complete_all(&fw_buf->completion);  			break;  		} @@ -670,15 +703,6 @@ exit:  	return fw_priv;  } -/* one pages buffer is mapped/unmapped only once */ -static int fw_map_pages_buf(struct firmware_buf *buf) -{ -	buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); -	if (!buf->data) -		return -ENOMEM; -	return 0; -} -  /* store the pages buffer info firmware from buf */  static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw)  { @@ -778,11 +802,6 @@ _request_firmware_prepare(const struct firmware **firmware_p, const char *name,  		return NULL;  	} -	if (fw_get_filesystem_firmware(firmware, name)) { -		dev_dbg(device, "firmware: direct-loading firmware %s\n", name); -		return NULL; -	} -  	ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf);  	if (!ret)  		fw_priv = fw_create_instance(firmware, name, device, @@ -832,6 +851,21 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,  	struct device *f_dev = &fw_priv->dev;  	struct firmware_buf *buf = fw_priv->buf;  	struct firmware_cache *fwc = &fw_cache; +	int direct_load = 0; + +	/* try direct loading from fs first */ +	if (fw_get_filesystem_firmware(buf)) { +		dev_dbg(f_dev->parent, "firmware: direct-loading" +			" firmware %s\n", buf->fw_id); + +		set_bit(FW_STATUS_DONE, &buf->status); +		complete_all(&buf->completion); +		direct_load = 1; +		goto handle_fw; +	} + +	/* fall back on userspace loading */ +	buf->fmt = PAGE_BUF;  	dev_set_uevent_suppress(f_dev, true); @@ -870,6 +904,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,  	del_timer_sync(&fw_priv->timeout); +handle_fw:  	mutex_lock(&fw_lock);  	if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status))  		retval = -ENOENT; @@ -884,9 +919,6 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,  	if (!retval && f_dev->parent)  		fw_add_devm_name(f_dev->parent, buf->fw_id); -	if (!retval) -		retval = fw_map_pages_buf(buf); -  	/*  	 * After caching firmware image is started, let it piggyback  	 * on request firmware. @@ -902,6 +934,9 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,  	fw_priv->buf = NULL;  	mutex_unlock(&fw_lock); +	if (direct_load) +		goto err_put_dev; +  	device_remove_file(f_dev, &dev_attr_loading);  err_del_bin_attr:  	device_remove_bin_file(f_dev, &firmware_attr_data); @@ -1129,6 +1164,8 @@ int uncache_firmware(const char *fw_name)  }  #ifdef CONFIG_PM_SLEEP +static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain); +  static struct fw_cache_entry *alloc_fw_cache_entry(const char *name)  {  	struct fw_cache_entry *fce; @@ -1142,17 +1179,27 @@ exit:  	return fce;  } -static int fw_cache_piggyback_on_request(const char *name) +static int __fw_entry_found(const char *name)  {  	struct firmware_cache *fwc = &fw_cache;  	struct fw_cache_entry *fce; -	int ret = 0; -	spin_lock(&fwc->name_lock);  	list_for_each_entry(fce, &fwc->fw_names, list) {  		if (!strcmp(fce->name, name)) -			goto found; +			return 1;  	} +	return 0; +} + +static int fw_cache_piggyback_on_request(const char *name) +{ +	struct firmware_cache *fwc = &fw_cache; +	struct fw_cache_entry *fce; +	int ret = 0; + +	spin_lock(&fwc->name_lock); +	if (__fw_entry_found(name)) +		goto found;  	fce = alloc_fw_cache_entry(name);  	if (fce) { @@ -1185,12 +1232,6 @@ static void __async_dev_cache_fw_image(void *fw_entry,  		free_fw_cache_entry(fce);  	} - -	spin_lock(&fwc->name_lock); -	fwc->cnt--; -	spin_unlock(&fwc->name_lock); - -	wake_up(&fwc->wait_queue);  }  /* called with dev->devres_lock held */ @@ -1229,11 +1270,19 @@ static void dev_cache_fw_image(struct device *dev, void *data)  		list_del(&fce->list);  		spin_lock(&fwc->name_lock); -		fwc->cnt++; -		list_add(&fce->list, &fwc->fw_names); +		/* only one cache entry for one firmware */ +		if (!__fw_entry_found(fce->name)) { +			list_add(&fce->list, &fwc->fw_names); +		} else { +			free_fw_cache_entry(fce); +			fce = NULL; +		}  		spin_unlock(&fwc->name_lock); -		async_schedule(__async_dev_cache_fw_image, (void *)fce); +		if (fce) +			async_schedule_domain(__async_dev_cache_fw_image, +					      (void *)fce, +					      &fw_cache_domain);  	}  } @@ -1275,6 +1324,9 @@ static void device_cache_fw_images(void)  	pr_debug("%s\n", __func__); +	/* cancel uncache work */ +	cancel_delayed_work_sync(&fwc->work); +  	/*  	 * use small loading timeout for caching devices' firmware  	 * because all these firmware images have been loaded @@ -1292,21 +1344,7 @@ static void device_cache_fw_images(void)  	mutex_unlock(&fw_lock);  	/* wait for completion of caching firmware for all devices */ -	spin_lock(&fwc->name_lock); -	for (;;) { -		prepare_to_wait(&fwc->wait_queue, &wait, -				TASK_UNINTERRUPTIBLE); -		if (!fwc->cnt) -			break; - -		spin_unlock(&fwc->name_lock); - -		schedule(); - -		spin_lock(&fwc->name_lock); -	} -	spin_unlock(&fwc->name_lock); -	finish_wait(&fwc->wait_queue, &wait); +	async_synchronize_full_domain(&fw_cache_domain);  	loading_timeout = old_timeout;  } @@ -1394,9 +1432,7 @@ static void __init fw_cache_init(void)  #ifdef CONFIG_PM_SLEEP  	spin_lock_init(&fw_cache.name_lock);  	INIT_LIST_HEAD(&fw_cache.fw_names); -	fw_cache.cnt = 0; -	init_waitqueue_head(&fw_cache.wait_queue);  	INIT_DELAYED_WORK(&fw_cache.work,  			  device_uncache_fw_images_work); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 8727e9c5eea..b2ee3bcd5a4 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -21,6 +21,7 @@  #include <linux/slab.h>  #include <linux/pm_runtime.h>  #include <linux/idr.h> +#include <linux/acpi.h>  #include "base.h"  #include "power/power.h" @@ -83,9 +84,16 @@ EXPORT_SYMBOL_GPL(platform_get_resource);   */  int platform_get_irq(struct platform_device *dev, unsigned int num)  { +#ifdef CONFIG_SPARC +	/* sparc does not have irqs represented as IORESOURCE_IRQ resources */ +	if (!dev || num >= dev->archdata.num_irqs) +		return -ENXIO; +	return dev->archdata.irqs[num]; +#else  	struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);  	return r ? r->start : -ENXIO; +#endif  }  EXPORT_SYMBOL_GPL(platform_get_irq); @@ -429,6 +437,7 @@ struct platform_device *platform_device_register_full(  		goto err_alloc;  	pdev->dev.parent = pdevinfo->parent; +	ACPI_HANDLE_SET(&pdev->dev, pdevinfo->acpi_node.handle);  	if (pdevinfo->dma_mask) {  		/* @@ -459,6 +468,7 @@ struct platform_device *platform_device_register_full(  	ret = platform_device_add(pdev);  	if (ret) {  err: +		ACPI_HANDLE_SET(&pdev->dev, NULL);  		kfree(pdev->dev.dma_mask);  err_alloc: @@ -474,8 +484,16 @@ static int platform_drv_probe(struct device *_dev)  {  	struct platform_driver *drv = to_platform_driver(_dev->driver);  	struct platform_device *dev = to_platform_device(_dev); +	int ret; + +	if (ACPI_HANDLE(_dev)) +		acpi_dev_pm_attach(_dev, true); -	return drv->probe(dev); +	ret = drv->probe(dev); +	if (ret && ACPI_HANDLE(_dev)) +		acpi_dev_pm_detach(_dev, true); + +	return ret;  }  static int platform_drv_probe_fail(struct device *_dev) @@ -487,8 +505,13 @@ static int platform_drv_remove(struct device *_dev)  {  	struct platform_driver *drv = to_platform_driver(_dev->driver);  	struct platform_device *dev = to_platform_device(_dev); +	int ret; -	return drv->remove(dev); +	ret = drv->remove(dev); +	if (ACPI_HANDLE(_dev)) +		acpi_dev_pm_detach(_dev, true); + +	return ret;  }  static void platform_drv_shutdown(struct device *_dev) @@ -497,6 +520,8 @@ static void platform_drv_shutdown(struct device *_dev)  	struct platform_device *dev = to_platform_device(_dev);  	drv->shutdown(dev); +	if (ACPI_HANDLE(_dev)) +		acpi_dev_pm_detach(_dev, true);  }  /** @@ -702,6 +727,10 @@ static int platform_match(struct device *dev, struct device_driver *drv)  	if (of_driver_match_device(dev, drv))  		return 1; +	/* Then try ACPI style match */ +	if (acpi_driver_match_device(dev, drv)) +		return 1; +  	/* Then try to match against the id table */  	if (pdrv->id_table)  		return platform_match_id(pdrv->id_table, pdev) != NULL; diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index eb78e9640c4..9d8fde70939 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -99,7 +99,7 @@ static void __pm_clk_remove(struct pm_clock_entry *ce)  	if (ce->status < PCE_STATUS_ERROR) {  		if (ce->status == PCE_STATUS_ENABLED) -			clk_disable(ce->clk); +			clk_disable_unprepare(ce->clk);  		if (ce->status >= PCE_STATUS_ACQUIRED)  			clk_put(ce->clk); @@ -396,7 +396,7 @@ static void enable_clock(struct device *dev, const char *con_id)  	clk = clk_get(dev, con_id);  	if (!IS_ERR(clk)) { -		clk_enable(clk); +		clk_prepare_enable(clk);  		clk_put(clk);  		dev_info(dev, "Runtime PM disabled, clock forced on.\n");  	} @@ -413,7 +413,7 @@ static void disable_clock(struct device *dev, const char *con_id)  	clk = clk_get(dev, con_id);  	if (!IS_ERR(clk)) { -		clk_disable(clk); +		clk_disable_unprepare(clk);  		clk_put(clk);  		dev_info(dev, "Runtime PM disabled, clock forced off.\n");  	} diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index c22b869245d..acc3a8ded29 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -470,10 +470,19 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)  		return -EBUSY;  	not_suspended = 0; -	list_for_each_entry(pdd, &genpd->dev_list, list_node) +	list_for_each_entry(pdd, &genpd->dev_list, list_node) { +		enum pm_qos_flags_status stat; + +		stat = dev_pm_qos_flags(pdd->dev, +					PM_QOS_FLAG_NO_POWER_OFF +						| PM_QOS_FLAG_REMOTE_WAKEUP); +		if (stat > PM_QOS_FLAGS_NONE) +			return -EBUSY; +  		if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)  		    || pdd->dev->power.irq_safe))  			not_suspended++; +	}  	if (not_suspended > genpd->in_progress)  		return -EBUSY; @@ -1862,7 +1871,7 @@ int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state)  	cpuidle_drv = cpuidle_driver_ref();  	if (!cpuidle_drv) {  		ret = -ENODEV; -		goto out; +		goto err_drv;  	}  	if (cpuidle_drv->state_count <= state) {  		ret = -EINVAL; @@ -1884,6 +1893,9 @@ int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state)   err:  	cpuidle_driver_unref(); + + err_drv: +	kfree(cpu_data);  	goto out;  } diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d9468642fc4..50b2831e027 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -23,6 +23,7 @@  #include <linux/rcupdate.h>  #include <linux/opp.h>  #include <linux/of.h> +#include <linux/export.h>  /*   * Internal data structure organization with the OPP layer library is as @@ -65,6 +66,7 @@ struct opp {  	unsigned long u_volt;  	struct device_opp *dev_opp; +	struct rcu_head head;  };  /** @@ -160,6 +162,7 @@ unsigned long opp_get_voltage(struct opp *opp)  	return v;  } +EXPORT_SYMBOL(opp_get_voltage);  /**   * opp_get_freq() - Gets the frequency corresponding to an available opp @@ -189,6 +192,7 @@ unsigned long opp_get_freq(struct opp *opp)  	return f;  } +EXPORT_SYMBOL(opp_get_freq);  /**   * opp_get_opp_count() - Get number of opps available in the opp list @@ -221,6 +225,7 @@ int opp_get_opp_count(struct device *dev)  	return count;  } +EXPORT_SYMBOL(opp_get_opp_count);  /**   * opp_find_freq_exact() - search for an exact frequency @@ -230,7 +235,10 @@ int opp_get_opp_count(struct device *dev)   *   * Searches for exact match in the opp list and returns pointer to the matching   * opp if found, else returns ERR_PTR in case of error and should be handled - * using IS_ERR. + * using IS_ERR. Error return values can be: + * EINVAL:	for bad pointer + * ERANGE:	no match found for search + * ENODEV:	if device not found in list of registered devices   *   * Note: available is a modifier for the search. if available=true, then the   * match is for exact matching frequency and is available in the stored OPP @@ -249,7 +257,7 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,  				bool available)  {  	struct device_opp *dev_opp; -	struct opp *temp_opp, *opp = ERR_PTR(-ENODEV); +	struct opp *temp_opp, *opp = ERR_PTR(-ERANGE);  	dev_opp = find_device_opp(dev);  	if (IS_ERR(dev_opp)) { @@ -268,6 +276,7 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,  	return opp;  } +EXPORT_SYMBOL(opp_find_freq_exact);  /**   * opp_find_freq_ceil() - Search for an rounded ceil freq @@ -278,7 +287,11 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,   * for a device.   *   * Returns matching *opp and refreshes *freq accordingly, else returns - * ERR_PTR in case of error and should be handled using IS_ERR. + * ERR_PTR in case of error and should be handled using IS_ERR. Error return + * values can be: + * EINVAL:	for bad pointer + * ERANGE:	no match found for search + * ENODEV:	if device not found in list of registered devices   *   * Locking: This function must be called under rcu_read_lock(). opp is a rcu   * protected pointer. The reason for the same is that the opp pointer which is @@ -289,7 +302,7 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,  struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)  {  	struct device_opp *dev_opp; -	struct opp *temp_opp, *opp = ERR_PTR(-ENODEV); +	struct opp *temp_opp, *opp = ERR_PTR(-ERANGE);  	if (!dev || !freq) {  		dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); @@ -298,7 +311,7 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)  	dev_opp = find_device_opp(dev);  	if (IS_ERR(dev_opp)) -		return opp; +		return ERR_CAST(dev_opp);  	list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {  		if (temp_opp->available && temp_opp->rate >= *freq) { @@ -310,6 +323,7 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)  	return opp;  } +EXPORT_SYMBOL(opp_find_freq_ceil);  /**   * opp_find_freq_floor() - Search for a rounded floor freq @@ -320,7 +334,11 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)   * for a device.   *   * Returns matching *opp and refreshes *freq accordingly, else returns - * ERR_PTR in case of error and should be handled using IS_ERR. + * ERR_PTR in case of error and should be handled using IS_ERR. Error return + * values can be: + * EINVAL:	for bad pointer + * ERANGE:	no match found for search + * ENODEV:	if device not found in list of registered devices   *   * Locking: This function must be called under rcu_read_lock(). opp is a rcu   * protected pointer. The reason for the same is that the opp pointer which is @@ -331,7 +349,7 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)  struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq)  {  	struct device_opp *dev_opp; -	struct opp *temp_opp, *opp = ERR_PTR(-ENODEV); +	struct opp *temp_opp, *opp = ERR_PTR(-ERANGE);  	if (!dev || !freq) {  		dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); @@ -340,7 +358,7 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq)  	dev_opp = find_device_opp(dev);  	if (IS_ERR(dev_opp)) -		return opp; +		return ERR_CAST(dev_opp);  	list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {  		if (temp_opp->available) { @@ -356,6 +374,7 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq)  	return opp;  } +EXPORT_SYMBOL(opp_find_freq_floor);  /**   * opp_add()  - Add an OPP table from a table definitions @@ -512,7 +531,7 @@ static int opp_set_availability(struct device *dev, unsigned long freq,  	list_replace_rcu(&opp->node, &new_opp->node);  	mutex_unlock(&dev_opp_list_lock); -	synchronize_rcu(); +	kfree_rcu(opp, head);  	/* Notify the change of the OPP availability */  	if (availability_req) @@ -522,13 +541,10 @@ static int opp_set_availability(struct device *dev, unsigned long freq,  		srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_DISABLE,  					 new_opp); -	/* clean up old opp */ -	new_opp = opp; -	goto out; +	return 0;  unlock:  	mutex_unlock(&dev_opp_list_lock); -out:  	kfree(new_opp);  	return r;  } @@ -552,6 +568,7 @@ int opp_enable(struct device *dev, unsigned long freq)  {  	return opp_set_availability(dev, freq, true);  } +EXPORT_SYMBOL(opp_enable);  /**   * opp_disable() - Disable a specific OPP @@ -573,6 +590,7 @@ int opp_disable(struct device *dev, unsigned long freq)  {  	return opp_set_availability(dev, freq, false);  } +EXPORT_SYMBOL(opp_disable);  #ifdef CONFIG_CPU_FREQ  /** diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 0dbfdf4419a..b16686a0a5a 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -93,8 +93,10 @@ extern void dpm_sysfs_remove(struct device *dev);  extern void rpm_sysfs_remove(struct device *dev);  extern int wakeup_sysfs_add(struct device *dev);  extern void wakeup_sysfs_remove(struct device *dev); -extern int pm_qos_sysfs_add(struct device *dev); -extern void pm_qos_sysfs_remove(struct device *dev); +extern int pm_qos_sysfs_add_latency(struct device *dev); +extern void pm_qos_sysfs_remove_latency(struct device *dev); +extern int pm_qos_sysfs_add_flags(struct device *dev); +extern void pm_qos_sysfs_remove_flags(struct device *dev);  #else /* CONFIG_PM */ diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 74a67e0019a..ff46387f530 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -40,6 +40,7 @@  #include <linux/device.h>  #include <linux/mutex.h>  #include <linux/export.h> +#include <linux/pm_runtime.h>  #include "power.h" @@ -48,6 +49,50 @@ static DEFINE_MUTEX(dev_pm_qos_mtx);  static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);  /** + * __dev_pm_qos_flags - Check PM QoS flags for a given device. + * @dev: Device to check the PM QoS flags for. + * @mask: Flags to check against. + * + * This routine must be called with dev->power.lock held. + */ +enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask) +{ +	struct dev_pm_qos *qos = dev->power.qos; +	struct pm_qos_flags *pqf; +	s32 val; + +	if (!qos) +		return PM_QOS_FLAGS_UNDEFINED; + +	pqf = &qos->flags; +	if (list_empty(&pqf->list)) +		return PM_QOS_FLAGS_UNDEFINED; + +	val = pqf->effective_flags & mask; +	if (val) +		return (val == mask) ? PM_QOS_FLAGS_ALL : PM_QOS_FLAGS_SOME; + +	return PM_QOS_FLAGS_NONE; +} + +/** + * dev_pm_qos_flags - Check PM QoS flags for a given device (locked). + * @dev: Device to check the PM QoS flags for. + * @mask: Flags to check against. + */ +enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask) +{ +	unsigned long irqflags; +	enum pm_qos_flags_status ret; + +	spin_lock_irqsave(&dev->power.lock, irqflags); +	ret = __dev_pm_qos_flags(dev, mask); +	spin_unlock_irqrestore(&dev->power.lock, irqflags); + +	return ret; +} + +/**   * __dev_pm_qos_read_value - Get PM QoS constraint for a given device.   * @dev: Device to get the PM QoS constraint value for.   * @@ -55,9 +100,7 @@ static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);   */  s32 __dev_pm_qos_read_value(struct device *dev)  { -	struct pm_qos_constraints *c = dev->power.constraints; - -	return c ? pm_qos_read_value(c) : 0; +	return dev->power.qos ? pm_qos_read_value(&dev->power.qos->latency) : 0;  }  /** @@ -76,30 +119,39 @@ s32 dev_pm_qos_read_value(struct device *dev)  	return ret;  } -/* - * apply_constraint - * @req: constraint request to apply - * @action: action to perform add/update/remove, of type enum pm_qos_req_action - * @value: defines the qos request +/** + * apply_constraint - Add/modify/remove device PM QoS request. + * @req: Constraint request to apply + * @action: Action to perform (add/update/remove). + * @value: Value to assign to the QoS request.   *   * Internal function to update the constraints list using the PM QoS core   * code and if needed call the per-device and the global notification   * callbacks   */  static int apply_constraint(struct dev_pm_qos_request *req, -			    enum pm_qos_req_action action, int value) +			    enum pm_qos_req_action action, s32 value)  { -	int ret, curr_value; - -	ret = pm_qos_update_target(req->dev->power.constraints, -				   &req->node, action, value); +	struct dev_pm_qos *qos = req->dev->power.qos; +	int ret; -	if (ret) { -		/* Call the global callbacks if needed */ -		curr_value = pm_qos_read_value(req->dev->power.constraints); -		blocking_notifier_call_chain(&dev_pm_notifiers, -					     (unsigned long)curr_value, -					     req); +	switch(req->type) { +	case DEV_PM_QOS_LATENCY: +		ret = pm_qos_update_target(&qos->latency, &req->data.pnode, +					   action, value); +		if (ret) { +			value = pm_qos_read_value(&qos->latency); +			blocking_notifier_call_chain(&dev_pm_notifiers, +						     (unsigned long)value, +						     req); +		} +		break; +	case DEV_PM_QOS_FLAGS: +		ret = pm_qos_update_flags(&qos->flags, &req->data.flr, +					  action, value); +		break; +	default: +		ret = -EINVAL;  	}  	return ret; @@ -114,28 +166,32 @@ static int apply_constraint(struct dev_pm_qos_request *req,   */  static int dev_pm_qos_constraints_allocate(struct device *dev)  { +	struct dev_pm_qos *qos;  	struct pm_qos_constraints *c;  	struct blocking_notifier_head *n; -	c = kzalloc(sizeof(*c), GFP_KERNEL); -	if (!c) +	qos = kzalloc(sizeof(*qos), GFP_KERNEL); +	if (!qos)  		return -ENOMEM;  	n = kzalloc(sizeof(*n), GFP_KERNEL);  	if (!n) { -		kfree(c); +		kfree(qos);  		return -ENOMEM;  	}  	BLOCKING_INIT_NOTIFIER_HEAD(n); +	c = &qos->latency;  	plist_head_init(&c->list);  	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;  	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;  	c->type = PM_QOS_MIN;  	c->notifiers = n; +	INIT_LIST_HEAD(&qos->flags.list); +  	spin_lock_irq(&dev->power.lock); -	dev->power.constraints = c; +	dev->power.qos = qos;  	spin_unlock_irq(&dev->power.lock);  	return 0; @@ -151,7 +207,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)  void dev_pm_qos_constraints_init(struct device *dev)  {  	mutex_lock(&dev_pm_qos_mtx); -	dev->power.constraints = NULL; +	dev->power.qos = NULL;  	dev->power.power_state = PMSG_ON;  	mutex_unlock(&dev_pm_qos_mtx);  } @@ -164,24 +220,28 @@ void dev_pm_qos_constraints_init(struct device *dev)   */  void dev_pm_qos_constraints_destroy(struct device *dev)  { +	struct dev_pm_qos *qos;  	struct dev_pm_qos_request *req, *tmp;  	struct pm_qos_constraints *c; +	struct pm_qos_flags *f;  	/* -	 * If the device's PM QoS resume latency limit has been exposed to user -	 * space, it has to be hidden at this point. +	 * If the device's PM QoS resume latency limit or PM QoS flags have been +	 * exposed to user space, they have to be hidden at this point.  	 */  	dev_pm_qos_hide_latency_limit(dev); +	dev_pm_qos_hide_flags(dev);  	mutex_lock(&dev_pm_qos_mtx);  	dev->power.power_state = PMSG_INVALID; -	c = dev->power.constraints; -	if (!c) +	qos = dev->power.qos; +	if (!qos)  		goto out; -	/* Flush the constraints list for the device */ -	plist_for_each_entry_safe(req, tmp, &c->list, node) { +	/* Flush the constraints lists for the device. */ +	c = &qos->latency; +	plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {  		/*  		 * Update constraints list and call the notification  		 * callbacks if needed @@ -189,13 +249,18 @@ void dev_pm_qos_constraints_destroy(struct device *dev)  		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);  		memset(req, 0, sizeof(*req));  	} +	f = &qos->flags; +	list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { +		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); +		memset(req, 0, sizeof(*req)); +	}  	spin_lock_irq(&dev->power.lock); -	dev->power.constraints = NULL; +	dev->power.qos = NULL;  	spin_unlock_irq(&dev->power.lock);  	kfree(c->notifiers); -	kfree(c); +	kfree(qos);   out:  	mutex_unlock(&dev_pm_qos_mtx); @@ -205,6 +270,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev)   * dev_pm_qos_add_request - inserts new qos request into the list   * @dev: target device for the constraint   * @req: pointer to a preallocated handle + * @type: type of the request   * @value: defines the qos request   *   * This function inserts a new entry in the device constraints list of @@ -218,9 +284,12 @@ void dev_pm_qos_constraints_destroy(struct device *dev)   * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory   * to allocate for data structures, -ENODEV if the device has just been removed   * from the system. + * + * Callers should ensure that the target device is not RPM_SUSPENDED before + * using this function for requests of type DEV_PM_QOS_FLAGS.   */  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, -			   s32 value) +			   enum dev_pm_qos_req_type type, s32 value)  {  	int ret = 0; @@ -235,7 +304,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,  	mutex_lock(&dev_pm_qos_mtx); -	if (!dev->power.constraints) { +	if (!dev->power.qos) {  		if (dev->power.power_state.event == PM_EVENT_INVALID) {  			/* The device has been removed from the system. */  			req->dev = NULL; @@ -251,8 +320,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,  		}  	} -	if (!ret) +	if (!ret) { +		req->type = type;  		ret = apply_constraint(req, PM_QOS_ADD_REQ, value); +	}   out:  	mutex_unlock(&dev_pm_qos_mtx); @@ -262,6 +333,37 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,  EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);  /** + * __dev_pm_qos_update_request - Modify an existing device PM QoS request. + * @req : PM QoS request to modify. + * @new_value: New value to request. + */ +static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, +				       s32 new_value) +{ +	s32 curr_value; +	int ret = 0; + +	if (!req->dev->power.qos) +		return -ENODEV; + +	switch(req->type) { +	case DEV_PM_QOS_LATENCY: +		curr_value = req->data.pnode.prio; +		break; +	case DEV_PM_QOS_FLAGS: +		curr_value = req->data.flr.flags; +		break; +	default: +		return -EINVAL; +	} + +	if (curr_value != new_value) +		ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); + +	return ret; +} + +/**   * dev_pm_qos_update_request - modifies an existing qos request   * @req : handle to list element holding a dev_pm_qos request to use   * @new_value: defines the qos request @@ -275,11 +377,13 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);   * 0 if the aggregated constraint value has not changed,   * -EINVAL in case of wrong parameters, -ENODEV if the device has been   * removed from the system + * + * Callers should ensure that the target device is not RPM_SUSPENDED before + * using this function for requests of type DEV_PM_QOS_FLAGS.   */ -int dev_pm_qos_update_request(struct dev_pm_qos_request *req, -			      s32 new_value) +int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value)  { -	int ret = 0; +	int ret;  	if (!req) /*guard against callers passing in null */  		return -EINVAL; @@ -289,17 +393,9 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req,  		return -EINVAL;  	mutex_lock(&dev_pm_qos_mtx); - -	if (req->dev->power.constraints) { -		if (new_value != req->node.prio) -			ret = apply_constraint(req, PM_QOS_UPDATE_REQ, -					       new_value); -	} else { -		/* Return if the device has been removed */ -		ret = -ENODEV; -	} - +	ret = __dev_pm_qos_update_request(req, new_value);  	mutex_unlock(&dev_pm_qos_mtx); +  	return ret;  }  EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); @@ -315,6 +411,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);   * 0 if the aggregated constraint value has not changed,   * -EINVAL in case of wrong parameters, -ENODEV if the device has been   * removed from the system + * + * Callers should ensure that the target device is not RPM_SUSPENDED before + * using this function for requests of type DEV_PM_QOS_FLAGS.   */  int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)  { @@ -329,7 +428,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)  	mutex_lock(&dev_pm_qos_mtx); -	if (req->dev->power.constraints) { +	if (req->dev->power.qos) {  		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,  				       PM_QOS_DEFAULT_VALUE);  		memset(req, 0, sizeof(*req)); @@ -362,13 +461,13 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)  	mutex_lock(&dev_pm_qos_mtx); -	if (!dev->power.constraints) +	if (!dev->power.qos)  		ret = dev->power.power_state.event != PM_EVENT_INVALID ?  			dev_pm_qos_constraints_allocate(dev) : -ENODEV;  	if (!ret)  		ret = blocking_notifier_chain_register( -				dev->power.constraints->notifiers, notifier); +				dev->power.qos->latency.notifiers, notifier);  	mutex_unlock(&dev_pm_qos_mtx);  	return ret; @@ -393,9 +492,9 @@ int dev_pm_qos_remove_notifier(struct device *dev,  	mutex_lock(&dev_pm_qos_mtx);  	/* Silently return if the constraints object is not present. */ -	if (dev->power.constraints) +	if (dev->power.qos)  		retval = blocking_notifier_chain_unregister( -				dev->power.constraints->notifiers, +				dev->power.qos->latency.notifiers,  				notifier);  	mutex_unlock(&dev_pm_qos_mtx); @@ -449,9 +548,10 @@ int dev_pm_qos_add_ancestor_request(struct device *dev,  		ancestor = ancestor->parent;  	if (ancestor) -		error = dev_pm_qos_add_request(ancestor, req, value); +		error = dev_pm_qos_add_request(ancestor, req, +					       DEV_PM_QOS_LATENCY, value); -	if (error) +	if (error < 0)  		req->dev = NULL;  	return error; @@ -459,10 +559,19 @@ int dev_pm_qos_add_ancestor_request(struct device *dev,  EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request);  #ifdef CONFIG_PM_RUNTIME -static void __dev_pm_qos_drop_user_request(struct device *dev) +static void __dev_pm_qos_drop_user_request(struct device *dev, +					   enum dev_pm_qos_req_type type)  { -	dev_pm_qos_remove_request(dev->power.pq_req); -	dev->power.pq_req = NULL; +	switch(type) { +	case DEV_PM_QOS_LATENCY: +		dev_pm_qos_remove_request(dev->power.qos->latency_req); +		dev->power.qos->latency_req = NULL; +		break; +	case DEV_PM_QOS_FLAGS: +		dev_pm_qos_remove_request(dev->power.qos->flags_req); +		dev->power.qos->flags_req = NULL; +		break; +	}  }  /** @@ -478,21 +587,21 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value)  	if (!device_is_registered(dev) || value < 0)  		return -EINVAL; -	if (dev->power.pq_req) +	if (dev->power.qos && dev->power.qos->latency_req)  		return -EEXIST;  	req = kzalloc(sizeof(*req), GFP_KERNEL);  	if (!req)  		return -ENOMEM; -	ret = dev_pm_qos_add_request(dev, req, value); +	ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY, value);  	if (ret < 0)  		return ret; -	dev->power.pq_req = req; -	ret = pm_qos_sysfs_add(dev); +	dev->power.qos->latency_req = req; +	ret = pm_qos_sysfs_add_latency(dev);  	if (ret) -		__dev_pm_qos_drop_user_request(dev); +		__dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY);  	return ret;  } @@ -504,10 +613,92 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit);   */  void dev_pm_qos_hide_latency_limit(struct device *dev)  { -	if (dev->power.pq_req) { -		pm_qos_sysfs_remove(dev); -		__dev_pm_qos_drop_user_request(dev); +	if (dev->power.qos && dev->power.qos->latency_req) { +		pm_qos_sysfs_remove_latency(dev); +		__dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY);  	}  }  EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); + +/** + * dev_pm_qos_expose_flags - Expose PM QoS flags of a device to user space. + * @dev: Device whose PM QoS flags are to be exposed to user space. + * @val: Initial values of the flags. + */ +int dev_pm_qos_expose_flags(struct device *dev, s32 val) +{ +	struct dev_pm_qos_request *req; +	int ret; + +	if (!device_is_registered(dev)) +		return -EINVAL; + +	if (dev->power.qos && dev->power.qos->flags_req) +		return -EEXIST; + +	req = kzalloc(sizeof(*req), GFP_KERNEL); +	if (!req) +		return -ENOMEM; + +	pm_runtime_get_sync(dev); +	ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val); +	if (ret < 0) +		goto fail; + +	dev->power.qos->flags_req = req; +	ret = pm_qos_sysfs_add_flags(dev); +	if (ret) +		__dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); + +fail: +	pm_runtime_put(dev); +	return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); + +/** + * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space. + * @dev: Device whose PM QoS flags are to be hidden from user space. + */ +void dev_pm_qos_hide_flags(struct device *dev) +{ +	if (dev->power.qos && dev->power.qos->flags_req) { +		pm_qos_sysfs_remove_flags(dev); +		pm_runtime_get_sync(dev); +		__dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); +		pm_runtime_put(dev); +	} +} +EXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags); + +/** + * dev_pm_qos_update_flags - Update PM QoS flags request owned by user space. + * @dev: Device to update the PM QoS flags request for. + * @mask: Flags to set/clear. + * @set: Whether to set or clear the flags (true means set). + */ +int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) +{ +	s32 value; +	int ret; + +	if (!dev->power.qos || !dev->power.qos->flags_req) +		return -EINVAL; + +	pm_runtime_get_sync(dev); +	mutex_lock(&dev_pm_qos_mtx); + +	value = dev_pm_qos_requested_flags(dev); +	if (set) +		value |= mask; +	else +		value &= ~mask; + +	ret = __dev_pm_qos_update_request(dev->power.qos->flags_req, value); + +	mutex_unlock(&dev_pm_qos_mtx); +	pm_runtime_put(dev); + +	return ret; +}  #endif /* CONFIG_PM_RUNTIME */ diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index b91dc6f1e91..50d16e3cb0a 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -221,7 +221,7 @@ static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show,  static ssize_t pm_qos_latency_show(struct device *dev,  				   struct device_attribute *attr, char *buf)  { -	return sprintf(buf, "%d\n", dev->power.pq_req->node.prio); +	return sprintf(buf, "%d\n", dev_pm_qos_requested_latency(dev));  }  static ssize_t pm_qos_latency_store(struct device *dev, @@ -237,12 +237,66 @@ static ssize_t pm_qos_latency_store(struct device *dev,  	if (value < 0)  		return -EINVAL; -	ret = dev_pm_qos_update_request(dev->power.pq_req, value); +	ret = dev_pm_qos_update_request(dev->power.qos->latency_req, value);  	return ret < 0 ? ret : n;  }  static DEVICE_ATTR(pm_qos_resume_latency_us, 0644,  		   pm_qos_latency_show, pm_qos_latency_store); + +static ssize_t pm_qos_no_power_off_show(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) +					& PM_QOS_FLAG_NO_POWER_OFF)); +} + +static ssize_t pm_qos_no_power_off_store(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, size_t n) +{ +	int ret; + +	if (kstrtoint(buf, 0, &ret)) +		return -EINVAL; + +	if (ret != 0 && ret != 1) +		return -EINVAL; + +	ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_NO_POWER_OFF, ret); +	return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_no_power_off, 0644, +		   pm_qos_no_power_off_show, pm_qos_no_power_off_store); + +static ssize_t pm_qos_remote_wakeup_show(struct device *dev, +					 struct device_attribute *attr, +					 char *buf) +{ +	return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) +					& PM_QOS_FLAG_REMOTE_WAKEUP)); +} + +static ssize_t pm_qos_remote_wakeup_store(struct device *dev, +					  struct device_attribute *attr, +					  const char *buf, size_t n) +{ +	int ret; + +	if (kstrtoint(buf, 0, &ret)) +		return -EINVAL; + +	if (ret != 0 && ret != 1) +		return -EINVAL; + +	ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP, ret); +	return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_remote_wakeup, 0644, +		   pm_qos_remote_wakeup_show, pm_qos_remote_wakeup_store);  #endif /* CONFIG_PM_RUNTIME */  #ifdef CONFIG_PM_SLEEP @@ -564,15 +618,27 @@ static struct attribute_group pm_runtime_attr_group = {  	.attrs	= runtime_attrs,  }; -static struct attribute *pm_qos_attrs[] = { +static struct attribute *pm_qos_latency_attrs[] = {  #ifdef CONFIG_PM_RUNTIME  	&dev_attr_pm_qos_resume_latency_us.attr,  #endif /* CONFIG_PM_RUNTIME */  	NULL,  }; -static struct attribute_group pm_qos_attr_group = { +static struct attribute_group pm_qos_latency_attr_group = {  	.name	= power_group_name, -	.attrs	= pm_qos_attrs, +	.attrs	= pm_qos_latency_attrs, +}; + +static struct attribute *pm_qos_flags_attrs[] = { +#ifdef CONFIG_PM_RUNTIME +	&dev_attr_pm_qos_no_power_off.attr, +	&dev_attr_pm_qos_remote_wakeup.attr, +#endif /* CONFIG_PM_RUNTIME */ +	NULL, +}; +static struct attribute_group pm_qos_flags_attr_group = { +	.name	= power_group_name, +	.attrs	= pm_qos_flags_attrs,  };  int dpm_sysfs_add(struct device *dev) @@ -615,14 +681,24 @@ void wakeup_sysfs_remove(struct device *dev)  	sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);  } -int pm_qos_sysfs_add(struct device *dev) +int pm_qos_sysfs_add_latency(struct device *dev) +{ +	return sysfs_merge_group(&dev->kobj, &pm_qos_latency_attr_group); +} + +void pm_qos_sysfs_remove_latency(struct device *dev) +{ +	sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_attr_group); +} + +int pm_qos_sysfs_add_flags(struct device *dev)  { -	return sysfs_merge_group(&dev->kobj, &pm_qos_attr_group); +	return sysfs_merge_group(&dev->kobj, &pm_qos_flags_attr_group);  } -void pm_qos_sysfs_remove(struct device *dev) +void pm_qos_sysfs_remove_flags(struct device *dev)  { -	sysfs_unmerge_group(&dev->kobj, &pm_qos_attr_group); +	sysfs_unmerge_group(&dev->kobj, &pm_qos_flags_attr_group);  }  void rpm_sysfs_remove(struct device *dev) diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 6be390bd8bd..f0d30543fcc 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -3,7 +3,7 @@  # subsystems should select the appropriate symbols.  config REGMAP -	default y if (REGMAP_I2C || REGMAP_SPI) +	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_MMIO || REGMAP_IRQ)  	select LZO_COMPRESS  	select LZO_DECOMPRESS  	select IRQ_DOMAIN if REGMAP_IRQ  |