regmap: Updates for v3.9
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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJRI3JNAAoJELSic+t+oim9u48P/RO7v12dlQsH97PjWv5MFMNZ LUqgfeP8zAyo3JiiHzQ8f+OFj5qKD1qE4hGRMPjoDQBEzNUYkppaEMolpeo3YP6k 51WkCrzgsAHvHRBO23v94JVXNiITKi7vtrzVvoY/j/0bKkwRtx80MHQngs5Z2gCr cdfTbGBtyeEJE7vm4CeKJA+LNyCmO6c2ykd65WYaNaczOo0TOTJwAjgDCjaytiKi wFPdm6ClO/vVeJdOVLowTHCyweQNSzhqbSNS8YZ8xVqaSUvLNvcfnHcSjiL+jjdn Rv3kxYyXzM/fBgrFKBEjFEOMWiL0+og9kbayeRUcCQsBUNPNVpMNfr1HKhyajI6M tFgg7rg9yodL+GHgiMBsEwQPVqeWDUvq3Hs5jFLqonejB+Owe5lzyaAYez7l5Grq SgyhiF+K3nhefFyaIgiUC/qC7DuSRTqVreXtd1sp5ceaYw6tDvAR00HSipVQABxu V4Gqz7czPk02IqabYxbuDfqLqOBk8Y0lygSCkvyI/9ueQt5VOhQNl7QFwGFYrLiW p7E4InDU/BoqXOR6VCXaHO0Gbj+IcRhqPW158t0S1Dz3hSevWN6vt2gNKKp6EWZI YY3kobN3VyNgwexOtZB4dCEtTwr7pZeQNsWMfEZHtMW/MY8kOdKyXe4QgOyOifTZ NKe4inksJI9dUEBOwAxP =S0R9 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
Коммит
88cff24159
|
@ -1,5 +1,5 @@
|
|||
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_REGMAP_I2C) += regmap-i2c.o
|
||||
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
struct regmap;
|
||||
struct regcache_ops;
|
||||
|
@ -25,6 +26,7 @@ struct regmap_debugfs_off_cache {
|
|||
off_t min;
|
||||
off_t max;
|
||||
unsigned int base_reg;
|
||||
unsigned int max_reg;
|
||||
};
|
||||
|
||||
struct regmap_format {
|
||||
|
@ -39,6 +41,13 @@ struct regmap_format {
|
|||
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 mutex mutex;
|
||||
spinlock_t spinlock;
|
||||
|
@ -53,6 +62,11 @@ struct regmap {
|
|||
void *bus_context;
|
||||
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
|
||||
struct dentry *debugfs;
|
||||
const char *debugfs_name;
|
||||
|
@ -74,6 +88,11 @@ struct regmap {
|
|||
const struct regmap_access_table *volatile_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 write_flag_mask;
|
||||
|
||||
|
@ -175,7 +194,10 @@ bool regcache_set_val(void *base, unsigned int idx,
|
|||
unsigned int val, unsigned int word_size);
|
||||
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_lzo_ops;
|
||||
extern struct regcache_ops regcache_flat_ops;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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 @@
|
|||
static const struct regcache_ops *cache_types[] = {
|
||||
®cache_rbtree_ops,
|
||||
®cache_lzo_ops,
|
||||
®cache_flat_ops,
|
||||
};
|
||||
|
||||
static int regcache_hw_init(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;
|
||||
loff_t p = 0;
|
||||
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
|
||||
|
@ -93,6 +95,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
|
|||
regmap_precious(map, i)) {
|
||||
if (c) {
|
||||
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,
|
||||
&map->debugfs_off_cache);
|
||||
c = NULL;
|
||||
|
@ -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 */
|
||||
if (c) {
|
||||
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,
|
||||
&map->debugfs_off_cache);
|
||||
}
|
||||
|
@ -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
|
||||
* no registers at all.
|
||||
*/
|
||||
if (list_empty(&map->debugfs_off_cache)) {
|
||||
WARN_ON(list_empty(&map->debugfs_off_cache));
|
||||
return base;
|
||||
}
|
||||
WARN_ON(list_empty(&map->debugfs_off_cache));
|
||||
ret = base;
|
||||
|
||||
/* Find the relevant block */
|
||||
/* Find the relevant block:offset */
|
||||
list_for_each_entry(c, &map->debugfs_off_cache, list) {
|
||||
if (from >= c->min && from <= c->max) {
|
||||
*pos = c->min;
|
||||
return c->base_reg;
|
||||
fpos_offset = from - c->min;
|
||||
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;
|
||||
ret = c->base_reg;
|
||||
*pos = c->max;
|
||||
ret = c->max_reg;
|
||||
}
|
||||
|
||||
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,
|
||||
unsigned int to, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
|
@ -165,14 +186,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
|
|||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* 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 */
|
||||
}
|
||||
regmap_calc_tot_len(map, buf, count);
|
||||
|
||||
/* Work out which register we're starting at */
|
||||
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,
|
|||
/* If we're in the region the user is trying to read */
|
||||
if (p >= *ppos) {
|
||||
/* ...but not beyond it */
|
||||
if (buf_pos + 1 + map->debugfs_tot_len >= count)
|
||||
if (buf_pos + map->debugfs_tot_len > count)
|
||||
break;
|
||||
|
||||
/* Format the register */
|
||||
|
|
|
@ -34,6 +34,7 @@ struct regmap_irq_chip_data {
|
|||
int irq;
|
||||
int wake_count;
|
||||
|
||||
void *status_reg_buf;
|
||||
unsigned int *status_buf;
|
||||
unsigned int *mask_buf;
|
||||
unsigned int *mask_buf_def;
|
||||
|
@ -87,6 +88,23 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
|
|||
if (ret != 0)
|
||||
dev_err(d->map->dev, "Failed to sync masks in %x\n",
|
||||
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)
|
||||
|
@ -129,16 +147,15 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
|
|||
struct regmap *map = d->map;
|
||||
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
|
||||
|
||||
if (!d->chip->wake_base)
|
||||
return -EINVAL;
|
||||
|
||||
if (on) {
|
||||
d->wake_buf[irq_data->reg_offset / map->reg_stride]
|
||||
&= ~irq_data->mask;
|
||||
if (d->wake_buf)
|
||||
d->wake_buf[irq_data->reg_offset / map->reg_stride]
|
||||
&= ~irq_data->mask;
|
||||
d->wake_count++;
|
||||
} else {
|
||||
d->wake_buf[irq_data->reg_offset / map->reg_stride]
|
||||
|= irq_data->mask;
|
||||
if (d->wake_buf)
|
||||
d->wake_buf[irq_data->reg_offset / map->reg_stride]
|
||||
|= irq_data->mask;
|
||||
d->wake_count--;
|
||||
}
|
||||
|
||||
|
@ -171,6 +188,62 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read in the statuses, using a single bulk read if possible
|
||||
* in order to reduce the I/O overheads.
|
||||
*/
|
||||
if (!map->use_single_rw && map->reg_stride == 1 &&
|
||||
data->irq_reg_stride == 1) {
|
||||
u8 *buf8 = data->status_reg_buf;
|
||||
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) {
|
||||
dev_err(map->dev, "Failed to read IRQ status: %d\n",
|
||||
ret);
|
||||
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
|
||||
|
@ -179,18 +252,6 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
|
|||
* doing a write per register.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
data->status_buf[i] &= ~data->mask_buf[i];
|
||||
|
||||
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,
|
|||
|
||||
d->irq_chip = regmap_irq_chip;
|
||||
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->map = map;
|
||||
d->chip = chip;
|
||||
|
@ -331,6 +387,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
|||
else
|
||||
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);
|
||||
|
||||
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,
|
|||
d->wake_buf[i] = d->mask_buf_def[i];
|
||||
reg = chip->wake_base +
|
||||
(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) {
|
||||
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
|
||||
reg, ret);
|
||||
|
@ -401,6 +472,7 @@ err_alloc:
|
|||
kfree(d->mask_buf_def);
|
||||
kfree(d->mask_buf);
|
||||
kfree(d->status_buf);
|
||||
kfree(d->status_reg_buf);
|
||||
kfree(d);
|
||||
return ret;
|
||||
}
|
||||
|
@ -422,6 +494,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
|
|||
kfree(d->wake_buf);
|
||||
kfree(d->mask_buf_def);
|
||||
kfree(d->mask_buf);
|
||||
kfree(d->status_reg_buf);
|
||||
kfree(d->status_buf);
|
||||
kfree(d);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -26,6 +27,7 @@
|
|||
struct regmap_mmio_context {
|
||||
void __iomem *regs;
|
||||
unsigned val_bytes;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
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;
|
||||
u32 offset;
|
||||
int ret;
|
||||
|
||||
BUG_ON(reg_size != 4);
|
||||
|
||||
if (ctx->clk) {
|
||||
ret = clk_enable(ctx->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
offset = *(u32 *)reg;
|
||||
|
||||
while (val_size) {
|
||||
|
@ -64,6 +73,9 @@ static int regmap_mmio_gather_write(void *context,
|
|||
offset += ctx->val_bytes;
|
||||
}
|
||||
|
||||
if (ctx->clk)
|
||||
clk_disable(ctx->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -80,9 +92,16 @@ static int regmap_mmio_read(void *context,
|
|||
{
|
||||
struct regmap_mmio_context *ctx = context;
|
||||
u32 offset;
|
||||
int ret;
|
||||
|
||||
BUG_ON(reg_size != 4);
|
||||
|
||||
if (ctx->clk) {
|
||||
ret = clk_enable(ctx->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
offset = *(u32 *)reg;
|
||||
|
||||
while (val_size) {
|
||||
|
@ -110,11 +129,20 @@ static int regmap_mmio_read(void *context,
|
|||
offset += ctx->val_bytes;
|
||||
}
|
||||
|
||||
if (ctx->clk)
|
||||
clk_disable(ctx->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -128,11 +156,14 @@ static struct regmap_bus regmap_mmio = {
|
|||
.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)
|
||||
{
|
||||
struct regmap_mmio_context *ctx;
|
||||
int min_stride;
|
||||
int ret;
|
||||
|
||||
if (config->reg_bits != 32)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -179,37 +210,59 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
|
|||
ctx->regs = regs;
|
||||
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;
|
||||
|
||||
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
|
||||
* @clk_id: register clock consumer ID
|
||||
* @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.
|
||||
*/
|
||||
struct regmap *regmap_init_mmio(struct device *dev,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config)
|
||||
struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
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))
|
||||
return ERR_CAST(ctx);
|
||||
|
||||
return regmap_init(dev, ®map_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
|
||||
* @clk_id: register clock consumer ID
|
||||
* @regs: Pointer to memory-mapped IO region
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
|
@ -217,18 +270,18 @@ EXPORT_SYMBOL_GPL(regmap_init_mmio);
|
|||
* to a struct regmap. The regmap will be automatically freed by the
|
||||
* device management code.
|
||||
*/
|
||||
struct regmap *devm_regmap_init_mmio(struct device *dev,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config)
|
||||
struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
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))
|
||||
return ERR_CAST(ctx);
|
||||
|
||||
return devm_regmap_init(dev, ®map_mmio, ctx, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_mmio);
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_mmio_clk);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -15,6 +15,21 @@
|
|||
#include <linux/init.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)
|
||||
{
|
||||
struct device *dev = context;
|
||||
|
@ -40,6 +55,43 @@ static int regmap_spi_gather_write(void *context,
|
|||
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,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
|
@ -53,6 +105,8 @@ static int regmap_spi_read(void *context,
|
|||
static struct regmap_bus regmap_spi = {
|
||||
.write = regmap_spi_write,
|
||||
.gather_write = regmap_spi_gather_write,
|
||||
.async_write = regmap_spi_async_write,
|
||||
.async_alloc = regmap_spi_async_alloc,
|
||||
.read = regmap_spi_read,
|
||||
.read_flag_mask = 0x80,
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/regmap.h>
|
||||
|
@ -34,6 +35,22 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
|
|||
unsigned int mask, unsigned int val,
|
||||
bool *change);
|
||||
|
||||
static int _regmap_bus_read(void *context, unsigned int reg,
|
||||
unsigned int *val);
|
||||
static int _regmap_bus_formatted_write(void *context, unsigned int reg,
|
||||
unsigned int val);
|
||||
static int _regmap_bus_raw_write(void *context, unsigned int reg,
|
||||
unsigned int val);
|
||||
|
||||
static void async_cleanup(struct work_struct *work)
|
||||
{
|
||||
struct regmap_async *async = container_of(work, struct regmap_async,
|
||||
cleanup);
|
||||
|
||||
kfree(async->work_buf);
|
||||
kfree(async);
|
||||
}
|
||||
|
||||
bool regmap_reg_in_ranges(unsigned int reg,
|
||||
const struct regmap_range *ranges,
|
||||
unsigned int nranges)
|
||||
|
@ -372,7 +389,7 @@ struct regmap *regmap_init(struct device *dev,
|
|||
enum regmap_endian reg_endian, val_endian;
|
||||
int i, j;
|
||||
|
||||
if (!bus || !config)
|
||||
if (!config)
|
||||
goto err;
|
||||
|
||||
map = kzalloc(sizeof(*map), GFP_KERNEL);
|
||||
|
@ -386,7 +403,8 @@ struct regmap *regmap_init(struct device *dev,
|
|||
map->unlock = config->unlock;
|
||||
map->lock_arg = config->lock_arg;
|
||||
} else {
|
||||
if (bus->fast_io) {
|
||||
if ((bus && bus->fast_io) ||
|
||||
config->fast_io) {
|
||||
spin_lock_init(&map->spinlock);
|
||||
map->lock = regmap_lock_spinlock;
|
||||
map->unlock = regmap_unlock_spinlock;
|
||||
|
@ -423,13 +441,27 @@ struct regmap *regmap_init(struct device *dev,
|
|||
map->cache_type = config->cache_type;
|
||||
map->name = config->name;
|
||||
|
||||
spin_lock_init(&map->async_lock);
|
||||
INIT_LIST_HEAD(&map->async_list);
|
||||
init_waitqueue_head(&map->async_waitq);
|
||||
|
||||
if (config->read_flag_mask || config->write_flag_mask) {
|
||||
map->read_flag_mask = config->read_flag_mask;
|
||||
map->write_flag_mask = config->write_flag_mask;
|
||||
} else {
|
||||
} else if (bus) {
|
||||
map->read_flag_mask = bus->read_flag_mask;
|
||||
}
|
||||
|
||||
if (!bus) {
|
||||
map->reg_read = config->reg_read;
|
||||
map->reg_write = config->reg_write;
|
||||
|
||||
map->defer_caching = false;
|
||||
goto skip_format_initialization;
|
||||
} else {
|
||||
map->reg_read = _regmap_bus_read;
|
||||
}
|
||||
|
||||
reg_endian = config->reg_format_endian;
|
||||
if (reg_endian == REGMAP_ENDIAN_DEFAULT)
|
||||
reg_endian = bus->reg_format_endian_default;
|
||||
|
@ -500,6 +532,12 @@ struct regmap *regmap_init(struct device *dev,
|
|||
}
|
||||
break;
|
||||
|
||||
case 24:
|
||||
if (reg_endian != REGMAP_ENDIAN_BIG)
|
||||
goto err_map;
|
||||
map->format.format_reg = regmap_format_24;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
switch (reg_endian) {
|
||||
case REGMAP_ENDIAN_BIG:
|
||||
|
@ -575,6 +613,16 @@ struct regmap *regmap_init(struct device *dev,
|
|||
goto err_map;
|
||||
}
|
||||
|
||||
if (map->format.format_write) {
|
||||
map->defer_caching = false;
|
||||
map->reg_write = _regmap_bus_formatted_write;
|
||||
} else if (map->format.format_val) {
|
||||
map->defer_caching = true;
|
||||
map->reg_write = _regmap_bus_raw_write;
|
||||
}
|
||||
|
||||
skip_format_initialization:
|
||||
|
||||
map->range_tree = RB_ROOT;
|
||||
for (i = 0; i < config->num_ranges; i++) {
|
||||
const struct regmap_range_cfg *range_cfg = &config->ranges[i];
|
||||
|
@ -776,7 +824,7 @@ void regmap_exit(struct regmap *map)
|
|||
regcache_exit(map);
|
||||
regmap_debugfs_exit(map);
|
||||
regmap_range_exit(map);
|
||||
if (map->bus->free_context)
|
||||
if (map->bus && map->bus->free_context)
|
||||
map->bus->free_context(map->bus_context);
|
||||
kfree(map->work_buf);
|
||||
kfree(map);
|
||||
|
@ -870,15 +918,20 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
|
|||
}
|
||||
|
||||
static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||
const void *val, size_t val_len)
|
||||
const void *val, size_t val_len, bool async)
|
||||
{
|
||||
struct regmap_range_node *range;
|
||||
unsigned long flags;
|
||||
u8 *u8 = map->work_buf;
|
||||
void *work_val = map->work_buf + map->format.reg_bytes +
|
||||
map->format.pad_bytes;
|
||||
void *buf;
|
||||
int ret = -ENOTSUPP;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
BUG_ON(!map->bus);
|
||||
|
||||
/* Check for unwritable registers before we start */
|
||||
if (map->writeable_reg)
|
||||
for (i = 0; i < val_len / map->format.val_bytes; i++)
|
||||
|
@ -918,7 +971,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
dev_dbg(map->dev, "Writing window %d/%zu\n",
|
||||
win_residue, val_len / map->format.val_bytes);
|
||||
ret = _regmap_raw_write(map, reg, val, win_residue *
|
||||
map->format.val_bytes);
|
||||
map->format.val_bytes, async);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
|
@ -941,6 +994,50 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
|
||||
u8[0] |= map->write_flag_mask;
|
||||
|
||||
if (async && map->bus->async_write) {
|
||||
struct regmap_async *async = map->bus->async_alloc();
|
||||
if (!async)
|
||||
return -ENOMEM;
|
||||
|
||||
async->work_buf = kzalloc(map->format.buf_size,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!async->work_buf) {
|
||||
kfree(async);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_WORK(&async->cleanup, async_cleanup);
|
||||
async->map = map;
|
||||
|
||||
/* If the caller supplied the value we can use it safely. */
|
||||
memcpy(async->work_buf, map->work_buf, map->format.pad_bytes +
|
||||
map->format.reg_bytes + map->format.val_bytes);
|
||||
if (val == work_val)
|
||||
val = async->work_buf + map->format.pad_bytes +
|
||||
map->format.reg_bytes;
|
||||
|
||||
spin_lock_irqsave(&map->async_lock, flags);
|
||||
list_add_tail(&async->list, &map->async_list);
|
||||
spin_unlock_irqrestore(&map->async_lock, flags);
|
||||
|
||||
ret = map->bus->async_write(map->bus_context, async->work_buf,
|
||||
map->format.reg_bytes +
|
||||
map->format.pad_bytes,
|
||||
val, val_len, async);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(map->dev, "Failed to schedule write: %d\n",
|
||||
ret);
|
||||
|
||||
spin_lock_irqsave(&map->async_lock, flags);
|
||||
list_del(&async->list);
|
||||
spin_unlock_irqrestore(&map->async_lock, flags);
|
||||
|
||||
kfree(async->work_buf);
|
||||
kfree(async);
|
||||
}
|
||||
}
|
||||
|
||||
trace_regmap_hw_write_start(map->dev, reg,
|
||||
val_len / map->format.val_bytes);
|
||||
|
||||
|
@ -948,8 +1045,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
* send the work_buf directly, otherwise try to do a gather
|
||||
* write.
|
||||
*/
|
||||
if (val == (map->work_buf + map->format.pad_bytes +
|
||||
map->format.reg_bytes))
|
||||
if (val == work_val)
|
||||
ret = map->bus->write(map->bus_context, map->work_buf,
|
||||
map->format.reg_bytes +
|
||||
map->format.pad_bytes +
|
||||
|
@ -981,14 +1077,62 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int _regmap_bus_formatted_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
int ret;
|
||||
struct regmap_range_node *range;
|
||||
struct regmap *map = context;
|
||||
|
||||
BUG_ON(!map->bus || !map->format.format_write);
|
||||
|
||||
range = _regmap_range_lookup(map, reg);
|
||||
if (range) {
|
||||
ret = _regmap_select_page(map, ®, range, 1);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
map->format.format_write(map, reg, val);
|
||||
|
||||
trace_regmap_hw_write_start(map->dev, reg, 1);
|
||||
|
||||
ret = map->bus->write(map->bus_context, map->work_buf,
|
||||
map->format.buf_size);
|
||||
|
||||
trace_regmap_hw_write_done(map->dev, reg, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _regmap_bus_raw_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct regmap *map = context;
|
||||
|
||||
BUG_ON(!map->bus || !map->format.format_val);
|
||||
|
||||
map->format.format_val(map->work_buf + map->format.reg_bytes
|
||||
+ map->format.pad_bytes, val, 0);
|
||||
return _regmap_raw_write(map, reg,
|
||||
map->work_buf +
|
||||
map->format.reg_bytes +
|
||||
map->format.pad_bytes,
|
||||
map->format.val_bytes, false);
|
||||
}
|
||||
|
||||
static inline void *_regmap_map_get_context(struct regmap *map)
|
||||
{
|
||||
return (map->bus) ? map : map->bus_context;
|
||||
}
|
||||
|
||||
int _regmap_write(struct regmap *map, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct regmap_range_node *range;
|
||||
int ret;
|
||||
BUG_ON(!map->format.format_write && !map->format.format_val);
|
||||
void *context = _regmap_map_get_context(map);
|
||||
|
||||
if (!map->cache_bypass && map->format.format_write) {
|
||||
if (!map->cache_bypass && !map->defer_caching) {
|
||||
ret = regcache_write(map, reg, val);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
@ -1005,33 +1149,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
|
|||
|
||||
trace_regmap_reg_write(map->dev, reg, val);
|
||||
|
||||
if (map->format.format_write) {
|
||||
range = _regmap_range_lookup(map, reg);
|
||||
if (range) {
|
||||
ret = _regmap_select_page(map, ®, range, 1);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
map->format.format_write(map, reg, val);
|
||||
|
||||
trace_regmap_hw_write_start(map->dev, reg, 1);
|
||||
|
||||
ret = map->bus->write(map->bus_context, map->work_buf,
|
||||
map->format.buf_size);
|
||||
|
||||
trace_regmap_hw_write_done(map->dev, reg, 1);
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
map->format.format_val(map->work_buf + map->format.reg_bytes
|
||||
+ map->format.pad_bytes, val, 0);
|
||||
return _regmap_raw_write(map, reg,
|
||||
map->work_buf +
|
||||
map->format.reg_bytes +
|
||||
map->format.pad_bytes,
|
||||
map->format.val_bytes);
|
||||
}
|
||||
return map->reg_write(context, reg, val);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1082,6 +1200,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (!map->bus)
|
||||
return -EINVAL;
|
||||
if (val_len % map->format.val_bytes)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
|
@ -1089,7 +1209,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
|
||||
map->lock(map->lock_arg);
|
||||
|
||||
ret = _regmap_raw_write(map, reg, val, val_len);
|
||||
ret = _regmap_raw_write(map, reg, val, val_len, false);
|
||||
|
||||
map->unlock(map->lock_arg);
|
||||
|
||||
|
@ -1118,6 +1238,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
|
|||
size_t val_bytes = map->format.val_bytes;
|
||||
void *wval;
|
||||
|
||||
if (!map->bus)
|
||||
return -EINVAL;
|
||||
if (!map->format.parse_val)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
|
@ -1145,14 +1267,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
|
|||
if (map->use_single_rw) {
|
||||
for (i = 0; i < val_count; i++) {
|
||||
ret = regmap_raw_write(map,
|
||||
reg + (i * map->reg_stride),
|
||||
val + (i * val_bytes),
|
||||
val_bytes);
|
||||
reg + (i * map->reg_stride),
|
||||
val + (i * val_bytes),
|
||||
val_bytes);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
|
||||
ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count,
|
||||
false);
|
||||
}
|
||||
|
||||
if (val_bytes != 1)
|
||||
|
@ -1164,6 +1287,48 @@ out:
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_bulk_write);
|
||||
|
||||
/**
|
||||
* regmap_raw_write_async(): Write raw values to one or more registers
|
||||
* asynchronously
|
||||
*
|
||||
* @map: Register map to write to
|
||||
* @reg: Initial register to write to
|
||||
* @val: Block of data to be written, laid out for direct transmission to the
|
||||
* device. Must be valid until regmap_async_complete() is called.
|
||||
* @val_len: Length of data pointed to by val.
|
||||
*
|
||||
* This function is intended to be used for things like firmware
|
||||
* download where a large block of data needs to be transferred to the
|
||||
* device. No formatting will be done on the data provided.
|
||||
*
|
||||
* If supported by the underlying bus the write will be scheduled
|
||||
* asynchronously, helping maximise I/O speed on higher speed buses
|
||||
* like SPI. regmap_async_complete() can be called to ensure that all
|
||||
* asynchrnous writes have been completed.
|
||||
*
|
||||
* A value of zero will be returned on success, a negative errno will
|
||||
* be returned in error cases.
|
||||
*/
|
||||
int regmap_raw_write_async(struct regmap *map, unsigned int reg,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (val_len % map->format.val_bytes)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
map->lock(map->lock_arg);
|
||||
|
||||
ret = _regmap_raw_write(map, reg, val, val_len, true);
|
||||
|
||||
map->unlock(map->lock_arg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_raw_write_async);
|
||||
|
||||
static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
||||
unsigned int val_len)
|
||||
{
|
||||
|
@ -1171,6 +1336,8 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
|||
u8 *u8 = map->work_buf;
|
||||
int ret;
|
||||
|
||||
BUG_ON(!map->bus);
|
||||
|
||||
range = _regmap_range_lookup(map, reg);
|
||||
if (range) {
|
||||
ret = _regmap_select_page(map, ®, range,
|
||||
|
@ -1202,10 +1369,29 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int _regmap_bus_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
int ret;
|
||||
struct regmap *map = context;
|
||||
|
||||
if (!map->format.parse_val)
|
||||
return -EINVAL;
|
||||
|
||||
ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
|
||||
if (ret == 0)
|
||||
*val = map->format.parse_val(map->work_buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _regmap_read(struct regmap *map, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
int ret;
|
||||
void *context = _regmap_map_get_context(map);
|
||||
|
||||
BUG_ON(!map->reg_read);
|
||||
|
||||
if (!map->cache_bypass) {
|
||||
ret = regcache_read(map, reg, val);
|
||||
|
@ -1213,26 +1399,21 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!map->format.parse_val)
|
||||
return -EINVAL;
|
||||
|
||||
if (map->cache_only)
|
||||
return -EBUSY;
|
||||
|
||||
ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
|
||||
ret = map->reg_read(context, reg, val);
|
||||
if (ret == 0) {
|
||||
*val = map->format.parse_val(map->work_buf);
|
||||
|
||||
#ifdef LOG_DEVICE
|
||||
if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
|
||||
dev_info(map->dev, "%x => %x\n", reg, *val);
|
||||
#endif
|
||||
|
||||
trace_regmap_reg_read(map->dev, reg, *val);
|
||||
}
|
||||
|
||||
if (ret == 0 && !map->cache_bypass)
|
||||
regcache_write(map, reg, *val);
|
||||
if (!map->cache_bypass)
|
||||
regcache_write(map, reg, *val);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1283,6 +1464,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
|||
unsigned int v;
|
||||
int ret, i;
|
||||
|
||||
if (!map->bus)
|
||||
return -EINVAL;
|
||||
if (val_len % map->format.val_bytes)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
|
@ -1334,6 +1517,8 @@ 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 (!map->bus)
|
||||
return -EINVAL;
|
||||
if (!map->format.parse_val)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
|
@ -1450,6 +1635,68 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_update_bits_check);
|
||||
|
||||
void regmap_async_complete_cb(struct regmap_async *async, int ret)
|
||||
{
|
||||
struct regmap *map = async->map;
|
||||
bool wake;
|
||||
|
||||
spin_lock(&map->async_lock);
|
||||
|
||||
list_del(&async->list);
|
||||
wake = list_empty(&map->async_list);
|
||||
|
||||
if (ret != 0)
|
||||
map->async_ret = ret;
|
||||
|
||||
spin_unlock(&map->async_lock);
|
||||
|
||||
schedule_work(&async->cleanup);
|
||||
|
||||
if (wake)
|
||||
wake_up(&map->async_waitq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_async_complete_cb);
|
||||
|
||||
static int regmap_async_is_done(struct regmap *map)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&map->async_lock, flags);
|
||||
ret = list_empty(&map->async_list);
|
||||
spin_unlock_irqrestore(&map->async_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* regmap_async_complete: Ensure all asynchronous I/O has completed.
|
||||
*
|
||||
* @map: Map to operate on.
|
||||
*
|
||||
* Blocks until any pending asynchronous I/O has completed. Returns
|
||||
* an error code for any failed I/O operations.
|
||||
*/
|
||||
int regmap_async_complete(struct regmap *map)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/* Nothing to do with no async support */
|
||||
if (!map->bus->async_write)
|
||||
return 0;
|
||||
|
||||
wait_event(map->async_waitq, regmap_async_is_done(map));
|
||||
|
||||
spin_lock_irqsave(&map->async_lock, flags);
|
||||
ret = map->async_ret;
|
||||
map->async_ret = 0;
|
||||
spin_unlock_irqrestore(&map->async_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_async_complete);
|
||||
|
||||
/**
|
||||
* regmap_register_patch: Register and apply register updates to be applied
|
||||
* on device initialistion
|
||||
|
|
|
@ -96,6 +96,7 @@ const struct regmap_irq_chip wm5102_aod = {
|
|||
.mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
|
||||
.ack_base = ARIZONA_AOD_IRQ1,
|
||||
.wake_base = ARIZONA_WAKE_CONTROL,
|
||||
.wake_invert = 1,
|
||||
.num_regs = 1,
|
||||
.irqs = wm5102_aod_irqs,
|
||||
.num_irqs = ARRAY_SIZE(wm5102_aod_irqs),
|
||||
|
|
|
@ -255,6 +255,7 @@ const struct regmap_irq_chip wm5110_aod = {
|
|||
.mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
|
||||
.ack_base = ARIZONA_AOD_IRQ1,
|
||||
.wake_base = ARIZONA_WAKE_CONTROL,
|
||||
.wake_invert = 1,
|
||||
.num_regs = 1,
|
||||
.irqs = wm5110_aod_irqs,
|
||||
.num_irqs = ARRAY_SIZE(wm5110_aod_irqs),
|
||||
|
|
|
@ -28,7 +28,8 @@ struct regmap_range_cfg;
|
|||
enum regcache_type {
|
||||
REGCACHE_NONE,
|
||||
REGCACHE_RBTREE,
|
||||
REGCACHE_COMPRESSED
|
||||
REGCACHE_COMPRESSED,
|
||||
REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -127,7 +128,18 @@ typedef void (*regmap_unlock)(void *);
|
|||
* @lock_arg: this field is passed as the only argument of lock/unlock
|
||||
* functions (ignored in case regular lock/unlock functions
|
||||
* 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.
|
||||
* @wr_table: Optional, points to a struct regmap_access_table specifying
|
||||
* valid ranges for write access.
|
||||
|
@ -177,6 +189,11 @@ struct regmap_config {
|
|||
regmap_unlock unlock;
|
||||
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;
|
||||
const struct regmap_access_table *wr_table;
|
||||
const struct regmap_access_table *rd_table;
|
||||
|
@ -235,14 +252,21 @@ struct regmap_range_cfg {
|
|||
unsigned int window_len;
|
||||
};
|
||||
|
||||
struct regmap_async;
|
||||
|
||||
typedef int (*regmap_hw_write)(void *context, const void *data,
|
||||
size_t count);
|
||||
typedef int (*regmap_hw_gather_write)(void *context,
|
||||
const void *reg, size_t reg_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,
|
||||
const void *reg_buf, size_t reg_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);
|
||||
|
||||
/**
|
||||
|
@ -255,8 +279,11 @@ typedef void (*regmap_hw_free_context)(void *context);
|
|||
* @write: Write operation.
|
||||
* @gather_write: Write operation with split register/value, return -ENOTSUPP
|
||||
* 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
|
||||
* data.
|
||||
* @async_alloc: Allocate a regmap_async() structure.
|
||||
* @read_flag_mask: Mask to be set in the top byte of the register when doing
|
||||
* a read.
|
||||
* @reg_format_endian_default: Default endianness for formatted register
|
||||
|
@ -265,13 +292,16 @@ typedef void (*regmap_hw_free_context)(void *context);
|
|||
* @val_format_endian_default: Default endianness for formatted register
|
||||
* values. Used when the regmap_config specifies DEFAULT. If this is
|
||||
* DEFAULT, BIG is assumed.
|
||||
* @async_size: Size of struct used for async work.
|
||||
*/
|
||||
struct regmap_bus {
|
||||
bool fast_io;
|
||||
regmap_hw_write write;
|
||||
regmap_hw_gather_write gather_write;
|
||||
regmap_hw_async_write async_write;
|
||||
regmap_hw_read read;
|
||||
regmap_hw_free_context free_context;
|
||||
regmap_hw_async_alloc async_alloc;
|
||||
u8 read_flag_mask;
|
||||
enum regmap_endian reg_format_endian_default;
|
||||
enum regmap_endian val_format_endian_default;
|
||||
|
@ -285,9 +315,9 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c,
|
|||
const struct regmap_config *config);
|
||||
struct regmap *regmap_init_spi(struct spi_device *dev,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *regmap_init_mmio(struct device *dev,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config);
|
||||
|
||||
struct regmap *devm_regmap_init(struct device *dev,
|
||||
const struct regmap_bus *bus,
|
||||
|
@ -297,9 +327,44 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
|
|||
const struct regmap_config *config);
|
||||
struct regmap *devm_regmap_init_spi(struct spi_device *dev,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *devm_regmap_init_mmio(struct device *dev,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
|
||||
void __iomem *regs,
|
||||
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);
|
||||
int regmap_reinit_cache(struct regmap *map,
|
||||
|
@ -310,6 +375,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
const void *val, size_t val_len);
|
||||
int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
|
||||
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_raw_read(struct regmap *map, unsigned int reg,
|
||||
void *val, size_t val_len);
|
||||
|
@ -321,6 +388,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
|
|||
unsigned int mask, unsigned int val,
|
||||
bool *change);
|
||||
int regmap_get_val_bytes(struct regmap *map);
|
||||
int regmap_async_complete(struct regmap *map);
|
||||
|
||||
int regcache_sync(struct regmap *map);
|
||||
int regcache_sync_region(struct regmap *map, unsigned int min,
|
||||
|
@ -381,6 +449,7 @@ struct regmap_irq_chip {
|
|||
unsigned int wake_base;
|
||||
unsigned int irq_reg_stride;
|
||||
unsigned int mask_invert;
|
||||
unsigned int wake_invert;
|
||||
bool runtime_pm;
|
||||
|
||||
int num_regs;
|
||||
|
@ -422,6 +491,13 @@ static inline int regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
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,
|
||||
const void *val, size_t val_count)
|
||||
{
|
||||
|
@ -500,6 +576,11 @@ static inline void regcache_mark_dirty(struct regmap *map)
|
|||
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,
|
||||
const struct reg_default *regs,
|
||||
int num_regs)
|
||||
|
|
Загрузка…
Ссылка в новой задаче