Commit b7d845f8 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: (36 commits)
  mfd: Clearing events requires event registers to be writable for da9052-core
  mfd: Fix annotations in da9052-core
  gpiolib: Mark da9052 driver broken
  mfd: Declare da9052_regmap_config for the bus drivers
  MFD: DA9052/53 MFD core module add SPI support v2
  MFD: DA9052/53 MFD core module
  regmap: Add irq_base accessor to regmap_irq
  regmap: Allow drivers to reinitialise the register cache at runtime
  regmap: Add trace event for successful cache reads
  regmap: Allow regmap_update_bits() users to detect changes
  regmap: Report if we actually handled an interrupt in regmap-irq
  regmap: Fix rbtreee build when not using debugfs
  regmap: Provide debugfs dump of the rbtree cache data
  regmap: Do debugfs init before cache init
  regmap: Suppress noop writes in regmap_update_bits()
  regmap: Remove indexed cache type
  regmap: Drop check whether a register is readable in regcache_read
  regmap: Properly round cache_word_size
  regmap: Add support for 10/14 register formating
  regmap: Try cached read before checking if a hardware read is possible
  ...
parents 2943c833 0a92815d
......@@ -13,3 +13,6 @@ config REGMAP_I2C
config REGMAP_SPI
tristate
config REGMAP_IRQ
bool
obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o regcache-lzo.o
obj-$(CONFIG_REGMAP) += regmap.o regcache.o
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.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_IRQ) += regmap-irq.o
......@@ -74,6 +74,7 @@ struct regmap {
struct reg_default *reg_defaults;
const void *reg_defaults_raw;
void *cache;
bool cache_dirty;
};
struct regcache_ops {
......@@ -105,7 +106,7 @@ static inline void regmap_debugfs_exit(struct regmap *map) { }
#endif
/* regcache core declarations */
int regcache_init(struct regmap *map);
int regcache_init(struct regmap *map, const struct regmap_config *config);
void regcache_exit(struct regmap *map);
int regcache_read(struct regmap *map,
unsigned int reg, unsigned int *value);
......@@ -118,10 +119,7 @@ unsigned int regcache_get_val(const void *base, unsigned int idx,
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);
int regcache_insert_reg(struct regmap *map, unsigned int reg,
unsigned int val);
extern struct regcache_ops regcache_indexed_ops;
extern struct regcache_ops regcache_rbtree_ops;
extern struct regcache_ops regcache_lzo_ops;
......
/*
* Register cache access API - indexed caching support
*
* Copyright 2011 Wolfson Microelectronics plc
*
* Author: Dimitris Papastamos <dp@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 "internal.h"
static int regcache_indexed_read(struct regmap *map, unsigned int reg,
unsigned int *value)
{
int ret;
ret = regcache_lookup_reg(map, reg);
if (ret >= 0)
*value = map->reg_defaults[ret].def;
return ret;
}
static int regcache_indexed_write(struct regmap *map, unsigned int reg,
unsigned int value)
{
int ret;
ret = regcache_lookup_reg(map, reg);
if (ret < 0)
return regcache_insert_reg(map, reg, value);
map->reg_defaults[ret].def = value;
return 0;
}
static int regcache_indexed_sync(struct regmap *map)
{
unsigned int i;
int ret;
for (i = 0; i < map->num_reg_defaults; i++) {
ret = _regmap_write(map, map->reg_defaults[i].reg,
map->reg_defaults[i].def);
if (ret < 0)
return ret;
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
map->reg_defaults[i].reg,
map->reg_defaults[i].def);
}
return 0;
}
struct regcache_ops regcache_indexed_ops = {
.type = REGCACHE_INDEXED,
.name = "indexed",
.read = regcache_indexed_read,
.write = regcache_indexed_write,
.sync = regcache_indexed_sync
};
......@@ -15,6 +15,8 @@
#include "internal.h"
static int regcache_lzo_exit(struct regmap *map);
struct regcache_lzo_ctx {
void *wmem;
void *dst;
......@@ -27,7 +29,7 @@ struct regcache_lzo_ctx {
};
#define LZO_BLOCK_NUM 8
static int regcache_lzo_block_count(void)
static int regcache_lzo_block_count(struct regmap *map)
{
return LZO_BLOCK_NUM;
}
......@@ -106,19 +108,22 @@ static inline int regcache_lzo_get_blkindex(struct regmap *map,
unsigned int reg)
{
return (reg * map->cache_word_size) /
DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count());
DIV_ROUND_UP(map->cache_size_raw,
regcache_lzo_block_count(map));
}
static inline int regcache_lzo_get_blkpos(struct regmap *map,
unsigned int reg)
{
return reg % (DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count()) /
return reg % (DIV_ROUND_UP(map->cache_size_raw,
regcache_lzo_block_count(map)) /
map->cache_word_size);
}
static inline int regcache_lzo_get_blksize(struct regmap *map)
{
return DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count());
return DIV_ROUND_UP(map->cache_size_raw,
regcache_lzo_block_count(map));
}
static int regcache_lzo_init(struct regmap *map)
......@@ -131,7 +136,7 @@ static int regcache_lzo_init(struct regmap *map)
ret = 0;
blkcount = regcache_lzo_block_count();
blkcount = regcache_lzo_block_count(map);
map->cache = kzalloc(blkcount * sizeof *lzo_blocks,
GFP_KERNEL);
if (!map->cache)
......@@ -190,7 +195,7 @@ static int regcache_lzo_init(struct regmap *map)
return 0;
err:
regcache_exit(map);
regcache_lzo_exit(map);
return ret;
}
......@@ -203,7 +208,7 @@ static int regcache_lzo_exit(struct regmap *map)
if (!lzo_blocks)
return 0;
blkcount = regcache_lzo_block_count();
blkcount = regcache_lzo_block_count(map);
/*
* the pointer to the bitmap used for syncing the cache
* is shared amongst all lzo_blocks. Ensure it is freed
......@@ -351,7 +356,7 @@ static int regcache_lzo_sync(struct regmap *map)
}
struct regcache_ops regcache_lzo_ops = {
.type = REGCACHE_LZO,
.type = REGCACHE_COMPRESSED,
.name = "lzo",
.init = regcache_lzo_init,
.exit = regcache_lzo_exit,
......
......@@ -11,12 +11,15 @@
*/
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/rbtree.h>
#include <linux/seq_file.h>
#include "internal.h"
static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
unsigned int value);
static int regcache_rbtree_exit(struct regmap *map);
struct regcache_rbtree_node {
/* the actual rbtree node holding this block */
......@@ -124,6 +127,60 @@ static int regcache_rbtree_insert(struct rb_root *root,
return 1;
}
#ifdef CONFIG_DEBUG_FS
static int rbtree_show(struct seq_file *s, void *ignored)
{
struct regmap *map = s->private;
struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
struct regcache_rbtree_node *n;
struct rb_node *node;
unsigned int base, top;
int nodes = 0;
int registers = 0;
mutex_lock(&map->lock);
for (node = rb_first(&rbtree_ctx->root); node != NULL;
node = rb_next(node)) {
n = container_of(node, struct regcache_rbtree_node, node);
regcache_rbtree_get_base_top_reg(n, &base, &top);
seq_printf(s, "%x-%x (%d)\n", base, top, top - base + 1);
nodes++;
registers += top - base + 1;
}
seq_printf(s, "%d nodes, %d registers, average %d registers\n",
nodes, registers, registers / nodes);
mutex_unlock(&map->lock);
return 0;
}
static int rbtree_open(struct inode *inode, struct file *file)
{
return single_open(file, rbtree_show, inode->i_private);
}
static const struct file_operations rbtree_fops = {
.open = rbtree_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void rbtree_debugfs_init(struct regmap *map)
{
debugfs_create_file("rbtree", 0400, map->debugfs, map, &rbtree_fops);
}
#else
static void rbtree_debugfs_init(struct regmap *map)
{
}
#endif
static int regcache_rbtree_init(struct regmap *map)
{
struct regcache_rbtree_ctx *rbtree_ctx;
......@@ -146,10 +203,12 @@ static int regcache_rbtree_init(struct regmap *map)
goto err;
}
rbtree_debugfs_init(map);
return 0;
err:
regcache_exit(map);
regcache_rbtree_exit(map);
return ret;
}
......
......@@ -19,7 +19,6 @@
#include "internal.h"
static const struct regcache_ops *cache_types[] = {
&regcache_indexed_ops,
&regcache_rbtree_ops,
&regcache_lzo_ops,
};
......@@ -61,8 +60,10 @@ static int regcache_hw_init(struct regmap *map)
map->reg_defaults = kmalloc(count * sizeof(struct reg_default),
GFP_KERNEL);
if (!map->reg_defaults)
return -ENOMEM;
if (!map->reg_defaults) {
ret = -ENOMEM;
goto err_free;
}
/* fill the reg_defaults */
map->num_reg_defaults = count;
......@@ -77,9 +78,15 @@ static int regcache_hw_init(struct regmap *map)
}
return 0;
err_free:
if (map->cache_free)
kfree(map->reg_defaults_raw);
return ret;
}
int regcache_init(struct regmap *map)
int regcache_init(struct regmap *map, const struct regmap_config *config)
{
int ret;
int i;
......@@ -100,6 +107,12 @@ int regcache_init(struct regmap *map)
return -EINVAL;
}
map->num_reg_defaults = config->num_reg_defaults;
map->num_reg_defaults_raw = config->num_reg_defaults_raw;
map->reg_defaults_raw = config->reg_defaults_raw;
map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8);
map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw;
map->cache = NULL;
map->cache_ops = cache_types[i];
......@@ -112,10 +125,10 @@ int regcache_init(struct regmap *map)
* won't vanish from under us. We'll need to make
* a copy of it.
*/
if (map->reg_defaults) {
if (config->reg_defaults) {
if (!map->num_reg_defaults)
return -EINVAL;
tmp_buf = kmemdup(map->reg_defaults, map->num_reg_defaults *
tmp_buf = kmemdup(config->reg_defaults, map->num_reg_defaults *
sizeof(struct reg_default), GFP_KERNEL);
if (!tmp_buf)
return -ENOMEM;
......@@ -136,9 +149,18 @@ int regcache_init(struct regmap *map)
if (map->cache_ops->init) {
dev_dbg(map->dev, "Initializing %s cache\n",
map->cache_ops->name);
return map->cache_ops->init(map);
ret = map->cache_ops->init(map);
if (ret)
goto err_free;
}
return 0;
err_free:
kfree(map->reg_defaults);
if (map->cache_free)
kfree(map->reg_defaults_raw);
return ret;
}
void regcache_exit(struct regmap *map)
......@@ -171,16 +193,21 @@ void regcache_exit(struct regmap *map)
int regcache_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
int ret;
if (map->cache_type == REGCACHE_NONE)
return -ENOSYS;
BUG_ON(!map->cache_ops);
if (!regmap_readable(map, reg))
return -EIO;
if (!regmap_volatile(map, reg)) {
ret = map->cache_ops->read(map, reg, value);
if (!regmap_volatile(map, reg))
return map->cache_ops->read(map, reg, value);
if (ret == 0)
trace_regmap_reg_read_cache(map->dev, reg, *value);
return ret;
}
return -EINVAL;
}
......@@ -241,6 +268,8 @@ int regcache_sync(struct regmap *map)
map->cache_ops->name);
name = map->cache_ops->name;
trace_regcache_sync(map->dev, name, "start");
if (!map->cache_dirty)
goto out;
if (map->cache_ops->sync) {
ret = map->cache_ops->sync(map);
} else {
......@@ -290,6 +319,23 @@ void regcache_cache_only(struct regmap *map, bool enable)
}
EXPORT_SYMBOL_GPL(regcache_cache_only);
/**
* regcache_mark_dirty: Mark the register cache as dirty
*
* @map: map to mark
*
* Mark the register cache as dirty, for example due to the device
* having been powered down for suspend. If the cache is not marked
* as dirty then the cache sync will be suppressed.
*/
void regcache_mark_dirty(struct regmap *map)
{
mutex_lock(&map->lock);
map->cache_dirty = true;
mutex_unlock(&map->lock);
}
EXPORT_SYMBOL_GPL(regcache_mark_dirty);
/**
* regcache_cache_bypass: Put a register map into cache bypass mode
*
......@@ -381,22 +427,3 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg)
else
return -ENOENT;
}
int regcache_insert_reg(struct regmap *map, unsigned int reg,
unsigned int val)
{
void *tmp;
tmp = krealloc(map->reg_defaults,
(map->num_reg_defaults + 1) * sizeof(struct reg_default),
GFP_KERNEL);
if (!tmp)
return -ENOMEM;
map->reg_defaults = tmp;
map->num_reg_defaults++;
map->reg_defaults[map->num_reg_defaults - 1].reg = reg;
map->reg_defaults[map->num_reg_defaults - 1].def = val;
sort(map->reg_defaults, map->num_reg_defaults,
sizeof(struct reg_default), regcache_default_cmp, NULL);
return 0;
}
/*
* regmap based irq_chip
*
* 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 as
* published by the Free Software Foundation.
*/
#include <linux/export.h>
#include <linux/regmap.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include "internal.h"
struct regmap_irq_chip_data {
struct mutex lock;
struct regmap *map;
struct regmap_irq_chip *chip;
int irq_base;
void *status_reg_buf;
unsigned int *status_buf;
unsigned int *mask_buf;
unsigned int *mask_buf_def;
};
static inline const
struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,
int irq)
{
return &data->chip->irqs[irq - data->irq_base];
}
static void regmap_irq_lock(struct irq_data *data)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
mutex_lock(&d->lock);
}
static void regmap_irq_sync_unlock(struct irq_data *data)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
int i, ret;
/*
* If there's been a change in the mask write it back to the
* hardware. We rely on the use of the regmap core cache to
* suppress pointless writes.
*/
for (i = 0; i < d->chip->num_regs; i++) {
ret = regmap_update_bits(d->map, d->chip->mask_base + i,
d->mask_buf_def[i], d->mask_buf[i]);
if (ret != 0)
dev_err(d->map->dev, "Failed to sync masks in %x\n",
d->chip->mask_base + i);
}
mutex_unlock(&d->lock);
}
static void regmap_irq_enable(struct irq_data *data)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq);
d->mask_buf[irq_data->reg_offset] &= ~irq_data->mask;
}
static void regmap_irq_disable(struct irq_data *data)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq);
d->mask_buf[irq_data->reg_offset] |= irq_data->mask;
}
static struct irq_chip regmap_irq_chip = {
.name = "regmap",
.irq_bus_lock = regmap_irq_lock,
.irq_bus_sync_unlock = regmap_irq_sync_unlock,
.irq_disable = regmap_irq_disable,
.irq_enable = regmap_irq_enable,
};
static irqreturn_t regmap_irq_thread(int irq, void *d)
{
struct regmap_irq_chip_data *data = d;
struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
int ret, i;
u8 *buf8 = data->status_reg_buf;
u16 *buf16 = data->status_reg_buf;
u32 *buf32 = data->status_reg_buf;
bool handled = false;
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;
}
/*
* Ignore masked IRQs and ack if we need to; we ack early so
* there is no race between handling and acknowleding the
* interrupt. We assume that typically few of the interrupts
* will fire simultaneously so don't worry about overhead from
* doing a write per register.
*/
for (i = 0; i < data->chip->num_regs; i++) {
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;
}
data->status_buf[i] &= ~data->mask_buf[i];
if (data->status_buf[i] && chip->ack_base) {
ret = regmap_write(map, chip->ack_base + i,
data->status_buf[i]);
if (ret != 0)
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
chip->ack_base + i, ret);
}
}
for (i = 0; i < chip->num_irqs; i++) {
if (data->status_buf[chip->irqs[i].reg_offset] &
chip->irqs[i].mask) {
handle_nested_irq(data->irq_base + i);
handled = true;
}
}
if (handled)
return IRQ_HANDLED;
else
return IRQ_NONE;
}
/**
* regmap_add_irq_chip(): Use standard regmap IRQ controller handling
*
* map: The regmap for the device.
* irq: The IRQ the device uses to signal interrupts
* irq_flags: The IRQF_ flags to use for the primary interrupt.
* chip: Configuration for the interrupt controller.
* data: Runtime data structure for the controller, allocated on success
*
* Returns 0 on success or an errno on failure.
*
* In order for this to be efficient the chip really should use a
* register cache. The chip driver is responsible for restoring the
* register values used by the IRQ controller over suspend and resume.
*/
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
int irq_base, struct regmap_irq_chip *chip,
struct regmap_irq_chip_data **data)
{
struct regmap_irq_chip_data *d;
int cur_irq, i;
int ret = -ENOMEM;
irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
if (irq_base < 0) {
dev_warn(map->dev, "Failed to allocate IRQs: %d\n",
irq_base);
return irq_base;
}
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d)
return -ENOMEM;
d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
GFP_KERNEL);
if (!d->status_buf)
goto err_alloc;
d->status_reg_buf = kzalloc(map->format.val_bytes * chip->num_regs,
GFP_KERNEL);
if (!d->status_reg_buf)
goto err_alloc;
d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
GFP_KERNEL);
if (!d->mask_buf)
goto err_alloc;
d->mask_buf_def = kzalloc(sizeof(unsigned int) * chip->num_regs,
GFP_KERNEL);
if (!d->mask_buf_def)
goto err_alloc;
d->map = map;
d->chip = chip;
d->irq_base = irq_base;
mutex_init(&d->lock);
for (i = 0; i < chip->num_irqs; i++)
d->mask_buf_def[chip->irqs[i].reg_offset]
|= chip->irqs[i].mask;
/* Mask all the interrupts by default */
for (i = 0; i < chip->num_regs; i++) {
d->mask_buf[i] = d->mask_buf_def[i];
ret = regmap_write(map, chip->mask_base + i, d->mask_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
chip->mask_base + i, ret);
goto err_alloc;
}
}
/* Register them with genirq */
for (cur_irq = irq_base;
cur_irq < chip->num_irqs + irq_base;
cur_irq++) {
irq_set_chip_data(cur_irq, d);
irq_set_chip_and_handler(cur_irq, &regmap_irq_chip,
handle_edge_irq);
irq_set_nested_thread(cur_irq, 1);
/* ARM needs us to explicitly flag the IRQ as valid
* and will set them noprobe when we do so. */
#ifdef CONFIG_ARM
set_irq_flags(cur_irq, IRQF_VALID);
#else
irq_set_noprobe(cur_irq);
#endif
}
ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags,
chip->name, d);
if (ret != 0) {
dev_err(map->dev, "Failed to request IRQ %d: %d\n", irq, ret);
goto err_alloc;
}
return 0;
err_alloc:
kfree(d->mask_buf_def);
kfree(d->mask_buf);
kfree(d->status_reg_buf);
kfree(d->status_buf);
kfree(d);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_add_irq_chip);
/**
* regmap_del_irq_chip(): Stop interrupt handling for a regmap IRQ chip
*
* @irq: Primary IRQ for the device
* @d: regmap_irq_chip_data allocated by regmap_add_irq_chip()
*/
void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
{
if (!d)
return;
free_irq(irq, d);
kfree(d->mask_buf_def);
kfree(d->mask_buf);
kfree(d->status_reg_buf);
kfree(d->status_buf);
kfree(d);
}
EXPORT_SYMBOL_GPL(regmap_del_irq_chip);
/**
* regmap_irq_chip_get_base(): Retrieve interrupt base for a regmap IRQ chip
*
* Useful for drivers to request their own IRQs.
*
* @data: regmap_irq controller to operate on.
*/
int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data)
{
return data->irq_base;
}
EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base);
......@@ -64,6 +64,18 @@ bool regmap_precious(struct regmap *map, unsigned int reg)
return false;
}
static bool regmap_volatile_range(struct regmap *map, unsigned int reg,
unsigned int num)
{
unsigned int i;
for (i = 0; i < num; i++)
if (!regmap_volatile(map, reg + i))
return false;
return true;
}
static void regmap_format_4_12_write(struct regmap *map,
unsigned int reg, unsigned int val)
{
......@@ -78,6 +90,16 @@ static void regmap_format_7_9_write(struct regmap *map,
*out = cpu_to_be16((reg << 9) | val);
}
static void regmap_format_10_14_write(struct regmap *map,
unsigned int reg, unsigned int val)
{
u8 *out = map->work_buf;
out[2] = val;
out[1] = (val >> 8) | (reg << 6);
out[0] = reg >> 2;
}
static void regmap_format_8(void *buf, unsigned int val)
{
u8 *b = buf;
......@@ -127,7 +149,7 @@ struct regmap *regmap_init(struct device *dev,
int ret = -EINVAL;
if (!bus || !config)
return NULL;
goto err;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (map == NULL) {
......@@ -147,12 +169,6 @@ struct regmap *regmap_init(struct device *dev,
map->volatile_reg = config->volatile_reg;
map->precious_reg = config->precious_reg;
map->cache_type = config->cache_type;
map->reg_defaults = config->reg_defaults;
map->num_reg_defaults = config->num_reg_defaults;
map->num_reg_defaults_raw = config->num_reg_defaults_raw;
map->reg_defaults_raw = config->reg_defaults_raw;
map->cache_size_raw = (config->val_bits / 8) * config->num_reg_defaults_raw;
map->cache_word_size = config->val_bits / 8;
if (config->read_flag_mask || config->write_flag_mask) {
map->read_flag_mask = config->read_flag_mask;
......@@ -182,6 +198,16 @@ struct regmap *regmap_init(struct device *dev,
}
break;
case 10:
switch (config->val_bits) {
case 14:
map->format.format_write = regmap_format_10_14_write;
break;
default:
goto err_map;
}
break;
case 8:
map->format.format_reg = regmap_format_8;
break;
......@@ -215,14 +241,16 @@ struct regmap *regmap_init(struct device *dev,
goto err_map;
}
ret = regcache_init(map);
if (ret < 0)
goto err_map;
regmap_debugfs_init(map);
ret = regcache_init(map, config);
if (ret < 0)
goto err_free_workbuf;
return map;
err_free_workbuf:
kfree(map->work_buf);
err_map:
kfree(map);
err:
......@@ -230,6 +258,39 @@ struct regmap *regmap_init(struct device *dev,
}
EXPORT_SYMBOL_GPL(regmap_init);
/**
* regmap_reinit_cache(): Reinitialise the current register cache
*
* @map: Register map to operate on.
* @config: New configuration. Only the cache data will be used.
*
* Discard any existing register cache for the map and initialize a
* new cache. This can be used to restore the cache to defaults or to
* update the cache configuration to reflect runtime discovery of the
* hardware.
*/
int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
{
int ret;
mutex_lock(&map->lock);
regcache_exit(map);
map->max_register = config->max_register;
map->writeable_reg = config->writeable_reg;
map->readable_reg = config->readable_reg;
map->volatile_reg = config->volatile_reg;
map->precious_reg = config->precious_reg;
map->cache_type = config->cache_type;
ret = regcache_init(map, config);
mutex_unlock(&map->lock);
return ret;
}
/**
* regmap_exit(): Free a previously allocated register map
*/
......@@ -306,8 +367,10 @@ int _regmap_write(struct regmap *map, unsigned int reg,
ret = regcache_write(map, reg, val);
if (ret != 0)
return ret;
if (map->cache_only)
if (map->cache_only) {
map->cache_dirty = true;
return 0;
}
}
trace_regmap_reg_write(map->dev, reg, val);
......@@ -375,9 +438,11 @@ EXPORT_SYMBOL_GPL(regmap_write);
int regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len)
{
size_t val_count = val_len / map->format.val_bytes;
int ret;
WARN_ON(map->cache_type != REGCACHE_NONE);
WARN_ON(!regmap_volatile_range(map, reg, val_count) &&
map->cache_type != REGCACHE_NONE);
mutex_lock(&map->lock);
......@@ -422,15 +487,15 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
{
int ret;
if (!map->format.parse_val)
return -EINVAL;
if (!map->cache_bypass) {
ret = regcache_read(map, reg, val);
if (ret == 0)
return 0;
}
if (!map->format.parse_val)
return -EINVAL;
if (map->cache_only)
return -EBUSY;
......@@ -481,15 +546,11 @@ EXPORT_SYMBOL_GPL(regmap_read);
int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
size_t val_len)
{
size_t val_count = val_len / map->format.val_bytes;
int ret;
int i;
bool vol = true;
for (i = 0; i < val_len / map->format.val_bytes; i++)
if (!regmap_volatile(map, reg + i))
vol = false;
WARN_ON(!vol && map->cache_type != REGCACHE_NONE);
WARN_ON(!regmap_volatile_range(map, reg, val_count) &&
map->cache_type != REGCACHE_NONE);
mutex_lock(&map->lock);
......@@ -517,16 +578,11 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
{
int ret, i;
size_t val_bytes = map->format.val_bytes;
bool vol = true;
bool vol = regmap_volatile_range(map, reg, val_count);
if (!map->format.parse_val)
return -EINVAL;
/* Is this a block of volatile registers? */
for (i = 0; i < val_count; i++)
if (!regmap_volatile(map, reg + i))
vol = false;
if (vol || map->cache_type == REGCACHE_NONE) {
ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
if (ret != 0)
......@@ -546,40 +602,73 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
}
EXPORT_SYMBOL_GPL(regmap_bulk_read);
/**
* remap_update_bits: Perform a read/modify/write cycle on the register map
*
* @map: Register map to update
* @reg: Register to update
* @mask: Bitmask to change
* @val: New value for bitmask
*
* Returns zero for success, a negative number on error.
*/
int regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val)
static int _regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change)
{
int ret;
unsigned int tmp;
unsigned int tmp, orig;
mutex_lock(&map->lock);
ret = _regmap_read(map, reg, &tmp);
ret = _regmap_read(map, reg, &orig);
if (ret != 0)
goto out;
tmp &= ~mask;
tmp = orig & ~mask;
tmp |= val & mask;
ret = _regmap_write(map, reg, tmp);
if (tmp != orig) {
ret = _regmap_write(map, reg, tmp);
*change = true;
} else {
*change = false;
}
out:
mutex_unlock(&map->lock);
return ret;
}
/**
* regmap_update_bits: Perform a read/modify/write cycle on the register map
*
* @map: Register map to update
* @reg: Register to update
* @mask: Bitmask to change
* @val: New value for bitmask
*
* Returns zero for success, a negative number on error.
*/
int regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val)
{
bool change;
return _regmap_update_bits(map, reg, mask, val, &change);
}
EXPORT_SYMBOL_GPL(regmap_update_bits);
/**
* regmap_update_bits_check: Perform a read/modify/write cycle on the
* register map 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
*
* Returns zero for success, a negative number on error.
*/
int regmap_update_bits_check(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change)
{
return _regmap_update_bits(map, reg, mask, val, change);
}
EXPORT_SYMBOL_GPL(regmap_update_bits_check);
static int __init regmap_initcall(void)
{
regmap_debugfs_initcall();
......
......@@ -70,7 +70,7 @@ config GPIO_GENERIC
config GPIO_DA9052
tristate "Dialog DA9052 GPIO"
depends on PMIC_DA9052
depends on PMIC_DA9052 && BROKEN
help
Say yes here to enable the GPIO driver for the DA9052 chip.
......
......@@ -328,6 +328,34 @@ config PMIC_DA903X
individual components like LCD backlight, voltage regulators,
LEDs and battery-charger under the corresponding menus.
config PMIC_DA9052
bool
select MFD_CORE
config MFD_DA9052_SPI
bool "Support Dialog Semiconductor DA9052/53 PMIC variants with SPI"
select REGMAP_SPI
select REGMAP_IRQ
select PMIC_DA9052
depends on SPI_MASTER=y
help
Support for the Dialog Semiconductor DA9052 PMIC
when controlled using SPI. This driver provides common support
for accessing the device, additional drivers must be enabled in
order to use the functionality of the device.
config MFD_DA9052_I2C
bool "Support Dialog Semiconductor DA9052/53 PMIC variants with I2C"
select REGMAP_I2C
select REGMAP_IRQ
select PMIC_DA9052
depends on I2C=y
help
Support for the Dialog Semiconductor DA9052 PMIC
when controlled using I2C. This driver provides common support
for accessing the device, additional drivers must be enabled in
order to use the functionality of the device.
config PMIC_ADP5520
bool "Analog Devices ADP5520/01 MFD PMIC Core Support"
depends on I2C=y
......
......@@ -67,6 +67,11 @@ endif
obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o
obj-$(CONFIG_PMIC_DA903X) += da903x.o
obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
......
This diff is collapsed.
/*
* I2C access for DA9052 PMICs.
*
* Copyright(c) 2011 Dialog Semiconductor Ltd.
*
* Author: David Dajun Chen <dchen@diasemi.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/mfd/core.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/mfd/da9052/da9052.h>
#include <linux/mfd/da9052/reg.h>
static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
{
int reg_val, ret;
ret = regmap_read(da9052->regmap, DA9052_CONTROL_B_REG, &reg_val);
if (ret < 0)
return ret;
if (reg_val & DA9052_CONTROL_B_WRITEMODE) {
reg_val &= ~DA9052_CONTROL_B_WRITEMODE;
ret = regmap_write(da9052->regmap, DA9052_CONTROL_B_REG,
reg_val);
if (ret < 0)
return ret;
}
return 0;
}
static int __devinit da9052_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct da9052 *da9052;
int ret;
da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL);
if (!da9052)
return -ENOMEM;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_info(&client->dev, "Error in %s:i2c_check_functionality\n",
__func__);
ret = -ENODEV;
goto err;
}
da9052->dev = &client->dev;
da9052->chip_irq = client->irq;
i2c_set_clientdata(client, da9052);
da9052->regmap = regmap_init_i2c(client, &da9052_regmap_config);
if (IS_ERR(da9052->regmap)) {
ret = PTR_ERR(da9052->regmap);
dev_err(&client->dev, "Failed to allocate register map: %d\n",
ret);
goto err;
}
ret = da9052_i2c_enable_multiwrite(da9052);
if (ret < 0)
goto err;
ret = da9052_device_init(da9052, id->driver_data);
if (ret != 0)
goto err;
return 0;
err:
kfree(da9052);
return ret;
}
static int da9052_i2c_remove(struct i2c_client *client)
{
struct da9052 *da9052 = i2c_get_clientdata(client);
da9052_device_exit(da9052);
kfree(da9052);
return 0;
}
static struct i2c_device_id da9052_i2c_id[] = {
{"da9052", DA9052},
{"da9053-aa", DA9053_AA},
{"da9053-ba", DA9053_BA},
{"da9053-bb", DA9053_BB},
{}
};
static struct i2c_driver da9052_i2c_driver = {
.probe = da9052_i2c_probe,
.remove = da9052_i2c_remove,
.id_table = da9052_i2c_id,
.driver = {
.name = "da9052",
.owner = THIS_MODULE,
},
};
static int __init da9052_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&da9052_i2c_driver);
if (ret != 0) {
pr_err("DA9052 I2C registration failed %d\n", ret);
return ret;
}
return 0;
}
subsys_initcall(da9052_i2c_init);
static void __exit da9052_i2c_exit(void)
{
i2c_del_driver(&da9052_i2c_driver);
}
module_exit(da9052_i2c_exit);
MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
MODULE_DESCRIPTION("I2C driver for Dialog DA9052 PMIC");
MODULE_LICENSE("GPL");
/*
* SPI access for Dialog DA9052 PMICs.
*
* Copyright(c) 2011 Dialog Semiconductor Ltd.
*
* Author: David Dajun Chen <dchen@diasemi.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/mfd/core.h>
#include <linux/spi/spi.h>
#include <linux/err.h>
#include <linux/mfd/da9052/da9052.h>
static int da9052_spi_probe(struct spi_device *spi)
{
int ret;
const struct spi_device_id *id = spi_get_device_id(spi);
struct da9052 *da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL);
if (!da9052)
return -ENOMEM;
spi->mode = SPI_MODE_0 | SPI_CPOL;
spi->bits_per_word = 8;
spi_setup(spi);
da9052->dev = &spi->dev;
da9052->chip_irq = spi->irq;
dev_set_drvdata(&spi->dev, da9052);
da9052_regmap_config.read_flag_mask = 1;
da9052_regmap_config.write_flag_mask = 0;
da9052->regmap = regmap_init_spi(spi, &da9052_regmap_config);
if (IS_ERR(da9052->regmap)) {
ret = PTR_ERR(da9052->regmap);
dev_err(&spi->dev, "Failed to allocate register map: %d\n",
ret);
goto err;
}
ret = da9052_device_init(da9052, id->driver_data);
if (ret != 0)
goto err;
return 0;
err:
kfree(da9052);
return ret;
}
static int da9052_spi_remove(struct spi_device *spi)
{
struct da9052 *da9052 = dev_get_drvdata(&spi->dev);
da9052_device_exit(da9052);
kfree(da9052);
return 0;
}
static struct spi_device_id da9052_spi_id[] = {
{"da9052", DA9052},
{"da9053-aa", DA9053_AA},
{"da9053-ba", DA9053_BA},
{"da9053-bb", DA9053_BB},
{}
};
static struct spi_driver da9052_spi_driver = {
.probe = da9052_spi_probe,
.remove = __devexit_p(da9052_spi_remove),
.id_table = da9052_spi_id,
.driver = {
.name = "da9052",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
};
static int __init da9052_spi_init(void)
{
int ret;
ret = spi_register_driver(&da9052_spi_driver);
if (ret != 0) {
pr_err("Failed to register DA9052 SPI driver, %d\n", ret);
return ret;
}
return 0;
}
subsys_initcall(da9052_spi_init);
static void __exit da9052_spi_exit(void)
{
spi_unregister_driver(&da9052_spi_driver);
}
module_exit(da9052_spi_exit);
MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
MODULE_DESCRIPTION("SPI driver for Dialog DA9052 PMIC");
MODULE_LICENSE("GPL");
/*
* da9052 declarations for DA9052 PMICs.
*
* Copyright(c) 2011 Dialog Semiconductor Ltd.
*
* Author: David Dajun Chen <dchen@diasemi.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __MFD_DA9052_DA9052_H
#define __MFD_DA9052_DA9052_H
#include <linux/interrupt.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/list.h>
#include <linux/mfd/core.h>
#include <linux/mfd/da9052/reg.h>
#define DA9052_IRQ_DCIN 0
#define DA9052_IRQ_VBUS 1
#define DA9052_IRQ_DCINREM 2
#define DA9052_IRQ_VBUSREM 3
#define DA9052_IRQ_VDDLOW 4
#define DA9052_IRQ_ALARM 5
#define DA9052_IRQ_SEQRDY 6
#define DA9052_IRQ_COMP1V2 7
#define DA9052_IRQ_NONKEY 8
#define DA9052_IRQ_IDFLOAT 9
#define DA9052_IRQ_IDGND 10
#define DA9052_IRQ_CHGEND 11
#define DA9052_IRQ_TBAT 12
#define DA9052_IRQ_ADC_EOM 13
#define DA9052_IRQ_PENDOWN 14
#define DA9052_IRQ_TSIREADY 15
#define DA9052_IRQ_GPI0 16
#define DA9052_IRQ_GPI1 17
#define DA9052_IRQ_GPI2 18
#define DA9052_IRQ_GPI3 19
#define DA9052_IRQ_GPI4 20
#define DA9052_IRQ_GPI5 21
#define DA9052_IRQ_GPI6 22
#define DA9052_IRQ_GPI7 23
#define DA9052_IRQ_GPI8 24
#define DA9052_IRQ_GPI9 25
#define DA9052_IRQ_GPI10 26
#define DA9052_IRQ_GPI11 27
#define DA9052_IRQ_GPI12 28
#define DA9052_IRQ_GPI13 29
#define DA9052_IRQ_GPI14 30
#define DA9052_IRQ_GPI15 31
enum da9052_chip_id {
DA9052,
DA9053_AA,
DA9053_BA,
DA9053_BB,
};
struct da9052_pdata;
struct da9052 {
struct mutex io_lock;
struct device *dev;
struct regmap *regmap;
int irq_base;
u8 chip_id;
int chip_irq;
};
/* Device I/O API */
static inline int da9052_reg_read(struct da9052 *da9052, unsigned char reg)
{
int val, ret;
ret = regmap_read(da9052->regmap, reg, &val);
if (ret < 0)
return ret;
return val;
}
static inline int da9052_reg_write(struct da9052 *da9052, unsigned char reg,
unsigned char val)
{
return regmap_write(da9052->regmap, reg, val);
}
static inline int da9052_group_read(struct da9052 *da9052, unsigned char reg,
unsigned reg_cnt, unsigned char *val)
{
return regmap_bulk_read(da9052->regmap, reg, val, reg_cnt);
}
static inline int da9052_group_write(struct da9052 *da9052, unsigned char reg,
unsigned reg_cnt, unsigned char *val)
{
return regmap_raw_write(da9052->regmap, reg, val, reg_cnt);
}
static inline int da9052_reg_update(struct da9052 *da9052, unsigned char reg,
unsigned char bit_mask,
unsigned char reg_val)
{
return regmap_update_bits(da9052->regmap, reg, bit_mask, reg_val);
}
int da9052_device_init(struct da9052 *da9052, u8 chip_id);
void da9052_device_exit(struct da9052 *da9052);
extern struct regmap_config da9052_regmap_config;
#endif /* __MFD_DA9052_DA9052_H */
/*
* Platform data declarations for DA9052 PMICs.
*
* Copyright(c) 2011 Dialog Semiconductor Ltd.
*
* Author: David Dajun Chen <dchen@diasemi.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __MFD_DA9052_PDATA_H__
#define __MFD_DA9052_PDATA_H__
#define DA9052_MAX_REGULATORS 14
struct da9052;
struct da9052_pdata {
struct led_platform_data *pled;
int (*init) (struct da9052 *da9052);
int irq_base;
int gpio_base;
int use_for_apm;
struct regulator_init_data *regulators[DA9052_MAX_REGULATORS];
};
#endif
This diff is collapsed.
......@@ -23,9 +23,8 @@ struct spi_device;
/* An enum of all the supported cache types */
enum regcache_type {
REGCACHE_NONE,
REGCACHE_INDEXED,
REGCACHE_RBTREE,
REGCACHE_LZO
REGCACHE_COMPRESSED
};
/**
......@@ -83,7 +82,7 @@ struct regmap_config {
bool (*precious_reg)(struct device *dev, unsigned int reg);
unsigned int max_register;
struct reg_default *reg_defaults;
const struct reg_default *reg_defaults;
unsigned int num_reg_defaults;
enum regcache_type cache_type;
const void *reg_defaults_raw;
......@@ -129,6 +128,8 @@ struct regmap *regmap_init_spi(struct spi_device *dev,
const struct regmap_config *config);
void regmap_exit(struct regmap *map);
int regmap_reinit_cache(struct regmap *map,
const struct regmap_config *config);
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
int regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len);
......@@ -139,9 +140,61 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
size_t val_count);
int regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val);
int regmap_update_bits_check(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change);
int regcache_sync(struct regmap *map);
void regcache_cache_only(struct regmap *map, bool enable);
void regcache_cache_bypass(struct regmap *map, bool enable);
void regcache_mark_dirty(struct regmap *map);
/**
* Description of an IRQ for the generic regmap irq_chip.
*
* @reg_offset: Offset of the status/mask register within the bank
* @mask: Mask used to flag/control the register.
*/
struct regmap_irq {
unsigned int reg_offset;
unsigned int mask;
};
/**
* Description of a generic regmap irq_chip. This is not intended to
* handle every possible interrupt controller, but it should handle a
* substantial proportion of those that are found in the wild.
*
* @name: Descriptive name for IRQ controller.
*
* @status_base: Base status register address.
* @mask_base: Base mask register address.
* @ack_base: Base ack address. If zero then the chip is clear on read.
*
* @num_regs: Number of registers in each control bank.
* @irqs: Descriptors for individual IRQs. Interrupt numbers are
* assigned based on the index in the array of the interrupt.
* @num_irqs: Number of descriptors.
*/
struct regmap_irq_chip {
const char *name;
unsigned int status_base;
unsigned int mask_base;
unsigned int ack_base;
int num_regs;
const struct regmap_irq *irqs;
int num_irqs;
};
struct regmap_irq_chip_data;
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
int irq_base, struct regmap_irq_chip *chip,
struct regmap_irq_chip_data **data);
void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data);
int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data);
#endif
......@@ -55,6 +55,15 @@ DEFINE_EVENT(regmap_reg, regmap_reg_read,
);
DEFINE_EVENT(regmap_reg, regmap_reg_read_cache,
TP_PROTO(struct device *dev, unsigned int reg,
unsigned int val),
TP_ARGS(dev, reg, val)
);
DECLARE_EVENT_CLASS(regmap_block,
TP_PROTO(struct device *dev, unsigned int reg, int count),
......
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