diff options
Diffstat (limited to 'drivers/mfd/m4sensorhub-irq.c')
| -rw-r--r-- | drivers/mfd/m4sensorhub-irq.c | 845 |
1 files changed, 845 insertions, 0 deletions
diff --git a/drivers/mfd/m4sensorhub-irq.c b/drivers/mfd/m4sensorhub-irq.c new file mode 100644 index 00000000000..41c8378c142 --- /dev/null +++ b/drivers/mfd/m4sensorhub-irq.c @@ -0,0 +1,845 @@ +/* + * 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 <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mutex.h> +#include <linux/wakelock.h> +#include <linux/i2c.h> +#include <linux/slab.h> + +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +#include <linux/m4sensorhub.h> + +#ifdef CONFIG_PM_DEEPSLEEP +#include <linux/suspend.h> +#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); + } + } +} |