diff options
Diffstat (limited to 'drivers/mfd/m4sensorhub-panic.c')
| -rw-r--r-- | drivers/mfd/m4sensorhub-panic.c | 317 | 
1 files changed, 317 insertions, 0 deletions
diff --git a/drivers/mfd/m4sensorhub-panic.c b/drivers/mfd/m4sensorhub-panic.c new file mode 100644 index 00000000000..34ad6658453 --- /dev/null +++ b/drivers/mfd/m4sensorhub-panic.c @@ -0,0 +1,317 @@ +/* + * 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/module.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/m4sensorhub.h> +#include <linux/slab.h> + + +/* --------------- Global Declarations -------------- */ +#define PANIC_BANK              0xFF /* Reserved for Panic bank */ +#define PANIC_CMD_CHECK         0xCD /* Panic Handoff command */ +#define PANIC_RESP_CHECK        0xDeadBeef /* Panic Handoff Magic code */ + +/* ------------ Local Function Prototypes ----------- */ + +/* --------------- Local Declarations -------------- */ +static const char *callback_name[PANICHDL_MAX] = { +	[PANICHDL_DISPLAY_RESTORE] = "display_restore", +	[PANICHDL_IRQ_RESTORE] = "irq_restore", +	[PANICHDL_HEARTRATE_RESTORE] = "heartrate_restore", +	[PANICHDL_PASSIVE_RESTORE] = "passive_restore", +	[PANICHDL_ALS_RESTORE] = "als_restore", +	[PANICHDL_FUSION_RESTORE] = "fusion_restore", +	[PANICHDL_MPU9150_RESTORE] = "mpu9150_restore", +	[PANICHDL_PEDOMETER_RESTORE] = "pedometer_restore", +	[PANICHDL_EXTERN_RESTORE] = "extern_restore", +}; + +struct m4sensorhub_panic_callback { +	void (*callback)(struct m4sensorhub_data *, void *); +	void *data; +}; + +struct m4sensorhub_panicdata { +	struct mutex lock;  /* lock callback and data */ +	struct m4sensorhub_panic_callback funcs[PANICHDL_MAX]; +}; + +union panic_buf { +	struct _in { +		unsigned char bank; +		unsigned char cmd; +	} in; +	unsigned int data; +}; + +/* -------------- Local Data Structures ------------- */ + +/* -------------- Global Functions ----------------- */ + +/* m4sensorhub_panic_init() + +   Initialized panic private data structures. + +   Returns 0 on success or negative error code on failure + +     m4sensorhub - pointer to the main m4sensorhub data struct +*/ +int m4sensorhub_panic_init(struct m4sensorhub_data *m4sensorhub) +{ +	int retval = 0; +	struct m4sensorhub_panicdata *data; + +	if (m4sensorhub == NULL) { +		KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); +		retval = -ENODATA; +		goto m4sensorhub_panic_init_fail; +	} + +	if (m4sensorhub->panicdata != NULL) { +		KDEBUG(M4SH_ERROR, +			"%s: Trying to overwrite previous panic data\n", +			__func__); +		retval = -EPERM; +		goto m4sensorhub_panic_init_fail; +	} + +	data = kzalloc(sizeof(struct m4sensorhub_panicdata), GFP_KERNEL); +	if (data == NULL) { +		KDEBUG(M4SH_ERROR, "%s: Memory error in panic_init\n", +			__func__); +		retval = -ENOMEM; +		goto m4sensorhub_panic_init_fail; +	} + +	mutex_init(&data->lock); +	m4sensorhub->panicdata = data; + +m4sensorhub_panic_init_fail: +	return retval; +} +EXPORT_SYMBOL_GPL(m4sensorhub_panic_init); + +/* m4sensorhub_panic_shutdown() + +   Shutdown the M4 sensor hub Panic subsystem + +     m4sensorhub - pointer to the main m4sensorhub data struct +*/ +void m4sensorhub_panic_shutdown(struct m4sensorhub_data *m4sensorhub) +{ +	struct m4sensorhub_panicdata *data = NULL; + +	if (m4sensorhub == NULL) { +		KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); +		return; +	} + +	data = m4sensorhub->panicdata; +	if (data != NULL) { +		m4sensorhub->panicdata = NULL; +		if (mutex_is_locked(&data->lock)) +			mutex_unlock(&data->lock); +		mutex_destroy(&data->lock); +		/* +		 * Callback and data pointers were passed to us, so +		 * we leave it to the registering functions to +		 * clean up their own memory. +		 */ +		kfree(data); +	} + +	return; +} +EXPORT_SYMBOL_GPL(m4sensorhub_panic_shutdown); + +/* m4sensorhub_panic_register() + +   Register a panic handler to monitor M4 panic reset + +   Returns 0 on success or negative error code on failure + +     m4sensorhub - pointer to the main m4sensorhub data struct +     index - M4 Sensor Hub panic handler to register for +     cb_func - panic handler function to execute after M4 reset +     data - pointer to data for panic handler function +*/ +int m4sensorhub_panic_register(struct m4sensorhub_data *m4sensorhub, +	enum m4sensorhub_panichdl_index index, +	void (*cb_func) (struct m4sensorhub_data *, void *), +	void *data) +{ +	struct m4sensorhub_panicdata *panicdata = NULL; +	int retval = 0; + +	if (m4sensorhub == NULL) { +		KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); +		retval = -ENODATA; +		goto m4sensorhub_panic_register_exit; +	} else if (cb_func == NULL) { +		KDEBUG(M4SH_ERROR, "%s: Callback is NULL\n", __func__); +		retval = -ENOSYS; +		goto m4sensorhub_panic_register_exit; +	} else if (index >= PANICHDL_MAX) { +		KDEBUG(M4SH_ERROR, "%s: Number of panic handlers exceeded\n", +			__func__); +		retval = -EOVERFLOW; +		goto m4sensorhub_panic_register_exit; +	} else if (m4sensorhub->panicdata == NULL) { +		KDEBUG(M4SH_ERROR, "%s: Panic data is missing\n", __func__); +		retval = -ENODATA; +		goto m4sensorhub_panic_register_exit; +	} + +	panicdata = (struct m4sensorhub_panicdata *)m4sensorhub->panicdata; +	mutex_lock(&panicdata->lock); +	if (panicdata->funcs[index].callback == NULL) { +		panicdata->funcs[index].callback = cb_func; +		panicdata->funcs[index].data = data; +		KDEBUG(M4SH_NOTICE, "%s: %s callback registered\n", +			__func__, callback_name[index]); +	} else { +		KDEBUG(M4SH_ERROR, "%s: %s %s", __func__, +			callback_name[index], "callback registration failed\n"); +		retval = -EPERM; +		goto m4sensorhub_panic_register_fail; +	} + +m4sensorhub_panic_register_fail: +	mutex_unlock(&panicdata->lock); +m4sensorhub_panic_register_exit: +	return retval; +} +EXPORT_SYMBOL_GPL(m4sensorhub_panic_register); + +/* m4sensorhub_panic_unregister() + +   Unregister an panic handler to monitor M4 panic reset + +   Returns 0 on success or negative error code on failure + +     m4sensorhub - pointer to the main m4sensorhub data struct +     index - M4 Sensor Hub panic handler to unresiter for +*/ +int m4sensorhub_panic_unregister(struct m4sensorhub_data *m4sensorhub, +	enum m4sensorhub_panichdl_index index) +{ +	int retval = 0; +	struct m4sensorhub_panicdata *panicdata = NULL; + +	if (m4sensorhub == NULL) { +		KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); +		retval = -ENODATA; +		goto m4sensorhub_panic_unregister_fail; +	} else if (index >= PANICHDL_MAX) { +		KDEBUG(M4SH_ERROR, "%s: Number of panic handlers exceeded\n", +			__func__); +		retval = -EOVERFLOW; +		goto m4sensorhub_panic_unregister_fail; +	} else if (m4sensorhub->panicdata == NULL) { +		KDEBUG(M4SH_ERROR, "%s: Panic data is missing\n", __func__); +		retval = -ENODATA; +		goto m4sensorhub_panic_unregister_fail; +	} + +	panicdata = (struct m4sensorhub_panicdata *)m4sensorhub->panicdata; +	mutex_lock(&panicdata->lock); +	panicdata->funcs[index].callback = NULL; +	panicdata->funcs[index].data = NULL; +	mutex_unlock(&panicdata->lock); +	KDEBUG(M4SH_NOTICE, "%s: %s callback un-registered\n", +			__func__, callback_name[index]); + +m4sensorhub_panic_unregister_fail: +	return retval; +} +EXPORT_SYMBOL_GPL(m4sensorhub_panic_unregister); + + +/* m4sensorhub_panic_process() + +   Check M4 if it's panicked, use I2C to communicate with M4 panic handler +   OMAP use the same i2c sequences to send command via i2c master, then M4 +   i2c slave program will handle these commands, it may have 2 slave programs +   1. Normal i2c slave program handles all vaild banks'(limit on +      M4SH_TYPE__NUM) command, for invalid bank, it always responses 0xFF +   2. Panic i2c slave program handles panic bank(reserved 0xFF for it) command, +      for others, it always responses 0x00 + +   To detect whether M4 is panicked, the process should be +   i. When OMAP got interrupt from M4, OMAP will check which irq is raised, it +      send normal banks' command to M4, for panic case, it always returns 0x00, +      so OMAP has a checkpoint as there's interrupt request from M4 without +      active IRQ +   ii.Then OMAP will confirm if M4 is panic via send panic bank command, if M4 +      is panicked, it will handle this bank and response panic magic code; +      Otherwise, if no panic magic code returned from M4, it always means M4 +      isn't panicked. + +     m4sensorhub - pointer to the main m4sensorhub data struct +	*/ +void m4sensorhub_panic_process(struct m4sensorhub_data *m4sensorhub) +{ +	int i, ret; +	union panic_buf buf; +	struct m4sensorhub_panic_callback handler; + +	if (m4sensorhub == NULL) { +		KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); +		return; +	} else if (m4sensorhub->panicdata == NULL) { +		KDEBUG(M4SH_ERROR, "%s: Panic data is missing\n", __func__); +		return; +	} + +	m4sensorhub_reg_access_lock(); + +	buf.in.bank = PANIC_BANK; +	buf.in.cmd = PANIC_CMD_CHECK; +	ret = m4sensorhub_i2c_write_read(m4sensorhub, +		(u8 *)&buf, sizeof(buf.in), sizeof(buf.data)); +	if ((ret != sizeof(buf.data)) || (buf.data != PANIC_RESP_CHECK)) { +		/* TODO maybe we shall check if M4/OMAP i2c broken */ +		KDEBUG(M4SH_ERROR, "m4sensorhub: %s ret=%d, data=0x%x\n", +			"Unknown IRQ status! M4 panic handoff", ret, buf.data); +		m4sensorhub_reg_access_unlock(); +		return; +	} + +	KDEBUG(M4SH_ERROR, "%s: Detected M4 panic, reset M4!\n", __func__); +	/* Passing "true" will reset M4 before trying to communicate */ +	ret = m4sensorhub_test_m4_reboot(m4sensorhub, true); +	if (ret < 0) { +		KDEBUG(M4SH_ERROR, "%s: Failed to restart M4, ret = %d\n", +			__func__, ret); +		BUG(); +	} +	m4sensorhub_reg_access_unlock(); + +	for (i = 0; i < PANICHDL_MAX; i++) { +		handler = ((struct m4sensorhub_panicdata *) +			(m4sensorhub->panicdata))->funcs[i]; +		if (handler.callback) { +			KDEBUG(M4SH_NOTICE, "%s: Calling %s as M4 restarted!\n", +				__func__, callback_name[i]); +			handler.callback(m4sensorhub, handler.data); +		} +	} +} +EXPORT_SYMBOL_GPL(m4sensorhub_panic_process);  |