xref: /openbmc/linux/drivers/mfd/pcf50633-irq.c (revision 2874c5fd)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2380c09f6SLars-Peter Clausen /* NXP PCF50633 Power Management Unit (PMU) driver
3380c09f6SLars-Peter Clausen  *
4380c09f6SLars-Peter Clausen  * (C) 2006-2008 by Openmoko, Inc.
5380c09f6SLars-Peter Clausen  * Author: Harald Welte <laforge@openmoko.org>
6380c09f6SLars-Peter Clausen  * 	   Balaji Rao <balajirrao@openmoko.org>
7380c09f6SLars-Peter Clausen  * All rights reserved.
8380c09f6SLars-Peter Clausen  */
9380c09f6SLars-Peter Clausen 
10380c09f6SLars-Peter Clausen #include <linux/interrupt.h>
11380c09f6SLars-Peter Clausen #include <linux/kernel.h>
12380c09f6SLars-Peter Clausen #include <linux/mutex.h>
135d4a357dSPaul Gortmaker #include <linux/export.h>
14380c09f6SLars-Peter Clausen #include <linux/slab.h>
15380c09f6SLars-Peter Clausen 
16380c09f6SLars-Peter Clausen #include <linux/mfd/pcf50633/core.h>
17624e26e0SAxel Lin #include <linux/mfd/pcf50633/mbc.h>
18380c09f6SLars-Peter Clausen 
19380c09f6SLars-Peter Clausen int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
20380c09f6SLars-Peter Clausen 			void (*handler) (int, void *), void *data)
21380c09f6SLars-Peter Clausen {
22380c09f6SLars-Peter Clausen 	if (irq < 0 || irq >= PCF50633_NUM_IRQ || !handler)
23380c09f6SLars-Peter Clausen 		return -EINVAL;
24380c09f6SLars-Peter Clausen 
25380c09f6SLars-Peter Clausen 	if (WARN_ON(pcf->irq_handler[irq].handler))
26380c09f6SLars-Peter Clausen 		return -EBUSY;
27380c09f6SLars-Peter Clausen 
28380c09f6SLars-Peter Clausen 	mutex_lock(&pcf->lock);
29380c09f6SLars-Peter Clausen 	pcf->irq_handler[irq].handler = handler;
30380c09f6SLars-Peter Clausen 	pcf->irq_handler[irq].data = data;
31380c09f6SLars-Peter Clausen 	mutex_unlock(&pcf->lock);
32380c09f6SLars-Peter Clausen 
33380c09f6SLars-Peter Clausen 	return 0;
34380c09f6SLars-Peter Clausen }
35380c09f6SLars-Peter Clausen EXPORT_SYMBOL_GPL(pcf50633_register_irq);
36380c09f6SLars-Peter Clausen 
37380c09f6SLars-Peter Clausen int pcf50633_free_irq(struct pcf50633 *pcf, int irq)
38380c09f6SLars-Peter Clausen {
39380c09f6SLars-Peter Clausen 	if (irq < 0 || irq >= PCF50633_NUM_IRQ)
40380c09f6SLars-Peter Clausen 		return -EINVAL;
41380c09f6SLars-Peter Clausen 
42380c09f6SLars-Peter Clausen 	mutex_lock(&pcf->lock);
43380c09f6SLars-Peter Clausen 	pcf->irq_handler[irq].handler = NULL;
44380c09f6SLars-Peter Clausen 	mutex_unlock(&pcf->lock);
45380c09f6SLars-Peter Clausen 
46380c09f6SLars-Peter Clausen 	return 0;
47380c09f6SLars-Peter Clausen }
48380c09f6SLars-Peter Clausen EXPORT_SYMBOL_GPL(pcf50633_free_irq);
49380c09f6SLars-Peter Clausen 
50380c09f6SLars-Peter Clausen static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
51380c09f6SLars-Peter Clausen {
52380c09f6SLars-Peter Clausen 	u8 reg, bit;
53e7238fd2SJavier Martinez Canillas 	int idx;
54380c09f6SLars-Peter Clausen 
55380c09f6SLars-Peter Clausen 	idx = irq >> 3;
56380c09f6SLars-Peter Clausen 	reg = PCF50633_REG_INT1M + idx;
57380c09f6SLars-Peter Clausen 	bit = 1 << (irq & 0x07);
58380c09f6SLars-Peter Clausen 
59380c09f6SLars-Peter Clausen 	pcf50633_reg_set_bit_mask(pcf, reg, bit, mask ? bit : 0);
60380c09f6SLars-Peter Clausen 
61380c09f6SLars-Peter Clausen 	mutex_lock(&pcf->lock);
62380c09f6SLars-Peter Clausen 
63380c09f6SLars-Peter Clausen 	if (mask)
64380c09f6SLars-Peter Clausen 		pcf->mask_regs[idx] |= bit;
65380c09f6SLars-Peter Clausen 	else
66380c09f6SLars-Peter Clausen 		pcf->mask_regs[idx] &= ~bit;
67380c09f6SLars-Peter Clausen 
68380c09f6SLars-Peter Clausen 	mutex_unlock(&pcf->lock);
69380c09f6SLars-Peter Clausen 
70e7238fd2SJavier Martinez Canillas 	return 0;
71380c09f6SLars-Peter Clausen }
72380c09f6SLars-Peter Clausen 
73380c09f6SLars-Peter Clausen int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
74380c09f6SLars-Peter Clausen {
75380c09f6SLars-Peter Clausen 	dev_dbg(pcf->dev, "Masking IRQ %d\n", irq);
76380c09f6SLars-Peter Clausen 
77380c09f6SLars-Peter Clausen 	return __pcf50633_irq_mask_set(pcf, irq, 1);
78380c09f6SLars-Peter Clausen }
79380c09f6SLars-Peter Clausen EXPORT_SYMBOL_GPL(pcf50633_irq_mask);
80380c09f6SLars-Peter Clausen 
81380c09f6SLars-Peter Clausen int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
82380c09f6SLars-Peter Clausen {
83380c09f6SLars-Peter Clausen 	dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq);
84380c09f6SLars-Peter Clausen 
85380c09f6SLars-Peter Clausen 	return __pcf50633_irq_mask_set(pcf, irq, 0);
86380c09f6SLars-Peter Clausen }
87380c09f6SLars-Peter Clausen EXPORT_SYMBOL_GPL(pcf50633_irq_unmask);
88380c09f6SLars-Peter Clausen 
89380c09f6SLars-Peter Clausen int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq)
90380c09f6SLars-Peter Clausen {
91380c09f6SLars-Peter Clausen 	u8 reg, bits;
92380c09f6SLars-Peter Clausen 
93380c09f6SLars-Peter Clausen 	reg =  irq >> 3;
94380c09f6SLars-Peter Clausen 	bits = 1 << (irq & 0x07);
95380c09f6SLars-Peter Clausen 
96380c09f6SLars-Peter Clausen 	return pcf->mask_regs[reg] & bits;
97380c09f6SLars-Peter Clausen }
98380c09f6SLars-Peter Clausen EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get);
99380c09f6SLars-Peter Clausen 
100380c09f6SLars-Peter Clausen static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq)
101380c09f6SLars-Peter Clausen {
102380c09f6SLars-Peter Clausen 	if (pcf->irq_handler[irq].handler)
103380c09f6SLars-Peter Clausen 		pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data);
104380c09f6SLars-Peter Clausen }
105380c09f6SLars-Peter Clausen 
106380c09f6SLars-Peter Clausen /* Maximum amount of time ONKEY is held before emergency action is taken */
107380c09f6SLars-Peter Clausen #define PCF50633_ONKEY1S_TIMEOUT 8
108380c09f6SLars-Peter Clausen 
109380c09f6SLars-Peter Clausen static irqreturn_t pcf50633_irq(int irq, void *data)
110380c09f6SLars-Peter Clausen {
111380c09f6SLars-Peter Clausen 	struct pcf50633 *pcf = data;
112380c09f6SLars-Peter Clausen 	int ret, i, j;
113380c09f6SLars-Peter Clausen 	u8 pcf_int[5], chgstat;
114380c09f6SLars-Peter Clausen 
115380c09f6SLars-Peter Clausen 	/* Read the 5 INT regs in one transaction */
116380c09f6SLars-Peter Clausen 	ret = pcf50633_read_block(pcf, PCF50633_REG_INT1,
117380c09f6SLars-Peter Clausen 						ARRAY_SIZE(pcf_int), pcf_int);
118380c09f6SLars-Peter Clausen 	if (ret != ARRAY_SIZE(pcf_int)) {
119380c09f6SLars-Peter Clausen 		dev_err(pcf->dev, "Error reading INT registers\n");
120380c09f6SLars-Peter Clausen 
121380c09f6SLars-Peter Clausen 		/*
122380c09f6SLars-Peter Clausen 		 * If this doesn't ACK the interrupt to the chip, we'll be
123380c09f6SLars-Peter Clausen 		 * called once again as we're level triggered.
124380c09f6SLars-Peter Clausen 		 */
125380c09f6SLars-Peter Clausen 		goto out;
126380c09f6SLars-Peter Clausen 	}
127380c09f6SLars-Peter Clausen 
128380c09f6SLars-Peter Clausen 	/* defeat 8s death from lowsys on A5 */
129380c09f6SLars-Peter Clausen 	pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN,  0x04);
130380c09f6SLars-Peter Clausen 
131380c09f6SLars-Peter Clausen 	/* We immediately read the usb and adapter status. We thus make sure
132380c09f6SLars-Peter Clausen 	 * only of USBINS/USBREM IRQ handlers are called */
133380c09f6SLars-Peter Clausen 	if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
134380c09f6SLars-Peter Clausen 		chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
135380c09f6SLars-Peter Clausen 		if (chgstat & (0x3 << 4))
136380c09f6SLars-Peter Clausen 			pcf_int[0] &= ~PCF50633_INT1_USBREM;
137380c09f6SLars-Peter Clausen 		else
138380c09f6SLars-Peter Clausen 			pcf_int[0] &= ~PCF50633_INT1_USBINS;
139380c09f6SLars-Peter Clausen 	}
140380c09f6SLars-Peter Clausen 
141380c09f6SLars-Peter Clausen 	/* Make sure only one of ADPINS or ADPREM is set */
142380c09f6SLars-Peter Clausen 	if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) {
143380c09f6SLars-Peter Clausen 		chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
144380c09f6SLars-Peter Clausen 		if (chgstat & (0x3 << 4))
145380c09f6SLars-Peter Clausen 			pcf_int[0] &= ~PCF50633_INT1_ADPREM;
146380c09f6SLars-Peter Clausen 		else
147380c09f6SLars-Peter Clausen 			pcf_int[0] &= ~PCF50633_INT1_ADPINS;
148380c09f6SLars-Peter Clausen 	}
149380c09f6SLars-Peter Clausen 
150380c09f6SLars-Peter Clausen 	dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x "
151380c09f6SLars-Peter Clausen 			"INT4=0x%02x INT5=0x%02x\n", pcf_int[0],
152380c09f6SLars-Peter Clausen 			pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]);
153380c09f6SLars-Peter Clausen 
154380c09f6SLars-Peter Clausen 	/* Some revisions of the chip don't have a 8s standby mode on
155380c09f6SLars-Peter Clausen 	 * ONKEY1S press. We try to manually do it in such cases. */
156380c09f6SLars-Peter Clausen 	if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) {
157380c09f6SLars-Peter Clausen 		dev_info(pcf->dev, "ONKEY1S held for %d secs\n",
158380c09f6SLars-Peter Clausen 							pcf->onkey1s_held);
159380c09f6SLars-Peter Clausen 		if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT)
160380c09f6SLars-Peter Clausen 			if (pcf->pdata->force_shutdown)
161380c09f6SLars-Peter Clausen 				pcf->pdata->force_shutdown(pcf);
162380c09f6SLars-Peter Clausen 	}
163380c09f6SLars-Peter Clausen 
164380c09f6SLars-Peter Clausen 	if (pcf_int[2] & PCF50633_INT3_ONKEY1S) {
165380c09f6SLars-Peter Clausen 		dev_info(pcf->dev, "ONKEY1S held\n");
166380c09f6SLars-Peter Clausen 		pcf->onkey1s_held = 1 ;
167380c09f6SLars-Peter Clausen 
168380c09f6SLars-Peter Clausen 		/* Unmask IRQ_SECOND */
169380c09f6SLars-Peter Clausen 		pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M,
170380c09f6SLars-Peter Clausen 						PCF50633_INT1_SECOND);
171380c09f6SLars-Peter Clausen 
172380c09f6SLars-Peter Clausen 		/* Unmask IRQ_ONKEYR */
173380c09f6SLars-Peter Clausen 		pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M,
174380c09f6SLars-Peter Clausen 						PCF50633_INT2_ONKEYR);
175380c09f6SLars-Peter Clausen 	}
176380c09f6SLars-Peter Clausen 
177380c09f6SLars-Peter Clausen 	if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) {
178380c09f6SLars-Peter Clausen 		pcf->onkey1s_held = 0;
179380c09f6SLars-Peter Clausen 
180380c09f6SLars-Peter Clausen 		/* Mask SECOND and ONKEYR interrupts */
181380c09f6SLars-Peter Clausen 		if (pcf->mask_regs[0] & PCF50633_INT1_SECOND)
182380c09f6SLars-Peter Clausen 			pcf50633_reg_set_bit_mask(pcf,
183380c09f6SLars-Peter Clausen 					PCF50633_REG_INT1M,
184380c09f6SLars-Peter Clausen 					PCF50633_INT1_SECOND,
185380c09f6SLars-Peter Clausen 					PCF50633_INT1_SECOND);
186380c09f6SLars-Peter Clausen 
187380c09f6SLars-Peter Clausen 		if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR)
188380c09f6SLars-Peter Clausen 			pcf50633_reg_set_bit_mask(pcf,
189380c09f6SLars-Peter Clausen 					PCF50633_REG_INT2M,
190380c09f6SLars-Peter Clausen 					PCF50633_INT2_ONKEYR,
191380c09f6SLars-Peter Clausen 					PCF50633_INT2_ONKEYR);
192380c09f6SLars-Peter Clausen 	}
193380c09f6SLars-Peter Clausen 
194380c09f6SLars-Peter Clausen 	/* Have we just resumed ? */
195380c09f6SLars-Peter Clausen 	if (pcf->is_suspended) {
196380c09f6SLars-Peter Clausen 		pcf->is_suspended = 0;
197380c09f6SLars-Peter Clausen 
198380c09f6SLars-Peter Clausen 		/* Set the resume reason filtering out non resumers */
199380c09f6SLars-Peter Clausen 		for (i = 0; i < ARRAY_SIZE(pcf_int); i++)
200380c09f6SLars-Peter Clausen 			pcf->resume_reason[i] = pcf_int[i] &
201380c09f6SLars-Peter Clausen 						pcf->pdata->resumers[i];
202380c09f6SLars-Peter Clausen 
203380c09f6SLars-Peter Clausen 		/* Make sure we don't pass on any ONKEY events to
204380c09f6SLars-Peter Clausen 		 * userspace now */
205380c09f6SLars-Peter Clausen 		pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF);
206380c09f6SLars-Peter Clausen 	}
207380c09f6SLars-Peter Clausen 
208380c09f6SLars-Peter Clausen 	for (i = 0; i < ARRAY_SIZE(pcf_int); i++) {
209380c09f6SLars-Peter Clausen 		/* Unset masked interrupts */
210380c09f6SLars-Peter Clausen 		pcf_int[i] &= ~pcf->mask_regs[i];
211380c09f6SLars-Peter Clausen 
212380c09f6SLars-Peter Clausen 		for (j = 0; j < 8 ; j++)
213380c09f6SLars-Peter Clausen 			if (pcf_int[i] & (1 << j))
214380c09f6SLars-Peter Clausen 				pcf50633_irq_call_handler(pcf, (i * 8) + j);
215380c09f6SLars-Peter Clausen 	}
216380c09f6SLars-Peter Clausen 
217380c09f6SLars-Peter Clausen out:
218380c09f6SLars-Peter Clausen 	return IRQ_HANDLED;
219380c09f6SLars-Peter Clausen }
220380c09f6SLars-Peter Clausen 
221380c09f6SLars-Peter Clausen #ifdef CONFIG_PM
222380c09f6SLars-Peter Clausen 
223380c09f6SLars-Peter Clausen int pcf50633_irq_suspend(struct pcf50633 *pcf)
224380c09f6SLars-Peter Clausen {
225380c09f6SLars-Peter Clausen 	int ret;
226380c09f6SLars-Peter Clausen 	int i;
227380c09f6SLars-Peter Clausen 	u8 res[5];
228380c09f6SLars-Peter Clausen 
229380c09f6SLars-Peter Clausen 
230380c09f6SLars-Peter Clausen 	/* Make sure our interrupt handlers are not called
231380c09f6SLars-Peter Clausen 	 * henceforth */
232380c09f6SLars-Peter Clausen 	disable_irq(pcf->irq);
233380c09f6SLars-Peter Clausen 
234380c09f6SLars-Peter Clausen 	/* Save the masks */
235380c09f6SLars-Peter Clausen 	ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M,
236380c09f6SLars-Peter Clausen 				ARRAY_SIZE(pcf->suspend_irq_masks),
237380c09f6SLars-Peter Clausen 					pcf->suspend_irq_masks);
238380c09f6SLars-Peter Clausen 	if (ret < 0) {
239380c09f6SLars-Peter Clausen 		dev_err(pcf->dev, "error saving irq masks\n");
240380c09f6SLars-Peter Clausen 		goto out;
241380c09f6SLars-Peter Clausen 	}
242380c09f6SLars-Peter Clausen 
243380c09f6SLars-Peter Clausen 	/* Write wakeup irq masks */
244380c09f6SLars-Peter Clausen 	for (i = 0; i < ARRAY_SIZE(res); i++)
245380c09f6SLars-Peter Clausen 		res[i] = ~pcf->pdata->resumers[i];
246380c09f6SLars-Peter Clausen 
247380c09f6SLars-Peter Clausen 	ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
248380c09f6SLars-Peter Clausen 					ARRAY_SIZE(res), &res[0]);
249380c09f6SLars-Peter Clausen 	if (ret < 0) {
250380c09f6SLars-Peter Clausen 		dev_err(pcf->dev, "error writing wakeup irq masks\n");
251380c09f6SLars-Peter Clausen 		goto out;
252380c09f6SLars-Peter Clausen 	}
253380c09f6SLars-Peter Clausen 
254380c09f6SLars-Peter Clausen 	pcf->is_suspended = 1;
255380c09f6SLars-Peter Clausen 
256380c09f6SLars-Peter Clausen out:
257380c09f6SLars-Peter Clausen 	return ret;
258380c09f6SLars-Peter Clausen }
259380c09f6SLars-Peter Clausen 
260380c09f6SLars-Peter Clausen int pcf50633_irq_resume(struct pcf50633 *pcf)
261380c09f6SLars-Peter Clausen {
262380c09f6SLars-Peter Clausen 	int ret;
263380c09f6SLars-Peter Clausen 
264380c09f6SLars-Peter Clausen 	/* Write the saved mask registers */
265380c09f6SLars-Peter Clausen 	ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
266380c09f6SLars-Peter Clausen 				ARRAY_SIZE(pcf->suspend_irq_masks),
267380c09f6SLars-Peter Clausen 					pcf->suspend_irq_masks);
268380c09f6SLars-Peter Clausen 	if (ret < 0)
269380c09f6SLars-Peter Clausen 		dev_err(pcf->dev, "Error restoring saved suspend masks\n");
270380c09f6SLars-Peter Clausen 
271380c09f6SLars-Peter Clausen 	enable_irq(pcf->irq);
272380c09f6SLars-Peter Clausen 
273380c09f6SLars-Peter Clausen 	return ret;
274380c09f6SLars-Peter Clausen }
275380c09f6SLars-Peter Clausen 
276380c09f6SLars-Peter Clausen #endif
277380c09f6SLars-Peter Clausen 
278380c09f6SLars-Peter Clausen int pcf50633_irq_init(struct pcf50633 *pcf, int irq)
279380c09f6SLars-Peter Clausen {
280380c09f6SLars-Peter Clausen 	int ret;
281380c09f6SLars-Peter Clausen 
282380c09f6SLars-Peter Clausen 	pcf->irq = irq;
283380c09f6SLars-Peter Clausen 
284380c09f6SLars-Peter Clausen 	/* Enable all interrupts except RTC SECOND */
285380c09f6SLars-Peter Clausen 	pcf->mask_regs[0] = 0x80;
286380c09f6SLars-Peter Clausen 	pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]);
287380c09f6SLars-Peter Clausen 	pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00);
288380c09f6SLars-Peter Clausen 	pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00);
289380c09f6SLars-Peter Clausen 	pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
290380c09f6SLars-Peter Clausen 	pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
291380c09f6SLars-Peter Clausen 
292380c09f6SLars-Peter Clausen 	ret = request_threaded_irq(irq, NULL, pcf50633_irq,
293380c09f6SLars-Peter Clausen 					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
294380c09f6SLars-Peter Clausen 					"pcf50633", pcf);
295380c09f6SLars-Peter Clausen 
296380c09f6SLars-Peter Clausen 	if (ret)
297380c09f6SLars-Peter Clausen 		dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
298380c09f6SLars-Peter Clausen 
299380c09f6SLars-Peter Clausen 	if (enable_irq_wake(irq) < 0)
300380c09f6SLars-Peter Clausen 		dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
301380c09f6SLars-Peter Clausen 			"in this hardware revision", irq);
302380c09f6SLars-Peter Clausen 
303380c09f6SLars-Peter Clausen 	return ret;
304380c09f6SLars-Peter Clausen }
305380c09f6SLars-Peter Clausen 
306380c09f6SLars-Peter Clausen void pcf50633_irq_free(struct pcf50633 *pcf)
307380c09f6SLars-Peter Clausen {
308380c09f6SLars-Peter Clausen 	free_irq(pcf->irq, pcf);
309380c09f6SLars-Peter Clausen }
310