Commit 4fc9ed33 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull regmap updates from Mark Brown:
 "The main thing this time around has been some improvments to async
  I/O.

   - Cleaned up the async I/O support and extended it to allow single
     register writes more easily.  This is now used where possible for
     internally generated I/O, providing performance improvements for
     devices that can do async I/O.
   - An API for issuing a sequence of register writes as a single
     operation.  Some devices and buses can take advantage of this to do
     the I/O faster.
   - Addition of regmap_field APIs which help drivers for devices with
     repeated IPs or which move registers around between revisions to
     share helpers.
   - Support for SPMI buses"

* tag 'regmap-v3.13' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: add SPMI support
  regmap: debugfs: Fix a boot time crash with early regmap init
  regmap: irq: clear status when disable irq
  regmap: Only send a single buffer for async I/O if writing one register
  regmap: spi: Handle async writes of only one buffer
  regmap: new API regmap_multi_reg_write() definition
  regmap: Use async I/O during cache sync
  regmap: Use async I/O for patch application
  regmap: Fix regmap_bulk_write single-rw mutex deadlock
  regmap: Provide asynchronous write and update bits operations
  regmap: Simplify the initiation of async I/O
  regmap: Don't generate gather writes for single register raw writes
  regmap: Cache async work structures
  regmap: add helper macro to set min/max range of register
  regmap: Add regmap_fields APIs
  regmap: add regmap_field_update_bits()
parents 7e238a2e 04bc9ac1
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
# subsystems should select the appropriate symbols. # subsystems should select the appropriate symbols.
config REGMAP config REGMAP
default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_MMIO || REGMAP_IRQ) default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ)
select LZO_COMPRESS select LZO_COMPRESS
select LZO_DECOMPRESS select LZO_DECOMPRESS
select IRQ_DOMAIN if REGMAP_IRQ select IRQ_DOMAIN if REGMAP_IRQ
...@@ -15,6 +15,9 @@ config REGMAP_I2C ...@@ -15,6 +15,9 @@ config REGMAP_I2C
config REGMAP_SPI config REGMAP_SPI
tristate tristate
config REGMAP_SPMI
tristate
config REGMAP_MMIO config REGMAP_MMIO
tristate tristate
......
...@@ -3,5 +3,6 @@ obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o ...@@ -3,5 +3,6 @@ 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
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o
obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o
...@@ -44,7 +44,6 @@ struct regmap_format { ...@@ -44,7 +44,6 @@ struct regmap_format {
struct regmap_async { struct regmap_async {
struct list_head list; struct list_head list;
struct work_struct cleanup;
struct regmap *map; struct regmap *map;
void *work_buf; void *work_buf;
}; };
...@@ -64,9 +63,11 @@ struct regmap { ...@@ -64,9 +63,11 @@ struct regmap {
void *bus_context; void *bus_context;
const char *name; const char *name;
bool async;
spinlock_t async_lock; spinlock_t async_lock;
wait_queue_head_t async_waitq; wait_queue_head_t async_waitq;
struct list_head async_list; struct list_head async_list;
struct list_head async_free;
int async_ret; int async_ret;
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
...@@ -179,6 +180,9 @@ struct regmap_field { ...@@ -179,6 +180,9 @@ struct regmap_field {
/* lsb */ /* lsb */
unsigned int shift; unsigned int shift;
unsigned int reg; unsigned int reg;
unsigned int id_size;
unsigned int id_offset;
}; };
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
...@@ -218,7 +222,7 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, ...@@ -218,7 +222,7 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
int regcache_lookup_reg(struct regmap *map, unsigned int reg); int regcache_lookup_reg(struct regmap *map, unsigned int reg);
int _regmap_raw_write(struct regmap *map, unsigned int reg, int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool async); const void *val, size_t val_len);
void regmap_async_complete_cb(struct regmap_async *async, int ret); void regmap_async_complete_cb(struct regmap_async *async, int ret);
......
...@@ -307,6 +307,8 @@ int regcache_sync(struct regmap *map) ...@@ -307,6 +307,8 @@ int regcache_sync(struct regmap *map)
if (!map->cache_dirty) if (!map->cache_dirty)
goto out; goto out;
map->async = true;
/* Apply any patch first */ /* Apply any patch first */
map->cache_bypass = 1; map->cache_bypass = 1;
for (i = 0; i < map->patch_regs; i++) { for (i = 0; i < map->patch_regs; i++) {
...@@ -332,11 +334,15 @@ int regcache_sync(struct regmap *map) ...@@ -332,11 +334,15 @@ int regcache_sync(struct regmap *map)
map->cache_dirty = false; map->cache_dirty = false;
out: out:
trace_regcache_sync(map->dev, name, "stop");
/* Restore the bypass state */ /* Restore the bypass state */
map->async = false;
map->cache_bypass = bypass; map->cache_bypass = bypass;
map->unlock(map->lock_arg); map->unlock(map->lock_arg);
regmap_async_complete(map);
trace_regcache_sync(map->dev, name, "stop");
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(regcache_sync); EXPORT_SYMBOL_GPL(regcache_sync);
...@@ -375,17 +381,23 @@ int regcache_sync_region(struct regmap *map, unsigned int min, ...@@ -375,17 +381,23 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
if (!map->cache_dirty) if (!map->cache_dirty)
goto out; goto out;
map->async = true;
if (map->cache_ops->sync) if (map->cache_ops->sync)
ret = map->cache_ops->sync(map, min, max); ret = map->cache_ops->sync(map, min, max);
else else
ret = regcache_default_sync(map, min, max); ret = regcache_default_sync(map, min, max);
out: out:
trace_regcache_sync(map->dev, name, "stop region");
/* Restore the bypass state */ /* Restore the bypass state */
map->cache_bypass = bypass; map->cache_bypass = bypass;
map->async = false;
map->unlock(map->lock_arg); map->unlock(map->lock_arg);
regmap_async_complete(map);
trace_regcache_sync(map->dev, name, "stop region");
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(regcache_sync_region); EXPORT_SYMBOL_GPL(regcache_sync_region);
...@@ -631,8 +643,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, ...@@ -631,8 +643,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,
map->cache_bypass = 1; map->cache_bypass = 1;
ret = _regmap_raw_write(map, base, *data, count * val_bytes, ret = _regmap_raw_write(map, base, *data, count * val_bytes);
false);
map->cache_bypass = 0; map->cache_bypass = 0;
......
...@@ -15,10 +15,19 @@ ...@@ -15,10 +15,19 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/list.h>
#include "internal.h" #include "internal.h"
struct regmap_debugfs_node {
struct regmap *map;
const char *name;
struct list_head link;
};
static struct dentry *regmap_debugfs_root; static struct dentry *regmap_debugfs_root;
static LIST_HEAD(regmap_debugfs_early_list);
static DEFINE_MUTEX(regmap_debugfs_early_lock);
/* Calculate the length of a fixed format */ /* Calculate the length of a fixed format */
static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size) static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size)
...@@ -465,6 +474,20 @@ void regmap_debugfs_init(struct regmap *map, const char *name) ...@@ -465,6 +474,20 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
struct rb_node *next; struct rb_node *next;
struct regmap_range_node *range_node; struct regmap_range_node *range_node;
/* If we don't have the debugfs root yet, postpone init */
if (!regmap_debugfs_root) {
struct regmap_debugfs_node *node;
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return;
node->map = map;
node->name = name;
mutex_lock(&regmap_debugfs_early_lock);
list_add(&node->link, &regmap_debugfs_early_list);
mutex_unlock(&regmap_debugfs_early_lock);
return;
}
INIT_LIST_HEAD(&map->debugfs_off_cache); INIT_LIST_HEAD(&map->debugfs_off_cache);
mutex_init(&map->cache_lock); mutex_init(&map->cache_lock);
...@@ -519,18 +542,42 @@ void regmap_debugfs_init(struct regmap *map, const char *name) ...@@ -519,18 +542,42 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
void regmap_debugfs_exit(struct regmap *map) void regmap_debugfs_exit(struct regmap *map)
{ {
debugfs_remove_recursive(map->debugfs); if (map->debugfs) {
mutex_lock(&map->cache_lock); debugfs_remove_recursive(map->debugfs);
regmap_debugfs_free_dump_cache(map); mutex_lock(&map->cache_lock);
mutex_unlock(&map->cache_lock); regmap_debugfs_free_dump_cache(map);
kfree(map->debugfs_name); mutex_unlock(&map->cache_lock);
kfree(map->debugfs_name);
} else {
struct regmap_debugfs_node *node, *tmp;
mutex_lock(&regmap_debugfs_early_lock);
list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list,
link) {
if (node->map == map) {
list_del(&node->link);
kfree(node);
}
}
mutex_unlock(&regmap_debugfs_early_lock);
}
} }
void regmap_debugfs_initcall(void) void regmap_debugfs_initcall(void)
{ {
struct regmap_debugfs_node *node, *tmp;
regmap_debugfs_root = debugfs_create_dir("regmap", NULL); regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
if (!regmap_debugfs_root) { if (!regmap_debugfs_root) {
pr_warn("regmap: Failed to create debugfs root\n"); pr_warn("regmap: Failed to create debugfs root\n");
return; return;
} }
mutex_lock(&regmap_debugfs_early_lock);
list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) {
regmap_debugfs_init(node->map, node->name);
list_del(&node->link);
kfree(node);
}
mutex_unlock(&regmap_debugfs_early_lock);
} }
...@@ -105,6 +105,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -105,6 +105,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
"Failed to sync wakes in %x: %d\n", "Failed to sync wakes in %x: %d\n",
reg, ret); reg, ret);
} }
if (!d->chip->init_ack_masked)
continue;
/*
* Ack all the masked interrupts uncondictionly,
* OR if there is masked interrupt which hasn't been Acked,
* it'll be ignored in irq handler, then may introduce irq storm
*/
if (d->mask_buf[i] && d->chip->ack_base) {
reg = d->chip->ack_base +
(i * map->reg_stride * d->irq_reg_stride);
ret = regmap_write(map, reg, d->mask_buf[i]);
if (ret != 0)
dev_err(d->map->dev, "Failed to ack 0x%x: %d\n",
reg, ret);
}
} }
if (d->chip->runtime_pm) if (d->chip->runtime_pm)
......
...@@ -73,7 +73,8 @@ static int regmap_spi_async_write(void *context, ...@@ -73,7 +73,8 @@ static int regmap_spi_async_write(void *context,
spi_message_init(&async->m); spi_message_init(&async->m);
spi_message_add_tail(&async->t[0], &async->m); spi_message_add_tail(&async->t[0], &async->m);
spi_message_add_tail(&async->t[1], &async->m); if (val)
spi_message_add_tail(&async->t[1], &async->m);
async->m.complete = regmap_spi_complete; async->m.complete = regmap_spi_complete;
async->m.context = async; async->m.context = async;
......
/*
* Register map access API - SPMI support
*
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* Based on regmap-i2c.c:
* Copyright 2011 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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/regmap.h>
#include <linux/spmi.h>
#include <linux/module.h>
#include <linux/init.h>
static int regmap_spmi_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
BUG_ON(reg_size != 2);
return spmi_ext_register_readl(context, *(u16 *)reg,
val, val_size);
}
static int regmap_spmi_gather_write(void *context,
const void *reg, size_t reg_size,
const void *val, size_t val_size)
{
BUG_ON(reg_size != 2);
return spmi_ext_register_writel(context, *(u16 *)reg, val, val_size);
}
static int regmap_spmi_write(void *context, const void *data,
size_t count)
{
BUG_ON(count < 2);
return regmap_spmi_gather_write(context, data, 2, data + 2, count - 2);
}
static struct regmap_bus regmap_spmi = {
.read = regmap_spmi_read,
.write = regmap_spmi_write,
.gather_write = regmap_spmi_gather_write,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
/**
* regmap_init_spmi(): Initialize register map
*
* @sdev: Device that will be interacted with
* @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_spmi(struct spmi_device *sdev,
const struct regmap_config *config)
{
return regmap_init(&sdev->dev, &regmap_spmi, sdev, config);
}
EXPORT_SYMBOL_GPL(regmap_init_spmi);
/**
* devm_regmap_init_spmi(): Initialise managed register map
*
* @sdev: Device that will be interacted with
* @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.
*/
struct regmap *devm_regmap_init_spmi(struct spmi_device *sdev,
const struct regmap_config *config)
{
return devm_regmap_init(&sdev->dev, &regmap_spmi, sdev, config);
}
EXPORT_SYMBOL_GPL(devm_regmap_init_spmi);
MODULE_LICENSE("GPL");
...@@ -42,15 +42,6 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, ...@@ -42,15 +42,6 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg,
static int _regmap_bus_raw_write(void *context, unsigned int reg, static int _regmap_bus_raw_write(void *context, unsigned int reg,
unsigned int val); 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, bool regmap_reg_in_ranges(unsigned int reg,
const struct regmap_range *ranges, const struct regmap_range *ranges,
unsigned int nranges) unsigned int nranges)
...@@ -465,6 +456,7 @@ struct regmap *regmap_init(struct device *dev, ...@@ -465,6 +456,7 @@ struct regmap *regmap_init(struct device *dev,
spin_lock_init(&map->async_lock); spin_lock_init(&map->async_lock);
INIT_LIST_HEAD(&map->async_list); INIT_LIST_HEAD(&map->async_list);
INIT_LIST_HEAD(&map->async_free);
init_waitqueue_head(&map->async_waitq); init_waitqueue_head(&map->async_waitq);
if (config->read_flag_mask || config->write_flag_mask) { if (config->read_flag_mask || config->write_flag_mask) {
...@@ -821,6 +813,8 @@ static void regmap_field_init(struct regmap_field *rm_field, ...@@ -821,6 +813,8 @@ static void regmap_field_init(struct regmap_field *rm_field,
rm_field->reg = reg_field.reg; rm_field->reg = reg_field.reg;
rm_field->shift = reg_field.lsb; rm_field->shift = reg_field.lsb;
rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb); rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb);
rm_field->id_size = reg_field.id_size;
rm_field->id_offset = reg_field.id_offset;
} }
/** /**
...@@ -942,12 +936,22 @@ EXPORT_SYMBOL_GPL(regmap_reinit_cache); ...@@ -942,12 +936,22 @@ EXPORT_SYMBOL_GPL(regmap_reinit_cache);
*/ */
void regmap_exit(struct regmap *map) void regmap_exit(struct regmap *map)
{ {
struct regmap_async *async;
regcache_exit(map); regcache_exit(map);
regmap_debugfs_exit(map); regmap_debugfs_exit(map);
regmap_range_exit(map); regmap_range_exit(map);
if (map->bus && map->bus->free_context) if (map->bus && map->bus->free_context)
map->bus->free_context(map->bus_context); map->bus->free_context(map->bus_context);
kfree(map->work_buf); kfree(map->work_buf);
while (!list_empty(&map->async_free)) {
async = list_first_entry_or_null(&map->async_free,
struct regmap_async,
list);
list_del(&async->list);
kfree(async->work_buf);
kfree(async);
}
kfree(map); kfree(map);
} }
EXPORT_SYMBOL_GPL(regmap_exit); EXPORT_SYMBOL_GPL(regmap_exit);
...@@ -1039,7 +1043,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, ...@@ -1039,7 +1043,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
} }
int _regmap_raw_write(struct regmap *map, unsigned int reg, int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool async) const void *val, size_t val_len)
{ {
struct regmap_range_node *range; struct regmap_range_node *range;
unsigned long flags; unsigned long flags;
...@@ -1091,7 +1095,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, ...@@ -1091,7 +1095,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
dev_dbg(map->dev, "Writing window %d/%zu\n", dev_dbg(map->dev, "Writing window %d/%zu\n",
win_residue, val_len / map->format.val_bytes); win_residue, val_len / map->format.val_bytes);
ret = _regmap_raw_write(map, reg, val, win_residue * ret = _regmap_raw_write(map, reg, val, win_residue *
map->format.val_bytes, async); map->format.val_bytes);
if (ret != 0) if (ret != 0)
return ret; return ret;
...@@ -1114,49 +1118,72 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, ...@@ -1114,49 +1118,72 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
u8[0] |= map->write_flag_mask; u8[0] |= map->write_flag_mask;
if (async && map->bus->async_write) { /*
struct regmap_async *async = map->bus->async_alloc(); * Essentially all I/O mechanisms will be faster with a single
if (!async) * buffer to write. Since register syncs often generate raw
return -ENOMEM; * writes of single registers optimise that case.
*/
if (val != work_val && val_len == map->format.val_bytes) {
memcpy(work_val, val, map->format.val_bytes);
val = work_val;
}
if (map->async && map->bus->async_write) {
struct regmap_async *async;
trace_regmap_async_write_start(map->dev, reg, val_len); trace_regmap_async_write_start(map->dev, reg, val_len);
async->work_buf = kzalloc(map->format.buf_size, spin_lock_irqsave(&map->async_lock, flags);
GFP_KERNEL | GFP_DMA); async = list_first_entry_or_null(&map->async_free,
if (!async->work_buf) { struct regmap_async,
kfree(async); list);
return -ENOMEM; if (async)
list_del(&async->list);
spin_unlock_irqrestore(&map->async_lock, flags);
if (!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; async->map = map;
/* If the caller supplied the value we can use it safely. */ /* If the caller supplied the value we can use it safely. */
memcpy(async->work_buf, map->work_buf, map->format.pad_bytes + memcpy(async->work_buf, map->work_buf, map->format.pad_bytes +
map->format.reg_bytes + map->format.val_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); spin_lock_irqsave(&map->async_lock, flags);
list_add_tail(&async->list, &map->async_list); list_add_tail(&async->list, &map->async_list);
spin_unlock_irqrestore(&map->async_lock, flags); spin_unlock_irqrestore(&map->async_lock, flags);
ret = map->bus->async_write(map->bus_context, async->work_buf, if (val != work_val)
map->format.reg_bytes + ret = map->bus->async_write(map->bus_context,
map->format.pad_bytes, async->work_buf,
val, val_len, async); map->format.reg_bytes +
map->format.pad_bytes,
val, val_len, async);
else
ret = map->bus->async_write(map->bus_context,
async->work_buf,
map->format.reg_bytes +
map->format.pad_bytes +
val_len, NULL, 0, async);
if (ret != 0) { if (ret != 0) {
dev_err(map->dev, "Failed to schedule write: %d\n", dev_err(map->dev, "Failed to schedule write: %d\n",
ret); ret);
spin_lock_irqsave(&map->async_lock, flags); spin_lock_irqsave(&map->async_lock, flags);
list_del(&async->list); list_move(&async->list, &map->async_free);
spin_unlock_irqrestore(&map->async_lock, flags); spin_unlock_irqrestore(&map->async_lock, flags);
kfree(async->work_buf);
kfree(async);
} }
return ret; return ret;
...@@ -1253,7 +1280,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, ...@@ -1253,7 +1280,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg,
map->work_buf + map->work_buf +
map->format.reg_bytes + map->format.reg_bytes +
map->format.pad_bytes, map->format.pad_bytes,
map->format.val_bytes, false); map->format.val_bytes);
} }
static inline void *_regmap_map_get_context(struct regmap *map) static inline void *_regmap_map_get_context(struct regmap *map)
...@@ -1317,6 +1344,37 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) ...@@ -1317,6 +1344,37 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
} }
EXPORT_SYMBOL_GPL(regmap_write); EXPORT_SYMBOL_GPL(regmap_write);
/**
* regmap_write_async(): Write a value to a single register asynchronously
*
* @map: Register map to write to
* @reg: Register to write to
* @val: Value to be written
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;
if (reg % map->reg_stride)
return -EINVAL;
map->lock(map->lock_arg);
map->async = true;
ret = _regmap_write(map, reg, val);
map->async = false;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_write_async);
/** /**
* regmap_raw_write(): Write raw values to one or more registers * regmap_raw_write(): Write raw values to one or more registers
* *
...@@ -1345,7 +1403,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, ...@@ -1345,7 +1403,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
map->lock(map->lock_arg); map->lock(map->lock_arg);
ret = _regmap_raw_write(map, reg, val, val_len, false); ret = _regmap_raw_write(map, reg, val, val_len);
map->unlock(map->lock_arg); map->unlock(map->lock_arg);
...@@ -1369,6 +1427,74 @@ int regmap_field_write(struct regmap_field *field, unsigned int val) ...@@ -1369,6 +1427,74 @@ int regmap_field_write(struct regmap_field *field, unsigned int val)
} }
EXPORT_SYMBOL_GPL(regmap_field_write); EXPORT_SYMBOL_GPL(regmap_field_write);
/**
* regmap_field_update_bits(): Perform a read/modify/write cycle
* on the register field
*
* @field: Register field to write to
* @mask: Bitmask to change
* @val: Value to be written
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_field_update_bits(struct regmap_field *field, unsigned int mask, unsigned int val)
{
mask = (mask << field->shift) & field->mask;
return regmap_update_bits(field->regmap, field->reg,
mask, val << field->shift);
}
EXPORT_SYMBOL_GPL(regmap_field_update_bits);
/**
* regmap_fields_write(): Write a value to a single register field with port ID
*
* @field: Register field to write to
* @id: port ID
* @val: Value to be written
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_fields_write(struct regmap_field *field, unsigned int id,
unsigned int val)
{
if (id >= field->id_size)
return -EINVAL;
return regmap_update_bits(field->regmap,
field->reg + (field->id_offset * id),
field->mask, val << field->shift);
}
EXPORT_SYMBOL_GPL(regmap_fields_write);
/**
* regmap_fields_update_bits(): Perform a read/modify/write cycle
* on the register field
*
* @field: Register field to write to
* @id: port ID
* @mask: Bitmask to change
* @val: Value to be written
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_fields_update_bits(struct regmap_field *field, unsigned int id,
unsigned int mask, unsigned int val)
{
if (id >= field->id_size)
return -EINVAL;
mask = (mask << field->shift) & field->mask;
return regmap_update_bits(field->regmap,
field->reg + (field->id_offset * id),
mask, val << field->shift);
}
EXPORT_SYMBOL_GPL(regmap_fields_update_bits);
/* /*
* regmap_bulk_write(): Write multiple registers to the device * regmap_bulk_write(): Write multiple registers to the device
* *
...@@ -1418,16 +1544,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, ...@@ -1418,16 +1544,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
*/ */
if (map->use_single_rw) { if (map->use_single_rw) {
for (i = 0; i < val_count; i++) { for (i = 0; i < val_count; i++) {
ret = regmap_raw_write(map, ret = _regmap_raw_write(map,
reg + (i * map->reg_stride), reg + (i * map->reg_stride),
val + (i * val_bytes), val + (i * val_bytes),
val_bytes); val_bytes);
if (ret != 0) if (ret != 0)
return ret; return ret;
} }
} else { } 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) if (val_bytes != 1)
...@@ -1439,6 +1564,47 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, ...@@ -1439,6 +1564,47 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
} }
EXPORT_SYMBOL_GPL(regmap_bulk_write); EXPORT_SYMBOL_GPL(regmap_bulk_write);
/*
* regmap_multi_reg_write(): Write multiple registers to the device
*
* where the set of register are supplied in any order
*
* @map: Register map to write to
* @regs: Array of structures containing register,value to be written
* @num_regs: Number of registers to write
*
* This function is intended to be used for writing a large block of data
* atomically to the device in single transfer for those I2C client devices
* that implement this alternative block write mode.
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_multi_reg_write(struct regmap *map, struct reg_default *regs,
int num_regs)
{
int ret = 0, i;
for (i = 0; i < num_regs; i++) {
int reg = regs[i].reg;
if (reg % map->reg_stride)
return -EINVAL;
}
map->lock(map->lock_arg);
for (i = 0; i < num_regs; i++) {
ret = _regmap_write(map, regs[i].reg, regs[i].def);
if (ret != 0)
goto out;
}
out:
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_multi_reg_write);
/** /**
* regmap_raw_write_async(): Write raw values to one or more registers * regmap_raw_write_async(): Write raw values to one or more registers
* asynchronously * asynchronously
...@@ -1473,7 +1639,11 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg, ...@@ -1473,7 +1639,11 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
map->lock(map->lock_arg); map->lock(map->lock_arg);
ret = _regmap_raw_write(map, reg, val, val_len, true); map->async = true;
ret = _regmap_raw_write(map, reg, val, val_len);
map->async = false;
map->unlock(map->lock_arg); map->unlock(map->lock_arg);
...@@ -1676,6 +1846,39 @@ int regmap_field_read(struct regmap_field *field, unsigned int *val) ...@@ -1676,6 +1846,39 @@ int regmap_field_read(struct regmap_field *field, unsigned int *val)
} }
EXPORT_SYMBOL_GPL(regmap_field_read); EXPORT_SYMBOL_GPL(regmap_field_read);
/**
* regmap_fields_read(): Read a value to a single register field with port ID
*
* @field: Register field to read from
* @id: port ID
* @val: Pointer to store read value
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_fields_read(struct regmap_field *field, unsigned int id,
unsigned int *val)
{
int ret;
unsigned int reg_val;
if (id >= field->id_size)
return -EINVAL;
ret = regmap_read(field->regmap,
field->reg + (field->id_offset * id),
&reg_val);
if (ret != 0)
return ret;
reg_val &= field->mask;
reg_val >>= field->shift;
*val = reg_val;
return ret;
}
EXPORT_SYMBOL_GPL(regmap_fields_read);
/** /**
* regmap_bulk_read(): Read multiple registers from the device * regmap_bulk_read(): Read multiple registers from the device
* *
...@@ -1787,6 +1990,41 @@ int regmap_update_bits(struct regmap *map, unsigned int reg, ...@@ -1787,6 +1990,41 @@ int regmap_update_bits(struct regmap *map, unsigned int reg,
} }
EXPORT_SYMBOL_GPL(regmap_update_bits); EXPORT_SYMBOL_GPL(regmap_update_bits);
/**
* regmap_update_bits_async: Perform a read/modify/write cycle on the register
* map asynchronously
*
* @map: Register map to update
* @reg: Register to update
* @mask: Bitmask to change
* @val: New value for bitmask
*
* With most buses the read must be done synchronously so this is most
* useful for devices with a cache which do not need to interact with
* the hardware to determine the current register value.
*
* Returns zero for success, a negative number on error.
*/
int regmap_update_bits_async(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val)
{
bool change;
int ret;
map->lock(map->lock_arg);
map->async = true;
ret = _regmap_update_bits(map, reg, mask, val, &change);
map->async = false;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits_async);
/** /**
* regmap_update_bits_check: Perform a read/modify/write cycle on the * regmap_update_bits_check: Perform a read/modify/write cycle on the
* register map and report if updated * register map and report if updated
...@@ -1812,6 +2050,43 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, ...@@ -1812,6 +2050,43 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
} }
EXPORT_SYMBOL_GPL(regmap_update_bits_check); EXPORT_SYMBOL_GPL(regmap_update_bits_check);
/**
* regmap_update_bits_check_async: Perform a read/modify/write cycle on the
* register map asynchronously and report if
* updated
*
* @map: Register map to update
* @reg: Register to update
* @mask: Bitmask to change
* @val: New value for bitmask
* @change: Boolean indicating if a write was done
*
* With most buses the read must be done synchronously so this is most
* useful for devices with a cache which do not need to interact with
* the hardware to determine the current register value.
*
* Returns zero for success, a negative number on error.
*/
int regmap_update_bits_check_async(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change)
{
int ret;
map->lock(map->lock_arg);
map->async = true;
ret = _regmap_update_bits(map, reg, mask, val, change);
map->async = false;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits_check_async);
void regmap_async_complete_cb(struct regmap_async *async, int ret) void regmap_async_complete_cb(struct regmap_async *async, int ret)
{ {
struct regmap *map = async->map; struct regmap *map = async->map;
...@@ -1820,8 +2095,7 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) ...@@ -1820,8 +2095,7 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret)
trace_regmap_async_io_complete(map->dev); trace_regmap_async_io_complete(map->dev);
spin_lock(&map->async_lock); spin_lock(&map->async_lock);
list_move(&async->list, &map->async_free);
list_del(&async->list);
wake = list_empty(&map->async_list); wake = list_empty(&map->async_list);
if (ret != 0) if (ret != 0)
...@@ -1829,8 +2103,6 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) ...@@ -1829,8 +2103,6 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret)
spin_unlock(&map->async_lock); spin_unlock(&map->async_lock);
schedule_work(&async->cleanup);
if (wake) if (wake)
wake_up(&map->async_waitq); wake_up(&map->async_waitq);
} }
...@@ -1906,6 +2178,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, ...@@ -1906,6 +2178,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
bypass = map->cache_bypass; bypass = map->cache_bypass;
map->cache_bypass = true; map->cache_bypass = true;
map->async = true;
/* Write out first; it's useful to apply even if we fail later. */ /* Write out first; it's useful to apply even if we fail later. */
for (i = 0; i < num_regs; i++) { for (i = 0; i < num_regs; i++) {
...@@ -1929,10 +2202,13 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, ...@@ -1929,10 +2202,13 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
} }
out: out:
map->async = false;
map->cache_bypass = bypass; map->cache_bypass = bypass;
map->unlock(map->lock_arg); map->unlock(map->lock_arg);
regmap_async_complete(map);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(regmap_register_patch); EXPORT_SYMBOL_GPL(regmap_register_patch);
......
...@@ -23,6 +23,7 @@ struct device; ...@@ -23,6 +23,7 @@ struct device;
struct i2c_client; struct i2c_client;
struct irq_domain; struct irq_domain;
struct spi_device; struct spi_device;
struct spmi_device;
struct regmap; struct regmap;
struct regmap_range_cfg; struct regmap_range_cfg;
struct regmap_field; struct regmap_field;
...@@ -70,6 +71,8 @@ struct regmap_range { ...@@ -70,6 +71,8 @@ struct regmap_range {
unsigned int range_max; unsigned int range_max;
}; };
#define regmap_reg_range(low, high) { .range_min = low, .range_max = high, }
/* /*
* A table of ranges including some yes ranges and some no ranges. * A table of ranges including some yes ranges and some no ranges.
* If a register belongs to a no_range, the corresponding check function * If a register belongs to a no_range, the corresponding check function
...@@ -318,6 +321,8 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, ...@@ -318,6 +321,8 @@ 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_spmi(struct spmi_device *dev,
const struct regmap_config *config);
struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, 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);
...@@ -330,6 +335,8 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, ...@@ -330,6 +335,8 @@ 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_spmi(struct spmi_device *dev,
const struct regmap_config *config);
struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, 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);
...@@ -374,10 +381,13 @@ int regmap_reinit_cache(struct regmap *map, ...@@ -374,10 +381,13 @@ int regmap_reinit_cache(struct regmap *map,
const struct regmap_config *config); const struct regmap_config *config);
struct regmap *dev_get_regmap(struct device *dev, const char *name); struct regmap *dev_get_regmap(struct device *dev, const char *name);
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val); int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val);
int regmap_raw_write(struct regmap *map, unsigned int reg, 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_multi_reg_write(struct regmap *map, struct reg_default *regs,
int num_regs);
int regmap_raw_write_async(struct regmap *map, unsigned int reg, int regmap_raw_write_async(struct regmap *map, unsigned int reg,
const void *val, size_t val_len); 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);
...@@ -387,9 +397,14 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, ...@@ -387,9 +397,14 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
size_t val_count); size_t val_count);
int regmap_update_bits(struct regmap *map, unsigned int reg, int regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val); unsigned int mask, unsigned int val);
int regmap_update_bits_async(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val);
int regmap_update_bits_check(struct regmap *map, unsigned int reg, 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_update_bits_check_async(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
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 regmap_async_complete(struct regmap *map);
bool regmap_can_raw_write(struct regmap *map); bool regmap_can_raw_write(struct regmap *map);
...@@ -425,11 +440,15 @@ bool regmap_reg_in_ranges(unsigned int reg, ...@@ -425,11 +440,15 @@ bool regmap_reg_in_ranges(unsigned int reg,
* @reg: Offset of the register within the regmap bank * @reg: Offset of the register within the regmap bank
* @lsb: lsb of the register field. * @lsb: lsb of the register field.
* @reg: msb of the register field. * @reg: msb of the register field.
* @id_size: port size if it has some ports
* @id_offset: address offset for each ports
*/ */
struct reg_field { struct reg_field {
unsigned int reg; unsigned int reg;
unsigned int lsb; unsigned int lsb;
unsigned int msb; unsigned int msb;
unsigned int id_size;
unsigned int id_offset;
}; };
#define REG_FIELD(_reg, _lsb, _msb) { \ #define REG_FIELD(_reg, _lsb, _msb) { \
...@@ -448,6 +467,15 @@ void devm_regmap_field_free(struct device *dev, struct regmap_field *field); ...@@ -448,6 +467,15 @@ void devm_regmap_field_free(struct device *dev, struct regmap_field *field);
int regmap_field_read(struct regmap_field *field, unsigned int *val); int regmap_field_read(struct regmap_field *field, unsigned int *val);
int regmap_field_write(struct regmap_field *field, unsigned int val); int regmap_field_write(struct regmap_field *field, unsigned int val);
int regmap_field_update_bits(struct regmap_field *field,
unsigned int mask, unsigned int val);
int regmap_fields_write(struct regmap_field *field, unsigned int id,
unsigned int val);
int regmap_fields_read(struct regmap_field *field, unsigned int id,
unsigned int *val);
int regmap_fields_update_bits(struct regmap_field *field, unsigned int id,
unsigned int mask, unsigned int val);
/** /**
* Description of an IRQ for the generic regmap irq_chip. * Description of an IRQ for the generic regmap irq_chip.
...@@ -527,6 +555,13 @@ static inline int regmap_write(struct regmap *map, unsigned int reg, ...@@ -527,6 +555,13 @@ static inline int regmap_write(struct regmap *map, unsigned int reg,
return -EINVAL; return -EINVAL;
} }
static inline int regmap_write_async(struct regmap *map, unsigned int reg,
unsigned int val)
{
WARN_ONCE(1, "regmap API is disabled");
return -EINVAL;
}
static inline int regmap_raw_write(struct regmap *map, unsigned int reg, static inline int regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len) const void *val, size_t val_len)
{ {
...@@ -576,6 +611,14 @@ static inline int regmap_update_bits(struct regmap *map, unsigned int reg, ...@@ -576,6 +611,14 @@ static inline int regmap_update_bits(struct regmap *map, unsigned int reg,
return -EINVAL; return -EINVAL;
} }
static inline int regmap_update_bits_async(struct regmap *map,
unsigned int reg,
unsigned int mask, unsigned int val)
{
WARN_ONCE(1, "regmap API is disabled");
return -EINVAL;
}
static inline int regmap_update_bits_check(struct regmap *map, static inline int regmap_update_bits_check(struct regmap *map,
unsigned int reg, unsigned int reg,
unsigned int mask, unsigned int val, unsigned int mask, unsigned int val,
...@@ -585,6 +628,16 @@ static inline int regmap_update_bits_check(struct regmap *map, ...@@ -585,6 +628,16 @@ static inline int regmap_update_bits_check(struct regmap *map,
return -EINVAL; return -EINVAL;
} }
static inline int regmap_update_bits_check_async(struct regmap *map,
unsigned int reg,
unsigned int mask,
unsigned int val,
bool *change)
{
WARN_ONCE(1, "regmap API is disabled");
return -EINVAL;
}
static inline int regmap_get_val_bytes(struct regmap *map) static inline int regmap_get_val_bytes(struct regmap *map)
{ {
WARN_ONCE(1, "regmap API is disabled"); WARN_ONCE(1, "regmap API is disabled");
......
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