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