mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-22 04:03:58 +08:00
regmap: Updates for v3.18
The main update this time around is the addition of a standard DT binding for specifying the endianness of devices. This allows drivers to support any endianness of device register map without any code, useful for configurable IP blocks. There's also a few bug fixes that I didn't get round to sending, none of them terribly severe or new, and a reduction in size for struct regmap. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJUMneFAAoJECTWi3JdVIfQ6ZgH/ApJnafN3v957xLDb1pOapf0 Ls8vbQzODSpBqQDWcLGTfOuE2jSqd9lhFVqjK1eh4OGAcQ5A6FIOJshaCRfEBM7E 96b/dNbHdCb2iigyQgIsH7tA9c8cQwianJM1nrkUdMLZ7loQtsKS6X7+8YbEM1Pt RNkYeS8ar4m88cRakxZayv7cERgZouoRTuOkTEycq0Uv1YJ+hf7XqUyO8A2So1xH TvU/i+UKKUoz4Rkh+hgMq/U8xm5zAXs6WJR7LfQKYQG0T/m7Ft81Mec3Imwy3SM+ WVDuCvSXrzj08J1jrmut92xB6JNpGxisbAO3YKeF8VXdFcgIF4F29gFe7Aei240= =N2kC -----END PGP SIGNATURE----- Merge tag 'regmap-v3.18' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap Pull regmap updates from Mark Brown: "The main update this time around is the addition of a standard DT binding for specifying the endianness of devices. This allows drivers to support any endianness of device register map without any code, useful for configurable IP blocks. There's also a few bug fixes that I didn't get round to sending, none of them terribly severe or new, and a reduction in size for struct regmap" * tag 'regmap-v3.18' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: regmap: Fix debugfs-file 'registers' mode regmap: fix possible ZERO_SIZE_PTR pointer dereferencing error. regmap: debugfs: fix possbile NULL pointer dereference regmap: fix NULL pointer dereference in _regmap_write/read regmap: fix NULL pointer dereference in regmap_get_val_endian regmap: cache: Do not fail silently from regcache_sync calls regmap: change struct regmap's internal locks as union regmap: Split regmap_get_endian() in two functions regmap: of_regmap_get_endian() cleanup regmap: Fix DT endianess parsing logic regmap: Add explicit dependencies to catch "select" misuse regmap: Restore L: linux-kernel@vger.kernel.org entry regmap: Add the DT binding documentation for endianness regmap: add DT endianness binding support.
This commit is contained in:
commit
c831dd7352
47
Documentation/devicetree/bindings/regmap/regmap.txt
Normal file
47
Documentation/devicetree/bindings/regmap/regmap.txt
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
Device-Tree binding for regmap
|
||||||
|
|
||||||
|
The endianness mode of CPU & Device scenarios:
|
||||||
|
Index Device Endianness properties
|
||||||
|
---------------------------------------------------
|
||||||
|
1 BE 'big-endian'
|
||||||
|
2 LE 'little-endian'
|
||||||
|
|
||||||
|
For one device driver, which will run in different scenarios above
|
||||||
|
on different SoCs using the devicetree, we need one way to simplify
|
||||||
|
this.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- {big,little}-endian: these are boolean properties, if absent
|
||||||
|
meaning that the CPU and the Device are in the same endianness mode,
|
||||||
|
these properties are for register values and all the buffers only.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
Scenario 1 : CPU in LE mode & device in LE mode.
|
||||||
|
dev: dev@40031000 {
|
||||||
|
compatible = "name";
|
||||||
|
reg = <0x40031000 0x1000>;
|
||||||
|
...
|
||||||
|
};
|
||||||
|
|
||||||
|
Scenario 2 : CPU in LE mode & device in BE mode.
|
||||||
|
dev: dev@40031000 {
|
||||||
|
compatible = "name";
|
||||||
|
reg = <0x40031000 0x1000>;
|
||||||
|
...
|
||||||
|
big-endian;
|
||||||
|
};
|
||||||
|
|
||||||
|
Scenario 3 : CPU in BE mode & device in BE mode.
|
||||||
|
dev: dev@40031000 {
|
||||||
|
compatible = "name";
|
||||||
|
reg = <0x40031000 0x1000>;
|
||||||
|
...
|
||||||
|
};
|
||||||
|
|
||||||
|
Scenario 4 : CPU in BE mode & device in LE mode.
|
||||||
|
dev: dev@40031000 {
|
||||||
|
compatible = "name";
|
||||||
|
reg = <0x40031000 0x1000>;
|
||||||
|
...
|
||||||
|
little-endian;
|
||||||
|
};
|
@ -7595,6 +7595,7 @@ F: fs/reiserfs/
|
|||||||
|
|
||||||
REGISTER MAP ABSTRACTION
|
REGISTER MAP ABSTRACTION
|
||||||
M: Mark Brown <broonie@kernel.org>
|
M: Mark Brown <broonie@kernel.org>
|
||||||
|
L: linux-kernel@vger.kernel.org
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git
|
||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/base/regmap/
|
F: drivers/base/regmap/
|
||||||
|
@ -11,12 +11,15 @@ config REGMAP
|
|||||||
|
|
||||||
config REGMAP_I2C
|
config REGMAP_I2C
|
||||||
tristate
|
tristate
|
||||||
|
depends on I2C
|
||||||
|
|
||||||
config REGMAP_SPI
|
config REGMAP_SPI
|
||||||
tristate
|
tristate
|
||||||
|
depends on SPI
|
||||||
|
|
||||||
config REGMAP_SPMI
|
config REGMAP_SPMI
|
||||||
tristate
|
tristate
|
||||||
|
depends on SPMI
|
||||||
|
|
||||||
config REGMAP_MMIO
|
config REGMAP_MMIO
|
||||||
tristate
|
tristate
|
||||||
|
@ -49,8 +49,10 @@ struct regmap_async {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct regmap {
|
struct regmap {
|
||||||
struct mutex mutex;
|
union {
|
||||||
spinlock_t spinlock;
|
struct mutex mutex;
|
||||||
|
spinlock_t spinlock;
|
||||||
|
};
|
||||||
unsigned long spinlock_flags;
|
unsigned long spinlock_flags;
|
||||||
regmap_lock lock;
|
regmap_lock lock;
|
||||||
regmap_unlock unlock;
|
regmap_unlock unlock;
|
||||||
|
@ -269,8 +269,11 @@ static int regcache_default_sync(struct regmap *map, unsigned int min,
|
|||||||
map->cache_bypass = 1;
|
map->cache_bypass = 1;
|
||||||
ret = _regmap_write(map, reg, val);
|
ret = _regmap_write(map, reg, val);
|
||||||
map->cache_bypass = 0;
|
map->cache_bypass = 0;
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
dev_err(map->dev, "Unable to sync register %#x. %d\n",
|
||||||
|
reg, ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
dev_dbg(map->dev, "Synced register %#x, value %#x\n", reg, val);
|
dev_dbg(map->dev, "Synced register %#x, value %#x\n", reg, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,8 +618,11 @@ static int regcache_sync_block_single(struct regmap *map, void *block,
|
|||||||
ret = _regmap_write(map, regtmp, val);
|
ret = _regmap_write(map, regtmp, val);
|
||||||
|
|
||||||
map->cache_bypass = 0;
|
map->cache_bypass = 0;
|
||||||
if (ret != 0)
|
if (ret != 0) {
|
||||||
|
dev_err(map->dev, "Unable to sync register %#x. %d\n",
|
||||||
|
regtmp, ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
|
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
|
||||||
regtmp, val);
|
regtmp, val);
|
||||||
}
|
}
|
||||||
@ -641,6 +647,9 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,
|
|||||||
map->cache_bypass = 1;
|
map->cache_bypass = 1;
|
||||||
|
|
||||||
ret = _regmap_raw_write(map, base, *data, count * val_bytes);
|
ret = _regmap_raw_write(map, base, *data, count * val_bytes);
|
||||||
|
if (ret)
|
||||||
|
dev_err(map->dev, "Unable to sync registers %#x-%#x. %d\n",
|
||||||
|
base, cur - map->reg_stride, ret);
|
||||||
|
|
||||||
map->cache_bypass = 0;
|
map->cache_bypass = 0;
|
||||||
|
|
||||||
|
@ -473,6 +473,7 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
|
|||||||
{
|
{
|
||||||
struct rb_node *next;
|
struct rb_node *next;
|
||||||
struct regmap_range_node *range_node;
|
struct regmap_range_node *range_node;
|
||||||
|
const char *devname = "dummy";
|
||||||
|
|
||||||
/* If we don't have the debugfs root yet, postpone init */
|
/* If we don't have the debugfs root yet, postpone init */
|
||||||
if (!regmap_debugfs_root) {
|
if (!regmap_debugfs_root) {
|
||||||
@ -491,12 +492,15 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
|
|||||||
INIT_LIST_HEAD(&map->debugfs_off_cache);
|
INIT_LIST_HEAD(&map->debugfs_off_cache);
|
||||||
mutex_init(&map->cache_lock);
|
mutex_init(&map->cache_lock);
|
||||||
|
|
||||||
|
if (map->dev)
|
||||||
|
devname = dev_name(map->dev);
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
|
map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
|
||||||
dev_name(map->dev), name);
|
devname, name);
|
||||||
name = map->debugfs_name;
|
name = map->debugfs_name;
|
||||||
} else {
|
} else {
|
||||||
name = dev_name(map->dev);
|
name = devname;
|
||||||
}
|
}
|
||||||
|
|
||||||
map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
|
map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
|
||||||
|
@ -168,6 +168,8 @@ static struct regmap_bus regmap_i2c = {
|
|||||||
.write = regmap_i2c_write,
|
.write = regmap_i2c_write,
|
||||||
.gather_write = regmap_i2c_gather_write,
|
.gather_write = regmap_i2c_gather_write,
|
||||||
.read = regmap_i2c_read,
|
.read = regmap_i2c_read,
|
||||||
|
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||||
|
.val_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
|
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
|
||||||
|
@ -109,6 +109,8 @@ static struct regmap_bus regmap_spi = {
|
|||||||
.async_alloc = regmap_spi_async_alloc,
|
.async_alloc = regmap_spi_async_alloc,
|
||||||
.read = regmap_spi_read,
|
.read = regmap_spi_read,
|
||||||
.read_flag_mask = 0x80,
|
.read_flag_mask = 0x80,
|
||||||
|
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||||
|
.val_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/rbtree.h>
|
#include <linux/rbtree.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
|
|
||||||
@ -448,6 +449,71 @@ int regmap_attach_dev(struct device *dev, struct regmap *map,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(regmap_attach_dev);
|
EXPORT_SYMBOL_GPL(regmap_attach_dev);
|
||||||
|
|
||||||
|
static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus,
|
||||||
|
const struct regmap_config *config)
|
||||||
|
{
|
||||||
|
enum regmap_endian endian;
|
||||||
|
|
||||||
|
/* Retrieve the endianness specification from the regmap config */
|
||||||
|
endian = config->reg_format_endian;
|
||||||
|
|
||||||
|
/* If the regmap config specified a non-default value, use that */
|
||||||
|
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||||
|
return endian;
|
||||||
|
|
||||||
|
/* Retrieve the endianness specification from the bus config */
|
||||||
|
if (bus && bus->reg_format_endian_default)
|
||||||
|
endian = bus->reg_format_endian_default;
|
||||||
|
|
||||||
|
/* If the bus specified a non-default value, use that */
|
||||||
|
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||||
|
return endian;
|
||||||
|
|
||||||
|
/* Use this if no other value was found */
|
||||||
|
return REGMAP_ENDIAN_BIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum regmap_endian regmap_get_val_endian(struct device *dev,
|
||||||
|
const struct regmap_bus *bus,
|
||||||
|
const struct regmap_config *config)
|
||||||
|
{
|
||||||
|
struct device_node *np;
|
||||||
|
enum regmap_endian endian;
|
||||||
|
|
||||||
|
/* Retrieve the endianness specification from the regmap config */
|
||||||
|
endian = config->val_format_endian;
|
||||||
|
|
||||||
|
/* If the regmap config specified a non-default value, use that */
|
||||||
|
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||||
|
return endian;
|
||||||
|
|
||||||
|
/* If the dev and dev->of_node exist try to get endianness from DT */
|
||||||
|
if (dev && dev->of_node) {
|
||||||
|
np = dev->of_node;
|
||||||
|
|
||||||
|
/* Parse the device's DT node for an endianness specification */
|
||||||
|
if (of_property_read_bool(np, "big-endian"))
|
||||||
|
endian = REGMAP_ENDIAN_BIG;
|
||||||
|
else if (of_property_read_bool(np, "little-endian"))
|
||||||
|
endian = REGMAP_ENDIAN_LITTLE;
|
||||||
|
|
||||||
|
/* If the endianness was specified in DT, use that */
|
||||||
|
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||||
|
return endian;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve the endianness specification from the bus config */
|
||||||
|
if (bus && bus->val_format_endian_default)
|
||||||
|
endian = bus->val_format_endian_default;
|
||||||
|
|
||||||
|
/* If the bus specified a non-default value, use that */
|
||||||
|
if (endian != REGMAP_ENDIAN_DEFAULT)
|
||||||
|
return endian;
|
||||||
|
|
||||||
|
/* Use this if no other value was found */
|
||||||
|
return REGMAP_ENDIAN_BIG;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* regmap_init(): Initialise register map
|
* regmap_init(): Initialise register map
|
||||||
*
|
*
|
||||||
@ -551,17 +617,8 @@ struct regmap *regmap_init(struct device *dev,
|
|||||||
map->reg_read = _regmap_bus_read;
|
map->reg_read = _regmap_bus_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
reg_endian = config->reg_format_endian;
|
reg_endian = regmap_get_reg_endian(bus, config);
|
||||||
if (reg_endian == REGMAP_ENDIAN_DEFAULT)
|
val_endian = regmap_get_val_endian(dev, bus, config);
|
||||||
reg_endian = bus->reg_format_endian_default;
|
|
||||||
if (reg_endian == REGMAP_ENDIAN_DEFAULT)
|
|
||||||
reg_endian = REGMAP_ENDIAN_BIG;
|
|
||||||
|
|
||||||
val_endian = config->val_format_endian;
|
|
||||||
if (val_endian == REGMAP_ENDIAN_DEFAULT)
|
|
||||||
val_endian = bus->val_format_endian_default;
|
|
||||||
if (val_endian == REGMAP_ENDIAN_DEFAULT)
|
|
||||||
val_endian = REGMAP_ENDIAN_BIG;
|
|
||||||
|
|
||||||
switch (config->reg_bits + map->reg_shift) {
|
switch (config->reg_bits + map->reg_shift) {
|
||||||
case 2:
|
case 2:
|
||||||
@ -1408,7 +1465,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LOG_DEVICE
|
#ifdef LOG_DEVICE
|
||||||
if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
|
if (map->dev && strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
|
||||||
dev_info(map->dev, "%x <= %x\n", reg, val);
|
dev_info(map->dev, "%x <= %x\n", reg, val);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1659,6 +1716,9 @@ out:
|
|||||||
} else {
|
} else {
|
||||||
void *wval;
|
void *wval;
|
||||||
|
|
||||||
|
if (!val_count)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL);
|
wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL);
|
||||||
if (!wval) {
|
if (!wval) {
|
||||||
dev_err(map->dev, "Error in memory allocation\n");
|
dev_err(map->dev, "Error in memory allocation\n");
|
||||||
@ -2058,7 +2118,7 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
|
|||||||
ret = map->reg_read(context, reg, val);
|
ret = map->reg_read(context, reg, val);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
#ifdef LOG_DEVICE
|
#ifdef LOG_DEVICE
|
||||||
if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
|
if (map->dev && strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
|
||||||
dev_info(map->dev, "%x => %x\n", reg, *val);
|
dev_info(map->dev, "%x => %x\n", reg, *val);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user