diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-01-29 20:35:24 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-01-29 20:35:24 +0100 |
commit | b5856f97b3cb792806cee5d2b2c747353d238bbf (patch) | |
tree | 57cb0ea8b513763074d0bdb95b07421e0be50c71 | |
parent | Merge tag 'regulator-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/... (diff) | |
parent | Merge remote-tracking branches 'regmap/topic/soundwire' and 'regmap/topic/zer... (diff) | |
download | linux-b5856f97b3cb792806cee5d2b2c747353d238bbf.tar.xz linux-b5856f97b3cb792806cee5d2b2c747353d238bbf.zip |
Merge tag 'regmap-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap
Pull regmap updates from Mark Brown:
"A very busy release for regmap, all fairly specialist stuff but
useful:
- Support for disabling locking from Bartosz Golaszewski, allowing
users that handle their own locking to save some overhead.
- Support for hwspinlocks in syscons in MFD from Baolin Wang, this is
going through the regmap tree since the first users turned up some
some cases that needed interface tweaks with 0 being used as a
syscon identifier.
- Support for devices with no read or write flag from Andrew F.
Davis.
- Basic support for devices on SoundWire buses from Vinod Koul"
* tag 'regmap-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
mfd: syscon: Add hardware spinlock support
regmap: Allow empty read/write_flag_mask
regcache: flat: Un-inline index lookup from cache access
regmap: Add SoundWire bus support
regmap: Add one flag to indicate if a hwlock should be used
regmap: debugfs: document why we don't create the debugfs entries
regmap: debugfs: emit a debug message when locking is disabled
regmap: use proper part of work_buf for storing val
regmap: potentially duplicate the name string stored in regmap
regmap: Disable debugfs when locking is disabled
regmap: rename regmap_lock_unlock_empty() to regmap_lock_unlock_none()
regmap: allow to disable all locking mechanisms
regmap: Remove the redundant config to select hwspinlock
-rw-r--r-- | Documentation/devicetree/bindings/mfd/syscon.txt | 8 | ||||
-rw-r--r-- | drivers/base/regmap/Kconfig | 6 | ||||
-rw-r--r-- | drivers/base/regmap/Makefile | 1 | ||||
-rw-r--r-- | drivers/base/regmap/internal.h | 8 | ||||
-rw-r--r-- | drivers/base/regmap/regcache-flat.c | 15 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 12 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-sdw.c | 88 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 47 | ||||
-rw-r--r-- | drivers/mfd/syscon.c | 19 | ||||
-rw-r--r-- | include/linux/regmap.h | 50 |
10 files changed, 229 insertions, 25 deletions
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/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 3a1535d812d8..067073e4beb1 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 @@ -39,5 +38,6 @@ config REGMAP_MMIO config REGMAP_IRQ bool -config REGMAP_HWSPINLOCK - bool +config REGMAP_SOUNDWIRE + tristate + depends on SOUNDWIRE_BUS diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index 0d298c446108..22d263cca395 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o obj-$(CONFIG_REGMAP_W1) += regmap-w1.o +obj-$(CONFIG_REGMAP_SOUNDWIRE) += regmap-sdw.o 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/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; } diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 36ce3511c733..f3266334063e 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -529,6 +529,18 @@ 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; + } + /* 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-sdw.c b/drivers/base/regmap/regmap-sdw.c new file mode 100644 index 000000000000..50a66382d87d --- /dev/null +++ b/drivers/base/regmap/regmap-sdw.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2015-17 Intel Corporation. + +#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/soundwire/sdw.h> +#include "internal.h" + +static int regmap_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct sdw_slave *slave = dev_to_sdw_dev(dev); + + return sdw_write(slave, reg, val); +} + +static int regmap_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int read; + + read = sdw_read(slave, reg); + if (read < 0) + return read; + + *val = read; + return 0; +} + +static struct regmap_bus regmap_sdw = { + .reg_read = regmap_sdw_read, + .reg_write = regmap_sdw_write, + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, +}; + +static int regmap_sdw_config_check(const struct regmap_config *config) +{ + /* All register are 8-bits wide as per MIPI Soundwire 1.0 Spec */ + if (config->val_bits != 8) + return -ENOTSUPP; + + /* Registers are 32 bits wide */ + if (config->reg_bits != 32) + return -ENOTSUPP; + + if (config->pad_bits != 0) + return -ENOTSUPP; + + return 0; +} + +struct regmap *__regmap_init_sdw(struct sdw_slave *sdw, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) +{ + int ret; + + ret = regmap_sdw_config_check(config); + if (ret) + return ERR_PTR(ret); + + return __regmap_init(&sdw->dev, ®map_sdw, + &sdw->dev, config, lock_key, lock_name); +} +EXPORT_SYMBOL_GPL(__regmap_init_sdw); + +struct regmap *__devm_regmap_init_sdw(struct sdw_slave *sdw, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) +{ + int ret; + + ret = regmap_sdw_config_check(config); + if (ret) + return ERR_PTR(ret); + + return __devm_regmap_init(&sdw->dev, ®map_sdw, + &sdw->dev, config, lock_key, lock_name); +} +EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw); + +MODULE_DESCRIPTION("Regmap SoundWire Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 8d516a9bfc01..ee302ccdfbc8 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,11 @@ static void regmap_unlock_hwlock_irqrestore(void *__map) hwspin_unlock_irqrestore(map->hwlock, &map->spinlock_flags); } -#endif + +static void regmap_lock_unlock_none(void *__map) +{ + +} static void regmap_lock_mutex(void *__map) { @@ -669,16 +672,26 @@ struct regmap *__regmap_init(struct device *dev, goto err; } - if (config->lock && config->unlock) { + 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); + } else if (config->lock && config->unlock) { map->lock = config->lock; map->unlock = config->unlock; map->lock_arg = config->lock_arg; - } else if (config->hwlock_id) { -#ifdef REGMAP_HWSPINLOCK + } else if (config->use_hwlock) { map->hwlock = hwspin_lock_request_specific(config->hwlock_id); if (!map->hwlock) { ret = -ENXIO; - goto err_map; + goto err_name; } switch (config->hwlock_mode) { @@ -697,10 +710,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) { @@ -762,14 +771,15 @@ 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); INIT_LIST_HEAD(&map->async_free); init_waitqueue_head(&map->async_waitq); - if (config->read_flag_mask || config->write_flag_mask) { + if (config->read_flag_mask || + config->write_flag_mask || + config->zero_flag_mask) { map->read_flag_mask = config->read_flag_mask; map->write_flag_mask = config->write_flag_mask; } else if (bus) { @@ -1116,8 +1126,10 @@ 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_name: + kfree_const(map->name); err_map: kfree(map); err: @@ -1305,8 +1317,9 @@ 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_const(map->name); kfree(map); } EXPORT_SYMBOL_GPL(regmap_exit); @@ -2423,13 +2436,15 @@ static int _regmap_bus_read(void *context, unsigned int reg, { int ret; struct regmap *map = context; + void *work_val = map->work_buf + map->format.reg_bytes + + map->format.pad_bytes; if (!map->format.parse_val) return -EINVAL; - ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); + ret = _regmap_raw_read(map, reg, work_val, map->format.val_bytes); if (ret == 0) - *val = map->format.parse_val(map->work_buf); + *val = map->format.parse_val(work_val); return ret; } 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 <linux/err.h> +#include <linux/hwspinlock.h> #include <linux/io.h> #include <linux/module.h> #include <linux/list.h> @@ -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; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 15eddc1353ba..20268b7d5001 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -30,6 +30,7 @@ struct regmap; struct regmap_range_cfg; struct regmap_field; struct snd_ac97; +struct sdw_slave; /* An enum of all the supported cache types */ enum regcache_type { @@ -264,6 +265,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. @@ -296,7 +300,10 @@ typedef void (*regmap_unlock)(void *); * a read. * @write_flag_mask: Mask to be set in the top bytes of the register when doing * a write. If both read_flag_mask and write_flag_mask are - * empty the regmap_bus default masks are used. + * empty and zero_flag_mask is not set the regmap_bus default + * masks are used. + * @zero_flag_mask: If set, read_flag_mask and write_flag_mask are used even + * if they are both empty. * @use_single_rw: If set, converts the bulk read and write operations into * a series of single read and write operations. This is useful * for device that does not support bulk read and write. @@ -317,6 +324,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. @@ -333,6 +341,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; @@ -355,6 +365,7 @@ struct regmap_config { unsigned long read_flag_mask; unsigned long write_flag_mask; + bool zero_flag_mask; bool use_single_rw; bool can_multi_write; @@ -365,6 +376,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; }; @@ -524,6 +536,10 @@ 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 *__regmap_init_sdw(struct sdw_slave *sdw, + 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, @@ -561,6 +577,10 @@ 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); +struct regmap *__devm_regmap_init_sdw(struct sdw_slave *sdw, + 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 @@ -710,6 +730,20 @@ int regmap_attach_dev(struct device *dev, struct regmap *map, bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); /** + * regmap_init_sdw() - Initialise register map + * + * @sdw: 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_sdw(sdw, config) \ + __regmap_lockdep_wrapper(__regmap_init_sdw, #config, \ + sdw, config) + + +/** * devm_regmap_init() - Initialise managed register map * * @dev: Device that will be interacted with @@ -839,6 +873,20 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); __regmap_lockdep_wrapper(__devm_regmap_init_ac97, #config, \ ac97, config) +/** + * devm_regmap_init_sdw() - Initialise managed register map + * + * @sdw: 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_sdw(sdw, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_sdw, #config, \ + sdw, config) + void regmap_exit(struct regmap *map); int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config); |