Skip to content

Commit

Permalink
Merge tag 'regmap-v3.13' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/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()
  • Loading branch information
torvalds committed Nov 12, 2013
2 parents 7e238a2 + 04bc9ac commit 4fc9ed3
Show file tree
Hide file tree
Showing 10 changed files with 560 additions and 58 deletions.
5 changes: 4 additions & 1 deletion drivers/base/regmap/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# subsystems should select the appropriate symbols.

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_DECOMPRESS
select IRQ_DOMAIN if REGMAP_IRQ
Expand All @@ -15,6 +15,9 @@ config REGMAP_I2C
config REGMAP_SPI
tristate

config REGMAP_SPMI
tristate

config REGMAP_MMIO
tristate

Expand Down
1 change: 1 addition & 0 deletions drivers/base/regmap/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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_REGMAP_I2C) += regmap-i2c.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_IRQ) += regmap-irq.o
8 changes: 6 additions & 2 deletions drivers/base/regmap/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ struct regmap_format {

struct regmap_async {
struct list_head list;
struct work_struct cleanup;
struct regmap *map;
void *work_buf;
};
Expand All @@ -64,9 +63,11 @@ struct regmap {
void *bus_context;
const char *name;

bool async;
spinlock_t async_lock;
wait_queue_head_t async_waitq;
struct list_head async_list;
struct list_head async_free;
int async_ret;

#ifdef CONFIG_DEBUG_FS
Expand Down Expand Up @@ -179,6 +180,9 @@ struct regmap_field {
/* lsb */
unsigned int shift;
unsigned int reg;

unsigned int id_size;
unsigned int id_offset;
};

#ifdef CONFIG_DEBUG_FS
Expand Down Expand Up @@ -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 _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);

Expand Down
19 changes: 15 additions & 4 deletions drivers/base/regmap/regcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ int regcache_sync(struct regmap *map)
if (!map->cache_dirty)
goto out;

map->async = true;

/* Apply any patch first */
map->cache_bypass = 1;
for (i = 0; i < map->patch_regs; i++) {
Expand All @@ -332,11 +334,15 @@ int regcache_sync(struct regmap *map)
map->cache_dirty = false;

out:
trace_regcache_sync(map->dev, name, "stop");
/* Restore the bypass state */
map->async = false;
map->cache_bypass = bypass;
map->unlock(map->lock_arg);

regmap_async_complete(map);

trace_regcache_sync(map->dev, name, "stop");

return ret;
}
EXPORT_SYMBOL_GPL(regcache_sync);
Expand Down Expand Up @@ -375,17 +381,23 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
if (!map->cache_dirty)
goto out;

map->async = true;

if (map->cache_ops->sync)
ret = map->cache_ops->sync(map, min, max);
else
ret = regcache_default_sync(map, min, max);

out:
trace_regcache_sync(map->dev, name, "stop region");
/* Restore the bypass state */
map->cache_bypass = bypass;
map->async = false;
map->unlock(map->lock_arg);

regmap_async_complete(map);

trace_regcache_sync(map->dev, name, "stop region");

return ret;
}
EXPORT_SYMBOL_GPL(regcache_sync_region);
Expand Down Expand Up @@ -631,8 +643,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,

map->cache_bypass = 1;

ret = _regmap_raw_write(map, base, *data, count * val_bytes,
false);
ret = _regmap_raw_write(map, base, *data, count * val_bytes);

map->cache_bypass = 0;

Expand Down
57 changes: 52 additions & 5 deletions drivers/base/regmap/regmap-debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/list.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 LIST_HEAD(regmap_debugfs_early_list);
static DEFINE_MUTEX(regmap_debugfs_early_lock);

/* Calculate the length of a fixed format */
static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size)
Expand Down Expand Up @@ -465,6 +474,20 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
struct rb_node *next;
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);
mutex_init(&map->cache_lock);

Expand Down Expand Up @@ -519,18 +542,42 @@ void regmap_debugfs_init(struct regmap *map, const char *name)

void regmap_debugfs_exit(struct regmap *map)
{
debugfs_remove_recursive(map->debugfs);
mutex_lock(&map->cache_lock);
regmap_debugfs_free_dump_cache(map);
mutex_unlock(&map->cache_lock);
kfree(map->debugfs_name);
if (map->debugfs) {
debugfs_remove_recursive(map->debugfs);
mutex_lock(&map->cache_lock);
regmap_debugfs_free_dump_cache(map);
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)
{
struct regmap_debugfs_node *node, *tmp;

regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
if (!regmap_debugfs_root) {
pr_warn("regmap: Failed to create debugfs root\n");
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);
}
16 changes: 16 additions & 0 deletions drivers/base/regmap/regmap-irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
"Failed to sync wakes in %x: %d\n",
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)
Expand Down
3 changes: 2 additions & 1 deletion drivers/base/regmap/regmap-spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ static int regmap_spi_async_write(void *context,

spi_message_init(&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.context = async;
Expand Down
90 changes: 90 additions & 0 deletions drivers/base/regmap/regmap-spmi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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 <[email protected]>
*
* 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");
Loading

0 comments on commit 4fc9ed3

Please sign in to comment.