1*d7d8d7a2SKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0+
2*d7d8d7a2SKrzysztof Kozlowski //
3*d7d8d7a2SKrzysztof Kozlowski // Interrupt controller support for MAX8998
4*d7d8d7a2SKrzysztof Kozlowski //
5*d7d8d7a2SKrzysztof Kozlowski // Copyright (C) 2010 Samsung Electronics Co.Ltd
6*d7d8d7a2SKrzysztof Kozlowski // Author: Joonyoung Shim <jy0922.shim@samsung.com>
72c7e6f57SJoonyoung Shim
82c7e6f57SJoonyoung Shim #include <linux/device.h>
92c7e6f57SJoonyoung Shim #include <linux/interrupt.h>
102c7e6f57SJoonyoung Shim #include <linux/irq.h>
11443c6ae2STomasz Figa #include <linux/irqdomain.h>
122c7e6f57SJoonyoung Shim #include <linux/mfd/max8998-private.h>
132c7e6f57SJoonyoung Shim
142c7e6f57SJoonyoung Shim struct max8998_irq_data {
152c7e6f57SJoonyoung Shim int reg;
162c7e6f57SJoonyoung Shim int mask;
172c7e6f57SJoonyoung Shim };
182c7e6f57SJoonyoung Shim
192c7e6f57SJoonyoung Shim static struct max8998_irq_data max8998_irqs[] = {
202c7e6f57SJoonyoung Shim [MAX8998_IRQ_DCINF] = {
212c7e6f57SJoonyoung Shim .reg = 1,
222c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_DCINF_MASK,
232c7e6f57SJoonyoung Shim },
242c7e6f57SJoonyoung Shim [MAX8998_IRQ_DCINR] = {
252c7e6f57SJoonyoung Shim .reg = 1,
262c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_DCINR_MASK,
272c7e6f57SJoonyoung Shim },
282c7e6f57SJoonyoung Shim [MAX8998_IRQ_JIGF] = {
292c7e6f57SJoonyoung Shim .reg = 1,
302c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_JIGF_MASK,
312c7e6f57SJoonyoung Shim },
322c7e6f57SJoonyoung Shim [MAX8998_IRQ_JIGR] = {
332c7e6f57SJoonyoung Shim .reg = 1,
342c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_JIGR_MASK,
352c7e6f57SJoonyoung Shim },
362c7e6f57SJoonyoung Shim [MAX8998_IRQ_PWRONF] = {
372c7e6f57SJoonyoung Shim .reg = 1,
382c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_PWRONF_MASK,
392c7e6f57SJoonyoung Shim },
402c7e6f57SJoonyoung Shim [MAX8998_IRQ_PWRONR] = {
412c7e6f57SJoonyoung Shim .reg = 1,
422c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_PWRONR_MASK,
432c7e6f57SJoonyoung Shim },
442c7e6f57SJoonyoung Shim [MAX8998_IRQ_WTSREVNT] = {
452c7e6f57SJoonyoung Shim .reg = 2,
462c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_WTSREVNT_MASK,
472c7e6f57SJoonyoung Shim },
482c7e6f57SJoonyoung Shim [MAX8998_IRQ_SMPLEVNT] = {
492c7e6f57SJoonyoung Shim .reg = 2,
502c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_SMPLEVNT_MASK,
512c7e6f57SJoonyoung Shim },
522c7e6f57SJoonyoung Shim [MAX8998_IRQ_ALARM1] = {
532c7e6f57SJoonyoung Shim .reg = 2,
542c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_ALARM1_MASK,
552c7e6f57SJoonyoung Shim },
562c7e6f57SJoonyoung Shim [MAX8998_IRQ_ALARM0] = {
572c7e6f57SJoonyoung Shim .reg = 2,
582c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_ALARM0_MASK,
592c7e6f57SJoonyoung Shim },
602c7e6f57SJoonyoung Shim [MAX8998_IRQ_ONKEY1S] = {
612c7e6f57SJoonyoung Shim .reg = 3,
622c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_ONKEY1S_MASK,
632c7e6f57SJoonyoung Shim },
642c7e6f57SJoonyoung Shim [MAX8998_IRQ_TOPOFFR] = {
652c7e6f57SJoonyoung Shim .reg = 3,
662c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_TOPOFFR_MASK,
672c7e6f57SJoonyoung Shim },
682c7e6f57SJoonyoung Shim [MAX8998_IRQ_DCINOVPR] = {
692c7e6f57SJoonyoung Shim .reg = 3,
702c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_DCINOVPR_MASK,
712c7e6f57SJoonyoung Shim },
722c7e6f57SJoonyoung Shim [MAX8998_IRQ_CHGRSTF] = {
732c7e6f57SJoonyoung Shim .reg = 3,
742c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_CHGRSTF_MASK,
752c7e6f57SJoonyoung Shim },
762c7e6f57SJoonyoung Shim [MAX8998_IRQ_DONER] = {
772c7e6f57SJoonyoung Shim .reg = 3,
782c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_DONER_MASK,
792c7e6f57SJoonyoung Shim },
802c7e6f57SJoonyoung Shim [MAX8998_IRQ_CHGFAULT] = {
812c7e6f57SJoonyoung Shim .reg = 3,
822c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_CHGFAULT_MASK,
832c7e6f57SJoonyoung Shim },
842c7e6f57SJoonyoung Shim [MAX8998_IRQ_LOBAT1] = {
852c7e6f57SJoonyoung Shim .reg = 4,
862c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_LOBAT1_MASK,
872c7e6f57SJoonyoung Shim },
882c7e6f57SJoonyoung Shim [MAX8998_IRQ_LOBAT2] = {
892c7e6f57SJoonyoung Shim .reg = 4,
902c7e6f57SJoonyoung Shim .mask = MAX8998_IRQ_LOBAT2_MASK,
912c7e6f57SJoonyoung Shim },
922c7e6f57SJoonyoung Shim };
932c7e6f57SJoonyoung Shim
942c7e6f57SJoonyoung Shim static inline struct max8998_irq_data *
irq_to_max8998_irq(struct max8998_dev * max8998,struct irq_data * data)95e5ad2344SThomas Gleixner irq_to_max8998_irq(struct max8998_dev *max8998, struct irq_data *data)
962c7e6f57SJoonyoung Shim {
97443c6ae2STomasz Figa return &max8998_irqs[data->hwirq];
982c7e6f57SJoonyoung Shim }
992c7e6f57SJoonyoung Shim
max8998_irq_lock(struct irq_data * data)1002898577eSMark Brown static void max8998_irq_lock(struct irq_data *data)
1012c7e6f57SJoonyoung Shim {
1022898577eSMark Brown struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data);
1032c7e6f57SJoonyoung Shim
1042c7e6f57SJoonyoung Shim mutex_lock(&max8998->irqlock);
1052c7e6f57SJoonyoung Shim }
1062c7e6f57SJoonyoung Shim
max8998_irq_sync_unlock(struct irq_data * data)1072898577eSMark Brown static void max8998_irq_sync_unlock(struct irq_data *data)
1082c7e6f57SJoonyoung Shim {
1092898577eSMark Brown struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data);
1102c7e6f57SJoonyoung Shim int i;
1112c7e6f57SJoonyoung Shim
1122c7e6f57SJoonyoung Shim for (i = 0; i < ARRAY_SIZE(max8998->irq_masks_cur); i++) {
1132c7e6f57SJoonyoung Shim /*
1142c7e6f57SJoonyoung Shim * If there's been a change in the mask write it back
1152c7e6f57SJoonyoung Shim * to the hardware.
1162c7e6f57SJoonyoung Shim */
1172c7e6f57SJoonyoung Shim if (max8998->irq_masks_cur[i] != max8998->irq_masks_cache[i]) {
1182c7e6f57SJoonyoung Shim max8998->irq_masks_cache[i] = max8998->irq_masks_cur[i];
1192c7e6f57SJoonyoung Shim max8998_write_reg(max8998->i2c, MAX8998_REG_IRQM1 + i,
1202c7e6f57SJoonyoung Shim max8998->irq_masks_cur[i]);
1212c7e6f57SJoonyoung Shim }
1222c7e6f57SJoonyoung Shim }
1232c7e6f57SJoonyoung Shim
1242c7e6f57SJoonyoung Shim mutex_unlock(&max8998->irqlock);
1252c7e6f57SJoonyoung Shim }
1262c7e6f57SJoonyoung Shim
max8998_irq_unmask(struct irq_data * data)1272898577eSMark Brown static void max8998_irq_unmask(struct irq_data *data)
1282c7e6f57SJoonyoung Shim {
1292898577eSMark Brown struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data);
130e5ad2344SThomas Gleixner struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, data);
1312c7e6f57SJoonyoung Shim
1322c7e6f57SJoonyoung Shim max8998->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
1332c7e6f57SJoonyoung Shim }
1342c7e6f57SJoonyoung Shim
max8998_irq_mask(struct irq_data * data)1352898577eSMark Brown static void max8998_irq_mask(struct irq_data *data)
1362c7e6f57SJoonyoung Shim {
1372898577eSMark Brown struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data);
138e5ad2344SThomas Gleixner struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, data);
1392c7e6f57SJoonyoung Shim
1402c7e6f57SJoonyoung Shim max8998->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
1412c7e6f57SJoonyoung Shim }
1422c7e6f57SJoonyoung Shim
1432c7e6f57SJoonyoung Shim static struct irq_chip max8998_irq_chip = {
1442c7e6f57SJoonyoung Shim .name = "max8998",
1452898577eSMark Brown .irq_bus_lock = max8998_irq_lock,
1462898577eSMark Brown .irq_bus_sync_unlock = max8998_irq_sync_unlock,
1472898577eSMark Brown .irq_mask = max8998_irq_mask,
1482898577eSMark Brown .irq_unmask = max8998_irq_unmask,
1492c7e6f57SJoonyoung Shim };
1502c7e6f57SJoonyoung Shim
max8998_irq_thread(int irq,void * data)1512c7e6f57SJoonyoung Shim static irqreturn_t max8998_irq_thread(int irq, void *data)
1522c7e6f57SJoonyoung Shim {
1532c7e6f57SJoonyoung Shim struct max8998_dev *max8998 = data;
1542c7e6f57SJoonyoung Shim u8 irq_reg[MAX8998_NUM_IRQ_REGS];
1552c7e6f57SJoonyoung Shim int ret;
1562c7e6f57SJoonyoung Shim int i;
1572c7e6f57SJoonyoung Shim
1582c7e6f57SJoonyoung Shim ret = max8998_bulk_read(max8998->i2c, MAX8998_REG_IRQ1,
1592c7e6f57SJoonyoung Shim MAX8998_NUM_IRQ_REGS, irq_reg);
1602c7e6f57SJoonyoung Shim if (ret < 0) {
1612c7e6f57SJoonyoung Shim dev_err(max8998->dev, "Failed to read interrupt register: %d\n",
1622c7e6f57SJoonyoung Shim ret);
1632c7e6f57SJoonyoung Shim return IRQ_NONE;
1642c7e6f57SJoonyoung Shim }
1652c7e6f57SJoonyoung Shim
1662c7e6f57SJoonyoung Shim /* Apply masking */
1672c7e6f57SJoonyoung Shim for (i = 0; i < MAX8998_NUM_IRQ_REGS; i++)
1682c7e6f57SJoonyoung Shim irq_reg[i] &= ~max8998->irq_masks_cur[i];
1692c7e6f57SJoonyoung Shim
1702c7e6f57SJoonyoung Shim /* Report */
1712c7e6f57SJoonyoung Shim for (i = 0; i < MAX8998_IRQ_NR; i++) {
172443c6ae2STomasz Figa if (irq_reg[max8998_irqs[i].reg - 1] & max8998_irqs[i].mask) {
173443c6ae2STomasz Figa irq = irq_find_mapping(max8998->irq_domain, i);
174443c6ae2STomasz Figa if (WARN_ON(!irq)) {
175443c6ae2STomasz Figa disable_irq_nosync(max8998->irq);
176443c6ae2STomasz Figa return IRQ_NONE;
177443c6ae2STomasz Figa }
178443c6ae2STomasz Figa handle_nested_irq(irq);
179443c6ae2STomasz Figa }
1802c7e6f57SJoonyoung Shim }
1812c7e6f57SJoonyoung Shim
1822c7e6f57SJoonyoung Shim return IRQ_HANDLED;
1832c7e6f57SJoonyoung Shim }
1842c7e6f57SJoonyoung Shim
max8998_irq_resume(struct max8998_dev * max8998)185cdd137c9SMyungJoo Ham int max8998_irq_resume(struct max8998_dev *max8998)
186cdd137c9SMyungJoo Ham {
187443c6ae2STomasz Figa if (max8998->irq && max8998->irq_domain)
188443c6ae2STomasz Figa max8998_irq_thread(max8998->irq, max8998);
189cdd137c9SMyungJoo Ham return 0;
190cdd137c9SMyungJoo Ham }
191cdd137c9SMyungJoo Ham
max8998_irq_domain_map(struct irq_domain * d,unsigned int irq,irq_hw_number_t hw)192443c6ae2STomasz Figa static int max8998_irq_domain_map(struct irq_domain *d, unsigned int irq,
193443c6ae2STomasz Figa irq_hw_number_t hw)
194443c6ae2STomasz Figa {
195443c6ae2STomasz Figa struct max8997_dev *max8998 = d->host_data;
196443c6ae2STomasz Figa
197443c6ae2STomasz Figa irq_set_chip_data(irq, max8998);
198443c6ae2STomasz Figa irq_set_chip_and_handler(irq, &max8998_irq_chip, handle_edge_irq);
199443c6ae2STomasz Figa irq_set_nested_thread(irq, 1);
200443c6ae2STomasz Figa irq_set_noprobe(irq);
2019bd09f34SRob Herring
202443c6ae2STomasz Figa return 0;
203443c6ae2STomasz Figa }
204443c6ae2STomasz Figa
2057ce7b26fSKrzysztof Kozlowski static const struct irq_domain_ops max8998_irq_domain_ops = {
206443c6ae2STomasz Figa .map = max8998_irq_domain_map,
207443c6ae2STomasz Figa };
208443c6ae2STomasz Figa
max8998_irq_init(struct max8998_dev * max8998)2092c7e6f57SJoonyoung Shim int max8998_irq_init(struct max8998_dev *max8998)
2102c7e6f57SJoonyoung Shim {
2112c7e6f57SJoonyoung Shim int i;
2122c7e6f57SJoonyoung Shim int ret;
213443c6ae2STomasz Figa struct irq_domain *domain;
2142c7e6f57SJoonyoung Shim
2152c7e6f57SJoonyoung Shim if (!max8998->irq) {
2162c7e6f57SJoonyoung Shim dev_warn(max8998->dev,
2172c7e6f57SJoonyoung Shim "No interrupt specified, no interrupts\n");
2182c7e6f57SJoonyoung Shim return 0;
2192c7e6f57SJoonyoung Shim }
2202c7e6f57SJoonyoung Shim
2212c7e6f57SJoonyoung Shim mutex_init(&max8998->irqlock);
2222c7e6f57SJoonyoung Shim
2232c7e6f57SJoonyoung Shim /* Mask the individual interrupt sources */
2242c7e6f57SJoonyoung Shim for (i = 0; i < MAX8998_NUM_IRQ_REGS; i++) {
2252c7e6f57SJoonyoung Shim max8998->irq_masks_cur[i] = 0xff;
2262c7e6f57SJoonyoung Shim max8998->irq_masks_cache[i] = 0xff;
2272c7e6f57SJoonyoung Shim max8998_write_reg(max8998->i2c, MAX8998_REG_IRQM1 + i, 0xff);
2282c7e6f57SJoonyoung Shim }
2292c7e6f57SJoonyoung Shim
2302c7e6f57SJoonyoung Shim max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM1, 0xff);
2312c7e6f57SJoonyoung Shim max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM2, 0xff);
2322c7e6f57SJoonyoung Shim
233443c6ae2STomasz Figa domain = irq_domain_add_simple(NULL, MAX8998_IRQ_NR,
234443c6ae2STomasz Figa max8998->irq_base, &max8998_irq_domain_ops, max8998);
235443c6ae2STomasz Figa if (!domain) {
236443c6ae2STomasz Figa dev_err(max8998->dev, "could not create irq domain\n");
237443c6ae2STomasz Figa return -ENODEV;
2382c7e6f57SJoonyoung Shim }
239443c6ae2STomasz Figa max8998->irq_domain = domain;
2402c7e6f57SJoonyoung Shim
2412c7e6f57SJoonyoung Shim ret = request_threaded_irq(max8998->irq, NULL, max8998_irq_thread,
2422c7e6f57SJoonyoung Shim IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
2432c7e6f57SJoonyoung Shim "max8998-irq", max8998);
2442c7e6f57SJoonyoung Shim if (ret) {
2452c7e6f57SJoonyoung Shim dev_err(max8998->dev, "Failed to request IRQ %d: %d\n",
2462c7e6f57SJoonyoung Shim max8998->irq, ret);
2472c7e6f57SJoonyoung Shim return ret;
2482c7e6f57SJoonyoung Shim }
2492c7e6f57SJoonyoung Shim
2502c7e6f57SJoonyoung Shim if (!max8998->ono)
2512c7e6f57SJoonyoung Shim return 0;
2522c7e6f57SJoonyoung Shim
2532c7e6f57SJoonyoung Shim ret = request_threaded_irq(max8998->ono, NULL, max8998_irq_thread,
2542c7e6f57SJoonyoung Shim IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
2552c7e6f57SJoonyoung Shim IRQF_ONESHOT, "max8998-ono", max8998);
2562c7e6f57SJoonyoung Shim if (ret)
2572c7e6f57SJoonyoung Shim dev_err(max8998->dev, "Failed to request IRQ %d: %d\n",
2582c7e6f57SJoonyoung Shim max8998->ono, ret);
2592c7e6f57SJoonyoung Shim
2602c7e6f57SJoonyoung Shim return 0;
2612c7e6f57SJoonyoung Shim }
2622c7e6f57SJoonyoung Shim
max8998_irq_exit(struct max8998_dev * max8998)2632c7e6f57SJoonyoung Shim void max8998_irq_exit(struct max8998_dev *max8998)
2642c7e6f57SJoonyoung Shim {
2651558b51eSAxel Lin if (max8998->ono)
2661558b51eSAxel Lin free_irq(max8998->ono, max8998);
2671558b51eSAxel Lin
2682c7e6f57SJoonyoung Shim if (max8998->irq)
2692c7e6f57SJoonyoung Shim free_irq(max8998->irq, max8998);
2702c7e6f57SJoonyoung Shim }
271