regmap: Updates for v4.16

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.
 -----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCgAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAlpvTfYTHGJyb29uaWVA
 a2VybmVsLm9yZwAKCRAk1otyXVSH0Cd8B/4zeYHL73moxvAxAH/99HGVCHQHcR4k
 hdLaGHs8JN5ZLoNCqixFdkffsdBM2H06JPOnBZMHHcb5Pf7KL3OEgPNeiMUpzxUL
 0EcXoZiJz0HzRVwi6M9nt3Bv41rD5+v9nS6oolsqbU1b6w5ofZQ7ENeN+nhxLk54
 Wy8avQ3VDmiKsSAAXDMEA6i58Zz7dT1ibyyOcYGyEdt7iFOL8d0aM4c3cAGHfL+E
 Ss9/dyeY/8glLDy+rxp5hZHkLY7FKFDQcD1/FSR26DHslc0Swa9mn/0SmhF0pQWe
 RVEn63b4B5iT0E8brlWHwNh466jT6+fyJKzYDzEM+v1RP4qUmluFubjK
 =X/P5
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2018-01-29 11:35:24 -08:00
commit b5856f97b3
10 changed files with 229 additions and 25 deletions

View File

@ -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>;
};

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;

View File

@ -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, &regmap_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, &regmap_sdw,
&sdw->dev, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw);
MODULE_DESCRIPTION("Regmap SoundWire Module");
MODULE_LICENSE("GPL v2");

View File

@ -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;
}

View File

@ -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;

View File

@ -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
@ -709,6 +729,20 @@ int regmap_attach_dev(struct device *dev, struct regmap *map,
ac97, config)
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
*
@ -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);