/* * Copyright (C) 2009 - 2010, Motorola, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307, USA */ #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PM_DEEPSLEEP #include #endif #define LONG_KEYPRESS_DURATION 4 /* in seconds */ #define NUM_INT_REGS 5 #define NUM_INTS_PER_REG 16 #define CPCAP_INT1_VALID_BITS 0xFFFB #define CPCAP_INT2_VALID_BITS 0xFFFF #define CPCAP_INT3_VALID_BITS 0xFFFF #define CPCAP_INT4_VALID_BITS 0x03FF #define CPCAP_INT5_VALID_BITS 0xFFFF struct cpcap_event_handler { void (*func)(enum cpcap_irqs, void *); void *data; }; struct cpcap_irq_info { uint8_t registered; uint8_t enabled; uint32_t count; }; struct cpcap_irqdata { struct mutex lock; struct work_struct work; struct workqueue_struct *workqueue; struct cpcap_device *cpcap; struct cpcap_event_handler event_handler[CPCAP_IRQ__NUM]; struct cpcap_irq_info irq_info[CPCAP_IRQ__NUM]; struct wake_lock wake_lock; }; #define EVENT_MASK(event) (1 << ((event) % NUM_INTS_PER_REG)) enum pwrkey_states { PWRKEY_RELEASE, /* Power key released state. */ PWRKEY_PRESS, /* Power key pressed state. */ PWRKEY_UNKNOWN, /* Unknown power key state. */ }; static irqreturn_t event_isr(int irq, void *data) { struct cpcap_irqdata *irq_data = data; disable_irq_nosync(irq); wake_lock(&irq_data->wake_lock); queue_work(irq_data->workqueue, &irq_data->work); return IRQ_HANDLED; } static unsigned short get_int_reg(enum cpcap_irqs event) { unsigned short ret; if ((event) >= CPCAP_IRQ_INT5_INDEX) ret = CPCAP_REG_MI1; else if ((event) >= CPCAP_IRQ_INT4_INDEX) ret = CPCAP_REG_INT4; else if ((event) >= CPCAP_IRQ_INT3_INDEX) ret = CPCAP_REG_INT3; else if ((event) >= CPCAP_IRQ_INT2_INDEX) ret = CPCAP_REG_INT2; else ret = CPCAP_REG_INT1; return ret; } static unsigned short get_mask_reg(enum cpcap_irqs event) { unsigned short ret; if (event >= CPCAP_IRQ_INT5_INDEX) ret = CPCAP_REG_MIM1; else if (event >= CPCAP_IRQ_INT4_INDEX) ret = CPCAP_REG_INTM4; else if (event >= CPCAP_IRQ_INT3_INDEX) ret = CPCAP_REG_INTM3; else if (event >= CPCAP_IRQ_INT2_INDEX) ret = CPCAP_REG_INTM2; else ret = CPCAP_REG_INTM1; return ret; } static unsigned short get_sense_reg(enum cpcap_irqs event) { unsigned short ret; if (event >= CPCAP_IRQ_INT5_INDEX) ret = CPCAP_REG_MI2; else if (event >= CPCAP_IRQ_INT4_INDEX) ret = CPCAP_REG_INTS4; else if (event >= CPCAP_IRQ_INT3_INDEX) ret = CPCAP_REG_INTS3; else if (event >= CPCAP_IRQ_INT2_INDEX) ret = CPCAP_REG_INTS2; else ret = CPCAP_REG_INTS1; return ret; } void cpcap_irq_mask_all(struct cpcap_device *cpcap) { int i; static const struct { unsigned short mask_reg; unsigned short valid; } int_reg[NUM_INT_REGS] = { {CPCAP_REG_INTM1, CPCAP_INT1_VALID_BITS}, {CPCAP_REG_INTM2, CPCAP_INT2_VALID_BITS}, {CPCAP_REG_INTM3, CPCAP_INT3_VALID_BITS}, {CPCAP_REG_INTM4, CPCAP_INT4_VALID_BITS}, {CPCAP_REG_MIM1, CPCAP_INT5_VALID_BITS} }; for (i = 0; i < NUM_INT_REGS; i++) { cpcap_regacc_write(cpcap, int_reg[i].mask_reg, int_reg[i].valid, int_reg[i].valid); } } struct pwrkey_data { struct cpcap_device *cpcap; enum pwrkey_states state; struct wake_lock wake_lock; struct delayed_work pwrkey_work; int power_double_pressed; #ifdef CONFIG_PM_DEEPSLEEP struct hrtimer longPress_timer; int expired; #endif }; #ifdef CONFIG_PM_DBG_DRV static struct cpcap_irq_pm_dbg { unsigned short en_ints[NUM_INT_REGS]; unsigned char suspend; unsigned char wakeup; } pm_dbg_info; #endif /* CONFIG_PM_DBG_DRV */ #ifdef CONFIG_PM_DEEPSLEEP static enum hrtimer_restart longPress_timer_callback(struct hrtimer *timer) { struct pwrkey_data *pwrkey_data = container_of(timer, struct pwrkey_data, longPress_timer); struct cpcap_device *cpcap = pwrkey_data->cpcap; enum pwrkey_states new_state = PWRKEY_PRESS; if (wake_lock_active(&pwrkey_data->wake_lock)) wake_unlock(&pwrkey_data->wake_lock); wake_lock_timeout(&pwrkey_data->wake_lock, 20); /* long timer expired without being cancelled so send long press keydown event */ pwrkey_data->expired = 1; cpcap_broadcast_key_event(cpcap, KEY_SENDFILE, new_state); pwrkey_data->state = new_state; return HRTIMER_NORESTART; } #endif static void pwrkey_work_func(struct work_struct *work) { struct pwrkey_data *pwrkey_data = container_of(work, struct pwrkey_data, pwrkey_work.work); struct cpcap_device *cpcap = pwrkey_data->cpcap; if (wake_lock_active(&pwrkey_data->wake_lock)) wake_unlock(&pwrkey_data->wake_lock); wake_lock_timeout(&pwrkey_data->wake_lock, 20); if (pwrkey_data->state == PWRKEY_RELEASE) { /* keyup was detected before keydown was sent, so send keydown first */ cpcap_broadcast_key_event(cpcap, KEY_END, PWRKEY_PRESS); } /* Send detected state (keyup/keydown) */ cpcap_broadcast_key_event(cpcap, KEY_END, pwrkey_data->state); } static void pwrkey_handler(enum cpcap_irqs irq, void *data) { struct pwrkey_data *pwrkey_data = data; enum pwrkey_states new_state, last_state = pwrkey_data->state; struct cpcap_device *cpcap = pwrkey_data->cpcap; new_state = (enum pwrkey_states) cpcap_irq_sense(cpcap, irq, 0); /* First do long keypress detection */ if (new_state == PWRKEY_RELEASE) { #ifdef CONFIG_PM_DEEPSLEEP /* Got a keyup so cancel 2 second timer */ hrtimer_cancel(&pwrkey_data->longPress_timer); /* If longpress keydown was previously sent, then send the long press keyup */ if (pwrkey_data->expired == 1) { pwrkey_data->expired = 0; #endif cpcap_broadcast_key_event(cpcap, KEY_SENDFILE, new_state); pwrkey_data->state = new_state; #ifdef CONFIG_PM_DEEPSLEEP } #endif } else if (new_state == PWRKEY_PRESS) { #ifdef CONFIG_PM_DEEPSLEEP /* Got a keydown so start long keypress timer */ pwrkey_data->expired = 0; hrtimer_start(&pwrkey_data->longPress_timer, ktime_set(LONG_KEYPRESS_DURATION, 0), HRTIMER_MODE_REL); #endif wake_lock_timeout(&pwrkey_data->wake_lock, (LONG_KEYPRESS_DURATION*HZ)+5); } /* Now do normal powerkey detection (in addition to long press) */ if ((new_state < PWRKEY_UNKNOWN) && (new_state != last_state)) { if (new_state == PWRKEY_PRESS) { if (wake_lock_active(&pwrkey_data->wake_lock)) wake_unlock(&pwrkey_data->wake_lock); if (delayed_work_pending(&pwrkey_data->pwrkey_work)) { /* If 600ms delayed work exists and we got a keydown, then a doublepress has occured */ cancel_delayed_work_sync(&pwrkey_data-> \ pwrkey_work); wake_lock_timeout(&pwrkey_data->wake_lock, 20); cpcap_broadcast_key_event(cpcap, KEY_POWER_DOUBLE, new_state); pwrkey_data->power_double_pressed = 1; } else { /* If no delayed work was pending and we got a keydown, then start 600ms delayed work */ wake_lock(&pwrkey_data->wake_lock); schedule_delayed_work(&pwrkey_data->pwrkey_work, msecs_to_jiffies(600)); } } else { /* Got a keyup. If we previously sent a doublepress keydown, then send a doublepress keyup now */ if (pwrkey_data->power_double_pressed) { if (wake_lock_active(&pwrkey_data->wake_lock)) wake_unlock(&pwrkey_data->wake_lock); wake_lock_timeout(&pwrkey_data->wake_lock, 20); cpcap_broadcast_key_event(cpcap, KEY_POWER_DOUBLE, new_state); pwrkey_data->power_double_pressed = 0; /* If the 600ms delayed work is done and we got a keyup, then send the keyup now */ } else if (!delayed_work_pending(&pwrkey_data-> \ pwrkey_work)) { if (wake_lock_active(&pwrkey_data->wake_lock)) wake_unlock(&pwrkey_data->wake_lock); wake_lock_timeout(&pwrkey_data->wake_lock, 20); cpcap_broadcast_key_event(cpcap, KEY_END, new_state); } /* If we got a keyup while 600ms delayed work is still pending, then do nothing now and let the delayed work handler handle this */ } pwrkey_data->state = new_state; } cpcap_irq_unmask(cpcap, CPCAP_IRQ_ON); } static int pwrkey_init(struct cpcap_device *cpcap) { struct pwrkey_data *data = kmalloc(sizeof(struct pwrkey_data), GFP_KERNEL); int retval; if (!data) return -ENOMEM; data->cpcap = cpcap; data->state = PWRKEY_RELEASE; data->power_double_pressed = 0; retval = cpcap_irq_register(cpcap, CPCAP_IRQ_ON, pwrkey_handler, data); if (retval) kfree(data); wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "pwrkey"); #ifdef CONFIG_PM_DEEPSLEEP hrtimer_init(&(data->longPress_timer), CLOCK_MONOTONIC, HRTIMER_MODE_REL); (data->longPress_timer).function = longPress_timer_callback; #endif INIT_DELAYED_WORK(&data->pwrkey_work, pwrkey_work_func); return retval; } static void pwrkey_remove(struct cpcap_device *cpcap) { struct pwrkey_data *data; cpcap_irq_get_data(cpcap, CPCAP_IRQ_ON, (void **)&data); if (!data) return; cancel_delayed_work_sync(&data->pwrkey_work); cpcap_irq_free(cpcap, CPCAP_IRQ_ON); wake_lock_destroy(&data->wake_lock); kfree(data); } static int int_read_and_clear(struct cpcap_device *cpcap, unsigned short status_reg, unsigned short mask_reg, unsigned short valid_mask, unsigned short *en) { unsigned short ireg_val, mreg_val; int ret; ret = cpcap_regacc_read(cpcap, status_reg, &ireg_val); if (ret) return ret; ret = cpcap_regacc_read(cpcap, mask_reg, &mreg_val); if (ret) return ret; *en |= ireg_val & ~mreg_val; *en &= valid_mask; ret = cpcap_regacc_write(cpcap, mask_reg, *en, *en); if (ret) return ret; ret = cpcap_regacc_write(cpcap, status_reg, *en, *en); if (ret) return ret; return 0; } static void irq_work_func(struct work_struct *work) { int retval = 0; unsigned short en_ints[NUM_INT_REGS]; int i; struct cpcap_irqdata *data; struct cpcap_device *cpcap; struct spi_device *spi; struct cpcap_platform_data *pdata; unsigned int irq_gpio; static const struct { unsigned short status_reg; unsigned short mask_reg; unsigned short valid; } int_reg[NUM_INT_REGS] = { {CPCAP_REG_INT1, CPCAP_REG_INTM1, CPCAP_INT1_VALID_BITS}, {CPCAP_REG_INT2, CPCAP_REG_INTM2, CPCAP_INT2_VALID_BITS}, {CPCAP_REG_INT3, CPCAP_REG_INTM3, CPCAP_INT3_VALID_BITS}, {CPCAP_REG_INT4, CPCAP_REG_INTM4, CPCAP_INT4_VALID_BITS}, {CPCAP_REG_MI1, CPCAP_REG_MIM1, CPCAP_INT5_VALID_BITS} }; for (i = 0; i < NUM_INT_REGS; ++i) en_ints[i] = 0; data = container_of(work, struct cpcap_irqdata, work); cpcap = data->cpcap; spi = cpcap->spi; pdata = (struct cpcap_platform_data *) spi->controller_data; irq_gpio = pdata->irq_gpio; while (gpio_get_value(irq_gpio)) { for (i = 0; i < NUM_INT_REGS; ++i) { retval = int_read_and_clear(cpcap, int_reg[i].status_reg, int_reg[i].mask_reg, int_reg[i].valid, &en_ints[i]); if (retval < 0) { dev_err(&cpcap->spi->dev, "Error reading interrupts\n"); break; } } } enable_irq(spi->irq); #ifdef CONFIG_PM_DBG_DRV if ((pm_dbg_info.suspend != 0) && (pm_dbg_info.wakeup == 0)) { for (i = 0; i < NUM_INT_REGS; ++i) pm_dbg_info.en_ints[i] = en_ints[i]; pm_dbg_info.wakeup = 1; } #endif /* CONFIG_PM_DBG_DRV */ /* lock protects event handlers and data */ mutex_lock(&data->lock); for (i = 0; i < NUM_INT_REGS; ++i) { unsigned char index; while (en_ints[i] > 0) { struct cpcap_event_handler *event_handler; /* find the first set bit */ index = (unsigned char)(ffs(en_ints[i]) - 1); if (index >= CPCAP_IRQ__NUM) goto error; /* clear the bit */ en_ints[i] &= ~(1 << index); /* find the event that occurred */ index += CPCAP_IRQ__START + (i * NUM_INTS_PER_REG); if (index >= CPCAP_IRQ__NUM) goto error; event_handler = &data->event_handler[index]; if (event_handler->func) event_handler->func(index, event_handler->data); data->irq_info[index].count++; } } error: mutex_unlock(&data->lock); wake_unlock(&data->wake_lock); } #ifdef CONFIG_DEBUG_FS static int cpcap_dbg_irq_show(struct seq_file *s, void *data) { static const char *irq_name[] = { [CPCAP_IRQ_HSCLK] = "HSCLK", [CPCAP_IRQ_PRIMAC] = "PRIMAC", [CPCAP_IRQ_SECMAC] = "SECMAC", [CPCAP_IRQ_LOWBPL] = "LOWBPL", [CPCAP_IRQ_SEC2PRI] = "SEC2PRI", [CPCAP_IRQ_LOWBPH] = "LOWBPH", [CPCAP_IRQ_EOL] = "EOL", [CPCAP_IRQ_TS] = "TS", [CPCAP_IRQ_ADCDONE] = "ADCDONE", [CPCAP_IRQ_HS] = "HS", [CPCAP_IRQ_MB2] = "MB2", [CPCAP_IRQ_VBUSOV] = "VBUSOV", [CPCAP_IRQ_RVRS_CHRG] = "RVRS_CHRG", [CPCAP_IRQ_CHRG_DET] = "CHRG_DET", [CPCAP_IRQ_IDFLOAT] = "IDFLOAT", [CPCAP_IRQ_IDGND] = "IDGND", [CPCAP_IRQ_SE1] = "SE1", [CPCAP_IRQ_SESSEND] = "SESSEND", [CPCAP_IRQ_SESSVLD] = "SESSVLD", [CPCAP_IRQ_VBUSVLD] = "VBUSVLD", [CPCAP_IRQ_CHRG_CURR1] = "CHRG_CURR1", [CPCAP_IRQ_CHRG_CURR2] = "CHRG_CURR2", [CPCAP_IRQ_RVRS_MODE] = "RVRS_MODE", [CPCAP_IRQ_ON] = "ON", [CPCAP_IRQ_ON2] = "ON2", [CPCAP_IRQ_CLK] = "CLK", [CPCAP_IRQ_1HZ] = "1HZ", [CPCAP_IRQ_PTT] = "PTT", [CPCAP_IRQ_SE0CONN] = "SE0CONN", [CPCAP_IRQ_CHRG_SE1B] = "CHRG_SE1B", [CPCAP_IRQ_UART_ECHO_OVERRUN] = "UART_ECHO_OVERRUN", [CPCAP_IRQ_EXTMEMHD] = "EXTMEMHD", [CPCAP_IRQ_WARM] = "WARM", [CPCAP_IRQ_SYSRSTR] = "SYSRSTR", [CPCAP_IRQ_SOFTRST] = "SOFTRST", [CPCAP_IRQ_DIEPWRDWN] = "DIEPWRDWN", [CPCAP_IRQ_DIETEMPH] = "DIETEMPH", [CPCAP_IRQ_PC] = "PC", [CPCAP_IRQ_OFLOWSW] = "OFLOWSW", [CPCAP_IRQ_TODA] = "TODA", [CPCAP_IRQ_OPT_SEL_DTCH] = "OPT_SEL_DTCH", [CPCAP_IRQ_OPT_SEL_STATE] = "OPT_SEL_STATE", [CPCAP_IRQ_ONEWIRE1] = "ONEWIRE1", [CPCAP_IRQ_ONEWIRE2] = "ONEWIRE2", [CPCAP_IRQ_ONEWIRE3] = "ONEWIRE3", [CPCAP_IRQ_UCRESET] = "UCRESET", [CPCAP_IRQ_PWRGOOD] = "PWRGOOD", [CPCAP_IRQ_USBDPLLCLK] = "USBDPLLCLK", [CPCAP_IRQ_DPI] = "DPI", [CPCAP_IRQ_DMI] = "DMI", [CPCAP_IRQ_UCBUSY] = "UCBUSY", [CPCAP_IRQ_GCAI_CURR1] = "GCAI_CURR1", [CPCAP_IRQ_GCAI_CURR2] = "GCAI_CURR2", [CPCAP_IRQ_SB_MAX_RETRANSMIT_ERR] = "SB_MAX_RETRANSMIT_ERR", [CPCAP_IRQ_BATTDETB] = "BATTDETB", [CPCAP_IRQ_PRIHALT] = "PRIHALT", [CPCAP_IRQ_SECHALT] = "SECHALT", [CPCAP_IRQ_CC_CAL] = "CC_CAL", [CPCAP_IRQ_UC_PRIROMR] = "UC_PRIROMR", [CPCAP_IRQ_UC_PRIRAMW] = "UC_PRIRAMW", [CPCAP_IRQ_UC_PRIRAMR] = "UC_PRIRAMR", [CPCAP_IRQ_UC_USEROFF] = "UC_USEROFF", [CPCAP_IRQ_UC_PRIMACRO_4] = "UC_PRIMACRO_4", [CPCAP_IRQ_UC_PRIMACRO_5] = "UC_PRIMACRO_5", [CPCAP_IRQ_UC_PRIMACRO_6] = "UC_PRIMACRO_6", [CPCAP_IRQ_UC_PRIMACRO_7] = "UC_PRIMACRO_7", [CPCAP_IRQ_UC_PRIMACRO_8] = "UC_PRIMACRO_8", [CPCAP_IRQ_UC_PRIMACRO_9] = "UC_PRIMACRO_9", [CPCAP_IRQ_UC_PRIMACRO_10] = "UC_PRIMACRO_10", [CPCAP_IRQ_UC_PRIMACRO_11] = "UC_PRIMACRO_11", [CPCAP_IRQ_UC_PRIMACRO_12] = "UC_PRIMACRO_12", [CPCAP_IRQ_UC_PRIMACRO_13] = "UC_PRIMACRO_13", [CPCAP_IRQ_UC_PRIMACRO_14] = "UC_PRIMACRO_14", [CPCAP_IRQ_UC_PRIMACRO_15] = "UC_PRIMACRO_15", }; unsigned int i; struct cpcap_irqdata *irqdata = s->private; seq_printf(s, "%21s%9s%12s%10s\n", "CPCAP IRQ", "Enabled", "Registered", "Count"); for (i = 0; i < CPCAP_IRQ__NUM; i++) { if ((i <= CPCAP_IRQ_CC_CAL) || (i >= CPCAP_IRQ_UC_PRIROMR)) { seq_printf(s, "%21s%9d%12d%10d\n", irq_name[i], irqdata->irq_info[i].enabled, irqdata->irq_info[i].registered, irqdata->irq_info[i].count); } } return 0; } static int cpcap_dbg_irq_open(struct inode *inode, struct file *file) { return single_open(file, cpcap_dbg_irq_show, inode->i_private); } static const struct file_operations debug_fops = { .open = cpcap_dbg_irq_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #endif int cpcap_irq_init(struct cpcap_device *cpcap) { int retval; struct spi_device *spi = cpcap->spi; struct cpcap_irqdata *data; data = kzalloc(sizeof(struct cpcap_irqdata), GFP_KERNEL); if (!data) return -ENOMEM; cpcap_irq_mask_all(cpcap); data->workqueue = create_workqueue("cpcap_irq"); INIT_WORK(&data->work, irq_work_func); mutex_init(&data->lock); wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "cpcap-irq"); data->cpcap = cpcap; retval = request_irq(spi->irq, event_isr, IRQF_DISABLED | IRQF_TRIGGER_RISING, "cpcap-irq", data); if (retval) { printk(KERN_ERR "cpcap_irq: Failed requesting irq.\n"); goto error; } enable_irq_wake(spi->irq); cpcap->irqdata = data; retval = pwrkey_init(cpcap); if (retval) { printk(KERN_ERR "cpcap_irq: Failed initializing pwrkey.\n"); goto error; } #ifdef CONFIG_DEBUG_FS (void)debugfs_create_file("cpcap-irq", S_IRUGO, NULL, data, &debug_fops); #endif return 0; error: free_irq(spi->irq, data); kfree(data); printk(KERN_ERR "cpcap_irq: Error registering cpcap irq.\n"); return retval; } void cpcap_irq_shutdown(struct cpcap_device *cpcap) { struct spi_device *spi = cpcap->spi; struct cpcap_irqdata *data = cpcap->irqdata; pwrkey_remove(cpcap); cancel_work_sync(&data->work); destroy_workqueue(data->workqueue); free_irq(spi->irq, data); kfree(data); } int cpcap_irq_register(struct cpcap_device *cpcap, enum cpcap_irqs irq, void (*cb_func) (enum cpcap_irqs, void *), void *data) { struct cpcap_irqdata *irqdata = cpcap->irqdata; int retval = 0; if ((irq >= CPCAP_IRQ__NUM) || (!cb_func)) return -EINVAL; mutex_lock(&irqdata->lock); if (irqdata->event_handler[irq].func == NULL) { irqdata->irq_info[irq].registered = 1; cpcap_irq_unmask(cpcap, irq); irqdata->event_handler[irq].func = cb_func; irqdata->event_handler[irq].data = data; } else retval = -EPERM; mutex_unlock(&irqdata->lock); return retval; } EXPORT_SYMBOL_GPL(cpcap_irq_register); int cpcap_irq_free(struct cpcap_device *cpcap, enum cpcap_irqs irq) { struct cpcap_irqdata *data = cpcap->irqdata; int retval; if (irq >= CPCAP_IRQ__NUM) return -EINVAL; mutex_lock(&data->lock); retval = cpcap_irq_mask(cpcap, irq); data->event_handler[irq].func = NULL; data->event_handler[irq].data = NULL; data->irq_info[irq].registered = 0; mutex_unlock(&data->lock); return retval; } EXPORT_SYMBOL_GPL(cpcap_irq_free); int cpcap_irq_get_data(struct cpcap_device *cpcap, enum cpcap_irqs irq, void **data) { struct cpcap_irqdata *irqdata = cpcap->irqdata; if (irq >= CPCAP_IRQ__NUM) return -EINVAL; mutex_lock(&irqdata->lock); *data = irqdata->event_handler[irq].data; mutex_unlock(&irqdata->lock); return 0; } EXPORT_SYMBOL_GPL(cpcap_irq_get_data); int cpcap_irq_clear(struct cpcap_device *cpcap, enum cpcap_irqs irq) { int retval = -EINVAL; if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) { retval = cpcap_regacc_write(cpcap, get_int_reg(irq), EVENT_MASK(irq), EVENT_MASK(irq)); } return retval; } EXPORT_SYMBOL_GPL(cpcap_irq_clear); int cpcap_irq_mask(struct cpcap_device *cpcap, enum cpcap_irqs irq) { struct cpcap_irqdata *data = cpcap->irqdata; int retval = -EINVAL; if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) { data->irq_info[irq].enabled = 0; retval = cpcap_regacc_write(cpcap, get_mask_reg(irq), EVENT_MASK(irq), EVENT_MASK(irq)); } return retval; } EXPORT_SYMBOL_GPL(cpcap_irq_mask); int cpcap_irq_unmask(struct cpcap_device *cpcap, enum cpcap_irqs irq) { struct cpcap_irqdata *data = cpcap->irqdata; int retval = -EINVAL; if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) { data->irq_info[irq].enabled = 1; retval = cpcap_regacc_write(cpcap, get_mask_reg(irq), 0, EVENT_MASK(irq)); } return retval; } EXPORT_SYMBOL_GPL(cpcap_irq_unmask); int cpcap_irq_mask_get(struct cpcap_device *cpcap, enum cpcap_irqs irq) { struct cpcap_irqdata *data = cpcap->irqdata; int retval = -EINVAL; if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) return data->irq_info[irq].enabled; return retval; } EXPORT_SYMBOL_GPL(cpcap_irq_mask_get); int cpcap_irq_sense(struct cpcap_device *cpcap, enum cpcap_irqs irq, unsigned char clear) { unsigned short val; int retval; if (irq >= CPCAP_IRQ__NUM) return -EINVAL; retval = cpcap_regacc_read(cpcap, get_sense_reg(irq), &val); if (retval) return retval; if (clear) retval = cpcap_irq_clear(cpcap, irq); if (retval) return retval; return ((val & EVENT_MASK(irq)) != 0) ? 1 : 0; } EXPORT_SYMBOL_GPL(cpcap_irq_sense); #ifdef CONFIG_PM_DBG_DRV void cpcap_irq_pm_dbg_suspend(void) { pm_dbg_info.suspend = 1; pm_dbg_info.wakeup = 0; } void cpcap_irq_pm_dbg_resume(void) { pm_dbg_info.suspend = 0; if (pm_dbg_info.wakeup != 0) { printk(KERN_INFO "PM_DBG WAKEUP CPCAP IRQ = 0x%x.0x%x.0%x.0x%x.0x%x\n", pm_dbg_info.en_ints[0], pm_dbg_info.en_ints[1], pm_dbg_info.en_ints[2], pm_dbg_info.en_ints[3], pm_dbg_info.en_ints[4]); } } #endif /* CONFIG_PM_DBG_DRV */