regmap: Updates for 3.5
A surprisingly large series of updates for regmap this time, mostly due to all the work Stephen Warren has done to add support for MMIO buses. This wasn't really the target for the framework but it turns out that there's a reasonable number of cases where it's very helpful to use the register cache support to allow the register map to remain available while the device is suspended. - A MMIO bus implementation, contributed by Stephen Warren. Currently this is limited to 32 bit systems and native endian registers. - Support for naming register maps, mainly intended for MMIO devices with multiple register banks. This was also contributed by Stephen Warren. - Support for register striding, again contributed by Stephen Warren and mainly intended for use with MMIO as typically the registers will be a fixed size but byte addressed. - irqdomain support for the generic regmap irq_chip, including support for dynamically allocate interrupt numbers. - A function dev_get_regmap() which allows frameworks using regmap to obtain the regmap for a device from the struct device, making life a little simpler for them. - Updates to regmap-irq to support more chips (contributed by Graeme Gregory) and to use irqdomains. - Support for devices with 24 bit register addresses. The striding support collided with all the topic branches so the branches look a bit messy and eventually I just gave up. There's also the TI Palmas driver and a couple of other isolated MFD patches that all depend on new regmap features so are being merged here. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJPtnhRAAoJEBus8iNuMP3d5yUQAJOHY+V9czTi87j6wRSOPJp+ JhTIpn5dgpe38gScGVxNXW9kvb+W/m9iVySvkWY4r79tGED8YjYQbof53mdilxww JhinDpUU4bSysgMCHJiVonOYwfeehBCuQhyk1DG4Fp7v8sc8sCFXo5HpdNMNDU+G QC0Dlo5kqceJY/miOdiprjKGcOLwZYtXb/c++CCfYbrQlg7+XdyDUcegQrDn3Tg9 ZIzl/cTuZJQ8FhewVR19ScmMA3pHaXXBWxuvdscEVPLIHoFi0Vbc/qFUUddEfA6Q g7wa5/hCOUnBTmCPdlt4fveXQllZSq5hj6cvLRuMtUFX9A1nqcYNUroLAnoJ9QVO 6JrCmvW6A8GxgMklf1aFvcOf3xoM0yQaTOy/bFzO52LHFV28bxcwG2572gLiMmn3 u8+z4voWy36kLDyUzwNzJ4vI1GjuyedPWSgXOX8D8S1UV1I0DeTrBEQEiVutJCP5 KsuGaYtmrXF20H7KnSufDYLTXMU/7hLMZhroNcglRKaYMf34A/kNGYHLb00JhrnC rWqPjGA+Wplj8IJXrZcALZqBODGcBL/jZR0+Uc1K03rnm5G/8O0IZqbwFIam0c/+ UDj/IrvoYliI2U3FdRS+MO+lN3+SK7OVvwP1qYROcmCsKt4R3FLLaPzLRXizrfHk JmlwCGWFIHu7jDtnUA6W =pjN4 -----END PGP SIGNATURE----- Merge tag 'regmap-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap Pull regmap updates from Mark Brown: "A surprisingly large series of updates for regmap this time, mostly due to all the work Stephen Warren has done to add support for MMIO buses. This wasn't really the target for the framework but it turns out that there's a reasonable number of cases where it's very helpful to use the register cache support to allow the register map to remain available while the device is suspended. - A MMIO bus implementation, contributed by Stephen Warren. Currently this is limited to 32 bit systems and native endian registers. - Support for naming register maps, mainly intended for MMIO devices with multiple register banks. This was also contributed by Stephen Warren. - Support for register striding, again contributed by Stephen Warren and mainly intended for use with MMIO as typically the registers will be a fixed size but byte addressed. - irqdomain support for the generic regmap irq_chip, including support for dynamically allocate interrupt numbers. - A function dev_get_regmap() which allows frameworks using regmap to obtain the regmap for a device from the struct device, making life a little simpler for them. - Updates to regmap-irq to support more chips (contributed by Graeme Gregory) and to use irqdomains. - Support for devices with 24 bit register addresses. The striding support collided with all the topic branches so the branches look a bit messy and eventually I just gave up. There's also the TI Palmas driver and a couple of other isolated MFD patches that all depend on new regmap features so are being merged here." * tag 'regmap-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: (24 commits) mfd: palmas PMIC device support Kconfig mfd: palmas PMIC device support regmap: Fix typo in IRQ register striding mfd: wm8994: Update to fully use irq_domain regmap: add support for non contiguous status to regmap-irq regmap: Convert regmap_irq to use irq_domain regmap: Pass back the allocated regmap IRQ controller data mfd: da9052: Fix genirq abuse regmap: Implement dev_get_regmap() regmap: Devices using format_write don't support bulk operations regmap: Converts group operation into single read write operations regmap: Cache single values read from the chip regmap: fix compile errors in regmap-irq.c due to stride changes regmap: implement register striding regmap: fix compilation when !CONFIG_DEBUG_FS regmap: allow regmap instances to be named regmap: validate regmap_raw_read/write val_len regmap: mmio: remove some error checks now in the core regmap: mmio: convert some error returns to BUG() regmap: add MMIO bus support ...
This commit is contained in:
Коммит
ae82a82820
|
@ -14,5 +14,8 @@ config REGMAP_I2C
|
|||
config REGMAP_SPI
|
||||
tristate
|
||||
|
||||
config REGMAP_MMIO
|
||||
tristate
|
||||
|
||||
config REGMAP_IRQ
|
||||
bool
|
||||
|
|
|
@ -3,4 +3,5 @@ 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_MMIO) += regmap-mmio.o
|
||||
obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o
|
||||
|
|
|
@ -26,21 +26,30 @@ struct regmap_format {
|
|||
size_t val_bytes;
|
||||
void (*format_write)(struct regmap *map,
|
||||
unsigned int reg, unsigned int val);
|
||||
void (*format_reg)(void *buf, unsigned int reg);
|
||||
void (*format_val)(void *buf, unsigned int val);
|
||||
void (*format_reg)(void *buf, unsigned int reg, unsigned int shift);
|
||||
void (*format_val)(void *buf, unsigned int val, unsigned int shift);
|
||||
unsigned int (*parse_val)(void *buf);
|
||||
};
|
||||
|
||||
typedef void (*regmap_lock)(struct regmap *map);
|
||||
typedef void (*regmap_unlock)(struct regmap *map);
|
||||
|
||||
struct regmap {
|
||||
struct mutex lock;
|
||||
struct mutex mutex;
|
||||
spinlock_t spinlock;
|
||||
regmap_lock lock;
|
||||
regmap_unlock unlock;
|
||||
|
||||
struct device *dev; /* Device we do I/O on */
|
||||
void *work_buf; /* Scratch buffer used to format I/O */
|
||||
struct regmap_format format; /* Buffer format */
|
||||
const struct regmap_bus *bus;
|
||||
void *bus_context;
|
||||
const char *name;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs;
|
||||
const char *debugfs_name;
|
||||
#endif
|
||||
|
||||
unsigned int max_register;
|
||||
|
@ -52,6 +61,10 @@ struct regmap {
|
|||
u8 read_flag_mask;
|
||||
u8 write_flag_mask;
|
||||
|
||||
/* number of bits to (left) shift the reg value when formatting*/
|
||||
int reg_shift;
|
||||
int reg_stride;
|
||||
|
||||
/* regcache specific members */
|
||||
const struct regcache_ops *cache_ops;
|
||||
enum regcache_type cache_type;
|
||||
|
@ -79,6 +92,9 @@ struct regmap {
|
|||
|
||||
struct reg_default *patch;
|
||||
int patch_regs;
|
||||
|
||||
/* if set, converts bulk rw to single rw */
|
||||
bool use_single_rw;
|
||||
};
|
||||
|
||||
struct regcache_ops {
|
||||
|
@ -101,11 +117,11 @@ int _regmap_write(struct regmap *map, unsigned int reg,
|
|||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
extern void regmap_debugfs_initcall(void);
|
||||
extern void regmap_debugfs_init(struct regmap *map);
|
||||
extern void regmap_debugfs_init(struct regmap *map, const char *name);
|
||||
extern void regmap_debugfs_exit(struct regmap *map);
|
||||
#else
|
||||
static inline void regmap_debugfs_initcall(void) { }
|
||||
static inline void regmap_debugfs_init(struct regmap *map) { }
|
||||
static inline void regmap_debugfs_init(struct regmap *map, const char *name) { }
|
||||
static inline void regmap_debugfs_exit(struct regmap *map) { }
|
||||
#endif
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ static int regcache_lzo_decompress_cache_block(struct regmap *map,
|
|||
static inline int regcache_lzo_get_blkindex(struct regmap *map,
|
||||
unsigned int reg)
|
||||
{
|
||||
return (reg * map->cache_word_size) /
|
||||
return ((reg / map->reg_stride) * map->cache_word_size) /
|
||||
DIV_ROUND_UP(map->cache_size_raw,
|
||||
regcache_lzo_block_count(map));
|
||||
}
|
||||
|
@ -116,9 +116,10 @@ static inline int regcache_lzo_get_blkindex(struct regmap *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(map)) /
|
||||
map->cache_word_size);
|
||||
return (reg / map->reg_stride) %
|
||||
(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)
|
||||
|
@ -322,7 +323,7 @@ static int regcache_lzo_write(struct regmap *map,
|
|||
}
|
||||
|
||||
/* set the bit so we know we have to sync this register */
|
||||
set_bit(reg, lzo_block->sync_bmp);
|
||||
set_bit(reg / map->reg_stride, lzo_block->sync_bmp);
|
||||
kfree(tmp_dst);
|
||||
kfree(lzo_block->src);
|
||||
return 0;
|
||||
|
|
|
@ -39,11 +39,12 @@ struct regcache_rbtree_ctx {
|
|||
};
|
||||
|
||||
static inline void regcache_rbtree_get_base_top_reg(
|
||||
struct regmap *map,
|
||||
struct regcache_rbtree_node *rbnode,
|
||||
unsigned int *base, unsigned int *top)
|
||||
{
|
||||
*base = rbnode->base_reg;
|
||||
*top = rbnode->base_reg + rbnode->blklen - 1;
|
||||
*top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride);
|
||||
}
|
||||
|
||||
static unsigned int regcache_rbtree_get_register(
|
||||
|
@ -70,7 +71,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
|
|||
|
||||
rbnode = rbtree_ctx->cached_rbnode;
|
||||
if (rbnode) {
|
||||
regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
|
||||
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
|
||||
&top_reg);
|
||||
if (reg >= base_reg && reg <= top_reg)
|
||||
return rbnode;
|
||||
}
|
||||
|
@ -78,7 +80,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
|
|||
node = rbtree_ctx->root.rb_node;
|
||||
while (node) {
|
||||
rbnode = container_of(node, struct regcache_rbtree_node, node);
|
||||
regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
|
||||
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
|
||||
&top_reg);
|
||||
if (reg >= base_reg && reg <= top_reg) {
|
||||
rbtree_ctx->cached_rbnode = rbnode;
|
||||
return rbnode;
|
||||
|
@ -92,7 +95,7 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int regcache_rbtree_insert(struct rb_root *root,
|
||||
static int regcache_rbtree_insert(struct regmap *map, struct rb_root *root,
|
||||
struct regcache_rbtree_node *rbnode)
|
||||
{
|
||||
struct rb_node **new, *parent;
|
||||
|
@ -106,7 +109,7 @@ static int regcache_rbtree_insert(struct rb_root *root,
|
|||
rbnode_tmp = container_of(*new, struct regcache_rbtree_node,
|
||||
node);
|
||||
/* base and top registers of the current rbnode */
|
||||
regcache_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp,
|
||||
regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp,
|
||||
&top_reg_tmp);
|
||||
/* base register of the rbnode to be added */
|
||||
base_reg = rbnode->base_reg;
|
||||
|
@ -138,19 +141,20 @@ static int rbtree_show(struct seq_file *s, void *ignored)
|
|||
unsigned int base, top;
|
||||
int nodes = 0;
|
||||
int registers = 0;
|
||||
int average;
|
||||
int this_registers, average;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
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);
|
||||
regcache_rbtree_get_base_top_reg(map, n, &base, &top);
|
||||
this_registers = ((top - base) / map->reg_stride) + 1;
|
||||
seq_printf(s, "%x-%x (%d)\n", base, top, this_registers);
|
||||
|
||||
nodes++;
|
||||
registers += top - base + 1;
|
||||
registers += this_registers;
|
||||
}
|
||||
|
||||
if (nodes)
|
||||
|
@ -161,7 +165,7 @@ static int rbtree_show(struct seq_file *s, void *ignored)
|
|||
seq_printf(s, "%d nodes, %d registers, average %d registers\n",
|
||||
nodes, registers, average);
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -255,7 +259,7 @@ static int regcache_rbtree_read(struct regmap *map,
|
|||
|
||||
rbnode = regcache_rbtree_lookup(map, reg);
|
||||
if (rbnode) {
|
||||
reg_tmp = reg - rbnode->base_reg;
|
||||
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
|
||||
*value = regcache_rbtree_get_register(rbnode, reg_tmp,
|
||||
map->cache_word_size);
|
||||
} else {
|
||||
|
@ -310,7 +314,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
|
|||
*/
|
||||
rbnode = regcache_rbtree_lookup(map, reg);
|
||||
if (rbnode) {
|
||||
reg_tmp = reg - rbnode->base_reg;
|
||||
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
|
||||
val = regcache_rbtree_get_register(rbnode, reg_tmp,
|
||||
map->cache_word_size);
|
||||
if (val == value)
|
||||
|
@ -321,13 +325,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
|
|||
/* look for an adjacent register to the one we are about to add */
|
||||
for (node = rb_first(&rbtree_ctx->root); node;
|
||||
node = rb_next(node)) {
|
||||
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node);
|
||||
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
|
||||
node);
|
||||
for (i = 0; i < rbnode_tmp->blklen; i++) {
|
||||
reg_tmp = rbnode_tmp->base_reg + i;
|
||||
if (abs(reg_tmp - reg) != 1)
|
||||
reg_tmp = rbnode_tmp->base_reg +
|
||||
(i * map->reg_stride);
|
||||
if (abs(reg_tmp - reg) != map->reg_stride)
|
||||
continue;
|
||||
/* decide where in the block to place our register */
|
||||
if (reg_tmp + 1 == reg)
|
||||
if (reg_tmp + map->reg_stride == reg)
|
||||
pos = i + 1;
|
||||
else
|
||||
pos = i;
|
||||
|
@ -357,7 +363,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
|
|||
return -ENOMEM;
|
||||
}
|
||||
regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size);
|
||||
regcache_rbtree_insert(&rbtree_ctx->root, rbnode);
|
||||
regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode);
|
||||
rbtree_ctx->cached_rbnode = rbnode;
|
||||
}
|
||||
|
||||
|
@ -397,7 +403,7 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
|
|||
end = rbnode->blklen;
|
||||
|
||||
for (i = base; i < end; i++) {
|
||||
regtmp = rbnode->base_reg + i;
|
||||
regtmp = rbnode->base_reg + (i * map->reg_stride);
|
||||
val = regcache_rbtree_get_register(rbnode, i,
|
||||
map->cache_word_size);
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ static int regcache_hw_init(struct regmap *map)
|
|||
for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) {
|
||||
val = regcache_get_val(map->reg_defaults_raw,
|
||||
i, map->cache_word_size);
|
||||
if (regmap_volatile(map, i))
|
||||
if (regmap_volatile(map, i * map->reg_stride))
|
||||
continue;
|
||||
count++;
|
||||
}
|
||||
|
@ -76,9 +76,9 @@ static int regcache_hw_init(struct regmap *map)
|
|||
for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
|
||||
val = regcache_get_val(map->reg_defaults_raw,
|
||||
i, map->cache_word_size);
|
||||
if (regmap_volatile(map, i))
|
||||
if (regmap_volatile(map, i * map->reg_stride))
|
||||
continue;
|
||||
map->reg_defaults[j].reg = i;
|
||||
map->reg_defaults[j].reg = i * map->reg_stride;
|
||||
map->reg_defaults[j].def = val;
|
||||
j++;
|
||||
}
|
||||
|
@ -98,6 +98,10 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
|
|||
int i;
|
||||
void *tmp_buf;
|
||||
|
||||
for (i = 0; i < config->num_reg_defaults; i++)
|
||||
if (config->reg_defaults[i].reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
if (map->cache_type == REGCACHE_NONE) {
|
||||
map->cache_bypass = true;
|
||||
return 0;
|
||||
|
@ -264,7 +268,7 @@ int regcache_sync(struct regmap *map)
|
|||
|
||||
BUG_ON(!map->cache_ops || !map->cache_ops->sync);
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
/* Remember the initial bypass state */
|
||||
bypass = map->cache_bypass;
|
||||
dev_dbg(map->dev, "Syncing %s cache\n",
|
||||
|
@ -278,6 +282,10 @@ int regcache_sync(struct regmap *map)
|
|||
/* Apply any patch first */
|
||||
map->cache_bypass = 1;
|
||||
for (i = 0; i < map->patch_regs; i++) {
|
||||
if (map->patch[i].reg % map->reg_stride) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def);
|
||||
if (ret != 0) {
|
||||
dev_err(map->dev, "Failed to write %x = %x: %d\n",
|
||||
|
@ -296,7 +304,7 @@ out:
|
|||
trace_regcache_sync(map->dev, name, "stop");
|
||||
/* Restore the bypass state */
|
||||
map->cache_bypass = bypass;
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -323,7 +331,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
|
|||
|
||||
BUG_ON(!map->cache_ops || !map->cache_ops->sync);
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
/* Remember the initial bypass state */
|
||||
bypass = map->cache_bypass;
|
||||
|
@ -342,7 +350,7 @@ out:
|
|||
trace_regcache_sync(map->dev, name, "stop region");
|
||||
/* Restore the bypass state */
|
||||
map->cache_bypass = bypass;
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -362,11 +370,11 @@ EXPORT_SYMBOL_GPL(regcache_sync_region);
|
|||
*/
|
||||
void regcache_cache_only(struct regmap *map, bool enable)
|
||||
{
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
WARN_ON(map->cache_bypass && enable);
|
||||
map->cache_only = enable;
|
||||
trace_regmap_cache_only(map->dev, enable);
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regcache_cache_only);
|
||||
|
||||
|
@ -381,9 +389,9 @@ EXPORT_SYMBOL_GPL(regcache_cache_only);
|
|||
*/
|
||||
void regcache_mark_dirty(struct regmap *map)
|
||||
{
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
map->cache_dirty = true;
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regcache_mark_dirty);
|
||||
|
||||
|
@ -400,11 +408,11 @@ EXPORT_SYMBOL_GPL(regcache_mark_dirty);
|
|||
*/
|
||||
void regcache_cache_bypass(struct regmap *map, bool enable)
|
||||
{
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
WARN_ON(map->cache_only && enable);
|
||||
map->cache_bypass = enable;
|
||||
trace_regmap_cache_bypass(map->dev, enable);
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
|
|||
val_len = 2 * map->format.val_bytes;
|
||||
tot_len = reg_len + val_len + 3; /* : \n */
|
||||
|
||||
for (i = 0; i < map->max_register + 1; i++) {
|
||||
for (i = 0; i <= map->max_register; i += map->reg_stride) {
|
||||
if (!regmap_readable(map, i))
|
||||
continue;
|
||||
|
||||
|
@ -197,7 +197,7 @@ static ssize_t regmap_access_read_file(struct file *file,
|
|||
reg_len = regmap_calc_reg_len(map->max_register, buf, count);
|
||||
tot_len = reg_len + 10; /* ': R W V P\n' */
|
||||
|
||||
for (i = 0; i < map->max_register + 1; i++) {
|
||||
for (i = 0; i <= map->max_register; i += map->reg_stride) {
|
||||
/* Ignore registers which are neither readable nor writable */
|
||||
if (!regmap_readable(map, i) && !regmap_writeable(map, i))
|
||||
continue;
|
||||
|
@ -242,10 +242,17 @@ static const struct file_operations regmap_access_fops = {
|
|||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
void regmap_debugfs_init(struct regmap *map)
|
||||
void regmap_debugfs_init(struct regmap *map, const char *name)
|
||||
{
|
||||
map->debugfs = debugfs_create_dir(dev_name(map->dev),
|
||||
regmap_debugfs_root);
|
||||
if (name) {
|
||||
map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
|
||||
dev_name(map->dev), name);
|
||||
name = map->debugfs_name;
|
||||
} else {
|
||||
name = dev_name(map->dev);
|
||||
}
|
||||
|
||||
map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
|
||||
if (!map->debugfs) {
|
||||
dev_warn(map->dev, "Failed to create debugfs directory\n");
|
||||
return;
|
||||
|
@ -274,6 +281,7 @@ void regmap_debugfs_init(struct regmap *map)
|
|||
void regmap_debugfs_exit(struct regmap *map)
|
||||
{
|
||||
debugfs_remove_recursive(map->debugfs);
|
||||
kfree(map->debugfs_name);
|
||||
}
|
||||
|
||||
void regmap_debugfs_initcall(void)
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
static int regmap_i2c_write(struct device *dev, const void *data, size_t count)
|
||||
static int regmap_i2c_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
int ret;
|
||||
|
||||
|
@ -29,10 +30,11 @@ static int regmap_i2c_write(struct device *dev, const void *data, size_t count)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
static int regmap_i2c_gather_write(struct device *dev,
|
||||
static int regmap_i2c_gather_write(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
const void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct i2c_msg xfer[2];
|
||||
int ret;
|
||||
|
@ -62,10 +64,11 @@ static int regmap_i2c_gather_write(struct device *dev,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
static int regmap_i2c_read(struct device *dev,
|
||||
static int regmap_i2c_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct i2c_msg xfer[2];
|
||||
int ret;
|
||||
|
@ -107,7 +110,7 @@ static struct regmap_bus regmap_i2c = {
|
|||
struct regmap *regmap_init_i2c(struct i2c_client *i2c,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return regmap_init(&i2c->dev, ®map_i2c, config);
|
||||
return regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_init_i2c);
|
||||
|
||||
|
@ -124,7 +127,7 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c);
|
|||
struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return devm_regmap_init(&i2c->dev, ®map_i2c, config);
|
||||
return devm_regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
@ -26,18 +27,20 @@ struct regmap_irq_chip_data {
|
|||
struct regmap_irq_chip *chip;
|
||||
|
||||
int irq_base;
|
||||
struct irq_domain *domain;
|
||||
|
||||
void *status_reg_buf;
|
||||
unsigned int *status_buf;
|
||||
unsigned int *mask_buf;
|
||||
unsigned int *mask_buf_def;
|
||||
|
||||
unsigned int irq_reg_stride;
|
||||
};
|
||||
|
||||
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];
|
||||
return &data->chip->irqs[irq];
|
||||
}
|
||||
|
||||
static void regmap_irq_lock(struct irq_data *data)
|
||||
|
@ -50,6 +53,7 @@ static void regmap_irq_lock(struct irq_data *data)
|
|||
static void regmap_irq_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
|
||||
struct regmap *map = d->map;
|
||||
int i, ret;
|
||||
|
||||
/*
|
||||
|
@ -58,11 +62,13 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
|
|||
* suppress pointless writes.
|
||||
*/
|
||||
for (i = 0; i < d->chip->num_regs; i++) {
|
||||
ret = regmap_update_bits(d->map, d->chip->mask_base + i,
|
||||
ret = regmap_update_bits(d->map, d->chip->mask_base +
|
||||
(i * map->reg_stride *
|
||||
d->irq_reg_stride),
|
||||
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);
|
||||
d->chip->mask_base + (i * map->reg_stride));
|
||||
}
|
||||
|
||||
mutex_unlock(&d->lock);
|
||||
|
@ -71,17 +77,19 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
|
|||
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);
|
||||
struct regmap *map = d->map;
|
||||
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
|
||||
|
||||
d->mask_buf[irq_data->reg_offset] &= ~irq_data->mask;
|
||||
d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~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);
|
||||
struct regmap *map = d->map;
|
||||
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
|
||||
|
||||
d->mask_buf[irq_data->reg_offset] |= irq_data->mask;
|
||||
d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
|
||||
}
|
||||
|
||||
static struct irq_chip regmap_irq_chip = {
|
||||
|
@ -98,18 +106,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *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
|
||||
|
@ -118,36 +116,34 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
|
|||
* 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();
|
||||
ret = regmap_read(map, chip->status_base + (i * map->reg_stride
|
||||
* data->irq_reg_stride),
|
||||
&data->status_buf[i]);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(map->dev, "Failed to read IRQ status: %d\n",
|
||||
ret);
|
||||
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,
|
||||
ret = regmap_write(map, chip->ack_base +
|
||||
(i * map->reg_stride *
|
||||
data->irq_reg_stride),
|
||||
data->status_buf[i]);
|
||||
if (ret != 0)
|
||||
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
|
||||
chip->ack_base + i, ret);
|
||||
chip->ack_base + (i * map->reg_stride),
|
||||
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);
|
||||
if (data->status_buf[chip->irqs[i].reg_offset /
|
||||
map->reg_stride] & chip->irqs[i].mask) {
|
||||
handle_nested_irq(irq_find_mapping(data->domain, i));
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
@ -158,6 +154,31 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
|
|||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int regmap_irq_map(struct irq_domain *h, unsigned int virq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
struct regmap_irq_chip_data *data = h->host_data;
|
||||
|
||||
irq_set_chip_data(virq, data);
|
||||
irq_set_chip_and_handler(virq, ®map_irq_chip, handle_edge_irq);
|
||||
irq_set_nested_thread(virq, 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(virq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(virq);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops regmap_domain_ops = {
|
||||
.map = regmap_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
/**
|
||||
* regmap_add_irq_chip(): Use standard regmap IRQ controller handling
|
||||
*
|
||||
|
@ -178,30 +199,37 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
|||
struct regmap_irq_chip_data **data)
|
||||
{
|
||||
struct regmap_irq_chip_data *d;
|
||||
int cur_irq, i;
|
||||
int 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;
|
||||
for (i = 0; i < chip->num_irqs; i++) {
|
||||
if (chip->irqs[i].reg_offset % map->reg_stride)
|
||||
return -EINVAL;
|
||||
if (chip->irqs[i].reg_offset / map->reg_stride >=
|
||||
chip->num_regs)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (irq_base) {
|
||||
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;
|
||||
|
||||
*data = d;
|
||||
|
||||
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)
|
||||
|
@ -215,54 +243,59 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
|||
d->map = map;
|
||||
d->chip = chip;
|
||||
d->irq_base = irq_base;
|
||||
|
||||
if (chip->irq_reg_stride)
|
||||
d->irq_reg_stride = chip->irq_reg_stride;
|
||||
else
|
||||
d->irq_reg_stride = 1;
|
||||
|
||||
mutex_init(&d->lock);
|
||||
|
||||
for (i = 0; i < chip->num_irqs; i++)
|
||||
d->mask_buf_def[chip->irqs[i].reg_offset]
|
||||
d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride]
|
||||
|= 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]);
|
||||
ret = regmap_write(map, chip->mask_base + (i * map->reg_stride
|
||||
* d->irq_reg_stride),
|
||||
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);
|
||||
chip->mask_base + (i * map->reg_stride), 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, ®map_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
|
||||
if (irq_base)
|
||||
d->domain = irq_domain_add_legacy(map->dev->of_node,
|
||||
chip->num_irqs, irq_base, 0,
|
||||
®map_domain_ops, d);
|
||||
else
|
||||
d->domain = irq_domain_add_linear(map->dev->of_node,
|
||||
chip->num_irqs,
|
||||
®map_domain_ops, d);
|
||||
if (!d->domain) {
|
||||
dev_err(map->dev, "Failed to create IRQ domain\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
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;
|
||||
goto err_domain;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_domain:
|
||||
/* Should really dispose of the domain but... */
|
||||
err_alloc:
|
||||
kfree(d->mask_buf_def);
|
||||
kfree(d->mask_buf);
|
||||
kfree(d->status_reg_buf);
|
||||
kfree(d->status_buf);
|
||||
kfree(d);
|
||||
return ret;
|
||||
|
@ -281,9 +314,9 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
|
|||
return;
|
||||
|
||||
free_irq(irq, d);
|
||||
/* We should unmap the domain but... */
|
||||
kfree(d->mask_buf_def);
|
||||
kfree(d->mask_buf);
|
||||
kfree(d->status_reg_buf);
|
||||
kfree(d->status_buf);
|
||||
kfree(d);
|
||||
}
|
||||
|
@ -298,6 +331,21 @@ EXPORT_SYMBOL_GPL(regmap_del_irq_chip);
|
|||
*/
|
||||
int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data)
|
||||
{
|
||||
WARN_ON(!data->irq_base);
|
||||
return data->irq_base;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base);
|
||||
|
||||
/**
|
||||
* regmap_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
|
||||
*
|
||||
* Useful for drivers to request their own IRQs.
|
||||
*
|
||||
* @data: regmap_irq controller to operate on.
|
||||
* @irq: index of the interrupt requested in the chip IRQs
|
||||
*/
|
||||
int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq)
|
||||
{
|
||||
return irq_create_mapping(data->domain, irq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_irq_get_virq);
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Register map access API - MMIO support
|
||||
*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct regmap_mmio_context {
|
||||
void __iomem *regs;
|
||||
unsigned val_bytes;
|
||||
};
|
||||
|
||||
static int regmap_mmio_gather_write(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
const void *val, size_t val_size)
|
||||
{
|
||||
struct regmap_mmio_context *ctx = context;
|
||||
u32 offset;
|
||||
|
||||
BUG_ON(reg_size != 4);
|
||||
|
||||
offset = be32_to_cpup(reg);
|
||||
|
||||
while (val_size) {
|
||||
switch (ctx->val_bytes) {
|
||||
case 1:
|
||||
writeb(*(u8 *)val, ctx->regs + offset);
|
||||
break;
|
||||
case 2:
|
||||
writew(be16_to_cpup(val), ctx->regs + offset);
|
||||
break;
|
||||
case 4:
|
||||
writel(be32_to_cpup(val), ctx->regs + offset);
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 8:
|
||||
writeq(be64_to_cpup(val), ctx->regs + offset);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* Should be caught by regmap_mmio_check_config */
|
||||
BUG();
|
||||
}
|
||||
val_size -= ctx->val_bytes;
|
||||
val += ctx->val_bytes;
|
||||
offset += ctx->val_bytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regmap_mmio_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
BUG_ON(count < 4);
|
||||
|
||||
return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4);
|
||||
}
|
||||
|
||||
static int regmap_mmio_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
struct regmap_mmio_context *ctx = context;
|
||||
u32 offset;
|
||||
|
||||
BUG_ON(reg_size != 4);
|
||||
|
||||
offset = be32_to_cpup(reg);
|
||||
|
||||
while (val_size) {
|
||||
switch (ctx->val_bytes) {
|
||||
case 1:
|
||||
*(u8 *)val = readb(ctx->regs + offset);
|
||||
break;
|
||||
case 2:
|
||||
*(u16 *)val = cpu_to_be16(readw(ctx->regs + offset));
|
||||
break;
|
||||
case 4:
|
||||
*(u32 *)val = cpu_to_be32(readl(ctx->regs + offset));
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 8:
|
||||
*(u64 *)val = cpu_to_be32(readq(ctx->regs + offset));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* Should be caught by regmap_mmio_check_config */
|
||||
BUG();
|
||||
}
|
||||
val_size -= ctx->val_bytes;
|
||||
val += ctx->val_bytes;
|
||||
offset += ctx->val_bytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void regmap_mmio_free_context(void *context)
|
||||
{
|
||||
kfree(context);
|
||||
}
|
||||
|
||||
static struct regmap_bus regmap_mmio = {
|
||||
.fast_io = true,
|
||||
.write = regmap_mmio_write,
|
||||
.gather_write = regmap_mmio_gather_write,
|
||||
.read = regmap_mmio_read,
|
||||
.free_context = regmap_mmio_free_context,
|
||||
};
|
||||
|
||||
struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct regmap_mmio_context *ctx;
|
||||
int min_stride;
|
||||
|
||||
if (config->reg_bits != 32)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (config->pad_bits)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
switch (config->val_bits) {
|
||||
case 8:
|
||||
/* The core treats 0 as 1 */
|
||||
min_stride = 0;
|
||||
break;
|
||||
case 16:
|
||||
min_stride = 2;
|
||||
break;
|
||||
case 32:
|
||||
min_stride = 4;
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 64:
|
||||
min_stride = 8;
|
||||
break;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (config->reg_stride < min_stride)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ctx = kzalloc(GFP_KERNEL, sizeof(*ctx));
|
||||
if (!ctx)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ctx->regs = regs;
|
||||
ctx->val_bytes = config->val_bits / 8;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* regmap_init_mmio(): Initialise register map
|
||||
*
|
||||
* @dev: Device that will be interacted with
|
||||
* @regs: Pointer to memory-mapped IO region
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer to
|
||||
* a struct regmap.
|
||||
*/
|
||||
struct regmap *regmap_init_mmio(struct device *dev,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct regmap_mmio_context *ctx;
|
||||
|
||||
ctx = regmap_mmio_gen_context(regs, config);
|
||||
if (IS_ERR(ctx))
|
||||
return ERR_CAST(ctx);
|
||||
|
||||
return regmap_init(dev, ®map_mmio, ctx, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_init_mmio);
|
||||
|
||||
/**
|
||||
* devm_regmap_init_mmio(): Initialise managed register map
|
||||
*
|
||||
* @dev: Device that will be interacted with
|
||||
* @regs: Pointer to memory-mapped IO region
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer
|
||||
* to a struct regmap. The regmap will be automatically freed by the
|
||||
* device management code.
|
||||
*/
|
||||
struct regmap *devm_regmap_init_mmio(struct device *dev,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct regmap_mmio_context *ctx;
|
||||
|
||||
ctx = regmap_mmio_gen_context(regs, config);
|
||||
if (IS_ERR(ctx))
|
||||
return ERR_CAST(ctx);
|
||||
|
||||
return devm_regmap_init(dev, ®map_mmio, ctx, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_mmio);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -15,17 +15,19 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static int regmap_spi_write(struct device *dev, const void *data, size_t count)
|
||||
static int regmap_spi_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
|
||||
return spi_write(spi, data, count);
|
||||
}
|
||||
|
||||
static int regmap_spi_gather_write(struct device *dev,
|
||||
static int regmap_spi_gather_write(void *context,
|
||||
const void *reg, size_t reg_len,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct spi_message m;
|
||||
struct spi_transfer t[2] = { { .tx_buf = reg, .len = reg_len, },
|
||||
|
@ -38,10 +40,11 @@ static int regmap_spi_gather_write(struct device *dev,
|
|||
return spi_sync(spi, &m);
|
||||
}
|
||||
|
||||
static int regmap_spi_read(struct device *dev,
|
||||
static int regmap_spi_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
|
||||
return spi_write_then_read(spi, reg, reg_size, val, val_size);
|
||||
|
@ -66,7 +69,7 @@ static struct regmap_bus regmap_spi = {
|
|||
struct regmap *regmap_init_spi(struct spi_device *spi,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return regmap_init(&spi->dev, ®map_spi, config);
|
||||
return regmap_init(&spi->dev, ®map_spi, &spi->dev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_init_spi);
|
||||
|
||||
|
@ -83,7 +86,7 @@ EXPORT_SYMBOL_GPL(regmap_init_spi);
|
|||
struct regmap *devm_regmap_init_spi(struct spi_device *spi,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return devm_regmap_init(&spi->dev, ®map_spi, config);
|
||||
return devm_regmap_init(&spi->dev, ®map_spi, &spi->dev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_spi);
|
||||
|
||||
|
|
|
@ -112,25 +112,36 @@ static void regmap_format_10_14_write(struct regmap *map,
|
|||
out[0] = reg >> 2;
|
||||
}
|
||||
|
||||
static void regmap_format_8(void *buf, unsigned int val)
|
||||
static void regmap_format_8(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
u8 *b = buf;
|
||||
|
||||
b[0] = val;
|
||||
b[0] = val << shift;
|
||||
}
|
||||
|
||||
static void regmap_format_16(void *buf, unsigned int val)
|
||||
static void regmap_format_16(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
__be16 *b = buf;
|
||||
|
||||
b[0] = cpu_to_be16(val);
|
||||
b[0] = cpu_to_be16(val << shift);
|
||||
}
|
||||
|
||||
static void regmap_format_32(void *buf, unsigned int val)
|
||||
static void regmap_format_24(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
u8 *b = buf;
|
||||
|
||||
val <<= shift;
|
||||
|
||||
b[0] = val >> 16;
|
||||
b[1] = val >> 8;
|
||||
b[2] = val;
|
||||
}
|
||||
|
||||
static void regmap_format_32(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
__be32 *b = buf;
|
||||
|
||||
b[0] = cpu_to_be32(val);
|
||||
b[0] = cpu_to_be32(val << shift);
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_8(void *buf)
|
||||
|
@ -149,6 +160,16 @@ static unsigned int regmap_parse_16(void *buf)
|
|||
return b[0];
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_24(void *buf)
|
||||
{
|
||||
u8 *b = buf;
|
||||
unsigned int ret = b[2];
|
||||
ret |= ((unsigned int)b[1]) << 8;
|
||||
ret |= ((unsigned int)b[0]) << 16;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_32(void *buf)
|
||||
{
|
||||
__be32 *b = buf;
|
||||
|
@ -158,11 +179,41 @@ static unsigned int regmap_parse_32(void *buf)
|
|||
return b[0];
|
||||
}
|
||||
|
||||
static void regmap_lock_mutex(struct regmap *map)
|
||||
{
|
||||
mutex_lock(&map->mutex);
|
||||
}
|
||||
|
||||
static void regmap_unlock_mutex(struct regmap *map)
|
||||
{
|
||||
mutex_unlock(&map->mutex);
|
||||
}
|
||||
|
||||
static void regmap_lock_spinlock(struct regmap *map)
|
||||
{
|
||||
spin_lock(&map->spinlock);
|
||||
}
|
||||
|
||||
static void regmap_unlock_spinlock(struct regmap *map)
|
||||
{
|
||||
spin_unlock(&map->spinlock);
|
||||
}
|
||||
|
||||
static void dev_get_regmap_release(struct device *dev, void *res)
|
||||
{
|
||||
/*
|
||||
* We don't actually have anything to do here; the goal here
|
||||
* is not to manage the regmap but to provide a simple way to
|
||||
* get the regmap back given a struct device.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* regmap_init(): Initialise register map
|
||||
*
|
||||
* @dev: Device that will be interacted with
|
||||
* @bus: Bus-specific callbacks to use with device
|
||||
* @bus_context: Data passed to bus-specific callbacks
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer to
|
||||
|
@ -171,9 +222,10 @@ static unsigned int regmap_parse_32(void *buf)
|
|||
*/
|
||||
struct regmap *regmap_init(struct device *dev,
|
||||
const struct regmap_bus *bus,
|
||||
void *bus_context,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct regmap *map;
|
||||
struct regmap *map, **m;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!bus || !config)
|
||||
|
@ -185,20 +237,36 @@ struct regmap *regmap_init(struct device *dev,
|
|||
goto err;
|
||||
}
|
||||
|
||||
mutex_init(&map->lock);
|
||||
if (bus->fast_io) {
|
||||
spin_lock_init(&map->spinlock);
|
||||
map->lock = regmap_lock_spinlock;
|
||||
map->unlock = regmap_unlock_spinlock;
|
||||
} else {
|
||||
mutex_init(&map->mutex);
|
||||
map->lock = regmap_lock_mutex;
|
||||
map->unlock = regmap_unlock_mutex;
|
||||
}
|
||||
map->format.buf_size = (config->reg_bits + config->val_bits) / 8;
|
||||
map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
|
||||
map->format.pad_bytes = config->pad_bits / 8;
|
||||
map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
|
||||
map->format.buf_size += map->format.pad_bytes;
|
||||
map->reg_shift = config->pad_bits % 8;
|
||||
if (config->reg_stride)
|
||||
map->reg_stride = config->reg_stride;
|
||||
else
|
||||
map->reg_stride = 1;
|
||||
map->use_single_rw = config->use_single_rw;
|
||||
map->dev = dev;
|
||||
map->bus = bus;
|
||||
map->bus_context = bus_context;
|
||||
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;
|
||||
map->name = config->name;
|
||||
|
||||
if (config->read_flag_mask || config->write_flag_mask) {
|
||||
map->read_flag_mask = config->read_flag_mask;
|
||||
|
@ -207,7 +275,7 @@ struct regmap *regmap_init(struct device *dev,
|
|||
map->read_flag_mask = bus->read_flag_mask;
|
||||
}
|
||||
|
||||
switch (config->reg_bits) {
|
||||
switch (config->reg_bits + map->reg_shift) {
|
||||
case 2:
|
||||
switch (config->val_bits) {
|
||||
case 6:
|
||||
|
@ -273,12 +341,19 @@ struct regmap *regmap_init(struct device *dev,
|
|||
map->format.format_val = regmap_format_16;
|
||||
map->format.parse_val = regmap_parse_16;
|
||||
break;
|
||||
case 24:
|
||||
map->format.format_val = regmap_format_24;
|
||||
map->format.parse_val = regmap_parse_24;
|
||||
break;
|
||||
case 32:
|
||||
map->format.format_val = regmap_format_32;
|
||||
map->format.parse_val = regmap_parse_32;
|
||||
break;
|
||||
}
|
||||
|
||||
if (map->format.format_write)
|
||||
map->use_single_rw = true;
|
||||
|
||||
if (!map->format.format_write &&
|
||||
!(map->format.format_reg && map->format.format_val))
|
||||
goto err_map;
|
||||
|
@ -289,14 +364,25 @@ struct regmap *regmap_init(struct device *dev,
|
|||
goto err_map;
|
||||
}
|
||||
|
||||
regmap_debugfs_init(map);
|
||||
regmap_debugfs_init(map, config->name);
|
||||
|
||||
ret = regcache_init(map, config);
|
||||
if (ret < 0)
|
||||
goto err_free_workbuf;
|
||||
|
||||
/* Add a devres resource for dev_get_regmap() */
|
||||
m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL);
|
||||
if (!m) {
|
||||
ret = -ENOMEM;
|
||||
goto err_cache;
|
||||
}
|
||||
*m = map;
|
||||
devres_add(dev, m);
|
||||
|
||||
return map;
|
||||
|
||||
err_cache:
|
||||
regcache_exit(map);
|
||||
err_free_workbuf:
|
||||
kfree(map->work_buf);
|
||||
err_map:
|
||||
|
@ -316,6 +402,7 @@ static void devm_regmap_release(struct device *dev, void *res)
|
|||
*
|
||||
* @dev: Device that will be interacted with
|
||||
* @bus: Bus-specific callbacks to use with device
|
||||
* @bus_context: Data passed to bus-specific callbacks
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer
|
||||
|
@ -325,6 +412,7 @@ static void devm_regmap_release(struct device *dev, void *res)
|
|||
*/
|
||||
struct regmap *devm_regmap_init(struct device *dev,
|
||||
const struct regmap_bus *bus,
|
||||
void *bus_context,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct regmap **ptr, *regmap;
|
||||
|
@ -333,7 +421,7 @@ struct regmap *devm_regmap_init(struct device *dev,
|
|||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
regmap = regmap_init(dev, bus, config);
|
||||
regmap = regmap_init(dev, bus, bus_context, config);
|
||||
if (!IS_ERR(regmap)) {
|
||||
*ptr = regmap;
|
||||
devres_add(dev, ptr);
|
||||
|
@ -360,7 +448,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
|
|||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
regcache_exit(map);
|
||||
regmap_debugfs_exit(map);
|
||||
|
@ -372,14 +460,14 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
|
|||
map->precious_reg = config->precious_reg;
|
||||
map->cache_type = config->cache_type;
|
||||
|
||||
regmap_debugfs_init(map);
|
||||
regmap_debugfs_init(map, config->name);
|
||||
|
||||
map->cache_bypass = false;
|
||||
map->cache_only = false;
|
||||
|
||||
ret = regcache_init(map, config);
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -391,11 +479,51 @@ void regmap_exit(struct regmap *map)
|
|||
{
|
||||
regcache_exit(map);
|
||||
regmap_debugfs_exit(map);
|
||||
if (map->bus->free_context)
|
||||
map->bus->free_context(map->bus_context);
|
||||
kfree(map->work_buf);
|
||||
kfree(map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_exit);
|
||||
|
||||
static int dev_get_regmap_match(struct device *dev, void *res, void *data)
|
||||
{
|
||||
struct regmap **r = res;
|
||||
if (!r || !*r) {
|
||||
WARN_ON(!r || !*r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the user didn't specify a name match any */
|
||||
if (data)
|
||||
return (*r)->name == data;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_get_regmap(): Obtain the regmap (if any) for a device
|
||||
*
|
||||
* @dev: Device to retrieve the map for
|
||||
* @name: Optional name for the register map, usually NULL.
|
||||
*
|
||||
* Returns the regmap for the device if one is present, or NULL. If
|
||||
* name is specified then it must match the name specified when
|
||||
* registering the device, if it is NULL then the first regmap found
|
||||
* will be used. Devices with multiple register maps are very rare,
|
||||
* generic code should normally not need to specify a name.
|
||||
*/
|
||||
struct regmap *dev_get_regmap(struct device *dev, const char *name)
|
||||
{
|
||||
struct regmap **r = devres_find(dev, dev_get_regmap_release,
|
||||
dev_get_regmap_match, (void *)name);
|
||||
|
||||
if (!r)
|
||||
return NULL;
|
||||
return *r;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_get_regmap);
|
||||
|
||||
static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
|
@ -408,7 +536,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
/* Check for unwritable registers before we start */
|
||||
if (map->writeable_reg)
|
||||
for (i = 0; i < val_len / map->format.val_bytes; i++)
|
||||
if (!map->writeable_reg(map->dev, reg + i))
|
||||
if (!map->writeable_reg(map->dev,
|
||||
reg + (i * map->reg_stride)))
|
||||
return -EINVAL;
|
||||
|
||||
if (!map->cache_bypass && map->format.parse_val) {
|
||||
|
@ -417,7 +546,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
for (i = 0; i < val_len / val_bytes; i++) {
|
||||
memcpy(map->work_buf, val + (i * val_bytes), val_bytes);
|
||||
ival = map->format.parse_val(map->work_buf);
|
||||
ret = regcache_write(map, reg + i, ival);
|
||||
ret = regcache_write(map, reg + (i * map->reg_stride),
|
||||
ival);
|
||||
if (ret) {
|
||||
dev_err(map->dev,
|
||||
"Error in caching of register: %u ret: %d\n",
|
||||
|
@ -431,7 +561,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
}
|
||||
}
|
||||
|
||||
map->format.format_reg(map->work_buf, reg);
|
||||
map->format.format_reg(map->work_buf, reg, map->reg_shift);
|
||||
|
||||
u8[0] |= map->write_flag_mask;
|
||||
|
||||
|
@ -444,12 +574,12 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
*/
|
||||
if (val == (map->work_buf + map->format.pad_bytes +
|
||||
map->format.reg_bytes))
|
||||
ret = map->bus->write(map->dev, map->work_buf,
|
||||
ret = map->bus->write(map->bus_context, map->work_buf,
|
||||
map->format.reg_bytes +
|
||||
map->format.pad_bytes +
|
||||
val_len);
|
||||
else if (map->bus->gather_write)
|
||||
ret = map->bus->gather_write(map->dev, map->work_buf,
|
||||
ret = map->bus->gather_write(map->bus_context, map->work_buf,
|
||||
map->format.reg_bytes +
|
||||
map->format.pad_bytes,
|
||||
val, val_len);
|
||||
|
@ -464,7 +594,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
memcpy(buf, map->work_buf, map->format.reg_bytes);
|
||||
memcpy(buf + map->format.reg_bytes + map->format.pad_bytes,
|
||||
val, val_len);
|
||||
ret = map->bus->write(map->dev, buf, len);
|
||||
ret = map->bus->write(map->bus_context, buf, len);
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
|
@ -498,7 +628,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
|
|||
|
||||
trace_regmap_hw_write_start(map->dev, reg, 1);
|
||||
|
||||
ret = map->bus->write(map->dev, map->work_buf,
|
||||
ret = map->bus->write(map->bus_context, map->work_buf,
|
||||
map->format.buf_size);
|
||||
|
||||
trace_regmap_hw_write_done(map->dev, reg, 1);
|
||||
|
@ -506,7 +636,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
|
|||
return ret;
|
||||
} else {
|
||||
map->format.format_val(map->work_buf + map->format.reg_bytes
|
||||
+ map->format.pad_bytes, val);
|
||||
+ map->format.pad_bytes, val, 0);
|
||||
return _regmap_raw_write(map, reg,
|
||||
map->work_buf +
|
||||
map->format.reg_bytes +
|
||||
|
@ -529,11 +659,14 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
|
|||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
map->lock(map);
|
||||
|
||||
ret = _regmap_write(map, reg, val);
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -560,11 +693,16 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
if (val_len % map->format.val_bytes)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
map->lock(map);
|
||||
|
||||
ret = _regmap_raw_write(map, reg, val, val_len);
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -593,8 +731,10 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
|
|||
|
||||
if (!map->format.parse_val)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
/* No formatting is require if val_byte is 1 */
|
||||
if (val_bytes == 1) {
|
||||
|
@ -609,13 +749,28 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
|
|||
for (i = 0; i < val_count * val_bytes; i += val_bytes)
|
||||
map->format.parse_val(wval + i);
|
||||
}
|
||||
ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
|
||||
/*
|
||||
* Some devices does not support bulk write, for
|
||||
* them we have a series of single write operations.
|
||||
*/
|
||||
if (map->use_single_rw) {
|
||||
for (i = 0; i < val_count; i++) {
|
||||
ret = regmap_raw_write(map,
|
||||
reg + (i * map->reg_stride),
|
||||
val + (i * val_bytes),
|
||||
val_bytes);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
|
||||
}
|
||||
|
||||
if (val_bytes != 1)
|
||||
kfree(wval);
|
||||
|
||||
out:
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_bulk_write);
|
||||
|
@ -626,7 +781,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
|||
u8 *u8 = map->work_buf;
|
||||
int ret;
|
||||
|
||||
map->format.format_reg(map->work_buf, reg);
|
||||
map->format.format_reg(map->work_buf, reg, map->reg_shift);
|
||||
|
||||
/*
|
||||
* Some buses or devices flag reads by setting the high bits in the
|
||||
|
@ -639,7 +794,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
|||
trace_regmap_hw_read_start(map->dev, reg,
|
||||
val_len / map->format.val_bytes);
|
||||
|
||||
ret = map->bus->read(map->dev, map->work_buf,
|
||||
ret = map->bus->read(map->bus_context, map->work_buf,
|
||||
map->format.reg_bytes + map->format.pad_bytes,
|
||||
val, val_len);
|
||||
|
||||
|
@ -672,6 +827,9 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
|
|||
trace_regmap_reg_read(map->dev, reg, *val);
|
||||
}
|
||||
|
||||
if (ret == 0 && !map->cache_bypass)
|
||||
regcache_write(map, reg, *val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -689,11 +847,14 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
|
|||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
map->lock(map);
|
||||
|
||||
ret = _regmap_read(map, reg, val);
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -718,7 +879,12 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
|||
unsigned int v;
|
||||
int ret, i;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
if (val_len % map->format.val_bytes)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
map->lock(map);
|
||||
|
||||
if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass ||
|
||||
map->cache_type == REGCACHE_NONE) {
|
||||
|
@ -730,16 +896,17 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
|||
* cost as we expect to hit the cache.
|
||||
*/
|
||||
for (i = 0; i < val_count; i++) {
|
||||
ret = _regmap_read(map, reg + i, &v);
|
||||
ret = _regmap_read(map, reg + (i * map->reg_stride),
|
||||
&v);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
map->format.format_val(val + (i * val_bytes), v);
|
||||
map->format.format_val(val + (i * val_bytes), v, 0);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -765,18 +932,37 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
|
|||
|
||||
if (!map->format.parse_val)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
if (vol || map->cache_type == REGCACHE_NONE) {
|
||||
ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
/*
|
||||
* Some devices does not support bulk read, for
|
||||
* them we have a series of single read operations.
|
||||
*/
|
||||
if (map->use_single_rw) {
|
||||
for (i = 0; i < val_count; i++) {
|
||||
ret = regmap_raw_read(map,
|
||||
reg + (i * map->reg_stride),
|
||||
val + (i * val_bytes),
|
||||
val_bytes);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = regmap_raw_read(map, reg, val,
|
||||
val_bytes * val_count);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < val_count * val_bytes; i += val_bytes)
|
||||
map->format.parse_val(val + i);
|
||||
} else {
|
||||
for (i = 0; i < val_count; i++) {
|
||||
unsigned int ival;
|
||||
ret = regmap_read(map, reg + i, &ival);
|
||||
ret = regmap_read(map, reg + (i * map->reg_stride),
|
||||
&ival);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
memcpy(val + (i * val_bytes), &ival, val_bytes);
|
||||
|
@ -794,7 +980,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
|
|||
int ret;
|
||||
unsigned int tmp, orig;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
ret = _regmap_read(map, reg, &orig);
|
||||
if (ret != 0)
|
||||
|
@ -811,7 +997,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
|
|||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -878,7 +1064,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
|
|||
if (map->patch)
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
bypass = map->cache_bypass;
|
||||
|
||||
|
@ -906,7 +1092,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
|
|||
out:
|
||||
map->cache_bypass = bypass;
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -376,6 +376,7 @@ config PMIC_DA9052
|
|||
|
||||
config MFD_DA9052_SPI
|
||||
bool "Support Dialog Semiconductor DA9052/53 PMIC variants with SPI"
|
||||
select IRQ_DOMAIN
|
||||
select REGMAP_SPI
|
||||
select REGMAP_IRQ
|
||||
select PMIC_DA9052
|
||||
|
@ -388,6 +389,7 @@ config MFD_DA9052_SPI
|
|||
|
||||
config MFD_DA9052_I2C
|
||||
bool "Support Dialog Semiconductor DA9052/53 PMIC variants with I2C"
|
||||
select IRQ_DOMAIN
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
select PMIC_DA9052
|
||||
|
@ -558,6 +560,7 @@ config MFD_WM8994
|
|||
bool "Support Wolfson Microelectronics WM8994"
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select IRQ_DOMAIN
|
||||
select REGMAP_IRQ
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
help
|
||||
|
@ -888,6 +891,16 @@ config MFD_ANATOP
|
|||
MFD controller. This controller embeds regulator and
|
||||
thermal devices for Freescale i.MX platforms.
|
||||
|
||||
config MFD_PALMAS
|
||||
bool "Support for the TI Palmas series chips"
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
depends on I2C=y
|
||||
help
|
||||
If you say yes here you get support for the Palmas
|
||||
series of PMIC chips from Texas Instruments.
|
||||
|
||||
endmenu
|
||||
endif
|
||||
|
||||
|
|
|
@ -113,6 +113,8 @@ obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
|
|||
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
|
||||
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
|
||||
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
|
||||
obj-$(CONFIG_MFD_PALMAS) += palmas.o
|
||||
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
|
||||
obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o
|
||||
obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o
|
||||
obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
|
||||
|
|
|
@ -659,12 +659,11 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
|
|||
ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
da9052->irq_base, &da9052_regmap_irq_chip,
|
||||
NULL);
|
||||
&da9052->irq_data);
|
||||
if (ret < 0)
|
||||
goto regmap_err;
|
||||
|
||||
desc = irq_to_desc(da9052->chip_irq);
|
||||
da9052->irq_base = regmap_irq_chip_get_base(desc->action->dev_id);
|
||||
da9052->irq_base = regmap_irq_chip_get_base(da9052->irq_data);
|
||||
|
||||
ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info,
|
||||
ARRAY_SIZE(da9052_subdev_info), NULL, 0);
|
||||
|
@ -681,8 +680,7 @@ regmap_err:
|
|||
|
||||
void da9052_device_exit(struct da9052 *da9052)
|
||||
{
|
||||
regmap_del_irq_chip(da9052->chip_irq,
|
||||
irq_get_irq_data(da9052->irq_base)->chip_data);
|
||||
regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data);
|
||||
mfd_remove_devices(da9052->dev);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* TI Palmas MFD Driver
|
||||
*
|
||||
* Copyright 2011-2012 Texas Instruments Inc.
|
||||
*
|
||||
* Author: Graeme Gregory <gg@slimlogic.co.uk>
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/palmas.h>
|
||||
|
||||
static const struct resource gpadc_resource[] = {
|
||||
{
|
||||
.name = "EOC_SW",
|
||||
.start = PALMAS_GPADC_EOC_SW_IRQ,
|
||||
.end = PALMAS_GPADC_EOC_SW_IRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct resource usb_resource[] = {
|
||||
{
|
||||
.name = "ID",
|
||||
.start = PALMAS_ID_OTG_IRQ,
|
||||
.end = PALMAS_ID_OTG_IRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "ID_WAKEUP",
|
||||
.start = PALMAS_ID_IRQ,
|
||||
.end = PALMAS_ID_IRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "VBUS",
|
||||
.start = PALMAS_VBUS_OTG_IRQ,
|
||||
.end = PALMAS_VBUS_OTG_IRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "VBUS_WAKEUP",
|
||||
.start = PALMAS_VBUS_IRQ,
|
||||
.end = PALMAS_VBUS_IRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct resource rtc_resource[] = {
|
||||
{
|
||||
.name = "RTC_ALARM",
|
||||
.start = PALMAS_RTC_ALARM_IRQ,
|
||||
.end = PALMAS_RTC_ALARM_IRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct resource pwron_resource[] = {
|
||||
{
|
||||
.name = "PWRON_BUTTON",
|
||||
.start = PALMAS_PWRON_IRQ,
|
||||
.end = PALMAS_PWRON_IRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
enum palmas_ids {
|
||||
PALMAS_PMIC_ID,
|
||||
PALMAS_GPIO_ID,
|
||||
PALMAS_LEDS_ID,
|
||||
PALMAS_WDT_ID,
|
||||
PALMAS_RTC_ID,
|
||||
PALMAS_PWRBUTTON_ID,
|
||||
PALMAS_GPADC_ID,
|
||||
PALMAS_RESOURCE_ID,
|
||||
PALMAS_CLK_ID,
|
||||
PALMAS_PWM_ID,
|
||||
PALMAS_USB_ID,
|
||||
};
|
||||
|
||||
static const struct mfd_cell palmas_children[] = {
|
||||
{
|
||||
.name = "palmas-pmic",
|
||||
.id = PALMAS_PMIC_ID,
|
||||
},
|
||||
{
|
||||
.name = "palmas-gpio",
|
||||
.id = PALMAS_GPIO_ID,
|
||||
},
|
||||
{
|
||||
.name = "palmas-leds",
|
||||
.id = PALMAS_LEDS_ID,
|
||||
},
|
||||
{
|
||||
.name = "palmas-wdt",
|
||||
.id = PALMAS_WDT_ID,
|
||||
},
|
||||
{
|
||||
.name = "palmas-rtc",
|
||||
.num_resources = ARRAY_SIZE(rtc_resource),
|
||||
.resources = rtc_resource,
|
||||
.id = PALMAS_RTC_ID,
|
||||
},
|
||||
{
|
||||
.name = "palmas-pwrbutton",
|
||||
.num_resources = ARRAY_SIZE(pwron_resource),
|
||||
.resources = pwron_resource,
|
||||
.id = PALMAS_PWRBUTTON_ID,
|
||||
},
|
||||
{
|
||||
.name = "palmas-gpadc",
|
||||
.num_resources = ARRAY_SIZE(gpadc_resource),
|
||||
.resources = gpadc_resource,
|
||||
.id = PALMAS_GPADC_ID,
|
||||
},
|
||||
{
|
||||
.name = "palmas-resource",
|
||||
.id = PALMAS_RESOURCE_ID,
|
||||
},
|
||||
{
|
||||
.name = "palmas-clk",
|
||||
.id = PALMAS_CLK_ID,
|
||||
},
|
||||
{
|
||||
.name = "palmas-pwm",
|
||||
.id = PALMAS_PWM_ID,
|
||||
},
|
||||
{
|
||||
.name = "palmas-usb",
|
||||
.num_resources = ARRAY_SIZE(usb_resource),
|
||||
.resources = usb_resource,
|
||||
.id = PALMAS_USB_ID,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct regmap_config palmas_regmap_config[PALMAS_NUM_CLIENTS] = {
|
||||
{
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE,
|
||||
PALMAS_PRIMARY_SECONDARY_PAD3),
|
||||
},
|
||||
{
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = PALMAS_BASE_TO_REG(PALMAS_GPADC_BASE,
|
||||
PALMAS_GPADC_SMPS_VSEL_MONITORING),
|
||||
},
|
||||
{
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = PALMAS_BASE_TO_REG(PALMAS_TRIM_GPADC_BASE,
|
||||
PALMAS_GPADC_TRIM16),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_irq palmas_irqs[] = {
|
||||
/* INT1 IRQs */
|
||||
[PALMAS_CHARG_DET_N_VBUS_OVV_IRQ] = {
|
||||
.mask = PALMAS_INT1_STATUS_CHARG_DET_N_VBUS_OVV,
|
||||
},
|
||||
[PALMAS_PWRON_IRQ] = {
|
||||
.mask = PALMAS_INT1_STATUS_PWRON,
|
||||
},
|
||||
[PALMAS_LONG_PRESS_KEY_IRQ] = {
|
||||
.mask = PALMAS_INT1_STATUS_LONG_PRESS_KEY,
|
||||
},
|
||||
[PALMAS_RPWRON_IRQ] = {
|
||||
.mask = PALMAS_INT1_STATUS_RPWRON,
|
||||
},
|
||||
[PALMAS_PWRDOWN_IRQ] = {
|
||||
.mask = PALMAS_INT1_STATUS_PWRDOWN,
|
||||
},
|
||||
[PALMAS_HOTDIE_IRQ] = {
|
||||
.mask = PALMAS_INT1_STATUS_HOTDIE,
|
||||
},
|
||||
[PALMAS_VSYS_MON_IRQ] = {
|
||||
.mask = PALMAS_INT1_STATUS_VSYS_MON,
|
||||
},
|
||||
[PALMAS_VBAT_MON_IRQ] = {
|
||||
.mask = PALMAS_INT1_STATUS_VBAT_MON,
|
||||
},
|
||||
/* INT2 IRQs*/
|
||||
[PALMAS_RTC_ALARM_IRQ] = {
|
||||
.mask = PALMAS_INT2_STATUS_RTC_ALARM,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[PALMAS_RTC_TIMER_IRQ] = {
|
||||
.mask = PALMAS_INT2_STATUS_RTC_TIMER,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[PALMAS_WDT_IRQ] = {
|
||||
.mask = PALMAS_INT2_STATUS_WDT,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[PALMAS_BATREMOVAL_IRQ] = {
|
||||
.mask = PALMAS_INT2_STATUS_BATREMOVAL,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[PALMAS_RESET_IN_IRQ] = {
|
||||
.mask = PALMAS_INT2_STATUS_RESET_IN,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[PALMAS_FBI_BB_IRQ] = {
|
||||
.mask = PALMAS_INT2_STATUS_FBI_BB,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[PALMAS_SHORT_IRQ] = {
|
||||
.mask = PALMAS_INT2_STATUS_SHORT,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
[PALMAS_VAC_ACOK_IRQ] = {
|
||||
.mask = PALMAS_INT2_STATUS_VAC_ACOK,
|
||||
.reg_offset = 1,
|
||||
},
|
||||
/* INT3 IRQs */
|
||||
[PALMAS_GPADC_AUTO_0_IRQ] = {
|
||||
.mask = PALMAS_INT3_STATUS_GPADC_AUTO_0,
|
||||
.reg_offset = 2,
|
||||
},
|
||||
[PALMAS_GPADC_AUTO_1_IRQ] = {
|
||||
.mask = PALMAS_INT3_STATUS_GPADC_AUTO_1,
|
||||
.reg_offset = 2,
|
||||
},
|
||||
[PALMAS_GPADC_EOC_SW_IRQ] = {
|
||||
.mask = PALMAS_INT3_STATUS_GPADC_EOC_SW,
|
||||
.reg_offset = 2,
|
||||
},
|
||||
[PALMAS_GPADC_EOC_RT_IRQ] = {
|
||||
.mask = PALMAS_INT3_STATUS_GPADC_EOC_RT,
|
||||
.reg_offset = 2,
|
||||
},
|
||||
[PALMAS_ID_OTG_IRQ] = {
|
||||
.mask = PALMAS_INT3_STATUS_ID_OTG,
|
||||
.reg_offset = 2,
|
||||
},
|
||||
[PALMAS_ID_IRQ] = {
|
||||
.mask = PALMAS_INT3_STATUS_ID,
|
||||
.reg_offset = 2,
|
||||
},
|
||||
[PALMAS_VBUS_OTG_IRQ] = {
|
||||
.mask = PALMAS_INT3_STATUS_VBUS_OTG,
|
||||
.reg_offset = 2,
|
||||
},
|
||||
[PALMAS_VBUS_IRQ] = {
|
||||
.mask = PALMAS_INT3_STATUS_VBUS,
|
||||
.reg_offset = 2,
|
||||
},
|
||||
/* INT4 IRQs */
|
||||
[PALMAS_GPIO_0_IRQ] = {
|
||||
.mask = PALMAS_INT4_STATUS_GPIO_0,
|
||||
.reg_offset = 3,
|
||||
},
|
||||
[PALMAS_GPIO_1_IRQ] = {
|
||||
.mask = PALMAS_INT4_STATUS_GPIO_1,
|
||||
.reg_offset = 3,
|
||||
},
|
||||
[PALMAS_GPIO_2_IRQ] = {
|
||||
.mask = PALMAS_INT4_STATUS_GPIO_2,
|
||||
.reg_offset = 3,
|
||||
},
|
||||
[PALMAS_GPIO_3_IRQ] = {
|
||||
.mask = PALMAS_INT4_STATUS_GPIO_3,
|
||||
.reg_offset = 3,
|
||||
},
|
||||
[PALMAS_GPIO_4_IRQ] = {
|
||||
.mask = PALMAS_INT4_STATUS_GPIO_4,
|
||||
.reg_offset = 3,
|
||||
},
|
||||
[PALMAS_GPIO_5_IRQ] = {
|
||||
.mask = PALMAS_INT4_STATUS_GPIO_5,
|
||||
.reg_offset = 3,
|
||||
},
|
||||
[PALMAS_GPIO_6_IRQ] = {
|
||||
.mask = PALMAS_INT4_STATUS_GPIO_6,
|
||||
.reg_offset = 3,
|
||||
},
|
||||
[PALMAS_GPIO_7_IRQ] = {
|
||||
.mask = PALMAS_INT4_STATUS_GPIO_7,
|
||||
.reg_offset = 3,
|
||||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip palmas_irq_chip = {
|
||||
.name = "palmas",
|
||||
.irqs = palmas_irqs,
|
||||
.num_irqs = ARRAY_SIZE(palmas_irqs),
|
||||
|
||||
.num_regs = 4,
|
||||
.irq_reg_stride = 5,
|
||||
.status_base = PALMAS_BASE_TO_REG(PALMAS_INTERRUPT_BASE,
|
||||
PALMAS_INT1_STATUS),
|
||||
.mask_base = PALMAS_BASE_TO_REG(PALMAS_INTERRUPT_BASE,
|
||||
PALMAS_INT1_MASK),
|
||||
};
|
||||
|
||||
static int __devinit palmas_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct palmas *palmas;
|
||||
struct palmas_platform_data *pdata;
|
||||
int ret = 0, i;
|
||||
unsigned int reg, addr;
|
||||
int slave;
|
||||
struct mfd_cell *children;
|
||||
|
||||
pdata = dev_get_platdata(&i2c->dev);
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
palmas = devm_kzalloc(&i2c->dev, sizeof(struct palmas), GFP_KERNEL);
|
||||
if (palmas == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, palmas);
|
||||
palmas->dev = &i2c->dev;
|
||||
palmas->id = id->driver_data;
|
||||
palmas->irq = i2c->irq;
|
||||
|
||||
for (i = 0; i < PALMAS_NUM_CLIENTS; i++) {
|
||||
if (i == 0)
|
||||
palmas->i2c_clients[i] = i2c;
|
||||
else {
|
||||
palmas->i2c_clients[i] =
|
||||
i2c_new_dummy(i2c->adapter,
|
||||
i2c->addr + i);
|
||||
if (!palmas->i2c_clients[i]) {
|
||||
dev_err(palmas->dev,
|
||||
"can't attach client %d\n", i);
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
palmas->regmap[i] = devm_regmap_init_i2c(palmas->i2c_clients[i],
|
||||
&palmas_regmap_config[i]);
|
||||
if (IS_ERR(palmas->regmap[i])) {
|
||||
ret = PTR_ERR(palmas->regmap[i]);
|
||||
dev_err(palmas->dev,
|
||||
"Failed to allocate regmap %d, err: %d\n",
|
||||
i, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(palmas->regmap[1], palmas->irq,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_LOW, -1, &palmas_irq_chip,
|
||||
&palmas->irq_data);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE);
|
||||
addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE,
|
||||
PALMAS_PRIMARY_SECONDARY_PAD1);
|
||||
|
||||
if (pdata->mux_from_pdata) {
|
||||
reg = pdata->pad1;
|
||||
ret = regmap_write(palmas->regmap[slave], addr, reg);
|
||||
if (ret)
|
||||
goto err;
|
||||
} else {
|
||||
ret = regmap_read(palmas->regmap[slave], addr, ®);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_0))
|
||||
palmas->gpio_muxed |= PALMAS_GPIO_0_MUXED;
|
||||
if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK))
|
||||
palmas->gpio_muxed |= PALMAS_GPIO_1_MUXED;
|
||||
else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK) ==
|
||||
(2 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT))
|
||||
palmas->led_muxed |= PALMAS_LED1_MUXED;
|
||||
else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK) ==
|
||||
(3 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT))
|
||||
palmas->pwm_muxed |= PALMAS_PWM1_MUXED;
|
||||
if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK))
|
||||
palmas->gpio_muxed |= PALMAS_GPIO_2_MUXED;
|
||||
else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK) ==
|
||||
(2 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_SHIFT))
|
||||
palmas->led_muxed |= PALMAS_LED2_MUXED;
|
||||
else if ((reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_MASK) ==
|
||||
(3 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_2_SHIFT))
|
||||
palmas->pwm_muxed |= PALMAS_PWM2_MUXED;
|
||||
if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_3))
|
||||
palmas->gpio_muxed |= PALMAS_GPIO_3_MUXED;
|
||||
|
||||
addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE,
|
||||
PALMAS_PRIMARY_SECONDARY_PAD2);
|
||||
|
||||
if (pdata->mux_from_pdata) {
|
||||
reg = pdata->pad2;
|
||||
ret = regmap_write(palmas->regmap[slave], addr, reg);
|
||||
if (ret)
|
||||
goto err;
|
||||
} else {
|
||||
ret = regmap_read(palmas->regmap[slave], addr, ®);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_4))
|
||||
palmas->gpio_muxed |= PALMAS_GPIO_4_MUXED;
|
||||
if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_5_MASK))
|
||||
palmas->gpio_muxed |= PALMAS_GPIO_5_MUXED;
|
||||
if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_6))
|
||||
palmas->gpio_muxed |= PALMAS_GPIO_6_MUXED;
|
||||
if (!(reg & PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_7_MASK))
|
||||
palmas->gpio_muxed |= PALMAS_GPIO_7_MUXED;
|
||||
|
||||
dev_info(palmas->dev, "Muxing GPIO %x, PWM %x, LED %x\n",
|
||||
palmas->gpio_muxed, palmas->pwm_muxed,
|
||||
palmas->led_muxed);
|
||||
|
||||
reg = pdata->power_ctrl;
|
||||
|
||||
slave = PALMAS_BASE_TO_SLAVE(PALMAS_PMU_CONTROL_BASE);
|
||||
addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE, PALMAS_POWER_CTRL);
|
||||
|
||||
ret = regmap_write(palmas->regmap[slave], addr, reg);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
children = kmemdup(palmas_children, sizeof(palmas_children),
|
||||
GFP_KERNEL);
|
||||
if (!children) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(palmas->dev, -1,
|
||||
children, ARRAY_SIZE(palmas_children),
|
||||
NULL, regmap_irq_chip_get_base(palmas->irq_data));
|
||||
kfree(children);
|
||||
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
mfd_remove_devices(palmas->dev);
|
||||
kfree(palmas);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int palmas_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct palmas *palmas = i2c_get_clientdata(i2c);
|
||||
|
||||
mfd_remove_devices(palmas->dev);
|
||||
regmap_del_irq_chip(palmas->irq, palmas->irq_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id palmas_i2c_id[] = {
|
||||
{ "palmas", },
|
||||
{ "twl6035", },
|
||||
{ "twl6037", },
|
||||
{ "tps65913", },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, palmas_i2c_id);
|
||||
|
||||
static struct of_device_id __devinitdata of_palmas_match_tbl[] = {
|
||||
{ .compatible = "ti,palmas", },
|
||||
{ /* end */ }
|
||||
};
|
||||
|
||||
static struct i2c_driver palmas_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "palmas",
|
||||
.of_match_table = of_palmas_match_tbl,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = palmas_i2c_probe,
|
||||
.remove = palmas_i2c_remove,
|
||||
.id_table = palmas_i2c_id,
|
||||
};
|
||||
|
||||
static int __init palmas_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&palmas_i2c_driver);
|
||||
}
|
||||
/* init early so consumer devices can complete system boot */
|
||||
subsys_initcall(palmas_i2c_init);
|
||||
|
||||
static void __exit palmas_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&palmas_i2c_driver);
|
||||
}
|
||||
module_exit(palmas_i2c_exit);
|
||||
|
||||
MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
|
||||
MODULE_DESCRIPTION("Palmas chip family multi-function driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -147,12 +147,6 @@ int wm8994_irq_init(struct wm8994 *wm8994)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!wm8994->irq_base) {
|
||||
dev_err(wm8994->dev,
|
||||
"No interrupt base specified, no interrupts\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
wm8994->irq_base, &wm8994_irq_chip,
|
||||
|
|
|
@ -80,6 +80,7 @@ struct da9052 {
|
|||
struct regmap *regmap;
|
||||
|
||||
int irq_base;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
u8 chip_id;
|
||||
|
||||
int chip_irq;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
enum wm8994_type {
|
||||
WM8994 = 0,
|
||||
|
@ -26,7 +27,6 @@ enum wm8994_type {
|
|||
|
||||
struct regulator_dev;
|
||||
struct regulator_bulk_data;
|
||||
struct regmap;
|
||||
|
||||
#define WM8994_NUM_GPIO_REGS 11
|
||||
#define WM8994_NUM_LDO_REGS 2
|
||||
|
@ -94,17 +94,17 @@ static inline int wm8994_request_irq(struct wm8994 *wm8994, int irq,
|
|||
irq_handler_t handler, const char *name,
|
||||
void *data)
|
||||
{
|
||||
if (!wm8994->irq_base)
|
||||
if (!wm8994->irq_data)
|
||||
return -EINVAL;
|
||||
return request_threaded_irq(wm8994->irq_base + irq, NULL, handler,
|
||||
IRQF_TRIGGER_RISING, name,
|
||||
return request_threaded_irq(regmap_irq_get_virq(wm8994->irq_data, irq),
|
||||
NULL, handler, IRQF_TRIGGER_RISING, name,
|
||||
data);
|
||||
}
|
||||
static inline void wm8994_free_irq(struct wm8994 *wm8994, int irq, void *data)
|
||||
{
|
||||
if (!wm8994->irq_base)
|
||||
if (!wm8994->irq_data)
|
||||
return;
|
||||
free_irq(wm8994->irq_base + irq, data);
|
||||
free_irq(regmap_irq_get_virq(wm8994->irq_data, irq), data);
|
||||
}
|
||||
|
||||
int wm8994_irq_init(struct wm8994 *wm8994);
|
||||
|
|
|
@ -46,7 +46,13 @@ struct reg_default {
|
|||
/**
|
||||
* Configuration for the register map of a device.
|
||||
*
|
||||
* @name: Optional name of the regmap. Useful when a device has multiple
|
||||
* register regions.
|
||||
*
|
||||
* @reg_bits: Number of bits in a register address, mandatory.
|
||||
* @reg_stride: The register address stride. Valid register addresses are a
|
||||
* multiple of this value. If set to 0, a value of 1 will be
|
||||
* used.
|
||||
* @pad_bits: Number of bits of padding between register and value.
|
||||
* @val_bits: Number of bits in a register value, mandatory.
|
||||
*
|
||||
|
@ -70,6 +76,9 @@ struct reg_default {
|
|||
* @write_flag_mask: Mask to be set in the top byte of the register when doing
|
||||
* a write. If both read_flag_mask and write_flag_mask are
|
||||
* empty the regmap_bus default masks are used.
|
||||
* @use_single_rw: If set, converts the bulk read and write operations into
|
||||
* a series of single read and write operations. This is useful
|
||||
* for device that does not support bulk read and write.
|
||||
*
|
||||
* @cache_type: The actual cache type.
|
||||
* @reg_defaults_raw: Power on reset values for registers (for use with
|
||||
|
@ -77,7 +86,10 @@ struct reg_default {
|
|||
* @num_reg_defaults_raw: Number of elements in reg_defaults_raw.
|
||||
*/
|
||||
struct regmap_config {
|
||||
const char *name;
|
||||
|
||||
int reg_bits;
|
||||
int reg_stride;
|
||||
int pad_bits;
|
||||
int val_bits;
|
||||
|
||||
|
@ -95,20 +107,25 @@ struct regmap_config {
|
|||
|
||||
u8 read_flag_mask;
|
||||
u8 write_flag_mask;
|
||||
|
||||
bool use_single_rw;
|
||||
};
|
||||
|
||||
typedef int (*regmap_hw_write)(struct device *dev, const void *data,
|
||||
typedef int (*regmap_hw_write)(void *context, const void *data,
|
||||
size_t count);
|
||||
typedef int (*regmap_hw_gather_write)(struct device *dev,
|
||||
typedef int (*regmap_hw_gather_write)(void *context,
|
||||
const void *reg, size_t reg_len,
|
||||
const void *val, size_t val_len);
|
||||
typedef int (*regmap_hw_read)(struct device *dev,
|
||||
typedef int (*regmap_hw_read)(void *context,
|
||||
const void *reg_buf, size_t reg_size,
|
||||
void *val_buf, size_t val_size);
|
||||
typedef void (*regmap_hw_free_context)(void *context);
|
||||
|
||||
/**
|
||||
* Description of a hardware bus for the register map infrastructure.
|
||||
*
|
||||
* @fast_io: Register IO is fast. Use a spinlock instead of a mutex
|
||||
* to perform locking.
|
||||
* @write: Write operation.
|
||||
* @gather_write: Write operation with split register/value, return -ENOTSUPP
|
||||
* if not implemented on a given device.
|
||||
|
@ -118,31 +135,42 @@ typedef int (*regmap_hw_read)(struct device *dev,
|
|||
* a read.
|
||||
*/
|
||||
struct regmap_bus {
|
||||
bool fast_io;
|
||||
regmap_hw_write write;
|
||||
regmap_hw_gather_write gather_write;
|
||||
regmap_hw_read read;
|
||||
regmap_hw_free_context free_context;
|
||||
u8 read_flag_mask;
|
||||
};
|
||||
|
||||
struct regmap *regmap_init(struct device *dev,
|
||||
const struct regmap_bus *bus,
|
||||
void *bus_context,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *regmap_init_i2c(struct i2c_client *i2c,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *regmap_init_spi(struct spi_device *dev,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *regmap_init_mmio(struct device *dev,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config);
|
||||
|
||||
struct regmap *devm_regmap_init(struct device *dev,
|
||||
const struct regmap_bus *bus,
|
||||
void *bus_context,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *devm_regmap_init_spi(struct spi_device *dev,
|
||||
const struct regmap_config *config);
|
||||
struct regmap *devm_regmap_init_mmio(struct device *dev,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config);
|
||||
|
||||
void regmap_exit(struct regmap *map);
|
||||
int regmap_reinit_cache(struct regmap *map,
|
||||
const struct regmap_config *config);
|
||||
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_raw_write(struct regmap *map, unsigned int reg,
|
||||
const void *val, size_t val_len);
|
||||
|
@ -191,6 +219,7 @@ struct regmap_irq {
|
|||
* @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.
|
||||
* @irq_reg_stride: Stride to use for chips where registers are not contiguous.
|
||||
*
|
||||
* @num_regs: Number of registers in each control bank.
|
||||
* @irqs: Descriptors for individual IRQs. Interrupt numbers are
|
||||
|
@ -203,6 +232,7 @@ struct regmap_irq_chip {
|
|||
unsigned int status_base;
|
||||
unsigned int mask_base;
|
||||
unsigned int ack_base;
|
||||
unsigned int irq_reg_stride;
|
||||
|
||||
int num_regs;
|
||||
|
||||
|
@ -217,6 +247,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
|||
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);
|
||||
int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq);
|
||||
|
||||
#else
|
||||
|
||||
|
@ -327,6 +358,13 @@ static inline int regmap_register_patch(struct regmap *map,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline struct regmap *dev_get_regmap(struct device *dev,
|
||||
const char *name)
|
||||
{
|
||||
WARN_ONCE(1, "regmap API is disabled");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче