162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// regmap based irq_chip 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright 2011 Wolfson Microelectronics plc 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/irq.h> 1362306a36Sopenharmony_ci#include <linux/irqdomain.h> 1462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "internal.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct regmap_irq_chip_data { 2162306a36Sopenharmony_ci struct mutex lock; 2262306a36Sopenharmony_ci struct irq_chip irq_chip; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci struct regmap *map; 2562306a36Sopenharmony_ci const struct regmap_irq_chip *chip; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci int irq_base; 2862306a36Sopenharmony_ci struct irq_domain *domain; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci int irq; 3162306a36Sopenharmony_ci int wake_count; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci void *status_reg_buf; 3462306a36Sopenharmony_ci unsigned int *main_status_buf; 3562306a36Sopenharmony_ci unsigned int *status_buf; 3662306a36Sopenharmony_ci unsigned int *mask_buf; 3762306a36Sopenharmony_ci unsigned int *mask_buf_def; 3862306a36Sopenharmony_ci unsigned int *wake_buf; 3962306a36Sopenharmony_ci unsigned int *type_buf; 4062306a36Sopenharmony_ci unsigned int *type_buf_def; 4162306a36Sopenharmony_ci unsigned int **config_buf; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci unsigned int irq_reg_stride; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci unsigned int (*get_irq_reg)(struct regmap_irq_chip_data *data, 4662306a36Sopenharmony_ci unsigned int base, int index); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci unsigned int clear_status:1; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic inline const 5262306a36Sopenharmony_cistruct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, 5362306a36Sopenharmony_ci int irq) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci return &data->chip->irqs[irq]; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic bool regmap_irq_can_bulk_read_status(struct regmap_irq_chip_data *data) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct regmap *map = data->map; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* 6362306a36Sopenharmony_ci * While possible that a user-defined ->get_irq_reg() callback might 6462306a36Sopenharmony_ci * be linear enough to support bulk reads, most of the time it won't. 6562306a36Sopenharmony_ci * Therefore only allow them if the default callback is being used. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci return data->irq_reg_stride == 1 && map->reg_stride == 1 && 6862306a36Sopenharmony_ci data->get_irq_reg == regmap_irq_get_irq_reg_linear && 6962306a36Sopenharmony_ci !map->use_single_read; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void regmap_irq_lock(struct irq_data *data) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci mutex_lock(&d->lock); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void regmap_irq_sync_unlock(struct irq_data *data) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 8262306a36Sopenharmony_ci struct regmap *map = d->map; 8362306a36Sopenharmony_ci int i, j, ret; 8462306a36Sopenharmony_ci u32 reg; 8562306a36Sopenharmony_ci u32 val; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (d->chip->runtime_pm) { 8862306a36Sopenharmony_ci ret = pm_runtime_get_sync(map->dev); 8962306a36Sopenharmony_ci if (ret < 0) 9062306a36Sopenharmony_ci dev_err(map->dev, "IRQ sync failed to resume: %d\n", 9162306a36Sopenharmony_ci ret); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (d->clear_status) { 9562306a36Sopenharmony_ci for (i = 0; i < d->chip->num_regs; i++) { 9662306a36Sopenharmony_ci reg = d->get_irq_reg(d, d->chip->status_base, i); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci ret = regmap_read(map, reg, &val); 9962306a36Sopenharmony_ci if (ret) 10062306a36Sopenharmony_ci dev_err(d->map->dev, 10162306a36Sopenharmony_ci "Failed to clear the interrupt status bits\n"); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci d->clear_status = false; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* 10862306a36Sopenharmony_ci * If there's been a change in the mask write it back to the 10962306a36Sopenharmony_ci * hardware. We rely on the use of the regmap core cache to 11062306a36Sopenharmony_ci * suppress pointless writes. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci for (i = 0; i < d->chip->num_regs; i++) { 11362306a36Sopenharmony_ci if (d->chip->handle_mask_sync) 11462306a36Sopenharmony_ci d->chip->handle_mask_sync(i, d->mask_buf_def[i], 11562306a36Sopenharmony_ci d->mask_buf[i], 11662306a36Sopenharmony_ci d->chip->irq_drv_data); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (d->chip->mask_base && !d->chip->handle_mask_sync) { 11962306a36Sopenharmony_ci reg = d->get_irq_reg(d, d->chip->mask_base, i); 12062306a36Sopenharmony_ci ret = regmap_update_bits(d->map, reg, 12162306a36Sopenharmony_ci d->mask_buf_def[i], 12262306a36Sopenharmony_ci d->mask_buf[i]); 12362306a36Sopenharmony_ci if (ret) 12462306a36Sopenharmony_ci dev_err(d->map->dev, "Failed to sync masks in %x\n", reg); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (d->chip->unmask_base && !d->chip->handle_mask_sync) { 12862306a36Sopenharmony_ci reg = d->get_irq_reg(d, d->chip->unmask_base, i); 12962306a36Sopenharmony_ci ret = regmap_update_bits(d->map, reg, 13062306a36Sopenharmony_ci d->mask_buf_def[i], ~d->mask_buf[i]); 13162306a36Sopenharmony_ci if (ret) 13262306a36Sopenharmony_ci dev_err(d->map->dev, "Failed to sync masks in %x\n", 13362306a36Sopenharmony_ci reg); 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci reg = d->get_irq_reg(d, d->chip->wake_base, i); 13762306a36Sopenharmony_ci if (d->wake_buf) { 13862306a36Sopenharmony_ci if (d->chip->wake_invert) 13962306a36Sopenharmony_ci ret = regmap_update_bits(d->map, reg, 14062306a36Sopenharmony_ci d->mask_buf_def[i], 14162306a36Sopenharmony_ci ~d->wake_buf[i]); 14262306a36Sopenharmony_ci else 14362306a36Sopenharmony_ci ret = regmap_update_bits(d->map, reg, 14462306a36Sopenharmony_ci d->mask_buf_def[i], 14562306a36Sopenharmony_ci d->wake_buf[i]); 14662306a36Sopenharmony_ci if (ret != 0) 14762306a36Sopenharmony_ci dev_err(d->map->dev, 14862306a36Sopenharmony_ci "Failed to sync wakes in %x: %d\n", 14962306a36Sopenharmony_ci reg, ret); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!d->chip->init_ack_masked) 15362306a36Sopenharmony_ci continue; 15462306a36Sopenharmony_ci /* 15562306a36Sopenharmony_ci * Ack all the masked interrupts unconditionally, 15662306a36Sopenharmony_ci * OR if there is masked interrupt which hasn't been Acked, 15762306a36Sopenharmony_ci * it'll be ignored in irq handler, then may introduce irq storm 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) { 16062306a36Sopenharmony_ci reg = d->get_irq_reg(d, d->chip->ack_base, i); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* some chips ack by write 0 */ 16362306a36Sopenharmony_ci if (d->chip->ack_invert) 16462306a36Sopenharmony_ci ret = regmap_write(map, reg, ~d->mask_buf[i]); 16562306a36Sopenharmony_ci else 16662306a36Sopenharmony_ci ret = regmap_write(map, reg, d->mask_buf[i]); 16762306a36Sopenharmony_ci if (d->chip->clear_ack) { 16862306a36Sopenharmony_ci if (d->chip->ack_invert && !ret) 16962306a36Sopenharmony_ci ret = regmap_write(map, reg, UINT_MAX); 17062306a36Sopenharmony_ci else if (!ret) 17162306a36Sopenharmony_ci ret = regmap_write(map, reg, 0); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci if (ret != 0) 17462306a36Sopenharmony_ci dev_err(d->map->dev, "Failed to ack 0x%x: %d\n", 17562306a36Sopenharmony_ci reg, ret); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci for (i = 0; i < d->chip->num_config_bases; i++) { 18062306a36Sopenharmony_ci for (j = 0; j < d->chip->num_config_regs; j++) { 18162306a36Sopenharmony_ci reg = d->get_irq_reg(d, d->chip->config_base[i], j); 18262306a36Sopenharmony_ci ret = regmap_write(map, reg, d->config_buf[i][j]); 18362306a36Sopenharmony_ci if (ret) 18462306a36Sopenharmony_ci dev_err(d->map->dev, 18562306a36Sopenharmony_ci "Failed to write config %x: %d\n", 18662306a36Sopenharmony_ci reg, ret); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (d->chip->runtime_pm) 19162306a36Sopenharmony_ci pm_runtime_put(map->dev); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* If we've changed our wakeup count propagate it to the parent */ 19462306a36Sopenharmony_ci if (d->wake_count < 0) 19562306a36Sopenharmony_ci for (i = d->wake_count; i < 0; i++) 19662306a36Sopenharmony_ci irq_set_irq_wake(d->irq, 0); 19762306a36Sopenharmony_ci else if (d->wake_count > 0) 19862306a36Sopenharmony_ci for (i = 0; i < d->wake_count; i++) 19962306a36Sopenharmony_ci irq_set_irq_wake(d->irq, 1); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci d->wake_count = 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci mutex_unlock(&d->lock); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void regmap_irq_enable(struct irq_data *data) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 20962306a36Sopenharmony_ci struct regmap *map = d->map; 21062306a36Sopenharmony_ci const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); 21162306a36Sopenharmony_ci unsigned int reg = irq_data->reg_offset / map->reg_stride; 21262306a36Sopenharmony_ci unsigned int mask; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * The type_in_mask flag means that the underlying hardware uses 21662306a36Sopenharmony_ci * separate mask bits for each interrupt trigger type, but we want 21762306a36Sopenharmony_ci * to have a single logical interrupt with a configurable type. 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci * If the interrupt we're enabling defines any supported types 22062306a36Sopenharmony_ci * then instead of using the regular mask bits for this interrupt, 22162306a36Sopenharmony_ci * use the value previously written to the type buffer at the 22262306a36Sopenharmony_ci * corresponding offset in regmap_irq_set_type(). 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci if (d->chip->type_in_mask && irq_data->type.types_supported) 22562306a36Sopenharmony_ci mask = d->type_buf[reg] & irq_data->mask; 22662306a36Sopenharmony_ci else 22762306a36Sopenharmony_ci mask = irq_data->mask; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (d->chip->clear_on_unmask) 23062306a36Sopenharmony_ci d->clear_status = true; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci d->mask_buf[reg] &= ~mask; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void regmap_irq_disable(struct irq_data *data) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 23862306a36Sopenharmony_ci struct regmap *map = d->map; 23962306a36Sopenharmony_ci const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int regmap_irq_set_type(struct irq_data *data, unsigned int type) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 24762306a36Sopenharmony_ci struct regmap *map = d->map; 24862306a36Sopenharmony_ci const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); 24962306a36Sopenharmony_ci int reg, ret; 25062306a36Sopenharmony_ci const struct regmap_irq_type *t = &irq_data->type; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if ((t->types_supported & type) != type) 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci reg = t->type_reg_offset / map->reg_stride; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (d->chip->type_in_mask) { 25862306a36Sopenharmony_ci ret = regmap_irq_set_type_config_simple(&d->type_buf, type, 25962306a36Sopenharmony_ci irq_data, reg, d->chip->irq_drv_data); 26062306a36Sopenharmony_ci if (ret) 26162306a36Sopenharmony_ci return ret; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (d->chip->set_type_config) { 26562306a36Sopenharmony_ci ret = d->chip->set_type_config(d->config_buf, type, irq_data, 26662306a36Sopenharmony_ci reg, d->chip->irq_drv_data); 26762306a36Sopenharmony_ci if (ret) 26862306a36Sopenharmony_ci return ret; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int regmap_irq_set_wake(struct irq_data *data, unsigned int on) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 27762306a36Sopenharmony_ci struct regmap *map = d->map; 27862306a36Sopenharmony_ci const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (on) { 28162306a36Sopenharmony_ci if (d->wake_buf) 28262306a36Sopenharmony_ci d->wake_buf[irq_data->reg_offset / map->reg_stride] 28362306a36Sopenharmony_ci &= ~irq_data->mask; 28462306a36Sopenharmony_ci d->wake_count++; 28562306a36Sopenharmony_ci } else { 28662306a36Sopenharmony_ci if (d->wake_buf) 28762306a36Sopenharmony_ci d->wake_buf[irq_data->reg_offset / map->reg_stride] 28862306a36Sopenharmony_ci |= irq_data->mask; 28962306a36Sopenharmony_ci d->wake_count--; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic const struct irq_chip regmap_irq_chip = { 29662306a36Sopenharmony_ci .irq_bus_lock = regmap_irq_lock, 29762306a36Sopenharmony_ci .irq_bus_sync_unlock = regmap_irq_sync_unlock, 29862306a36Sopenharmony_ci .irq_disable = regmap_irq_disable, 29962306a36Sopenharmony_ci .irq_enable = regmap_irq_enable, 30062306a36Sopenharmony_ci .irq_set_type = regmap_irq_set_type, 30162306a36Sopenharmony_ci .irq_set_wake = regmap_irq_set_wake, 30262306a36Sopenharmony_ci}; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic inline int read_sub_irq_data(struct regmap_irq_chip_data *data, 30562306a36Sopenharmony_ci unsigned int b) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci const struct regmap_irq_chip *chip = data->chip; 30862306a36Sopenharmony_ci struct regmap *map = data->map; 30962306a36Sopenharmony_ci struct regmap_irq_sub_irq_map *subreg; 31062306a36Sopenharmony_ci unsigned int reg; 31162306a36Sopenharmony_ci int i, ret = 0; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!chip->sub_reg_offsets) { 31462306a36Sopenharmony_ci reg = data->get_irq_reg(data, chip->status_base, b); 31562306a36Sopenharmony_ci ret = regmap_read(map, reg, &data->status_buf[b]); 31662306a36Sopenharmony_ci } else { 31762306a36Sopenharmony_ci /* 31862306a36Sopenharmony_ci * Note we can't use ->get_irq_reg() here because the offsets 31962306a36Sopenharmony_ci * in 'subreg' are *not* interchangeable with indices. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci subreg = &chip->sub_reg_offsets[b]; 32262306a36Sopenharmony_ci for (i = 0; i < subreg->num_regs; i++) { 32362306a36Sopenharmony_ci unsigned int offset = subreg->offset[i]; 32462306a36Sopenharmony_ci unsigned int index = offset / map->reg_stride; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ret = regmap_read(map, chip->status_base + offset, 32762306a36Sopenharmony_ci &data->status_buf[index]); 32862306a36Sopenharmony_ci if (ret) 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci return ret; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic irqreturn_t regmap_irq_thread(int irq, void *d) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct regmap_irq_chip_data *data = d; 33862306a36Sopenharmony_ci const struct regmap_irq_chip *chip = data->chip; 33962306a36Sopenharmony_ci struct regmap *map = data->map; 34062306a36Sopenharmony_ci int ret, i; 34162306a36Sopenharmony_ci bool handled = false; 34262306a36Sopenharmony_ci u32 reg; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (chip->handle_pre_irq) 34562306a36Sopenharmony_ci chip->handle_pre_irq(chip->irq_drv_data); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (chip->runtime_pm) { 34862306a36Sopenharmony_ci ret = pm_runtime_get_sync(map->dev); 34962306a36Sopenharmony_ci if (ret < 0) { 35062306a36Sopenharmony_ci dev_err(map->dev, "IRQ thread failed to resume: %d\n", 35162306a36Sopenharmony_ci ret); 35262306a36Sopenharmony_ci goto exit; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* 35762306a36Sopenharmony_ci * Read only registers with active IRQs if the chip has 'main status 35862306a36Sopenharmony_ci * register'. Else read in the statuses, using a single bulk read if 35962306a36Sopenharmony_ci * possible in order to reduce the I/O overheads. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (chip->no_status) { 36362306a36Sopenharmony_ci /* no status register so default to all active */ 36462306a36Sopenharmony_ci memset32(data->status_buf, GENMASK(31, 0), chip->num_regs); 36562306a36Sopenharmony_ci } else if (chip->num_main_regs) { 36662306a36Sopenharmony_ci unsigned int max_main_bits; 36762306a36Sopenharmony_ci unsigned long size; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci size = chip->num_regs * sizeof(unsigned int); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci max_main_bits = (chip->num_main_status_bits) ? 37262306a36Sopenharmony_ci chip->num_main_status_bits : chip->num_regs; 37362306a36Sopenharmony_ci /* Clear the status buf as we don't read all status regs */ 37462306a36Sopenharmony_ci memset(data->status_buf, 0, size); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* We could support bulk read for main status registers 37762306a36Sopenharmony_ci * but I don't expect to see devices with really many main 37862306a36Sopenharmony_ci * status registers so let's only support single reads for the 37962306a36Sopenharmony_ci * sake of simplicity. and add bulk reads only if needed 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_ci for (i = 0; i < chip->num_main_regs; i++) { 38262306a36Sopenharmony_ci reg = data->get_irq_reg(data, chip->main_status, i); 38362306a36Sopenharmony_ci ret = regmap_read(map, reg, &data->main_status_buf[i]); 38462306a36Sopenharmony_ci if (ret) { 38562306a36Sopenharmony_ci dev_err(map->dev, 38662306a36Sopenharmony_ci "Failed to read IRQ status %d\n", 38762306a36Sopenharmony_ci ret); 38862306a36Sopenharmony_ci goto exit; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Read sub registers with active IRQs */ 39362306a36Sopenharmony_ci for (i = 0; i < chip->num_main_regs; i++) { 39462306a36Sopenharmony_ci unsigned int b; 39562306a36Sopenharmony_ci const unsigned long mreg = data->main_status_buf[i]; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci for_each_set_bit(b, &mreg, map->format.val_bytes * 8) { 39862306a36Sopenharmony_ci if (i * map->format.val_bytes * 8 + b > 39962306a36Sopenharmony_ci max_main_bits) 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci ret = read_sub_irq_data(data, b); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (ret != 0) { 40462306a36Sopenharmony_ci dev_err(map->dev, 40562306a36Sopenharmony_ci "Failed to read IRQ status %d\n", 40662306a36Sopenharmony_ci ret); 40762306a36Sopenharmony_ci goto exit; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci } else if (regmap_irq_can_bulk_read_status(data)) { 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci u8 *buf8 = data->status_reg_buf; 41562306a36Sopenharmony_ci u16 *buf16 = data->status_reg_buf; 41662306a36Sopenharmony_ci u32 *buf32 = data->status_reg_buf; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci BUG_ON(!data->status_reg_buf); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci ret = regmap_bulk_read(map, chip->status_base, 42162306a36Sopenharmony_ci data->status_reg_buf, 42262306a36Sopenharmony_ci chip->num_regs); 42362306a36Sopenharmony_ci if (ret != 0) { 42462306a36Sopenharmony_ci dev_err(map->dev, "Failed to read IRQ status: %d\n", 42562306a36Sopenharmony_ci ret); 42662306a36Sopenharmony_ci goto exit; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci for (i = 0; i < data->chip->num_regs; i++) { 43062306a36Sopenharmony_ci switch (map->format.val_bytes) { 43162306a36Sopenharmony_ci case 1: 43262306a36Sopenharmony_ci data->status_buf[i] = buf8[i]; 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci case 2: 43562306a36Sopenharmony_ci data->status_buf[i] = buf16[i]; 43662306a36Sopenharmony_ci break; 43762306a36Sopenharmony_ci case 4: 43862306a36Sopenharmony_ci data->status_buf[i] = buf32[i]; 43962306a36Sopenharmony_ci break; 44062306a36Sopenharmony_ci default: 44162306a36Sopenharmony_ci BUG(); 44262306a36Sopenharmony_ci goto exit; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci } else { 44762306a36Sopenharmony_ci for (i = 0; i < data->chip->num_regs; i++) { 44862306a36Sopenharmony_ci unsigned int reg = data->get_irq_reg(data, 44962306a36Sopenharmony_ci data->chip->status_base, i); 45062306a36Sopenharmony_ci ret = regmap_read(map, reg, &data->status_buf[i]); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (ret != 0) { 45362306a36Sopenharmony_ci dev_err(map->dev, 45462306a36Sopenharmony_ci "Failed to read IRQ status: %d\n", 45562306a36Sopenharmony_ci ret); 45662306a36Sopenharmony_ci goto exit; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (chip->status_invert) 46262306a36Sopenharmony_ci for (i = 0; i < data->chip->num_regs; i++) 46362306a36Sopenharmony_ci data->status_buf[i] = ~data->status_buf[i]; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* 46662306a36Sopenharmony_ci * Ignore masked IRQs and ack if we need to; we ack early so 46762306a36Sopenharmony_ci * there is no race between handling and acknowledging the 46862306a36Sopenharmony_ci * interrupt. We assume that typically few of the interrupts 46962306a36Sopenharmony_ci * will fire simultaneously so don't worry about overhead from 47062306a36Sopenharmony_ci * doing a write per register. 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_ci for (i = 0; i < data->chip->num_regs; i++) { 47362306a36Sopenharmony_ci data->status_buf[i] &= ~data->mask_buf[i]; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) { 47662306a36Sopenharmony_ci reg = data->get_irq_reg(data, data->chip->ack_base, i); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (chip->ack_invert) 47962306a36Sopenharmony_ci ret = regmap_write(map, reg, 48062306a36Sopenharmony_ci ~data->status_buf[i]); 48162306a36Sopenharmony_ci else 48262306a36Sopenharmony_ci ret = regmap_write(map, reg, 48362306a36Sopenharmony_ci data->status_buf[i]); 48462306a36Sopenharmony_ci if (chip->clear_ack) { 48562306a36Sopenharmony_ci if (chip->ack_invert && !ret) 48662306a36Sopenharmony_ci ret = regmap_write(map, reg, UINT_MAX); 48762306a36Sopenharmony_ci else if (!ret) 48862306a36Sopenharmony_ci ret = regmap_write(map, reg, 0); 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci if (ret != 0) 49162306a36Sopenharmony_ci dev_err(map->dev, "Failed to ack 0x%x: %d\n", 49262306a36Sopenharmony_ci reg, ret); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci for (i = 0; i < chip->num_irqs; i++) { 49762306a36Sopenharmony_ci if (data->status_buf[chip->irqs[i].reg_offset / 49862306a36Sopenharmony_ci map->reg_stride] & chip->irqs[i].mask) { 49962306a36Sopenharmony_ci handle_nested_irq(irq_find_mapping(data->domain, i)); 50062306a36Sopenharmony_ci handled = true; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ciexit: 50562306a36Sopenharmony_ci if (chip->handle_post_irq) 50662306a36Sopenharmony_ci chip->handle_post_irq(chip->irq_drv_data); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (chip->runtime_pm) 50962306a36Sopenharmony_ci pm_runtime_put(map->dev); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (handled) 51262306a36Sopenharmony_ci return IRQ_HANDLED; 51362306a36Sopenharmony_ci else 51462306a36Sopenharmony_ci return IRQ_NONE; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int regmap_irq_map(struct irq_domain *h, unsigned int virq, 51862306a36Sopenharmony_ci irq_hw_number_t hw) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct regmap_irq_chip_data *data = h->host_data; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci irq_set_chip_data(virq, data); 52362306a36Sopenharmony_ci irq_set_chip(virq, &data->irq_chip); 52462306a36Sopenharmony_ci irq_set_nested_thread(virq, 1); 52562306a36Sopenharmony_ci irq_set_parent(virq, data->irq); 52662306a36Sopenharmony_ci irq_set_noprobe(virq); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci return 0; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic const struct irq_domain_ops regmap_domain_ops = { 53262306a36Sopenharmony_ci .map = regmap_irq_map, 53362306a36Sopenharmony_ci .xlate = irq_domain_xlate_onetwocell, 53462306a36Sopenharmony_ci}; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/** 53762306a36Sopenharmony_ci * regmap_irq_get_irq_reg_linear() - Linear IRQ register mapping callback. 53862306a36Sopenharmony_ci * @data: Data for the &struct regmap_irq_chip 53962306a36Sopenharmony_ci * @base: Base register 54062306a36Sopenharmony_ci * @index: Register index 54162306a36Sopenharmony_ci * 54262306a36Sopenharmony_ci * Returns the register address corresponding to the given @base and @index 54362306a36Sopenharmony_ci * by the formula ``base + index * regmap_stride * irq_reg_stride``. 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ciunsigned int regmap_irq_get_irq_reg_linear(struct regmap_irq_chip_data *data, 54662306a36Sopenharmony_ci unsigned int base, int index) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct regmap *map = data->map; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return base + index * map->reg_stride * data->irq_reg_stride; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_irq_get_irq_reg_linear); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/** 55562306a36Sopenharmony_ci * regmap_irq_set_type_config_simple() - Simple IRQ type configuration callback. 55662306a36Sopenharmony_ci * @buf: Buffer containing configuration register values, this is a 2D array of 55762306a36Sopenharmony_ci * `num_config_bases` rows, each of `num_config_regs` elements. 55862306a36Sopenharmony_ci * @type: The requested IRQ type. 55962306a36Sopenharmony_ci * @irq_data: The IRQ being configured. 56062306a36Sopenharmony_ci * @idx: Index of the irq's config registers within each array `buf[i]` 56162306a36Sopenharmony_ci * @irq_drv_data: Driver specific IRQ data 56262306a36Sopenharmony_ci * 56362306a36Sopenharmony_ci * This is a &struct regmap_irq_chip->set_type_config callback suitable for 56462306a36Sopenharmony_ci * chips with one config register. Register values are updated according to 56562306a36Sopenharmony_ci * the &struct regmap_irq_type data associated with an IRQ. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ciint regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type, 56862306a36Sopenharmony_ci const struct regmap_irq *irq_data, 56962306a36Sopenharmony_ci int idx, void *irq_drv_data) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci const struct regmap_irq_type *t = &irq_data->type; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (t->type_reg_mask) 57462306a36Sopenharmony_ci buf[0][idx] &= ~t->type_reg_mask; 57562306a36Sopenharmony_ci else 57662306a36Sopenharmony_ci buf[0][idx] &= ~(t->type_falling_val | 57762306a36Sopenharmony_ci t->type_rising_val | 57862306a36Sopenharmony_ci t->type_level_low_val | 57962306a36Sopenharmony_ci t->type_level_high_val); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci switch (type) { 58262306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 58362306a36Sopenharmony_ci buf[0][idx] |= t->type_falling_val; 58462306a36Sopenharmony_ci break; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 58762306a36Sopenharmony_ci buf[0][idx] |= t->type_rising_val; 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 59162306a36Sopenharmony_ci buf[0][idx] |= (t->type_falling_val | 59262306a36Sopenharmony_ci t->type_rising_val); 59362306a36Sopenharmony_ci break; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 59662306a36Sopenharmony_ci buf[0][idx] |= t->type_level_high_val; 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 60062306a36Sopenharmony_ci buf[0][idx] |= t->type_level_low_val; 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci default: 60462306a36Sopenharmony_ci return -EINVAL; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_irq_set_type_config_simple); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci/** 61262306a36Sopenharmony_ci * regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling 61362306a36Sopenharmony_ci * 61462306a36Sopenharmony_ci * @fwnode: The firmware node where the IRQ domain should be added to. 61562306a36Sopenharmony_ci * @map: The regmap for the device. 61662306a36Sopenharmony_ci * @irq: The IRQ the device uses to signal interrupts. 61762306a36Sopenharmony_ci * @irq_flags: The IRQF_ flags to use for the primary interrupt. 61862306a36Sopenharmony_ci * @irq_base: Allocate at specific IRQ number if irq_base > 0. 61962306a36Sopenharmony_ci * @chip: Configuration for the interrupt controller. 62062306a36Sopenharmony_ci * @data: Runtime data structure for the controller, allocated on success. 62162306a36Sopenharmony_ci * 62262306a36Sopenharmony_ci * Returns 0 on success or an errno on failure. 62362306a36Sopenharmony_ci * 62462306a36Sopenharmony_ci * In order for this to be efficient the chip really should use a 62562306a36Sopenharmony_ci * register cache. The chip driver is responsible for restoring the 62662306a36Sopenharmony_ci * register values used by the IRQ controller over suspend and resume. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ciint regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, 62962306a36Sopenharmony_ci struct regmap *map, int irq, 63062306a36Sopenharmony_ci int irq_flags, int irq_base, 63162306a36Sopenharmony_ci const struct regmap_irq_chip *chip, 63262306a36Sopenharmony_ci struct regmap_irq_chip_data **data) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct regmap_irq_chip_data *d; 63562306a36Sopenharmony_ci int i; 63662306a36Sopenharmony_ci int ret = -ENOMEM; 63762306a36Sopenharmony_ci u32 reg; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (chip->num_regs <= 0) 64062306a36Sopenharmony_ci return -EINVAL; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (chip->clear_on_unmask && (chip->ack_base || chip->use_ack)) 64362306a36Sopenharmony_ci return -EINVAL; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (chip->mask_base && chip->unmask_base && !chip->mask_unmask_non_inverted) 64662306a36Sopenharmony_ci return -EINVAL; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci for (i = 0; i < chip->num_irqs; i++) { 64962306a36Sopenharmony_ci if (chip->irqs[i].reg_offset % map->reg_stride) 65062306a36Sopenharmony_ci return -EINVAL; 65162306a36Sopenharmony_ci if (chip->irqs[i].reg_offset / map->reg_stride >= 65262306a36Sopenharmony_ci chip->num_regs) 65362306a36Sopenharmony_ci return -EINVAL; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (irq_base) { 65762306a36Sopenharmony_ci irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); 65862306a36Sopenharmony_ci if (irq_base < 0) { 65962306a36Sopenharmony_ci dev_warn(map->dev, "Failed to allocate IRQs: %d\n", 66062306a36Sopenharmony_ci irq_base); 66162306a36Sopenharmony_ci return irq_base; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci d = kzalloc(sizeof(*d), GFP_KERNEL); 66662306a36Sopenharmony_ci if (!d) 66762306a36Sopenharmony_ci return -ENOMEM; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (chip->num_main_regs) { 67062306a36Sopenharmony_ci d->main_status_buf = kcalloc(chip->num_main_regs, 67162306a36Sopenharmony_ci sizeof(*d->main_status_buf), 67262306a36Sopenharmony_ci GFP_KERNEL); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (!d->main_status_buf) 67562306a36Sopenharmony_ci goto err_alloc; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci d->status_buf = kcalloc(chip->num_regs, sizeof(*d->status_buf), 67962306a36Sopenharmony_ci GFP_KERNEL); 68062306a36Sopenharmony_ci if (!d->status_buf) 68162306a36Sopenharmony_ci goto err_alloc; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci d->mask_buf = kcalloc(chip->num_regs, sizeof(*d->mask_buf), 68462306a36Sopenharmony_ci GFP_KERNEL); 68562306a36Sopenharmony_ci if (!d->mask_buf) 68662306a36Sopenharmony_ci goto err_alloc; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci d->mask_buf_def = kcalloc(chip->num_regs, sizeof(*d->mask_buf_def), 68962306a36Sopenharmony_ci GFP_KERNEL); 69062306a36Sopenharmony_ci if (!d->mask_buf_def) 69162306a36Sopenharmony_ci goto err_alloc; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (chip->wake_base) { 69462306a36Sopenharmony_ci d->wake_buf = kcalloc(chip->num_regs, sizeof(*d->wake_buf), 69562306a36Sopenharmony_ci GFP_KERNEL); 69662306a36Sopenharmony_ci if (!d->wake_buf) 69762306a36Sopenharmony_ci goto err_alloc; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (chip->type_in_mask) { 70162306a36Sopenharmony_ci d->type_buf_def = kcalloc(chip->num_regs, 70262306a36Sopenharmony_ci sizeof(*d->type_buf_def), GFP_KERNEL); 70362306a36Sopenharmony_ci if (!d->type_buf_def) 70462306a36Sopenharmony_ci goto err_alloc; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci d->type_buf = kcalloc(chip->num_regs, sizeof(*d->type_buf), GFP_KERNEL); 70762306a36Sopenharmony_ci if (!d->type_buf) 70862306a36Sopenharmony_ci goto err_alloc; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (chip->num_config_bases && chip->num_config_regs) { 71262306a36Sopenharmony_ci /* 71362306a36Sopenharmony_ci * Create config_buf[num_config_bases][num_config_regs] 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci d->config_buf = kcalloc(chip->num_config_bases, 71662306a36Sopenharmony_ci sizeof(*d->config_buf), GFP_KERNEL); 71762306a36Sopenharmony_ci if (!d->config_buf) 71862306a36Sopenharmony_ci goto err_alloc; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci for (i = 0; i < chip->num_config_bases; i++) { 72162306a36Sopenharmony_ci d->config_buf[i] = kcalloc(chip->num_config_regs, 72262306a36Sopenharmony_ci sizeof(**d->config_buf), 72362306a36Sopenharmony_ci GFP_KERNEL); 72462306a36Sopenharmony_ci if (!d->config_buf[i]) 72562306a36Sopenharmony_ci goto err_alloc; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci d->irq_chip = regmap_irq_chip; 73062306a36Sopenharmony_ci d->irq_chip.name = chip->name; 73162306a36Sopenharmony_ci d->irq = irq; 73262306a36Sopenharmony_ci d->map = map; 73362306a36Sopenharmony_ci d->chip = chip; 73462306a36Sopenharmony_ci d->irq_base = irq_base; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (chip->irq_reg_stride) 73762306a36Sopenharmony_ci d->irq_reg_stride = chip->irq_reg_stride; 73862306a36Sopenharmony_ci else 73962306a36Sopenharmony_ci d->irq_reg_stride = 1; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (chip->get_irq_reg) 74262306a36Sopenharmony_ci d->get_irq_reg = chip->get_irq_reg; 74362306a36Sopenharmony_ci else 74462306a36Sopenharmony_ci d->get_irq_reg = regmap_irq_get_irq_reg_linear; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (regmap_irq_can_bulk_read_status(d)) { 74762306a36Sopenharmony_ci d->status_reg_buf = kmalloc_array(chip->num_regs, 74862306a36Sopenharmony_ci map->format.val_bytes, 74962306a36Sopenharmony_ci GFP_KERNEL); 75062306a36Sopenharmony_ci if (!d->status_reg_buf) 75162306a36Sopenharmony_ci goto err_alloc; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci mutex_init(&d->lock); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci for (i = 0; i < chip->num_irqs; i++) 75762306a36Sopenharmony_ci d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] 75862306a36Sopenharmony_ci |= chip->irqs[i].mask; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci /* Mask all the interrupts by default */ 76162306a36Sopenharmony_ci for (i = 0; i < chip->num_regs; i++) { 76262306a36Sopenharmony_ci d->mask_buf[i] = d->mask_buf_def[i]; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (chip->handle_mask_sync) { 76562306a36Sopenharmony_ci ret = chip->handle_mask_sync(i, d->mask_buf_def[i], 76662306a36Sopenharmony_ci d->mask_buf[i], 76762306a36Sopenharmony_ci chip->irq_drv_data); 76862306a36Sopenharmony_ci if (ret) 76962306a36Sopenharmony_ci goto err_alloc; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (chip->mask_base && !chip->handle_mask_sync) { 77362306a36Sopenharmony_ci reg = d->get_irq_reg(d, chip->mask_base, i); 77462306a36Sopenharmony_ci ret = regmap_update_bits(d->map, reg, 77562306a36Sopenharmony_ci d->mask_buf_def[i], 77662306a36Sopenharmony_ci d->mask_buf[i]); 77762306a36Sopenharmony_ci if (ret) { 77862306a36Sopenharmony_ci dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", 77962306a36Sopenharmony_ci reg, ret); 78062306a36Sopenharmony_ci goto err_alloc; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (chip->unmask_base && !chip->handle_mask_sync) { 78562306a36Sopenharmony_ci reg = d->get_irq_reg(d, chip->unmask_base, i); 78662306a36Sopenharmony_ci ret = regmap_update_bits(d->map, reg, 78762306a36Sopenharmony_ci d->mask_buf_def[i], ~d->mask_buf[i]); 78862306a36Sopenharmony_ci if (ret) { 78962306a36Sopenharmony_ci dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", 79062306a36Sopenharmony_ci reg, ret); 79162306a36Sopenharmony_ci goto err_alloc; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (!chip->init_ack_masked) 79662306a36Sopenharmony_ci continue; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* Ack masked but set interrupts */ 79962306a36Sopenharmony_ci if (d->chip->no_status) { 80062306a36Sopenharmony_ci /* no status register so default to all active */ 80162306a36Sopenharmony_ci d->status_buf[i] = GENMASK(31, 0); 80262306a36Sopenharmony_ci } else { 80362306a36Sopenharmony_ci reg = d->get_irq_reg(d, d->chip->status_base, i); 80462306a36Sopenharmony_ci ret = regmap_read(map, reg, &d->status_buf[i]); 80562306a36Sopenharmony_ci if (ret != 0) { 80662306a36Sopenharmony_ci dev_err(map->dev, "Failed to read IRQ status: %d\n", 80762306a36Sopenharmony_ci ret); 80862306a36Sopenharmony_ci goto err_alloc; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (chip->status_invert) 81362306a36Sopenharmony_ci d->status_buf[i] = ~d->status_buf[i]; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { 81662306a36Sopenharmony_ci reg = d->get_irq_reg(d, d->chip->ack_base, i); 81762306a36Sopenharmony_ci if (chip->ack_invert) 81862306a36Sopenharmony_ci ret = regmap_write(map, reg, 81962306a36Sopenharmony_ci ~(d->status_buf[i] & d->mask_buf[i])); 82062306a36Sopenharmony_ci else 82162306a36Sopenharmony_ci ret = regmap_write(map, reg, 82262306a36Sopenharmony_ci d->status_buf[i] & d->mask_buf[i]); 82362306a36Sopenharmony_ci if (chip->clear_ack) { 82462306a36Sopenharmony_ci if (chip->ack_invert && !ret) 82562306a36Sopenharmony_ci ret = regmap_write(map, reg, UINT_MAX); 82662306a36Sopenharmony_ci else if (!ret) 82762306a36Sopenharmony_ci ret = regmap_write(map, reg, 0); 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci if (ret != 0) { 83062306a36Sopenharmony_ci dev_err(map->dev, "Failed to ack 0x%x: %d\n", 83162306a36Sopenharmony_ci reg, ret); 83262306a36Sopenharmony_ci goto err_alloc; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* Wake is disabled by default */ 83862306a36Sopenharmony_ci if (d->wake_buf) { 83962306a36Sopenharmony_ci for (i = 0; i < chip->num_regs; i++) { 84062306a36Sopenharmony_ci d->wake_buf[i] = d->mask_buf_def[i]; 84162306a36Sopenharmony_ci reg = d->get_irq_reg(d, d->chip->wake_base, i); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (chip->wake_invert) 84462306a36Sopenharmony_ci ret = regmap_update_bits(d->map, reg, 84562306a36Sopenharmony_ci d->mask_buf_def[i], 84662306a36Sopenharmony_ci 0); 84762306a36Sopenharmony_ci else 84862306a36Sopenharmony_ci ret = regmap_update_bits(d->map, reg, 84962306a36Sopenharmony_ci d->mask_buf_def[i], 85062306a36Sopenharmony_ci d->wake_buf[i]); 85162306a36Sopenharmony_ci if (ret != 0) { 85262306a36Sopenharmony_ci dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", 85362306a36Sopenharmony_ci reg, ret); 85462306a36Sopenharmony_ci goto err_alloc; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (irq_base) 86062306a36Sopenharmony_ci d->domain = irq_domain_create_legacy(fwnode, chip->num_irqs, 86162306a36Sopenharmony_ci irq_base, 0, 86262306a36Sopenharmony_ci ®map_domain_ops, d); 86362306a36Sopenharmony_ci else 86462306a36Sopenharmony_ci d->domain = irq_domain_create_linear(fwnode, chip->num_irqs, 86562306a36Sopenharmony_ci ®map_domain_ops, d); 86662306a36Sopenharmony_ci if (!d->domain) { 86762306a36Sopenharmony_ci dev_err(map->dev, "Failed to create IRQ domain\n"); 86862306a36Sopenharmony_ci ret = -ENOMEM; 86962306a36Sopenharmony_ci goto err_alloc; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci ret = request_threaded_irq(irq, NULL, regmap_irq_thread, 87362306a36Sopenharmony_ci irq_flags | IRQF_ONESHOT, 87462306a36Sopenharmony_ci chip->name, d); 87562306a36Sopenharmony_ci if (ret != 0) { 87662306a36Sopenharmony_ci dev_err(map->dev, "Failed to request IRQ %d for %s: %d\n", 87762306a36Sopenharmony_ci irq, chip->name, ret); 87862306a36Sopenharmony_ci goto err_domain; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci *data = d; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return 0; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cierr_domain: 88662306a36Sopenharmony_ci /* Should really dispose of the domain but... */ 88762306a36Sopenharmony_cierr_alloc: 88862306a36Sopenharmony_ci kfree(d->type_buf); 88962306a36Sopenharmony_ci kfree(d->type_buf_def); 89062306a36Sopenharmony_ci kfree(d->wake_buf); 89162306a36Sopenharmony_ci kfree(d->mask_buf_def); 89262306a36Sopenharmony_ci kfree(d->mask_buf); 89362306a36Sopenharmony_ci kfree(d->status_buf); 89462306a36Sopenharmony_ci kfree(d->status_reg_buf); 89562306a36Sopenharmony_ci if (d->config_buf) { 89662306a36Sopenharmony_ci for (i = 0; i < chip->num_config_bases; i++) 89762306a36Sopenharmony_ci kfree(d->config_buf[i]); 89862306a36Sopenharmony_ci kfree(d->config_buf); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci kfree(d); 90162306a36Sopenharmony_ci return ret; 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_add_irq_chip_fwnode); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci/** 90662306a36Sopenharmony_ci * regmap_add_irq_chip() - Use standard regmap IRQ controller handling 90762306a36Sopenharmony_ci * 90862306a36Sopenharmony_ci * @map: The regmap for the device. 90962306a36Sopenharmony_ci * @irq: The IRQ the device uses to signal interrupts. 91062306a36Sopenharmony_ci * @irq_flags: The IRQF_ flags to use for the primary interrupt. 91162306a36Sopenharmony_ci * @irq_base: Allocate at specific IRQ number if irq_base > 0. 91262306a36Sopenharmony_ci * @chip: Configuration for the interrupt controller. 91362306a36Sopenharmony_ci * @data: Runtime data structure for the controller, allocated on success. 91462306a36Sopenharmony_ci * 91562306a36Sopenharmony_ci * Returns 0 on success or an errno on failure. 91662306a36Sopenharmony_ci * 91762306a36Sopenharmony_ci * This is the same as regmap_add_irq_chip_fwnode, except that the firmware 91862306a36Sopenharmony_ci * node of the regmap is used. 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_ciint regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, 92162306a36Sopenharmony_ci int irq_base, const struct regmap_irq_chip *chip, 92262306a36Sopenharmony_ci struct regmap_irq_chip_data **data) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci return regmap_add_irq_chip_fwnode(dev_fwnode(map->dev), map, irq, 92562306a36Sopenharmony_ci irq_flags, irq_base, chip, data); 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_add_irq_chip); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci/** 93062306a36Sopenharmony_ci * regmap_del_irq_chip() - Stop interrupt handling for a regmap IRQ chip 93162306a36Sopenharmony_ci * 93262306a36Sopenharmony_ci * @irq: Primary IRQ for the device 93362306a36Sopenharmony_ci * @d: ®map_irq_chip_data allocated by regmap_add_irq_chip() 93462306a36Sopenharmony_ci * 93562306a36Sopenharmony_ci * This function also disposes of all mapped IRQs on the chip. 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_civoid regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci unsigned int virq; 94062306a36Sopenharmony_ci int i, hwirq; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci if (!d) 94362306a36Sopenharmony_ci return; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci free_irq(irq, d); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* Dispose all virtual irq from irq domain before removing it */ 94862306a36Sopenharmony_ci for (hwirq = 0; hwirq < d->chip->num_irqs; hwirq++) { 94962306a36Sopenharmony_ci /* Ignore hwirq if holes in the IRQ list */ 95062306a36Sopenharmony_ci if (!d->chip->irqs[hwirq].mask) 95162306a36Sopenharmony_ci continue; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* 95462306a36Sopenharmony_ci * Find the virtual irq of hwirq on chip and if it is 95562306a36Sopenharmony_ci * there then dispose it 95662306a36Sopenharmony_ci */ 95762306a36Sopenharmony_ci virq = irq_find_mapping(d->domain, hwirq); 95862306a36Sopenharmony_ci if (virq) 95962306a36Sopenharmony_ci irq_dispose_mapping(virq); 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci irq_domain_remove(d->domain); 96362306a36Sopenharmony_ci kfree(d->type_buf); 96462306a36Sopenharmony_ci kfree(d->type_buf_def); 96562306a36Sopenharmony_ci kfree(d->wake_buf); 96662306a36Sopenharmony_ci kfree(d->mask_buf_def); 96762306a36Sopenharmony_ci kfree(d->mask_buf); 96862306a36Sopenharmony_ci kfree(d->status_reg_buf); 96962306a36Sopenharmony_ci kfree(d->status_buf); 97062306a36Sopenharmony_ci if (d->config_buf) { 97162306a36Sopenharmony_ci for (i = 0; i < d->chip->num_config_bases; i++) 97262306a36Sopenharmony_ci kfree(d->config_buf[i]); 97362306a36Sopenharmony_ci kfree(d->config_buf); 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci kfree(d); 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_del_irq_chip); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_cistatic void devm_regmap_irq_chip_release(struct device *dev, void *res) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct regmap_irq_chip_data *d = *(struct regmap_irq_chip_data **)res; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci regmap_del_irq_chip(d->irq, d); 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic int devm_regmap_irq_chip_match(struct device *dev, void *res, void *data) 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci struct regmap_irq_chip_data **r = res; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (!r || !*r) { 99262306a36Sopenharmony_ci WARN_ON(!r || !*r); 99362306a36Sopenharmony_ci return 0; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci return *r == data; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci/** 99962306a36Sopenharmony_ci * devm_regmap_add_irq_chip_fwnode() - Resource managed regmap_add_irq_chip_fwnode() 100062306a36Sopenharmony_ci * 100162306a36Sopenharmony_ci * @dev: The device pointer on which irq_chip belongs to. 100262306a36Sopenharmony_ci * @fwnode: The firmware node where the IRQ domain should be added to. 100362306a36Sopenharmony_ci * @map: The regmap for the device. 100462306a36Sopenharmony_ci * @irq: The IRQ the device uses to signal interrupts 100562306a36Sopenharmony_ci * @irq_flags: The IRQF_ flags to use for the primary interrupt. 100662306a36Sopenharmony_ci * @irq_base: Allocate at specific IRQ number if irq_base > 0. 100762306a36Sopenharmony_ci * @chip: Configuration for the interrupt controller. 100862306a36Sopenharmony_ci * @data: Runtime data structure for the controller, allocated on success 100962306a36Sopenharmony_ci * 101062306a36Sopenharmony_ci * Returns 0 on success or an errno on failure. 101162306a36Sopenharmony_ci * 101262306a36Sopenharmony_ci * The ®map_irq_chip_data will be automatically released when the device is 101362306a36Sopenharmony_ci * unbound. 101462306a36Sopenharmony_ci */ 101562306a36Sopenharmony_ciint devm_regmap_add_irq_chip_fwnode(struct device *dev, 101662306a36Sopenharmony_ci struct fwnode_handle *fwnode, 101762306a36Sopenharmony_ci struct regmap *map, int irq, 101862306a36Sopenharmony_ci int irq_flags, int irq_base, 101962306a36Sopenharmony_ci const struct regmap_irq_chip *chip, 102062306a36Sopenharmony_ci struct regmap_irq_chip_data **data) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct regmap_irq_chip_data **ptr, *d; 102362306a36Sopenharmony_ci int ret; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci ptr = devres_alloc(devm_regmap_irq_chip_release, sizeof(*ptr), 102662306a36Sopenharmony_ci GFP_KERNEL); 102762306a36Sopenharmony_ci if (!ptr) 102862306a36Sopenharmony_ci return -ENOMEM; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci ret = regmap_add_irq_chip_fwnode(fwnode, map, irq, irq_flags, irq_base, 103162306a36Sopenharmony_ci chip, &d); 103262306a36Sopenharmony_ci if (ret < 0) { 103362306a36Sopenharmony_ci devres_free(ptr); 103462306a36Sopenharmony_ci return ret; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci *ptr = d; 103862306a36Sopenharmony_ci devres_add(dev, ptr); 103962306a36Sopenharmony_ci *data = d; 104062306a36Sopenharmony_ci return 0; 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip_fwnode); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci/** 104562306a36Sopenharmony_ci * devm_regmap_add_irq_chip() - Resource managed regmap_add_irq_chip() 104662306a36Sopenharmony_ci * 104762306a36Sopenharmony_ci * @dev: The device pointer on which irq_chip belongs to. 104862306a36Sopenharmony_ci * @map: The regmap for the device. 104962306a36Sopenharmony_ci * @irq: The IRQ the device uses to signal interrupts 105062306a36Sopenharmony_ci * @irq_flags: The IRQF_ flags to use for the primary interrupt. 105162306a36Sopenharmony_ci * @irq_base: Allocate at specific IRQ number if irq_base > 0. 105262306a36Sopenharmony_ci * @chip: Configuration for the interrupt controller. 105362306a36Sopenharmony_ci * @data: Runtime data structure for the controller, allocated on success 105462306a36Sopenharmony_ci * 105562306a36Sopenharmony_ci * Returns 0 on success or an errno on failure. 105662306a36Sopenharmony_ci * 105762306a36Sopenharmony_ci * The ®map_irq_chip_data will be automatically released when the device is 105862306a36Sopenharmony_ci * unbound. 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_ciint devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq, 106162306a36Sopenharmony_ci int irq_flags, int irq_base, 106262306a36Sopenharmony_ci const struct regmap_irq_chip *chip, 106362306a36Sopenharmony_ci struct regmap_irq_chip_data **data) 106462306a36Sopenharmony_ci{ 106562306a36Sopenharmony_ci return devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(map->dev), map, 106662306a36Sopenharmony_ci irq, irq_flags, irq_base, chip, 106762306a36Sopenharmony_ci data); 106862306a36Sopenharmony_ci} 106962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci/** 107262306a36Sopenharmony_ci * devm_regmap_del_irq_chip() - Resource managed regmap_del_irq_chip() 107362306a36Sopenharmony_ci * 107462306a36Sopenharmony_ci * @dev: Device for which the resource was allocated. 107562306a36Sopenharmony_ci * @irq: Primary IRQ for the device. 107662306a36Sopenharmony_ci * @data: ®map_irq_chip_data allocated by regmap_add_irq_chip(). 107762306a36Sopenharmony_ci * 107862306a36Sopenharmony_ci * A resource managed version of regmap_del_irq_chip(). 107962306a36Sopenharmony_ci */ 108062306a36Sopenharmony_civoid devm_regmap_del_irq_chip(struct device *dev, int irq, 108162306a36Sopenharmony_ci struct regmap_irq_chip_data *data) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci int rc; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci WARN_ON(irq != data->irq); 108662306a36Sopenharmony_ci rc = devres_release(dev, devm_regmap_irq_chip_release, 108762306a36Sopenharmony_ci devm_regmap_irq_chip_match, data); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (rc != 0) 109062306a36Sopenharmony_ci WARN_ON(rc); 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_regmap_del_irq_chip); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci/** 109562306a36Sopenharmony_ci * regmap_irq_chip_get_base() - Retrieve interrupt base for a regmap IRQ chip 109662306a36Sopenharmony_ci * 109762306a36Sopenharmony_ci * @data: regmap irq controller to operate on. 109862306a36Sopenharmony_ci * 109962306a36Sopenharmony_ci * Useful for drivers to request their own IRQs. 110062306a36Sopenharmony_ci */ 110162306a36Sopenharmony_ciint regmap_irq_chip_get_base(struct regmap_irq_chip_data *data) 110262306a36Sopenharmony_ci{ 110362306a36Sopenharmony_ci WARN_ON(!data->irq_base); 110462306a36Sopenharmony_ci return data->irq_base; 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci/** 110962306a36Sopenharmony_ci * regmap_irq_get_virq() - Map an interrupt on a chip to a virtual IRQ 111062306a36Sopenharmony_ci * 111162306a36Sopenharmony_ci * @data: regmap irq controller to operate on. 111262306a36Sopenharmony_ci * @irq: index of the interrupt requested in the chip IRQs. 111362306a36Sopenharmony_ci * 111462306a36Sopenharmony_ci * Useful for drivers to request their own IRQs. 111562306a36Sopenharmony_ci */ 111662306a36Sopenharmony_ciint regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci /* Handle holes in the IRQ list */ 111962306a36Sopenharmony_ci if (!data->chip->irqs[irq].mask) 112062306a36Sopenharmony_ci return -EINVAL; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci return irq_create_mapping(data->domain, irq); 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_irq_get_virq); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci/** 112762306a36Sopenharmony_ci * regmap_irq_get_domain() - Retrieve the irq_domain for the chip 112862306a36Sopenharmony_ci * 112962306a36Sopenharmony_ci * @data: regmap_irq controller to operate on. 113062306a36Sopenharmony_ci * 113162306a36Sopenharmony_ci * Useful for drivers to request their own IRQs and for integration 113262306a36Sopenharmony_ci * with subsystems. For ease of integration NULL is accepted as a 113362306a36Sopenharmony_ci * domain, allowing devices to just call this even if no domain is 113462306a36Sopenharmony_ci * allocated. 113562306a36Sopenharmony_ci */ 113662306a36Sopenharmony_cistruct irq_domain *regmap_irq_get_domain(struct regmap_irq_chip_data *data) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci if (data) 113962306a36Sopenharmony_ci return data->domain; 114062306a36Sopenharmony_ci else 114162306a36Sopenharmony_ci return NULL; 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_irq_get_domain); 1144