Commit 7cd58b0a authored by Linus Torvalds's avatar Linus Torvalds

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

Pull regmap updates from Mark Brown:
 "A few fixes plus a few features, the most generally useful thing being
  the register paging support which can be used by quite a few devices:

   - Support for wake IRQs in regmap-irq
   - Support for register paging
   - Support for explicitly specified endianness, mostly for MMIO."

* tag 'regmap-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: Fix incorrect arguments to kzalloc() call
  regmap: Add hook for printk logging for debugging during early init
  regmap: Fix work_buf switching for page update during virtual range access.
  regmap: Add support for register indirect addressing.
  regmap: Move lock out from internal function _regmap_update_bits().
  regmap: mmio: Staticize regmap_mmio_gen_context()
  regmap: Remove warning on stubbed dev_get_regmap()
  regmap: Implement support for wake IRQs
  regmap: Don't try to map non-existant IRQs
  regmap: Constify regmap_irq_chip
  regmap: mmio: request native endian formatting
  regmap: allow busses to request formatting with specific endianness
parents e2b34e31 38e23194
...@@ -95,6 +95,9 @@ struct regmap { ...@@ -95,6 +95,9 @@ struct regmap {
/* if set, converts bulk rw to single rw */ /* if set, converts bulk rw to single rw */
bool use_single_rw; bool use_single_rw;
struct rb_root range_tree;
void *selector_work_buf; /* Scratch buffer used for selector */
}; };
struct regcache_ops { struct regcache_ops {
...@@ -115,6 +118,20 @@ bool regmap_precious(struct regmap *map, unsigned int reg); ...@@ -115,6 +118,20 @@ bool regmap_precious(struct regmap *map, unsigned int reg);
int _regmap_write(struct regmap *map, unsigned int reg, int _regmap_write(struct regmap *map, unsigned int reg,
unsigned int val); unsigned int val);
struct regmap_range_node {
struct rb_node node;
unsigned int range_min;
unsigned int range_max;
unsigned int selector_reg;
unsigned int selector_mask;
int selector_shift;
unsigned int window_start;
unsigned int window_len;
};
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
extern void regmap_debugfs_initcall(void); extern void regmap_debugfs_initcall(void);
extern void regmap_debugfs_init(struct regmap *map, const char *name); extern void regmap_debugfs_init(struct regmap *map, const char *name);
......
...@@ -24,14 +24,18 @@ struct regmap_irq_chip_data { ...@@ -24,14 +24,18 @@ struct regmap_irq_chip_data {
struct mutex lock; struct mutex lock;
struct regmap *map; struct regmap *map;
struct regmap_irq_chip *chip; const struct regmap_irq_chip *chip;
int irq_base; int irq_base;
struct irq_domain *domain; struct irq_domain *domain;
int irq;
int wake_count;
unsigned int *status_buf; unsigned int *status_buf;
unsigned int *mask_buf; unsigned int *mask_buf;
unsigned int *mask_buf_def; unsigned int *mask_buf_def;
unsigned int *wake_buf;
unsigned int irq_reg_stride; unsigned int irq_reg_stride;
}; };
...@@ -71,6 +75,16 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -71,6 +75,16 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
d->chip->mask_base + (i * map->reg_stride)); d->chip->mask_base + (i * map->reg_stride));
} }
/* If we've changed our wakeup count propagate it to the parent */
if (d->wake_count < 0)
for (i = d->wake_count; i < 0; i++)
irq_set_irq_wake(d->irq, 0);
else if (d->wake_count > 0)
for (i = 0; i < d->wake_count; i++)
irq_set_irq_wake(d->irq, 1);
d->wake_count = 0;
mutex_unlock(&d->lock); mutex_unlock(&d->lock);
} }
...@@ -92,18 +106,41 @@ static void regmap_irq_disable(struct irq_data *data) ...@@ -92,18 +106,41 @@ static void regmap_irq_disable(struct irq_data *data)
d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
} }
static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
if (!d->chip->wake_base)
return -EINVAL;
if (on) {
d->wake_buf[irq_data->reg_offset / map->reg_stride]
&= ~irq_data->mask;
d->wake_count++;
} else {
d->wake_buf[irq_data->reg_offset / map->reg_stride]
|= irq_data->mask;
d->wake_count--;
}
return 0;
}
static struct irq_chip regmap_irq_chip = { static struct irq_chip regmap_irq_chip = {
.name = "regmap", .name = "regmap",
.irq_bus_lock = regmap_irq_lock, .irq_bus_lock = regmap_irq_lock,
.irq_bus_sync_unlock = regmap_irq_sync_unlock, .irq_bus_sync_unlock = regmap_irq_sync_unlock,
.irq_disable = regmap_irq_disable, .irq_disable = regmap_irq_disable,
.irq_enable = regmap_irq_enable, .irq_enable = regmap_irq_enable,
.irq_set_wake = regmap_irq_set_wake,
}; };
static irqreturn_t regmap_irq_thread(int irq, void *d) static irqreturn_t regmap_irq_thread(int irq, void *d)
{ {
struct regmap_irq_chip_data *data = d; struct regmap_irq_chip_data *data = d;
struct regmap_irq_chip *chip = data->chip; const struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map; struct regmap *map = data->map;
int ret, i; int ret, i;
bool handled = false; bool handled = false;
...@@ -195,7 +232,7 @@ static struct irq_domain_ops regmap_domain_ops = { ...@@ -195,7 +232,7 @@ static struct irq_domain_ops regmap_domain_ops = {
* register values used by the IRQ controller over suspend and resume. * 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 regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
int irq_base, struct regmap_irq_chip *chip, int irq_base, const struct regmap_irq_chip *chip,
struct regmap_irq_chip_data **data) struct regmap_irq_chip_data **data)
{ {
struct regmap_irq_chip_data *d; struct regmap_irq_chip_data *d;
...@@ -240,6 +277,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -240,6 +277,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
if (!d->mask_buf_def) if (!d->mask_buf_def)
goto err_alloc; goto err_alloc;
if (chip->wake_base) {
d->wake_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
GFP_KERNEL);
if (!d->wake_buf)
goto err_alloc;
}
d->irq = irq;
d->map = map; d->map = map;
d->chip = chip; d->chip = chip;
d->irq_base = irq_base; d->irq_base = irq_base;
...@@ -294,6 +339,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -294,6 +339,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
err_domain: err_domain:
/* Should really dispose of the domain but... */ /* Should really dispose of the domain but... */
err_alloc: err_alloc:
kfree(d->wake_buf);
kfree(d->mask_buf_def); kfree(d->mask_buf_def);
kfree(d->mask_buf); kfree(d->mask_buf);
kfree(d->status_buf); kfree(d->status_buf);
...@@ -315,6 +361,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) ...@@ -315,6 +361,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
free_irq(irq, d); free_irq(irq, d);
/* We should unmap the domain but... */ /* We should unmap the domain but... */
kfree(d->wake_buf);
kfree(d->mask_buf_def); kfree(d->mask_buf_def);
kfree(d->mask_buf); kfree(d->mask_buf);
kfree(d->status_buf); kfree(d->status_buf);
...@@ -346,6 +393,10 @@ EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); ...@@ -346,6 +393,10 @@ EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base);
*/ */
int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq)
{ {
/* Handle holes in the IRQ list */
if (!data->chip->irqs[irq].mask)
return -EINVAL;
return irq_create_mapping(data->domain, irq); return irq_create_mapping(data->domain, irq);
} }
EXPORT_SYMBOL_GPL(regmap_irq_get_virq); EXPORT_SYMBOL_GPL(regmap_irq_get_virq);
...@@ -37,7 +37,7 @@ static int regmap_mmio_gather_write(void *context, ...@@ -37,7 +37,7 @@ static int regmap_mmio_gather_write(void *context,
BUG_ON(reg_size != 4); BUG_ON(reg_size != 4);
offset = be32_to_cpup(reg); offset = *(u32 *)reg;
while (val_size) { while (val_size) {
switch (ctx->val_bytes) { switch (ctx->val_bytes) {
...@@ -45,14 +45,14 @@ static int regmap_mmio_gather_write(void *context, ...@@ -45,14 +45,14 @@ static int regmap_mmio_gather_write(void *context,
writeb(*(u8 *)val, ctx->regs + offset); writeb(*(u8 *)val, ctx->regs + offset);
break; break;
case 2: case 2:
writew(be16_to_cpup(val), ctx->regs + offset); writew(*(u16 *)val, ctx->regs + offset);
break; break;
case 4: case 4:
writel(be32_to_cpup(val), ctx->regs + offset); writel(*(u32 *)val, ctx->regs + offset);
break; break;
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
case 8: case 8:
writeq(be64_to_cpup(val), ctx->regs + offset); writeq(*(u64 *)val, ctx->regs + offset);
break; break;
#endif #endif
default: default:
...@@ -83,7 +83,7 @@ static int regmap_mmio_read(void *context, ...@@ -83,7 +83,7 @@ static int regmap_mmio_read(void *context,
BUG_ON(reg_size != 4); BUG_ON(reg_size != 4);
offset = be32_to_cpup(reg); offset = *(u32 *)reg;
while (val_size) { while (val_size) {
switch (ctx->val_bytes) { switch (ctx->val_bytes) {
...@@ -91,14 +91,14 @@ static int regmap_mmio_read(void *context, ...@@ -91,14 +91,14 @@ static int regmap_mmio_read(void *context,
*(u8 *)val = readb(ctx->regs + offset); *(u8 *)val = readb(ctx->regs + offset);
break; break;
case 2: case 2:
*(u16 *)val = cpu_to_be16(readw(ctx->regs + offset)); *(u16 *)val = readw(ctx->regs + offset);
break; break;
case 4: case 4:
*(u32 *)val = cpu_to_be32(readl(ctx->regs + offset)); *(u32 *)val = readl(ctx->regs + offset);
break; break;
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
case 8: case 8:
*(u64 *)val = cpu_to_be32(readq(ctx->regs + offset)); *(u64 *)val = readq(ctx->regs + offset);
break; break;
#endif #endif
default: default:
...@@ -124,9 +124,11 @@ static struct regmap_bus regmap_mmio = { ...@@ -124,9 +124,11 @@ static struct regmap_bus regmap_mmio = {
.gather_write = regmap_mmio_gather_write, .gather_write = regmap_mmio_gather_write,
.read = regmap_mmio_read, .read = regmap_mmio_read,
.free_context = regmap_mmio_free_context, .free_context = regmap_mmio_free_context,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
}; };
struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
const struct regmap_config *config) const struct regmap_config *config)
{ {
struct regmap_mmio_context *ctx; struct regmap_mmio_context *ctx;
...@@ -162,7 +164,15 @@ struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, ...@@ -162,7 +164,15 @@ struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
if (config->reg_stride < min_stride) if (config->reg_stride < min_stride)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
ctx = kzalloc(GFP_KERNEL, sizeof(*ctx)); switch (config->reg_format_endian) {
case REGMAP_ENDIAN_DEFAULT:
case REGMAP_ENDIAN_NATIVE:
break;
default:
return ERR_PTR(-EINVAL);
}
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) if (!ctx)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
......
This diff is collapsed.
...@@ -14,12 +14,14 @@ ...@@ -14,12 +14,14 @@
*/ */
#include <linux/list.h> #include <linux/list.h>
#include <linux/rbtree.h>
struct module; struct module;
struct device; struct device;
struct i2c_client; struct i2c_client;
struct spi_device; struct spi_device;
struct regmap; struct regmap;
struct regmap_range_cfg;
/* An enum of all the supported cache types */ /* An enum of all the supported cache types */
enum regcache_type { enum regcache_type {
...@@ -43,6 +45,14 @@ struct reg_default { ...@@ -43,6 +45,14 @@ struct reg_default {
#ifdef CONFIG_REGMAP #ifdef CONFIG_REGMAP
enum regmap_endian {
/* Unspecified -> 0 -> Backwards compatible default */
REGMAP_ENDIAN_DEFAULT = 0,
REGMAP_ENDIAN_BIG,
REGMAP_ENDIAN_LITTLE,
REGMAP_ENDIAN_NATIVE,
};
/** /**
* Configuration for the register map of a device. * Configuration for the register map of a device.
* *
...@@ -84,6 +94,15 @@ struct reg_default { ...@@ -84,6 +94,15 @@ struct reg_default {
* @reg_defaults_raw: Power on reset values for registers (for use with * @reg_defaults_raw: Power on reset values for registers (for use with
* register cache support). * register cache support).
* @num_reg_defaults_raw: Number of elements in reg_defaults_raw. * @num_reg_defaults_raw: Number of elements in reg_defaults_raw.
* @reg_format_endian: Endianness for formatted register addresses. If this is
* DEFAULT, the @reg_format_endian_default value from the
* regmap bus is used.
* @val_format_endian: Endianness for formatted register values. If this is
* DEFAULT, the @reg_format_endian_default value from the
* regmap bus is used.
*
* @ranges: Array of configuration entries for virtual address ranges.
* @num_ranges: Number of range configuration entries.
*/ */
struct regmap_config { struct regmap_config {
const char *name; const char *name;
...@@ -109,6 +128,43 @@ struct regmap_config { ...@@ -109,6 +128,43 @@ struct regmap_config {
u8 write_flag_mask; u8 write_flag_mask;
bool use_single_rw; bool use_single_rw;
enum regmap_endian reg_format_endian;
enum regmap_endian val_format_endian;
const struct regmap_range_cfg *ranges;
unsigned int n_ranges;
};
/**
* Configuration for indirectly accessed or paged registers.
* Registers, mapped to this virtual range, are accessed in two steps:
* 1. page selector register update;
* 2. access through data window registers.
*
* @range_min: Address of the lowest register address in virtual range.
* @range_max: Address of the highest register in virtual range.
*
* @page_sel_reg: Register with selector field.
* @page_sel_mask: Bit shift for selector value.
* @page_sel_shift: Bit mask for selector value.
*
* @window_start: Address of first (lowest) register in data window.
* @window_len: Number of registers in data window.
*/
struct regmap_range_cfg {
/* Registers of virtual address range */
unsigned int range_min;
unsigned int range_max;
/* Page selector for indirect addressing */
unsigned int selector_reg;
unsigned int selector_mask;
int selector_shift;
/* Data window (per each page) */
unsigned int window_start;
unsigned int window_len;
}; };
typedef int (*regmap_hw_write)(void *context, const void *data, typedef int (*regmap_hw_write)(void *context, const void *data,
...@@ -133,6 +189,12 @@ typedef void (*regmap_hw_free_context)(void *context); ...@@ -133,6 +189,12 @@ typedef void (*regmap_hw_free_context)(void *context);
* data. * data.
* @read_flag_mask: Mask to be set in the top byte of the register when doing * @read_flag_mask: Mask to be set in the top byte of the register when doing
* a read. * a read.
* @reg_format_endian_default: Default endianness for formatted register
* addresses. Used when the regmap_config specifies DEFAULT. If this is
* DEFAULT, BIG is assumed.
* @val_format_endian_default: Default endianness for formatted register
* values. Used when the regmap_config specifies DEFAULT. If this is
* DEFAULT, BIG is assumed.
*/ */
struct regmap_bus { struct regmap_bus {
bool fast_io; bool fast_io;
...@@ -141,6 +203,8 @@ struct regmap_bus { ...@@ -141,6 +203,8 @@ struct regmap_bus {
regmap_hw_read read; regmap_hw_read read;
regmap_hw_free_context free_context; regmap_hw_free_context free_context;
u8 read_flag_mask; u8 read_flag_mask;
enum regmap_endian reg_format_endian_default;
enum regmap_endian val_format_endian_default;
}; };
struct regmap *regmap_init(struct device *dev, struct regmap *regmap_init(struct device *dev,
...@@ -219,6 +283,7 @@ struct regmap_irq { ...@@ -219,6 +283,7 @@ struct regmap_irq {
* @status_base: Base status register address. * @status_base: Base status register address.
* @mask_base: Base mask register address. * @mask_base: Base mask register address.
* @ack_base: Base ack address. If zero then the chip is clear on read. * @ack_base: Base ack address. If zero then the chip is clear on read.
* @wake_base: Base address for wake enables. If zero unsupported.
* @irq_reg_stride: Stride to use for chips where registers are not contiguous. * @irq_reg_stride: Stride to use for chips where registers are not contiguous.
* *
* @num_regs: Number of registers in each control bank. * @num_regs: Number of registers in each control bank.
...@@ -232,6 +297,7 @@ struct regmap_irq_chip { ...@@ -232,6 +297,7 @@ struct regmap_irq_chip {
unsigned int status_base; unsigned int status_base;
unsigned int mask_base; unsigned int mask_base;
unsigned int ack_base; unsigned int ack_base;
unsigned int wake_base;
unsigned int irq_reg_stride; unsigned int irq_reg_stride;
int num_regs; int num_regs;
...@@ -243,7 +309,7 @@ struct regmap_irq_chip { ...@@ -243,7 +309,7 @@ struct regmap_irq_chip {
struct regmap_irq_chip_data; struct regmap_irq_chip_data;
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
int irq_base, struct regmap_irq_chip *chip, int irq_base, const struct regmap_irq_chip *chip,
struct regmap_irq_chip_data **data); struct regmap_irq_chip_data **data);
void regmap_del_irq_chip(int irq, 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_chip_get_base(struct regmap_irq_chip_data *data);
...@@ -361,7 +427,6 @@ static inline int regmap_register_patch(struct regmap *map, ...@@ -361,7 +427,6 @@ static inline int regmap_register_patch(struct regmap *map,
static inline struct regmap *dev_get_regmap(struct device *dev, static inline struct regmap *dev_get_regmap(struct device *dev,
const char *name) const char *name)
{ {
WARN_ONCE(1, "regmap API is disabled");
return NULL; return NULL;
} }
......
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