From 90f790d2dc96f5a61855ae65b90e30c40c893a20 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 20 Aug 2012 21:45:05 +0100 Subject: regmap: irq: Allow users to retrieve the irq_domain This is useful for integration with other subsystems, especially MFD, and provides an alternative API for users that request their own IRQs. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 5b6b1d8e..5972ad9 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -458,3 +458,22 @@ int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) return irq_create_mapping(data->domain, irq); } EXPORT_SYMBOL_GPL(regmap_irq_get_virq); + +/** + * regmap_irq_get_domain(): Retrieve the irq_domain for the chip + * + * Useful for drivers to request their own IRQs and for integration + * with subsystems. For ease of integration NULL is accepted as a + * domain, allowing devices to just call this even if no domain is + * allocated. + * + * @data: regmap_irq controller to operate on. + */ +struct irq_domain *regmap_irq_get_domain(struct regmap_irq_chip_data *data) +{ + if (data) + return data->domain; + else + return NULL; +} +EXPORT_SYMBOL_GPL(regmap_irq_get_domain); -- cgit v1.1 From e3549cd01347ef211d01353bdf2572b086574007 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 2 Oct 2012 20:17:15 +0100 Subject: regmap: Rename n_ranges to num_ranges This makes things consistent with the rest of the API and is actually what the documentation says. We don't currently have any in tree users so low cost. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 52069d2..ea9d6eb 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -519,7 +519,7 @@ struct regmap *regmap_init(struct device *dev, } map->range_tree = RB_ROOT; - for (i = 0; i < config->n_ranges; i++) { + for (i = 0; i < config->num_ranges; i++) { const struct regmap_range_cfg *range_cfg = &config->ranges[i]; struct regmap_range_node *new; @@ -532,7 +532,7 @@ struct regmap *regmap_init(struct device *dev, /* Make sure, that this register range has no selector or data window within its boundary */ - for (j = 0; j < config->n_ranges; j++) { + for (j = 0; j < config->num_ranges; j++) { unsigned sel_reg = config->ranges[j].selector_reg; unsigned win_min = config->ranges[j].window_start; unsigned win_max = win_min + -- cgit v1.1 From 061adc064adbbdd9eb127ab2e86b7a71f4ccaf2e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 3 Oct 2012 12:17:51 +0100 Subject: regmap: When we sanity check during range adds say what errors we find Rather than just returning a single error code for every possible thing we can notice print an error message saying what the problem was. This makes it very much easier to figure out what's wrong and fix it. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ea9d6eb..0544f63 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -524,11 +524,29 @@ struct regmap *regmap_init(struct device *dev, struct regmap_range_node *new; /* Sanity check */ - if (range_cfg->range_max < range_cfg->range_min || - range_cfg->range_max > map->max_register || - range_cfg->selector_reg > map->max_register || - range_cfg->window_len == 0) + if (range_cfg->range_max < range_cfg->range_min) { + dev_err(map->dev, "Invalid range %d: %d < %d\n", i, + range_cfg->range_max, range_cfg->range_min); goto err_range; + } + + if (range_cfg->range_max > map->max_register) { + dev_err(map->dev, "Invalid range %d: %d > %d\n", i, + range_cfg->range_max, map->max_register); + goto err_range; + } + + if (range_cfg->selector_reg > map->max_register) { + dev_err(map->dev, + "Invalid range %d: selector out of map\n", i); + goto err_range; + } + + if (range_cfg->window_len == 0) { + dev_err(map->dev, "Invalid range %d: window_len 0\n", + i); + goto err_range; + } /* Make sure, that this register range has no selector or data window within its boundary */ @@ -540,11 +558,17 @@ struct regmap *regmap_init(struct device *dev, if (range_cfg->range_min <= sel_reg && sel_reg <= range_cfg->range_max) { + dev_err(map->dev, + "Range %d: selector for %d in window\n", + i, j); goto err_range; } if (!(win_max < range_cfg->range_min || win_min > range_cfg->range_max)) { + dev_err(map->dev, + "Range %d: window for %d in window\n", + i, j); goto err_range; } } @@ -564,6 +588,7 @@ struct regmap *regmap_init(struct device *dev, new->window_len = range_cfg->window_len; if (_regmap_range_add(map, new) == false) { + dev_err(map->dev, "Failed to add range %d\n", i); kfree(new); goto err_range; } -- cgit v1.1 From d058bb49618482f2eff0db57618c9a7352916dd5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 3 Oct 2012 12:40:47 +0100 Subject: regmap: Allow ranges to be named For more useful diagnostics. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 80f9ab9..27e66c3 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -120,6 +120,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, struct regmap_range_node { struct rb_node node; + const char *name; unsigned int range_min; unsigned int range_max; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 0544f63..ce5129d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -579,6 +579,7 @@ struct regmap *regmap_init(struct device *dev, goto err_range; } + new->name = range_cfg->name; new->range_min = range_cfg->range_min; new->range_max = range_cfg->range_max; new->selector_reg = range_cfg->selector_reg; -- cgit v1.1 From bd9cc12f4a7e7389432bba0cae6970dfc28f423c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 3 Oct 2012 12:45:37 +0100 Subject: regmap: Factor out debugfs register read This will allow the use of the same code for reading register ranges. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index bb1ff17..25b6843 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -56,15 +56,15 @@ static const struct file_operations regmap_name_fops = { .llseek = default_llseek, }; -static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, + unsigned int to, char __user *user_buf, + size_t count, loff_t *ppos) { int reg_len, val_len, tot_len; size_t buf_pos = 0; loff_t p = 0; ssize_t ret; int i; - struct regmap *map = file->private_data; char *buf; unsigned int val; @@ -80,7 +80,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, val_len = 2 * map->format.val_bytes; tot_len = reg_len + val_len + 3; /* : \n */ - for (i = 0; i <= map->max_register; i += map->reg_stride) { + for (i = from; i <= to; i += map->reg_stride) { if (!regmap_readable(map, i)) continue; @@ -95,7 +95,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, /* Format the register */ snprintf(buf + buf_pos, count - buf_pos, "%.*x: ", - reg_len, i); + reg_len, i - from); buf_pos += reg_len + 2; /* Format the value, write all X if we can't read */ @@ -126,6 +126,15 @@ out: return ret; } +static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct regmap *map = file->private_data; + + return regmap_read_debugfs(map, 0, map->max_register, user_buf, + count, ppos); +} + #undef REGMAP_ALLOW_WRITE_DEBUGFS #ifdef REGMAP_ALLOW_WRITE_DEBUGFS /* -- cgit v1.1 From 4b020b3f9ba2af8031c5c7d759fbafd234d1c390 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 3 Oct 2012 13:13:16 +0100 Subject: regmap: Provide debugfs read of register ranges If a register range is named then provide a debugfs file showing the contents of the range separately. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap-debugfs.c | 31 +++++++++++++++++++++++++++++++ drivers/base/regmap/regmap.c | 1 + 3 files changed, 33 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 27e66c3..ac869d2 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -121,6 +121,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, struct regmap_range_node { struct rb_node node; const char *name; + struct regmap *map; unsigned int range_min; unsigned int range_max; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 25b6843..f4b9dd0 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -183,6 +183,22 @@ static const struct file_operations regmap_map_fops = { .llseek = default_llseek, }; +static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct regmap_range_node *range = file->private_data; + struct regmap *map = range->map; + + return regmap_read_debugfs(map, range->range_min, range->range_max, + user_buf, count, ppos); +} + +static const struct file_operations regmap_range_fops = { + .open = simple_open, + .read = regmap_range_read_file, + .llseek = default_llseek, +}; + static ssize_t regmap_access_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -253,6 +269,9 @@ static const struct file_operations regmap_access_fops = { void regmap_debugfs_init(struct regmap *map, const char *name) { + struct rb_node *next; + struct regmap_range_node *range_node; + if (name) { map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", dev_name(map->dev), name); @@ -285,6 +304,18 @@ void regmap_debugfs_init(struct regmap *map, const char *name) debugfs_create_bool("cache_bypass", 0400, map->debugfs, &map->cache_bypass); } + + next = rb_first(&map->range_tree); + while (next) { + range_node = rb_entry(next, struct regmap_range_node, node); + + if (range_node->name) + debugfs_create_file(range_node->name, 0400, + map->debugfs, range_node, + ®map_range_fops); + + next = rb_next(&range_node->node); + } } void regmap_debugfs_exit(struct regmap *map) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ce5129d..366b629 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -579,6 +579,7 @@ struct regmap *regmap_init(struct device *dev, goto err_range; } + new->map = map; new->name = range_cfg->name; new->range_min = range_cfg->range_min; new->range_max = range_cfg->range_max; -- cgit v1.1 From 98bc7dfd76407eaa0964ecb4d5319c957a3b9df9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 4 Oct 2012 17:31:11 +0100 Subject: regmap: Factor range lookup out of page selection This will support a subsequent update to allow bulk writes to cross window boundaries. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 91 +++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 40 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 366b629..4bb926c 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -765,59 +765,57 @@ struct regmap *dev_get_regmap(struct device *dev, const char *name) EXPORT_SYMBOL_GPL(dev_get_regmap); static int _regmap_select_page(struct regmap *map, unsigned int *reg, + struct regmap_range_node *range, unsigned int val_num) { - struct regmap_range_node *range; void *orig_work_buf; unsigned int win_offset; unsigned int win_page; bool page_chg; int ret; - range = _regmap_range_lookup(map, *reg); - if (range) { - win_offset = (*reg - range->range_min) % range->window_len; - win_page = (*reg - range->range_min) / range->window_len; + win_offset = (*reg - range->range_min) % range->window_len; + win_page = (*reg - range->range_min) / range->window_len; - if (val_num > 1) { - /* Bulk write shouldn't cross range boundary */ - if (*reg + val_num - 1 > range->range_max) - return -EINVAL; + if (val_num > 1) { + /* Bulk write shouldn't cross range boundary */ + if (*reg + val_num - 1 > range->range_max) + return -EINVAL; - /* ... or single page boundary */ - if (val_num > range->window_len - win_offset) - return -EINVAL; - } + /* ... or single page boundary */ + if (val_num > range->window_len - win_offset) + return -EINVAL; + } - /* It is possible to have selector register inside data window. - In that case, selector register is located on every page and - it needs no page switching, when accessed alone. */ - if (val_num > 1 || - range->window_start + win_offset != range->selector_reg) { - /* Use separate work_buf during page switching */ - orig_work_buf = map->work_buf; - map->work_buf = map->selector_work_buf; + /* It is possible to have selector register inside data window. + In that case, selector register is located on every page and + it needs no page switching, when accessed alone. */ + if (val_num > 1 || + range->window_start + win_offset != range->selector_reg) { + /* Use separate work_buf during page switching */ + orig_work_buf = map->work_buf; + map->work_buf = map->selector_work_buf; - ret = _regmap_update_bits(map, range->selector_reg, - range->selector_mask, - win_page << range->selector_shift, - &page_chg); + ret = _regmap_update_bits(map, range->selector_reg, + range->selector_mask, + win_page << range->selector_shift, + &page_chg); - map->work_buf = orig_work_buf; + map->work_buf = orig_work_buf; - if (ret < 0) - return ret; - } - - *reg = range->window_start + win_offset; + if (ret < 0) + return ret; } + *reg = range->window_start + win_offset; + return 0; } static int _regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { + struct regmap_range_node *range; u8 *u8 = map->work_buf; void *buf; int ret = -ENOTSUPP; @@ -852,9 +850,13 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, } } - ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes); - if (ret < 0) - return ret; + range = _regmap_range_lookup(map, reg); + if (range) { + ret = _regmap_select_page(map, ®, range, + val_len / map->format.val_bytes); + if (ret < 0) + return ret; + } map->format.format_reg(map->work_buf, reg, map->reg_shift); @@ -903,6 +905,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { + struct regmap_range_node *range; int ret; BUG_ON(!map->format.format_write && !map->format.format_val); @@ -924,9 +927,12 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_reg_write(map->dev, reg, val); if (map->format.format_write) { - ret = _regmap_select_page(map, ®, 1); - if (ret < 0) - return ret; + range = _regmap_range_lookup(map, reg); + if (range) { + ret = _regmap_select_page(map, ®, range, 1); + if (ret < 0) + return ret; + } map->format.format_write(map, reg, val); @@ -1082,12 +1088,17 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write); static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int val_len) { + struct regmap_range_node *range; u8 *u8 = map->work_buf; int ret; - ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes); - if (ret < 0) - return ret; + range = _regmap_range_lookup(map, reg); + if (range) { + ret = _regmap_select_page(map, ®, range, + val_len / map->format.val_bytes); + if (ret < 0) + return ret; + } map->format.format_reg(map->work_buf, reg, map->reg_shift); -- cgit v1.1 From 0ff3e62ff119f2b65b0a8ad48fcb669f609fd904 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 4 Oct 2012 17:39:13 +0100 Subject: regmap: Make return code checks consistent The range code was written to check for return codes less than zero as errors but throughout the rest of the API return codes not equal to zero are errors. Change all these checks to match the house style. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 4bb926c..baf9586 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -606,7 +606,7 @@ struct regmap *regmap_init(struct device *dev, } ret = regcache_init(map, config); - if (ret < 0) + if (ret != 0) goto err_range; regmap_debugfs_init(map, config->name); @@ -803,7 +803,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, map->work_buf = orig_work_buf; - if (ret < 0) + if (ret != 0) return ret; } @@ -854,7 +854,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, if (range) { ret = _regmap_select_page(map, ®, range, val_len / map->format.val_bytes); - if (ret < 0) + if (ret != 0) return ret; } @@ -930,7 +930,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, range = _regmap_range_lookup(map, reg); if (range) { ret = _regmap_select_page(map, ®, range, 1); - if (ret < 0) + if (ret != 0) return ret; } @@ -1096,7 +1096,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, if (range) { ret = _regmap_select_page(map, ®, range, val_len / map->format.val_bytes); - if (ret < 0) + if (ret != 0) return ret; } -- cgit v1.1 From 8a2ceac6617a67d8a1ee4bd255743d577bde311a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 4 Oct 2012 18:20:18 +0100 Subject: regmap: Split raw writes that cross window boundaries If a block write covers a paged memory region and crosses a window boundary then rather than failing the write split the transfer up into multiple writes, making the whole process more transparent for drivers. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index baf9586..96253cd 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -852,8 +852,30 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, range = _regmap_range_lookup(map, reg); if (range) { - ret = _regmap_select_page(map, ®, range, - val_len / map->format.val_bytes); + int val_num = val_len / map->format.val_bytes; + int win_offset = (reg - range->range_min) % range->window_len; + int win_residue = range->window_len - win_offset; + + /* If the write goes beyond the end of the window split it */ + while (val_num > win_residue) { + dev_dbg(map->dev, "Writing window %d/%d\n", + win_residue, val_len / map->format.val_bytes); + ret = _regmap_raw_write(map, reg, val, win_residue * + map->format.val_bytes); + if (ret != 0) + return ret; + + reg += win_residue; + val_num -= win_residue; + val += win_residue * map->format.val_bytes; + val_len -= win_residue * map->format.val_bytes; + + win_offset = (reg - range->range_min) % + range->window_len; + win_residue = range->window_len - win_offset; + } + + ret = _regmap_select_page(map, ®, range, val_num); if (ret != 0) return ret; } -- cgit v1.1 From a8f28cfad8cd44d7c34b166d0e5ace1125dbee1f Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 8 Oct 2012 22:06:30 +0200 Subject: regmap: silence GCC warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building regmap.o triggers this GCC warning: drivers/base/regmap/regmap.c: In function ‘regmap_raw_read’: drivers/base/regmap/regmap.c:1172:6: warning: ‘ret’ may be used uninitialized in this function [-Wmaybe-uninitialized] Long story short: Jakub Jelinek pointed out that there is a type mismatch between 'num' in regmap_volatile_range() and 'val_count' in regmap_raw_read(). And indeed, converting 'num' to the type of 'val_count' (ie, size_t) makes this warning go away. Signed-off-by: Paul Bolle Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 52069d2..9455595 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -82,7 +82,7 @@ bool regmap_precious(struct regmap *map, unsigned int reg) } static bool regmap_volatile_range(struct regmap *map, unsigned int reg, - unsigned int num) + size_t num) { unsigned int i; -- cgit v1.1 From 0d4529c534c1c664f25088eb5f5b4d7ce0ee2510 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Tue, 16 Oct 2012 15:56:59 +0200 Subject: regmap: make lock/unlock functions customizable It is sometimes convenient for a regmap user to override the standard regmap lock/unlock functions with custom functions. For instance this can be useful in case an already existing spinlock or mutex has to be used for locking a set of registers instead of the internal regmap spinlock/mutex. Note that the fast_io field of struct regmap_bus is ignored in case custom locking functions are used. Signed-off-by: Davide Ciminaghi Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 4 +-- drivers/base/regmap/regmap.c | 65 ++++++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 30 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 80f9ab9..b1ee824 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -31,14 +31,12 @@ struct regmap_format { unsigned int (*parse_val)(void *buf); }; -typedef void (*regmap_lock)(struct regmap *map); -typedef void (*regmap_unlock)(struct regmap *map); - struct regmap { struct mutex mutex; spinlock_t spinlock; regmap_lock lock; regmap_unlock unlock; + void *lock_arg; /* This is passed to lock/unlock functions */ struct device *dev; /* Device we do I/O on */ void *work_buf; /* Scratch buffer used to format I/O */ diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 52069d2..5d8b7525 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -214,23 +214,27 @@ static unsigned int regmap_parse_32_native(void *buf) return *(u32 *)buf; } -static void regmap_lock_mutex(struct regmap *map) +static void regmap_lock_mutex(void *__map) { + struct regmap *map = __map; mutex_lock(&map->mutex); } -static void regmap_unlock_mutex(struct regmap *map) +static void regmap_unlock_mutex(void *__map) { + struct regmap *map = __map; mutex_unlock(&map->mutex); } -static void regmap_lock_spinlock(struct regmap *map) +static void regmap_lock_spinlock(void *__map) { + struct regmap *map = __map; spin_lock(&map->spinlock); } -static void regmap_unlock_spinlock(struct regmap *map) +static void regmap_unlock_spinlock(void *__map) { + struct regmap *map = __map; spin_unlock(&map->spinlock); } @@ -335,14 +339,21 @@ struct regmap *regmap_init(struct device *dev, goto err; } - if (bus->fast_io) { - spin_lock_init(&map->spinlock); - map->lock = regmap_lock_spinlock; - map->unlock = regmap_unlock_spinlock; + if (config->lock && config->unlock) { + map->lock = config->lock; + map->unlock = config->unlock; + map->lock_arg = config->lock_arg; } else { - mutex_init(&map->mutex); - map->lock = regmap_lock_mutex; - map->unlock = regmap_unlock_mutex; + if (bus->fast_io) { + spin_lock_init(&map->spinlock); + map->lock = regmap_lock_spinlock; + map->unlock = regmap_unlock_spinlock; + } else { + mutex_init(&map->mutex); + map->lock = regmap_lock_mutex; + map->unlock = regmap_unlock_mutex; + } + map->lock_arg = map; } map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.pad_bytes = config->pad_bits / 8; @@ -939,11 +950,11 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) if (reg % map->reg_stride) return -EINVAL; - map->lock(map); + map->lock(map->lock_arg); ret = _regmap_write(map, reg, val); - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -975,11 +986,11 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, if (reg % map->reg_stride) return -EINVAL; - map->lock(map); + map->lock(map->lock_arg); ret = _regmap_raw_write(map, reg, val, val_len); - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -1011,7 +1022,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (reg % map->reg_stride) return -EINVAL; - map->lock(map); + map->lock(map->lock_arg); /* No formatting is require if val_byte is 1 */ if (val_bytes == 1) { @@ -1047,7 +1058,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, kfree(wval); out: - map->unlock(map); + map->unlock(map->lock_arg); return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_write); @@ -1137,11 +1148,11 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) if (reg % map->reg_stride) return -EINVAL; - map->lock(map); + map->lock(map->lock_arg); ret = _regmap_read(map, reg, val); - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -1171,7 +1182,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, if (reg % map->reg_stride) return -EINVAL; - map->lock(map); + map->lock(map->lock_arg); if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || map->cache_type == REGCACHE_NONE) { @@ -1193,7 +1204,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, } out: - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -1300,9 +1311,9 @@ int regmap_update_bits(struct regmap *map, unsigned int reg, bool change; int ret; - map->lock(map); + map->lock(map->lock_arg); ret = _regmap_update_bits(map, reg, mask, val, &change); - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -1326,9 +1337,9 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, { int ret; - map->lock(map); + map->lock(map->lock_arg); ret = _regmap_update_bits(map, reg, mask, val, change); - map->unlock(map); + map->unlock(map->lock_arg); return ret; } EXPORT_SYMBOL_GPL(regmap_update_bits_check); @@ -1357,7 +1368,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, if (map->patch) return -EBUSY; - map->lock(map); + map->lock(map->lock_arg); bypass = map->cache_bypass; @@ -1385,7 +1396,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, out: map->cache_bypass = bypass; - map->unlock(map); + map->unlock(map->lock_arg); return ret; } -- cgit v1.1 From 5f986c590fcf4284924fcda991cf14ab32bff49f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Oct 2012 01:07:27 +0200 Subject: PM / QoS: Prepare device structure for adding more constraint types Currently struct dev_pm_info contains only one PM QoS constraints pointer reserved for latency requirements. Since one more device constraints type (i.e. flags) will be necessary, introduce a new structure, struct dev_pm_qos, that eventually will contain all of the available device PM QoS constraints and replace the "constraints" pointer in struct dev_pm_info with a pointer to the new structure called "qos". Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet --- drivers/base/power/qos.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 74a67e0..40ff1b0 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -55,9 +55,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; } /** @@ -91,12 +89,12 @@ static int apply_constraint(struct dev_pm_qos_request *req, { int ret, curr_value; - ret = pm_qos_update_target(req->dev->power.constraints, + ret = pm_qos_update_target(&req->dev->power.qos->latency, &req->node, action, value); if (ret) { /* Call the global callbacks if needed */ - curr_value = pm_qos_read_value(req->dev->power.constraints); + curr_value = pm_qos_read_value(&req->dev->power.qos->latency); blocking_notifier_call_chain(&dev_pm_notifiers, (unsigned long)curr_value, req); @@ -114,20 +112,22 @@ 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; @@ -135,7 +135,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) c->notifiers = n; spin_lock_irq(&dev->power.lock); - dev->power.constraints = c; + dev->power.qos = qos; spin_unlock_irq(&dev->power.lock); return 0; @@ -151,7 +151,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,6 +164,7 @@ 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; @@ -176,10 +177,11 @@ void dev_pm_qos_constraints_destroy(struct device *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; + c = &qos->latency; /* Flush the constraints list for the device */ plist_for_each_entry_safe(req, tmp, &c->list, node) { /* @@ -191,7 +193,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) } spin_lock_irq(&dev->power.lock); - dev->power.constraints = NULL; + dev->power.qos = NULL; spin_unlock_irq(&dev->power.lock); kfree(c->notifiers); @@ -235,7 +237,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; @@ -290,7 +292,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, mutex_lock(&dev_pm_qos_mtx); - if (req->dev->power.constraints) { + if (req->dev->power.qos) { if (new_value != req->node.prio) ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); @@ -329,7 +331,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 +364,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 +395,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); -- cgit v1.1 From 021c870ba4ab4bc9a23d5db4e324f50f26d8ab24 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Oct 2012 01:09:00 +0200 Subject: PM / QoS: Prepare struct dev_pm_qos_request for more request types The subsequent patches will use struct dev_pm_qos_request for representing both latency requests and flags requests. To make that easier, put the node member of struct dev_pm_qos_request (under the name "pnode") into a union called "data" that will represent the request's value and list node depending on its type. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Reviewed-by: mark gross --- drivers/base/power/qos.c | 6 +++--- drivers/base/power/sysfs.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 40ff1b0..96d27b8 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -90,7 +90,7 @@ static int apply_constraint(struct dev_pm_qos_request *req, int ret, curr_value; ret = pm_qos_update_target(&req->dev->power.qos->latency, - &req->node, action, value); + &req->data.pnode, action, value); if (ret) { /* Call the global callbacks if needed */ @@ -183,7 +183,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) c = &qos->latency; /* Flush the constraints list for the device */ - plist_for_each_entry_safe(req, tmp, &c->list, node) { + plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { /* * Update constraints list and call the notification * callbacks if needed @@ -293,7 +293,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, mutex_lock(&dev_pm_qos_mtx); if (req->dev->power.qos) { - if (new_value != req->node.prio) + if (new_value != req->data.pnode.prio) ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); } else { diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index b91dc6f..54c61ff 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->power.pq_req->data.pnode.prio); } static ssize_t pm_qos_latency_store(struct device *dev, -- cgit v1.1 From ae0fb4b72c8db7e6c4ef32bc58a43a759ad414b9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Oct 2012 01:09:12 +0200 Subject: PM / QoS: Introduce PM QoS device flags support Modify the device PM QoS core code to support PM QoS flags requests. First, add a new field of type struct pm_qos_flags called "flags" to struct dev_pm_qos for representing the list of PM QoS flags requests for the given device. Accordingly, add a new "type" field to struct dev_pm_qos_request (along with an enum for representing request types) and a new member called "flr" to its data union for representig flags requests. Second, modify dev_pm_qos_add_request(), dev_pm_qos_update_request(), the internal routine apply_constraint() used by them and their existing callers to cover flags requests as well as latency requests. In particular, dev_pm_qos_add_request() gets a new argument called "type" for specifying the type of a request to be added. Finally, introduce two routines, __dev_pm_qos_flags() and dev_pm_qos_flags(), allowing their callers to check which PM QoS flags have been requested for the given device (the caller is supposed to pass the mask of flags to check as the routine's second argument and examine its return value for the result). Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Reviewed-by: mark gross --- drivers/base/power/qos.c | 124 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 98 insertions(+), 26 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 96d27b8..3c66f75d1 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -48,6 +48,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. * @@ -74,30 +118,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.qos->latency, - &req->data.pnode, 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.qos->latency); - 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; @@ -134,6 +187,8 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) c->type = PM_QOS_MIN; c->notifiers = n; + INIT_LIST_HEAD(&qos->flags.list); + spin_lock_irq(&dev->power.lock); dev->power.qos = qos; spin_unlock_irq(&dev->power.lock); @@ -207,6 +262,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 @@ -222,7 +278,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) * from the system. */ 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; @@ -253,8 +309,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); @@ -281,6 +339,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) { + s32 curr_value; int ret = 0; if (!req) /*guard against callers passing in null */ @@ -292,15 +351,27 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, mutex_lock(&dev_pm_qos_mtx); - if (req->dev->power.qos) { - if (new_value != req->data.pnode.prio) - ret = apply_constraint(req, PM_QOS_UPDATE_REQ, - new_value); - } else { - /* Return if the device has been removed */ + if (!req->dev->power.qos) { ret = -ENODEV; + goto out; } + 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: + ret = -EINVAL; + goto out; + } + + if (curr_value != new_value) + ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); + + out: mutex_unlock(&dev_pm_qos_mtx); return ret; } @@ -451,7 +522,8 @@ 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) req->dev = NULL; @@ -487,7 +559,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) 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; -- cgit v1.1 From e39473d0b9448e770f49b0b15e514be884264438 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 24 Oct 2012 02:08:18 +0200 Subject: PM / QoS: Make it possible to expose PM QoS device flags to user space Define two device PM QoS flags, PM_QOS_FLAG_NO_POWER_OFF and PM_QOS_FLAG_REMOTE_WAKEUP, and introduce routines dev_pm_qos_expose_flags() and dev_pm_qos_hide_flags() allowing the caller to expose those two flags to user space or to hide them from it, respectively. After the flags have been exposed, user space will see two additional sysfs attributes, pm_qos_no_power_off and pm_qos_remote_wakeup, under the device's /sys/devices/.../power/ directory. Then, writing 1 to one of them will update the PM QoS flags request owned by user space so that the corresponding flag is requested to be set. In turn, writing 0 to one of them will cause the corresponding flag in the user space's request to be cleared (however, the owners of the other PM QoS flags requests for the same device may still request the flag to be set and it may be effectively set even if user space doesn't request that). Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Acked-by: mark gross --- drivers/base/power/power.h | 6 +- drivers/base/power/qos.c | 168 +++++++++++++++++++++++++++++++++++---------- drivers/base/power/sysfs.c | 94 ++++++++++++++++++++++--- 3 files changed, 221 insertions(+), 47 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 0dbfdf4..b16686a 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 3c66f75d1..167834d 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "power.h" @@ -322,6 +323,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 @@ -336,11 +368,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); * -EINVAL in case of wrong parameters, -ENODEV if the device has been * removed from the system */ -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) { - s32 curr_value; - int ret = 0; + int ret; if (!req) /*guard against callers passing in null */ return -EINVAL; @@ -350,29 +380,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.qos) { - ret = -ENODEV; - goto out; - } - - 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: - ret = -EINVAL; - goto out; - } - - if (curr_value != new_value) - ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); - - out: + __dev_pm_qos_update_request(req, new_value); mutex_unlock(&dev_pm_qos_mtx); + return ret; } EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); @@ -533,10 +543,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; + } } /** @@ -552,7 +571,7 @@ 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); @@ -563,10 +582,10 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 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; } @@ -578,10 +597,87 @@ 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; + + ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val); + if (ret < 0) + return ret; + + 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); + + 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); + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); + } +} +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 54c61ff..50d16e3 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->data.pnode.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) -- cgit v1.1 From 34b1f76275a2cb8c1ce8e00095d200552b235122 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 24 Oct 2012 02:08:30 +0200 Subject: PM / Domains: Check device PM QoS flags in pm_genpd_poweroff() Make the generic PM domains pm_genpd_poweroff() function take device PM QoS flags into account when deciding whether or not to remove power from the domain. After this change the routine will return -EBUSY without executing the domain's .power_off() callback if there is at least one PM QoS flags request for at least one device in the domain and at least of those request has at least one of the NO_POWER_OFF and REMOTE_WAKEUP flags set. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Reviewed-by: mark gross --- drivers/base/power/domain.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index c22b869..e2cf392 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; -- cgit v1.1 From 1a61cfe3445218637f38b355c76fc3132865a0a6 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 25 Oct 2012 14:07:18 -0200 Subject: regmap: Fix printing of size_t variable val_bytes is of 'size_t', so it should be printed as '%zu'. Fixes the following build warning on x86: drivers/base/regmap/regmap.c:872:4: warning: format '%d' expects argument of type 'int', but argument 5 has type 'size_t' [-Wformat] Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 96253cd..336bedb 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -858,7 +858,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, /* If the write goes beyond the end of the window split it */ while (val_num > win_residue) { - dev_dbg(map->dev, "Writing window %d/%d\n", + dev_dbg(map->dev, "Writing window %d/%zu\n", win_residue, val_len / map->format.val_bytes); ret = _regmap_raw_write(map, reg, val, win_residue * map->format.val_bytes); -- cgit v1.1 From 967857dfd990081f6702daebc6c9bc6f4ba7a39e Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 23 Oct 2012 13:01:50 -0700 Subject: drivers/base: remove CONFIG_EXPERIMENTAL This config item has not carried much meaning for a while now and is almost always enabled by default. As agreed during the Linux kernel summit, remove it. CC: Greg Kroah-Hartman Signed-off-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/base/Kconfig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 08b4c52..e439ebc 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -57,7 +57,7 @@ config DEVTMPFS_MOUNT on the rootfs is completely empty. config STANDALONE - bool "Select only drivers that don't need compile-time external firmware" if EXPERIMENTAL + bool "Select only drivers that don't need compile-time external firmware" default y help Select this option if you don't have magic firmware for drivers that @@ -185,7 +185,6 @@ config DMA_SHARED_BUFFER bool default n select ANON_INODES - depends on EXPERIMENTAL help This option enables the framework for buffer-sharing between multiple drivers. A buffer is associated with a file using driver @@ -193,8 +192,8 @@ config DMA_SHARED_BUFFER driver. config CMA - bool "Contiguous Memory Allocator (EXPERIMENTAL)" - depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK && EXPERIMENTAL + bool "Contiguous Memory Allocator" + depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK select MIGRATION select MEMORY_ISOLATION help -- cgit v1.1 From 91872392f08486f692887d2f06a333f512648f22 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 9 Oct 2012 19:52:05 +0200 Subject: drivers/base: Add a DEVICE_BOOL_ATTR macro ... which, analogous to DEVICE_INT_ATTR provides functionality to set/clear bools. Its purpose is to be used where values need to be used as booleans in configuration context. Next patch uses this. Signed-off-by: Borislav Petkov Acked-by: Greg Kroah-Hartman Acked-by: Tony Luck --- drivers/base/core.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index abea76c..c8ae1f6 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -171,6 +171,27 @@ ssize_t device_show_int(struct device *dev, } EXPORT_SYMBOL_GPL(device_show_int); +ssize_t device_store_bool(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + + if (strtobool(buf, ea->var) < 0) + return -EINVAL; + + return size; +} +EXPORT_SYMBOL_GPL(device_store_bool); + +ssize_t device_show_bool(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + + return snprintf(buf, PAGE_SIZE, "%d\n", *(bool *)(ea->var)); +} +EXPORT_SYMBOL_GPL(device_show_bool); + /** * device_release - free device structure. * @kobj: device's kobject. -- cgit v1.1 From 6d04b8ac575c38d94515b4e8f3b800c5c61ef611 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 26 Oct 2012 19:05:32 +0100 Subject: regmap: core: Report registers in hex when we can't cache This seems to be the most common way of reporting register numbers, it's certainly what we do for trace. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 52069d2..810f509 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -814,7 +814,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, ival); if (ret) { dev_err(map->dev, - "Error in caching of register: %u ret: %d\n", + "Error in caching of register: %x ret: %d\n", reg + i, ret); return ret; } -- cgit v1.1 From f9652875dcd49d400b775aecc8e0bf76e405b70a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 30 Oct 2012 20:00:30 +0100 Subject: PM / QoS: Fix the return value of dev_pm_qos_update_request() Commit e39473d (PM / QoS: Make it possible to expose PM QoS device flags to user space) introduced __dev_pm_qos_update_request() to be called internally by dev_pm_qos_update_request(), but forgot to make the latter actually use the return value of the former. Fix this mistake. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 167834d..3a7687a 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -380,7 +380,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) return -EINVAL; mutex_lock(&dev_pm_qos_mtx); - __dev_pm_qos_update_request(req, new_value); + ret = __dev_pm_qos_update_request(req, new_value); mutex_unlock(&dev_pm_qos_mtx); return ret; -- cgit v1.1 From b1d6d822550ee4e71ab9d55e65f8e096fb11e62b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Oct 2012 18:04:53 +0100 Subject: drivers/base: fix typo in comment for arch_setup_pdev_archdata() Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 8727e9c..af1d47f 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -44,7 +44,7 @@ EXPORT_SYMBOL_GPL(platform_bus); * be setup before the platform_notifier is called. So if a user needs to * manipulate any relevant information in the pdev_archdata they can do: * - * platform_devic_alloc() + * platform_device_alloc() * ... manipulate ... * platform_device_add() * -- cgit v1.1 From a369a7ebbfce5caa38e3d5645ea050f4590dea7a Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 28 Oct 2012 01:05:41 -0700 Subject: drivers: base: Convert dev_printk(KERN_ to dev_( dev_ calls take less code than dev_printk(KERN_ and reducing object size is good. Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/base/attribute_container.c | 2 +- drivers/base/devres.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index 8fc200b..d78b204 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -158,7 +158,7 @@ attribute_container_add_device(struct device *dev, ic = kzalloc(sizeof(*ic), GFP_KERNEL); if (!ic) { - dev_printk(KERN_ERR, dev, "failed to allocate class container\n"); + dev_err(dev, "failed to allocate class container\n"); continue; } diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 8731979..6683906 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -50,8 +50,8 @@ static void devres_log(struct device *dev, struct devres_node *node, const char *op) { if (unlikely(log_devres)) - dev_printk(KERN_ERR, dev, "DEVRES %3s %p %s (%lu bytes)\n", - op, node, node->name, (unsigned long)node->size); + dev_err(dev, "DEVRES %3s %p %s (%lu bytes)\n", + op, node, node->name, (unsigned long)node->size); } #else /* CONFIG_DEBUG_DEVRES */ #define set_node_dbginfo(node, n, s) do {} while (0) -- cgit v1.1 From 9eaee2cdcf9ead20f234b15ed26f82a96a4fa8fb Mon Sep 17 00:00:00 2001 From: "Lan,Tianyu" Date: Thu, 1 Nov 2012 22:45:30 +0100 Subject: PM / QoS: Fix a free error in the dev_pm_qos_constraints_destroy() Free a wrong point to struct dev_pm_qos->latency which suppose to be the point to struct dev_pm_qos. The patch is to fix the issue. Signed-off-by: Lan Tianyu Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 3a7687a..31d3f48 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -253,7 +253,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) spin_unlock_irq(&dev->power.lock); kfree(c->notifiers); - kfree(c); + kfree(qos); out: mutex_unlock(&dev_pm_qos_mtx); -- cgit v1.1 From 436ede8942ab43474182c6454f420d71f7bb1163 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Nov 2012 13:10:09 +0100 Subject: PM / QoS: Document request manipulation requirement for flags In fact, the callers of dev_pm_qos_add_request(), dev_pm_qos_update_request() and dev_pm_qos_remove_request() for requests of type DEV_PM_QOS_FLAGS need to ensure that the target device is not RPM_SUSPENDED before using any of these functions (or be prepared for the new PM QoS flags to take effect after the device has been resumed). Document this in their kerneldoc comments. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 31d3f48..081db2d 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -277,6 +277,9 @@ 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, enum dev_pm_qos_req_type type, s32 value) @@ -367,6 +370,9 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, * 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) { @@ -398,6 +404,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) { -- cgit v1.1 From 5cf8f7db8274f68b180ad277dbb0308e72b66efd Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Mon, 29 Oct 2012 23:26:56 +0000 Subject: sparc: Add sparc support for platform_get_irq() This adds sparc support for platform_get_irq that in the normal case use platform_get_resource() to get an irq. This standard approach fails for sparc as there are no resources of type IORESOURCE_IRQ for irqs for sparc. Cross platform drivers can then use this standard platform function and work on sparc instead of having to have a special case for sparc. Signed-off-by: Andreas Larsson Signed-off-by: David S. Miller --- drivers/base/platform.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 8727e9c..72c776f 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -83,9 +83,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); -- cgit v1.1 From 7e4d68443a80574392d1027ff34992ab945934a6 Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Thu, 8 Nov 2012 11:14:08 +0800 Subject: PM / QoS: Resume device before exposing/hiding PM QoS flags Since dev_pm_qos_add_request(), dev_pm_qos_update_request() and dev_pm_qos_remove_request() for PM QoS flags should not be invoked when device in RPM_SUSPENDED, add pm_runtime_get_sync() and pm_runtime_put() around these functions in dev_pm_qos_expose_flags() and dev_pm_qos_hide_flags(). [rjw: Modified the subject and changelog to better reflect the code changes made.] Signed-off-by: Lan Tianyu Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 081db2d..fdc3894 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -633,15 +633,18 @@ int dev_pm_qos_expose_flags(struct device *dev, s32 val) 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) - return ret; + 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); @@ -654,7 +657,9 @@ 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); -- cgit v1.1 From ce2fcbd99cef580623116bb33531dbc3e6f690b0 Mon Sep 17 00:00:00 2001 From: Chuansheng Liu Date: Thu, 8 Nov 2012 19:14:40 +0800 Subject: firmware loader: Fix the race FW_STATUS_DONE is followed by class_timeout There is a race as below when calling request_firmware(): CPU1 CPU2 write 0 > loading mutex_lock(&fw_lock) ... set_bit FW_STATUS_DONE class_timeout is coming set_bit FW_STATUS_ABORT complete_all &completion ... mutex_unlock(&fw_lock) In this time, the bit FW_STATUS_DONE and FW_STATUS_ABORT are set, and request_firmware() will return failure due to condition in _request_firmware_load(): if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) retval = -ENOENT; But from the above scenerio, it should be a successful requesting. So we need judge if the bit FW_STATUS_DONE is already set before calling fw_load_abort() in timeout function. As Ming's proposal, we need change the timer into sched_work to benefit from using &fw_lock mutex also. Signed-off-by: liu chuansheng Acked-by: Ming Lei Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 8945f4e..b44ed35 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -143,7 +143,7 @@ struct fw_cache_entry { }; struct firmware_priv { - struct timer_list timeout; + struct delayed_work timeout_work; bool nowait; struct device dev; struct firmware_buf *buf; @@ -667,11 +667,18 @@ static struct bin_attribute firmware_attr_data = { .write = firmware_data_write, }; -static void firmware_class_timeout(u_long data) +static void firmware_class_timeout_work(struct work_struct *work) { - struct firmware_priv *fw_priv = (struct firmware_priv *) data; + struct firmware_priv *fw_priv = container_of(work, + struct firmware_priv, timeout_work.work); + mutex_lock(&fw_lock); + if (test_bit(FW_STATUS_DONE, &(fw_priv->buf->status))) { + mutex_unlock(&fw_lock); + return; + } fw_load_abort(fw_priv); + mutex_unlock(&fw_lock); } static struct firmware_priv * @@ -690,8 +697,8 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, fw_priv->nowait = nowait; fw_priv->fw = firmware; - setup_timer(&fw_priv->timeout, - firmware_class_timeout, (u_long) fw_priv); + INIT_DELAYED_WORK(&fw_priv->timeout_work, + firmware_class_timeout_work); f_dev = &fw_priv->dev; @@ -858,7 +865,9 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, dev_dbg(f_dev->parent, "firmware: direct-loading" " firmware %s\n", buf->fw_id); + mutex_lock(&fw_lock); set_bit(FW_STATUS_DONE, &buf->status); + mutex_unlock(&fw_lock); complete_all(&buf->completion); direct_load = 1; goto handle_fw; @@ -894,15 +903,14 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, dev_set_uevent_suppress(f_dev, false); dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); if (timeout != MAX_SCHEDULE_TIMEOUT) - mod_timer(&fw_priv->timeout, - round_jiffies_up(jiffies + timeout)); + schedule_delayed_work(&fw_priv->timeout_work, timeout); kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); } wait_for_completion(&buf->completion); - del_timer_sync(&fw_priv->timeout); + cancel_delayed_work_sync(&fw_priv->timeout_work); handle_fw: mutex_lock(&fw_lock); -- cgit v1.1 From bd9eb7fbe69111ea0ff1f999ef4a5f26d223d1d5 Mon Sep 17 00:00:00 2001 From: Chuansheng Liu Date: Sat, 10 Nov 2012 01:27:22 +0800 Subject: firmware loader: Fix the concurrent request_firmware() race for kref_get/put There is one race that both request_firmware() with the same firmware name. The race scenerio is as below: CPU1 CPU2 request_firmware() --> _request_firmware_load() return err another request_firmware() is coming --> _request_firmware_cleanup is called --> _request_firmware_prepare --> release_firmware ---> fw_lookup_and_allocate_buf --> spin_lock(&fwc->lock) ... __fw_lookup_buf() return true fw_free_buf() will be called --> ... kref_put --> decrease the refcount to 0 kref_get(&tmp->ref) ==> it will trigger warning due to refcount == 0 __fw_free_buf() --> ... spin_unlock(&fwc->lock) spin_lock(&fwc->lock) list_del(&buf->list) spin_unlock(&fwc->lock) kfree(buf) After that, the freed buf will be used. The key race is decreasing refcount to 0 and list_del is not protected together by fwc->lock, and it is possible another thread try to get it between refcount==0 and list_del. Fix it here to protect it together. Acked-by: Ming Lei Signed-off-by: liu chuansheng Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b44ed35..be5f7aa 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -246,7 +246,6 @@ static void __fw_free_buf(struct kref *ref) __func__, buf->fw_id, buf, buf->data, (unsigned int)buf->size); - spin_lock(&fwc->lock); list_del(&buf->list); spin_unlock(&fwc->lock); @@ -263,7 +262,10 @@ static void __fw_free_buf(struct kref *ref) static void fw_free_buf(struct firmware_buf *buf) { - kref_put(&buf->ref, __fw_free_buf); + struct firmware_cache *fwc = buf->fwc; + spin_lock(&fwc->lock); + if (!kref_put(&buf->ref, __fw_free_buf)) + spin_unlock(&fwc->lock); } /* direct firmware loading support */ -- cgit v1.1 From 60dac5e284fe99751e3beefe1a9cc7a0771ad73c Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Sat, 27 Oct 2012 20:37:10 -0200 Subject: firmware: use noinline_for_stack The comment above fw_file_size() suggests it is noinline for stack size reasons. Use noinline_for_stack to make this more clear. Signed-off-by: Cesar Eduardo Barros Acked-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index be5f7aa..4b04ec4 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -277,7 +277,7 @@ static const char *fw_path[] = { }; /* Don't inline this: 'struct kstat' is biggish */ -static noinline long fw_file_size(struct file *file) +static noinline_for_stack long fw_file_size(struct file *file) { struct kstat st; if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st)) -- cgit v1.1 From 27602842060484b564cd725241b402b0bddfb830 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 3 Nov 2012 17:47:58 +0800 Subject: firmware loader: introduce module parameter to customize(v4) fw search path This patch introduces one module parameter of 'path' in firmware_class to support customizing firmware image search path, so that people can use its own firmware path if the default built-in paths can't meet their demand[1], and the typical usage is passing the below from kernel command parameter when 'firmware_class' is built in kernel: firmware_class.path=$CUSTOMIZED_PATH [1], https://lkml.org/lkml/2012/10/11/337 Cc: Linus Torvalds Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 4b04ec4..7888af7 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -269,13 +269,23 @@ static void fw_free_buf(struct firmware_buf *buf) } /* direct firmware loading support */ -static const char *fw_path[] = { +static char fw_path_para[256]; +static const char * const fw_path[] = { + fw_path_para, "/lib/firmware/updates/" UTS_RELEASE, "/lib/firmware/updates", "/lib/firmware/" UTS_RELEASE, "/lib/firmware" }; +/* + * Typical usage is that passing 'firmware_class.path=$CUSTOMIZED_PATH' + * from kernel command line because firmware_class is generally built in + * kernel instead of module. + */ +module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644); +MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path"); + /* Don't inline this: 'struct kstat' is biggish */ static noinline_for_stack long fw_file_size(struct file *file) { @@ -317,6 +327,11 @@ static bool fw_get_filesystem_firmware(struct firmware_buf *buf) for (i = 0; i < ARRAY_SIZE(fw_path); i++) { struct file *file; + + /* skip the unset customized path */ + if (!fw_path[i][0]) + continue; + snprintf(path, PATH_MAX, "%s/%s", fw_path[i], buf->fw_id); file = filp_open(path, O_RDONLY, 0); -- cgit v1.1 From 6a927857d890658789e6e54b058ef8527de8200a Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 3 Nov 2012 17:48:16 +0800 Subject: firmware loader: document firmware cache mechanism This patch documents the firmware cache mechanism so that users of request_firmware() know that it can be called safely inside device's suspend and resume callback, and the device's firmware needn't be cached any more by individual driver itself to deal with firmware loss during system resume. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 7888af7..d814603 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -988,6 +988,9 @@ err_put_dev: * firmware image for this or any other device. * * Caller must hold the reference count of @device. + * + * The function can be called safely inside device's suspend and + * resume callback. **/ int request_firmware(const struct firmware **firmware_p, const char *name, -- cgit v1.1 From 91e5687805885f9fceb60b95e950a3d3bdcf4764 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 31 Oct 2012 22:45:02 +0100 Subject: ACPI: Add support for platform bus type With ACPI 5 it is now possible to enumerate traditional SoC peripherals, like serial bus controllers and slave devices behind them. These devices are typically based on IP-blocks used in many existing SoC platforms and platform drivers for them may already be present in the kernel tree. To make driver "porting" more straightforward, add ACPI support to the platform bus type. Instead of writing ACPI "glue" drivers for the existing platform drivers, register the platform bus type with ACPI to create platform device objects for the drivers and bind the corresponding ACPI handles to those platform devices. This should allow us to reuse the existing platform drivers for the devices in question with the minimum amount of modifications. This changeset is based on Mika Westerberg's and Mathias Nyman's work. Signed-off-by: Mathias Nyman Signed-off-by: Mika Westerberg Acked-by: Greg Kroah-Hartman Acked-by: H. Peter Anvin Acked-by: Tony Luck Signed-off-by: Rafael J. Wysocki --- drivers/base/platform.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 72c776f..7de29eb 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "base.h" #include "power/power.h" @@ -709,6 +710,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; -- cgit v1.1 From dde8437d06560366d8988c92b5774039ec703864 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Tue, 23 Oct 2012 01:21:49 +0200 Subject: PM / OPP: RCU reclaim synchronize_rcu() blocks the caller of opp_enable/disbale for a complete grace period. This blocking duration prevents any intensive use of the functions. Replace synchronize_rcu() by call_rcu() which will call our function for freeing the old opp element. The duration of opp_enable() and opp_disable() will be no more dependant of the grace period. Signed-off-by: Vincent Guittot Reviewed-by: Paul E. McKenney Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d946864..1211210 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -65,6 +65,7 @@ struct opp { unsigned long u_volt; struct device_opp *dev_opp; + struct rcu_head head; }; /** @@ -442,6 +443,17 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) } /** + * opp_free_rcu() - helper to clear the struct opp when grace period has + * elapsed without blocking the the caller of opp_set_availability + */ +static void opp_free_rcu(struct rcu_head *head) +{ + struct opp *opp = container_of(head, struct opp, head); + + kfree(opp); +} + +/** * opp_set_availability() - helper to set the availability of an opp * @dev: device for which we do this operation * @freq: OPP frequency to modify availability @@ -512,7 +524,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(); + call_rcu(&opp->head, opp_free_rcu); /* Notify the change of the OPP availability */ if (availability_req) @@ -522,13 +534,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; } -- cgit v1.1 From 80126ce7aeb4e187429681ef8a7785b7dcd7a348 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 23 Oct 2012 01:27:44 +0200 Subject: PM / OPP: Export symbols for module usage. Export the OPP functions for use by driver modules. Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org [nm@ti.com: expansion of functions exported] Signed-off-by: Nishanth Menon Signed-off-by: Liam Girdwood Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 1211210..6688886 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -23,6 +23,7 @@ #include #include #include +#include /* * Internal data structure organization with the OPP layer library is as @@ -161,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 @@ -190,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 @@ -222,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 @@ -269,6 +273,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 @@ -311,6 +316,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 @@ -357,6 +363,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 @@ -561,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 @@ -582,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 /** -- cgit v1.1 From 0779726cc265805d0f7c7dd1d791fa4076b31a9a Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 24 Oct 2012 22:00:12 +0200 Subject: PM / OPP: predictable fail results for opp_find* functions, v2 Currently the opp_find* functions return -ENODEV when: a) it cant find a device (e.g. request for an OPP search on device which was not registered) b) When it cant find a match for the search strategy used This makes life a little in-efficient for users such as devfreq to make reasonable judgement before switching search strategies. So, standardize the return results as following: -EINVAL for bad pointer parameters -ENODEV when device cannot be found -ERANGE when search fails This has the following benefit for devfreq implementation: The search fails when an unregistered device pointer is provided. This is a trigger to change the search direction and search for a better fit, however, if we cannot differentiate between a valid search range failure Vs an unregistered device, second search goes through the same fail return condition. This can be avoided by appropriate handling of error return code. With this change, we also fix devfreq for the improved search strategy with updated error code. Signed-off-by: Nishanth Menon Reviewed-by: Kevin Hilman Acked-by: MyungJoo Ham Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 6688886..c8a908b 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -235,7 +235,10 @@ EXPORT_SYMBOL(opp_get_opp_count); * * 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 @@ -254,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)) { @@ -284,7 +287,11 @@ EXPORT_SYMBOL(opp_find_freq_exact); * 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 @@ -295,7 +302,7 @@ EXPORT_SYMBOL(opp_find_freq_exact); 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); @@ -304,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) { @@ -327,7 +334,11 @@ EXPORT_SYMBOL(opp_find_freq_ceil); * 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 @@ -338,7 +349,7 @@ EXPORT_SYMBOL(opp_find_freq_ceil); 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); @@ -347,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) { -- cgit v1.1 From ea83f81b489be3be268ed7fabfe8dd94bdc45a29 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 31 Oct 2012 01:29:17 +0100 Subject: PM / OPP: using kfree_rcu() to simplify the code The callback function of call_rcu() just calls a kfree(), so we can use kfree_rcu() instead of call_rcu() + callback function. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index c8a908b..50b2831 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -461,17 +461,6 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) } /** - * opp_free_rcu() - helper to clear the struct opp when grace period has - * elapsed without blocking the the caller of opp_set_availability - */ -static void opp_free_rcu(struct rcu_head *head) -{ - struct opp *opp = container_of(head, struct opp, head); - - kfree(opp); -} - -/** * opp_set_availability() - helper to set the availability of an opp * @dev: device for which we do this operation * @freq: OPP frequency to modify availability @@ -542,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); - call_rcu(&opp->head, opp_free_rcu); + kfree_rcu(opp, head); /* Notify the change of the OPP availability */ if (availability_req) -- cgit v1.1 From c122f27e1c1bada4cdf19669afed5a00a69bc5a5 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Tue, 23 Oct 2012 01:18:40 +0200 Subject: base: power - use clk_prepare_enable and clk_prepare_disable When PM runtime is enabled in DaVinci and the machine migrates to common clk framework, the clk_enable() gets called without clk_prepare(). This patch is to fix this issue so that PM run time can inter work with common clk framework. Signed-off-by: Murali Karicheri Signed-off-by: Rafael J. Wysocki --- drivers/base/power/clock_ops.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index eb78e96..9d8fde7 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"); } -- cgit v1.1 From 4b6d1f12f9c4e0e420d5747d3ae285d8f66d627f Mon Sep 17 00:00:00 2001 From: LongX Zhang Date: Thu, 25 Oct 2012 00:21:28 +0200 Subject: driver core / PM: move the calling to device_pm_remove behind the calling to bus_remove_device We hit an hang issue when removing a mmc device on Medfield Android phone by sysfs interface. device_pm_remove will call pm_runtime_remove which would disable runtime PM of the device. After that pm_runtime_get* or pm_runtime_put* will be ignored. So if we disable the runtime PM before device really be removed, drivers' _remove callback may access HW even pm_runtime_get* fails. That is bad. Consider below call sequence when removing a device: device_del => device_pm_remove => class_intf->remove_dev(dev, class_intf) => pm_runtime_get_sync/put_sync => bus_remove_device => device_release_driver => pm_runtime_get_sync/put_sync remove_dev might call pm_runtime_get_sync/put_sync. Then, generic device_release_driver also calls pm_runtime_get_sync/put_sync. Since device_del => device_pm_remove firstly, later _get_sync wouldn't really wake up the device. I git log -p to find the patch which moves the calling to device_pm_remove ahead. It's below patch: commit 775b64d2b6ca37697de925f70799c710aab5849a Author: Rafael J. Wysocki Date: Sat Jan 12 20:40:46 2008 +0100 PM: Acquire device locks on suspend This patch reorganizes the way suspend and resume notifications are sent to drivers. The major changes are that now the PM core acquires every device semaphore before calling the methods, and calls to device_add() during suspends will fail, while calls to device_del() during suspends will block. It also provides a way to safely remove a suspended device with the help of the PM core, by using the device_pm_schedule_removal() callback introduced specifically for this purpose, and updates two drivers (msr and cpuid) that need to use it. As device_pm_schedule_removal is deleted by another patch, we need also revert other parts of the patch, i.e. move the calling of device_pm_remove after the calling to bus_remove_device. Signed-off-by: LongX Zhang Acked-by: Greg Kroah-Hartman Signed-off-by: Rafael J. Wysocki --- drivers/base/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index abea76c..150a415 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 -- cgit v1.1 From d6ff85513d523551177b1564b62d64c864b97d68 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 13 Nov 2012 17:47:13 +0100 Subject: driver: platform: fix documentation for platform_get_irq_byname Probably due to copy&paste, some stuff was simply forgotten. Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 1e8f654..d5e3b45 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -122,7 +122,7 @@ struct resource *platform_get_resource_byname(struct platform_device *dev, EXPORT_SYMBOL_GPL(platform_get_resource_byname); /** - * platform_get_irq - get an IRQ for a device + * platform_get_irq_byname - get an IRQ for a device by name * @dev: platform device * @name: IRQ name */ -- cgit v1.1 From 863f9f30e6c1e30cb19a0cd17c5cf8879257dfd7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 21 Nov 2012 00:21:59 +0100 Subject: ACPI / platform: Initialize ACPI handles of platform devices in advance The current platform device creation and registration code in acpi_create_platform_device() is quite convoluted. This function takes an ACPI device node as an argument and eventually calls platform_device_register_resndata() to create and register a platform device object on the basis of the information contained in that code. However, it doesn't associate the new platform device with the ACPI node directly, but instead it relies on acpi_platform_notify(), called from within device_add(), to find that ACPI node again with the help of acpi_platform_find_device() and acpi_platform_match() and then attach the new platform device to it. This causes an additional ACPI namespace walk to happen and is clearly suboptimal. Use the observation that it is now possible to initialize the ACPI handle of a device before calling device_add() for it to make this code more straightforward. Namely, add a new field to struct platform_device_info allowing us to pass the ACPI handle of interest to platform_device_register_full(), which will then use it to initialize the new device's ACPI handle before registering it. This will cause acpi_platform_notify() to use the ACPI handle from the device structure directly instead of using the .find_device() routine provided by the device's bus type. In consequence, acpi_platform_bus, acpi_platform_find_device(), and acpi_platform_match() are not necessary any more, so remove them. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Acked-by: Greg Kroah-Hartman --- drivers/base/platform.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 7de29eb..49fd96e 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -437,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) { /* @@ -467,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: -- cgit v1.1 From 76aad392f75e6ce5be3f106554e16f7ff96543e5 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Tue, 20 Nov 2012 15:20:30 +0100 Subject: regmap: introduce tables for readable/writeable/volatile/precious checks Many of the regmap enabled drivers implementing one or more of the readable, writeable, volatile and precious methods use the same code pattern: return ((reg >= X && reg <= Y) || (reg >= W && reg <= Z) || ...) Switch to a data driven approach, using tables to describe readable/writeable/volatile and precious registers ranges instead. The table based check can still be overridden by passing the usual function pointers via struct regmap_config. Signed-off-by: Davide Ciminaghi Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 4 ++++ drivers/base/regmap/regmap.c | 46 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 2cd01b5..288e135 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -55,6 +55,10 @@ struct regmap { bool (*readable_reg)(struct device *dev, unsigned int reg); bool (*volatile_reg)(struct device *dev, unsigned int reg); bool (*precious_reg)(struct device *dev, unsigned int reg); + const struct regmap_access_table *wr_table; + const struct regmap_access_table *rd_table; + const struct regmap_access_table *volatile_table; + const struct regmap_access_table *precious_table; u8 read_flag_mask; u8 write_flag_mask; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 64eb835..96f7e85 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -34,6 +34,36 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); +bool regmap_reg_in_ranges(unsigned int reg, + const struct regmap_range *ranges, + unsigned int nranges) +{ + const struct regmap_range *r; + int i; + + for (i = 0, r = ranges; i < nranges; i++, r++) + if (regmap_reg_in_range(reg, r)) + return true; + return false; +} +EXPORT_SYMBOL_GPL(regmap_reg_in_ranges); + +static bool _regmap_check_range_table(struct regmap *map, + unsigned int reg, + const struct regmap_access_table *table) +{ + /* Check "no ranges" first */ + if (regmap_reg_in_ranges(reg, table->no_ranges, table->n_no_ranges)) + return false; + + /* In case zero "yes ranges" are supplied, any reg is OK */ + if (!table->n_yes_ranges) + return true; + + return regmap_reg_in_ranges(reg, table->yes_ranges, + table->n_yes_ranges); +} + bool regmap_writeable(struct regmap *map, unsigned int reg) { if (map->max_register && reg > map->max_register) @@ -42,6 +72,9 @@ bool regmap_writeable(struct regmap *map, unsigned int reg) if (map->writeable_reg) return map->writeable_reg(map->dev, reg); + if (map->wr_table) + return _regmap_check_range_table(map, reg, map->wr_table); + return true; } @@ -56,6 +89,9 @@ bool regmap_readable(struct regmap *map, unsigned int reg) if (map->readable_reg) return map->readable_reg(map->dev, reg); + if (map->rd_table) + return _regmap_check_range_table(map, reg, map->rd_table); + return true; } @@ -67,6 +103,9 @@ bool regmap_volatile(struct regmap *map, unsigned int reg) if (map->volatile_reg) return map->volatile_reg(map->dev, reg); + if (map->volatile_table) + return _regmap_check_range_table(map, reg, map->volatile_table); + return true; } @@ -78,6 +117,9 @@ bool regmap_precious(struct regmap *map, unsigned int reg) if (map->precious_reg) return map->precious_reg(map->dev, reg); + if (map->precious_table) + return _regmap_check_range_table(map, reg, map->precious_table); + return false; } @@ -370,6 +412,10 @@ struct regmap *regmap_init(struct device *dev, map->bus = bus; map->bus_context = bus_context; map->max_register = config->max_register; + map->wr_table = config->wr_table; + map->rd_table = config->rd_table; + map->volatile_table = config->volatile_table; + map->precious_table = config->precious_table; map->writeable_reg = config->writeable_reg; map->readable_reg = config->readable_reg; map->volatile_reg = config->volatile_reg; -- cgit v1.1 From a7227a0faa117d0bc532aea546ae5ac5f89e8ed7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 23 Nov 2012 20:55:06 +0100 Subject: PM / QoS: fix wrong error-checking condition dev_pm_qos_add_request() can return 0, 1, or a negative error code, therefore the correct error test is "if (error < 0)." Checking just for non-zero return code leads to erroneous setting of the req->dev pointer to NULL, which then leads to a repeated call to dev_pm_qos_add_ancestor_request() in st1232_ts_irq_handler(). This in turn leads to an Oops, when the I2C host adapter is unloaded and reloaded again because of the inconsistent state of its QoS request list. Signed-off-by: Guennadi Liakhovetski Cc: Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 74a67e0..fbbd4ed 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -451,7 +451,7 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, if (ancestor) error = dev_pm_qos_add_request(ancestor, req, value); - if (error) + if (error < 0) req->dev = NULL; return error; -- cgit v1.1 From 35546bd477146b75ae2a9ff2cb9bfcdb0f701015 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 24 Nov 2012 10:10:51 +0100 Subject: PM / QoS: Handle device PM QoS flags while removing constraints PM QoS flags have to be handled by dev_pm_qos_constraints_destroy() in the same way as PM QoS resume latency constraints. That is, if they have been exposed to user space, they have to be hidden from it and the list of flags requests has to be flushed before destroying the device's PM QoS object. Make that happen. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index fdc3894..f03f1ba 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -223,12 +223,14 @@ 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); @@ -237,8 +239,8 @@ void dev_pm_qos_constraints_destroy(struct device *dev) if (!qos) goto out; + /* Flush the constraints lists for the device. */ c = &qos->latency; - /* Flush the constraints list for the device */ plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { /* * Update constraints list and call the notification @@ -247,6 +249,11 @@ 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.qos = NULL; -- cgit v1.1 From 0246c4fafccd6c3580bb33e8d30a2c7927e09f02 Mon Sep 17 00:00:00 2001 From: ShuoX Liu Date: Fri, 23 Nov 2012 15:14:12 +0800 Subject: driver core: use initcall_debug to control shutdown info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit syscore_shutdown uses initcall_debug to control the debug info output. It’s a good programming. But device_shutdown doesn’t. The patch changes device_shutdown to follow the style. Signed-off-by: Yanmin Zhang Signed-off-by: ShuoX Liu Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index abea76c..985f69c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1840,10 +1840,12 @@ void device_shutdown(void) pm_runtime_barrier(dev); if (dev->bus && dev->bus->shutdown) { - dev_dbg(dev, "shutdown\n"); + if (initcall_debug) + dev_info(dev, "shutdown\n"); dev->bus->shutdown(dev); } else if (dev->driver && dev->driver->shutdown) { - dev_dbg(dev, "shutdown\n"); + if (initcall_debug) + dev_info(dev, "shutdown\n"); dev->driver->shutdown(dev); } -- cgit v1.1 From 93058424af5c555f28175d41384d799f34b4f07e Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 18 Nov 2012 21:27:55 -0800 Subject: drivers/base/core.c: Mark to_root_device static Nothing outside of drivers/base/core.c references this function. Signed-off-by: Josh Triplett Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 985f69c..442e5d3 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1399,7 +1399,7 @@ struct root_device { struct module *owner; }; -inline struct root_device *to_root_device(struct device *d) +static inline struct root_device *to_root_device(struct device *d) { return container_of(d, struct root_device, dev); } -- cgit v1.1 From c6c22955f80f2db9614b01fe5a3d1cfcd8b3d848 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 26 Nov 2012 10:41:48 -0300 Subject: [media] dma-mapping: fix dma_common_get_sgtable() conditional compilation dma_common_get_sgtable() function doesn't depend on ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY, so it must not be compiled conditionally. Reported-by: Stephen Rothwell Signed-off-by: Marek Szyprowski Acked-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/base/dma-mapping.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c index 3fbedc7..0ce39a3 100644 --- a/drivers/base/dma-mapping.c +++ b/drivers/base/dma-mapping.c @@ -218,6 +218,8 @@ void dmam_release_declared_memory(struct device *dev) } EXPORT_SYMBOL(dmam_release_declared_memory); +#endif + /* * Create scatter-list for the already allocated DMA buffer. */ @@ -236,8 +238,6 @@ int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, } EXPORT_SYMBOL(dma_common_get_sgtable); -#endif - /* * Create userspace mapping for the DMA-coherent memory. */ -- cgit v1.1 From 94d76d5de38d7502c3e78fcd6bf50da95e3e0361 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 26 Nov 2012 10:04:53 +0100 Subject: platform / ACPI: Attach/detach ACPI PM during probe/remove/shutdown Drivers usually expect that the devices they are supposed to handle will be operational when their .probe() routines are called, but that need not be the case on some ACPI-based systems with ACPI-based device enumeration where the BIOSes don't put devices into D0 by default. To work around this problem it is sufficient to change bus type .probe() routines to ensure that devices will be powered on before the drivers' .probe() routines run (and their .remove() and .shutdown() routines accordingly). Modify platform_drv_probe() to run acpi_dev_pm_attach() for devices whose ACPI handles are present, so that ACPI power management is used to change their power states. Analogously, modify platform_drv_remove() and platform_drv_shutdown() to call acpi_dev_pm_detach() for those devices, so that they are not subject to ACPI PM any more. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Reviewed-by: Mika Westerberg Tested-by: Mika Westerberg --- drivers/base/platform.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 49fd96e..b2ee3bc 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -484,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; - return drv->probe(dev); + if (ACPI_HANDLE(_dev)) + acpi_dev_pm_attach(_dev, true); + + 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) @@ -497,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) @@ -507,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); } /** -- cgit v1.1 From a42d1e31d4a2380cf1bda8e3510ff1859ddf55a5 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Mon, 19 Nov 2012 13:19:18 -0500 Subject: driver core: remove CONFIG_HOTPLUG ifdefs Remove conditional code based on CONFIG_HOTPLUG being false. It's always on now in preparation of it going away as an option. Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 181ed26..24eb078 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -164,8 +164,6 @@ static const struct kset_uevent_ops bus_uevent_ops = { static struct kset *bus_kset; - -#ifdef CONFIG_HOTPLUG /* Manually detach a device from its associated driver. */ static ssize_t driver_unbind(struct device_driver *drv, const char *buf, size_t count) @@ -252,7 +250,6 @@ static ssize_t store_drivers_probe(struct bus_type *bus, return -EINVAL; return count; } -#endif static struct device *next_device(struct klist_iter *i) { @@ -618,11 +615,6 @@ static void driver_remove_attrs(struct bus_type *bus, } } -#ifdef CONFIG_HOTPLUG -/* - * Thanks to drivers making their tables __devinit, we can't allow manual - * bind and unbind from userspace unless CONFIG_HOTPLUG is enabled. - */ static int __must_check add_bind_files(struct device_driver *drv) { int ret; @@ -666,12 +658,6 @@ static void remove_probe_files(struct bus_type *bus) bus_remove_file(bus, &bus_attr_drivers_autoprobe); bus_remove_file(bus, &bus_attr_drivers_probe); } -#else -static inline int add_bind_files(struct device_driver *drv) { return 0; } -static inline void remove_bind_files(struct device_driver *drv) {} -static inline int add_probe_files(struct bus_type *bus) { return 0; } -static inline void remove_probe_files(struct bus_type *bus) {} -#endif static ssize_t driver_uevent_store(struct device_driver *drv, const char *buf, size_t count) -- cgit v1.1 From cbc1938badc31f43ab77e92a9b1a51c4fe8b4113 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 6 Dec 2012 13:29:05 +0900 Subject: regmap: Cache register and value sizes for debugfs No point in calculating them every time. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 4 ++++ drivers/base/regmap/regmap-debugfs.c | 24 ++++++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index ac869d2..1abcd27 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -50,6 +50,10 @@ struct regmap { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; const char *debugfs_name; + + unsigned int debugfs_reg_len; + unsigned int debugfs_val_len; + unsigned int debugfs_tot_len; #endif unsigned int max_register; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index f4b9dd0..00fbd58 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -60,7 +60,6 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, unsigned int to, char __user *user_buf, size_t count, loff_t *ppos) { - int reg_len, val_len, tot_len; size_t buf_pos = 0; loff_t p = 0; ssize_t ret; @@ -76,9 +75,13 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, return -ENOMEM; /* Calculate the length of a fixed format */ - reg_len = regmap_calc_reg_len(map->max_register, buf, count); - val_len = 2 * map->format.val_bytes; - tot_len = reg_len + val_len + 3; /* : \n */ + if (!map->debugfs_tot_len) { + map->debugfs_reg_len = regmap_calc_reg_len(map->max_register, + buf, count); + map->debugfs_val_len = 2 * map->format.val_bytes; + map->debugfs_tot_len = map->debugfs_reg_len + + map->debugfs_val_len + 3; /* : \n */ + } for (i = from; i <= to; i += map->reg_stride) { if (!regmap_readable(map, i)) @@ -90,26 +93,27 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, /* If we're in the region the user is trying to read */ if (p >= *ppos) { /* ...but not beyond it */ - if (buf_pos >= count - 1 - tot_len) + if (buf_pos >= count - 1 - map->debugfs_tot_len) break; /* Format the register */ snprintf(buf + buf_pos, count - buf_pos, "%.*x: ", - reg_len, i - from); - buf_pos += reg_len + 2; + map->debugfs_reg_len, i - from); + buf_pos += map->debugfs_reg_len + 2; /* Format the value, write all X if we can't read */ ret = regmap_read(map, i, &val); if (ret == 0) snprintf(buf + buf_pos, count - buf_pos, - "%.*x", val_len, val); + "%.*x", map->debugfs_val_len, val); else - memset(buf + buf_pos, 'X', val_len); + memset(buf + buf_pos, 'X', + map->debugfs_val_len); buf_pos += 2 * map->format.val_bytes; buf[buf_pos++] = '\n'; } - p += tot_len; + p += map->debugfs_tot_len; } ret = buf_pos; -- cgit v1.1 From db04328c167ff8e7c57f4a3532214aeada3a82fd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 11 Dec 2012 01:14:11 +0900 Subject: regmap: debugfs: Avoid overflows for very small reads If count is less than the size of a register then we may hit integer wraparound when trying to move backwards to check if we're still in the buffer. Instead move the position forwards to check if it's still in the buffer, we are unlikely to be able to allocate a buffer sufficiently big to overflow here. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/base/regmap/regmap-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 00fbd58..3df274e 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -93,7 +93,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, /* If we're in the region the user is trying to read */ if (p >= *ppos) { /* ...but not beyond it */ - if (buf_pos >= count - 1 - map->debugfs_tot_len) + if (buf_pos + 1 + map->debugfs_tot_len >= count) break; /* Format the register */ -- cgit v1.1 From afab2f7b21b042bcbffb1e82f78243382a122d70 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 9 Dec 2012 17:20:10 +0900 Subject: regmap: debugfs: Factor out initial seek In preparation for doing things a bit more quickly than a linear scan factor out the initial seek from the debugfs register dump. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 39 +++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 3df274e..749a1dc 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -56,16 +56,46 @@ static const struct file_operations regmap_name_fops = { .llseek = default_llseek, }; +/* + * Work out where the start offset maps into register numbers, bearing + * in mind that we suppress hidden registers. + */ +static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, + unsigned int base, + loff_t from, + loff_t *pos) +{ + loff_t p = *pos; + unsigned int i; + + for (i = base; i <= map->max_register; i += map->reg_stride) { + if (!regmap_readable(map, i)) + continue; + + if (regmap_precious(map, i)) + continue; + + if (i >= from) { + *pos = p; + return i; + } + + p += map->debugfs_tot_len; + } + + return base; +} + static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, unsigned int to, char __user *user_buf, size_t count, loff_t *ppos) { size_t buf_pos = 0; - loff_t p = 0; + loff_t p = *ppos; ssize_t ret; int i; char *buf; - unsigned int val; + unsigned int val, start_reg; if (*ppos < 0 || !count) return -EINVAL; @@ -83,7 +113,10 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, map->debugfs_val_len + 3; /* : \n */ } - for (i = from; i <= to; i += map->reg_stride) { + /* Work out which register we're starting at */ + start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p); + + for (i = start_reg; i <= to; i += map->reg_stride) { if (!regmap_readable(map, i)) continue; -- cgit v1.1 From 5166b7c006eeb4f6becc0822974d8da259484ba1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 11 Dec 2012 01:24:29 +0900 Subject: regmap: debugfs: Cache offsets of valid regions for dump Avoid doing a linear scan of the entire register map for each read() of the debugfs register dump by recording the offsets where valid registers exist when we first read the registers file. This assumes the set of valid registers never changes, if this is not the case invalidation of the cache will be required. This could be further improved for large blocks of contiguous registers by calculating the register we will read from within the block - currently we do a linear scan of the block. An rbtree may also be worthwhile. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 10 ++++++ drivers/base/regmap/regmap-debugfs.c | 67 ++++++++++++++++++++++++++++-------- 2 files changed, 63 insertions(+), 14 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 1abcd27..9c3b0e7 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -15,10 +15,18 @@ #include #include +#include struct regmap; struct regcache_ops; +struct regmap_debugfs_off_cache { + struct list_head list; + off_t min; + off_t max; + unsigned int base_reg; +}; + struct regmap_format { size_t buf_size; size_t reg_bytes; @@ -54,6 +62,8 @@ struct regmap { unsigned int debugfs_reg_len; unsigned int debugfs_val_len; unsigned int debugfs_tot_len; + + struct list_head debugfs_off_cache; #endif unsigned int max_register; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 749a1dc..07aad78 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -65,25 +65,53 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, loff_t from, loff_t *pos) { - loff_t p = *pos; - unsigned int i; - - for (i = base; i <= map->max_register; i += map->reg_stride) { - if (!regmap_readable(map, i)) - continue; - - if (regmap_precious(map, i)) - continue; + struct regmap_debugfs_off_cache *c = NULL; + loff_t p = 0; + unsigned int i, ret; + + /* + * If we don't have a cache build one so we don't have to do a + * linear scan each time. + */ + if (list_empty(&map->debugfs_off_cache)) { + for (i = base; i <= map->max_register; i += map->reg_stride) { + /* Skip unprinted registers, closing off cache entry */ + if (!regmap_readable(map, i) || + regmap_precious(map, i)) { + if (c) { + c->max = p - 1; + list_add_tail(&c->list, + &map->debugfs_off_cache); + c = NULL; + } + + continue; + } + + /* No cache entry? Start a new one */ + if (!c) { + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + break; + c->min = p; + c->base_reg = i; + } + + p += map->debugfs_tot_len; + } + } - if (i >= from) { - *pos = p; - return i; + /* Find the relevant block */ + list_for_each_entry(c, &map->debugfs_off_cache, list) { + if (*pos >= c->min && *pos <= c->max) { + *pos = c->min; + return c->base_reg; } - p += map->debugfs_tot_len; + ret = c->max; } - return base; + return ret; } static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, @@ -309,6 +337,8 @@ void regmap_debugfs_init(struct regmap *map, const char *name) struct rb_node *next; struct regmap_range_node *range_node; + INIT_LIST_HEAD(&map->debugfs_off_cache); + if (name) { map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", dev_name(map->dev), name); @@ -357,7 +387,16 @@ void regmap_debugfs_init(struct regmap *map, const char *name) void regmap_debugfs_exit(struct regmap *map) { + struct regmap_debugfs_off_cache *c; + debugfs_remove_recursive(map->debugfs); + while (!list_empty(&map->debugfs_off_cache)) { + c = list_first_entry(&map->debugfs_off_cache, + struct regmap_debugfs_off_cache, + list); + list_del(&c->list); + kfree(c); + } kfree(map->debugfs_name); } -- cgit v1.1 From 4009793e15d44469da1547a46ab129cc08ffa503 Mon Sep 17 00:00:00 2001 From: Vitaly Andrianov Date: Wed, 5 Dec 2012 09:29:25 -0500 Subject: drivers: cma: represent physical addresses as phys_addr_t This commit changes the CMA early initialization code to use phys_addr_t for representing physical addresses instead of unsigned long. Without this change, among other things, dma_declare_contiguous() simply discards any memory regions whose address is not representable as unsigned long. This is a problem on 32-bit PAE machines where unsigned long is 32-bit but physical address space is larger. Signed-off-by: Vitaly Andrianov Signed-off-by: Cyril Chemparathy Acked-by: Michal Nazarewicz Signed-off-by: Marek Szyprowski --- drivers/base/dma-contiguous.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 612afcc..0ca5442 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -57,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) { @@ -70,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; @@ -88,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; } @@ -106,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); @@ -126,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); } @@ -227,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, @@ -268,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; } @@ -285,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; } -- cgit v1.1 From fa7194eb99b8e9fefe96f045002648ffb55f53c0 Mon Sep 17 00:00:00 2001 From: Yasuaki Ishimatsu Date: Tue, 11 Dec 2012 16:00:44 -0800 Subject: memory hotplug: suppress "Device memoryX does not have a release() function" warning When calling remove_memory_block(), the function shows following message at device_release(). "Device 'memory528' does not have a release() function, it is broken and must be fixed." The reason is memory_block's device struct does not have a release() function. So the patch registers memory_block_release() to the device's release() function for suppressing the warning message. Additionally, the patch moves kfree(mem) into the release function since the release function is prepared as a means to free a memory_block struct. Signed-off-by: Yasuaki Ishimatsu Acked-by: David Rientjes Cc: Jiang Liu Cc: Minchan Kim Acked-by: KOSAKI Motohiro Cc: Wen Congyang Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/memory.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 86c8821..7eb1211 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -70,6 +70,13 @@ void unregister_memory_isolate_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(unregister_memory_isolate_notifier); +static void memory_block_release(struct device *dev) +{ + struct memory_block *mem = container_of(dev, struct memory_block, dev); + + kfree(mem); +} + /* * register_memory - Setup a sysfs device for a memory block */ @@ -80,6 +87,7 @@ int register_memory(struct memory_block *memory) memory->dev.bus = &memory_subsys; memory->dev.id = memory->start_section_nr / sections_per_block; + memory->dev.release = memory_block_release; error = device_register(&memory->dev); return error; @@ -635,7 +643,6 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, mem_remove_simple_file(mem, phys_device); mem_remove_simple_file(mem, removable); unregister_memory(mem); - kfree(mem); } else kobject_put(&mem->dev.kobj); -- cgit v1.1 From 8732794b166196cc501c2ddd9e7c97cf45ab64c5 Mon Sep 17 00:00:00 2001 From: Wen Congyang Date: Tue, 11 Dec 2012 16:00:56 -0800 Subject: numa: convert static memory to dynamically allocated memory for per node device We use a static array to store struct node. In many cases, we don't have too many nodes, and some memory will be unused. Convert it to per-device dynamically allocated memory. Signed-off-by: Wen Congyang Cc: David Rientjes Cc: Jiang Liu Cc: Minchan Kim Cc: KOSAKI Motohiro Cc: Yasuaki Ishimatsu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/node.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/node.c b/drivers/base/node.c index af1a177..28216ce 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -306,7 +306,7 @@ void unregister_node(struct node *node) device_unregister(&node->dev); } -struct node node_devices[MAX_NUMNODES]; +struct node *node_devices[MAX_NUMNODES]; /* * register cpu under node @@ -323,15 +323,15 @@ int register_cpu_under_node(unsigned int cpu, unsigned int nid) if (!obj) return 0; - ret = sysfs_create_link(&node_devices[nid].dev.kobj, + ret = sysfs_create_link(&node_devices[nid]->dev.kobj, &obj->kobj, kobject_name(&obj->kobj)); if (ret) return ret; return sysfs_create_link(&obj->kobj, - &node_devices[nid].dev.kobj, - kobject_name(&node_devices[nid].dev.kobj)); + &node_devices[nid]->dev.kobj, + kobject_name(&node_devices[nid]->dev.kobj)); } int unregister_cpu_under_node(unsigned int cpu, unsigned int nid) @@ -345,10 +345,10 @@ int unregister_cpu_under_node(unsigned int cpu, unsigned int nid) if (!obj) return 0; - sysfs_remove_link(&node_devices[nid].dev.kobj, + sysfs_remove_link(&node_devices[nid]->dev.kobj, kobject_name(&obj->kobj)); sysfs_remove_link(&obj->kobj, - kobject_name(&node_devices[nid].dev.kobj)); + kobject_name(&node_devices[nid]->dev.kobj)); return 0; } @@ -390,15 +390,15 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid) continue; if (page_nid != nid) continue; - ret = sysfs_create_link_nowarn(&node_devices[nid].dev.kobj, + ret = sysfs_create_link_nowarn(&node_devices[nid]->dev.kobj, &mem_blk->dev.kobj, kobject_name(&mem_blk->dev.kobj)); if (ret) return ret; return sysfs_create_link_nowarn(&mem_blk->dev.kobj, - &node_devices[nid].dev.kobj, - kobject_name(&node_devices[nid].dev.kobj)); + &node_devices[nid]->dev.kobj, + kobject_name(&node_devices[nid]->dev.kobj)); } /* mem section does not span the specified node */ return 0; @@ -431,10 +431,10 @@ int unregister_mem_sect_under_nodes(struct memory_block *mem_blk, continue; if (node_test_and_set(nid, *unlinked_nodes)) continue; - sysfs_remove_link(&node_devices[nid].dev.kobj, + sysfs_remove_link(&node_devices[nid]->dev.kobj, kobject_name(&mem_blk->dev.kobj)); sysfs_remove_link(&mem_blk->dev.kobj, - kobject_name(&node_devices[nid].dev.kobj)); + kobject_name(&node_devices[nid]->dev.kobj)); } NODEMASK_FREE(unlinked_nodes); return 0; @@ -500,7 +500,7 @@ static void node_hugetlb_work(struct work_struct *work) static void init_node_hugetlb_work(int nid) { - INIT_WORK(&node_devices[nid].node_work, node_hugetlb_work); + INIT_WORK(&node_devices[nid]->node_work, node_hugetlb_work); } static int node_memory_callback(struct notifier_block *self, @@ -517,7 +517,7 @@ static int node_memory_callback(struct notifier_block *self, * when transitioning to/from memoryless state. */ if (nid != NUMA_NO_NODE) - schedule_work(&node_devices[nid].node_work); + schedule_work(&node_devices[nid]->node_work); break; case MEM_GOING_ONLINE: @@ -558,9 +558,13 @@ int register_one_node(int nid) struct node *parent = NULL; if (p_node != nid) - parent = &node_devices[p_node]; + parent = node_devices[p_node]; - error = register_node(&node_devices[nid], nid, parent); + node_devices[nid] = kzalloc(sizeof(struct node), GFP_KERNEL); + if (!node_devices[nid]) + return -ENOMEM; + + error = register_node(node_devices[nid], nid, parent); /* link cpu under this node */ for_each_present_cpu(cpu) { @@ -581,7 +585,9 @@ int register_one_node(int nid) void unregister_one_node(int nid) { - unregister_node(&node_devices[nid]); + unregister_node(node_devices[nid]); + kfree(node_devices[nid]); + node_devices[nid] = NULL; } /* -- cgit v1.1 From 8c7b5b4ed948d1ddf9672ee932a16750b280822a Mon Sep 17 00:00:00 2001 From: Yasuaki Ishimatsu Date: Tue, 11 Dec 2012 16:00:57 -0800 Subject: memory-hotplug: suppress "Device nodeX does not have a release() function" warning When calling unregister_node(), the function shows following message at device_release(). "Device 'node2' does not have a release() function, it is broken and must be fixed." The reason is node's device struct does not have a release() function. So the patch registers node_device_release() to the device's release() function for suppressing the warning message. Additionally, the patch adds memset() to initialize a node struct into register_node(). Because the node struct is part of node_devices[] array and it cannot be freed by node_device_release(). So if system reuses the node struct, it has a garbage. Signed-off-by: Yasuaki Ishimatsu Signed-off-by: Wen Congyang Cc: David Rientjes Cc: Jiang Liu Cc: Minchan Kim Cc: KOSAKI Motohiro Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/node.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/node.c b/drivers/base/node.c index 28216ce..4282e82 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -252,6 +252,24 @@ static inline void hugetlb_register_node(struct node *node) {} static inline void hugetlb_unregister_node(struct node *node) {} #endif +static void node_device_release(struct device *dev) +{ + struct node *node = to_node(dev); + +#if defined(CONFIG_MEMORY_HOTPLUG_SPARSE) && defined(CONFIG_HUGETLBFS) + /* + * We schedule the work only when a memory section is + * onlined/offlined on this node. When we come here, + * all the memory on this node has been offlined, + * so we won't enqueue new work to this work. + * + * The work is using node->node_work, so we should + * flush work before freeing the memory. + */ + flush_work(&node->node_work); +#endif + kfree(node); +} /* * register_node - Setup a sysfs device for a node. @@ -265,6 +283,7 @@ int register_node(struct node *node, int num, struct node *parent) node->dev.id = num; node->dev.bus = &node_subsys; + node->dev.release = node_device_release; error = device_register(&node->dev); if (!error){ @@ -586,7 +605,6 @@ int register_one_node(int nid) void unregister_one_node(int nid) { unregister_node(node_devices[nid]); - kfree(node_devices[nid]); node_devices[nid] = NULL; } -- cgit v1.1 From fa264375175a382621c5344a6508e02ec4d1c3c0 Mon Sep 17 00:00:00 2001 From: Yasuaki Ishimatsu Date: Tue, 11 Dec 2012 16:02:52 -0800 Subject: mm: cleanup register_node() register_node() is defined as extern in include/linux/node.h. But the function is only called from register_one_node() in driver/base/node.c. So the patch defines register_node() as static. Signed-off-by: Yasuaki Ishimatsu Acked-by: David Rientjes Acked-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/node.c b/drivers/base/node.c index 4282e82..fffed4c 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -277,7 +277,7 @@ static void node_device_release(struct device *dev) * * Initialize and register the node device. */ -int register_node(struct node *node, int num, struct node *parent) +static int register_node(struct node *node, int num, struct node *parent) { int error; -- cgit v1.1 From fcf07d22f089856631b52a75c35ba3c33b70a1b4 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Tue, 11 Dec 2012 16:03:13 -0800 Subject: drivers/base/node.c: cleanup node_state_attr[] use [index] = init_value use N_xxxxx instead of hardcode. Make it more readability and easier to add new state. Signed-off-by: Lai Jiangshan Signed-off-by: Wen Congyang Acked-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/node.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/node.c b/drivers/base/node.c index fffed4c..294e316 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -638,23 +638,23 @@ static ssize_t show_node_state(struct device *dev, { __ATTR(name, 0444, show_node_state, NULL), state } static struct node_attr node_state_attr[] = { - _NODE_ATTR(possible, N_POSSIBLE), - _NODE_ATTR(online, N_ONLINE), - _NODE_ATTR(has_normal_memory, N_NORMAL_MEMORY), - _NODE_ATTR(has_cpu, N_CPU), + [N_POSSIBLE] = _NODE_ATTR(possible, N_POSSIBLE), + [N_ONLINE] = _NODE_ATTR(online, N_ONLINE), + [N_NORMAL_MEMORY] = _NODE_ATTR(has_normal_memory, N_NORMAL_MEMORY), #ifdef CONFIG_HIGHMEM - _NODE_ATTR(has_high_memory, N_HIGH_MEMORY), + [N_HIGH_MEMORY] = _NODE_ATTR(has_high_memory, N_HIGH_MEMORY), #endif + [N_CPU] = _NODE_ATTR(has_cpu, N_CPU), }; static struct attribute *node_state_attrs[] = { - &node_state_attr[0].attr.attr, - &node_state_attr[1].attr.attr, - &node_state_attr[2].attr.attr, - &node_state_attr[3].attr.attr, + &node_state_attr[N_POSSIBLE].attr.attr, + &node_state_attr[N_ONLINE].attr.attr, + &node_state_attr[N_NORMAL_MEMORY].attr.attr, #ifdef CONFIG_HIGHMEM - &node_state_attr[4].attr.attr, + &node_state_attr[N_HIGH_MEMORY].attr.attr, #endif + &node_state_attr[N_CPU].attr.attr, NULL }; -- cgit v1.1 From 511c2aba8f07fc45bdcba548cb63f7b8a450c6dc Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Tue, 11 Dec 2012 16:03:16 -0800 Subject: mm, memory-hotplug: dynamic configure movable memory and portion memory Add online_movable and online_kernel for logic memory hotplug. This is the dynamic version of "movablecore" & "kernelcore". We have the same reason to introduce it as to introduce "movablecore" & "kernelcore". It has the same motive as "movablecore" & "kernelcore", but it is dynamic/running-time: o We can configure memory as kernelcore or movablecore after boot. Userspace workload is increased, we need more hugepage, we can't use "online_movable" to add memory and allow the system use more THP(transparent-huge-page), vice-verse when kernel workload is increase. Also help for virtualization to dynamic configure host/guest's memory, to save/(reduce waste) memory. Memory capacity on Demand o When a new node is physically online after boot, we need to use "online_movable" or "online_kernel" to configure/portion it as we expected when we logic-online it. This configuration also helps for physically-memory-migrate. o all benefit as the same as existed "movablecore" & "kernelcore". o Preparing for movable-node, which is very important for power-saving, hardware partitioning and high-available-system(hardware fault management). (Note, we don't introduce movable-node here.) Action behavior: When a memoryblock/memorysection is onlined by "online_movable", the kernel will not have directly reference to the page of the memoryblock, thus we can remove that memory any time when needed. When it is online by "online_kernel", the kernel can use it. When it is online by "online", the zone type doesn't changed. Current constraints: Only the memoryblock which is adjacent to the ZONE_MOVABLE can be online from ZONE_NORMAL to ZONE_MOVABLE. [akpm@linux-foundation.org: use min_t, cleanups] Signed-off-by: Lai Jiangshan Signed-off-by: Wen Congyang Cc: Yasuaki Ishimatsu Cc: Lai Jiangshan Cc: Jiang Liu Cc: KOSAKI Motohiro Cc: Minchan Kim Cc: Mel Gorman Cc: David Rientjes Cc: Yinghai Lu Cc: Rusty Russell Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/memory.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 7eb1211..987604d 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -254,7 +254,7 @@ static bool pages_correctly_reserved(unsigned long start_pfn, * OK to have direct references to sparsemem variables in here. */ static int -memory_block_action(unsigned long phys_index, unsigned long action) +memory_block_action(unsigned long phys_index, unsigned long action, int online_type) { unsigned long start_pfn; unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; @@ -269,7 +269,7 @@ memory_block_action(unsigned long phys_index, unsigned long action) if (!pages_correctly_reserved(start_pfn, nr_pages)) return -EBUSY; - ret = online_pages(start_pfn, nr_pages); + ret = online_pages(start_pfn, nr_pages, online_type); break; case MEM_OFFLINE: ret = offline_pages(start_pfn, nr_pages); @@ -284,7 +284,8 @@ memory_block_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) + unsigned long to_state, unsigned long from_state_req, + int online_type) { int ret = 0; @@ -296,7 +297,7 @@ static int __memory_block_change_state(struct memory_block *mem, if (to_state == MEM_OFFLINE) mem->state = MEM_GOING_OFFLINE; - ret = memory_block_action(mem->start_section_nr, to_state); + ret = memory_block_action(mem->start_section_nr, to_state, online_type); if (ret) { mem->state = from_state_req; @@ -319,12 +320,14 @@ out: } static int memory_block_change_state(struct memory_block *mem, - unsigned long to_state, unsigned long from_state_req) + unsigned long to_state, unsigned long from_state_req, + int online_type) { int ret; mutex_lock(&mem->state_mutex); - ret = __memory_block_change_state(mem, to_state, from_state_req); + ret = __memory_block_change_state(mem, to_state, from_state_req, + online_type); mutex_unlock(&mem->state_mutex); return ret; @@ -338,10 +341,18 @@ store_mem_state(struct device *dev, mem = container_of(dev, struct memory_block, dev); - if (!strncmp(buf, "online", min((int)count, 6))) - ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); - else if(!strncmp(buf, "offline", min((int)count, 7))) - ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); + if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) + ret = memory_block_change_state(mem, MEM_ONLINE, + MEM_OFFLINE, ONLINE_KERNEL); + else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) + ret = memory_block_change_state(mem, MEM_ONLINE, + MEM_OFFLINE, ONLINE_MOVABLE); + else if (!strncmp(buf, "online", min_t(int, count, 6))) + ret = memory_block_change_state(mem, MEM_ONLINE, + MEM_OFFLINE, ONLINE_KEEP); + else if(!strncmp(buf, "offline", min_t(int, count, 7))) + ret = memory_block_change_state(mem, MEM_OFFLINE, + MEM_ONLINE, -1); if (ret) return ret; @@ -676,7 +687,7 @@ int offline_memory_block(struct memory_block *mem) mutex_lock(&mem->state_mutex); if (mem->state != MEM_OFFLINE) - ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); + ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); mutex_unlock(&mem->state_mutex); return ret; -- cgit v1.1 From 8cebfcd074a3044780f3f9af236fc8534d89e55e Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Wed, 12 Dec 2012 13:51:36 -0800 Subject: hugetlb: use N_MEMORY instead N_HIGH_MEMORY N_HIGH_MEMORY stands for the nodes that has normal or high memory. N_MEMORY stands for the nodes that has any memory. The code here need to handle with the nodes which have memory, we should use N_MEMORY instead. Signed-off-by: Lai Jiangshan Acked-by: Hillf Danton Signed-off-by: Wen Congyang Cc: Christoph Lameter Cc: Hillf Danton Cc: Lin Feng Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/node.c b/drivers/base/node.c index 294e316..49dbe7d 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -227,7 +227,7 @@ static node_registration_func_t __hugetlb_unregister_node; static inline bool hugetlb_register_node(struct node *node) { if (__hugetlb_register_node && - node_state(node->dev.id, N_HIGH_MEMORY)) { + node_state(node->dev.id, N_MEMORY)) { __hugetlb_register_node(node); return true; } -- cgit v1.1 From 20b2f52b73febce476fc9376f0296c1aa0e4f5a7 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Wed, 12 Dec 2012 13:52:00 -0800 Subject: numa: add CONFIG_MOVABLE_NODE for movable-dedicated node We need a node which only contains movable memory. This feature is very important for node hotplug. If a node has normal/highmem, the memory may be used by the kernel and can't be offlined. If the node only contains movable memory, we can offline the memory and the node. All are prepared, we can actually introduce N_MEMORY. add CONFIG_MOVABLE_NODE make we can use it for movable-dedicated node [akpm@linux-foundation.org: fix Kconfig text] Signed-off-by: Lai Jiangshan Tested-by: Yasuaki Ishimatsu Signed-off-by: Wen Congyang Cc: Jiang Liu Cc: KOSAKI Motohiro Cc: Minchan Kim Cc: Mel Gorman Cc: David Rientjes Cc: Yinghai Lu Cc: Rusty Russell Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/node.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/node.c b/drivers/base/node.c index 49dbe7d..fac124a 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -644,6 +644,9 @@ static struct node_attr node_state_attr[] = { #ifdef CONFIG_HIGHMEM [N_HIGH_MEMORY] = _NODE_ATTR(has_high_memory, N_HIGH_MEMORY), #endif +#ifdef CONFIG_MOVABLE_NODE + [N_MEMORY] = _NODE_ATTR(has_memory, N_MEMORY), +#endif [N_CPU] = _NODE_ATTR(has_cpu, N_CPU), }; @@ -654,6 +657,9 @@ static struct attribute *node_state_attrs[] = { #ifdef CONFIG_HIGHMEM &node_state_attr[N_HIGH_MEMORY].attr.attr, #endif +#ifdef CONFIG_MOVABLE_NODE + &node_state_attr[N_MEMORY].attr.attr, +#endif &node_state_attr[N_CPU].attr.attr, NULL }; -- cgit v1.1 From b6fa0cd62c5b9d47f8e5d42cb2876677a5ed701e Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 28 Sep 2012 09:29:43 +0200 Subject: dma-buf: might_sleep() in dma_buf_unmap_attachment() We never really clarified if unmap could be done in atomic context. But since mapping might require sleeping, this implies mutex in use to synchronize mapping/unmapping, so unmap could sleep as well. Add a might_sleep() to clarify this. Signed-off-by: Rob Clark Acked-by: Daniel Vetter Reviewed-by: Maarten Lankhorst Signed-off-by: Sumit Semwal --- drivers/base/dma-buf.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index 460e22d..a3f79c4 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c @@ -298,6 +298,8 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, struct sg_table *sg_table, enum dma_data_direction direction) { + might_sleep(); + if (WARN_ON(!attach || !attach->dmabuf || !sg_table)) return; -- cgit v1.1 From 1ac12b4b6d707937f9de6d09622823b2fd0c93ef Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Dec 2012 12:10:06 -0500 Subject: vfs: turn is_dir argument to kern_path_create into a lookup_flags arg Where we can pass in LOOKUP_DIRECTORY or LOOKUP_REVAL. Any other flags passed in here are currently ignored. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- drivers/base/devtmpfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 147d1a4..17cf7ca 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -148,7 +148,7 @@ static int dev_mkdir(const char *name, umode_t mode) struct path path; int err; - dentry = kern_path_create(AT_FDCWD, name, &path, 1); + dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY); if (IS_ERR(dentry)) return PTR_ERR(dentry); -- cgit v1.1 From 55ac85e942c6783e728964861df36fc80e8ced93 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 19 Dec 2012 19:42:28 +0530 Subject: regmap: irq: enable wake support by default regmap-irq framework is used vastly by mfd drivers and some of devices like TPS65910, TPS80036 do not support the wake base register to enable wake. Currently wake in regmap-irq only supported if client driver passes the wake base register. As the regmap-irq is mostly used by mfd devices and it is require to have wake support from these devices in most of use cases, enabling wake support by default in regmap-irq. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 5972ad9..9129493 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -129,16 +129,15 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) struct regmap *map = d->map; const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); - if (!d->chip->wake_base) - return -EINVAL; - if (on) { - d->wake_buf[irq_data->reg_offset / map->reg_stride] - &= ~irq_data->mask; + if (d->wake_buf) + d->wake_buf[irq_data->reg_offset / map->reg_stride] + &= ~irq_data->mask; d->wake_count++; } else { - d->wake_buf[irq_data->reg_offset / map->reg_stride] - |= irq_data->mask; + if (d->wake_buf) + d->wake_buf[irq_data->reg_offset / map->reg_stride] + |= irq_data->mask; d->wake_count--; } @@ -316,11 +315,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->irq_chip = regmap_irq_chip; d->irq_chip.name = chip->name; - if (!chip->wake_base) { - d->irq_chip.irq_set_wake = NULL; - d->irq_chip.flags |= IRQCHIP_MASK_ON_SUSPEND | - IRQCHIP_SKIP_SET_WAKE; - } d->irq = irq; d->map = map; d->chip = chip; -- cgit v1.1 From 2ac902ce17f9dfa0d4d1f0818be147b5d2515fb7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 19 Dec 2012 14:51:55 +0000 Subject: regmap: flat: Add flat cache type While for I2C and SPI devices the overhead of using rbtree for devices with only one block of registers is negligible the same isn't always going to be true for MMIO devices where the I/O costs are very much lower. Cater for these devices by adding a simple flat array type for them where the lookups are simple array accesses, taking us right back to the original ASoC cache implementation. Thanks to Magnus Damm for the discussion which prompted this. Signed-off-by: Mark Brown --- drivers/base/regmap/Makefile | 2 +- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache-flat.c | 72 +++++++++++++++++++++++++++++++++++++ drivers/base/regmap/regcache.c | 1 + 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 drivers/base/regmap/regcache-flat.c (limited to 'drivers/base') diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index 5e75d1b..cf12998 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_REGMAP) += regmap.o regcache.o -obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o +obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 401d191..e22bb80 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -177,5 +177,6 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg); extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; +extern struct regcache_ops regcache_flat_ops; #endif diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c new file mode 100644 index 0000000..d9762e4 --- /dev/null +++ b/drivers/base/regmap/regcache-flat.c @@ -0,0 +1,72 @@ +/* + * Register cache access API - flat caching support + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "internal.h" + +static int regcache_flat_init(struct regmap *map) +{ + int i; + unsigned int *cache; + + map->cache = kzalloc(sizeof(unsigned int) * (map->max_register + 1), + GFP_KERNEL); + if (!map->cache) + return -ENOMEM; + + cache = map->cache; + + for (i = 0; i < map->num_reg_defaults; i++) + cache[map->reg_defaults[i].reg] = map->reg_defaults[i].def; + + return 0; +} + +static int regcache_flat_exit(struct regmap *map) +{ + kfree(map->cache); + map->cache = NULL; + + return 0; +} + +static int regcache_flat_read(struct regmap *map, + unsigned int reg, unsigned int *value) +{ + unsigned int *cache = map->cache; + + *value = cache[reg]; + + return 0; +} + +static int regcache_flat_write(struct regmap *map, unsigned int reg, + unsigned int value) +{ + unsigned int *cache = map->cache; + + cache[reg] = value; + + return 0; +} + +struct regcache_ops regcache_flat_ops = { + .type = REGCACHE_FLAT, + .name = "flat", + .init = regcache_flat_init, + .exit = regcache_flat_exit, + .read = regcache_flat_read, + .write = regcache_flat_write, +}; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 835883b..e69ff3e 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -22,6 +22,7 @@ static const struct regcache_ops *cache_types[] = { ®cache_rbtree_ops, ®cache_lzo_ops, + ®cache_flat_ops, }; static int regcache_hw_init(struct regmap *map) -- cgit v1.1 From bbae92ca49f77898277576e3377b09e1391a3271 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 3 Jan 2013 13:58:33 +0000 Subject: regmap: irq: Factor register read out of the IRQ parsing loop In preparation for adding back support for block reads. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 9129493..329be98 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -170,13 +170,6 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } } - /* - * Ignore masked IRQs and ack if we need to; we ack early so - * there is no race between handling and acknowleding the - * interrupt. We assume that typically few of the interrupts - * will fire simultaneously so don't worry about overhead from - * doing a write per register. - */ for (i = 0; i < data->chip->num_regs; i++) { ret = regmap_read(map, chip->status_base + (i * map->reg_stride * data->irq_reg_stride), @@ -189,7 +182,16 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) pm_runtime_put(map->dev); return IRQ_NONE; } + } + /* + * Ignore masked IRQs and ack if we need to; we ack early so + * there is no race between handling and acknowleding the + * interrupt. We assume that typically few of the interrupts + * will fire simultaneously so don't worry about overhead from + * doing a write per register. + */ + for (i = 0; i < data->chip->num_regs; i++) { data->status_buf[i] &= ~data->mask_buf[i]; if (data->status_buf[i] && chip->ack_base) { -- cgit v1.1 From a7440eaa90cf2659920b9b28973cc1a13a2b331f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 3 Jan 2013 14:27:15 +0000 Subject: regmap: irq: Use a bulk read for interrupt status where possible If the interrupt status registers are a single block of registers and the chip supports bulk reads then do a single bulk read rather than pay the extra I/O cost. This restores the original behaviour which was lost when support for register striding was added. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 67 +++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 7 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 329be98..aaf599a 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -34,6 +34,7 @@ struct regmap_irq_chip_data { int irq; int wake_count; + void *status_reg_buf; unsigned int *status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; @@ -170,18 +171,60 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } } - for (i = 0; i < data->chip->num_regs; i++) { - ret = regmap_read(map, chip->status_base + (i * map->reg_stride - * data->irq_reg_stride), - &data->status_buf[i]); + /* + * Read in the statuses, using a single bulk read if possible + * in order to reduce the I/O overheads. + */ + if (!map->use_single_rw && map->reg_stride == 1 && + data->irq_reg_stride == 1) { + u8 *buf8 = data->status_reg_buf; + u16 *buf16 = data->status_reg_buf; + u32 *buf32 = data->status_reg_buf; + BUG_ON(!data->status_reg_buf); + + ret = regmap_bulk_read(map, chip->status_base, + data->status_reg_buf, + chip->num_regs); if (ret != 0) { dev_err(map->dev, "Failed to read IRQ status: %d\n", - ret); - if (chip->runtime_pm) - pm_runtime_put(map->dev); + ret); return IRQ_NONE; } + + for (i = 0; i < data->chip->num_regs; i++) { + switch (map->format.val_bytes) { + case 1: + data->status_buf[i] = buf8[i]; + break; + case 2: + data->status_buf[i] = buf16[i]; + break; + case 4: + data->status_buf[i] = buf32[i]; + break; + default: + BUG(); + return IRQ_NONE; + } + } + + } else { + for (i = 0; i < data->chip->num_regs; i++) { + ret = regmap_read(map, chip->status_base + + (i * map->reg_stride + * data->irq_reg_stride), + &data->status_buf[i]); + + if (ret != 0) { + dev_err(map->dev, + "Failed to read IRQ status: %d\n", + ret); + if (chip->runtime_pm) + pm_runtime_put(map->dev); + return IRQ_NONE; + } + } } /* @@ -327,6 +370,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, else d->irq_reg_stride = 1; + if (!map->use_single_rw && map->reg_stride == 1 && + d->irq_reg_stride == 1) { + d->status_reg_buf = kmalloc(map->format.val_bytes * + chip->num_regs, GFP_KERNEL); + if (!d->status_reg_buf) + goto err_alloc; + } + mutex_init(&d->lock); for (i = 0; i < chip->num_irqs; i++) @@ -397,6 +448,7 @@ err_alloc: kfree(d->mask_buf_def); kfree(d->mask_buf); kfree(d->status_buf); + kfree(d->status_reg_buf); kfree(d); return ret; } @@ -418,6 +470,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); + kfree(d->status_reg_buf); kfree(d->status_buf); kfree(d); } -- cgit v1.1 From 33be49324f7f3e9dff10d3d07a5c78ee82f8d54e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Jan 2013 16:32:54 +0000 Subject: regmap: irq: Fix sync of wake statuses to hardware This wasn't implemented but happened to work on test systems due to lack of wake mask inversion support. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index aaf599a..be55799 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -88,6 +88,17 @@ static void regmap_irq_sync_unlock(struct irq_data *data) if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", reg); + + reg = d->chip->wake_base + + (i * map->reg_stride * d->irq_reg_stride); + if (d->wake_buf) { + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], d->wake_buf[i]); + if (ret != 0) + dev_err(d->map->dev, + "Failed to sync wakes in %x: %d\n", + reg, ret); + } } if (d->chip->runtime_pm) -- cgit v1.1 From 9442490a02867088bcd5ed6231fa3b35a733c2b8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Jan 2013 16:35:07 +0000 Subject: regmap: irq: Support wake IRQ mask inversion Support devices which have an enable rather than mask register for wake sources. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index be55799..4706c63 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -92,8 +92,14 @@ static void regmap_irq_sync_unlock(struct irq_data *data) reg = d->chip->wake_base + (i * map->reg_stride * d->irq_reg_stride); if (d->wake_buf) { - ret = regmap_update_bits(d->map, reg, - d->mask_buf_def[i], d->wake_buf[i]); + if (d->chip->wake_invert) + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], + ~d->wake_buf[i]); + else + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], + d->wake_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync wakes in %x: %d\n", @@ -419,8 +425,15 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->wake_buf[i] = d->mask_buf_def[i]; reg = chip->wake_base + (i * map->reg_stride * d->irq_reg_stride); - ret = regmap_update_bits(map, reg, d->wake_buf[i], - d->wake_buf[i]); + + if (chip->wake_invert) + ret = regmap_update_bits(map, reg, + d->mask_buf_def[i], + 0); + else + ret = regmap_update_bits(map, reg, + d->mask_buf_def[i], + d->wake_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", reg, ret); -- cgit v1.1 From 4ce4780270b69a0f4ce81887c2ef864c04cb7f9f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 18 Dec 2012 14:07:49 +0100 Subject: PM / QoS: Rename local variable in dev_pm_qos_add_ancestor_request() Local variable 'error' in dev_pm_qos_add_ancestor_request() need not contain error codes only, so rename it to 'ret'. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index ff46387..d213495 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -542,19 +542,19 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, struct dev_pm_qos_request *req, s32 value) { struct device *ancestor = dev->parent; - int error = -ENODEV; + int ret = -ENODEV; while (ancestor && !ancestor->power.ignore_children) ancestor = ancestor->parent; if (ancestor) - error = dev_pm_qos_add_request(ancestor, req, - DEV_PM_QOS_LATENCY, value); + ret = dev_pm_qos_add_request(ancestor, req, + DEV_PM_QOS_LATENCY, value); - if (error < 0) + if (ret < 0) req->dev = NULL; - return error; + return ret; } EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); -- cgit v1.1 From 9f6d8f6ab26b42620a914d67f29822f9bba90233 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 22 Dec 2012 23:59:01 +0100 Subject: PM: Move disabling/enabling runtime PM to late suspend/early resume Currently, the PM core disables runtime PM for all devices right after executing subsystem/driver .suspend() callbacks for them and re-enables it right before executing subsystem/driver .resume() callbacks for them. This may lead to problems when there are two devices such that the .suspend() callback executed for one of them depends on runtime PM working for the other. In that case, if runtime PM has already been disabled for the second device, the first one's .suspend() won't work correctly (and analogously for resume). To make those issues go away, make the PM core disable runtime PM for devices right before executing subsystem/driver .suspend_late() callbacks for them and enable runtime PM for them right after executing subsystem/driver .resume_early() callbacks for them. This way the potential conflitcs between .suspend_late()/.resume_early() and their runtime PM counterparts are still prevented from happening, but the subtle ordering issues related to disabling/enabling runtime PM for devices during system suspend/resume are much easier to avoid. Reported-and-tested-by: Jan-Matthias Braun Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Kevin Hilman Cc: 3.4+ --- drivers/base/power/main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index a3c1404..2b7f77d 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -513,6 +513,8 @@ static int device_resume_early(struct device *dev, pm_message_t state) Out: TRACE_RESUME(error); + + pm_runtime_enable(dev); return error; } @@ -589,8 +591,6 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) if (!dev->power.is_suspended) goto Unlock; - pm_runtime_enable(dev); - if (dev->pm_domain) { info = "power domain "; callback = pm_op(&dev->pm_domain->ops, state); @@ -930,6 +930,8 @@ static int device_suspend_late(struct device *dev, pm_message_t state) pm_callback_t callback = NULL; char *info = NULL; + __pm_runtime_disable(dev, false); + if (dev->power.syscore) return 0; @@ -1133,11 +1135,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) Complete: complete_all(&dev->power.completion); - if (error) async_error = error; - else if (dev->power.is_suspended) - __pm_runtime_disable(dev, false); return error; } -- cgit v1.1 From 120f80518125b8c312007a54106759d608487553 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Jan 2013 15:32:00 +0000 Subject: regmap: debugfs: Fix attempts to read nonexistant register blocks Return the start of the last block we tried to read rather than a position, and also make sure we update the byte position while we're at it. Without this reads that go into nonexistant areas get confused. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 07aad78..43d51dc 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -108,7 +108,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, return c->base_reg; } - ret = c->max; + *pos = c->min; + ret = c->base_reg; } return ret; -- cgit v1.1 From 5a1d6d172bc8a3ecf29add6c84d047025cb71566 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Jan 2013 20:40:19 +0000 Subject: regmap: debugfs: Fix check for block start in cached seeks Check for the block we were asked to start from, not the position we're in. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 43d51dc..e8d15db 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -103,7 +103,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, /* Find the relevant block */ list_for_each_entry(c, &map->debugfs_off_cache, list) { - if (*pos >= c->min && *pos <= c->max) { + if (from >= c->min && from <= c->max) { *pos = c->min; return c->base_reg; } -- cgit v1.1 From 95f971c745a343255744703dc4ae8d78508519cc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Jan 2013 13:35:58 +0000 Subject: regmap: debugfs: Discard the cache if we fail to allocate an entry Rather than trying to soldier on with a partially allocated cache just throw the cache away and pretend we don't have one in case we can get a full cache next time around. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index e8d15db..9099cd3 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -56,6 +56,19 @@ static const struct file_operations regmap_name_fops = { .llseek = default_llseek, }; +static void regmap_debugfs_free_dump_cache(struct regmap *map) +{ + struct regmap_debugfs_off_cache *c; + + while (!list_empty(&map->debugfs_off_cache)) { + c = list_first_entry(&map->debugfs_off_cache, + struct regmap_debugfs_off_cache, + list); + list_del(&c->list); + kfree(c); + } +} + /* * Work out where the start offset maps into register numbers, bearing * in mind that we suppress hidden registers. @@ -91,8 +104,10 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, /* No cache entry? Start a new one */ if (!c) { c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) - break; + if (!c) { + regmap_debugfs_free_dump_cache(map); + return base; + } c->min = p; c->base_reg = i; } @@ -388,16 +403,8 @@ void regmap_debugfs_init(struct regmap *map, const char *name) void regmap_debugfs_exit(struct regmap *map) { - struct regmap_debugfs_off_cache *c; - debugfs_remove_recursive(map->debugfs); - while (!list_empty(&map->debugfs_off_cache)) { - c = list_first_entry(&map->debugfs_off_cache, - struct regmap_debugfs_off_cache, - list); - list_del(&c->list); - kfree(c); - } + regmap_debugfs_free_dump_cache(map); kfree(map->debugfs_name); } -- cgit v1.1 From 5bd9f4bb34c16b62725b9486a290c01b1fdfec1c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Jan 2013 13:44:50 +0000 Subject: regmap: debugfs: Ensure a correct return value for empty caches This should never happen in the real world. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 9099cd3..720e1424 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -116,6 +116,16 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, } } + /* + * This should never happen; we return above if we fail to + * allocate and we should never be in this code if there are + * no registers at all. + */ + if (list_empty(&map->debugfs_off_cache)) { + WARN_ON(list_empty(&map->debugfs_off_cache)); + return base; + } + /* Find the relevant block */ list_for_each_entry(c, &map->debugfs_off_cache, list) { if (from >= c->min && from <= c->max) { -- cgit v1.1 From e8d6539c8a94b88fc7ca5d6bdd9eeb0e64b434e4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Jan 2013 18:47:52 +0000 Subject: regmap: debugfs: Make sure we store the last entry in the offset cache Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 720e1424..46a213a 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -116,6 +116,15 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, } } + /* Close the last entry off if we didn't scan beyond it */ + if (c) { + c->max = p - 1; + list_add_tail(&c->list, + &map->debugfs_off_cache); + } else { + return base; + } + /* * This should never happen; we return above if we fail to * allocate and we should never be in this code if there are -- cgit v1.1 From 237019e7e356abb1bad591fa5edab68224793143 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 10 Jan 2013 17:06:10 +0100 Subject: regmap: Add support for 24 bit wide register addresses Since regmap already has support for formatting 24 bit wide values, so adding support for 24 bit wide registers is pretty much straight forward. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 42d5cb0..26af93a 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -500,6 +500,12 @@ struct regmap *regmap_init(struct device *dev, } break; + case 24: + if (reg_endian != REGMAP_ENDIAN_BIG) + goto err_map; + map->format.format_reg = regmap_format_24; + break; + case 32: switch (reg_endian) { case REGMAP_ENDIAN_BIG: -- cgit v1.1 From ad278406b3b8b8e454af23b63df3c3d63f6aee94 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sat, 12 Jan 2013 12:54:12 -0800 Subject: regmap: Add provisions to have user-defined read operation This commit is a preparatory commit to provide "no-bus" configuration option for regmap API. It adds necessary plumbing needed to have the ability to provide user define register read function. Signed-off-by: Andrey Smirnov Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 2 ++ drivers/base/regmap/regmap.c | 35 ++++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 401d191..471eb90 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -74,6 +74,8 @@ struct regmap { const struct regmap_access_table *volatile_table; const struct regmap_access_table *precious_table; + int (*reg_read)(void *context, unsigned int reg, unsigned int *val); + u8 read_flag_mask; u8 write_flag_mask; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 42d5cb0..ceaefcf 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -34,6 +34,9 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); +static int _regmap_bus_read(void *context, unsigned int reg, + unsigned int *val); + bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges) @@ -430,6 +433,8 @@ struct regmap *regmap_init(struct device *dev, map->read_flag_mask = bus->read_flag_mask; } + map->reg_read = _regmap_bus_read; + reg_endian = config->reg_format_endian; if (reg_endian == REGMAP_ENDIAN_DEFAULT) reg_endian = bus->reg_format_endian_default; @@ -1202,10 +1207,27 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, return ret; } +static int _regmap_bus_read(void *context, unsigned int reg, + unsigned int *val) +{ + int ret; + struct regmap *map = context; + + if (!map->format.parse_val) + return -EINVAL; + + ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); + if (ret == 0) + *val = map->format.parse_val(map->work_buf); + + return ret; +} + static int _regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; + BUG_ON(!map->reg_read); if (!map->cache_bypass) { ret = regcache_read(map, reg, val); @@ -1213,26 +1235,21 @@ static int _regmap_read(struct regmap *map, unsigned int reg, return 0; } - if (!map->format.parse_val) - return -EINVAL; - if (map->cache_only) return -EBUSY; - ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); + ret = map->reg_read(map, reg, val); if (ret == 0) { - *val = map->format.parse_val(map->work_buf); - #ifdef LOG_DEVICE if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) dev_info(map->dev, "%x => %x\n", reg, *val); #endif trace_regmap_reg_read(map->dev, reg, *val); - } - if (ret == 0 && !map->cache_bypass) - regcache_write(map, reg, *val); + if (!map->cache_bypass) + regcache_write(map, reg, *val); + } return ret; } -- cgit v1.1 From 07c320dc31d757b8cb59c64dab320215c929bf02 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sat, 12 Jan 2013 12:54:13 -0800 Subject: regmap: Add provisions to have user-defined write operation This commit is a preparatory commit to provide "no-bus" configuration option for regmap API. It adds necessary plumbing needed to have the ability to provide user define register write function. Signed-off-by: Andrey Smirnov Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap.c | 83 +++++++++++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 29 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 471eb90..51f0574 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -75,6 +75,7 @@ struct regmap { const struct regmap_access_table *precious_table; int (*reg_read)(void *context, unsigned int reg, unsigned int *val); + int (*reg_write)(void *context, unsigned int reg, unsigned int val); u8 read_flag_mask; u8 write_flag_mask; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ceaefcf..6845a07 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -36,6 +36,10 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, static int _regmap_bus_read(void *context, unsigned int reg, unsigned int *val); +static int _regmap_bus_formatted_write(void *context, unsigned int reg, + unsigned int val); +static int _regmap_bus_raw_write(void *context, unsigned int reg, + unsigned int val); bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, @@ -580,6 +584,11 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } + if (map->format.format_write) + map->reg_write = _regmap_bus_formatted_write; + else if (map->format.format_val) + map->reg_write = _regmap_bus_raw_write; + map->range_tree = RB_ROOT; for (i = 0; i < config->num_ranges; i++) { const struct regmap_range_cfg *range_cfg = &config->ranges[i]; @@ -986,12 +995,54 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, return ret; } +static int _regmap_bus_formatted_write(void *context, unsigned int reg, + unsigned int val) +{ + int ret; + struct regmap_range_node *range; + struct regmap *map = context; + + BUG_ON(!map->format.format_write); + + range = _regmap_range_lookup(map, reg); + if (range) { + ret = _regmap_select_page(map, ®, range, 1); + if (ret != 0) + return ret; + } + + map->format.format_write(map, reg, val); + + trace_regmap_hw_write_start(map->dev, reg, 1); + + ret = map->bus->write(map->bus_context, map->work_buf, + map->format.buf_size); + + trace_regmap_hw_write_done(map->dev, reg, 1); + + return ret; +} + +static int _regmap_bus_raw_write(void *context, unsigned int reg, + unsigned int val) +{ + struct regmap *map = context; + + BUG_ON(!map->format.format_val); + + map->format.format_val(map->work_buf + map->format.reg_bytes + + map->format.pad_bytes, val, 0); + return _regmap_raw_write(map, reg, + map->work_buf + + map->format.reg_bytes + + map->format.pad_bytes, + map->format.val_bytes); +} + int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { - struct regmap_range_node *range; int ret; - BUG_ON(!map->format.format_write && !map->format.format_val); if (!map->cache_bypass && map->format.format_write) { ret = regcache_write(map, reg, val); @@ -1010,33 +1061,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_reg_write(map->dev, reg, val); - if (map->format.format_write) { - range = _regmap_range_lookup(map, reg); - if (range) { - ret = _regmap_select_page(map, ®, range, 1); - if (ret != 0) - return ret; - } - - map->format.format_write(map, reg, val); - - trace_regmap_hw_write_start(map->dev, reg, 1); - - ret = map->bus->write(map->bus_context, map->work_buf, - map->format.buf_size); - - trace_regmap_hw_write_done(map->dev, reg, 1); - - return ret; - } else { - map->format.format_val(map->work_buf + map->format.reg_bytes - + map->format.pad_bytes, val, 0); - return _regmap_raw_write(map, reg, - map->work_buf + - map->format.reg_bytes + - map->format.pad_bytes, - map->format.val_bytes); - } + return map->reg_write(map, reg, val); } /** -- cgit v1.1 From f32ca3db7f0b05a88edf37ccb00e262290a213e7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Jan 2013 00:33:36 +0900 Subject: regmap: debugfs: Fix seeking from the cache We don't want to bomb out early if we failed to get the cache any more, just soldier on instead and we won't get confused and always return the first block. Reported-by: Philipp Zabel --- drivers/base/regmap/regmap-debugfs.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 46a213a..d9a6c94 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -121,8 +121,6 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, c->max = p - 1; list_add_tail(&c->list, &map->debugfs_off_cache); - } else { - return base; } /* -- cgit v1.1 From 4adf07fba3bd64472921a01aae0e116f9f948b77 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 15 Jan 2013 10:43:43 +0200 Subject: firmware: make sure the fw file size is not 0 If the requested firmware file size is 0 bytes in the filesytem, we will try to vmalloc(0), which causes a warning: vmalloc: allocation failure: 0 bytes kworker/1:1: page allocation failure: order:0, mode:0xd2 __vmalloc_node_range+0x164/0x208 __vmalloc_node+0x4c/0x58 vmalloc+0x38/0x44 _request_firmware_load+0x220/0x6b0 request_firmware+0x64/0xc8 wl18xx_setup+0xb4/0x570 [wl18xx] wlcore_nvs_cb+0x64/0x9f8 [wlcore] request_firmware_work_func+0x94/0x100 process_one_work+0x1d0/0x750 worker_thread+0x184/0x4ac kthread+0xb4/0xc0 To fix this, check whether the file size is less than or equal to zero in fw_read_file_contents(). Cc: stable [3.7] Signed-off-by: Luciano Coelho Acked-by: Ming Lei Signed-off-by: Linus Torvalds --- drivers/base/firmware_class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index d814603..b392b35 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -305,7 +305,7 @@ static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf char *buf; size = fw_file_size(file); - if (size < 0) + if (size <= 0) return false; buf = vmalloc(size); if (!buf) -- cgit v1.1 From 30a4840a4cd74301058a1f054f335185f978ace8 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 15 Jan 2013 15:27:46 +0100 Subject: drivers/base/cpu.c: Fix typo in comment [ We should make fun of people who can't speel too, but then we'd have no time for any real work at all - Linus ] Signed-off-by: Linus Torvalds --- drivers/base/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 6345294..fb10728 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -224,7 +224,7 @@ static void cpu_device_release(struct device *dev) * by the cpu device. * * Never copy this way of doing things, or you too will be made fun of - * on the linux-kerenl list, you have been warned. + * on the linux-kernel list, you have been warned. */ } -- cgit v1.1 From 1a5d76dbe825fa6deba3d8979bbd334ea17b6dcc Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 8 Dec 2012 15:38:06 -0200 Subject: drivers/base/core.c: Remove two unused variables and two useless calls to kfree old_class_name, and new_class_name are never used. This patch remove the declaration and calls to kfree. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r1 forall@ type T; identifier i; @@ * T *i = NULL; ... when != i * kfree(i); // Signed-off-by: Peter Senna Tschudin Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index a235085..27603a6 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1685,8 +1685,6 @@ EXPORT_SYMBOL_GPL(device_destroy); */ int device_rename(struct device *dev, const char *new_name) { - char *old_class_name = NULL; - char *new_class_name = NULL; char *old_device_name = NULL; int error; @@ -1717,8 +1715,6 @@ int device_rename(struct device *dev, const char *new_name) out: put_device(dev); - kfree(new_class_name); - kfree(old_class_name); kfree(old_device_name); return error; -- cgit v1.1 From f5e097f0465e874ac76e8b6ae355b6faa83973a1 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 11 Dec 2012 16:05:26 +0100 Subject: dma_buf: Cleanup dma_buf_fd Remove redundant 'error' variable. Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/base/dma-buf.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index a3f79c4..ff5b745 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c @@ -134,15 +134,14 @@ EXPORT_SYMBOL_GPL(dma_buf_export); */ int dma_buf_fd(struct dma_buf *dmabuf, int flags) { - int error, fd; + int fd; if (!dmabuf || !dmabuf->file) return -EINVAL; - error = get_unused_fd_flags(flags); - if (error < 0) - return error; - fd = error; + fd = get_unused_fd_flags(flags); + if (fd < 0) + return fd; fd_install(fd, dmabuf->file); -- cgit v1.1 From e79798659339be800bf553c0b6fb06745aecf37f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 12 Dec 2012 10:19:07 -0800 Subject: drivers: base: Convert print_symbol to %pSR Use the new vsprintf extension to avoid any possible message interleaving. Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 27603a6..2937c63 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -97,8 +97,8 @@ static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, if (dev_attr->show) ret = dev_attr->show(dev, dev_attr, buf); if (ret >= (ssize_t)PAGE_SIZE) { - print_symbol("dev_attr_show: %s returned bad count\n", - (unsigned long)dev_attr->show); + printk("dev_attr_show: %pSR returned bad count\n", + dev_attr->show); } return ret; } -- cgit v1.1 From 190888ac01d059e38ffe77a2291d44cafa9016fb Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 19 Nov 2012 23:35:17 +0800 Subject: driver core: fix possible missing of device probe Inside bus_add_driver(), one device might be added(device_add()) into the bus or probed which is triggered by deferred probe just after completing of driver_attach() and before 'klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers)', so the device won't be probed by this driver. This patch moves the below line 'klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers)' before driver_attach() inside bus_add_driver() to fix the problem. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 24eb078..f9d3132 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -700,12 +700,12 @@ int bus_add_driver(struct device_driver *drv) if (error) goto out_unregister; + klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_unregister; } - klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); module_add_driver(drv->owner, drv); error = driver_create_file(drv, &driver_attr_uevent); -- cgit v1.1 From 53a9c87e7e02abf3575be98c9fb2018fa30a61b9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 17 Jan 2013 13:10:23 -0800 Subject: Revert "drivers: base: Convert print_symbol to %pSR" This reverts commit e79798659339be800bf553c0b6fb06745aecf37f as %pSR isn't in the tree yet. Cc: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 2937c63..27603a6 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -97,8 +97,8 @@ static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, if (dev_attr->show) ret = dev_attr->show(dev, dev_attr, buf); if (ret >= (ssize_t)PAGE_SIZE) { - printk("dev_attr_show: %pSR returned bad count\n", - dev_attr->show); + print_symbol("dev_attr_show: %s returned bad count\n", + (unsigned long)dev_attr->show); } return ret; } -- cgit v1.1 From 373d4d099761cb1f637bed488ab3871945882273 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Jan 2013 17:17:39 +1030 Subject: taint: add explicit flag to show whether lock dep is still OK. Fix up all callers as they were before, with make one change: an unsigned module taints the kernel, but doesn't turn off lockdep. Signed-off-by: Rusty Russell --- drivers/base/regmap/regmap-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 46a213a..f63c830 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -267,7 +267,7 @@ static ssize_t regmap_map_write_file(struct file *file, return -EINVAL; /* Userspace has been fiddling around behind the kernel's back */ - add_taint(TAINT_USER); + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); regmap_write(map, reg, value); return buf_size; -- cgit v1.1 From 31b35e9edd51cab96d880248206c90b7177e3e5c Mon Sep 17 00:00:00 2001 From: Nestor Ovroy Date: Fri, 18 Jan 2013 16:51:03 +0100 Subject: regmap: fix small typo in regmap_bulk_write comment Signed-off-by: Nestor Ovroy Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 42d5cb0..f00b059 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1106,7 +1106,7 @@ EXPORT_SYMBOL_GPL(regmap_raw_write); * @val_count: Number of registers to write * * This function is intended to be used for writing a large block of - * data to be device either in single transfer or multiple transfer. + * data to the device either in single transfer or multiple transfer. * * A value of zero will be returned on success, a negative errno will * be returned in error cases. -- cgit v1.1 From ab78029ecc347debbd737f06688d788bd9d60c1d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 22 Jan 2013 10:56:14 -0700 Subject: drivers/pinctrl: grab default handles from device core This makes the device core auto-grab the pinctrl handle and set the "default" (PINCTRL_STATE_DEFAULT) state for every device that is present in the device model right before probe. This will account for the lion's share of embedded silicon devcies. A modification of the semantics for pinctrl_get() is also done: previously if the pinctrl handle for a certain device was already taken, the pinctrl core would return an error. Now, since the core may have already default-grabbed the handle and set its state to "default", if the handle was already taken, this will be disregarded and the located, previously instanitated handle will be returned to the caller. This way all code in drivers explicitly requesting their pinctrl handlers will still be functional, and drivers that want to explicitly retrieve and switch their handles can still do that. But if the desired functionality is just boilerplate of this type in the probe() function: struct pinctrl *p; p = devm_pinctrl_get_select_default(&dev); if (IS_ERR(p)) { if (PTR_ERR(p) == -EPROBE_DEFER) return -EPROBE_DEFER; dev_warn(&dev, "no pinctrl handle\n"); } The discussion began with the addition of such boilerplate to the omap4 keypad driver: http://marc.info/?l=linux-input&m=135091157719300&w=2 A previous approach using notifiers was discussed: http://marc.info/?l=linux-kernel&m=135263661110528&w=2 This failed because it could not handle deferred probes. This patch alone does not solve the entire dilemma faced: whether code should be distributed into the drivers or if it should be centralized to e.g. a PM domain. But it solves the immediate issue of the addition of boilerplate to a lot of drivers that just want to grab the default state. As mentioned, they can later explicitly retrieve the handle and set different states, and this could as well be done by e.g. PM domains as it is only related to a certain struct device * pointer. ChangeLog v4->v5 (Stephen): - Simplified the devicecore grab code. - Deleted a piece of documentation recommending that pins be mapped to a device rather than hogged. ChangeLog v3->v4 (Linus): - Drop overzealous NULL checks. - Move kref initialization to pinctrl_create(). - Seeking Tested-by from Stephen Warren so we do not disturb the Tegra platform. - Seeking ACK on this from Greg (and others who like it) so I can merge it through the pinctrl subsystem. ChangeLog v2->v3 (Linus): - Abstain from using IS_ERR_OR_NULL() in the driver core, Russell recently sent a patch to remove it. Handle the NULL case explicitly even though it's a bogus case. - Make sure we handle probe deferral correctly in the device core file. devm_kfree() the container on error so we don't waste memory for devices without pinctrl handles. - Introduce reference counting into the pinctrl core using so that we don't release pinctrl handles that have been obtained for two or more places. ChangeLog v1->v2 (Linus): - Only store a pointer in the device struct, and only allocate this if it's really used by the device. Cc: Felipe Balbi Cc: Benoit Cousson Cc: Dmitry Torokhov Cc: Thomas Petazzoni Cc: Mitch Bradley Cc: Ulf Hansson Cc: Rafael J. Wysocki Cc: Jean-Christophe PLAGNIOL-VILLARD Cc: Rickard Andersson Cc: Russell King Reviewed-by: Mark Brown Acked-by: Greg Kroah-Hartman Signed-off-by: Linus Walleij [swarren: fixed and simplified error-handling in pinctrl_bind_pins(), to correctly handle deferred probe. Removed admonition from docs not to use pinctrl hogs for devices] Signed-off-by: Stephen Warren Signed-off-by: Linus Walleij --- drivers/base/Makefile | 1 + drivers/base/dd.c | 7 +++++ drivers/base/pinctrl.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 drivers/base/pinctrl.c (limited to 'drivers/base') diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 5aa2d70..4e22ce3 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -21,6 +21,7 @@ endif obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o obj-$(CONFIG_REGMAP) += regmap/ obj-$(CONFIG_SOC_BUS) += soc.o +obj-$(CONFIG_PINCTRL) += pinctrl.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/dd.c b/drivers/base/dd.c index e3bbed8..65631015 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "base.h" #include "power/power.h" @@ -269,6 +270,12 @@ static int really_probe(struct device *dev, struct device_driver *drv) WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; + + /* If using pinctrl, bind pins now before probing */ + ret = pinctrl_bind_pins(dev); + if (ret) + goto probe_failed; + if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", __func__, dev_name(dev)); diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c new file mode 100644 index 0000000..67a274e --- /dev/null +++ b/drivers/base/pinctrl.c @@ -0,0 +1,69 @@ +/* + * Driver core interface to the pinctrl subsystem. + * + * Copyright (C) 2012 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * Based on bits of regulator core, gpio core and clk core + * + * Author: Linus Walleij + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include + +/** + * pinctrl_bind_pins() - called by the device core before probe + * @dev: the device that is just about to probe + */ +int pinctrl_bind_pins(struct device *dev) +{ + int ret; + + dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL); + if (!dev->pins) + return -ENOMEM; + + dev->pins->p = devm_pinctrl_get(dev); + if (IS_ERR(dev->pins->p)) { + dev_dbg(dev, "no pinctrl handle\n"); + ret = PTR_ERR(dev->pins->p); + goto cleanup_alloc; + } + + dev->pins->default_state = pinctrl_lookup_state(dev->pins->p, + PINCTRL_STATE_DEFAULT); + if (IS_ERR(dev->pins->default_state)) { + dev_dbg(dev, "no default pinctrl state\n"); + ret = 0; + goto cleanup_get; + } + + ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state); + if (ret) { + dev_dbg(dev, "failed to activate default pinctrl state\n"); + goto cleanup_get; + } + + return 0; + + /* + * If no pinctrl handle or default state was found for this device, + * let's explicitly free the pin container in the device, there is + * no point in keeping it around. + */ +cleanup_get: + devm_pinctrl_put(dev->pins->p); +cleanup_alloc: + devm_kfree(dev, dev->pins); + dev->pins = NULL; + + /* Only return deferrals */ + if (ret != -EPROBE_DEFER) + ret = 0; + + return ret; +} -- cgit v1.1 From 6802771bba0455a751d8f4ece7587585be3eaa2f Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Wed, 23 Jan 2013 04:26:28 +0800 Subject: PM/Qos: Expose dev_pm_qos_flags symbol The dev_pm_qos_flags() will be used in the usb core which could be compiled as a module. This patch is to export it. Acked-by: Alan Stern Signed-off-by: Lan Tianyu Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/qos.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index d213495..3d4d1f8 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -91,6 +91,7 @@ enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask) return ret; } +EXPORT_SYMBOL_GPL(dev_pm_qos_flags); /** * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. -- cgit v1.1 From a4ca26a43e39d521b3913f09faf82dfbbbca5f6a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 11 Jan 2013 13:37:23 +0100 Subject: PM / Domains: don't use [delayed_]work_pending() There's no need to test whether a (delayed) work item is pending before queueing, flushing or cancelling it, so remove work_pending() tests used in those cases. Signed-off-by: Tejun Heo Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index acc3a8d..9a6b05a 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -433,8 +433,7 @@ static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) */ void genpd_queue_power_off_work(struct generic_pm_domain *genpd) { - if (!work_pending(&genpd->power_off_work)) - queue_work(pm_wq, &genpd->power_off_work); + queue_work(pm_wq, &genpd->power_off_work); } /** -- cgit v1.1 From a3471469bcba61f8f18ca4c267b0cdd90a61e035 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 26 Jan 2013 11:45:35 +0000 Subject: regmap: regmap: avoid spurious warning in regmap_read_debugfs Gcc warns about the case where regmap_read_debugfs tries to walk an empty map->debugfs_off_cache list, which would results in uninitialized variable getting returned, if we hadn't checked the same condition just before that. After an originally suggested inferior patch from Arnd Bergmann, this is the solution that Russell King came up with, sidestepping the problem by merging the error case for an empty list with the normal path. Without this patch, building mxs_defconfig results in: drivers/base/regmap/regmap-debugfs.c: In function 'regmap_read_debugfs': drivers/base/regmap/regmap-debugfs.c:147:9: : warning: 'ret' may be used uninitialized in this function [-Wmaybe-uninitialized] Reported-by: Vincent Stehle Cc: Mark Brown Cc: Greg Kroah-Hartman Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index d9a6c94..faa93cc 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -128,10 +128,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, * allocate and we should never be in this code if there are * no registers at all. */ - if (list_empty(&map->debugfs_off_cache)) { - WARN_ON(list_empty(&map->debugfs_off_cache)); - return base; - } + WARN_ON(list_empty(&map->debugfs_off_cache)); + ret = base; /* Find the relevant block */ list_for_each_entry(c, &map->debugfs_off_cache, list) { -- cgit v1.1 From d2a5884a64161b524cc6749ee11b95d252e497f3 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sun, 27 Jan 2013 10:49:05 -0800 Subject: regmap: Add "no-bus" option for regmap API This commit adds provision for "no-bus" usage of the regmap API. In this configuration user can provide API with two callbacks 'reg_read' and 'reg_write' which are to be called when reads and writes to one of device's registers is performed. This is useful for devices that expose registers but whose register access sequence does not fit the 'bus' abstraction. Signed-off-by: Andrey Smirnov Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 2 ++ drivers/base/regmap/regmap.c | 58 +++++++++++++++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 12 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 51f0574..b55fde5 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -77,6 +77,8 @@ struct regmap { int (*reg_read)(void *context, unsigned int reg, unsigned int *val); int (*reg_write)(void *context, unsigned int reg, unsigned int val); + bool defer_caching; + u8 read_flag_mask; u8 write_flag_mask; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6845a07..9592030 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -379,7 +379,7 @@ struct regmap *regmap_init(struct device *dev, enum regmap_endian reg_endian, val_endian; int i, j; - if (!bus || !config) + if (!config) goto err; map = kzalloc(sizeof(*map), GFP_KERNEL); @@ -393,7 +393,8 @@ struct regmap *regmap_init(struct device *dev, map->unlock = config->unlock; map->lock_arg = config->lock_arg; } else { - if (bus->fast_io) { + if ((bus && bus->fast_io) || + config->fast_io) { spin_lock_init(&map->spinlock); map->lock = regmap_lock_spinlock; map->unlock = regmap_unlock_spinlock; @@ -433,11 +434,19 @@ struct regmap *regmap_init(struct device *dev, if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; map->write_flag_mask = config->write_flag_mask; - } else { + } else if (bus) { map->read_flag_mask = bus->read_flag_mask; } - map->reg_read = _regmap_bus_read; + if (!bus) { + map->reg_read = config->reg_read; + map->reg_write = config->reg_write; + + map->defer_caching = false; + goto skip_format_initialization; + } else { + map->reg_read = _regmap_bus_read; + } reg_endian = config->reg_format_endian; if (reg_endian == REGMAP_ENDIAN_DEFAULT) @@ -584,10 +593,15 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } - if (map->format.format_write) + if (map->format.format_write) { + map->defer_caching = false; map->reg_write = _regmap_bus_formatted_write; - else if (map->format.format_val) + } else if (map->format.format_val) { + map->defer_caching = true; map->reg_write = _regmap_bus_raw_write; + } + +skip_format_initialization: map->range_tree = RB_ROOT; for (i = 0; i < config->num_ranges; i++) { @@ -790,7 +804,7 @@ void regmap_exit(struct regmap *map) regcache_exit(map); regmap_debugfs_exit(map); regmap_range_exit(map); - if (map->bus->free_context) + if (map->bus && map->bus->free_context) map->bus->free_context(map->bus_context); kfree(map->work_buf); kfree(map); @@ -893,6 +907,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, size_t len; int i; + BUG_ON(!map->bus); + /* Check for unwritable registers before we start */ if (map->writeable_reg) for (i = 0; i < val_len / map->format.val_bytes; i++) @@ -1002,7 +1018,7 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, struct regmap_range_node *range; struct regmap *map = context; - BUG_ON(!map->format.format_write); + BUG_ON(!map->bus || !map->format.format_write); range = _regmap_range_lookup(map, reg); if (range) { @@ -1028,7 +1044,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, { struct regmap *map = context; - BUG_ON(!map->format.format_val); + BUG_ON(!map->bus || !map->format.format_val); map->format.format_val(map->work_buf + map->format.reg_bytes + map->format.pad_bytes, val, 0); @@ -1039,12 +1055,18 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, map->format.val_bytes); } +static inline void *_regmap_map_get_context(struct regmap *map) +{ + return (map->bus) ? map : map->bus_context; +} + int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; + void *context = _regmap_map_get_context(map); - if (!map->cache_bypass && map->format.format_write) { + if (!map->cache_bypass && !map->defer_caching) { ret = regcache_write(map, reg, val); if (ret != 0) return ret; @@ -1061,7 +1083,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_reg_write(map->dev, reg, val); - return map->reg_write(map, reg, val); + return map->reg_write(context, reg, val); } /** @@ -1112,6 +1134,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, { int ret; + if (!map->bus) + return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; if (reg % map->reg_stride) @@ -1148,6 +1172,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_bytes = map->format.val_bytes; void *wval; + if (!map->bus) + return -EINVAL; if (!map->format.parse_val) return -EINVAL; if (reg % map->reg_stride) @@ -1201,6 +1227,8 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, u8 *u8 = map->work_buf; int ret; + BUG_ON(!map->bus); + range = _regmap_range_lookup(map, reg); if (range) { ret = _regmap_select_page(map, ®, range, @@ -1252,6 +1280,8 @@ static int _regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; + void *context = _regmap_map_get_context(map); + BUG_ON(!map->reg_read); if (!map->cache_bypass) { @@ -1263,7 +1293,7 @@ static int _regmap_read(struct regmap *map, unsigned int reg, if (map->cache_only) return -EBUSY; - ret = map->reg_read(map, reg, val); + ret = map->reg_read(context, reg, val); if (ret == 0) { #ifdef LOG_DEVICE if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) @@ -1325,6 +1355,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int v; int ret, i; + if (!map->bus) + return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; if (reg % map->reg_stride) @@ -1376,6 +1408,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_bytes = map->format.val_bytes; bool vol = regmap_volatile_range(map, reg, val_count); + if (!map->bus) + return -EINVAL; if (!map->format.parse_val) return -EINVAL; if (reg % map->reg_stride) -- cgit v1.1 From 0d509f2b112b21411712f0bf789b372987967e49 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 27 Jan 2013 22:07:38 +0800 Subject: regmap: Add asynchronous I/O support Some use cases like firmware download can transfer a lot of data in quick succession. With high speed buses these use cases can benefit from having multiple transfers scheduled at once since this allows the bus to minimise the delay between transfers. Support this by adding regmap_raw_write_async(), allowing raw transfers to be scheduled, and regmap_async_complete() to wait for them to finish. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 15 ++++ drivers/base/regmap/regmap.c | 182 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 187 insertions(+), 10 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 51f0574..2025186 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -16,6 +16,7 @@ #include #include #include +#include struct regmap; struct regcache_ops; @@ -39,6 +40,13 @@ struct regmap_format { unsigned int (*parse_val)(void *buf); }; +struct regmap_async { + struct list_head list; + struct work_struct cleanup; + struct regmap *map; + void *work_buf; +}; + struct regmap { struct mutex mutex; spinlock_t spinlock; @@ -53,6 +61,11 @@ struct regmap { void *bus_context; const char *name; + spinlock_t async_lock; + wait_queue_head_t async_waitq; + struct list_head async_list; + int async_ret; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; const char *debugfs_name; @@ -178,6 +191,8 @@ bool regcache_set_val(void *base, unsigned int idx, unsigned int val, unsigned int word_size); int regcache_lookup_reg(struct regmap *map, unsigned int reg); +void regmap_async_complete_cb(struct regmap_async *async, int ret); + extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6845a07..e57b7bc 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -41,6 +41,15 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val); +static void async_cleanup(struct work_struct *work) +{ + struct regmap_async *async = container_of(work, struct regmap_async, + cleanup); + + kfree(async->work_buf); + kfree(async); +} + bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges) @@ -430,6 +439,10 @@ struct regmap *regmap_init(struct device *dev, map->cache_type = config->cache_type; map->name = config->name; + spin_lock_init(&map->async_lock); + INIT_LIST_HEAD(&map->async_list); + init_waitqueue_head(&map->async_waitq); + if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; map->write_flag_mask = config->write_flag_mask; @@ -884,10 +897,13 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, } static int _regmap_raw_write(struct regmap *map, unsigned int reg, - const void *val, size_t val_len) + const void *val, size_t val_len, bool async) { struct regmap_range_node *range; + unsigned long flags; u8 *u8 = map->work_buf; + void *work_val = map->work_buf + map->format.reg_bytes + + map->format.pad_bytes; void *buf; int ret = -ENOTSUPP; size_t len; @@ -932,7 +948,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, dev_dbg(map->dev, "Writing window %d/%zu\n", win_residue, val_len / map->format.val_bytes); ret = _regmap_raw_write(map, reg, val, win_residue * - map->format.val_bytes); + map->format.val_bytes, async); if (ret != 0) return ret; @@ -955,6 +971,50 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, u8[0] |= map->write_flag_mask; + if (async && map->bus->async_write) { + struct regmap_async *async = map->bus->async_alloc(); + if (!async) + return -ENOMEM; + + async->work_buf = kzalloc(map->format.buf_size, + GFP_KERNEL | GFP_DMA); + if (!async->work_buf) { + kfree(async); + return -ENOMEM; + } + + INIT_WORK(&async->cleanup, async_cleanup); + async->map = map; + + /* If the caller supplied the value we can use it safely. */ + memcpy(async->work_buf, map->work_buf, map->format.pad_bytes + + map->format.reg_bytes + map->format.val_bytes); + if (val == work_val) + val = async->work_buf + map->format.pad_bytes + + map->format.reg_bytes; + + spin_lock_irqsave(&map->async_lock, flags); + list_add_tail(&async->list, &map->async_list); + spin_unlock_irqrestore(&map->async_lock, flags); + + ret = map->bus->async_write(map->bus_context, async->work_buf, + map->format.reg_bytes + + map->format.pad_bytes, + val, val_len, async); + + if (ret != 0) { + dev_err(map->dev, "Failed to schedule write: %d\n", + ret); + + spin_lock_irqsave(&map->async_lock, flags); + list_del(&async->list); + spin_unlock_irqrestore(&map->async_lock, flags); + + kfree(async->work_buf); + kfree(async); + } + } + trace_regmap_hw_write_start(map->dev, reg, val_len / map->format.val_bytes); @@ -962,8 +1022,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, * send the work_buf directly, otherwise try to do a gather * write. */ - if (val == (map->work_buf + map->format.pad_bytes + - map->format.reg_bytes)) + if (val == work_val) ret = map->bus->write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes + @@ -1036,7 +1095,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, map->work_buf + map->format.reg_bytes + map->format.pad_bytes, - map->format.val_bytes); + map->format.val_bytes, false); } int _regmap_write(struct regmap *map, unsigned int reg, @@ -1119,7 +1178,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, map->lock(map->lock_arg); - ret = _regmap_raw_write(map, reg, val, val_len); + ret = _regmap_raw_write(map, reg, val, val_len, false); map->unlock(map->lock_arg); @@ -1175,14 +1234,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (map->use_single_rw) { for (i = 0; i < val_count; i++) { ret = regmap_raw_write(map, - reg + (i * map->reg_stride), - val + (i * val_bytes), - val_bytes); + reg + (i * map->reg_stride), + val + (i * val_bytes), + val_bytes); if (ret != 0) return ret; } } else { - ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); + ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count, + false); } if (val_bytes != 1) @@ -1194,6 +1254,48 @@ out: } EXPORT_SYMBOL_GPL(regmap_bulk_write); +/** + * regmap_raw_write_async(): Write raw values to one or more registers + * asynchronously + * + * @map: Register map to write to + * @reg: Initial register to write to + * @val: Block of data to be written, laid out for direct transmission to the + * device. Must be valid until regmap_async_complete() is called. + * @val_len: Length of data pointed to by val. + * + * This function is intended to be used for things like firmware + * download where a large block of data needs to be transferred to the + * device. No formatting will be done on the data provided. + * + * If supported by the underlying bus the write will be scheduled + * asynchronously, helping maximise I/O speed on higher speed buses + * like SPI. regmap_async_complete() can be called to ensure that all + * asynchrnous writes have been completed. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_raw_write_async(struct regmap *map, unsigned int reg, + const void *val, size_t val_len) +{ + int ret; + + if (val_len % map->format.val_bytes) + return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map->lock_arg); + + ret = _regmap_raw_write(map, reg, val, val_len, true); + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_raw_write_async); + static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int val_len) { @@ -1492,6 +1594,66 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_update_bits_check); +void regmap_async_complete_cb(struct regmap_async *async, int ret) +{ + struct regmap *map = async->map; + bool wake; + + spin_lock(&map->async_lock); + + list_del(&async->list); + wake = list_empty(&map->async_list); + + if (ret != 0) + map->async_ret = ret; + + spin_unlock(&map->async_lock); + + schedule_work(&async->cleanup); + + if (wake) + wake_up(&map->async_waitq); +} + +static int regmap_async_is_done(struct regmap *map) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&map->async_lock, flags); + ret = list_empty(&map->async_list); + spin_unlock_irqrestore(&map->async_lock, flags); + + return ret; +} + +/** + * regmap_async_complete: Ensure all asynchronous I/O has completed. + * + * @map: Map to operate on. + * + * Blocks until any pending asynchronous I/O has completed. Returns + * an error code for any failed I/O operations. + */ +int regmap_async_complete(struct regmap *map) +{ + unsigned long flags; + int ret; + + /* Nothing to do with no async support */ + if (!map->bus->async_write) + return 0; + + wait_event(map->async_waitq, regmap_async_is_done(map)); + + spin_lock_irqsave(&map->async_lock, flags); + ret = map->async_ret; + map->async_ret = 0; + spin_unlock_irqrestore(&map->async_lock, flags); + + return ret; +} + /** * regmap_register_patch: Register and apply register updates to be applied * on device initialistion -- cgit v1.1 From e0356dfe98f227cd18314bdbcf93a9b464026ce7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 29 Jan 2013 12:14:12 +0800 Subject: regmap: spi: Support asynchronous I/O for SPI Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-spi.c | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index ffa46a9..913274b 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -15,6 +15,21 @@ #include #include +#include "internal.h" + +struct regmap_async_spi { + struct regmap_async core; + struct spi_message m; + struct spi_transfer t[2]; +}; + +static void regmap_spi_complete(void *data) +{ + struct regmap_async_spi *async = data; + + regmap_async_complete_cb(&async->core, async->m.status); +} + static int regmap_spi_write(void *context, const void *data, size_t count) { struct device *dev = context; @@ -40,6 +55,41 @@ static int regmap_spi_gather_write(void *context, return spi_sync(spi, &m); } +static int regmap_spi_async_write(void *context, + const void *reg, size_t reg_len, + const void *val, size_t val_len, + struct regmap_async *a) +{ + struct regmap_async_spi *async = container_of(a, + struct regmap_async_spi, + core); + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + + async->t[0].tx_buf = reg; + async->t[0].len = reg_len; + async->t[1].tx_buf = val; + async->t[1].len = val_len; + + spi_message_init(&async->m); + spi_message_add_tail(&async->t[0], &async->m); + spi_message_add_tail(&async->t[1], &async->m); + + async->m.complete = regmap_spi_complete; + async->m.context = async; + + return spi_async(spi, &async->m); +} + +static struct regmap_async *regmap_spi_async_alloc(void) +{ + struct regmap_async_spi *async_spi; + + async_spi = kzalloc(sizeof(*async_spi), GFP_KERNEL); + + return &async_spi->core; +} + static int regmap_spi_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) @@ -53,6 +103,8 @@ static int regmap_spi_read(void *context, static struct regmap_bus regmap_spi = { .write = regmap_spi_write, .gather_write = regmap_spi_gather_write, + .async_write = regmap_spi_async_write, + .async_alloc = regmap_spi_async_alloc, .read = regmap_spi_read, .read_flag_mask = 0x80, }; -- cgit v1.1 From b2ccd7632939b8c8d53ea1c4aa21ebf8d0add7cf Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 28 Jan 2013 18:26:15 +0000 Subject: PM / OPP: switch exported symbols to GPL variant We are GPLV2 library, so be clear in the symbols exported as well. Signed-off-by: Nishanth Menon Acked-by: Shawn Guo Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 50b2831..0dbe270 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -162,7 +162,7 @@ unsigned long opp_get_voltage(struct opp *opp) return v; } -EXPORT_SYMBOL(opp_get_voltage); +EXPORT_SYMBOL_GPL(opp_get_voltage); /** * opp_get_freq() - Gets the frequency corresponding to an available opp @@ -192,7 +192,7 @@ unsigned long opp_get_freq(struct opp *opp) return f; } -EXPORT_SYMBOL(opp_get_freq); +EXPORT_SYMBOL_GPL(opp_get_freq); /** * opp_get_opp_count() - Get number of opps available in the opp list @@ -225,7 +225,7 @@ int opp_get_opp_count(struct device *dev) return count; } -EXPORT_SYMBOL(opp_get_opp_count); +EXPORT_SYMBOL_GPL(opp_get_opp_count); /** * opp_find_freq_exact() - search for an exact frequency @@ -276,7 +276,7 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq, return opp; } -EXPORT_SYMBOL(opp_find_freq_exact); +EXPORT_SYMBOL_GPL(opp_find_freq_exact); /** * opp_find_freq_ceil() - Search for an rounded ceil freq @@ -323,7 +323,7 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) return opp; } -EXPORT_SYMBOL(opp_find_freq_ceil); +EXPORT_SYMBOL_GPL(opp_find_freq_ceil); /** * opp_find_freq_floor() - Search for a rounded floor freq @@ -374,7 +374,7 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) return opp; } -EXPORT_SYMBOL(opp_find_freq_floor); +EXPORT_SYMBOL_GPL(opp_find_freq_floor); /** * opp_add() - Add an OPP table from a table definitions @@ -568,7 +568,7 @@ int opp_enable(struct device *dev, unsigned long freq) { return opp_set_availability(dev, freq, true); } -EXPORT_SYMBOL(opp_enable); +EXPORT_SYMBOL_GPL(opp_enable); /** * opp_disable() - Disable a specific OPP @@ -590,7 +590,7 @@ int opp_disable(struct device *dev, unsigned long freq) { return opp_set_availability(dev, freq, false); } -EXPORT_SYMBOL(opp_disable); +EXPORT_SYMBOL_GPL(opp_disable); #ifdef CONFIG_CPU_FREQ /** -- cgit v1.1 From 74c46c6eaf9724edbfc12cc67e62773b708eb2ed Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Mon, 28 Jan 2013 18:26:16 +0000 Subject: PM / OPP: Export more symbols for module usage Export cpufreq helpers in OPP to make the cpufreq-core0 and highbank-cpufreq drivers loadable as modules. Signed-off-by: Mark Langsdorf Signed-off-by: Nishanth Menon Acked-by: Shawn Guo Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 0dbe270..32ee0fc 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -661,6 +661,7 @@ int opp_init_cpufreq_table(struct device *dev, return 0; } +EXPORT_SYMBOL_GPL(opp_init_cpufreq_table); /** * opp_free_cpufreq_table() - free the cpufreq table @@ -678,6 +679,7 @@ void opp_free_cpufreq_table(struct device *dev, kfree(*table); *table = NULL; } +EXPORT_SYMBOL_GPL(opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ /** @@ -738,4 +740,5 @@ int of_init_opp_table(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(of_init_opp_table); #endif -- cgit v1.1 From 4fa3e78be7e985ca814ce2aa0c09cbee404efcf7 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 29 Jan 2013 16:44:27 -0700 Subject: Driver core: treat unregistered bus_types as having no devices A bus_type has a list of devices (klist_devices), but the list and the subsys_private structure that contains it are not initialized until the bus_type is registered with bus_register(). The panic/reboot path has fixups that look up devices in pci_bus_type. If we panic before registering pci_bus_type, the bus_type exists but the list does not, so mach_reboot_fixups() trips over a null pointer and panics again: mach_reboot_fixups pci_get_device .. bus_find_device(&pci_bus_type, ...) bus->p is NULL Joonsoo reported a problem when panicking before PCI was initialized. I think this patch should be sufficient to replace the patch he posted here: https://lkml.org/lkml/2012/12/28/75 ("[PATCH] x86, reboot: skip reboot_fixups in early boot phase") Reported-by: Joonsoo Kim Signed-off-by: Bjorn Helgaas Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index f9d3132..519865b 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -290,7 +290,7 @@ int bus_for_each_dev(struct bus_type *bus, struct device *start, struct device *dev; int error = 0; - if (!bus) + if (!bus || !bus->p) return -EINVAL; klist_iter_init_node(&bus->p->klist_devices, &i, @@ -324,7 +324,7 @@ struct device *bus_find_device(struct bus_type *bus, struct klist_iter i; struct device *dev; - if (!bus) + if (!bus || !bus->p) return NULL; klist_iter_init_node(&bus->p->klist_devices, &i, -- cgit v1.1 From 4e0c92d015235d1dc65f9668a10cef2cea468ba2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 31 Jan 2013 11:13:54 +0100 Subject: firmware: Refactoring for splitting user-mode helper code Since 3.7 kernel, the firmware loader can read the firmware files directly, and the traditional user-mode helper is invoked only as a fallback. This seems working pretty well, and the next step would be to reduce the redundant user-mode helper stuff in future. This patch is a preparation for that: refactor the code for splitting user-mode helper stuff more easily. No functional change. Acked-by: Ming Lei Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 287 ++++++++++++++++++++++-------------------- 1 file changed, 154 insertions(+), 133 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b392b35..40ec60a 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -319,7 +319,8 @@ static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf return true; } -static bool fw_get_filesystem_firmware(struct firmware_buf *buf) +static bool fw_get_filesystem_firmware(struct device *device, + struct firmware_buf *buf) { int i; bool success = false; @@ -343,6 +344,16 @@ static bool fw_get_filesystem_firmware(struct firmware_buf *buf) break; } __putname(path); + + if (success) { + dev_dbg(device, "firmware: direct-loading firmware %s\n", + buf->fw_id); + mutex_lock(&fw_lock); + set_bit(FW_STATUS_DONE, &buf->status); + complete_all(&buf->completion); + mutex_unlock(&fw_lock); + } + return success; } @@ -796,99 +807,112 @@ static int fw_add_devm_name(struct device *dev, const char *name) } #endif -static void _request_firmware_cleanup(const struct firmware **firmware_p) +/* wait until the shared firmware_buf becomes ready (or error) */ +static int sync_cached_firmware_buf(struct firmware_buf *buf) { - release_firmware(*firmware_p); - *firmware_p = NULL; + int ret = 0; + + mutex_lock(&fw_lock); + while (!test_bit(FW_STATUS_DONE, &buf->status)) { + if (test_bit(FW_STATUS_ABORT, &buf->status)) { + ret = -ENOENT; + break; + } + mutex_unlock(&fw_lock); + wait_for_completion(&buf->completion); + mutex_lock(&fw_lock); + } + mutex_unlock(&fw_lock); + return ret; } -static struct firmware_priv * -_request_firmware_prepare(const struct firmware **firmware_p, const char *name, - struct device *device, bool uevent, bool nowait) +/* prepare firmware and firmware_buf structs; + * return 0 if a firmware is already assigned, 1 if need to load one, + * or a negative error code + */ +static int +_request_firmware_prepare(struct firmware **firmware_p, const char *name, + struct device *device) { struct firmware *firmware; - struct firmware_priv *fw_priv = NULL; struct firmware_buf *buf; int ret; - if (!firmware_p) - return ERR_PTR(-EINVAL); - *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); if (!firmware) { dev_err(device, "%s: kmalloc(struct firmware) failed\n", __func__); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } if (fw_get_builtin_firmware(firmware, name)) { dev_dbg(device, "firmware: using built-in firmware %s\n", name); - return NULL; + return 0; /* assigned */ } ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); - if (!ret) - fw_priv = fw_create_instance(firmware, name, device, - uevent, nowait); - if (IS_ERR(fw_priv) || ret < 0) { - kfree(firmware); - *firmware_p = NULL; - return ERR_PTR(-ENOMEM); - } else if (fw_priv) { - fw_priv->buf = buf; + /* + * bind with 'buf' now to avoid warning in failure path + * of requesting firmware. + */ + firmware->priv = buf; - /* - * bind with 'buf' now to avoid warning in failure path - * of requesting firmware. - */ - firmware->priv = buf; - return fw_priv; + if (ret > 0) { + ret = sync_cached_firmware_buf(buf); + if (!ret) { + fw_set_page_data(buf, firmware); + return 0; /* assigned */ + } } - /* share the cached buf, which is inprogessing or completed */ - check_status: + if (ret < 0) + return ret; + return 1; /* need to load */ +} + +static int assign_firmware_buf(struct firmware *fw, struct device *device) +{ + struct firmware_buf *buf = fw->priv; + mutex_lock(&fw_lock); - if (test_bit(FW_STATUS_ABORT, &buf->status)) { - fw_priv = ERR_PTR(-ENOENT); - firmware->priv = buf; - _request_firmware_cleanup(firmware_p); - goto exit; - } else if (test_bit(FW_STATUS_DONE, &buf->status)) { - fw_priv = NULL; - fw_set_page_data(buf, firmware); - goto exit; + if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) { + mutex_unlock(&fw_lock); + return -ENOENT; } - mutex_unlock(&fw_lock); - wait_for_completion(&buf->completion); - goto check_status; -exit: + /* + * add firmware name into devres list so that we can auto cache + * and uncache firmware for device. + * + * device may has been deleted already, but the problem + * should be fixed in devres or driver core. + */ + if (device) + fw_add_devm_name(device, buf->fw_id); + + /* + * After caching firmware image is started, let it piggyback + * on request firmware. + */ + if (buf->fwc->state == FW_LOADER_START_CACHE) { + if (fw_cache_piggyback_on_request(buf->fw_id)) + kref_get(&buf->ref); + } + + /* pass the pages buffer to driver at the last minute */ + fw_set_page_data(buf, fw); mutex_unlock(&fw_lock); - return fw_priv; + return 0; } +/* load a firmware via user helper */ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, long timeout) { int retval = 0; 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); - - mutex_lock(&fw_lock); - set_bit(FW_STATUS_DONE, &buf->status); - mutex_unlock(&fw_lock); - complete_all(&buf->completion); - direct_load = 1; - goto handle_fw; - } /* fall back on userspace loading */ buf->fmt = PAGE_BUF; @@ -929,38 +953,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, cancel_delayed_work_sync(&fw_priv->timeout_work); -handle_fw: - mutex_lock(&fw_lock); - if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) - retval = -ENOENT; - - /* - * add firmware name into devres list so that we can auto cache - * and uncache firmware for device. - * - * f_dev->parent may has been deleted already, but the problem - * should be fixed in devres or driver core. - */ - if (!retval && f_dev->parent) - fw_add_devm_name(f_dev->parent, buf->fw_id); - - /* - * After caching firmware image is started, let it piggyback - * on request firmware. - */ - if (!retval && fwc->state == FW_LOADER_START_CACHE) { - if (fw_cache_piggyback_on_request(buf->fw_id)) - kref_get(&buf->ref); - } - - /* pass the pages buffer to driver at the last minute */ - fw_set_page_data(buf, fw_priv->fw); - 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: @@ -972,6 +965,73 @@ err_put_dev: return retval; } +static int fw_load_from_user_helper(struct firmware *firmware, + const char *name, struct device *device, + bool uevent, bool nowait, long timeout) +{ + struct firmware_priv *fw_priv; + + fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); + if (IS_ERR(fw_priv)) + return PTR_ERR(fw_priv); + + fw_priv->buf = firmware->priv; + return _request_firmware_load(fw_priv, uevent, timeout); +} + +/* called from request_firmware() and request_firmware_work_func() */ +static int +_request_firmware(const struct firmware **firmware_p, const char *name, + struct device *device, bool uevent, bool nowait) +{ + struct firmware *fw; + long timeout; + int ret; + + if (!firmware_p) + return -EINVAL; + + ret = _request_firmware_prepare(&fw, name, device); + if (ret <= 0) /* error or already assigned */ + goto out; + + ret = 0; + timeout = firmware_loading_timeout(); + if (nowait) { + timeout = usermodehelper_read_lock_wait(timeout); + if (!timeout) { + dev_dbg(device, "firmware: %s loading timed out\n", + name); + ret = -EBUSY; + goto out; + } + } else { + ret = usermodehelper_read_trylock(); + if (WARN_ON(ret)) { + dev_err(device, "firmware: %s will not be loaded\n", + name); + goto out; + } + } + + if (!fw_get_filesystem_firmware(device, fw->priv)) + ret = fw_load_from_user_helper(fw, name, device, + uevent, nowait, timeout); + if (!ret) + ret = assign_firmware_buf(fw, device); + + usermodehelper_read_unlock(); + + out: + if (ret < 0) { + release_firmware(fw); + fw = NULL; + } + + *firmware_p = fw; + return ret; +} + /** * request_firmware: - send firmware request and wait for it * @firmware_p: pointer to firmware image @@ -996,26 +1056,7 @@ int request_firmware(const struct firmware **firmware_p, const char *name, struct device *device) { - struct firmware_priv *fw_priv; - int ret; - - fw_priv = _request_firmware_prepare(firmware_p, name, device, true, - false); - if (IS_ERR_OR_NULL(fw_priv)) - return PTR_RET(fw_priv); - - ret = usermodehelper_read_trylock(); - if (WARN_ON(ret)) { - dev_err(device, "firmware: %s will not be loaded\n", name); - } else { - ret = _request_firmware_load(fw_priv, true, - firmware_loading_timeout()); - usermodehelper_read_unlock(); - } - if (ret) - _request_firmware_cleanup(firmware_p); - - return ret; + return _request_firmware(firmware_p, name, device, true, false); } /** @@ -1046,33 +1087,13 @@ static void request_firmware_work_func(struct work_struct *work) { struct firmware_work *fw_work; const struct firmware *fw; - struct firmware_priv *fw_priv; - long timeout; - int ret; fw_work = container_of(work, struct firmware_work, work); - fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device, - fw_work->uevent, true); - if (IS_ERR_OR_NULL(fw_priv)) { - ret = PTR_RET(fw_priv); - goto out; - } - timeout = usermodehelper_read_lock_wait(firmware_loading_timeout()); - if (timeout) { - ret = _request_firmware_load(fw_priv, fw_work->uevent, timeout); - usermodehelper_read_unlock(); - } else { - dev_dbg(fw_work->device, "firmware: %s loading timed out\n", - fw_work->name); - ret = -EAGAIN; - } - if (ret) - _request_firmware_cleanup(&fw); - - out: + _request_firmware(&fw, fw_work->name, fw_work->device, + fw_work->uevent, true); fw_work->cont(fw, fw_work->context); - put_device(fw_work->device); + put_device(fw_work->device); /* taken in request_firmware_nowait() */ module_put(fw_work->module); kfree(fw_work); -- cgit v1.1 From 7b1269f778782d2f42994a74bf4014d0cbebbf9f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 31 Jan 2013 11:13:55 +0100 Subject: firmware: Make user-mode helper optional This patch adds a new kconfig, CONFIG_FW_LOADER_USER_HELPER, and guards the user-helper codes in firmware_class.c with ifdefs. Yeah, yeah, there are lots of ifdefs in this patch. The further clean-up with code shuffling follows in the next. Acked-by: Ming Lei Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/Kconfig | 11 +++++++++ drivers/base/firmware_class.c | 57 +++++++++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 13 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index c8b4539..07abd9d 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -145,6 +145,17 @@ config EXTRA_FIRMWARE_DIR this option you can point it elsewhere, such as /lib/firmware/ or some other directory containing the firmware files. +config FW_LOADER_USER_HELPER + bool "Fallback user-helper invocation for firmware loading" + depends on FW_LOADER + default y + help + This option enables / disables the invocation of user-helper + (e.g. udev) for loading firmware files as a fallback after the + direct file loading in kernel fails. The user-mode helper is + no longer required unless you have a special firmware file that + resides in a non-standard path. + config DEBUG_DRIVER bool "Driver Core verbose debug messages" depends on DEBUG_KERNEL diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 40ec60a..84b1c8d 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -88,10 +88,12 @@ enum { FW_STATUS_ABORT, }; +#ifdef CONFIG_FW_LOADER_USER_HELPER enum fw_buf_fmt { VMALLOC_BUF, /* used in direct loading */ PAGE_BUF, /* used in loading via userspace */ }; +#endif /* CONFIG_FW_LOADER_USER_HELPER */ static int loading_timeout = 60; /* In seconds */ @@ -128,12 +130,14 @@ struct firmware_buf { struct completion completion; struct firmware_cache *fwc; unsigned long status; - enum fw_buf_fmt fmt; void *data; size_t size; +#ifdef CONFIG_FW_LOADER_USER_HELPER + enum fw_buf_fmt fmt; struct page **pages; int nr_pages; int page_array_size; +#endif char fw_id[]; }; @@ -142,6 +146,7 @@ struct fw_cache_entry { char name[]; }; +#ifdef CONFIG_FW_LOADER_USER_HELPER struct firmware_priv { struct delayed_work timeout_work; bool nowait; @@ -149,6 +154,7 @@ struct firmware_priv { struct firmware_buf *buf; struct firmware *fw; }; +#endif struct fw_name_devm { unsigned long magic; @@ -182,7 +188,9 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name, strcpy(buf->fw_id, fw_name); buf->fwc = fwc; init_completion(&buf->completion); +#ifdef CONFIG_FW_LOADER_USER_HELPER buf->fmt = VMALLOC_BUF; +#endif pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); @@ -240,7 +248,6 @@ static void __fw_free_buf(struct kref *ref) { struct firmware_buf *buf = to_fwbuf(ref); struct firmware_cache *fwc = buf->fwc; - int i; pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", __func__, buf->fw_id, buf, buf->data, @@ -250,12 +257,15 @@ static void __fw_free_buf(struct kref *ref) spin_unlock(&fwc->lock); +#ifdef CONFIG_FW_LOADER_USER_HELPER if (buf->fmt == PAGE_BUF) { + int i; vunmap(buf->data); for (i = 0; i < buf->nr_pages; i++) __free_page(buf->pages[i]); kfree(buf->pages); } else +#endif vfree(buf->data); kfree(buf); } @@ -357,6 +367,19 @@ static bool fw_get_filesystem_firmware(struct device *device, return success; } +/* firmware holds the ownership of pages */ +static void firmware_free_data(const struct firmware *fw) +{ + /* Loaded directly? */ + if (!fw->priv) { + vfree(fw->data); + return; + } + fw_free_buf(fw->priv); +} + +#ifdef CONFIG_FW_LOADER_USER_HELPER + static struct firmware_priv *to_firmware_priv(struct device *dev) { return container_of(dev, struct firmware_priv, dev); @@ -446,17 +469,6 @@ static ssize_t firmware_loading_show(struct device *dev, return sprintf(buf, "%d\n", loading); } -/* firmware holds the ownership of pages */ -static void firmware_free_data(const struct firmware *fw) -{ - /* Loaded directly? */ - if (!fw->priv) { - vfree(fw->data); - return; - } - fw_free_buf(fw->priv); -} - /* Some architectures don't have PAGE_KERNEL_RO */ #ifndef PAGE_KERNEL_RO #define PAGE_KERNEL_RO PAGE_KERNEL @@ -737,12 +749,15 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, exit: return fw_priv; } +#endif /* CONFIG_FW_LOADER_USER_HELPER */ /* store the pages buffer info firmware from buf */ static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) { fw->priv = buf; +#ifdef CONFIG_FW_LOADER_USER_HELPER fw->pages = buf->pages; +#endif fw->size = buf->size; fw->data = buf->data; @@ -906,6 +921,7 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device) return 0; } +#ifdef CONFIG_FW_LOADER_USER_HELPER /* load a firmware via user helper */ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, long timeout) @@ -978,6 +994,15 @@ static int fw_load_from_user_helper(struct firmware *firmware, fw_priv->buf = firmware->priv; return _request_firmware_load(fw_priv, uevent, timeout); } +#else /* CONFIG_FW_LOADER_USER_HELPER */ +static inline int +fw_load_from_user_helper(struct firmware *firmware, const char *name, + struct device *device, bool uevent, bool nowait, + long timeout) +{ + return -ENOENT; +} +#endif /* CONFIG_FW_LOADER_USER_HELPER */ /* called from request_firmware() and request_firmware_work_func() */ static int @@ -1495,7 +1520,11 @@ static void __init fw_cache_init(void) static int __init firmware_class_init(void) { fw_cache_init(); +#ifdef CONFIG_FW_LOADER_USER_HELPER return class_register(&firmware_class); +#else + return 0; +#endif } static void __exit firmware_class_exit(void) @@ -1504,7 +1533,9 @@ static void __exit firmware_class_exit(void) unregister_syscore_ops(&fw_syscore_ops); unregister_pm_notifier(&fw_cache.pm_notify); #endif +#ifdef CONFIG_FW_LOADER_USER_HELPER class_unregister(&firmware_class); +#endif } fs_initcall(firmware_class_init); -- cgit v1.1 From cd7239fab7d32e56909027bfb5a6c2d7d3d862f8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 31 Jan 2013 11:13:56 +0100 Subject: firmware: Reduce ifdef CONFIG_FW_LOADER_USER_HELPER By shuffling the code, reduce a few ifdefs in firmware_class.c. Also, firmware_buf fmt field is changed to is_pages_buf boolean for simplification. Acked-by: Ming Lei Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 313 ++++++++++++++++++++---------------------- 1 file changed, 151 insertions(+), 162 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 84b1c8d..51e62ca 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -88,13 +88,6 @@ enum { FW_STATUS_ABORT, }; -#ifdef CONFIG_FW_LOADER_USER_HELPER -enum fw_buf_fmt { - VMALLOC_BUF, /* used in direct loading */ - PAGE_BUF, /* used in loading via userspace */ -}; -#endif /* CONFIG_FW_LOADER_USER_HELPER */ - static int loading_timeout = 60; /* In seconds */ static inline long firmware_loading_timeout(void) @@ -133,7 +126,7 @@ struct firmware_buf { void *data; size_t size; #ifdef CONFIG_FW_LOADER_USER_HELPER - enum fw_buf_fmt fmt; + bool is_paged_buf; struct page **pages; int nr_pages; int page_array_size; @@ -146,16 +139,6 @@ struct fw_cache_entry { char name[]; }; -#ifdef CONFIG_FW_LOADER_USER_HELPER -struct firmware_priv { - struct delayed_work timeout_work; - bool nowait; - struct device dev; - struct firmware_buf *buf; - struct firmware *fw; -}; -#endif - struct fw_name_devm { unsigned long magic; char name[]; @@ -188,9 +171,6 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name, strcpy(buf->fw_id, fw_name); buf->fwc = fwc; init_completion(&buf->completion); -#ifdef CONFIG_FW_LOADER_USER_HELPER - buf->fmt = VMALLOC_BUF; -#endif pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); @@ -256,9 +236,8 @@ static void __fw_free_buf(struct kref *ref) list_del(&buf->list); spin_unlock(&fwc->lock); - #ifdef CONFIG_FW_LOADER_USER_HELPER - if (buf->fmt == PAGE_BUF) { + if (buf->is_paged_buf) { int i; vunmap(buf->data); for (i = 0; i < buf->nr_pages; i++) @@ -378,7 +357,89 @@ static void firmware_free_data(const struct firmware *fw) fw_free_buf(fw->priv); } +/* store the pages buffer info firmware from buf */ +static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) +{ + fw->priv = buf; +#ifdef CONFIG_FW_LOADER_USER_HELPER + fw->pages = buf->pages; +#endif + fw->size = buf->size; + fw->data = buf->data; + + pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", + __func__, buf->fw_id, buf, buf->data, + (unsigned int)buf->size); +} + +#ifdef CONFIG_PM_SLEEP +static void fw_name_devm_release(struct device *dev, void *res) +{ + struct fw_name_devm *fwn = res; + + if (fwn->magic == (unsigned long)&fw_cache) + pr_debug("%s: fw_name-%s devm-%p released\n", + __func__, fwn->name, res); +} + +static int fw_devm_match(struct device *dev, void *res, + void *match_data) +{ + struct fw_name_devm *fwn = res; + + return (fwn->magic == (unsigned long)&fw_cache) && + !strcmp(fwn->name, match_data); +} + +static struct fw_name_devm *fw_find_devm_name(struct device *dev, + const char *name) +{ + struct fw_name_devm *fwn; + + fwn = devres_find(dev, fw_name_devm_release, + fw_devm_match, (void *)name); + return fwn; +} + +/* add firmware name into devres list */ +static int fw_add_devm_name(struct device *dev, const char *name) +{ + struct fw_name_devm *fwn; + + fwn = fw_find_devm_name(dev, name); + if (fwn) + return 1; + + fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm) + + strlen(name) + 1, GFP_KERNEL); + if (!fwn) + return -ENOMEM; + + fwn->magic = (unsigned long)&fw_cache; + strcpy(fwn->name, name); + devres_add(dev, fwn); + + return 0; +} +#else +static int fw_add_devm_name(struct device *dev, const char *name) +{ + return 0; +} +#endif + + +/* + * user-mode helper code + */ #ifdef CONFIG_FW_LOADER_USER_HELPER +struct firmware_priv { + struct delayed_work timeout_work; + bool nowait; + struct device dev; + struct firmware_buf *buf; + struct firmware *fw; +}; static struct firmware_priv *to_firmware_priv(struct device *dev) { @@ -477,7 +538,7 @@ static ssize_t firmware_loading_show(struct device *dev, /* one pages buffer should be mapped/unmapped only once */ static int fw_map_pages_buf(struct firmware_buf *buf) { - if (buf->fmt != PAGE_BUF) + if (!buf->is_paged_buf) return 0; if (buf->data) @@ -749,78 +810,89 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, exit: return fw_priv; } -#endif /* CONFIG_FW_LOADER_USER_HELPER */ -/* store the pages buffer info firmware from buf */ -static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) +/* load a firmware via user helper */ +static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, + long timeout) { - fw->priv = buf; -#ifdef CONFIG_FW_LOADER_USER_HELPER - fw->pages = buf->pages; -#endif - fw->size = buf->size; - fw->data = buf->data; + int retval = 0; + struct device *f_dev = &fw_priv->dev; + struct firmware_buf *buf = fw_priv->buf; - pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", - __func__, buf->fw_id, buf, buf->data, - (unsigned int)buf->size); -} + /* fall back on userspace loading */ + buf->is_paged_buf = true; -#ifdef CONFIG_PM_SLEEP -static void fw_name_devm_release(struct device *dev, void *res) -{ - struct fw_name_devm *fwn = res; + dev_set_uevent_suppress(f_dev, true); - if (fwn->magic == (unsigned long)&fw_cache) - pr_debug("%s: fw_name-%s devm-%p released\n", - __func__, fwn->name, res); -} + /* Need to pin this module until class device is destroyed */ + __module_get(THIS_MODULE); -static int fw_devm_match(struct device *dev, void *res, - void *match_data) -{ - struct fw_name_devm *fwn = res; + retval = device_add(f_dev); + if (retval) { + dev_err(f_dev, "%s: device_register failed\n", __func__); + goto err_put_dev; + } - return (fwn->magic == (unsigned long)&fw_cache) && - !strcmp(fwn->name, match_data); -} + retval = device_create_bin_file(f_dev, &firmware_attr_data); + if (retval) { + dev_err(f_dev, "%s: sysfs_create_bin_file failed\n", __func__); + goto err_del_dev; + } -static struct fw_name_devm *fw_find_devm_name(struct device *dev, - const char *name) -{ - struct fw_name_devm *fwn; + retval = device_create_file(f_dev, &dev_attr_loading); + if (retval) { + dev_err(f_dev, "%s: device_create_file failed\n", __func__); + goto err_del_bin_attr; + } - fwn = devres_find(dev, fw_name_devm_release, - fw_devm_match, (void *)name); - return fwn; -} + if (uevent) { + dev_set_uevent_suppress(f_dev, false); + dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); + if (timeout != MAX_SCHEDULE_TIMEOUT) + schedule_delayed_work(&fw_priv->timeout_work, timeout); -/* add firmware name into devres list */ -static int fw_add_devm_name(struct device *dev, const char *name) -{ - struct fw_name_devm *fwn; + kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); + } - fwn = fw_find_devm_name(dev, name); - if (fwn) - return 1; + wait_for_completion(&buf->completion); - fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm) + - strlen(name) + 1, GFP_KERNEL); - if (!fwn) - return -ENOMEM; + cancel_delayed_work_sync(&fw_priv->timeout_work); - fwn->magic = (unsigned long)&fw_cache; - strcpy(fwn->name, name); - devres_add(dev, fwn); + fw_priv->buf = NULL; - return 0; + device_remove_file(f_dev, &dev_attr_loading); +err_del_bin_attr: + device_remove_bin_file(f_dev, &firmware_attr_data); +err_del_dev: + device_del(f_dev); +err_put_dev: + put_device(f_dev); + return retval; } -#else -static int fw_add_devm_name(struct device *dev, const char *name) + +static int fw_load_from_user_helper(struct firmware *firmware, + const char *name, struct device *device, + bool uevent, bool nowait, long timeout) { - return 0; + struct firmware_priv *fw_priv; + + fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); + if (IS_ERR(fw_priv)) + return PTR_ERR(fw_priv); + + fw_priv->buf = firmware->priv; + return _request_firmware_load(fw_priv, uevent, timeout); } -#endif +#else /* CONFIG_FW_LOADER_USER_HELPER */ +static inline int +fw_load_from_user_helper(struct firmware *firmware, const char *name, + struct device *device, bool uevent, bool nowait, + long timeout) +{ + return -ENOENT; +} +#endif /* CONFIG_FW_LOADER_USER_HELPER */ + /* wait until the shared firmware_buf becomes ready (or error) */ static int sync_cached_firmware_buf(struct firmware_buf *buf) @@ -921,89 +993,6 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device) return 0; } -#ifdef CONFIG_FW_LOADER_USER_HELPER -/* load a firmware via user helper */ -static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, - long timeout) -{ - int retval = 0; - struct device *f_dev = &fw_priv->dev; - struct firmware_buf *buf = fw_priv->buf; - - /* fall back on userspace loading */ - buf->fmt = PAGE_BUF; - - dev_set_uevent_suppress(f_dev, true); - - /* Need to pin this module until class device is destroyed */ - __module_get(THIS_MODULE); - - retval = device_add(f_dev); - if (retval) { - dev_err(f_dev, "%s: device_register failed\n", __func__); - goto err_put_dev; - } - - retval = device_create_bin_file(f_dev, &firmware_attr_data); - if (retval) { - dev_err(f_dev, "%s: sysfs_create_bin_file failed\n", __func__); - goto err_del_dev; - } - - retval = device_create_file(f_dev, &dev_attr_loading); - if (retval) { - dev_err(f_dev, "%s: device_create_file failed\n", __func__); - goto err_del_bin_attr; - } - - if (uevent) { - dev_set_uevent_suppress(f_dev, false); - dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); - if (timeout != MAX_SCHEDULE_TIMEOUT) - schedule_delayed_work(&fw_priv->timeout_work, timeout); - - kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); - } - - wait_for_completion(&buf->completion); - - cancel_delayed_work_sync(&fw_priv->timeout_work); - - fw_priv->buf = NULL; - - device_remove_file(f_dev, &dev_attr_loading); -err_del_bin_attr: - device_remove_bin_file(f_dev, &firmware_attr_data); -err_del_dev: - device_del(f_dev); -err_put_dev: - put_device(f_dev); - return retval; -} - -static int fw_load_from_user_helper(struct firmware *firmware, - const char *name, struct device *device, - bool uevent, bool nowait, long timeout) -{ - struct firmware_priv *fw_priv; - - fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); - if (IS_ERR(fw_priv)) - return PTR_ERR(fw_priv); - - fw_priv->buf = firmware->priv; - return _request_firmware_load(fw_priv, uevent, timeout); -} -#else /* CONFIG_FW_LOADER_USER_HELPER */ -static inline int -fw_load_from_user_helper(struct firmware *firmware, const char *name, - struct device *device, bool uevent, bool nowait, - long timeout) -{ - return -ENOENT; -} -#endif /* CONFIG_FW_LOADER_USER_HELPER */ - /* called from request_firmware() and request_firmware_work_func() */ static int _request_firmware(const struct firmware **firmware_p, const char *name, -- cgit v1.1 From 807be03cae191cb88e2f267adcd49aba785c658b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 31 Jan 2013 11:13:57 +0100 Subject: firmware: Ignore abort check when no user-helper is used FW_STATUS_ABORT can be set only during the user-helper invocation, thus we can ignore the check when CONFIG_HW_LOADER_USER_HELPER is disabled. Acked-by: Ming Lei Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 51e62ca..4a223fe 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -454,6 +454,9 @@ static void fw_load_abort(struct firmware_priv *fw_priv) complete_all(&buf->completion); } +#define is_fw_load_aborted(buf) \ + test_bit(FW_STATUS_ABORT, &(buf)->status) + static ssize_t firmware_timeout_show(struct class *class, struct class_attribute *attr, char *buf) @@ -891,6 +894,10 @@ fw_load_from_user_helper(struct firmware *firmware, const char *name, { return -ENOENT; } + +/* No abort during direct loading */ +#define is_fw_load_aborted(buf) false + #endif /* CONFIG_FW_LOADER_USER_HELPER */ @@ -901,7 +908,7 @@ static int sync_cached_firmware_buf(struct firmware_buf *buf) mutex_lock(&fw_lock); while (!test_bit(FW_STATUS_DONE, &buf->status)) { - if (test_bit(FW_STATUS_ABORT, &buf->status)) { + if (is_fw_load_aborted(buf)) { ret = -ENOENT; break; } @@ -963,7 +970,7 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device) struct firmware_buf *buf = fw->priv; mutex_lock(&fw_lock); - if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) { + if (!buf->size || is_fw_load_aborted(buf)) { mutex_unlock(&fw_lock); return -ENOENT; } -- cgit v1.1 From 30b2a553742747d951861a6f582fa90dd9220124 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Sat, 2 Feb 2013 22:50:14 -0700 Subject: regmap: include linux/sched.h to fix build This fixes: drivers/base/regmap/regmap.c: In function 'regmap_async_complete_cb': drivers/base/regmap/regmap.c:1656:3: error: 'TASK_NORMAL' undeclared (first use in this function) drivers/base/regmap/regmap.c:1656:3: note: each undeclared identifier is reported only once for each function it appears in drivers/base/regmap/regmap.c: In function 'regmap_async_complete': drivers/base/regmap/regmap.c:1688:2: error: 'TASK_UNINTERRUPTIBLE' undeclared (first use in this function) drivers/base/regmap/regmap.c:1688:2: error: implicit declaration of function 'schedule' An alternative might be to adjust linux/wait.h to include linux/sched.h, but since that hasn't been done before, I assume we're consciously avoiding doing that. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e57b7bc..0bd5d0a 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -16,6 +16,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include -- cgit v1.1 From f804fb562b0d9f4a8546fa2808d14e80aea8ff26 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 3 Feb 2013 00:14:13 +0800 Subject: regmap: Export regmap_async_complete_cb This fixes below build error when CONFIG_REGMAP=y && CONFIG_REGMAP_SPI=m ERROR: "regmap_async_complete_cb" [drivers/base/regmap/regmap-spi.ko] undefined! make[1]: *** [__modpost] Error 1 make: *** [modules] Error 2 Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 0bd5d0a..6d1e756 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1615,6 +1615,7 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) if (wake) wake_up(&map->async_waitq); } +EXPORT_SYMBOL_GPL(regmap_async_complete_cb); static int regmap_async_is_done(struct regmap *map) { -- cgit v1.1 From f88948eff9a6160ed74e3ee4b12f41f5beeff115 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 5 Feb 2013 13:53:26 +0000 Subject: regmap: Export regmap_async_complete() Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6d1e756..296df36 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1655,6 +1655,7 @@ int regmap_async_complete(struct regmap *map) return ret; } +EXPORT_SYMBOL_GPL(regmap_async_complete); /** * regmap_register_patch: Register and apply register updates to be applied -- cgit v1.1 From 95601d65a1aa0902f838a2919e11ee6311efe371 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 5 Feb 2013 14:14:32 +0000 Subject: regmap: spi: Handle allocation failures gracefully Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-spi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 913274b..4c506bd 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -86,6 +86,8 @@ static struct regmap_async *regmap_spi_async_alloc(void) struct regmap_async_spi *async_spi; async_spi = kzalloc(sizeof(*async_spi), GFP_KERNEL); + if (!async_spi) + return NULL; return &async_spi->core; } -- cgit v1.1 From 9f3b795a626ee79574595e06d1437fe0c7d51d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Fri, 1 Feb 2013 20:40:17 +0100 Subject: driver-core: constify data for class_find_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All in-kernel users of class_find_device() don't really need mutable data for match callback. In two places (kernel/power/suspend_test.c, drivers/scsi/osd/osd_uld.c) this patch changes match callbacks to use const search data. The const is propagated to rtc_class_open() and power_supply_get_by_name() parameters. Note that there's a dev reference leak in suspend_test.c that's not touched in this patch. Signed-off-by: Michał Mirosław Acked-by: Grant Likely Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 4 ++-- drivers/base/core.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/class.c b/drivers/base/class.c index 03243d4..3ce8454 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -420,8 +420,8 @@ EXPORT_SYMBOL_GPL(class_for_each_device); * code. There's no locking restriction. */ struct device *class_find_device(struct class *class, struct device *start, - void *data, - int (*match)(struct device *, void *)) + const void *data, + int (*match)(struct device *, const void *)) { struct class_dev_iter iter; struct device *dev; diff --git a/drivers/base/core.c b/drivers/base/core.c index 27603a6..56536f4b0 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1617,9 +1617,9 @@ struct device *device_create(struct class *class, struct device *parent, } EXPORT_SYMBOL_GPL(device_create); -static int __match_devt(struct device *dev, void *data) +static int __match_devt(struct device *dev, const void *data) { - dev_t *devt = data; + const dev_t *devt = data; return dev->devt == *devt; } -- cgit v1.1 From f3eb83994cb4c172ce5028b5ae7ea08462cc3f1d Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 7 Feb 2013 10:51:56 +0000 Subject: regmap: debugfs: Fix reading in register field units At the moment, if the length of the register field format is N bytes, we can only get anything meaningful back to userspace by providing a buffer that is N + 2 bytes large. Fix this so we that we only need to provide a buffer of N bytes. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index faa93cc..0e94fd3 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -185,7 +185,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, /* If we're in the region the user is trying to read */ if (p >= *ppos) { /* ...but not beyond it */ - if (buf_pos + 1 + map->debugfs_tot_len >= count) + if (buf_pos + map->debugfs_tot_len > count) break; /* Format the register */ -- cgit v1.1 From 7e73c5ae6e7991a6c01f6d096ff8afaef4458c36 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 6 Feb 2013 13:00:36 +0100 Subject: PM: Introduce suspend state PM_SUSPEND_FREEZE PM_SUSPEND_FREEZE state is a general state that does not need any platform specific support, it equals frozen processes + suspended devices + idle processors. Compared with PM_SUSPEND_MEMORY, PM_SUSPEND_FREEZE saves less power because the system is still in a running state. PM_SUSPEND_FREEZE has less resume latency because it does not touch BIOS, and the processors are in idle state. Compared with RTPM/idle, PM_SUSPEND_FREEZE saves more power as 1. the processor has longer sleep time because processes are frozen. The deeper c-state the processor supports, more power saving we can get. 2. PM_SUSPEND_FREEZE uses system suspend code path, thus we can get more power saving from the devices that does not have good RTPM support. This state is useful for 1) platforms that do not have STR, or have a broken STR. 2) platforms that have an extremely low power idle state, which can be used to replace STR. The following describes how PM_SUSPEND_FREEZE state works. 1. echo freeze > /sys/power/state 2. the processes are frozen. 3. all the devices are suspended. 4. all the processors are blocked by a wait queue 5. all the processors idles and enters (Deep) c-state. 6. an interrupt fires. 7. a processor is woken up and handles the irq. 8. if it is a general event, a) the irq handler runs and quites. b) goto step 4. 9. if it is a real wake event, say, power button pressing, keyboard touch, mouse moving, a) the irq handler runs and activate the wakeup source b) wakeup_source_activate() notifies the wait queue. c) system starts resuming from PM_SUSPEND_FREEZE 10. all the devices are resumed. 11. all the processes are unfrozen. 12. system is back to working. Known Issue: The wakeup of this new PM_SUSPEND_FREEZE state may behave differently from the previous suspend state. Take ACPI platform for example, there are some GPEs that only enabled when the system is in sleep state, to wake the system backk from S3/S4. But we are not touching these GPEs during transition to PM_SUSPEND_FREEZE. This means we may lose some wake event. But on the other hand, as we do not disable all the Interrupts during PM_SUSPEND_FREEZE, we may get some extra "wakeup" Interrupts, that are not available for S3/S4. The patches has been tested on an old Sony laptop, and here are the results: Average Power: 1. RPTM/idle for half an hour: 14.8W, 12.6W, 14.1W, 12.5W, 14.4W, 13.2W, 12.9W 2. Freeze for half an hour: 11W, 10.4W, 9.4W, 11.3W 10.5W 3. RTPM/idle for three hours: 11.6W 4. Freeze for three hours: 10W 5. Suspend to Memory: 0.5~0.9W Average Resume Latency: 1. RTPM/idle with a black screen: (From pressing keyboard to screen back) Less than 0.2s 2. Freeze: (From pressing power button to screen back) 2.50s 3. Suspend to Memory: (From pressing power button to screen back) 4.33s >From the results, we can see that all the platforms should benefit from this patch, even if it does not have Low Power S0. Signed-off-by: Zhang Rui Signed-off-by: Rafael J. Wysocki --- drivers/base/power/wakeup.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index e6ee5e8..79715e7 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -382,6 +382,12 @@ static void wakeup_source_activate(struct wakeup_source *ws) { unsigned int cec; + /* + * active wakeup source should bring the system + * out of PM_SUSPEND_FREEZE state + */ + freeze_wake(); + ws->active = true; ws->active_count++; ws->last_time = ktime_get(); -- cgit v1.1 From c2c1ee66016a45477f58f0fd30907b1e959ca76b Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 8 Feb 2013 12:47:14 +0000 Subject: regmap: debugfs: Add a `max_reg' member in struct regmap_debugfs_off_cache We are keeping track of the maximum register as well, this will make things easier for us in sharing this code with the code implementing the register ranges functionality. It also simplifies a bit the calculations when looking for the relevant block:offset from within the cache. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap-debugfs.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 401d191..26b8ffd 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -25,6 +25,7 @@ struct regmap_debugfs_off_cache { off_t min; off_t max; unsigned int base_reg; + unsigned int max_reg; }; struct regmap_format { diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 0e94fd3..3fade1c 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -81,6 +81,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, struct regmap_debugfs_off_cache *c = NULL; loff_t p = 0; unsigned int i, ret; + unsigned int fpos_offset; + unsigned int reg_offset; /* * If we don't have a cache build one so we don't have to do a @@ -93,6 +95,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, regmap_precious(map, i)) { if (c) { c->max = p - 1; + fpos_offset = c->max - c->min; + reg_offset = fpos_offset / map->debugfs_tot_len; + c->max_reg = c->base_reg + reg_offset; list_add_tail(&c->list, &map->debugfs_off_cache); c = NULL; @@ -119,6 +124,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, /* Close the last entry off if we didn't scan beyond it */ if (c) { c->max = p - 1; + fpos_offset = c->max - c->min; + reg_offset = fpos_offset / map->debugfs_tot_len; + c->max_reg = c->base_reg + reg_offset; list_add_tail(&c->list, &map->debugfs_off_cache); } -- cgit v1.1 From cf57d6071f0610c99856c006ac06eb98a151f485 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 8 Feb 2013 12:47:20 +0000 Subject: regmap: debugfs: Optimize seeking within blocks of registers Optimize this so that we can better guess where to start scanning from. We know the length of the register field format, therefore given the file pointer position align to the nearest register field and scan from there onwards. We round down in this calculation and we let the rest of the code figure out where to start scanning from. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 3fade1c..246f459 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -139,15 +139,17 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, WARN_ON(list_empty(&map->debugfs_off_cache)); ret = base; - /* Find the relevant block */ + /* Find the relevant block:offset */ list_for_each_entry(c, &map->debugfs_off_cache, list) { if (from >= c->min && from <= c->max) { - *pos = c->min; - return c->base_reg; + fpos_offset = from - c->min; + reg_offset = fpos_offset / map->debugfs_tot_len; + *pos = c->min + (reg_offset * map->debugfs_tot_len); + return c->base_reg + reg_offset; } - *pos = c->min; - ret = c->base_reg; + *pos = c->max; + ret = c->max_reg; } return ret; -- cgit v1.1 From 4dd7c5531d3b046574da39096b1a66c738aca102 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Mon, 11 Feb 2013 10:50:59 +0000 Subject: regmap: debugfs: Factor out debugfs_tot_len calc into a function In preparation to support the regmap debugfs ranges functionality factor this code out to a separate function. We'll need to ensure that the value has been correctly calculated from two separate places in the code. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 246f459..78d5f20 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -155,6 +155,19 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, return ret; } +static inline void regmap_calc_tot_len(struct regmap *map, + void *buf, size_t count) +{ + /* Calculate the length of a fixed format */ + if (!map->debugfs_tot_len) { + map->debugfs_reg_len = regmap_calc_reg_len(map->max_register, + buf, count); + map->debugfs_val_len = 2 * map->format.val_bytes; + map->debugfs_tot_len = map->debugfs_reg_len + + map->debugfs_val_len + 3; /* : \n */ + } +} + static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, unsigned int to, char __user *user_buf, size_t count, loff_t *ppos) @@ -173,14 +186,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, if (!buf) return -ENOMEM; - /* Calculate the length of a fixed format */ - if (!map->debugfs_tot_len) { - map->debugfs_reg_len = regmap_calc_reg_len(map->max_register, - buf, count); - map->debugfs_val_len = 2 * map->format.val_bytes; - map->debugfs_tot_len = map->debugfs_reg_len + - map->debugfs_val_len + 3; /* : \n */ - } + regmap_calc_tot_len(map, buf, count); /* Work out which register we're starting at */ start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p); -- cgit v1.1 From 878ec67b3ac528a2b6d44055f049cdbb9cfda30c Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 14 Feb 2013 17:39:08 +0100 Subject: regmap: mmio: add register clock support Some mmio devices have a dedicated interface clock that needs to be enabled to access their registers. This patch optionally enables a clock before accessing registers in the regmap_bus callbacks. I added (devm_)regmap_init_mmio_clk variants of the init functions that have an added clk_id string parameter. This is passed to clk_get to request the clock from the clk framework. Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-mmio.c | 79 ++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 13 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index f05fc74..98745dd 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -26,6 +27,7 @@ struct regmap_mmio_context { void __iomem *regs; unsigned val_bytes; + struct clk *clk; }; static int regmap_mmio_gather_write(void *context, @@ -34,9 +36,16 @@ static int regmap_mmio_gather_write(void *context, { struct regmap_mmio_context *ctx = context; u32 offset; + int ret; BUG_ON(reg_size != 4); + if (ctx->clk) { + ret = clk_enable(ctx->clk); + if (ret < 0) + return ret; + } + offset = *(u32 *)reg; while (val_size) { @@ -64,6 +73,9 @@ static int regmap_mmio_gather_write(void *context, offset += ctx->val_bytes; } + if (ctx->clk) + clk_disable(ctx->clk); + return 0; } @@ -80,9 +92,16 @@ static int regmap_mmio_read(void *context, { struct regmap_mmio_context *ctx = context; u32 offset; + int ret; BUG_ON(reg_size != 4); + if (ctx->clk) { + ret = clk_enable(ctx->clk); + if (ret < 0) + return ret; + } + offset = *(u32 *)reg; while (val_size) { @@ -110,11 +129,20 @@ static int regmap_mmio_read(void *context, offset += ctx->val_bytes; } + if (ctx->clk) + clk_disable(ctx->clk); + return 0; } static void regmap_mmio_free_context(void *context) { + struct regmap_mmio_context *ctx = context; + + if (ctx->clk) { + clk_unprepare(ctx->clk); + clk_put(ctx->clk); + } kfree(context); } @@ -128,11 +156,14 @@ static struct regmap_bus regmap_mmio = { .val_format_endian_default = REGMAP_ENDIAN_NATIVE, }; -static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, +static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, + const char *clk_id, + void __iomem *regs, const struct regmap_config *config) { struct regmap_mmio_context *ctx; int min_stride; + int ret; if (config->reg_bits != 32) return ERR_PTR(-EINVAL); @@ -179,37 +210,59 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, ctx->regs = regs; ctx->val_bytes = config->val_bits / 8; + if (clk_id == NULL) + return ctx; + + ctx->clk = clk_get(dev, clk_id); + if (IS_ERR(ctx->clk)) { + ret = PTR_ERR(ctx->clk); + goto err_free; + } + + ret = clk_prepare(ctx->clk); + if (ret < 0) { + clk_put(ctx->clk); + goto err_free; + } + return ctx; + +err_free: + kfree(ctx); + + return ERR_PTR(ret); } /** - * regmap_init_mmio(): Initialise register map + * regmap_init_mmio_clk(): Initialise register map with register clock * * @dev: Device that will be interacted with + * @clk_id: register clock consumer ID * @regs: Pointer to memory-mapped IO region * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. */ -struct regmap *regmap_init_mmio(struct device *dev, - void __iomem *regs, - const struct regmap_config *config) +struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, + void __iomem *regs, + const struct regmap_config *config) { struct regmap_mmio_context *ctx; - ctx = regmap_mmio_gen_context(regs, config); + ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); if (IS_ERR(ctx)) return ERR_CAST(ctx); return regmap_init(dev, ®map_mmio, ctx, config); } -EXPORT_SYMBOL_GPL(regmap_init_mmio); +EXPORT_SYMBOL_GPL(regmap_init_mmio_clk); /** - * devm_regmap_init_mmio(): Initialise managed register map + * devm_regmap_init_mmio_clk(): Initialise managed register map with clock * * @dev: Device that will be interacted with + * @clk_id: register clock consumer ID * @regs: Pointer to memory-mapped IO region * @config: Configuration for register map * @@ -217,18 +270,18 @@ EXPORT_SYMBOL_GPL(regmap_init_mmio); * to a struct regmap. The regmap will be automatically freed by the * device management code. */ -struct regmap *devm_regmap_init_mmio(struct device *dev, - void __iomem *regs, - const struct regmap_config *config) +struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, + void __iomem *regs, + const struct regmap_config *config) { struct regmap_mmio_context *ctx; - ctx = regmap_mmio_gen_context(regs, config); + ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); if (IS_ERR(ctx)) return ERR_CAST(ctx); return devm_regmap_init(dev, ®map_mmio, ctx, config); } -EXPORT_SYMBOL_GPL(devm_regmap_init_mmio); +EXPORT_SYMBOL_GPL(devm_regmap_init_mmio_clk); MODULE_LICENSE("GPL v2"); -- cgit v1.1 From d72cca1eee5b26e313da2a380d4862924e271031 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 14 Feb 2013 18:14:27 +0000 Subject: drivercore: Fix ordering between deferred_probe and exiting initcalls One of the side effects of deferred probe is that some drivers which used to be probed before initcalls completed are now happening slightly later. This causes two problems. - If a console driver gets deferred, then it may not be ready when userspace starts. For example, if a uart depends on pinctrl, then the uart will get deferred and /dev/console will not be available - __init sections will be discarded before built-in drivers are probed. Strictly speaking, __init functions should not be called in a drivers __probe path, but there are a lot of drivers (console stuff again) that do anyway. In the past it was perfectly safe to do so because all built-in drivers got probed before the end of initcalls. This patch fixes the problem by forcing the first pass of the deferred list to complete at late_initcall time. This is late enough to catch the drivers that are known to have the above issues. Signed-off-by: Grant Likely Tested-by: Haojian Zhuang Cc: Arnd Bergmann Cc: Russell King Cc: Linus Torvalds Cc: stable # 3.4+ Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index e3bbed8..61d3e1b 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -172,6 +172,8 @@ static int deferred_probe_initcall(void) driver_deferred_probe_enable = true; driver_deferred_probe_trigger(); + /* Sort as many dependencies as possible before exiting initcalls */ + flush_workqueue(deferred_wq); return 0; } late_initcall(deferred_probe_initcall); -- cgit v1.1 From 74fef7a8fd1d2bd94f925d6638bb4c3049e7c381 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 18 Feb 2013 21:09:03 +0200 Subject: base: memory: fix soft/hard_offline_page permissions those two sysfs files don't have a 'show' method, so they shouldn't have a read permission. Thanks to Greg Kroah-Hartman for actually looking into the source code and figuring out we had a real bug with these two files. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 987604d..83d0b17 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -494,8 +494,8 @@ store_hard_offline_page(struct device *dev, return ret ? ret : count; } -static DEVICE_ATTR(soft_offline_page, 0644, NULL, store_soft_offline_page); -static DEVICE_ATTR(hard_offline_page, 0644, NULL, store_hard_offline_page); +static DEVICE_ATTR(soft_offline_page, S_IWUSR, NULL, store_soft_offline_page); +static DEVICE_ATTR(hard_offline_page, S_IWUSR, NULL, store_hard_offline_page); static __init int memory_fail_init(void) { -- cgit v1.1 From 6677e3eaf4d78abd7b09133414c05dc3ec353e7f Mon Sep 17 00:00:00 2001 From: Yasuaki Ishimatsu Date: Fri, 22 Feb 2013 16:32:52 -0800 Subject: memory-hotplug: check whether all memory blocks are offlined or not when removing memory We remove the memory like this: 1. lock memory hotplug 2. offline a memory block 3. unlock memory hotplug 4. repeat 1-3 to offline all memory blocks 5. lock memory hotplug 6. remove memory(TODO) 7. unlock memory hotplug All memory blocks must be offlined before removing memory. But we don't hold the lock in the whole operation. So we should check whether all memory blocks are offlined before step6. Otherwise, kernel maybe panicked. Offlining a memory block and removing a memory device can be two different operations. Users can just offline some memory blocks without removing the memory device. For this purpose, the kernel has held lock_memory_hotplug() in __offline_pages(). To reuse the code for memory hot-remove, we repeat step 1-3 to offline all the memory blocks, repeatedly lock and unlock memory hotplug, but not hold the memory hotplug lock in the whole operation. Signed-off-by: Wen Congyang Signed-off-by: Yasuaki Ishimatsu Signed-off-by: Tang Chen Acked-by: KAMEZAWA Hiroyuki Cc: KOSAKI Motohiro Cc: Jiang Liu Cc: Jianguo Wu Cc: Kamezawa Hiroyuki Cc: Lai Jiangshan Cc: Wu Jianguo Cc: Ingo Molnar Cc: Thomas Gleixner Cc: "H. Peter Anvin" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/memory.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 83d0b17..a51007b 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -693,6 +693,12 @@ int offline_memory_block(struct memory_block *mem) return ret; } +/* return true if the memory block is offlined, otherwise, return false */ +bool is_memblock_offlined(struct memory_block *mem) +{ + return mem->state == MEM_OFFLINE; +} + /* * Initialize the sysfs support for memory devices... */ -- cgit v1.1 From e823407f7b11fa06ba8e7a2801eb9ed11268a7ec Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 22 Feb 2013 16:34:11 -0800 Subject: pm / runtime: introduce pm_runtime_set_memalloc_noio() Introduce the flag memalloc_noio in 'struct dev_pm_info' to help PM core to teach mm not allocating memory with GFP_KERNEL flag for avoiding probable deadlock. As explained in the comment, any GFP_KERNEL allocation inside runtime_resume() or runtime_suspend() on any one of device in the path from one block or network device to the root device in the device tree may cause deadlock, the introduced pm_runtime_set_memalloc_noio() sets or clears the flag on device in the path recursively. Signed-off-by: Ming Lei Cc: Minchan Kim Cc: Alan Stern Cc: Oliver Neukum Cc: Jiri Kosina Cc: Mel Gorman Cc: KAMEZAWA Hiroyuki Cc: Michal Hocko Cc: Ingo Molnar Cc: Peter Zijlstra Cc: "Rafael J. Wysocki" Cc: Greg KH Cc: Jens Axboe Cc: "David S. Miller" Cc: Eric Dumazet Cc: David Decotigny Cc: Tom Herbert Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/power/runtime.c | 70 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 3148b10..cd92e1c 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -124,6 +124,76 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev) } EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration); +static int dev_memalloc_noio(struct device *dev, void *data) +{ + return dev->power.memalloc_noio; +} + +/* + * pm_runtime_set_memalloc_noio - Set a device's memalloc_noio flag. + * @dev: Device to handle. + * @enable: True for setting the flag and False for clearing the flag. + * + * Set the flag for all devices in the path from the device to the + * root device in the device tree if @enable is true, otherwise clear + * the flag for devices in the path whose siblings don't set the flag. + * + * The function should only be called by block device, or network + * device driver for solving the deadlock problem during runtime + * resume/suspend: + * + * If memory allocation with GFP_KERNEL is called inside runtime + * resume/suspend callback of any one of its ancestors(or the + * block device itself), the deadlock may be triggered inside the + * memory allocation since it might not complete until the block + * device becomes active and the involed page I/O finishes. The + * situation is pointed out first by Alan Stern. Network device + * are involved in iSCSI kind of situation. + * + * The lock of dev_hotplug_mutex is held in the function for handling + * hotplug race because pm_runtime_set_memalloc_noio() may be called + * in async probe(). + * + * The function should be called between device_add() and device_del() + * on the affected device(block/network device). + */ +void pm_runtime_set_memalloc_noio(struct device *dev, bool enable) +{ + static DEFINE_MUTEX(dev_hotplug_mutex); + + mutex_lock(&dev_hotplug_mutex); + for (;;) { + bool enabled; + + /* hold power lock since bitfield is not SMP-safe. */ + spin_lock_irq(&dev->power.lock); + enabled = dev->power.memalloc_noio; + dev->power.memalloc_noio = enable; + spin_unlock_irq(&dev->power.lock); + + /* + * not need to enable ancestors any more if the device + * has been enabled. + */ + if (enabled && enable) + break; + + dev = dev->parent; + + /* + * clear flag of the parent device only if all the + * children don't set the flag because ancestor's + * flag was set by any one of the descendants. + */ + if (!dev || (!enable && + device_for_each_child(dev, NULL, + dev_memalloc_noio))) + break; + } + mutex_unlock(&dev_hotplug_mutex); +} +EXPORT_SYMBOL_GPL(pm_runtime_set_memalloc_noio); + /** * rpm_check_suspend_allowed - Test whether a device may be suspended. * @dev: Device to test. -- cgit v1.1 From db88175f41a29c1ffff1a6938a7969d206a47326 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 22 Feb 2013 16:34:19 -0800 Subject: pm / runtime: force memory allocation with no I/O during Runtime PM callbcack Apply the introduced memalloc_noio_save() and memalloc_noio_restore() to force memory allocation with no I/O during runtime_resume/runtime_suspend callback on device with the flag of 'memalloc_noio' set. Signed-off-by: Ming Lei Cc: "David S. Miller" Cc: Eric Dumazet Cc: David Decotigny Cc: Tom Herbert Cc: Ingo Molnar Cc: Jens Axboe Cc: Minchan Kim Cc: Alan Stern Cc: Oliver Neukum Cc: Jiri Kosina Cc: Mel Gorman Cc: KAMEZAWA Hiroyuki Cc: Michal Hocko Cc: Ingo Molnar Cc: Peter Zijlstra Cc: "Rafael J. Wysocki" Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/power/runtime.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index cd92e1c..1244930 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -348,7 +348,24 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) if (!cb) return -ENOSYS; - retval = __rpm_callback(cb, dev); + if (dev->power.memalloc_noio) { + unsigned int noio_flag; + + /* + * Deadlock might be caused if memory allocation with + * GFP_KERNEL happens inside runtime_suspend and + * runtime_resume callbacks of one block device's + * ancestor or the block device itself. Network + * device might be thought as part of iSCSI block + * device, so network device and its ancestor should + * be marked as memalloc_noio too. + */ + noio_flag = memalloc_noio_save(); + retval = __rpm_callback(cb, dev); + memalloc_noio_restore(noio_flag); + } else { + retval = __rpm_callback(cb, dev); + } dev->power.runtime_error = retval; return retval != -EACCES ? retval : -EIO; -- cgit v1.1 From 3dadecce20603aa380023c65e6f55f108fd5e952 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 24 Jan 2013 02:18:08 -0500 Subject: switch vfs_getattr() to struct path Signed-off-by: Al Viro --- drivers/base/devtmpfs.c | 3 ++- drivers/base/firmware_class.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 17cf7ca..01fc5b0 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -302,7 +302,8 @@ static int handle_remove(const char *nodename, struct device *dev) if (dentry->d_inode) { struct kstat stat; - err = vfs_getattr(parent.mnt, dentry, &stat); + struct path p = {.mnt = parent.mnt, .dentry = dentry}; + err = vfs_getattr(&p, &stat); if (!err && dev_mynode(dev, dentry->d_inode, &stat)) { struct iattr newattrs; /* diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b392b35..a2be09d 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -290,7 +290,7 @@ MODULE_PARM_DESC(path, "customized firmware image search path with a higher prio static noinline_for_stack long fw_file_size(struct file *file) { struct kstat st; - if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st)) + if (vfs_getattr(&file->f_path, &st)) return -1; if (!S_ISREG(st.mode)) return -1; -- cgit v1.1 From f00b4dad9d9eb001a04cf72e8351a2a1b9e99322 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 20 Dec 2012 14:14:23 +0100 Subject: dma-buf: implement vmap refcounting in the interface logic All drivers which implement this need to have some sort of refcount to allow concurrent vmap usage. Hence implement this in the dma-buf core. To protect against concurrent calls we need a lock, which potentially causes new funny locking inversions. But this shouldn't be a problem for exporters with statically allocated backing storage, and more dynamic drivers have decent issues already anyway. Inspired by some refactoring patches from Aaron Plattner, who implemented the same idea, but only for drm/prime drivers. v2: Check in dma_buf_release that no dangling vmaps are left. Suggested by Aaron Plattner. We might want to do similar checks for attachments, but that's for another patch. Also fix up ERR_PTR return for vmap. v3: Check whether the passed-in vmap address matches with the cached one for vunmap. Eventually we might want to remove that parameter - compared to the kmap functions there's no need for the vaddr for unmapping. Suggested by Chris Wilson. v4: Fix a brown-paper-bag bug spotted by Aaron Plattner. Cc: Aaron Plattner Reviewed-by: Aaron Plattner Tested-by: Aaron Plattner Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter Signed-off-by: Sumit Semwal --- drivers/base/dma-buf.c | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index ff5b745..3945238 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c @@ -39,6 +39,8 @@ static int dma_buf_release(struct inode *inode, struct file *file) dmabuf = file->private_data; + BUG_ON(dmabuf->vmapping_counter); + dmabuf->ops->release(dmabuf); kfree(dmabuf); return 0; @@ -481,12 +483,34 @@ EXPORT_SYMBOL_GPL(dma_buf_mmap); */ void *dma_buf_vmap(struct dma_buf *dmabuf) { + void *ptr; + if (WARN_ON(!dmabuf)) return NULL; - if (dmabuf->ops->vmap) - return dmabuf->ops->vmap(dmabuf); - return NULL; + if (!dmabuf->ops->vmap) + return NULL; + + mutex_lock(&dmabuf->lock); + if (dmabuf->vmapping_counter) { + dmabuf->vmapping_counter++; + BUG_ON(!dmabuf->vmap_ptr); + ptr = dmabuf->vmap_ptr; + goto out_unlock; + } + + BUG_ON(dmabuf->vmap_ptr); + + ptr = dmabuf->ops->vmap(dmabuf); + if (IS_ERR_OR_NULL(ptr)) + goto out_unlock; + + dmabuf->vmap_ptr = ptr; + dmabuf->vmapping_counter = 1; + +out_unlock: + mutex_unlock(&dmabuf->lock); + return ptr; } EXPORT_SYMBOL_GPL(dma_buf_vmap); @@ -500,7 +524,16 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) if (WARN_ON(!dmabuf)) return; - if (dmabuf->ops->vunmap) - dmabuf->ops->vunmap(dmabuf, vaddr); + BUG_ON(!dmabuf->vmap_ptr); + BUG_ON(dmabuf->vmapping_counter == 0); + BUG_ON(dmabuf->vmap_ptr != vaddr); + + mutex_lock(&dmabuf->lock); + if (--dmabuf->vmapping_counter == 0) { + if (dmabuf->ops->vunmap) + dmabuf->ops->vunmap(dmabuf, vaddr); + dmabuf->vmap_ptr = NULL; + } + mutex_unlock(&dmabuf->lock); } EXPORT_SYMBOL_GPL(dma_buf_vunmap); -- cgit v1.1 From 495c10cc1c0c359871d5bef32dd173252fc17995 Mon Sep 17 00:00:00 2001 From: John Sheu Date: Mon, 11 Feb 2013 17:50:24 -0800 Subject: CHROMIUM: dma-buf: restore args on failure of dma_buf_mmap Callers to dma_buf_mmap expect to fput() the vma struct's vm_file themselves on failure. Not restoring the struct's data on failure causes a double-decrement of the vm_file's refcount. Signed-off-by: John Sheu Reviewed-by: Daniel Vetter Signed-off-by: Sumit Semwal --- drivers/base/dma-buf.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index 3945238..2a7cb0d 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c @@ -447,6 +447,9 @@ EXPORT_SYMBOL_GPL(dma_buf_kunmap); int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, unsigned long pgoff) { + struct file *oldfile; + int ret; + if (WARN_ON(!dmabuf || !vma)) return -EINVAL; @@ -460,14 +463,22 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, return -EINVAL; /* readjust the vma */ - if (vma->vm_file) - fput(vma->vm_file); - - vma->vm_file = get_file(dmabuf->file); - + get_file(dmabuf->file); + oldfile = vma->vm_file; + vma->vm_file = dmabuf->file; vma->vm_pgoff = pgoff; - return dmabuf->ops->mmap(dmabuf, vma); + ret = dmabuf->ops->mmap(dmabuf, vma); + if (ret) { + /* restore old parameters on failure */ + vma->vm_file = oldfile; + fput(dmabuf->file); + } else { + if (oldfile) + fput(oldfile); + } + return ret; + } EXPORT_SYMBOL_GPL(dma_buf_mmap); -- cgit v1.1 From 283189d3be56aa6db6f192bb255df68493cd79ac Mon Sep 17 00:00:00 2001 From: Li Fei Date: Thu, 28 Feb 2013 15:37:11 +0800 Subject: regmap: irq: call pm_runtime_put in pm_runtime_get_sync failed case Even in failed case of pm_runtime_get_sync, the usage_count is incremented. In order to keep the usage_count with correct value and runtime power management to behave correctly, call pm_runtime_put(_sync) in such case. Signed-off-by Liu Chuansheng Signed-off-by: Li Fei Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 4706c63..020ea2b 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -184,6 +184,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) if (ret < 0) { dev_err(map->dev, "IRQ thread failed to resume: %d\n", ret); + pm_runtime_put(map->dev); return IRQ_NONE; } } -- cgit v1.1 From b81ea1b5ac4d3c6a628158b736dd4a98c46c29d9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 22:48:14 +0100 Subject: PM / QoS: Fix concurrency issues and memory leaks in device PM QoS The current device PM QoS code assumes that certain functions will never be called in parallel with each other (for example, it is assumed that dev_pm_qos_expose_flags() won't be called in parallel with dev_pm_qos_hide_flags() for the same device and analogously for the latency limit), which may be overly optimistic. Moreover, dev_pm_qos_expose_flags() and dev_pm_qos_expose_latency_limit() leak memory in error code paths (req needs to be freed on errors) and __dev_pm_qos_drop_user_request() forgets to free the request. To fix the above issues put more things under the device PM QoS mutex to make them mutually exclusive and add the missing freeing of memory. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/qos.c | 129 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 42 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 3d4d1f8..2159d62 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -344,6 +344,13 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 curr_value; int ret = 0; + if (!req) /*guard against callers passing in null */ + return -EINVAL; + + if (WARN(!dev_pm_qos_request_active(req), + "%s() called for unknown object\n", __func__)) + return -EINVAL; + if (!req->dev->power.qos) return -ENODEV; @@ -386,6 +393,17 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) { int ret; + mutex_lock(&dev_pm_qos_mtx); + 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); + +static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req) +{ + int ret = 0; + if (!req) /*guard against callers passing in null */ return -EINVAL; @@ -393,13 +411,15 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) "%s() called for unknown object\n", __func__)) return -EINVAL; - mutex_lock(&dev_pm_qos_mtx); - ret = __dev_pm_qos_update_request(req, new_value); - mutex_unlock(&dev_pm_qos_mtx); - + if (req->dev->power.qos) { + ret = apply_constraint(req, PM_QOS_REMOVE_REQ, + PM_QOS_DEFAULT_VALUE); + memset(req, 0, sizeof(*req)); + } else { + ret = -ENODEV; + } return ret; } -EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); /** * dev_pm_qos_remove_request - modifies an existing qos request @@ -418,26 +438,10 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); */ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) { - int ret = 0; - - if (!req) /*guard against callers passing in null */ - return -EINVAL; - - if (WARN(!dev_pm_qos_request_active(req), - "%s() called for unknown object\n", __func__)) - return -EINVAL; + int ret; mutex_lock(&dev_pm_qos_mtx); - - if (req->dev->power.qos) { - ret = apply_constraint(req, PM_QOS_REMOVE_REQ, - PM_QOS_DEFAULT_VALUE); - memset(req, 0, sizeof(*req)); - } else { - /* Return if the device has been removed */ - ret = -ENODEV; - } - + ret = __dev_pm_qos_remove_request(req); mutex_unlock(&dev_pm_qos_mtx); return ret; } @@ -563,16 +567,20 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); static void __dev_pm_qos_drop_user_request(struct device *dev, enum dev_pm_qos_req_type type) { + struct dev_pm_qos_request *req = NULL; + switch(type) { case DEV_PM_QOS_LATENCY: - dev_pm_qos_remove_request(dev->power.qos->latency_req); + req = 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); + req = dev->power.qos->flags_req; dev->power.qos->flags_req = NULL; break; } + __dev_pm_qos_remove_request(req); + kfree(req); } /** @@ -588,22 +596,36 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (!device_is_registered(dev) || value < 0) return -EINVAL; - 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, DEV_PM_QOS_LATENCY, value); - if (ret < 0) + if (ret < 0) { + kfree(req); return ret; + } + + mutex_lock(&dev_pm_qos_mtx); + + if (!dev->power.qos) + ret = -ENODEV; + else if (dev->power.qos->latency_req) + ret = -EEXIST; + + if (ret < 0) { + __dev_pm_qos_remove_request(req); + kfree(req); + goto out; + } 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_LATENCY); + out: + mutex_unlock(&dev_pm_qos_mtx); return ret; } EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); @@ -614,10 +636,14 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); */ void dev_pm_qos_hide_latency_limit(struct device *dev) { + mutex_lock(&dev_pm_qos_mtx); + 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); } + + mutex_unlock(&dev_pm_qos_mtx); } EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); @@ -634,24 +660,37 @@ int dev_pm_qos_expose_flags(struct device *dev, s32 val) 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; + if (ret < 0) { + kfree(req); + return ret; + } + + pm_runtime_get_sync(dev); + mutex_lock(&dev_pm_qos_mtx); + + if (!dev->power.qos) + ret = -ENODEV; + else if (dev->power.qos->flags_req) + ret = -EEXIST; + + if (ret < 0) { + __dev_pm_qos_remove_request(req); + kfree(req); + goto out; + } 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: + out: + mutex_unlock(&dev_pm_qos_mtx); pm_runtime_put(dev); return ret; } @@ -663,12 +702,16 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); */ void dev_pm_qos_hide_flags(struct device *dev) { + pm_runtime_get_sync(dev); + mutex_lock(&dev_pm_qos_mtx); + 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); } + + mutex_unlock(&dev_pm_qos_mtx); + pm_runtime_put(dev); } EXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags); @@ -683,12 +726,14 @@ 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); + if (!dev->power.qos || !dev->power.qos->flags_req) { + ret = -EINVAL; + goto out; + } + value = dev_pm_qos_requested_flags(dev); if (set) value |= mask; @@ -697,9 +742,9 @@ int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) ret = __dev_pm_qos_update_request(dev->power.qos->flags_req, value); + out: mutex_unlock(&dev_pm_qos_mtx); pm_runtime_put(dev); - return ret; } #endif /* CONFIG_PM_RUNTIME */ -- cgit v1.1 From 37530f2bda039774bd65aea14cc1d1dd26a82b9e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 4 Mar 2013 14:22:57 +0100 Subject: PM / QoS: Remove device PM QoS sysfs attributes at the right place Device PM QoS sysfs attributes, if present during device removal, are removed from within device_pm_remove(), which is too late, since dpm_sysfs_remove() has already removed the whole attribute group they belonged to. However, moving the removal of those attributes to dpm_sysfs_remove() alone is not sufficient, because in theory they still can be re-added right after being removed by it (the device's driver is still bound to it at that point). For this reason, move the entire desctruction of device PM QoS constraints to dpm_sysfs_remove() and make it prevent any new constraints from being added after it has run. Also, move the initialization of the power.qos field in struct device to device_pm_init_common() and drop the no longer needed dev_pm_qos_constraints_init(). Reported-by: Sasha Levin Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 2 - drivers/base/power/power.h | 8 +-- drivers/base/power/qos.c | 120 ++++++++++++++++++++------------------------- drivers/base/power/sysfs.c | 1 + 4 files changed, 55 insertions(+), 76 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 2b7f77d..15beb50 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -99,7 +99,6 @@ void device_pm_add(struct device *dev) dev_warn(dev, "parent %s should not be sleeping\n", dev_name(dev->parent)); list_add_tail(&dev->power.entry, &dpm_list); - dev_pm_qos_constraints_init(dev); mutex_unlock(&dpm_list_mtx); } @@ -113,7 +112,6 @@ void device_pm_remove(struct device *dev) dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); complete_all(&dev->power.completion); mutex_lock(&dpm_list_mtx); - dev_pm_qos_constraints_destroy(dev); list_del_init(&dev->power.entry); mutex_unlock(&dpm_list_mtx); device_wakeup_disable(dev); diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index b16686a..cfc3226 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -4,7 +4,7 @@ static inline void device_pm_init_common(struct device *dev) { if (!dev->power.early_init) { spin_lock_init(&dev->power.lock); - dev->power.power_state = PMSG_INVALID; + dev->power.qos = NULL; dev->power.early_init = true; } } @@ -56,14 +56,10 @@ extern void device_pm_move_last(struct device *); static inline void device_pm_sleep_init(struct device *dev) {} -static inline void device_pm_add(struct device *dev) -{ - dev_pm_qos_constraints_init(dev); -} +static inline void device_pm_add(struct device *dev) {} static inline void device_pm_remove(struct device *dev) { - dev_pm_qos_constraints_destroy(dev); pm_runtime_remove(dev); } diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 2159d62..5f74587e 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "power.h" @@ -61,7 +62,7 @@ enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask) struct pm_qos_flags *pqf; s32 val; - if (!qos) + if (IS_ERR_OR_NULL(qos)) return PM_QOS_FLAGS_UNDEFINED; pqf = &qos->flags; @@ -101,7 +102,8 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_flags); */ s32 __dev_pm_qos_read_value(struct device *dev) { - return dev->power.qos ? pm_qos_read_value(&dev->power.qos->latency) : 0; + return IS_ERR_OR_NULL(dev->power.qos) ? + 0 : pm_qos_read_value(&dev->power.qos->latency); } /** @@ -198,20 +200,8 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) return 0; } -/** - * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer. - * @dev: target device - * - * Called from the device PM subsystem during device insertion under - * device_pm_lock(). - */ -void dev_pm_qos_constraints_init(struct device *dev) -{ - mutex_lock(&dev_pm_qos_mtx); - dev->power.qos = NULL; - dev->power.power_state = PMSG_ON; - mutex_unlock(&dev_pm_qos_mtx); -} +static void __dev_pm_qos_hide_latency_limit(struct device *dev); +static void __dev_pm_qos_hide_flags(struct device *dev); /** * dev_pm_qos_constraints_destroy @@ -226,16 +216,15 @@ void dev_pm_qos_constraints_destroy(struct device *dev) struct pm_qos_constraints *c; struct pm_qos_flags *f; + mutex_lock(&dev_pm_qos_mtx); + /* * 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_pm_qos_hide_latency_limit(dev); + __dev_pm_qos_hide_flags(dev); - dev->power.power_state = PMSG_INVALID; qos = dev->power.qos; if (!qos) goto out; @@ -257,7 +246,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) } spin_lock_irq(&dev->power.lock); - dev->power.qos = NULL; + dev->power.qos = ERR_PTR(-ENODEV); spin_unlock_irq(&dev->power.lock); kfree(c->notifiers); @@ -301,32 +290,19 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, "%s() called for already added request\n", __func__)) return -EINVAL; - req->dev = dev; - mutex_lock(&dev_pm_qos_mtx); - if (!dev->power.qos) { - if (dev->power.power_state.event == PM_EVENT_INVALID) { - /* The device has been removed from the system. */ - req->dev = NULL; - ret = -ENODEV; - goto out; - } else { - /* - * Allocate the constraints data on the first call to - * add_request, i.e. only if the data is not already - * allocated and if the device has not been removed. - */ - ret = dev_pm_qos_constraints_allocate(dev); - } - } + if (IS_ERR(dev->power.qos)) + ret = -ENODEV; + else if (!dev->power.qos) + ret = dev_pm_qos_constraints_allocate(dev); if (!ret) { + req->dev = dev; req->type = type; ret = apply_constraint(req, PM_QOS_ADD_REQ, value); } - out: mutex_unlock(&dev_pm_qos_mtx); return ret; @@ -351,7 +327,7 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, "%s() called for unknown object\n", __func__)) return -EINVAL; - if (!req->dev->power.qos) + if (IS_ERR_OR_NULL(req->dev->power.qos)) return -ENODEV; switch(req->type) { @@ -402,7 +378,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req) { - int ret = 0; + int ret; if (!req) /*guard against callers passing in null */ return -EINVAL; @@ -411,13 +387,11 @@ static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req) "%s() called for unknown object\n", __func__)) return -EINVAL; - if (req->dev->power.qos) { - ret = apply_constraint(req, PM_QOS_REMOVE_REQ, - PM_QOS_DEFAULT_VALUE); - memset(req, 0, sizeof(*req)); - } else { - ret = -ENODEV; - } + if (IS_ERR_OR_NULL(req->dev->power.qos)) + return -ENODEV; + + ret = apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); + memset(req, 0, sizeof(*req)); return ret; } @@ -466,9 +440,10 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) mutex_lock(&dev_pm_qos_mtx); - if (!dev->power.qos) - ret = dev->power.power_state.event != PM_EVENT_INVALID ? - dev_pm_qos_constraints_allocate(dev) : -ENODEV; + if (IS_ERR(dev->power.qos)) + ret = -ENODEV; + else if (!dev->power.qos) + ret = dev_pm_qos_constraints_allocate(dev); if (!ret) ret = blocking_notifier_chain_register( @@ -497,7 +472,7 @@ 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.qos) + if (!IS_ERR_OR_NULL(dev->power.qos)) retval = blocking_notifier_chain_unregister( dev->power.qos->latency.notifiers, notifier); @@ -608,7 +583,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) mutex_lock(&dev_pm_qos_mtx); - if (!dev->power.qos) + if (IS_ERR_OR_NULL(dev->power.qos)) ret = -ENODEV; else if (dev->power.qos->latency_req) ret = -EEXIST; @@ -630,6 +605,14 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) } EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); +static void __dev_pm_qos_hide_latency_limit(struct device *dev) +{ + if (!IS_ERR_OR_NULL(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); + } +} + /** * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space. * @dev: Device whose PM QoS latency limit is to be hidden from user space. @@ -637,12 +620,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); void dev_pm_qos_hide_latency_limit(struct device *dev) { mutex_lock(&dev_pm_qos_mtx); - - 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); - } - + __dev_pm_qos_hide_latency_limit(dev); mutex_unlock(&dev_pm_qos_mtx); } EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); @@ -673,7 +651,7 @@ int dev_pm_qos_expose_flags(struct device *dev, s32 val) pm_runtime_get_sync(dev); mutex_lock(&dev_pm_qos_mtx); - if (!dev->power.qos) + if (IS_ERR_OR_NULL(dev->power.qos)) ret = -ENODEV; else if (dev->power.qos->flags_req) ret = -EEXIST; @@ -696,6 +674,14 @@ int dev_pm_qos_expose_flags(struct device *dev, s32 val) } EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); +static void __dev_pm_qos_hide_flags(struct device *dev) +{ + if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->flags_req) { + pm_qos_sysfs_remove_flags(dev); + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_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. @@ -704,12 +690,7 @@ void dev_pm_qos_hide_flags(struct device *dev) { pm_runtime_get_sync(dev); mutex_lock(&dev_pm_qos_mtx); - - if (dev->power.qos && dev->power.qos->flags_req) { - pm_qos_sysfs_remove_flags(dev); - __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); - } - + __dev_pm_qos_hide_flags(dev); mutex_unlock(&dev_pm_qos_mtx); pm_runtime_put(dev); } @@ -729,7 +710,7 @@ int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) pm_runtime_get_sync(dev); mutex_lock(&dev_pm_qos_mtx); - if (!dev->power.qos || !dev->power.qos->flags_req) { + if (IS_ERR_OR_NULL(dev->power.qos) || !dev->power.qos->flags_req) { ret = -EINVAL; goto out; } @@ -747,4 +728,7 @@ int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) pm_runtime_put(dev); return ret; } +#else /* !CONFIG_PM_RUNTIME */ +static void __dev_pm_qos_hide_latency_limit(struct device *dev) {} +static void __dev_pm_qos_hide_flags(struct device *dev) {} #endif /* CONFIG_PM_RUNTIME */ diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 50d16e3..a53ebd2 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -708,6 +708,7 @@ void rpm_sysfs_remove(struct device *dev) void dpm_sysfs_remove(struct device *dev) { + dev_pm_qos_constraints_destroy(dev); rpm_sysfs_remove(dev); sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); sysfs_remove_group(&dev->kobj, &pm_attr_group); -- cgit v1.1