18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// regmap based irq_chip 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright 2011 Wolfson Microelectronics plc 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/export.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/irq.h> 138c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 148c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 158c2ecf20Sopenharmony_ci#include <linux/regmap.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "internal.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct regmap_irq_chip_data { 218c2ecf20Sopenharmony_ci struct mutex lock; 228c2ecf20Sopenharmony_ci struct irq_chip irq_chip; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci struct regmap *map; 258c2ecf20Sopenharmony_ci const struct regmap_irq_chip *chip; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci int irq_base; 288c2ecf20Sopenharmony_ci struct irq_domain *domain; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci int irq; 318c2ecf20Sopenharmony_ci int wake_count; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci void *status_reg_buf; 348c2ecf20Sopenharmony_ci unsigned int *main_status_buf; 358c2ecf20Sopenharmony_ci unsigned int *status_buf; 368c2ecf20Sopenharmony_ci unsigned int *mask_buf; 378c2ecf20Sopenharmony_ci unsigned int *mask_buf_def; 388c2ecf20Sopenharmony_ci unsigned int *wake_buf; 398c2ecf20Sopenharmony_ci unsigned int *type_buf; 408c2ecf20Sopenharmony_ci unsigned int *type_buf_def; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci unsigned int irq_reg_stride; 438c2ecf20Sopenharmony_ci unsigned int type_reg_stride; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci bool clear_status:1; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic inline const 498c2ecf20Sopenharmony_cistruct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, 508c2ecf20Sopenharmony_ci int irq) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return &data->chip->irqs[irq]; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void regmap_irq_lock(struct irq_data *data) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci mutex_lock(&d->lock); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int regmap_irq_update_bits(struct regmap_irq_chip_data *d, 638c2ecf20Sopenharmony_ci unsigned int reg, unsigned int mask, 648c2ecf20Sopenharmony_ci unsigned int val) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci if (d->chip->mask_writeonly) 678c2ecf20Sopenharmony_ci return regmap_write_bits(d->map, reg, mask, val); 688c2ecf20Sopenharmony_ci else 698c2ecf20Sopenharmony_ci return regmap_update_bits(d->map, reg, mask, val); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void regmap_irq_sync_unlock(struct irq_data *data) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 758c2ecf20Sopenharmony_ci struct regmap *map = d->map; 768c2ecf20Sopenharmony_ci int i, ret; 778c2ecf20Sopenharmony_ci u32 reg; 788c2ecf20Sopenharmony_ci u32 unmask_offset; 798c2ecf20Sopenharmony_ci u32 val; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (d->chip->runtime_pm) { 828c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(map->dev); 838c2ecf20Sopenharmony_ci if (ret < 0) 848c2ecf20Sopenharmony_ci dev_err(map->dev, "IRQ sync failed to resume: %d\n", 858c2ecf20Sopenharmony_ci ret); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (d->clear_status) { 898c2ecf20Sopenharmony_ci for (i = 0; i < d->chip->num_regs; i++) { 908c2ecf20Sopenharmony_ci reg = d->chip->status_base + 918c2ecf20Sopenharmony_ci (i * map->reg_stride * d->irq_reg_stride); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = regmap_read(map, reg, &val); 948c2ecf20Sopenharmony_ci if (ret) 958c2ecf20Sopenharmony_ci dev_err(d->map->dev, 968c2ecf20Sopenharmony_ci "Failed to clear the interrupt status bits\n"); 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci d->clear_status = false; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* 1038c2ecf20Sopenharmony_ci * If there's been a change in the mask write it back to the 1048c2ecf20Sopenharmony_ci * hardware. We rely on the use of the regmap core cache to 1058c2ecf20Sopenharmony_ci * suppress pointless writes. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci for (i = 0; i < d->chip->num_regs; i++) { 1088c2ecf20Sopenharmony_ci if (!d->chip->mask_base) 1098c2ecf20Sopenharmony_ci continue; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci reg = d->chip->mask_base + 1128c2ecf20Sopenharmony_ci (i * map->reg_stride * d->irq_reg_stride); 1138c2ecf20Sopenharmony_ci if (d->chip->mask_invert) { 1148c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, reg, 1158c2ecf20Sopenharmony_ci d->mask_buf_def[i], ~d->mask_buf[i]); 1168c2ecf20Sopenharmony_ci } else if (d->chip->unmask_base) { 1178c2ecf20Sopenharmony_ci /* set mask with mask_base register */ 1188c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, reg, 1198c2ecf20Sopenharmony_ci d->mask_buf_def[i], ~d->mask_buf[i]); 1208c2ecf20Sopenharmony_ci if (ret < 0) 1218c2ecf20Sopenharmony_ci dev_err(d->map->dev, 1228c2ecf20Sopenharmony_ci "Failed to sync unmasks in %x\n", 1238c2ecf20Sopenharmony_ci reg); 1248c2ecf20Sopenharmony_ci unmask_offset = d->chip->unmask_base - 1258c2ecf20Sopenharmony_ci d->chip->mask_base; 1268c2ecf20Sopenharmony_ci /* clear mask with unmask_base register */ 1278c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, 1288c2ecf20Sopenharmony_ci reg + unmask_offset, 1298c2ecf20Sopenharmony_ci d->mask_buf_def[i], 1308c2ecf20Sopenharmony_ci d->mask_buf[i]); 1318c2ecf20Sopenharmony_ci } else { 1328c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, reg, 1338c2ecf20Sopenharmony_ci d->mask_buf_def[i], d->mask_buf[i]); 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci if (ret != 0) 1368c2ecf20Sopenharmony_ci dev_err(d->map->dev, "Failed to sync masks in %x\n", 1378c2ecf20Sopenharmony_ci reg); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci reg = d->chip->wake_base + 1408c2ecf20Sopenharmony_ci (i * map->reg_stride * d->irq_reg_stride); 1418c2ecf20Sopenharmony_ci if (d->wake_buf) { 1428c2ecf20Sopenharmony_ci if (d->chip->wake_invert) 1438c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, reg, 1448c2ecf20Sopenharmony_ci d->mask_buf_def[i], 1458c2ecf20Sopenharmony_ci ~d->wake_buf[i]); 1468c2ecf20Sopenharmony_ci else 1478c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, reg, 1488c2ecf20Sopenharmony_ci d->mask_buf_def[i], 1498c2ecf20Sopenharmony_ci d->wake_buf[i]); 1508c2ecf20Sopenharmony_ci if (ret != 0) 1518c2ecf20Sopenharmony_ci dev_err(d->map->dev, 1528c2ecf20Sopenharmony_ci "Failed to sync wakes in %x: %d\n", 1538c2ecf20Sopenharmony_ci reg, ret); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (!d->chip->init_ack_masked) 1578c2ecf20Sopenharmony_ci continue; 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * Ack all the masked interrupts unconditionally, 1608c2ecf20Sopenharmony_ci * OR if there is masked interrupt which hasn't been Acked, 1618c2ecf20Sopenharmony_ci * it'll be ignored in irq handler, then may introduce irq storm 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) { 1648c2ecf20Sopenharmony_ci reg = d->chip->ack_base + 1658c2ecf20Sopenharmony_ci (i * map->reg_stride * d->irq_reg_stride); 1668c2ecf20Sopenharmony_ci /* some chips ack by write 0 */ 1678c2ecf20Sopenharmony_ci if (d->chip->ack_invert) 1688c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, ~d->mask_buf[i]); 1698c2ecf20Sopenharmony_ci else 1708c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, d->mask_buf[i]); 1718c2ecf20Sopenharmony_ci if (d->chip->clear_ack) { 1728c2ecf20Sopenharmony_ci if (d->chip->ack_invert && !ret) 1738c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, UINT_MAX); 1748c2ecf20Sopenharmony_ci else if (!ret) 1758c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, 0); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci if (ret != 0) 1788c2ecf20Sopenharmony_ci dev_err(d->map->dev, "Failed to ack 0x%x: %d\n", 1798c2ecf20Sopenharmony_ci reg, ret); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* Don't update the type bits if we're using mask bits for irq type. */ 1848c2ecf20Sopenharmony_ci if (!d->chip->type_in_mask) { 1858c2ecf20Sopenharmony_ci for (i = 0; i < d->chip->num_type_reg; i++) { 1868c2ecf20Sopenharmony_ci if (!d->type_buf_def[i]) 1878c2ecf20Sopenharmony_ci continue; 1888c2ecf20Sopenharmony_ci reg = d->chip->type_base + 1898c2ecf20Sopenharmony_ci (i * map->reg_stride * d->type_reg_stride); 1908c2ecf20Sopenharmony_ci if (d->chip->type_invert) 1918c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, reg, 1928c2ecf20Sopenharmony_ci d->type_buf_def[i], ~d->type_buf[i]); 1938c2ecf20Sopenharmony_ci else 1948c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, reg, 1958c2ecf20Sopenharmony_ci d->type_buf_def[i], d->type_buf[i]); 1968c2ecf20Sopenharmony_ci if (ret != 0) 1978c2ecf20Sopenharmony_ci dev_err(d->map->dev, "Failed to sync type in %x\n", 1988c2ecf20Sopenharmony_ci reg); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (d->chip->runtime_pm) 2038c2ecf20Sopenharmony_ci pm_runtime_put(map->dev); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* If we've changed our wakeup count propagate it to the parent */ 2068c2ecf20Sopenharmony_ci if (d->wake_count < 0) 2078c2ecf20Sopenharmony_ci for (i = d->wake_count; i < 0; i++) 2088c2ecf20Sopenharmony_ci irq_set_irq_wake(d->irq, 0); 2098c2ecf20Sopenharmony_ci else if (d->wake_count > 0) 2108c2ecf20Sopenharmony_ci for (i = 0; i < d->wake_count; i++) 2118c2ecf20Sopenharmony_ci irq_set_irq_wake(d->irq, 1); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci d->wake_count = 0; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci mutex_unlock(&d->lock); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void regmap_irq_enable(struct irq_data *data) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 2218c2ecf20Sopenharmony_ci struct regmap *map = d->map; 2228c2ecf20Sopenharmony_ci const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); 2238c2ecf20Sopenharmony_ci unsigned int reg = irq_data->reg_offset / map->reg_stride; 2248c2ecf20Sopenharmony_ci unsigned int mask, type; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci type = irq_data->type.type_falling_val | irq_data->type.type_rising_val; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* 2298c2ecf20Sopenharmony_ci * The type_in_mask flag means that the underlying hardware uses 2308c2ecf20Sopenharmony_ci * separate mask bits for rising and falling edge interrupts, but 2318c2ecf20Sopenharmony_ci * we want to make them into a single virtual interrupt with 2328c2ecf20Sopenharmony_ci * configurable edge. 2338c2ecf20Sopenharmony_ci * 2348c2ecf20Sopenharmony_ci * If the interrupt we're enabling defines the falling or rising 2358c2ecf20Sopenharmony_ci * masks then instead of using the regular mask bits for this 2368c2ecf20Sopenharmony_ci * interrupt, use the value previously written to the type buffer 2378c2ecf20Sopenharmony_ci * at the corresponding offset in regmap_irq_set_type(). 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci if (d->chip->type_in_mask && type) 2408c2ecf20Sopenharmony_ci mask = d->type_buf[reg] & irq_data->mask; 2418c2ecf20Sopenharmony_ci else 2428c2ecf20Sopenharmony_ci mask = irq_data->mask; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (d->chip->clear_on_unmask) 2458c2ecf20Sopenharmony_ci d->clear_status = true; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci d->mask_buf[reg] &= ~mask; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void regmap_irq_disable(struct irq_data *data) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 2538c2ecf20Sopenharmony_ci struct regmap *map = d->map; 2548c2ecf20Sopenharmony_ci const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int regmap_irq_set_type(struct irq_data *data, unsigned int type) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 2628c2ecf20Sopenharmony_ci struct regmap *map = d->map; 2638c2ecf20Sopenharmony_ci const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); 2648c2ecf20Sopenharmony_ci int reg; 2658c2ecf20Sopenharmony_ci const struct regmap_irq_type *t = &irq_data->type; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if ((t->types_supported & type) != type) 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci reg = t->type_reg_offset / map->reg_stride; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (t->type_reg_mask) 2738c2ecf20Sopenharmony_ci d->type_buf[reg] &= ~t->type_reg_mask; 2748c2ecf20Sopenharmony_ci else 2758c2ecf20Sopenharmony_ci d->type_buf[reg] &= ~(t->type_falling_val | 2768c2ecf20Sopenharmony_ci t->type_rising_val | 2778c2ecf20Sopenharmony_ci t->type_level_low_val | 2788c2ecf20Sopenharmony_ci t->type_level_high_val); 2798c2ecf20Sopenharmony_ci switch (type) { 2808c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 2818c2ecf20Sopenharmony_ci d->type_buf[reg] |= t->type_falling_val; 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 2858c2ecf20Sopenharmony_ci d->type_buf[reg] |= t->type_rising_val; 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 2898c2ecf20Sopenharmony_ci d->type_buf[reg] |= (t->type_falling_val | 2908c2ecf20Sopenharmony_ci t->type_rising_val); 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 2948c2ecf20Sopenharmony_ci d->type_buf[reg] |= t->type_level_high_val; 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 2988c2ecf20Sopenharmony_ci d->type_buf[reg] |= t->type_level_low_val; 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci default: 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int regmap_irq_set_wake(struct irq_data *data, unsigned int on) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 3098c2ecf20Sopenharmony_ci struct regmap *map = d->map; 3108c2ecf20Sopenharmony_ci const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (on) { 3138c2ecf20Sopenharmony_ci if (d->wake_buf) 3148c2ecf20Sopenharmony_ci d->wake_buf[irq_data->reg_offset / map->reg_stride] 3158c2ecf20Sopenharmony_ci &= ~irq_data->mask; 3168c2ecf20Sopenharmony_ci d->wake_count++; 3178c2ecf20Sopenharmony_ci } else { 3188c2ecf20Sopenharmony_ci if (d->wake_buf) 3198c2ecf20Sopenharmony_ci d->wake_buf[irq_data->reg_offset / map->reg_stride] 3208c2ecf20Sopenharmony_ci |= irq_data->mask; 3218c2ecf20Sopenharmony_ci d->wake_count--; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic const struct irq_chip regmap_irq_chip = { 3288c2ecf20Sopenharmony_ci .irq_bus_lock = regmap_irq_lock, 3298c2ecf20Sopenharmony_ci .irq_bus_sync_unlock = regmap_irq_sync_unlock, 3308c2ecf20Sopenharmony_ci .irq_disable = regmap_irq_disable, 3318c2ecf20Sopenharmony_ci .irq_enable = regmap_irq_enable, 3328c2ecf20Sopenharmony_ci .irq_set_type = regmap_irq_set_type, 3338c2ecf20Sopenharmony_ci .irq_set_wake = regmap_irq_set_wake, 3348c2ecf20Sopenharmony_ci}; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic inline int read_sub_irq_data(struct regmap_irq_chip_data *data, 3378c2ecf20Sopenharmony_ci unsigned int b) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci const struct regmap_irq_chip *chip = data->chip; 3408c2ecf20Sopenharmony_ci struct regmap *map = data->map; 3418c2ecf20Sopenharmony_ci struct regmap_irq_sub_irq_map *subreg; 3428c2ecf20Sopenharmony_ci int i, ret = 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (!chip->sub_reg_offsets) { 3458c2ecf20Sopenharmony_ci /* Assume linear mapping */ 3468c2ecf20Sopenharmony_ci ret = regmap_read(map, chip->status_base + 3478c2ecf20Sopenharmony_ci (b * map->reg_stride * data->irq_reg_stride), 3488c2ecf20Sopenharmony_ci &data->status_buf[b]); 3498c2ecf20Sopenharmony_ci } else { 3508c2ecf20Sopenharmony_ci subreg = &chip->sub_reg_offsets[b]; 3518c2ecf20Sopenharmony_ci for (i = 0; i < subreg->num_regs; i++) { 3528c2ecf20Sopenharmony_ci unsigned int offset = subreg->offset[i]; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci ret = regmap_read(map, chip->status_base + offset, 3558c2ecf20Sopenharmony_ci &data->status_buf[offset]); 3568c2ecf20Sopenharmony_ci if (ret) 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci return ret; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic irqreturn_t regmap_irq_thread(int irq, void *d) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *data = d; 3668c2ecf20Sopenharmony_ci const struct regmap_irq_chip *chip = data->chip; 3678c2ecf20Sopenharmony_ci struct regmap *map = data->map; 3688c2ecf20Sopenharmony_ci int ret, i; 3698c2ecf20Sopenharmony_ci bool handled = false; 3708c2ecf20Sopenharmony_ci u32 reg; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (chip->handle_pre_irq) 3738c2ecf20Sopenharmony_ci chip->handle_pre_irq(chip->irq_drv_data); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (chip->runtime_pm) { 3768c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(map->dev); 3778c2ecf20Sopenharmony_ci if (ret < 0) { 3788c2ecf20Sopenharmony_ci dev_err(map->dev, "IRQ thread failed to resume: %d\n", 3798c2ecf20Sopenharmony_ci ret); 3808c2ecf20Sopenharmony_ci goto exit; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* 3858c2ecf20Sopenharmony_ci * Read only registers with active IRQs if the chip has 'main status 3868c2ecf20Sopenharmony_ci * register'. Else read in the statuses, using a single bulk read if 3878c2ecf20Sopenharmony_ci * possible in order to reduce the I/O overheads. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (chip->num_main_regs) { 3918c2ecf20Sopenharmony_ci unsigned int max_main_bits; 3928c2ecf20Sopenharmony_ci unsigned long size; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci size = chip->num_regs * sizeof(unsigned int); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci max_main_bits = (chip->num_main_status_bits) ? 3978c2ecf20Sopenharmony_ci chip->num_main_status_bits : chip->num_regs; 3988c2ecf20Sopenharmony_ci /* Clear the status buf as we don't read all status regs */ 3998c2ecf20Sopenharmony_ci memset(data->status_buf, 0, size); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* We could support bulk read for main status registers 4028c2ecf20Sopenharmony_ci * but I don't expect to see devices with really many main 4038c2ecf20Sopenharmony_ci * status registers so let's only support single reads for the 4048c2ecf20Sopenharmony_ci * sake of simplicity. and add bulk reads only if needed 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci for (i = 0; i < chip->num_main_regs; i++) { 4078c2ecf20Sopenharmony_ci ret = regmap_read(map, chip->main_status + 4088c2ecf20Sopenharmony_ci (i * map->reg_stride 4098c2ecf20Sopenharmony_ci * data->irq_reg_stride), 4108c2ecf20Sopenharmony_ci &data->main_status_buf[i]); 4118c2ecf20Sopenharmony_ci if (ret) { 4128c2ecf20Sopenharmony_ci dev_err(map->dev, 4138c2ecf20Sopenharmony_ci "Failed to read IRQ status %d\n", 4148c2ecf20Sopenharmony_ci ret); 4158c2ecf20Sopenharmony_ci goto exit; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* Read sub registers with active IRQs */ 4208c2ecf20Sopenharmony_ci for (i = 0; i < chip->num_main_regs; i++) { 4218c2ecf20Sopenharmony_ci unsigned int b; 4228c2ecf20Sopenharmony_ci const unsigned long mreg = data->main_status_buf[i]; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci for_each_set_bit(b, &mreg, map->format.val_bytes * 8) { 4258c2ecf20Sopenharmony_ci if (i * map->format.val_bytes * 8 + b > 4268c2ecf20Sopenharmony_ci max_main_bits) 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci ret = read_sub_irq_data(data, b); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (ret != 0) { 4318c2ecf20Sopenharmony_ci dev_err(map->dev, 4328c2ecf20Sopenharmony_ci "Failed to read IRQ status %d\n", 4338c2ecf20Sopenharmony_ci ret); 4348c2ecf20Sopenharmony_ci goto exit; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci } else if (!map->use_single_read && map->reg_stride == 1 && 4408c2ecf20Sopenharmony_ci data->irq_reg_stride == 1) { 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci u8 *buf8 = data->status_reg_buf; 4438c2ecf20Sopenharmony_ci u16 *buf16 = data->status_reg_buf; 4448c2ecf20Sopenharmony_ci u32 *buf32 = data->status_reg_buf; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci BUG_ON(!data->status_reg_buf); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ret = regmap_bulk_read(map, chip->status_base, 4498c2ecf20Sopenharmony_ci data->status_reg_buf, 4508c2ecf20Sopenharmony_ci chip->num_regs); 4518c2ecf20Sopenharmony_ci if (ret != 0) { 4528c2ecf20Sopenharmony_ci dev_err(map->dev, "Failed to read IRQ status: %d\n", 4538c2ecf20Sopenharmony_ci ret); 4548c2ecf20Sopenharmony_ci goto exit; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for (i = 0; i < data->chip->num_regs; i++) { 4588c2ecf20Sopenharmony_ci switch (map->format.val_bytes) { 4598c2ecf20Sopenharmony_ci case 1: 4608c2ecf20Sopenharmony_ci data->status_buf[i] = buf8[i]; 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci case 2: 4638c2ecf20Sopenharmony_ci data->status_buf[i] = buf16[i]; 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci case 4: 4668c2ecf20Sopenharmony_ci data->status_buf[i] = buf32[i]; 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci default: 4698c2ecf20Sopenharmony_ci BUG(); 4708c2ecf20Sopenharmony_ci goto exit; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci } else { 4758c2ecf20Sopenharmony_ci for (i = 0; i < data->chip->num_regs; i++) { 4768c2ecf20Sopenharmony_ci ret = regmap_read(map, chip->status_base + 4778c2ecf20Sopenharmony_ci (i * map->reg_stride 4788c2ecf20Sopenharmony_ci * data->irq_reg_stride), 4798c2ecf20Sopenharmony_ci &data->status_buf[i]); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (ret != 0) { 4828c2ecf20Sopenharmony_ci dev_err(map->dev, 4838c2ecf20Sopenharmony_ci "Failed to read IRQ status: %d\n", 4848c2ecf20Sopenharmony_ci ret); 4858c2ecf20Sopenharmony_ci goto exit; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* 4918c2ecf20Sopenharmony_ci * Ignore masked IRQs and ack if we need to; we ack early so 4928c2ecf20Sopenharmony_ci * there is no race between handling and acknowleding the 4938c2ecf20Sopenharmony_ci * interrupt. We assume that typically few of the interrupts 4948c2ecf20Sopenharmony_ci * will fire simultaneously so don't worry about overhead from 4958c2ecf20Sopenharmony_ci * doing a write per register. 4968c2ecf20Sopenharmony_ci */ 4978c2ecf20Sopenharmony_ci for (i = 0; i < data->chip->num_regs; i++) { 4988c2ecf20Sopenharmony_ci data->status_buf[i] &= ~data->mask_buf[i]; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) { 5018c2ecf20Sopenharmony_ci reg = chip->ack_base + 5028c2ecf20Sopenharmony_ci (i * map->reg_stride * data->irq_reg_stride); 5038c2ecf20Sopenharmony_ci if (chip->ack_invert) 5048c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, 5058c2ecf20Sopenharmony_ci ~data->status_buf[i]); 5068c2ecf20Sopenharmony_ci else 5078c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, 5088c2ecf20Sopenharmony_ci data->status_buf[i]); 5098c2ecf20Sopenharmony_ci if (chip->clear_ack) { 5108c2ecf20Sopenharmony_ci if (chip->ack_invert && !ret) 5118c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, UINT_MAX); 5128c2ecf20Sopenharmony_ci else if (!ret) 5138c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, 0); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci if (ret != 0) 5168c2ecf20Sopenharmony_ci dev_err(map->dev, "Failed to ack 0x%x: %d\n", 5178c2ecf20Sopenharmony_ci reg, ret); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci for (i = 0; i < chip->num_irqs; i++) { 5228c2ecf20Sopenharmony_ci if (data->status_buf[chip->irqs[i].reg_offset / 5238c2ecf20Sopenharmony_ci map->reg_stride] & chip->irqs[i].mask) { 5248c2ecf20Sopenharmony_ci handle_nested_irq(irq_find_mapping(data->domain, i)); 5258c2ecf20Sopenharmony_ci handled = true; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ciexit: 5308c2ecf20Sopenharmony_ci if (chip->runtime_pm) 5318c2ecf20Sopenharmony_ci pm_runtime_put(map->dev); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (chip->handle_post_irq) 5348c2ecf20Sopenharmony_ci chip->handle_post_irq(chip->irq_drv_data); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (handled) 5378c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5388c2ecf20Sopenharmony_ci else 5398c2ecf20Sopenharmony_ci return IRQ_NONE; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic int regmap_irq_map(struct irq_domain *h, unsigned int virq, 5438c2ecf20Sopenharmony_ci irq_hw_number_t hw) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *data = h->host_data; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci irq_set_chip_data(virq, data); 5488c2ecf20Sopenharmony_ci irq_set_chip(virq, &data->irq_chip); 5498c2ecf20Sopenharmony_ci irq_set_nested_thread(virq, 1); 5508c2ecf20Sopenharmony_ci irq_set_parent(virq, data->irq); 5518c2ecf20Sopenharmony_ci irq_set_noprobe(virq); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci return 0; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic const struct irq_domain_ops regmap_domain_ops = { 5578c2ecf20Sopenharmony_ci .map = regmap_irq_map, 5588c2ecf20Sopenharmony_ci .xlate = irq_domain_xlate_onetwocell, 5598c2ecf20Sopenharmony_ci}; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/** 5628c2ecf20Sopenharmony_ci * regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling 5638c2ecf20Sopenharmony_ci * 5648c2ecf20Sopenharmony_ci * @fwnode: The firmware node where the IRQ domain should be added to. 5658c2ecf20Sopenharmony_ci * @map: The regmap for the device. 5668c2ecf20Sopenharmony_ci * @irq: The IRQ the device uses to signal interrupts. 5678c2ecf20Sopenharmony_ci * @irq_flags: The IRQF_ flags to use for the primary interrupt. 5688c2ecf20Sopenharmony_ci * @irq_base: Allocate at specific IRQ number if irq_base > 0. 5698c2ecf20Sopenharmony_ci * @chip: Configuration for the interrupt controller. 5708c2ecf20Sopenharmony_ci * @data: Runtime data structure for the controller, allocated on success. 5718c2ecf20Sopenharmony_ci * 5728c2ecf20Sopenharmony_ci * Returns 0 on success or an errno on failure. 5738c2ecf20Sopenharmony_ci * 5748c2ecf20Sopenharmony_ci * In order for this to be efficient the chip really should use a 5758c2ecf20Sopenharmony_ci * register cache. The chip driver is responsible for restoring the 5768c2ecf20Sopenharmony_ci * register values used by the IRQ controller over suspend and resume. 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_ciint regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, 5798c2ecf20Sopenharmony_ci struct regmap *map, int irq, 5808c2ecf20Sopenharmony_ci int irq_flags, int irq_base, 5818c2ecf20Sopenharmony_ci const struct regmap_irq_chip *chip, 5828c2ecf20Sopenharmony_ci struct regmap_irq_chip_data **data) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *d; 5858c2ecf20Sopenharmony_ci int i; 5868c2ecf20Sopenharmony_ci int ret = -ENOMEM; 5878c2ecf20Sopenharmony_ci int num_type_reg; 5888c2ecf20Sopenharmony_ci u32 reg; 5898c2ecf20Sopenharmony_ci u32 unmask_offset; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (chip->num_regs <= 0) 5928c2ecf20Sopenharmony_ci return -EINVAL; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (chip->clear_on_unmask && (chip->ack_base || chip->use_ack)) 5958c2ecf20Sopenharmony_ci return -EINVAL; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci for (i = 0; i < chip->num_irqs; i++) { 5988c2ecf20Sopenharmony_ci if (chip->irqs[i].reg_offset % map->reg_stride) 5998c2ecf20Sopenharmony_ci return -EINVAL; 6008c2ecf20Sopenharmony_ci if (chip->irqs[i].reg_offset / map->reg_stride >= 6018c2ecf20Sopenharmony_ci chip->num_regs) 6028c2ecf20Sopenharmony_ci return -EINVAL; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (irq_base) { 6068c2ecf20Sopenharmony_ci irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); 6078c2ecf20Sopenharmony_ci if (irq_base < 0) { 6088c2ecf20Sopenharmony_ci dev_warn(map->dev, "Failed to allocate IRQs: %d\n", 6098c2ecf20Sopenharmony_ci irq_base); 6108c2ecf20Sopenharmony_ci return irq_base; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci d = kzalloc(sizeof(*d), GFP_KERNEL); 6158c2ecf20Sopenharmony_ci if (!d) 6168c2ecf20Sopenharmony_ci return -ENOMEM; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (chip->num_main_regs) { 6198c2ecf20Sopenharmony_ci d->main_status_buf = kcalloc(chip->num_main_regs, 6208c2ecf20Sopenharmony_ci sizeof(unsigned int), 6218c2ecf20Sopenharmony_ci GFP_KERNEL); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (!d->main_status_buf) 6248c2ecf20Sopenharmony_ci goto err_alloc; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int), 6288c2ecf20Sopenharmony_ci GFP_KERNEL); 6298c2ecf20Sopenharmony_ci if (!d->status_buf) 6308c2ecf20Sopenharmony_ci goto err_alloc; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci d->mask_buf = kcalloc(chip->num_regs, sizeof(unsigned int), 6338c2ecf20Sopenharmony_ci GFP_KERNEL); 6348c2ecf20Sopenharmony_ci if (!d->mask_buf) 6358c2ecf20Sopenharmony_ci goto err_alloc; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci d->mask_buf_def = kcalloc(chip->num_regs, sizeof(unsigned int), 6388c2ecf20Sopenharmony_ci GFP_KERNEL); 6398c2ecf20Sopenharmony_ci if (!d->mask_buf_def) 6408c2ecf20Sopenharmony_ci goto err_alloc; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (chip->wake_base) { 6438c2ecf20Sopenharmony_ci d->wake_buf = kcalloc(chip->num_regs, sizeof(unsigned int), 6448c2ecf20Sopenharmony_ci GFP_KERNEL); 6458c2ecf20Sopenharmony_ci if (!d->wake_buf) 6468c2ecf20Sopenharmony_ci goto err_alloc; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci num_type_reg = chip->type_in_mask ? chip->num_regs : chip->num_type_reg; 6508c2ecf20Sopenharmony_ci if (num_type_reg) { 6518c2ecf20Sopenharmony_ci d->type_buf_def = kcalloc(num_type_reg, 6528c2ecf20Sopenharmony_ci sizeof(unsigned int), GFP_KERNEL); 6538c2ecf20Sopenharmony_ci if (!d->type_buf_def) 6548c2ecf20Sopenharmony_ci goto err_alloc; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci d->type_buf = kcalloc(num_type_reg, sizeof(unsigned int), 6578c2ecf20Sopenharmony_ci GFP_KERNEL); 6588c2ecf20Sopenharmony_ci if (!d->type_buf) 6598c2ecf20Sopenharmony_ci goto err_alloc; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci d->irq_chip = regmap_irq_chip; 6638c2ecf20Sopenharmony_ci d->irq_chip.name = chip->name; 6648c2ecf20Sopenharmony_ci d->irq = irq; 6658c2ecf20Sopenharmony_ci d->map = map; 6668c2ecf20Sopenharmony_ci d->chip = chip; 6678c2ecf20Sopenharmony_ci d->irq_base = irq_base; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (chip->irq_reg_stride) 6708c2ecf20Sopenharmony_ci d->irq_reg_stride = chip->irq_reg_stride; 6718c2ecf20Sopenharmony_ci else 6728c2ecf20Sopenharmony_ci d->irq_reg_stride = 1; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (chip->type_reg_stride) 6758c2ecf20Sopenharmony_ci d->type_reg_stride = chip->type_reg_stride; 6768c2ecf20Sopenharmony_ci else 6778c2ecf20Sopenharmony_ci d->type_reg_stride = 1; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (!map->use_single_read && map->reg_stride == 1 && 6808c2ecf20Sopenharmony_ci d->irq_reg_stride == 1) { 6818c2ecf20Sopenharmony_ci d->status_reg_buf = kmalloc_array(chip->num_regs, 6828c2ecf20Sopenharmony_ci map->format.val_bytes, 6838c2ecf20Sopenharmony_ci GFP_KERNEL); 6848c2ecf20Sopenharmony_ci if (!d->status_reg_buf) 6858c2ecf20Sopenharmony_ci goto err_alloc; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci mutex_init(&d->lock); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci for (i = 0; i < chip->num_irqs; i++) 6918c2ecf20Sopenharmony_ci d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] 6928c2ecf20Sopenharmony_ci |= chip->irqs[i].mask; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* Mask all the interrupts by default */ 6958c2ecf20Sopenharmony_ci for (i = 0; i < chip->num_regs; i++) { 6968c2ecf20Sopenharmony_ci d->mask_buf[i] = d->mask_buf_def[i]; 6978c2ecf20Sopenharmony_ci if (!chip->mask_base) 6988c2ecf20Sopenharmony_ci continue; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci reg = chip->mask_base + 7018c2ecf20Sopenharmony_ci (i * map->reg_stride * d->irq_reg_stride); 7028c2ecf20Sopenharmony_ci if (chip->mask_invert) 7038c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, reg, 7048c2ecf20Sopenharmony_ci d->mask_buf[i], ~d->mask_buf[i]); 7058c2ecf20Sopenharmony_ci else if (d->chip->unmask_base) { 7068c2ecf20Sopenharmony_ci unmask_offset = d->chip->unmask_base - 7078c2ecf20Sopenharmony_ci d->chip->mask_base; 7088c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, 7098c2ecf20Sopenharmony_ci reg + unmask_offset, 7108c2ecf20Sopenharmony_ci d->mask_buf[i], 7118c2ecf20Sopenharmony_ci d->mask_buf[i]); 7128c2ecf20Sopenharmony_ci } else 7138c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, reg, 7148c2ecf20Sopenharmony_ci d->mask_buf[i], d->mask_buf[i]); 7158c2ecf20Sopenharmony_ci if (ret != 0) { 7168c2ecf20Sopenharmony_ci dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", 7178c2ecf20Sopenharmony_ci reg, ret); 7188c2ecf20Sopenharmony_ci goto err_alloc; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (!chip->init_ack_masked) 7228c2ecf20Sopenharmony_ci continue; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* Ack masked but set interrupts */ 7258c2ecf20Sopenharmony_ci reg = chip->status_base + 7268c2ecf20Sopenharmony_ci (i * map->reg_stride * d->irq_reg_stride); 7278c2ecf20Sopenharmony_ci ret = regmap_read(map, reg, &d->status_buf[i]); 7288c2ecf20Sopenharmony_ci if (ret != 0) { 7298c2ecf20Sopenharmony_ci dev_err(map->dev, "Failed to read IRQ status: %d\n", 7308c2ecf20Sopenharmony_ci ret); 7318c2ecf20Sopenharmony_ci goto err_alloc; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { 7358c2ecf20Sopenharmony_ci reg = chip->ack_base + 7368c2ecf20Sopenharmony_ci (i * map->reg_stride * d->irq_reg_stride); 7378c2ecf20Sopenharmony_ci if (chip->ack_invert) 7388c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, 7398c2ecf20Sopenharmony_ci ~(d->status_buf[i] & d->mask_buf[i])); 7408c2ecf20Sopenharmony_ci else 7418c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, 7428c2ecf20Sopenharmony_ci d->status_buf[i] & d->mask_buf[i]); 7438c2ecf20Sopenharmony_ci if (chip->clear_ack) { 7448c2ecf20Sopenharmony_ci if (chip->ack_invert && !ret) 7458c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, UINT_MAX); 7468c2ecf20Sopenharmony_ci else if (!ret) 7478c2ecf20Sopenharmony_ci ret = regmap_write(map, reg, 0); 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci if (ret != 0) { 7508c2ecf20Sopenharmony_ci dev_err(map->dev, "Failed to ack 0x%x: %d\n", 7518c2ecf20Sopenharmony_ci reg, ret); 7528c2ecf20Sopenharmony_ci goto err_alloc; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci /* Wake is disabled by default */ 7588c2ecf20Sopenharmony_ci if (d->wake_buf) { 7598c2ecf20Sopenharmony_ci for (i = 0; i < chip->num_regs; i++) { 7608c2ecf20Sopenharmony_ci d->wake_buf[i] = d->mask_buf_def[i]; 7618c2ecf20Sopenharmony_ci reg = chip->wake_base + 7628c2ecf20Sopenharmony_ci (i * map->reg_stride * d->irq_reg_stride); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (chip->wake_invert) 7658c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, reg, 7668c2ecf20Sopenharmony_ci d->mask_buf_def[i], 7678c2ecf20Sopenharmony_ci 0); 7688c2ecf20Sopenharmony_ci else 7698c2ecf20Sopenharmony_ci ret = regmap_irq_update_bits(d, reg, 7708c2ecf20Sopenharmony_ci d->mask_buf_def[i], 7718c2ecf20Sopenharmony_ci d->wake_buf[i]); 7728c2ecf20Sopenharmony_ci if (ret != 0) { 7738c2ecf20Sopenharmony_ci dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", 7748c2ecf20Sopenharmony_ci reg, ret); 7758c2ecf20Sopenharmony_ci goto err_alloc; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (chip->num_type_reg && !chip->type_in_mask) { 7818c2ecf20Sopenharmony_ci for (i = 0; i < chip->num_type_reg; ++i) { 7828c2ecf20Sopenharmony_ci reg = chip->type_base + 7838c2ecf20Sopenharmony_ci (i * map->reg_stride * d->type_reg_stride); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci ret = regmap_read(map, reg, &d->type_buf_def[i]); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci if (d->chip->type_invert) 7888c2ecf20Sopenharmony_ci d->type_buf_def[i] = ~d->type_buf_def[i]; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci if (ret) { 7918c2ecf20Sopenharmony_ci dev_err(map->dev, "Failed to get type defaults at 0x%x: %d\n", 7928c2ecf20Sopenharmony_ci reg, ret); 7938c2ecf20Sopenharmony_ci goto err_alloc; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (irq_base) 7998c2ecf20Sopenharmony_ci d->domain = irq_domain_add_legacy(to_of_node(fwnode), 8008c2ecf20Sopenharmony_ci chip->num_irqs, irq_base, 8018c2ecf20Sopenharmony_ci 0, ®map_domain_ops, d); 8028c2ecf20Sopenharmony_ci else 8038c2ecf20Sopenharmony_ci d->domain = irq_domain_add_linear(to_of_node(fwnode), 8048c2ecf20Sopenharmony_ci chip->num_irqs, 8058c2ecf20Sopenharmony_ci ®map_domain_ops, d); 8068c2ecf20Sopenharmony_ci if (!d->domain) { 8078c2ecf20Sopenharmony_ci dev_err(map->dev, "Failed to create IRQ domain\n"); 8088c2ecf20Sopenharmony_ci ret = -ENOMEM; 8098c2ecf20Sopenharmony_ci goto err_alloc; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci ret = request_threaded_irq(irq, NULL, regmap_irq_thread, 8138c2ecf20Sopenharmony_ci irq_flags | IRQF_ONESHOT, 8148c2ecf20Sopenharmony_ci chip->name, d); 8158c2ecf20Sopenharmony_ci if (ret != 0) { 8168c2ecf20Sopenharmony_ci dev_err(map->dev, "Failed to request IRQ %d for %s: %d\n", 8178c2ecf20Sopenharmony_ci irq, chip->name, ret); 8188c2ecf20Sopenharmony_ci goto err_domain; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci *data = d; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return 0; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cierr_domain: 8268c2ecf20Sopenharmony_ci /* Should really dispose of the domain but... */ 8278c2ecf20Sopenharmony_cierr_alloc: 8288c2ecf20Sopenharmony_ci kfree(d->type_buf); 8298c2ecf20Sopenharmony_ci kfree(d->type_buf_def); 8308c2ecf20Sopenharmony_ci kfree(d->wake_buf); 8318c2ecf20Sopenharmony_ci kfree(d->mask_buf_def); 8328c2ecf20Sopenharmony_ci kfree(d->mask_buf); 8338c2ecf20Sopenharmony_ci kfree(d->status_buf); 8348c2ecf20Sopenharmony_ci kfree(d->status_reg_buf); 8358c2ecf20Sopenharmony_ci kfree(d); 8368c2ecf20Sopenharmony_ci return ret; 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_add_irq_chip_fwnode); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci/** 8418c2ecf20Sopenharmony_ci * regmap_add_irq_chip() - Use standard regmap IRQ controller handling 8428c2ecf20Sopenharmony_ci * 8438c2ecf20Sopenharmony_ci * @map: The regmap for the device. 8448c2ecf20Sopenharmony_ci * @irq: The IRQ the device uses to signal interrupts. 8458c2ecf20Sopenharmony_ci * @irq_flags: The IRQF_ flags to use for the primary interrupt. 8468c2ecf20Sopenharmony_ci * @irq_base: Allocate at specific IRQ number if irq_base > 0. 8478c2ecf20Sopenharmony_ci * @chip: Configuration for the interrupt controller. 8488c2ecf20Sopenharmony_ci * @data: Runtime data structure for the controller, allocated on success. 8498c2ecf20Sopenharmony_ci * 8508c2ecf20Sopenharmony_ci * Returns 0 on success or an errno on failure. 8518c2ecf20Sopenharmony_ci * 8528c2ecf20Sopenharmony_ci * This is the same as regmap_add_irq_chip_fwnode, except that the firmware 8538c2ecf20Sopenharmony_ci * node of the regmap is used. 8548c2ecf20Sopenharmony_ci */ 8558c2ecf20Sopenharmony_ciint regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, 8568c2ecf20Sopenharmony_ci int irq_base, const struct regmap_irq_chip *chip, 8578c2ecf20Sopenharmony_ci struct regmap_irq_chip_data **data) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci return regmap_add_irq_chip_fwnode(dev_fwnode(map->dev), map, irq, 8608c2ecf20Sopenharmony_ci irq_flags, irq_base, chip, data); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_add_irq_chip); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci/** 8658c2ecf20Sopenharmony_ci * regmap_del_irq_chip() - Stop interrupt handling for a regmap IRQ chip 8668c2ecf20Sopenharmony_ci * 8678c2ecf20Sopenharmony_ci * @irq: Primary IRQ for the device 8688c2ecf20Sopenharmony_ci * @d: ®map_irq_chip_data allocated by regmap_add_irq_chip() 8698c2ecf20Sopenharmony_ci * 8708c2ecf20Sopenharmony_ci * This function also disposes of all mapped IRQs on the chip. 8718c2ecf20Sopenharmony_ci */ 8728c2ecf20Sopenharmony_civoid regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci unsigned int virq; 8758c2ecf20Sopenharmony_ci int hwirq; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci if (!d) 8788c2ecf20Sopenharmony_ci return; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci free_irq(irq, d); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* Dispose all virtual irq from irq domain before removing it */ 8838c2ecf20Sopenharmony_ci for (hwirq = 0; hwirq < d->chip->num_irqs; hwirq++) { 8848c2ecf20Sopenharmony_ci /* Ignore hwirq if holes in the IRQ list */ 8858c2ecf20Sopenharmony_ci if (!d->chip->irqs[hwirq].mask) 8868c2ecf20Sopenharmony_ci continue; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci /* 8898c2ecf20Sopenharmony_ci * Find the virtual irq of hwirq on chip and if it is 8908c2ecf20Sopenharmony_ci * there then dispose it 8918c2ecf20Sopenharmony_ci */ 8928c2ecf20Sopenharmony_ci virq = irq_find_mapping(d->domain, hwirq); 8938c2ecf20Sopenharmony_ci if (virq) 8948c2ecf20Sopenharmony_ci irq_dispose_mapping(virq); 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci irq_domain_remove(d->domain); 8988c2ecf20Sopenharmony_ci kfree(d->type_buf); 8998c2ecf20Sopenharmony_ci kfree(d->type_buf_def); 9008c2ecf20Sopenharmony_ci kfree(d->wake_buf); 9018c2ecf20Sopenharmony_ci kfree(d->mask_buf_def); 9028c2ecf20Sopenharmony_ci kfree(d->mask_buf); 9038c2ecf20Sopenharmony_ci kfree(d->status_reg_buf); 9048c2ecf20Sopenharmony_ci kfree(d->status_buf); 9058c2ecf20Sopenharmony_ci kfree(d); 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_del_irq_chip); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic void devm_regmap_irq_chip_release(struct device *dev, void *res) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *d = *(struct regmap_irq_chip_data **)res; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci regmap_del_irq_chip(d->irq, d); 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_cistatic int devm_regmap_irq_chip_match(struct device *dev, void *res, void *data) 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct regmap_irq_chip_data **r = res; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (!r || !*r) { 9228c2ecf20Sopenharmony_ci WARN_ON(!r || !*r); 9238c2ecf20Sopenharmony_ci return 0; 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci return *r == data; 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci/** 9298c2ecf20Sopenharmony_ci * devm_regmap_add_irq_chip_fwnode() - Resource managed regmap_add_irq_chip_fwnode() 9308c2ecf20Sopenharmony_ci * 9318c2ecf20Sopenharmony_ci * @dev: The device pointer on which irq_chip belongs to. 9328c2ecf20Sopenharmony_ci * @fwnode: The firmware node where the IRQ domain should be added to. 9338c2ecf20Sopenharmony_ci * @map: The regmap for the device. 9348c2ecf20Sopenharmony_ci * @irq: The IRQ the device uses to signal interrupts 9358c2ecf20Sopenharmony_ci * @irq_flags: The IRQF_ flags to use for the primary interrupt. 9368c2ecf20Sopenharmony_ci * @irq_base: Allocate at specific IRQ number if irq_base > 0. 9378c2ecf20Sopenharmony_ci * @chip: Configuration for the interrupt controller. 9388c2ecf20Sopenharmony_ci * @data: Runtime data structure for the controller, allocated on success 9398c2ecf20Sopenharmony_ci * 9408c2ecf20Sopenharmony_ci * Returns 0 on success or an errno on failure. 9418c2ecf20Sopenharmony_ci * 9428c2ecf20Sopenharmony_ci * The ®map_irq_chip_data will be automatically released when the device is 9438c2ecf20Sopenharmony_ci * unbound. 9448c2ecf20Sopenharmony_ci */ 9458c2ecf20Sopenharmony_ciint devm_regmap_add_irq_chip_fwnode(struct device *dev, 9468c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode, 9478c2ecf20Sopenharmony_ci struct regmap *map, int irq, 9488c2ecf20Sopenharmony_ci int irq_flags, int irq_base, 9498c2ecf20Sopenharmony_ci const struct regmap_irq_chip *chip, 9508c2ecf20Sopenharmony_ci struct regmap_irq_chip_data **data) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci struct regmap_irq_chip_data **ptr, *d; 9538c2ecf20Sopenharmony_ci int ret; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_regmap_irq_chip_release, sizeof(*ptr), 9568c2ecf20Sopenharmony_ci GFP_KERNEL); 9578c2ecf20Sopenharmony_ci if (!ptr) 9588c2ecf20Sopenharmony_ci return -ENOMEM; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci ret = regmap_add_irq_chip_fwnode(fwnode, map, irq, irq_flags, irq_base, 9618c2ecf20Sopenharmony_ci chip, &d); 9628c2ecf20Sopenharmony_ci if (ret < 0) { 9638c2ecf20Sopenharmony_ci devres_free(ptr); 9648c2ecf20Sopenharmony_ci return ret; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci *ptr = d; 9688c2ecf20Sopenharmony_ci devres_add(dev, ptr); 9698c2ecf20Sopenharmony_ci *data = d; 9708c2ecf20Sopenharmony_ci return 0; 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip_fwnode); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci/** 9758c2ecf20Sopenharmony_ci * devm_regmap_add_irq_chip() - Resource manager regmap_add_irq_chip() 9768c2ecf20Sopenharmony_ci * 9778c2ecf20Sopenharmony_ci * @dev: The device pointer on which irq_chip belongs to. 9788c2ecf20Sopenharmony_ci * @map: The regmap for the device. 9798c2ecf20Sopenharmony_ci * @irq: The IRQ the device uses to signal interrupts 9808c2ecf20Sopenharmony_ci * @irq_flags: The IRQF_ flags to use for the primary interrupt. 9818c2ecf20Sopenharmony_ci * @irq_base: Allocate at specific IRQ number if irq_base > 0. 9828c2ecf20Sopenharmony_ci * @chip: Configuration for the interrupt controller. 9838c2ecf20Sopenharmony_ci * @data: Runtime data structure for the controller, allocated on success 9848c2ecf20Sopenharmony_ci * 9858c2ecf20Sopenharmony_ci * Returns 0 on success or an errno on failure. 9868c2ecf20Sopenharmony_ci * 9878c2ecf20Sopenharmony_ci * The ®map_irq_chip_data will be automatically released when the device is 9888c2ecf20Sopenharmony_ci * unbound. 9898c2ecf20Sopenharmony_ci */ 9908c2ecf20Sopenharmony_ciint devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq, 9918c2ecf20Sopenharmony_ci int irq_flags, int irq_base, 9928c2ecf20Sopenharmony_ci const struct regmap_irq_chip *chip, 9938c2ecf20Sopenharmony_ci struct regmap_irq_chip_data **data) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci return devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(map->dev), map, 9968c2ecf20Sopenharmony_ci irq, irq_flags, irq_base, chip, 9978c2ecf20Sopenharmony_ci data); 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci/** 10028c2ecf20Sopenharmony_ci * devm_regmap_del_irq_chip() - Resource managed regmap_del_irq_chip() 10038c2ecf20Sopenharmony_ci * 10048c2ecf20Sopenharmony_ci * @dev: Device for which which resource was allocated. 10058c2ecf20Sopenharmony_ci * @irq: Primary IRQ for the device. 10068c2ecf20Sopenharmony_ci * @data: ®map_irq_chip_data allocated by regmap_add_irq_chip(). 10078c2ecf20Sopenharmony_ci * 10088c2ecf20Sopenharmony_ci * A resource managed version of regmap_del_irq_chip(). 10098c2ecf20Sopenharmony_ci */ 10108c2ecf20Sopenharmony_civoid devm_regmap_del_irq_chip(struct device *dev, int irq, 10118c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *data) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci int rc; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci WARN_ON(irq != data->irq); 10168c2ecf20Sopenharmony_ci rc = devres_release(dev, devm_regmap_irq_chip_release, 10178c2ecf20Sopenharmony_ci devm_regmap_irq_chip_match, data); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci if (rc != 0) 10208c2ecf20Sopenharmony_ci WARN_ON(rc); 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_regmap_del_irq_chip); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci/** 10258c2ecf20Sopenharmony_ci * regmap_irq_chip_get_base() - Retrieve interrupt base for a regmap IRQ chip 10268c2ecf20Sopenharmony_ci * 10278c2ecf20Sopenharmony_ci * @data: regmap irq controller to operate on. 10288c2ecf20Sopenharmony_ci * 10298c2ecf20Sopenharmony_ci * Useful for drivers to request their own IRQs. 10308c2ecf20Sopenharmony_ci */ 10318c2ecf20Sopenharmony_ciint regmap_irq_chip_get_base(struct regmap_irq_chip_data *data) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci WARN_ON(!data->irq_base); 10348c2ecf20Sopenharmony_ci return data->irq_base; 10358c2ecf20Sopenharmony_ci} 10368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci/** 10398c2ecf20Sopenharmony_ci * regmap_irq_get_virq() - Map an interrupt on a chip to a virtual IRQ 10408c2ecf20Sopenharmony_ci * 10418c2ecf20Sopenharmony_ci * @data: regmap irq controller to operate on. 10428c2ecf20Sopenharmony_ci * @irq: index of the interrupt requested in the chip IRQs. 10438c2ecf20Sopenharmony_ci * 10448c2ecf20Sopenharmony_ci * Useful for drivers to request their own IRQs. 10458c2ecf20Sopenharmony_ci */ 10468c2ecf20Sopenharmony_ciint regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci /* Handle holes in the IRQ list */ 10498c2ecf20Sopenharmony_ci if (!data->chip->irqs[irq].mask) 10508c2ecf20Sopenharmony_ci return -EINVAL; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci return irq_create_mapping(data->domain, irq); 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_irq_get_virq); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci/** 10578c2ecf20Sopenharmony_ci * regmap_irq_get_domain() - Retrieve the irq_domain for the chip 10588c2ecf20Sopenharmony_ci * 10598c2ecf20Sopenharmony_ci * @data: regmap_irq controller to operate on. 10608c2ecf20Sopenharmony_ci * 10618c2ecf20Sopenharmony_ci * Useful for drivers to request their own IRQs and for integration 10628c2ecf20Sopenharmony_ci * with subsystems. For ease of integration NULL is accepted as a 10638c2ecf20Sopenharmony_ci * domain, allowing devices to just call this even if no domain is 10648c2ecf20Sopenharmony_ci * allocated. 10658c2ecf20Sopenharmony_ci */ 10668c2ecf20Sopenharmony_cistruct irq_domain *regmap_irq_get_domain(struct regmap_irq_chip_data *data) 10678c2ecf20Sopenharmony_ci{ 10688c2ecf20Sopenharmony_ci if (data) 10698c2ecf20Sopenharmony_ci return data->domain; 10708c2ecf20Sopenharmony_ci else 10718c2ecf20Sopenharmony_ci return NULL; 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_irq_get_domain); 1074