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