mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-30 11:16:48 +07:00
regmap: Updates for 3.6
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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJQDEZ4AAoJEBus8iNuMP3diagQAIsrFy8zV+7JikPJN5H70gsj lvlTzmT0ldMYhheuciBCTgyQCSZd2MH6FpfI8El0sYk9TsZyBASiOMNBvRPhEubP DetnEBY85mtU9Wryi+AuGrFnt00us4+wf37Ubf5AXF5q+ZcNQzJaR59iVcdghrf6 Dj0kdErHuwziBF3gmm4MGX72qP15QlG9GtO2MwiuLtsnJ34lNlTnglClxUaKX8vi eFY+wfnsfam8CSbfs9XewUsvdA1c2o+7/pm3pVOuns6FA7CyfeaWQixmo7VcKLHn g70Qezc+HNjpxHcZaP94Oaq1dC/32rwHXZweSfePj6c3y4Mr2czCTF4jPUtV6cfA LQdRcCi73iKwI1XrdUEqJOojEHErmeL+YZ9SzTSroNHv54V7KvMHz99gYs31f+gH R44ZxbTWKhpjeFmDUqv/rZ891pKUli4AHNJi9jOTgHmEdBK+b0dDUmPZhkz2x19N 8KOpr5T3yDbancULdpfkp3vOTtJE3eACFFytH9ZtM7SeCoYWtwDSR3ALpmQ41JFk p+ITOrsBYC3kiwjMrBEVd6HFvYl60Q8RDwHoZpqk2PNpy9yuyYqFXjmdPsjxf/gw WGTcYEceocsylJ20GnZ3Btkk3uANRPmPoQZdhOO/IB9cnCw7m9EDZqNBpY/LLiIB /jWn4E28pSxTnVYkd7b9 =uSSe -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
7cd58b0a3b
@ -95,6 +95,9 @@ struct regmap {
|
||||
|
||||
/* if set, converts bulk rw to single rw */
|
||||
bool use_single_rw;
|
||||
|
||||
struct rb_root range_tree;
|
||||
void *selector_work_buf; /* Scratch buffer used for selector */
|
||||
};
|
||||
|
||||
struct regcache_ops {
|
||||
@ -115,6 +118,20 @@ bool regmap_precious(struct regmap *map, unsigned int reg);
|
||||
int _regmap_write(struct regmap *map, unsigned int reg,
|
||||
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
|
||||
extern void regmap_debugfs_initcall(void);
|
||||
extern void regmap_debugfs_init(struct regmap *map, const char *name);
|
||||
|
@ -24,14 +24,18 @@ struct regmap_irq_chip_data {
|
||||
struct mutex lock;
|
||||
|
||||
struct regmap *map;
|
||||
struct regmap_irq_chip *chip;
|
||||
const struct regmap_irq_chip *chip;
|
||||
|
||||
int irq_base;
|
||||
struct irq_domain *domain;
|
||||
|
||||
int irq;
|
||||
int wake_count;
|
||||
|
||||
unsigned int *status_buf;
|
||||
unsigned int *mask_buf;
|
||||
unsigned int *mask_buf_def;
|
||||
unsigned int *wake_buf;
|
||||
|
||||
unsigned int irq_reg_stride;
|
||||
};
|
||||
@ -71,6 +75,16 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
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 = {
|
||||
.name = "regmap",
|
||||
.irq_bus_lock = regmap_irq_lock,
|
||||
.irq_bus_sync_unlock = regmap_irq_sync_unlock,
|
||||
.irq_disable = regmap_irq_disable,
|
||||
.irq_enable = regmap_irq_enable,
|
||||
.irq_set_wake = regmap_irq_set_wake,
|
||||
};
|
||||
|
||||
static irqreturn_t regmap_irq_thread(int irq, void *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;
|
||||
int ret, i;
|
||||
bool handled = false;
|
||||
@ -195,7 +232,7 @@ static struct irq_domain_ops regmap_domain_ops = {
|
||||
* register values used by the IRQ controller over suspend and resume.
|
||||
*/
|
||||
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
||||
int irq_base, struct regmap_irq_chip *chip,
|
||||
int irq_base, const struct regmap_irq_chip *chip,
|
||||
struct regmap_irq_chip_data **data)
|
||||
{
|
||||
struct regmap_irq_chip_data *d;
|
||||
@ -240,6 +277,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
||||
if (!d->mask_buf_def)
|
||||
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->chip = chip;
|
||||
d->irq_base = irq_base;
|
||||
@ -294,6 +339,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
||||
err_domain:
|
||||
/* Should really dispose of the domain but... */
|
||||
err_alloc:
|
||||
kfree(d->wake_buf);
|
||||
kfree(d->mask_buf_def);
|
||||
kfree(d->mask_buf);
|
||||
kfree(d->status_buf);
|
||||
@ -315,6 +361,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
|
||||
|
||||
free_irq(irq, d);
|
||||
/* We should unmap the domain but... */
|
||||
kfree(d->wake_buf);
|
||||
kfree(d->mask_buf_def);
|
||||
kfree(d->mask_buf);
|
||||
kfree(d->status_buf);
|
||||
@ -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)
|
||||
{
|
||||
/* Handle holes in the IRQ list */
|
||||
if (!data->chip->irqs[irq].mask)
|
||||
return -EINVAL;
|
||||
|
||||
return irq_create_mapping(data->domain, irq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_irq_get_virq);
|
||||
|
@ -37,7 +37,7 @@ static int regmap_mmio_gather_write(void *context,
|
||||
|
||||
BUG_ON(reg_size != 4);
|
||||
|
||||
offset = be32_to_cpup(reg);
|
||||
offset = *(u32 *)reg;
|
||||
|
||||
while (val_size) {
|
||||
switch (ctx->val_bytes) {
|
||||
@ -45,14 +45,14 @@ static int regmap_mmio_gather_write(void *context,
|
||||
writeb(*(u8 *)val, ctx->regs + offset);
|
||||
break;
|
||||
case 2:
|
||||
writew(be16_to_cpup(val), ctx->regs + offset);
|
||||
writew(*(u16 *)val, ctx->regs + offset);
|
||||
break;
|
||||
case 4:
|
||||
writel(be32_to_cpup(val), ctx->regs + offset);
|
||||
writel(*(u32 *)val, ctx->regs + offset);
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 8:
|
||||
writeq(be64_to_cpup(val), ctx->regs + offset);
|
||||
writeq(*(u64 *)val, ctx->regs + offset);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@ -83,7 +83,7 @@ static int regmap_mmio_read(void *context,
|
||||
|
||||
BUG_ON(reg_size != 4);
|
||||
|
||||
offset = be32_to_cpup(reg);
|
||||
offset = *(u32 *)reg;
|
||||
|
||||
while (val_size) {
|
||||
switch (ctx->val_bytes) {
|
||||
@ -91,14 +91,14 @@ static int regmap_mmio_read(void *context,
|
||||
*(u8 *)val = readb(ctx->regs + offset);
|
||||
break;
|
||||
case 2:
|
||||
*(u16 *)val = cpu_to_be16(readw(ctx->regs + offset));
|
||||
*(u16 *)val = readw(ctx->regs + offset);
|
||||
break;
|
||||
case 4:
|
||||
*(u32 *)val = cpu_to_be32(readl(ctx->regs + offset));
|
||||
*(u32 *)val = readl(ctx->regs + offset);
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 8:
|
||||
*(u64 *)val = cpu_to_be32(readq(ctx->regs + offset));
|
||||
*(u64 *)val = readq(ctx->regs + offset);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@ -124,9 +124,11 @@ static struct regmap_bus regmap_mmio = {
|
||||
.gather_write = regmap_mmio_gather_write,
|
||||
.read = regmap_mmio_read,
|
||||
.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)
|
||||
{
|
||||
struct regmap_mmio_context *ctx;
|
||||
@ -162,7 +164,15 @@ struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
|
||||
if (config->reg_stride < min_stride)
|
||||
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)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -15,12 +15,25 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/rbtree.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/regmap.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* Sometimes for failures during very early init the trace
|
||||
* infrastructure isn't available early enough to be used. For this
|
||||
* sort of problem defining LOG_DEVICE will add printks for basic
|
||||
* register I/O on a specific device.
|
||||
*/
|
||||
#undef LOG_DEVICE
|
||||
|
||||
static int _regmap_update_bits(struct regmap *map, unsigned int reg,
|
||||
unsigned int mask, unsigned int val,
|
||||
bool *change);
|
||||
|
||||
bool regmap_writeable(struct regmap *map, unsigned int reg)
|
||||
{
|
||||
if (map->max_register && reg > map->max_register)
|
||||
@ -119,13 +132,19 @@ static void regmap_format_8(void *buf, unsigned int val, unsigned int shift)
|
||||
b[0] = val << shift;
|
||||
}
|
||||
|
||||
static void regmap_format_16(void *buf, unsigned int val, unsigned int shift)
|
||||
static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
__be16 *b = buf;
|
||||
|
||||
b[0] = cpu_to_be16(val << shift);
|
||||
}
|
||||
|
||||
static void regmap_format_16_native(void *buf, unsigned int val,
|
||||
unsigned int shift)
|
||||
{
|
||||
*(u16 *)buf = val << shift;
|
||||
}
|
||||
|
||||
static void regmap_format_24(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
u8 *b = buf;
|
||||
@ -137,13 +156,19 @@ static void regmap_format_24(void *buf, unsigned int val, unsigned int shift)
|
||||
b[2] = val;
|
||||
}
|
||||
|
||||
static void regmap_format_32(void *buf, unsigned int val, unsigned int shift)
|
||||
static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
__be32 *b = buf;
|
||||
|
||||
b[0] = cpu_to_be32(val << shift);
|
||||
}
|
||||
|
||||
static void regmap_format_32_native(void *buf, unsigned int val,
|
||||
unsigned int shift)
|
||||
{
|
||||
*(u32 *)buf = val << shift;
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_8(void *buf)
|
||||
{
|
||||
u8 *b = buf;
|
||||
@ -151,7 +176,7 @@ static unsigned int regmap_parse_8(void *buf)
|
||||
return b[0];
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_16(void *buf)
|
||||
static unsigned int regmap_parse_16_be(void *buf)
|
||||
{
|
||||
__be16 *b = buf;
|
||||
|
||||
@ -160,6 +185,11 @@ static unsigned int regmap_parse_16(void *buf)
|
||||
return b[0];
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_16_native(void *buf)
|
||||
{
|
||||
return *(u16 *)buf;
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_24(void *buf)
|
||||
{
|
||||
u8 *b = buf;
|
||||
@ -170,7 +200,7 @@ static unsigned int regmap_parse_24(void *buf)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_32(void *buf)
|
||||
static unsigned int regmap_parse_32_be(void *buf)
|
||||
{
|
||||
__be32 *b = buf;
|
||||
|
||||
@ -179,6 +209,11 @@ static unsigned int regmap_parse_32(void *buf)
|
||||
return b[0];
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_32_native(void *buf)
|
||||
{
|
||||
return *(u32 *)buf;
|
||||
}
|
||||
|
||||
static void regmap_lock_mutex(struct regmap *map)
|
||||
{
|
||||
mutex_lock(&map->mutex);
|
||||
@ -208,6 +243,67 @@ static void dev_get_regmap_release(struct device *dev, void *res)
|
||||
*/
|
||||
}
|
||||
|
||||
static bool _regmap_range_add(struct regmap *map,
|
||||
struct regmap_range_node *data)
|
||||
{
|
||||
struct rb_root *root = &map->range_tree;
|
||||
struct rb_node **new = &(root->rb_node), *parent = NULL;
|
||||
|
||||
while (*new) {
|
||||
struct regmap_range_node *this =
|
||||
container_of(*new, struct regmap_range_node, node);
|
||||
|
||||
parent = *new;
|
||||
if (data->range_max < this->range_min)
|
||||
new = &((*new)->rb_left);
|
||||
else if (data->range_min > this->range_max)
|
||||
new = &((*new)->rb_right);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
rb_link_node(&data->node, parent, new);
|
||||
rb_insert_color(&data->node, root);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct regmap_range_node *_regmap_range_lookup(struct regmap *map,
|
||||
unsigned int reg)
|
||||
{
|
||||
struct rb_node *node = map->range_tree.rb_node;
|
||||
|
||||
while (node) {
|
||||
struct regmap_range_node *this =
|
||||
container_of(node, struct regmap_range_node, node);
|
||||
|
||||
if (reg < this->range_min)
|
||||
node = node->rb_left;
|
||||
else if (reg > this->range_max)
|
||||
node = node->rb_right;
|
||||
else
|
||||
return this;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void regmap_range_exit(struct regmap *map)
|
||||
{
|
||||
struct rb_node *next;
|
||||
struct regmap_range_node *range_node;
|
||||
|
||||
next = rb_first(&map->range_tree);
|
||||
while (next) {
|
||||
range_node = rb_entry(next, struct regmap_range_node, node);
|
||||
next = rb_next(&range_node->node);
|
||||
rb_erase(&range_node->node, &map->range_tree);
|
||||
kfree(range_node);
|
||||
}
|
||||
|
||||
kfree(map->selector_work_buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* regmap_init(): Initialise register map
|
||||
*
|
||||
@ -227,6 +323,8 @@ struct regmap *regmap_init(struct device *dev,
|
||||
{
|
||||
struct regmap *map, **m;
|
||||
int ret = -EINVAL;
|
||||
enum regmap_endian reg_endian, val_endian;
|
||||
int i, j;
|
||||
|
||||
if (!bus || !config)
|
||||
goto err;
|
||||
@ -275,6 +373,18 @@ struct regmap *regmap_init(struct device *dev,
|
||||
map->read_flag_mask = bus->read_flag_mask;
|
||||
}
|
||||
|
||||
reg_endian = config->reg_format_endian;
|
||||
if (reg_endian == REGMAP_ENDIAN_DEFAULT)
|
||||
reg_endian = bus->reg_format_endian_default;
|
||||
if (reg_endian == REGMAP_ENDIAN_DEFAULT)
|
||||
reg_endian = REGMAP_ENDIAN_BIG;
|
||||
|
||||
val_endian = config->val_format_endian;
|
||||
if (val_endian == REGMAP_ENDIAN_DEFAULT)
|
||||
val_endian = bus->val_format_endian_default;
|
||||
if (val_endian == REGMAP_ENDIAN_DEFAULT)
|
||||
val_endian = REGMAP_ENDIAN_BIG;
|
||||
|
||||
switch (config->reg_bits + map->reg_shift) {
|
||||
case 2:
|
||||
switch (config->val_bits) {
|
||||
@ -321,11 +431,29 @@ struct regmap *regmap_init(struct device *dev,
|
||||
break;
|
||||
|
||||
case 16:
|
||||
map->format.format_reg = regmap_format_16;
|
||||
switch (reg_endian) {
|
||||
case REGMAP_ENDIAN_BIG:
|
||||
map->format.format_reg = regmap_format_16_be;
|
||||
break;
|
||||
case REGMAP_ENDIAN_NATIVE:
|
||||
map->format.format_reg = regmap_format_16_native;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
}
|
||||
break;
|
||||
|
||||
case 32:
|
||||
map->format.format_reg = regmap_format_32;
|
||||
switch (reg_endian) {
|
||||
case REGMAP_ENDIAN_BIG:
|
||||
map->format.format_reg = regmap_format_32_be;
|
||||
break;
|
||||
case REGMAP_ENDIAN_NATIVE:
|
||||
map->format.format_reg = regmap_format_32_native;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -338,21 +466,47 @@ struct regmap *regmap_init(struct device *dev,
|
||||
map->format.parse_val = regmap_parse_8;
|
||||
break;
|
||||
case 16:
|
||||
map->format.format_val = regmap_format_16;
|
||||
map->format.parse_val = regmap_parse_16;
|
||||
switch (val_endian) {
|
||||
case REGMAP_ENDIAN_BIG:
|
||||
map->format.format_val = regmap_format_16_be;
|
||||
map->format.parse_val = regmap_parse_16_be;
|
||||
break;
|
||||
case REGMAP_ENDIAN_NATIVE:
|
||||
map->format.format_val = regmap_format_16_native;
|
||||
map->format.parse_val = regmap_parse_16_native;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
if (val_endian != REGMAP_ENDIAN_BIG)
|
||||
goto err_map;
|
||||
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;
|
||||
switch (val_endian) {
|
||||
case REGMAP_ENDIAN_BIG:
|
||||
map->format.format_val = regmap_format_32_be;
|
||||
map->format.parse_val = regmap_parse_32_be;
|
||||
break;
|
||||
case REGMAP_ENDIAN_NATIVE:
|
||||
map->format.format_val = regmap_format_32_native;
|
||||
map->format.parse_val = regmap_parse_32_native;
|
||||
break;
|
||||
default:
|
||||
goto err_map;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (map->format.format_write)
|
||||
if (map->format.format_write) {
|
||||
if ((reg_endian != REGMAP_ENDIAN_BIG) ||
|
||||
(val_endian != REGMAP_ENDIAN_BIG))
|
||||
goto err_map;
|
||||
map->use_single_rw = true;
|
||||
}
|
||||
|
||||
if (!map->format.format_write &&
|
||||
!(map->format.format_reg && map->format.format_val))
|
||||
@ -364,27 +518,88 @@ struct regmap *regmap_init(struct device *dev,
|
||||
goto err_map;
|
||||
}
|
||||
|
||||
regmap_debugfs_init(map, config->name);
|
||||
map->range_tree = RB_ROOT;
|
||||
for (i = 0; i < config->n_ranges; i++) {
|
||||
const struct regmap_range_cfg *range_cfg = &config->ranges[i];
|
||||
struct regmap_range_node *new;
|
||||
|
||||
/* Sanity check */
|
||||
if (range_cfg->range_max < range_cfg->range_min ||
|
||||
range_cfg->range_max > map->max_register ||
|
||||
range_cfg->selector_reg > map->max_register ||
|
||||
range_cfg->window_len == 0)
|
||||
goto err_range;
|
||||
|
||||
/* Make sure, that this register range has no selector
|
||||
or data window within its boundary */
|
||||
for (j = 0; j < config->n_ranges; j++) {
|
||||
unsigned sel_reg = config->ranges[j].selector_reg;
|
||||
unsigned win_min = config->ranges[j].window_start;
|
||||
unsigned win_max = win_min +
|
||||
config->ranges[j].window_len - 1;
|
||||
|
||||
if (range_cfg->range_min <= sel_reg &&
|
||||
sel_reg <= range_cfg->range_max) {
|
||||
goto err_range;
|
||||
}
|
||||
|
||||
if (!(win_max < range_cfg->range_min ||
|
||||
win_min > range_cfg->range_max)) {
|
||||
goto err_range;
|
||||
}
|
||||
}
|
||||
|
||||
new = kzalloc(sizeof(*new), GFP_KERNEL);
|
||||
if (new == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_range;
|
||||
}
|
||||
|
||||
new->range_min = range_cfg->range_min;
|
||||
new->range_max = range_cfg->range_max;
|
||||
new->selector_reg = range_cfg->selector_reg;
|
||||
new->selector_mask = range_cfg->selector_mask;
|
||||
new->selector_shift = range_cfg->selector_shift;
|
||||
new->window_start = range_cfg->window_start;
|
||||
new->window_len = range_cfg->window_len;
|
||||
|
||||
if (_regmap_range_add(map, new) == false) {
|
||||
kfree(new);
|
||||
goto err_range;
|
||||
}
|
||||
|
||||
if (map->selector_work_buf == NULL) {
|
||||
map->selector_work_buf =
|
||||
kzalloc(map->format.buf_size, GFP_KERNEL);
|
||||
if (map->selector_work_buf == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_range;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = regcache_init(map, config);
|
||||
if (ret < 0)
|
||||
goto err_debugfs;
|
||||
goto err_range;
|
||||
|
||||
regmap_debugfs_init(map, config->name);
|
||||
|
||||
/* 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;
|
||||
goto err_debugfs;
|
||||
}
|
||||
*m = map;
|
||||
devres_add(dev, m);
|
||||
|
||||
return map;
|
||||
|
||||
err_cache:
|
||||
regcache_exit(map);
|
||||
err_debugfs:
|
||||
regmap_debugfs_exit(map);
|
||||
regcache_exit(map);
|
||||
err_range:
|
||||
regmap_range_exit(map);
|
||||
kfree(map->work_buf);
|
||||
err_map:
|
||||
kfree(map);
|
||||
@ -481,6 +696,7 @@ void regmap_exit(struct regmap *map)
|
||||
{
|
||||
regcache_exit(map);
|
||||
regmap_debugfs_exit(map);
|
||||
regmap_range_exit(map);
|
||||
if (map->bus->free_context)
|
||||
map->bus->free_context(map->bus_context);
|
||||
kfree(map->work_buf);
|
||||
@ -526,6 +742,57 @@ struct regmap *dev_get_regmap(struct device *dev, const char *name)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_get_regmap);
|
||||
|
||||
static int _regmap_select_page(struct regmap *map, unsigned int *reg,
|
||||
unsigned int val_num)
|
||||
{
|
||||
struct regmap_range_node *range;
|
||||
void *orig_work_buf;
|
||||
unsigned int win_offset;
|
||||
unsigned int win_page;
|
||||
bool page_chg;
|
||||
int ret;
|
||||
|
||||
range = _regmap_range_lookup(map, *reg);
|
||||
if (range) {
|
||||
win_offset = (*reg - range->range_min) % range->window_len;
|
||||
win_page = (*reg - range->range_min) / range->window_len;
|
||||
|
||||
if (val_num > 1) {
|
||||
/* Bulk write shouldn't cross range boundary */
|
||||
if (*reg + val_num - 1 > range->range_max)
|
||||
return -EINVAL;
|
||||
|
||||
/* ... or single page boundary */
|
||||
if (val_num > range->window_len - win_offset)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* It is possible to have selector register inside data window.
|
||||
In that case, selector register is located on every page and
|
||||
it needs no page switching, when accessed alone. */
|
||||
if (val_num > 1 ||
|
||||
range->window_start + win_offset != range->selector_reg) {
|
||||
/* Use separate work_buf during page switching */
|
||||
orig_work_buf = map->work_buf;
|
||||
map->work_buf = map->selector_work_buf;
|
||||
|
||||
ret = _regmap_update_bits(map, range->selector_reg,
|
||||
range->selector_mask,
|
||||
win_page << range->selector_shift,
|
||||
&page_chg);
|
||||
|
||||
map->work_buf = orig_work_buf;
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
*reg = range->window_start + win_offset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
@ -563,6 +830,10 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||
}
|
||||
}
|
||||
|
||||
ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
map->format.format_reg(map->work_buf, reg, map->reg_shift);
|
||||
|
||||
u8[0] |= map->write_flag_mask;
|
||||
@ -623,9 +894,18 @@ int _regmap_write(struct regmap *map, unsigned int reg,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LOG_DEVICE
|
||||
if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
|
||||
dev_info(map->dev, "%x <= %x\n", reg, val);
|
||||
#endif
|
||||
|
||||
trace_regmap_reg_write(map->dev, reg, val);
|
||||
|
||||
if (map->format.format_write) {
|
||||
ret = _regmap_select_page(map, ®, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
map->format.format_write(map, reg, val);
|
||||
|
||||
trace_regmap_hw_write_start(map->dev, reg, 1);
|
||||
@ -783,6 +1063,10 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
||||
u8 *u8 = map->work_buf;
|
||||
int ret;
|
||||
|
||||
ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
map->format.format_reg(map->work_buf, reg, map->reg_shift);
|
||||
|
||||
/*
|
||||
@ -826,6 +1110,12 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
|
||||
ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
|
||||
if (ret == 0) {
|
||||
*val = map->format.parse_val(map->work_buf);
|
||||
|
||||
#ifdef LOG_DEVICE
|
||||
if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
|
||||
dev_info(map->dev, "%x => %x\n", reg, *val);
|
||||
#endif
|
||||
|
||||
trace_regmap_reg_read(map->dev, reg, *val);
|
||||
}
|
||||
|
||||
@ -982,11 +1272,9 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
|
||||
int ret;
|
||||
unsigned int tmp, orig;
|
||||
|
||||
map->lock(map);
|
||||
|
||||
ret = _regmap_read(map, reg, &orig);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
tmp = orig & ~mask;
|
||||
tmp |= val & mask;
|
||||
@ -998,9 +1286,6 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
|
||||
*change = false;
|
||||
}
|
||||
|
||||
out:
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1018,7 +1303,13 @@ int regmap_update_bits(struct regmap *map, unsigned int reg,
|
||||
unsigned int mask, unsigned int val)
|
||||
{
|
||||
bool change;
|
||||
return _regmap_update_bits(map, reg, mask, val, &change);
|
||||
int ret;
|
||||
|
||||
map->lock(map);
|
||||
ret = _regmap_update_bits(map, reg, mask, val, &change);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_update_bits);
|
||||
|
||||
@ -1038,7 +1329,12 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
|
||||
unsigned int mask, unsigned int val,
|
||||
bool *change)
|
||||
{
|
||||
return _regmap_update_bits(map, reg, mask, val, change);
|
||||
int ret;
|
||||
|
||||
map->lock(map);
|
||||
ret = _regmap_update_bits(map, reg, mask, val, change);
|
||||
map->unlock(map);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_update_bits_check);
|
||||
|
||||
|
@ -14,12 +14,14 @@
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
|
||||
struct module;
|
||||
struct device;
|
||||
struct i2c_client;
|
||||
struct spi_device;
|
||||
struct regmap;
|
||||
struct regmap_range_cfg;
|
||||
|
||||
/* An enum of all the supported cache types */
|
||||
enum regcache_type {
|
||||
@ -43,6 +45,14 @@ struct reg_default {
|
||||
|
||||
#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.
|
||||
*
|
||||
@ -84,6 +94,15 @@ struct reg_default {
|
||||
* @reg_defaults_raw: Power on reset values for registers (for use with
|
||||
* register cache support).
|
||||
* @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 {
|
||||
const char *name;
|
||||
@ -109,6 +128,43 @@ struct regmap_config {
|
||||
u8 write_flag_mask;
|
||||
|
||||
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,
|
||||
@ -133,6 +189,12 @@ typedef void (*regmap_hw_free_context)(void *context);
|
||||
* data.
|
||||
* @read_flag_mask: Mask to be set in the top byte of the register when doing
|
||||
* 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 {
|
||||
bool fast_io;
|
||||
@ -141,6 +203,8 @@ struct regmap_bus {
|
||||
regmap_hw_read read;
|
||||
regmap_hw_free_context free_context;
|
||||
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,
|
||||
@ -219,6 +283,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.
|
||||
* @wake_base: Base address for wake enables. If zero unsupported.
|
||||
* @irq_reg_stride: Stride to use for chips where registers are not contiguous.
|
||||
*
|
||||
* @num_regs: Number of registers in each control bank.
|
||||
@ -232,6 +297,7 @@ struct regmap_irq_chip {
|
||||
unsigned int status_base;
|
||||
unsigned int mask_base;
|
||||
unsigned int ack_base;
|
||||
unsigned int wake_base;
|
||||
unsigned int irq_reg_stride;
|
||||
|
||||
int num_regs;
|
||||
@ -243,7 +309,7 @@ struct regmap_irq_chip {
|
||||
struct regmap_irq_chip_data;
|
||||
|
||||
int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
||||
int irq_base, struct regmap_irq_chip *chip,
|
||||
int irq_base, const struct regmap_irq_chip *chip,
|
||||
struct regmap_irq_chip_data **data);
|
||||
void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data);
|
||||
int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data);
|
||||
@ -361,7 +427,6 @@ static inline int regmap_register_patch(struct regmap *map,
|
||||
static inline struct regmap *dev_get_regmap(struct device *dev,
|
||||
const char *name)
|
||||
{
|
||||
WARN_ONCE(1, "regmap API is disabled");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user