From 7ff0589c7bff4ca31b255ac2028f633f14047762 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 16 Jun 2015 08:52:22 +0000 Subject: regmap: add force_write option on _regmap_update_bits() Sometimes we want to write data even though it doesn't change value. Then, force_write option on _regmap_update_bits() helps this purpose. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7111d04f2621..69ec411ce722 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -34,7 +34,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, - bool *change); + bool *change, bool force_write); static int _regmap_bus_reg_read(void *context, unsigned int reg, unsigned int *val); @@ -1178,7 +1178,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, ret = _regmap_update_bits(map, range->selector_reg, range->selector_mask, win_page << range->selector_shift, - &page_chg); + &page_chg, false); map->work_buf = orig_work_buf; @@ -2327,7 +2327,7 @@ EXPORT_SYMBOL_GPL(regmap_bulk_read); static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, - bool *change) + bool *change, bool force_write) { int ret; unsigned int tmp, orig; @@ -2339,7 +2339,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, tmp = orig & ~mask; tmp |= val & mask; - if (tmp != orig) { + if (force_write || (tmp != orig)) { ret = _regmap_write(map, reg, tmp); if (change) *change = true; @@ -2367,7 +2367,7 @@ int regmap_update_bits(struct regmap *map, unsigned int reg, int ret; map->lock(map->lock_arg); - ret = _regmap_update_bits(map, reg, mask, val, NULL); + ret = _regmap_update_bits(map, reg, mask, val, NULL, false); map->unlock(map->lock_arg); return ret; @@ -2398,7 +2398,7 @@ int regmap_update_bits_async(struct regmap *map, unsigned int reg, map->async = true; - ret = _regmap_update_bits(map, reg, mask, val, NULL); + ret = _regmap_update_bits(map, reg, mask, val, NULL, false); map->async = false; @@ -2427,7 +2427,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, int ret; map->lock(map->lock_arg); - ret = _regmap_update_bits(map, reg, mask, val, change); + ret = _regmap_update_bits(map, reg, mask, val, change, false); map->unlock(map->lock_arg); return ret; } @@ -2460,7 +2460,7 @@ int regmap_update_bits_check_async(struct regmap *map, unsigned int reg, map->async = true; - ret = _regmap_update_bits(map, reg, mask, val, change); + ret = _regmap_update_bits(map, reg, mask, val, change, false); map->async = false; -- cgit v1.3-6-gb490 From fd4b7286ccc469bf5dde22db6b8fcc455c3c4a66 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 16 Jun 2015 08:52:39 +0000 Subject: regmap: add regmap_write_bits() regmap_write_bits() is similar to regmap_update_bits(), but regmap_write_bits() write data to register even though it is same value. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 23 +++++++++++++++++++++++ include/linux/regmap.h | 9 +++++++++ 2 files changed, 32 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 69ec411ce722..d93bb9a8ab98 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2374,6 +2374,29 @@ int regmap_update_bits(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_update_bits); +/** + * regmap_write_bits: Perform a read/modify/write cycle on the register map + * + * @map: Register map to update + * @reg: Register to update + * @mask: Bitmask to change + * @val: New value for bitmask + * + * Returns zero for success, a negative number on error. + */ +int regmap_write_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val) +{ + int ret; + + map->lock(map->lock_arg); + ret = _regmap_update_bits(map, reg, mask, val, NULL, true); + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_write_bits); + /** * regmap_update_bits_async: Perform a read/modify/write cycle on the register * map asynchronously diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 59c55ea0f0b5..e4b9ad4f05ef 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -424,6 +424,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_count); int regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val); +int regmap_write_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val); int regmap_update_bits_async(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val); int regmap_update_bits_check(struct regmap *map, unsigned int reg, @@ -645,6 +647,13 @@ static inline int regmap_update_bits(struct regmap *map, unsigned int reg, return -EINVAL; } +static inline int regmap_write_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline int regmap_update_bits_async(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val) -- cgit v1.3-6-gb490 From e874e6c7edc43436f73cf84157d9221f8b807c36 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 16 Jun 2015 08:52:55 +0000 Subject: regmap: add regmap_fields_force_write() regmap_fields_force_write() is similar to regmap_fields_write(), but regmap_fields_force_write() write data to register even though it is same value. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 12 ++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 14 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index d93bb9a8ab98..dd63bcbbf8a5 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1624,6 +1624,18 @@ int regmap_fields_write(struct regmap_field *field, unsigned int id, } EXPORT_SYMBOL_GPL(regmap_fields_write); +int regmap_fields_force_write(struct regmap_field *field, unsigned int id, + unsigned int val) +{ + if (id >= field->id_size) + return -EINVAL; + + return regmap_write_bits(field->regmap, + field->reg + (field->id_offset * id), + field->mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_fields_force_write); + /** * regmap_fields_update_bits(): Perform a read/modify/write cycle * on the register field diff --git a/include/linux/regmap.h b/include/linux/regmap.h index e4b9ad4f05ef..519c96231a91 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -505,6 +505,8 @@ int regmap_field_update_bits(struct regmap_field *field, int regmap_fields_write(struct regmap_field *field, unsigned int id, unsigned int val); +int regmap_fields_force_write(struct regmap_field *field, unsigned int id, + unsigned int val); int regmap_fields_read(struct regmap_field *field, unsigned int id, unsigned int *val); int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, -- cgit v1.3-6-gb490 From 04dc91ce2cca5927159c689aa1f47663f8c51530 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 13 Jul 2015 12:26:44 +0200 Subject: regmap: Add better support for devices without readback support Currently regmap requires that a reg_read callback is supplied, otherwise a warning is emitted each time regmap_read() is called. This means a device or bus without readback support needs to supply dummy reg_read callback. Apart from that regmap_read() will still work fine if a cache is used. Remove the warning and let regmap_readable() return false if not reg_read callback is supplied. This means a device no longer has to supply a dummy callback if it does not support readback and it also doesn't have to have a readable_reg callback that always returns false since this is now implicit. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7111d04f2621..8894b992043e 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -93,6 +93,9 @@ bool regmap_writeable(struct regmap *map, unsigned int reg) bool regmap_readable(struct regmap *map, unsigned int reg) { + if (!map->reg_read) + return false; + if (map->max_register && reg > map->max_register) return false; @@ -2097,8 +2100,6 @@ static int _regmap_read(struct regmap *map, unsigned int reg, int ret; void *context = _regmap_map_get_context(map); - WARN_ON(!map->reg_read); - if (!map->cache_bypass) { ret = regcache_read(map, reg, val); if (ret == 0) -- cgit v1.3-6-gb490 From fa3eec7791b0fe27e3112804a71ba445ff336a6b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 1 Jul 2015 23:51:43 +0100 Subject: regmap: Silence warning on invalid zero length read Zero length reads make no sense in a regmap context and are likely to trigger bugs further down the stack so insert an error check, also silencing compiler warnings about use of ret in cases where we iterate per register. Reported-by: Russell King Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 8894b992043e..9c1f856842a3 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2180,6 +2180,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, return -EINVAL; if (reg % map->reg_stride) return -EINVAL; + if (val_count == 0) + return -EINVAL; map->lock(map->lock_arg); -- cgit v1.3-6-gb490 From 8019ff6cfc0440415fcfb6352c58c3951e6ab053 Mon Sep 17 00:00:00 2001 From: Nariman Poushin Date: Thu, 16 Jul 2015 16:36:21 +0100 Subject: regmap: Use reg_sequence for multi_reg_write / register_patch Separate the functionality using sequences of register writes from the functions that take register defaults. This change renames the arguments in order to support the extension of reg_sequence to take an optional delay to be applied after any given register in a sequence is written. This avoids adding an int to all register defaults, which could substantially increase memory usage for regmaps with large default tables. This also updates all the clients of multi_reg_write/register_patch. Signed-off-by: Nariman Poushin Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 2 +- drivers/base/regmap/regmap.c | 22 +++++++++++----------- drivers/gpu/drm/i2c/adv7511.c | 2 +- drivers/input/misc/drv260x.c | 6 +++--- drivers/input/misc/drv2665.c | 2 +- drivers/input/misc/drv2667.c | 4 ++-- drivers/mfd/arizona-core.c | 2 +- drivers/mfd/twl6040.c | 2 +- drivers/mfd/wm5102-tables.c | 6 +++--- drivers/mfd/wm5110-tables.c | 6 +++--- drivers/mfd/wm8994-core.c | 8 ++++---- drivers/mfd/wm8997-tables.c | 2 +- include/linux/regmap.h | 17 ++++++++++++++--- sound/soc/codecs/arizona.c | 2 +- sound/soc/codecs/cs35l32.c | 2 +- sound/soc/codecs/cs42l52.c | 2 +- sound/soc/codecs/da7210.c | 4 ++-- sound/soc/codecs/rt5640.c | 2 +- sound/soc/codecs/rt5645.c | 4 ++-- sound/soc/codecs/rt5651.c | 2 +- sound/soc/codecs/rt5670.c | 2 +- sound/soc/codecs/rt5677.c | 2 +- sound/soc/codecs/tlv320aic3x.c | 2 +- sound/soc/codecs/wm2200.c | 2 +- sound/soc/codecs/wm5100.c | 2 +- sound/soc/codecs/wm8962.c | 2 +- sound/soc/codecs/wm8993.c | 2 +- 27 files changed, 62 insertions(+), 51 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index b2b2849fc6d3..873ddf91c9d3 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -136,7 +136,7 @@ struct regmap { /* if set, the HW registers are known to match map->reg_defaults */ bool no_sync_defaults; - struct reg_default *patch; + struct reg_sequence *patch; int patch_regs; /* if set, converts bulk rw to single rw */ diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7111d04f2621..2cbb4502747d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1743,7 +1743,7 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write); * relative. The page register has been written if that was neccessary. */ static int _regmap_raw_multi_reg_write(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, size_t num_regs) { int ret; @@ -1800,12 +1800,12 @@ static unsigned int _regmap_register_page(struct regmap *map, } static int _regmap_range_multi_paged_reg_write(struct regmap *map, - struct reg_default *regs, + struct reg_sequence *regs, size_t num_regs) { int ret; int i, n; - struct reg_default *base; + struct reg_sequence *base; unsigned int this_page = 0; /* * the set of registers are not neccessarily in order, but @@ -1843,7 +1843,7 @@ static int _regmap_range_multi_paged_reg_write(struct regmap *map, } static int _regmap_multi_reg_write(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, size_t num_regs) { int i; @@ -1895,8 +1895,8 @@ static int _regmap_multi_reg_write(struct regmap *map, struct regmap_range_node *range; range = _regmap_range_lookup(map, reg); if (range) { - size_t len = sizeof(struct reg_default)*num_regs; - struct reg_default *base = kmemdup(regs, len, + size_t len = sizeof(struct reg_sequence)*num_regs; + struct reg_sequence *base = kmemdup(regs, len, GFP_KERNEL); if (!base) return -ENOMEM; @@ -1929,7 +1929,7 @@ static int _regmap_multi_reg_write(struct regmap *map, * A value of zero will be returned on success, a negative errno will be * returned in error cases. */ -int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, +int regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs) { int ret; @@ -1962,7 +1962,7 @@ EXPORT_SYMBOL_GPL(regmap_multi_reg_write); * be returned in error cases. */ int regmap_multi_reg_write_bypassed(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, int num_regs) { int ret; @@ -2552,10 +2552,10 @@ EXPORT_SYMBOL_GPL(regmap_async_complete); * The caller must ensure that this function cannot be called * concurrently with either itself or regcache_sync(). */ -int regmap_register_patch(struct regmap *map, const struct reg_default *regs, +int regmap_register_patch(struct regmap *map, const struct reg_sequence *regs, int num_regs) { - struct reg_default *p; + struct reg_sequence *p; int ret; bool bypass; @@ -2564,7 +2564,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, return 0; p = krealloc(map->patch, - sizeof(struct reg_default) * (map->patch_regs + num_regs), + sizeof(struct reg_sequence) * (map->patch_regs + num_regs), GFP_KERNEL); if (p) { memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs)); diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index 2aaa3c88999e..00416f23b5cb 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -54,7 +54,7 @@ static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder) } /* ADI recommended values for proper operation. */ -static const struct reg_default adv7511_fixed_registers[] = { +static const struct reg_sequence adv7511_fixed_registers[] = { { 0x98, 0x03 }, { 0x9a, 0xe0 }, { 0x9c, 0x30 }, diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index e5d60ecd29a4..f5c9cf2f4073 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -313,14 +313,14 @@ static void drv260x_close(struct input_dev *input) gpiod_set_value(haptics->enable_gpio, 0); } -static const struct reg_default drv260x_lra_cal_regs[] = { +static const struct reg_sequence drv260x_lra_cal_regs[] = { { DRV260X_MODE, DRV260X_AUTO_CAL }, { DRV260X_CTRL3, DRV260X_NG_THRESH_2 }, { DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH }, }; -static const struct reg_default drv260x_lra_init_regs[] = { +static const struct reg_sequence drv260x_lra_init_regs[] = { { DRV260X_MODE, DRV260X_RT_PLAYBACK }, { DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS | DRV260X_AUDIO_HAPTICS_FILTER_125HZ }, @@ -337,7 +337,7 @@ static const struct reg_default drv260x_lra_init_regs[] = { { DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS }, }; -static const struct reg_default drv260x_erm_cal_regs[] = { +static const struct reg_sequence drv260x_erm_cal_regs[] = { { DRV260X_MODE, DRV260X_AUTO_CAL }, { DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT }, { DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT }, diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c index 0afaa33de07d..924456e3ca75 100644 --- a/drivers/input/misc/drv2665.c +++ b/drivers/input/misc/drv2665.c @@ -132,7 +132,7 @@ static void drv2665_close(struct input_dev *input) "Failed to enter standby mode: %d\n", error); } -static const struct reg_default drv2665_init_regs[] = { +static const struct reg_sequence drv2665_init_regs[] = { { DRV2665_CTRL_2, 0 | DRV2665_10_MS_IDLE_TOUT }, { DRV2665_CTRL_1, DRV2665_25_VPP_GAIN }, }; diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c index fc0fddf0896a..047136aa646f 100644 --- a/drivers/input/misc/drv2667.c +++ b/drivers/input/misc/drv2667.c @@ -262,14 +262,14 @@ static void drv2667_close(struct input_dev *input) "Failed to enter standby mode: %d\n", error); } -static const struct reg_default drv2667_init_regs[] = { +static const struct reg_sequence drv2667_init_regs[] = { { DRV2667_CTRL_2, 0 }, { DRV2667_CTRL_1, DRV2667_25_VPP_GAIN }, { DRV2667_WV_SEQ_0, 1 }, { DRV2667_WV_SEQ_1, 0 } }; -static const struct reg_default drv2667_page1_init[] = { +static const struct reg_sequence drv2667_page1_init[] = { { DRV2667_RAM_HDR_SZ, 0x05 }, { DRV2667_RAM_START_HI, 0x80 }, { DRV2667_RAM_START_LO, 0x06 }, diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index bebf58a06a6b..66d50be11960 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -392,7 +392,7 @@ err: * Register patch to some of the CODECs internal write sequences * to ensure a clean exit from the low power sleep state. */ -static const struct reg_default wm5110_sleep_patch[] = { +static const struct reg_sequence wm5110_sleep_patch[] = { { 0x337A, 0xC100 }, { 0x337B, 0x0041 }, { 0x3300, 0xA210 }, diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index c5265c1262c5..583dc33432f3 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -86,7 +86,7 @@ static const struct reg_default twl6040_defaults[] = { { 0x2E, 0x00 }, /* REG_STATUS (ro) */ }; -static struct reg_default twl6040_patch[] = { +static struct reg_sequence twl6040_patch[] = { /* * Select I2C bus access to dual access registers * Interrupt register is cleared on read diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index aeae6ec123b3..423fb3730dc7 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -21,7 +21,7 @@ #define WM5102_NUM_AOD_ISR 2 #define WM5102_NUM_ISR 5 -static const struct reg_default wm5102_reva_patch[] = { +static const struct reg_sequence wm5102_reva_patch[] = { { 0x80, 0x0003 }, { 0x221, 0x0090 }, { 0x211, 0x0014 }, @@ -57,7 +57,7 @@ static const struct reg_default wm5102_reva_patch[] = { { 0x80, 0x0000 }, }; -static const struct reg_default wm5102_revb_patch[] = { +static const struct reg_sequence wm5102_revb_patch[] = { { 0x19, 0x0001 }, { 0x80, 0x0003 }, { 0x081, 0xE022 }, @@ -80,7 +80,7 @@ static const struct reg_default wm5102_revb_patch[] = { /* We use a function so we can use ARRAY_SIZE() */ int wm5102_patch(struct arizona *arizona) { - const struct reg_default *wm5102_patch; + const struct reg_sequence *wm5102_patch; int patch_size; switch (arizona->rev) { diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 12cad94b4035..26ce14f903fe 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -21,7 +21,7 @@ #define WM5110_NUM_AOD_ISR 2 #define WM5110_NUM_ISR 5 -static const struct reg_default wm5110_reva_patch[] = { +static const struct reg_sequence wm5110_reva_patch[] = { { 0x80, 0x3 }, { 0x44, 0x20 }, { 0x45, 0x40 }, @@ -134,7 +134,7 @@ static const struct reg_default wm5110_reva_patch[] = { { 0x209, 0x002A }, }; -static const struct reg_default wm5110_revb_patch[] = { +static const struct reg_sequence wm5110_revb_patch[] = { { 0x80, 0x3 }, { 0x36e, 0x0210 }, { 0x370, 0x0210 }, @@ -224,7 +224,7 @@ static const struct reg_default wm5110_revb_patch[] = { { 0x80, 0x0 }, }; -static const struct reg_default wm5110_revd_patch[] = { +static const struct reg_sequence wm5110_revd_patch[] = { { 0x80, 0x3 }, { 0x80, 0x3 }, { 0x393, 0x27 }, diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 53ae5af5d6e4..0f4169a3a5d4 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -243,21 +243,21 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo) } #endif -static const struct reg_default wm8994_revc_patch[] = { +static const struct reg_sequence wm8994_revc_patch[] = { { 0x102, 0x3 }, { 0x56, 0x3 }, { 0x817, 0x0 }, { 0x102, 0x0 }, }; -static const struct reg_default wm8958_reva_patch[] = { +static const struct reg_sequence wm8958_reva_patch[] = { { 0x102, 0x3 }, { 0xcb, 0x81 }, { 0x817, 0x0 }, { 0x102, 0x0 }, }; -static const struct reg_default wm1811_reva_patch[] = { +static const struct reg_sequence wm1811_reva_patch[] = { { 0x102, 0x3 }, { 0x56, 0xc07 }, { 0x5d, 0x7e }, @@ -326,7 +326,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) { struct wm8994_pdata *pdata; struct regmap_config *regmap_config; - const struct reg_default *regmap_patch = NULL; + const struct reg_sequence *regmap_patch = NULL; const char *devname; int ret, i, patch_regs = 0; int pulls = 0; diff --git a/drivers/mfd/wm8997-tables.c b/drivers/mfd/wm8997-tables.c index c0c25d75aacc..cab2c68f1737 100644 --- a/drivers/mfd/wm8997-tables.c +++ b/drivers/mfd/wm8997-tables.c @@ -17,7 +17,7 @@ #include "arizona.h" -static const struct reg_default wm8997_reva_patch[] = { +static const struct reg_sequence wm8997_reva_patch[] = { { 0x80, 0x0003 }, { 0x214, 0x0008 }, { 0x458, 0x0000 }, diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 59c55ea0f0b5..c9ef2ec69142 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -50,6 +50,17 @@ struct reg_default { unsigned int def; }; +/** + * Register/value pairs for sequences of writes + * + * @reg: Register address. + * @def: Register value. + */ +struct reg_sequence { + unsigned int reg; + unsigned int def; +}; + #ifdef CONFIG_REGMAP enum regmap_endian { @@ -410,10 +421,10 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count); -int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, +int regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs); int regmap_multi_reg_write_bypassed(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, int num_regs); int regmap_raw_write_async(struct regmap *map, unsigned int reg, const void *val, size_t val_len); @@ -450,7 +461,7 @@ void regcache_mark_dirty(struct regmap *map); bool regmap_check_range_table(struct regmap *map, unsigned int reg, const struct regmap_access_table *table); -int regmap_register_patch(struct regmap *map, const struct reg_default *regs, +int regmap_register_patch(struct regmap *map, const struct reg_sequence *regs, int num_regs); int regmap_parse_val(struct regmap *map, const void *buf, unsigned int *val); diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 802e05eae3e9..5edd33fcd68c 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1366,7 +1366,7 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec, { struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = priv->arizona; - struct reg_default dac_comp[] = { + struct reg_sequence dac_comp[] = { { 0x80, 0x3 }, { ARIZONA_DAC_COMP_1, 0 }, { ARIZONA_DAC_COMP_2, 0 }, diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index 8f40025b7e7c..2813a1b0c949 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -276,7 +276,7 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs35l32 = { }; /* Current and threshold powerup sequence Pg37 in datasheet */ -static const struct reg_default cs35l32_monitor_patch[] = { +static const struct reg_sequence cs35l32_monitor_patch[] = { { 0x00, 0x99 }, { 0x48, 0x17 }, diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 4de52c9957ac..8b2d05933594 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1118,7 +1118,7 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs42l52 = { }; /* Current and threshold powerup sequence Pg37 */ -static const struct reg_default cs42l52_threshold_patch[] = { +static const struct reg_sequence cs42l52_threshold_patch[] = { { 0x00, 0x99 }, { 0x3E, 0xBA }, diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index 21810e5f3321..bf0fb3d4df22 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -1182,7 +1182,7 @@ static struct snd_soc_codec_driver soc_codec_dev_da7210 = { #if IS_ENABLED(CONFIG_I2C) -static struct reg_default da7210_regmap_i2c_patch[] = { +static struct reg_sequence da7210_regmap_i2c_patch[] = { /* System controller master disable */ { DA7210_STARTUP1, 0x00 }, @@ -1269,7 +1269,7 @@ static struct i2c_driver da7210_i2c_driver = { #if defined(CONFIG_SPI_MASTER) -static struct reg_default da7210_regmap_spi_patch[] = { +static struct reg_sequence da7210_regmap_spi_patch[] = { /* Dummy read to give two pulses over nCS for SPI */ { DA7210_AUX2, 0x00 }, { DA7210_AUX2, 0x00 }, diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 9bc78e57513d..1ed1f8895e12 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -51,7 +51,7 @@ static const struct regmap_range_cfg rt5640_ranges[] = { .window_len = 0x1, }, }; -static const struct reg_default init_list[] = { +static const struct reg_sequence init_list[] = { {RT5640_PR_BASE + 0x3d, 0x3600}, {RT5640_PR_BASE + 0x12, 0x0aa8}, {RT5640_PR_BASE + 0x14, 0x0aaa}, diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 9ce311e088fc..c0f4be430e70 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -54,7 +54,7 @@ static const struct regmap_range_cfg rt5645_ranges[] = { }, }; -static const struct reg_default init_list[] = { +static const struct reg_sequence init_list[] = { {RT5645_PR_BASE + 0x3d, 0x3600}, {RT5645_PR_BASE + 0x1c, 0xfd20}, {RT5645_PR_BASE + 0x20, 0x611f}, @@ -63,7 +63,7 @@ static const struct reg_default init_list[] = { }; #define RT5645_INIT_REG_LEN ARRAY_SIZE(init_list) -static const struct reg_default rt5650_init_list[] = { +static const struct reg_sequence rt5650_init_list[] = { {0xf6, 0x0100}, }; diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index a3506e193abc..db9b8667f136 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -46,7 +46,7 @@ static const struct regmap_range_cfg rt5651_ranges[] = { .window_len = 0x1, }, }; -static struct reg_default init_list[] = { +static struct reg_sequence init_list[] = { {RT5651_PR_BASE + 0x3d, 0x3e00}, }; diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index a9123d414178..462a91f7cf68 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -51,7 +51,7 @@ static const struct regmap_range_cfg rt5670_ranges[] = { .window_len = 0x1, }, }; -static const struct reg_default init_list[] = { +static const struct reg_sequence init_list[] = { { RT5670_PR_BASE + 0x14, 0x9a8a }, { RT5670_PR_BASE + 0x38, 0x3ba1 }, { RT5670_PR_BASE + 0x3d, 0x3640 }, diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 31d969ac1192..b89775251470 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -54,7 +54,7 @@ static const struct regmap_range_cfg rt5677_ranges[] = { }, }; -static const struct reg_default init_list[] = { +static const struct reg_sequence init_list[] = { {RT5677_ASRC_12, 0x0018}, {RT5677_PR_BASE + 0x3d, 0x364d}, {RT5677_PR_BASE + 0x17, 0x4fc0}, diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index a7cf19b53fb2..83ae1eb44d4f 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1668,7 +1668,7 @@ static const struct i2c_device_id aic3x_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); -static const struct reg_default aic3007_class_d[] = { +static const struct reg_sequence aic3007_class_d[] = { /* Class-D speaker driver init; datasheet p. 46 */ { AIC3X_PAGE_SELECT, 0x0D }, { 0xD, 0x0D }, diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index c83083285e53..6c607928fb9b 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -897,7 +897,7 @@ static bool wm2200_readable_register(struct device *dev, unsigned int reg) } } -static const struct reg_default wm2200_reva_patch[] = { +static const struct reg_sequence wm2200_reva_patch[] = { { 0x07, 0x0003 }, { 0x102, 0x0200 }, { 0x203, 0x0084 }, diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 4c10cd88c1af..26d79bbb7599 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1247,7 +1247,7 @@ static const struct snd_soc_dapm_route wm5100_dapm_routes[] = { { "PWM2", NULL, "PWM2 Driver" }, }; -static const struct reg_default wm5100_reva_patches[] = { +static const struct reg_sequence wm5100_reva_patches[] = { { WM5100_AUDIO_IF_1_10, 0 }, { WM5100_AUDIO_IF_1_11, 1 }, { WM5100_AUDIO_IF_1_12, 2 }, diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index c5748fd4f296..05492e826aea 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3495,7 +3495,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8962 = { }; /* Improve power consumption for IN4 DC measurement mode */ -static const struct reg_default wm8962_dc_measure[] = { +static const struct reg_sequence wm8962_dc_measure[] = { { 0xfd, 0x1 }, { 0xcc, 0x40 }, { 0xfd, 0 }, diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 8a8db8605dc2..52ec64d8502d 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1595,7 +1595,7 @@ static int wm8993_resume(struct snd_soc_codec *codec) #endif /* Tune DC servo configuration */ -static struct reg_default wm8993_regmap_patch[] = { +static struct reg_sequence wm8993_regmap_patch[] = { { 0x44, 3 }, { 0x56, 3 }, { 0x44, 0 }, -- cgit v1.3-6-gb490 From 2de9d6006c190bb0f706e8404de94cd94293801f Mon Sep 17 00:00:00 2001 From: Nariman Poushin Date: Thu, 16 Jul 2015 16:36:22 +0100 Subject: regmap: Apply optional delay in multi_reg_write/register_patch Add an optional delay_us field in reg_sequence to allow the client to specify a delay (in microseconds) to be applied after any given write in a sequence of writes. We treat a delay in a sequence the same way we treat a page change as they are logically similar in that you can coalesce all write before a delay (in the same way you can coalesce all writes before a page change is needed) Signed-off-by: Nariman Poushin Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 54 +++++++++++++++++++++++++++++++++++++++----- include/linux/regmap.h | 5 +++- 2 files changed, 52 insertions(+), 7 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 2cbb4502747d..b3a5aa5cd580 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -18,6 +18,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include "trace.h" @@ -1807,10 +1808,12 @@ static int _regmap_range_multi_paged_reg_write(struct regmap *map, int i, n; struct reg_sequence *base; unsigned int this_page = 0; + unsigned int page_change = 0; /* * the set of registers are not neccessarily in order, but * since the order of write must be preserved this algorithm - * chops the set each time the page changes + * chops the set each time the page changes. This also applies + * if there is a delay required at any point in the sequence. */ base = regs; for (i = 0, n = 0; i < num_regs; i++, n++) { @@ -1826,16 +1829,48 @@ static int _regmap_range_multi_paged_reg_write(struct regmap *map, this_page = win_page; if (win_page != this_page) { this_page = win_page; + page_change = 1; + } + } + + /* If we have both a page change and a delay make sure to + * write the regs and apply the delay before we change the + * page. + */ + + if (page_change || regs[i].delay_us) { + + /* For situations where the first write requires + * a delay we need to make sure we don't call + * raw_multi_reg_write with n=0 + * This can't occur with page breaks as we + * never write on the first iteration + */ + if (regs[i].delay_us && i == 0) + n = 1; + ret = _regmap_raw_multi_reg_write(map, base, n); if (ret != 0) return ret; + + if (regs[i].delay_us) + udelay(regs[i].delay_us); + base += n; n = 0; - } - ret = _regmap_select_page(map, &base[n].reg, range, 1); - if (ret != 0) - return ret; + + if (page_change) { + ret = _regmap_select_page(map, + &base[n].reg, + range, 1); + if (ret != 0) + return ret; + + page_change = 0; + } + } + } if (n > 0) return _regmap_raw_multi_reg_write(map, base, n); @@ -1854,6 +1889,9 @@ static int _regmap_multi_reg_write(struct regmap *map, ret = _regmap_write(map, regs[i].reg, regs[i].def); if (ret != 0) return ret; + + if (regs[i].delay_us) + udelay(regs[i].delay_us); } return 0; } @@ -1893,8 +1931,12 @@ static int _regmap_multi_reg_write(struct regmap *map, for (i = 0; i < num_regs; i++) { unsigned int reg = regs[i].reg; struct regmap_range_node *range; + + /* Coalesce all the writes between a page break or a delay + * in a sequence + */ range = _regmap_range_lookup(map, reg); - if (range) { + if (range || regs[i].delay_us) { size_t len = sizeof(struct reg_sequence)*num_regs; struct reg_sequence *base = kmemdup(regs, len, GFP_KERNEL); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index c9ef2ec69142..5a7cf2136c81 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -51,14 +51,17 @@ struct reg_default { }; /** - * Register/value pairs for sequences of writes + * Register/value pairs for sequences of writes with an optional delay in + * microseconds to be applied after each write. * * @reg: Register address. * @def: Register value. + * @delay_us: Delay to be applied after the register write in microseconds */ struct reg_sequence { unsigned int reg; unsigned int def; + unsigned int delay_us; }; #ifdef CONFIG_REGMAP -- cgit v1.3-6-gb490 From f4745a92781b872455f32feb01d1dce92aefcb6c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 29 Jun 2015 22:13:38 +0100 Subject: PM / clk: don't return int on __pm_clk_enable() Static analysis by cppcheck found an issue that was recently introduced by commit 471f7707b6f0b1 ("PM / clock_ops: make __pm_clk_enable more generic") where a return status in ret was not being initialised and garbage being returned when ce->status >= PCE_STATUS_ERROR. The fact that ret is not being checked by the caller and that ret is only used internally __pm_clk_enable() to check if clk_enable() was OK means we can ignore returning it instead turn __pm_clk_enable() into function with a void return. Fixes: 471f7707b6f0b1 ("PM / clock_ops: make __pm_clk_enable more generic") Signed-off-by: Colin Ian King Signed-off-by: Rafael J. Wysocki --- drivers/base/power/clock_ops.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index acef9f9f759a..652b5a367c1f 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -38,7 +38,7 @@ struct pm_clock_entry { * @dev: The device for the given clock * @ce: PM clock entry corresponding to the clock. */ -static inline int __pm_clk_enable(struct device *dev, struct pm_clock_entry *ce) +static inline void __pm_clk_enable(struct device *dev, struct pm_clock_entry *ce) { int ret; @@ -50,8 +50,6 @@ static inline int __pm_clk_enable(struct device *dev, struct pm_clock_entry *ce) dev_err(dev, "%s: failed to enable clk %p, error %d\n", __func__, ce->clk, ret); } - - return ret; } /** -- cgit v1.3-6-gb490 From d3dc5430d68fb91a62d971648170b34d46ab85bc Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 23 Jun 2015 14:32:55 +0100 Subject: regmap: debugfs: Allow writes to cache state settings Allow the user to write the cache_only and cache_bypass settings. This can be useful for debugging. Since this can lead to the hardware getting out-of-sync with the cache, at least for the period that the cache state is forced, the kernel is tainted and the action is recorded in the kernel log. When disabling cache_only through debugfs a cache sync will be performed. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 90 ++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 5799a0b9e6cc..6a61e4fa73a2 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -469,6 +469,87 @@ static const struct file_operations regmap_access_fops = { .llseek = default_llseek, }; +static ssize_t regmap_cache_only_write_file(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct regmap *map = container_of(file->private_data, + struct regmap, cache_only); + ssize_t result; + bool was_enabled, require_sync = false; + int err; + + map->lock(map->lock_arg); + + was_enabled = map->cache_only; + + result = debugfs_write_file_bool(file, user_buf, count, ppos); + if (result < 0) { + map->unlock(map->lock_arg); + return result; + } + + if (map->cache_only && !was_enabled) { + dev_warn(map->dev, "debugfs cache_only=Y forced\n"); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + } else if (!map->cache_only && was_enabled) { + dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n"); + require_sync = true; + } + + map->unlock(map->lock_arg); + + if (require_sync) { + err = regcache_sync(map); + if (err) + dev_err(map->dev, "Failed to sync cache %d\n", err); + } + + return result; +} + +static const struct file_operations regmap_cache_only_fops = { + .open = simple_open, + .read = debugfs_read_file_bool, + .write = regmap_cache_only_write_file, +}; + +static ssize_t regmap_cache_bypass_write_file(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct regmap *map = container_of(file->private_data, + struct regmap, cache_bypass); + ssize_t result; + bool was_enabled; + + map->lock(map->lock_arg); + + was_enabled = map->cache_bypass; + + result = debugfs_write_file_bool(file, user_buf, count, ppos); + if (result < 0) + goto out; + + if (map->cache_bypass && !was_enabled) { + dev_warn(map->dev, "debugfs cache_bypass=Y forced\n"); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + } else if (!map->cache_bypass && was_enabled) { + dev_warn(map->dev, "debugfs cache_bypass=N forced\n"); + } + +out: + map->unlock(map->lock_arg); + + return result; +} + +static const struct file_operations regmap_cache_bypass_fops = { + .open = simple_open, + .read = debugfs_read_file_bool, + .write = regmap_cache_bypass_write_file, +}; + void regmap_debugfs_init(struct regmap *map, const char *name) { struct rb_node *next; @@ -530,12 +611,13 @@ void regmap_debugfs_init(struct regmap *map, const char *name) } if (map->cache_type) { - debugfs_create_bool("cache_only", 0400, map->debugfs, - &map->cache_only); + debugfs_create_file("cache_only", 0600, map->debugfs, + &map->cache_only, ®map_cache_only_fops); debugfs_create_bool("cache_dirty", 0400, map->debugfs, &map->cache_dirty); - debugfs_create_bool("cache_bypass", 0400, map->debugfs, - &map->cache_bypass); + debugfs_create_file("cache_bypass", 0600, map->debugfs, + &map->cache_bypass, + ®map_cache_bypass_fops); } next = rb_first(&map->range_tree); -- cgit v1.3-6-gb490 From 019d8817b1b064c2bacfbcf40fc68184438ad05a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 15 Jul 2015 14:40:06 +0200 Subject: PM / sleep: Allow devices without runtime PM to do direct-complete Don't unset the direct_complete flag on devices that have runtime PM disabled, if they are runtime suspended. This is needed because otherwise ancestor devices wouldn't be able to do direct_complete without adding runtime PM support to all its descendants. Also removes pm_runtime_suspended_if_enabled() because it's now unused. Signed-off-by: Tomeu Vizoso Signed-off-by: Alan Stern Signed-off-by: Rafael J. Wysocki --- Documentation/power/devices.txt | 7 +++++++ Documentation/power/runtime_pm.txt | 4 ---- drivers/base/power/main.c | 2 +- include/linux/pm_runtime.h | 6 ------ 4 files changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers/base') diff --git a/Documentation/power/devices.txt b/Documentation/power/devices.txt index d172bce0fd49..8ba6625fdd63 100644 --- a/Documentation/power/devices.txt +++ b/Documentation/power/devices.txt @@ -341,6 +341,13 @@ the phases are: and is entirely responsible for bringing the device back to the functional state as appropriate. + Note that this direct-complete procedure applies even if the device is + disabled for runtime PM; only the runtime-PM status matters. It follows + that if a device has system-sleep callbacks but does not support runtime + PM, then its prepare callback must never return a positive value. This + is because all devices are initially set to runtime-suspended with + runtime PM disabled. + 2. The suspend methods should quiesce the device to stop it from performing I/O. They also may save the device registers and put it into the appropriate low-power state, depending on the bus type the device is on, diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index e76dc0ad4d2b..0784bc3a2ab5 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt @@ -445,10 +445,6 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: bool pm_runtime_status_suspended(struct device *dev); - return true if the device's runtime PM status is 'suspended' - bool pm_runtime_suspended_if_enabled(struct device *dev); - - return true if the device's runtime PM status is 'suspended' and its - 'power.disable_depth' field is equal to 1 - void pm_runtime_allow(struct device *dev); - set the power.runtime_auto flag for the device and decrease its usage counter (used by the /sys/devices/.../power/control interface to diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 30b7bbfdc558..1710c26ba097 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1377,7 +1377,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (dev->power.direct_complete) { if (pm_runtime_status_suspended(dev)) { pm_runtime_disable(dev); - if (pm_runtime_suspended_if_enabled(dev)) + if (pm_runtime_status_suspended(dev)) goto Complete; pm_runtime_enable(dev); diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 30e84d48bfea..3bdbb4189780 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -98,11 +98,6 @@ static inline bool pm_runtime_status_suspended(struct device *dev) return dev->power.runtime_status == RPM_SUSPENDED; } -static inline bool pm_runtime_suspended_if_enabled(struct device *dev) -{ - return pm_runtime_status_suspended(dev) && dev->power.disable_depth == 1; -} - static inline bool pm_runtime_enabled(struct device *dev) { return !dev->power.disable_depth; @@ -164,7 +159,6 @@ static inline void device_set_run_wake(struct device *dev, bool enable) {} static inline bool pm_runtime_suspended(struct device *dev) { return false; } static inline bool pm_runtime_active(struct device *dev) { return true; } static inline bool pm_runtime_status_suspended(struct device *dev) { return false; } -static inline bool pm_runtime_suspended_if_enabled(struct device *dev) { return false; } static inline bool pm_runtime_enabled(struct device *dev) { return false; } static inline void pm_runtime_no_callbacks(struct device *dev) {} -- cgit v1.3-6-gb490 From 4a7cc831670550e6b48ef5760e7213f89935ff0d Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Thu, 9 Jul 2015 16:00:44 +0800 Subject: genirq/MSI: Move msi_list from struct pci_dev to struct device Move msi_list from struct pci_dev into struct device, so we can support non-PCI-device based generic MSI interrupts. msi_list is now conditional under CONFIG_GENERIC_MSI_IRQ, which is selected from CONFIG_PCI_MSI, so no functional change for PCI MSI users. Signed-off-by: Jiang Liu Reviewed-by: Yijing Wang Acked-by: Bjorn Helgaas Cc: Tony Luck Cc: linux-arm-kernel@lists.infradead.org Cc: Grant Likely Cc: Marc Zyngier Cc: Stuart Yoder Cc: Borislav Petkov Cc: Greg Kroah-Hartman Cc: Joe Perches Cc: Dmitry Torokhov Cc: Paul Gortmaker Cc: Luis R. Rodriguez Cc: Rafael J. Wysocki Cc: Joerg Roedel Cc: Alexander Gordeev Link: http://lkml.kernel.org/r/1436428847-8886-10-git-send-email-jiang.liu@linux.intel.com Signed-off-by: Thomas Gleixner --- drivers/base/core.c | 3 +++ drivers/pci/msi.c | 3 +-- include/linux/device.h | 4 ++++ include/linux/msi.h | 2 +- include/linux/pci.h | 1 - 5 files changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index dafae6d2f7ac..18e2a89aa138 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -662,6 +662,9 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->devres_head); device_pm_init(dev); set_dev_node(dev, -1); +#ifdef CONFIG_GENERIC_MSI_IRQ + INIT_LIST_HEAD(&dev->msi_list); +#endif } EXPORT_SYMBOL_GPL(device_initialize); diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index f0714c3fd315..4ef5021a084d 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -900,7 +900,7 @@ void pci_msi_shutdown(struct pci_dev *dev) return; BUG_ON(list_empty(dev_to_msi_list(&dev->dev))); - desc = first_msi_entry(dev); + desc = first_pci_msi_entry(dev); pci_msi_set_enable(dev, 0); pci_intx_for_msi(dev, 1); @@ -1044,7 +1044,6 @@ EXPORT_SYMBOL(pci_msi_enabled); void pci_msi_init_pci_dev(struct pci_dev *dev) { - INIT_LIST_HEAD(&dev->msi_list); } /** diff --git a/include/linux/device.h b/include/linux/device.h index 5a31bf3a4024..22227e7fe463 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -713,6 +713,7 @@ struct device_dma_parameters { * along with subsystem-level and driver-level callbacks. * @pins: For device pin management. * See Documentation/pinctrl.txt for details. + * @msi_list: Hosts MSI descriptors * @numa_node: NUMA node this device is close to. * @dma_mask: Dma mask (if dma'ble device). * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all @@ -776,6 +777,9 @@ struct device { #ifdef CONFIG_PINCTRL struct dev_pin_info *pins; #endif +#ifdef CONFIG_GENERIC_MSI_IRQ + struct list_head msi_list; +#endif #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ diff --git a/include/linux/msi.h b/include/linux/msi.h index cfbd2afeaf64..57fe766a14bf 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -45,7 +45,7 @@ struct msi_desc { /* Helpers to hide struct msi_desc implementation details */ #define msi_desc_to_dev(desc) (&(desc)->dev.dev) -#define dev_to_msi_list(dev) (&to_pci_dev((dev))->msi_list) +#define dev_to_msi_list(dev) (&(dev)->msi_list) #define first_msi_entry(dev) \ list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list) #define for_each_msi_entry(desc, dev) \ diff --git a/include/linux/pci.h b/include/linux/pci.h index 8a0321a8fb59..fbf245f5eba7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -366,7 +366,6 @@ struct pci_dev { struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ #ifdef CONFIG_PCI_MSI - struct list_head msi_list; const struct attribute_group **msi_irq_groups; #endif struct pci_vpd *vpd; -- cgit v1.3-6-gb490 From f78f5b90c4ffa559e400c3919a02236101f29f3f Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 18 Jun 2015 15:50:02 -0700 Subject: rcu: Rename rcu_lockdep_assert() to RCU_LOCKDEP_WARN() This commit renames rcu_lockdep_assert() to RCU_LOCKDEP_WARN() for consistency with the WARN() series of macros. This also requires inverting the sense of the conditional, which this commit also does. Reported-by: Ingo Molnar Signed-off-by: Paul E. McKenney Reviewed-by: Ingo Molnar --- Documentation/RCU/whatisRCU.txt | 2 +- arch/x86/kernel/cpu/mcheck/mce.c | 6 ++-- arch/x86/kernel/traps.c | 2 +- drivers/base/power/opp.c | 4 +-- include/linux/fdtable.h | 4 +-- include/linux/rcupdate.h | 63 ++++++++++++++++++++++++++-------------- kernel/cgroup.c | 4 +-- kernel/pid.c | 5 ++-- kernel/rcu/srcu.c | 10 +++---- kernel/rcu/tiny.c | 8 ++--- kernel/rcu/tree.c | 28 +++++++++--------- kernel/rcu/tree_plugin.h | 8 ++--- kernel/rcu/update.c | 4 +-- kernel/sched/core.c | 8 ++--- kernel/workqueue.c | 20 ++++++------- security/device_cgroup.c | 6 ++-- 16 files changed, 101 insertions(+), 81 deletions(-) (limited to 'drivers/base') diff --git a/Documentation/RCU/whatisRCU.txt b/Documentation/RCU/whatisRCU.txt index 5746b0c77f3e..adc2184009c5 100644 --- a/Documentation/RCU/whatisRCU.txt +++ b/Documentation/RCU/whatisRCU.txt @@ -883,7 +883,7 @@ All: lockdep-checked RCU-protected pointer access rcu_access_pointer rcu_dereference_raw - rcu_lockdep_assert + RCU_LOCKDEP_WARN rcu_sleep_check RCU_NONIDLE diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index df919ff103c3..3d6b5269fb2e 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -54,9 +54,9 @@ static DEFINE_MUTEX(mce_chrdev_read_mutex); #define rcu_dereference_check_mce(p) \ ({ \ - rcu_lockdep_assert(rcu_read_lock_sched_held() || \ - lockdep_is_held(&mce_chrdev_read_mutex), \ - "suspicious rcu_dereference_check_mce() usage"); \ + RCU_LOCKDEP_WARN(!rcu_read_lock_sched_held() && \ + !lockdep_is_held(&mce_chrdev_read_mutex), \ + "suspicious rcu_dereference_check_mce() usage"); \ smp_load_acquire(&(p)); \ }) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index f5791927aa64..c5a5231d1d11 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -136,7 +136,7 @@ enum ctx_state ist_enter(struct pt_regs *regs) preempt_count_add(HARDIRQ_OFFSET); /* This code is a bit fragile. Test it. */ - rcu_lockdep_assert(rcu_is_watching(), "ist_enter didn't work"); + RCU_LOCKDEP_WARN(!rcu_is_watching(), "ist_enter didn't work"); return prev_state; } diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 677fb2843553..3b188f20b43f 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -110,8 +110,8 @@ static DEFINE_MUTEX(dev_opp_list_lock); #define opp_rcu_lockdep_assert() \ do { \ - rcu_lockdep_assert(rcu_read_lock_held() || \ - lockdep_is_held(&dev_opp_list_lock), \ + RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \ + !lockdep_is_held(&dev_opp_list_lock), \ "Missing rcu_read_lock() or " \ "dev_opp_list_lock protection"); \ } while (0) diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h index fbb88740634a..674e3e226465 100644 --- a/include/linux/fdtable.h +++ b/include/linux/fdtable.h @@ -86,8 +86,8 @@ static inline struct file *__fcheck_files(struct files_struct *files, unsigned i static inline struct file *fcheck_files(struct files_struct *files, unsigned int fd) { - rcu_lockdep_assert(rcu_read_lock_held() || - lockdep_is_held(&files->file_lock), + RCU_LOCKDEP_WARN(!rcu_read_lock_held() && + !lockdep_is_held(&files->file_lock), "suspicious rcu_dereference_check() usage"); return __fcheck_files(files, fd); } diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 33ec16b9c2ee..ff476515f716 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -536,6 +536,11 @@ static inline int rcu_read_lock_sched_held(void) #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ +/* Deprecate rcu_lockdep_assert(): Use RCU_LOCKDEP_WARN() instead. */ +static inline void __attribute((deprecated)) deprecate_rcu_lockdep_assert(void) +{ +} + #ifdef CONFIG_PROVE_RCU /** @@ -546,17 +551,32 @@ static inline int rcu_read_lock_sched_held(void) #define rcu_lockdep_assert(c, s) \ do { \ static bool __section(.data.unlikely) __warned; \ + deprecate_rcu_lockdep_assert(); \ if (debug_lockdep_rcu_enabled() && !__warned && !(c)) { \ __warned = true; \ lockdep_rcu_suspicious(__FILE__, __LINE__, s); \ } \ } while (0) +/** + * RCU_LOCKDEP_WARN - emit lockdep splat if specified condition is met + * @c: condition to check + * @s: informative message + */ +#define RCU_LOCKDEP_WARN(c, s) \ + do { \ + static bool __section(.data.unlikely) __warned; \ + if (debug_lockdep_rcu_enabled() && !__warned && (c)) { \ + __warned = true; \ + lockdep_rcu_suspicious(__FILE__, __LINE__, s); \ + } \ + } while (0) + #if defined(CONFIG_PROVE_RCU) && !defined(CONFIG_PREEMPT_RCU) static inline void rcu_preempt_sleep_check(void) { - rcu_lockdep_assert(!lock_is_held(&rcu_lock_map), - "Illegal context switch in RCU read-side critical section"); + RCU_LOCKDEP_WARN(lock_is_held(&rcu_lock_map), + "Illegal context switch in RCU read-side critical section"); } #else /* #ifdef CONFIG_PROVE_RCU */ static inline void rcu_preempt_sleep_check(void) @@ -567,15 +587,16 @@ static inline void rcu_preempt_sleep_check(void) #define rcu_sleep_check() \ do { \ rcu_preempt_sleep_check(); \ - rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map), \ - "Illegal context switch in RCU-bh read-side critical section"); \ - rcu_lockdep_assert(!lock_is_held(&rcu_sched_lock_map), \ - "Illegal context switch in RCU-sched read-side critical section"); \ + RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map), \ + "Illegal context switch in RCU-bh read-side critical section"); \ + RCU_LOCKDEP_WARN(lock_is_held(&rcu_sched_lock_map), \ + "Illegal context switch in RCU-sched read-side critical section"); \ } while (0) #else /* #ifdef CONFIG_PROVE_RCU */ -#define rcu_lockdep_assert(c, s) do { } while (0) +#define rcu_lockdep_assert(c, s) deprecate_rcu_lockdep_assert() +#define RCU_LOCKDEP_WARN(c, s) do { } while (0) #define rcu_sleep_check() do { } while (0) #endif /* #else #ifdef CONFIG_PROVE_RCU */ @@ -606,13 +627,13 @@ static inline void rcu_preempt_sleep_check(void) ({ \ /* Dependency order vs. p above. */ \ typeof(*p) *________p1 = (typeof(*p) *__force)lockless_dereference(p); \ - rcu_lockdep_assert(c, "suspicious rcu_dereference_check() usage"); \ + RCU_LOCKDEP_WARN(!(c), "suspicious rcu_dereference_check() usage"); \ rcu_dereference_sparse(p, space); \ ((typeof(*p) __force __kernel *)(________p1)); \ }) #define __rcu_dereference_protected(p, c, space) \ ({ \ - rcu_lockdep_assert(c, "suspicious rcu_dereference_protected() usage"); \ + RCU_LOCKDEP_WARN(!(c), "suspicious rcu_dereference_protected() usage"); \ rcu_dereference_sparse(p, space); \ ((typeof(*p) __force __kernel *)(p)); \ }) @@ -836,8 +857,8 @@ static inline void rcu_read_lock(void) __rcu_read_lock(); __acquire(RCU); rcu_lock_acquire(&rcu_lock_map); - rcu_lockdep_assert(rcu_is_watching(), - "rcu_read_lock() used illegally while idle"); + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_lock() used illegally while idle"); } /* @@ -887,8 +908,8 @@ static inline void rcu_read_lock(void) */ static inline void rcu_read_unlock(void) { - rcu_lockdep_assert(rcu_is_watching(), - "rcu_read_unlock() used illegally while idle"); + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_unlock() used illegally while idle"); __release(RCU); __rcu_read_unlock(); rcu_lock_release(&rcu_lock_map); /* Keep acq info for rls diags. */ @@ -916,8 +937,8 @@ static inline void rcu_read_lock_bh(void) local_bh_disable(); __acquire(RCU_BH); rcu_lock_acquire(&rcu_bh_lock_map); - rcu_lockdep_assert(rcu_is_watching(), - "rcu_read_lock_bh() used illegally while idle"); + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_lock_bh() used illegally while idle"); } /* @@ -927,8 +948,8 @@ static inline void rcu_read_lock_bh(void) */ static inline void rcu_read_unlock_bh(void) { - rcu_lockdep_assert(rcu_is_watching(), - "rcu_read_unlock_bh() used illegally while idle"); + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_unlock_bh() used illegally while idle"); rcu_lock_release(&rcu_bh_lock_map); __release(RCU_BH); local_bh_enable(); @@ -952,8 +973,8 @@ static inline void rcu_read_lock_sched(void) preempt_disable(); __acquire(RCU_SCHED); rcu_lock_acquire(&rcu_sched_lock_map); - rcu_lockdep_assert(rcu_is_watching(), - "rcu_read_lock_sched() used illegally while idle"); + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_lock_sched() used illegally while idle"); } /* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */ @@ -970,8 +991,8 @@ static inline notrace void rcu_read_lock_sched_notrace(void) */ static inline void rcu_read_unlock_sched(void) { - rcu_lockdep_assert(rcu_is_watching(), - "rcu_read_unlock_sched() used illegally while idle"); + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_unlock_sched() used illegally while idle"); rcu_lock_release(&rcu_sched_lock_map); __release(RCU_SCHED); preempt_enable(); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index f89d9292eee6..b89f3168411b 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -107,8 +107,8 @@ static DEFINE_SPINLOCK(release_agent_path_lock); struct percpu_rw_semaphore cgroup_threadgroup_rwsem; #define cgroup_assert_mutex_or_rcu_locked() \ - rcu_lockdep_assert(rcu_read_lock_held() || \ - lockdep_is_held(&cgroup_mutex), \ + RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \ + !lockdep_is_held(&cgroup_mutex), \ "cgroup_mutex or RCU read lock required"); /* diff --git a/kernel/pid.c b/kernel/pid.c index 4fd07d5b7baf..ca368793808e 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -451,9 +451,8 @@ EXPORT_SYMBOL(pid_task); */ struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns) { - rcu_lockdep_assert(rcu_read_lock_held(), - "find_task_by_pid_ns() needs rcu_read_lock()" - " protection"); + RCU_LOCKDEP_WARN(!rcu_read_lock_held(), + "find_task_by_pid_ns() needs rcu_read_lock() protection"); return pid_task(find_pid_ns(nr, ns), PIDTYPE_PID); } diff --git a/kernel/rcu/srcu.c b/kernel/rcu/srcu.c index de35087c92a5..d3fcb2ec8536 100644 --- a/kernel/rcu/srcu.c +++ b/kernel/rcu/srcu.c @@ -415,11 +415,11 @@ static void __synchronize_srcu(struct srcu_struct *sp, int trycount) struct rcu_head *head = &rcu.head; bool done = false; - rcu_lockdep_assert(!lock_is_held(&sp->dep_map) && - !lock_is_held(&rcu_bh_lock_map) && - !lock_is_held(&rcu_lock_map) && - !lock_is_held(&rcu_sched_lock_map), - "Illegal synchronize_srcu() in same-type SRCU (or RCU) read-side critical section"); + RCU_LOCKDEP_WARN(lock_is_held(&sp->dep_map) || + lock_is_held(&rcu_bh_lock_map) || + lock_is_held(&rcu_lock_map) || + lock_is_held(&rcu_sched_lock_map), + "Illegal synchronize_srcu() in same-type SRCU (or in RCU) read-side critical section"); might_sleep(); init_completion(&rcu.completion); diff --git a/kernel/rcu/tiny.c b/kernel/rcu/tiny.c index c291bd65d2cb..d0471056d0af 100644 --- a/kernel/rcu/tiny.c +++ b/kernel/rcu/tiny.c @@ -191,10 +191,10 @@ static void rcu_process_callbacks(struct softirq_action *unused) */ void synchronize_sched(void) { - rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && - !lock_is_held(&rcu_lock_map) && - !lock_is_held(&rcu_sched_lock_map), - "Illegal synchronize_sched() in RCU read-side critical section"); + RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map) || + lock_is_held(&rcu_lock_map) || + lock_is_held(&rcu_sched_lock_map), + "Illegal synchronize_sched() in RCU read-side critical section"); cond_resched(); } EXPORT_SYMBOL_GPL(synchronize_sched); diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index cb64d7e13d24..0a73d26357a2 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -649,12 +649,12 @@ static void rcu_eqs_enter_common(long long oldval, bool user) * It is illegal to enter an extended quiescent state while * in an RCU read-side critical section. */ - rcu_lockdep_assert(!lock_is_held(&rcu_lock_map), - "Illegal idle entry in RCU read-side critical section."); - rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map), - "Illegal idle entry in RCU-bh read-side critical section."); - rcu_lockdep_assert(!lock_is_held(&rcu_sched_lock_map), - "Illegal idle entry in RCU-sched read-side critical section."); + RCU_LOCKDEP_WARN(lock_is_held(&rcu_lock_map), + "Illegal idle entry in RCU read-side critical section."); + RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map), + "Illegal idle entry in RCU-bh read-side critical section."); + RCU_LOCKDEP_WARN(lock_is_held(&rcu_sched_lock_map), + "Illegal idle entry in RCU-sched read-side critical section."); } /* @@ -3161,10 +3161,10 @@ static inline int rcu_blocking_is_gp(void) */ void synchronize_sched(void) { - rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && - !lock_is_held(&rcu_lock_map) && - !lock_is_held(&rcu_sched_lock_map), - "Illegal synchronize_sched() in RCU-sched read-side critical section"); + RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map) || + lock_is_held(&rcu_lock_map) || + lock_is_held(&rcu_sched_lock_map), + "Illegal synchronize_sched() in RCU-sched read-side critical section"); if (rcu_blocking_is_gp()) return; if (rcu_gp_is_expedited()) @@ -3188,10 +3188,10 @@ EXPORT_SYMBOL_GPL(synchronize_sched); */ void synchronize_rcu_bh(void) { - rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && - !lock_is_held(&rcu_lock_map) && - !lock_is_held(&rcu_sched_lock_map), - "Illegal synchronize_rcu_bh() in RCU-bh read-side critical section"); + RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map) || + lock_is_held(&rcu_lock_map) || + lock_is_held(&rcu_sched_lock_map), + "Illegal synchronize_rcu_bh() in RCU-bh read-side critical section"); if (rcu_blocking_is_gp()) return; if (rcu_gp_is_expedited()) diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index a983bc68a146..9e922f111d63 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -538,10 +538,10 @@ EXPORT_SYMBOL_GPL(call_rcu); */ void synchronize_rcu(void) { - rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map) && - !lock_is_held(&rcu_lock_map) && - !lock_is_held(&rcu_sched_lock_map), - "Illegal synchronize_rcu() in RCU read-side critical section"); + RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map) || + lock_is_held(&rcu_lock_map) || + lock_is_held(&rcu_sched_lock_map), + "Illegal synchronize_rcu() in RCU read-side critical section"); if (!rcu_scheduler_active) return; if (rcu_gp_is_expedited()) diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index a0a0dd03c73a..47268fb1d27b 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -589,8 +589,8 @@ EXPORT_SYMBOL_GPL(call_rcu_tasks); void synchronize_rcu_tasks(void) { /* Complain if the scheduler has not started. */ - rcu_lockdep_assert(!rcu_scheduler_active, - "synchronize_rcu_tasks called too soon"); + RCU_LOCKDEP_WARN(rcu_scheduler_active, + "synchronize_rcu_tasks called too soon"); /* Wait for the grace period. */ wait_rcu_gp(call_rcu_tasks); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 78b4bad10081..5e73c79fadd0 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2200,8 +2200,8 @@ unsigned long to_ratio(u64 period, u64 runtime) #ifdef CONFIG_SMP inline struct dl_bw *dl_bw_of(int i) { - rcu_lockdep_assert(rcu_read_lock_sched_held(), - "sched RCU must be held"); + RCU_LOCKDEP_WARN(!rcu_read_lock_sched_held(), + "sched RCU must be held"); return &cpu_rq(i)->rd->dl_bw; } @@ -2210,8 +2210,8 @@ static inline int dl_bw_cpus(int i) struct root_domain *rd = cpu_rq(i)->rd; int cpus = 0; - rcu_lockdep_assert(rcu_read_lock_sched_held(), - "sched RCU must be held"); + RCU_LOCKDEP_WARN(!rcu_read_lock_sched_held(), + "sched RCU must be held"); for_each_cpu_and(i, rd->span, cpu_active_mask) cpus++; diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 4c4f06176f74..cb91c63b4f4a 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -338,20 +338,20 @@ static void workqueue_sysfs_unregister(struct workqueue_struct *wq); #include #define assert_rcu_or_pool_mutex() \ - rcu_lockdep_assert(rcu_read_lock_sched_held() || \ - lockdep_is_held(&wq_pool_mutex), \ - "sched RCU or wq_pool_mutex should be held") + RCU_LOCKDEP_WARN(!rcu_read_lock_sched_held() && \ + !lockdep_is_held(&wq_pool_mutex), \ + "sched RCU or wq_pool_mutex should be held") #define assert_rcu_or_wq_mutex(wq) \ - rcu_lockdep_assert(rcu_read_lock_sched_held() || \ - lockdep_is_held(&wq->mutex), \ - "sched RCU or wq->mutex should be held") + RCU_LOCKDEP_WARN(!rcu_read_lock_sched_held() && \ + !lockdep_is_held(&wq->mutex), \ + "sched RCU or wq->mutex should be held") #define assert_rcu_or_wq_mutex_or_pool_mutex(wq) \ - rcu_lockdep_assert(rcu_read_lock_sched_held() || \ - lockdep_is_held(&wq->mutex) || \ - lockdep_is_held(&wq_pool_mutex), \ - "sched RCU, wq->mutex or wq_pool_mutex should be held") + RCU_LOCKDEP_WARN(!rcu_read_lock_sched_held() && \ + !lockdep_is_held(&wq->mutex) && \ + !lockdep_is_held(&wq_pool_mutex), \ + "sched RCU, wq->mutex or wq_pool_mutex should be held") #define for_each_cpu_worker_pool(pool, cpu) \ for ((pool) = &per_cpu(cpu_worker_pools, cpu)[0]; \ diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 188c1d26393b..73455089feef 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -400,9 +400,9 @@ static bool verify_new_ex(struct dev_cgroup *dev_cgroup, { bool match = false; - rcu_lockdep_assert(rcu_read_lock_held() || - lockdep_is_held(&devcgroup_mutex), - "device_cgroup:verify_new_ex called without proper synchronization"); + RCU_LOCKDEP_WARN(!rcu_read_lock_held() && + lockdep_is_held(&devcgroup_mutex), + "device_cgroup:verify_new_ex called without proper synchronization"); if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) { if (behavior == DEVCG_DEFAULT_ALLOW) { -- cgit v1.3-6-gb490 From 13b2c4a0c3b1cd37ee6bcfbb5b6e2b94e9a75364 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 27 Jul 2015 18:03:56 +0300 Subject: PM / QoS: Make it possible to expose device latency tolerance to userspace Typically when a device is created the bus core it belongs to (for example PCI) does not know if the device supports things like latency tolerance. This is left to the driver that binds to the device in question. However, at that time the device has already been created and there is no way to set its dev->power.set_latency_tolerance anymore. So follow what has been done for other PM QoS attributes as well and allow drivers to expose and hide latency tolerance from userspace, if the device supports it. Acked-by: Rafael J. Wysocki Signed-off-by: Mika Westerberg Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/base/power/power.h | 2 ++ drivers/base/power/qos.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/base/power/sysfs.c | 11 +++++++++++ include/linux/pm_qos.h | 5 +++++ 4 files changed, 55 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index f1a5d95e7b20..998fa6b23084 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -73,6 +73,8 @@ extern int pm_qos_sysfs_add_resume_latency(struct device *dev); extern void pm_qos_sysfs_remove_resume_latency(struct device *dev); extern int pm_qos_sysfs_add_flags(struct device *dev); extern void pm_qos_sysfs_remove_flags(struct device *dev); +extern int pm_qos_sysfs_add_latency_tolerance(struct device *dev); +extern void pm_qos_sysfs_remove_latency_tolerance(struct device *dev); #else /* CONFIG_PM */ diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index e56d538d039e..7f3646e459cb 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -883,3 +883,40 @@ int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val) mutex_unlock(&dev_pm_qos_mtx); return ret; } + +/** + * dev_pm_qos_expose_latency_tolerance - Expose latency tolerance to userspace + * @dev: Device whose latency tolerance to expose + */ +int dev_pm_qos_expose_latency_tolerance(struct device *dev) +{ + int ret; + + if (!dev->power.set_latency_tolerance) + return -EINVAL; + + mutex_lock(&dev_pm_qos_sysfs_mtx); + ret = pm_qos_sysfs_add_latency_tolerance(dev); + mutex_unlock(&dev_pm_qos_sysfs_mtx); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_tolerance); + +/** + * dev_pm_qos_hide_latency_tolerance - Hide latency tolerance from userspace + * @dev: Device whose latency tolerance to hide + */ +void dev_pm_qos_hide_latency_tolerance(struct device *dev) +{ + mutex_lock(&dev_pm_qos_sysfs_mtx); + pm_qos_sysfs_remove_latency_tolerance(dev); + mutex_unlock(&dev_pm_qos_sysfs_mtx); + + /* Remove the request from user space now */ + pm_runtime_get_sync(dev); + dev_pm_qos_update_user_latency_tolerance(dev, + PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT); + pm_runtime_put(dev); +} +EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_tolerance); diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index d2be3f9c211c..a7b46798c81d 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -738,6 +738,17 @@ void pm_qos_sysfs_remove_flags(struct device *dev) sysfs_unmerge_group(&dev->kobj, &pm_qos_flags_attr_group); } +int pm_qos_sysfs_add_latency_tolerance(struct device *dev) +{ + return sysfs_merge_group(&dev->kobj, + &pm_qos_latency_tolerance_attr_group); +} + +void pm_qos_sysfs_remove_latency_tolerance(struct device *dev) +{ + sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_tolerance_attr_group); +} + void rpm_sysfs_remove(struct device *dev) { sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 7b3ae0cffc05..0f65d36c2a75 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -161,6 +161,8 @@ void dev_pm_qos_hide_flags(struct device *dev); int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set); s32 dev_pm_qos_get_user_latency_tolerance(struct device *dev); int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val); +int dev_pm_qos_expose_latency_tolerance(struct device *dev); +void dev_pm_qos_hide_latency_tolerance(struct device *dev); static inline s32 dev_pm_qos_requested_resume_latency(struct device *dev) { @@ -229,6 +231,9 @@ static inline s32 dev_pm_qos_get_user_latency_tolerance(struct device *dev) { return PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; } static inline int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val) { return 0; } +static inline int dev_pm_qos_expose_latency_tolerance(struct device *dev) + { return 0; } +static inline void dev_pm_qos_hide_latency_tolerance(struct device *dev) {} static inline s32 dev_pm_qos_requested_resume_latency(struct device *dev) { return 0; } static inline s32 dev_pm_qos_requested_flags(struct device *dev) { return 0; } -- cgit v1.3-6-gb490 From ddef08dd00f5548f943422c86e4ffe67dd040b6c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 27 Jul 2015 18:03:58 +0300 Subject: Driver core: wakeup the parent device before trying probe If the parent is still suspended when driver probe is attempted, the result may be failure. For example, if the parent is a PCI MFD device that has been suspended when we try to probe our device, any register reads will return 0xffffffff. To fix the problem, making sure the parent is always awake before attempting driver probe. Signed-off-by: Heikki Krogerus Signed-off-by: Rafael J. Wysocki Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/base/dd.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index a638bbb1a27a..2d6df1dd3852 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -399,6 +399,8 @@ EXPORT_SYMBOL_GPL(wait_for_device_probe); * * This function must be called with @dev lock held. When called for a * USB interface, @dev->parent lock must be held as well. + * + * If the device has a parent, runtime-resume the parent before driver probing. */ int driver_probe_device(struct device_driver *drv, struct device *dev) { @@ -410,10 +412,16 @@ int driver_probe_device(struct device_driver *drv, struct device *dev) pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); + if (dev->parent) + pm_runtime_get_sync(dev->parent); + pm_runtime_barrier(dev); ret = really_probe(dev, drv); pm_request_idle(dev); + if (dev->parent) + pm_runtime_put(dev->parent); + return ret; } @@ -507,11 +515,17 @@ static void __device_attach_async_helper(void *_dev, async_cookie_t cookie) device_lock(dev); + if (dev->parent) + pm_runtime_get_sync(dev->parent); + bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); dev_dbg(dev, "async probe completed\n"); pm_request_idle(dev); + if (dev->parent) + pm_runtime_put(dev->parent); + device_unlock(dev); put_device(dev); @@ -541,6 +555,9 @@ static int __device_attach(struct device *dev, bool allow_async) .want_async = false, }; + if (dev->parent) + pm_runtime_get_sync(dev->parent); + ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); if (!ret && allow_async && data.have_async) { @@ -557,6 +574,9 @@ static int __device_attach(struct device *dev, bool allow_async) } else { pm_request_idle(dev); } + + if (dev->parent) + pm_runtime_put(dev->parent); } out_unlock: device_unlock(dev); -- cgit v1.3-6-gb490 From 3d060aeb72113cda0acf906bfe26914fc689506a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 27 Jul 2015 18:04:00 +0300 Subject: driver core: implement device_for_each_child_reverse() The new function device_for_each_child_reverse() is helpful to traverse the registered devices in a reversed order, e.g. in the case when an operation on each device should be done first on the last added device, then on one before last and so on. Signed-off-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman Signed-off-by: Lee Jones --- drivers/base/core.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 2 ++ 2 files changed, 45 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index dafae6d2f7ac..7d6279554afc 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1252,6 +1252,19 @@ void device_unregister(struct device *dev) } EXPORT_SYMBOL_GPL(device_unregister); +static struct device *prev_device(struct klist_iter *i) +{ + struct klist_node *n = klist_prev(i); + struct device *dev = NULL; + struct device_private *p; + + if (n) { + p = to_device_private_parent(n); + dev = p->device; + } + return dev; +} + static struct device *next_device(struct klist_iter *i) { struct klist_node *n = klist_next(i); @@ -1340,6 +1353,36 @@ int device_for_each_child(struct device *parent, void *data, } EXPORT_SYMBOL_GPL(device_for_each_child); +/** + * device_for_each_child_reverse - device child iterator in reversed order. + * @parent: parent struct device. + * @fn: function to be called for each device. + * @data: data for the callback. + * + * Iterate over @parent's child devices, and call @fn for each, + * passing it @data. + * + * We check the return of @fn each time. If it returns anything + * other than 0, we break out and return that value. + */ +int device_for_each_child_reverse(struct device *parent, void *data, + int (*fn)(struct device *dev, void *data)) +{ + struct klist_iter i; + struct device *child; + int error = 0; + + if (!parent->p) + return 0; + + klist_iter_init(&parent->p->klist_children, &i); + while ((child = prev_device(&i)) && !error) + error = fn(child, data); + klist_iter_exit(&i); + return error; +} +EXPORT_SYMBOL_GPL(device_for_each_child_reverse); + /** * device_find_child - device iterator for locating a particular device. * @parent: parent struct device diff --git a/include/linux/device.h b/include/linux/device.h index 5a31bf3a4024..af6fbc35d8a6 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -958,6 +958,8 @@ extern int __must_check device_add(struct device *dev); extern void device_del(struct device *dev); extern int device_for_each_child(struct device *dev, void *data, int (*fn)(struct device *dev, void *data)); +extern int device_for_each_child_reverse(struct device *dev, void *data, + int (*fn)(struct device *dev, void *data)); extern struct device *device_find_child(struct device *dev, void *data, int (*match)(struct device *dev, void *data)); extern int device_rename(struct device *dev, const char *new_name); -- cgit v1.3-6-gb490 From c09fcc4b2b48d58d769a8cff5041533535ece449 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 28 Jul 2015 14:46:16 +0100 Subject: drivers/base: Add MSI domain support for non-PCI devices With the msi_list and the msi_domain properties now being at the generic device level, it is starting to be relatively easy to offer a generic way of providing non-PCI MSIs. The two major hurdles with this idea are: - Lack of global ID that identifies a device: this is worked around by having a global ID allocator for each device that gets enrolled in the platform MSI subsystem - Lack of standard way to write the message in the generating device. This is solved by mandating driver code to provide a write_msg callback, so that everyone can have their own square wheel Apart from that, the API is fairly straightforward: - platform_msi_create_irq_domain creates an MSI domain that gets tagged with DOMAIN_BUS_PLATFORM_MSI - platform_msi_domain_alloc_irqs allocate MSIs for a given device, populating the msi_list - platform_msi_domain_free_irqs does what is written on the tin [ tglx: Created a seperate struct platform_msi_desc and added kerneldoc entries ] Signed-off-by: Marc Zyngier Cc: Cc: Yijing Wang Cc: Ma Jun Cc: Lorenzo Pieralisi Cc: Duc Dang Cc: Hanjun Guo Cc: Bjorn Helgaas Cc: Jiang Liu Cc: Jason Cooper Link: http://lkml.kernel.org/r/1438091186-10244-10-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner --- drivers/base/Makefile | 1 + drivers/base/platform-msi.c | 282 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/msi.h | 22 ++++ 3 files changed, 305 insertions(+) create mode 100644 drivers/base/platform-msi.c (limited to 'drivers/base') diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 527d291706e8..6b2a84e7f2be 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_REGMAP) += regmap/ obj-$(CONFIG_SOC_BUS) += soc.o obj-$(CONFIG_PINCTRL) += pinctrl.o obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o +obj-$(CONFIG_GENERIC_MSI_IRQ_DOMAIN) += platform-msi.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c new file mode 100644 index 000000000000..1857a5dd0816 --- /dev/null +++ b/drivers/base/platform-msi.c @@ -0,0 +1,282 @@ +/* + * MSI framework for platform devices + * + * Copyright (C) 2015 ARM Limited, All Rights Reserved. + * Author: Marc Zyngier + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#define DEV_ID_SHIFT 24 + +/* + * Internal data structure containing a (made up, but unique) devid + * and the callback to write the MSI message. + */ +struct platform_msi_priv_data { + irq_write_msi_msg_t write_msg; + int devid; +}; + +/* The devid allocator */ +static DEFINE_IDA(platform_msi_devid_ida); + +#ifdef GENERIC_MSI_DOMAIN_OPS +/* + * Convert an msi_desc to a globaly unique identifier (per-device + * devid + msi_desc position in the msi_list). + */ +static irq_hw_number_t platform_msi_calc_hwirq(struct msi_desc *desc) +{ + u32 devid; + + devid = desc->platform.msi_priv_data->devid; + + return (devid << (32 - DEV_ID_SHIFT)) | desc->platform.msi_index; +} + +static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) +{ + arg->desc = desc; + arg->hwirq = platform_msi_calc_hwirq(desc); +} + +static int platform_msi_init(struct irq_domain *domain, + struct msi_domain_info *info, + unsigned int virq, irq_hw_number_t hwirq, + msi_alloc_info_t *arg) +{ + struct irq_data *data; + + irq_domain_set_hwirq_and_chip(domain, virq, hwirq, + info->chip, info->chip_data); + + /* + * Save the MSI descriptor in handler_data so that the + * irq_write_msi_msg callback can retrieve it (and the + * associated device). + */ + data = irq_domain_get_irq_data(domain, virq); + data->handler_data = arg->desc; + + return 0; +} +#else +#define platform_msi_set_desc NULL +#define platform_msi_init NULL +#endif + +static void platform_msi_update_dom_ops(struct msi_domain_info *info) +{ + struct msi_domain_ops *ops = info->ops; + + BUG_ON(!ops); + + if (ops->msi_init == NULL) + ops->msi_init = platform_msi_init; + if (ops->set_desc == NULL) + ops->set_desc = platform_msi_set_desc; +} + +static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct msi_desc *desc = irq_data_get_irq_handler_data(data); + struct platform_msi_priv_data *priv_data; + + priv_data = desc->platform.msi_priv_data; + + priv_data->write_msg(desc, msg); +} + +static void platform_msi_update_chip_ops(struct msi_domain_info *info) +{ + struct irq_chip *chip = info->chip; + + BUG_ON(!chip); + if (!chip->irq_mask) + chip->irq_mask = irq_chip_mask_parent; + if (!chip->irq_unmask) + chip->irq_unmask = irq_chip_unmask_parent; + if (!chip->irq_eoi) + chip->irq_eoi = irq_chip_eoi_parent; + if (!chip->irq_set_affinity) + chip->irq_set_affinity = msi_domain_set_affinity; + if (!chip->irq_write_msi_msg) + chip->irq_write_msi_msg = platform_msi_write_msg; +} + +static void platform_msi_free_descs(struct device *dev) +{ + struct msi_desc *desc, *tmp; + + list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) { + list_del(&desc->list); + free_msi_entry(desc); + } +} + +static int platform_msi_alloc_descs(struct device *dev, int nvec, + struct platform_msi_priv_data *data) + +{ + int i; + + for (i = 0; i < nvec; i++) { + struct msi_desc *desc; + + desc = alloc_msi_entry(dev); + if (!desc) + break; + + desc->platform.msi_priv_data = data; + desc->platform.msi_index = i; + desc->nvec_used = 1; + + list_add_tail(&desc->list, dev_to_msi_list(dev)); + } + + if (i != nvec) { + /* Clean up the mess */ + platform_msi_free_descs(dev); + + return -ENOMEM; + } + + return 0; +} + +/** + * platform_msi_create_irq_domain - Create a platform MSI interrupt domain + * @np: Optional device-tree node of the interrupt controller + * @info: MSI domain info + * @parent: Parent irq domain + * + * Updates the domain and chip ops and creates a platform MSI + * interrupt domain. + * + * Returns: + * A domain pointer or NULL in case of failure. + */ +struct irq_domain *platform_msi_create_irq_domain(struct device_node *np, + struct msi_domain_info *info, + struct irq_domain *parent) +{ + struct irq_domain *domain; + + if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) + platform_msi_update_dom_ops(info); + if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) + platform_msi_update_chip_ops(info); + + domain = msi_create_irq_domain(np, info, parent); + if (domain) + domain->bus_token = DOMAIN_BUS_PLATFORM_MSI; + + return domain; +} + +/** + * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev + * @dev: The device for which to allocate interrupts + * @nvec: The number of interrupts to allocate + * @write_msi_msg: Callback to write an interrupt message for @dev + * + * Returns: + * Zero for success, or an error code in case of failure + */ +int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, + irq_write_msi_msg_t write_msi_msg) +{ + struct platform_msi_priv_data *priv_data; + int err; + + /* + * Limit the number of interrupts to 256 per device. Should we + * need to bump this up, DEV_ID_SHIFT should be adjusted + * accordingly (which would impact the max number of MSI + * capable devices). + */ + if (!dev->msi_domain || !write_msi_msg || !nvec || + nvec > (1 << (32 - DEV_ID_SHIFT))) + return -EINVAL; + + if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) { + dev_err(dev, "Incompatible msi_domain, giving up\n"); + return -EINVAL; + } + + /* Already had a helping of MSI? Greed... */ + if (!list_empty(dev_to_msi_list(dev))) + return -EBUSY; + + priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL); + if (!priv_data) + return -ENOMEM; + + priv_data->devid = ida_simple_get(&platform_msi_devid_ida, + 0, 1 << DEV_ID_SHIFT, GFP_KERNEL); + if (priv_data->devid < 0) { + err = priv_data->devid; + goto out_free_data; + } + + priv_data->write_msg = write_msi_msg; + + err = platform_msi_alloc_descs(dev, nvec, priv_data); + if (err) + goto out_free_id; + + err = msi_domain_alloc_irqs(dev->msi_domain, dev, nvec); + if (err) + goto out_free_desc; + + return 0; + +out_free_desc: + platform_msi_free_descs(dev); +out_free_id: + ida_simple_remove(&platform_msi_devid_ida, priv_data->devid); +out_free_data: + kfree(priv_data); + + return err; +} + +/** + * platform_msi_domain_free_irqs - Free MSI interrupts for @dev + * @dev: The device for which to free interrupts + */ +void platform_msi_domain_free_irqs(struct device *dev) +{ + struct msi_desc *desc; + + desc = first_msi_entry(dev); + if (desc) { + struct platform_msi_priv_data *data; + + data = desc->platform.msi_priv_data; + + ida_simple_remove(&platform_msi_devid_ida, data->devid); + kfree(data); + } + + msi_domain_free_irqs(dev->msi_domain, dev); + platform_msi_free_descs(dev); +} diff --git a/include/linux/msi.h b/include/linux/msi.h index f83c87e447bc..809b749f9300 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -15,9 +15,23 @@ extern int pci_msi_ignore_mask; struct irq_data; struct msi_desc; struct pci_dev; +struct platform_msi_priv_data; void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg); +typedef void (*irq_write_msi_msg_t)(struct msi_desc *desc, + struct msi_msg *msg); + +/** + * platform_msi_desc - Platform device specific msi descriptor data + * @msi_priv_data: Pointer to platform private data + * @msi_index: The index of the MSI descriptor for multi MSI + */ +struct platform_msi_desc { + struct platform_msi_priv_data *msi_priv_data; + u16 msi_index; +}; + /** * struct msi_desc - Descriptor structure for MSI based interrupts * @list: List head for management @@ -36,6 +50,7 @@ void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg); * @default_irq:[PCI MSI/X] The default pre-assigned non-MSI irq * @mask_pos: [PCI MSI] Mask register position * @mask_base: [PCI MSI-X] Mask register base address + * @platform: [platform] Platform device specific msi descriptor data */ struct msi_desc { /* Shared device/bus type independent data */ @@ -71,6 +86,7 @@ struct msi_desc { * anonymous for now as it would require an immediate * tree wide cleanup. */ + struct platform_msi_desc platform; }; }; @@ -257,6 +273,12 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain); +struct irq_domain *platform_msi_create_irq_domain(struct device_node *np, + struct msi_domain_info *info, + struct irq_domain *parent); +int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, + irq_write_msi_msg_t write_msi_msg); +void platform_msi_domain_free_irqs(struct device *dev); #endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN -- cgit v1.3-6-gb490 From ba2bbfbf63075850bb523e2adb815d45e3509995 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Jun 2015 15:17:53 +0200 Subject: PM / Domains: Remove intermediate states from the power off sequence Genpd's ->runtime_suspend() (assigned to pm_genpd_runtime_suspend()) doesn't immediately walk the hierarchy of ->runtime_suspend() callbacks. Instead, pm_genpd_runtime_suspend() calls pm_genpd_poweroff() which postpones that until *all* the devices in the genpd are runtime suspended. When pm_genpd_poweroff() discovers that the last device in the genpd is about to be runtime suspended, it calls __pm_genpd_save_device() for *all* the devices in the genpd sequentially. Furthermore, __pm_genpd_save_device() invokes the ->start() callback, walks the hierarchy of the ->runtime_suspend() callbacks and invokes the ->stop() callback. This causes a "thundering herd" problem. Let's address this issue by having pm_genpd_runtime_suspend() immediately walk the hierarchy of the ->runtime_suspend() callbacks, instead of postponing that to the power off sequence via pm_genpd_poweroff(). If the selected ->runtime_suspend() callback doesn't return an error code, call pm_genpd_poweroff() to see if it's feasible to also power off the PM domain. Adopting this change enables us to simplify parts of the code in genpd, for example the locking mechanism. Additionally, it gives some positive side effects, as described below. i) One device's ->runtime_resume() latency is no longer affected by other devices' latencies in a genpd. The complexity genpd has to support the option to abort the power off sequence suffers from latency issues. More precisely, a device that is requested to be runtime resumed, may end up waiting for __pm_genpd_save_device() to complete its operations for *another* device. That's because pm_genpd_poweroff() can't confirm an abort request while it waits for __pm_genpd_save_device() to return. As this patch removes the intermediate states in pm_genpd_poweroff() while powering off the PM domain, we no longer need the ability to abort that sequence. ii) Make pm_runtime[_status]_suspended() reliable when used with genpd. Until the last device in a genpd becomes idle, pm_genpd_runtime_suspend() will return 0 without actually walking the hierarchy of the ->runtime_suspend() callbacks. However, by returning 0 the runtime PM core considers the device as runtime_suspended, so pm_runtime[_status]_suspended() will return true, even though the device isn't (yet) runtime suspended. After this patch, since pm_genpd_runtime_suspend() immediately walks the hierarchy of the ->runtime_suspend() callbacks, pm_runtime[_status]_suspended() will accurately reflect the status of the device. iii) Enable fine-grained PM through runtime PM callbacks in drivers/subsystems. There are currently cases were drivers/subsystems implements runtime PM callbacks to deploy fine-grained PM (e.g. gate clocks, move pinctrl to power-save state, etc.). While using the genpd, pm_genpd_runtime_suspend() postpones invoking these callbacks until *all* the devices in the genpd are runtime suspended. In essence, one runtime resumed device prevents fine-grained PM for other devices within the same genpd. After this patch, since pm_genpd_runtime_suspend() immediately walks the hierarchy of the ->runtime_suspend() callbacks, fine-grained PM is enabled throughout all the levels of runtime PM callbacks. iiii) Enable fine-grained PM for IRQ safe devices Per the definition for an IRQ safe device, its runtime PM callbacks must be able to execute in atomic context. In the path while genpd walks the hierarchy of the ->runtime_suspend() callbacks for the device, it uses a mutex. Therefore, genpd prevents that path to be executed for IRQ safe devices. As this patch changes pm_genpd_runtime_suspend() to immediately walk the hierarchy of the ->runtime_suspend() callbacks and without needing to use a mutex, fine-grained PM is enabled throughout all the levels of runtime PM callbacks for IRQ safe devices. Unfortunately this patch also comes with a drawback, as described in the summary below. Driver's/subsystem's runtime PM callbacks may be invoked even when the genpd hasn't actually powered off the PM domain, potentially introducing unnecessary latency. However, in most cases, saving/restoring register contexts for devices are typically fast operations or can be optimized in device specific ways (e.g. shadow copies of register contents in memory, device-specific checks to see if context has been lost before restoring context, etc.). Still, in some cases the driver/subsystem may suffer from latency if runtime PM is used in a very fine-grained manner (e.g. for each IO request or xfer). To prevent that extra overhead, the driver/subsystem may deploy the runtime PM autosuspend feature. Signed-off-by: Ulf Hansson Reviewed-by: Kevin Hilman Tested-by: Geert Uytterhoeven Tested-by: Lina Iyer Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 363 ++++++++------------------------------------ include/linux/pm_domain.h | 7 - 2 files changed, 62 insertions(+), 308 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 0ee43c1056e0..a1abe16dfe16 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -114,8 +114,12 @@ static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) stop_latency_ns, "stop"); } -static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) +static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev, + bool timed) { + if (!timed) + return GENPD_DEV_CALLBACK(genpd, int, start, dev); + return GENPD_DEV_TIMED_CALLBACK(genpd, int, start, dev, start_latency_ns, "start"); } @@ -136,41 +140,6 @@ static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) smp_mb__after_atomic(); } -static void genpd_acquire_lock(struct generic_pm_domain *genpd) -{ - DEFINE_WAIT(wait); - - mutex_lock(&genpd->lock); - /* - * Wait for the domain to transition into either the active, - * or the power off state. - */ - for (;;) { - prepare_to_wait(&genpd->status_wait_queue, &wait, - TASK_UNINTERRUPTIBLE); - if (genpd->status == GPD_STATE_ACTIVE - || genpd->status == GPD_STATE_POWER_OFF) - break; - mutex_unlock(&genpd->lock); - - schedule(); - - mutex_lock(&genpd->lock); - } - finish_wait(&genpd->status_wait_queue, &wait); -} - -static void genpd_release_lock(struct generic_pm_domain *genpd) -{ - mutex_unlock(&genpd->lock); -} - -static void genpd_set_active(struct generic_pm_domain *genpd) -{ - if (genpd->resume_count == 0) - genpd->status = GPD_STATE_ACTIVE; -} - static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) { s64 usecs64; @@ -251,35 +220,14 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed) * resume a device belonging to it. */ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) - __releases(&genpd->lock) __acquires(&genpd->lock) { struct gpd_link *link; - DEFINE_WAIT(wait); int ret = 0; - /* If the domain's master is being waited for, we have to wait too. */ - for (;;) { - prepare_to_wait(&genpd->status_wait_queue, &wait, - TASK_UNINTERRUPTIBLE); - if (genpd->status != GPD_STATE_WAIT_MASTER) - break; - mutex_unlock(&genpd->lock); - - schedule(); - - mutex_lock(&genpd->lock); - } - finish_wait(&genpd->status_wait_queue, &wait); - if (genpd->status == GPD_STATE_ACTIVE || (genpd->prepared_count > 0 && genpd->suspend_power_off)) return 0; - if (genpd->status != GPD_STATE_POWER_OFF) { - genpd_set_active(genpd); - return 0; - } - if (genpd->cpuidle_data) { cpuidle_pause_and_lock(); genpd->cpuidle_data->idle_state->disabled = true; @@ -294,20 +242,8 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) */ list_for_each_entry(link, &genpd->slave_links, slave_node) { genpd_sd_counter_inc(link->master); - genpd->status = GPD_STATE_WAIT_MASTER; - - mutex_unlock(&genpd->lock); ret = pm_genpd_poweron(link->master); - - mutex_lock(&genpd->lock); - - /* - * The "wait for parent" status is guaranteed not to change - * while the master is powering on. - */ - genpd->status = GPD_STATE_POWER_OFF; - wake_up_all(&genpd->status_wait_queue); if (ret) { genpd_sd_counter_dec(link->master); goto err; @@ -319,8 +255,7 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) goto err; out: - genpd_set_active(genpd); - + genpd->status = GPD_STATE_ACTIVE; return 0; err: @@ -356,20 +291,18 @@ int pm_genpd_name_poweron(const char *domain_name) return genpd ? pm_genpd_poweron(genpd) : -EINVAL; } -static int genpd_start_dev_no_timing(struct generic_pm_domain *genpd, - struct device *dev) -{ - return GENPD_DEV_CALLBACK(genpd, int, start, dev); -} - static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) { return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev, save_state_latency_ns, "state save"); } -static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev) +static int genpd_restore_dev(struct generic_pm_domain *genpd, + struct device *dev, bool timed) { + if (!timed) + return GENPD_DEV_CALLBACK(genpd, int, restore_state, dev); + return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev, restore_state_latency_ns, "state restore"); @@ -415,89 +348,6 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, return NOTIFY_DONE; } -/** - * __pm_genpd_save_device - Save the pre-suspend state of a device. - * @pdd: Domain data of the device to save the state of. - * @genpd: PM domain the device belongs to. - */ -static int __pm_genpd_save_device(struct pm_domain_data *pdd, - struct generic_pm_domain *genpd) - __releases(&genpd->lock) __acquires(&genpd->lock) -{ - struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); - struct device *dev = pdd->dev; - int ret = 0; - - if (gpd_data->need_restore > 0) - return 0; - - /* - * If the value of the need_restore flag is still unknown at this point, - * we trust that pm_genpd_poweroff() has verified that the device is - * already runtime PM suspended. - */ - if (gpd_data->need_restore < 0) { - gpd_data->need_restore = 1; - return 0; - } - - mutex_unlock(&genpd->lock); - - genpd_start_dev(genpd, dev); - ret = genpd_save_dev(genpd, dev); - genpd_stop_dev(genpd, dev); - - mutex_lock(&genpd->lock); - - if (!ret) - gpd_data->need_restore = 1; - - return ret; -} - -/** - * __pm_genpd_restore_device - Restore the pre-suspend state of a device. - * @pdd: Domain data of the device to restore the state of. - * @genpd: PM domain the device belongs to. - */ -static void __pm_genpd_restore_device(struct pm_domain_data *pdd, - struct generic_pm_domain *genpd) - __releases(&genpd->lock) __acquires(&genpd->lock) -{ - struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); - struct device *dev = pdd->dev; - int need_restore = gpd_data->need_restore; - - gpd_data->need_restore = 0; - mutex_unlock(&genpd->lock); - - genpd_start_dev(genpd, dev); - - /* - * Call genpd_restore_dev() for recently added devices too (need_restore - * is negative then). - */ - if (need_restore) - genpd_restore_dev(genpd, dev); - - mutex_lock(&genpd->lock); -} - -/** - * genpd_abort_poweroff - Check if a PM domain power off should be aborted. - * @genpd: PM domain to check. - * - * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during - * a "power off" operation, which means that a "power on" has occured in the - * meantime, or if its resume_count field is different from zero, which means - * that one of its devices has been resumed in the meantime. - */ -static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) -{ - return genpd->status == GPD_STATE_WAIT_MASTER - || genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0; -} - /** * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). * @genpd: PM domait to power off. @@ -515,34 +365,26 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) * @genpd: PM domain to power down. * * If all of the @genpd's devices have been suspended and all of its subdomains - * have been powered down, run the runtime suspend callbacks provided by all of - * the @genpd's devices' drivers and remove power from @genpd. + * have been powered down, remove power from @genpd. */ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) - __releases(&genpd->lock) __acquires(&genpd->lock) { struct pm_domain_data *pdd; struct gpd_link *link; - unsigned int not_suspended; - int ret = 0; + unsigned int not_suspended = 0; - start: /* * Do not try to power off the domain in the following situations: * (1) The domain is already in the "power off" state. - * (2) The domain is waiting for its master to power up. - * (3) One of the domain's devices is being resumed right now. - * (4) System suspend is in progress. + * (2) System suspend is in progress. */ if (genpd->status == GPD_STATE_POWER_OFF - || genpd->status == GPD_STATE_WAIT_MASTER - || genpd->resume_count > 0 || genpd->prepared_count > 0) + || genpd->prepared_count > 0) return 0; if (atomic_read(&genpd->sd_count) > 0) return -EBUSY; - not_suspended = 0; list_for_each_entry(pdd, &genpd->dev_list, list_node) { enum pm_qos_flags_status stat; @@ -560,41 +402,11 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) if (not_suspended > genpd->in_progress) return -EBUSY; - if (genpd->poweroff_task) { - /* - * Another instance of pm_genpd_poweroff() is executing - * callbacks, so tell it to start over and return. - */ - genpd->status = GPD_STATE_REPEAT; - return 0; - } - if (genpd->gov && genpd->gov->power_down_ok) { if (!genpd->gov->power_down_ok(&genpd->domain)) return -EAGAIN; } - genpd->status = GPD_STATE_BUSY; - genpd->poweroff_task = current; - - list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) { - ret = atomic_read(&genpd->sd_count) == 0 ? - __pm_genpd_save_device(pdd, genpd) : -EBUSY; - - if (genpd_abort_poweroff(genpd)) - goto out; - - if (ret) { - genpd_set_active(genpd); - goto out; - } - - if (genpd->status == GPD_STATE_REPEAT) { - genpd->poweroff_task = NULL; - goto start; - } - } - if (genpd->cpuidle_data) { /* * If cpuidle_data is set, cpuidle should turn the domain off @@ -607,14 +419,14 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) cpuidle_pause_and_lock(); genpd->cpuidle_data->idle_state->disabled = false; cpuidle_resume_and_unlock(); - goto out; + return 0; } if (genpd->power_off) { - if (atomic_read(&genpd->sd_count) > 0) { - ret = -EBUSY; - goto out; - } + int ret; + + if (atomic_read(&genpd->sd_count) > 0) + return -EBUSY; /* * If sd_count > 0 at this point, one of the subdomains hasn't @@ -625,10 +437,8 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) * happen very often). */ ret = genpd_power_off(genpd, true); - if (ret == -EBUSY) { - genpd_set_active(genpd); - goto out; - } + if (ret) + return ret; } genpd->status = GPD_STATE_POWER_OFF; @@ -638,10 +448,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) genpd_queue_power_off_work(link->master); } - out: - genpd->poweroff_task = NULL; - wake_up_all(&genpd->status_wait_queue); - return ret; + return 0; } /** @@ -654,9 +461,9 @@ static void genpd_power_off_work_fn(struct work_struct *work) genpd = container_of(work, struct generic_pm_domain, power_off_work); - genpd_acquire_lock(genpd); + mutex_lock(&genpd->lock); pm_genpd_poweroff(genpd); - genpd_release_lock(genpd); + mutex_unlock(&genpd->lock); } /** @@ -670,7 +477,6 @@ static void genpd_power_off_work_fn(struct work_struct *work) static int pm_genpd_runtime_suspend(struct device *dev) { struct generic_pm_domain *genpd; - struct generic_pm_domain_data *gpd_data; bool (*stop_ok)(struct device *__dev); int ret; @@ -684,10 +490,16 @@ static int pm_genpd_runtime_suspend(struct device *dev) if (stop_ok && !stop_ok(dev)) return -EBUSY; - ret = genpd_stop_dev(genpd, dev); + ret = genpd_save_dev(genpd, dev); if (ret) return ret; + ret = genpd_stop_dev(genpd, dev); + if (ret) { + genpd_restore_dev(genpd, dev, true); + return ret; + } + /* * If power.irq_safe is set, this routine will be run with interrupts * off, so it can't use mutexes. @@ -696,16 +508,6 @@ static int pm_genpd_runtime_suspend(struct device *dev) return 0; mutex_lock(&genpd->lock); - - /* - * If we have an unknown state of the need_restore flag, it means none - * of the runtime PM callbacks has been invoked yet. Let's update the - * flag to reflect that the current state is active. - */ - gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); - if (gpd_data->need_restore < 0) - gpd_data->need_restore = 0; - genpd->in_progress++; pm_genpd_poweroff(genpd); genpd->in_progress--; @@ -725,8 +527,8 @@ static int pm_genpd_runtime_suspend(struct device *dev) static int pm_genpd_runtime_resume(struct device *dev) { struct generic_pm_domain *genpd; - DEFINE_WAIT(wait); int ret; + bool timed = true; dev_dbg(dev, "%s()\n", __func__); @@ -735,39 +537,21 @@ static int pm_genpd_runtime_resume(struct device *dev) return -EINVAL; /* If power.irq_safe, the PM domain is never powered off. */ - if (dev->power.irq_safe) - return genpd_start_dev_no_timing(genpd, dev); + if (dev->power.irq_safe) { + timed = false; + goto out; + } mutex_lock(&genpd->lock); ret = __pm_genpd_poweron(genpd); - if (ret) { - mutex_unlock(&genpd->lock); - return ret; - } - genpd->status = GPD_STATE_BUSY; - genpd->resume_count++; - for (;;) { - prepare_to_wait(&genpd->status_wait_queue, &wait, - TASK_UNINTERRUPTIBLE); - /* - * If current is the powering off task, we have been called - * reentrantly from one of the device callbacks, so we should - * not wait. - */ - if (!genpd->poweroff_task || genpd->poweroff_task == current) - break; - mutex_unlock(&genpd->lock); + mutex_unlock(&genpd->lock); - schedule(); + if (ret) + return ret; - mutex_lock(&genpd->lock); - } - finish_wait(&genpd->status_wait_queue, &wait); - __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd); - genpd->resume_count--; - genpd_set_active(genpd); - wake_up_all(&genpd->status_wait_queue); - mutex_unlock(&genpd->lock); + out: + genpd_start_dev(genpd, dev, timed); + genpd_restore_dev(genpd, dev, timed); return 0; } @@ -883,7 +667,7 @@ static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd, { struct gpd_link *link; - if (genpd->status != GPD_STATE_POWER_OFF) + if (genpd->status == GPD_STATE_ACTIVE) return; list_for_each_entry(link, &genpd->slave_links, slave_node) { @@ -960,14 +744,14 @@ static int pm_genpd_prepare(struct device *dev) if (resume_needed(dev, genpd)) pm_runtime_resume(dev); - genpd_acquire_lock(genpd); + mutex_lock(&genpd->lock); if (genpd->prepared_count++ == 0) { genpd->suspended_count = 0; genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF; } - genpd_release_lock(genpd); + mutex_unlock(&genpd->lock); if (genpd->suspend_power_off) { pm_runtime_put_noidle(dev); @@ -1102,7 +886,7 @@ static int pm_genpd_resume_noirq(struct device *dev) pm_genpd_sync_poweron(genpd, true); genpd->suspended_count--; - return genpd_start_dev(genpd, dev); + return genpd_start_dev(genpd, dev, true); } /** @@ -1230,7 +1014,7 @@ static int pm_genpd_thaw_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev); + return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev, true); } /** @@ -1324,7 +1108,7 @@ static int pm_genpd_restore_noirq(struct device *dev) pm_genpd_sync_poweron(genpd, true); - return genpd_start_dev(genpd, dev); + return genpd_start_dev(genpd, dev, true); } /** @@ -1440,7 +1224,6 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, gpd_data->td = *td; gpd_data->base.dev = dev; - gpd_data->need_restore = -1; gpd_data->td.constraint_changed = true; gpd_data->td.effective_constraint_ns = -1; gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; @@ -1502,7 +1285,7 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, if (IS_ERR(gpd_data)) return PTR_ERR(gpd_data); - genpd_acquire_lock(genpd); + mutex_lock(&genpd->lock); if (genpd->prepared_count > 0) { ret = -EAGAIN; @@ -1519,7 +1302,7 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); out: - genpd_release_lock(genpd); + mutex_unlock(&genpd->lock); if (ret) genpd_free_dev_data(dev, gpd_data); @@ -1563,7 +1346,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, gpd_data = to_gpd_data(pdd); dev_pm_qos_remove_notifier(dev, &gpd_data->nb); - genpd_acquire_lock(genpd); + mutex_lock(&genpd->lock); if (genpd->prepared_count > 0) { ret = -EAGAIN; @@ -1578,14 +1361,14 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, list_del_init(&pdd->list_node); - genpd_release_lock(genpd); + mutex_unlock(&genpd->lock); genpd_free_dev_data(dev, gpd_data); return 0; out: - genpd_release_lock(genpd); + mutex_unlock(&genpd->lock); dev_pm_qos_add_notifier(dev, &gpd_data->nb); return ret; @@ -1606,17 +1389,9 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, || genpd == subdomain) return -EINVAL; - start: - genpd_acquire_lock(genpd); + mutex_lock(&genpd->lock); mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); - if (subdomain->status != GPD_STATE_POWER_OFF - && subdomain->status != GPD_STATE_ACTIVE) { - mutex_unlock(&subdomain->lock); - genpd_release_lock(genpd); - goto start; - } - if (genpd->status == GPD_STATE_POWER_OFF && subdomain->status != GPD_STATE_POWER_OFF) { ret = -EINVAL; @@ -1644,7 +1419,7 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, out: mutex_unlock(&subdomain->lock); - genpd_release_lock(genpd); + mutex_unlock(&genpd->lock); return ret; } @@ -1692,8 +1467,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) return -EINVAL; - start: - genpd_acquire_lock(genpd); + mutex_lock(&genpd->lock); list_for_each_entry(link, &genpd->master_links, master_node) { if (link->slave != subdomain) @@ -1701,13 +1475,6 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); - if (subdomain->status != GPD_STATE_POWER_OFF - && subdomain->status != GPD_STATE_ACTIVE) { - mutex_unlock(&subdomain->lock); - genpd_release_lock(genpd); - goto start; - } - list_del(&link->master_node); list_del(&link->slave_node); kfree(link); @@ -1720,7 +1487,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, break; } - genpd_release_lock(genpd); + mutex_unlock(&genpd->lock); return ret; } @@ -1744,7 +1511,7 @@ int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) if (IS_ERR_OR_NULL(genpd) || state < 0) return -EINVAL; - genpd_acquire_lock(genpd); + mutex_lock(&genpd->lock); if (genpd->cpuidle_data) { ret = -EEXIST; @@ -1775,7 +1542,7 @@ int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) genpd_recalc_cpu_exit_latency(genpd); out: - genpd_release_lock(genpd); + mutex_unlock(&genpd->lock); return ret; err: @@ -1812,7 +1579,7 @@ int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd) if (IS_ERR_OR_NULL(genpd)) return -EINVAL; - genpd_acquire_lock(genpd); + mutex_lock(&genpd->lock); cpuidle_data = genpd->cpuidle_data; if (!cpuidle_data) { @@ -1830,7 +1597,7 @@ int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd) kfree(cpuidle_data); out: - genpd_release_lock(genpd); + mutex_unlock(&genpd->lock); return ret; } @@ -1912,9 +1679,6 @@ void pm_genpd_init(struct generic_pm_domain *genpd, genpd->in_progress = 0; atomic_set(&genpd->sd_count, 0); genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; - init_waitqueue_head(&genpd->status_wait_queue); - genpd->poweroff_task = NULL; - genpd->resume_count = 0; genpd->device_count = 0; genpd->max_off_time_ns = -1; genpd->max_off_time_changed = true; @@ -2293,9 +2057,6 @@ static int pm_genpd_summary_one(struct seq_file *s, { static const char * const status_lookup[] = { [GPD_STATE_ACTIVE] = "on", - [GPD_STATE_WAIT_MASTER] = "wait-master", - [GPD_STATE_BUSY] = "busy", - [GPD_STATE_REPEAT] = "off-in-progress", [GPD_STATE_POWER_OFF] = "off" }; struct pm_domain_data *pm_data; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 681ccb053f72..b2725e6e8e7b 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -22,9 +22,6 @@ enum gpd_status { GPD_STATE_ACTIVE = 0, /* PM domain is active */ - GPD_STATE_WAIT_MASTER, /* PM domain's master is being waited for */ - GPD_STATE_BUSY, /* Something is happening to the PM domain */ - GPD_STATE_REPEAT, /* Power off in progress, to be repeated */ GPD_STATE_POWER_OFF, /* PM domain is off */ }; @@ -59,9 +56,6 @@ struct generic_pm_domain { unsigned int in_progress; /* Number of devices being suspended now */ atomic_t sd_count; /* Number of subdomains with power "on" */ enum gpd_status status; /* Current state of the domain */ - wait_queue_head_t status_wait_queue; - struct task_struct *poweroff_task; /* Powering off task */ - unsigned int resume_count; /* Number of devices being resumed */ unsigned int device_count; /* Number of devices */ unsigned int suspended_count; /* System suspend device counter */ unsigned int prepared_count; /* Suspend counter of prepared devices */ @@ -113,7 +107,6 @@ struct generic_pm_domain_data { struct pm_domain_data base; struct gpd_timing_data td; struct notifier_block nb; - int need_restore; }; #ifdef CONFIG_PM_GENERIC_DOMAINS -- cgit v1.3-6-gb490 From 311fa6adf92c5110057daa439fdaff012864aa2b Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 31 Jul 2015 10:20:00 +0100 Subject: PM / Domains: Return -EPROBE_DEFER if we fail to init or turn-on domain When a device is probed, the function dev_pm_domain_attach() is called to see if there is a power-domain that is associated with the device and needs to be turned on. If dev_pm_domain_attach() does not return -EPROBE_DEFER then the device will be probed. For devices using genpd, dev_pm_domain_attach() will call genpd_dev_pm_attach(). If genpd_dev_pm_attach() does not find a power domain associated with the device then it returns an error code not equal to -EPROBE_DEFER to allow the device to be probed. However, if genpd_dev_pm_attach() does find a power-domain that is associated with the device, then it does not return -EPROBE_DEFER on failure and hence the device will still be probed. Furthermore, genpd_dev_pm_attach() does not check the error code returned by pm_genpd_poweron() to see if the power-domain was turned on successfully. Fix this by checking the return code from pm_genpd_poweron() and returning -EPROBE_DEFER from genpd_dev_pm_attach on failure, if there is a power-domain associated with the device. Signed-off-by: Jon Hunter Acked-by: Ulf Hansson Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index a1abe16dfe16..7666a1cbaf95 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1947,7 +1947,10 @@ static void genpd_dev_pm_sync(struct device *dev) * Both generic and legacy Samsung-specific DT bindings are supported to keep * backwards compatibility with existing DTBs. * - * Returns 0 on successfully attached PM domain or negative error code. + * Returns 0 on successfully attached PM domain or negative error code. Note + * that if a power-domain exists for the device, but it cannot be found or + * turned on, then return -EPROBE_DEFER to ensure that the device is not + * probed and to re-try again later. */ int genpd_dev_pm_attach(struct device *dev) { @@ -1984,7 +1987,7 @@ int genpd_dev_pm_attach(struct device *dev) dev_dbg(dev, "%s() failed to find PM domain: %ld\n", __func__, PTR_ERR(pd)); of_node_put(dev->of_node); - return PTR_ERR(pd); + return -EPROBE_DEFER; } dev_dbg(dev, "adding to PM domain %s\n", pd->name); @@ -2002,14 +2005,15 @@ int genpd_dev_pm_attach(struct device *dev) dev_err(dev, "failed to add to PM domain %s: %d", pd->name, ret); of_node_put(dev->of_node); - return ret; + goto out; } dev->pm_domain->detach = genpd_dev_pm_detach; dev->pm_domain->sync = genpd_dev_pm_sync; - pm_genpd_poweron(pd); + ret = pm_genpd_poweron(pd); - return 0; +out: + return ret ? -EPROBE_DEFER : 0; } EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ -- cgit v1.3-6-gb490 From 64526370d11ce8868ca495723d595b61e8697fbf Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 15 Jul 2015 10:29:00 +0900 Subject: devres: fix devres_get() Currently, devres_get() passes devres_free() the pointer to devres, but devres_free() should be given with the pointer to resource data. Fixes: 9ac7849e35f7 ("devres: device resource management") Signed-off-by: Masahiro Yamada Acked-by: Tejun Heo Cc: stable # 2.6.21+ Signed-off-by: Greg Kroah-Hartman --- drivers/base/devres.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/devres.c b/drivers/base/devres.c index c8a53d1e019f..875464690117 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -297,10 +297,10 @@ void * devres_get(struct device *dev, void *new_res, if (!dr) { add_dr(dev, &new_dr->node); dr = new_dr; - new_dr = NULL; + new_res = NULL; } spin_unlock_irqrestore(&dev->devres_lock, flags); - devres_free(new_dr); + devres_free(new_res); return dr->data; } -- cgit v1.3-6-gb490 From a885de67157e8e65b92af2e0a77f6eadd112d0b7 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Wed, 29 Jul 2015 23:26:28 +0300 Subject: firmware: fix wrong memory deallocation in fw_add_devm_name() Device resource data allocated with devres_alloc() must be deallocated by devres_free(). Signed-off-by: Vladimir Zapolskiy Acked-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 894bda114224..8524450e75bd 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -443,7 +443,7 @@ static int fw_add_devm_name(struct device *dev, const char *name) return -ENOMEM; fwn->name = kstrdup_const(name, GFP_KERNEL); if (!fwn->name) { - kfree(fwn); + devres_free(fwn); return -ENOMEM; } -- cgit v1.3-6-gb490 From eda5867b6992e3de888b516c0ff0fa1f1ee881af Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sun, 19 Jul 2015 20:06:21 +0200 Subject: cpu: Remove bogus __ref annotation of cpu_subsys_online() In commit 0db0628d9012 ("kernel: delete __cpuinit usage from all core kernel files") cpu_up() lost its __cpuinit annotation, vanishing the need for cpu_subsys_online() to have a __ref annotation. Just drop it to be able to catch real section mismatches in the future. Signed-off-by: Mathias Krause Cc: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman --- drivers/base/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 78720e706176..91bbb1959d8d 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -41,7 +41,7 @@ static void change_cpu_under_node(struct cpu *cpu, cpu->node_id = to_nid; } -static int __ref cpu_subsys_online(struct device *dev) +static int cpu_subsys_online(struct device *dev) { struct cpu *cpu = container_of(dev, struct cpu, dev); int cpuid = dev->id; -- cgit v1.3-6-gb490 From ecc87eed7beeb50c0be0b73322d62135277ea2b0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Aug 2015 16:51:11 +0300 Subject: device property: fix potential NULL pointer dereference In device_add_property_set() we check pset parameter for a NULL, but few lines later we do a pointer arithmetic without check that will crash kernel in the set_secondary_fwnode(). Here we check if pset parameter is NULL and return immediately. Fixes: 16ba08d5c9ec (device property: Introduce firmware node type for platform data) Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/base/property.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/property.c b/drivers/base/property.c index f3f6d167f3f1..37a7bb7b239d 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -27,9 +27,10 @@ */ void device_add_property_set(struct device *dev, struct property_set *pset) { - if (pset) - pset->fwnode.type = FWNODE_PDATA; + if (!pset) + return; + pset->fwnode.type = FWNODE_PDATA; set_secondary_fwnode(dev, &pset->fwnode); } EXPORT_SYMBOL_GPL(device_add_property_set); -- cgit v1.3-6-gb490 From 82b2c3c5b838b4fac9471eab320670aff5a822e0 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Mon, 29 Jun 2015 16:59:02 +0200 Subject: driver core: fix docbook for device_private.device This field refers to the public device struct, not to classes. Signed-off-by: Tomeu Vizoso Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index fd3347d9f153..29c985eb9f4c 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -63,7 +63,7 @@ struct driver_private { * binding of drivers which were unable to get all the resources needed by * the device; typically because it depends on another driver getting * probed first. - * @device - pointer back to the struct class that this structure is + * @device - pointer back to the struct device that this structure is * associated with. * * Nothing outside of the driver core should ever touch these fields. -- cgit v1.3-6-gb490 From 52cdbdd49853dfa856082edb0f4c4c0249d9df07 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Mon, 27 Jul 2015 20:43:01 +0300 Subject: driver core: correct device's shutdown order Now device's shutdown sequence is performed in reverse order of their registration in devices_kset list and this sequence corresponds to the reverse device's creation order. So, devices_kset data tracks "parent<-child" device's dependencies only. Unfortunately, that's not enough and causes problems in case of implementing board's specific shutdown procedures. For example [1]: "DRA7XX_evm uses PCF8575 and one of the PCF output lines feeds to MMC/SD and this line should be driven high in order for the MMC/SD to be detected. This line is modelled as regulator and the hsmmc driver takes care of enabling and disabling it. In the case of 'reboot', during shutdown path as part of it's cleanup process the hsmmc driver disables this regulator. This makes MMC boot not functional." To handle this issue the .shutdown() callback could be implemented for PCF8575 device where corresponding GPIO pins will be configured to states, required for correct warm/cold reset. This can be achieved only when all .shutdown() callbacks have been called already for all PCF8575's consumers. But devices_kset is not filled correctly now: devices_kset: Device61 4e000000.dmm devices_kset: Device62 48070000.i2c devices_kset: Device63 48072000.i2c devices_kset: Device64 48060000.i2c devices_kset: Device65 4809c000.mmc ... devices_kset: Device102 fixedregulator-sd ... devices_kset: Device181 0-0020 // PCF8575 devices_kset: Device182 gpiochip496 devices_kset: Device183 0-0021 // PCF8575 devices_kset: Device184 gpiochip480 As can be seen from above .shutdown() callback for PCF8575 will be called before its consumers, which, in turn means, that any changes of PCF8575 GPIO's pins will be or unsafe or overwritten later by GPIO's consumers. The problem can be solved if devices_kset list will be filled not only according device creation order, but also according device's probing order to track "supplier<-consumer" dependencies also. Hence, as a fix, lets add devices_kset_move_last(), devices_kset_move_before(), devices_kset_move_after() and call them from device_move() and also add call of devices_kset_move_last() in really_probe(). After this change all entries in devices_kset will be sorted according to device's creation ("parent<-child") and probing ("supplier<-consumer") order. devices_kset after: devices_kset: Device121 48070000.i2c devices_kset: Device122 i2c-0 ... devices_kset: Device147 regulator.24 devices_kset: Device148 0-0020 devices_kset: Device149 gpiochip496 devices_kset: Device150 0-0021 devices_kset: Device151 gpiochip480 devices_kset: Device152 0-0019 ... devices_kset: Device372 fixedregulator-sd devices_kset: Device373 regulator.29 devices_kset: Device374 4809c000.mmc devices_kset: Device375 mmc0 [1] http://www.spinics.net/lists/linux-mmc/msg29825.html Cc: Sekhar Nori Signed-off-by: Grygorii Strashko Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 1 + drivers/base/core.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/base/dd.c | 8 ++++++++ 3 files changed, 58 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 29c985eb9f4c..1782f3aa386e 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -134,6 +134,7 @@ extern int devres_release_all(struct device *dev); /* /sys/devices directory */ extern struct kset *devices_kset; +extern void devices_kset_move_last(struct device *dev); #if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS) extern void module_add_driver(struct module *mod, struct device_driver *drv); diff --git a/drivers/base/core.c b/drivers/base/core.c index dafae6d2f7ac..fc5a558f62f9 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -533,6 +533,52 @@ static DEVICE_ATTR_RO(dev); /* /sys/devices/ */ struct kset *devices_kset; +/** + * devices_kset_move_before - Move device in the devices_kset's list. + * @deva: Device to move. + * @devb: Device @deva should come before. + */ +static void devices_kset_move_before(struct device *deva, struct device *devb) +{ + if (!devices_kset) + return; + pr_debug("devices_kset: Moving %s before %s\n", + dev_name(deva), dev_name(devb)); + spin_lock(&devices_kset->list_lock); + list_move_tail(&deva->kobj.entry, &devb->kobj.entry); + spin_unlock(&devices_kset->list_lock); +} + +/** + * devices_kset_move_after - Move device in the devices_kset's list. + * @deva: Device to move + * @devb: Device @deva should come after. + */ +static void devices_kset_move_after(struct device *deva, struct device *devb) +{ + if (!devices_kset) + return; + pr_debug("devices_kset: Moving %s after %s\n", + dev_name(deva), dev_name(devb)); + spin_lock(&devices_kset->list_lock); + list_move(&deva->kobj.entry, &devb->kobj.entry); + spin_unlock(&devices_kset->list_lock); +} + +/** + * devices_kset_move_last - move the device to the end of devices_kset's list. + * @dev: device to move + */ +void devices_kset_move_last(struct device *dev) +{ + if (!devices_kset) + return; + pr_debug("devices_kset: Moving %s to end of list\n", dev_name(dev)); + spin_lock(&devices_kset->list_lock); + list_move_tail(&dev->kobj.entry, &devices_kset->list); + spin_unlock(&devices_kset->list_lock); +} + /** * device_create_file - create sysfs attribute file for device. * @dev: device. @@ -1923,12 +1969,15 @@ int device_move(struct device *dev, struct device *new_parent, break; case DPM_ORDER_DEV_AFTER_PARENT: device_pm_move_after(dev, new_parent); + devices_kset_move_after(dev, new_parent); break; case DPM_ORDER_PARENT_BEFORE_DEV: device_pm_move_before(new_parent, dev); + devices_kset_move_before(new_parent, dev); break; case DPM_ORDER_DEV_LAST: device_pm_move_last(dev); + devices_kset_move_last(dev); break; } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index a638bbb1a27a..cc2b1d4801fd 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -304,6 +304,14 @@ static int really_probe(struct device *dev, struct device_driver *drv) goto probe_failed; } + /* + * Ensure devices are listed in devices_kset in correct order + * It's important to move Dev to the end of devices_kset before + * calling .probe, because it could be recursive and parent Dev + * should always go first + */ + devices_kset_move_last(dev); + if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) -- cgit v1.3-6-gb490 From 737002b5de3d15b3012feea17f782a628b24699b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Jul 2015 16:22:58 +0530 Subject: PM / OPP: Relocate few routines In order to prepare for the later commits, this relocates few routines towards the top as they will be used earlier in the code. Reviewed-by: Stephen Boyd Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 277 ++++++++++++++++++++++++----------------------- 1 file changed, 139 insertions(+), 138 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 677fb2843553..8c3fd57975fb 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -437,6 +437,102 @@ static struct device_opp *_add_device_opp(struct device *dev) return dev_opp; } +/** + * _kfree_device_rcu() - Free device_opp RCU handler + * @head: RCU head + */ +static void _kfree_device_rcu(struct rcu_head *head) +{ + struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head); + + kfree_rcu(device_opp, rcu_head); +} + +/** + * _kfree_opp_rcu() - Free OPP RCU handler + * @head: RCU head + */ +static void _kfree_opp_rcu(struct rcu_head *head) +{ + struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head); + + kfree_rcu(opp, rcu_head); +} + +/** + * _opp_remove() - Remove an OPP from a table definition + * @dev_opp: points back to the device_opp struct this opp belongs to + * @opp: pointer to the OPP to remove + * + * This function removes an opp definition from the opp list. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * It is assumed that the caller holds required mutex for an RCU updater + * strategy. + */ +static void _opp_remove(struct device_opp *dev_opp, + struct dev_pm_opp *opp) +{ + /* + * Notify the changes in the availability of the operable + * frequency/voltage list. + */ + srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp); + list_del_rcu(&opp->node); + call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); + + if (list_empty(&dev_opp->opp_list)) { + list_del_rcu(&dev_opp->node); + call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head, + _kfree_device_rcu); + } +} + +/** + * dev_pm_opp_remove() - Remove an OPP from OPP list + * @dev: device for which we do this operation + * @freq: OPP to remove with matching 'freq' + * + * This function removes an opp from the opp list. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +void dev_pm_opp_remove(struct device *dev, unsigned long freq) +{ + struct dev_pm_opp *opp; + struct device_opp *dev_opp; + bool found = false; + + /* Hold our list modification lock here */ + mutex_lock(&dev_opp_list_lock); + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) + goto unlock; + + list_for_each_entry(opp, &dev_opp->opp_list, node) { + if (opp->rate == freq) { + found = true; + break; + } + } + + if (!found) { + dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n", + __func__, freq); + goto unlock; + } + + _opp_remove(dev_opp, opp); +unlock: + mutex_unlock(&dev_opp_list_lock); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_remove); + /** * _opp_add_dynamic() - Allocate a dynamic OPP. * @dev: device for which we do this operation @@ -569,102 +665,6 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) } EXPORT_SYMBOL_GPL(dev_pm_opp_add); -/** - * _kfree_opp_rcu() - Free OPP RCU handler - * @head: RCU head - */ -static void _kfree_opp_rcu(struct rcu_head *head) -{ - struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head); - - kfree_rcu(opp, rcu_head); -} - -/** - * _kfree_device_rcu() - Free device_opp RCU handler - * @head: RCU head - */ -static void _kfree_device_rcu(struct rcu_head *head) -{ - struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head); - - kfree_rcu(device_opp, rcu_head); -} - -/** - * _opp_remove() - Remove an OPP from a table definition - * @dev_opp: points back to the device_opp struct this opp belongs to - * @opp: pointer to the OPP to remove - * - * This function removes an opp definition from the opp list. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * It is assumed that the caller holds required mutex for an RCU updater - * strategy. - */ -static void _opp_remove(struct device_opp *dev_opp, - struct dev_pm_opp *opp) -{ - /* - * Notify the changes in the availability of the operable - * frequency/voltage list. - */ - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp); - list_del_rcu(&opp->node); - call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); - - if (list_empty(&dev_opp->opp_list)) { - list_del_rcu(&dev_opp->node); - call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head, - _kfree_device_rcu); - } -} - -/** - * dev_pm_opp_remove() - Remove an OPP from OPP list - * @dev: device for which we do this operation - * @freq: OPP to remove with matching 'freq' - * - * This function removes an opp from the opp list. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - */ -void dev_pm_opp_remove(struct device *dev, unsigned long freq) -{ - struct dev_pm_opp *opp; - struct device_opp *dev_opp; - bool found = false; - - /* Hold our list modification lock here */ - mutex_lock(&dev_opp_list_lock); - - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) - goto unlock; - - list_for_each_entry(opp, &dev_opp->opp_list, node) { - if (opp->rate == freq) { - found = true; - break; - } - } - - if (!found) { - dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n", - __func__, freq); - goto unlock; - } - - _opp_remove(dev_opp, opp); -unlock: - mutex_unlock(&dev_opp_list_lock); -} -EXPORT_SYMBOL_GPL(dev_pm_opp_remove); - /** * _opp_set_availability() - helper to set the availability of an opp * @dev: device for which we do this operation @@ -824,6 +824,49 @@ struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev) EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); #ifdef CONFIG_OF +/** + * of_free_opp_table() - Free OPP table entries created from static DT entries + * @dev: device pointer used to lookup device OPPs. + * + * Free OPPs created using static entries present in DT. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function indirectly uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +void of_free_opp_table(struct device *dev) +{ + struct device_opp *dev_opp; + struct dev_pm_opp *opp, *tmp; + + /* Check for existing list for 'dev' */ + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) { + int error = PTR_ERR(dev_opp); + + if (error != -ENODEV) + WARN(1, "%s: dev_opp: %d\n", + IS_ERR_OR_NULL(dev) ? + "Invalid device" : dev_name(dev), + error); + return; + } + + /* Hold our list modification lock here */ + mutex_lock(&dev_opp_list_lock); + + /* Free static OPPs */ + list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { + if (!opp->dynamic) + _opp_remove(dev_opp, opp); + } + + mutex_unlock(&dev_opp_list_lock); +} +EXPORT_SYMBOL_GPL(of_free_opp_table); + /** * of_init_opp_table() - Initialize opp table from device tree * @dev: device pointer used to lookup device OPPs. @@ -882,46 +925,4 @@ int of_init_opp_table(struct device *dev) return 0; } EXPORT_SYMBOL_GPL(of_init_opp_table); - -/** - * of_free_opp_table() - Free OPP table entries created from static DT entries - * @dev: device pointer used to lookup device OPPs. - * - * Free OPPs created using static entries present in DT. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function indirectly uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - */ -void of_free_opp_table(struct device *dev) -{ - struct device_opp *dev_opp; - struct dev_pm_opp *opp, *tmp; - - /* Check for existing list for 'dev' */ - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) { - int error = PTR_ERR(dev_opp); - if (error != -ENODEV) - WARN(1, "%s: dev_opp: %d\n", - IS_ERR_OR_NULL(dev) ? - "Invalid device" : dev_name(dev), - error); - return; - } - - /* Hold our list modification lock here */ - mutex_lock(&dev_opp_list_lock); - - /* Free static OPPs */ - list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { - if (!opp->dynamic) - _opp_remove(dev_opp, opp); - } - - mutex_unlock(&dev_opp_list_lock); -} -EXPORT_SYMBOL_GPL(of_free_opp_table); #endif -- cgit v1.3-6-gb490 From 3bac42caec612a1b82db7944570353cddca6a013 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Jul 2015 16:22:59 +0530 Subject: PM / OPP: Create _remove_device_opp() for freeing dev_opp This will be used from multiple places later. Lets create a separate routine for that. Reviewed-by: Stephen Boyd Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 8c3fd57975fb..7895fdd64192 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -448,6 +448,22 @@ static void _kfree_device_rcu(struct rcu_head *head) kfree_rcu(device_opp, rcu_head); } +/** + * _remove_device_opp() - Removes a device OPP table + * @dev_opp: device OPP table to be removed. + * + * Removes/frees device OPP table it it doesn't contain any OPPs. + */ +static void _remove_device_opp(struct device_opp *dev_opp) +{ + if (!list_empty(&dev_opp->opp_list)) + return; + + list_del_rcu(&dev_opp->node); + call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head, + _kfree_device_rcu); +} + /** * _kfree_opp_rcu() - Free OPP RCU handler * @head: RCU head @@ -481,11 +497,7 @@ static void _opp_remove(struct device_opp *dev_opp, list_del_rcu(&opp->node); call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); - if (list_empty(&dev_opp->opp_list)) { - list_del_rcu(&dev_opp->node); - call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head, - _kfree_device_rcu); - } + _remove_device_opp(dev_opp); } /** -- cgit v1.3-6-gb490 From aa5f2f854f03e6dc3dec8874bbcff1452b4bc09e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Jul 2015 16:23:00 +0530 Subject: PM / OPP: Allocate dev_opp from _add_device_opp() There is no need to complicate _opp_add_dynamic() with allocation of dev_opp as well. Allocate it from _add_device_opp() instead. Reviewed-by: Stephen Boyd Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 50 ++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 23 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 7895fdd64192..28d70c9f86ed 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -408,11 +408,11 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); /** - * _add_device_opp() - Allocate a new device OPP table + * _add_device_opp() - Find device OPP table or allocate a new one * @dev: device for which we do this operation * - * New device node which uses OPPs - used when multiple devices with OPP tables - * are maintained. + * It tries to find an existing table first, if it couldn't find one, it + * allocates a new OPP table and returns that. * * Return: valid device_opp pointer if success, else NULL. */ @@ -420,6 +420,11 @@ static struct device_opp *_add_device_opp(struct device *dev) { struct device_opp *dev_opp; + /* Check for existing list for 'dev' first */ + dev_opp = _find_device_opp(dev); + if (!IS_ERR(dev_opp)) + return dev_opp; + /* * Allocate a new device OPP table. In the infrequent case where a new * device is needed to be added, we pay this penalty. @@ -575,7 +580,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_remove); static int _opp_add_dynamic(struct device *dev, unsigned long freq, long u_volt, bool dynamic) { - struct device_opp *dev_opp = NULL; + struct device_opp *dev_opp; struct dev_pm_opp *opp, *new_opp; struct list_head *head; int ret; @@ -592,19 +597,11 @@ static int _opp_add_dynamic(struct device *dev, unsigned long freq, new_opp->rate = freq; new_opp->u_volt = u_volt; new_opp->available = true; - new_opp->dynamic = dynamic; - - /* Check for existing list for 'dev' */ - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) { - dev_opp = _add_device_opp(dev); - if (!dev_opp) { - ret = -ENOMEM; - goto free_opp; - } - head = &dev_opp->opp_list; - goto list_add; + dev_opp = _add_device_opp(dev); + if (!dev_opp) { + ret = -ENOMEM; + goto free_opp; } /* @@ -612,15 +609,22 @@ static int _opp_add_dynamic(struct device *dev, unsigned long freq, * and discard if already present */ head = &dev_opp->opp_list; + + /* + * Need to use &dev_opp->opp_list in the condition part of the 'for' + * loop, don't replace it with head otherwise it will become an infinite + * loop. + */ list_for_each_entry_rcu(opp, &dev_opp->opp_list, node) { - if (new_opp->rate <= opp->rate) - break; - else + if (new_opp->rate > opp->rate) { head = &opp->node; - } + continue; + } + + if (new_opp->rate < opp->rate) + break; - /* Duplicate OPPs ? */ - if (new_opp->rate == opp->rate) { + /* Duplicate OPPs */ ret = opp->available && new_opp->u_volt == opp->u_volt ? 0 : -EEXIST; @@ -630,7 +634,7 @@ static int _opp_add_dynamic(struct device *dev, unsigned long freq, goto free_opp; } -list_add: + new_opp->dynamic = dynamic; new_opp->dev_opp = dev_opp; list_add_rcu(&new_opp->node, head); mutex_unlock(&dev_opp_list_lock); -- cgit v1.3-6-gb490 From 23dacf6d2e993551ef3ae0629baf6f473930513c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Jul 2015 16:23:01 +0530 Subject: PM / OPP: Break _opp_add_dynamic() into smaller functions Later commits would add support for new OPP bindings and this would be required then. So, lets do it in a separate patch to make it easily reviewable. Another change worth noticing is INIT_LIST_HEAD(&opp->node). We weren't doing it earlier as we never tried to delete a list node before it is added to list. But this wouldn't be the case anymore. We might try to delete a node (just to reuse the same code paths), without it being getting added to the list. Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Viresh Kumar Reviewed-by: Stephen Boyd Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 125 ++++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 49 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 28d70c9f86ed..0d8dbf21c299 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -484,6 +484,7 @@ static void _kfree_opp_rcu(struct rcu_head *head) * _opp_remove() - Remove an OPP from a table definition * @dev_opp: points back to the device_opp struct this opp belongs to * @opp: pointer to the OPP to remove + * @notify: OPP_EVENT_REMOVE notification should be sent or not * * This function removes an opp definition from the opp list. * @@ -492,13 +493,14 @@ static void _kfree_opp_rcu(struct rcu_head *head) * strategy. */ static void _opp_remove(struct device_opp *dev_opp, - struct dev_pm_opp *opp) + struct dev_pm_opp *opp, bool notify) { /* * Notify the changes in the availability of the operable * frequency/voltage list. */ - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp); + if (notify) + srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp); list_del_rcu(&opp->node); call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); @@ -544,12 +546,70 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq) goto unlock; } - _opp_remove(dev_opp, opp); + _opp_remove(dev_opp, opp, true); unlock: mutex_unlock(&dev_opp_list_lock); } EXPORT_SYMBOL_GPL(dev_pm_opp_remove); +static struct dev_pm_opp *_allocate_opp(struct device *dev, + struct device_opp **dev_opp) +{ + struct dev_pm_opp *opp; + + /* allocate new OPP node */ + opp = kzalloc(sizeof(*opp), GFP_KERNEL); + if (!opp) + return NULL; + + INIT_LIST_HEAD(&opp->node); + + *dev_opp = _add_device_opp(dev); + if (!*dev_opp) { + kfree(opp); + return NULL; + } + + return opp; +} + +static int _opp_add(struct dev_pm_opp *new_opp, struct device_opp *dev_opp) +{ + struct dev_pm_opp *opp; + struct list_head *head = &dev_opp->opp_list; + + /* + * Insert new OPP in order of increasing frequency and discard if + * already present. + * + * Need to use &dev_opp->opp_list in the condition part of the 'for' + * loop, don't replace it with head otherwise it will become an infinite + * loop. + */ + list_for_each_entry_rcu(opp, &dev_opp->opp_list, node) { + if (new_opp->rate > opp->rate) { + head = &opp->node; + continue; + } + + if (new_opp->rate < opp->rate) + break; + + /* Duplicate OPPs */ + dev_warn(dev_opp->dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n", + __func__, opp->rate, opp->u_volt, opp->available, + new_opp->rate, new_opp->u_volt, new_opp->available); + + return opp->available && new_opp->u_volt == opp->u_volt ? + 0 : -EEXIST; + } + + new_opp->dev_opp = dev_opp; + list_add_rcu(&new_opp->node, head); + + return 0; +} + /** * _opp_add_dynamic() - Allocate a dynamic OPP. * @dev: device for which we do this operation @@ -581,62 +641,28 @@ static int _opp_add_dynamic(struct device *dev, unsigned long freq, long u_volt, bool dynamic) { struct device_opp *dev_opp; - struct dev_pm_opp *opp, *new_opp; - struct list_head *head; + struct dev_pm_opp *new_opp; int ret; - /* allocate new OPP node */ - new_opp = kzalloc(sizeof(*new_opp), GFP_KERNEL); - if (!new_opp) - return -ENOMEM; - /* Hold our list modification lock here */ mutex_lock(&dev_opp_list_lock); + new_opp = _allocate_opp(dev, &dev_opp); + if (!new_opp) { + ret = -ENOMEM; + goto unlock; + } + /* populate the opp table */ new_opp->rate = freq; new_opp->u_volt = u_volt; new_opp->available = true; + new_opp->dynamic = dynamic; - dev_opp = _add_device_opp(dev); - if (!dev_opp) { - ret = -ENOMEM; - goto free_opp; - } - - /* - * Insert new OPP in order of increasing frequency - * and discard if already present - */ - head = &dev_opp->opp_list; - - /* - * Need to use &dev_opp->opp_list in the condition part of the 'for' - * loop, don't replace it with head otherwise it will become an infinite - * loop. - */ - list_for_each_entry_rcu(opp, &dev_opp->opp_list, node) { - if (new_opp->rate > opp->rate) { - head = &opp->node; - continue; - } - - if (new_opp->rate < opp->rate) - break; - - /* Duplicate OPPs */ - ret = opp->available && new_opp->u_volt == opp->u_volt ? - 0 : -EEXIST; - - dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n", - __func__, opp->rate, opp->u_volt, opp->available, - new_opp->rate, new_opp->u_volt, new_opp->available); + ret = _opp_add(new_opp, dev_opp); + if (ret) goto free_opp; - } - new_opp->dynamic = dynamic; - new_opp->dev_opp = dev_opp; - list_add_rcu(&new_opp->node, head); mutex_unlock(&dev_opp_list_lock); /* @@ -647,8 +673,9 @@ static int _opp_add_dynamic(struct device *dev, unsigned long freq, return 0; free_opp: + _opp_remove(dev_opp, new_opp, false); +unlock: mutex_unlock(&dev_opp_list_lock); - kfree(new_opp); return ret; } @@ -876,7 +903,7 @@ void of_free_opp_table(struct device *dev) /* Free static OPPs */ list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { if (!opp->dynamic) - _opp_remove(dev_opp, opp); + _opp_remove(dev_opp, opp, true); } mutex_unlock(&dev_opp_list_lock); -- cgit v1.3-6-gb490 From 274659029c9de5b11678d3a52a45e3dbc9177a48 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Jul 2015 16:23:02 +0530 Subject: PM / OPP: Add support to parse "operating-points-v2" bindings This adds support in OPP library to parse and create list of OPPs from operating-points-v2 bindings. It takes care of most of the properties of new bindings (except shared-opp, which will be handled separately). For backward compatibility, we keep supporting earlier bindings. We try to search for the new bindings first, in case they aren't present we look for the old deprecated ones. There are few things marked as TODO: - Support for multiple OPP tables - Support for multiple regulators They should be fixed separately. Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Viresh Kumar Reviewed-by: Stephen Boyd Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 257 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 233 insertions(+), 24 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 0d8dbf21c299..0e0eff4d9299 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -51,10 +51,15 @@ * order. * @dynamic: not-created from static DT entries. * @available: true/false - marks if this OPP as available or not + * @turbo: true if turbo (boost) OPP * @rate: Frequency in hertz - * @u_volt: Nominal voltage in microvolts corresponding to this OPP + * @u_volt: Target voltage in microvolts corresponding to this OPP + * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP + * @u_volt_max: Maximum voltage in microvolts corresponding to this OPP + * @u_amp: Maximum current drawn by the device in microamperes * @dev_opp: points back to the device_opp struct this opp belongs to * @rcu_head: RCU callback head used for deferred freeing + * @np: OPP's device node. * * This structure stores the OPP information for a given device. */ @@ -63,11 +68,18 @@ struct dev_pm_opp { bool available; bool dynamic; + bool turbo; unsigned long rate; + unsigned long u_volt; + unsigned long u_volt_min; + unsigned long u_volt_max; + unsigned long u_amp; struct device_opp *dev_opp; struct rcu_head rcu_head; + + struct device_node *np; }; /** @@ -679,6 +691,125 @@ unlock: return ret; } +/* TODO: Support multiple regulators */ +static int opp_get_microvolt(struct dev_pm_opp *opp, struct device *dev) +{ + u32 microvolt[3] = {0}; + int count, ret; + + count = of_property_count_u32_elems(opp->np, "opp-microvolt"); + if (!count) + return 0; + + /* There can be one or three elements here */ + if (count != 1 && count != 3) { + dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n", + __func__, count); + return -EINVAL; + } + + ret = of_property_read_u32_array(opp->np, "opp-microvolt", microvolt, + count); + if (ret) { + dev_err(dev, "%s: error parsing opp-microvolt: %d\n", __func__, + ret); + return -EINVAL; + } + + opp->u_volt = microvolt[0]; + opp->u_volt_min = microvolt[1]; + opp->u_volt_max = microvolt[2]; + + return 0; +} + +/** + * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings) + * @dev: device for which we do this operation + * @np: device node + * + * This function adds an opp definition to the opp list and returns status. The + * opp can be controlled using dev_pm_opp_enable/disable functions and may be + * removed by dev_pm_opp_remove. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + * -EINVAL Failed parsing the OPP node + */ +static int _opp_add_static_v2(struct device *dev, struct device_node *np) +{ + struct device_opp *dev_opp; + struct dev_pm_opp *new_opp; + u64 rate; + int ret; + + /* Hold our list modification lock here */ + mutex_lock(&dev_opp_list_lock); + + new_opp = _allocate_opp(dev, &dev_opp); + if (!new_opp) { + ret = -ENOMEM; + goto unlock; + } + + ret = of_property_read_u64(np, "opp-hz", &rate); + if (ret < 0) { + dev_err(dev, "%s: opp-hz not found\n", __func__); + goto free_opp; + } + + /* + * Rate is defined as an unsigned long in clk API, and so casting + * explicitly to its type. Must be fixed once rate is 64 bit + * guaranteed in clk API. + */ + new_opp->rate = (unsigned long)rate; + new_opp->turbo = of_property_read_bool(np, "turbo-mode"); + + new_opp->np = np; + new_opp->dynamic = false; + new_opp->available = true; + + ret = opp_get_microvolt(new_opp, dev); + if (ret) + goto free_opp; + + of_property_read_u32(np, "opp-microamp", (u32 *)&new_opp->u_amp); + + ret = _opp_add(new_opp, dev_opp); + if (ret) + goto free_opp; + + mutex_unlock(&dev_opp_list_lock); + + pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu\n", + __func__, new_opp->turbo, new_opp->rate, new_opp->u_volt, + new_opp->u_volt_min, new_opp->u_volt_max); + + /* + * Notify the changes in the availability of the operable + * frequency/voltage list. + */ + srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp); + return 0; + +free_opp: + _opp_remove(dev_opp, new_opp, false); +unlock: + mutex_unlock(&dev_opp_list_lock); + return ret; +} + /** * dev_pm_opp_add() - Add an OPP table from a table definitions * @dev: device for which we do this operation @@ -910,29 +1041,64 @@ void of_free_opp_table(struct device *dev) } EXPORT_SYMBOL_GPL(of_free_opp_table); -/** - * of_init_opp_table() - Initialize opp table from device tree - * @dev: device pointer used to lookup device OPPs. - * - * Register the initial OPP table with the OPP library for given device. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function indirectly uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - * - * Return: - * 0 On success OR - * Duplicate OPPs (both freq and volt are same) and opp->available - * -EEXIST Freq are same and volt are different OR - * Duplicate OPPs (both freq and volt are same) and !opp->available - * -ENOMEM Memory allocation failure - * -ENODEV when 'operating-points' property is not found or is invalid data - * in device node. - * -ENODATA when empty 'operating-points' property is found - */ -int of_init_opp_table(struct device *dev) +/* Returns opp descriptor node from its phandle. Caller must do of_node_put() */ +static struct device_node * +_of_get_opp_desc_node_from_prop(struct device *dev, const struct property *prop) +{ + struct device_node *opp_np; + + opp_np = of_find_node_by_phandle(be32_to_cpup(prop->value)); + if (!opp_np) { + dev_err(dev, "%s: Prop: %s contains invalid opp desc phandle\n", + __func__, prop->name); + return ERR_PTR(-EINVAL); + } + + return opp_np; +} + +/* Initializes OPP tables based on new bindings */ +static int _of_init_opp_table_v2(struct device *dev, + const struct property *prop) +{ + struct device_node *opp_np, *np; + int ret = 0, count = 0; + + if (!prop->value) + return -ENODATA; + + /* Get opp node */ + opp_np = _of_get_opp_desc_node_from_prop(dev, prop); + if (IS_ERR(opp_np)) + return PTR_ERR(opp_np); + + /* We have opp-list node now, iterate over it and add OPPs */ + for_each_available_child_of_node(opp_np, np) { + count++; + + ret = _opp_add_static_v2(dev, np); + if (ret) { + dev_err(dev, "%s: Failed to add OPP, %d\n", __func__, + ret); + break; + } + } + + /* There should be one of more OPP defined */ + if (WARN_ON(!count)) + goto put_opp_np; + + if (ret) + of_free_opp_table(dev); + +put_opp_np: + of_node_put(opp_np); + + return ret; +} + +/* Initializes OPP tables based on old-deprecated bindings */ +static int _of_init_opp_table_v1(struct device *dev) { const struct property *prop; const __be32 *val; @@ -967,5 +1133,48 @@ int of_init_opp_table(struct device *dev) return 0; } + +/** + * of_init_opp_table() - Initialize opp table from device tree + * @dev: device pointer used to lookup device OPPs. + * + * Register the initial OPP table with the OPP library for given device. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function indirectly uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + * -ENODEV when 'operating-points' property is not found or is invalid data + * in device node. + * -ENODATA when empty 'operating-points' property is found + * -EINVAL when invalid entries are found in opp-v2 table + */ +int of_init_opp_table(struct device *dev) +{ + const struct property *prop; + + /* + * OPPs have two version of bindings now. The older one is deprecated, + * try for the new binding first. + */ + prop = of_find_property(dev->of_node, "operating-points-v2", NULL); + if (!prop) { + /* + * Try old-deprecated bindings for backward compatibility with + * older dtbs. + */ + return _of_init_opp_table_v1(dev); + } + + return _of_init_opp_table_v2(dev, prop); +} EXPORT_SYMBOL_GPL(of_init_opp_table); #endif -- cgit v1.3-6-gb490 From 3ca9bb33c627f22640cfca97fdf88eec0a120dfd Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Jul 2015 16:23:03 +0530 Subject: PM / OPP: Add clock-latency-ns support With "operating-points-v2" bindings, clock-latency is defined per OPP. Users of this value expect a single value which defines the latency to switch to any clock rate. Find maximum clock-latency-ns from the OPP table to service requests from such users. Reviewed-by: Stephen Boyd Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 41 +++++++++++++++++++++++++++++++++++++++-- include/linux/pm_opp.h | 6 ++++++ 2 files changed, 45 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 0e0eff4d9299..8638204c457e 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -57,6 +57,8 @@ * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP * @u_volt_max: Maximum voltage in microvolts corresponding to this OPP * @u_amp: Maximum current drawn by the device in microamperes + * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's + * frequency from any other OPP's frequency. * @dev_opp: points back to the device_opp struct this opp belongs to * @rcu_head: RCU callback head used for deferred freeing * @np: OPP's device node. @@ -75,6 +77,7 @@ struct dev_pm_opp { unsigned long u_volt_min; unsigned long u_volt_max; unsigned long u_amp; + unsigned long clock_latency_ns; struct device_opp *dev_opp; struct rcu_head rcu_head; @@ -109,6 +112,8 @@ struct device_opp { struct srcu_notifier_head srcu_head; struct rcu_head rcu_head; struct list_head opp_list; + + unsigned long clock_latency_ns_max; }; /* @@ -225,6 +230,32 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) } EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); +/** + * dev_pm_opp_get_max_clock_latency() - Get max clock latency in nanoseconds + * @dev: device for which we do this operation + * + * Return: This function returns the max clock latency in nanoseconds. + * + * Locking: This function takes rcu_read_lock(). + */ +unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) +{ + struct device_opp *dev_opp; + unsigned long clock_latency_ns; + + rcu_read_lock(); + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) + clock_latency_ns = 0; + else + clock_latency_ns = dev_opp->clock_latency_ns_max; + + rcu_read_unlock(); + return clock_latency_ns; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); + /** * dev_pm_opp_get_opp_count() - Get number of opps available in the opp list * @dev: device for which we do this operation @@ -779,6 +810,8 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) new_opp->np = np; new_opp->dynamic = false; new_opp->available = true; + of_property_read_u32(np, "clock-latency-ns", + (u32 *)&new_opp->clock_latency_ns); ret = opp_get_microvolt(new_opp, dev); if (ret) @@ -790,11 +823,15 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) if (ret) goto free_opp; + if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max) + dev_opp->clock_latency_ns_max = new_opp->clock_latency_ns; + mutex_unlock(&dev_opp_list_lock); - pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu\n", + pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n", __func__, new_opp->turbo, new_opp->rate, new_opp->u_volt, - new_opp->u_volt_min, new_opp->u_volt_max); + new_opp->u_volt_min, new_opp->u_volt_max, + new_opp->clock_latency_ns); /* * Notify the changes in the availability of the operable diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index cec2d4540914..20324b579adc 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -31,6 +31,7 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp); unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); int dev_pm_opp_get_opp_count(struct device *dev); +unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev); struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, @@ -67,6 +68,11 @@ static inline int dev_pm_opp_get_opp_count(struct device *dev) return 0; } +static inline unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) +{ + return 0; +} + static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available) { -- cgit v1.3-6-gb490 From 064416586190cb058d4c76a4e26b1996f434b6ca Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Jul 2015 16:23:04 +0530 Subject: PM / OPP: Add OPP sharing information to OPP library An opp can be shared by multiple devices, for example its very common for CPUs to share the OPPs, i.e. when they share clock/voltage rails. This patch adds support of shared OPPs to the OPP library. Instead of a single device, dev_opp will now contain a list of devices that use it. It also senses if the device (we are trying to initialize OPPs for) shares OPPs with a device added earlier and in that case we update the list of devices managed by OPPs instead of duplicating OPPs again. The same infrastructure will be used for the old OPP bindings, with later patches. Reviewed-by: Stephen Boyd Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 174 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 150 insertions(+), 24 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 8638204c457e..5d699e3ec136 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -85,6 +85,21 @@ struct dev_pm_opp { struct device_node *np; }; +/** + * struct device_list_opp - devices managed by 'struct device_opp' + * @node: list node + * @dev: device to which the struct object belongs + * @rcu_head: RCU callback head used for deferred freeing + * + * This is an internal data structure maintaining the list of devices that are + * managed by 'struct device_opp'. + */ +struct device_list_opp { + struct list_head node; + const struct device *dev; + struct rcu_head rcu_head; +}; + /** * struct device_opp - Device opp structure * @node: list node - contains the devices with OPPs that @@ -92,10 +107,12 @@ struct dev_pm_opp { * list. * RCU usage: nodes are not modified in the list of device_opp, * however addition is possible and is secured by dev_opp_list_lock - * @dev: device pointer * @srcu_head: notifier head to notify the OPP availability changes. * @rcu_head: RCU callback head used for deferred freeing + * @dev_list: list of devices that share these OPPs * @opp_list: list of opps + * @np: struct device_node pointer for opp's DT node. + * @shared_opp: OPP is shared between multiple devices. * * This is an internal data structure maintaining the link to opps attached to * a device. This structure is not meant to be shared to users as it is @@ -108,12 +125,14 @@ struct dev_pm_opp { struct device_opp { struct list_head node; - struct device *dev; struct srcu_notifier_head srcu_head; struct rcu_head rcu_head; + struct list_head dev_list; struct list_head opp_list; + struct device_node *np; unsigned long clock_latency_ns_max; + bool shared_opp; }; /* @@ -133,6 +152,38 @@ do { \ "dev_opp_list_lock protection"); \ } while (0) +static struct device_list_opp *_find_list_dev(const struct device *dev, + struct device_opp *dev_opp) +{ + struct device_list_opp *list_dev; + + list_for_each_entry(list_dev, &dev_opp->dev_list, node) + if (list_dev->dev == dev) + return list_dev; + + return NULL; +} + +static struct device_opp *_managed_opp(const struct device_node *np) +{ + struct device_opp *dev_opp; + + list_for_each_entry_rcu(dev_opp, &dev_opp_list, node) { + if (dev_opp->np == np) { + /* + * Multiple devices can point to the same OPP table and + * so will have same node-pointer, np. + * + * But the OPPs will be considered as shared only if the + * OPP table contains a "opp-shared" property. + */ + return dev_opp->shared_opp ? dev_opp : NULL; + } + } + + return NULL; +} + /** * _find_device_opp() - find device_opp struct using device pointer * @dev: device pointer used to lookup device OPPs @@ -149,21 +200,18 @@ do { \ */ static struct device_opp *_find_device_opp(struct device *dev) { - struct device_opp *tmp_dev_opp, *dev_opp = ERR_PTR(-ENODEV); + struct device_opp *dev_opp; if (unlikely(IS_ERR_OR_NULL(dev))) { pr_err("%s: Invalid parameters\n", __func__); return ERR_PTR(-EINVAL); } - list_for_each_entry_rcu(tmp_dev_opp, &dev_opp_list, node) { - if (tmp_dev_opp->dev == dev) { - dev_opp = tmp_dev_opp; - break; - } - } + list_for_each_entry_rcu(dev_opp, &dev_opp_list, node) + if (_find_list_dev(dev, dev_opp)) + return dev_opp; - return dev_opp; + return ERR_PTR(-ENODEV); } /** @@ -450,6 +498,39 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); +/* List-dev Helpers */ +static void _kfree_list_dev_rcu(struct rcu_head *head) +{ + struct device_list_opp *list_dev; + + list_dev = container_of(head, struct device_list_opp, rcu_head); + kfree_rcu(list_dev, rcu_head); +} + +static void _remove_list_dev(struct device_list_opp *list_dev, + struct device_opp *dev_opp) +{ + list_del(&list_dev->node); + call_srcu(&dev_opp->srcu_head.srcu, &list_dev->rcu_head, + _kfree_list_dev_rcu); +} + +static struct device_list_opp *_add_list_dev(const struct device *dev, + struct device_opp *dev_opp) +{ + struct device_list_opp *list_dev; + + list_dev = kzalloc(sizeof(*list_dev), GFP_KERNEL); + if (!list_dev) + return NULL; + + /* Initialize list-dev */ + list_dev->dev = dev; + list_add_rcu(&list_dev->node, &dev_opp->dev_list); + + return list_dev; +} + /** * _add_device_opp() - Find device OPP table or allocate a new one * @dev: device for which we do this operation @@ -462,6 +543,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); static struct device_opp *_add_device_opp(struct device *dev) { struct device_opp *dev_opp; + struct device_list_opp *list_dev; /* Check for existing list for 'dev' first */ dev_opp = _find_device_opp(dev); @@ -476,7 +558,14 @@ static struct device_opp *_add_device_opp(struct device *dev) if (!dev_opp) return NULL; - dev_opp->dev = dev; + INIT_LIST_HEAD(&dev_opp->dev_list); + + list_dev = _add_list_dev(dev, dev_opp); + if (!list_dev) { + kfree(dev_opp); + return NULL; + } + srcu_init_notifier_head(&dev_opp->srcu_head); INIT_LIST_HEAD(&dev_opp->opp_list); @@ -504,9 +593,19 @@ static void _kfree_device_rcu(struct rcu_head *head) */ static void _remove_device_opp(struct device_opp *dev_opp) { + struct device_list_opp *list_dev; + if (!list_empty(&dev_opp->opp_list)) return; + list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp, + node); + + _remove_list_dev(list_dev, dev_opp); + + /* dev_list must be empty now */ + WARN_ON(!list_empty(&dev_opp->dev_list)); + list_del_rcu(&dev_opp->node); call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head, _kfree_device_rcu); @@ -616,7 +715,8 @@ static struct dev_pm_opp *_allocate_opp(struct device *dev, return opp; } -static int _opp_add(struct dev_pm_opp *new_opp, struct device_opp *dev_opp) +static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, + struct device_opp *dev_opp) { struct dev_pm_opp *opp; struct list_head *head = &dev_opp->opp_list; @@ -639,7 +739,7 @@ static int _opp_add(struct dev_pm_opp *new_opp, struct device_opp *dev_opp) break; /* Duplicate OPPs */ - dev_warn(dev_opp->dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n", + dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n", __func__, opp->rate, opp->u_volt, opp->available, new_opp->rate, new_opp->u_volt, new_opp->available); @@ -702,7 +802,7 @@ static int _opp_add_dynamic(struct device *dev, unsigned long freq, new_opp->available = true; new_opp->dynamic = dynamic; - ret = _opp_add(new_opp, dev_opp); + ret = _opp_add(dev, new_opp, dev_opp); if (ret) goto free_opp; @@ -819,7 +919,7 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) of_property_read_u32(np, "opp-microamp", (u32 *)&new_opp->u_amp); - ret = _opp_add(new_opp, dev_opp); + ret = _opp_add(dev, new_opp, dev_opp); if (ret) goto free_opp; @@ -1052,6 +1152,9 @@ void of_free_opp_table(struct device *dev) struct device_opp *dev_opp; struct dev_pm_opp *opp, *tmp; + /* Hold our list modification lock here */ + mutex_lock(&dev_opp_list_lock); + /* Check for existing list for 'dev' */ dev_opp = _find_device_opp(dev); if (IS_ERR(dev_opp)) { @@ -1062,18 +1165,21 @@ void of_free_opp_table(struct device *dev) IS_ERR_OR_NULL(dev) ? "Invalid device" : dev_name(dev), error); - return; + goto unlock; } - /* Hold our list modification lock here */ - mutex_lock(&dev_opp_list_lock); - - /* Free static OPPs */ - list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { - if (!opp->dynamic) - _opp_remove(dev_opp, opp, true); + /* Find if dev_opp manages a single device */ + if (list_is_singular(&dev_opp->dev_list)) { + /* Free static OPPs */ + list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { + if (!opp->dynamic) + _opp_remove(dev_opp, opp, true); + } + } else { + _remove_list_dev(_find_list_dev(dev, dev_opp), dev_opp); } +unlock: mutex_unlock(&dev_opp_list_lock); } EXPORT_SYMBOL_GPL(of_free_opp_table); @@ -1099,6 +1205,7 @@ static int _of_init_opp_table_v2(struct device *dev, const struct property *prop) { struct device_node *opp_np, *np; + struct device_opp *dev_opp; int ret = 0, count = 0; if (!prop->value) @@ -1109,6 +1216,14 @@ static int _of_init_opp_table_v2(struct device *dev, if (IS_ERR(opp_np)) return PTR_ERR(opp_np); + dev_opp = _managed_opp(opp_np); + if (dev_opp) { + /* OPPs are already managed */ + if (!_add_list_dev(dev, dev_opp)) + ret = -ENOMEM; + goto put_opp_np; + } + /* We have opp-list node now, iterate over it and add OPPs */ for_each_available_child_of_node(opp_np, np) { count++; @@ -1125,8 +1240,19 @@ static int _of_init_opp_table_v2(struct device *dev, if (WARN_ON(!count)) goto put_opp_np; - if (ret) + if (!ret) { + if (!dev_opp) { + dev_opp = _find_device_opp(dev); + if (WARN_ON(!dev_opp)) + goto put_opp_np; + } + + dev_opp->np = opp_np; + dev_opp->shared_opp = of_property_read_bool(opp_np, + "opp-shared"); + } else { of_free_opp_table(dev); + } put_opp_np: of_node_put(opp_np); -- cgit v1.3-6-gb490 From ad656a6a8b1c8b4b2e723646e0402867f2f45395 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 13 Jun 2015 15:10:21 +0530 Subject: PM / OPP: Add support for opp-suspend With "operating-points-v2" bindings, it's possible to specify the OPP to which the device must be switched, before suspending. This patch adds support for getting that information. Reviewed-by: Stephen Boyd Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 5d699e3ec136..0ebcea49145a 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -133,6 +133,7 @@ struct device_opp { struct device_node *np; unsigned long clock_latency_ns_max; bool shared_opp; + struct dev_pm_opp *suspend_opp; }; /* @@ -923,6 +924,16 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) if (ret) goto free_opp; + /* OPP to select on device suspend */ + if (of_property_read_bool(np, "opp-suspend")) { + if (dev_opp->suspend_opp) + dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n", + __func__, dev_opp->suspend_opp->rate, + new_opp->rate); + else + dev_opp->suspend_opp = new_opp; + } + if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max) dev_opp->clock_latency_ns_max = new_opp->clock_latency_ns; -- cgit v1.3-6-gb490 From 8d4d4e98acd68c31435ebb7beea591dbf60b9eb2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 12 Jun 2015 17:10:38 +0530 Subject: PM / OPP: Add helpers for initializing CPU OPPs With "operating-points-v2" its possible to tell which devices share OPPs. We already have infrastructure to decode that information. This patch adds following APIs: - of_get_cpus_sharing_opps: Returns cpumask of CPUs sharing OPPs (only valid with v2 bindings). - of_cpumask_init_opp_table: Initializes OPPs for all CPUs present in cpumask. - of_cpumask_free_opp_table: Frees OPPs for all CPUs present in cpumask. - set_cpus_sharing_opps: Sets which CPUs share OPPs (only valid with old OPP bindings, as this information isn't present in DT). Reviewed-by: Stephen Boyd Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 23 +++++++ 2 files changed, 198 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 0ebcea49145a..663aae1c9834 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -11,6 +11,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -1195,6 +1196,26 @@ unlock: } EXPORT_SYMBOL_GPL(of_free_opp_table); +void of_cpumask_free_opp_table(cpumask_var_t cpumask) +{ + struct device *cpu_dev; + int cpu; + + WARN_ON(cpumask_empty(cpumask)); + + for_each_cpu(cpu, cpumask) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + cpu); + continue; + } + + of_free_opp_table(cpu_dev); + } +} +EXPORT_SYMBOL_GPL(of_cpumask_free_opp_table); + /* Returns opp descriptor node from its phandle. Caller must do of_node_put() */ static struct device_node * _of_get_opp_desc_node_from_prop(struct device *dev, const struct property *prop) @@ -1211,6 +1232,31 @@ _of_get_opp_desc_node_from_prop(struct device *dev, const struct property *prop) return opp_np; } +/* Returns opp descriptor node for a device. Caller must do of_node_put() */ +static struct device_node *_of_get_opp_desc_node(struct device *dev) +{ + const struct property *prop; + + prop = of_find_property(dev->of_node, "operating-points-v2", NULL); + if (!prop) + return ERR_PTR(-ENODEV); + if (!prop->value) + return ERR_PTR(-ENODATA); + + /* + * TODO: Support for multiple OPP tables. + * + * There should be only ONE phandle present in "operating-points-v2" + * property. + */ + if (prop->length != sizeof(__be32)) { + dev_err(dev, "%s: Invalid opp desc phandle\n", __func__); + return ERR_PTR(-EINVAL); + } + + return _of_get_opp_desc_node_from_prop(dev, prop); +} + /* Initializes OPP tables based on new bindings */ static int _of_init_opp_table_v2(struct device *dev, const struct property *prop) @@ -1351,4 +1397,133 @@ int of_init_opp_table(struct device *dev) return _of_init_opp_table_v2(dev, prop); } EXPORT_SYMBOL_GPL(of_init_opp_table); + +int of_cpumask_init_opp_table(cpumask_var_t cpumask) +{ + struct device *cpu_dev; + int cpu, ret = 0; + + WARN_ON(cpumask_empty(cpumask)); + + for_each_cpu(cpu, cpumask) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + cpu); + continue; + } + + ret = of_init_opp_table(cpu_dev); + if (ret) { + pr_err("%s: couldn't find opp table for cpu:%d, %d\n", + __func__, cpu, ret); + + /* Free all other OPPs */ + of_cpumask_free_opp_table(cpumask); + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(of_cpumask_init_opp_table); + +/* Required only for V1 bindings, as v2 can manage it from DT itself */ +int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) +{ + struct device_list_opp *list_dev; + struct device_opp *dev_opp; + struct device *dev; + int cpu, ret = 0; + + rcu_read_lock(); + + dev_opp = _find_device_opp(cpu_dev); + if (IS_ERR(dev_opp)) { + ret = -EINVAL; + goto out_rcu_read_unlock; + } + + for_each_cpu(cpu, cpumask) { + if (cpu == cpu_dev->id) + continue; + + dev = get_cpu_device(cpu); + if (!dev) { + dev_err(cpu_dev, "%s: failed to get cpu%d device\n", + __func__, cpu); + continue; + } + + list_dev = _add_list_dev(dev, dev_opp); + if (!list_dev) { + dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", + __func__, cpu); + continue; + } + } +out_rcu_read_unlock: + rcu_read_unlock(); + + return 0; +} +EXPORT_SYMBOL_GPL(set_cpus_sharing_opps); + +/* + * Works only for OPP v2 bindings. + * + * cpumask should be already set to mask of cpu_dev->id. + * Returns -ENOENT if operating-points-v2 bindings aren't supported. + */ +int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) +{ + struct device_node *np, *tmp_np; + struct device *tcpu_dev; + int cpu, ret = 0; + + /* Get OPP descriptor node */ + np = _of_get_opp_desc_node(cpu_dev); + if (IS_ERR(np)) { + dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, + PTR_ERR(np)); + return -ENOENT; + } + + /* OPPs are shared ? */ + if (!of_property_read_bool(np, "opp-shared")) + goto put_cpu_node; + + for_each_possible_cpu(cpu) { + if (cpu == cpu_dev->id) + continue; + + tcpu_dev = get_cpu_device(cpu); + if (!tcpu_dev) { + dev_err(cpu_dev, "%s: failed to get cpu%d device\n", + __func__, cpu); + ret = -ENODEV; + goto put_cpu_node; + } + + /* Get OPP descriptor node */ + tmp_np = _of_get_opp_desc_node(tcpu_dev); + if (IS_ERR(tmp_np)) { + dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", + __func__, PTR_ERR(tmp_np)); + ret = PTR_ERR(tmp_np); + goto put_cpu_node; + } + + /* CPUs are sharing opp node */ + if (np == tmp_np) + cpumask_set_cpu(cpu, cpumask); + + of_node_put(tmp_np); + } + +put_cpu_node: + of_node_put(np); + return ret; +} +EXPORT_SYMBOL_GPL(of_get_cpus_sharing_opps); #endif diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 20324b579adc..bb52fae5b921 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -121,6 +121,10 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier( #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) int of_init_opp_table(struct device *dev); void of_free_opp_table(struct device *dev); +int of_cpumask_init_opp_table(cpumask_var_t cpumask); +void of_cpumask_free_opp_table(cpumask_var_t cpumask); +int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask); +int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask); #else static inline int of_init_opp_table(struct device *dev) { @@ -130,6 +134,25 @@ static inline int of_init_opp_table(struct device *dev) static inline void of_free_opp_table(struct device *dev) { } + +static inline int of_cpumask_init_opp_table(cpumask_var_t cpumask) +{ + return -ENOSYS; +} + +static inline void of_cpumask_free_opp_table(cpumask_var_t cpumask) +{ +} + +static inline int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) +{ + return -ENOSYS; +} + +static inline int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) +{ + return -ENOSYS; +} #endif #endif /* __LINUX_OPP_H__ */ -- cgit v1.3-6-gb490 From 19445b25e350ebebaa304bb2135619f643302947 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 9 Jul 2015 17:43:35 +0200 Subject: PM / OPP: add dev_pm_opp_is_turbo() helper Add dev_pm_opp_is_turbo() helper to verify if an opp is to be used only for turbo mode or not. Reviewed-by: Stephen Boyd Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 7 +++++++ 2 files changed, 41 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 663aae1c9834..204c6c945168 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -280,6 +280,40 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) } EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); +/** + * dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not + * @opp: opp for which turbo mode is being verified + * + * Turbo OPPs are not for normal use, and can be enabled (under certain + * conditions) for short duration of times to finish high throughput work + * quickly. Running on them for longer times may overheat the chip. + * + * Return: true if opp is turbo opp, else false. + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. This means that opp which could have been fetched by + * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are + * under RCU lock. The pointer returned by the opp_find_freq family must be + * used in the same section as the usage of this function with the pointer + * prior to unlocking with rcu_read_unlock() to maintain the integrity of the + * pointer. + */ +bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) +{ + struct dev_pm_opp *tmp_opp; + + opp_rcu_lockdep_assert(); + + tmp_opp = rcu_dereference(opp); + if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) { + pr_err("%s: Invalid parameters\n", __func__); + return false; + } + + return tmp_opp->turbo; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); + /** * dev_pm_opp_get_max_clock_latency() - Get max clock latency in nanoseconds * @dev: device for which we do this operation diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index bb52fae5b921..cab7ba55bedb 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -30,6 +30,8 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp); unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); +bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp); + int dev_pm_opp_get_opp_count(struct device *dev); unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev); @@ -63,6 +65,11 @@ static inline unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) return 0; } +static inline bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) +{ + return false; +} + static inline int dev_pm_opp_get_opp_count(struct device *dev) { return 0; -- cgit v1.3-6-gb490 From 1635e88885a8e16dec21206c981421f1f3c3b1df Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 6 Aug 2015 10:35:23 +0800 Subject: regmap: debugfs: Fix misuse of IS_ENABLED IS_ENABLED should only be used for CONFIG_* symbols. I have done a small test: #define REGMAP_ALLOW_WRITE_DEBUGFS IS_ENABLED(REGMAP_ALLOW_WRITE_DEBUGFS) returns 0. #define REGMAP_ALLOW_WRITE_DEBUGFS 0 IS_ENABLED(REGMAP_ALLOW_WRITE_DEBUGFS) returns 0. #define REGMAP_ALLOW_WRITE_DEBUGFS 1 IS_ENABLED(REGMAP_ALLOW_WRITE_DEBUGFS) returns 1. #define REGMAP_ALLOW_WRITE_DEBUGFS 2 IS_ENABLED(REGMAP_ALLOW_WRITE_DEBUGFS) returns 0. So fix the misuse of IS_ENABLED(REGMAP_ALLOW_WRITE_DEBUGFS) and switch to use #if defined(REGMAP_ALLOW_WRITE_DEBUGFS) instead. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 6a61e4fa73a2..f42f2bac6466 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -599,10 +599,11 @@ void regmap_debugfs_init(struct regmap *map, const char *name) if (map->max_register || regmap_readable(map, 0)) { umode_t registers_mode; - if (IS_ENABLED(REGMAP_ALLOW_WRITE_DEBUGFS)) - registers_mode = 0600; - else - registers_mode = 0400; +#if defined(REGMAP_ALLOW_WRITE_DEBUGFS) + registers_mode = 0600; +#else + registers_mode = 0400; +#endif debugfs_create_file("registers", registers_mode, map->debugfs, map, ®map_map_fops); -- cgit v1.3-6-gb490 From 3cfe7a74d42b7e3644f8b2b26aa20146d4f90f0f Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 8 Jul 2015 14:30:18 +0800 Subject: regmap: Use different lockdep class for each regmap init call Lockdep validator complains about recursive locking and deadlock when two different regmap instances are called in a nested order. That happens anytime a regmap read/write call needs to access another regmap. This is because, for performance reason, lockdep groups all locks initialized by the same mutex_init() in the same lock class. Therefore all regmap mutexes are in the same lock class, leading to lockdep "nested locking" warnings if a regmap accesses another regmap. In general, it is impossible to establish in advance the hierarchy of regmaps, so we make sure that each regmap init call initializes its own static lock_class_key. This is done by wrapping all regmap_init calls into macros. This also allows us to give meaningful names to the lock_class_key. For example, in rt5677 case, we have in /proc/lockdep_chains: irq_context: 0 [ffffffc0018d2198] &dev->mutex [ffffffc0018d2198] &dev->mutex [ffffffc001bd7f60] rt5677:5104:(&rt5677_regmap)->_lock [ffffffc001bd7f58] rt5677:5096:(&rt5677_regmap_physical)->_lock [ffffffc001b95448] &(&base->lock)->rlock The above would have resulted in a lockdep recursive warning previously. This is not the case anymore as the lockdep validator now clearly identifies the 2 regmaps as separate. Signed-off-by: Nicolas Boichat Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-ac97.c | 22 +++-- drivers/base/regmap/regmap-i2c.c | 22 +++-- drivers/base/regmap/regmap-mmio.c | 27 ++++-- drivers/base/regmap/regmap-spi.c | 22 +++-- drivers/base/regmap/regmap-spmi.c | 44 +++++---- drivers/base/regmap/regmap.c | 31 +++--- include/linux/regmap.h | 192 ++++++++++++++++++++++++++++---------- 7 files changed, 250 insertions(+), 110 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c index 8d304e2a943d..aa631be8b821 100644 --- a/drivers/base/regmap/regmap-ac97.c +++ b/drivers/base/regmap/regmap-ac97.c @@ -87,12 +87,15 @@ static const struct regmap_bus ac97_regmap_bus = { * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. */ -struct regmap *regmap_init_ac97(struct snd_ac97 *ac97, - const struct regmap_config *config) +struct regmap *__regmap_init_ac97(struct snd_ac97 *ac97, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { - return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config); + return __regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(regmap_init_ac97); +EXPORT_SYMBOL_GPL(__regmap_init_ac97); /** * devm_regmap_init_ac97(): Initialise AC'97 register map @@ -104,11 +107,14 @@ EXPORT_SYMBOL_GPL(regmap_init_ac97); * to a struct regmap. The regmap will be automatically freed by the * device management code. */ -struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97, - const struct regmap_config *config) +struct regmap *__devm_regmap_init_ac97(struct snd_ac97 *ac97, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { - return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config); + return __devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(devm_regmap_init_ac97); +EXPORT_SYMBOL_GPL(__devm_regmap_init_ac97); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 4b76e33110a2..3163b22e2baf 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -242,17 +242,20 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. */ -struct regmap *regmap_init_i2c(struct i2c_client *i2c, - const struct regmap_config *config) +struct regmap *__regmap_init_i2c(struct i2c_client *i2c, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config); if (IS_ERR(bus)) return ERR_CAST(bus); - return regmap_init(&i2c->dev, bus, &i2c->dev, config); + return __regmap_init(&i2c->dev, bus, &i2c->dev, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(regmap_init_i2c); +EXPORT_SYMBOL_GPL(__regmap_init_i2c); /** * devm_regmap_init_i2c(): Initialise managed register map @@ -264,16 +267,19 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c); * to a struct regmap. The regmap will be automatically freed by the * device management code. */ -struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, - const struct regmap_config *config) +struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config); if (IS_ERR(bus)) return ERR_CAST(bus); - return devm_regmap_init(&i2c->dev, bus, &i2c->dev, config); + return __devm_regmap_init(&i2c->dev, bus, &i2c->dev, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(devm_regmap_init_i2c); +EXPORT_SYMBOL_GPL(__devm_regmap_init_i2c); MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 04a329a377e9..a1b2b270e4bc 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -307,9 +307,11 @@ err_free: * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. */ -struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, - void __iomem *regs, - const struct regmap_config *config) +struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, + void __iomem *regs, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { struct regmap_mmio_context *ctx; @@ -317,9 +319,10 @@ struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, if (IS_ERR(ctx)) return ERR_CAST(ctx); - return regmap_init(dev, ®map_mmio, ctx, config); + return __regmap_init(dev, ®map_mmio, ctx, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(regmap_init_mmio_clk); +EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk); /** * devm_regmap_init_mmio_clk(): Initialise managed register map with clock @@ -333,9 +336,12 @@ EXPORT_SYMBOL_GPL(regmap_init_mmio_clk); * to a struct regmap. The regmap will be automatically freed by the * device management code. */ -struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, - void __iomem *regs, - const struct regmap_config *config) +struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, + const char *clk_id, + void __iomem *regs, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { struct regmap_mmio_context *ctx; @@ -343,8 +349,9 @@ struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, if (IS_ERR(ctx)) return ERR_CAST(ctx); - return devm_regmap_init(dev, ®map_mmio, ctx, config); + return __devm_regmap_init(dev, ®map_mmio, ctx, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(devm_regmap_init_mmio_clk); +EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk); MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 53d1148e80a0..4c7850d660d1 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -122,12 +122,15 @@ static struct regmap_bus regmap_spi = { * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. */ -struct regmap *regmap_init_spi(struct spi_device *spi, - const struct regmap_config *config) +struct regmap *__regmap_init_spi(struct spi_device *spi, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { - return regmap_init(&spi->dev, ®map_spi, &spi->dev, config); + return __regmap_init(&spi->dev, ®map_spi, &spi->dev, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(regmap_init_spi); +EXPORT_SYMBOL_GPL(__regmap_init_spi); /** * devm_regmap_init_spi(): Initialise register map @@ -139,11 +142,14 @@ EXPORT_SYMBOL_GPL(regmap_init_spi); * to a struct regmap. The map will be automatically freed by the * device management code. */ -struct regmap *devm_regmap_init_spi(struct spi_device *spi, - const struct regmap_config *config) +struct regmap *__devm_regmap_init_spi(struct spi_device *spi, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { - return devm_regmap_init(&spi->dev, ®map_spi, &spi->dev, config); + return __devm_regmap_init(&spi->dev, ®map_spi, &spi->dev, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(devm_regmap_init_spi); +EXPORT_SYMBOL_GPL(__devm_regmap_init_spi); MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap-spmi.c b/drivers/base/regmap/regmap-spmi.c index d7026dc33388..7f50f5862d39 100644 --- a/drivers/base/regmap/regmap-spmi.c +++ b/drivers/base/regmap/regmap-spmi.c @@ -99,12 +99,15 @@ static struct regmap_bus regmap_spmi_base = { * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. */ -struct regmap *regmap_init_spmi_base(struct spmi_device *sdev, - const struct regmap_config *config) +struct regmap *__regmap_init_spmi_base(struct spmi_device *sdev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { - return regmap_init(&sdev->dev, ®map_spmi_base, sdev, config); + return __regmap_init(&sdev->dev, ®map_spmi_base, sdev, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(regmap_init_spmi_base); +EXPORT_SYMBOL_GPL(__regmap_init_spmi_base); /** * devm_regmap_init_spmi_base(): Create managed regmap for Base register space @@ -115,12 +118,15 @@ EXPORT_SYMBOL_GPL(regmap_init_spmi_base); * to a struct regmap. The regmap will be automatically freed by the * device management code. */ -struct regmap *devm_regmap_init_spmi_base(struct spmi_device *sdev, - const struct regmap_config *config) +struct regmap *__devm_regmap_init_spmi_base(struct spmi_device *sdev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { - return devm_regmap_init(&sdev->dev, ®map_spmi_base, sdev, config); + return __devm_regmap_init(&sdev->dev, ®map_spmi_base, sdev, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_base); +EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_base); static int regmap_spmi_ext_read(void *context, const void *reg, size_t reg_size, @@ -230,12 +236,15 @@ static struct regmap_bus regmap_spmi_ext = { * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. */ -struct regmap *regmap_init_spmi_ext(struct spmi_device *sdev, - const struct regmap_config *config) +struct regmap *__regmap_init_spmi_ext(struct spmi_device *sdev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { - return regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config); + return __regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(regmap_init_spmi_ext); +EXPORT_SYMBOL_GPL(__regmap_init_spmi_ext); /** * devm_regmap_init_spmi_ext(): Create managed regmap for Ext register space @@ -246,11 +255,14 @@ EXPORT_SYMBOL_GPL(regmap_init_spmi_ext); * to a struct regmap. The regmap will be automatically freed by the * device management code. */ -struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *sdev, - const struct regmap_config *config) +struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { - return devm_regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config); + return __devm_regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config, + lock_key, lock_name); } -EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_ext); +EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext); MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7111d04f2621..b9fddccd6e06 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -527,10 +527,12 @@ EXPORT_SYMBOL_GPL(regmap_get_val_endian); * a struct regmap. This function should generally not be called * directly, it should be called by bus-specific init functions. */ -struct regmap *regmap_init(struct device *dev, - const struct regmap_bus *bus, - void *bus_context, - const struct regmap_config *config) +struct regmap *__regmap_init(struct device *dev, + const struct regmap_bus *bus, + void *bus_context, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { struct regmap *map; int ret = -EINVAL; @@ -556,10 +558,14 @@ struct regmap *regmap_init(struct device *dev, spin_lock_init(&map->spinlock); map->lock = regmap_lock_spinlock; map->unlock = regmap_unlock_spinlock; + lockdep_set_class_and_name(&map->spinlock, + lock_key, lock_name); } else { mutex_init(&map->mutex); map->lock = regmap_lock_mutex; map->unlock = regmap_unlock_mutex; + lockdep_set_class_and_name(&map->mutex, + lock_key, lock_name); } map->lock_arg = map; } @@ -899,7 +905,7 @@ err_map: err: return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(regmap_init); +EXPORT_SYMBOL_GPL(__regmap_init); static void devm_regmap_release(struct device *dev, void *res) { @@ -919,10 +925,12 @@ static void devm_regmap_release(struct device *dev, void *res) * directly, it should be called by bus-specific init functions. The * map will be automatically freed by the device management code. */ -struct regmap *devm_regmap_init(struct device *dev, - const struct regmap_bus *bus, - void *bus_context, - const struct regmap_config *config) +struct regmap *__devm_regmap_init(struct device *dev, + const struct regmap_bus *bus, + void *bus_context, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) { struct regmap **ptr, *regmap; @@ -930,7 +938,8 @@ struct regmap *devm_regmap_init(struct device *dev, if (!ptr) return ERR_PTR(-ENOMEM); - regmap = regmap_init(dev, bus, bus_context, config); + regmap = __regmap_init(dev, bus, bus_context, config, + lock_key, lock_name); if (!IS_ERR(regmap)) { *ptr = regmap; devres_add(dev, ptr); @@ -940,7 +949,7 @@ struct regmap *devm_regmap_init(struct device *dev, return regmap; } -EXPORT_SYMBOL_GPL(devm_regmap_init); +EXPORT_SYMBOL_GPL(__devm_regmap_init); static void regmap_field_init(struct regmap_field *rm_field, struct regmap *regmap, struct reg_field reg_field) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 59c55ea0f0b5..5d7027286032 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -17,6 +17,7 @@ #include #include #include +#include struct module; struct device; @@ -324,46 +325,147 @@ struct regmap_bus { enum regmap_endian val_format_endian_default; }; -struct regmap *regmap_init(struct device *dev, - const struct regmap_bus *bus, - void *bus_context, - const struct regmap_config *config); -int regmap_attach_dev(struct device *dev, struct regmap *map, - const struct regmap_config *config); -struct regmap *regmap_init_i2c(struct i2c_client *i2c, - const struct regmap_config *config); -struct regmap *regmap_init_spi(struct spi_device *dev, - const struct regmap_config *config); -struct regmap *regmap_init_spmi_base(struct spmi_device *dev, - const struct regmap_config *config); -struct regmap *regmap_init_spmi_ext(struct spmi_device *dev, - const struct regmap_config *config); -struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, - void __iomem *regs, - const struct regmap_config *config); -struct regmap *regmap_init_ac97(struct snd_ac97 *ac97, - const struct regmap_config *config); - -struct regmap *devm_regmap_init(struct device *dev, - const struct regmap_bus *bus, - void *bus_context, - const struct regmap_config *config); -struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, - const struct regmap_config *config); -struct regmap *devm_regmap_init_spi(struct spi_device *dev, - const struct regmap_config *config); -struct regmap *devm_regmap_init_spmi_base(struct spmi_device *dev, - const struct regmap_config *config); -struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *dev, - const struct regmap_config *config); -struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, - void __iomem *regs, - const struct regmap_config *config); -struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97, - const struct regmap_config *config); +/* + * __regmap_init functions. + * + * These functions take a lock key and name parameter, and should not be called + * directly. Instead, use the regmap_init macros that generate a key and name + * for each call. + */ +struct regmap *__regmap_init(struct device *dev, + const struct regmap_bus *bus, + void *bus_context, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__regmap_init_i2c(struct i2c_client *i2c, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__regmap_init_spi(struct spi_device *dev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__regmap_init_spmi_base(struct spmi_device *dev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__regmap_init_spmi_ext(struct spmi_device *dev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, + void __iomem *regs, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__regmap_init_ac97(struct snd_ac97 *ac97, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); + +struct regmap *__devm_regmap_init(struct device *dev, + const struct regmap_bus *bus, + void *bus_context, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__devm_regmap_init_spi(struct spi_device *dev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__devm_regmap_init_spmi_base(struct spmi_device *dev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *dev, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, + const char *clk_id, + void __iomem *regs, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +struct regmap *__devm_regmap_init_ac97(struct snd_ac97 *ac97, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); +/* + * Wrapper for regmap_init macros to include a unique lockdep key and name + * for each call. No-op if CONFIG_LOCKDEP is not set. + * + * @fn: Real function to call (in the form __[*_]regmap_init[_*]) + * @name: Config variable name (#config in the calling macro) + **/ +#ifdef CONFIG_LOCKDEP +#define __regmap_lockdep_wrapper(fn, name, ...) \ +( \ + ({ \ + static struct lock_class_key _key; \ + fn(__VA_ARGS__, &_key, \ + KBUILD_BASENAME ":" \ + __stringify(__LINE__) ":" \ + "(" name ")->lock"); \ + }) \ +) +#else +#define __regmap_lockdep_wrapper(fn, name, ...) fn(__VA_ARGS__, NULL, NULL) +#endif + +#define regmap_init(dev, bus, bus_context, config) \ + __regmap_lockdep_wrapper(__regmap_init, #config, \ + dev, bus, bus_context, config) +int regmap_attach_dev(struct device *dev, struct regmap *map, + const struct regmap_config *config); +#define regmap_init_i2c(i2c, config) \ + __regmap_lockdep_wrapper(__regmap_init_i2c, #config, \ + i2c, config) +#define regmap_init_spi(dev, config) \ + __regmap_lockdep_wrapper(__regmap_init_spi, #config, \ + dev, config) +#define regmap_init_spmi_base(dev, config) \ + __regmap_lockdep_wrapper(__regmap_init_spmi_base, #config, \ + dev, config) +#define regmap_init_spmi_ext(dev, config) \ + __regmap_lockdep_wrapper(__regmap_init_spmi_ext, #config, \ + dev, config) +#define regmap_init_mmio_clk(dev, clk_id, regs, config) \ + __regmap_lockdep_wrapper(__regmap_init_mmio_clk, #config, \ + dev, clk_id, regs, config) +#define regmap_init_ac97(ac97, config) \ + __regmap_lockdep_wrapper(__regmap_init_ac97, #config, \ + ac97, config) bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); +#define devm_regmap_init(dev, bus, bus_context, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init, #config, \ + dev, bus, bus_context, config) +#define devm_regmap_init_i2c(i2c, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_i2c, #config, \ + i2c, config) +#define devm_regmap_init_spi(dev, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_spi, #config, \ + dev, config) +#define devm_regmap_init_spmi_base(dev, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_spmi_base, #config, \ + dev, config) +#define devm_regmap_init_spmi_ext(dev, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_spmi_ext, #config, \ + dev, config) +#define devm_regmap_init_mmio_clk(dev, clk_id, regs, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_mmio_clk, #config, \ + dev, clk_id, regs, config) +#define devm_regmap_init_ac97(ac97, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_ac97, #config, \ + ac97, config) + /** * regmap_init_mmio(): Initialise register map * @@ -374,12 +476,8 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. */ -static inline struct regmap *regmap_init_mmio(struct device *dev, - void __iomem *regs, - const struct regmap_config *config) -{ - return regmap_init_mmio_clk(dev, NULL, regs, config); -} +#define regmap_init_mmio(dev, regs, config) \ + regmap_init_mmio_clk(dev, NULL, regs, config) /** * devm_regmap_init_mmio(): Initialise managed register map @@ -392,12 +490,8 @@ static inline struct regmap *regmap_init_mmio(struct device *dev, * to a struct regmap. The regmap will be automatically freed by the * device management code. */ -static inline struct regmap *devm_regmap_init_mmio(struct device *dev, - void __iomem *regs, - const struct regmap_config *config) -{ - return devm_regmap_init_mmio_clk(dev, NULL, regs, config); -} +#define devm_regmap_init_mmio(dev, regs, config) \ + devm_regmap_init_mmio_clk(dev, NULL, regs, config) void regmap_exit(struct regmap *map); int regmap_reinit_cache(struct regmap *map, -- cgit v1.3-6-gb490 From 1ed8111443ae8caa455e7107031da36d1a6d351a Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 11 Aug 2015 18:04:21 +0800 Subject: regmap: Move documentation to regmap.h Init functions defined in regmap*.c files are now prefixed with __, take lockdep key and class parameters, and should not be called directly: move the documentation to regmap.h, where the macros are defined. Signed-off-by: Nicolas Boichat Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-ac97.c | 19 ----- drivers/base/regmap/regmap-i2c.c | 19 ----- drivers/base/regmap/regmap-mmio.c | 23 ----- drivers/base/regmap/regmap-spi.c | 19 ----- drivers/base/regmap/regmap-spmi.c | 34 -------- drivers/base/regmap/regmap.c | 25 ------ include/linux/regmap.h | 173 +++++++++++++++++++++++++++++++++++--- 7 files changed, 162 insertions(+), 150 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c index aa631be8b821..c03ebfd4c731 100644 --- a/drivers/base/regmap/regmap-ac97.c +++ b/drivers/base/regmap/regmap-ac97.c @@ -78,15 +78,6 @@ static const struct regmap_bus ac97_regmap_bus = { .reg_read = regmap_ac97_reg_read, }; -/** - * regmap_init_ac97(): Initialise AC'97 register map - * - * @ac97: Device that will be interacted with - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer to - * a struct regmap. - */ struct regmap *__regmap_init_ac97(struct snd_ac97 *ac97, const struct regmap_config *config, struct lock_class_key *lock_key, @@ -97,16 +88,6 @@ struct regmap *__regmap_init_ac97(struct snd_ac97 *ac97, } EXPORT_SYMBOL_GPL(__regmap_init_ac97); -/** - * devm_regmap_init_ac97(): Initialise AC'97 register map - * - * @ac97: Device that will be interacted with - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer - * to a struct regmap. The regmap will be automatically freed by the - * device management code. - */ struct regmap *__devm_regmap_init_ac97(struct snd_ac97 *ac97, const struct regmap_config *config, struct lock_class_key *lock_key, diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 3163b22e2baf..7007d6ea333c 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -233,15 +233,6 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, return ERR_PTR(-ENOTSUPP); } -/** - * regmap_init_i2c(): Initialise register map - * - * @i2c: Device that will be interacted with - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer to - * a struct regmap. - */ struct regmap *__regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config, struct lock_class_key *lock_key, @@ -257,16 +248,6 @@ struct regmap *__regmap_init_i2c(struct i2c_client *i2c, } EXPORT_SYMBOL_GPL(__regmap_init_i2c); -/** - * devm_regmap_init_i2c(): Initialise managed register map - * - * @i2c: Device that will be interacted with - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer - * to a struct regmap. The regmap will be automatically freed by the - * device management code. - */ struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config, struct lock_class_key *lock_key, diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index a1b2b270e4bc..426a57e41ac7 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -296,17 +296,6 @@ err_free: return ERR_PTR(ret); } -/** - * regmap_init_mmio_clk(): Initialise register map with register clock - * - * @dev: Device that will be interacted with - * @clk_id: register clock consumer ID - * @regs: Pointer to memory-mapped IO region - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer to - * a struct regmap. - */ struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, void __iomem *regs, const struct regmap_config *config, @@ -324,18 +313,6 @@ struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, } EXPORT_SYMBOL_GPL(__regmap_init_mmio_clk); -/** - * devm_regmap_init_mmio_clk(): Initialise managed register map with clock - * - * @dev: Device that will be interacted with - * @clk_id: register clock consumer ID - * @regs: Pointer to memory-mapped IO region - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer - * to a struct regmap. The regmap will be automatically freed by the - * device management code. - */ struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, void __iomem *regs, diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 4c7850d660d1..edd9a839d004 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -113,15 +113,6 @@ static struct regmap_bus regmap_spi = { .val_format_endian_default = REGMAP_ENDIAN_BIG, }; -/** - * regmap_init_spi(): Initialise register map - * - * @spi: Device that will be interacted with - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer to - * a struct regmap. - */ struct regmap *__regmap_init_spi(struct spi_device *spi, const struct regmap_config *config, struct lock_class_key *lock_key, @@ -132,16 +123,6 @@ struct regmap *__regmap_init_spi(struct spi_device *spi, } EXPORT_SYMBOL_GPL(__regmap_init_spi); -/** - * devm_regmap_init_spi(): Initialise register map - * - * @spi: Device that will be interacted with - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer - * to a struct regmap. The map will be automatically freed by the - * device management code. - */ struct regmap *__devm_regmap_init_spi(struct spi_device *spi, const struct regmap_config *config, struct lock_class_key *lock_key, diff --git a/drivers/base/regmap/regmap-spmi.c b/drivers/base/regmap/regmap-spmi.c index 7f50f5862d39..7e58f6560399 100644 --- a/drivers/base/regmap/regmap-spmi.c +++ b/drivers/base/regmap/regmap-spmi.c @@ -91,14 +91,6 @@ static struct regmap_bus regmap_spmi_base = { .val_format_endian_default = REGMAP_ENDIAN_NATIVE, }; -/** - * regmap_init_spmi_base(): Create regmap for the Base register space - * @sdev: SPMI device that will be interacted with - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer to - * a struct regmap. - */ struct regmap *__regmap_init_spmi_base(struct spmi_device *sdev, const struct regmap_config *config, struct lock_class_key *lock_key, @@ -109,15 +101,6 @@ struct regmap *__regmap_init_spmi_base(struct spmi_device *sdev, } EXPORT_SYMBOL_GPL(__regmap_init_spmi_base); -/** - * devm_regmap_init_spmi_base(): Create managed regmap for Base register space - * @sdev: SPMI device that will be interacted with - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer - * to a struct regmap. The regmap will be automatically freed by the - * device management code. - */ struct regmap *__devm_regmap_init_spmi_base(struct spmi_device *sdev, const struct regmap_config *config, struct lock_class_key *lock_key, @@ -228,14 +211,6 @@ static struct regmap_bus regmap_spmi_ext = { .val_format_endian_default = REGMAP_ENDIAN_NATIVE, }; -/** - * regmap_init_spmi_ext(): Create regmap for Ext register space - * @sdev: Device that will be interacted with - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer to - * a struct regmap. - */ struct regmap *__regmap_init_spmi_ext(struct spmi_device *sdev, const struct regmap_config *config, struct lock_class_key *lock_key, @@ -246,15 +221,6 @@ struct regmap *__regmap_init_spmi_ext(struct spmi_device *sdev, } EXPORT_SYMBOL_GPL(__regmap_init_spmi_ext); -/** - * devm_regmap_init_spmi_ext(): Create managed regmap for Ext register space - * @sdev: SPMI device that will be interacted with - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer - * to a struct regmap. The regmap will be automatically freed by the - * device management code. - */ struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev, const struct regmap_config *config, struct lock_class_key *lock_key, diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index b9fddccd6e06..53ba9d9e17d1 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -515,18 +515,6 @@ enum regmap_endian regmap_get_val_endian(struct device *dev, } EXPORT_SYMBOL_GPL(regmap_get_val_endian); -/** - * regmap_init(): Initialise register map - * - * @dev: Device that will be interacted with - * @bus: Bus-specific callbacks to use with device - * @bus_context: Data passed to bus-specific callbacks - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer to - * a struct regmap. This function should generally not be called - * directly, it should be called by bus-specific init functions. - */ struct regmap *__regmap_init(struct device *dev, const struct regmap_bus *bus, void *bus_context, @@ -912,19 +900,6 @@ static void devm_regmap_release(struct device *dev, void *res) regmap_exit(*(struct regmap **)res); } -/** - * devm_regmap_init(): Initialise managed register map - * - * @dev: Device that will be interacted with - * @bus: Bus-specific callbacks to use with device - * @bus_context: Data passed to bus-specific callbacks - * @config: Configuration for register map - * - * The return value will be an ERR_PTR() on error or a valid pointer - * to a struct regmap. This function should generally not be called - * directly, it should be called by bus-specific init functions. The - * map will be automatically freed by the device management code. - */ struct regmap *__devm_regmap_init(struct device *dev, const struct regmap_bus *bus, void *bus_context, diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 5d7027286032..ee032eca630d 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -419,65 +419,202 @@ struct regmap *__devm_regmap_init_ac97(struct snd_ac97 *ac97, #define __regmap_lockdep_wrapper(fn, name, ...) fn(__VA_ARGS__, NULL, NULL) #endif +/** + * regmap_init(): Initialise register map + * + * @dev: Device that will be interacted with + * @bus: Bus-specific callbacks to use with device + * @bus_context: Data passed to bus-specific callbacks + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. This function should generally not be called + * directly, it should be called by bus-specific init functions. + */ #define regmap_init(dev, bus, bus_context, config) \ __regmap_lockdep_wrapper(__regmap_init, #config, \ dev, bus, bus_context, config) int regmap_attach_dev(struct device *dev, struct regmap *map, const struct regmap_config *config); + +/** + * regmap_init_i2c(): Initialise register map + * + * @i2c: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ #define regmap_init_i2c(i2c, config) \ __regmap_lockdep_wrapper(__regmap_init_i2c, #config, \ i2c, config) + +/** + * regmap_init_spi(): Initialise register map + * + * @spi: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ #define regmap_init_spi(dev, config) \ __regmap_lockdep_wrapper(__regmap_init_spi, #config, \ dev, config) + +/** + * regmap_init_spmi_base(): Create regmap for the Base register space + * @sdev: SPMI device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ #define regmap_init_spmi_base(dev, config) \ __regmap_lockdep_wrapper(__regmap_init_spmi_base, #config, \ dev, config) + +/** + * regmap_init_spmi_ext(): Create regmap for Ext register space + * @sdev: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ #define regmap_init_spmi_ext(dev, config) \ __regmap_lockdep_wrapper(__regmap_init_spmi_ext, #config, \ dev, config) + +/** + * regmap_init_mmio_clk(): Initialise register map with register clock + * + * @dev: Device that will be interacted with + * @clk_id: register clock consumer ID + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ #define regmap_init_mmio_clk(dev, clk_id, regs, config) \ __regmap_lockdep_wrapper(__regmap_init_mmio_clk, #config, \ dev, clk_id, regs, config) + +/** + * regmap_init_mmio(): Initialise register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +#define regmap_init_mmio(dev, regs, config) \ + regmap_init_mmio_clk(dev, NULL, regs, config) + +/** + * regmap_init_ac97(): Initialise AC'97 register map + * + * @ac97: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ #define regmap_init_ac97(ac97, config) \ __regmap_lockdep_wrapper(__regmap_init_ac97, #config, \ ac97, config) bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); +/** + * devm_regmap_init(): Initialise managed register map + * + * @dev: Device that will be interacted with + * @bus: Bus-specific callbacks to use with device + * @bus_context: Data passed to bus-specific callbacks + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. This function should generally not be called + * directly, it should be called by bus-specific init functions. The + * map will be automatically freed by the device management code. + */ #define devm_regmap_init(dev, bus, bus_context, config) \ __regmap_lockdep_wrapper(__devm_regmap_init, #config, \ dev, bus, bus_context, config) + +/** + * devm_regmap_init_i2c(): Initialise managed register map + * + * @i2c: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ #define devm_regmap_init_i2c(i2c, config) \ __regmap_lockdep_wrapper(__devm_regmap_init_i2c, #config, \ i2c, config) + +/** + * devm_regmap_init_spi(): Initialise register map + * + * @spi: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The map will be automatically freed by the + * device management code. + */ #define devm_regmap_init_spi(dev, config) \ __regmap_lockdep_wrapper(__devm_regmap_init_spi, #config, \ dev, config) + +/** + * devm_regmap_init_spmi_base(): Create managed regmap for Base register space + * @sdev: SPMI device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ #define devm_regmap_init_spmi_base(dev, config) \ __regmap_lockdep_wrapper(__devm_regmap_init_spmi_base, #config, \ dev, config) + +/** + * devm_regmap_init_spmi_ext(): Create managed regmap for Ext register space + * @sdev: SPMI device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ #define devm_regmap_init_spmi_ext(dev, config) \ __regmap_lockdep_wrapper(__devm_regmap_init_spmi_ext, #config, \ dev, config) -#define devm_regmap_init_mmio_clk(dev, clk_id, regs, config) \ - __regmap_lockdep_wrapper(__devm_regmap_init_mmio_clk, #config, \ - dev, clk_id, regs, config) -#define devm_regmap_init_ac97(ac97, config) \ - __regmap_lockdep_wrapper(__devm_regmap_init_ac97, #config, \ - ac97, config) /** - * regmap_init_mmio(): Initialise register map + * devm_regmap_init_mmio_clk(): Initialise managed register map with clock * * @dev: Device that will be interacted with + * @clk_id: register clock consumer ID * @regs: Pointer to memory-mapped IO region * @config: Configuration for register map * - * The return value will be an ERR_PTR() on error or a valid pointer to - * a struct regmap. + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. */ -#define regmap_init_mmio(dev, regs, config) \ - regmap_init_mmio_clk(dev, NULL, regs, config) +#define devm_regmap_init_mmio_clk(dev, clk_id, regs, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_mmio_clk, #config, \ + dev, clk_id, regs, config) /** * devm_regmap_init_mmio(): Initialise managed register map @@ -493,6 +630,20 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); #define devm_regmap_init_mmio(dev, regs, config) \ devm_regmap_init_mmio_clk(dev, NULL, regs, config) +/** + * devm_regmap_init_ac97(): Initialise AC'97 register map + * + * @ac97: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +#define devm_regmap_init_ac97(ac97, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_ac97, #config, \ + ac97, config) + void regmap_exit(struct regmap *map); int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config); -- cgit v1.3-6-gb490 From 2f9b660b2128c92d66f18ac7fbd7c39a91cec159 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 12 Aug 2015 12:12:28 +0200 Subject: regmap: Fix integertypes for register address and value These values are defined as unsigned int in the struct and are assigned to int values. This patch fixes the type to be unsigned int instead. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7111d04f2621..9b4badc2479d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1768,8 +1768,8 @@ static int _regmap_raw_multi_reg_write(struct regmap *map, u8 = buf; for (i = 0; i < num_regs; i++) { - int reg = regs[i].reg; - int val = regs[i].def; + unsigned int reg = regs[i].reg; + unsigned int val = regs[i].def; trace_regmap_hw_write_start(map, reg, 1); map->format.format_reg(u8, reg, map->reg_shift); u8 += reg_bytes + pad_bytes; -- cgit v1.3-6-gb490 From b486afbd1baf796a9e4b793b2f9121c12e1469af Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 12 Aug 2015 15:02:19 +0800 Subject: regmap: fix typos in regmap.c There are two typos in drivers/base/regmap/regmap.c, and they may introduce some noise when checking new patches. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7111d04f2621..cae3f268267e 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1740,7 +1740,7 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write); * * the (register,newvalue) pairs in regs have not been formatted, but * they are all in the same page and have been changed to being page - * relative. The page register has been written if that was neccessary. + * relative. The page register has been written if that was necessary. */ static int _regmap_raw_multi_reg_write(struct regmap *map, const struct reg_default *regs, @@ -2050,7 +2050,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, /* * Some buses or devices flag reads by setting the high bits in the - * register addresss; since it's always the high bits for all + * register address; since it's always the high bits for all * current formats we can do this here rather than in * formatting. This may break if we get interesting formats. */ -- cgit v1.3-6-gb490 From 4c96b7dc0d393f12c17e0d81db15aa4a820a6ab3 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Wed, 12 Aug 2015 17:06:26 -0500 Subject: Add a matching set of device_ functions for determining mac/phy OF has some helper functions for parsing MAC and PHY settings. In cases where the platform is providing this information rather than the device itself, there needs to be similar functions for ACPI. These functions are slightly modified versions of the ones in of_net which can use information provided via DT or ACPI. Signed-off-by: Jeremy Linton Signed-off-by: David S. Miller --- drivers/base/property.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/property.h | 4 +++ 2 files changed, 77 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/property.c b/drivers/base/property.c index f3f6d167f3f1..2e8cd147f02d 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include /** * device_add_property_set - Add a collection of properties to a device object. @@ -533,3 +535,74 @@ bool device_dma_is_coherent(struct device *dev) return coherent; } EXPORT_SYMBOL_GPL(device_dma_is_coherent); + +/** + * device_get_phy_mode - Get phy mode for given device_node + * @dev: Pointer to the given device + * + * The function gets phy interface string from property 'phy-mode' or + * 'phy-connection-type', and return its index in phy_modes table, or errno in + * error case. + */ +int device_get_phy_mode(struct device *dev) +{ + const char *pm; + int err, i; + + err = device_property_read_string(dev, "phy-mode", &pm); + if (err < 0) + err = device_property_read_string(dev, + "phy-connection-type", &pm); + if (err < 0) + return err; + + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) + if (!strcasecmp(pm, phy_modes(i))) + return i; + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(device_get_phy_mode); + +static void *device_get_mac_addr(struct device *dev, + const char *name, char *addr, + int alen) +{ + int ret = device_property_read_u8_array(dev, name, addr, alen); + + if (ret == 0 && is_valid_ether_addr(addr)) + return addr; + return NULL; +} + +/** + * Search the device tree for the best MAC address to use. 'mac-address' is + * checked first, because that is supposed to contain to "most recent" MAC + * address. If that isn't set, then 'local-mac-address' is checked next, + * because that is the default address. If that isn't set, then the obsolete + * 'address' is checked, just in case we're using an old device tree. + * + * Note that the 'address' property is supposed to contain a virtual address of + * the register set, but some DTS files have redefined that property to be the + * MAC address. + * + * All-zero MAC addresses are rejected, because those could be properties that + * exist in the device tree, but were not set by U-Boot. For example, the + * DTS could define 'mac-address' and 'local-mac-address', with zero MAC + * addresses. Some older U-Boots only initialized 'local-mac-address'. In + * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists + * but is all zeros. +*/ +void *device_get_mac_address(struct device *dev, char *addr, int alen) +{ + addr = device_get_mac_addr(dev, "mac-address", addr, alen); + if (addr) + return addr; + + addr = device_get_mac_addr(dev, "local-mac-address", addr, alen); + if (addr) + return addr; + + return device_get_mac_addr(dev, "address", addr, alen); +} +EXPORT_SYMBOL(device_get_mac_address); diff --git a/include/linux/property.h b/include/linux/property.h index 76ebde9c11d4..a59c6ee566c2 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -166,4 +166,8 @@ void device_add_property_set(struct device *dev, struct property_set *pset); bool device_dma_is_coherent(struct device *dev); +int device_get_phy_mode(struct device *dev); + +void *device_get_mac_address(struct device *dev, char *addr, int alen); + #endif /* _LINUX_PROPERTY_H_ */ -- cgit v1.3-6-gb490 From 07ea400e1b26726f21b2c2299d187d6eb7eb4324 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 12 Aug 2015 12:12:33 +0200 Subject: regmap: Fix regmap_can_raw_write check This function is missing a check if map->bus->write is implemented. If it is not implemented arbitrary raw writes are not possible. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7111d04f2621..8e7208d92de1 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1382,7 +1382,8 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, */ bool regmap_can_raw_write(struct regmap *map) { - return map->bus && map->format.format_val && map->format.format_reg; + return map->bus && map->bus->write && map->format.format_val && + map->format.format_reg; } EXPORT_SYMBOL_GPL(regmap_can_raw_write); -- cgit v1.3-6-gb490 From 9a16ea900fadc88714e3a32214dea8e968ccd889 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 20 Aug 2015 11:12:35 +0200 Subject: regmap: regmap_raw_read return error on !bus->read Return -ENOTSUPP if map->bus->read is not implemented and we do not use the cache. This code path would directly use bus->read would run into an NULL pointer for the read function. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7111d04f2621..fc14a7cc8c85 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2184,6 +2184,11 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || map->cache_type == REGCACHE_NONE) { + if (!map->bus->read) { + ret = -ENOTSUPP; + goto out; + } + /* Physical block read if there's no cache involved */ ret = _regmap_raw_read(map, reg, val, val_len); -- cgit v1.3-6-gb490 From 2f710a3a8089c12dfe3c0cf04bb0a3dee3dea019 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Wed, 19 Aug 2015 11:46:42 -0500 Subject: device property: Add ETH_ALEN check, update comments. This patch adds MAC address length check back into the device_get_mac_addr() function before calling is_valid_ether_addr() similar to the way the OF routine does it. Update the comments for the two new functions. Signed-off-by: Jeremy Linton Signed-off-by: David S. Miller --- drivers/base/property.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/property.c b/drivers/base/property.c index 2e8cd147f02d..4c2082899322 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -537,7 +537,7 @@ bool device_dma_is_coherent(struct device *dev) EXPORT_SYMBOL_GPL(device_dma_is_coherent); /** - * device_get_phy_mode - Get phy mode for given device_node + * device_get_phy_mode - Get phy mode for given device * @dev: Pointer to the given device * * The function gets phy interface string from property 'phy-mode' or @@ -570,13 +570,18 @@ static void *device_get_mac_addr(struct device *dev, { int ret = device_property_read_u8_array(dev, name, addr, alen); - if (ret == 0 && is_valid_ether_addr(addr)) + if (ret == 0 && alen == ETH_ALEN && is_valid_ether_addr(addr)) return addr; return NULL; } /** - * Search the device tree for the best MAC address to use. 'mac-address' is + * device_get_mac_address - Get the MAC for a given device + * @dev: Pointer to the device + * @addr: Address of buffer to store the MAC in + * @alen: Length of the buffer pointed to by addr, should be ETH_ALEN + * + * Search the firmware node for the best MAC address to use. 'mac-address' is * checked first, because that is supposed to contain to "most recent" MAC * address. If that isn't set, then 'local-mac-address' is checked next, * because that is the default address. If that isn't set, then the obsolete @@ -587,11 +592,11 @@ static void *device_get_mac_addr(struct device *dev, * MAC address. * * All-zero MAC addresses are rejected, because those could be properties that - * exist in the device tree, but were not set by U-Boot. For example, the - * DTS could define 'mac-address' and 'local-mac-address', with zero MAC - * addresses. Some older U-Boots only initialized 'local-mac-address'. In - * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists - * but is all zeros. + * exist in the firmware tables, but were not updated by the firmware. For + * example, the DTS could define 'mac-address' and 'local-mac-address', with + * zero MAC addresses. Some older U-Boots only initialized 'local-mac-address'. + * In this case, the real MAC is in 'local-mac-address', and 'mac-address' + * exists but is all zeros. */ void *device_get_mac_address(struct device *dev, char *addr, int alen) { -- cgit v1.3-6-gb490 From c594b7f21d7d02115e828db46fddbba1da7ed1b8 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 21 Aug 2015 10:26:41 +0200 Subject: regmap: Fix regmap_bulk_write for bus writes The regmap config does not prohibit val_bytes that are not powers of two. But the current code of regmap_bulk_write for use_single_rw does limit the possible val_bytes to 1, 2 and 4. This patch fixes the behaviour to allow bus writes with non-standard val_bytes sizes. Cc: Stephen Boyd Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 9c1f856842a3..90bf5ea34c47 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1680,9 +1680,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, /* * Some devices don't support bulk write, for - * them we have a series of single write operations. + * them we have a series of single write operations in the first two if + * blocks. + * + * The first if block is used for memory mapped io. It does not allow + * val_bytes of 3 for example. + * The second one is used for busses which do not have this limitation + * and can write arbitrary value lengths. */ - if (!map->bus || map->use_single_rw) { + if (!map->bus) { map->lock(map->lock_arg); for (i = 0; i < val_count; i++) { unsigned int ival; @@ -1714,6 +1720,17 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, } out: map->unlock(map->lock_arg); + } else if (map->use_single_rw) { + map->lock(map->lock_arg); + for (i = 0; i < val_count; i++) { + ret = _regmap_raw_write(map, + reg + (i * map->reg_stride), + val + (i * val_bytes), + val_bytes); + if (ret) + break; + } + map->unlock(map->lock_arg); } else { void *wval; -- cgit v1.3-6-gb490 From 67921a1a6660d32cc2770d05d656a1187b6d94d5 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 21 Aug 2015 10:26:42 +0200 Subject: regmap: Split use_single_rw internally into use_single_read/write use_single_rw currently reflects the capabilities of the connected device. The capabilities of the bus are currently missing for this variable. As there are read only and write only buses we need seperate values for use_single_rw to also reflect tha capabilities of the bus. This patch splits use_single_rw into use_single_read and use_single_write. The initialization is changed to check the configuration for use_single_rw and to check the capabilities of the used bus. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 6 ++++-- drivers/base/regmap/regcache.c | 2 +- drivers/base/regmap/regmap-irq.c | 4 ++-- drivers/base/regmap/regmap.c | 9 +++++---- 4 files changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index b2b2849fc6d3..d744ae3926dd 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -139,8 +139,10 @@ struct regmap { struct reg_default *patch; int patch_regs; - /* if set, converts bulk rw to single rw */ - bool use_single_rw; + /* if set, converts bulk read to single read */ + bool use_single_read; + /* if set, converts bulk read to single read */ + bool use_single_write; /* if set, the device supports multi write mode */ bool can_multi_write; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index b9862d741a56..6f8a13ec32a4 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -729,7 +729,7 @@ int regcache_sync_block(struct regmap *map, void *block, unsigned int block_base, unsigned int start, unsigned int end) { - if (regmap_can_raw_write(map) && !map->use_single_rw) + if (regmap_can_raw_write(map) && !map->use_single_write) return regcache_sync_block_raw(map, block, cache_present, block_base, start, end); else diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 2597600a5d26..38d1f72d869c 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -209,7 +209,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) * Read in the statuses, using a single bulk read if possible * in order to reduce the I/O overheads. */ - if (!map->use_single_rw && map->reg_stride == 1 && + if (!map->use_single_read && map->reg_stride == 1 && data->irq_reg_stride == 1) { u8 *buf8 = data->status_reg_buf; u16 *buf16 = data->status_reg_buf; @@ -398,7 +398,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, else d->irq_reg_stride = 1; - if (!map->use_single_rw && map->reg_stride == 1 && + if (!map->use_single_read && map->reg_stride == 1 && d->irq_reg_stride == 1) { d->status_reg_buf = kmalloc(map->format.val_bytes * chip->num_regs, GFP_KERNEL); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 90bf5ea34c47..bc82fd34483b 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -576,7 +576,8 @@ struct regmap *regmap_init(struct device *dev, map->reg_stride = config->reg_stride; else map->reg_stride = 1; - map->use_single_rw = config->use_single_rw; + map->use_single_read = config->use_single_rw || !bus || !bus->read; + map->use_single_write = config->use_single_rw || !bus || !bus->write; map->can_multi_write = config->can_multi_write; map->dev = dev; map->bus = bus; @@ -766,7 +767,7 @@ struct regmap *regmap_init(struct device *dev, if ((reg_endian != REGMAP_ENDIAN_BIG) || (val_endian != REGMAP_ENDIAN_BIG)) goto err_map; - map->use_single_rw = true; + map->use_single_write = true; } if (!map->format.format_write && @@ -1720,7 +1721,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, } out: map->unlock(map->lock_arg); - } else if (map->use_single_rw) { + } else if (map->use_single_write) { map->lock(map->lock_arg); for (i = 0; i < val_count; i++) { ret = _regmap_raw_write(map, @@ -2312,7 +2313,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, * Some devices does not support bulk read, for * them we have a series of single read operations. */ - if (map->use_single_rw) { + if (map->use_single_read) { for (i = 0; i < val_count; i++) { ret = regmap_raw_read(map, reg + (i * map->reg_stride), -- cgit v1.3-6-gb490 From 9c9f7f675970ba1b888272f016157de21f69e7e2 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 21 Aug 2015 10:26:43 +0200 Subject: regmap: No multi_write support if bus->write does not exist There is no multi_write support available if we cannot use raw_write. This is the case if bus->write is not implemented. This patch adds a condition that we need bus and bus->write so that can_multi_write is true. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index bc82fd34483b..27456c7978b9 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -578,7 +578,7 @@ struct regmap *regmap_init(struct device *dev, map->reg_stride = 1; map->use_single_read = config->use_single_rw || !bus || !bus->read; map->use_single_write = config->use_single_rw || !bus || !bus->write; - map->can_multi_write = config->can_multi_write; + map->can_multi_write = config->can_multi_write && bus && bus->write; map->dev = dev; map->bus = bus; map->bus_context = bus_context; -- cgit v1.3-6-gb490 From 7f5dcaf1fdf289767a126a0a5cc3ef39b5254b06 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sun, 7 Jun 2015 15:20:11 +0100 Subject: drivercore: Fix unregistration path of platform devices The unregister path of platform_device is broken. On registration, it will register all resources with either a parent already set, or type==IORESOURCE_{IO,MEM}. However, on unregister it will release everything with type==IORESOURCE_{IO,MEM}, but ignore the others. There are also cases where resources don't get registered in the first place, like with devices created by of_platform_populate()*. Fix the unregister path to be symmetrical with the register path by checking the parent pointer instead of the type field to decide which resources to unregister. This is safe because the upshot of the registration path algorithm is that registered resources have a parent pointer, and non-registered resources do not. * It can be argued that of_platform_populate() should be registering it's resources, and they argument has some merit. However, there are quite a few platforms that end up broken if we try to do that due to overlapping resources in the device tree. Until that is fixed, we need to solve the immediate problem. Cc: Pantelis Antoniou Cc: Wolfram Sang Cc: Rob Herring Cc: Greg Kroah-Hartman Cc: Ricardo Ribalda Delgado Signed-off-by: Grant Likely Tested-by: Ricardo Ribalda Delgado Tested-by: Wolfram Sang Cc: stable@vger.kernel.org Signed-off-by: Rob Herring --- drivers/base/platform.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 063f0ab15259..f80aaaf9f610 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -375,9 +375,7 @@ int platform_device_add(struct platform_device *pdev) while (--i >= 0) { struct resource *r = &pdev->resource[i]; - unsigned long type = resource_type(r); - - if (type == IORESOURCE_MEM || type == IORESOURCE_IO) + if (r->parent) release_resource(r); } @@ -408,9 +406,7 @@ void platform_device_del(struct platform_device *pdev) for (i = 0; i < pdev->num_resources; i++) { struct resource *r = &pdev->resource[i]; - unsigned long type = resource_type(r); - - if (type == IORESOURCE_MEM || type == IORESOURCE_IO) + if (r->parent) release_resource(r); } } -- cgit v1.3-6-gb490 From 4f73b0654d8a954540d49bb0a300f31663423db9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 10 Aug 2015 19:56:47 +0300 Subject: device property: fallback to pset when gettng one string The one string as an equivalent to an array of one element. Allow user to read one string as a plain string. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/base/property.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/property.c b/drivers/base/property.c index 37a7bb7b239d..841b15c5c058 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -462,7 +462,8 @@ int fwnode_property_read_string(struct fwnode_handle *fwnode, return acpi_dev_prop_read(to_acpi_node(fwnode), propname, DEV_PROP_STRING, val, 1); - return -ENXIO; + return pset_prop_read_array(to_pset(fwnode), propname, + DEV_PROP_STRING, val, 1); } EXPORT_SYMBOL_GPL(fwnode_property_read_string); -- cgit v1.3-6-gb490 From 4fa7508e9f1c64ae39516e40ee5495aaa4616ad7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 Aug 2015 20:27:04 -0700 Subject: device property: Return -ENXIO if there is no suitable FW interface Return -ENXIO if device property array access functions don't find a suitable firmware interface. This lets drivers decide if they should use available platform data instead. Cc: Rafael J. Wysocki Signed-off-by: Guenter Roeck Tested-by: Jeremy Linton Signed-off-by: David S. Miller --- drivers/base/property.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/property.c b/drivers/base/property.c index 4c2082899322..a5efb43258a9 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -155,6 +155,7 @@ EXPORT_SYMBOL_GPL(fwnode_property_present); * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, * %-EOVERFLOW if the size of the property is not as expected. + * %-ENXIO if no suitable firmware interface is present. */ int device_property_read_u8_array(struct device *dev, const char *propname, u8 *val, size_t nval) @@ -179,6 +180,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u8_array); * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, * %-EOVERFLOW if the size of the property is not as expected. + * %-ENXIO if no suitable firmware interface is present. */ int device_property_read_u16_array(struct device *dev, const char *propname, u16 *val, size_t nval) @@ -203,6 +205,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u16_array); * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, * %-EOVERFLOW if the size of the property is not as expected. + * %-ENXIO if no suitable firmware interface is present. */ int device_property_read_u32_array(struct device *dev, const char *propname, u32 *val, size_t nval) @@ -227,6 +230,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u32_array); * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, * %-EOVERFLOW if the size of the property is not as expected. + * %-ENXIO if no suitable firmware interface is present. */ int device_property_read_u64_array(struct device *dev, const char *propname, u64 *val, size_t nval) @@ -251,6 +255,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u64_array); * %-ENODATA if the property does not have a value, * %-EPROTO or %-EILSEQ if the property is not an array of strings, * %-EOVERFLOW if the size of the property is not as expected. + * %-ENXIO if no suitable firmware interface is present. */ int device_property_read_string_array(struct device *dev, const char *propname, const char **val, size_t nval) @@ -272,6 +277,7 @@ EXPORT_SYMBOL_GPL(device_property_read_string_array); * %-EINVAL if given arguments are not valid, * %-ENODATA if the property does not have a value, * %-EPROTO or %-EILSEQ if the property type is not a string. + * %-ENXIO if no suitable firmware interface is present. */ int device_property_read_string(struct device *dev, const char *propname, const char **val) @@ -293,9 +299,11 @@ EXPORT_SYMBOL_GPL(device_property_read_string); else if (is_acpi_node(_fwnode_)) \ _ret_ = acpi_dev_prop_read(to_acpi_node(_fwnode_), _propname_, \ _proptype_, _val_, _nval_); \ - else \ + else if (is_pset(_fwnode_)) \ _ret_ = pset_prop_read_array(to_pset(_fwnode_), _propname_, \ _proptype_, _val_, _nval_); \ + else \ + _ret_ = -ENXIO; \ _ret_; \ }) @@ -433,9 +441,10 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode, else if (is_acpi_node(fwnode)) return acpi_dev_prop_read(to_acpi_node(fwnode), propname, DEV_PROP_STRING, val, nval); - - return pset_prop_read_array(to_pset(fwnode), propname, - DEV_PROP_STRING, val, nval); + else if (is_pset(fwnode)) + return pset_prop_read_array(to_pset(fwnode), propname, + DEV_PROP_STRING, val, nval); + return -ENXIO; } EXPORT_SYMBOL_GPL(fwnode_property_read_string_array); -- cgit v1.3-6-gb490 From 1f821ed7afaa7ed689322ee2369f270e374a6350 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 12 Aug 2015 16:30:18 +0530 Subject: PM / OPP: Free resources and properly return error on failure _of_init_opp_table_v2() isn't freeing up resources on some errors and the error values returned are also not correct always. This fixes following problems: - Return -ENOENT, if no entries are found in the table. - Use IS_ERR() to properly check return value of _find_device_opp(). - Return error value with PTR_ERR() in above case. - Free table if _find_device_opp() fails. Reported-by: Dan Carpenter Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 204c6c945168..4d6c4576f7ae 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -1323,28 +1323,30 @@ static int _of_init_opp_table_v2(struct device *dev, if (ret) { dev_err(dev, "%s: Failed to add OPP, %d\n", __func__, ret); - break; + goto free_table; } } /* There should be one of more OPP defined */ - if (WARN_ON(!count)) + if (WARN_ON(!count)) { + ret = -ENOENT; goto put_opp_np; + } - if (!ret) { - if (!dev_opp) { - dev_opp = _find_device_opp(dev); - if (WARN_ON(!dev_opp)) - goto put_opp_np; - } - - dev_opp->np = opp_np; - dev_opp->shared_opp = of_property_read_bool(opp_np, - "opp-shared"); - } else { - of_free_opp_table(dev); + dev_opp = _find_device_opp(dev); + if (WARN_ON(IS_ERR(dev_opp))) { + ret = PTR_ERR(dev_opp); + goto free_table; } + dev_opp->np = opp_np; + dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared"); + + of_node_put(opp_np); + return 0; + +free_table: + of_free_opp_table(dev); put_opp_np: of_node_put(opp_np); -- cgit v1.3-6-gb490 From 68fa9f0ab1b61cfc7deee699da8b5b5cb12f7a58 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 17 Aug 2015 19:20:20 +0530 Subject: PM / OPP: Fix static checker warning (broken 64bit big endian systems) Dan Carpenter reported (generated with static checker): drivers/base/power/opp.c:949 _opp_add_static_v2() warn: passing casted pointer '&new_opp->clock_latency_ns' to 'of_property_read_u32()' 64 vs 32. This code will break on 64 bit, big endian machines. Fix this by reading the value in a u32 type variable first and then assigning it to the unsigned long variable. Reported-by: Dan Carpenter Suggested-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 4d6c4576f7ae..803d8b7ced89 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -918,6 +918,7 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) struct device_opp *dev_opp; struct dev_pm_opp *new_opp; u64 rate; + u32 val; int ret; /* Hold our list modification lock here */ @@ -946,14 +947,16 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) new_opp->np = np; new_opp->dynamic = false; new_opp->available = true; - of_property_read_u32(np, "clock-latency-ns", - (u32 *)&new_opp->clock_latency_ns); + + if (!of_property_read_u32(np, "clock-latency-ns", &val)) + new_opp->clock_latency_ns = val; ret = opp_get_microvolt(new_opp, dev); if (ret) goto free_opp; - of_property_read_u32(np, "opp-microamp", (u32 *)&new_opp->u_amp); + if (!of_property_read_u32(new_opp->np, "opp-microamp", &val)) + new_opp->u_amp = val; ret = _opp_add(dev, new_opp, dev_opp); if (ret) -- cgit v1.3-6-gb490 From 50a3cb04a5f9cd5323a76db9ee409a7f3004259a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 12 Aug 2015 15:59:39 +0530 Subject: PM / OPP: Drop unlikely before IS_ERR(_OR_NULL) IS_ERR(_OR_NULL) already contain an 'unlikely' compiler flag and there is no need to do that again from its callers. Drop it. Acked-by: Pavel Machek Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 803d8b7ced89..bb703b5ebaff 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -204,7 +204,7 @@ static struct device_opp *_find_device_opp(struct device *dev) { struct device_opp *dev_opp; - if (unlikely(IS_ERR_OR_NULL(dev))) { + if (IS_ERR_OR_NULL(dev)) { pr_err("%s: Invalid parameters\n", __func__); return ERR_PTR(-EINVAL); } @@ -239,7 +239,7 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) opp_rcu_lockdep_assert(); tmp_opp = rcu_dereference(opp); - if (unlikely(IS_ERR_OR_NULL(tmp_opp)) || !tmp_opp->available) + if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) pr_err("%s: Invalid parameters\n", __func__); else v = tmp_opp->u_volt; @@ -271,7 +271,7 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) opp_rcu_lockdep_assert(); tmp_opp = rcu_dereference(opp); - if (unlikely(IS_ERR_OR_NULL(tmp_opp)) || !tmp_opp->available) + if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) pr_err("%s: Invalid parameters\n", __func__); else f = tmp_opp->rate; -- cgit v1.3-6-gb490 From 15dec67ae3ddb222fe6907db299229906ede8143 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 11 Aug 2015 14:50:49 +0200 Subject: PM / domains: Align column headers and data in pm_genpd_summary output "domain": header is indented by 4, data by 0 spaces => 0 spaces "/device": header is indented by 11, data by 4 spaces => 4 spaces "slaves": header is indented by 47, data by 49 spaces => 48 spaces Ruler: 1234567890123456789012345678901234567890123456789012345678901234567890 Before: domain status slaves /device runtime status ---------------------------------------------------------------------- a3sp on a2us /devices/platform/e60b0000.i2c suspended After: domain status slaves /device runtime status ---------------------------------------------------------------------- a3sp on a2us /devices/platform/e60b0000.i2c suspended Signed-off-by: Geert Uytterhoeven Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 7666a1cbaf95..9bd2b28da1ed 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2074,7 +2074,7 @@ static int pm_genpd_summary_one(struct seq_file *s, if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) goto exit; - seq_printf(s, "%-30s %-15s ", genpd->name, status_lookup[genpd->status]); + seq_printf(s, "%-30s %-15s ", genpd->name, status_lookup[genpd->status]); /* * Modifications on the list require holding locks on both @@ -2109,8 +2109,8 @@ static int pm_genpd_summary_show(struct seq_file *s, void *data) struct generic_pm_domain *genpd; int ret = 0; - seq_puts(s, " domain status slaves\n"); - seq_puts(s, " /device runtime status\n"); + seq_puts(s, "domain status slaves\n"); + seq_puts(s, " /device runtime status\n"); seq_puts(s, "----------------------------------------------------------------------\n"); ret = mutex_lock_interruptible(&gpd_list_lock); -- cgit v1.3-6-gb490 From be5ed55de011bddbb8ef7b3453edf07a26865beb Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 13 Aug 2015 11:51:57 +0530 Subject: PM / Domains: Make pm_genpd_init() available to modules Export symbol pm_genpd_init so it can be used in loadable kernel modules Signed-off-by: Rajendra Nayak Reported-by: Stephen Rothwell Acked-by: Geert Uytterhoeven Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 9bd2b28da1ed..6d465a9352f1 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1716,6 +1716,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd, list_add(&genpd->gpd_list_node, &gpd_list); mutex_unlock(&gpd_list_lock); } +EXPORT_SYMBOL_GPL(pm_genpd_init); #ifdef CONFIG_PM_GENERIC_DOMAINS_OF /* -- cgit v1.3-6-gb490 From 8bb6944e5aabaf9b7977502667a6981733564f85 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 27 Aug 2015 10:17:00 +0100 Subject: PM / Domains: Fix typo in description of genpd_dev_pm_detach() The function genpd_dev_pm_detach() detaches a device from a PM domain, however, in the description, the "dev" argument for the function is described as the device to "attach" instead of "detach". Correct this. Signed-off-by: Jon Hunter Acked-by: Geert Uytterhoeven Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 6d465a9352f1..416720159e96 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1890,7 +1890,7 @@ EXPORT_SYMBOL_GPL(of_genpd_get_from_provider); /** * genpd_dev_pm_detach - Detach a device from its PM domain. - * @dev: Device to attach. + * @dev: Device to detach. * @power_off: Currently not used * * Try to locate a corresponding generic PM domain, which the device was -- cgit v1.3-6-gb490 From adaac459759db4a1fd35baddbe47bac700095496 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sun, 30 Aug 2015 09:33:53 +0200 Subject: regmap: Introduce max_raw_read/write for regmap_bulk_read/write There are some buses which have a limit on the maximum number of bytes that can be send/received. An example for this is I2C_FUNC_SMBUS_I2C_BLOCK which does not support any reads/writes of more than 32 bytes. The regmap_bulk operations should still be able to utilize the full 32 bytes in this case. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 4 ++ drivers/base/regmap/regmap.c | 85 ++++++++++++++++++++++++++++++++++-------- include/linux/regmap.h | 4 ++ 3 files changed, 78 insertions(+), 15 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index d744ae3926dd..fc554e357c5d 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -146,6 +146,10 @@ struct regmap { /* if set, the device supports multi write mode */ bool can_multi_write; + /* if set, raw reads/writes are limited to this size */ + size_t max_raw_read; + size_t max_raw_write; + struct rb_root range_tree; void *selector_work_buf; /* Scratch buffer used for selector */ }; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index a6b6f7ee87ee..7cbe42680877 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -579,6 +579,8 @@ struct regmap *regmap_init(struct device *dev, map->use_single_read = config->use_single_rw || !bus || !bus->read; map->use_single_write = config->use_single_rw || !bus || !bus->write; map->can_multi_write = config->can_multi_write && bus && bus->write; + map->max_raw_read = bus->max_raw_read; + map->max_raw_write = bus->max_raw_write; map->dev = dev; map->bus = bus; map->bus_context = bus_context; @@ -1674,6 +1676,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, { int ret = 0, i; size_t val_bytes = map->format.val_bytes; + size_t total_size = val_bytes * val_count; if (map->bus && !map->format.parse_inplace) return -EINVAL; @@ -1722,16 +1725,37 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, } out: map->unlock(map->lock_arg); - } else if (map->use_single_write) { + } else if (map->use_single_write || + (map->max_raw_write && map->max_raw_write < total_size)) { + int chunk_stride = map->reg_stride; + size_t chunk_size = val_bytes; + size_t chunk_count = val_count; + + if (!map->use_single_write) { + chunk_size = map->max_raw_write; + if (chunk_size % val_bytes) + chunk_size -= chunk_size % val_bytes; + chunk_count = total_size / chunk_size; + chunk_stride *= chunk_size / val_bytes; + } + map->lock(map->lock_arg); - for (i = 0; i < val_count; i++) { + /* Write as many bytes as possible with chunk_size */ + for (i = 0; i < chunk_count; i++) { ret = _regmap_raw_write(map, - reg + (i * map->reg_stride), - val + (i * val_bytes), - val_bytes); + reg + (i * chunk_stride), + val + (i * chunk_size), + chunk_size); if (ret) break; } + + /* Write remaining bytes */ + if (!ret && chunk_size * i < total_size) { + ret = _regmap_raw_write(map, reg + (i * chunk_stride), + val + (i * chunk_size), + total_size - i * chunk_size); + } map->unlock(map->lock_arg); } else { void *wval; @@ -2319,20 +2343,51 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, * Some devices does not support bulk read, for * them we have a series of single read operations. */ - if (map->use_single_read) { - for (i = 0; i < val_count; i++) { - ret = regmap_raw_read(map, - reg + (i * map->reg_stride), - val + (i * val_bytes), - val_bytes); - if (ret != 0) - return ret; - } - } else { + size_t total_size = val_bytes * val_count; + + if (!map->use_single_read && + (!map->max_raw_read || map->max_raw_read > total_size)) { ret = regmap_raw_read(map, reg, val, val_bytes * val_count); if (ret != 0) return ret; + } else { + /* + * Some devices do not support bulk read or do not + * support large bulk reads, for them we have a series + * of read operations. + */ + int chunk_stride = map->reg_stride; + size_t chunk_size = val_bytes; + size_t chunk_count = val_count; + + if (!map->use_single_read) { + chunk_size = map->max_raw_read; + if (chunk_size % val_bytes) + chunk_size -= chunk_size % val_bytes; + chunk_count = total_size / chunk_size; + chunk_stride *= chunk_size / val_bytes; + } + + /* Read bytes that fit into a multiple of chunk_size */ + for (i = 0; i < chunk_count; i++) { + ret = regmap_raw_read(map, + reg + (i * chunk_stride), + val + (i * chunk_size), + chunk_size); + if (ret != 0) + return ret; + } + + /* Read remaining bytes */ + if (chunk_size * i < total_size) { + ret = regmap_raw_read(map, + reg + (i * chunk_stride), + val + (i * chunk_size), + total_size - i * chunk_size); + if (ret != 0) + return ret; + } } for (i = 0; i < val_count * val_bytes; i += val_bytes) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 73fc34d0c4c2..327b8f291d3f 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -311,6 +311,8 @@ typedef void (*regmap_hw_free_context)(void *context); * @val_format_endian_default: Default endianness for formatted register * values. Used when the regmap_config specifies DEFAULT. If this is * DEFAULT, BIG is assumed. + * @max_raw_read: Max raw read size that can be used on the bus. + * @max_raw_write: Max raw write size that can be used on the bus. */ struct regmap_bus { bool fast_io; @@ -325,6 +327,8 @@ struct regmap_bus { u8 read_flag_mask; enum regmap_endian reg_format_endian_default; enum regmap_endian val_format_endian_default; + size_t max_raw_read; + size_t max_raw_write; }; struct regmap *regmap_init(struct device *dev, -- cgit v1.3-6-gb490 From f50c9eb4e9304cf555206c93152f580c0e7213b2 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sun, 30 Aug 2015 09:33:54 +0200 Subject: regmap: regmap max_raw_read/write getter functions Add functions to access the maximum size we can read/write using regmap_raw_read/write(). This helps drivers that need to know how much they can write with the raw functions without problems. There are some devices (e.g. bmc150) that have fifos as registers which need to be read in specific chunks otherwise samples are dropped. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 22 ++++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 24 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7cbe42680877..47210101e308 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1393,6 +1393,28 @@ bool regmap_can_raw_write(struct regmap *map) } EXPORT_SYMBOL_GPL(regmap_can_raw_write); +/** + * regmap_get_raw_read_max - Get the maximum size we can read + * + * @map: Map to check. + */ +size_t regmap_get_raw_read_max(struct regmap *map) +{ + return map->max_raw_read; +} +EXPORT_SYMBOL_GPL(regmap_get_raw_read_max); + +/** + * regmap_get_raw_write_max - Get the maximum size we can read + * + * @map: Map to check. + */ +size_t regmap_get_raw_write_max(struct regmap *map) +{ + return map->max_raw_write; +} +EXPORT_SYMBOL_GPL(regmap_get_raw_write_max); + static int _regmap_bus_formatted_write(void *context, unsigned int reg, unsigned int val) { diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 327b8f291d3f..6724d0e3819e 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -444,6 +444,8 @@ int regmap_get_max_register(struct regmap *map); int regmap_get_reg_stride(struct regmap *map); int regmap_async_complete(struct regmap *map); bool regmap_can_raw_write(struct regmap *map); +size_t regmap_get_raw_read_max(struct regmap *map); +size_t regmap_get_raw_write_max(struct regmap *map); int regcache_sync(struct regmap *map); int regcache_sync_region(struct regmap *map, unsigned int min, -- cgit v1.3-6-gb490 From c335931ed9d22c30017cf957518262c2fe6502ce Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sun, 30 Aug 2015 09:33:55 +0200 Subject: regmap: Add raw_write/read checks for max_raw_write/read sizes Check in regmap_raw_read() and regmap_raw_write() for correct maximum sizes of the operations. Return -E2BIG if this size is not supported because it is too big. Also this patch causes an uninitialized variable warning so it initializes ret (although not necessary). Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 47210101e308..d2efa4b33294 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1584,6 +1584,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; + if (map->max_raw_write && map->max_raw_write > val_len) + return -E2BIG; map->lock(map->lock_arg); @@ -2256,6 +2258,10 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, ret = -ENOTSUPP; goto out; } + if (map->max_raw_read && map->max_raw_read < val_len) { + ret = -E2BIG; + goto out; + } /* Physical block read if there's no cache involved */ ret = _regmap_raw_read(map, reg, val, val_len); -- cgit v1.3-6-gb490 From 29332534e2b68b5889a40ccb6606ba0d06750a69 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sun, 30 Aug 2015 09:33:56 +0200 Subject: regmap-i2c: Add smbus i2c block support This allows to read/write up to 32 bytes of data and is to be prefered if supported before the register read/write smbus support. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-i2c.c | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 4b76e33110a2..ddb9b0efb724 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -209,11 +209,60 @@ static struct regmap_bus regmap_i2c = { .val_format_endian_default = REGMAP_ENDIAN_BIG, }; +static int regmap_i2c_smbus_i2c_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + + if (count < 1) + return -EINVAL; + if (count >= I2C_SMBUS_BLOCK_MAX) + return -E2BIG; + + --count; + return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count, + ((u8 *)data + 1)); +} + +static int regmap_i2c_smbus_i2c_read(void *context, const void *reg, + size_t reg_size, void *val, + size_t val_size) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + int ret; + + if (reg_size != 1 || val_size < 1) + return -EINVAL; + if (val_size >= I2C_SMBUS_BLOCK_MAX) + return -E2BIG; + + ret = i2c_smbus_read_i2c_block_data(i2c, ((u8 *)reg)[0], val_size, val); + if (ret == val_size) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static struct regmap_bus regmap_i2c_smbus_i2c_block = { + .write = regmap_i2c_smbus_i2c_write, + .read = regmap_i2c_smbus_i2c_read, + .max_raw_read = I2C_SMBUS_BLOCK_MAX, + .max_raw_write = I2C_SMBUS_BLOCK_MAX, +}; + static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, const struct regmap_config *config) { if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) return ®map_i2c; + else if (config->reg_bits == 8 && + i2c_check_functionality(i2c->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK)) + return ®map_i2c_smbus_i2c_block; else if (config->val_bits == 16 && config->reg_bits == 8 && i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA)) -- cgit v1.3-6-gb490 From d5b98eb12420ce856caaf57dc5256eedc56a3747 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 28 Aug 2015 20:04:53 +0100 Subject: regmap: Support bulk reads for devices without raw formatting When doing a bulk read from a device which lacks raw I/O support we fall back to doing register at a time reads but we still use the raw formatters in order to render the data into the word size used by the device (since bulk reads still operate on the device word size rather than unsigned ints). This means that devices without raw formatting such as those that provide reg_read() are not supported. Provide handling for them by copying the values read into native endian values of the appropriate size. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 27456c7978b9..b77f1c6abdad 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2338,7 +2338,34 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, &ival); if (ret != 0) return ret; - map->format.format_val(val + (i * val_bytes), ival, 0); + + if (map->format.format_val) { + map->format.format_val(val + (i * val_bytes), ival, 0); + } else { + /* Devices providing read and write + * operations can use the bulk I/O + * functions if they define a val_bytes, + * we assume that the values are native + * endian. + */ + u32 *u32 = val; + u16 *u16 = val; + u8 *u8 = val; + + switch (map->format.val_bytes) { + case 4: + u32[i] = ival; + break; + case 2: + u16[i] = ival; + break; + case 1: + u8[i] = ival; + break; + default: + return -EINVAL; + } + } } } -- cgit v1.3-6-gb490 From 17649c90ff4c5246bb4babf6260029968a6d119d Mon Sep 17 00:00:00 2001 From: Sergey SENOZHATSKY Date: Mon, 31 Aug 2015 18:54:58 +0900 Subject: regmap: fix a NULL pointer dereference in __regmap_init __regmap_init() may receive a NULL `struct regmap_bus *bus' pointer, for example, from snd_hdac_regmap_init(), and it make sure that it does not NULL deference `bus`, except around ->max_raw_read and ->max_raw_write initialisation. Add missing check. Signed-off-by: Sergey Senozhatsky Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index d2efa4b33294..2ffdb62f75f7 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -579,8 +579,10 @@ struct regmap *regmap_init(struct device *dev, map->use_single_read = config->use_single_rw || !bus || !bus->read; map->use_single_write = config->use_single_rw || !bus || !bus->write; map->can_multi_write = config->can_multi_write && bus && bus->write; - map->max_raw_read = bus->max_raw_read; - map->max_raw_write = bus->max_raw_write; + if (bus) { + map->max_raw_read = bus->max_raw_read; + map->max_raw_write = bus->max_raw_write; + } map->dev = dev; map->bus = bus; map->bus_context = bus_context; -- cgit v1.3-6-gb490 From 29e47e2173349ee06bd339f7753821c720d50923 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 Sep 2015 10:16:13 +0200 Subject: PM / Domains: Try power off masters in error path of __pm_genpd_poweron() While powering up a genpd, its domain masters are first being powered up. In the error path of __pm_genpd_poweron(), we didn't care to try power off these domain masters. Let's deal with that to avoid leaving unused PM domains powered. Signed-off-by: Ulf Hansson Reviewed-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 416720159e96..62f757250235 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -212,6 +212,18 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed) return ret; } +/** + * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). + * @genpd: PM domait to power off. + * + * Queue up the execution of pm_genpd_poweroff() unless it's already been done + * before. + */ +static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) +{ + queue_work(pm_wq, &genpd->power_off_work); +} + /** * __pm_genpd_poweron - Restore power to a given PM domain and its masters. * @genpd: PM domain to power up. @@ -259,8 +271,12 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) return 0; err: - list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node) + list_for_each_entry_continue_reverse(link, + &genpd->slave_links, + slave_node) { genpd_sd_counter_dec(link->master); + genpd_queue_power_off_work(link->master); + } return ret; } @@ -348,18 +364,6 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, return NOTIFY_DONE; } -/** - * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). - * @genpd: PM domait to power off. - * - * Queue up the execution of pm_genpd_poweroff() unless it's already been done - * before. - */ -static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) -{ - queue_work(pm_wq, &genpd->power_off_work); -} - /** * pm_genpd_poweroff - Remove power from a given PM domain. * @genpd: PM domain to power down. -- cgit v1.3-6-gb490 From 04697858d89e4bf2650364f8d6956e2554e8ef88 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 4 Sep 2015 15:42:39 -0700 Subject: mm: check if section present during memory block registering Tony Luck found on his setup, if memory block size 512M will cause crash during booting. BUG: unable to handle kernel paging request at ffffea0074000020 IP: get_nid_for_pfn+0x17/0x40 PGD 128ffcb067 PUD 128ffc9067 PMD 0 Oops: 0000 [#1] SMP Modules linked in: CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.2.0-rc8 #1 ... Call Trace: ? register_mem_sect_under_node+0x66/0xe0 register_one_node+0x17b/0x240 ? pci_iommu_alloc+0x6e/0x6e topology_init+0x3c/0x95 do_one_initcall+0xcd/0x1f0 The system has non continuous RAM address: BIOS-e820: [mem 0x0000001300000000-0x0000001cffffffff] usable BIOS-e820: [mem 0x0000001d70000000-0x0000001ec7ffefff] usable BIOS-e820: [mem 0x0000001f00000000-0x0000002bffffffff] usable BIOS-e820: [mem 0x0000002c18000000-0x0000002d6fffefff] usable BIOS-e820: [mem 0x0000002e00000000-0x00000039ffffffff] usable So there are start sections in memory block not present. For example: memory block : [0x2c18000000, 0x2c20000000) 512M first three sections are not present. The current register_mem_sect_under_node() assume first section is present, but memory block section number range [start_section_nr, end_section_nr] would include not present section. For arch that support vmemmap, we don't setup memmap for struct page area within not present sections area. So skip the pfn range that belong to absent section. [akpm@linux-foundation.org: simplification] [rientjes@google.com: more simplification] Fixes: bdee237c0343 ("x86: mm: Use 2GB memory block size on large memory x86-64 systems") Fixes: 982792c782ef ("x86, mm: probe memory block size for generic x86 64bit") Signed-off-by: Yinghai Lu Signed-off-by: David Rientjes Reported-by: Tony Luck Tested-by: Tony Luck Cc: Greg KH Cc: Ingo Molnar Tested-by: David Rientjes Cc: [3.15+] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/node.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/node.c b/drivers/base/node.c index 31df474d72f4..560751bad294 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -392,6 +392,16 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid) for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) { int page_nid; + /* + * memory block could have several absent sections from start. + * skip pfn range from absent section + */ + if (!pfn_present(pfn)) { + pfn = round_down(pfn + PAGES_PER_SECTION, + PAGES_PER_SECTION) - 1; + continue; + } + page_nid = get_nid_for_pfn(pfn); if (page_nid < 0) continue; -- cgit v1.3-6-gb490 From 30e7a65b3fdb53cc49f85c965095e40aceea3961 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 3 Sep 2015 09:10:37 +0100 Subject: PM / Domains: Ensure subdomain is not in use before removing The function pm_genpd_remove_subdomain() removes a subdomain from a generic PM domain, however, it does not check if the subdomain has any slave domains or device attached before doing so. Therefore, add a test to verify that the subdomain does not have any slave domains associated or any device attached before removing. Signed-off-by: Jon Hunter Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 62f757250235..16550c63d611 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1473,6 +1473,13 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, mutex_lock(&genpd->lock); + if (!list_empty(&subdomain->slave_links) || subdomain->device_count) { + pr_warn("%s: unable to remove subdomain %s\n", genpd->name, + subdomain->name); + ret = -EBUSY; + goto out; + } + list_for_each_entry(link, &genpd->master_links, master_node) { if (link->slave != subdomain) continue; @@ -1491,6 +1498,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, break; } +out: mutex_unlock(&genpd->lock); return ret; -- cgit v1.3-6-gb490 From 5b902d6f97f573fde911338e5d943e6b07fac7f9 Mon Sep 17 00:00:00 2001 From: Julien Grall Date: Thu, 3 Sep 2015 23:59:50 +0100 Subject: device property: Don't overwrite addr when failing in device_get_mac_address The function device_get_mac_address is trying different property names in order to get the mac address. To check the return value, the variable addr (which contain the buffer pass by the caller) will be re-used. This means that if the previous property is not found, the next property will be read using a NULL buffer. Therefore it's only possible to retrieve the mac if node contains a property "mac-address". Fix it by using a temporary buffer for the return value. This has been introduced by commit 4c96b7dc0d393f12c17e0d81db15aa4a820a6ab3 "Add a matching set of device_ functions for determining mac/phy" Signed-off-by: Julien Grall Cc: Jeremy Linton Cc: David S. Miller Reviewed-by: Jeremy Linton Signed-off-by: David S. Miller --- drivers/base/property.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/property.c b/drivers/base/property.c index ff03f2348f77..2d75366c61e0 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -611,13 +611,15 @@ static void *device_get_mac_addr(struct device *dev, */ void *device_get_mac_address(struct device *dev, char *addr, int alen) { - addr = device_get_mac_addr(dev, "mac-address", addr, alen); - if (addr) - return addr; + char *res; - addr = device_get_mac_addr(dev, "local-mac-address", addr, alen); - if (addr) - return addr; + res = device_get_mac_addr(dev, "mac-address", addr, alen); + if (res) + return res; + + res = device_get_mac_addr(dev, "local-mac-address", addr, alen); + if (res) + return res; return device_get_mac_addr(dev, "address", addr, alen); } -- cgit v1.3-6-gb490 From 4eafbd15b6c84cd3f6c76022c8a6c27f7cc076e1 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 8 Sep 2015 18:41:01 +0200 Subject: PM / OPP: add dev_pm_opp_get_suspend_opp() helper Add dev_pm_opp_get_suspend_opp() helper to obtain suspend opp. Acked-by: Viresh Kumar Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 30 ++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 6 ++++++ 2 files changed, 36 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index bb703b5ebaff..3df62dbcec3a 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -340,6 +340,36 @@ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) } EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); +/** + * dev_pm_opp_get_suspend_opp() - Get suspend opp + * @dev: device for which we do this operation + * + * Return: This function returns pointer to the suspend opp if it is + * defined, otherwise it returns NULL. + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. + */ +struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev) +{ + struct device_opp *dev_opp; + struct dev_pm_opp *opp; + + opp_rcu_lockdep_assert(); + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) + opp = NULL; + else + opp = dev_opp->suspend_opp; + + return opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp); + /** * dev_pm_opp_get_opp_count() - Get number of opps available in the opp list * @dev: device for which we do this operation diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index cab7ba55bedb..e817722ee3f0 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -34,6 +34,7 @@ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp); int dev_pm_opp_get_opp_count(struct device *dev); unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev); +struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev); struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, @@ -80,6 +81,11 @@ static inline unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) return 0; } +static inline struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev) +{ + return NULL; +} + static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available) { -- cgit v1.3-6-gb490 From 1b2b90cbea00670221f062814dc8bcecb3af7b90 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 9 Sep 2015 16:58:22 +0530 Subject: PM / OPP: Return suspend_opp only if it is enabled There is no point returning suspend_opp, if it is disabled by the core. As we can't use it at all. Fix it. Fixes: 4eafbd15b6c8 ("PM / OPP: add dev_pm_opp_get_suspend_opp() helper") Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/opp.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 3df62dbcec3a..4eff4cd7eb54 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -345,7 +345,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); * @dev: device for which we do this operation * * Return: This function returns pointer to the suspend opp if it is - * defined, otherwise it returns NULL. + * defined and available, otherwise it returns NULL. * * Locking: This function must be called under rcu_read_lock(). opp is a rcu * protected pointer. The reason for the same is that the opp pointer which is @@ -356,17 +356,15 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev) { struct device_opp *dev_opp; - struct dev_pm_opp *opp; opp_rcu_lockdep_assert(); dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) - opp = NULL; - else - opp = dev_opp->suspend_opp; + if (IS_ERR(dev_opp) || !dev_opp->suspend_opp || + !dev_opp->suspend_opp->available) + return NULL; - return opp; + return dev_opp->suspend_opp; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp); -- cgit v1.3-6-gb490 From e4084a16bbe07957811c75dfb7c9bf25c5862ba0 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sun, 13 Sep 2015 13:37:03 +0100 Subject: platform-msi: Do not cache msi_desc in handler_data The current implementation of platform MSI caches the msi_desc pointer in irq_data::handler_data. This is a bit silly, as we also have irq_data::msi_desc, which is perfectly valid. Remove the useless assignment and simplify the whole flow. Reported-by: Ma Jun Signed-off-by: Marc Zyngier Reviewed-by: Jiang Liu Link: http://lkml.kernel.org/r/1442147824-20971-1-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner --- drivers/base/platform-msi.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index 1857a5dd0816..134483daac25 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -63,20 +63,8 @@ static int platform_msi_init(struct irq_domain *domain, unsigned int virq, irq_hw_number_t hwirq, msi_alloc_info_t *arg) { - struct irq_data *data; - - irq_domain_set_hwirq_and_chip(domain, virq, hwirq, - info->chip, info->chip_data); - - /* - * Save the MSI descriptor in handler_data so that the - * irq_write_msi_msg callback can retrieve it (and the - * associated device). - */ - data = irq_domain_get_irq_data(domain, virq); - data->handler_data = arg->desc; - - return 0; + return irq_domain_set_hwirq_and_chip(domain, virq, hwirq, + info->chip, info->chip_data); } #else #define platform_msi_set_desc NULL @@ -97,7 +85,7 @@ static void platform_msi_update_dom_ops(struct msi_domain_info *info) static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg) { - struct msi_desc *desc = irq_data_get_irq_handler_data(data); + struct msi_desc *desc = irq_data_get_msi_desc(data); struct platform_msi_priv_data *priv_data; priv_data = desc->platform.msi_priv_data; -- cgit v1.3-6-gb490