diff options
Diffstat (limited to 'drivers/mfd/m4sensorhub-panic.c')
| -rw-r--r-- | drivers/mfd/m4sensorhub-panic.c | 248 | 
1 files changed, 248 insertions, 0 deletions
diff --git a/drivers/mfd/m4sensorhub-panic.c b/drivers/mfd/m4sensorhub-panic.c new file mode 100644 index 00000000000..aaf35f89886 --- /dev/null +++ b/drivers/mfd/m4sensorhub-panic.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2012 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_IRQ_RESTORE] = "irq_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; + +	data = kzalloc(sizeof(struct m4sensorhub_panicdata), GFP_KERNEL); +	if (data) { +		mutex_init(&data->lock); +		m4sensorhub->panicdata = data; +	} else { +		KDEBUG(M4SH_ERROR, "m4sensorhub: Memory error in panic_init\n"); +		retval = -ENOMEM; +	} +	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) +{ +	if (m4sensorhub && m4sensorhub->panicdata) { +		struct m4sensorhub_panicdata *data = m4sensorhub->panicdata; +		m4sensorhub->panicdata = NULL; +		if (mutex_is_locked(&data->lock)) +			mutex_unlock(&data->lock); +		mutex_destroy(&data->lock); +		kfree(data); +	} +} +EXPORT_SYMBOL_GPL(m4sensorhub_panic_shutdown); + +/* m4sensorhub_panic_register() + +   Register 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 resiter 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; +	int retval = 0; + +	if (!m4sensorhub || (index >= PANICHDL_MAX) || !cb_func) +		return -EINVAL; + +	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, "m4sensorhub: %s callback registered\n", +			callback_name[index]); +	} else { +		KDEBUG(M4SH_ERROR, "m4sensorhub: %s callback"\ +			" registration failed\n", callback_name[index]); +		retval = -EPERM; +	} +	mutex_unlock(&panicdata->lock); + +	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) +{ +	struct m4sensorhub_panicdata *panicdata; + +	if (!m4sensorhub || (index >= PANICHDL_MAX)) +		return -EINVAL; + +	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, "m4sensorhub: %s callback un-registered\n", +			callback_name[index]); + +	return 0; +} +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 || !m4sensorhub->panicdata) { +		KDEBUG(M4SH_ERROR, "m4sensorhub: Invalid parameter in %s!\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: Unknown IRQ status! "\ +				"M4 panic handoff ret=%d, data=0x%x\n",\ +				ret, buf.data); +		m4sensorhub_reg_access_unlock(); +		return; +	} + +	KDEBUG(M4SH_ERROR, "m4sensorhub_panic: Detected M4 panic, reset M4!\n"); +	m4sensorhub->pdev->hw_reset(m4sensorhub); +	msleep(100); +	ret = m4sensorhub_load_firmware(m4sensorhub, 0); +	if (ret < 0) { +		KDEBUG(M4SH_ERROR, "m4sensorhub_panic: "\ +			"Failed to restart M4, ret = %d\n", 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, "m4sensorhub_panic: "\ +				"Calling %s as M4 restarted!\n",\ +				callback_name[i]); +			handler.callback(m4sensorhub, handler.data); +		} +	} +} +EXPORT_SYMBOL_GPL(m4sensorhub_panic_process);  |