mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-10 23:29:46 +00:00
regmap: Updates for v4.4
Quite a few new features for regmap this time, mostly expanding things around the edges of the existing functionality to cover more devices rather than thinsg with wide applicability: - Support for offload of the update_bits() operation to hardware where devices implement bit level access. - Support for a few extra operations that need scratch buffers on fast_io devices where we can't sleep. - Expanded the feature set of regmap_irq to cope with some extra register layouts. - Cleanups to the debugfs code. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJWN6usAAoJECTWi3JdVIfQT7cH/1sNJlpt8s2pu/29LtBN0oMH ZPPT8p46Xph7lbVMbMWZfgcobtISKMYo8dGDEeIYR1c1DZObErJSDxj5I/g5PxUv FuJ1kVeJ0L0GJE78sB69LQQUAYXfmbtOnmivok/huNID+wpZbTE7uZjAby4tffuk Mom2TLSnrm63j/zDnsi4VBAB9w1pQCIp+UiJ8LR8PCglcSHJ/eZLrh53F6BzXRU8 9QseD5YQCh4d3go6QZhVNX8iJEby9ehmtCnzN8zEfpXKpc2T1pCoHEH/BnKnxd16 i35A4RjW0GZfilS1815prjDhHJSbrQiqzyIh7Wua+zll7kCx6K/oLdjUfTIK9L0= =A8Lm -----END PGP SIGNATURE----- Merge tag 'regmap-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap Pull regmap updates from Mark Brown: "Quite a few new features for regmap this time, mostly expanding things around the edges of the existing functionality to cover more devices rather than thinsg with wide applicability: - Support for offload of the update_bits() operation to hardware where devices implement bit level access. - Support for a few extra operations that need scratch buffers on fast_io devices where we can't sleep. - Expanded the feature set of regmap_irq to cope with some extra register layouts. - Cleanups to the debugfs code" * tag 'regmap-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: regmap: Allow installing custom reg_update_bits function regmap: debugfs: simplify regmap_reg_ranges_read_file() slightly regmap: debugfs: use memcpy instead of snprintf regmap: debugfs: use snprintf return value in regmap_reg_ranges_read_file() regmap: Add generic macro to define regmap_irq regmap: debugfs: Remove scratch buffer for register length calculation regmap: irq: add ack_invert flag for chips using cleared bits as ack regmap: irq: add support for chips who have separate unmask registers regmap: Allocate buffers with GFP_ATOMIC when fast_io == true
This commit is contained in:
commit
5062ecdb66
@ -59,6 +59,7 @@ struct regmap {
|
|||||||
regmap_lock lock;
|
regmap_lock lock;
|
||||||
regmap_unlock unlock;
|
regmap_unlock unlock;
|
||||||
void *lock_arg; /* This is passed to lock/unlock functions */
|
void *lock_arg; /* This is passed to lock/unlock functions */
|
||||||
|
gfp_t alloc_flags;
|
||||||
|
|
||||||
struct device *dev; /* Device we do I/O on */
|
struct device *dev; /* Device we do I/O on */
|
||||||
void *work_buf; /* Scratch buffer used to format I/O */
|
void *work_buf; /* Scratch buffer used to format I/O */
|
||||||
@ -98,6 +99,8 @@ struct regmap {
|
|||||||
|
|
||||||
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
|
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
|
||||||
int (*reg_write)(void *context, unsigned int reg, unsigned int val);
|
int (*reg_write)(void *context, unsigned int reg, unsigned int val);
|
||||||
|
int (*reg_update_bits)(void *context, unsigned int reg,
|
||||||
|
unsigned int mask, unsigned int val);
|
||||||
|
|
||||||
bool defer_caching;
|
bool defer_caching;
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ static LIST_HEAD(regmap_debugfs_early_list);
|
|||||||
static DEFINE_MUTEX(regmap_debugfs_early_lock);
|
static DEFINE_MUTEX(regmap_debugfs_early_lock);
|
||||||
|
|
||||||
/* Calculate the length of a fixed format */
|
/* Calculate the length of a fixed format */
|
||||||
static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size)
|
static size_t regmap_calc_reg_len(int max_val)
|
||||||
{
|
{
|
||||||
return snprintf(NULL, 0, "%x", max_val);
|
return snprintf(NULL, 0, "%x", max_val);
|
||||||
}
|
}
|
||||||
@ -173,8 +173,7 @@ static inline void regmap_calc_tot_len(struct regmap *map,
|
|||||||
{
|
{
|
||||||
/* Calculate the length of a fixed format */
|
/* Calculate the length of a fixed format */
|
||||||
if (!map->debugfs_tot_len) {
|
if (!map->debugfs_tot_len) {
|
||||||
map->debugfs_reg_len = regmap_calc_reg_len(map->max_register,
|
map->debugfs_reg_len = regmap_calc_reg_len(map->max_register),
|
||||||
buf, count);
|
|
||||||
map->debugfs_val_len = 2 * map->format.val_bytes;
|
map->debugfs_val_len = 2 * map->format.val_bytes;
|
||||||
map->debugfs_tot_len = map->debugfs_reg_len +
|
map->debugfs_tot_len = map->debugfs_reg_len +
|
||||||
map->debugfs_val_len + 3; /* : \n */
|
map->debugfs_val_len + 3; /* : \n */
|
||||||
@ -338,6 +337,7 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
|
|||||||
char *buf;
|
char *buf;
|
||||||
char *entry;
|
char *entry;
|
||||||
int ret;
|
int ret;
|
||||||
|
unsigned entry_len;
|
||||||
|
|
||||||
if (*ppos < 0 || !count)
|
if (*ppos < 0 || !count)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -365,18 +365,15 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
|
|||||||
p = 0;
|
p = 0;
|
||||||
mutex_lock(&map->cache_lock);
|
mutex_lock(&map->cache_lock);
|
||||||
list_for_each_entry(c, &map->debugfs_off_cache, list) {
|
list_for_each_entry(c, &map->debugfs_off_cache, list) {
|
||||||
snprintf(entry, PAGE_SIZE, "%x-%x",
|
entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n",
|
||||||
c->base_reg, c->max_reg);
|
c->base_reg, c->max_reg);
|
||||||
if (p >= *ppos) {
|
if (p >= *ppos) {
|
||||||
if (buf_pos + 1 + strlen(entry) > count)
|
if (buf_pos + entry_len > count)
|
||||||
break;
|
break;
|
||||||
snprintf(buf + buf_pos, count - buf_pos,
|
memcpy(buf + buf_pos, entry, entry_len);
|
||||||
"%s", entry);
|
buf_pos += entry_len;
|
||||||
buf_pos += strlen(entry);
|
|
||||||
buf[buf_pos] = '\n';
|
|
||||||
buf_pos++;
|
|
||||||
}
|
}
|
||||||
p += strlen(entry) + 1;
|
p += entry_len;
|
||||||
}
|
}
|
||||||
mutex_unlock(&map->cache_lock);
|
mutex_unlock(&map->cache_lock);
|
||||||
|
|
||||||
@ -420,7 +417,7 @@ static ssize_t regmap_access_read_file(struct file *file,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Calculate the length of a fixed format */
|
/* Calculate the length of a fixed format */
|
||||||
reg_len = regmap_calc_reg_len(map->max_register, buf, count);
|
reg_len = regmap_calc_reg_len(map->max_register);
|
||||||
tot_len = reg_len + 10; /* ': R W V P\n' */
|
tot_len = reg_len + 10; /* ': R W V P\n' */
|
||||||
|
|
||||||
for (i = 0; i <= map->max_register; i += map->reg_stride) {
|
for (i = 0; i <= map->max_register; i += map->reg_stride) {
|
||||||
|
@ -63,6 +63,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
|
|||||||
struct regmap *map = d->map;
|
struct regmap *map = d->map;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
u32 unmask_offset;
|
||||||
|
|
||||||
if (d->chip->runtime_pm) {
|
if (d->chip->runtime_pm) {
|
||||||
ret = pm_runtime_get_sync(map->dev);
|
ret = pm_runtime_get_sync(map->dev);
|
||||||
@ -79,12 +80,28 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
|
|||||||
for (i = 0; i < d->chip->num_regs; i++) {
|
for (i = 0; i < d->chip->num_regs; i++) {
|
||||||
reg = d->chip->mask_base +
|
reg = d->chip->mask_base +
|
||||||
(i * map->reg_stride * d->irq_reg_stride);
|
(i * map->reg_stride * d->irq_reg_stride);
|
||||||
if (d->chip->mask_invert)
|
if (d->chip->mask_invert) {
|
||||||
ret = regmap_update_bits(d->map, reg,
|
ret = regmap_update_bits(d->map, reg,
|
||||||
d->mask_buf_def[i], ~d->mask_buf[i]);
|
d->mask_buf_def[i], ~d->mask_buf[i]);
|
||||||
else
|
} else if (d->chip->unmask_base) {
|
||||||
|
/* set mask with mask_base register */
|
||||||
|
ret = regmap_update_bits(d->map, reg,
|
||||||
|
d->mask_buf_def[i], ~d->mask_buf[i]);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(d->map->dev,
|
||||||
|
"Failed to sync unmasks in %x\n",
|
||||||
|
reg);
|
||||||
|
unmask_offset = d->chip->unmask_base -
|
||||||
|
d->chip->mask_base;
|
||||||
|
/* clear mask with unmask_base register */
|
||||||
|
ret = regmap_update_bits(d->map,
|
||||||
|
reg + unmask_offset,
|
||||||
|
d->mask_buf_def[i],
|
||||||
|
d->mask_buf[i]);
|
||||||
|
} else {
|
||||||
ret = regmap_update_bits(d->map, reg,
|
ret = regmap_update_bits(d->map, reg,
|
||||||
d->mask_buf_def[i], d->mask_buf[i]);
|
d->mask_buf_def[i], d->mask_buf[i]);
|
||||||
|
}
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
dev_err(d->map->dev, "Failed to sync masks in %x\n",
|
dev_err(d->map->dev, "Failed to sync masks in %x\n",
|
||||||
reg);
|
reg);
|
||||||
@ -116,7 +133,11 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
|
|||||||
if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) {
|
if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) {
|
||||||
reg = d->chip->ack_base +
|
reg = d->chip->ack_base +
|
||||||
(i * map->reg_stride * d->irq_reg_stride);
|
(i * map->reg_stride * d->irq_reg_stride);
|
||||||
ret = regmap_write(map, reg, d->mask_buf[i]);
|
/* some chips ack by write 0 */
|
||||||
|
if (d->chip->ack_invert)
|
||||||
|
ret = regmap_write(map, reg, ~d->mask_buf[i]);
|
||||||
|
else
|
||||||
|
ret = regmap_write(map, reg, d->mask_buf[i]);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
dev_err(d->map->dev, "Failed to ack 0x%x: %d\n",
|
dev_err(d->map->dev, "Failed to ack 0x%x: %d\n",
|
||||||
reg, ret);
|
reg, ret);
|
||||||
@ -339,6 +360,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
|||||||
int i;
|
int i;
|
||||||
int ret = -ENOMEM;
|
int ret = -ENOMEM;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
u32 unmask_offset;
|
||||||
|
|
||||||
if (chip->num_regs <= 0)
|
if (chip->num_regs <= 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -420,7 +442,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
|||||||
if (chip->mask_invert)
|
if (chip->mask_invert)
|
||||||
ret = regmap_update_bits(map, reg,
|
ret = regmap_update_bits(map, reg,
|
||||||
d->mask_buf[i], ~d->mask_buf[i]);
|
d->mask_buf[i], ~d->mask_buf[i]);
|
||||||
else
|
else if (d->chip->unmask_base) {
|
||||||
|
unmask_offset = d->chip->unmask_base -
|
||||||
|
d->chip->mask_base;
|
||||||
|
ret = regmap_update_bits(d->map,
|
||||||
|
reg + unmask_offset,
|
||||||
|
d->mask_buf[i],
|
||||||
|
d->mask_buf[i]);
|
||||||
|
} else
|
||||||
ret = regmap_update_bits(map, reg,
|
ret = regmap_update_bits(map, reg,
|
||||||
d->mask_buf[i], d->mask_buf[i]);
|
d->mask_buf[i], d->mask_buf[i]);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
@ -445,7 +474,11 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
|||||||
if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) {
|
if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) {
|
||||||
reg = chip->ack_base +
|
reg = chip->ack_base +
|
||||||
(i * map->reg_stride * d->irq_reg_stride);
|
(i * map->reg_stride * d->irq_reg_stride);
|
||||||
ret = regmap_write(map, reg,
|
if (chip->ack_invert)
|
||||||
|
ret = regmap_write(map, reg,
|
||||||
|
~(d->status_buf[i] & d->mask_buf[i]));
|
||||||
|
else
|
||||||
|
ret = regmap_write(map, reg,
|
||||||
d->status_buf[i] & d->mask_buf[i]);
|
d->status_buf[i] & d->mask_buf[i]);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
|
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
|
||||||
|
@ -561,6 +561,16 @@ struct regmap *__regmap_init(struct device *dev,
|
|||||||
}
|
}
|
||||||
map->lock_arg = map;
|
map->lock_arg = map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When we write in fast-paths with regmap_bulk_write() don't allocate
|
||||||
|
* scratch buffers with sleeping allocations.
|
||||||
|
*/
|
||||||
|
if ((bus && bus->fast_io) || config->fast_io)
|
||||||
|
map->alloc_flags = GFP_ATOMIC;
|
||||||
|
else
|
||||||
|
map->alloc_flags = GFP_KERNEL;
|
||||||
|
|
||||||
map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
|
map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
|
||||||
map->format.pad_bytes = config->pad_bits / 8;
|
map->format.pad_bytes = config->pad_bits / 8;
|
||||||
map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
|
map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
|
||||||
@ -619,6 +629,7 @@ struct regmap *__regmap_init(struct device *dev,
|
|||||||
goto skip_format_initialization;
|
goto skip_format_initialization;
|
||||||
} else {
|
} else {
|
||||||
map->reg_read = _regmap_bus_read;
|
map->reg_read = _regmap_bus_read;
|
||||||
|
map->reg_update_bits = bus->reg_update_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
reg_endian = regmap_get_reg_endian(bus, config);
|
reg_endian = regmap_get_reg_endian(bus, config);
|
||||||
@ -1786,7 +1797,7 @@ out:
|
|||||||
if (!val_count)
|
if (!val_count)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL);
|
wval = kmemdup(val, val_count * val_bytes, map->alloc_flags);
|
||||||
if (!wval) {
|
if (!wval) {
|
||||||
dev_err(map->dev, "Error in memory allocation\n");
|
dev_err(map->dev, "Error in memory allocation\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -2509,20 +2520,26 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
|
|||||||
int ret;
|
int ret;
|
||||||
unsigned int tmp, orig;
|
unsigned int tmp, orig;
|
||||||
|
|
||||||
ret = _regmap_read(map, reg, &orig);
|
if (change)
|
||||||
if (ret != 0)
|
*change = false;
|
||||||
return ret;
|
|
||||||
|
|
||||||
tmp = orig & ~mask;
|
if (regmap_volatile(map, reg) && map->reg_update_bits) {
|
||||||
tmp |= val & mask;
|
ret = map->reg_update_bits(map->bus_context, reg, mask, val);
|
||||||
|
if (ret == 0 && change)
|
||||||
if (force_write || (tmp != orig)) {
|
|
||||||
ret = _regmap_write(map, reg, tmp);
|
|
||||||
if (change)
|
|
||||||
*change = true;
|
*change = true;
|
||||||
} else {
|
} else {
|
||||||
if (change)
|
ret = _regmap_read(map, reg, &orig);
|
||||||
*change = false;
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
tmp = orig & ~mask;
|
||||||
|
tmp |= val & mask;
|
||||||
|
|
||||||
|
if (force_write || (tmp != orig)) {
|
||||||
|
ret = _regmap_write(map, reg, tmp);
|
||||||
|
if (ret == 0 && change)
|
||||||
|
*change = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -296,6 +296,8 @@ typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg,
|
|||||||
unsigned int *val);
|
unsigned int *val);
|
||||||
typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg,
|
typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg,
|
||||||
unsigned int val);
|
unsigned int val);
|
||||||
|
typedef int (*regmap_hw_reg_update_bits)(void *context, unsigned int reg,
|
||||||
|
unsigned int mask, unsigned int val);
|
||||||
typedef struct regmap_async *(*regmap_hw_async_alloc)(void);
|
typedef struct regmap_async *(*regmap_hw_async_alloc)(void);
|
||||||
typedef void (*regmap_hw_free_context)(void *context);
|
typedef void (*regmap_hw_free_context)(void *context);
|
||||||
|
|
||||||
@ -335,6 +337,7 @@ struct regmap_bus {
|
|||||||
regmap_hw_gather_write gather_write;
|
regmap_hw_gather_write gather_write;
|
||||||
regmap_hw_async_write async_write;
|
regmap_hw_async_write async_write;
|
||||||
regmap_hw_reg_write reg_write;
|
regmap_hw_reg_write reg_write;
|
||||||
|
regmap_hw_reg_update_bits reg_update_bits;
|
||||||
regmap_hw_read read;
|
regmap_hw_read read;
|
||||||
regmap_hw_reg_read reg_read;
|
regmap_hw_reg_read reg_read;
|
||||||
regmap_hw_free_context free_context;
|
regmap_hw_free_context free_context;
|
||||||
@ -791,6 +794,9 @@ struct regmap_irq {
|
|||||||
unsigned int mask;
|
unsigned int mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define REGMAP_IRQ_REG(_irq, _off, _mask) \
|
||||||
|
[_irq] = { .reg_offset = (_off), .mask = (_mask) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description of a generic regmap irq_chip. This is not intended to
|
* Description of a generic regmap irq_chip. This is not intended to
|
||||||
* handle every possible interrupt controller, but it should handle a
|
* handle every possible interrupt controller, but it should handle a
|
||||||
@ -800,6 +806,8 @@ struct regmap_irq {
|
|||||||
*
|
*
|
||||||
* @status_base: Base status register address.
|
* @status_base: Base status register address.
|
||||||
* @mask_base: Base mask register address.
|
* @mask_base: Base mask register address.
|
||||||
|
* @unmask_base: Base unmask register address. for chips who have
|
||||||
|
* separate mask and unmask registers
|
||||||
* @ack_base: Base ack address. If zero then the chip is clear on read.
|
* @ack_base: Base ack address. If zero then the chip is clear on read.
|
||||||
* Using zero value is possible with @use_ack bit.
|
* Using zero value is possible with @use_ack bit.
|
||||||
* @wake_base: Base address for wake enables. If zero unsupported.
|
* @wake_base: Base address for wake enables. If zero unsupported.
|
||||||
@ -807,6 +815,7 @@ struct regmap_irq {
|
|||||||
* @init_ack_masked: Ack all masked interrupts once during initalization.
|
* @init_ack_masked: Ack all masked interrupts once during initalization.
|
||||||
* @mask_invert: Inverted mask register: cleared bits are masked out.
|
* @mask_invert: Inverted mask register: cleared bits are masked out.
|
||||||
* @use_ack: Use @ack register even if it is zero.
|
* @use_ack: Use @ack register even if it is zero.
|
||||||
|
* @ack_invert: Inverted ack register: cleared bits for ack.
|
||||||
* @wake_invert: Inverted wake register: cleared bits are wake enabled.
|
* @wake_invert: Inverted wake register: cleared bits are wake enabled.
|
||||||
* @runtime_pm: Hold a runtime PM lock on the device when accessing it.
|
* @runtime_pm: Hold a runtime PM lock on the device when accessing it.
|
||||||
*
|
*
|
||||||
@ -820,12 +829,14 @@ struct regmap_irq_chip {
|
|||||||
|
|
||||||
unsigned int status_base;
|
unsigned int status_base;
|
||||||
unsigned int mask_base;
|
unsigned int mask_base;
|
||||||
|
unsigned int unmask_base;
|
||||||
unsigned int ack_base;
|
unsigned int ack_base;
|
||||||
unsigned int wake_base;
|
unsigned int wake_base;
|
||||||
unsigned int irq_reg_stride;
|
unsigned int irq_reg_stride;
|
||||||
bool init_ack_masked:1;
|
bool init_ack_masked:1;
|
||||||
bool mask_invert:1;
|
bool mask_invert:1;
|
||||||
bool use_ack:1;
|
bool use_ack:1;
|
||||||
|
bool ack_invert:1;
|
||||||
bool wake_invert:1;
|
bool wake_invert:1;
|
||||||
bool runtime_pm:1;
|
bool runtime_pm:1;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user