summaryrefslogtreecommitdiff
path: root/drivers/mfd/m4sensorhub-panic.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/m4sensorhub-panic.c')
-rw-r--r--drivers/mfd/m4sensorhub-panic.c317
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);