18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* NXP PCF50633 Power Management Unit (PMU) driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * (C) 2006-2008 by Openmoko, Inc.
58c2ecf20Sopenharmony_ci * Author: Harald Welte <laforge@openmoko.org>
68c2ecf20Sopenharmony_ci * 	   Balaji Rao <balajirrao@openmoko.org>
78c2ecf20Sopenharmony_ci * All rights reserved.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/mutex.h>
138c2ecf20Sopenharmony_ci#include <linux/export.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/mfd/pcf50633/core.h>
178c2ecf20Sopenharmony_ci#include <linux/mfd/pcf50633/mbc.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ciint pcf50633_register_irq(struct pcf50633 *pcf, int irq,
208c2ecf20Sopenharmony_ci			void (*handler) (int, void *), void *data)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	if (irq < 0 || irq >= PCF50633_NUM_IRQ || !handler)
238c2ecf20Sopenharmony_ci		return -EINVAL;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	if (WARN_ON(pcf->irq_handler[irq].handler))
268c2ecf20Sopenharmony_ci		return -EBUSY;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	mutex_lock(&pcf->lock);
298c2ecf20Sopenharmony_ci	pcf->irq_handler[irq].handler = handler;
308c2ecf20Sopenharmony_ci	pcf->irq_handler[irq].data = data;
318c2ecf20Sopenharmony_ci	mutex_unlock(&pcf->lock);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	return 0;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pcf50633_register_irq);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ciint pcf50633_free_irq(struct pcf50633 *pcf, int irq)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	if (irq < 0 || irq >= PCF50633_NUM_IRQ)
408c2ecf20Sopenharmony_ci		return -EINVAL;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	mutex_lock(&pcf->lock);
438c2ecf20Sopenharmony_ci	pcf->irq_handler[irq].handler = NULL;
448c2ecf20Sopenharmony_ci	mutex_unlock(&pcf->lock);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	return 0;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pcf50633_free_irq);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	u8 reg, bit;
538c2ecf20Sopenharmony_ci	int idx;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	idx = irq >> 3;
568c2ecf20Sopenharmony_ci	reg = PCF50633_REG_INT1M + idx;
578c2ecf20Sopenharmony_ci	bit = 1 << (irq & 0x07);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	pcf50633_reg_set_bit_mask(pcf, reg, bit, mask ? bit : 0);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	mutex_lock(&pcf->lock);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (mask)
648c2ecf20Sopenharmony_ci		pcf->mask_regs[idx] |= bit;
658c2ecf20Sopenharmony_ci	else
668c2ecf20Sopenharmony_ci		pcf->mask_regs[idx] &= ~bit;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	mutex_unlock(&pcf->lock);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return 0;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ciint pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	dev_dbg(pcf->dev, "Masking IRQ %d\n", irq);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return __pcf50633_irq_mask_set(pcf, irq, 1);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pcf50633_irq_mask);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ciint pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return __pcf50633_irq_mask_set(pcf, irq, 0);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pcf50633_irq_unmask);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciint pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	u8 reg, bits;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	reg =  irq >> 3;
948c2ecf20Sopenharmony_ci	bits = 1 << (irq & 0x07);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return pcf->mask_regs[reg] & bits;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pcf50633_irq_mask_get);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	if (pcf->irq_handler[irq].handler)
1038c2ecf20Sopenharmony_ci		pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Maximum amount of time ONKEY is held before emergency action is taken */
1078c2ecf20Sopenharmony_ci#define PCF50633_ONKEY1S_TIMEOUT 8
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic irqreturn_t pcf50633_irq(int irq, void *data)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct pcf50633 *pcf = data;
1128c2ecf20Sopenharmony_ci	int ret, i, j;
1138c2ecf20Sopenharmony_ci	u8 pcf_int[5], chgstat;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* Read the 5 INT regs in one transaction */
1168c2ecf20Sopenharmony_ci	ret = pcf50633_read_block(pcf, PCF50633_REG_INT1,
1178c2ecf20Sopenharmony_ci						ARRAY_SIZE(pcf_int), pcf_int);
1188c2ecf20Sopenharmony_ci	if (ret != ARRAY_SIZE(pcf_int)) {
1198c2ecf20Sopenharmony_ci		dev_err(pcf->dev, "Error reading INT registers\n");
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		/*
1228c2ecf20Sopenharmony_ci		 * If this doesn't ACK the interrupt to the chip, we'll be
1238c2ecf20Sopenharmony_ci		 * called once again as we're level triggered.
1248c2ecf20Sopenharmony_ci		 */
1258c2ecf20Sopenharmony_ci		goto out;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/* defeat 8s death from lowsys on A5 */
1298c2ecf20Sopenharmony_ci	pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN,  0x04);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* We immediately read the usb and adapter status. We thus make sure
1328c2ecf20Sopenharmony_ci	 * only of USBINS/USBREM IRQ handlers are called */
1338c2ecf20Sopenharmony_ci	if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
1348c2ecf20Sopenharmony_ci		chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
1358c2ecf20Sopenharmony_ci		if (chgstat & (0x3 << 4))
1368c2ecf20Sopenharmony_ci			pcf_int[0] &= ~PCF50633_INT1_USBREM;
1378c2ecf20Sopenharmony_ci		else
1388c2ecf20Sopenharmony_ci			pcf_int[0] &= ~PCF50633_INT1_USBINS;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* Make sure only one of ADPINS or ADPREM is set */
1428c2ecf20Sopenharmony_ci	if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) {
1438c2ecf20Sopenharmony_ci		chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
1448c2ecf20Sopenharmony_ci		if (chgstat & (0x3 << 4))
1458c2ecf20Sopenharmony_ci			pcf_int[0] &= ~PCF50633_INT1_ADPREM;
1468c2ecf20Sopenharmony_ci		else
1478c2ecf20Sopenharmony_ci			pcf_int[0] &= ~PCF50633_INT1_ADPINS;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x "
1518c2ecf20Sopenharmony_ci			"INT4=0x%02x INT5=0x%02x\n", pcf_int[0],
1528c2ecf20Sopenharmony_ci			pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* Some revisions of the chip don't have a 8s standby mode on
1558c2ecf20Sopenharmony_ci	 * ONKEY1S press. We try to manually do it in such cases. */
1568c2ecf20Sopenharmony_ci	if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) {
1578c2ecf20Sopenharmony_ci		dev_info(pcf->dev, "ONKEY1S held for %d secs\n",
1588c2ecf20Sopenharmony_ci							pcf->onkey1s_held);
1598c2ecf20Sopenharmony_ci		if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT)
1608c2ecf20Sopenharmony_ci			if (pcf->pdata->force_shutdown)
1618c2ecf20Sopenharmony_ci				pcf->pdata->force_shutdown(pcf);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (pcf_int[2] & PCF50633_INT3_ONKEY1S) {
1658c2ecf20Sopenharmony_ci		dev_info(pcf->dev, "ONKEY1S held\n");
1668c2ecf20Sopenharmony_ci		pcf->onkey1s_held = 1 ;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		/* Unmask IRQ_SECOND */
1698c2ecf20Sopenharmony_ci		pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M,
1708c2ecf20Sopenharmony_ci						PCF50633_INT1_SECOND);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		/* Unmask IRQ_ONKEYR */
1738c2ecf20Sopenharmony_ci		pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M,
1748c2ecf20Sopenharmony_ci						PCF50633_INT2_ONKEYR);
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) {
1788c2ecf20Sopenharmony_ci		pcf->onkey1s_held = 0;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		/* Mask SECOND and ONKEYR interrupts */
1818c2ecf20Sopenharmony_ci		if (pcf->mask_regs[0] & PCF50633_INT1_SECOND)
1828c2ecf20Sopenharmony_ci			pcf50633_reg_set_bit_mask(pcf,
1838c2ecf20Sopenharmony_ci					PCF50633_REG_INT1M,
1848c2ecf20Sopenharmony_ci					PCF50633_INT1_SECOND,
1858c2ecf20Sopenharmony_ci					PCF50633_INT1_SECOND);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR)
1888c2ecf20Sopenharmony_ci			pcf50633_reg_set_bit_mask(pcf,
1898c2ecf20Sopenharmony_ci					PCF50633_REG_INT2M,
1908c2ecf20Sopenharmony_ci					PCF50633_INT2_ONKEYR,
1918c2ecf20Sopenharmony_ci					PCF50633_INT2_ONKEYR);
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* Have we just resumed ? */
1958c2ecf20Sopenharmony_ci	if (pcf->is_suspended) {
1968c2ecf20Sopenharmony_ci		pcf->is_suspended = 0;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		/* Set the resume reason filtering out non resumers */
1998c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(pcf_int); i++)
2008c2ecf20Sopenharmony_ci			pcf->resume_reason[i] = pcf_int[i] &
2018c2ecf20Sopenharmony_ci						pcf->pdata->resumers[i];
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		/* Make sure we don't pass on any ONKEY events to
2048c2ecf20Sopenharmony_ci		 * userspace now */
2058c2ecf20Sopenharmony_ci		pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF);
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pcf_int); i++) {
2098c2ecf20Sopenharmony_ci		/* Unset masked interrupts */
2108c2ecf20Sopenharmony_ci		pcf_int[i] &= ~pcf->mask_regs[i];
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		for (j = 0; j < 8 ; j++)
2138c2ecf20Sopenharmony_ci			if (pcf_int[i] & (1 << j))
2148c2ecf20Sopenharmony_ci				pcf50633_irq_call_handler(pcf, (i * 8) + j);
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ciout:
2188c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ciint pcf50633_irq_suspend(struct pcf50633 *pcf)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	int ret;
2268c2ecf20Sopenharmony_ci	int i;
2278c2ecf20Sopenharmony_ci	u8 res[5];
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* Make sure our interrupt handlers are not called
2318c2ecf20Sopenharmony_ci	 * henceforth */
2328c2ecf20Sopenharmony_ci	disable_irq(pcf->irq);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* Save the masks */
2358c2ecf20Sopenharmony_ci	ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M,
2368c2ecf20Sopenharmony_ci				ARRAY_SIZE(pcf->suspend_irq_masks),
2378c2ecf20Sopenharmony_ci					pcf->suspend_irq_masks);
2388c2ecf20Sopenharmony_ci	if (ret < 0) {
2398c2ecf20Sopenharmony_ci		dev_err(pcf->dev, "error saving irq masks\n");
2408c2ecf20Sopenharmony_ci		goto out;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/* Write wakeup irq masks */
2448c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(res); i++)
2458c2ecf20Sopenharmony_ci		res[i] = ~pcf->pdata->resumers[i];
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
2488c2ecf20Sopenharmony_ci					ARRAY_SIZE(res), &res[0]);
2498c2ecf20Sopenharmony_ci	if (ret < 0) {
2508c2ecf20Sopenharmony_ci		dev_err(pcf->dev, "error writing wakeup irq masks\n");
2518c2ecf20Sopenharmony_ci		goto out;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	pcf->is_suspended = 1;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ciout:
2578c2ecf20Sopenharmony_ci	return ret;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ciint pcf50633_irq_resume(struct pcf50633 *pcf)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	int ret;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* Write the saved mask registers */
2658c2ecf20Sopenharmony_ci	ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
2668c2ecf20Sopenharmony_ci				ARRAY_SIZE(pcf->suspend_irq_masks),
2678c2ecf20Sopenharmony_ci					pcf->suspend_irq_masks);
2688c2ecf20Sopenharmony_ci	if (ret < 0)
2698c2ecf20Sopenharmony_ci		dev_err(pcf->dev, "Error restoring saved suspend masks\n");
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	enable_irq(pcf->irq);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return ret;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci#endif
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ciint pcf50633_irq_init(struct pcf50633 *pcf, int irq)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	int ret;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	pcf->irq = irq;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/* Enable all interrupts except RTC SECOND */
2858c2ecf20Sopenharmony_ci	pcf->mask_regs[0] = 0x80;
2868c2ecf20Sopenharmony_ci	pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]);
2878c2ecf20Sopenharmony_ci	pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00);
2888c2ecf20Sopenharmony_ci	pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00);
2898c2ecf20Sopenharmony_ci	pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
2908c2ecf20Sopenharmony_ci	pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	ret = request_threaded_irq(irq, NULL, pcf50633_irq,
2938c2ecf20Sopenharmony_ci					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
2948c2ecf20Sopenharmony_ci					"pcf50633", pcf);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (ret)
2978c2ecf20Sopenharmony_ci		dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (enable_irq_wake(irq) < 0)
3008c2ecf20Sopenharmony_ci		dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
3018c2ecf20Sopenharmony_ci			"in this hardware revision", irq);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return ret;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_civoid pcf50633_irq_free(struct pcf50633 *pcf)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	free_irq(pcf->irq, pcf);
3098c2ecf20Sopenharmony_ci}
310