Commit e129e413 authored by Mark Brown's avatar Mark Brown

regmap-irq cleanups and refactoring

Merge series from Aidan MacDonald <aidanmacdonald.0x0@gmail.com>:

This series is an attempt at cleaning up the regmap-irq API in order
to simplify things and consolidate existing features, while at the
same time generalizing it to support a wider range of hardware.

There is a new system for IRQ type configuration, some tweaks to
unmask registers so they're more intuitive and useful, and a new
callback for calculating register addresses. There's also a few
minor code cleanups in here.

In v2 I've taken the approach of adding new features and deprecating
existing ones rather than removing them aggressively. Warnings will
be issued for any drivers that use deprecated features, but they'll
otherwise continue to function normally.

One important caveat: not all of these changes are tested beyond
compile testing, since I don't have hardware to exercise all of
the features.
parents a5201d42 48e014ee
...@@ -30,6 +30,9 @@ struct regmap_irq_chip_data { ...@@ -30,6 +30,9 @@ struct regmap_irq_chip_data {
int irq; int irq;
int wake_count; int wake_count;
unsigned int mask_base;
unsigned int unmask_base;
void *status_reg_buf; void *status_reg_buf;
unsigned int *main_status_buf; unsigned int *main_status_buf;
unsigned int *status_buf; unsigned int *status_buf;
...@@ -39,33 +42,15 @@ struct regmap_irq_chip_data { ...@@ -39,33 +42,15 @@ struct regmap_irq_chip_data {
unsigned int *type_buf; unsigned int *type_buf;
unsigned int *type_buf_def; unsigned int *type_buf_def;
unsigned int **virt_buf; unsigned int **virt_buf;
unsigned int **config_buf;
unsigned int irq_reg_stride; unsigned int irq_reg_stride;
unsigned int type_reg_stride;
bool clear_status:1; unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data,
}; unsigned int base, int index);
static int sub_irq_reg(struct regmap_irq_chip_data *data, unsigned int clear_status:1;
unsigned int base_reg, int i) };
{
const struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
struct regmap_irq_sub_irq_map *subreg;
unsigned int offset;
int reg = 0;
if (!chip->sub_reg_offsets || !chip->not_fixed_stride) {
/* Assume linear mapping */
reg = base_reg + (i * map->reg_stride * data->irq_reg_stride);
} else {
subreg = &chip->sub_reg_offsets[i];
offset = subreg->offset[0];
reg = base_reg + offset;
}
return reg;
}
static inline const static inline const
struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,
...@@ -74,21 +59,25 @@ struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, ...@@ -74,21 +59,25 @@ struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,
return &data->chip->irqs[irq]; return &data->chip->irqs[irq];
} }
static void regmap_irq_lock(struct irq_data *data) static bool regmap_irq_can_bulk_read_status(struct regmap_irq_chip_data *data)
{ {
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap *map = data->map;
mutex_lock(&d->lock); /*
* While possible that a user-defined ->get_irq_reg() callback might
* be linear enough to support bulk reads, most of the time it won't.
* Therefore only allow them if the default callback is being used.
*/
return data->irq_reg_stride == 1 && map->reg_stride == 1 &&
data->get_irq_reg == regmap_irq_get_irq_reg_linear &&
!map->use_single_read;
} }
static int regmap_irq_update_bits(struct regmap_irq_chip_data *d, static void regmap_irq_lock(struct irq_data *data)
unsigned int reg, unsigned int mask,
unsigned int val)
{ {
if (d->chip->mask_writeonly) struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
return regmap_write_bits(d->map, reg, mask, val);
else mutex_lock(&d->lock);
return regmap_update_bits(d->map, reg, mask, val);
} }
static void regmap_irq_sync_unlock(struct irq_data *data) static void regmap_irq_sync_unlock(struct irq_data *data)
...@@ -97,7 +86,6 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -97,7 +86,6 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
struct regmap *map = d->map; struct regmap *map = d->map;
int i, j, ret; int i, j, ret;
u32 reg; u32 reg;
u32 unmask_offset;
u32 val; u32 val;
if (d->chip->runtime_pm) { if (d->chip->runtime_pm) {
...@@ -109,7 +97,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -109,7 +97,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
if (d->clear_status) { if (d->clear_status) {
for (i = 0; i < d->chip->num_regs; i++) { for (i = 0; i < d->chip->num_regs; i++) {
reg = sub_irq_reg(d, d->chip->status_base, i); reg = d->get_irq_reg(d, d->chip->status_base, i);
ret = regmap_read(map, reg, &val); ret = regmap_read(map, reg, &val);
if (ret) if (ret)
...@@ -126,44 +114,32 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -126,44 +114,32 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* suppress pointless writes. * suppress pointless writes.
*/ */
for (i = 0; i < d->chip->num_regs; i++) { for (i = 0; i < d->chip->num_regs; i++) {
if (!d->chip->mask_base) if (d->mask_base) {
continue; reg = d->get_irq_reg(d, d->mask_base, i);
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], d->mask_buf[i]);
if (ret)
dev_err(d->map->dev, "Failed to sync masks in %x\n",
reg);
}
reg = sub_irq_reg(d, d->chip->mask_base, i); if (d->unmask_base) {
if (d->chip->mask_invert) { reg = d->get_irq_reg(d, d->unmask_base, i);
ret = regmap_irq_update_bits(d, reg, ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], ~d->mask_buf[i]);
} else if (d->chip->unmask_base) {
/* set mask with mask_base register */
ret = regmap_irq_update_bits(d, reg,
d->mask_buf_def[i], ~d->mask_buf[i]); d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret < 0) if (ret)
dev_err(d->map->dev, dev_err(d->map->dev, "Failed to sync masks in %x\n",
"Failed to sync unmasks in %x\n",
reg); reg);
unmask_offset = d->chip->unmask_base -
d->chip->mask_base;
/* clear mask with unmask_base register */
ret = regmap_irq_update_bits(d,
reg + unmask_offset,
d->mask_buf_def[i],
d->mask_buf[i]);
} else {
ret = regmap_irq_update_bits(d, reg,
d->mask_buf_def[i], d->mask_buf[i]);
} }
if (ret != 0)
dev_err(d->map->dev, "Failed to sync masks in %x\n",
reg);
reg = sub_irq_reg(d, d->chip->wake_base, i); reg = d->get_irq_reg(d, d->chip->wake_base, i);
if (d->wake_buf) { if (d->wake_buf) {
if (d->chip->wake_invert) if (d->chip->wake_invert)
ret = regmap_irq_update_bits(d, reg, ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], d->mask_buf_def[i],
~d->wake_buf[i]); ~d->wake_buf[i]);
else else
ret = regmap_irq_update_bits(d, reg, ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], d->mask_buf_def[i],
d->wake_buf[i]); d->wake_buf[i]);
if (ret != 0) if (ret != 0)
...@@ -180,7 +156,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -180,7 +156,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* it'll be ignored in irq handler, then may introduce irq storm * it'll be ignored in irq handler, then may introduce irq storm
*/ */
if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) { if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) {
reg = sub_irq_reg(d, d->chip->ack_base, i); reg = d->get_irq_reg(d, d->chip->ack_base, i);
/* some chips ack by write 0 */ /* some chips ack by write 0 */
if (d->chip->ack_invert) if (d->chip->ack_invert)
...@@ -204,12 +180,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -204,12 +180,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
for (i = 0; i < d->chip->num_type_reg; i++) { for (i = 0; i < d->chip->num_type_reg; i++) {
if (!d->type_buf_def[i]) if (!d->type_buf_def[i])
continue; continue;
reg = sub_irq_reg(d, d->chip->type_base, i); reg = d->get_irq_reg(d, d->chip->type_base, i);
if (d->chip->type_invert) if (d->chip->type_invert)
ret = regmap_irq_update_bits(d, reg, ret = regmap_update_bits(d->map, reg,
d->type_buf_def[i], ~d->type_buf[i]); d->type_buf_def[i], ~d->type_buf[i]);
else else
ret = regmap_irq_update_bits(d, reg, ret = regmap_update_bits(d->map, reg,
d->type_buf_def[i], d->type_buf[i]); d->type_buf_def[i], d->type_buf[i]);
if (ret != 0) if (ret != 0)
dev_err(d->map->dev, "Failed to sync type in %x\n", dev_err(d->map->dev, "Failed to sync type in %x\n",
...@@ -220,8 +196,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -220,8 +196,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
if (d->chip->num_virt_regs) { if (d->chip->num_virt_regs) {
for (i = 0; i < d->chip->num_virt_regs; i++) { for (i = 0; i < d->chip->num_virt_regs; i++) {
for (j = 0; j < d->chip->num_regs; j++) { for (j = 0; j < d->chip->num_regs; j++) {
reg = sub_irq_reg(d, d->chip->virt_reg_base[i], reg = d->get_irq_reg(d, d->chip->virt_reg_base[i],
j); j);
ret = regmap_write(map, reg, d->virt_buf[i][j]); ret = regmap_write(map, reg, d->virt_buf[i][j]);
if (ret != 0) if (ret != 0)
dev_err(d->map->dev, dev_err(d->map->dev,
...@@ -231,6 +207,17 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -231,6 +207,17 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
} }
} }
for (i = 0; i < d->chip->num_config_bases; i++) {
for (j = 0; j < d->chip->num_config_regs; j++) {
reg = d->get_irq_reg(d, d->chip->config_base[i], j);
ret = regmap_write(map, reg, d->config_buf[i][j]);
if (ret)
dev_err(d->map->dev,
"Failed to write config %x: %d\n",
reg, ret);
}
}
if (d->chip->runtime_pm) if (d->chip->runtime_pm)
pm_runtime_put(map->dev); pm_runtime_put(map->dev);
...@@ -253,22 +240,19 @@ static void regmap_irq_enable(struct irq_data *data) ...@@ -253,22 +240,19 @@ static void regmap_irq_enable(struct irq_data *data)
struct regmap *map = d->map; struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
unsigned int reg = irq_data->reg_offset / map->reg_stride; unsigned int reg = irq_data->reg_offset / map->reg_stride;
unsigned int mask, type; unsigned int mask;
type = irq_data->type.type_falling_val | irq_data->type.type_rising_val;
/* /*
* The type_in_mask flag means that the underlying hardware uses * The type_in_mask flag means that the underlying hardware uses
* separate mask bits for rising and falling edge interrupts, but * separate mask bits for each interrupt trigger type, but we want
* we want to make them into a single virtual interrupt with * to have a single logical interrupt with a configurable type.
* configurable edge.
* *
* If the interrupt we're enabling defines the falling or rising * If the interrupt we're enabling defines any supported types
* masks then instead of using the regular mask bits for this * then instead of using the regular mask bits for this interrupt,
* interrupt, use the value previously written to the type buffer * use the value previously written to the type buffer at the
* at the corresponding offset in regmap_irq_set_type(). * corresponding offset in regmap_irq_set_type().
*/ */
if (d->chip->type_in_mask && type) if (d->chip->type_in_mask && irq_data->type.types_supported)
mask = d->type_buf[reg] & irq_data->mask; mask = d->type_buf[reg] & irq_data->mask;
else else
mask = irq_data->mask; mask = irq_data->mask;
...@@ -293,7 +277,7 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type) ...@@ -293,7 +277,7 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
struct regmap *map = d->map; struct regmap *map = d->map;
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
int reg; int reg, ret;
const struct regmap_irq_type *t = &irq_data->type; const struct regmap_irq_type *t = &irq_data->type;
if ((t->types_supported & type) != type) if ((t->types_supported & type) != type)
...@@ -333,9 +317,19 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type) ...@@ -333,9 +317,19 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
return -EINVAL; return -EINVAL;
} }
if (d->chip->set_type_virt) if (d->chip->set_type_virt) {
return d->chip->set_type_virt(d->virt_buf, type, data->hwirq, ret = d->chip->set_type_virt(d->virt_buf, type, data->hwirq,
reg); reg);
if (ret)
return ret;
}
if (d->chip->set_type_config) {
ret = d->chip->set_type_config(d->config_buf, type,
irq_data, reg);
if (ret)
return ret;
}
return 0; return 0;
} }
...@@ -376,14 +370,17 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data, ...@@ -376,14 +370,17 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
const 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;
struct regmap_irq_sub_irq_map *subreg; struct regmap_irq_sub_irq_map *subreg;
unsigned int reg;
int i, ret = 0; int i, ret = 0;
if (!chip->sub_reg_offsets) { if (!chip->sub_reg_offsets) {
/* Assume linear mapping */ reg = data->get_irq_reg(data, chip->status_base, b);
ret = regmap_read(map, chip->status_base + ret = regmap_read(map, reg, &data->status_buf[b]);
(b * map->reg_stride * data->irq_reg_stride),
&data->status_buf[b]);
} else { } else {
/*
* Note we can't use ->get_irq_reg() here because the offsets
* in 'subreg' are *not* interchangeable with indices.
*/
subreg = &chip->sub_reg_offsets[b]; subreg = &chip->sub_reg_offsets[b];
for (i = 0; i < subreg->num_regs; i++) { for (i = 0; i < subreg->num_regs; i++) {
unsigned int offset = subreg->offset[i]; unsigned int offset = subreg->offset[i];
...@@ -449,10 +446,18 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) ...@@ -449,10 +446,18 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
* sake of simplicity. and add bulk reads only if needed * sake of simplicity. and add bulk reads only if needed
*/ */
for (i = 0; i < chip->num_main_regs; i++) { for (i = 0; i < chip->num_main_regs; i++) {
ret = regmap_read(map, chip->main_status + /*
(i * map->reg_stride * For not_fixed_stride, don't use ->get_irq_reg().
* data->irq_reg_stride), * It would produce an incorrect result.
&data->main_status_buf[i]); */
if (data->chip->not_fixed_stride)
reg = chip->main_status +
i * map->reg_stride * data->irq_reg_stride;
else
reg = data->get_irq_reg(data,
chip->main_status, i);
ret = regmap_read(map, reg, &data->main_status_buf[i]);
if (ret) { if (ret) {
dev_err(map->dev, dev_err(map->dev,
"Failed to read IRQ status %d\n", "Failed to read IRQ status %d\n",
...@@ -481,8 +486,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) ...@@ -481,8 +486,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
} }
} }
} else if (!map->use_single_read && map->reg_stride == 1 && } else if (regmap_irq_can_bulk_read_status(data)) {
data->irq_reg_stride == 1) {
u8 *buf8 = data->status_reg_buf; u8 *buf8 = data->status_reg_buf;
u16 *buf16 = data->status_reg_buf; u16 *buf16 = data->status_reg_buf;
...@@ -518,7 +522,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) ...@@ -518,7 +522,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
} else { } else {
for (i = 0; i < data->chip->num_regs; i++) { for (i = 0; i < data->chip->num_regs; i++) {
unsigned int reg = sub_irq_reg(data, unsigned int reg = data->get_irq_reg(data,
data->chip->status_base, i); data->chip->status_base, i);
ret = regmap_read(map, reg, &data->status_buf[i]); ret = regmap_read(map, reg, &data->status_buf[i]);
...@@ -546,7 +550,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) ...@@ -546,7 +550,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
data->status_buf[i] &= ~data->mask_buf[i]; data->status_buf[i] &= ~data->mask_buf[i];
if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) { if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) {
reg = sub_irq_reg(data, data->chip->ack_base, i); reg = data->get_irq_reg(data, data->chip->ack_base, i);
if (chip->ack_invert) if (chip->ack_invert)
ret = regmap_write(map, reg, ret = regmap_write(map, reg,
...@@ -606,6 +610,91 @@ static const struct irq_domain_ops regmap_domain_ops = { ...@@ -606,6 +610,91 @@ static const struct irq_domain_ops regmap_domain_ops = {
.xlate = irq_domain_xlate_onetwocell, .xlate = irq_domain_xlate_onetwocell,
}; };
/**
* regmap_irq_get_irq_reg_linear() - Linear IRQ register mapping callback.
* @data: Data for the &struct regmap_irq_chip
* @base: Base register
* @index: Register index
*
* Returns the register address corresponding to the given @base and @index
* by the formula ``base + index * regmap_stride * irq_reg_stride``.
*/
unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data,
unsigned int base, int index)
{
const struct regmap_irq_chip *chip = data->chip;
struct regmap *map = data->map;
/*
* FIXME: This is for backward compatibility and should be removed
* when not_fixed_stride is dropped (it's only used by qcom-pm8008).
*/
if (chip->not_fixed_stride && chip->sub_reg_offsets) {
struct regmap_irq_sub_irq_map *subreg;
subreg = &chip->sub_reg_offsets[0];
return base + subreg->offset[0];
}
return base + index * map->reg_stride * chip->irq_reg_stride;
}
EXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear);
/**
* regmap_irq_set_type_config_simple() - Simple IRQ type configuration callback.
* @buf: Buffer containing configuration register values, this is a 2D array of
* `num_config_bases` rows, each of `num_config_regs` elements.
* @type: The requested IRQ type.
* @irq_data: The IRQ being configured.
* @idx: Index of the irq's config registers within each array `buf[i]`
*
* This is a &struct regmap_irq_chip->set_type_config callback suitable for
* chips with one config register. Register values are updated according to
* the &struct regmap_irq_type data associated with an IRQ.
*/
int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx)
{
const struct regmap_irq_type *t = &irq_data->type;
if (t->type_reg_mask)
buf[0][idx] &= ~t->type_reg_mask;
else
buf[0][idx] &= ~(t->type_falling_val |
t->type_rising_val |
t->type_level_low_val |
t->type_level_high_val);
switch (type) {
case IRQ_TYPE_EDGE_FALLING:
buf[0][idx] |= t->type_falling_val;
break;
case IRQ_TYPE_EDGE_RISING:
buf[0][idx] |= t->type_rising_val;
break;
case IRQ_TYPE_EDGE_BOTH:
buf[0][idx] |= (t->type_falling_val |
t->type_rising_val);
break;
case IRQ_TYPE_LEVEL_HIGH:
buf[0][idx] |= t->type_level_high_val;
break;
case IRQ_TYPE_LEVEL_LOW:
buf[0][idx] |= t->type_level_low_val;
break;
default:
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_GPL(regmap_irq_set_type_config_simple);
/** /**
* regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling * regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling
* *
...@@ -634,7 +723,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -634,7 +723,6 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
int ret = -ENOMEM; int ret = -ENOMEM;
int num_type_reg; int num_type_reg;
u32 reg; u32 reg;
u32 unmask_offset;
if (chip->num_regs <= 0) if (chip->num_regs <= 0)
return -EINVAL; return -EINVAL;
...@@ -651,11 +739,19 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -651,11 +739,19 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
} }
if (chip->not_fixed_stride) { if (chip->not_fixed_stride) {
dev_warn(map->dev, "not_fixed_stride is deprecated; use ->get_irq_reg() instead");
for (i = 0; i < chip->num_regs; i++) for (i = 0; i < chip->num_regs; i++)
if (chip->sub_reg_offsets[i].num_regs != 1) if (chip->sub_reg_offsets[i].num_regs != 1)
return -EINVAL; return -EINVAL;
} }
if (chip->num_type_reg)
dev_warn(map->dev, "type registers are deprecated; use config registers instead");
if (chip->num_virt_regs || chip->virt_reg_base || chip->set_type_virt)
dev_warn(map->dev, "virtual registers are deprecated; use config registers instead");
if (irq_base) { if (irq_base) {
irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
if (irq_base < 0) { if (irq_base < 0) {
...@@ -671,30 +767,30 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -671,30 +767,30 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (chip->num_main_regs) { if (chip->num_main_regs) {
d->main_status_buf = kcalloc(chip->num_main_regs, d->main_status_buf = kcalloc(chip->num_main_regs,
sizeof(unsigned int), sizeof(*d->main_status_buf),
GFP_KERNEL); GFP_KERNEL);
if (!d->main_status_buf) if (!d->main_status_buf)
goto err_alloc; goto err_alloc;
} }
d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int), d->status_buf = kcalloc(chip->num_regs, sizeof(*d->status_buf),
GFP_KERNEL); GFP_KERNEL);
if (!d->status_buf) if (!d->status_buf)
goto err_alloc; goto err_alloc;
d->mask_buf = kcalloc(chip->num_regs, sizeof(unsigned int), d->mask_buf = kcalloc(chip->num_regs, sizeof(*d->mask_buf),
GFP_KERNEL); GFP_KERNEL);
if (!d->mask_buf) if (!d->mask_buf)
goto err_alloc; goto err_alloc;
d->mask_buf_def = kcalloc(chip->num_regs, sizeof(unsigned int), d->mask_buf_def = kcalloc(chip->num_regs, sizeof(*d->mask_buf_def),
GFP_KERNEL); GFP_KERNEL);
if (!d->mask_buf_def) if (!d->mask_buf_def)
goto err_alloc; goto err_alloc;
if (chip->wake_base) { if (chip->wake_base) {
d->wake_buf = kcalloc(chip->num_regs, sizeof(unsigned int), d->wake_buf = kcalloc(chip->num_regs, sizeof(*d->wake_buf),
GFP_KERNEL); GFP_KERNEL);
if (!d->wake_buf) if (!d->wake_buf)
goto err_alloc; goto err_alloc;
...@@ -703,11 +799,11 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -703,11 +799,11 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
num_type_reg = chip->type_in_mask ? chip->num_regs : chip->num_type_reg; num_type_reg = chip->type_in_mask ? chip->num_regs : chip->num_type_reg;
if (num_type_reg) { if (num_type_reg) {
d->type_buf_def = kcalloc(num_type_reg, d->type_buf_def = kcalloc(num_type_reg,
sizeof(unsigned int), GFP_KERNEL); sizeof(*d->type_buf_def), GFP_KERNEL);
if (!d->type_buf_def) if (!d->type_buf_def)
goto err_alloc; goto err_alloc;
d->type_buf = kcalloc(num_type_reg, sizeof(unsigned int), d->type_buf = kcalloc(num_type_reg, sizeof(*d->type_buf),
GFP_KERNEL); GFP_KERNEL);
if (!d->type_buf) if (!d->type_buf)
goto err_alloc; goto err_alloc;
...@@ -724,13 +820,31 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -724,13 +820,31 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
for (i = 0; i < chip->num_virt_regs; i++) { for (i = 0; i < chip->num_virt_regs; i++) {
d->virt_buf[i] = kcalloc(chip->num_regs, d->virt_buf[i] = kcalloc(chip->num_regs,
sizeof(unsigned int), sizeof(**d->virt_buf),
GFP_KERNEL); GFP_KERNEL);
if (!d->virt_buf[i]) if (!d->virt_buf[i])
goto err_alloc; goto err_alloc;
} }
} }
if (chip->num_config_bases && chip->num_config_regs) {
/*
* Create config_buf[num_config_bases][num_config_regs]
*/
d->config_buf = kcalloc(chip->num_config_bases,
sizeof(*d->config_buf), GFP_KERNEL);
if (!d->config_buf)
goto err_alloc;
for (i = 0; i < chip->num_config_regs; i++) {
d->config_buf[i] = kcalloc(chip->num_config_regs,
sizeof(**d->config_buf),
GFP_KERNEL);
if (!d->config_buf[i])
goto err_alloc;
}
}
d->irq_chip = regmap_irq_chip; d->irq_chip = regmap_irq_chip;
d->irq_chip.name = chip->name; d->irq_chip.name = chip->name;
d->irq = irq; d->irq = irq;
...@@ -738,18 +852,53 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -738,18 +852,53 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
d->chip = chip; d->chip = chip;
d->irq_base = irq_base; d->irq_base = irq_base;
if (chip->mask_base && chip->unmask_base &&
!chip->mask_unmask_non_inverted) {
/*
* Chips that specify both mask_base and unmask_base used to
* get inverted mask behavior by default, with no way to ask
* for the normal, non-inverted behavior. This "inverted by
* default" behavior is deprecated, but we have to support it
* until existing drivers have been fixed.
*
* Existing drivers should be updated by swapping mask_base
* and unmask_base and setting mask_unmask_non_inverted=true.
* New drivers should always set the flag.
*/
dev_warn(map->dev, "mask_base and unmask_base are inverted, please fix it");
/* Might as well warn about mask_invert while we're at it... */
if (chip->mask_invert)
dev_warn(map->dev, "mask_invert=true ignored");
d->mask_base = chip->unmask_base;
d->unmask_base = chip->mask_base;
} else if (chip->mask_invert) {
/*
* Swap the roles of mask_base and unmask_base if the bits are
* inverted. This is deprecated, drivers should use unmask_base
* directly.
*/
dev_warn(map->dev, "mask_invert=true is deprecated; please switch to unmask_base");
d->mask_base = chip->unmask_base;
d->unmask_base = chip->mask_base;
} else {
d->mask_base = chip->mask_base;
d->unmask_base = chip->unmask_base;
}
if (chip->irq_reg_stride) if (chip->irq_reg_stride)
d->irq_reg_stride = chip->irq_reg_stride; d->irq_reg_stride = chip->irq_reg_stride;
else else
d->irq_reg_stride = 1; d->irq_reg_stride = 1;
if (chip->type_reg_stride) if (chip->get_irq_reg)
d->type_reg_stride = chip->type_reg_stride; d->get_irq_reg = chip->get_irq_reg;
else else
d->type_reg_stride = 1; d->get_irq_reg = regmap_irq_get_irq_reg_linear;
if (!map->use_single_read && map->reg_stride == 1 && if (regmap_irq_can_bulk_read_status(d)) {
d->irq_reg_stride == 1) {
d->status_reg_buf = kmalloc_array(chip->num_regs, d->status_reg_buf = kmalloc_array(chip->num_regs,
map->format.val_bytes, map->format.val_bytes,
GFP_KERNEL); GFP_KERNEL);
...@@ -766,35 +915,34 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -766,35 +915,34 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
/* Mask all the interrupts by default */ /* Mask all the interrupts by default */
for (i = 0; i < chip->num_regs; i++) { for (i = 0; i < chip->num_regs; i++) {
d->mask_buf[i] = d->mask_buf_def[i]; d->mask_buf[i] = d->mask_buf_def[i];
if (!chip->mask_base)
continue;
reg = sub_irq_reg(d, d->chip->mask_base, i); if (d->mask_base) {
reg = d->get_irq_reg(d, d->mask_base, i);
ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], d->mask_buf[i]);
if (ret) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
reg, ret);
goto err_alloc;
}
}
if (chip->mask_invert) if (d->unmask_base) {
ret = regmap_irq_update_bits(d, reg, reg = d->get_irq_reg(d, d->unmask_base, i);
d->mask_buf[i], ~d->mask_buf[i]); ret = regmap_update_bits(d->map, reg,
else if (d->chip->unmask_base) { d->mask_buf_def[i], ~d->mask_buf[i]);
unmask_offset = d->chip->unmask_base - if (ret) {
d->chip->mask_base; dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
ret = regmap_irq_update_bits(d, reg, ret);
reg + unmask_offset, goto err_alloc;
d->mask_buf[i], }
d->mask_buf[i]);
} else
ret = regmap_irq_update_bits(d, reg,
d->mask_buf[i], d->mask_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
reg, ret);
goto err_alloc;
} }
if (!chip->init_ack_masked) if (!chip->init_ack_masked)
continue; continue;
/* Ack masked but set interrupts */ /* Ack masked but set interrupts */
reg = sub_irq_reg(d, d->chip->status_base, i); reg = d->get_irq_reg(d, d->chip->status_base, i);
ret = regmap_read(map, reg, &d->status_buf[i]); ret = regmap_read(map, reg, &d->status_buf[i]);
if (ret != 0) { if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n", dev_err(map->dev, "Failed to read IRQ status: %d\n",
...@@ -806,7 +954,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -806,7 +954,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
d->status_buf[i] = ~d->status_buf[i]; d->status_buf[i] = ~d->status_buf[i];
if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) {
reg = sub_irq_reg(d, d->chip->ack_base, i); reg = d->get_irq_reg(d, d->chip->ack_base, i);
if (chip->ack_invert) if (chip->ack_invert)
ret = regmap_write(map, reg, ret = regmap_write(map, reg,
~(d->status_buf[i] & d->mask_buf[i])); ~(d->status_buf[i] & d->mask_buf[i]));
...@@ -831,14 +979,14 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -831,14 +979,14 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (d->wake_buf) { if (d->wake_buf) {
for (i = 0; i < chip->num_regs; i++) { for (i = 0; i < chip->num_regs; i++) {
d->wake_buf[i] = d->mask_buf_def[i]; d->wake_buf[i] = d->mask_buf_def[i];
reg = sub_irq_reg(d, d->chip->wake_base, i); reg = d->get_irq_reg(d, d->chip->wake_base, i);
if (chip->wake_invert) if (chip->wake_invert)
ret = regmap_irq_update_bits(d, reg, ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], d->mask_buf_def[i],
0); 0);
else else
ret = regmap_irq_update_bits(d, reg, ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], d->mask_buf_def[i],
d->wake_buf[i]); d->wake_buf[i]);
if (ret != 0) { if (ret != 0) {
...@@ -851,7 +999,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -851,7 +999,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (chip->num_type_reg && !chip->type_in_mask) { if (chip->num_type_reg && !chip->type_in_mask) {
for (i = 0; i < chip->num_type_reg; ++i) { for (i = 0; i < chip->num_type_reg; ++i) {
reg = sub_irq_reg(d, d->chip->type_base, i); reg = d->get_irq_reg(d, d->chip->type_base, i);
ret = regmap_read(map, reg, &d->type_buf_def[i]); ret = regmap_read(map, reg, &d->type_buf_def[i]);
...@@ -907,6 +1055,11 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, ...@@ -907,6 +1055,11 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
kfree(d->virt_buf[i]); kfree(d->virt_buf[i]);
kfree(d->virt_buf); kfree(d->virt_buf);
} }
if (d->config_buf) {
for (i = 0; i < chip->num_config_bases; i++)
kfree(d->config_buf[i]);
kfree(d->config_buf);
}
kfree(d); kfree(d);
return ret; return ret;
} }
...@@ -947,7 +1100,7 @@ EXPORT_SYMBOL_GPL(regmap_add_irq_chip); ...@@ -947,7 +1100,7 @@ EXPORT_SYMBOL_GPL(regmap_add_irq_chip);
void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
{ {
unsigned int virq; unsigned int virq;
int hwirq; int i, hwirq;
if (!d) if (!d)
return; return;
...@@ -977,6 +1130,11 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) ...@@ -977,6 +1130,11 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
kfree(d->mask_buf); kfree(d->mask_buf);
kfree(d->status_reg_buf); kfree(d->status_reg_buf);
kfree(d->status_buf); kfree(d->status_buf);
if (d->config_buf) {
for (i = 0; i < d->chip->num_config_bases; i++)
kfree(d->config_buf[i]);
kfree(d->config_buf);
}
kfree(d); kfree(d);
} }
EXPORT_SYMBOL_GPL(regmap_del_irq_chip); EXPORT_SYMBOL_GPL(regmap_del_irq_chip);
......
...@@ -1440,6 +1440,8 @@ struct regmap_irq_sub_irq_map { ...@@ -1440,6 +1440,8 @@ struct regmap_irq_sub_irq_map {
unsigned int *offset; unsigned int *offset;
}; };
struct regmap_irq_chip_data;
/** /**
* struct regmap_irq_chip - Description of a generic regmap irq_chip. * struct regmap_irq_chip - Description of a generic regmap irq_chip.
* *
...@@ -1467,32 +1469,50 @@ struct regmap_irq_sub_irq_map { ...@@ -1467,32 +1469,50 @@ struct regmap_irq_sub_irq_map {
* main_status set. * main_status set.
* *
* @status_base: Base status register address. * @status_base: Base status register address.
* @mask_base: Base mask register address. * @mask_base: Base mask register address. Mask bits are set to 1 when an
* @mask_writeonly: Base mask register is write only. * interrupt is masked, 0 when unmasked.
* @unmask_base: Base unmask register address. for chips who have * @unmask_base: Base unmask register address. Unmask bits are set to 1 when
* separate mask and unmask registers * an interrupt is unmasked and 0 when masked.
* @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.
* Using zero value is possible with @use_ack bit. * Using zero value is possible with @use_ack bit.
* @wake_base: Base address for wake enables. If zero unsupported. * @wake_base: Base address for wake enables. If zero unsupported.
* @type_base: Base address for irq type. If zero unsupported. * @type_base: Base address for irq type. If zero unsupported. Deprecated,
* @virt_reg_base: Base addresses for extra config regs. * use @config_base instead.
* @virt_reg_base: Base addresses for extra config regs. Deprecated, use
* @config_base instead.
* @config_base: Base address for IRQ type config regs. If null 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.
* @init_ack_masked: Ack all masked interrupts once during initalization. * @init_ack_masked: Ack all masked interrupts once during initalization.
* @mask_invert: Inverted mask register: cleared bits are masked out. * @mask_invert: Inverted mask register: cleared bits are masked out.
* Deprecated; prefer describing an inverted mask register as
* an unmask register.
* @mask_unmask_non_inverted: Controls mask bit inversion for chips that set
* both @mask_base and @unmask_base. If false, mask and unmask bits are
* inverted (which is deprecated behavior); if true, bits will not be
* inverted and the registers keep their normal behavior. Note that if
* you use only one of @mask_base or @unmask_base, this flag has no
* effect and is unnecessary. Any new drivers that set both @mask_base
* and @unmask_base should set this to true to avoid relying on the
* deprecated behavior.
* @use_ack: Use @ack register even if it is zero. * @use_ack: Use @ack register even if it is zero.
* @ack_invert: Inverted ack register: cleared bits for ack. * @ack_invert: Inverted ack register: cleared bits for ack.
* @clear_ack: Use this to set 1 and 0 or vice-versa to clear interrupts. * @clear_ack: Use this to set 1 and 0 or vice-versa to clear interrupts.
* @wake_invert: Inverted wake register: cleared bits are wake enabled. * @wake_invert: Inverted wake register: cleared bits are wake enabled.
* @type_invert: Invert the type flags. * @type_invert: Invert the type flags. Deprecated, use config registers
* @type_in_mask: Use the mask registers for controlling irq type. For * instead.
* interrupts defining type_rising/falling_mask use mask_base * @type_in_mask: Use the mask registers for controlling irq type. Use this if
* for edge configuration and never update bits in type_base. * the hardware provides separate bits for rising/falling edge
* or low/high level interrupts and they should be combined into
* a single logical interrupt. Use &struct regmap_irq_type data
* to define the mask bit for each irq type.
* @clear_on_unmask: For chips with interrupts cleared on read: read the status * @clear_on_unmask: For chips with interrupts cleared on read: read the status
* registers before unmasking interrupts to clear any bits * registers before unmasking interrupts to clear any bits
* set when they were masked. * set when they were masked.
* @not_fixed_stride: Used when chip peripherals are not laid out with fixed * @not_fixed_stride: Used when chip peripherals are not laid out with fixed
* stride. Must be used with sub_reg_offsets containing the * stride. Must be used with sub_reg_offsets containing the
* offsets to each peripheral. * offsets to each peripheral. Deprecated; the same thing
* can be accomplished with a @get_irq_reg callback, without
* the need for a @sub_reg_offsets table.
* @status_invert: Inverted status register: cleared bits are active interrupts. * @status_invert: Inverted status register: cleared bits are active interrupts.
* @runtime_pm: Hold a runtime PM lock on the device when accessing it. * @runtime_pm: Hold a runtime PM lock on the device when accessing it.
* *
...@@ -1500,17 +1520,28 @@ struct regmap_irq_sub_irq_map { ...@@ -1500,17 +1520,28 @@ struct regmap_irq_sub_irq_map {
* @irqs: Descriptors for individual IRQs. Interrupt numbers are * @irqs: Descriptors for individual IRQs. Interrupt numbers are
* assigned based on the index in the array of the interrupt. * assigned based on the index in the array of the interrupt.
* @num_irqs: Number of descriptors. * @num_irqs: Number of descriptors.
* @num_type_reg: Number of type registers. * @num_type_reg: Number of type registers. Deprecated, use config registers
* instead.
* @num_virt_regs: Number of non-standard irq configuration registers. * @num_virt_regs: Number of non-standard irq configuration registers.
* If zero unsupported. * If zero unsupported. Deprecated, use config registers
* @type_reg_stride: Stride to use for chips where type registers are not * instead.
* contiguous. * @num_config_bases: Number of config base registers.
* @num_config_regs: Number of config registers for each config base register.
* @handle_pre_irq: Driver specific callback to handle interrupt from device * @handle_pre_irq: Driver specific callback to handle interrupt from device
* before regmap_irq_handler process the interrupts. * before regmap_irq_handler process the interrupts.
* @handle_post_irq: Driver specific callback to handle interrupt from device * @handle_post_irq: Driver specific callback to handle interrupt from device
* after handling the interrupts in regmap_irq_handler(). * after handling the interrupts in regmap_irq_handler().
* @set_type_virt: Driver specific callback to extend regmap_irq_set_type() * @set_type_virt: Driver specific callback to extend regmap_irq_set_type()
* and configure virt regs. * and configure virt regs. Deprecated, use @set_type_config
* callback and config registers instead.
* @set_type_config: Callback used for configuring irq types.
* @get_irq_reg: Callback for mapping (base register, index) pairs to register
* addresses. The base register will be one of @status_base,
* @mask_base, etc., @main_status, or any of @config_base.
* The index will be in the range [0, num_main_regs[ for the
* main status base, [0, num_type_settings[ for any config
* register base, and [0, num_regs[ for any other base.
* If unspecified then regmap_irq_get_irq_reg_linear() is used.
* @irq_drv_data: Driver specific IRQ data which is passed as parameter when * @irq_drv_data: Driver specific IRQ data which is passed as parameter when
* driver specific pre/post interrupt handler is called. * driver specific pre/post interrupt handler is called.
* *
...@@ -1533,20 +1564,21 @@ struct regmap_irq_chip { ...@@ -1533,20 +1564,21 @@ struct regmap_irq_chip {
unsigned int wake_base; unsigned int wake_base;
unsigned int type_base; unsigned int type_base;
unsigned int *virt_reg_base; unsigned int *virt_reg_base;
const unsigned int *config_base;
unsigned int irq_reg_stride; unsigned int irq_reg_stride;
bool mask_writeonly:1; unsigned int init_ack_masked:1;
bool init_ack_masked:1; unsigned int mask_invert:1;
bool mask_invert:1; unsigned int mask_unmask_non_inverted:1;
bool use_ack:1; unsigned int use_ack:1;
bool ack_invert:1; unsigned int ack_invert:1;
bool clear_ack:1; unsigned int clear_ack:1;
bool wake_invert:1; unsigned int wake_invert:1;
bool runtime_pm:1; unsigned int runtime_pm:1;
bool type_invert:1; unsigned int type_invert:1;
bool type_in_mask:1; unsigned int type_in_mask:1;
bool clear_on_unmask:1; unsigned int clear_on_unmask:1;
bool not_fixed_stride:1; unsigned int not_fixed_stride:1;
bool status_invert:1; unsigned int status_invert:1;
int num_regs; int num_regs;
...@@ -1555,16 +1587,24 @@ struct regmap_irq_chip { ...@@ -1555,16 +1587,24 @@ struct regmap_irq_chip {
int num_type_reg; int num_type_reg;
int num_virt_regs; int num_virt_regs;
unsigned int type_reg_stride; int num_config_bases;
int num_config_regs;
int (*handle_pre_irq)(void *irq_drv_data); int (*handle_pre_irq)(void *irq_drv_data);
int (*handle_post_irq)(void *irq_drv_data); int (*handle_post_irq)(void *irq_drv_data);
int (*set_type_virt)(unsigned int **buf, unsigned int type, int (*set_type_virt)(unsigned int **buf, unsigned int type,
unsigned long hwirq, int reg); unsigned long hwirq, int reg);
int (*set_type_config)(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx);
unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data,
unsigned int base, int index);
void *irq_drv_data; void *irq_drv_data;
}; };
struct regmap_irq_chip_data; unsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data,
unsigned int base, int index);
int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
const struct regmap_irq *irq_data, int idx);
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, const struct regmap_irq_chip *chip, int irq_base, const struct regmap_irq_chip *chip,
......
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