From 98a175b60f46a80dfa44fb0e0807f2e5a351f35f Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Sat, 9 Jun 2012 16:40:38 +0530 Subject: regulator: core: Add regulator_set_voltage_time_sel to calculate ramp delay. This patch adds regulator_set_voltage_time_sel(), to move into core, the commonly used code by drivers to provide the .set_voltage_time_sel callback. It will also allow us to configure different ramp delay for different regulators easily. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- include/linux/regulator/driver.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 80226383e56..ae5c2537923 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -170,6 +170,7 @@ enum regulator_type { * * @min_uV: Voltage given by the lowest selector (if linear mapping) * @uV_step: Voltage increase with each selector (if linear mapping) + * @ramp_delay: Time to settle down after voltage change (unit: mV/us) * @volt_table: Voltage mapping table (if table based mapping) * * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ @@ -189,6 +190,7 @@ struct regulator_desc { unsigned int min_uV; unsigned int uV_step; + unsigned int ramp_delay; const unsigned int *volt_table; @@ -285,6 +287,9 @@ int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel); int regulator_is_enabled_regmap(struct regulator_dev *rdev); int regulator_enable_regmap(struct regulator_dev *rdev); int regulator_disable_regmap(struct regulator_dev *rdev); +int regulator_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector); void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data); -- cgit v1.2.3-70-g09d2 From 2177905ca7419c49910d47e38e44790affd918cc Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 11 Jun 2012 23:56:09 -0700 Subject: Input: fix input.h kernel-doc warning Fix kernel-doc warning in input.h: Warning(include/linux/input.h:140): No description found for parameter 'len' Signed-off-by: Randy Dunlap Signed-off-by: Dmitry Torokhov --- include/linux/input.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/input.h b/include/linux/input.h index a8167145357..2740d080ec6 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -116,6 +116,7 @@ struct input_keymap_entry { /** * EVIOCGMTSLOTS(len) - get MT slot values + * @len: size of the data buffer in bytes * * The ioctl buffer argument should be binary equivalent to * -- cgit v1.2.3-70-g09d2 From b4bc9ef6253578ecc71eec79a7dd423d0a282a4b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 13 Jun 2012 11:27:11 +0800 Subject: regulator: tps65217: Convert to regulator_[is_enabled|get_voltage_sel]_regmap This patch converts .is_enabled and .get_voltage_sel to regulator_is_enabled_regmap and regulator_get_voltage_sel_regmap. For .enable, .disable, and .set_voltage_sel, the write protect level is either 1 or 2. So we cannot use regulator_[enable|disable|set_voltage_sel]_regmap. Now we store the enable reg/mask and vsel reg/mask in regulator_desc, so we can remove enable_mask, set_vout_reg, and set_vout_mask from struct tps_info. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65217-regulator.c | 114 ++++++++++++--------------------- include/linux/mfd/tps65217.h | 6 -- 2 files changed, 42 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 9d371d2cbca..f5fa05b5bea 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -26,7 +26,7 @@ #include #include -#define TPS65217_REGULATOR(_name, _id, _ops, _n) \ +#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em) \ { \ .name = _name, \ .id = _id, \ @@ -34,9 +34,13 @@ .n_voltages = _n, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .vsel_reg = _vr, \ + .vsel_mask = _vm, \ + .enable_reg = TPS65217_REG_ENABLE, \ + .enable_mask = _em, \ } \ -#define TPS65217_INFO(_nm, _min, _max, _f1, _f2, _t, _n, _em, _vr, _vm) \ +#define TPS65217_INFO(_nm, _min, _max, _f1, _f2, _t, _n)\ { \ .name = _nm, \ .min_uV = _min, \ @@ -45,9 +49,6 @@ .uv_to_vsel = _f2, \ .table = _t, \ .table_len = _n, \ - .enable_mask = _em, \ - .set_vout_reg = _vr, \ - .set_vout_mask = _vm, \ } static const int LDO1_VSEL_table[] = { @@ -127,46 +128,21 @@ static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel) static struct tps_info tps65217_pmic_regs[] = { TPS65217_INFO("DCDC1", 900000, 1800000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC1_EN, - TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("DCDC2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC2_EN, - TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("DCDC3", 900000, 1500000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC3_EN, - TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL, LDO1_VSEL_table, - 16, TPS65217_ENABLE_LDO1_EN, TPS65217_REG_DEFLDO1, - TPS65217_DEFLDO1_LDO1_MASK), + 16), TPS65217_INFO("LDO2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_LDO2_EN, - TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("LDO3", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32, - TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN, - TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK), + tps65217_uv_to_vsel2, NULL, 32), TPS65217_INFO("LDO4", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32, - TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN, - TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK), + tps65217_uv_to_vsel2, NULL, 32), }; -static int tps65217_pmic_is_enabled(struct regulator_dev *dev) -{ - int ret; - struct tps65217 *tps = rdev_get_drvdata(dev); - unsigned int data, rid = rdev_get_id(dev); - - if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) - return -EINVAL; - - ret = tps65217_reg_read(tps, TPS65217_REG_ENABLE, &data); - if (ret) - return ret; - - return (data & tps->info[rid]->enable_mask) ? 1 : 0; -} - static int tps65217_pmic_enable(struct regulator_dev *dev) { struct tps65217 *tps = rdev_get_drvdata(dev); @@ -177,9 +153,8 @@ static int tps65217_pmic_enable(struct regulator_dev *dev) /* Enable the regulator and password protection is level 1 */ return tps65217_set_bits(tps, TPS65217_REG_ENABLE, - tps->info[rid]->enable_mask, - tps->info[rid]->enable_mask, - TPS65217_PROTECT_L1); + dev->desc->enable_mask, dev->desc->enable_mask, + TPS65217_PROTECT_L1); } static int tps65217_pmic_disable(struct regulator_dev *dev) @@ -192,25 +167,7 @@ static int tps65217_pmic_disable(struct regulator_dev *dev) /* Disable the regulator and password protection is level 1 */ return tps65217_clear_bits(tps, TPS65217_REG_ENABLE, - tps->info[rid]->enable_mask, TPS65217_PROTECT_L1); -} - -static int tps65217_pmic_get_voltage_sel(struct regulator_dev *dev) -{ - int ret; - struct tps65217 *tps = rdev_get_drvdata(dev); - unsigned int selector, rid = rdev_get_id(dev); - - if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) - return -EINVAL; - - ret = tps65217_reg_read(tps, tps->info[rid]->set_vout_reg, &selector); - if (ret) - return ret; - - selector &= tps->info[rid]->set_vout_mask; - - return selector; + dev->desc->enable_mask, TPS65217_PROTECT_L1); } static int tps65217_pmic_set_voltage_sel(struct regulator_dev *dev, @@ -221,8 +178,7 @@ static int tps65217_pmic_set_voltage_sel(struct regulator_dev *dev, unsigned int rid = rdev_get_id(dev); /* Set the voltage based on vsel value and write protect level is 2 */ - ret = tps65217_set_bits(tps, tps->info[rid]->set_vout_reg, - tps->info[rid]->set_vout_mask, + ret = tps65217_set_bits(tps, dev->desc->vsel_reg, dev->desc->vsel_mask, selector, TPS65217_PROTECT_L2); /* Set GO bit for DCDCx to initiate voltage transistion */ @@ -285,10 +241,10 @@ static int tps65217_pmic_list_voltage(struct regulator_dev *dev, /* Operations permitted on DCDCx, LDO2, LDO3 and LDO4 */ static struct regulator_ops tps65217_pmic_ops = { - .is_enabled = tps65217_pmic_is_enabled, + .is_enabled = regulator_is_enabled_regmap, .enable = tps65217_pmic_enable, .disable = tps65217_pmic_disable, - .get_voltage_sel = tps65217_pmic_get_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = tps65217_pmic_set_voltage_sel, .list_voltage = tps65217_pmic_list_voltage, .map_voltage = tps65217_pmic_map_voltage, @@ -296,22 +252,36 @@ static struct regulator_ops tps65217_pmic_ops = { /* Operations permitted on LDO1 */ static struct regulator_ops tps65217_pmic_ldo1_ops = { - .is_enabled = tps65217_pmic_is_enabled, + .is_enabled = regulator_is_enabled_regmap, .enable = tps65217_pmic_enable, .disable = tps65217_pmic_disable, - .get_voltage_sel = tps65217_pmic_get_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = tps65217_pmic_set_voltage_sel, .list_voltage = tps65217_pmic_list_voltage, }; static const struct regulator_desc regulators[] = { - TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16), - TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32), - TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32), + TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC1_EN), + TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC2_EN), + TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC3_EN), + TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16, + TPS65217_REG_DEFLDO1, TPS65217_DEFLDO1_LDO1_MASK, + TPS65217_ENABLE_LDO1_EN), + TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64, + TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK, + TPS65217_ENABLE_LDO2_EN), + TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32, + TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK, + TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN), + TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32, + TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK, + TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN), }; static int __devinit tps65217_regulator_probe(struct platform_device *pdev) diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index e030ef9a64e..4e035a41a9b 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -229,9 +229,6 @@ struct tps65217_board { * @uv_to_vsel: Function pointer to get selector from voltage * @table: Table for non-uniform voltage step-size * @table_len: Length of the voltage table - * @enable_mask: Regulator enable mask bits - * @set_vout_reg: Regulator output voltage set register - * @set_vout_mask: Regulator output voltage set mask * * This data is used to check the regualtor voltage limits while setting. */ @@ -243,9 +240,6 @@ struct tps_info { int (*uv_to_vsel)(int uV, unsigned int *vsel); const int *table; unsigned int table_len; - unsigned int enable_mask; - unsigned int set_vout_reg; - unsigned int set_vout_mask; }; /** -- cgit v1.2.3-70-g09d2 From 047fe3605235888f3ebcda0c728cb31937eadfe6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 12 Jun 2012 15:24:40 +0200 Subject: splice: fix racy pipe->buffers uses Dave Jones reported a kernel BUG at mm/slub.c:3474! triggered by splice_shrink_spd() called from vmsplice_to_pipe() commit 35f3d14dbbc5 (pipe: add support for shrinking and growing pipes) added capability to adjust pipe->buffers. Problem is some paths don't hold pipe mutex and assume pipe->buffers doesn't change for their duration. Fix this by adding nr_pages_max field in struct splice_pipe_desc, and use it in place of pipe->buffers where appropriate. splice_shrink_spd() loses its struct pipe_inode_info argument. Reported-by: Dave Jones Signed-off-by: Eric Dumazet Cc: Jens Axboe Cc: Alexander Viro Cc: Tom Herbert Cc: stable # 2.6.35 Tested-by: Dave Jones Signed-off-by: Jens Axboe --- fs/splice.c | 35 ++++++++++++++++++++--------------- include/linux/splice.h | 8 ++++---- kernel/relay.c | 5 +++-- kernel/trace/trace.c | 6 ++++-- mm/shmem.c | 3 ++- net/core/skbuff.c | 1 + 6 files changed, 34 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/fs/splice.c b/fs/splice.c index c9f1318a3b8..7bf08fa22ec 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -273,13 +273,16 @@ void spd_release_page(struct splice_pipe_desc *spd, unsigned int i) * Check if we need to grow the arrays holding pages and partial page * descriptions. */ -int splice_grow_spd(struct pipe_inode_info *pipe, struct splice_pipe_desc *spd) +int splice_grow_spd(const struct pipe_inode_info *pipe, struct splice_pipe_desc *spd) { - if (pipe->buffers <= PIPE_DEF_BUFFERS) + unsigned int buffers = ACCESS_ONCE(pipe->buffers); + + spd->nr_pages_max = buffers; + if (buffers <= PIPE_DEF_BUFFERS) return 0; - spd->pages = kmalloc(pipe->buffers * sizeof(struct page *), GFP_KERNEL); - spd->partial = kmalloc(pipe->buffers * sizeof(struct partial_page), GFP_KERNEL); + spd->pages = kmalloc(buffers * sizeof(struct page *), GFP_KERNEL); + spd->partial = kmalloc(buffers * sizeof(struct partial_page), GFP_KERNEL); if (spd->pages && spd->partial) return 0; @@ -289,10 +292,9 @@ int splice_grow_spd(struct pipe_inode_info *pipe, struct splice_pipe_desc *spd) return -ENOMEM; } -void splice_shrink_spd(struct pipe_inode_info *pipe, - struct splice_pipe_desc *spd) +void splice_shrink_spd(struct splice_pipe_desc *spd) { - if (pipe->buffers <= PIPE_DEF_BUFFERS) + if (spd->nr_pages_max <= PIPE_DEF_BUFFERS) return; kfree(spd->pages); @@ -315,6 +317,7 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, struct splice_pipe_desc spd = { .pages = pages, .partial = partial, + .nr_pages_max = PIPE_DEF_BUFFERS, .flags = flags, .ops = &page_cache_pipe_buf_ops, .spd_release = spd_release_page, @@ -326,7 +329,7 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, index = *ppos >> PAGE_CACHE_SHIFT; loff = *ppos & ~PAGE_CACHE_MASK; req_pages = (len + loff + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - nr_pages = min(req_pages, pipe->buffers); + nr_pages = min(req_pages, spd.nr_pages_max); /* * Lookup the (hopefully) full range of pages we need. @@ -497,7 +500,7 @@ fill_it: if (spd.nr_pages) error = splice_to_pipe(pipe, &spd); - splice_shrink_spd(pipe, &spd); + splice_shrink_spd(&spd); return error; } @@ -598,6 +601,7 @@ ssize_t default_file_splice_read(struct file *in, loff_t *ppos, struct splice_pipe_desc spd = { .pages = pages, .partial = partial, + .nr_pages_max = PIPE_DEF_BUFFERS, .flags = flags, .ops = &default_pipe_buf_ops, .spd_release = spd_release_page, @@ -608,8 +612,8 @@ ssize_t default_file_splice_read(struct file *in, loff_t *ppos, res = -ENOMEM; vec = __vec; - if (pipe->buffers > PIPE_DEF_BUFFERS) { - vec = kmalloc(pipe->buffers * sizeof(struct iovec), GFP_KERNEL); + if (spd.nr_pages_max > PIPE_DEF_BUFFERS) { + vec = kmalloc(spd.nr_pages_max * sizeof(struct iovec), GFP_KERNEL); if (!vec) goto shrink_ret; } @@ -617,7 +621,7 @@ ssize_t default_file_splice_read(struct file *in, loff_t *ppos, offset = *ppos & ~PAGE_CACHE_MASK; nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - for (i = 0; i < nr_pages && i < pipe->buffers && len; i++) { + for (i = 0; i < nr_pages && i < spd.nr_pages_max && len; i++) { struct page *page; page = alloc_page(GFP_USER); @@ -665,7 +669,7 @@ ssize_t default_file_splice_read(struct file *in, loff_t *ppos, shrink_ret: if (vec != __vec) kfree(vec); - splice_shrink_spd(pipe, &spd); + splice_shrink_spd(&spd); return res; err: @@ -1614,6 +1618,7 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov, struct splice_pipe_desc spd = { .pages = pages, .partial = partial, + .nr_pages_max = PIPE_DEF_BUFFERS, .flags = flags, .ops = &user_page_pipe_buf_ops, .spd_release = spd_release_page, @@ -1629,13 +1634,13 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov, spd.nr_pages = get_iovec_page_array(iov, nr_segs, spd.pages, spd.partial, false, - pipe->buffers); + spd.nr_pages_max); if (spd.nr_pages <= 0) ret = spd.nr_pages; else ret = splice_to_pipe(pipe, &spd); - splice_shrink_spd(pipe, &spd); + splice_shrink_spd(&spd); return ret; } diff --git a/include/linux/splice.h b/include/linux/splice.h index 26e5b613ded..09a545a7dfa 100644 --- a/include/linux/splice.h +++ b/include/linux/splice.h @@ -51,7 +51,8 @@ struct partial_page { struct splice_pipe_desc { struct page **pages; /* page map */ struct partial_page *partial; /* pages[] may not be contig */ - int nr_pages; /* number of pages in map */ + int nr_pages; /* number of populated pages in map */ + unsigned int nr_pages_max; /* pages[] & partial[] arrays size */ unsigned int flags; /* splice flags */ const struct pipe_buf_operations *ops;/* ops associated with output pipe */ void (*spd_release)(struct splice_pipe_desc *, unsigned int); @@ -85,9 +86,8 @@ extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *, /* * for dynamic pipe sizing */ -extern int splice_grow_spd(struct pipe_inode_info *, struct splice_pipe_desc *); -extern void splice_shrink_spd(struct pipe_inode_info *, - struct splice_pipe_desc *); +extern int splice_grow_spd(const struct pipe_inode_info *, struct splice_pipe_desc *); +extern void splice_shrink_spd(struct splice_pipe_desc *); extern void spd_release_page(struct splice_pipe_desc *, unsigned int); extern const struct pipe_buf_operations page_cache_pipe_buf_ops; diff --git a/kernel/relay.c b/kernel/relay.c index ab56a1764d4..e8cd2027abb 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -1235,6 +1235,7 @@ static ssize_t subbuf_splice_actor(struct file *in, struct splice_pipe_desc spd = { .pages = pages, .nr_pages = 0, + .nr_pages_max = PIPE_DEF_BUFFERS, .partial = partial, .flags = flags, .ops = &relay_pipe_buf_ops, @@ -1302,8 +1303,8 @@ static ssize_t subbuf_splice_actor(struct file *in, ret += padding; out: - splice_shrink_spd(pipe, &spd); - return ret; + splice_shrink_spd(&spd); + return ret; } static ssize_t relay_file_splice_read(struct file *in, diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 68032c6177d..28848808222 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -3609,6 +3609,7 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, .pages = pages_def, .partial = partial_def, .nr_pages = 0, /* This gets updated below. */ + .nr_pages_max = PIPE_DEF_BUFFERS, .flags = flags, .ops = &tracing_pipe_buf_ops, .spd_release = tracing_spd_release_pipe, @@ -3680,7 +3681,7 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, ret = splice_to_pipe(pipe, &spd); out: - splice_shrink_spd(pipe, &spd); + splice_shrink_spd(&spd); return ret; out_err: @@ -4231,6 +4232,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, struct splice_pipe_desc spd = { .pages = pages_def, .partial = partial_def, + .nr_pages_max = PIPE_DEF_BUFFERS, .flags = flags, .ops = &buffer_pipe_buf_ops, .spd_release = buffer_spd_release, @@ -4318,7 +4320,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, } ret = splice_to_pipe(pipe, &spd); - splice_shrink_spd(pipe, &spd); + splice_shrink_spd(&spd); out: return ret; } diff --git a/mm/shmem.c b/mm/shmem.c index 585bd220a21..c244e93a70f 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1577,6 +1577,7 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos, struct splice_pipe_desc spd = { .pages = pages, .partial = partial, + .nr_pages_max = PIPE_DEF_BUFFERS, .flags = flags, .ops = &page_cache_pipe_buf_ops, .spd_release = spd_release_page, @@ -1665,7 +1666,7 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos, if (spd.nr_pages) error = splice_to_pipe(pipe, &spd); - splice_shrink_spd(pipe, &spd); + splice_shrink_spd(&spd); if (error > 0) { *ppos += error; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 016694d6248..bac3c5756d6 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1755,6 +1755,7 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset, struct splice_pipe_desc spd = { .pages = pages, .partial = partial, + .nr_pages_max = MAX_SKB_FRAGS, .flags = flags, .ops = &sock_pipe_buf_ops, .spd_release = sock_spd_release, -- cgit v1.2.3-70-g09d2 From 76aaa5101fffaef12b45b4c01ed0d0528f23dedf Mon Sep 17 00:00:00 2001 From: Asias He Date: Thu, 14 Jun 2012 09:04:07 +0200 Subject: block: Drop dead function blk_abort_queue() This function was only used by btrfs code in btrfs_abort_devices() (seems in a wrong way). It was removed in commit d07eb9117050c9ed3f78296ebcc06128b52693be, So, Let's remove the dead code to avoid any confusion. Changes in v2: update commit log, btrfs_abort_devices() was removed already. Cc: Jens Axboe Cc: linux-kernel@vger.kernel.org Cc: Chris Mason Cc: linux-btrfs@vger.kernel.org Cc: David Sterba Signed-off-by: Asias He Signed-off-by: Jens Axboe --- block/blk-timeout.c | 41 ----------------------------------------- include/linux/blkdev.h | 1 - 2 files changed, 42 deletions(-) (limited to 'include') diff --git a/block/blk-timeout.c b/block/blk-timeout.c index 78035488895..6e4744cbfb5 100644 --- a/block/blk-timeout.c +++ b/block/blk-timeout.c @@ -197,44 +197,3 @@ void blk_add_timer(struct request *req) mod_timer(&q->timeout, expiry); } -/** - * blk_abort_queue -- Abort all request on given queue - * @queue: pointer to queue - * - */ -void blk_abort_queue(struct request_queue *q) -{ - unsigned long flags; - struct request *rq, *tmp; - LIST_HEAD(list); - - /* - * Not a request based block device, nothing to abort - */ - if (!q->request_fn) - return; - - spin_lock_irqsave(q->queue_lock, flags); - - elv_abort_queue(q); - - /* - * Splice entries to local list, to avoid deadlocking if entries - * get readded to the timeout list by error handling - */ - list_splice_init(&q->timeout_list, &list); - - list_for_each_entry_safe(rq, tmp, &list, timeout_list) - blk_abort_request(rq); - - /* - * Occasionally, blk_abort_request() will return without - * deleting the element from the list. Make sure we add those back - * instead of leaving them on the local stack list. - */ - list_splice(&list, &q->timeout_list); - - spin_unlock_irqrestore(q->queue_lock, flags); - -} -EXPORT_SYMBOL_GPL(blk_abort_queue); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index ba43f408baa..07954b05b86 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -827,7 +827,6 @@ extern bool __blk_end_request_err(struct request *rq, int error); extern void blk_complete_request(struct request *); extern void __blk_complete_request(struct request *); extern void blk_abort_request(struct request *); -extern void blk_abort_queue(struct request_queue *); extern void blk_unprep_request(struct request *); /* -- cgit v1.2.3-70-g09d2 From 6f0b2c696ca340cc2da381fe693fda3f8fdb2149 Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Mon, 11 Jun 2012 17:41:08 +0530 Subject: regulator: Add ramp_delay configuration to constraints For some hardwares ramp_delay for BUCKs is a configurable parameter which can be configured through DT or board file.This patch adds ramp_delay to regulator constraints and allow user to configure it for regulators which supports this feature, through DT or board file. It will provide two ways of setting the ramp_delay for a regulator: First, by setting it as constraints in board file(for configurable regulators) and set_machine_constraints() will take care of setting it on hardware by calling(the provided) .set_ramp_delay() operation(callback). Second, by setting it as data in regulator_desc(as fixed/default ramp_delay rate) for a regulator in driver. regulator_set_voltage_time_sel() will give preference to constraints->ramp_delay while reading ramp_delay rate for regulator. Similarly users should also take care accordingly while refering ramp_delay rate(in case of implementing their private .set_voltage_time_sel() callbacks for different regulators). [Rewrote subject for 80 columns -- broonie] Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/regulator.txt | 1 + drivers/regulator/core.c | 23 ++++++++++++++++++---- drivers/regulator/of_regulator.c | 6 +++++- include/linux/regulator/driver.h | 3 +++ include/linux/regulator/machine.h | 3 +++ 5 files changed, 31 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index 5b7a408acda..d0a7b1296a3 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -10,6 +10,7 @@ Optional properties: - regulator-always-on: boolean, regulator should never be disabled - regulator-boot-on: bootloader/firmware enabled regulator - -supply: phandle to the parent supply/regulator node +- regulator-ramp-delay: ramp delay for regulator(in mV/uS) Example: diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 6ffca9b3238..b615ae6606d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -967,6 +967,14 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } + if (rdev->constraints->ramp_delay && ops->set_ramp_delay) { + ret = ops->set_ramp_delay(rdev, rdev->constraints->ramp_delay); + if (ret < 0) { + rdev_err(rdev, "failed to set ramp_delay\n"); + goto out; + } + } + print_constraints(rdev); return 0; out: @@ -2296,10 +2304,17 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, unsigned int new_selector) { - if (rdev->desc->ramp_delay && rdev->desc->uV_step) - return DIV_ROUND_UP(rdev->desc->uV_step * - abs(new_selector - old_selector), - rdev->desc->ramp_delay * 1000); + if (rdev->desc->uV_step) { + if (rdev->constraints->ramp_delay) + return DIV_ROUND_UP(rdev->desc->uV_step * + abs(new_selector - old_selector), + rdev->constraints->ramp_delay * 1000); + if (rdev->desc->ramp_delay) + return DIV_ROUND_UP(rdev->desc->uV_step * + abs(new_selector - old_selector), + rdev->desc->ramp_delay * 1000); + rdev_warn(rdev, "ramp_delay not set\n"); + } return 0; } diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 56593b75168..e2a73107906 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -20,7 +20,7 @@ static void of_get_regulation_constraints(struct device_node *np, struct regulator_init_data **init_data) { const __be32 *min_uV, *max_uV, *uV_offset; - const __be32 *min_uA, *max_uA; + const __be32 *min_uA, *max_uA, *ramp_delay; struct regulation_constraints *constraints = &(*init_data)->constraints; constraints->name = of_get_property(np, "regulator-name", NULL); @@ -60,6 +60,10 @@ static void of_get_regulation_constraints(struct device_node *np, constraints->always_on = true; else /* status change should be possible if not always on. */ constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS; + + ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL); + if (ramp_delay) + constraints->min_uV = be32_to_cpu(*ramp_delay); } /** diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index ae5c2537923..ddc155d262d 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -67,6 +67,8 @@ enum regulator_status { * * @enable_time: Time taken for the regulator voltage output voltage to * stabilise after being enabled, in microseconds. + * @set_ramp_delay: Set the ramp delay for the regulator. The driver should + * select ramp delay equal to or less than(closest) ramp_delay. * @set_voltage_time_sel: Time taken for the regulator voltage output voltage * to stabilise after being set to a new value, in microseconds. * The function provides the from and to voltage selector, the @@ -113,6 +115,7 @@ struct regulator_ops { /* Time taken to enable or set voltage on the regulator */ int (*enable_time) (struct regulator_dev *); + int (*set_ramp_delay) (struct regulator_dev *, int ramp_delay); int (*set_voltage_time_sel) (struct regulator_dev *, unsigned int old_selector, unsigned int new_selector); diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index b02108446be..5f37ad3cc17 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -92,6 +92,7 @@ struct regulator_state { * mode. * @initial_state: Suspend state to set by default. * @initial_mode: Mode to set at startup. + * @ramp_delay: Time to settle down after voltage change (unit: mV/us) */ struct regulation_constraints { @@ -125,6 +126,8 @@ struct regulation_constraints { /* mode to set on startup */ unsigned int initial_mode; + unsigned int ramp_delay; + /* constraint flags */ unsigned always_on:1; /* regulator never off when system is on */ unsigned boot_on:1; /* bootloader/firmware enabled regulator */ -- cgit v1.2.3-70-g09d2 From ea38d13fd1666bc030cb1c0feec5b0da2f89f9b2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 18 Jun 2012 14:03:16 +0800 Subject: regulator: core: Change the unit of ramp_delay from mV/uS to uV/uS This change makes it possible to set ramp_delay with 0.xxx mV/uS without truncation issue. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/regulator.txt | 2 +- drivers/regulator/core.c | 4 ++-- include/linux/regulator/driver.h | 2 +- include/linux/regulator/machine.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index d0a7b1296a3..bec5d574741 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -10,7 +10,7 @@ Optional properties: - regulator-always-on: boolean, regulator should never be disabled - regulator-boot-on: bootloader/firmware enabled regulator - -supply: phandle to the parent supply/regulator node -- regulator-ramp-delay: ramp delay for regulator(in mV/uS) +- regulator-ramp-delay: ramp delay for regulator(in uV/uS) Example: diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 73a3d874ca6..ce0a3462e0d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2319,11 +2319,11 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, if (rdev->desc->uV_step) { return DIV_ROUND_UP(rdev->desc->uV_step * abs(new_selector - old_selector), - ramp_delay * 1000); + ramp_delay); } else if (rdev->desc->volt_table) { return DIV_ROUND_UP(abs(rdev->desc->volt_table[new_selector] - rdev->desc->volt_table[old_selector]), - ramp_delay * 1000); + ramp_delay); } else { rdev_warn(rdev, "Unsupported voltage mapping settings\n"); } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index ddc155d262d..84f999ed394 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -173,7 +173,7 @@ enum regulator_type { * * @min_uV: Voltage given by the lowest selector (if linear mapping) * @uV_step: Voltage increase with each selector (if linear mapping) - * @ramp_delay: Time to settle down after voltage change (unit: mV/us) + * @ramp_delay: Time to settle down after voltage change (unit: uV/us) * @volt_table: Voltage mapping table (if table based mapping) * * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 5f37ad3cc17..40dd0a394cf 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -92,7 +92,7 @@ struct regulator_state { * mode. * @initial_state: Suspend state to set by default. * @initial_mode: Mode to set at startup. - * @ramp_delay: Time to settle down after voltage change (unit: mV/us) + * @ramp_delay: Time to settle down after voltage change (unit: uV/us) */ struct regulation_constraints { -- cgit v1.2.3-70-g09d2 From c848bc8538cd29e92223d5e72e8fb60e6f2e176e Mon Sep 17 00:00:00 2001 From: Sangbeom Kim Date: Mon, 18 Jun 2012 09:49:20 +0900 Subject: regulator: s5m8767a: Support AP watchdog reset operation The S5M8767A can't know status of ap reset. So, After AP watchdog reset, AP can't boot normally. Problem can be happened like below condition. - AP Bootable lowest voltage(vdd_arm): 0.9v - AP DVFS voltage table: 0.8v, 0.9v, 1.0v - During AP works on lowest voltage(0.8V), watchdog reset is asserted - AP can't boot, because vdd arm is still 0.8v Solution - Basic concept: After ap watchdog reset, GPIO configuration is changed by default value - S5M8767A has function of voltage control with gpio (8 levels with 3 gpios) - Set bootable voltage on level 0 -> can work with default gpio configuration - In the probing, Change voltage control level from level 0 to level 1 - Execute normal dvfs operation on level 1 - After watchdog reset, ap gpio is set by default value - PMIC operation mode is changed by ap reset (level1 -> level0) - Regardless of previous vdd_arm voltage, AP always can be booted. Signed-off-by: Sangbeom Kim Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 137 ++++++++++++++++++++++++----------- include/linux/mfd/s5m87xx/s5m-core.h | 5 ++ 2 files changed, 98 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index a4a3c7eefd1..fd89574c405 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -41,6 +41,7 @@ struct s5m8767_info { u8 buck3_vol[8]; u8 buck4_vol[8]; int buck_gpios[3]; + int buck_ds[3]; int buck_gpioindex; }; @@ -283,17 +284,17 @@ static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg) reg = S5M8767_REG_BUCK1CTRL2; break; case S5M8767_BUCK2: - reg = S5M8767_REG_BUCK2DVS1; + reg = S5M8767_REG_BUCK2DVS2; if (s5m8767->buck2_gpiodvs) reg += s5m8767->buck_gpioindex; break; case S5M8767_BUCK3: - reg = S5M8767_REG_BUCK3DVS1; + reg = S5M8767_REG_BUCK3DVS2; if (s5m8767->buck3_gpiodvs) reg += s5m8767->buck_gpioindex; break; case S5M8767_BUCK4: - reg = S5M8767_REG_BUCK4DVS1; + reg = S5M8767_REG_BUCK4DVS2; if (s5m8767->buck4_gpiodvs) reg += s5m8767->buck_gpioindex; break; @@ -512,7 +513,7 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) struct regulator_config config = { }; struct regulator_dev **rdev; struct s5m8767_info *s5m8767; - int i, ret, size; + int i, ret, size, buck_init; if (!pdata) { dev_err(pdev->dev.parent, "Platform data not supplied\n"); @@ -563,12 +564,37 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) s5m8767->buck_gpios[0] = pdata->buck_gpios[0]; s5m8767->buck_gpios[1] = pdata->buck_gpios[1]; s5m8767->buck_gpios[2] = pdata->buck_gpios[2]; + s5m8767->buck_ds[0] = pdata->buck_ds[0]; + s5m8767->buck_ds[1] = pdata->buck_ds[1]; + s5m8767->buck_ds[2] = pdata->buck_ds[2]; + s5m8767->ramp_delay = pdata->buck_ramp_delay; s5m8767->buck2_ramp = pdata->buck2_ramp_enable; s5m8767->buck3_ramp = pdata->buck3_ramp_enable; s5m8767->buck4_ramp = pdata->buck4_ramp_enable; s5m8767->opmode = pdata->opmode; + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck2_init, + pdata->buck2_init + + buck_voltage_val2.step); + + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS2, buck_init); + + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck3_init, + pdata->buck3_init + + buck_voltage_val2.step); + + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS2, buck_init); + + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck4_init, + pdata->buck4_init + + buck_voltage_val2.step); + + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS2, buck_init); + for (i = 0; i < 8; i++) { if (s5m8767->buck2_gpiodvs) { s5m8767->buck2_vol[i] = @@ -598,48 +624,71 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) } } - if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || - pdata->buck4_gpiodvs) { - if (gpio_is_valid(pdata->buck_gpios[0]) && - gpio_is_valid(pdata->buck_gpios[1]) && - gpio_is_valid(pdata->buck_gpios[2])) { - ret = gpio_request(pdata->buck_gpios[0], - "S5M8767 SET1"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request for SET1\n"); - - ret = gpio_request(pdata->buck_gpios[1], - "S5M8767 SET2"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request for SET2\n"); - - ret = gpio_request(pdata->buck_gpios[2], - "S5M8767 SET3"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request for SET3\n"); - /* SET1 GPIO */ - gpio_direction_output(pdata->buck_gpios[0], - (s5m8767->buck_gpioindex >> 2) & 0x1); - /* SET2 GPIO */ - gpio_direction_output(pdata->buck_gpios[1], - (s5m8767->buck_gpioindex >> 1) & 0x1); - /* SET3 GPIO */ - gpio_direction_output(pdata->buck_gpios[2], - (s5m8767->buck_gpioindex >> 0) & 0x1); - ret = 0; - } else { - dev_err(&pdev->dev, "GPIO NOT VALID\n"); - ret = -EINVAL; - return ret; - } + if (gpio_is_valid(pdata->buck_gpios[0]) && + gpio_is_valid(pdata->buck_gpios[1]) && + gpio_is_valid(pdata->buck_gpios[2])) { + ret = gpio_request(pdata->buck_gpios[0], "S5M8767 SET1"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " for SET1\n"); + + ret = gpio_request(pdata->buck_gpios[1], "S5M8767 SET2"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " for SET2\n"); + + ret = gpio_request(pdata->buck_gpios[2], "S5M8767 SET3"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " for SET3\n"); + /* SET1 GPIO */ + gpio_direction_output(pdata->buck_gpios[0], + (s5m8767->buck_gpioindex >> 2) & 0x1); + /* SET2 GPIO */ + gpio_direction_output(pdata->buck_gpios[1], + (s5m8767->buck_gpioindex >> 1) & 0x1); + /* SET3 GPIO */ + gpio_direction_output(pdata->buck_gpios[2], + (s5m8767->buck_gpioindex >> 0) & 0x1); + ret = 0; + + } else { + dev_err(&pdev->dev, "GPIO NOT VALID\n"); + ret = -EINVAL; + return ret; } - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, - (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, - (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, - (pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); + ret = gpio_request(pdata->buck_ds[0], "S5M8767 DS2"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS2\n"); + + ret = gpio_request(pdata->buck_ds[1], "S5M8767 DS3"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS3\n"); + + ret = gpio_request(pdata->buck_ds[2], "S5M8767 DS4"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS4\n"); + + /* DS2 GPIO */ + gpio_direction_output(pdata->buck_ds[0], 0x0); + /* DS3 GPIO */ + gpio_direction_output(pdata->buck_ds[1], 0x0); + /* DS4 GPIO */ + gpio_direction_output(pdata->buck_ds[2], 0x0); + + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || + pdata->buck4_gpiodvs) { + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, + (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, + (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, + (pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + } /* Initialize GPIO DVS registers */ for (i = 0; i < 8; i++) { diff --git a/include/linux/mfd/s5m87xx/s5m-core.h b/include/linux/mfd/s5m87xx/s5m-core.h index 21603b42f22..0b2e0ed309f 100644 --- a/include/linux/mfd/s5m87xx/s5m-core.h +++ b/include/linux/mfd/s5m87xx/s5m-core.h @@ -347,6 +347,7 @@ struct s5m_platform_data { bool buck_voltage_lock; int buck_gpios[3]; + int buck_ds[3]; int buck2_voltage[8]; bool buck2_gpiodvs; int buck3_voltage[8]; @@ -369,6 +370,10 @@ struct s5m_platform_data { bool buck2_ramp_enable; bool buck3_ramp_enable; bool buck4_ramp_enable; + + int buck2_init; + int buck3_init; + int buck4_init; }; #endif /* __LINUX_MFD_S5M_CORE_H */ -- cgit v1.2.3-70-g09d2 From af8b5fc31099abd7f3b297332c9e280ec0b30a71 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Tue, 19 Jun 2012 07:08:22 +0000 Subject: regulator: add new regulator driver for lp872x This driver supports TI/National LP8720, LP8725 PMIC. Signed-off-by: Milo(Woogyom) Kim Reviewed-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/lp872x.c | 957 +++++++++++++++++++++++++++++++++++++++ include/linux/regulator/lp872x.h | 90 ++++ 4 files changed, 1055 insertions(+) create mode 100644 drivers/regulator/lp872x.c create mode 100644 include/linux/regulator/lp872x.h (limited to 'include') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 00ffe05d802..870f7300b15 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -224,6 +224,13 @@ config REGULATOR_LP3972 Say Y here to support the voltage regulators and convertors on National Semiconductors LP3972 PMIC +config REGULATOR_LP872X + tristate "TI/National Semiconductor LP8720/LP8725 voltage regulators" + depends on I2C + select REGMAP_I2C + help + This driver supports LP8720/LP8725 PMIC + config REGULATOR_PCF50633 tristate "NXP PCF50633 regulator driver" depends on MFD_PCF50633 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index d8544539efe..e6f700748a3 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o +obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c new file mode 100644 index 00000000000..d51d0985204 --- /dev/null +++ b/drivers/regulator/lp872x.c @@ -0,0 +1,957 @@ +/* + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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 +#include +#include +#include +#include +#include + +/* Registers : LP8720/8725 shared */ +#define LP872X_GENERAL_CFG 0x00 +#define LP872X_LDO1_VOUT 0x01 +#define LP872X_LDO2_VOUT 0x02 +#define LP872X_LDO3_VOUT 0x03 +#define LP872X_LDO4_VOUT 0x04 +#define LP872X_LDO5_VOUT 0x05 + +/* Registers : LP8720 */ +#define LP8720_BUCK_VOUT1 0x06 +#define LP8720_BUCK_VOUT2 0x07 +#define LP8720_ENABLE 0x08 + +/* Registers : LP8725 */ +#define LP8725_LILO1_VOUT 0x06 +#define LP8725_LILO2_VOUT 0x07 +#define LP8725_BUCK1_VOUT1 0x08 +#define LP8725_BUCK1_VOUT2 0x09 +#define LP8725_BUCK2_VOUT1 0x0A +#define LP8725_BUCK2_VOUT2 0x0B +#define LP8725_BUCK_CTRL 0x0C +#define LP8725_LDO_CTRL 0x0D + +/* Mask/shift : LP8720/LP8725 shared */ +#define LP872X_VOUT_M 0x1F +#define LP872X_START_DELAY_M 0xE0 +#define LP872X_START_DELAY_S 5 +#define LP872X_EN_LDO1_M BIT(0) +#define LP872X_EN_LDO2_M BIT(1) +#define LP872X_EN_LDO3_M BIT(2) +#define LP872X_EN_LDO4_M BIT(3) +#define LP872X_EN_LDO5_M BIT(4) + +/* Mask/shift : LP8720 */ +#define LP8720_TIMESTEP_S 0 /* Addr 00h */ +#define LP8720_TIMESTEP_M BIT(0) +#define LP8720_EXT_DVS_M BIT(2) +#define LP8720_BUCK_FPWM_S 5 /* Addr 07h */ +#define LP8720_BUCK_FPWM_M BIT(5) +#define LP8720_EN_BUCK_M BIT(5) /* Addr 08h */ +#define LP8720_DVS_SEL_M BIT(7) + +/* Mask/shift : LP8725 */ +#define LP8725_TIMESTEP_M 0xC0 /* Addr 00h */ +#define LP8725_TIMESTEP_S 6 +#define LP8725_BUCK1_EN_M BIT(0) +#define LP8725_DVS1_M BIT(2) +#define LP8725_DVS2_M BIT(3) +#define LP8725_BUCK2_EN_M BIT(4) +#define LP8725_BUCK_CL_M 0xC0 /* Addr 09h, 0Bh */ +#define LP8725_BUCK_CL_S 6 +#define LP8725_BUCK1_FPWM_S 1 /* Addr 0Ch */ +#define LP8725_BUCK1_FPWM_M BIT(1) +#define LP8725_BUCK2_FPWM_S 5 +#define LP8725_BUCK2_FPWM_M BIT(5) +#define LP8725_EN_LILO1_M BIT(5) /* Addr 0Dh */ +#define LP8725_EN_LILO2_M BIT(6) + +/* PWM mode */ +#define LP872X_FORCE_PWM 1 +#define LP872X_AUTO_PWM 0 + +#define LP8720_NUM_REGULATORS 6 +#define LP8725_NUM_REGULATORS 9 +#define EXTERN_DVS_USED 0 +#define MAX_DELAY 6 + +/* dump registers in regmap-debugfs */ +#define MAX_REGISTERS 0x0F + +enum lp872x_id { + LP8720, + LP8725, +}; + +struct lp872x { + struct regmap *regmap; + struct device *dev; + enum lp872x_id chipid; + struct lp872x_platform_data *pdata; + struct regulator_dev **regulators; + int num_regulators; + enum lp872x_dvs_state dvs_pin; + int dvs_gpio; +}; + +/* LP8720/LP8725 shared voltage table for LDOs */ +static const unsigned int lp872x_ldo_vtbl[] = { + 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000, + 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 2000000, + 2100000, 2200000, 2300000, 2400000, 2500000, 2600000, 2650000, 2700000, + 2750000, 2800000, 2850000, 2900000, 2950000, 3000000, 3100000, 3300000, +}; + +/* LP8720 LDO4 voltage table */ +static const unsigned int lp8720_ldo4_vtbl[] = { + 800000, 850000, 900000, 1000000, 1100000, 1200000, 1250000, 1300000, + 1350000, 1400000, 1450000, 1500000, 1550000, 1600000, 1650000, 1700000, + 1750000, 1800000, 1850000, 1900000, 2000000, 2100000, 2200000, 2300000, + 2400000, 2500000, 2600000, 2650000, 2700000, 2750000, 2800000, 2850000, +}; + +/* LP8725 LILO(Low Input Low Output) voltage table */ +static const unsigned int lp8725_lilo_vtbl[] = { + 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, + 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1700000, + 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, + 2600000, 2700000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000, +}; + +/* LP8720 BUCK voltage table */ +#define EXT_R 0 /* external resistor divider */ +static const unsigned int lp8720_buck_vtbl[] = { + EXT_R, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, + 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, + 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, + 1950000, 2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, +}; + +/* LP8725 BUCK voltage table */ +static const unsigned int lp8725_buck_vtbl[] = { + 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, + 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1700000, + 1750000, 1800000, 1850000, 1900000, 2000000, 2100000, 2200000, 2300000, + 2400000, 2500000, 2600000, 2700000, 2800000, 2850000, 2900000, 3000000, +}; + +/* LP8725 BUCK current limit */ +static const unsigned int lp8725_buck_uA[] = { + 460000, 780000, 1050000, 1370000, +}; + +static int lp872x_read_byte(struct lp872x *lp, u8 addr, u8 *data) +{ + int ret; + unsigned int val; + + ret = regmap_read(lp->regmap, addr, &val); + if (ret < 0) { + dev_err(lp->dev, "failed to read 0x%.2x\n", addr); + return ret; + } + + *data = (u8)val; + return 0; +} + +static inline int lp872x_write_byte(struct lp872x *lp, u8 addr, u8 data) +{ + return regmap_write(lp->regmap, addr, data); +} + +static inline int lp872x_update_bits(struct lp872x *lp, u8 addr, + unsigned int mask, u8 data) +{ + return regmap_update_bits(lp->regmap, addr, mask, data); +} + +static int _rdev_to_offset(struct regulator_dev *rdev) +{ + enum lp872x_regulator_id id = rdev_get_id(rdev); + + switch (id) { + case LP8720_ID_LDO1 ... LP8720_ID_BUCK: + return id; + case LP8725_ID_LDO1 ... LP8725_ID_BUCK2: + return id - LP8725_ID_BASE; + default: + return -EINVAL; + } +} + +static int lp872x_get_timestep_usec(struct lp872x *lp) +{ + enum lp872x_id chip = lp->chipid; + u8 val, mask, shift; + int *time_usec, size, ret; + int lp8720_time_usec[] = { 25, 50 }; + int lp8725_time_usec[] = { 32, 64, 128, 256 }; + + switch (chip) { + case LP8720: + mask = LP8720_TIMESTEP_M; + shift = LP8720_TIMESTEP_S; + time_usec = &lp8720_time_usec[0]; + size = ARRAY_SIZE(lp8720_time_usec); + break; + case LP8725: + mask = LP8725_TIMESTEP_M; + shift = LP8725_TIMESTEP_S; + time_usec = &lp8725_time_usec[0]; + size = ARRAY_SIZE(lp8725_time_usec); + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val); + if (ret) + return -EINVAL; + + val = (val & mask) >> shift; + if (val >= size) + return -EINVAL; + + return *(time_usec + val); +} + +static int lp872x_regulator_enable_time(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id regulator = rdev_get_id(rdev); + int time_step_us = lp872x_get_timestep_usec(lp); + int ret, offset; + u8 addr, val; + + if (time_step_us < 0) + return -EINVAL; + + switch (regulator) { + case LP8720_ID_LDO1 ... LP8720_ID_LDO5: + case LP8725_ID_LDO1 ... LP8725_ID_LILO2: + offset = _rdev_to_offset(rdev); + if (offset < 0) + return -EINVAL; + + addr = LP872X_LDO1_VOUT + offset; + break; + case LP8720_ID_BUCK: + addr = LP8720_BUCK_VOUT1; + break; + case LP8725_ID_BUCK1: + addr = LP8725_BUCK1_VOUT1; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT1; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + val = (val & LP872X_START_DELAY_M) >> LP872X_START_DELAY_S; + + return val > MAX_DELAY ? 0 : val * time_step_us; +} + +static void lp872x_set_dvs(struct lp872x *lp, int gpio) +{ + enum lp872x_dvs_sel dvs_sel = lp->pdata->dvs->vsel; + enum lp872x_dvs_state state; + + state = dvs_sel == SEL_V1 ? DVS_HIGH : DVS_LOW; + gpio_set_value(gpio, state); + lp->dvs_pin = state; +} + +static u8 lp872x_select_buck_vout_addr(struct lp872x *lp, + enum lp872x_regulator_id buck) +{ + u8 val, addr; + + if (lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val)) + return 0; + + switch (buck) { + case LP8720_ID_BUCK: + if (val & LP8720_EXT_DVS_M) { + addr = (lp->dvs_pin == DVS_HIGH) ? + LP8720_BUCK_VOUT1 : LP8720_BUCK_VOUT2; + } else { + if (lp872x_read_byte(lp, LP8720_ENABLE, &val)) + return 0; + + addr = val & LP8720_DVS_SEL_M ? + LP8720_BUCK_VOUT1 : LP8720_BUCK_VOUT2; + } + break; + case LP8725_ID_BUCK1: + if (val & LP8725_DVS1_M) + addr = LP8725_BUCK1_VOUT1; + else + addr = (lp->dvs_pin == DVS_HIGH) ? + LP8725_BUCK1_VOUT1 : LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = val & LP8725_DVS2_M ? + LP8725_BUCK2_VOUT1 : LP8725_BUCK2_VOUT2; + break; + default: + return 0; + } + + return addr; +} + +static bool lp872x_is_valid_buck_addr(u8 addr) +{ + switch (addr) { + case LP8720_BUCK_VOUT1: + case LP8720_BUCK_VOUT2: + case LP8725_BUCK1_VOUT1: + case LP8725_BUCK1_VOUT2: + case LP8725_BUCK2_VOUT1: + case LP8725_BUCK2_VOUT2: + return true; + default: + return false; + } +} + +static int lp872x_buck_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask = LP872X_VOUT_M; + struct lp872x_dvs *dvs = lp->pdata->dvs; + + if (dvs && gpio_is_valid(dvs->gpio)) + lp872x_set_dvs(lp, dvs->gpio); + + addr = lp872x_select_buck_vout_addr(lp, buck); + if (!lp872x_is_valid_buck_addr(addr)) + return -EINVAL; + + return lp872x_update_bits(lp, addr, mask, selector); +} + +static int lp872x_buck_get_voltage_sel(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, val; + int ret; + + addr = lp872x_select_buck_vout_addr(lp, buck); + if (!lp872x_is_valid_buck_addr(addr)) + return -EINVAL; + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + return val & LP872X_VOUT_M; +} + +static int lp8725_buck_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + int i, max = ARRAY_SIZE(lp8725_buck_uA); + u8 addr, val; + + switch (buck) { + case LP8725_ID_BUCK1: + addr = LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT2; + break; + default: + return -EINVAL; + } + + for (i = 0 ; i < max ; i++) + if (lp8725_buck_uA[i] >= min_uA && + lp8725_buck_uA[i] <= max_uA) + break; + + if (i == max) + return -EINVAL; + + val = i << LP8725_BUCK_CL_S; + + return lp872x_update_bits(lp, addr, LP8725_BUCK_CL_M, val); +} + +static int lp8725_buck_get_current_limit(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, val; + int ret; + + switch (buck) { + case LP8725_ID_BUCK1: + addr = LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT2; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + val = (val & LP8725_BUCK_CL_M) >> LP8725_BUCK_CL_S; + + return (val < ARRAY_SIZE(lp8725_buck_uA)) ? + lp8725_buck_uA[val] : -EINVAL; +} + +static int lp872x_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask, shift, val; + + switch (buck) { + case LP8720_ID_BUCK: + addr = LP8720_BUCK_VOUT2; + mask = LP8720_BUCK_FPWM_M; + shift = LP8720_BUCK_FPWM_S; + break; + case LP8725_ID_BUCK1: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK1_FPWM_M; + shift = LP8725_BUCK1_FPWM_S; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK2_FPWM_M; + shift = LP8725_BUCK2_FPWM_S; + break; + default: + return -EINVAL; + } + + if (mode == REGULATOR_MODE_FAST) + val = LP872X_FORCE_PWM << shift; + else if (mode == REGULATOR_MODE_NORMAL) + val = LP872X_AUTO_PWM << shift; + else + return -EINVAL; + + return lp872x_update_bits(lp, addr, mask, val); +} + +static unsigned int lp872x_buck_get_mode(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask, val; + int ret; + + switch (buck) { + case LP8720_ID_BUCK: + addr = LP8720_BUCK_VOUT2; + mask = LP8720_BUCK_FPWM_M; + break; + case LP8725_ID_BUCK1: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK1_FPWM_M; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK2_FPWM_M; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + return val & mask ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops lp872x_ldo_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, +}; + +static struct regulator_ops lp8720_buck_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = lp872x_buck_set_voltage_sel, + .get_voltage_sel = lp872x_buck_get_voltage_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, + .set_mode = lp872x_buck_set_mode, + .get_mode = lp872x_buck_get_mode, +}; + +static struct regulator_ops lp8725_buck_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = lp872x_buck_set_voltage_sel, + .get_voltage_sel = lp872x_buck_get_voltage_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, + .set_mode = lp872x_buck_set_mode, + .get_mode = lp872x_buck_get_mode, + .set_current_limit = lp8725_buck_set_current_limit, + .get_current_limit = lp8725_buck_get_current_limit, +}; + +static struct regulator_desc lp8720_regulator_desc[] = { + { + .name = "ldo1", + .id = LP8720_ID_LDO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO1_M, + }, + { + .name = "ldo2", + .id = LP8720_ID_LDO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO2_M, + }, + { + .name = "ldo3", + .id = LP8720_ID_LDO3, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO3_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO3_M, + }, + { + .name = "ldo4", + .id = LP8720_ID_LDO4, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8720_ldo4_vtbl), + .volt_table = lp8720_ldo4_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO4_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO4_M, + }, + { + .name = "ldo5", + .id = LP8720_ID_LDO5, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO5_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO5_M, + }, + { + .name = "buck", + .id = LP8720_ID_BUCK, + .ops = &lp8720_buck_ops, + .n_voltages = ARRAY_SIZE(lp8720_buck_vtbl), + .volt_table = lp8720_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP8720_EN_BUCK_M, + }, +}; + +static struct regulator_desc lp8725_regulator_desc[] = { + { + .name = "ldo1", + .id = LP8725_ID_LDO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO1_M, + }, + { + .name = "ldo2", + .id = LP8725_ID_LDO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO2_M, + }, + { + .name = "ldo3", + .id = LP8725_ID_LDO3, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO3_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO3_M, + }, + { + .name = "ldo4", + .id = LP8725_ID_LDO4, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO4_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO4_M, + }, + { + .name = "ldo5", + .id = LP8725_ID_LDO5, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO5_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO5_M, + }, + { + .name = "lilo1", + .id = LP8725_ID_LILO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), + .volt_table = lp8725_lilo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8725_LILO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP8725_EN_LILO1_M, + }, + { + .name = "lilo2", + .id = LP8725_ID_LILO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), + .volt_table = lp8725_lilo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8725_LILO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP8725_EN_LILO2_M, + }, + { + .name = "buck1", + .id = LP8725_ID_BUCK1, + .ops = &lp8725_buck_ops, + .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), + .volt_table = lp8725_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP872X_GENERAL_CFG, + .enable_mask = LP8725_BUCK1_EN_M, + }, + { + .name = "buck2", + .id = LP8725_ID_BUCK2, + .ops = &lp8725_buck_ops, + .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), + .volt_table = lp8725_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP872X_GENERAL_CFG, + .enable_mask = LP8725_BUCK2_EN_M, + }, +}; + +static int lp872x_check_dvs_validity(struct lp872x *lp) +{ + struct lp872x_dvs *dvs = lp->pdata->dvs; + u8 val = 0; + int ret; + + ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val); + if (ret) + return ret; + + ret = 0; + if (lp->chipid == LP8720) { + if (val & LP8720_EXT_DVS_M) + ret = dvs ? 0 : -EINVAL; + } else { + if ((val & LP8725_DVS1_M) == EXTERN_DVS_USED) + ret = dvs ? 0 : -EINVAL; + } + + return ret; +} + +static int lp872x_init_dvs(struct lp872x *lp) +{ + int ret, gpio; + struct lp872x_dvs *dvs = lp->pdata->dvs; + enum lp872x_dvs_state pinstate; + + ret = lp872x_check_dvs_validity(lp); + if (ret) { + dev_warn(lp->dev, "invalid dvs data: %d\n", ret); + return ret; + } + + gpio = dvs->gpio; + if (!gpio_is_valid(gpio)) { + dev_err(lp->dev, "invalid gpio: %d\n", gpio); + return -EINVAL; + } + + pinstate = dvs->init_state; + ret = devm_gpio_request_one(lp->dev, gpio, pinstate, "LP872X DVS"); + if (ret) { + dev_err(lp->dev, "gpio request err: %d\n", ret); + return ret; + } + + lp->dvs_pin = pinstate; + lp->dvs_gpio = gpio; + + return 0; +} + +static int lp872x_config(struct lp872x *lp) +{ + struct lp872x_platform_data *pdata = lp->pdata; + int ret; + + if (!pdata) { + dev_warn(lp->dev, "no platform data\n"); + return 0; + } + + if (!pdata->update_config) + return 0; + + ret = lp872x_write_byte(lp, LP872X_GENERAL_CFG, pdata->general_config); + if (ret) + return ret; + + return lp872x_init_dvs(lp); +} + +static struct regulator_init_data +*lp872x_find_regulator_init_data(int idx, struct lp872x *lp) +{ + int i, base, id, max_regulators; + + switch (lp->chipid) { + case LP8720: + base = LP8720_ID_BASE; + max_regulators = LP8720_NUM_REGULATORS; + break; + case LP8725: + base = LP8725_ID_BASE; + max_regulators = LP8725_NUM_REGULATORS; + break; + default: + return NULL; + } + + id = base + idx; + for (i = 0 ; i < max_regulators ; i++) + if (lp->pdata->regulator_data[i].id == id) + break; + + return (i == max_regulators) ? NULL : + lp->pdata->regulator_data[i].init_data; +} + +static int lp872x_regulator_register(struct lp872x *lp) +{ + struct regulator_desc *desc; + struct regulator_config cfg = { }; + struct regulator_dev *rdev; + int i, ret; + + for (i = 0 ; i < lp->num_regulators ; i++) { + desc = (lp->chipid == LP8720) ? &lp8720_regulator_desc[i] : + &lp8725_regulator_desc[i]; + + cfg.dev = lp->dev; + cfg.init_data = lp872x_find_regulator_init_data(i, lp); + cfg.driver_data = lp; + cfg.regmap = lp->regmap; + + rdev = regulator_register(desc, &cfg); + if (IS_ERR(rdev)) { + dev_err(lp->dev, "regulator register err"); + ret = PTR_ERR(rdev); + goto err; + } + + *(lp->regulators + i) = rdev; + } + + return 0; +err: + while (--i >= 0) { + rdev = *(lp->regulators + i); + regulator_unregister(rdev); + } + return ret; +} + +static void lp872x_regulator_unregister(struct lp872x *lp) +{ + struct regulator_dev *rdev; + int i; + + for (i = 0 ; i < lp->num_regulators ; i++) { + rdev = *(lp->regulators + i); + regulator_unregister(rdev); + } +} + +static const struct regmap_config lp872x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX_REGISTERS, +}; + +static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) +{ + struct lp872x *lp; + struct lp872x_platform_data *pdata = cl->dev.platform_data; + int ret, size, num_regulators; + const int lp872x_num_regulators[] = { + [LP8720] = LP8720_NUM_REGULATORS, + [LP8725] = LP8725_NUM_REGULATORS, + }; + + lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); + if (!lp) + goto err_mem; + + num_regulators = lp872x_num_regulators[id->driver_data]; + size = sizeof(struct regulator_dev *) * num_regulators; + + lp->regulators = devm_kzalloc(&cl->dev, size, GFP_KERNEL); + if (!lp->regulators) + goto err_mem; + + lp->regmap = devm_regmap_init_i2c(cl, &lp872x_regmap_config); + if (IS_ERR(lp->regmap)) { + ret = PTR_ERR(lp->regmap); + dev_err(&cl->dev, "regmap init i2c err: %d\n", ret); + goto err_dev; + } + + lp->dev = &cl->dev; + lp->pdata = pdata; + lp->chipid = id->driver_data; + lp->num_regulators = num_regulators; + i2c_set_clientdata(cl, lp); + + ret = lp872x_config(lp); + if (ret) + goto err_dev; + + return lp872x_regulator_register(lp); + +err_mem: + return -ENOMEM; +err_dev: + return ret; +} + +static int __devexit lp872x_remove(struct i2c_client *cl) +{ + struct lp872x *lp = i2c_get_clientdata(cl); + + lp872x_regulator_unregister(lp); + return 0; +} + +static const struct i2c_device_id lp872x_ids[] = { + {"lp8720", LP8720}, + {"lp8725", LP8725}, + { } +}; +MODULE_DEVICE_TABLE(i2c, lp872x_ids); + +static struct i2c_driver lp872x_driver = { + .driver = { + .name = "lp872x", + .owner = THIS_MODULE, + }, + .probe = lp872x_probe, + .remove = __devexit_p(lp872x_remove), + .id_table = lp872x_ids, +}; + +module_i2c_driver(lp872x_driver); + +MODULE_DESCRIPTION("TI/National Semiconductor LP872x PMU Regulator Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/regulator/lp872x.h b/include/linux/regulator/lp872x.h new file mode 100644 index 00000000000..132e05c4666 --- /dev/null +++ b/include/linux/regulator/lp872x.h @@ -0,0 +1,90 @@ +/* + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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. + * + */ + +#ifndef __LP872X_REGULATOR_H__ +#define __LP872X_REGULATOR_H__ + +#include +#include +#include + +#define LP872X_MAX_REGULATORS 9 + +enum lp872x_regulator_id { + LP8720_ID_BASE, + LP8720_ID_LDO1 = LP8720_ID_BASE, + LP8720_ID_LDO2, + LP8720_ID_LDO3, + LP8720_ID_LDO4, + LP8720_ID_LDO5, + LP8720_ID_BUCK, + + LP8725_ID_BASE, + LP8725_ID_LDO1 = LP8725_ID_BASE, + LP8725_ID_LDO2, + LP8725_ID_LDO3, + LP8725_ID_LDO4, + LP8725_ID_LDO5, + LP8725_ID_LILO1, + LP8725_ID_LILO2, + LP8725_ID_BUCK1, + LP8725_ID_BUCK2, + + LP872X_ID_MAX, +}; + +enum lp872x_dvs_state { + DVS_LOW = GPIOF_OUT_INIT_LOW, + DVS_HIGH = GPIOF_OUT_INIT_HIGH, +}; + +enum lp872x_dvs_sel { + SEL_V1, + SEL_V2, +}; + +/** + * lp872x_dvs + * @gpio : gpio pin number for dvs control + * @vsel : dvs selector for buck v1 or buck v2 register + * @init_state : initial dvs pin state + */ +struct lp872x_dvs { + int gpio; + enum lp872x_dvs_sel vsel; + enum lp872x_dvs_state init_state; +}; + +/** + * lp872x_regdata + * @id : regulator id + * @init_data : init data for each regulator + */ +struct lp872x_regulator_data { + enum lp872x_regulator_id id; + struct regulator_init_data *init_data; +}; + +/** + * lp872x_platform_data + * @general_config : the value of LP872X_GENERAL_CFG register + * @update_config : if LP872X_GENERAL_CFG register is updated, set true + * @regulator_data : platform regulator id and init data + * @dvs : dvs data for buck voltage control + */ +struct lp872x_platform_data { + u8 general_config; + bool update_config; + struct lp872x_regulator_data regulator_data[LP872X_MAX_REGULATORS]; + struct lp872x_dvs *dvs; +}; + +#endif -- cgit v1.2.3-70-g09d2 From 4244854d22bf8f782698c5224b9191c8d2d42610 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Sat, 30 Jun 2012 03:04:26 +0000 Subject: sctp: be more restrictive in transport selection on bundled sacks It was noticed recently that when we send data on a transport, its possible that we might bundle a sack that arrived on a different transport. While this isn't a major problem, it does go against the SHOULD requirement in section 6.4 of RFC 2960: An endpoint SHOULD transmit reply chunks (e.g., SACK, HEARTBEAT ACK, etc.) to the same destination transport address from which it received the DATA or control chunk to which it is replying. This rule should also be followed if the endpoint is bundling DATA chunks together with the reply chunk. This patch seeks to correct that. It restricts the bundling of sack operations to only those transports which have moved the ctsn of the association forward since the last sack. By doing this we guarantee that we only bundle outbound saks on a transport that has received a chunk since the last sack. This brings us into stricter compliance with the RFC. Vlad had initially suggested that we strictly allow only sack bundling on the transport that last moved the ctsn forward. While this makes sense, I was concerned that doing so prevented us from bundling in the case where we had received chunks that moved the ctsn on multiple transports. In those cases, the RFC allows us to select any of the transports having received chunks to bundle the sack on. so I've modified the approach to allow for that, by adding a state variable to each transport that tracks weather it has moved the ctsn since the last sack. This I think keeps our behavior (and performance), close enough to our current profile that I think we can do this without a sysctl knob to enable/disable it. Signed-off-by: Neil Horman CC: Vlad Yaseivch CC: David S. Miller CC: linux-sctp@vger.kernel.org Reported-by: Michele Baldessari Reported-by: sorin serban Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 4 ++++ include/net/sctp/tsnmap.h | 3 ++- net/sctp/associola.c | 1 + net/sctp/output.c | 5 +++++ net/sctp/sm_make_chunk.c | 16 ++++++++++++++++ net/sctp/sm_sideeffect.c | 2 +- net/sctp/transport.c | 2 ++ net/sctp/tsnmap.c | 6 +++++- net/sctp/ulpevent.c | 3 ++- net/sctp/ulpqueue.c | 2 +- 10 files changed, 39 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index e4652fe5895..fecdf31816f 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -912,6 +912,9 @@ struct sctp_transport { /* Is this structure kfree()able? */ malloced:1; + /* Has this transport moved the ctsn since we last sacked */ + __u32 sack_generation; + struct flowi fl; /* This is the peer's IP address and port. */ @@ -1584,6 +1587,7 @@ struct sctp_association { */ __u8 sack_needed; /* Do we need to sack the peer? */ __u32 sack_cnt; + __u32 sack_generation; /* These are capabilities which our peer advertised. */ __u8 ecn_capable:1, /* Can peer do ECN? */ diff --git a/include/net/sctp/tsnmap.h b/include/net/sctp/tsnmap.h index e7728bc14cc..2c5d2b4d5d1 100644 --- a/include/net/sctp/tsnmap.h +++ b/include/net/sctp/tsnmap.h @@ -117,7 +117,8 @@ void sctp_tsnmap_free(struct sctp_tsnmap *map); int sctp_tsnmap_check(const struct sctp_tsnmap *, __u32 tsn); /* Mark this TSN as seen. */ -int sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn); +int sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn, + struct sctp_transport *trans); /* Mark this TSN and all lower as seen. */ void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 5bc9ab161b3..b16517ee1aa 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -271,6 +271,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a */ asoc->peer.sack_needed = 1; asoc->peer.sack_cnt = 0; + asoc->peer.sack_generation = 1; /* Assume that the peer will tell us if he recognizes ASCONF * as part of INIT exchange. diff --git a/net/sctp/output.c b/net/sctp/output.c index f1b7d4bb591..6ae47acaaec 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -248,6 +248,11 @@ static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt, /* If the SACK timer is running, we have a pending SACK */ if (timer_pending(timer)) { struct sctp_chunk *sack; + + if (pkt->transport->sack_generation != + pkt->transport->asoc->peer.sack_generation) + return retval; + asoc->a_rwnd = asoc->rwnd; sack = sctp_make_sack(asoc); if (sack) { diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index a85eeeb55dd..b6de71efb14 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -734,8 +734,10 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc) int len; __u32 ctsn; __u16 num_gabs, num_dup_tsns; + struct sctp_association *aptr = (struct sctp_association *)asoc; struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; struct sctp_gap_ack_block gabs[SCTP_MAX_GABS]; + struct sctp_transport *trans; memset(gabs, 0, sizeof(gabs)); ctsn = sctp_tsnmap_get_ctsn(map); @@ -805,6 +807,20 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc) sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns, sctp_tsnmap_get_dups(map)); + /* Once we have a sack generated, check to see what our sack + * generation is, if its 0, reset the transports to 0, and reset + * the association generation to 1 + * + * The idea is that zero is never used as a valid generation for the + * association so no transport will match after a wrap event like this, + * Until the next sack + */ + if (++aptr->peer.sack_generation == 0) { + list_for_each_entry(trans, &asoc->peer.transport_addr_list, + transports) + trans->sack_generation = 0; + aptr->peer.sack_generation = 1; + } nodata: return retval; } diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index c96d1a81cf4..8716da1a859 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1268,7 +1268,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_REPORT_TSN: /* Record the arrival of a TSN. */ error = sctp_tsnmap_mark(&asoc->peer.tsn_map, - cmd->obj.u32); + cmd->obj.u32, NULL); break; case SCTP_CMD_REPORT_FWDTSN: diff --git a/net/sctp/transport.c b/net/sctp/transport.c index b026ba0c699..1dcceb6e0ce 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -68,6 +68,8 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, peer->af_specific = sctp_get_af_specific(addr->sa.sa_family); memset(&peer->saddr, 0, sizeof(union sctp_addr)); + peer->sack_generation = 0; + /* From 6.3.1 RTO Calculation: * * C1) Until an RTT measurement has been made for a packet sent to the diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c index f1e40cebc98..b5fb7c40902 100644 --- a/net/sctp/tsnmap.c +++ b/net/sctp/tsnmap.c @@ -114,7 +114,8 @@ int sctp_tsnmap_check(const struct sctp_tsnmap *map, __u32 tsn) /* Mark this TSN as seen. */ -int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn) +int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn, + struct sctp_transport *trans) { u16 gap; @@ -133,6 +134,9 @@ int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn) */ map->max_tsn_seen++; map->cumulative_tsn_ack_point++; + if (trans) + trans->sack_generation = + trans->asoc->peer.sack_generation; map->base_tsn++; } else { /* Either we already have a gap, or about to record a gap, so diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 8a84017834c..33d89477619 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -715,7 +715,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc, * can mark it as received so the tsn_map is updated correctly. */ if (sctp_tsnmap_mark(&asoc->peer.tsn_map, - ntohl(chunk->subh.data_hdr->tsn))) + ntohl(chunk->subh.data_hdr->tsn), + chunk->transport)) goto fail_mark; /* First calculate the padding, so we don't inadvertently diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index f2d1de7f2ff..f5a6a4f4faf 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -1051,7 +1051,7 @@ void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk, if (chunk && (freed >= needed)) { __u32 tsn; tsn = ntohl(chunk->subh.data_hdr->tsn); - sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn); + sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn, chunk->transport); sctp_ulpq_tail_data(ulpq, chunk, gfp); sctp_ulpq_partial_delivery(ulpq, chunk, gfp); -- cgit v1.2.3-70-g09d2 From d172f319c1094ef22d2a00f43e8a7da4dd02c8f3 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 29 Jun 2012 09:45:16 +0800 Subject: regulator: tps65217: Convert LDO1 to use regulator_list_voltage_table Convert tps65217_pmic_ldo1_ops to use regulator_list_voltage_table. We have n_voltages and volt_table settings in regulator_desc, so we don't need the table and table_len fields in struct tps_info. Thus remove them from struct tps_info. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65217-regulator.c | 47 ++++++++++++++++------------------ include/linux/mfd/tps65217.h | 4 --- 2 files changed, 22 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index f5fa05b5bea..3a2b839276f 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -26,7 +26,7 @@ #include #include -#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em) \ +#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em, _t) \ { \ .name = _name, \ .id = _id, \ @@ -38,20 +38,19 @@ .vsel_mask = _vm, \ .enable_reg = TPS65217_REG_ENABLE, \ .enable_mask = _em, \ + .volt_table = _t, \ } \ -#define TPS65217_INFO(_nm, _min, _max, _f1, _f2, _t, _n)\ +#define TPS65217_INFO(_nm, _min, _max, _f1, _f2) \ { \ .name = _nm, \ .min_uV = _min, \ .max_uV = _max, \ .vsel_to_uv = _f1, \ .uv_to_vsel = _f2, \ - .table = _t, \ - .table_len = _n, \ } -static const int LDO1_VSEL_table[] = { +static const unsigned int LDO1_VSEL_table[] = { 1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1800000, 2500000, 2750000, @@ -128,19 +127,18 @@ static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel) static struct tps_info tps65217_pmic_regs[] = { TPS65217_INFO("DCDC1", 900000, 1800000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), + tps65217_uv_to_vsel1), TPS65217_INFO("DCDC2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), + tps65217_uv_to_vsel1), TPS65217_INFO("DCDC3", 900000, 1500000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), - TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL, LDO1_VSEL_table, - 16), + tps65217_uv_to_vsel1), + TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL), TPS65217_INFO("LDO2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), + tps65217_uv_to_vsel1), TPS65217_INFO("LDO3", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32), + tps65217_uv_to_vsel2), TPS65217_INFO("LDO4", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32), + tps65217_uv_to_vsel2), }; static int tps65217_pmic_enable(struct regulator_dev *dev) @@ -230,12 +228,9 @@ static int tps65217_pmic_list_voltage(struct regulator_dev *dev, if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) return -EINVAL; - if (selector >= tps->info[rid]->table_len) + if (selector >= dev->desc->n_voltages) return -EINVAL; - if (tps->info[rid]->table) - return tps->info[rid]->table[selector]; - return tps->info[rid]->vsel_to_uv(selector); } @@ -257,31 +252,33 @@ static struct regulator_ops tps65217_pmic_ldo1_ops = { .disable = tps65217_pmic_disable, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = tps65217_pmic_set_voltage_sel, - .list_voltage = tps65217_pmic_list_voltage, + .list_voltage = regulator_list_voltage_table, }; static const struct regulator_desc regulators[] = { TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC1_EN), + TPS65217_ENABLE_DC1_EN, NULL), TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC2_EN), + TPS65217_ENABLE_DC2_EN, NULL), TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC3_EN), + TPS65217_ENABLE_DC3_EN, NULL), TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16, TPS65217_REG_DEFLDO1, TPS65217_DEFLDO1_LDO1_MASK, - TPS65217_ENABLE_LDO1_EN), + TPS65217_ENABLE_LDO1_EN, LDO1_VSEL_table), TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64, TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK, - TPS65217_ENABLE_LDO2_EN), + TPS65217_ENABLE_LDO2_EN, NULL), TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32, TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK, - TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN), + TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN, + NULL), TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32, TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK, - TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN), + TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN, + NULL), }; static int __devinit tps65217_regulator_probe(struct platform_device *pdev) diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index 4e035a41a9b..3a80da103f3 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -227,8 +227,6 @@ struct tps65217_board { * @max_uV: minimum micro volts * @vsel_to_uv: Function pointer to get voltage from selector * @uv_to_vsel: Function pointer to get selector from voltage - * @table: Table for non-uniform voltage step-size - * @table_len: Length of the voltage table * * This data is used to check the regualtor voltage limits while setting. */ @@ -238,8 +236,6 @@ struct tps_info { int max_uV; int (*vsel_to_uv)(unsigned int vsel); int (*uv_to_vsel)(int uV, unsigned int *vsel); - const int *table; - unsigned int table_len; }; /** -- cgit v1.2.3-70-g09d2 From cba6d0d64ee53772b285d0c0c288deefbeaf7775 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 2 Jul 2012 07:08:42 -0700 Subject: Revert "rcu: Move PREEMPT_RCU preemption to switch_to() invocation" This reverts commit 616c310e83b872024271c915c1b9ab505b9efad9. (Move PREEMPT_RCU preemption to switch_to() invocation). Testing by Sasha Levin showed that this can result in deadlock due to invoking the scheduler when one of the runqueue locks is held. Because this commit was simply a performance optimization, revert it. Reported-by: Sasha Levin Signed-off-by: Paul E. McKenney Tested-by: Sasha Levin --- arch/um/drivers/mconsole_kern.c | 1 - include/linux/rcupdate.h | 1 - include/linux/rcutiny.h | 6 ++++++ include/linux/sched.h | 10 ---------- kernel/rcutree.c | 1 + kernel/rcutree.h | 1 + kernel/rcutree_plugin.h | 14 +++++++++++--- kernel/sched/core.c | 1 - 8 files changed, 19 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index 88e466b159d..43b39d61b53 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c @@ -705,7 +705,6 @@ static void stack_proc(void *arg) struct task_struct *from = current, *to = arg; to->thread.saved_task = from; - rcu_switch_from(from); switch_to(from, to, from); } diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 26d1a47591f..9cac722b169 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -184,7 +184,6 @@ static inline int rcu_preempt_depth(void) /* Internal to kernel */ extern void rcu_sched_qs(int cpu); extern void rcu_bh_qs(int cpu); -extern void rcu_preempt_note_context_switch(void); extern void rcu_check_callbacks(int cpu, int user); struct notifier_block; extern void rcu_idle_enter(void); diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index 854dc4c5c27..4e56a9c69a3 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -87,6 +87,10 @@ static inline void kfree_call_rcu(struct rcu_head *head, #ifdef CONFIG_TINY_RCU +static inline void rcu_preempt_note_context_switch(void) +{ +} + static inline int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies) { *delta_jiffies = ULONG_MAX; @@ -95,6 +99,7 @@ static inline int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies) #else /* #ifdef CONFIG_TINY_RCU */ +void rcu_preempt_note_context_switch(void); int rcu_preempt_needs_cpu(void); static inline int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies) @@ -108,6 +113,7 @@ static inline int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies) static inline void rcu_note_context_switch(int cpu) { rcu_sched_qs(cpu); + rcu_preempt_note_context_switch(); } /* diff --git a/include/linux/sched.h b/include/linux/sched.h index 4059c0f33f0..06a4c5f4f55 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1871,22 +1871,12 @@ static inline void rcu_copy_process(struct task_struct *p) INIT_LIST_HEAD(&p->rcu_node_entry); } -static inline void rcu_switch_from(struct task_struct *prev) -{ - if (prev->rcu_read_lock_nesting != 0) - rcu_preempt_note_context_switch(); -} - #else static inline void rcu_copy_process(struct task_struct *p) { } -static inline void rcu_switch_from(struct task_struct *prev) -{ -} - #endif #ifdef CONFIG_SMP diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 38ecdda3f55..4b97bba7396 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -201,6 +201,7 @@ void rcu_note_context_switch(int cpu) { trace_rcu_utilization("Start context switch"); rcu_sched_qs(cpu); + rcu_preempt_note_context_switch(cpu); trace_rcu_utilization("End context switch"); } EXPORT_SYMBOL_GPL(rcu_note_context_switch); diff --git a/kernel/rcutree.h b/kernel/rcutree.h index ea056495783..19b61ac1079 100644 --- a/kernel/rcutree.h +++ b/kernel/rcutree.h @@ -444,6 +444,7 @@ DECLARE_PER_CPU(char, rcu_cpu_has_work); /* Forward declarations for rcutree_plugin.h */ static void rcu_bootup_announce(void); long rcu_batches_completed(void); +static void rcu_preempt_note_context_switch(int cpu); static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp); #ifdef CONFIG_HOTPLUG_CPU static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp, diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 5271a020887..3e4899459f3 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -153,7 +153,7 @@ static void rcu_preempt_qs(int cpu) * * Caller must disable preemption. */ -void rcu_preempt_note_context_switch(void) +static void rcu_preempt_note_context_switch(int cpu) { struct task_struct *t = current; unsigned long flags; @@ -164,7 +164,7 @@ void rcu_preempt_note_context_switch(void) (t->rcu_read_unlock_special & RCU_READ_UNLOCK_BLOCKED) == 0) { /* Possibly blocking in an RCU read-side critical section. */ - rdp = __this_cpu_ptr(rcu_preempt_state.rda); + rdp = per_cpu_ptr(rcu_preempt_state.rda, cpu); rnp = rdp->mynode; raw_spin_lock_irqsave(&rnp->lock, flags); t->rcu_read_unlock_special |= RCU_READ_UNLOCK_BLOCKED; @@ -228,7 +228,7 @@ void rcu_preempt_note_context_switch(void) * means that we continue to block the current grace period. */ local_irq_save(flags); - rcu_preempt_qs(smp_processor_id()); + rcu_preempt_qs(cpu); local_irq_restore(flags); } @@ -1001,6 +1001,14 @@ void rcu_force_quiescent_state(void) } EXPORT_SYMBOL_GPL(rcu_force_quiescent_state); +/* + * Because preemptible RCU does not exist, we never have to check for + * CPUs being in quiescent states. + */ +static void rcu_preempt_note_context_switch(int cpu) +{ +} + /* * Because preemptible RCU does not exist, there are never any preempted * RCU readers. diff --git a/kernel/sched/core.c b/kernel/sched/core.c index d5594a4268d..eaead2df6aa 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2081,7 +2081,6 @@ context_switch(struct rq *rq, struct task_struct *prev, #endif /* Here we just switch the register state and the stack. */ - rcu_switch_from(prev); switch_to(prev, next, prev); barrier(); -- cgit v1.2.3-70-g09d2 From d4db2935e4fffeba42540b0dc9d85e3036701221 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 29 Jun 2012 09:56:08 -0600 Subject: KVM: Pass kvm_irqfd to functions Prune this down to just the struct kvm_irqfd so we can avoid changing function definition for every flag or field we use. Signed-off-by: Alex Williamson Acked-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- include/linux/kvm_host.h | 4 ++-- virt/kvm/eventfd.c | 20 ++++++++++---------- virt/kvm/kvm_main.c | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c4464356b35..96c158a37d3 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -815,7 +815,7 @@ static inline void kvm_free_irq_routing(struct kvm *kvm) {} #ifdef CONFIG_HAVE_KVM_EVENTFD void kvm_eventfd_init(struct kvm *kvm); -int kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags); +int kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args); void kvm_irqfd_release(struct kvm *kvm); void kvm_irq_routing_update(struct kvm *, struct kvm_irq_routing_table *); int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args); @@ -824,7 +824,7 @@ int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args); static inline void kvm_eventfd_init(struct kvm *kvm) {} -static inline int kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags) +static inline int kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args) { return -EINVAL; } diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index f59c1e8de7a..c307c24c147 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -198,7 +198,7 @@ static void irqfd_update(struct kvm *kvm, struct _irqfd *irqfd, } static int -kvm_irqfd_assign(struct kvm *kvm, int fd, int gsi) +kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args) { struct kvm_irq_routing_table *irq_rt; struct _irqfd *irqfd, *tmp; @@ -212,12 +212,12 @@ kvm_irqfd_assign(struct kvm *kvm, int fd, int gsi) return -ENOMEM; irqfd->kvm = kvm; - irqfd->gsi = gsi; + irqfd->gsi = args->gsi; INIT_LIST_HEAD(&irqfd->list); INIT_WORK(&irqfd->inject, irqfd_inject); INIT_WORK(&irqfd->shutdown, irqfd_shutdown); - file = eventfd_fget(fd); + file = eventfd_fget(args->fd); if (IS_ERR(file)) { ret = PTR_ERR(file); goto fail; @@ -298,19 +298,19 @@ kvm_eventfd_init(struct kvm *kvm) * shutdown any irqfd's that match fd+gsi */ static int -kvm_irqfd_deassign(struct kvm *kvm, int fd, int gsi) +kvm_irqfd_deassign(struct kvm *kvm, struct kvm_irqfd *args) { struct _irqfd *irqfd, *tmp; struct eventfd_ctx *eventfd; - eventfd = eventfd_ctx_fdget(fd); + eventfd = eventfd_ctx_fdget(args->fd); if (IS_ERR(eventfd)) return PTR_ERR(eventfd); spin_lock_irq(&kvm->irqfds.lock); list_for_each_entry_safe(irqfd, tmp, &kvm->irqfds.items, list) { - if (irqfd->eventfd == eventfd && irqfd->gsi == gsi) { + if (irqfd->eventfd == eventfd && irqfd->gsi == args->gsi) { /* * This rcu_assign_pointer is needed for when * another thread calls kvm_irq_routing_update before @@ -338,12 +338,12 @@ kvm_irqfd_deassign(struct kvm *kvm, int fd, int gsi) } int -kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags) +kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args) { - if (flags & KVM_IRQFD_FLAG_DEASSIGN) - return kvm_irqfd_deassign(kvm, fd, gsi); + if (args->flags & KVM_IRQFD_FLAG_DEASSIGN) + return kvm_irqfd_deassign(kvm, args); - return kvm_irqfd_assign(kvm, fd, gsi); + return kvm_irqfd_assign(kvm, args); } /* diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 7e140683ff1..e98a5cac55c 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2047,7 +2047,7 @@ static long kvm_vm_ioctl(struct file *filp, r = -EFAULT; if (copy_from_user(&data, argp, sizeof data)) goto out; - r = kvm_irqfd(kvm, data.fd, data.gsi, data.flags); + r = kvm_irqfd(kvm, &data); break; } case KVM_IOEVENTFD: { -- cgit v1.2.3-70-g09d2 From 79511ed3225a64f6b7fc749f4f9c1ed82f24f729 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 14:23:10 +0100 Subject: regulator: core: Allow fixed enable_time to be set in the regulator_desc Many regulators have a fixed specification for their enable time. Allow this to be set in the regulator_desc as a number to save them having to implement an explicit operation. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 +- include/linux/regulator/driver.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index aa82c0465f4..6e488aa6f1e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1189,7 +1189,7 @@ overflow_err: static int _regulator_get_enable_time(struct regulator_dev *rdev) { if (!rdev->desc->ops->enable_time) - return 0; + return rdev->desc->enable_time; return rdev->desc->ops->enable_time(rdev); } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 84f999ed394..176bd433558 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -180,6 +180,8 @@ enum regulator_type { * @vsel_mask: Mask for register bitfield used for selector * @enable_reg: Register for control when using regmap enable/disable ops * @enable_mask: Mask for control when using regmap enable/disable ops + * + * @enable_time: Time taken for initial enable of regulator (in uS). */ struct regulator_desc { const char *name; @@ -201,6 +203,8 @@ struct regulator_desc { unsigned int vsel_mask; unsigned int enable_reg; unsigned int enable_mask; + + unsigned int enable_time; }; /** -- cgit v1.2.3-70-g09d2 From 65f735082de35aa4d44e8d0afe862798d0e09e29 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 14:14:38 +0100 Subject: regulator: core: Add core support for GPIO controlled enable lines It is very common for regulators to support having their enable signal controlled by a GPIO. Since there are a bunch of fiddly things to get right like handling the operations when the enable signal is tied to a rail and it's just replicated code add support for this to the core. Drivers should set ena_gpio in their config if they have a GPIO control, using ena_gpio_flags to specify any flags (including GPIOF_OUT_INIT_ for the initial state) and ena_gpio_invert if the GPIO is active low. The core will then override any enable and disable operations the driver has and instead control the specified GPIO. This will in the future also allow us to further extend the core by identifying when several enable signals have been tied together and handling this properly. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 35 ++++++++++++++++++++++++++++++++++- include/linux/regulator/driver.h | 11 +++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 82650a16a97..8d81bafcb72 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1491,7 +1492,11 @@ static int _regulator_do_enable(struct regulator_dev *rdev) trace_regulator_enable(rdev_get_name(rdev)); - if (rdev->desc->ops->enable) { + if (rdev->ena_gpio) { + gpio_set_value_cansleep(rdev->ena_gpio, + !rdev->ena_gpio_invert); + rdev->ena_gpio_state = 1; + } else if (rdev->desc->ops->enable) { ret = rdev->desc->ops->enable(rdev); if (ret < 0) return ret; @@ -1846,6 +1851,10 @@ EXPORT_SYMBOL_GPL(regulator_disable_regmap); static int _regulator_is_enabled(struct regulator_dev *rdev) { + /* A GPIO control always takes precedence */ + if (rdev->ena_gpio) + return rdev->ena_gpio_state; + /* If we don't know then assume that the regulator is always on */ if (!rdev->desc->ops->is_enabled) return 1; @@ -3243,6 +3252,26 @@ regulator_register(const struct regulator_desc *regulator_desc, dev_set_drvdata(&rdev->dev, rdev); + if (config->ena_gpio) { + ret = gpio_request_one(config->ena_gpio, + GPIOF_DIR_OUT | config->ena_gpio_flags, + rdev_get_name(rdev)); + if (ret != 0) { + rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", + config->ena_gpio, ret); + goto clean; + } + + rdev->ena_gpio = config->ena_gpio; + rdev->ena_gpio_invert = config->ena_gpio_invert; + + if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH) + rdev->ena_gpio_state = 1; + + if (rdev->ena_gpio_invert) + rdev->ena_gpio_state = !rdev->ena_gpio_state; + } + /* set regulator constraints */ if (init_data) constraints = &init_data->constraints; @@ -3311,6 +3340,8 @@ unset_supplies: scrub: if (rdev->supply) regulator_put(rdev->supply); + if (rdev->ena_gpio) + gpio_free(rdev->ena_gpio); kfree(rdev->constraints); device_unregister(&rdev->dev); /* device core frees rdev */ @@ -3344,6 +3375,8 @@ void regulator_unregister(struct regulator_dev *rdev) unset_regulator_supplies(rdev); list_del(&rdev->list); kfree(rdev->constraints); + if (rdev->ena_gpio) + gpio_free(rdev->ena_gpio); device_unregister(&rdev->dev); mutex_unlock(®ulator_list_mutex); } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 176bd433558..b1b7b8b43ec 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -220,6 +220,9 @@ struct regulator_desc { * @of_node: OpenFirmware node to parse for device tree bindings (may be * NULL). * @regmap: regmap to use for core regmap helpers + * @ena_gpio: GPIO controlling regulator enable. + * @ena_gpio_invert: Sense for GPIO enable control. + * @ena_gpio_flags: Flags to use when calling gpio_request_one() */ struct regulator_config { struct device *dev; @@ -227,6 +230,10 @@ struct regulator_config { void *driver_data; struct device_node *of_node; struct regmap *regmap; + + int ena_gpio; + unsigned int ena_gpio_invert:1; + unsigned int ena_gpio_flags; }; /* @@ -265,6 +272,10 @@ struct regulator_dev { void *reg_data; /* regulator_dev data */ struct dentry *debugfs; + + int ena_gpio; + unsigned int ena_gpio_invert:1; + unsigned int ena_gpio_state:1; }; struct regulator_dev * -- cgit v1.2.3-70-g09d2 From 5a081caa0414b9bbb82c17ffab9d6fe66edbb72f Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 6 Jun 2012 10:09:25 +0300 Subject: rpmsg: avoid premature deallocation of endpoints When an inbound message arrives, the rpmsg core looks up its associated endpoint and invokes the registered callback. If a message arrives while its endpoint is being removed (because the rpmsg driver was removed, or a recovery of a remote processor has kicked in) we must ensure atomicity, i.e.: - Either the ept is removed before it is found or - The ept is found but will not be freed until the callback returns This is achieved by maintaining a per-ept reference count, which, when drops to zero, will trigger deallocation of the ept. With this in hand, it is now forbidden to directly deallocate epts once they have been added to the endpoints idr. Cc: stable Reported-by: Fernando Guzman Lugo Signed-off-by: Ohad Ben-Cohen --- drivers/rpmsg/virtio_rpmsg_bus.c | 36 ++++++++++++++++++++++++++++++++++-- include/linux/rpmsg.h | 3 +++ 2 files changed, 37 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 75506ec2840..9623327ba50 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -188,6 +188,26 @@ static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env) rpdev->id.name); } +/** + * __ept_release() - deallocate an rpmsg endpoint + * @kref: the ept's reference count + * + * This function deallocates an ept, and is invoked when its @kref refcount + * drops to zero. + * + * Never invoke this function directly! + */ +static void __ept_release(struct kref *kref) +{ + struct rpmsg_endpoint *ept = container_of(kref, struct rpmsg_endpoint, + refcount); + /* + * At this point no one holds a reference to ept anymore, + * so we can directly free it + */ + kfree(ept); +} + /* for more info, see below documentation of rpmsg_create_ept() */ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, struct rpmsg_channel *rpdev, rpmsg_rx_cb_t cb, @@ -206,6 +226,8 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, return NULL; } + kref_init(&ept->refcount); + ept->rpdev = rpdev; ept->cb = cb; ept->priv = priv; @@ -238,7 +260,7 @@ rem_idr: idr_remove(&vrp->endpoints, request); free_ept: mutex_unlock(&vrp->endpoints_lock); - kfree(ept); + kref_put(&ept->refcount, __ept_release); return NULL; } @@ -306,7 +328,7 @@ __rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept) idr_remove(&vrp->endpoints, ept->addr); mutex_unlock(&vrp->endpoints_lock); - kfree(ept); + kref_put(&ept->refcount, __ept_release); } /** @@ -790,7 +812,13 @@ static void rpmsg_recv_done(struct virtqueue *rvq) /* use the dst addr to fetch the callback of the appropriate user */ mutex_lock(&vrp->endpoints_lock); + ept = idr_find(&vrp->endpoints, msg->dst); + + /* let's make sure no one deallocates ept while we use it */ + if (ept) + kref_get(&ept->refcount); + mutex_unlock(&vrp->endpoints_lock); if (ept && ept->cb) @@ -798,6 +826,10 @@ static void rpmsg_recv_done(struct virtqueue *rvq) else dev_warn(dev, "msg received with no recepient\n"); + /* farewell, ept, we don't need you anymore */ + if (ept) + kref_put(&ept->refcount, __ept_release); + /* publish the real size of the buffer */ sg_init_one(&sg, msg, RPMSG_BUF_SIZE); diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index a8e50e44203..195f373590b 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -38,6 +38,7 @@ #include #include #include +#include /* The feature bitmap for virtio rpmsg */ #define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ @@ -120,6 +121,7 @@ typedef void (*rpmsg_rx_cb_t)(struct rpmsg_channel *, void *, int, void *, u32); /** * struct rpmsg_endpoint - binds a local rpmsg address to its user * @rpdev: rpmsg channel device + * @refcount: when this drops to zero, the ept is deallocated * @cb: rx callback handler * @addr: local rpmsg address * @priv: private data for the driver's use @@ -140,6 +142,7 @@ typedef void (*rpmsg_rx_cb_t)(struct rpmsg_channel *, void *, int, void *, u32); */ struct rpmsg_endpoint { struct rpmsg_channel *rpdev; + struct kref refcount; rpmsg_rx_cb_t cb; u32 addr; void *priv; -- cgit v1.2.3-70-g09d2 From 15fd943af50dbc5f7f4de33835795c72595f7bf4 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Thu, 7 Jun 2012 15:39:35 +0300 Subject: rpmsg: make sure inflight messages don't invoke just-removed callbacks When inbound messages arrive, rpmsg core looks up their associated endpoint (by destination address) and then invokes their callback. We've made sure that endpoints will never be de-allocated after they were found by rpmsg core, but we also need to protect against the (rare) scenario where the rpmsg driver was just removed, and its callback function isn't available anymore. This is achieved by introducing a callback mutex, which must be taken before the callback is invoked, and, obviously, before it is removed. Cc: stable Reported-by: Fernando Guzman Lugo Signed-off-by: Ohad Ben-Cohen --- drivers/rpmsg/virtio_rpmsg_bus.c | 25 +++++++++++++++++++------ include/linux/rpmsg.h | 3 +++ 2 files changed, 22 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 9623327ba50..39d3aa41add 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -227,6 +227,7 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, } kref_init(&ept->refcount); + mutex_init(&ept->cb_lock); ept->rpdev = rpdev; ept->cb = cb; @@ -324,10 +325,16 @@ EXPORT_SYMBOL(rpmsg_create_ept); static void __rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept) { + /* make sure new inbound messages can't find this ept anymore */ mutex_lock(&vrp->endpoints_lock); idr_remove(&vrp->endpoints, ept->addr); mutex_unlock(&vrp->endpoints_lock); + /* make sure in-flight inbound messages won't invoke cb anymore */ + mutex_lock(&ept->cb_lock); + ept->cb = NULL; + mutex_unlock(&ept->cb_lock); + kref_put(&ept->refcount, __ept_release); } @@ -821,14 +828,20 @@ static void rpmsg_recv_done(struct virtqueue *rvq) mutex_unlock(&vrp->endpoints_lock); - if (ept && ept->cb) - ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, msg->src); - else - dev_warn(dev, "msg received with no recepient\n"); + if (ept) { + /* make sure ept->cb doesn't go away while we use it */ + mutex_lock(&ept->cb_lock); - /* farewell, ept, we don't need you anymore */ - if (ept) + if (ept->cb) + ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, + msg->src); + + mutex_unlock(&ept->cb_lock); + + /* farewell, ept, we don't need you anymore */ kref_put(&ept->refcount, __ept_release); + } else + dev_warn(dev, "msg received with no recepient\n"); /* publish the real size of the buffer */ sg_init_one(&sg, msg, RPMSG_BUF_SIZE); diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index 195f373590b..82a673905ed 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -39,6 +39,7 @@ #include #include #include +#include /* The feature bitmap for virtio rpmsg */ #define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ @@ -123,6 +124,7 @@ typedef void (*rpmsg_rx_cb_t)(struct rpmsg_channel *, void *, int, void *, u32); * @rpdev: rpmsg channel device * @refcount: when this drops to zero, the ept is deallocated * @cb: rx callback handler + * @cb_lock: must be taken before accessing/changing @cb * @addr: local rpmsg address * @priv: private data for the driver's use * @@ -144,6 +146,7 @@ struct rpmsg_endpoint { struct rpmsg_channel *rpdev; struct kref refcount; rpmsg_rx_cb_t cb; + struct mutex cb_lock; u32 addr; void *priv; }; -- cgit v1.2.3-70-g09d2 From 2dfd06036ba7ae8e7be2daf5a2fff1dac42390bf Mon Sep 17 00:00:00 2001 From: Junxiao Bi Date: Wed, 27 Jun 2012 17:09:54 +0800 Subject: aio: make kiocb->private NUll in init_sync_kiocb() Ocfs2 uses kiocb.*private as a flag of unsigned long size. In commit a11f7e6 ocfs2: serialize unaligned aio, the unaligned io flag is involved in it to serialize the unaligned aio. As *private is not initialized in init_sync_kiocb() of do_sync_write(), this unaligned io flag may be unexpectly set in an aligned dio. And this will cause OCFS2_I(inode)->ip_unaligned_aio decreased to -1 in ocfs2_dio_end_io(), thus the following unaligned dio will hang forever at ocfs2_aiodio_wait() in ocfs2_file_aio_write(). Signed-off-by: Junxiao Bi Cc: stable@vger.kernel.org Acked-by: Jeff Moyer Signed-off-by: Joel Becker --- include/linux/aio.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/aio.h b/include/linux/aio.h index 2314ad8b3c9..b1a520ec8b5 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -140,6 +140,7 @@ struct kiocb { (x)->ki_dtor = NULL; \ (x)->ki_obj.tsk = tsk; \ (x)->ki_user_data = 0; \ + (x)->private = NULL; \ } while (0) #define AIO_RING_MAGIC 0xa10a10a1 -- cgit v1.2.3-70-g09d2 From 6be5bfc3bf0d31a70745a52e69f7f46de974193f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 5 Jul 2012 18:12:01 +0530 Subject: regulator: fixed: dt: support for input supply Add support for input supply in DT parsing of node. The input supply will be provided by the property "vin-supply" in the regulator node. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/fixed-regulator.txt | 2 ++ drivers/regulator/fixed.c | 19 ++++++++++++++++++- include/linux/regulator/fixed.h | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt index 2f5b6b1ba15..4fae41d5479 100644 --- a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt @@ -10,6 +10,7 @@ Optional properties: If this property is missing, the default assumed is Active low. - gpio-open-drain: GPIO is open drain type. If this property is missing then default assumption is false. +-vin-supply: Input supply name. Any property defined as part of the core regulator binding, defined in regulator.txt, can also be used. @@ -29,4 +30,5 @@ Example: enable-active-high; regulator-boot-on; gpio-open-drain; + vin-supply = <&parent_reg>; }; diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 1a0b17e8d8a..185468c4d38 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -102,6 +102,9 @@ of_get_fixed_voltage_config(struct device *dev) if (of_find_property(np, "gpio-open-drain", NULL)) config->gpio_is_open_drain = true; + if (of_find_property(np, "vin-supply", NULL)) + config->input_supply = "vin"; + return config; } @@ -169,6 +172,17 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.enable_time = config->startup_delay; + if (config->input_supply) { + drvdata->desc.supply_name = kstrdup(config->input_supply, + GFP_KERNEL); + if (!drvdata->desc.supply_name) { + dev_err(&pdev->dev, + "Failed to allocate input supply\n"); + ret = -ENOMEM; + goto err_name; + } + } + if (config->microvolts) drvdata->desc.n_voltages = 1; @@ -202,7 +216,7 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) if (IS_ERR(drvdata->dev)) { ret = PTR_ERR(drvdata->dev); dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); - goto err_name; + goto err_input; } platform_set_drvdata(pdev, drvdata); @@ -212,6 +226,8 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) return 0; +err_input: + kfree(drvdata->desc.supply_name); err_name: kfree(drvdata->desc.name); err: @@ -223,6 +239,7 @@ static int __devexit reg_fixed_voltage_remove(struct platform_device *pdev) struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); regulator_unregister(drvdata->dev); + kfree(drvdata->desc.supply_name); kfree(drvdata->desc.name); return 0; diff --git a/include/linux/regulator/fixed.h b/include/linux/regulator/fixed.h index f83f7440b48..d6c24c53adf 100644 --- a/include/linux/regulator/fixed.h +++ b/include/linux/regulator/fixed.h @@ -22,6 +22,7 @@ struct regulator_init_data; /** * struct fixed_voltage_config - fixed_voltage_config structure * @supply_name: Name of the regulator supply + * @input_supply: Name of the input regulator supply * @microvolts: Output voltage of regulator * @gpio: GPIO to use for enable control * set to -EINVAL if not used @@ -46,6 +47,7 @@ struct regulator_init_data; */ struct fixed_voltage_config { const char *supply_name; + const char *input_supply; int microvolts; int gpio; unsigned startup_delay; -- cgit v1.2.3-70-g09d2 From f567fde24640cf6f2d6416196bfc8b3fefc8e433 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 20 Jun 2012 14:14:05 +0530 Subject: gpio: fix bits conflict for gpio flags The bit 2 and 3 in GPIO flag are allocated for the flag OPEN_DRAIN/OPEN_SOURCE. These bits are reused for the flag EXPORT/EXPORT_CHANGEABLE and so creating conflict. Fix this conflict by assigning bit 4 and 5 for the flag EXPORT/EXPORT_CHANGEABLE. Signed-off-by: Laxman Dewangan Signed-off-by: Linus Walleij --- include/linux/gpio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/gpio.h b/include/linux/gpio.h index f07fc2d0815..2e31e8b3a19 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -22,8 +22,8 @@ /* Gpio pin is open source */ #define GPIOF_OPEN_SOURCE (1 << 3) -#define GPIOF_EXPORT (1 << 2) -#define GPIOF_EXPORT_CHANGEABLE (1 << 3) +#define GPIOF_EXPORT (1 << 4) +#define GPIOF_EXPORT_CHANGEABLE (1 << 5) #define GPIOF_EXPORT_DIR_FIXED (GPIOF_EXPORT) #define GPIOF_EXPORT_DIR_CHANGEABLE (GPIOF_EXPORT | GPIOF_EXPORT_CHANGEABLE) -- cgit v1.2.3-70-g09d2 From 5167e8d5417bf5c322a703d2927daec727ea40dd Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 22 Jun 2012 15:52:09 +0200 Subject: sched/nohz: Rewrite and fix load-avg computation -- again Thanks to Charles Wang for spotting the defects in the current code: - If we go idle during the sample window -- after sampling, we get a negative bias because we can negate our own sample. - If we wake up during the sample window we get a positive bias because we push the sample to a known active period. So rewrite the entire nohz load-avg muck once again, now adding copious documentation to the code. Reported-and-tested-by: Doug Smythies Reported-and-tested-by: Charles Wang Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Cc: stable@kernel.org Link: http://lkml.kernel.org/r/1340373782.18025.74.camel@twins [ minor edits ] Signed-off-by: Ingo Molnar --- include/linux/sched.h | 8 ++ kernel/sched/core.c | 275 ++++++++++++++++++++++++++++++++++------------- kernel/sched/idle_task.c | 1 - kernel/sched/sched.h | 2 - kernel/time/tick-sched.c | 2 + 5 files changed, 213 insertions(+), 75 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 4059c0f33f0..20cb7497c59 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1909,6 +1909,14 @@ static inline int set_cpus_allowed_ptr(struct task_struct *p, } #endif +#ifdef CONFIG_NO_HZ +void calc_load_enter_idle(void); +void calc_load_exit_idle(void); +#else +static inline void calc_load_enter_idle(void) { } +static inline void calc_load_exit_idle(void) { } +#endif /* CONFIG_NO_HZ */ + #ifndef CONFIG_CPUMASK_OFFSTACK static inline int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask) { diff --git a/kernel/sched/core.c b/kernel/sched/core.c index d5594a4268d..bb840405335 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2161,11 +2161,73 @@ unsigned long this_cpu_load(void) } +/* + * Global load-average calculations + * + * We take a distributed and async approach to calculating the global load-avg + * in order to minimize overhead. + * + * The global load average is an exponentially decaying average of nr_running + + * nr_uninterruptible. + * + * Once every LOAD_FREQ: + * + * nr_active = 0; + * for_each_possible_cpu(cpu) + * nr_active += cpu_of(cpu)->nr_running + cpu_of(cpu)->nr_uninterruptible; + * + * avenrun[n] = avenrun[0] * exp_n + nr_active * (1 - exp_n) + * + * Due to a number of reasons the above turns in the mess below: + * + * - for_each_possible_cpu() is prohibitively expensive on machines with + * serious number of cpus, therefore we need to take a distributed approach + * to calculating nr_active. + * + * \Sum_i x_i(t) = \Sum_i x_i(t) - x_i(t_0) | x_i(t_0) := 0 + * = \Sum_i { \Sum_j=1 x_i(t_j) - x_i(t_j-1) } + * + * So assuming nr_active := 0 when we start out -- true per definition, we + * can simply take per-cpu deltas and fold those into a global accumulate + * to obtain the same result. See calc_load_fold_active(). + * + * Furthermore, in order to avoid synchronizing all per-cpu delta folding + * across the machine, we assume 10 ticks is sufficient time for every + * cpu to have completed this task. + * + * This places an upper-bound on the IRQ-off latency of the machine. Then + * again, being late doesn't loose the delta, just wrecks the sample. + * + * - cpu_rq()->nr_uninterruptible isn't accurately tracked per-cpu because + * this would add another cross-cpu cacheline miss and atomic operation + * to the wakeup path. Instead we increment on whatever cpu the task ran + * when it went into uninterruptible state and decrement on whatever cpu + * did the wakeup. This means that only the sum of nr_uninterruptible over + * all cpus yields the correct result. + * + * This covers the NO_HZ=n code, for extra head-aches, see the comment below. + */ + /* Variables and functions for calc_load */ static atomic_long_t calc_load_tasks; static unsigned long calc_load_update; unsigned long avenrun[3]; -EXPORT_SYMBOL(avenrun); +EXPORT_SYMBOL(avenrun); /* should be removed */ + +/** + * get_avenrun - get the load average array + * @loads: pointer to dest load array + * @offset: offset to add + * @shift: shift count to shift the result left + * + * These values are estimates at best, so no need for locking. + */ +void get_avenrun(unsigned long *loads, unsigned long offset, int shift) +{ + loads[0] = (avenrun[0] + offset) << shift; + loads[1] = (avenrun[1] + offset) << shift; + loads[2] = (avenrun[2] + offset) << shift; +} static long calc_load_fold_active(struct rq *this_rq) { @@ -2182,6 +2244,9 @@ static long calc_load_fold_active(struct rq *this_rq) return delta; } +/* + * a1 = a0 * e + a * (1 - e) + */ static unsigned long calc_load(unsigned long load, unsigned long exp, unsigned long active) { @@ -2193,30 +2258,118 @@ calc_load(unsigned long load, unsigned long exp, unsigned long active) #ifdef CONFIG_NO_HZ /* - * For NO_HZ we delay the active fold to the next LOAD_FREQ update. + * Handle NO_HZ for the global load-average. + * + * Since the above described distributed algorithm to compute the global + * load-average relies on per-cpu sampling from the tick, it is affected by + * NO_HZ. + * + * The basic idea is to fold the nr_active delta into a global idle-delta upon + * entering NO_HZ state such that we can include this as an 'extra' cpu delta + * when we read the global state. + * + * Obviously reality has to ruin such a delightfully simple scheme: + * + * - When we go NO_HZ idle during the window, we can negate our sample + * contribution, causing under-accounting. + * + * We avoid this by keeping two idle-delta counters and flipping them + * when the window starts, thus separating old and new NO_HZ load. + * + * The only trick is the slight shift in index flip for read vs write. + * + * 0s 5s 10s 15s + * +10 +10 +10 +10 + * |-|-----------|-|-----------|-|-----------|-| + * r:0 0 1 1 0 0 1 1 0 + * w:0 1 1 0 0 1 1 0 0 + * + * This ensures we'll fold the old idle contribution in this window while + * accumlating the new one. + * + * - When we wake up from NO_HZ idle during the window, we push up our + * contribution, since we effectively move our sample point to a known + * busy state. + * + * This is solved by pushing the window forward, and thus skipping the + * sample, for this cpu (effectively using the idle-delta for this cpu which + * was in effect at the time the window opened). This also solves the issue + * of having to deal with a cpu having been in NOHZ idle for multiple + * LOAD_FREQ intervals. * * When making the ILB scale, we should try to pull this in as well. */ -static atomic_long_t calc_load_tasks_idle; +static atomic_long_t calc_load_idle[2]; +static int calc_load_idx; -void calc_load_account_idle(struct rq *this_rq) +static inline int calc_load_write_idx(void) { + int idx = calc_load_idx; + + /* + * See calc_global_nohz(), if we observe the new index, we also + * need to observe the new update time. + */ + smp_rmb(); + + /* + * If the folding window started, make sure we start writing in the + * next idle-delta. + */ + if (!time_before(jiffies, calc_load_update)) + idx++; + + return idx & 1; +} + +static inline int calc_load_read_idx(void) +{ + return calc_load_idx & 1; +} + +void calc_load_enter_idle(void) +{ + struct rq *this_rq = this_rq(); long delta; + /* + * We're going into NOHZ mode, if there's any pending delta, fold it + * into the pending idle delta. + */ delta = calc_load_fold_active(this_rq); - if (delta) - atomic_long_add(delta, &calc_load_tasks_idle); + if (delta) { + int idx = calc_load_write_idx(); + atomic_long_add(delta, &calc_load_idle[idx]); + } } -static long calc_load_fold_idle(void) +void calc_load_exit_idle(void) { - long delta = 0; + struct rq *this_rq = this_rq(); + + /* + * If we're still before the sample window, we're done. + */ + if (time_before(jiffies, this_rq->calc_load_update)) + return; /* - * Its got a race, we don't care... + * We woke inside or after the sample window, this means we're already + * accounted through the nohz accounting, so skip the entire deal and + * sync up for the next window. */ - if (atomic_long_read(&calc_load_tasks_idle)) - delta = atomic_long_xchg(&calc_load_tasks_idle, 0); + this_rq->calc_load_update = calc_load_update; + if (time_before(jiffies, this_rq->calc_load_update + 10)) + this_rq->calc_load_update += LOAD_FREQ; +} + +static long calc_load_fold_idle(void) +{ + int idx = calc_load_read_idx(); + long delta = 0; + + if (atomic_long_read(&calc_load_idle[idx])) + delta = atomic_long_xchg(&calc_load_idle[idx], 0); return delta; } @@ -2302,66 +2455,39 @@ static void calc_global_nohz(void) { long delta, active, n; - /* - * If we crossed a calc_load_update boundary, make sure to fold - * any pending idle changes, the respective CPUs might have - * missed the tick driven calc_load_account_active() update - * due to NO_HZ. - */ - delta = calc_load_fold_idle(); - if (delta) - atomic_long_add(delta, &calc_load_tasks); - - /* - * It could be the one fold was all it took, we done! - */ - if (time_before(jiffies, calc_load_update + 10)) - return; - - /* - * Catch-up, fold however many we are behind still - */ - delta = jiffies - calc_load_update - 10; - n = 1 + (delta / LOAD_FREQ); + if (!time_before(jiffies, calc_load_update + 10)) { + /* + * Catch-up, fold however many we are behind still + */ + delta = jiffies - calc_load_update - 10; + n = 1 + (delta / LOAD_FREQ); - active = atomic_long_read(&calc_load_tasks); - active = active > 0 ? active * FIXED_1 : 0; + active = atomic_long_read(&calc_load_tasks); + active = active > 0 ? active * FIXED_1 : 0; - avenrun[0] = calc_load_n(avenrun[0], EXP_1, active, n); - avenrun[1] = calc_load_n(avenrun[1], EXP_5, active, n); - avenrun[2] = calc_load_n(avenrun[2], EXP_15, active, n); + avenrun[0] = calc_load_n(avenrun[0], EXP_1, active, n); + avenrun[1] = calc_load_n(avenrun[1], EXP_5, active, n); + avenrun[2] = calc_load_n(avenrun[2], EXP_15, active, n); - calc_load_update += n * LOAD_FREQ; -} -#else -void calc_load_account_idle(struct rq *this_rq) -{ -} + calc_load_update += n * LOAD_FREQ; + } -static inline long calc_load_fold_idle(void) -{ - return 0; + /* + * Flip the idle index... + * + * Make sure we first write the new time then flip the index, so that + * calc_load_write_idx() will see the new time when it reads the new + * index, this avoids a double flip messing things up. + */ + smp_wmb(); + calc_load_idx++; } +#else /* !CONFIG_NO_HZ */ -static void calc_global_nohz(void) -{ -} -#endif +static inline long calc_load_fold_idle(void) { return 0; } +static inline void calc_global_nohz(void) { } -/** - * get_avenrun - get the load average array - * @loads: pointer to dest load array - * @offset: offset to add - * @shift: shift count to shift the result left - * - * These values are estimates at best, so no need for locking. - */ -void get_avenrun(unsigned long *loads, unsigned long offset, int shift) -{ - loads[0] = (avenrun[0] + offset) << shift; - loads[1] = (avenrun[1] + offset) << shift; - loads[2] = (avenrun[2] + offset) << shift; -} +#endif /* CONFIG_NO_HZ */ /* * calc_load - update the avenrun load estimates 10 ticks after the @@ -2369,11 +2495,18 @@ void get_avenrun(unsigned long *loads, unsigned long offset, int shift) */ void calc_global_load(unsigned long ticks) { - long active; + long active, delta; if (time_before(jiffies, calc_load_update + 10)) return; + /* + * Fold the 'old' idle-delta to include all NO_HZ cpus. + */ + delta = calc_load_fold_idle(); + if (delta) + atomic_long_add(delta, &calc_load_tasks); + active = atomic_long_read(&calc_load_tasks); active = active > 0 ? active * FIXED_1 : 0; @@ -2384,12 +2517,7 @@ void calc_global_load(unsigned long ticks) calc_load_update += LOAD_FREQ; /* - * Account one period with whatever state we found before - * folding in the nohz state and ageing the entire idle period. - * - * This avoids loosing a sample when we go idle between - * calc_load_account_active() (10 ticks ago) and now and thus - * under-accounting. + * In case we idled for multiple LOAD_FREQ intervals, catch up in bulk. */ calc_global_nohz(); } @@ -2406,13 +2534,16 @@ static void calc_load_account_active(struct rq *this_rq) return; delta = calc_load_fold_active(this_rq); - delta += calc_load_fold_idle(); if (delta) atomic_long_add(delta, &calc_load_tasks); this_rq->calc_load_update += LOAD_FREQ; } +/* + * End of global load-average stuff + */ + /* * The exact cpuload at various idx values, calculated at every tick would be * load = (2^idx - 1) / 2^idx * load + 1 / 2^idx * cur_load diff --git a/kernel/sched/idle_task.c b/kernel/sched/idle_task.c index b44d604b35d..b6baf370cae 100644 --- a/kernel/sched/idle_task.c +++ b/kernel/sched/idle_task.c @@ -25,7 +25,6 @@ static void check_preempt_curr_idle(struct rq *rq, struct task_struct *p, int fl static struct task_struct *pick_next_task_idle(struct rq *rq) { schedstat_inc(rq, sched_goidle); - calc_load_account_idle(rq); return rq->idle; } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 6d52cea7f33..55844f24435 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -942,8 +942,6 @@ static inline u64 sched_avg_period(void) return (u64)sysctl_sched_time_avg * NSEC_PER_MSEC / 2; } -void calc_load_account_idle(struct rq *this_rq); - #ifdef CONFIG_SCHED_HRTICK /* diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 86999783392..4a08472c3ca 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -406,6 +406,7 @@ static void tick_nohz_stop_sched_tick(struct tick_sched *ts) */ if (!ts->tick_stopped) { select_nohz_load_balancer(1); + calc_load_enter_idle(); ts->idle_tick = hrtimer_get_expires(&ts->sched_timer); ts->tick_stopped = 1; @@ -597,6 +598,7 @@ void tick_nohz_idle_exit(void) account_idle_ticks(ticks); #endif + calc_load_exit_idle(); touch_softlockup_watchdog(); /* * Cancel the scheduled timer and restore the tick -- cgit v1.2.3-70-g09d2 From 19228a6a59250d414824ae07e06ad057a404ea3e Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 6 Jul 2012 14:13:12 +0530 Subject: regulator: tps65910: add support for input supply There is multiple voltage input pins on device which takes the voltage input for different voltage regulator. Support to configure the voltage input supplied by different regulator for each regulators. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/tps65910.txt | 25 +++++++++++++++ drivers/regulator/tps65910-regulator.c | 36 ++++++++++++++++++++++ include/linux/mfd/tps65910.h | 2 ++ 3 files changed, 63 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/mfd/tps65910.txt b/Documentation/devicetree/bindings/mfd/tps65910.txt index b51d2066599..31be5a3d9f7 100644 --- a/Documentation/devicetree/bindings/mfd/tps65910.txt +++ b/Documentation/devicetree/bindings/mfd/tps65910.txt @@ -32,6 +32,28 @@ Optional properties: comparator. (see VMBCH_VSEL in TPS65910 datasheet) - ti,en-gpio-sleep: enable sleep control for gpios There should be 9 entries here, one for each gpio. +- xxx-supply: Input voltage supply regulator. + Missing of these properties will be assume as there is no supply regulator + for that input pins and always powered on. + The valid input supply properties are: + tps65910: + vcc1-supply: VDD1 input. + vcc2-supply: VDD2 input. + vcc3-supply: VAUX33 and VMMC input. + vcc4-supply: VAUX1 and VAUX2 input. + vcc5-supply: VPLL and VDAC input. + vcc6-supply: VDIG1 and VDIG2 input. + vcc7-supply: VRTC input. + vccio-supply: VIO input. + tps65911: + vcc1-supply: VDD1 input. + vcc2-supply: VDD2 input. + vcc3-supply: LDO6, LDO7 and LDO8 input. + vcc4-supply: LDO5 input. + vcc5-supply: LDO3 and LDO4 input. + vcc6-supply: LDO1 and LDO2 input. + vcc7-supply: VRTC input. + vccio-supply: VIO input. Regulator Optional properties: - ti,regulator-ext-sleep-control: enable external sleep @@ -57,6 +79,9 @@ Example: ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>; + vcc7-supply = <®_parent>; + vcc1-supply = <®_parent>; + regulators { #address-cells = <1>; #size-cells = <0>; diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index a534e087200..e319d963fee 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -85,6 +85,7 @@ static const unsigned int VMMC_VSEL_table[] = { struct tps_info { const char *name; + const char *vin_name; u8 n_voltages; const unsigned int *voltage_table; int enable_time_us; @@ -93,20 +94,24 @@ struct tps_info { static struct tps_info tps65910_regs[] = { { .name = "vrtc", + .vin_name = "vcc7", .enable_time_us = 2200, }, { .name = "vio", + .vin_name = "vccio", .n_voltages = ARRAY_SIZE(VIO_VSEL_table), .voltage_table = VIO_VSEL_table, .enable_time_us = 350, }, { .name = "vdd1", + .vin_name = "vcc1", .enable_time_us = 350, }, { .name = "vdd2", + .vin_name = "vcc2", .enable_time_us = 350, }, { @@ -117,48 +122,56 @@ static struct tps_info tps65910_regs[] = { }, { .name = "vdig1", + .vin_name = "vcc6", .n_voltages = ARRAY_SIZE(VDIG1_VSEL_table), .voltage_table = VDIG1_VSEL_table, .enable_time_us = 100, }, { .name = "vdig2", + .vin_name = "vcc6", .n_voltages = ARRAY_SIZE(VDIG2_VSEL_table), .voltage_table = VDIG2_VSEL_table, .enable_time_us = 100, }, { .name = "vpll", + .vin_name = "vcc5", .n_voltages = ARRAY_SIZE(VPLL_VSEL_table), .voltage_table = VPLL_VSEL_table, .enable_time_us = 100, }, { .name = "vdac", + .vin_name = "vcc5", .n_voltages = ARRAY_SIZE(VDAC_VSEL_table), .voltage_table = VDAC_VSEL_table, .enable_time_us = 100, }, { .name = "vaux1", + .vin_name = "vcc4", .n_voltages = ARRAY_SIZE(VAUX1_VSEL_table), .voltage_table = VAUX1_VSEL_table, .enable_time_us = 100, }, { .name = "vaux2", + .vin_name = "vcc4", .n_voltages = ARRAY_SIZE(VAUX2_VSEL_table), .voltage_table = VAUX2_VSEL_table, .enable_time_us = 100, }, { .name = "vaux33", + .vin_name = "vcc3", .n_voltages = ARRAY_SIZE(VAUX33_VSEL_table), .voltage_table = VAUX33_VSEL_table, .enable_time_us = 100, }, { .name = "vmmc", + .vin_name = "vcc3", .n_voltages = ARRAY_SIZE(VMMC_VSEL_table), .voltage_table = VMMC_VSEL_table, .enable_time_us = 100, @@ -168,21 +181,25 @@ static struct tps_info tps65910_regs[] = { static struct tps_info tps65911_regs[] = { { .name = "vrtc", + .vin_name = "vcc7", .enable_time_us = 2200, }, { .name = "vio", + .vin_name = "vccio", .n_voltages = ARRAY_SIZE(VIO_VSEL_table), .voltage_table = VIO_VSEL_table, .enable_time_us = 350, }, { .name = "vdd1", + .vin_name = "vcc1", .n_voltages = 73, .enable_time_us = 350, }, { .name = "vdd2", + .vin_name = "vcc2", .n_voltages = 73, .enable_time_us = 350, }, @@ -193,41 +210,49 @@ static struct tps_info tps65911_regs[] = { }, { .name = "ldo1", + .vin_name = "vcc6", .n_voltages = 47, .enable_time_us = 420, }, { .name = "ldo2", + .vin_name = "vcc6", .n_voltages = 47, .enable_time_us = 420, }, { .name = "ldo3", + .vin_name = "vcc5", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo4", + .vin_name = "vcc5", .n_voltages = 47, .enable_time_us = 230, }, { .name = "ldo5", + .vin_name = "vcc4", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo6", + .vin_name = "vcc3", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo7", + .vin_name = "vcc3", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo8", + .vin_name = "vcc3", .n_voltages = 24, .enable_time_us = 230, }, @@ -1013,6 +1038,9 @@ static struct tps65910_board *tps65910_parse_dt_reg_data( *tps65910_reg_matches = matches; for (idx = 0; idx < count; idx++) { + struct tps_info *info = matches[idx].driver_data; + char in_supply[32]; /* 32 is max size of property name */ + if (!matches[idx].init_data || !matches[idx].of_node) continue; @@ -1023,6 +1051,13 @@ static struct tps65910_board *tps65910_parse_dt_reg_data( "ti,regulator-ext-sleep-control", &prop); if (!ret) pmic_plat_data->regulator_ext_sleep_control[idx] = prop; + + if (info->vin_name) { + snprintf(in_supply, 32, "%s-supply", info->vin_name); + if (of_find_property(np, in_supply, 0)) + pmic_plat_data->input_supply[idx] = + info->vin_name; + } } return pmic_plat_data; @@ -1126,6 +1161,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev) pmic->info[i] = info; pmic->desc[i].name = info->name; + pmic->desc[i].supply_name = pmic_plat_data->input_supply[i]; pmic->desc[i].id = i; pmic->desc[i].n_voltages = info->n_voltages; pmic->desc[i].enable_time = info->enable_time_us; diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index dd8dc0a6c46..c5f806011b3 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -799,6 +799,7 @@ struct tps65910_sleep_keepon_data { /** * struct tps65910_board * Board platform data may be used to initialize regulators. + * @input_supply: Name of input supply regulator. */ struct tps65910_board { @@ -811,6 +812,7 @@ struct tps65910_board { struct tps65910_sleep_keepon_data *slp_keepon; bool en_gpio_sleep[TPS6591X_MAX_NUM_GPIO]; unsigned long regulator_ext_sleep_control[TPS65910_NUM_REGS]; + const char *input_supply[TPS65910_NUM_REGS]; struct regulator_init_data *tps65910_pmic_init_data[TPS65910_NUM_REGS]; }; -- cgit v1.2.3-70-g09d2 From c540521bba5d2f24bd2c0417157bfaf8b85e2eee Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Thu, 5 Jul 2012 11:23:24 -0700 Subject: security: Minor improvements to no_new_privs documentation The documentation didn't actually mention how to enable no_new_privs. This also adds a note about possible interactions between no_new_privs and LSMs (i.e. why teaching systemd to set no_new_privs is not necessarily a good idea), and it references the new docs from include/linux/prctl.h. Suggested-by: Rob Landley Signed-off-by: Andy Lutomirski Acked-by: Kees Cook Signed-off-by: James Morris --- Documentation/prctl/no_new_privs.txt | 7 +++++++ include/linux/prctl.h | 2 ++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/Documentation/prctl/no_new_privs.txt b/Documentation/prctl/no_new_privs.txt index cb705ec69ab..f7be84fba91 100644 --- a/Documentation/prctl/no_new_privs.txt +++ b/Documentation/prctl/no_new_privs.txt @@ -25,6 +25,13 @@ bits will no longer change the uid or gid; file capabilities will not add to the permitted set, and LSMs will not relax constraints after execve. +To set no_new_privs, use prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0). + +Be careful, though: LSMs might also not tighten constraints on exec +in no_new_privs mode. (This means that setting up a general-purpose +service launcher to set no_new_privs before execing daemons may +interfere with LSM-based sandboxing.) + Note that no_new_privs does not prevent privilege changes that do not involve execve. An appropriately privileged task can still call setuid(2) and receive SCM_RIGHTS datagrams. diff --git a/include/linux/prctl.h b/include/linux/prctl.h index 3988012255d..289760f424a 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h @@ -141,6 +141,8 @@ * Changing LSM security domain is considered a new privilege. So, for example, * asking selinux for a specific new context (e.g. with runcon) will result * in execve returning -EPERM. + * + * See Documentation/prctl/no_new_privs.txt for more details. */ #define PR_SET_NO_NEW_PRIVS 38 #define PR_GET_NO_NEW_PRIVS 39 -- cgit v1.2.3-70-g09d2 From 222a806af830fda34ad1f6bc991cd226916de060 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Thu, 21 Jun 2012 12:23:42 -0700 Subject: [SCSI] Fix NULL dereferences in scsi_cmd_to_driver Avoid crashing if the private_data pointer happens to be NULL. This has been seen sometimes when a host reset happens, notably when there are many LUNs: host3: Assigned Port ID 0c1601 scsi host3: libfc: Host reset succeeded on port (0c1601) BUG: unable to handle kernel NULL pointer dereference at 0000000000000350 IP: [] scsi_send_eh_cmnd+0x58/0x3a0 Process scsi_eh_3 (pid: 4144, threadinfo ffff88030920c000, task ffff880326b160c0) Stack: 000000010372e6ba 0000000000000282 000027100920dca0 ffffffffa0038ee0 0000000000000000 0000000000030003 ffff88030920dc80 ffff88030920dc80 00000002000e0000 0000000a00004000 ffff8803242f7760 ffff88031326ed80 Call Trace: [] ? lock_timer_base+0x70/0x70 [] scsi_eh_tur+0x3e/0xc0 [] scsi_eh_test_devices+0x76/0x170 [] scsi_eh_host_reset+0x85/0x160 [] scsi_eh_ready_devs+0x91/0x110 [] scsi_unjam_host+0xed/0x1f0 [] scsi_error_handler+0x1a8/0x200 [] ? scsi_unjam_host+0x1f0/0x1f0 [] kthread+0x9e/0xb0 [] kernel_thread_helper+0x4/0x10 [] ? kthread_freezable_should_stop+0x70/0x70 [] ? gs_change+0x13/0x13 Code: 25 28 00 00 00 48 89 45 c8 31 c0 48 8b 87 80 00 00 00 48 8d b5 60 ff ff ff 89 d1 48 89 fb 41 89 d6 4c 89 fa 48 8b 80 b8 00 00 00 <48> 8b 80 50 03 00 00 48 8b 00 48 89 85 38 ff ff ff 48 8b 07 4c RIP [] scsi_send_eh_cmnd+0x58/0x3a0 RSP CR2: 0000000000000350 Signed-off-by: Mark Rustad Tested-by: Marcus Dennis Cc: Signed-off-by: James Bottomley --- include/scsi/scsi_cmnd.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 1e1198546c7..ac06cc59589 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -134,10 +134,16 @@ struct scsi_cmnd { static inline struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd) { + struct scsi_driver **sdp; + if (!cmd->request->rq_disk) return NULL; - return *(struct scsi_driver **)cmd->request->rq_disk->private_data; + sdp = (struct scsi_driver **)cmd->request->rq_disk->private_data; + if (!sdp) + return NULL; + + return *sdp; } extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t); -- cgit v1.2.3-70-g09d2 From 6ef1b512f4e6f936d89aa20be3d97a7ec7c290ac Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 22 Jun 2012 10:52:34 -0700 Subject: [SCSI] libsas: fix taskfile corruption in sas_ata_qc_fill_rtf fill_result_tf() grabs the taskfile flags from the originating qc which sas_ata_qc_fill_rtf() promptly overwrites. The presence of an ata_taskfile in the sata_device makes it tempting to just copy the full contents in sas_ata_qc_fill_rtf(). However, libata really only wants the fis contents and expects the other portions of the taskfile to not be touched by ->qc_fill_rtf. To that end store a fis buffer in the sata_device and use ata_tf_from_fis() like every other ->qc_fill_rtf() implementation. Cc: Reported-by: Praveen Murali Tested-by: Praveen Murali Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_task.c | 2 +- drivers/scsi/libsas/sas_ata.c | 12 ++++++------ include/scsi/libsas.h | 6 ++++-- 3 files changed, 11 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c index 532d212b6b2..393e7ce8e95 100644 --- a/drivers/scsi/aic94xx/aic94xx_task.c +++ b/drivers/scsi/aic94xx/aic94xx_task.c @@ -201,7 +201,7 @@ static void asd_get_response_tasklet(struct asd_ascb *ascb, if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) { resp->frame_len = le16_to_cpu(*(__le16 *)(r+6)); - memcpy(&resp->ending_fis[0], r+16, 24); + memcpy(&resp->ending_fis[0], r+16, ATA_RESP_FIS_SIZE); ts->buf_valid_size = sizeof(*resp); } } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 441d88ad99a..d109cc3a17b 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -139,12 +139,12 @@ static void sas_ata_task_done(struct sas_task *task) if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD || ((stat->stat == SAM_STAT_CHECK_CONDITION && dev->sata_dev.command_set == ATAPI_COMMAND_SET))) { - ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); + memcpy(dev->sata_dev.fis, resp->ending_fis, ATA_RESP_FIS_SIZE); if (!link->sactive) { - qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); + qc->err_mask |= ac_err_mask(dev->sata_dev.fis[2]); } else { - link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.tf.command); + link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.fis[2]); if (unlikely(link->eh_info.err_mask)) qc->flags |= ATA_QCFLAG_FAILED; } @@ -161,8 +161,8 @@ static void sas_ata_task_done(struct sas_task *task) qc->flags |= ATA_QCFLAG_FAILED; } - dev->sata_dev.tf.feature = 0x04; /* status err */ - dev->sata_dev.tf.command = ATA_ERR; + dev->sata_dev.fis[3] = 0x04; /* status err */ + dev->sata_dev.fis[2] = ATA_ERR; } } @@ -269,7 +269,7 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) { struct domain_device *dev = qc->ap->private_data; - memcpy(&qc->result_tf, &dev->sata_dev.tf, sizeof(qc->result_tf)); + ata_tf_from_fis(dev->sata_dev.fis, &qc->result_tf); return true; } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index f4f1c96dca7..10ce74f589c 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -163,6 +163,8 @@ enum ata_command_set { ATAPI_COMMAND_SET = 1, }; +#define ATA_RESP_FIS_SIZE 24 + struct sata_device { enum ata_command_set command_set; struct smp_resp rps_resp; /* report_phy_sata_resp */ @@ -171,7 +173,7 @@ struct sata_device { struct ata_port *ap; struct ata_host ata_host; - struct ata_taskfile tf; + u8 fis[ATA_RESP_FIS_SIZE]; }; enum { @@ -537,7 +539,7 @@ enum exec_status { */ struct ata_task_resp { u16 frame_len; - u8 ending_fis[24]; /* dev to host or data-in */ + u8 ending_fis[ATA_RESP_FIS_SIZE]; /* dev to host or data-in */ }; #define SAS_STATUS_BUF_SIZE 96 -- cgit v1.2.3-70-g09d2 From dbf0e4c7257f8d684ec1a3c919853464293de66e Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 9 Jul 2012 11:09:21 -0400 Subject: PCI: EHCI: fix crash during suspend on ASUS computers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quite a few ASUS computers experience a nasty problem, related to the EHCI controllers, when going into system suspend. It was observed that the problem didn't occur if the controllers were not put into the D3 power state before starting the suspend, and commit 151b61284776be2d6f02d48c23c3625678960b97 (USB: EHCI: fix crash during suspend on ASUS computers) was created to do this. It turned out this approach messed up other computers that didn't have the problem -- it prevented USB wakeup from working. Consequently commit c2fb8a3fa25513de8fedb38509b1f15a5bbee47b (USB: add NO_D3_DURING_SLEEP flag and revert 151b61284776be2) was merged; it reverted the earlier commit and added a whitelist of known good board names. Now we know the actual cause of the problem. Thanks to AceLan Kao for tracking it down. According to him, an engineer at ASUS explained that some of their BIOSes contain a bug that was added in an attempt to work around a problem in early versions of Windows. When the computer goes into S3 suspend, the BIOS tries to verify that the EHCI controllers were first quiesced by the OS. Nothing's wrong with this, but the BIOS does it by checking that the PCI COMMAND registers contain 0 without checking the controllers' power state. If the register isn't 0, the BIOS assumes the controller needs to be quiesced and tries to do so. This involves making various MMIO accesses to the controller, which don't work very well if the controller is already in D3. The end result is a system hang or memory corruption. Since the value in the PCI COMMAND register doesn't matter once the controller has been suspended, and since the value will be restored anyway when the controller is resumed, we can work around the BIOS bug simply by setting the register to 0 during system suspend. This patch (as1590) does so and also reverts the second commit mentioned above, which is now unnecessary. In theory we could do this for every PCI device. However to avoid introducing new problems, the patch restricts itself to EHCI host controllers. Finally the affected systems can suspend with USB wakeup working properly. Reference: https://bugzilla.kernel.org/show_bug.cgi?id=37632 Reference: https://bugzilla.kernel.org/show_bug.cgi?id=42728 Based-on-patch-by: AceLan Kao Signed-off-by: Alan Stern Tested-by: Dâniel Fraga Tested-by: Javier Marcet Tested-by: Andrey Rahmatullin Tested-by: Oleksij Rempel Tested-by: Pavel Pisa Cc: stable Acked-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-driver.c | 12 ++++++++++++ drivers/pci/pci.c | 5 ----- drivers/pci/quirks.c | 26 -------------------------- include/linux/pci.h | 2 -- 4 files changed, 12 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index bf0cee629b6..099f46cd8e8 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -748,6 +748,18 @@ static int pci_pm_suspend_noirq(struct device *dev) pci_pm_set_unknown_state(pci_dev); + /* + * Some BIOSes from ASUS have a bug: If a USB EHCI host controller's + * PCI COMMAND register isn't 0, the BIOS assumes that the controller + * hasn't been quiesced and tries to turn it off. If the controller + * is already in D3, this can hang or cause memory corruption. + * + * Since the value of the COMMAND register doesn't matter once the + * device has been suspended, we can safely set it to 0 here. + */ + if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) + pci_write_config_word(pci_dev, PCI_COMMAND, 0); + return 0; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 77cb54a65cd..447e83472c0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1744,11 +1744,6 @@ int pci_prepare_to_sleep(struct pci_dev *dev) if (target_state == PCI_POWER_ERROR) return -EIO; - /* Some devices mustn't be in D3 during system sleep */ - if (target_state == PCI_D3hot && - (dev->dev_flags & PCI_DEV_FLAGS_NO_D3_DURING_SLEEP)) - return 0; - pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev)); error = pci_set_power_state(dev, target_state); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 194b243a281..2a752167754 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2929,32 +2929,6 @@ static void __devinit disable_igfx_irq(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); -/* - * The Intel 6 Series/C200 Series chipset's EHCI controllers on many - * ASUS motherboards will cause memory corruption or a system crash - * if they are in D3 while the system is put into S3 sleep. - */ -static void __devinit asus_ehci_no_d3(struct pci_dev *dev) -{ - const char *sys_info; - static const char good_Asus_board[] = "P8Z68-V"; - - if (dev->dev_flags & PCI_DEV_FLAGS_NO_D3_DURING_SLEEP) - return; - if (dev->subsystem_vendor != PCI_VENDOR_ID_ASUSTEK) - return; - sys_info = dmi_get_system_info(DMI_BOARD_NAME); - if (sys_info && memcmp(sys_info, good_Asus_board, - sizeof(good_Asus_board) - 1) == 0) - return; - - dev_info(&dev->dev, "broken D3 during system sleep on ASUS\n"); - dev->dev_flags |= PCI_DEV_FLAGS_NO_D3_DURING_SLEEP; - device_set_wakeup_capable(&dev->dev, false); -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1c26, asus_ehci_no_d3); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1c2d, asus_ehci_no_d3); - static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) { diff --git a/include/linux/pci.h b/include/linux/pci.h index fefb4e19bf6..d8c379dba6a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -176,8 +176,6 @@ enum pci_dev_flags { PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2, /* Provide indication device is assigned by a Virtual Machine Manager */ PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) 4, - /* Device causes system crash if in D3 during S3 sleep */ - PCI_DEV_FLAGS_NO_D3_DURING_SLEEP = (__force pci_dev_flags_t) 8, }; enum pci_irq_reroute_variant { -- cgit v1.2.3-70-g09d2 From f55a6faa384304c89cfef162768e88374d3312cb Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 10 Jul 2012 18:43:19 -0400 Subject: hrtimer: Provide clock_was_set_delayed() clock_was_set() cannot be called from hard interrupt context because it calls on_each_cpu(). For fixing the widely reported leap seconds issue it is necessary to call it from hard interrupt context, i.e. the timer tick code, which does the timekeeping updates. Provide a new function which denotes it in the hrtimer cpu base structure of the cpu on which it is called and raise the hrtimer softirq. We then execute the clock_was_set() notificiation from softirq context in run_hrtimer_softirq(). The hrtimer softirq is rarely used, so polling the flag there is not a performance issue. [ tglx: Made it depend on CONFIG_HIGH_RES_TIMERS. We really should get rid of all this ifdeffery ASAP ] Signed-off-by: John Stultz Reported-by: Jan Engelhardt Reviewed-by: Ingo Molnar Acked-by: Peter Zijlstra Acked-by: Prarit Bhargava Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/1341960205-56738-2-git-send-email-johnstul@us.ibm.com Signed-off-by: Thomas Gleixner --- include/linux/hrtimer.h | 9 ++++++++- kernel/hrtimer.c | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index fd0dc30c9f1..c9ec9400ee5 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -165,6 +165,7 @@ enum hrtimer_base_type { * @lock: lock protecting the base and associated clock bases * and timers * @active_bases: Bitfield to mark bases with active timers + * @clock_was_set: Indicates that clock was set from irq context. * @expires_next: absolute time of the next event which was scheduled * via clock_set_next_event() * @hres_active: State of high resolution mode @@ -177,7 +178,8 @@ enum hrtimer_base_type { */ struct hrtimer_cpu_base { raw_spinlock_t lock; - unsigned long active_bases; + unsigned int active_bases; + unsigned int clock_was_set; #ifdef CONFIG_HIGH_RES_TIMERS ktime_t expires_next; int hres_active; @@ -286,6 +288,8 @@ extern void hrtimer_peek_ahead_timers(void); # define MONOTONIC_RES_NSEC HIGH_RES_NSEC # define KTIME_MONOTONIC_RES KTIME_HIGH_RES +extern void clock_was_set_delayed(void); + #else # define MONOTONIC_RES_NSEC LOW_RES_NSEC @@ -306,6 +310,9 @@ static inline int hrtimer_is_hres_active(struct hrtimer *timer) { return 0; } + +static inline void clock_was_set_delayed(void) { } + #endif extern void clock_was_set(void); diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index ae34bf51682..3c24fb2c25c 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -717,6 +717,19 @@ static int hrtimer_switch_to_hres(void) return 1; } +/* + * Called from timekeeping code to reprogramm the hrtimer interrupt + * device. If called from the timer interrupt context we defer it to + * softirq context. + */ +void clock_was_set_delayed(void) +{ + struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); + + cpu_base->clock_was_set = 1; + __raise_softirq_irqoff(HRTIMER_SOFTIRQ); +} + #else static inline int hrtimer_hres_active(void) { return 0; } @@ -1395,6 +1408,13 @@ void hrtimer_peek_ahead_timers(void) static void run_hrtimer_softirq(struct softirq_action *h) { + struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); + + if (cpu_base->clock_was_set) { + cpu_base->clock_was_set = 0; + clock_was_set(); + } + hrtimer_peek_ahead_timers(); } -- cgit v1.2.3-70-g09d2 From f6c06abfb3972ad4914cef57d8348fcb2932bc3b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 10 Jul 2012 18:43:24 -0400 Subject: timekeeping: Provide hrtimer update function To finally fix the infamous leap second issue and other race windows caused by functions which change the offsets between the various time bases (CLOCK_MONOTONIC, CLOCK_REALTIME and CLOCK_BOOTTIME) we need a function which atomically gets the current monotonic time and updates the offsets of CLOCK_REALTIME and CLOCK_BOOTTIME with minimalistic overhead. The previous patch which provides ktime_t offsets allows us to make this function almost as cheap as ktime_get() which is going to be replaced in hrtimer_interrupt(). Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Acked-by: Peter Zijlstra Acked-by: Prarit Bhargava Cc: stable@vger.kernel.org Signed-off-by: John Stultz Link: http://lkml.kernel.org/r/1341960205-56738-7-git-send-email-johnstul@us.ibm.com Signed-off-by: Thomas Gleixner --- include/linux/hrtimer.h | 1 + kernel/time/timekeeping.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index c9ec9400ee5..cc07d2777bb 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -327,6 +327,7 @@ extern ktime_t ktime_get(void); extern ktime_t ktime_get_real(void); extern ktime_t ktime_get_boottime(void); extern ktime_t ktime_get_monotonic_offset(void); +extern ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot); DECLARE_PER_CPU(struct tick_device, tick_cpu_device); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 1c038dac71a..269b1fe5f2a 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1271,6 +1271,40 @@ void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, } while (read_seqretry(&timekeeper.lock, seq)); } +#ifdef CONFIG_HIGH_RES_TIMERS +/** + * ktime_get_update_offsets - hrtimer helper + * @offs_real: pointer to storage for monotonic -> realtime offset + * @offs_boot: pointer to storage for monotonic -> boottime offset + * + * Returns current monotonic time and updates the offsets + * Called from hrtimer_interupt() or retrigger_next_event() + */ +ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot) +{ + ktime_t now; + unsigned int seq; + u64 secs, nsecs; + + do { + seq = read_seqbegin(&timekeeper.lock); + + secs = timekeeper.xtime.tv_sec; + nsecs = timekeeper.xtime.tv_nsec; + nsecs += timekeeping_get_ns(); + /* If arch requires, add in gettimeoffset() */ + nsecs += arch_gettimeoffset(); + + *offs_real = timekeeper.offs_real; + *offs_boot = timekeeper.offs_boot; + } while (read_seqretry(&timekeeper.lock, seq)); + + now = ktime_add_ns(ktime_set(secs, 0), nsecs); + now = ktime_sub(now, *offs_real); + return now; +} +#endif + /** * ktime_get_monotonic_offset() - get wall_to_monotonic in ktime_t format */ -- cgit v1.2.3-70-g09d2 From d8adde17e5f858427504725218c56aef90e90fc7 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 11 Jul 2012 14:01:52 -0700 Subject: memory hotplug: fix invalid memory access caused by stale kswapd pointer kswapd_stop() is called to destroy the kswapd work thread when all memory of a NUMA node has been offlined. But kswapd_stop() only terminates the work thread without resetting NODE_DATA(nid)->kswapd to NULL. The stale pointer will prevent kswapd_run() from creating a new work thread when adding memory to the memory-less NUMA node again. Eventually the stale pointer may cause invalid memory access. An example stack dump as below. It's reproduced with 2.6.32, but latest kernel has the same issue. BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] exit_creds+0x12/0x78 PGD 0 Oops: 0000 [#1] SMP last sysfs file: /sys/devices/system/memory/memory391/state CPU 11 Modules linked in: cpufreq_conservative cpufreq_userspace cpufreq_powersave acpi_cpufreq microcode fuse loop dm_mod tpm_tis rtc_cmos i2c_i801 rtc_core tpm serio_raw pcspkr sg tpm_bios igb i2c_core iTCO_wdt rtc_lib mptctl iTCO_vendor_support button dca bnx2 usbhid hid uhci_hcd ehci_hcd usbcore sd_mod crc_t10dif edd ext3 mbcache jbd fan ide_pci_generic ide_core ata_generic ata_piix libata thermal processor thermal_sys hwmon mptsas mptscsih mptbase scsi_transport_sas scsi_mod Pid: 7949, comm: sh Not tainted 2.6.32.12-qiuxishi-5-default #92 Tecal RH2285 RIP: 0010:exit_creds+0x12/0x78 RSP: 0018:ffff8806044f1d78 EFLAGS: 00010202 RAX: 0000000000000000 RBX: ffff880604f22140 RCX: 0000000000019502 RDX: 0000000000000000 RSI: 0000000000000202 RDI: 0000000000000000 RBP: ffff880604f22150 R08: 0000000000000000 R09: ffffffff81a4dc10 R10: 00000000000032a0 R11: ffff880006202500 R12: 0000000000000000 R13: 0000000000c40000 R14: 0000000000008000 R15: 0000000000000001 FS: 00007fbc03d066f0(0000) GS:ffff8800282e0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000000 CR3: 000000060f029000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process sh (pid: 7949, threadinfo ffff8806044f0000, task ffff880603d7c600) Stack: ffff880604f22140 ffffffff8103aac5 ffff880604f22140 ffffffff8104d21e ffff880006202500 0000000000008000 0000000000c38000 ffffffff810bd5b1 0000000000000000 ffff880603d7c600 00000000ffffdd29 0000000000000003 Call Trace: __put_task_struct+0x5d/0x97 kthread_stop+0x50/0x58 offline_pages+0x324/0x3da memory_block_change_state+0x179/0x1db store_mem_state+0x9e/0xbb sysfs_write_file+0xd0/0x107 vfs_write+0xad/0x169 sys_write+0x45/0x6e system_call_fastpath+0x16/0x1b Code: ff 4d 00 0f 94 c0 84 c0 74 08 48 89 ef e8 1f fd ff ff 5b 5d 31 c0 41 5c c3 53 48 8b 87 20 06 00 00 48 89 fb 48 8b bf 18 06 00 00 <8b> 00 48 c7 83 18 06 00 00 00 00 00 00 f0 ff 0f 0f 94 c0 84 c0 RIP exit_creds+0x12/0x78 RSP CR2: 0000000000000000 [akpm@linux-foundation.org: add pglist_data.kswapd locking comments] Signed-off-by: Xishi Qiu Signed-off-by: Jiang Liu Acked-by: KAMEZAWA Hiroyuki Acked-by: KOSAKI Motohiro Acked-by: Mel Gorman Acked-by: David Rientjes Reviewed-by: Minchan Kim Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 2 +- mm/vmscan.c | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 2427706f78b..68c569fcbb6 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -694,7 +694,7 @@ typedef struct pglist_data { range, including holes */ int node_id; wait_queue_head_t kswapd_wait; - struct task_struct *kswapd; + struct task_struct *kswapd; /* Protected by lock_memory_hotplug() */ int kswapd_max_order; enum zone_type classzone_idx; } pg_data_t; diff --git a/mm/vmscan.c b/mm/vmscan.c index eeb3bc9d1d3..661576324c7 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2955,14 +2955,17 @@ int kswapd_run(int nid) } /* - * Called by memory hotplug when all memory in a node is offlined. + * Called by memory hotplug when all memory in a node is offlined. Caller must + * hold lock_memory_hotplug(). */ void kswapd_stop(int nid) { struct task_struct *kswapd = NODE_DATA(nid)->kswapd; - if (kswapd) + if (kswapd) { kthread_stop(kswapd); + NODE_DATA(nid)->kswapd = NULL; + } } static int __init kswapd_init(void) -- cgit v1.2.3-70-g09d2 From 99ab7b19440a72ebdf225f99b20f8ef40decee86 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 11 Jul 2012 14:02:53 -0700 Subject: mm: sparse: fix usemap allocation above node descriptor section After commit f5bf18fa22f8 ("bootmem/sparsemem: remove limit constraint in alloc_bootmem_section"), usemap allocations may easily be placed outside the optimal section that holds the node descriptor, even if there is space available in that section. This results in unnecessary hotplug dependencies that need to have the node unplugged before the section holding the usemap. The reason is that the bootmem allocator doesn't guarantee a linear search starting from the passed allocation goal but may start out at a much higher address absent an upper limit. Fix this by trying the allocation with the limit at the section end, then retry without if that fails. This keeps the fix from f5bf18fa22f8 of not panicking if the allocation does not fit in the section, but still makes sure to try to stay within the section at first. Signed-off-by: Yinghai Lu Signed-off-by: Johannes Weiner Cc: [3.3.x, 3.4.x] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 5 +++++ mm/bootmem.c | 2 +- mm/nobootmem.c | 2 +- mm/sparse.c | 18 +++++++++++++----- 4 files changed, 20 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 324fe08ea3b..6d6795d46a7 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -91,6 +91,11 @@ extern void *__alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal); +void *___alloc_bootmem_node_nopanic(pg_data_t *pgdat, + unsigned long size, + unsigned long align, + unsigned long goal, + unsigned long limit); extern void *__alloc_bootmem_low(unsigned long size, unsigned long align, unsigned long goal); diff --git a/mm/bootmem.c b/mm/bootmem.c index ec4fcb7a56c..73096630cb3 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -698,7 +698,7 @@ void * __init __alloc_bootmem(unsigned long size, unsigned long align, return ___alloc_bootmem(size, align, goal, limit); } -static void * __init ___alloc_bootmem_node_nopanic(pg_data_t *pgdat, +void * __init ___alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal, unsigned long limit) { diff --git a/mm/nobootmem.c b/mm/nobootmem.c index d23415c001b..0900b3910cd 100644 --- a/mm/nobootmem.c +++ b/mm/nobootmem.c @@ -274,7 +274,7 @@ void * __init __alloc_bootmem(unsigned long size, unsigned long align, return ___alloc_bootmem(size, align, goal, limit); } -static void * __init ___alloc_bootmem_node_nopanic(pg_data_t *pgdat, +void * __init ___alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal, diff --git a/mm/sparse.c b/mm/sparse.c index e861397016a..c7bb952400c 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -275,8 +275,9 @@ static unsigned long * __init sparse_early_usemaps_alloc_pgdat_section(struct pglist_data *pgdat, unsigned long size) { - pg_data_t *host_pgdat; - unsigned long goal; + unsigned long goal, limit; + unsigned long *p; + int nid; /* * A page may contain usemaps for other sections preventing the * page being freed and making a section unremovable while @@ -288,9 +289,16 @@ sparse_early_usemaps_alloc_pgdat_section(struct pglist_data *pgdat, * this problem. */ goal = __pa(pgdat) & (PAGE_SECTION_MASK << PAGE_SHIFT); - host_pgdat = NODE_DATA(early_pfn_to_nid(goal >> PAGE_SHIFT)); - return __alloc_bootmem_node_nopanic(host_pgdat, size, - SMP_CACHE_BYTES, goal); + limit = goal + (1UL << PA_SECTION_SHIFT); + nid = early_pfn_to_nid(goal >> PAGE_SHIFT); +again: + p = ___alloc_bootmem_node_nopanic(NODE_DATA(nid), size, + SMP_CACHE_BYTES, goal, limit); + if (!p && limit) { + limit = 0; + goto again; + } + return p; } static void __init check_usemap_section_nr(int nid, unsigned long *usemap) -- cgit v1.2.3-70-g09d2 From 29f6738609e40227dabcc63bfb3b84b3726a75bd Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 11 Jul 2012 14:02:56 -0700 Subject: memblock: free allocated memblock_reserved_regions later memblock_free_reserved_regions() calls memblock_free(), but memblock_free() would double reserved.regions too, so we could free the old range for reserved.regions. Also tj said there is another bug which could be related to this. | I don't think we're saving any noticeable | amount by doing this "free - give it to page allocator - reserve | again" dancing. We should just allocate regions aligned to page | boundaries and free them later when memblock is no longer in use. in that case, when DEBUG_PAGEALLOC, will get panic: memblock_free: [0x0000102febc080-0x0000102febf080] memblock_free_reserved_regions+0x37/0x39 BUG: unable to handle kernel paging request at ffff88102febd948 IP: [] __next_free_mem_range+0x9b/0x155 PGD 4826063 PUD cf67a067 PMD cf7fa067 PTE 800000102febd160 Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC CPU 0 Pid: 0, comm: swapper Not tainted 3.5.0-rc2-next-20120614-sasha #447 RIP: 0010:[] [] __next_free_mem_range+0x9b/0x155 See the discussion at https://lkml.org/lkml/2012/6/13/469 So try to allocate with PAGE_SIZE alignment and free it later. Reported-by: Sasha Levin Acked-by: Tejun Heo Cc: Benjamin Herrenschmidt Signed-off-by: Yinghai Lu Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memblock.h | 4 +--- mm/memblock.c | 51 ++++++++++++++++++++++-------------------------- mm/nobootmem.c | 38 ++++++++++++++++++++++-------------- 3 files changed, 47 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/linux/memblock.h b/include/linux/memblock.h index a6bb1023514..19dc455b4f3 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -50,9 +50,7 @@ phys_addr_t memblock_find_in_range_node(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align, int nid); phys_addr_t memblock_find_in_range(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align); -int memblock_free_reserved_regions(void); -int memblock_reserve_reserved_regions(void); - +phys_addr_t get_allocated_memblock_reserved_regions_info(phys_addr_t *addr); void memblock_allow_resize(void); int memblock_add_node(phys_addr_t base, phys_addr_t size, int nid); int memblock_add(phys_addr_t base, phys_addr_t size); diff --git a/mm/memblock.c b/mm/memblock.c index d4382095f8b..5cc6731b00c 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -143,30 +143,6 @@ phys_addr_t __init_memblock memblock_find_in_range(phys_addr_t start, MAX_NUMNODES); } -/* - * Free memblock.reserved.regions - */ -int __init_memblock memblock_free_reserved_regions(void) -{ - if (memblock.reserved.regions == memblock_reserved_init_regions) - return 0; - - return memblock_free(__pa(memblock.reserved.regions), - sizeof(struct memblock_region) * memblock.reserved.max); -} - -/* - * Reserve memblock.reserved.regions - */ -int __init_memblock memblock_reserve_reserved_regions(void) -{ - if (memblock.reserved.regions == memblock_reserved_init_regions) - return 0; - - return memblock_reserve(__pa(memblock.reserved.regions), - sizeof(struct memblock_region) * memblock.reserved.max); -} - static void __init_memblock memblock_remove_region(struct memblock_type *type, unsigned long r) { type->total_size -= type->regions[r].size; @@ -184,6 +160,18 @@ static void __init_memblock memblock_remove_region(struct memblock_type *type, u } } +phys_addr_t __init_memblock get_allocated_memblock_reserved_regions_info( + phys_addr_t *addr) +{ + if (memblock.reserved.regions == memblock_reserved_init_regions) + return 0; + + *addr = __pa(memblock.reserved.regions); + + return PAGE_ALIGN(sizeof(struct memblock_region) * + memblock.reserved.max); +} + /** * memblock_double_array - double the size of the memblock regions array * @type: memblock type of the regions array being doubled @@ -204,6 +192,7 @@ static int __init_memblock memblock_double_array(struct memblock_type *type, phys_addr_t new_area_size) { struct memblock_region *new_array, *old_array; + phys_addr_t old_alloc_size, new_alloc_size; phys_addr_t old_size, new_size, addr; int use_slab = slab_is_available(); int *in_slab; @@ -217,6 +206,12 @@ static int __init_memblock memblock_double_array(struct memblock_type *type, /* Calculate new doubled size */ old_size = type->max * sizeof(struct memblock_region); new_size = old_size << 1; + /* + * We need to allocated new one align to PAGE_SIZE, + * so we can free them completely later. + */ + old_alloc_size = PAGE_ALIGN(old_size); + new_alloc_size = PAGE_ALIGN(new_size); /* Retrieve the slab flag */ if (type == &memblock.memory) @@ -245,11 +240,11 @@ static int __init_memblock memblock_double_array(struct memblock_type *type, addr = memblock_find_in_range(new_area_start + new_area_size, memblock.current_limit, - new_size, sizeof(phys_addr_t)); + new_alloc_size, PAGE_SIZE); if (!addr && new_area_size) addr = memblock_find_in_range(0, min(new_area_start, memblock.current_limit), - new_size, sizeof(phys_addr_t)); + new_alloc_size, PAGE_SIZE); new_array = addr ? __va(addr) : 0; } @@ -279,13 +274,13 @@ static int __init_memblock memblock_double_array(struct memblock_type *type, kfree(old_array); else if (old_array != memblock_memory_init_regions && old_array != memblock_reserved_init_regions) - memblock_free(__pa(old_array), old_size); + memblock_free(__pa(old_array), old_alloc_size); /* Reserve the new array if that comes from the memblock. * Otherwise, we needn't do it */ if (!use_slab) - BUG_ON(memblock_reserve(addr, new_size)); + BUG_ON(memblock_reserve(addr, new_alloc_size)); /* Update slab flag */ *in_slab = use_slab; diff --git a/mm/nobootmem.c b/mm/nobootmem.c index 0900b3910cd..405573010f9 100644 --- a/mm/nobootmem.c +++ b/mm/nobootmem.c @@ -105,27 +105,35 @@ static void __init __free_pages_memory(unsigned long start, unsigned long end) __free_pages_bootmem(pfn_to_page(i), 0); } +static unsigned long __init __free_memory_core(phys_addr_t start, + phys_addr_t end) +{ + unsigned long start_pfn = PFN_UP(start); + unsigned long end_pfn = min_t(unsigned long, + PFN_DOWN(end), max_low_pfn); + + if (start_pfn > end_pfn) + return 0; + + __free_pages_memory(start_pfn, end_pfn); + + return end_pfn - start_pfn; +} + unsigned long __init free_low_memory_core_early(int nodeid) { unsigned long count = 0; - phys_addr_t start, end; + phys_addr_t start, end, size; u64 i; - /* free reserved array temporarily so that it's treated as free area */ - memblock_free_reserved_regions(); - - for_each_free_mem_range(i, MAX_NUMNODES, &start, &end, NULL) { - unsigned long start_pfn = PFN_UP(start); - unsigned long end_pfn = min_t(unsigned long, - PFN_DOWN(end), max_low_pfn); - if (start_pfn < end_pfn) { - __free_pages_memory(start_pfn, end_pfn); - count += end_pfn - start_pfn; - } - } + for_each_free_mem_range(i, MAX_NUMNODES, &start, &end, NULL) + count += __free_memory_core(start, end); + + /* free range that is used for reserved array if we allocate it */ + size = get_allocated_memblock_reserved_regions_info(&start); + if (size) + count += __free_memory_core(start, start + size); - /* put region array back? */ - memblock_reserve_reserved_regions(); return count; } -- cgit v1.2.3-70-g09d2 From faa95fde43f758d0a9e0e3d8751dac79aae1f08d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 11 Jul 2012 19:44:13 +0800 Subject: regulator: tps65910: Remvoe tps65910_reg_[read|modify_bits|read_locked|write_locked] functions The tps65910 mfd driver has been converted to regmap APIs. This patch adds tps65910_reg_update_bits() in include/linux/mfd/tps65910.h. Thus we can use tps65910_reg_read/tps65910_reg_write/tps65910_reg_update_bits directly and remove tps65910_reg_[read|modify_bits|read_locked|write_locked] functions. With this change, we can also remove the mutex in struct tps65910_reg. Signed-off-by: Axel Lin Tested-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65910-regulator.c | 184 +++++++++++++-------------------- include/linux/mfd/tps65910.h | 6 ++ 2 files changed, 79 insertions(+), 111 deletions(-) (limited to 'include') diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 64c93e0005b..d2ba066c095 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -296,7 +296,6 @@ struct tps65910_reg { struct tps65910 *mfd; struct regulator_dev **rdev; struct tps_info **info; - struct mutex mutex; int num_regulators; int mode; int (*get_ctrl_reg)(int); @@ -304,71 +303,6 @@ struct tps65910_reg { unsigned int board_ext_control[TPS65910_NUM_REGS]; }; -static inline int tps65910_read(struct tps65910_reg *pmic, u8 reg) -{ - unsigned int val; - int err; - - err = tps65910_reg_read(pmic->mfd, reg, &val); - if (err) - return err; - - return val; -} - -static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg, - u8 set_mask, u8 clear_mask) -{ - int err, data; - - mutex_lock(&pmic->mutex); - - data = tps65910_read(pmic, reg); - if (data < 0) { - dev_err(pmic->mfd->dev, "Read from reg 0x%x failed\n", reg); - err = data; - goto out; - } - - data &= ~clear_mask; - data |= set_mask; - err = tps65910_reg_write(pmic->mfd, reg, data); - if (err) - dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg); - -out: - mutex_unlock(&pmic->mutex); - return err; -} - -static int tps65910_reg_read_locked(struct tps65910_reg *pmic, u8 reg) -{ - int data; - - mutex_lock(&pmic->mutex); - - data = tps65910_read(pmic, reg); - if (data < 0) - dev_err(pmic->mfd->dev, "Read from reg 0x%x failed\n", reg); - - mutex_unlock(&pmic->mutex); - return data; -} - -static int tps65910_reg_write_locked(struct tps65910_reg *pmic, u8 reg, u8 val) -{ - int err; - - mutex_lock(&pmic->mutex); - - err = tps65910_reg_write(pmic->mfd, reg, val); - if (err < 0) - dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg); - - mutex_unlock(&pmic->mutex); - return err; -} - static int tps65910_get_ctrl_register(int id) { switch (id) { @@ -449,8 +383,9 @@ static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) switch (mode) { case REGULATOR_MODE_NORMAL: - return tps65910_modify_bits(pmic, reg, LDO_ST_ON_BIT, - LDO_ST_MODE_BIT); + return tps65910_reg_update_bits(pmic->mfd, reg, + LDO_ST_MODE_BIT | LDO_ST_ON_BIT, + LDO_ST_ON_BIT); case REGULATOR_MODE_IDLE: value = LDO_ST_ON_BIT | LDO_ST_MODE_BIT; return tps65910_reg_set_bits(mfd, reg, value); @@ -464,15 +399,15 @@ static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) static unsigned int tps65910_get_mode(struct regulator_dev *dev) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); - int reg, value, id = rdev_get_id(dev); + int ret, reg, value, id = rdev_get_id(dev); reg = pmic->get_ctrl_reg(id); if (reg < 0) return reg; - value = tps65910_reg_read_locked(pmic, reg); - if (value < 0) - return value; + ret = tps65910_reg_read(pmic->mfd, reg, &value); + if (ret < 0) + return ret; if (!(value & LDO_ST_ON_BIT)) return REGULATOR_MODE_STANDBY; @@ -485,33 +420,51 @@ static unsigned int tps65910_get_mode(struct regulator_dev *dev) static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); - int id = rdev_get_id(dev); + int ret, id = rdev_get_id(dev); int opvsel = 0, srvsel = 0, vselmax = 0, mult = 0, sr = 0; switch (id) { case TPS65910_REG_VDD1: - opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_OP); - mult = tps65910_reg_read_locked(pmic, TPS65910_VDD1); + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD1_OP, &opvsel); + if (ret < 0) + return ret; + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD1, &mult); + if (ret < 0) + return ret; mult = (mult & VDD1_VGAIN_SEL_MASK) >> VDD1_VGAIN_SEL_SHIFT; - srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_SR); + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD1_SR, &srvsel); + if (ret < 0) + return ret; sr = opvsel & VDD1_OP_CMD_MASK; opvsel &= VDD1_OP_SEL_MASK; srvsel &= VDD1_SR_SEL_MASK; vselmax = 75; break; case TPS65910_REG_VDD2: - opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_OP); - mult = tps65910_reg_read_locked(pmic, TPS65910_VDD2); + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD2_OP, &opvsel); + if (ret < 0) + return ret; + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD2, &mult); + if (ret < 0) + return ret; mult = (mult & VDD2_VGAIN_SEL_MASK) >> VDD2_VGAIN_SEL_SHIFT; - srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_SR); + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD2_SR, &srvsel); + if (ret < 0) + return ret; sr = opvsel & VDD2_OP_CMD_MASK; opvsel &= VDD2_OP_SEL_MASK; srvsel &= VDD2_SR_SEL_MASK; vselmax = 75; break; case TPS65911_REG_VDDCTRL: - opvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_OP); - srvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_SR); + ret = tps65910_reg_read(pmic->mfd, TPS65911_VDDCTRL_OP, + &opvsel); + if (ret < 0) + return ret; + ret = tps65910_reg_read(pmic->mfd, TPS65911_VDDCTRL_SR, + &srvsel); + if (ret < 0) + return ret; sr = opvsel & VDDCTRL_OP_CMD_MASK; opvsel &= VDDCTRL_OP_SEL_MASK; srvsel &= VDDCTRL_SR_SEL_MASK; @@ -545,15 +498,15 @@ static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) static int tps65910_get_voltage_sel(struct regulator_dev *dev) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); - int reg, value, id = rdev_get_id(dev); + int ret, reg, value, id = rdev_get_id(dev); reg = pmic->get_ctrl_reg(id); if (reg < 0) return reg; - value = tps65910_reg_read_locked(pmic, reg); - if (value < 0) - return value; + ret = tps65910_reg_read(pmic->mfd, reg, &value); + if (ret < 0) + return ret; switch (id) { case TPS65910_REG_VIO: @@ -583,12 +536,14 @@ static int tps65910_get_voltage_vdd3(struct regulator_dev *dev) static int tps65911_get_voltage_sel(struct regulator_dev *dev) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); - int id = rdev_get_id(dev); - u8 value, reg; + int ret, id = rdev_get_id(dev); + unsigned int value, reg; reg = pmic->get_ctrl_reg(id); - value = tps65910_reg_read_locked(pmic, reg); + ret = tps65910_reg_read(pmic->mfd, reg, &value); + if (ret < 0) + return ret; switch (id) { case TPS65911_REG_LDO1: @@ -630,10 +585,10 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, dcdc_mult--; vsel = (selector % VDD1_2_NUM_VOLT_FINE) + 3; - tps65910_modify_bits(pmic, TPS65910_VDD1, - (dcdc_mult << VDD1_VGAIN_SEL_SHIFT), - VDD1_VGAIN_SEL_MASK); - tps65910_reg_write_locked(pmic, TPS65910_VDD1_OP, vsel); + tps65910_reg_update_bits(pmic->mfd, TPS65910_VDD1, + VDD1_VGAIN_SEL_MASK, + dcdc_mult << VDD1_VGAIN_SEL_SHIFT); + tps65910_reg_write(pmic->mfd, TPS65910_VDD1_OP, vsel); break; case TPS65910_REG_VDD2: dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1; @@ -641,14 +596,14 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, dcdc_mult--; vsel = (selector % VDD1_2_NUM_VOLT_FINE) + 3; - tps65910_modify_bits(pmic, TPS65910_VDD2, - (dcdc_mult << VDD2_VGAIN_SEL_SHIFT), - VDD1_VGAIN_SEL_MASK); - tps65910_reg_write_locked(pmic, TPS65910_VDD2_OP, vsel); + tps65910_reg_update_bits(pmic->mfd, TPS65910_VDD2, + VDD1_VGAIN_SEL_MASK, + dcdc_mult << VDD2_VGAIN_SEL_SHIFT); + tps65910_reg_write(pmic->mfd, TPS65910_VDD2_OP, vsel); break; case TPS65911_REG_VDDCTRL: vsel = selector + 3; - tps65910_reg_write_locked(pmic, TPS65911_VDDCTRL_OP, vsel); + tps65910_reg_write(pmic->mfd, TPS65911_VDDCTRL_OP, vsel); } return 0; @@ -674,8 +629,8 @@ static int tps65910_set_voltage_sel(struct regulator_dev *dev, case TPS65910_REG_VAUX2: case TPS65910_REG_VAUX33: case TPS65910_REG_VMMC: - return tps65910_modify_bits(pmic, reg, - (selector << LDO_SEL_SHIFT), LDO_SEL_MASK); + return tps65910_reg_update_bits(pmic->mfd, reg, LDO_SEL_MASK, + selector << LDO_SEL_SHIFT); } return -EINVAL; @@ -695,18 +650,18 @@ static int tps65911_set_voltage_sel(struct regulator_dev *dev, case TPS65911_REG_LDO1: case TPS65911_REG_LDO2: case TPS65911_REG_LDO4: - return tps65910_modify_bits(pmic, reg, - (selector << LDO_SEL_SHIFT), LDO1_SEL_MASK); + return tps65910_reg_update_bits(pmic->mfd, reg, LDO1_SEL_MASK, + selector << LDO_SEL_SHIFT); case TPS65911_REG_LDO3: case TPS65911_REG_LDO5: case TPS65911_REG_LDO6: case TPS65911_REG_LDO7: case TPS65911_REG_LDO8: - return tps65910_modify_bits(pmic, reg, - (selector << LDO_SEL_SHIFT), LDO3_SEL_MASK); + return tps65910_reg_update_bits(pmic->mfd, reg, LDO3_SEL_MASK, + selector << LDO_SEL_SHIFT); case TPS65910_REG_VIO: - return tps65910_modify_bits(pmic, reg, - (selector << LDO_SEL_SHIFT), LDO_SEL_MASK); + return tps65910_reg_update_bits(pmic->mfd, reg, LDO_SEL_MASK, + selector << LDO_SEL_SHIFT); } return -EINVAL; @@ -918,19 +873,27 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, (tps65910_chip_id(mfd) == TPS65911))) { int op_reg_add = pmic->get_ctrl_reg(id) + 1; int sr_reg_add = pmic->get_ctrl_reg(id) + 2; - int opvsel = tps65910_reg_read_locked(pmic, op_reg_add); - int srvsel = tps65910_reg_read_locked(pmic, sr_reg_add); + int opvsel, srvsel; + + ret = tps65910_reg_read(pmic->mfd, op_reg_add, &opvsel); + if (ret < 0) + return ret; + ret = tps65910_reg_read(pmic->mfd, sr_reg_add, &srvsel); + if (ret < 0) + return ret; + if (opvsel & VDD1_OP_CMD_MASK) { u8 reg_val = srvsel & VDD1_OP_SEL_MASK; - ret = tps65910_reg_write_locked(pmic, op_reg_add, - reg_val); + + ret = tps65910_reg_write(pmic->mfd, op_reg_add, + reg_val); if (ret < 0) { dev_err(mfd->dev, "Error in configuring op register\n"); return ret; } } - ret = tps65910_reg_write_locked(pmic, sr_reg_add, 0); + ret = tps65910_reg_write(pmic->mfd, sr_reg_add, 0); if (ret < 0) { dev_err(mfd->dev, "Error in settting sr register\n"); return ret; @@ -1100,7 +1063,6 @@ static __devinit int tps65910_probe(struct platform_device *pdev) return -ENOMEM; } - mutex_init(&pmic->mutex); pmic->mfd = tps65910; platform_set_drvdata(pdev, pmic); diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index c5f806011b3..1aca1fbbc13 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -882,4 +882,10 @@ static inline int tps65910_reg_clear_bits(struct tps65910 *tps65910, u8 reg, return regmap_update_bits(tps65910->regmap, reg, mask, 0); } +static inline int tps65910_reg_update_bits(struct tps65910 *tps65910, u8 reg, + u8 mask, u8 val) +{ + return regmap_update_bits(tps65910->regmap, reg, mask, val); +} + #endif /* __LINUX_MFD_TPS65910_H */ -- cgit v1.2.3-70-g09d2 From a7f1b63eb85606ad77a559b95c703b99e1205aa4 Mon Sep 17 00:00:00 2001 From: AnilKumar Ch Date: Tue, 10 Jul 2012 16:39:42 +0530 Subject: regulator: tps65217: Add device tree support This commit adds device tree support for tps65217 pmic. And usage details are added to device tree documentation. Driver is tested by using kernel module with regulator set and get APIs. Signed-off-by: AnilKumar Ch Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/tps65217.txt | 91 ++++++++++++++++++++++ drivers/mfd/tps65217.c | 67 +++++++++++++++- drivers/regulator/tps65217-regulator.c | 1 + include/linux/mfd/tps65217.h | 3 +- 4 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/tps65217.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/regulator/tps65217.txt b/Documentation/devicetree/bindings/regulator/tps65217.txt new file mode 100644 index 00000000000..0487e9675ba --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/tps65217.txt @@ -0,0 +1,91 @@ +TPS65217 family of regulators + +Required properties: +- compatible: "ti,tps65217" +- reg: I2C slave address +- regulators: list of regulators provided by this controller, must be named + after their hardware counterparts: dcdc[1-3] and ldo[1-4] +- regulators: This is the list of child nodes that specify the regulator + initialization data for defined regulators. Not all regulators for the given + device need to be present. The definition for each of these nodes is defined + using the standard binding for regulators found at + Documentation/devicetree/bindings/regulator/regulator.txt. + + The valid names for regulators are: + tps65217: dcdc1, dcdc2, dcdc3, ldo1, ldo2, ldo3 and ldo4 + +Each regulator is defined using the standard binding for regulators. + +Example: + + tps: tps@24 { + compatible = "ti,tps65217"; + + regulators { + #address-cells = <1>; + #size-cells = <0>; + + dcdc1_reg: regulator@0 { + reg = <0>; + regulator-compatible = "dcdc1"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + dcdc2_reg: regulator@1 { + reg = <1>; + regulator-compatible = "dcdc2"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + dcdc3_reg: regulator@2 { + reg = <2>; + regulator-compatible = "dcdc3"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1500000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo1_reg: regulator@3 { + reg = <3>; + regulator-compatible = "ldo1"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo2_reg: regulator@4 { + reg = <4>; + regulator-compatible = "ldo2"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo3_reg: regulator@5 { + reg = <5>; + regulator-compatible = "ldo3"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo4_reg: regulator@6 { + reg = <6>; + regulator-compatible = "ldo4"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + }; + }; diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index db194e433c0..61c097a98f5 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -132,6 +133,61 @@ int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg, } EXPORT_SYMBOL_GPL(tps65217_clear_bits); +#ifdef CONFIG_OF +static struct of_regulator_match reg_matches[] = { + { .name = "dcdc1", .driver_data = (void *)TPS65217_DCDC_1 }, + { .name = "dcdc2", .driver_data = (void *)TPS65217_DCDC_2 }, + { .name = "dcdc3", .driver_data = (void *)TPS65217_DCDC_3 }, + { .name = "ldo1", .driver_data = (void *)TPS65217_LDO_1 }, + { .name = "ldo2", .driver_data = (void *)TPS65217_LDO_2 }, + { .name = "ldo3", .driver_data = (void *)TPS65217_LDO_3 }, + { .name = "ldo4", .driver_data = (void *)TPS65217_LDO_4 }, +}; + +static struct tps65217_board *tps65217_parse_dt(struct i2c_client *client) +{ + struct device_node *node = client->dev.of_node; + struct tps65217_board *pdata; + struct device_node *regs; + int count = ARRAY_SIZE(reg_matches); + int ret, i; + + regs = of_find_node_by_name(node, "regulators"); + if (!regs) + return NULL; + + ret = of_regulator_match(&client->dev, regs, reg_matches, count); + of_node_put(regs); + if ((ret < 0) || (ret > count)) + return NULL; + + count = ret; + pdata = devm_kzalloc(&client->dev, count * sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + for (i = 0; i < count; i++) { + if (!reg_matches[i].init_data || !reg_matches[i].of_node) + continue; + + pdata->tps65217_init_data[i] = reg_matches[i].init_data; + pdata->of_node[i] = reg_matches[i].of_node; + } + + return pdata; +} + +static struct of_device_id tps65217_of_match[] = { + { .compatible = "ti,tps65217", }, + { }, +}; +#else +static struct tps65217_board *tps65217_parse_dt(struct i2c_client *client) +{ + return NULL; +} +#endif + static struct regmap_config tps65217_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -141,10 +197,14 @@ static int __devinit tps65217_probe(struct i2c_client *client, const struct i2c_device_id *ids) { struct tps65217 *tps; + struct regulator_init_data *reg_data; struct tps65217_board *pdata = client->dev.platform_data; int i, ret; unsigned int version; + if (!pdata && client->dev.of_node) + pdata = tps65217_parse_dt(client); + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); if (!tps) return -ENOMEM; @@ -182,8 +242,9 @@ static int __devinit tps65217_probe(struct i2c_client *client, } pdev->dev.parent = tps->dev; - platform_device_add_data(pdev, &pdata->tps65217_init_data[i], - sizeof(pdata->tps65217_init_data[i])); + pdev->dev.of_node = pdata->of_node[i]; + reg_data = pdata->tps65217_init_data[i]; + platform_device_add_data(pdev, reg_data, sizeof(*reg_data)); tps->regulator_pdev[i] = pdev; platform_device_add(pdev); @@ -212,6 +273,8 @@ MODULE_DEVICE_TABLE(i2c, tps65217_id_table); static struct i2c_driver tps65217_driver = { .driver = { .name = "tps65217", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps65217_of_match), }, .id_table = tps65217_id_table, .probe = tps65217_probe, diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index f7d0495b003..6caa222af77 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -293,6 +293,7 @@ static int __devinit tps65217_regulator_probe(struct platform_device *pdev) tps->info[pdev->id] = info; config.dev = &pdev->dev; + config.of_node = pdev->dev.of_node; config.init_data = pdev->dev.platform_data; config.driver_data = tps; diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index 3a80da103f3..12c06870829 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -217,7 +217,8 @@ enum tps65217_regulator_id { * Board data may be used to initialize regulator. */ struct tps65217_board { - struct regulator_init_data *tps65217_init_data; + struct regulator_init_data *tps65217_init_data[TPS65217_NUM_REGULATOR]; + struct device_node *of_node[TPS65217_NUM_REGULATOR]; }; /** -- cgit v1.2.3-70-g09d2 From d2cfdb055d77f8d0864d75adfc1a3f0e0ec40e69 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 17 Jul 2012 11:34:06 +0530 Subject: regulator: tps65910: set input_supply on desc unconditionally Set the supply_name in the regulator descriptor unconditionally and make this parameter as required parameter in the device node for successfully registration of the regulator. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/tps65910.txt | 33 ++++++++++++++-------- drivers/regulator/tps65910-regulator.c | 11 +------- include/linux/mfd/tps65910.h | 2 -- 3 files changed, 23 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/mfd/tps65910.txt b/Documentation/devicetree/bindings/mfd/tps65910.txt index 31be5a3d9f7..d2802d4717b 100644 --- a/Documentation/devicetree/bindings/mfd/tps65910.txt +++ b/Documentation/devicetree/bindings/mfd/tps65910.txt @@ -25,17 +25,12 @@ Required properties: tps65911: vrtc, vio, vdd1, vdd3, vddctrl, ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8 -Optional properties: -- ti,vmbch-threshold: (tps65911) main battery charged threshold - comparator. (see VMBCH_VSEL in TPS65910 datasheet) -- ti,vmbch2-threshold: (tps65911) main battery discharged threshold - comparator. (see VMBCH_VSEL in TPS65910 datasheet) -- ti,en-gpio-sleep: enable sleep control for gpios - There should be 9 entries here, one for each gpio. - xxx-supply: Input voltage supply regulator. - Missing of these properties will be assume as there is no supply regulator - for that input pins and always powered on. - The valid input supply properties are: + These entries are require if regulators are enabled for a device. Missing of these + properties can cause the regulator registration fails. + If some of input supply is powered through battery or always-on supply then + also it is require to have these parameters with proper node handle of always + on power supply. tps65910: vcc1-supply: VDD1 input. vcc2-supply: VDD2 input. @@ -55,6 +50,16 @@ Optional properties: vcc7-supply: VRTC input. vccio-supply: VIO input. +Optional properties: +- ti,vmbch-threshold: (tps65911) main battery charged threshold + comparator. (see VMBCH_VSEL in TPS65910 datasheet) +- ti,vmbch2-threshold: (tps65911) main battery discharged threshold + comparator. (see VMBCH_VSEL in TPS65910 datasheet) +- ti,en-ck32k-xtal: enable external 32-kHz crystal oscillator (see CK32K_CTRL + in TPS6591X datasheet) +- ti,en-gpio-sleep: enable sleep control for gpios + There should be 9 entries here, one for each gpio. + Regulator Optional properties: - ti,regulator-ext-sleep-control: enable external sleep control through external inputs [0 (not enabled), 1 (EN1), 2 (EN2) or 4(EN3)] @@ -79,8 +84,14 @@ Example: ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>; - vcc7-supply = <®_parent>; vcc1-supply = <®_parent>; + vcc2-supply = <&some_reg>; + vcc3-supply = <...>; + vcc4-supply = <...>; + vcc5-supply = <...>; + vcc6-supply = <...>; + vcc7-supply = <...>; + vccio-supply = <...>; regulators { #address-cells = <1>; diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index d2ba066c095..793adda560c 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -1001,9 +1001,6 @@ static struct tps65910_board *tps65910_parse_dt_reg_data( *tps65910_reg_matches = matches; for (idx = 0; idx < count; idx++) { - struct tps_info *info = matches[idx].driver_data; - char in_supply[32]; /* 32 is max size of property name */ - if (!matches[idx].init_data || !matches[idx].of_node) continue; @@ -1015,12 +1012,6 @@ static struct tps65910_board *tps65910_parse_dt_reg_data( if (!ret) pmic_plat_data->regulator_ext_sleep_control[idx] = prop; - if (info->vin_name) { - snprintf(in_supply, 32, "%s-supply", info->vin_name); - if (of_find_property(np, in_supply, 0)) - pmic_plat_data->input_supply[idx] = - info->vin_name; - } } return pmic_plat_data; @@ -1123,7 +1114,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev) pmic->info[i] = info; pmic->desc[i].name = info->name; - pmic->desc[i].supply_name = pmic_plat_data->input_supply[i]; + pmic->desc[i].supply_name = info->vin_name; pmic->desc[i].id = i; pmic->desc[i].n_voltages = info->n_voltages; pmic->desc[i].enable_time = info->enable_time_us; diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index 1aca1fbbc13..6c4c478e21a 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -799,7 +799,6 @@ struct tps65910_sleep_keepon_data { /** * struct tps65910_board * Board platform data may be used to initialize regulators. - * @input_supply: Name of input supply regulator. */ struct tps65910_board { @@ -812,7 +811,6 @@ struct tps65910_board { struct tps65910_sleep_keepon_data *slp_keepon; bool en_gpio_sleep[TPS6591X_MAX_NUM_GPIO]; unsigned long regulator_ext_sleep_control[TPS65910_NUM_REGS]; - const char *input_supply[TPS65910_NUM_REGS]; struct regulator_init_data *tps65910_pmic_init_data[TPS65910_NUM_REGS]; }; -- cgit v1.2.3-70-g09d2