regmap: Changes for v4.5

There's no real overall theme to the regmap changes for this release,
 it's a collection of individual features.  The main bits are:
 
  - Support for 64 bit registers, mainly for MMIO use, from Xiubo Li.
  - Support for trigger type configuration for regmap-irq from Laxman
    Dewangan.
  - Use native physical I/O for MMIO register maps to avoid confusion
    with the conversions that readl() and writel() do to little endian on
    big endian systems (with some DT updates to fix some workarounds
    people were doing), code from Simon Arlott.
  - Use a binary search rather than iteraton to improve the runtime
    performance of the rbtree code from Nikesh Oswal.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJWk9fCAAoJECTWi3JdVIfQpn4H/0nJwe03NcY829ioXEP+ICHF
 yE1Y8FH5q+NeVAo+bbQQYiTEGRQbp+E82S7OTfNKY0Z3QkPRZVtz5yMI/L2yK8I0
 HT2WJakJY6LhQfJcWgjRRfBrQvH/+2fk+3mAs0q0r+mrOoitfCb1V7uaNoVF+Hyk
 0mli7lcf2MB1akF98SwcaM3v3HhGXUUxFnclvzsMWTEp4GCt/xqwEkTm/0xn0N17
 rlxLe1RtaGMM0xfbEN3sfDX5YAPmtBi4m+CdrP6l5qgisnABPIq83RKRFNyQBcGD
 8OkA2Iu0B7eJPFPQ8sD/hELBNYyrVTwvhJQdJnle7++/Dis9EMiffMRSW8eZh+0=
 =JaJb
 -----END PGP SIGNATURE-----

Merge tag 'regmap-v4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap

Pull regmap updates from Mark Brown:
 "There's no real overall theme to the regmap changes for this release,
  it's a collection of individual features.  The main bits are:

   - Support for 64 bit registers, mainly for MMIO use, from Xiubo Li.

   - Support for trigger type configuration for regmap-irq from Laxman
     Dewangan.

   - Use native physical I/O for MMIO register maps to avoid confusion
     with the conversions that readl() and writel() do to little endian
     on big endian systems (with some DT updates to fix some workarounds
     people were doing), code from Simon Arlott.

   - Use a binary search rather than iteraton to improve the runtime
     performance of the rbtree code from Nikesh Oswal"

* tag 'regmap-v4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: debugfs: Use seq_file for the access map
  regmap: irq: add support for configuration of trigger type
  regmap: use IS_ALIGNED instead of % to improve the performance
  regmap: cache: Move the num_reg_defaults check as early as possible
  regmap: cache: Add warning info for the cache check
  regmap: missing case statement
  regmap: shift wrapping bugs in 64 bit code
  regmap: cache: Add 64-bit mode support
  regmap: cache: To suppress the noise of checkpatch
  regmap: fix the warning about unused variable
  regmap: add 64-bit mode support
  regmap: mmio: Add regmap_mmio_get_min_stride
  regmap: mmio: remove the useless code
  regmap: Fix leftover from struct reg_default to struct reg_sequence change
  regmap: replace kmalloc with kmalloc_array
  regmap: replace kzalloc with kcalloc
  regmap: rbtree: When adding a reg do a bsearch for target node
  regmap-mmio: Use native endianness for read/write
This commit is contained in:
Linus Torvalds 2016-01-11 20:14:34 -08:00
commit e795e5f4e0
18 changed files with 341 additions and 119 deletions

View File

@ -73,7 +73,6 @@ uart0: serial@10000100 {
timer: timer@10000040 {
compatible = "syscon";
reg = <0x10000040 0x2c>;
little-endian;
};
reboot {

View File

@ -98,7 +98,6 @@ upg_irq0_intc: upg_irq0_intc@406780 {
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7125-sun-top-ctrl", "syscon";
reg = <0x404000 0x60c>;
little-endian;
};
reboot {

View File

@ -118,7 +118,6 @@ upg_aon_irq0_intc: upg_aon_irq0_intc@408b80 {
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7346-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
little-endian;
};
reboot {

View File

@ -112,7 +112,6 @@ upg_aon_irq0_intc: upg_aon_irq0_intc@408b80 {
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7358-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
little-endian;
};
reboot {

View File

@ -112,7 +112,6 @@ upg_aon_irq0_intc: upg_aon_irq0_intc@408b80 {
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7360-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
little-endian;
};
reboot {

View File

@ -118,7 +118,6 @@ upg_aon_irq0_intc: upg_aon_irq0_intc@408b80 {
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7362-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
little-endian;
};
reboot {

View File

@ -99,7 +99,6 @@ upg_irq0_intc: upg_irq0_intc@406780 {
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7420-sun-top-ctrl", "syscon";
reg = <0x404000 0x60c>;
little-endian;
};
reboot {

View File

@ -100,7 +100,6 @@ upg_irq0_intc: upg_irq0_intc@406780 {
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7425-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
little-endian;
};
reboot {

View File

@ -114,7 +114,6 @@ upg_irq0_intc: upg_irq0_intc@406780 {
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7425-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
little-endian;
};
reboot {

View File

@ -21,7 +21,7 @@ static int regcache_flat_init(struct regmap *map)
int i;
unsigned int *cache;
map->cache = kzalloc(sizeof(unsigned int) * (map->max_register + 1),
map->cache = kcalloc(map->max_register + 1, sizeof(unsigned int),
GFP_KERNEL);
if (!map->cache)
return -ENOMEM;

View File

@ -139,7 +139,7 @@ static int regcache_lzo_init(struct regmap *map)
ret = 0;
blkcount = regcache_lzo_block_count(map);
map->cache = kzalloc(blkcount * sizeof *lzo_blocks,
map->cache = kcalloc(blkcount, sizeof(*lzo_blocks),
GFP_KERNEL);
if (!map->cache)
return -ENOMEM;
@ -152,8 +152,8 @@ static int regcache_lzo_init(struct regmap *map)
* that register.
*/
bmp_size = map->num_reg_defaults_raw;
sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof(long),
GFP_KERNEL);
sync_bmp = kmalloc_array(BITS_TO_LONGS(bmp_size), sizeof(long),
GFP_KERNEL);
if (!sync_bmp) {
ret = -ENOMEM;
goto err;

View File

@ -361,13 +361,14 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
rbnode->base_reg = reg;
}
rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
GFP_KERNEL);
rbnode->block = kmalloc_array(rbnode->blklen, map->cache_word_size,
GFP_KERNEL);
if (!rbnode->block)
goto err_free;
rbnode->cache_present = kzalloc(BITS_TO_LONGS(rbnode->blklen) *
sizeof(*rbnode->cache_present), GFP_KERNEL);
rbnode->cache_present = kcalloc(BITS_TO_LONGS(rbnode->blklen),
sizeof(*rbnode->cache_present),
GFP_KERNEL);
if (!rbnode->cache_present)
goto err_free_block;
@ -413,8 +414,8 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
max = reg + max_dist;
/* look for an adjacent register to the one we are about to add */
for (node = rb_first(&rbtree_ctx->root); node;
node = rb_next(node)) {
node = rbtree_ctx->root.rb_node;
while (node) {
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
node);
@ -425,6 +426,11 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
new_base_reg = min(reg, base_reg);
new_top_reg = max(reg, top_reg);
} else {
if (max < base_reg)
node = node->rb_left;
else
node = node->rb_right;
continue;
}

View File

@ -100,15 +100,25 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
int i;
void *tmp_buf;
for (i = 0; i < config->num_reg_defaults; i++)
if (config->reg_defaults[i].reg % map->reg_stride)
return -EINVAL;
if (map->cache_type == REGCACHE_NONE) {
if (config->reg_defaults || config->num_reg_defaults_raw)
dev_warn(map->dev,
"No cache used with register defaults set!\n");
map->cache_bypass = true;
return 0;
}
if (config->reg_defaults && !config->num_reg_defaults) {
dev_err(map->dev,
"Register defaults are set without the number!\n");
return -EINVAL;
}
for (i = 0; i < config->num_reg_defaults; i++)
if (config->reg_defaults[i].reg % map->reg_stride)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(cache_types); i++)
if (cache_types[i]->type == map->cache_type)
break;
@ -138,8 +148,6 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
* a copy of it.
*/
if (config->reg_defaults) {
if (!map->num_reg_defaults)
return -EINVAL;
tmp_buf = kmemdup(config->reg_defaults, map->num_reg_defaults *
sizeof(struct reg_default), GFP_KERNEL);
if (!tmp_buf)
@ -535,19 +543,30 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
switch (map->cache_word_size) {
case 1: {
u8 *cache = base;
cache[idx] = val;
break;
}
case 2: {
u16 *cache = base;
cache[idx] = val;
break;
}
case 4: {
u32 *cache = base;
cache[idx] = val;
break;
}
#ifdef CONFIG_64BIT
case 8: {
u64 *cache = base;
cache[idx] = val;
break;
}
#endif
default:
BUG();
}
@ -568,16 +587,26 @@ unsigned int regcache_get_val(struct regmap *map, const void *base,
switch (map->cache_word_size) {
case 1: {
const u8 *cache = base;
return cache[idx];
}
case 2: {
const u16 *cache = base;
return cache[idx];
}
case 4: {
const u32 *cache = base;
return cache[idx];
}
#ifdef CONFIG_64BIT
case 8: {
const u64 *cache = base;
return cache[idx];
}
#endif
default:
BUG();
}

View File

@ -397,72 +397,39 @@ static const struct file_operations regmap_reg_ranges_fops = {
.llseek = default_llseek,
};
static ssize_t regmap_access_read_file(struct file *file,
char __user *user_buf, size_t count,
loff_t *ppos)
static int regmap_access_show(struct seq_file *s, void *ignored)
{
int reg_len, tot_len;
size_t buf_pos = 0;
loff_t p = 0;
ssize_t ret;
int i;
struct regmap *map = file->private_data;
char *buf;
struct regmap *map = s->private;
int i, reg_len;
if (*ppos < 0 || !count)
return -EINVAL;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* Calculate the length of a fixed format */
reg_len = regmap_calc_reg_len(map->max_register);
tot_len = reg_len + 10; /* ': R W V P\n' */
for (i = 0; i <= map->max_register; i += map->reg_stride) {
/* Ignore registers which are neither readable nor writable */
if (!regmap_readable(map, i) && !regmap_writeable(map, i))
continue;
/* If we're in the region the user is trying to read */
if (p >= *ppos) {
/* ...but not beyond it */
if (buf_pos + tot_len + 1 >= count)
break;
/* Format the register */
snprintf(buf + buf_pos, count - buf_pos,
"%.*x: %c %c %c %c\n",
reg_len, i,
regmap_readable(map, i) ? 'y' : 'n',
regmap_writeable(map, i) ? 'y' : 'n',
regmap_volatile(map, i) ? 'y' : 'n',
regmap_precious(map, i) ? 'y' : 'n');
buf_pos += tot_len;
}
p += tot_len;
/* Format the register */
seq_printf(s, "%.*x: %c %c %c %c\n", reg_len, i,
regmap_readable(map, i) ? 'y' : 'n',
regmap_writeable(map, i) ? 'y' : 'n',
regmap_volatile(map, i) ? 'y' : 'n',
regmap_precious(map, i) ? 'y' : 'n');
}
ret = buf_pos;
return 0;
}
if (copy_to_user(user_buf, buf, buf_pos)) {
ret = -EFAULT;
goto out;
}
*ppos += buf_pos;
out:
kfree(buf);
return ret;
static int access_open(struct inode *inode, struct file *file)
{
return single_open(file, regmap_access_show, inode->i_private);
}
static const struct file_operations regmap_access_fops = {
.open = simple_open,
.read = regmap_access_read_file,
.llseek = default_llseek,
.open = access_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static ssize_t regmap_cache_only_write_file(struct file *file,

View File

@ -39,8 +39,11 @@ struct regmap_irq_chip_data {
unsigned int *mask_buf;
unsigned int *mask_buf_def;
unsigned int *wake_buf;
unsigned int *type_buf;
unsigned int *type_buf_def;
unsigned int irq_reg_stride;
unsigned int type_reg_stride;
};
static inline const
@ -144,6 +147,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
}
}
for (i = 0; i < d->chip->num_type_reg; i++) {
if (!d->type_buf_def[i])
continue;
reg = d->chip->type_base +
(i * map->reg_stride * d->type_reg_stride);
if (d->chip->type_invert)
ret = regmap_update_bits(d->map, reg,
d->type_buf_def[i], ~d->type_buf[i]);
else
ret = regmap_update_bits(d->map, reg,
d->type_buf_def[i], d->type_buf[i]);
if (ret != 0)
dev_err(d->map->dev, "Failed to sync type in %x\n",
reg);
}
if (d->chip->runtime_pm)
pm_runtime_put(map->dev);
@ -178,6 +197,38 @@ static void regmap_irq_disable(struct irq_data *data)
d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
}
static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
int reg = irq_data->type_reg_offset / map->reg_stride;
if (!(irq_data->type_rising_mask | irq_data->type_falling_mask))
return 0;
d->type_buf[reg] &= ~(irq_data->type_falling_mask |
irq_data->type_rising_mask);
switch (type) {
case IRQ_TYPE_EDGE_FALLING:
d->type_buf[reg] |= irq_data->type_falling_mask;
break;
case IRQ_TYPE_EDGE_RISING:
d->type_buf[reg] |= irq_data->type_rising_mask;
break;
case IRQ_TYPE_EDGE_BOTH:
d->type_buf[reg] |= (irq_data->type_falling_mask |
irq_data->type_rising_mask);
break;
default:
return -EINVAL;
}
return 0;
}
static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
@ -204,6 +255,7 @@ static const struct irq_chip regmap_irq_chip = {
.irq_bus_sync_unlock = regmap_irq_sync_unlock,
.irq_disable = regmap_irq_disable,
.irq_enable = regmap_irq_enable,
.irq_set_type = regmap_irq_set_type,
.irq_set_wake = regmap_irq_set_wake,
};
@ -386,28 +438,40 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
if (!d)
return -ENOMEM;
d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int),
GFP_KERNEL);
if (!d->status_buf)
goto err_alloc;
d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
d->mask_buf = kcalloc(chip->num_regs, sizeof(unsigned int),
GFP_KERNEL);
if (!d->mask_buf)
goto err_alloc;
d->mask_buf_def = kzalloc(sizeof(unsigned int) * chip->num_regs,
d->mask_buf_def = kcalloc(chip->num_regs, sizeof(unsigned int),
GFP_KERNEL);
if (!d->mask_buf_def)
goto err_alloc;
if (chip->wake_base) {
d->wake_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
d->wake_buf = kcalloc(chip->num_regs, sizeof(unsigned int),
GFP_KERNEL);
if (!d->wake_buf)
goto err_alloc;
}
if (chip->num_type_reg) {
d->type_buf_def = kcalloc(chip->num_type_reg,
sizeof(unsigned int), GFP_KERNEL);
if (!d->type_buf_def)
goto err_alloc;
d->type_buf = kcalloc(chip->num_type_reg, sizeof(unsigned int),
GFP_KERNEL);
if (!d->type_buf)
goto err_alloc;
}
d->irq_chip = regmap_irq_chip;
d->irq_chip.name = chip->name;
d->irq = irq;
@ -420,10 +484,16 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
else
d->irq_reg_stride = 1;
if (chip->type_reg_stride)
d->type_reg_stride = chip->type_reg_stride;
else
d->type_reg_stride = 1;
if (!map->use_single_read && map->reg_stride == 1 &&
d->irq_reg_stride == 1) {
d->status_reg_buf = kmalloc(map->format.val_bytes *
chip->num_regs, GFP_KERNEL);
d->status_reg_buf = kmalloc_array(chip->num_regs,
map->format.val_bytes,
GFP_KERNEL);
if (!d->status_reg_buf)
goto err_alloc;
}
@ -511,6 +581,33 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
}
}
if (chip->num_type_reg) {
for (i = 0; i < chip->num_irqs; i++) {
reg = chip->irqs[i].type_reg_offset / map->reg_stride;
d->type_buf_def[reg] |= chip->irqs[i].type_rising_mask |
chip->irqs[i].type_falling_mask;
}
for (i = 0; i < chip->num_type_reg; ++i) {
if (!d->type_buf_def[i])
continue;
reg = chip->type_base +
(i * map->reg_stride * d->type_reg_stride);
if (chip->type_invert)
ret = regmap_update_bits(map, reg,
d->type_buf_def[i], 0xFF);
else
ret = regmap_update_bits(map, reg,
d->type_buf_def[i], 0x0);
if (ret != 0) {
dev_err(map->dev,
"Failed to set type in 0x%x: %x\n",
reg, ret);
goto err_alloc;
}
}
}
if (irq_base)
d->domain = irq_domain_add_legacy(map->dev->of_node,
chip->num_irqs, irq_base, 0,
@ -541,6 +638,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
err_domain:
/* Should really dispose of the domain but... */
err_alloc:
kfree(d->type_buf);
kfree(d->type_buf_def);
kfree(d->wake_buf);
kfree(d->mask_buf_def);
kfree(d->mask_buf);
@ -564,6 +663,8 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
free_irq(irq, d);
irq_domain_remove(d->domain);
kfree(d->type_buf);
kfree(d->type_buf_def);
kfree(d->wake_buf);
kfree(d->mask_buf_def);
kfree(d->mask_buf);

View File

@ -61,6 +61,33 @@ static int regmap_mmio_regbits_check(size_t reg_bits)
}
}
static int regmap_mmio_get_min_stride(size_t val_bits)
{
int min_stride;
switch (val_bits) {
case 8:
/* The core treats 0 as 1 */
min_stride = 0;
return 0;
case 16:
min_stride = 2;
break;
case 32:
min_stride = 4;
break;
#ifdef CONFIG_64BIT
case 64:
min_stride = 8;
break;
#endif
default:
return -EINVAL;
}
return min_stride;
}
static inline void regmap_mmio_count_check(size_t count, u32 offset)
{
BUG_ON(count <= offset);
@ -106,17 +133,17 @@ static int regmap_mmio_gather_write(void *context,
while (val_size) {
switch (ctx->val_bytes) {
case 1:
writeb(*(u8 *)val, ctx->regs + offset);
__raw_writeb(*(u8 *)val, ctx->regs + offset);
break;
case 2:
writew(*(u16 *)val, ctx->regs + offset);
__raw_writew(*(u16 *)val, ctx->regs + offset);
break;
case 4:
writel(*(u32 *)val, ctx->regs + offset);
__raw_writel(*(u32 *)val, ctx->regs + offset);
break;
#ifdef CONFIG_64BIT
case 8:
writeq(*(u64 *)val, ctx->regs + offset);
__raw_writeq(*(u64 *)val, ctx->regs + offset);
break;
#endif
default:
@ -166,17 +193,17 @@ static int regmap_mmio_read(void *context,
while (val_size) {
switch (ctx->val_bytes) {
case 1:
*(u8 *)val = readb(ctx->regs + offset);
*(u8 *)val = __raw_readb(ctx->regs + offset);
break;
case 2:
*(u16 *)val = readw(ctx->regs + offset);
*(u16 *)val = __raw_readw(ctx->regs + offset);
break;
case 4:
*(u32 *)val = readl(ctx->regs + offset);
*(u32 *)val = __raw_readl(ctx->regs + offset);
break;
#ifdef CONFIG_64BIT
case 8:
*(u64 *)val = readq(ctx->regs + offset);
*(u64 *)val = __raw_readq(ctx->regs + offset);
break;
#endif
default:
@ -231,26 +258,9 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
if (config->pad_bits)
return ERR_PTR(-EINVAL);
switch (config->val_bits) {
case 8:
/* The core treats 0 as 1 */
min_stride = 0;
break;
case 16:
min_stride = 2;
break;
case 32:
min_stride = 4;
break;
#ifdef CONFIG_64BIT
case 64:
min_stride = 8;
break;
#endif
break;
default:
return ERR_PTR(-EINVAL);
}
min_stride = regmap_mmio_get_min_stride(config->val_bits);
if (min_stride < 0)
return ERR_PTR(min_stride);
if (config->reg_stride < min_stride)
return ERR_PTR(-EINVAL);

View File

@ -245,6 +245,28 @@ static void regmap_format_32_native(void *buf, unsigned int val,
*(u32 *)buf = val << shift;
}
#ifdef CONFIG_64BIT
static void regmap_format_64_be(void *buf, unsigned int val, unsigned int shift)
{
__be64 *b = buf;
b[0] = cpu_to_be64((u64)val << shift);
}
static void regmap_format_64_le(void *buf, unsigned int val, unsigned int shift)
{
__le64 *b = buf;
b[0] = cpu_to_le64((u64)val << shift);
}
static void regmap_format_64_native(void *buf, unsigned int val,
unsigned int shift)
{
*(u64 *)buf = (u64)val << shift;
}
#endif
static void regmap_parse_inplace_noop(void *buf)
{
}
@ -332,6 +354,41 @@ static unsigned int regmap_parse_32_native(const void *buf)
return *(u32 *)buf;
}
#ifdef CONFIG_64BIT
static unsigned int regmap_parse_64_be(const void *buf)
{
const __be64 *b = buf;
return be64_to_cpu(b[0]);
}
static unsigned int regmap_parse_64_le(const void *buf)
{
const __le64 *b = buf;
return le64_to_cpu(b[0]);
}
static void regmap_parse_64_be_inplace(void *buf)
{
__be64 *b = buf;
b[0] = be64_to_cpu(b[0]);
}
static void regmap_parse_64_le_inplace(void *buf)
{
__le64 *b = buf;
b[0] = le64_to_cpu(b[0]);
}
static unsigned int regmap_parse_64_native(const void *buf)
{
return *(u64 *)buf;
}
#endif
static void regmap_lock_mutex(void *__map)
{
struct regmap *map = __map;
@ -712,6 +769,21 @@ struct regmap *__regmap_init(struct device *dev,
}
break;
#ifdef CONFIG_64BIT
case 64:
switch (reg_endian) {
case REGMAP_ENDIAN_BIG:
map->format.format_reg = regmap_format_64_be;
break;
case REGMAP_ENDIAN_NATIVE:
map->format.format_reg = regmap_format_64_native;
break;
default:
goto err_map;
}
break;
#endif
default:
goto err_map;
}
@ -771,6 +843,28 @@ struct regmap *__regmap_init(struct device *dev,
goto err_map;
}
break;
#ifdef CONFIG_64BIT
case 64:
switch (val_endian) {
case REGMAP_ENDIAN_BIG:
map->format.format_val = regmap_format_64_be;
map->format.parse_val = regmap_parse_64_be;
map->format.parse_inplace = regmap_parse_64_be_inplace;
break;
case REGMAP_ENDIAN_LITTLE:
map->format.format_val = regmap_format_64_le;
map->format.parse_val = regmap_parse_64_le;
map->format.parse_inplace = regmap_parse_64_le_inplace;
break;
case REGMAP_ENDIAN_NATIVE:
map->format.format_val = regmap_format_64_native;
map->format.parse_val = regmap_parse_64_native;
break;
default:
goto err_map;
}
break;
#endif
}
if (map->format.format_write) {
@ -1513,7 +1607,7 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;
if (reg % map->reg_stride)
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
@ -1540,7 +1634,7 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;
if (reg % map->reg_stride)
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
@ -1714,7 +1808,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
if (map->bus && !map->format.parse_inplace)
return -EINVAL;
if (reg % map->reg_stride)
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
/*
@ -1983,7 +2077,7 @@ static int _regmap_multi_reg_write(struct regmap *map,
int reg = regs[i].reg;
if (!map->writeable_reg(map->dev, reg))
return -EINVAL;
if (reg % map->reg_stride)
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
}
@ -2133,7 +2227,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
if (val_len % map->format.val_bytes)
return -EINVAL;
if (reg % map->reg_stride)
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
@ -2260,7 +2354,7 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
int ret;
if (reg % map->reg_stride)
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
@ -2296,7 +2390,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
return -EINVAL;
if (val_len % map->format.val_bytes)
return -EINVAL;
if (reg % map->reg_stride)
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
if (val_count == 0)
return -EINVAL;
@ -2414,7 +2508,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
size_t val_bytes = map->format.val_bytes;
bool vol = regmap_volatile_range(map, reg, val_count);
if (reg % map->reg_stride)
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
if (map->bus && map->format.parse_inplace && (vol || map->cache_type == REGCACHE_NONE)) {
@ -2488,11 +2582,19 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
* we assume that the values are native
* endian.
*/
#ifdef CONFIG_64BIT
u64 *u64 = val;
#endif
u32 *u32 = val;
u16 *u16 = val;
u8 *u8 = val;
switch (map->format.val_bytes) {
#ifdef CONFIG_64BIT
case 8:
u64[i] = ival;
break;
#endif
case 4:
u32[i] = ival;
break;

View File

@ -788,10 +788,16 @@ int regmap_fields_update_bits(struct regmap_field *field, unsigned int id,
*
* @reg_offset: Offset of the status/mask register within the bank
* @mask: Mask used to flag/control the register.
* @type_reg_offset: Offset register for the irq type setting.
* @type_rising_mask: Mask bit to configure RISING type irq.
* @type_falling_mask: Mask bit to configure FALLING type irq.
*/
struct regmap_irq {
unsigned int reg_offset;
unsigned int mask;
unsigned int type_reg_offset;
unsigned int type_rising_mask;
unsigned int type_falling_mask;
};
#define REGMAP_IRQ_REG(_irq, _off, _mask) \
@ -811,18 +817,23 @@ struct regmap_irq {
* @ack_base: Base ack address. If zero then the chip is clear on read.
* Using zero value is possible with @use_ack bit.
* @wake_base: Base address for wake enables. If zero unsupported.
* @type_base: Base address for irq type. If zero unsupported.
* @irq_reg_stride: Stride to use for chips where registers are not contiguous.
* @init_ack_masked: Ack all masked interrupts once during initalization.
* @mask_invert: Inverted mask register: cleared bits are masked out.
* @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.
* @type_invert: Invert the type flags.
* @runtime_pm: Hold a runtime PM lock on the device when accessing it.
*
* @num_regs: Number of registers in each control bank.
* @irqs: Descriptors for individual IRQs. Interrupt numbers are
* assigned based on the index in the array of the interrupt.
* @num_irqs: Number of descriptors.
* @num_type_reg: Number of type registers.
* @type_reg_stride: Stride to use for chips where type registers are not
* contiguous.
*/
struct regmap_irq_chip {
const char *name;
@ -832,6 +843,7 @@ struct regmap_irq_chip {
unsigned int unmask_base;
unsigned int ack_base;
unsigned int wake_base;
unsigned int type_base;
unsigned int irq_reg_stride;
bool init_ack_masked:1;
bool mask_invert:1;
@ -839,11 +851,15 @@ struct regmap_irq_chip {
bool ack_invert:1;
bool wake_invert:1;
bool runtime_pm:1;
bool type_invert:1;
int num_regs;
const struct regmap_irq *irqs;
int num_irqs;
int num_type_reg;
unsigned int type_reg_stride;
};
struct regmap_irq_chip_data;
@ -1021,7 +1037,7 @@ static inline void regmap_async_complete(struct regmap *map)
}
static inline int regmap_register_patch(struct regmap *map,
const struct reg_default *regs,
const struct reg_sequence *regs,
int num_regs)
{
WARN_ONCE(1, "regmap API is disabled");