Commit 88cff241 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull regmap updates from Mark Brown:
 "Several nice new features and performance improvements here,
  especially the first:

   - Support for using the cache infrastructure without the physical
     I/O, allowing devices which don't fit the physical model regmap has
     to take advantage of the cache infrastructure, contributed by
     Andrey Smirnov.

   - Several small improvements to the support for wake capable IRQs.

   - Support for asynchronous I/O, allowing us to come much closer to
     saturating fast buses like SPI.

   - Support for simple array caches, giving higher performance for use
     with MMIO devices.

   - Restoration of the use of bulk reads for handling interrupts,
     giving a performance improvement.

   - Support for 24 bit register addresses.

   - More performance improvements for debugfs."

* tag 'regmap-3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: (24 commits)
  regmap: mmio: add register clock support
  regmap: debugfs: Factor out debugfs_tot_len calc into a function
  regmap: debugfs: Optimize seeking within blocks of registers
  regmap: debugfs: Add a `max_reg' member in struct regmap_debugfs_off_cache
  regmap: debugfs: Fix reading in register field units
  regmap: spi: Handle allocation failures gracefully
  regmap: Export regmap_async_complete()
  regmap: Export regmap_async_complete_cb
  regmap: include linux/sched.h to fix build
  regmap: spi: Support asynchronous I/O for SPI
  regmap: Add asynchronous I/O support
  regmap: Add "no-bus" option for regmap API
  regmap: regmap: avoid spurious warning in regmap_read_debugfs
  regmap: Add provisions to have user-defined write operation
  regmap: Add provisions to have user-defined read operation
  regmap: Add support for 24 bit wide register addresses
  mfd: wm5110: Mark wakes as inverted
  mfd: wm5102: Mark wakes as inverted
  regmap: irq: Support wake IRQ mask inversion
  regmap: irq: Fix sync of wake statuses to hardware
  ...
parents 9ae46e67 a2b37efc
obj-$(CONFIG_REGMAP) += regmap.o regcache.o obj-$(CONFIG_REGMAP) += regmap.o regcache.o
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/wait.h>
struct regmap; struct regmap;
struct regcache_ops; struct regcache_ops;
...@@ -25,6 +26,7 @@ struct regmap_debugfs_off_cache { ...@@ -25,6 +26,7 @@ struct regmap_debugfs_off_cache {
off_t min; off_t min;
off_t max; off_t max;
unsigned int base_reg; unsigned int base_reg;
unsigned int max_reg;
}; };
struct regmap_format { struct regmap_format {
...@@ -39,6 +41,13 @@ struct regmap_format { ...@@ -39,6 +41,13 @@ struct regmap_format {
unsigned int (*parse_val)(void *buf); unsigned int (*parse_val)(void *buf);
}; };
struct regmap_async {
struct list_head list;
struct work_struct cleanup;
struct regmap *map;
void *work_buf;
};
struct regmap { struct regmap {
struct mutex mutex; struct mutex mutex;
spinlock_t spinlock; spinlock_t spinlock;
...@@ -53,6 +62,11 @@ struct regmap { ...@@ -53,6 +62,11 @@ struct regmap {
void *bus_context; void *bus_context;
const char *name; const char *name;
spinlock_t async_lock;
wait_queue_head_t async_waitq;
struct list_head async_list;
int async_ret;
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
struct dentry *debugfs; struct dentry *debugfs;
const char *debugfs_name; const char *debugfs_name;
...@@ -74,6 +88,11 @@ struct regmap { ...@@ -74,6 +88,11 @@ struct regmap {
const struct regmap_access_table *volatile_table; const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table; const struct regmap_access_table *precious_table;
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg, unsigned int val);
bool defer_caching;
u8 read_flag_mask; u8 read_flag_mask;
u8 write_flag_mask; u8 write_flag_mask;
...@@ -175,7 +194,10 @@ bool regcache_set_val(void *base, unsigned int idx, ...@@ -175,7 +194,10 @@ bool regcache_set_val(void *base, unsigned int idx,
unsigned int val, unsigned int word_size); unsigned int val, unsigned int word_size);
int regcache_lookup_reg(struct regmap *map, unsigned int reg); int regcache_lookup_reg(struct regmap *map, unsigned int reg);
void regmap_async_complete_cb(struct regmap_async *async, int ret);
extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_rbtree_ops;
extern struct regcache_ops regcache_lzo_ops; extern struct regcache_ops regcache_lzo_ops;
extern struct regcache_ops regcache_flat_ops;
#endif #endif
/*
* Register cache access API - flat caching support
*
* Copyright 2012 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/seq_file.h>
#include "internal.h"
static int regcache_flat_init(struct regmap *map)
{
int i;
unsigned int *cache;
map->cache = kzalloc(sizeof(unsigned int) * (map->max_register + 1),
GFP_KERNEL);
if (!map->cache)
return -ENOMEM;
cache = map->cache;
for (i = 0; i < map->num_reg_defaults; i++)
cache[map->reg_defaults[i].reg] = map->reg_defaults[i].def;
return 0;
}
static int regcache_flat_exit(struct regmap *map)
{
kfree(map->cache);
map->cache = NULL;
return 0;
}
static int regcache_flat_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
unsigned int *cache = map->cache;
*value = cache[reg];
return 0;
}
static int regcache_flat_write(struct regmap *map, unsigned int reg,
unsigned int value)
{
unsigned int *cache = map->cache;
cache[reg] = value;
return 0;
}
struct regcache_ops regcache_flat_ops = {
.type = REGCACHE_FLAT,
.name = "flat",
.init = regcache_flat_init,
.exit = regcache_flat_exit,
.read = regcache_flat_read,
.write = regcache_flat_write,
};
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
static const struct regcache_ops *cache_types[] = { static const struct regcache_ops *cache_types[] = {
&regcache_rbtree_ops, &regcache_rbtree_ops,
&regcache_lzo_ops, &regcache_lzo_ops,
&regcache_flat_ops,
}; };
static int regcache_hw_init(struct regmap *map) static int regcache_hw_init(struct regmap *map)
......
...@@ -81,6 +81,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, ...@@ -81,6 +81,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
struct regmap_debugfs_off_cache *c = NULL; struct regmap_debugfs_off_cache *c = NULL;
loff_t p = 0; loff_t p = 0;
unsigned int i, ret; unsigned int i, ret;
unsigned int fpos_offset;
unsigned int reg_offset;
/* /*
* If we don't have a cache build one so we don't have to do a * If we don't have a cache build one so we don't have to do a
...@@ -93,6 +95,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, ...@@ -93,6 +95,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
regmap_precious(map, i)) { regmap_precious(map, i)) {
if (c) { if (c) {
c->max = p - 1; c->max = p - 1;
fpos_offset = c->max - c->min;
reg_offset = fpos_offset / map->debugfs_tot_len;
c->max_reg = c->base_reg + reg_offset;
list_add_tail(&c->list, list_add_tail(&c->list,
&map->debugfs_off_cache); &map->debugfs_off_cache);
c = NULL; c = NULL;
...@@ -119,6 +124,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, ...@@ -119,6 +124,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
/* Close the last entry off if we didn't scan beyond it */ /* Close the last entry off if we didn't scan beyond it */
if (c) { if (c) {
c->max = p - 1; c->max = p - 1;
fpos_offset = c->max - c->min;
reg_offset = fpos_offset / map->debugfs_tot_len;
c->max_reg = c->base_reg + reg_offset;
list_add_tail(&c->list, list_add_tail(&c->list,
&map->debugfs_off_cache); &map->debugfs_off_cache);
} }
...@@ -128,25 +136,38 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, ...@@ -128,25 +136,38 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
* allocate and we should never be in this code if there are * allocate and we should never be in this code if there are
* no registers at all. * no registers at all.
*/ */
if (list_empty(&map->debugfs_off_cache)) { WARN_ON(list_empty(&map->debugfs_off_cache));
WARN_ON(list_empty(&map->debugfs_off_cache)); ret = base;
return base;
}
/* Find the relevant block */ /* Find the relevant block:offset */
list_for_each_entry(c, &map->debugfs_off_cache, list) { list_for_each_entry(c, &map->debugfs_off_cache, list) {
if (from >= c->min && from <= c->max) { if (from >= c->min && from <= c->max) {
*pos = c->min; fpos_offset = from - c->min;
return c->base_reg; reg_offset = fpos_offset / map->debugfs_tot_len;
*pos = c->min + (reg_offset * map->debugfs_tot_len);
return c->base_reg + reg_offset;
} }
*pos = c->min; *pos = c->max;
ret = c->base_reg; ret = c->max_reg;
} }
return ret; return ret;
} }
static inline void regmap_calc_tot_len(struct regmap *map,
void *buf, size_t count)
{
/* Calculate the length of a fixed format */
if (!map->debugfs_tot_len) {
map->debugfs_reg_len = regmap_calc_reg_len(map->max_register,
buf, count);
map->debugfs_val_len = 2 * map->format.val_bytes;
map->debugfs_tot_len = map->debugfs_reg_len +
map->debugfs_val_len + 3; /* : \n */
}
}
static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
unsigned int to, char __user *user_buf, unsigned int to, char __user *user_buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
...@@ -165,14 +186,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, ...@@ -165,14 +186,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
/* Calculate the length of a fixed format */ regmap_calc_tot_len(map, buf, count);
if (!map->debugfs_tot_len) {
map->debugfs_reg_len = regmap_calc_reg_len(map->max_register,
buf, count);
map->debugfs_val_len = 2 * map->format.val_bytes;
map->debugfs_tot_len = map->debugfs_reg_len +
map->debugfs_val_len + 3; /* : \n */
}
/* Work out which register we're starting at */ /* Work out which register we're starting at */
start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p); start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p);
...@@ -187,7 +201,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, ...@@ -187,7 +201,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
/* If we're in the region the user is trying to read */ /* If we're in the region the user is trying to read */
if (p >= *ppos) { if (p >= *ppos) {
/* ...but not beyond it */ /* ...but not beyond it */
if (buf_pos + 1 + map->debugfs_tot_len >= count) if (buf_pos + map->debugfs_tot_len > count)
break; break;
/* Format the register */ /* Format the register */
......
...@@ -34,6 +34,7 @@ struct regmap_irq_chip_data { ...@@ -34,6 +34,7 @@ struct regmap_irq_chip_data {
int irq; int irq;
int wake_count; int wake_count;
void *status_reg_buf;
unsigned int *status_buf; unsigned int *status_buf;
unsigned int *mask_buf; unsigned int *mask_buf;
unsigned int *mask_buf_def; unsigned int *mask_buf_def;
...@@ -87,6 +88,23 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -87,6 +88,23 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
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);
reg = d->chip->wake_base +
(i * map->reg_stride * d->irq_reg_stride);
if (d->wake_buf) {
if (d->chip->wake_invert)
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i],
~d->wake_buf[i]);
else
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i],
d->wake_buf[i]);
if (ret != 0)
dev_err(d->map->dev,
"Failed to sync wakes in %x: %d\n",
reg, ret);
}
} }
if (d->chip->runtime_pm) if (d->chip->runtime_pm)
...@@ -129,16 +147,15 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) ...@@ -129,16 +147,15 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
struct regmap *map = d->map; struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
if (!d->chip->wake_base)
return -EINVAL;
if (on) { if (on) {
d->wake_buf[irq_data->reg_offset / map->reg_stride] if (d->wake_buf)
&= ~irq_data->mask; d->wake_buf[irq_data->reg_offset / map->reg_stride]
&= ~irq_data->mask;
d->wake_count++; d->wake_count++;
} else { } else {
d->wake_buf[irq_data->reg_offset / map->reg_stride] if (d->wake_buf)
|= irq_data->mask; d->wake_buf[irq_data->reg_offset / map->reg_stride]
|= irq_data->mask;
d->wake_count--; d->wake_count--;
} }
...@@ -172,25 +189,69 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) ...@@ -172,25 +189,69 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
} }
/* /*
* Ignore masked IRQs and ack if we need to; we ack early so * Read in the statuses, using a single bulk read if possible
* there is no race between handling and acknowleding the * in order to reduce the I/O overheads.
* interrupt. We assume that typically few of the interrupts
* will fire simultaneously so don't worry about overhead from
* doing a write per register.
*/ */
for (i = 0; i < data->chip->num_regs; i++) { if (!map->use_single_rw && map->reg_stride == 1 &&
ret = regmap_read(map, chip->status_base + (i * map->reg_stride data->irq_reg_stride == 1) {
* data->irq_reg_stride), u8 *buf8 = data->status_reg_buf;
&data->status_buf[i]); u16 *buf16 = data->status_reg_buf;
u32 *buf32 = data->status_reg_buf;
BUG_ON(!data->status_reg_buf);
ret = regmap_bulk_read(map, chip->status_base,
data->status_reg_buf,
chip->num_regs);
if (ret != 0) { if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n", dev_err(map->dev, "Failed to read IRQ status: %d\n",
ret); ret);
if (chip->runtime_pm)
pm_runtime_put(map->dev);
return IRQ_NONE; return IRQ_NONE;
} }
for (i = 0; i < data->chip->num_regs; i++) {
switch (map->format.val_bytes) {
case 1:
data->status_buf[i] = buf8[i];
break;
case 2:
data->status_buf[i] = buf16[i];
break;
case 4:
data->status_buf[i] = buf32[i];
break;
default:
BUG();
return IRQ_NONE;
}
}
} else {
for (i = 0; i < data->chip->num_regs; i++) {
ret = regmap_read(map, chip->status_base +
(i * map->reg_stride
* data->irq_reg_stride),
&data->status_buf[i]);
if (ret != 0) {
dev_err(map->dev,
"Failed to read IRQ status: %d\n",
ret);
if (chip->runtime_pm)
pm_runtime_put(map->dev);
return IRQ_NONE;
}
}
}
/*
* Ignore masked IRQs and ack if we need to; we ack early so
* there is no race between handling and acknowleding the
* interrupt. We assume that typically few of the interrupts
* will fire simultaneously so don't worry about overhead from
* doing a write per register.
*/
for (i = 0; i < data->chip->num_regs; i++) {
data->status_buf[i] &= ~data->mask_buf[i]; data->status_buf[i] &= ~data->mask_buf[i];
if (data->status_buf[i] && chip->ack_base) { if (data->status_buf[i] && chip->ack_base) {
...@@ -316,11 +377,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -316,11 +377,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
d->irq_chip = regmap_irq_chip; d->irq_chip = regmap_irq_chip;
d->irq_chip.name = chip->name; d->irq_chip.name = chip->name;
if (!chip->wake_base) {
d->irq_chip.irq_set_wake = NULL;
d->irq_chip.flags |= IRQCHIP_MASK_ON_SUSPEND |
IRQCHIP_SKIP_SET_WAKE;
}
d->irq = irq; d->irq = irq;
d->map = map; d->map = map;
d->chip = chip; d->chip = chip;
...@@ -331,6 +387,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -331,6 +387,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
else else
d->irq_reg_stride = 1; d->irq_reg_stride = 1;
if (!map->use_single_rw && map->reg_stride == 1 &&
d->irq_reg_stride == 1) {
d->status_reg_buf = kmalloc(map->format.val_bytes *
chip->num_regs, GFP_KERNEL);
if (!d->status_reg_buf)
goto err_alloc;
}
mutex_init(&d->lock); mutex_init(&d->lock);
for (i = 0; i < chip->num_irqs; i++) for (i = 0; i < chip->num_irqs; i++)
...@@ -361,8 +425,15 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -361,8 +425,15 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
d->wake_buf[i] = d->mask_buf_def[i]; d->wake_buf[i] = d->mask_buf_def[i];
reg = chip->wake_base + reg = chip->wake_base +
(i * map->reg_stride * d->irq_reg_stride); (i * map->reg_stride * d->irq_reg_stride);
ret = regmap_update_bits(map, reg, d->wake_buf[i],
d->wake_buf[i]); if (chip->wake_invert)
ret = regmap_update_bits(map, reg,
d->mask_buf_def[i],
0);
else
ret = regmap_update_bits(map, reg,
d->mask_buf_def[i],
d->wake_buf[i]);
if (ret != 0) { if (ret != 0) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
reg, ret); reg, ret);
...@@ -401,6 +472,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -401,6 +472,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
kfree(d->mask_buf_def); kfree(d->mask_buf_def);
kfree(d->mask_buf); kfree(d->mask_buf);
kfree(d->status_buf); kfree(d->status_buf);
kfree(d->status_reg_buf);
kfree(d); kfree(d);
return ret; return ret;
} }
...@@ -422,6 +494,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) ...@@ -422,6 +494,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
kfree(d->wake_buf); kfree(d->wake_buf);
kfree(d->mask_buf_def); kfree(d->mask_buf_def);
kfree(d->mask_buf); kfree(d->mask_buf);
kfree(d->status_reg_buf);
kfree(d->status_buf); kfree(d->status_buf);
kfree(d); kfree(d);
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
...@@ -26,6 +27,7 @@ ...@@ -26,6 +27,7 @@
struct regmap_mmio_context { struct regmap_mmio_context {
void __iomem *regs; void __iomem *regs;
unsigned val_bytes; unsigned val_bytes;
struct clk *clk;
}; };
static int regmap_mmio_gather_write(void *context, static int regmap_mmio_gather_write(void *context,
...@@ -34,9 +36,16 @@ static int regmap_mmio_gather_write(void *context, ...@@ -34,9 +36,16 @@ static int regmap_mmio_gather_write(void *context,
{ {
struct regmap_mmio_context *ctx = context; struct regmap_mmio_context *ctx = context;
u32 offset; u32 offset;
int ret;
BUG_ON(reg_size != 4); BUG_ON(reg_size != 4);
if (ctx->clk) {
ret = clk_enable(ctx->clk);
if (ret < 0)
return ret;
}
offset = *(u32 *)reg; offset = *(u32 *)reg;
while (val_size) { while (val_size) {
...@@ -64,6 +73,9 @@ static int regmap_mmio_gather_write(void *context, ...@@ -64,6 +73,9 @@ static int regmap_mmio_gather_write(void *context,
offset += ctx->val_bytes; offset += ctx->val_bytes;
} }
if (ctx->clk)
clk_disable(ctx->clk);
return 0; return 0;
} }
...@@ -80,9 +92,16 @@ static int regmap_mmio_read(void *context, ...@@ -80,9 +92,16 @@ static int regmap_mmio_read(void *context,
{ {
struct regmap_mmio_context *ctx = context; struct regmap_mmio_context *ctx = context;
u32 offset; u32 offset;
int ret;
BUG_ON(reg_size != 4); BUG_ON(reg_size != 4);
if (ctx->clk) {
ret = clk_enable(ctx->clk);
if (ret < 0)
return ret;
}
offset = *(u32 *)reg; offset = *(u32 *)reg;
while (val_size) { while (val_size) {
...@@ -110,11 +129,20 @@ static int regmap_mmio_read(void *context, ...@@ -110,11 +129,20 @@ static int regmap_mmio_read(void *context,
offset += ctx->val_bytes; offset += ctx->val_bytes;
} }
if (ctx->clk)
clk_disable(ctx->clk);
return 0; return 0;
} }
static void regmap_mmio_free_context(void *context) static void regmap_mmio_free_context(void *context)
{ {
struct regmap_mmio_context *ctx = context;
if (ctx->clk) {
clk_unprepare(ctx->clk);
clk_put(ctx->clk);
}
kfree(context); kfree(context);
} }
...@@ -128,11 +156,14 @@ static struct regmap_bus regmap_mmio = { ...@@ -128,11 +156,14 @@ static struct regmap_bus regmap_mmio = {
.val_format_endian_default = REGMAP_ENDIAN_NATIVE, .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
}; };
static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
const char *clk_id,
void __iomem *regs,
const struct regmap_config *config) const struct regmap_config *config)
{ {
struct regmap_mmio_context *ctx; struct regmap_mmio_context *ctx;
int min_stride; int min_stride;
int ret;
if (config->reg_bits != 32) if (config->reg_bits != 32)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
...@@ -179,37 +210,59 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, ...@@ -179,37 +210,59 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
ctx->regs = regs; ctx->regs = regs;
ctx->val_bytes = config->val_bits / 8; ctx->val_bytes = config->val_bits / 8;
if (clk_id == NULL)
return ctx;
ctx->clk = clk_get(dev, clk_id);
if (IS_ERR(ctx->clk)) {
ret = PTR_ERR(ctx->clk);
goto err_free;
}
ret = clk_prepare(ctx->clk);
if (ret < 0) {
clk_put(ctx->clk);
goto err_free;
}
return ctx; return ctx;
err_free:
kfree(ctx);
return ERR_PTR(ret);
} }
/** /**
* regmap_init_mmio(): Initialise register map * regmap_init_mmio_clk(): Initialise register map with register clock
* *
* @dev: Device that will be interacted with * @dev: Device that will be interacted with
* @clk_id: register clock consumer ID
* @regs: Pointer to memory-mapped IO region * @regs: Pointer to memory-mapped IO region
* @config: Configuration for register map * @config: Configuration for register map
* *
* The return value will be an ERR_PTR() on error or a valid pointer to * The return value will be an ERR_PTR() on error or a valid pointer to
* a struct regmap. * a struct regmap.
*/ */
struct regmap *regmap_init_mmio(struct device *dev, struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs, void __iomem *regs,
const struct regmap_config *config) const struct regmap_config *config)
{ {
struct regmap_mmio_context *ctx; struct regmap_mmio_context *ctx;
ctx = regmap_mmio_gen_context(regs, config); ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
if (IS_ERR(ctx)) if (IS_ERR(ctx))
return ERR_CAST(ctx); return ERR_CAST(ctx);
return regmap_init(dev, &regmap_mmio, ctx, config); return regmap_init(dev, &regmap_mmio, ctx, config);
} }
EXPORT_SYMBOL_GPL(regmap_init_mmio); EXPORT_SYMBOL_GPL(regmap_init_mmio_clk);
/** /**
* devm_regmap_init_mmio(): Initialise managed register map * devm_regmap_init_mmio_clk(): Initialise managed register map with clock
* *
* @dev: Device that will be interacted with * @dev: Device that will be interacted with
* @clk_id: register clock consumer ID
* @regs: Pointer to memory-mapped IO region * @regs: Pointer to memory-mapped IO region
* @config: Configuration for register map * @config: Configuration for register map
* *
...@@ -217,18 +270,18 @@ EXPORT_SYMBOL_GPL(regmap_init_mmio); ...@@ -217,18 +270,18 @@ EXPORT_SYMBOL_GPL(regmap_init_mmio);
* to a struct regmap. The regmap will be automatically freed by the * to a struct regmap. The regmap will be automatically freed by the
* device management code. * device management code.
*/ */
struct regmap *devm_regmap_init_mmio(struct device *dev, struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs, void __iomem *regs,
const struct regmap_config *config) const struct regmap_config *config)
{ {
struct regmap_mmio_context *ctx; struct regmap_mmio_context *ctx;
ctx = regmap_mmio_gen_context(regs, config); ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
if (IS_ERR(ctx)) if (IS_ERR(ctx))
return ERR_CAST(ctx); return ERR_CAST(ctx);
return devm_regmap_init(dev, &regmap_mmio, ctx, config); return devm_regmap_init(dev, &regmap_mmio, ctx, config);
} }
EXPORT_SYMBOL_GPL(devm_regmap_init_mmio); EXPORT_SYMBOL_GPL(devm_regmap_init_mmio_clk);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -15,6 +15,21 @@ ...@@ -15,6 +15,21 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include "internal.h"
struct regmap_async_spi {
struct regmap_async core;
struct spi_message m;
struct spi_transfer t[2];
};
static void regmap_spi_complete(void *data)
{
struct regmap_async_spi *async = data;
regmap_async_complete_cb(&async->core, async->m.status);
}
static int regmap_spi_write(void *context, const void *data, size_t count) static int regmap_spi_write(void *context, const void *data, size_t count)
{ {
struct device *dev = context; struct device *dev = context;
...@@ -40,6 +55,43 @@ static int regmap_spi_gather_write(void *context, ...@@ -40,6 +55,43 @@ static int regmap_spi_gather_write(void *context,
return spi_sync(spi, &m); return spi_sync(spi, &m);
} }
static int regmap_spi_async_write(void *context,
const void *reg, size_t reg_len,
const void *val, size_t val_len,
struct regmap_async *a)
{
struct regmap_async_spi *async = container_of(a,
struct regmap_async_spi,
core);
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
async->t[0].tx_buf = reg;
async->t[0].len = reg_len;
async->t[1].tx_buf = val;
async->t[1].len = val_len;
spi_message_init(&async->m);
spi_message_add_tail(&async->t[0], &async->m);
spi_message_add_tail(&async->t[1], &async->m);
async->m.complete = regmap_spi_complete;
async->m.context = async;
return spi_async(spi, &async->m);
}
static struct regmap_async *regmap_spi_async_alloc(void)
{
struct regmap_async_spi *async_spi;
async_spi = kzalloc(sizeof(*async_spi), GFP_KERNEL);
if (!async_spi)
return NULL;
return &async_spi->core;
}
static int regmap_spi_read(void *context, static int regmap_spi_read(void *context,
const void *reg, size_t reg_size, const void *reg, size_t reg_size,
void *val, size_t val_size) void *val, size_t val_size)
...@@ -53,6 +105,8 @@ static int regmap_spi_read(void *context, ...@@ -53,6 +105,8 @@ static int regmap_spi_read(void *context,
static struct regmap_bus regmap_spi = { static struct regmap_bus regmap_spi = {
.write = regmap_spi_write, .write = regmap_spi_write,
.gather_write = regmap_spi_gather_write, .gather_write = regmap_spi_gather_write,
.async_write = regmap_spi_async_write,
.async_alloc = regmap_spi_async_alloc,
.read = regmap_spi_read, .read = regmap_spi_read,
.read_flag_mask = 0x80, .read_flag_mask = 0x80,
}; };
......
This diff is collapsed.
...@@ -96,6 +96,7 @@ const struct regmap_irq_chip wm5102_aod = { ...@@ -96,6 +96,7 @@ const struct regmap_irq_chip wm5102_aod = {
.mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1, .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
.ack_base = ARIZONA_AOD_IRQ1, .ack_base = ARIZONA_AOD_IRQ1,
.wake_base = ARIZONA_WAKE_CONTROL, .wake_base = ARIZONA_WAKE_CONTROL,
.wake_invert = 1,
.num_regs = 1, .num_regs = 1,
.irqs = wm5102_aod_irqs, .irqs = wm5102_aod_irqs,
.num_irqs = ARRAY_SIZE(wm5102_aod_irqs), .num_irqs = ARRAY_SIZE(wm5102_aod_irqs),
......
...@@ -255,6 +255,7 @@ const struct regmap_irq_chip wm5110_aod = { ...@@ -255,6 +255,7 @@ const struct regmap_irq_chip wm5110_aod = {
.mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1, .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
.ack_base = ARIZONA_AOD_IRQ1, .ack_base = ARIZONA_AOD_IRQ1,
.wake_base = ARIZONA_WAKE_CONTROL, .wake_base = ARIZONA_WAKE_CONTROL,
.wake_invert = 1,
.num_regs = 1, .num_regs = 1,
.irqs = wm5110_aod_irqs, .irqs = wm5110_aod_irqs,
.num_irqs = ARRAY_SIZE(wm5110_aod_irqs), .num_irqs = ARRAY_SIZE(wm5110_aod_irqs),
......
...@@ -28,7 +28,8 @@ struct regmap_range_cfg; ...@@ -28,7 +28,8 @@ struct regmap_range_cfg;
enum regcache_type { enum regcache_type {
REGCACHE_NONE, REGCACHE_NONE,
REGCACHE_RBTREE, REGCACHE_RBTREE,
REGCACHE_COMPRESSED REGCACHE_COMPRESSED,
REGCACHE_FLAT,
}; };
/** /**
...@@ -127,7 +128,18 @@ typedef void (*regmap_unlock)(void *); ...@@ -127,7 +128,18 @@ typedef void (*regmap_unlock)(void *);
* @lock_arg: this field is passed as the only argument of lock/unlock * @lock_arg: this field is passed as the only argument of lock/unlock
* functions (ignored in case regular lock/unlock functions * functions (ignored in case regular lock/unlock functions
* are not overridden). * are not overridden).
* * @reg_read: Optional callback that if filled will be used to perform
* all the reads from the registers. Should only be provided for
* devices whos read operation cannot be represented as a simple read
* operation on a bus such as SPI, I2C, etc. Most of the devices do
* not need this.
* @reg_write: Same as above for writing.
* @fast_io: Register IO is fast. Use a spinlock instead of a mutex
* to perform locking. This field is ignored if custom lock/unlock
* functions are used (see fields lock/unlock of struct regmap_config).
* This field is a duplicate of a similar file in
* 'struct regmap_bus' and serves exact same purpose.
* Use it only for "no-bus" cases.
* @max_register: Optional, specifies the maximum valid register index. * @max_register: Optional, specifies the maximum valid register index.
* @wr_table: Optional, points to a struct regmap_access_table specifying * @wr_table: Optional, points to a struct regmap_access_table specifying
* valid ranges for write access. * valid ranges for write access.
...@@ -177,6 +189,11 @@ struct regmap_config { ...@@ -177,6 +189,11 @@ struct regmap_config {
regmap_unlock unlock; regmap_unlock unlock;
void *lock_arg; void *lock_arg;
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg, unsigned int val);
bool fast_io;
unsigned int max_register; unsigned int max_register;
const struct regmap_access_table *wr_table; const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table; const struct regmap_access_table *rd_table;
...@@ -235,14 +252,21 @@ struct regmap_range_cfg { ...@@ -235,14 +252,21 @@ struct regmap_range_cfg {
unsigned int window_len; unsigned int window_len;
}; };
struct regmap_async;
typedef int (*regmap_hw_write)(void *context, const void *data, typedef int (*regmap_hw_write)(void *context, const void *data,
size_t count); size_t count);
typedef int (*regmap_hw_gather_write)(void *context, typedef int (*regmap_hw_gather_write)(void *context,
const void *reg, size_t reg_len, const void *reg, size_t reg_len,
const void *val, size_t val_len); const void *val, size_t val_len);
typedef int (*regmap_hw_async_write)(void *context,
const void *reg, size_t reg_len,
const void *val, size_t val_len,
struct regmap_async *async);
typedef int (*regmap_hw_read)(void *context, typedef int (*regmap_hw_read)(void *context,
const void *reg_buf, size_t reg_size, const void *reg_buf, size_t reg_size,
void *val_buf, size_t val_size); void *val_buf, size_t val_size);
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);
/** /**
...@@ -255,8 +279,11 @@ typedef void (*regmap_hw_free_context)(void *context); ...@@ -255,8 +279,11 @@ typedef void (*regmap_hw_free_context)(void *context);
* @write: Write operation. * @write: Write operation.
* @gather_write: Write operation with split register/value, return -ENOTSUPP * @gather_write: Write operation with split register/value, return -ENOTSUPP
* if not implemented on a given device. * if not implemented on a given device.
* @async_write: Write operation which completes asynchronously, optional and
* must serialise with respect to non-async I/O.
* @read: Read operation. Data is returned in the buffer used to transmit * @read: Read operation. Data is returned in the buffer used to transmit
* data. * data.
* @async_alloc: Allocate a regmap_async() structure.
* @read_flag_mask: Mask to be set in the top byte of the register when doing * @read_flag_mask: Mask to be set in the top byte of the register when doing
* a read. * a read.
* @reg_format_endian_default: Default endianness for formatted register * @reg_format_endian_default: Default endianness for formatted register
...@@ -265,13 +292,16 @@ typedef void (*regmap_hw_free_context)(void *context); ...@@ -265,13 +292,16 @@ typedef void (*regmap_hw_free_context)(void *context);
* @val_format_endian_default: Default endianness for formatted register * @val_format_endian_default: Default endianness for formatted register
* values. Used when the regmap_config specifies DEFAULT. If this is * values. Used when the regmap_config specifies DEFAULT. If this is
* DEFAULT, BIG is assumed. * DEFAULT, BIG is assumed.
* @async_size: Size of struct used for async work.
*/ */
struct regmap_bus { struct regmap_bus {
bool fast_io; bool fast_io;
regmap_hw_write write; regmap_hw_write write;
regmap_hw_gather_write gather_write; regmap_hw_gather_write gather_write;
regmap_hw_async_write async_write;
regmap_hw_read read; regmap_hw_read read;
regmap_hw_free_context free_context; regmap_hw_free_context free_context;
regmap_hw_async_alloc async_alloc;
u8 read_flag_mask; u8 read_flag_mask;
enum regmap_endian reg_format_endian_default; enum regmap_endian reg_format_endian_default;
enum regmap_endian val_format_endian_default; enum regmap_endian val_format_endian_default;
...@@ -285,9 +315,9 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, ...@@ -285,9 +315,9 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config); const struct regmap_config *config);
struct regmap *regmap_init_spi(struct spi_device *dev, struct regmap *regmap_init_spi(struct spi_device *dev,
const struct regmap_config *config); const struct regmap_config *config);
struct regmap *regmap_init_mmio(struct device *dev, struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs, void __iomem *regs,
const struct regmap_config *config); const struct regmap_config *config);
struct regmap *devm_regmap_init(struct device *dev, struct regmap *devm_regmap_init(struct device *dev,
const struct regmap_bus *bus, const struct regmap_bus *bus,
...@@ -297,9 +327,44 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, ...@@ -297,9 +327,44 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config); const struct regmap_config *config);
struct regmap *devm_regmap_init_spi(struct spi_device *dev, struct regmap *devm_regmap_init_spi(struct spi_device *dev,
const struct regmap_config *config); const struct regmap_config *config);
struct regmap *devm_regmap_init_mmio(struct device *dev, struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs, void __iomem *regs,
const struct regmap_config *config); const struct regmap_config *config);
/**
* regmap_init_mmio(): Initialise register map
*
* @dev: Device that will be interacted with
* @regs: Pointer to memory-mapped IO region
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer to
* a struct regmap.
*/
static inline struct regmap *regmap_init_mmio(struct device *dev,
void __iomem *regs,
const struct regmap_config *config)
{
return regmap_init_mmio_clk(dev, NULL, regs, config);
}
/**
* devm_regmap_init_mmio(): Initialise managed register map
*
* @dev: Device that will be interacted with
* @regs: Pointer to memory-mapped IO region
* @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.
*/
static inline struct regmap *devm_regmap_init_mmio(struct device *dev,
void __iomem *regs,
const struct regmap_config *config)
{
return devm_regmap_init_mmio_clk(dev, NULL, regs, config);
}
void regmap_exit(struct regmap *map); void regmap_exit(struct regmap *map);
int regmap_reinit_cache(struct regmap *map, int regmap_reinit_cache(struct regmap *map,
...@@ -310,6 +375,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, ...@@ -310,6 +375,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len); const void *val, size_t val_len);
int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
size_t val_count); size_t val_count);
int regmap_raw_write_async(struct regmap *map, unsigned int reg,
const void *val, size_t val_len);
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val); int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
int regmap_raw_read(struct regmap *map, unsigned int reg, int regmap_raw_read(struct regmap *map, unsigned int reg,
void *val, size_t val_len); void *val, size_t val_len);
...@@ -321,6 +388,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, ...@@ -321,6 +388,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val, unsigned int mask, unsigned int val,
bool *change); bool *change);
int regmap_get_val_bytes(struct regmap *map); int regmap_get_val_bytes(struct regmap *map);
int regmap_async_complete(struct regmap *map);
int regcache_sync(struct regmap *map); int regcache_sync(struct regmap *map);
int regcache_sync_region(struct regmap *map, unsigned int min, int regcache_sync_region(struct regmap *map, unsigned int min,
...@@ -381,6 +449,7 @@ struct regmap_irq_chip { ...@@ -381,6 +449,7 @@ struct regmap_irq_chip {
unsigned int wake_base; unsigned int wake_base;
unsigned int irq_reg_stride; unsigned int irq_reg_stride;
unsigned int mask_invert; unsigned int mask_invert;
unsigned int wake_invert;
bool runtime_pm; bool runtime_pm;
int num_regs; int num_regs;
...@@ -422,6 +491,13 @@ static inline int regmap_raw_write(struct regmap *map, unsigned int reg, ...@@ -422,6 +491,13 @@ static inline int regmap_raw_write(struct regmap *map, unsigned int reg,
return -EINVAL; return -EINVAL;
} }
static inline int regmap_raw_write_async(struct regmap *map, unsigned int reg,
const void *val, size_t val_len)
{
WARN_ONCE(1, "regmap API is disabled");
return -EINVAL;
}
static inline int regmap_bulk_write(struct regmap *map, unsigned int reg, static inline int regmap_bulk_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_count) const void *val, size_t val_count)
{ {
...@@ -500,6 +576,11 @@ static inline void regcache_mark_dirty(struct regmap *map) ...@@ -500,6 +576,11 @@ static inline void regcache_mark_dirty(struct regmap *map)
WARN_ONCE(1, "regmap API is disabled"); WARN_ONCE(1, "regmap API is disabled");
} }
static inline void regmap_async_complete(struct regmap *map)
{
WARN_ONCE(1, "regmap API is disabled");
}
static inline int regmap_register_patch(struct regmap *map, static inline int regmap_register_patch(struct regmap *map,
const struct reg_default *regs, const struct reg_default *regs,
int num_regs) int num_regs)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment