From a1a68fcaf165a6ed202d8e29a692c559e10106c4 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 20 Nov 2017 15:27:28 +0800 Subject: regmap: Remove the redundant config to select hwspinlock The hwspinlock was changed to a bool by commit d048236dfdfe ("hwspinlock: Change hwspinlock to a bool"), so we do not need the REGMAP_HWSPINLOCK config to select hwspinlock or not. Signed-off-by: Baolin Wang Signed-off-by: Mark Brown --- drivers/base/regmap/Kconfig | 4 ---- drivers/base/regmap/regmap.c | 11 ++--------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 3a1535d812d8..0368fd7b3a41 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -6,7 +6,6 @@ config REGMAP default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ) select IRQ_DOMAIN if REGMAP_IRQ - select REGMAP_HWSPINLOCK if HWSPINLOCK=y bool config REGCACHE_COMPRESSED @@ -38,6 +37,3 @@ config REGMAP_MMIO config REGMAP_IRQ bool - -config REGMAP_HWSPINLOCK - bool diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 8d516a9bfc01..f25ab18ca057 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -414,7 +414,6 @@ static unsigned int regmap_parse_64_native(const void *buf) } #endif -#ifdef REGMAP_HWSPINLOCK static void regmap_lock_hwlock(void *__map) { struct regmap *map = __map; @@ -457,7 +456,6 @@ static void regmap_unlock_hwlock_irqrestore(void *__map) hwspin_unlock_irqrestore(map->hwlock, &map->spinlock_flags); } -#endif static void regmap_lock_mutex(void *__map) { @@ -674,7 +672,6 @@ struct regmap *__regmap_init(struct device *dev, map->unlock = config->unlock; map->lock_arg = config->lock_arg; } else if (config->hwlock_id) { -#ifdef REGMAP_HWSPINLOCK map->hwlock = hwspin_lock_request_specific(config->hwlock_id); if (!map->hwlock) { ret = -ENXIO; @@ -697,10 +694,6 @@ struct regmap *__regmap_init(struct device *dev, } map->lock_arg = map; -#else - ret = -EINVAL; - goto err_map; -#endif } else { if ((bus && bus->fast_io) || config->fast_io) { @@ -1116,7 +1109,7 @@ err_range: regmap_range_exit(map); kfree(map->work_buf); err_hwlock: - if (IS_ENABLED(REGMAP_HWSPINLOCK) && map->hwlock) + if (map->hwlock) hwspin_lock_free(map->hwlock); err_map: kfree(map); @@ -1305,7 +1298,7 @@ void regmap_exit(struct regmap *map) kfree(async->work_buf); kfree(async); } - if (IS_ENABLED(REGMAP_HWSPINLOCK) && map->hwlock) + if (map->hwlock) hwspin_lock_free(map->hwlock); kfree(map); } -- cgit v1.2.3-59-g8ed1b From c9b41fcf272b4926b373d21c2b83dfe374313780 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 6 Dec 2017 15:26:21 +0100 Subject: regmap: allow to disable all locking mechanisms We have a use case in the at24 EEPROM driver (recently converted to using regmap instead of raw i2c/smbus calls) where we read from/write to the regmap in a loop, while protecting the entire loop with a mutex. Currently this implicitly makes us use two mutexes - one in the driver and one in regmap. While browsing the code for similar use cases I noticed a significant number of places where locking *seems* redundant. Allow users to completely disable any locking mechanisms in regmap config. Signed-off-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 9 ++++++++- include/linux/regmap.h | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 8d516a9bfc01..72917b2fc10e 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -459,6 +459,11 @@ static void regmap_unlock_hwlock_irqrestore(void *__map) } #endif +static void regmap_lock_unlock_empty(void *__map) +{ + +} + static void regmap_lock_mutex(void *__map) { struct regmap *map = __map; @@ -669,7 +674,9 @@ struct regmap *__regmap_init(struct device *dev, goto err; } - if (config->lock && config->unlock) { + if (config->disable_locking) { + map->lock = map->unlock = regmap_lock_unlock_empty; + } else if (config->lock && config->unlock) { map->lock = config->lock; map->unlock = config->unlock; map->lock_arg = config->lock_arg; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 15eddc1353ba..072a90229e34 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -264,6 +264,9 @@ typedef void (*regmap_unlock)(void *); * field is NULL but precious_table (see below) is not, the * check is performed on such table (a register is precious if * it belongs to one of the ranges specified by precious_table). + * @disable_locking: This regmap is either protected by external means or + * is guaranteed not be be accessed from multiple threads. + * Don't use any locking mechanisms. * @lock: Optional lock callback (overrides regmap's default lock * function, based on spinlock or mutex). * @unlock: As above for unlocking. @@ -333,6 +336,8 @@ struct regmap_config { bool (*readable_reg)(struct device *dev, unsigned int reg); bool (*volatile_reg)(struct device *dev, unsigned int reg); bool (*precious_reg)(struct device *dev, unsigned int reg); + + bool disable_locking; regmap_lock lock; regmap_unlock unlock; void *lock_arg; -- cgit v1.2.3-59-g8ed1b From 81e30b189f593afbf10a7bf47f18f030f8aea3b5 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 13 Dec 2017 10:28:10 +0100 Subject: regmap: rename regmap_lock_unlock_empty() to regmap_lock_unlock_none() Minor naming convention tweak. Suggested-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 72917b2fc10e..54b1aa371c61 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -459,7 +459,7 @@ static void regmap_unlock_hwlock_irqrestore(void *__map) } #endif -static void regmap_lock_unlock_empty(void *__map) +static void regmap_lock_unlock_none(void *__map) { } @@ -675,7 +675,7 @@ struct regmap *__regmap_init(struct device *dev, } if (config->disable_locking) { - map->lock = map->unlock = regmap_lock_unlock_empty; + map->lock = map->unlock = regmap_lock_unlock_none; } else if (config->lock && config->unlock) { map->lock = config->lock; map->unlock = config->unlock; -- cgit v1.2.3-59-g8ed1b From 72465736adf2aade263a9475a1d42007fd49e703 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 12 Dec 2017 16:56:43 +0000 Subject: regmap: Disable debugfs when locking is disabled The recently added support for disabling the regmap internal locking left debugfs enabled for devices with the locking disabled. This is a problem since debugfs allows userspace to do things like initiate reads from the hardware which will use the scratch buffers protected by the regmap locking so could cause data corruption. For safety address this by just disabling debugfs for these devices. That is overly conservative since some of the debugfs files just read internal data structures but it's much simpler to implmement and less likely to lead to problems with tooling that works with debugfs. Reported-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 8 ++++++++ drivers/base/regmap/regmap-debugfs.c | 3 +++ drivers/base/regmap/regmap.c | 1 + 3 files changed, 12 insertions(+) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 8641183cac2f..53785e0e297a 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -77,6 +77,7 @@ struct regmap { int async_ret; #ifdef CONFIG_DEBUG_FS + bool debugfs_disable; struct dentry *debugfs; const char *debugfs_name; @@ -215,10 +216,17 @@ struct regmap_field { extern void regmap_debugfs_initcall(void); extern void regmap_debugfs_init(struct regmap *map, const char *name); extern void regmap_debugfs_exit(struct regmap *map); + +static inline void regmap_debugfs_disable(struct regmap *map) +{ + map->debugfs_disable = true; +} + #else static inline void regmap_debugfs_initcall(void) { } static inline void regmap_debugfs_init(struct regmap *map, const char *name) { } static inline void regmap_debugfs_exit(struct regmap *map) { } +static inline void regmap_debugfs_disable(struct regmap *map) { } #endif /* regcache core declarations */ diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 36ce3511c733..c8ecefd75d6f 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -529,6 +529,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) struct regmap_range_node *range_node; const char *devname = "dummy"; + if (map->debugfs_disable) + return; + /* If we don't have the debugfs root yet, postpone init */ if (!regmap_debugfs_root) { struct regmap_debugfs_node *node; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 54b1aa371c61..df9ca36753ff 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -676,6 +676,7 @@ struct regmap *__regmap_init(struct device *dev, if (config->disable_locking) { map->lock = map->unlock = regmap_lock_unlock_none; + regmap_debugfs_disable(map); } else if (config->lock && config->unlock) { map->lock = config->lock; map->unlock = config->unlock; -- cgit v1.2.3-59-g8ed1b From 8253bb3f82554cedb830a4cb65c84796df129c81 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 13 Dec 2017 17:25:31 +0100 Subject: regmap: potentially duplicate the name string stored in regmap Currently we just copy over the pointer passed to regmap_init() in the regmap config struct. To be on the safe side: duplicate the string with kstrdup_const() so that if an unaware user passes an address to a stack-allocated buffer, we won't crash. Signed-off-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 496da7bc5e77..84b5784e171b 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -672,6 +672,14 @@ struct regmap *__regmap_init(struct device *dev, goto err; } + if (config->name) { + map->name = kstrdup_const(config->name, GFP_KERNEL); + if (!map->name) { + ret = -ENOMEM; + goto err_map; + } + } + if (config->disable_locking) { map->lock = map->unlock = regmap_lock_unlock_none; regmap_debugfs_disable(map); @@ -683,7 +691,7 @@ struct regmap *__regmap_init(struct device *dev, map->hwlock = hwspin_lock_request_specific(config->hwlock_id); if (!map->hwlock) { ret = -ENXIO; - goto err_map; + goto err_name; } switch (config->hwlock_mode) { @@ -763,7 +771,6 @@ struct regmap *__regmap_init(struct device *dev, map->volatile_reg = config->volatile_reg; map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; - map->name = config->name; spin_lock_init(&map->async_lock); INIT_LIST_HEAD(&map->async_list); @@ -1119,6 +1126,8 @@ err_range: err_hwlock: if (map->hwlock) hwspin_lock_free(map->hwlock); +err_name: + kfree_const(map->name); err_map: kfree(map); err: @@ -1308,6 +1317,7 @@ void regmap_exit(struct regmap *map) } if (map->hwlock) hwspin_lock_free(map->hwlock); + kfree_const(map->name); kfree(map); } EXPORT_SYMBOL_GPL(regmap_exit); -- cgit v1.2.3-59-g8ed1b From a5ba91c380b8bcca21e6166fc71c5e5ac9f0db68 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 21 Dec 2017 12:12:50 +0100 Subject: regmap: debugfs: emit a debug message when locking is disabled We currently silently omit creating the debugfs entries when regmap locking is disabled. Users may not be aware of the reason for which regmap files don't show up in debugfs. Add a dev_dbg() message explaining that. Signed-off-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index c8ecefd75d6f..ae962b756863 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -529,8 +529,10 @@ void regmap_debugfs_init(struct regmap *map, const char *name) struct regmap_range_node *range_node; const char *devname = "dummy"; - if (map->debugfs_disable) + if (map->debugfs_disable) { + dev_dbg(map->dev, "regmap locking disabled - not creating debugfs entries\n"); return; + } /* If we don't have the debugfs root yet, postpone init */ if (!regmap_debugfs_root) { -- cgit v1.2.3-59-g8ed1b From 078711d7f88d33b0adebb402a1bcb2aa89afe68b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 22 Dec 2017 18:42:08 +0100 Subject: regmap: debugfs: document why we don't create the debugfs entries This is a follow-up to commit a5ba91c380b8 ("regmap: debugfs: emit a debug message when locking is disabled"). I figured that a user may see this message, grep the code, come to this place and he still won't know why we actually disabled debugfs. Add a comment explaining the reason. Signed-off-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index ae962b756863..f3266334063e 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -529,6 +529,13 @@ void regmap_debugfs_init(struct regmap *map, const char *name) struct regmap_range_node *range_node; const char *devname = "dummy"; + /* + * Userspace can initiate reads from the hardware over debugfs. + * Normally internal regmap structures and buffers are protected with + * a mutex or a spinlock, but if the regmap owner decided to disable + * all locking mechanisms, this is no longer the case. For safety: + * don't create the debugfs entries if locking is disabled. + */ if (map->debugfs_disable) { dev_dbg(map->dev, "regmap locking disabled - not creating debugfs entries\n"); return; -- cgit v1.2.3-59-g8ed1b From a4887813c3a9481ab87c8a71ab1de50b975cc823 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 25 Dec 2017 14:37:09 +0800 Subject: regmap: Add one flag to indicate if a hwlock should be used Since the hwlock id 0 is valid for hardware spinlock core, but now id 0 is treated as one invalid value for regmap. Thus we should add one extra flag for regmap config to indicate if a hardware spinlock should be used, then id 0 can be valid for regmap to request. Signed-off-by: Baolin Wang Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- include/linux/regmap.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index f25ab18ca057..d23a5c99b639 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -671,7 +671,7 @@ struct regmap *__regmap_init(struct device *dev, map->lock = config->lock; map->unlock = config->unlock; map->lock_arg = config->lock_arg; - } else if (config->hwlock_id) { + } else if (config->use_hwlock) { map->hwlock = hwspin_lock_request_specific(config->hwlock_id); if (!map->hwlock) { ret = -ENXIO; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 15eddc1353ba..c78e0057df66 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -317,6 +317,7 @@ typedef void (*regmap_unlock)(void *); * * @ranges: Array of configuration entries for virtual address ranges. * @num_ranges: Number of range configuration entries. + * @use_hwlock: Indicate if a hardware spinlock should be used. * @hwlock_id: Specify the hardware spinlock id. * @hwlock_mode: The hardware spinlock mode, should be HWLOCK_IRQSTATE, * HWLOCK_IRQ or 0. @@ -365,6 +366,7 @@ struct regmap_config { const struct regmap_range_cfg *ranges; unsigned int num_ranges; + bool use_hwlock; unsigned int hwlock_id; unsigned int hwlock_mode; }; -- cgit v1.2.3-59-g8ed1b From 46318b9784fb7b8363cc67bec24796b15dbba1e9 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 7 Jan 2018 17:22:33 -0600 Subject: regcache: flat: Un-inline index lookup from cache access This makes the code slightly more readable and allows for cleaner addition of functionality in later patches. Signed-off-by: Andrew F. Davis Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-flat.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c index 4d2e50bfc726..bc6cd88b8cc6 100644 --- a/drivers/base/regmap/regcache-flat.c +++ b/drivers/base/regmap/regcache-flat.c @@ -37,9 +37,12 @@ static int regcache_flat_init(struct regmap *map) cache = map->cache; - for (i = 0; i < map->num_reg_defaults; i++) - cache[regcache_flat_get_index(map, map->reg_defaults[i].reg)] = - map->reg_defaults[i].def; + for (i = 0; i < map->num_reg_defaults; i++) { + unsigned int reg = map->reg_defaults[i].reg; + unsigned int index = regcache_flat_get_index(map, reg); + + cache[index] = map->reg_defaults[i].def; + } return 0; } @@ -56,8 +59,9 @@ static int regcache_flat_read(struct regmap *map, unsigned int reg, unsigned int *value) { unsigned int *cache = map->cache; + unsigned int index = regcache_flat_get_index(map, reg); - *value = cache[regcache_flat_get_index(map, reg)]; + *value = cache[index]; return 0; } @@ -66,8 +70,9 @@ static int regcache_flat_write(struct regmap *map, unsigned int reg, unsigned int value) { unsigned int *cache = map->cache; + unsigned int index = regcache_flat_get_index(map, reg); - cache[regcache_flat_get_index(map, reg)] = value; + cache[index] = value; return 0; } -- cgit v1.2.3-59-g8ed1b From 3bafc09e779710abaa7b836fe3bbeeeab7754c2b Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 25 Dec 2017 14:37:10 +0800 Subject: mfd: syscon: Add hardware spinlock support Some system control registers need hardware spinlock to synchronize between the multiple subsystems, so we should add hardware spinlock support for syscon. Signed-off-by: Baolin Wang Acked-by: Rob Herring Acked-by: Lee Jones Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/syscon.txt | 8 ++++++++ drivers/mfd/syscon.c | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/syscon.txt b/Documentation/devicetree/bindings/mfd/syscon.txt index 8b92d4576c42..25d9e9c2fd53 100644 --- a/Documentation/devicetree/bindings/mfd/syscon.txt +++ b/Documentation/devicetree/bindings/mfd/syscon.txt @@ -16,9 +16,17 @@ Required properties: Optional property: - reg-io-width: the size (in bytes) of the IO accesses that should be performed on the device. +- hwlocks: reference to a phandle of a hardware spinlock provider node. Examples: gpr: iomuxc-gpr@20e0000 { compatible = "fsl,imx6q-iomuxc-gpr", "syscon"; reg = <0x020e0000 0x38>; + hwlocks = <&hwlock1 1>; +}; + +hwlock1: hwspinlock@40500000 { + ... + reg = <0x40500000 0x1000>; + #hwlock-cells = <1>; }; diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index b93fe4c4957a..7eaa40bc703f 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -87,6 +88,24 @@ static struct syscon *of_syscon_register(struct device_node *np) if (ret) reg_io_width = 4; + ret = of_hwspin_lock_get_id(np, 0); + if (ret > 0 || (IS_ENABLED(CONFIG_HWSPINLOCK) && ret == 0)) { + syscon_config.use_hwlock = true; + syscon_config.hwlock_id = ret; + syscon_config.hwlock_mode = HWLOCK_IRQSTATE; + } else if (ret < 0) { + switch (ret) { + case -ENOENT: + /* Ignore missing hwlock, it's optional. */ + break; + default: + pr_err("Failed to retrieve valid hwlock: %d\n", ret); + /* fall-through */ + case -EPROBE_DEFER: + goto err_regmap; + } + } + syscon_config.reg_stride = reg_io_width; syscon_config.val_bits = reg_io_width * 8; syscon_config.max_register = resource_size(&res) - reg_io_width; -- cgit v1.2.3-59-g8ed1b