Skip to content

Commit 1c12fbd

Browse files
mbriandbroonie
authored andcommitted
regmap: irq: Add support for chips without separate IRQ status
Some GPIO chips allow to rise an IRQ on GPIO level changes but do not provide an IRQ status for each separate line: only the current gpio level can be retrieved. Add support for these chips, emulating IRQ status by comparing GPIO levels with the levels during the previous interrupt. Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://patch.msgid.link/20250522-mdb-max7360-support-v9-5-74fc03517e41@bootlin.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent eb4e029 commit 1c12fbd

File tree

2 files changed

+71
-31
lines changed

2 files changed

+71
-31
lines changed

drivers/base/regmap/regmap-irq.c

Lines changed: 68 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
//
77
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
88

9+
#include <linux/array_size.h>
910
#include <linux/device.h>
1011
#include <linux/export.h>
1112
#include <linux/interrupt.h>
1213
#include <linux/irq.h>
1314
#include <linux/irqdomain.h>
15+
#include <linux/overflow.h>
1416
#include <linux/pm_runtime.h>
1517
#include <linux/regmap.h>
1618
#include <linux/slab.h>
@@ -33,6 +35,7 @@ struct regmap_irq_chip_data {
3335
void *status_reg_buf;
3436
unsigned int *main_status_buf;
3537
unsigned int *status_buf;
38+
unsigned int *prev_status_buf;
3639
unsigned int *mask_buf;
3740
unsigned int *mask_buf_def;
3841
unsigned int *wake_buf;
@@ -332,27 +335,13 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
332335
return ret;
333336
}
334337

335-
static irqreturn_t regmap_irq_thread(int irq, void *d)
338+
static int read_irq_data(struct regmap_irq_chip_data *data)
336339
{
337-
struct regmap_irq_chip_data *data = d;
338340
const struct regmap_irq_chip *chip = data->chip;
339341
struct regmap *map = data->map;
340342
int ret, i;
341-
bool handled = false;
342343
u32 reg;
343344

344-
if (chip->handle_pre_irq)
345-
chip->handle_pre_irq(chip->irq_drv_data);
346-
347-
if (chip->runtime_pm) {
348-
ret = pm_runtime_get_sync(map->dev);
349-
if (ret < 0) {
350-
dev_err(map->dev, "IRQ thread failed to resume: %d\n",
351-
ret);
352-
goto exit;
353-
}
354-
}
355-
356345
/*
357346
* Read only registers with active IRQs if the chip has 'main status
358347
* register'. Else read in the statuses, using a single bulk read if
@@ -379,10 +368,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
379368
reg = data->get_irq_reg(data, chip->main_status, i);
380369
ret = regmap_read(map, reg, &data->main_status_buf[i]);
381370
if (ret) {
382-
dev_err(map->dev,
383-
"Failed to read IRQ status %d\n",
384-
ret);
385-
goto exit;
371+
dev_err(map->dev, "Failed to read IRQ status %d\n", ret);
372+
return ret;
386373
}
387374
}
388375

@@ -398,10 +385,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
398385
ret = read_sub_irq_data(data, b);
399386

400387
if (ret != 0) {
401-
dev_err(map->dev,
402-
"Failed to read IRQ status %d\n",
403-
ret);
404-
goto exit;
388+
dev_err(map->dev, "Failed to read IRQ status %d\n", ret);
389+
return ret;
405390
}
406391
}
407392

@@ -418,9 +403,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
418403
data->status_reg_buf,
419404
chip->num_regs);
420405
if (ret != 0) {
421-
dev_err(map->dev, "Failed to read IRQ status: %d\n",
422-
ret);
423-
goto exit;
406+
dev_err(map->dev, "Failed to read IRQ status: %d\n", ret);
407+
return ret;
424408
}
425409

426410
for (i = 0; i < data->chip->num_regs; i++) {
@@ -436,7 +420,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
436420
break;
437421
default:
438422
BUG();
439-
goto exit;
423+
return -EIO;
440424
}
441425
}
442426

@@ -447,10 +431,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
447431
ret = regmap_read(map, reg, &data->status_buf[i]);
448432

449433
if (ret != 0) {
450-
dev_err(map->dev,
451-
"Failed to read IRQ status: %d\n",
452-
ret);
453-
goto exit;
434+
dev_err(map->dev, "Failed to read IRQ status: %d\n", ret);
435+
return ret;
454436
}
455437
}
456438
}
@@ -459,6 +441,42 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
459441
for (i = 0; i < data->chip->num_regs; i++)
460442
data->status_buf[i] = ~data->status_buf[i];
461443

444+
return 0;
445+
}
446+
447+
static irqreturn_t regmap_irq_thread(int irq, void *d)
448+
{
449+
struct regmap_irq_chip_data *data = d;
450+
const struct regmap_irq_chip *chip = data->chip;
451+
struct regmap *map = data->map;
452+
int ret, i;
453+
bool handled = false;
454+
u32 reg;
455+
456+
if (chip->handle_pre_irq)
457+
chip->handle_pre_irq(chip->irq_drv_data);
458+
459+
if (chip->runtime_pm) {
460+
ret = pm_runtime_get_sync(map->dev);
461+
if (ret < 0) {
462+
dev_err(map->dev, "IRQ thread failed to resume: %d\n", ret);
463+
goto exit;
464+
}
465+
}
466+
467+
ret = read_irq_data(data);
468+
if (ret < 0)
469+
goto exit;
470+
471+
if (chip->status_is_level) {
472+
for (i = 0; i < data->chip->num_regs; i++) {
473+
unsigned int val = data->status_buf[i];
474+
475+
data->status_buf[i] ^= data->prev_status_buf[i];
476+
data->prev_status_buf[i] = val;
477+
}
478+
}
479+
462480
/*
463481
* Ignore masked IRQs and ack if we need to; we ack early so
464482
* there is no race between handling and acknowledging the
@@ -705,6 +723,13 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
705723
if (!d->status_buf)
706724
goto err_alloc;
707725

726+
if (chip->status_is_level) {
727+
d->prev_status_buf = kcalloc(chip->num_regs, sizeof(*d->prev_status_buf),
728+
GFP_KERNEL);
729+
if (!d->prev_status_buf)
730+
goto err_alloc;
731+
}
732+
708733
d->mask_buf = kcalloc(chip->num_regs, sizeof(*d->mask_buf),
709734
GFP_KERNEL);
710735
if (!d->mask_buf)
@@ -881,6 +906,16 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
881906
}
882907
}
883908

909+
/* Store current levels */
910+
if (chip->status_is_level) {
911+
ret = read_irq_data(d);
912+
if (ret < 0)
913+
goto err_alloc;
914+
915+
memcpy(d->prev_status_buf, d->status_buf,
916+
array_size(d->chip->num_regs, sizeof(d->prev_status_buf[0])));
917+
}
918+
884919
ret = regmap_irq_create_domain(fwnode, irq_base, chip, d);
885920
if (ret)
886921
goto err_alloc;
@@ -908,6 +943,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
908943
kfree(d->mask_buf);
909944
kfree(d->main_status_buf);
910945
kfree(d->status_buf);
946+
kfree(d->prev_status_buf);
911947
kfree(d->status_reg_buf);
912948
if (d->config_buf) {
913949
for (i = 0; i < chip->num_config_bases; i++)
@@ -985,6 +1021,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
9851021
kfree(d->main_status_buf);
9861022
kfree(d->status_reg_buf);
9871023
kfree(d->status_buf);
1024+
kfree(d->prev_status_buf);
9881025
if (d->config_buf) {
9891026
for (i = 0; i < d->chip->num_config_bases; i++)
9901027
kfree(d->config_buf[i]);

include/linux/regmap.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,6 +1641,8 @@ struct regmap_irq_chip_data;
16411641
* @ack_invert: Inverted ack register: cleared bits for ack.
16421642
* @clear_ack: Use this to set 1 and 0 or vice-versa to clear interrupts.
16431643
* @status_invert: Inverted status register: cleared bits are active interrupts.
1644+
* @status_is_level: Status register is actuall signal level: Xor status
1645+
* register with previous value to get active interrupts.
16441646
* @wake_invert: Inverted wake register: cleared bits are wake enabled.
16451647
* @type_in_mask: Use the mask registers for controlling irq type. Use this if
16461648
* the hardware provides separate bits for rising/falling edge
@@ -1704,6 +1706,7 @@ struct regmap_irq_chip {
17041706
unsigned int ack_invert:1;
17051707
unsigned int clear_ack:1;
17061708
unsigned int status_invert:1;
1709+
unsigned int status_is_level:1;
17071710
unsigned int wake_invert:1;
17081711
unsigned int type_in_mask:1;
17091712
unsigned int clear_on_unmask:1;

0 commit comments

Comments
 (0)