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