[PATCH] regmap: irq: Add support to clear ack registers

From: b_lkasam
Date: Fri Sep 25 2020 - 09:44:18 EST


For particular codec HWs have requirement to
writing interrupt clear and mask interrupt clear
register to toggle interrupt status. To accommodate it,
need to add one more field (clear_ack) in the regmap_irq
struct and update regmap-irq driver to support it.

Signed-off-by: Laxminath Kasam <lkasam@xxxxxxxxxxxxxx>
---
drivers/base/regmap/regmap-irq.c | 52 ++++++++++++++++++++++++++++++++++++----
include/linux/regmap.h | 2 ++
2 files changed, 49 insertions(+), 5 deletions(-)

diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 369a57e..53a1557 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -164,10 +164,21 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
reg = d->chip->ack_base +
(i * map->reg_stride * d->irq_reg_stride);
/* some chips ack by write 0 */
- if (d->chip->ack_invert)
+ if (d->chip->ack_invert) {
ret = regmap_write(map, reg, ~d->mask_buf[i]);
- else
+ if (d->chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(map, reg,
+ d->mask_buf[i]);
+ }
+ } else {
ret = regmap_write(map, reg, d->mask_buf[i]);
+ if (d->chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(map, reg,
+ ~d->mask_buf[i]);
+ }
+ }
if (ret != 0)
dev_err(d->map->dev, "Failed to ack 0x%x: %d\n",
reg, ret);
@@ -493,7 +504,23 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) {
reg = chip->ack_base +
(i * map->reg_stride * data->irq_reg_stride);
- ret = regmap_write(map, reg, data->status_buf[i]);
+ if (chip->ack_invert) {
+ ret = regmap_write(map, reg,
+ ~data->status_buf[i]);
+ if (chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(map, reg,
+ data->status_buf[i]);
+ }
+ } else {
+ ret = regmap_write(map, reg,
+ data->status_buf[i]);
+ if (chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(map, reg,
+ ~data->status_buf[i]);
+ }
+ }
if (ret != 0)
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
reg, ret);
@@ -716,12 +743,27 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) {
reg = chip->ack_base +
(i * map->reg_stride * d->irq_reg_stride);
- if (chip->ack_invert)
+ if (chip->ack_invert) {
ret = regmap_write(map, reg,
~(d->status_buf[i] & d->mask_buf[i]));
- else
+ if (d->chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(
+ map, reg,
+ (d->status_buf[i] &
+ d->mask_buf[i]);
+ }
+ } else {
ret = regmap_write(map, reg,
d->status_buf[i] & d->mask_buf[i]);
+ if (d->chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(
+ map, reg,
+ ~(d->status_buf[i] &
+ d->mask_buf[i]);
+ }
+ }
if (ret != 0) {
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
reg, ret);
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 1970ed5..0701671 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1305,6 +1305,7 @@ struct regmap_irq_sub_irq_map {
* @mask_invert: Inverted mask register: cleared bits are masked out.
* @use_ack: Use @ack register even if it is zero.
* @ack_invert: Inverted ack register: cleared bits for ack.
+ * @clear_ack: Use this to set 1 and 0 or vice-versa to clear interrupts.
* @wake_invert: Inverted wake register: cleared bits are wake enabled.
* @type_invert: Invert the type flags.
* @type_in_mask: Use the mask registers for controlling irq type. For
@@ -1353,6 +1354,7 @@ struct regmap_irq_chip {
bool mask_invert:1;
bool use_ack:1;
bool ack_invert:1;
+ bool clear_ack:1;
bool wake_invert:1;
bool runtime_pm:1;
bool type_invert:1;
--
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc.,
is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
From 708ddb8ff2e673ef332bcd594490744b9e472227 Mon Sep 17 00:00:00 2001
From: Laxminath Kasam <lkasam@xxxxxxxxxxxxxx>
Date: Wed, 23 Sep 2020 18:42:36 +0530
Subject: [PATCH] regmap: irq: Add support to clear ack registers

For particular codec HWs have requirement to
writing interrupt clear and mask interrupt clear
register to toggle interrupt status. To accommodate it,
need to add one more field (clear_ack) in the regmap_irq
struct and update regmap-irq driver to support it.

Signed-off-by: Laxminath Kasam <lkasam@xxxxxxxxxxxxxx>
---
drivers/base/regmap/regmap-irq.c | 52 ++++++++++++++++++++++++++++++++++++----
include/linux/regmap.h | 2 ++
2 files changed, 49 insertions(+), 5 deletions(-)

diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 369a57e..53a1557 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -164,10 +164,21 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
reg = d->chip->ack_base +
(i * map->reg_stride * d->irq_reg_stride);
/* some chips ack by write 0 */
- if (d->chip->ack_invert)
+ if (d->chip->ack_invert) {
ret = regmap_write(map, reg, ~d->mask_buf[i]);
- else
+ if (d->chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(map, reg,
+ d->mask_buf[i]);
+ }
+ } else {
ret = regmap_write(map, reg, d->mask_buf[i]);
+ if (d->chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(map, reg,
+ ~d->mask_buf[i]);
+ }
+ }
if (ret != 0)
dev_err(d->map->dev, "Failed to ack 0x%x: %d\n",
reg, ret);
@@ -493,7 +504,23 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) {
reg = chip->ack_base +
(i * map->reg_stride * data->irq_reg_stride);
- ret = regmap_write(map, reg, data->status_buf[i]);
+ if (chip->ack_invert) {
+ ret = regmap_write(map, reg,
+ ~data->status_buf[i]);
+ if (chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(map, reg,
+ data->status_buf[i]);
+ }
+ } else {
+ ret = regmap_write(map, reg,
+ data->status_buf[i]);
+ if (chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(map, reg,
+ ~data->status_buf[i]);
+ }
+ }
if (ret != 0)
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
reg, ret);
@@ -716,12 +743,27 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) {
reg = chip->ack_base +
(i * map->reg_stride * d->irq_reg_stride);
- if (chip->ack_invert)
+ if (chip->ack_invert) {
ret = regmap_write(map, reg,
~(d->status_buf[i] & d->mask_buf[i]));
- else
+ if (d->chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(
+ map, reg,
+ (d->status_buf[i] &
+ d->mask_buf[i]);
+ }
+ } else {
ret = regmap_write(map, reg,
d->status_buf[i] & d->mask_buf[i]);
+ if (d->chip->clear_ack) {
+ if (!ret)
+ ret = regmap_write(
+ map, reg,
+ ~(d->status_buf[i] &
+ d->mask_buf[i]);
+ }
+ }
if (ret != 0) {
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
reg, ret);
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 1970ed5..0701671 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -1305,6 +1305,7 @@ struct regmap_irq_sub_irq_map {
* @mask_invert: Inverted mask register: cleared bits are masked out.
* @use_ack: Use @ack register even if it is zero.
* @ack_invert: Inverted ack register: cleared bits for ack.
+ * @clear_ack: Use this to set 1 and 0 or vice-versa to clear interrupts.
* @wake_invert: Inverted wake register: cleared bits are wake enabled.
* @type_invert: Invert the type flags.
* @type_in_mask: Use the mask registers for controlling irq type. For
@@ -1353,6 +1354,7 @@ struct regmap_irq_chip {
bool mask_invert:1;
bool use_ack:1;
bool ack_invert:1;
+ bool clear_ack:1;
bool wake_invert:1;
bool runtime_pm:1;
bool type_invert:1;
--
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc.,
is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.