diff -drupN a/drivers/mfd/rn5t567-irq.c b/drivers/mfd/rn5t567-irq.c --- a/drivers/mfd/rn5t567-irq.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/mfd/rn5t567-irq.c 2022-06-09 05:02:30.000000000 +0300 @@ -0,0 +1,145 @@ +/* + * Interrupt driver for RICOH567 power management chip. + * + * Copyright (C) 2016 Ingenic Semiconductor Co., Ltd. + * + * Author: cli + * + * Based on code + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#include +#include +#include +#include +#include +#include +#include + +struct rn5t567_irq_data { + struct device *dev; + struct regmap *regmap; + struct mutex irq_lock; + struct irq_domain *irq_domain; +}; + +static struct rn5t567_irq_data *rn5t567 = NULL; + +static irqreturn_t rn5t567_irq_thread_handler(int virq, void *data) +{ + struct rn5t567_irq_data *pdata = data; + int enable, pending = 0, ret; + + might_sleep(); + + mutex_lock(&pdata->irq_lock); + + ret = regmap_read(pdata->regmap, RN5T567_INTMON, &pending); + ret |=regmap_read(pdata->regmap, RN5T567_INTEN, &enable); + if (!ret) { + dev_warn(pdata->dev, "irq read i2c reg faild: %d\n", ret); + mutex_unlock(&pdata->irq_lock); + return IRQ_HANDLED; + } + pending = (pending & enable); + mutex_unlock(&pdata->irq_lock); + + while (pending) { + int cur_irq , i; + i = fls(pending) - 1; + cur_irq = irq_find_mapping(pdata->irq_domain, i); + handle_nested_irq(cur_irq); + pending &= ~BIT(i); + }; + + return IRQ_HANDLED; +} + +static int rn5t567_domain_xlate_onecell(struct irq_domain *d, struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + struct regmap *regmap = (struct regmap *)d->host_data; + int ret; + + if (!regmap) + return -EINVAL; + + ret = irq_domain_xlate_onecell(d, ctrlr, intspec, intsize, + out_hwirq, out_type); + if (ret) + return ret; + + switch (*out_hwirq) { + case RN5T567_IRQ_WDG: + break; + case RN5T567_IRQ_SYSTEM: + case RN5T567_IRQ_DCDC: + case RN5T567_IRQ_GPIO: + regmap_update_bits(regmap, RN5T567_INTEN, BIT(*out_hwirq), BIT(*out_hwirq)); + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct irq_domain_ops rn5t567_irq_domain_ops = { + .xlate = rn5t567_domain_xlate_onecell, +}; + +int rn5t567_irq_init(struct i2c_client *i2c, struct regmap *regmap) +{ + int ret; + + i2c->irq = irq_of_parse_and_map(i2c->dev.of_node, 0); + if (!i2c->irq) + return 0; + + rn5t567 = devm_kzalloc(&i2c->dev, sizeof(struct rn5t567_irq_data), GFP_KERNEL); + if (!rn5t567) + return -ENOMEM; + + mutex_init(&rn5t567->irq_lock); + rn5t567->regmap = regmap; + + regmap_write(rn5t567->regmap, RN5T567_INTEN, 0); + regmap_write(rn5t567->regmap, RN5T567_INTPOL, 0); + + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rn5t567_irq_thread_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, i2c->name, rn5t567); + if (ret) { + dev_err(&i2c->dev, "irq request failed: %d\n", ret); + return ret; + } + + rn5t567->irq_domain = irq_domain_add_linear(i2c->dev.of_node, RN5T567_IRQ_NUM, + &rn5t567_irq_domain_ops, regmap); + if (IS_ERR(rn5t567->irq_domain)) { + ret = PTR_ERR(rn5t567->irq_domain); + dev_err(&i2c->dev, "irq domain add failed: %d\n", ret); + return ret; + } + + rn5t567->dev = &i2c->dev; + + return 0; +} + +void rn5t567_irq_deinit(void) +{ + if (rn5t567->irq_domain) + irq_domain_remove(rn5t567->irq_domain); + + regmap_write(rn5t567->regmap, RN5T567_INTEN, 0); +}