blob: 20e39e9893f22cb14133e8c241b557780e5f5aa1 [file] [log] [blame]
/*
* 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 2
#define NUM_INTS_PER_REG 32
#define EVENT_MASK(event) (1 << ((event >= M4SH_WAKEIRQ__START) ? \
(event - M4SH_WAKEIRQ__START) : event))
#define DBG_BUF_LINE_LEN 80
/* --------------- Global Declarations -------------- */
/* ------------ Local Function Prototypes ----------- */
static unsigned short get_enable_reg(enum m4sensorhub_irqs event);
static irqreturn_t m4sensorhub_wake_event_threaded(int irq, void *devid);
static irqreturn_t m4sensorhub_nowake_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 * const irq_name[] = {
[M4SH_NOWAKEIRQ_ACCEL] = "NOWAKE_ACCEL",
[M4SH_NOWAKEIRQ_GYRO] = "NOWAKE_GYRO",
[M4SH_NOWAKEIRQ_COMPASS] = "NOWAKE_COMPASS",
[M4SH_NOWAKEIRQ_FUSION] = "NOWAKE_FUSION",
[M4SH_NOWAKEIRQ_PRESSURE] = "NOWAKE_PRESSURE",
[M4SH_NOWAKEIRQ_ADS] = "NOWAKE_ADS",
[M4SH_NOWAKEIRQ_PPG] = "NOWAKE_PPG",
[M4SH_NOWAKEIRQ_HEARTRATE] = "NOWAKE_HEARTRATE",
[M4SH_NOWAKEIRQ_PEDOMETER] = "NOWAKE_PEDOMETER",
[M4SH_NOWAKEIRQ_ALS] = "NOWAKE_ALS",
[M4SH_NOWAKEIRQ_ACTIVITY_CHANGE] = "NOWAKE_ACTIVITY_CHANGE",
[M4SH_NOWAKEIRQ_BATCH] = "NOWAKE_BATCH",
[M4SH_NOWAKEIRQ_SIMPLEFS] = "NOWAKE_SIMPLEFS",
[M4SH_WAKEIRQ_STILL] = "WAKE_STILL",
[M4SH_WAKEIRQ_MOTION] = "WAKE_MOTION",
[M4SH_WAKEIRQ_GESTURE] = "WAKE_GESTURE",
[M4SH_WAKEIRQ_PASSIVE] = "WAKE_PASSIVE",
[M4SH_WAKEIRQ_AP_ALARM_EXPIRED] = "WAKE_ALARM_EXPIRED",
[M4SH_WAKEIRQ_M4_READY] = "WAKE_M4_READY",
[M4SH_WAKEIRQ_SIGNIFICANT_MOTION] = "WAKE_SIGNIFICANT_MOTION",
};
/* -------------- 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 no_wake_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;
uint32_t valid_bits;
} int_registers[NUM_INT_REGS] = {
{M4SH_REG_GENERAL_NOWAKEINTSTATUS,
M4SH_REG_GENERAL_NOWAKEINTENABLE,
0xFFFFFFFF},
{M4SH_REG_GENERAL_WAKEINTSTATUS,
M4SH_REG_GENERAL_WAKEINTENABLE,
0xFFFFFFFF},
};
static irqreturn_t wake_event_isr(int irq, void *data)
{
struct m4sensorhub_irqdata *irq_data = data;
/*
* This wakelock is held to prevent the kernel from going
* to sleep before the event_thread for this irq gets to run
* and so this is released when the event_thread finishes execution
*/
wake_lock(&irq_data->wake_lock);
return IRQ_WAKE_THREAD;
}
static irqreturn_t nowake_event_isr(int irq, void *data)
{
struct m4sensorhub_irqdata *irq_data = data;
/*
* This wakelock is held to prevent the kernel from going
* to sleep before the event_thread for this irq gets to run
* and so this is released when the event_thread finishes execution
*/
wake_lock(&irq_data->no_wake_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;
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->no_wake_wake_lock, WAKE_LOCK_SUSPEND,
"m4sensorhub-irq no wake");
wake_lock_init(&data->tm_wake_lock, WAKE_LOCK_SUSPEND,
"m4sensorhub-timed-irq");
/* request wake irq */
retval = request_threaded_irq(m4sensorhub->hwconfig.wakeirq,
wake_event_isr, m4sensorhub_wake_event_threaded,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"m4sensorhub-wakeirq", data);
if (retval) {
KDEBUG(M4SH_ERROR, "%s: Failed requesting wakeirq = %d\n",
__func__, retval);
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_wakeirq;
}
/* request nowake irq */
retval = request_threaded_irq(m4sensorhub->hwconfig.nowakeirq,
nowake_event_isr, m4sensorhub_nowake_event_threaded,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"m4sensorhub-nowakeirq", data);
if (retval) {
KDEBUG(M4SH_ERROR, "%s: Failed requesting nowakeirq = %d\n",
__func__, retval);
goto err_free_nowakeirq;
}
#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;
err_free_nowakeirq:
free_irq(m4sensorhub->hwconfig.nowakeirq, data);
#ifdef CONFIG_DEBUG_FS
err_disa_irq:
#endif
disable_irq_wake(i2c->irq);
err_free_wakeirq:
free_irq(i2c->irq, data);
err_destroy_wq:
wake_lock_destroy(&data->wake_lock);
wake_lock_destroy(&data->no_wake_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);
free_irq(m4sensorhub->hwconfig.nowakeirq, 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->no_wake_wake_lock))
wake_unlock(&data->no_wake_wake_lock);
wake_lock_destroy(&data->no_wake_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;
uint32_t mask;
uint32_t value = 0;
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;
}
mask = EVENT_MASK(irq);
retval = m4sensorhub_reg_write(m4sensorhub, get_enable_reg(irq),
(unsigned char *)&value, (unsigned char *)&mask);
if (retval < 0) {
KDEBUG(M4SH_ERROR, "%s: Register write failed\n", __func__);
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;
uint32_t mask;
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;
}
mask = EVENT_MASK(irq);
retval = m4sensorhub_reg_write(m4sensorhub, get_enable_reg(irq),
(unsigned char *)&mask, (unsigned char *)&mask);
if (retval < 0) {
KDEBUG(M4SH_ERROR, "%s: Register write failed\n", __func__);
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;
uint32_t value = 0;
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;
}
retval = m4sensorhub_reg_write(m4sensorhub,
M4SH_REG_GENERAL_NOWAKEINTENABLE,
(unsigned char *)&value, m4sh_no_mask);
if (retval < 0) {
KDEBUG(M4SH_ERROR, "%s: Failed to disable no wake irq\n",
__func__);
goto m4sensorhub_irq_disable_all_fail;
}
retval = m4sensorhub_reg_write(m4sensorhub,
M4SH_REG_GENERAL_WAKEINTENABLE,
(unsigned char *)&value, m4sh_no_mask);
if (retval < 0) {
KDEBUG(M4SH_ERROR, "%s: Failed to disable wake irq\n",
__func__);
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_NOWAKEIRQ__START) &&
(event < M4SH_NOWAKEIRQ__MAX))
ret = M4SH_REG_GENERAL_NOWAKEINTENABLE;
else if ((event >= M4SH_WAKEIRQ__START) && (event < M4SH_WAKEIRQ__MAX))
ret = M4SH_REG_GENERAL_WAKEINTENABLE;
else
ret = M4SH_REG__INVALID;
return ret;
}
static void m4sensorhub_print_irq_sources(uint32_t en_ints, char nowake)
{
int i;
unsigned char index;
if (m4sensorhub_debug < M4SH_NOTICE)
goto error;
KDEBUG(M4SH_NOTICE, "%s: M4 %s IRQ register: 0x%08X", __func__,
(nowake ? "NOWAKE" : "WAKE"), en_ints);
/* Decode the bits */
for (i = 0; ((i < sizeof(en_ints)) && (en_ints != 0)); i++) {
if (en_ints & (0x01 << i)) {
if (nowake)
index = i;
else
index = i + M4SH_WAKEIRQ__START;
if (index >= M4SH_IRQ__NUM)
KDEBUG(M4SH_NOTICE, "%s: IRQ index is %d\n",
__func__, index);
if (index <= ARRAY_SIZE(irq_name))
KDEBUG(M4SH_NOTICE, "%s: %s\n",
__func__, irq_name[index]);
else
KDEBUG(M4SH_NOTICE, "%s: %s IRQ %d\n",
__func__,
(nowake ? "NOWAKE" : "WAKE"), index);
}
/* Clear the bit that we finished processing */
en_ints &= ~(0x01 << i);
}
error:
return;
}
static irqreturn_t m4sensorhub_wake_event_threaded(int irq, void *devid)
{
uint32_t en_ints = 0, value = 0, is_irq_set = 0;
int index;
struct m4sensorhub_irqdata *data = devid;
struct m4sensorhub_data *m4sensorhub;
struct i2c_client *i2c;
m4sensorhub = data->m4sensorhub;
i2c = m4sensorhub->i2c_client;
/* There is an issue where nowake irq seems to run constantly without
* allowing wake irq to run when both are set. We want to disable
* nowakeirq while processing wake irq since wake irq should have
* priority. */
disable_irq(m4sensorhub->hwconfig.nowakeirq);
if (m4sensorhub->irq_dbg.suspend == 1) {
/* Delay IRQ until I2C bus is powered */
KDEBUG(M4SH_INFO, "%s: IRQ while suspended--%s\n", __func__,
"will re-enable on resume");
disable_irq_nosync(m4sensorhub->hwconfig.wakeirq);
m4sensorhub->irq_dbg.pending_wakeirq = true;
m4sensorhub->irq_dbg.print_wakeirq_flags = true;
goto error;
}
/* M4 is expected to clear these bits when read */
if (m4sensorhub_reg_read(m4sensorhub,
M4SH_REG_GENERAL_WAKEINTSTATUS,
(unsigned char *)&value) < 0) {
dev_err(&m4sensorhub->i2c_client->dev,
"Error reading wake int status register\n");
goto error;
}
en_ints = 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.print_wakeirq_flags) {
m4sensorhub_print_irq_sources(en_ints, 0);
m4sensorhub->irq_dbg.print_wakeirq_flags = false;
}
while (en_ints > 0) {
struct m4sensorhub_event_handler *event_handler;
/* find the first set bit */
index = (ffs(en_ints) - 1);
if (index >= M4SH_WAKEIRQ__MAX)
goto error;
/* clear the bit */
en_ints &= ~(1 << index);
/* find the event that occurred */
index += M4SH_WAKEIRQ__START;
if (index >= M4SH_WAKEIRQ__MAX)
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:
enable_irq(m4sensorhub->hwconfig.nowakeirq);
wake_unlock(&data->wake_lock);
return IRQ_HANDLED;
}
static irqreturn_t m4sensorhub_nowake_event_threaded(int irq, void *devid)
{
uint32_t en_ints = 0, value = 0, is_irq_set = 0;
int index;
struct m4sensorhub_irqdata *data = devid;
struct m4sensorhub_data *m4sensorhub;
struct i2c_client *i2c;
m4sensorhub = data->m4sensorhub;
i2c = m4sensorhub->i2c_client;
/* M4 is expected to clear these bits when read */
if (m4sensorhub_reg_read(m4sensorhub, M4SH_REG_GENERAL_NOWAKEINTSTATUS,
(unsigned char *)&value) < 0) {
dev_err(&m4sensorhub->i2c_client->dev,
"Error reading nowake INT status register\n");
goto error;
}
en_ints = value;
is_irq_set |= value;
if (!is_irq_set) {
KDEBUG(M4SH_ERROR, "%s: Got nowake int with no bits set\n",
__func__);
goto error;
}
if (m4sensorhub->irq_dbg.suspend == 1)
m4sensorhub_print_irq_sources(en_ints, 1);
while (en_ints > 0) {
struct m4sensorhub_event_handler *event_handler;
/* find the first set bit */
index = (ffs(en_ints) - 1);
if (index >= M4SH_NOWAKEIRQ__MAX)
goto error;
/* clear the bit */
en_ints &= ~(1 << index);
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);
}
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->no_wake_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;
uint32_t nowake_en_ints = 0, wake_en_ints = 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;
if (i < M4SH_NOWAKEIRQ__MAX)
nowake_en_ints |= EVENT_MASK(i);
else
wake_en_ints |= EVENT_MASK(i);
}
mutex_unlock(&((struct m4sensorhub_irqdata *)data)->lock);
KDEBUG(M4SH_INFO, "%s: Resetting NOWAKEINT-%x\n", __func__,
nowake_en_ints);
if (m4sensorhub_reg_write(m4sensorhub,
M4SH_REG_GENERAL_NOWAKEINTENABLE,
(unsigned char *)&nowake_en_ints,
m4sh_no_mask) < 0) {
KDEBUG(M4SH_ERROR, "%s: Failed to reset NOWAKEINT\n",
__func__);
}
KDEBUG(M4SH_INFO, "%s: Resetting WAKEINT-%x\n", __func__,
wake_en_ints);
if (m4sensorhub_reg_write(m4sensorhub,
M4SH_REG_GENERAL_WAKEINTENABLE,
(unsigned char *)&wake_en_ints,
m4sh_no_mask) < 0) {
KDEBUG(M4SH_ERROR, "%s: Failed to reset WAKEINT\n", __func__);
}
}