mirror of https://github.com/OpenIPC/firmware.git
150 lines
3.9 KiB
Diff
150 lines
3.9 KiB
Diff
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 <chen.li@ingenic.com>
|
|
+ *
|
|
+ * Based on code
|
|
+ * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
|
|
+ * Author: Laxman dewangan <ldewangan@nvidia.com>
|
|
+ *
|
|
+ * 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 <http://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ */
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/irq.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/mfd/rn5t567.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/irqdomain.h>
|
|
+#include <linux/of_irq.h>
|
|
+
|
|
+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);
|
|
+}
|