diff options
Diffstat (limited to 'drivers/base/regmap/regmap-irq.c')
| -rw-r--r-- | drivers/base/regmap/regmap-irq.c | 813 |
1 files changed, 555 insertions, 258 deletions
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 330c1f7e9665..6112d942499b 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -1,20 +1,18 @@ -/* - * regmap based irq_chip - * - * Copyright 2011 Wolfson Microelectronics plc - * - * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - +// SPDX-License-Identifier: GPL-2.0 +// +// regmap based irq_chip +// +// Copyright 2011 Wolfson Microelectronics plc +// +// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + +#include <linux/array_size.h> #include <linux/device.h> #include <linux/export.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqdomain.h> +#include <linux/overflow.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -23,6 +21,7 @@ struct regmap_irq_chip_data { struct mutex lock; + struct lock_class_key lock_key; struct irq_chip irq_chip; struct regmap *map; @@ -35,17 +34,22 @@ struct regmap_irq_chip_data { int wake_count; void *status_reg_buf; + unsigned int *main_status_buf; unsigned int *status_buf; + unsigned int *prev_status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; unsigned int *wake_buf; unsigned int *type_buf; unsigned int *type_buf_def; + unsigned int **config_buf; 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); + + unsigned int clear_status:1; }; static inline const @@ -55,30 +59,33 @@ struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, 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, - unsigned int reg, unsigned int mask, - unsigned int val) +static void regmap_irq_lock(struct irq_data *data) { - if (d->chip->mask_writeonly) - return regmap_write_bits(d->map, reg, mask, val); - else - return regmap_update_bits(d->map, reg, mask, val); + struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + + mutex_lock(&d->lock); } static void regmap_irq_sync_unlock(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap *map = d->map; - int i, ret; + int i, j, ret; u32 reg; - u32 unmask_offset; u32 val; if (d->chip->runtime_pm) { @@ -90,8 +97,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) if (d->clear_status) { for (i = 0; i < d->chip->num_regs; i++) { - reg = d->chip->status_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = d->get_irq_reg(d, d->chip->status_base, i); ret = regmap_read(map, reg, &val); if (ret) @@ -108,46 +114,37 @@ static void regmap_irq_sync_unlock(struct irq_data *data) * suppress pointless writes. */ for (i = 0; i < d->chip->num_regs; i++) { - if (!d->chip->mask_base) - continue; + if (d->chip->handle_mask_sync) + d->chip->handle_mask_sync(i, d->mask_buf_def[i], + d->mask_buf[i], + d->chip->irq_drv_data); + + if (d->chip->mask_base && !d->chip->handle_mask_sync) { + reg = d->get_irq_reg(d, d->chip->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 = d->chip->mask_base + - (i * map->reg_stride * d->irq_reg_stride); - if (d->chip->mask_invert) { - ret = regmap_irq_update_bits(d, 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, + if (d->chip->unmask_base && !d->chip->handle_mask_sync) { + reg = d->get_irq_reg(d, d->chip->unmask_base, i); + ret = regmap_update_bits(d->map, reg, d->mask_buf_def[i], ~d->mask_buf[i]); - if (ret < 0) - dev_err(d->map->dev, - "Failed to sync unmasks in %x\n", + if (ret) + dev_err(d->map->dev, "Failed to sync masks in %x\n", 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 = d->chip->wake_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = d->get_irq_reg(d, d->chip->wake_base, i); if (d->wake_buf) { 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->wake_buf[i]); else - ret = regmap_irq_update_bits(d, reg, + ret = regmap_update_bits(d->map, reg, d->mask_buf_def[i], d->wake_buf[i]); if (ret != 0) @@ -164,35 +161,33 @@ static void regmap_irq_sync_unlock(struct irq_data *data) * 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)) { - reg = d->chip->ack_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = d->get_irq_reg(d, d->chip->ack_base, i); + /* some chips ack by write 0 */ if (d->chip->ack_invert) ret = regmap_write(map, reg, ~d->mask_buf[i]); else ret = regmap_write(map, reg, d->mask_buf[i]); + if (d->chip->clear_ack) { + if (d->chip->ack_invert && !ret) + ret = regmap_write(map, reg, UINT_MAX); + else if (!ret) + ret = regmap_write(map, reg, 0); + } if (ret != 0) dev_err(d->map->dev, "Failed to ack 0x%x: %d\n", reg, ret); } } - /* Don't update the type bits if we're using mask bits for irq type. */ - if (!d->chip->type_in_mask) { - for (i = 0; i < d->chip->num_type_reg; i++) { - if (!d->type_buf_def[i]) - continue; - reg = d->chip->type_base + - (i * map->reg_stride * d->type_reg_stride); - if (d->chip->type_invert) - ret = regmap_irq_update_bits(d, reg, - d->type_buf_def[i], ~d->type_buf[i]); - else - ret = regmap_irq_update_bits(d, reg, - d->type_buf_def[i], d->type_buf[i]); - if (ret != 0) - dev_err(d->map->dev, "Failed to sync type in %x\n", - reg); + 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); } } @@ -202,10 +197,10 @@ static void regmap_irq_sync_unlock(struct irq_data *data) /* 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); + disable_irq_wake(d->irq); else if (d->wake_count > 0) for (i = 0; i < d->wake_count; i++) - irq_set_irq_wake(d->irq, 1); + enable_irq_wake(d->irq); d->wake_count = 0; @@ -217,30 +212,28 @@ static void regmap_irq_enable(struct irq_data *data) 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); - unsigned int mask, type; - - type = irq_data->type.type_falling_val | irq_data->type.type_rising_val; + unsigned int reg = irq_data->reg_offset / map->reg_stride; + unsigned int mask; /* * The type_in_mask flag means that the underlying hardware uses - * separate mask bits for rising and falling edge interrupts, but - * we want to make them into a single virtual interrupt with - * configurable edge. + * separate mask bits for each interrupt trigger type, but we want + * to have a single logical interrupt with a configurable type. * - * If the interrupt we're enabling defines the falling or rising - * masks then instead of using the regular mask bits for this - * interrupt, use the value previously written to the type buffer - * at the corresponding offset in regmap_irq_set_type(). + * If the interrupt we're enabling defines any supported types + * then instead of using the regular mask bits for this interrupt, + * use the value previously written to the type buffer at the + * corresponding offset in regmap_irq_set_type(). */ - if (d->chip->type_in_mask && type) - mask = d->type_buf[irq_data->reg_offset / map->reg_stride]; + if (d->chip->type_in_mask && irq_data->type.types_supported) + mask = d->type_buf[reg] & irq_data->mask; else mask = irq_data->mask; if (d->chip->clear_on_unmask) d->clear_status = true; - d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~mask; + d->mask_buf[reg] &= ~mask; } static void regmap_irq_disable(struct irq_data *data) @@ -257,7 +250,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 *map = d->map; 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; if ((t->types_supported & type) != type) @@ -265,37 +258,20 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type) reg = t->type_reg_offset / map->reg_stride; - if (t->type_reg_mask) - d->type_buf[reg] &= ~t->type_reg_mask; - else - d->type_buf[reg] &= ~(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: - d->type_buf[reg] |= t->type_falling_val; - break; - - case IRQ_TYPE_EDGE_RISING: - d->type_buf[reg] |= t->type_rising_val; - break; - - case IRQ_TYPE_EDGE_BOTH: - d->type_buf[reg] |= (t->type_falling_val | - t->type_rising_val); - break; - - case IRQ_TYPE_LEVEL_HIGH: - d->type_buf[reg] |= t->type_level_high_val; - break; + if (d->chip->type_in_mask) { + ret = regmap_irq_set_type_config_simple(&d->type_buf, type, + irq_data, reg, d->chip->irq_drv_data); + if (ret) + return ret; + } - case IRQ_TYPE_LEVEL_LOW: - d->type_buf[reg] |= t->type_level_low_val; - break; - default: - return -EINVAL; + if (d->chip->set_type_config) { + ret = d->chip->set_type_config(d->config_buf, type, irq_data, + reg, d->chip->irq_drv_data); + if (ret) + return ret; } + return 0; } @@ -329,34 +305,95 @@ static const struct irq_chip regmap_irq_chip = { .irq_set_wake = regmap_irq_set_wake, }; -static irqreturn_t regmap_irq_thread(int irq, void *d) +static inline int read_sub_irq_data(struct regmap_irq_chip_data *data, + unsigned int b) { - struct regmap_irq_chip_data *data = d; const struct regmap_irq_chip *chip = data->chip; + const struct regmap_irq_sub_irq_map *subreg; struct regmap *map = data->map; - int ret, i; - bool handled = false; - u32 reg; + unsigned int reg; + int i, ret = 0; - if (chip->handle_pre_irq) - chip->handle_pre_irq(chip->irq_drv_data); + if (!chip->sub_reg_offsets) { + reg = data->get_irq_reg(data, chip->status_base, b); + ret = regmap_read(map, reg, &data->status_buf[b]); + } 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]; + for (i = 0; i < subreg->num_regs; i++) { + unsigned int offset = subreg->offset[i]; + unsigned int index = offset / map->reg_stride; - if (chip->runtime_pm) { - ret = pm_runtime_get_sync(map->dev); - if (ret < 0) { - dev_err(map->dev, "IRQ thread failed to resume: %d\n", - ret); - pm_runtime_put(map->dev); - goto exit; + ret = regmap_read(map, chip->status_base + offset, + &data->status_buf[index]); + if (ret) + break; } } + return ret; +} + +static int read_irq_data(struct regmap_irq_chip_data *data) +{ + const struct regmap_irq_chip *chip = data->chip; + struct regmap *map = data->map; + int ret, i; + u32 reg; /* - * Read in the statuses, using a single bulk read if possible - * in order to reduce the I/O overheads. + * Read only registers with active IRQs if the chip has 'main status + * register'. Else read in the statuses, using a single bulk read if + * possible in order to reduce the I/O overheads. */ - if (!map->use_single_read && map->reg_stride == 1 && - data->irq_reg_stride == 1) { + + if (chip->no_status) { + /* no status register so default to all active */ + memset32(data->status_buf, GENMASK(31, 0), chip->num_regs); + } else if (chip->num_main_regs) { + unsigned int max_main_bits; + + max_main_bits = (chip->num_main_status_bits) ? + chip->num_main_status_bits : chip->num_regs; + /* Clear the status buf as we don't read all status regs */ + memset32(data->status_buf, 0, chip->num_regs); + + /* We could support bulk read for main status registers + * but I don't expect to see devices with really many main + * status registers so let's only support single reads for the + * sake of simplicity. and add bulk reads only if needed + */ + for (i = 0; i < chip->num_main_regs; i++) { + reg = data->get_irq_reg(data, chip->main_status, i); + ret = regmap_read(map, reg, &data->main_status_buf[i]); + if (ret) { + dev_err(map->dev, "Failed to read IRQ status %d\n", ret); + return ret; + } + } + + /* Read sub registers with active IRQs */ + for (i = 0; i < chip->num_main_regs; i++) { + unsigned int b; + const unsigned long mreg = data->main_status_buf[i]; + + for_each_set_bit(b, &mreg, map->format.val_bytes * 8) { + if (i * map->format.val_bytes * 8 + b > + max_main_bits) + break; + ret = read_sub_irq_data(data, b); + + if (ret != 0) { + dev_err(map->dev, "Failed to read IRQ status %d\n", ret); + return ret; + } + } + + } + } else if (regmap_irq_can_bulk_read_status(data)) { + u8 *buf8 = data->status_reg_buf; u16 *buf16 = data->status_reg_buf; u32 *buf32 = data->status_reg_buf; @@ -367,9 +404,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) data->status_reg_buf, chip->num_regs); if (ret != 0) { - dev_err(map->dev, "Failed to read IRQ status: %d\n", - ret); - goto exit; + dev_err(map->dev, "Failed to read IRQ status: %d\n", ret); + return ret; } for (i = 0; i < data->chip->num_regs; i++) { @@ -385,31 +421,66 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) break; default: BUG(); - goto exit; + return -EIO; } } } else { for (i = 0; i < data->chip->num_regs; i++) { - ret = regmap_read(map, chip->status_base + - (i * map->reg_stride - * data->irq_reg_stride), - &data->status_buf[i]); + unsigned int reg = data->get_irq_reg(data, + data->chip->status_base, i); + ret = regmap_read(map, reg, &data->status_buf[i]); if (ret != 0) { - dev_err(map->dev, - "Failed to read IRQ status: %d\n", - ret); - if (chip->runtime_pm) - pm_runtime_put(map->dev); - goto exit; + dev_err(map->dev, "Failed to read IRQ status: %d\n", ret); + return ret; } } } + if (chip->status_invert) + for (i = 0; i < data->chip->num_regs; i++) + data->status_buf[i] = ~data->status_buf[i]; + + return 0; +} + +static irqreturn_t regmap_irq_thread(int irq, void *d) +{ + struct regmap_irq_chip_data *data = d; + const struct regmap_irq_chip *chip = data->chip; + struct regmap *map = data->map; + int ret, i; + bool handled = false; + u32 reg; + + if (chip->handle_pre_irq) + chip->handle_pre_irq(chip->irq_drv_data); + + if (chip->runtime_pm) { + ret = pm_runtime_get_sync(map->dev); + if (ret < 0) { + dev_err(map->dev, "IRQ thread failed to resume: %d\n", ret); + goto exit; + } + } + + ret = read_irq_data(data); + if (ret < 0) + goto exit; + + if (chip->status_is_level) { + for (i = 0; i < data->chip->num_regs; i++) { + unsigned int val = data->status_buf[i]; + + data->status_buf[i] ^= data->prev_status_buf[i]; + data->prev_status_buf[i] = val; + } + } + /* * Ignore masked IRQs and ack if we need to; we ack early so - * there is no race between handling and acknowleding the + * there is no race between handling and acknowledging the * interrupt. We assume that typically few of the interrupts * will fire simultaneously so don't worry about overhead from * doing a write per register. @@ -418,9 +489,20 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) data->status_buf[i] &= ~data->mask_buf[i]; if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) { - reg = chip->ack_base + - (i * map->reg_stride * data->irq_reg_stride); - ret = regmap_write(map, reg, data->status_buf[i]); + reg = data->get_irq_reg(data, data->chip->ack_base, i); + + if (chip->ack_invert) + ret = regmap_write(map, reg, + ~data->status_buf[i]); + else + ret = regmap_write(map, reg, + data->status_buf[i]); + if (chip->clear_ack) { + if (chip->ack_invert && !ret) + ret = regmap_write(map, reg, UINT_MAX); + else if (!ret) + ret = regmap_write(map, reg, 0); + } if (ret != 0) dev_err(map->dev, "Failed to ack 0x%x: %d\n", reg, ret); @@ -435,25 +517,29 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } } - if (chip->runtime_pm) - pm_runtime_put(map->dev); - exit: if (chip->handle_post_irq) chip->handle_post_irq(chip->irq_drv_data); + if (chip->runtime_pm) + pm_runtime_put(map->dev); + if (handled) return IRQ_HANDLED; else return IRQ_NONE; } +static struct lock_class_key regmap_irq_lock_class; +static struct lock_class_key regmap_irq_request_class; + 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_lockdep_class(virq, ®map_irq_lock_class, ®map_irq_request_class); irq_set_chip(virq, &data->irq_chip); irq_set_nested_thread(virq, 1); irq_set_parent(virq, data->irq); @@ -468,8 +554,108 @@ static const struct irq_domain_ops regmap_domain_ops = { }; /** - * regmap_add_irq_chip() - Use standard regmap IRQ controller handling + * 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) +{ + struct regmap *map = data->map; + + return base + index * map->reg_stride * data->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]` + * @irq_drv_data: Driver specific IRQ data + * + * 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, void *irq_drv_data) +{ + 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); + +static int regmap_irq_create_domain(struct fwnode_handle *fwnode, int irq_base, + const struct regmap_irq_chip *chip, + struct regmap_irq_chip_data *d) +{ + struct irq_domain_info info = { + .fwnode = fwnode, + .size = chip->num_irqs, + .hwirq_max = chip->num_irqs, + .virq_base = irq_base, + .ops = ®map_domain_ops, + .host_data = d, + .name_suffix = chip->domain_suffix, + }; + + d->domain = irq_domain_instantiate(&info); + if (IS_ERR(d->domain)) { + dev_err(d->map->dev, "Failed to create IRQ domain\n"); + return PTR_ERR(d->domain); + } + + return 0; +} + + +/** + * regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling * + * @fwnode: The firmware node where the IRQ domain should be added to. * @map: The regmap for the device. * @irq: The IRQ the device uses to signal interrupts. * @irq_flags: The IRQF_ flags to use for the primary interrupt. @@ -483,16 +669,16 @@ static const struct irq_domain_ops regmap_domain_ops = { * register cache. The chip driver is responsible for restoring the * register values used by the IRQ controller over suspend and resume. */ -int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, - int irq_base, const struct regmap_irq_chip *chip, - struct regmap_irq_chip_data **data) +int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, + struct regmap *map, int irq, + int irq_flags, int irq_base, + const struct regmap_irq_chip *chip, + struct regmap_irq_chip_data **data) { struct regmap_irq_chip_data *d; int i; int ret = -ENOMEM; - int num_type_reg; u32 reg; - u32 unmask_offset; if (chip->num_regs <= 0) return -EINVAL; @@ -500,6 +686,9 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (chip->clear_on_unmask && (chip->ack_base || chip->use_ack)) return -EINVAL; + if (chip->mask_base && chip->unmask_base && !chip->mask_unmask_non_inverted) + return -EINVAL; + for (i = 0; i < chip->num_irqs; i++) { if (chip->irqs[i].reg_offset % map->reg_stride) return -EINVAL; @@ -521,41 +710,73 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!d) return -ENOMEM; - d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int), + if (chip->num_main_regs) { + d->main_status_buf = kcalloc(chip->num_main_regs, + sizeof(*d->main_status_buf), + GFP_KERNEL); + + if (!d->main_status_buf) + goto err_alloc; + } + + d->status_buf = kcalloc(chip->num_regs, sizeof(*d->status_buf), GFP_KERNEL); if (!d->status_buf) goto err_alloc; - d->mask_buf = kcalloc(chip->num_regs, sizeof(unsigned int), + if (chip->status_is_level) { + d->prev_status_buf = kcalloc(chip->num_regs, sizeof(*d->prev_status_buf), + GFP_KERNEL); + if (!d->prev_status_buf) + goto err_alloc; + } + + d->mask_buf = kcalloc(chip->num_regs, sizeof(*d->mask_buf), GFP_KERNEL); if (!d->mask_buf) 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); if (!d->mask_buf_def) goto err_alloc; 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); if (!d->wake_buf) goto err_alloc; } - num_type_reg = chip->type_in_mask ? chip->num_regs : chip->num_type_reg; - if (num_type_reg) { - d->type_buf_def = kcalloc(num_type_reg, - sizeof(unsigned int), GFP_KERNEL); + if (chip->type_in_mask) { + d->type_buf_def = kcalloc(chip->num_regs, + sizeof(*d->type_buf_def), GFP_KERNEL); if (!d->type_buf_def) goto err_alloc; - d->type_buf = kcalloc(num_type_reg, sizeof(unsigned int), - GFP_KERNEL); + d->type_buf = kcalloc(chip->num_regs, sizeof(*d->type_buf), GFP_KERNEL); if (!d->type_buf) 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_bases; 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.name = chip->name; d->irq = irq; @@ -568,13 +789,12 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, else d->irq_reg_stride = 1; - if (chip->type_reg_stride) - d->type_reg_stride = chip->type_reg_stride; + if (chip->get_irq_reg) + d->get_irq_reg = chip->get_irq_reg; 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 && - d->irq_reg_stride == 1) { + if (regmap_irq_can_bulk_read_status(d)) { d->status_reg_buf = kmalloc_array(chip->num_regs, map->format.val_bytes, GFP_KERNEL); @@ -582,7 +802,13 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, goto err_alloc; } - mutex_init(&d->lock); + /* + * If one regmap-irq is the parent of another then we'll try + * to lock the child with the parent locked, use an explicit + * lock_key so lockdep can figure out what's going on. + */ + lockdep_register_key(&d->lock_key); + mutex_init_with_key(&d->lock, &d->lock_key); for (i = 0; i < chip->num_irqs; i++) d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] @@ -591,56 +817,76 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, /* Mask all the interrupts by default */ for (i = 0; i < chip->num_regs; i++) { d->mask_buf[i] = d->mask_buf_def[i]; - if (!chip->mask_base) - continue; - reg = chip->mask_base + - (i * map->reg_stride * d->irq_reg_stride); - if (chip->mask_invert) - ret = regmap_irq_update_bits(d, reg, - d->mask_buf[i], ~d->mask_buf[i]); - else if (d->chip->unmask_base) { - unmask_offset = d->chip->unmask_base - - d->chip->mask_base; - ret = regmap_irq_update_bits(d, - reg + unmask_offset, - 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->handle_mask_sync) { + ret = chip->handle_mask_sync(i, d->mask_buf_def[i], + d->mask_buf[i], + chip->irq_drv_data); + if (ret) + goto err_mutex; + } + + if (chip->mask_base && !chip->handle_mask_sync) { + reg = d->get_irq_reg(d, chip->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_mutex; + } + } + + if (chip->unmask_base && !chip->handle_mask_sync) { + reg = d->get_irq_reg(d, chip->unmask_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_mutex; + } } if (!chip->init_ack_masked) continue; /* Ack masked but set interrupts */ - reg = chip->status_base + - (i * map->reg_stride * d->irq_reg_stride); - ret = regmap_read(map, reg, &d->status_buf[i]); - if (ret != 0) { - dev_err(map->dev, "Failed to read IRQ status: %d\n", - ret); - goto err_alloc; + if (d->chip->no_status) { + /* no status register so default to all active */ + d->status_buf[i] = UINT_MAX; + } else { + reg = d->get_irq_reg(d, d->chip->status_base, i); + ret = regmap_read(map, reg, &d->status_buf[i]); + if (ret != 0) { + dev_err(map->dev, "Failed to read IRQ status: %d\n", + ret); + goto err_mutex; + } } + if (chip->status_invert) + d->status_buf[i] = ~d->status_buf[i]; + if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { - reg = chip->ack_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = d->get_irq_reg(d, d->chip->ack_base, i); if (chip->ack_invert) ret = regmap_write(map, reg, ~(d->status_buf[i] & d->mask_buf[i])); else ret = regmap_write(map, reg, d->status_buf[i] & d->mask_buf[i]); + if (chip->clear_ack) { + if (chip->ack_invert && !ret) + ret = regmap_write(map, reg, UINT_MAX); + else if (!ret) + ret = regmap_write(map, reg, 0); + } if (ret != 0) { dev_err(map->dev, "Failed to ack 0x%x: %d\n", reg, ret); - goto err_alloc; + goto err_mutex; } } } @@ -649,59 +895,37 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (d->wake_buf) { for (i = 0; i < chip->num_regs; i++) { d->wake_buf[i] = d->mask_buf_def[i]; - reg = chip->wake_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = d->get_irq_reg(d, d->chip->wake_base, i); if (chip->wake_invert) - ret = regmap_irq_update_bits(d, reg, + ret = regmap_update_bits(d->map, reg, d->mask_buf_def[i], 0); else - ret = regmap_irq_update_bits(d, reg, + ret = regmap_update_bits(d->map, reg, d->mask_buf_def[i], d->wake_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", reg, ret); - goto err_alloc; + goto err_mutex; } } } - if (chip->num_type_reg && !chip->type_in_mask) { - for (i = 0; i < chip->num_type_reg; ++i) { - if (!d->type_buf_def[i]) - continue; - - reg = chip->type_base + - (i * map->reg_stride * d->type_reg_stride); - - ret = regmap_read(map, reg, &d->type_buf_def[i]); - - if (d->chip->type_invert) - d->type_buf_def[i] = ~d->type_buf_def[i]; + /* Store current levels */ + if (chip->status_is_level) { + ret = read_irq_data(d); + if (ret < 0) + goto err_mutex; - if (ret) { - dev_err(map->dev, "Failed to get type defaults at 0x%x: %d\n", - reg, ret); - goto err_alloc; - } - } + memcpy(d->prev_status_buf, d->status_buf, + array_size(d->chip->num_regs, sizeof(d->prev_status_buf[0]))); } - 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 = regmap_irq_create_domain(fwnode, irq_base, chip, d); + if (ret) + goto err_mutex; ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags | IRQF_ONESHOT, @@ -718,17 +942,51 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, err_domain: /* Should really dispose of the domain but... */ +err_mutex: + mutex_destroy(&d->lock); + lockdep_unregister_key(&d->lock_key); err_alloc: kfree(d->type_buf); kfree(d->type_buf_def); kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); + kfree(d->main_status_buf); kfree(d->status_buf); + kfree(d->prev_status_buf); kfree(d->status_reg_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); return ret; } +EXPORT_SYMBOL_GPL(regmap_add_irq_chip_fwnode); + +/** + * regmap_add_irq_chip() - Use standard regmap IRQ controller handling + * + * @map: The regmap for the device. + * @irq: The IRQ the device uses to signal interrupts. + * @irq_flags: The IRQF_ flags to use for the primary interrupt. + * @irq_base: Allocate at specific IRQ number if irq_base > 0. + * @chip: Configuration for the interrupt controller. + * @data: Runtime data structure for the controller, allocated on success. + * + * Returns 0 on success or an errno on failure. + * + * This is the same as regmap_add_irq_chip_fwnode, except that the firmware + * node of the regmap is used. + */ +int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, + int irq_base, const struct regmap_irq_chip *chip, + struct regmap_irq_chip_data **data) +{ + return regmap_add_irq_chip_fwnode(dev_fwnode(map->dev), map, irq, + irq_flags, irq_base, chip, data); +} EXPORT_SYMBOL_GPL(regmap_add_irq_chip); /** @@ -742,7 +1000,7 @@ EXPORT_SYMBOL_GPL(regmap_add_irq_chip); void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) { unsigned int virq; - int hwirq; + int i, hwirq; if (!d) return; @@ -770,8 +1028,17 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); + kfree(d->main_status_buf); kfree(d->status_reg_buf); kfree(d->status_buf); + kfree(d->prev_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); + } + mutex_destroy(&d->lock); + lockdep_unregister_key(&d->lock_key); kfree(d); } EXPORT_SYMBOL_GPL(regmap_del_irq_chip); @@ -796,9 +1063,10 @@ static int devm_regmap_irq_chip_match(struct device *dev, void *res, void *data) } /** - * devm_regmap_add_irq_chip() - Resource manager regmap_add_irq_chip() + * devm_regmap_add_irq_chip_fwnode() - Resource managed regmap_add_irq_chip_fwnode() * * @dev: The device pointer on which irq_chip belongs to. + * @fwnode: The firmware node where the IRQ domain should be added to. * @map: The regmap for the device. * @irq: The IRQ the device uses to signal interrupts * @irq_flags: The IRQF_ flags to use for the primary interrupt. @@ -811,10 +1079,12 @@ static int devm_regmap_irq_chip_match(struct device *dev, void *res, void *data) * The ®map_irq_chip_data will be automatically released when the device is * unbound. */ -int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq, - int irq_flags, int irq_base, - const struct regmap_irq_chip *chip, - struct regmap_irq_chip_data **data) +int devm_regmap_add_irq_chip_fwnode(struct device *dev, + struct fwnode_handle *fwnode, + struct regmap *map, int irq, + int irq_flags, int irq_base, + const struct regmap_irq_chip *chip, + struct regmap_irq_chip_data **data) { struct regmap_irq_chip_data **ptr, *d; int ret; @@ -824,8 +1094,8 @@ int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq, if (!ptr) return -ENOMEM; - ret = regmap_add_irq_chip(map, irq, irq_flags, irq_base, - chip, &d); + ret = regmap_add_irq_chip_fwnode(fwnode, map, irq, irq_flags, irq_base, + chip, &d); if (ret < 0) { devres_free(ptr); return ret; @@ -836,12 +1106,39 @@ int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq, *data = d; return 0; } +EXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip_fwnode); + +/** + * devm_regmap_add_irq_chip() - Resource managed regmap_add_irq_chip() + * + * @dev: The device pointer on which irq_chip belongs to. + * @map: The regmap for the device. + * @irq: The IRQ the device uses to signal interrupts + * @irq_flags: The IRQF_ flags to use for the primary interrupt. + * @irq_base: Allocate at specific IRQ number if irq_base > 0. + * @chip: Configuration for the interrupt controller. + * @data: Runtime data structure for the controller, allocated on success + * + * Returns 0 on success or an errno on failure. + * + * The ®map_irq_chip_data will be automatically released when the device is + * unbound. + */ +int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq, + int irq_flags, int irq_base, + const struct regmap_irq_chip *chip, + struct regmap_irq_chip_data **data) +{ + return devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(map->dev), map, + irq, irq_flags, irq_base, chip, + data); +} EXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip); /** * devm_regmap_del_irq_chip() - Resource managed regmap_del_irq_chip() * - * @dev: Device for which which resource was allocated. + * @dev: Device for which the resource was allocated. * @irq: Primary IRQ for the device. * @data: ®map_irq_chip_data allocated by regmap_add_irq_chip(). * |
