xref: /openbmc/linux/drivers/mfd/max8998-irq.c (revision d7d8d7a2)
1d7d8d7a2SKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0+
2d7d8d7a2SKrzysztof Kozlowski //
3d7d8d7a2SKrzysztof Kozlowski // Interrupt controller support for MAX8998
4d7d8d7a2SKrzysztof Kozlowski //
5d7d8d7a2SKrzysztof Kozlowski // Copyright (C) 2010 Samsung Electronics Co.Ltd
6d7d8d7a2SKrzysztof 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