/* * Copyright (C) 2012-2014 Motorola, Inc. * * 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 #ifdef CONFIG_PM_DEEPSLEEP #include #endif #define NUM_INT_REGS 3 #define NUM_INTS_PER_REG 8 #define NUM_INTS_LAST_REG (((M4SH_IRQ__NUM-1)%NUM_INTS_PER_REG)+1) #define INTR_VALID_BITS(n) (unsigned char)((1 << (n)) - 1) #define EVENT_MASK(event) (1 << ((event) % NUM_INTS_PER_REG)) #define DBG_BUF_LINE_LEN 80 /* --------------- Global Declarations -------------- */ /* ------------ Local Function Prototypes ----------- */ static unsigned short get_enable_reg(enum m4sensorhub_irqs event); static irqreturn_t event_threaded(int irq, void *devid); #ifdef CONFIG_DEBUG_FS static int m4sensorhub_dbg_irq_open(struct inode *inode, struct file *file); #endif static void m4sensorhub_irq_restore(struct m4sensorhub_data *m4sensorhub, void *data); /* ---------------- Local Declarations -------------- */ static const char *irq_name[] = { [M4SH_IRQ_TMP_DATA_READY] = "TMP_DATA_READY", [M4SH_IRQ_PRESSURE_DATA_READY] = "PRES_DATA_READY", [M4SH_IRQ_GYRO_DATA_READY] = "GYRO_DATA_READY", [M4SH_IRQ_PEDOMETER_DATA_READY] = "PEDO_DATA_READY", [M4SH_IRQ_COMPASS_DATA_READY] = "COMPASS_DATA_READY", [M4SH_IRQ_FUSION_DATA_READY] = "FUSION_DATA_READY", [M4SH_IRQ_ACCEL_DATA_READY] = "ACCEL_DATA_READY", [M4SH_IRQ_GESTURE_DETECTED] = "GESTURE_DETECTED", [M4SH_IRQ_STILL_DETECTED] = "STILL_DETECTED", [M4SH_IRQ_MOTION_DETECTED] = "MOTION_DETECTED", [M4SH_IRQ_ACTIVITY_CHANGE] = "ACTIVITY_CHANGE", [M4SH_IRQ_DLCMD_RESP_READY] = "DLCMD_RESP_READY", [M4SH_IRQ_MIC_DATA_READY] = "MIC_DATA_READY", [M4SH_IRQ_WRIST_READY] = "WRIST_READY", [M4SH_IRQ_PASSIVE_BUFFER_FULL] = "PASSIVE_BUFFER_FULL", [M4SH_IRQ_LIGHTSENSOR_DATA_READY] = "ALS_DATA_READY", [M4SH_IRQ_HRSENSOR_DATA_READY] = "HRSENSOR_DATA_READY", [M4SH_IRQ_AP_ALARM_EXPIRED] = "AP_ALARM_EXPIRED", [M4SH_IRQ_HEARTRATE_DATA_READY] = "HR_DATA_READY", }; /* -------------- Local Data Structures ------------- */ struct m4sensorhub_event_handler { void (*func)(enum m4sensorhub_irqs, void *); void *data; }; struct m4sensorhub_irq_info { uint8_t registered; uint8_t enabled; uint32_t ena_fired; uint32_t disa_fired; uint8_t tm_wakelock; }; struct m4sensorhub_irqdata { struct mutex lock; /* lock event handlers and data */ struct m4sensorhub_data *m4sensorhub; struct m4sensorhub_event_handler event_handler[M4SH_IRQ__NUM]; struct m4sensorhub_irq_info irq_info[M4SH_IRQ__NUM]; struct wake_lock wake_lock; struct wake_lock tm_wake_lock; /* timeout wakelock */ #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; #endif }; static const struct { enum m4sensorhub_reg status_reg; enum m4sensorhub_reg enable_reg; unsigned char valid_bits; } int_registers[NUM_INT_REGS] = { {M4SH_REG_GENERAL_INTERRUPT0STATUS, M4SH_REG_GENERAL_INTERRUPT0ENABLE, INTR_VALID_BITS(NUM_INTS_PER_REG)}, {M4SH_REG_GENERAL_INTERRUPT1STATUS, M4SH_REG_GENERAL_INTERRUPT1ENABLE, INTR_VALID_BITS(NUM_INTS_PER_REG)}, {M4SH_REG_GENERAL_INTERRUPT2STATUS, M4SH_REG_GENERAL_INTERRUPT2ENABLE, INTR_VALID_BITS(NUM_INTS_LAST_REG)}, }; static irqreturn_t event_isr(int irq, void *data) { struct m4sensorhub_irqdata *irq_data = data; wake_lock(&irq_data->wake_lock); return IRQ_WAKE_THREAD; } #ifdef CONFIG_DEBUG_FS static const struct file_operations debug_fops = { .open = m4sensorhub_dbg_irq_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #endif /* -------------- Global Functions ----------------- */ /* m4sensorhub_irq_init() Intialize M4 sensor hub IRQ subsystem Returns 0 on success. Returns negative error code on failure m4sensorhub - pointer to the main m4sensorhub data struct */ int m4sensorhub_irq_init(struct m4sensorhub_data *m4sensorhub) { int retval = 0; struct i2c_client *i2c = NULL; struct m4sensorhub_irqdata *data = NULL; if (m4sensorhub == NULL) { KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); retval = -ENODATA; goto done; } else if (m4sensorhub->i2c_client == NULL) { KDEBUG(M4SH_ERROR, "%s: I2C client is missing\n", __func__); retval = -ENODATA; goto done; } i2c = m4sensorhub->i2c_client; data = kzalloc(sizeof(struct m4sensorhub_irqdata), GFP_KERNEL); if (data == NULL) { KDEBUG(M4SH_ERROR, "%s: Failed to allocat irqdata\n", __func__); retval = -ENOMEM; goto done; } mutex_init(&data->lock); data->m4sensorhub = m4sensorhub; m4sensorhub->irqdata = data; KDEBUG(M4SH_INFO, "%s: %u IRQs with valid_bits %02X%02X%02X\n", __func__, M4SH_IRQ__NUM, int_registers[2].valid_bits, int_registers[1].valid_bits, int_registers[0].valid_bits); retval = m4sensorhub_irq_disable_all(m4sensorhub); if (retval < 0) { KDEBUG(M4SH_ERROR, "%s: Failed disable all irqs\n", __func__); goto err_free; } wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "m4sensorhub-irq"); wake_lock_init(&data->tm_wake_lock, WAKE_LOCK_SUSPEND, "m4sensorhub-timed-irq"); retval = request_threaded_irq(i2c->irq, event_isr, event_threaded, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "m4sensorhub-irq", data); if (retval) { KDEBUG(M4SH_ERROR, "%s: Failed requesting irq.\n", __func__); goto err_destroy_wq; } retval = enable_irq_wake(i2c->irq); if (retval) { KDEBUG(M4SH_ERROR, "%s: Failed enabling irq wake.\n", __func__); goto err_free_irq; } #ifdef CONFIG_DEBUG_FS data->debugfs = debugfs_create_file("m4sensorhub-irq", S_IRUGO, NULL, data, &debug_fops); if (data->debugfs == NULL) { KDEBUG(M4SH_ERROR, "%s: Error creating debufs\n", __func__); retval = -EINVAL; goto err_disa_irq; } #endif m4sensorhub_panic_register(m4sensorhub, PANICHDL_IRQ_RESTORE, m4sensorhub_irq_restore, data); KDEBUG(M4SH_INFO, "%s: m4sensorhub IRQ subsystem initialized\n", __func__); retval = 0; goto done; #ifdef CONFIG_DEBUG_FS err_disa_irq: #endif disable_irq_wake(i2c->irq); err_free_irq: free_irq(i2c->irq, data); err_destroy_wq: wake_lock_destroy(&data->wake_lock); wake_lock_destroy(&data->tm_wake_lock); err_free: mutex_destroy(&data->lock); m4sensorhub->irqdata = NULL; data->m4sensorhub = NULL; kfree(data); done: return retval; } EXPORT_SYMBOL_GPL(m4sensorhub_irq_init); /* m4sensorhub_irq_shutdown() Shutdown the M4 sensor hub IRQ subsystem m4sensorhub - pointer to the main m4sensorhub data struct */ void m4sensorhub_irq_shutdown(struct m4sensorhub_data *m4sensorhub) { struct i2c_client *i2c = NULL; struct m4sensorhub_irqdata *data = NULL; KDEBUG(M4SH_INFO, "%s: shutdown m4sensorhub IRQ subsystem\n", __func__); if (m4sensorhub == NULL) { KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); return; } else if (m4sensorhub->i2c_client) { KDEBUG(M4SH_ERROR, "%s: I2C client is NULL\n", __func__); return; } else if (m4sensorhub->irqdata) { KDEBUG(M4SH_ERROR, "%s: IRQ data is NULL\n", __func__); return; } i2c = m4sensorhub->i2c_client; data = m4sensorhub->irqdata; m4sensorhub_panic_unregister(m4sensorhub, PANICHDL_IRQ_RESTORE); #ifdef CONFIG_DEBUG_FS debugfs_remove(data->debugfs); #endif disable_irq_wake(i2c->irq); free_irq(i2c->irq, data); m4sensorhub->irqdata = NULL; data->m4sensorhub = NULL; if (wake_lock_active(&data->wake_lock)) wake_unlock(&data->wake_lock); wake_lock_destroy(&data->wake_lock); if (wake_lock_active(&data->tm_wake_lock)) wake_unlock(&data->tm_wake_lock); wake_lock_destroy(&data->tm_wake_lock); if (mutex_is_locked(&data->lock)) mutex_unlock(&data->lock); mutex_destroy(&data->lock); kfree(data); } EXPORT_SYMBOL_GPL(m4sensorhub_irq_shutdown); /* m4sensorhub_irq_register() Register an interupt handler in the M4 Sensor Hub IRQ subsystem. This does not enable the IRQ, that needs to be done by caller with m4sensorhub_irq_enable() Returns 0 on success. Returns negative error code on failure m4sensorhub - pointer to the main m4sensorhub data struct irq - M4 Sensor Hub interupt to resiter for cb_func - IRQ handler function to execute on inturrupt data - pointer to data for IRQ handler function */ int m4sensorhub_irq_register(struct m4sensorhub_data *m4sensorhub, enum m4sensorhub_irqs irq, void (*cb_func) (enum m4sensorhub_irqs, void *), void *data, uint8_t enable_timed_wakelock) { struct m4sensorhub_irqdata *irqdata = NULL; int retval = 0; if (m4sensorhub == NULL) { KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); retval = -ENODATA; goto m4sensorhub_irq_register_exit; } else if (irq >= M4SH_IRQ__NUM) { KDEBUG(M4SH_ERROR, "%s: IRQ %d exceeds maximum %d\n", __func__, irq, M4SH_IRQ__NUM); retval = -EINVAL; goto m4sensorhub_irq_register_exit; } else if (cb_func == NULL) { KDEBUG(M4SH_ERROR, "%s: Handler missing for IRQ %d\n", __func__, irq); retval = -ENOSYS; goto m4sensorhub_irq_register_exit; } irqdata = m4sensorhub->irqdata; if (irqdata == NULL) { KDEBUG(M4SH_ERROR, "%s: Caller irqdata is NULL (irq=%d)\n", __func__, irq); retval = -ENODATA; goto m4sensorhub_irq_register_exit; } mutex_lock(&irqdata->lock); if (irqdata->event_handler[irq].func == NULL) { irqdata->irq_info[irq].registered = 1; irqdata->irq_info[irq].tm_wakelock = enable_timed_wakelock; irqdata->event_handler[irq].func = cb_func; irqdata->event_handler[irq].data = data; KDEBUG(M4SH_NOTICE, "%s: %s IRQ registered\n", __func__, irq_name[irq]); } else { KDEBUG(M4SH_ERROR, "%s: %s IRQ registration failed\n", __func__, irq_name[irq]); retval = -EPERM; goto m4sensorhub_irq_register_fail; } m4sensorhub_irq_register_fail: mutex_unlock(&irqdata->lock); m4sensorhub_irq_register_exit: return retval; } EXPORT_SYMBOL_GPL(m4sensorhub_irq_register); /* m4sensorhub_irq_unregister() Unregister an interupt handler in the M4 Sensor Hub IRQ subsystem Returns 0 on success. Returns negative error code on failure m4sensorhub - pointer to the main m4sensorhub data struct irq - M4 Sensor Hub interupt to unresiter for */ int m4sensorhub_irq_unregister(struct m4sensorhub_data *m4sensorhub, enum m4sensorhub_irqs irq) { struct m4sensorhub_irqdata *data = NULL; int retval = 0; if (m4sensorhub == NULL) { KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); retval = -ENODATA; goto m4sensorhub_irq_unregister_exit; } else if (irq >= M4SH_IRQ__NUM) { KDEBUG(M4SH_ERROR, "%s: IRQ %d exceeds maximum %d\n", __func__, irq, M4SH_IRQ__NUM); retval = -ENODATA; goto m4sensorhub_irq_unregister_exit; } else if (m4sensorhub->irqdata == NULL) { KDEBUG(M4SH_ERROR, "%s: IRQ data is NULL\n", __func__); retval = -ENODATA; goto m4sensorhub_irq_unregister_exit; } data = m4sensorhub->irqdata; retval = m4sensorhub_irq_disable(m4sensorhub, irq); if (retval < 0) { KDEBUG(M4SH_ERROR, "%s: Failed to disable IRQ %d\n", __func__, irq); goto m4sensorhub_irq_unregister_exit; } mutex_lock(&data->lock); data->event_handler[irq].func = NULL; data->event_handler[irq].data = NULL; data->irq_info[irq].registered = 0; mutex_unlock(&data->lock); KDEBUG(M4SH_NOTICE, "%s: %s IRQ un-registered\n", __func__, irq_name[irq]); m4sensorhub_irq_unregister_exit: return retval; } EXPORT_SYMBOL_GPL(m4sensorhub_irq_unregister); /* m4sensorhub_irq_enable_get() Check if an IRQ is enabled Returns 1 if enabled, 0 if disabled. Returns negative error code on failure m4sensorhub - pointer to the main m4sensorhub data struct irq - M4 Sensor Hub interupt to check */ int m4sensorhub_irq_enable_get(struct m4sensorhub_data *m4sensorhub, enum m4sensorhub_irqs irq) { struct m4sensorhub_irqdata *data = NULL; int retval = -EINVAL; if (m4sensorhub == NULL) { KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); retval = -ENODATA; goto m4sensorhub_irq_enable_get_fail; } else if (irq >= M4SH_IRQ__NUM) { KDEBUG(M4SH_ERROR, "%s: IRQ %d exceeds maximum %d\n", __func__, irq, M4SH_IRQ__NUM); retval = -ENODATA; goto m4sensorhub_irq_enable_get_fail; } else if (m4sensorhub->irqdata == NULL) { KDEBUG(M4SH_ERROR, "%s: IRQ data is NULL\n", __func__); retval = -ENODATA; goto m4sensorhub_irq_enable_get_fail; } data = m4sensorhub->irqdata; retval = data->irq_info[irq].enabled; m4sensorhub_irq_enable_get_fail: return retval; } EXPORT_SYMBOL_GPL(m4sensorhub_irq_enable_get); /* m4sensorhub_irq_disable() Disable M4 Sensor Hub subsystem IRQ Returns 0 on success. Returns negative error code on failure m4sensorhub - pointer to the main m4sensorhub data struct irq - M4 Sensor Hub interupt to disable */ int m4sensorhub_irq_disable(struct m4sensorhub_data *m4sensorhub, enum m4sensorhub_irqs irq) { struct m4sensorhub_irqdata *data = NULL; int retval = -EINVAL; bool enabled = true; if (m4sensorhub == NULL) { KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); retval = -ENODATA; goto m4sensorhub_irq_disable_fail; } else if (irq >= M4SH_IRQ__NUM) { KDEBUG(M4SH_ERROR, "%s: IRQ %d exceeds maximum %d\n", __func__, irq, M4SH_IRQ__NUM); retval = -ENODATA; goto m4sensorhub_irq_disable_fail; } else if (m4sensorhub->irqdata == NULL) { KDEBUG(M4SH_ERROR, "%s: IRQ data is NULL\n", __func__); retval = -ENODATA; goto m4sensorhub_irq_disable_fail; } data = m4sensorhub->irqdata; mutex_lock(&data->lock); if (data->irq_info[irq].enabled == 0) enabled = false; mutex_unlock(&data->lock); /* Checking if the IRQ was previously disabled only to print an error */ if (!enabled) { KDEBUG(M4SH_INFO, "%s: IRQ %d is already disabled\n", __func__, irq); retval = 0; goto m4sensorhub_irq_disable_fail; } retval = m4sensorhub_reg_write_1byte(m4sensorhub, get_enable_reg(irq), 0, EVENT_MASK(irq)); if (retval < 0) { KDEBUG(M4SH_ERROR, "%s: Register write failed\n", __func__); goto m4sensorhub_irq_disable_fail; } else if (retval != 1) { KDEBUG(M4SH_ERROR, "%s: Wrote %d bytes instead of 1\n", __func__, retval); goto m4sensorhub_irq_disable_fail; } mutex_lock(&data->lock); data->irq_info[irq].enabled = 0; mutex_unlock(&data->lock); m4sensorhub_irq_disable_fail: return retval; } EXPORT_SYMBOL_GPL(m4sensorhub_irq_disable); /* m4sensorhub_irq_enable() Enable M4 Sensor Hub subsystem IRQ Returns 0 on success. Returns negative error code on failure m4sensorhub - pointer to the main m4sensorhub data struct irq - M4 Sensor Hub interupt to enable */ int m4sensorhub_irq_enable(struct m4sensorhub_data *m4sensorhub, enum m4sensorhub_irqs irq) { struct m4sensorhub_irqdata *data = NULL; int retval = -EINVAL; bool disabled = true; if (m4sensorhub == NULL) { KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); retval = -ENODATA; goto m4sensorhub_irq_enable_fail; } else if (irq >= M4SH_IRQ__NUM) { KDEBUG(M4SH_ERROR, "%s: IRQ %d exceeds maximum %d\n", __func__, irq, M4SH_IRQ__NUM); retval = -ENODATA; goto m4sensorhub_irq_enable_fail; } else if (m4sensorhub->irqdata == NULL) { KDEBUG(M4SH_ERROR, "%s: IRQ data is NULL\n", __func__); retval = -ENODATA; goto m4sensorhub_irq_enable_fail; } data = m4sensorhub->irqdata; mutex_lock(&data->lock); if (data->irq_info[irq].enabled == 1) disabled = false; mutex_unlock(&data->lock); /* Checking if the IRQ was previously enabled only to print an error */ if (!disabled) { KDEBUG(M4SH_INFO, "%s: IRQ %d is already enabled\n", __func__, irq); retval = 0; goto m4sensorhub_irq_enable_fail; } retval = m4sensorhub_reg_write_1byte(m4sensorhub, get_enable_reg(irq), EVENT_MASK(irq), EVENT_MASK(irq)); if (retval < 0) { KDEBUG(M4SH_ERROR, "%s: Register write failed\n", __func__); goto m4sensorhub_irq_enable_fail; } else if (retval != 1) { KDEBUG(M4SH_ERROR, "%s: Wrote %d bytes instead of 1\n", __func__, retval); goto m4sensorhub_irq_enable_fail; } mutex_lock(&data->lock); data->irq_info[irq].enabled = 1; mutex_unlock(&data->lock); m4sensorhub_irq_enable_fail: return retval; } EXPORT_SYMBOL_GPL(m4sensorhub_irq_enable); /* m4sensorhub_irq_disable_all() Disables all M4 IRQs (bypasses m4sensorhub_irq_disable()) */ int m4sensorhub_irq_disable_all(struct m4sensorhub_data *m4sensorhub) { int retval = 0; int i = 0; struct m4sensorhub_irqdata *data = NULL; if (m4sensorhub == NULL) { KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); retval = -ENODATA; goto m4sensorhub_irq_disable_all_fail; } else if (m4sensorhub->irqdata == NULL) { KDEBUG(M4SH_ERROR, "%s: IRQ data is NULL\n", __func__); retval = -ENODATA; goto m4sensorhub_irq_disable_all_fail; } for (i = 0; i < NUM_INT_REGS; i++) { retval = m4sensorhub_reg_write_1byte(m4sensorhub, int_registers[i].enable_reg, 0, int_registers[i].valid_bits); if (retval < 0) { KDEBUG(M4SH_ERROR, "%s: Failed to disable INT%d\n", __func__, i); goto m4sensorhub_irq_disable_all_fail; } else if (retval != 1) { KDEBUG(M4SH_ERROR, "%s: Wrote %d bytes instead of 1\n", __func__, retval); retval = -EINVAL; goto m4sensorhub_irq_disable_all_fail; } } data = m4sensorhub->irqdata; mutex_lock(&data->lock); for (i = 0; i < M4SH_IRQ__NUM; i++) data->irq_info[i].enabled = 0; mutex_unlock(&data->lock); m4sensorhub_irq_disable_all_fail: return retval; } EXPORT_SYMBOL_GPL(m4sensorhub_irq_disable_all); /* --------------- Local Functions ----------------- */ static unsigned short get_enable_reg(enum m4sensorhub_irqs event) { unsigned short ret; if ((event) >= M4SH_IRQ__NUM) ret = M4SH_REG__INVALID; else if ((event) >= M4SH_IRQ_INT2_INDEX) ret = M4SH_REG_GENERAL_INTERRUPT2ENABLE; else if ((event) >= M4SH_IRQ_INT1_INDEX) ret = M4SH_REG_GENERAL_INTERRUPT1ENABLE; else if ((event) >= M4SH_IRQ_INT0_INDEX) ret = M4SH_REG_GENERAL_INTERRUPT0ENABLE; else ret = M4SH_REG__INVALID; return ret; } static void m4sensorhub_print_irq_sources(unsigned short *en_ints) { char buffer[DBG_BUF_LINE_LEN]; int i; unsigned char index; unsigned short cur_ints; if (m4sensorhub_debug < M4SH_NOTICE) goto error; strcpy(buffer, "M4 IRQ Registers:"); for (i = 0; (i < NUM_INT_REGS) && (strlen(buffer) < DBG_BUF_LINE_LEN-5); ++i) { sprintf(&buffer[strlen(buffer)], " 0x%02x", en_ints[i]); } KDEBUG(M4SH_NOTICE, "%s: %s\n", __func__, buffer); /* Decode the bits */ KDEBUG(M4SH_NOTICE, "%s: M4 IRQ Sources:\n", __func__); for (i = 0; i < NUM_INT_REGS; ++i) { cur_ints = en_ints[i]; while (cur_ints > 0) { /* find the first set bit */ index = (unsigned char) (ffs(cur_ints) - 1); if (index >= M4SH_IRQ__NUM) { KDEBUG(M4SH_NOTICE, "%s: No set bits found\n", __func__); goto error; } /* clear the bit */ cur_ints &= ~(1 << index); /* find the event that occurred */ index += M4SH_IRQ__START + (i * NUM_INTS_PER_REG); if (index >= M4SH_IRQ__NUM) { KDEBUG(M4SH_NOTICE, "%s: IRQ index is %d\n", __func__, index); goto error; } if (index <= ARRAY_SIZE(irq_name)) KDEBUG(M4SH_NOTICE, "\t%s\n", irq_name[index]); else KDEBUG(M4SH_NOTICE, "\tIRQ %d\n", index); } } error: return; } static irqreturn_t event_threaded(int irq, void *devid) { unsigned short en_ints[NUM_INT_REGS] = { 0 }; int i; struct m4sensorhub_irqdata *data = devid; struct m4sensorhub_data *m4sensorhub; struct i2c_client *i2c; unsigned char value, is_irq_set = 0; m4sensorhub = data->m4sensorhub; i2c = m4sensorhub->i2c_client; for (i = 0; i < NUM_INT_REGS; ++i) { /* M4 is expected to clear these bits when read */ if (1 != m4sensorhub_reg_read(m4sensorhub, int_registers[i].status_reg, &value)) { dev_err(&m4sensorhub->i2c_client->dev, "Error reading INT%d\n", i); goto error; } en_ints[i] = value; is_irq_set |= value; } if (!is_irq_set) { /* Got the checkpoint to check if M4 panicked */ m4sensorhub_panic_process(m4sensorhub); goto error; } if (m4sensorhub->irq_dbg.suspend == 1) m4sensorhub_print_irq_sources(en_ints); for (i = 0; i < NUM_INT_REGS; ++i) { unsigned char index; while (en_ints[i] > 0) { struct m4sensorhub_event_handler *event_handler; /* find the first set bit */ index = (unsigned char)(ffs(en_ints[i]) - 1); if (index >= M4SH_IRQ__NUM) goto error; /* clear the bit */ en_ints[i] &= ~(1 << index); /* find the event that occurred */ index += M4SH_IRQ__START + (i * NUM_INTS_PER_REG); if (index >= M4SH_IRQ__NUM) goto error; if (data->irq_info[index].enabled) { event_handler = &data->event_handler[index]; if (event_handler && event_handler->func) { event_handler->func(index, event_handler->data); if (data->irq_info[index].tm_wakelock) { /* Hold a 500ms wakelock to let data get to apps */ wake_lock_timeout( &data->tm_wake_lock, 0.5 * HZ); } } mutex_lock(&data->lock); data->irq_info[index].ena_fired++; mutex_unlock(&data->lock); } else { mutex_lock(&data->lock); data->irq_info[index].disa_fired++; mutex_unlock(&data->lock); } } } error: wake_unlock(&data->wake_lock); return IRQ_HANDLED; } #ifdef CONFIG_DEBUG_FS static int m4sensorhub_dbg_irq_show(struct seq_file *s, void *data) { unsigned int i; struct m4sensorhub_irqdata *irqdata = s->private; seq_printf(s, "%21s%9s%12s%15s%16s\n", "M4SENSORHUB IRQ", "Enabled", "Registered", "Fired Enabled", "Fired Disabled"); for (i = 0; i < M4SH_IRQ__NUM; i++) { seq_printf(s, "%21s%9d%12d%15d%16d\n", irq_name[i], irqdata->irq_info[i].enabled, irqdata->irq_info[i].registered, irqdata->irq_info[i].ena_fired, irqdata->irq_info[i].disa_fired); } return 0; } static int m4sensorhub_dbg_irq_open(struct inode *inode, struct file *file) { return single_open(file, m4sensorhub_dbg_irq_show, inode->i_private); } #endif /* m4sensorhub_irq_restore() Callback Handler is called by Panic after M4 has been restarted */ static void m4sensorhub_irq_restore(struct m4sensorhub_data *m4sensorhub, void *data) { int i; unsigned short en_ints[NUM_INT_REGS] = {0}; mutex_lock(&((struct m4sensorhub_irqdata *)data)->lock); for (i = 0; i < M4SH_IRQ__NUM; i++) { if (!((struct m4sensorhub_irqdata *)data)->irq_info[i].enabled) continue; en_ints[i/NUM_INTS_PER_REG] |= EVENT_MASK(i); } mutex_unlock(&((struct m4sensorhub_irqdata *)data)->lock); for (i = 0; i < NUM_INT_REGS; i++) { KDEBUG(M4SH_INFO, "%s: Reseting INT%d-%02X\n", __func__, i, en_ints[i]); if (1 != m4sensorhub_reg_write_1byte(m4sensorhub, int_registers[i].enable_reg, en_ints[i], int_registers[i].valid_bits)) { KDEBUG(M4SH_ERROR, "%s: Failed reseting INT%d\n", __func__, i); } } }