Sensorhub  is MCU dedicated to collect data and manage several sensors.
Sensorhub is a spi device which provides a layer for IIO devices. It provides
some data parsing and common mechanism for sensorhub sensors.

Signed-off-by: Karol Wrona <k.wr...@samsung.com>
Acked-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 drivers/misc/Kconfig                  |    1 +
 drivers/misc/Makefile                 |    1 +
 drivers/misc/sensorhub/Kconfig        |   13 +
 drivers/misc/sensorhub/Makefile       |    8 +
 drivers/misc/sensorhub/ssp.h          |  323 ++++++++++++++
 drivers/misc/sensorhub/ssp_data.c     |   61 +++
 drivers/misc/sensorhub/ssp_dev.c      |  782 +++++++++++++++++++++++++++++++++
 drivers/misc/sensorhub/ssp_firmware.c |  571 ++++++++++++++++++++++++
 drivers/misc/sensorhub/ssp_spi.c      |  700 +++++++++++++++++++++++++++++
 9 files changed, 2460 insertions(+)
 create mode 100644 drivers/misc/sensorhub/Kconfig
 create mode 100644 drivers/misc/sensorhub/Makefile
 create mode 100644 drivers/misc/sensorhub/ssp.h
 create mode 100644 drivers/misc/sensorhub/ssp_data.c
 create mode 100644 drivers/misc/sensorhub/ssp_dev.c
 create mode 100644 drivers/misc/sensorhub/ssp_firmware.c
 create mode 100644 drivers/misc/sensorhub/ssp_spi.c

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b841180..faf8521 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -527,4 +527,5 @@ source "drivers/misc/vmw_vmci/Kconfig"
 source "drivers/misc/mic/Kconfig"
 source "drivers/misc/genwqe/Kconfig"
 source "drivers/misc/echo/Kconfig"
+source "drivers/misc/sensorhub/Kconfig"
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 5497d02..9c36078 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,3 +55,4 @@ obj-y                         += mic/
 obj-$(CONFIG_GENWQE)           += genwqe/
 obj-$(CONFIG_ECHO)             += echo/
 obj-$(CONFIG_VEXPRESS_SYSCFG)  += vexpress-syscfg.o
+obj-$(CONFIG_SENSORS_SSP)      += sensorhub/
diff --git a/drivers/misc/sensorhub/Kconfig b/drivers/misc/sensorhub/Kconfig
new file mode 100644
index 0000000..0af30ec
--- /dev/null
+++ b/drivers/misc/sensorhub/Kconfig
@@ -0,0 +1,13 @@
+#
+# sensor drivers configuration
+#
+config SENSORS_SSP
+       tristate "Sensors ssp"
+       default n
+       depends on SPI
+       help
+         SSP driver for sensor hub.
+         If you say yes here you get ssp support for
+         sensor hub.
+         To compile this driver as a module, choose M here: the
+         module will be called ssp_dev.
diff --git a/drivers/misc/sensorhub/Makefile b/drivers/misc/sensorhub/Makefile
new file mode 100644
index 0000000..754da8b
--- /dev/null
+++ b/drivers/misc/sensorhub/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the sensor drivers.
+#
+
+# Each configuration option enables a list of files.
+obj-$(CONFIG_SENSORS_SSP)              += ssp_dev.o ssp_spi.o ssp_data.o \
+                                          ssp_firmware.o
+
diff --git a/drivers/misc/sensorhub/ssp.h b/drivers/misc/sensorhub/ssp.h
new file mode 100644
index 0000000..c49f958
--- /dev/null
+++ b/drivers/misc/sensorhub/ssp.h
@@ -0,0 +1,323 @@
+/*
+ *  Copyright (C) 2011, Samsung Electronics Co. Ltd. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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.
+ *
+ */
+
+#ifndef __SSP_SENSORHUB_H__
+#define __SSP_SENSORHUB_H__
+
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/common/ssp_sensors.h>
+
+#define DEVICE_ID              0x55
+
+#ifdef SSP_DBG
+#define ssp_info(fmt, ...) pr_info("[SSP] "fmt, ##__VA_ARGS__)
+
+#define ssp_dbg(format, ...) pr_info("[SSP] "format, ##__VA_ARGS__)
+#else
+#define ssp_info(fmt, ...)
+
+#define ssp_dbg(format, ...)
+#endif
+
+#define SSP_SW_RESET_TIME      3000
+/* Sensor polling in ms */
+#define DEFUALT_POLLING_DELAY  200
+#define DEFAULT_RETRIES                3
+#define DATA_PACKET_SIZE       960
+
+enum {
+       KERNEL_BINARY = 0,
+       KERNEL_CRASHED_BINARY,
+};
+
+enum {
+       INITIALIZATION_STATE = 0,
+       NO_SENSOR_STATE,
+       ADD_SENSOR_STATE,
+       RUNNING_SENSOR_STATE,
+};
+
+/* Firmware download STATE */
+enum {
+       FW_DL_STATE_FAIL = -1,
+       FW_DL_STATE_NONE = 0,
+       FW_DL_STATE_NEED_TO_SCHEDULE,
+       FW_DL_STATE_SCHEDULED,
+       FW_DL_STATE_DOWNLOADING,
+       FW_DL_STATE_SYNC,
+       FW_DL_STATE_DONE,
+};
+
+#define SSP_INVALID_REVISION   99999
+#define SSP_INVALID_REVISION2  0xFFFFFF
+
+/* AP -> SSP Instruction */
+#define MSG2SSP_INST_BYPASS_SENSOR_ADD         0xA1
+#define MSG2SSP_INST_BYPASS_SENSOR_REMOVE      0xA2
+#define MSG2SSP_INST_REMOVE_ALL                        0xA3
+#define MSG2SSP_INST_CHANGE_DELAY              0xA4
+#define MSG2SSP_INST_LIBRARY_ADD               0xB1
+#define MSG2SSP_INST_LIBRARY_REMOVE            0xB2
+#define MSG2SSP_INST_LIB_NOTI                  0xB4
+#define MSG2SSP_INST_LIB_DATA                  0xC1
+
+#define MSG2SSP_AP_MCU_SET_GYRO_CAL            0xCD
+#define MSG2SSP_AP_MCU_SET_ACCEL_CAL           0xCE
+#define MSG2SSP_AP_STATUS_SHUTDOWN             0xD0
+#define MSG2SSP_AP_STATUS_WAKEUP               0xD1
+#define MSG2SSP_AP_STATUS_SLEEP                        0xD2
+#define MSG2SSP_AP_STATUS_RESUME               0xD3
+#define MSG2SSP_AP_STATUS_SUSPEND              0xD4
+#define MSG2SSP_AP_STATUS_RESET                        0xD5
+#define MSG2SSP_AP_STATUS_POW_CONNECTED                0xD6
+#define MSG2SSP_AP_STATUS_POW_DISCONNECTED     0xD7
+#define MSG2SSP_AP_TEMPHUMIDITY_CAL_DONE       0xDA
+#define MSG2SSP_AP_MCU_SET_DUMPMODE            0xDB
+#define MSG2SSP_AP_MCU_DUMP_CHECK              0xDC
+#define MSG2SSP_AP_MCU_BATCH_FLUSH             0xDD
+#define MSG2SSP_AP_MCU_BATCH_COUNT             0xDF
+
+#define MSG2SSP_AP_WHOAMI                      0x0F
+#define MSG2SSP_AP_FIRMWARE_REV                        0xF0
+#define MSG2SSP_AP_SENSOR_FORMATION            0xF1
+#define MSG2SSP_AP_SENSOR_PROXTHRESHOLD                0xF2
+#define MSG2SSP_AP_SENSOR_BARCODE_EMUL         0xF3
+#define MSG2SSP_AP_SENSOR_SCANNING             0xF4
+#define MSG2SSP_AP_SET_MAGNETIC_HWOFFSET       0xF5
+#define MSG2SSP_AP_GET_MAGNETIC_HWOFFSET       0xF6
+#define MSG2SSP_AP_SENSOR_GESTURE_CURRENT      0xF7
+#define MSG2SSP_AP_GET_THERM                   0xF8
+#define MSG2SSP_AP_GET_BIG_DATA                        0xF9
+#define MSG2SSP_AP_SET_BIG_DATA                        0xFA
+#define MSG2SSP_AP_START_BIG_DATA              0xFB
+#define MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX  0xFD
+#define MSG2SSP_AP_SENSOR_TILT                 0xEA
+#define MSG2SSP_AP_MCU_SET_TIME                        0xFE
+#define MSG2SSP_AP_MCU_GET_TIME                        0xFF
+
+#define MSG2SSP_AP_FUSEROM                     0X01
+/* voice data */
+#define TYPE_WAKE_UP_VOICE_SERVICE             0x01
+#define TYPE_WAKE_UP_VOICE_SOUND_SOURCE_AM     0x01
+#define TYPE_WAKE_UP_VOICE_SOUND_SOURCE_GRAMMER        0x02
+
+/* Factory Test */
+#define ACCELEROMETER_FACTORY                  0x80
+#define GYROSCOPE_FACTORY                      0x81
+#define GEOMAGNETIC_FACTORY                    0x82
+#define PRESSURE_FACTORY                       0x85
+#define GESTURE_FACTORY                                0x86
+#define TEMPHUMIDITY_CRC_FACTORY               0x88
+#define GYROSCOPE_TEMP_FACTORY                 0x8A
+#define GYROSCOPE_DPS_FACTORY                  0x8B
+#define MCU_FACTORY                            0x8C
+#define MCU_SLEEP_FACTORY                      0x8D
+
+/* Factory data length */
+#define ACCEL_FACTORY_DATA_LENGTH              1
+#define GYRO_FACTORY_DATA_LENGTH               36
+#define MAGNETIC_FACTORY_DATA_LENGTH           26
+#define PRESSURE_FACTORY_DATA_LENGTH           1
+#define MCU_FACTORY_DATA_LENGTH                        5
+#define        GYRO_TEMP_FACTORY_DATA_LENGTH           2
+#define        GYRO_DPS_FACTORY_DATA_LENGTH            1
+#define TEMPHUMIDITY_FACTORY_DATA_LENGTH       1
+#define MCU_SLEEP_FACTORY_DATA_LENGTH          FACTORY_DATA_MAX
+#define GESTURE_FACTORY_DATA_LENGTH            4
+
+/* SSP -> AP ACK about write CMD */
+#define MSG_ACK                0x80    /* ACK from SSP to AP */
+#define MSG_NAK                0x70    /* NAK from SSP to AP */
+
+/* Accelerometer sensor*/
+/* 16bits */
+#define MAX_ACCEL_1G   16384
+#define MAX_ACCEL_2G   32767
+#define MIN_ACCEL_2G   -32768
+#define MAX_ACCEL_4G   65536
+
+#define MAX_GYRO       32767
+#define MIN_GYRO       -32768
+
+struct sensorhub_info {
+       char *fw_name;
+       char *fw_crashed_name;
+       unsigned int fw_rev;
+};
+
+struct calibraion_data {
+       s16 x;
+       s16 y;
+       s16 z;
+};
+
+/* ssp_msg options bit*/
+#define SSP_SPI                0       /* read write mask */
+#define SSP_RETURN     2       /* write and read option */
+#define SSP_GYRO_DPS   3       /* gyro dps mask */
+#define SSP_INDEX      3       /* data index mask */
+
+#define SSP_SPI_MASK           (3 << SSP_SPI)  /* read write mask */
+#define SSP_GYRO_DPS_MASK      (3 << SSP_GYRO_DPS)
+                               /* dump index mask. Index is up to 8191 */
+#define SSP_INDEX_MASK         (8191 << SSP_INDEX)
+
+struct ssp_msg {
+       u8 cmd;
+       /* FIXME it is pushed to STM as first 9 bytes */
+       u16 length;
+       u16 options;
+       __le32 data;
+
+       struct list_head list;
+       struct completion *done;
+       char *buffer;
+       u8 free_buffer;
+       bool *dead_hook;
+       bool dead;
+} __attribute__((__packed__));
+
+enum {
+       AP2HUB_READ = 0,
+       AP2HUB_WRITE,
+       HUB2AP_WRITE,
+       AP2HUB_READY,
+       AP2HUB_RETURN
+};
+
+enum {
+       BIG_TYPE_DUMP = 0,
+       BIG_TYPE_READ_LIB,
+       BIG_TYPE_VOICE_NET,
+       BIG_TYPE_VOICE_GRAM,
+       BIG_TYPE_VOICE_PCM,
+       BIG_TYPE_TEMP,
+       BIG_TYPE_MAX,
+};
+
+struct ssp_data {
+       struct spi_device *spi;
+       struct sensorhub_info *sensorhub_info;
+       struct timer_list wdt_timer;
+       struct workqueue_struct *wdt_wq;
+       struct work_struct work_wdt;
+
+       struct regulator *hrm_supply_18;
+       struct regulator *hrm_supply_33;
+
+       struct delayed_work work_firmware;
+       struct delayed_work work_refresh;
+
+       bool shut_down;
+       bool mcu_dump_mode;
+       bool time_syncing;
+
+       unsigned char fuse_rom_data[3];
+       unsigned char mag_reg_data;
+       int library_length;
+       int check_status[SSP_SENSOR_MAX];
+
+       unsigned int com_fail_cnt;
+       unsigned int reset_cnt;
+       unsigned int time_out_cnt;
+       unsigned int irq_cnt;
+
+       unsigned int sensor_state;
+       unsigned int cur_firm_rev;
+
+       char last_resume_state;
+       char last_ap_state;
+
+       unsigned int sensor_enable;
+       u32 delay_buf[SSP_SENSOR_MAX];
+       s32 batch_latency_buf[SSP_SENSOR_MAX];
+       s8 batch_opt_buf[SSP_SENSOR_MAX];
+
+       int accel_position;
+       int mag_position;
+       int fw_dl_state;
+
+       u32 mag_matrix_size;
+       u8 *mag_matrix;
+
+       struct mutex comm_mutex;
+       struct mutex pending_mutex;
+
+       void (*get_positions)(int *, int *);
+
+       int mcu_reset;
+       int ap_mcu_int;
+       int mcu_ap_int;
+       struct list_head pending_list;
+
+       void (*ssp_big_task[BIG_TYPE_MAX])(struct work_struct *);
+       u64 timestamp;
+
+       struct iio_dev *sensor_devs[SSP_SENSOR_MAX];
+       atomic_t enable_refcount;
+};
+
+struct ssp_big {
+       struct ssp_data *data;
+       struct work_struct work;
+       u32 length;
+       u32 addr;
+};
+
+void ssp_enable(struct ssp_data *, bool);
+
+void clean_pending_list(struct ssp_data *);
+
+void toggle_mcu_reset(struct ssp_data *);
+
+int initialize_mcu(struct ssp_data *);
+
+void initialize_function_pointer(struct ssp_data *);
+
+int check_fwbl(struct ssp_data *);
+
+int ssp_send_cmd(struct ssp_data *, char, int);
+
+int ssp_send_instruction(struct ssp_data *, u8, u8, u8 *, u8);
+
+int flush(struct ssp_data *, u8);
+
+int select_irq_msg(struct ssp_data *);
+
+int get_chipid(struct ssp_data *);
+
+int get_fuserom_data(struct ssp_data *);
+
+int set_big_data_start(struct ssp_data *, u8 , u32);
+
+int set_sensor_position(struct ssp_data *);
+
+int set_magnetic_static_matrix(struct ssp_data *);
+
+unsigned int get_sensor_scanning_info(struct ssp_data *);
+
+unsigned int get_firmware_rev(struct ssp_data *);
+
+int forced_to_download_binary(struct ssp_data *, int);
+
+int queue_refresh_task(struct ssp_data *data, int delay);
+
+int ssp_start_big_data(struct ssp_data *data, u8 type, u32 len);
+
+#endif /* __SSP_SENSORHUB_H__ */
diff --git a/drivers/misc/sensorhub/ssp_data.c 
b/drivers/misc/sensorhub/ssp_data.c
new file mode 100644
index 0000000..4112cf2
--- /dev/null
+++ b/drivers/misc/sensorhub/ssp_data.c
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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.
+ *
+ */
+#include "ssp.h"
+
+static void sync_sensor_state(struct ssp_data *data)
+{
+       int i, ret;
+
+       for (i = 0; i < SSP_SENSOR_MAX; ++i) {
+               if ((data->sensor_state << i)  == 1)
+                       ret = ssp_enable_sensor(data, i, data->delay_buf[i]);
+                       if (ret < 0)
+                               continue;
+       }
+
+       ret  = ssp_send_cmd(data, MSG2SSP_AP_MCU_SET_DUMPMODE,
+                           data->mcu_dump_mode);
+       if (ret < 0) {
+               pr_err("[SSP]: %s - MSG2SSP_AP_MCU_SET_DUMPMODE failed\n",
+                      __func__);
+       }
+}
+
+void refresh_task(struct work_struct *work)
+{
+       struct ssp_data *data = container_of((struct delayed_work *)work,
+                       struct ssp_data, work_refresh);
+
+       data->reset_cnt++;
+
+       if (initialize_mcu(data) > 0) {
+               sync_sensor_state(data);
+               if (data->last_ap_state != 0)
+                       ssp_send_cmd(data, data->last_ap_state, 0);
+               if (data->last_resume_state != 0)
+                       ssp_send_cmd(data, data->last_resume_state, 0);
+               data->time_out_cnt = 0;
+       }
+}
+
+int queue_refresh_task(struct ssp_data *data, int delay)
+{
+       cancel_delayed_work_sync(&data->work_refresh);
+
+       INIT_DELAYED_WORK(&data->work_refresh, refresh_task);
+
+       return queue_delayed_work(data->wdt_wq, &data->work_refresh,
+                       msecs_to_jiffies(delay));
+}
diff --git a/drivers/misc/sensorhub/ssp_dev.c b/drivers/misc/sensorhub/ssp_dev.c
new file mode 100644
index 0000000..6b42a3b
--- /dev/null
+++ b/drivers/misc/sensorhub/ssp_dev.c
@@ -0,0 +1,782 @@
+/*
+ *  Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+#include "ssp.h"
+
+#define SSP_WDT_TIME           (10 * HZ)
+#define LIMIT_RESET_CNT                20
+#define LIMIT_TIMEOUT_CNT      3
+
+const struct sensorhub_info rinato_info = {
+       .fw_name = "ssp_B2.fw",
+       .fw_crashed_name = "ssp_crashed.fw",
+       .fw_rev = 14031200,
+};
+
+const struct sensorhub_info thermostat_info = {
+       .fw_name = "thermostat_B2.fw",
+       .fw_crashed_name = "ssp_crashed.fw",
+       .fw_rev = 14080600,
+};
+
+static void reset_mcu(struct ssp_data *data)
+{
+       ssp_enable(data, false);
+       clean_pending_list(data);
+       toggle_mcu_reset(data);
+       ssp_enable(data, true);
+}
+
+static void wdt_work_func(struct work_struct *work)
+{
+       struct ssp_data *data = container_of(work, struct ssp_data, work_wdt);
+
+       ssp_dbg("%s(%u) - Sensor state: 0x%x, RC: %u, CC: %u\n", __func__,
+               data->irq_cnt, data->sensor_state, data->reset_cnt,
+               data->com_fail_cnt);
+
+       switch (data->fw_dl_state) {
+       case FW_DL_STATE_FAIL:
+       case FW_DL_STATE_DOWNLOADING:
+       case FW_DL_STATE_SYNC:
+               ssp_dbg("[SSP] : %s firmware downloading state = %d\n",
+                       __func__, data->fw_dl_state);
+               return;
+       }
+       /* FIXME time_out_cnt should be atomc*/
+       if (data->time_out_cnt > LIMIT_TIMEOUT_CNT) {
+               if (data->com_fail_cnt < LIMIT_RESET_CNT) {
+                       pr_info("[SSP] : %s - time_out_cnt(%u), pending(%u)\n",
+                               __func__, data->time_out_cnt,
+                               !list_empty(&data->pending_list));
+                       data->com_fail_cnt++;
+                       reset_mcu(data);
+               } else
+                       ssp_enable(data, false);
+
+               data->time_out_cnt = 0;
+       }
+
+       data->irq_cnt = 0;
+}
+
+static void wdt_timer_func(unsigned long ptr)
+{
+       struct ssp_data *data = (struct ssp_data *)ptr;
+
+       queue_work(data->wdt_wq, &data->work_wdt);
+       mod_timer(&data->wdt_timer,
+                 round_jiffies_up(jiffies + SSP_WDT_TIME));
+}
+
+static void enable_wdt_timer(struct ssp_data *data)
+{
+       mod_timer(&data->wdt_timer,
+                 round_jiffies_up(jiffies + SSP_WDT_TIME));
+}
+
+static void disable_wdt_timer(struct ssp_data *data)
+{
+       del_timer_sync(&data->wdt_timer);
+       cancel_work_sync(&data->work_wdt);
+}
+
+static int initialize_wdt_timer(struct ssp_data *data)
+{
+       setup_timer(&data->wdt_timer, wdt_timer_func,
+                   (unsigned long)data);
+
+       data->wdt_wq = create_singlethread_workqueue("ssp_wdt_wq");
+       if (IS_ERR(data->wdt_wq))
+               return PTR_ERR(data->wdt_wq);
+
+       INIT_WORK(&data->work_wdt, wdt_work_func);
+
+       return 0;
+}
+
+/**
+ * ssp_get_sensor_delay() - gets sensor data acquisition period
+ * @data:      sensorhub structure
+ * @type:      SSP sensor type
+ *
+ * Returns acquisition period in ms
+ */
+u32 ssp_get_sensor_delay(struct ssp_data *data, enum ssp_sensor_type type)
+{
+       return data->delay_buf[type];
+}
+EXPORT_SYMBOL(ssp_get_sensor_delay);
+
+/**
+ * ssp_enable_sensor() - enables data acquisition for sensor
+ * @data:      sensorhub structure
+ * @type:      SSP sensor type
+ * @delay:     delay in ms
+ */
+int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,
+                      u32 delay)
+{
+       int ret = 0;
+       struct {
+               __le32 a;
+               __le32 b;
+               u8 c;
+       } __attribute__((__packed__)) to_send;
+
+       to_send.a = cpu_to_le32(delay);
+       to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
+       to_send.c = data->batch_opt_buf[type];
+
+       switch (data->check_status[type]) {
+       case INITIALIZATION_STATE:
+               /*FIXME do calibration step*/
+       case ADD_SENSOR_STATE:
+               ret = ssp_send_instruction(data, MSG2SSP_INST_BYPASS_SENSOR_ADD,
+                                      type, (char *)&to_send, sizeof(to_send));
+               if (ret < 0) {
+                       dev_err(&data->spi->dev, "Enabling sensor failed\n");
+                       data->check_status[type] = NO_SENSOR_STATE;
+                       goto derror;
+               }
+
+               data->sensor_enable |= 1 << type;
+               data->check_status[type] = RUNNING_SENSOR_STATE;
+               break;
+       case RUNNING_SENSOR_STATE:
+               ret = ssp_send_instruction(data,
+                                          MSG2SSP_INST_CHANGE_DELAY, type,
+                                      (char *)&to_send, sizeof(to_send));
+               if (ret < 0) {
+                       dev_err(&data->spi->dev,
+                               "Changing delay sensor failed\n");
+                       goto derror;
+               }
+               break;
+       default:
+               data->check_status[type] = ADD_SENSOR_STATE;
+               break;
+       }
+
+       data->delay_buf[type] = delay;
+
+       if (atomic_inc_return(&data->enable_refcount) == 1)
+               enable_wdt_timer(data);
+
+       return 0;
+
+derror:
+       return -EIO;
+}
+EXPORT_SYMBOL(ssp_enable_sensor);
+
+/**
+ * ssp_change_delay() - changes data acquisition for sensor
+ * @data:      sensorhub structure
+ * @type:      SSP sensor type
+ * @delay:     delay in ms
+ */
+int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type,
+                    u32 delay)
+{
+       int ret = 0;
+       struct {
+               __le32 a;
+               __le32 b;
+               u8 c;
+       } __attribute__((__packed__)) to_send;
+
+       to_send.a = cpu_to_le32(delay);
+       to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
+       to_send.c = data->batch_opt_buf[type];
+
+       ret = ssp_send_instruction(data, MSG2SSP_INST_CHANGE_DELAY, type,
+                              (char *)&to_send, sizeof(to_send));
+       if (ret < 0) {
+               dev_err(&data->spi->dev,
+                       "Changing delay sensor failed\n");
+               return ret;
+       }
+
+       data->delay_buf[type] = delay;
+
+       return ret;
+}
+EXPORT_SYMBOL(ssp_change_delay);
+
+/**
+ * ssp_disable_sensor() - disables sensor
+ *
+ * @data:      sensorhub structure
+ * @type:      SSP sensor type
+ *
+ * Returns  < 0 if not succeed
+ */
+int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type)
+{
+       int ret = 0;
+       __le32 command;
+
+       if (data->sensor_enable & (1 << type)) {
+               command  = cpu_to_le32(data->delay_buf[type]);
+               ret = ssp_send_instruction(data,
+                                          MSG2SSP_INST_BYPASS_SENSOR_REMOVE,
+                                      type, (char *)&command, sizeof(__le32));
+               if (ret < 0) {
+                       dev_err(&data->spi->dev, "Remove sensor fail\n");
+                       return ret;
+               }
+
+               data->sensor_enable &= (~(1 << type));
+       }
+
+       data->check_status[type] = ADD_SENSOR_STATE;
+
+       if (atomic_dec_and_test(&data->enable_refcount))
+               disable_wdt_timer(data);
+
+       return ret;
+}
+EXPORT_SYMBOL(ssp_disable_sensor);
+
+void ssp_enable(struct ssp_data *data, bool enable)
+{
+       dev_info(&data->spi->dev, "Enable = %d, old enable = %d\n", enable,
+                data->shut_down);
+
+       if (enable && data->shut_down) {
+               data->shut_down = false;
+               enable_irq(data->spi->irq);
+               enable_irq_wake(data->spi->irq);
+       } else if (!enable && !data->shut_down) {
+               data->shut_down = true;
+               disable_irq(data->spi->irq);
+               disable_irq_wake(data->spi->irq);
+       } else {
+               dev_err(&data->spi->dev,
+                       "Error / enable = %d, old enable = %d\n", enable,
+                       data->shut_down);
+       }
+}
+
+static irqreturn_t sensordata_irq_thread_fn(int irq, void *dev_id)
+{
+       struct ssp_data *data = dev_id;
+
+       select_irq_msg(data);
+       data->irq_cnt++;
+
+       return IRQ_HANDLED;
+}
+
+static void initialize_variable(struct ssp_data *data)
+{
+       int sensor_index;
+
+       for (sensor_index = 0; sensor_index < SSP_SENSOR_MAX; sensor_index++) {
+               data->delay_buf[sensor_index] = DEFUALT_POLLING_DELAY;
+               data->batch_latency_buf[sensor_index] = 0;
+               data->batch_opt_buf[sensor_index] = 0;
+               data->check_status[sensor_index] = INITIALIZATION_STATE;
+       }
+
+       data->delay_buf[SSP_BIO_HRM_LIB] = 100;
+
+       data->shut_down = true;
+       data->time_syncing = true;
+
+       INIT_LIST_HEAD(&data->pending_list);
+
+       atomic_set(&data->enable_refcount, 0);
+}
+
+int initialize_mcu(struct ssp_data *data)
+{
+       int ret;
+
+       clean_pending_list(data);
+
+       ret = get_chipid(data);
+       if (ret != DEVICE_ID) {
+               dev_err(&data->spi->dev, "%s - MCU %s ret = %d\n", __func__,
+                      ret < 0 ? "is not working" : "identyfication failed",
+                      ret);
+               return ret >= 0 ? -ENODEV : ret;
+       }
+
+       dev_info(&data->spi->dev, "MCU device ID = %d, reading ID = %d\n",
+                DEVICE_ID, ret);
+
+       ret = set_sensor_position(data);
+       if (ret < 0) {
+               dev_err(&data->spi->dev, "%s - set_sensor_position failed\n",
+                       __func__);
+               return ret;
+       }
+
+       ret = set_magnetic_static_matrix(data);
+       if (ret < 0)
+               dev_err(&data->spi->dev,
+                       "%s - set_magnetic_static_matrix failed\n", __func__);
+
+       ret = get_fuserom_data(data);
+       if (ret < 0)
+               dev_err(&data->spi->dev,
+                       "%s - get_fuserom_data failed\n", __func__);
+
+       data->sensor_state = get_sensor_scanning_info(data);
+       if (data->sensor_state == 0) {
+               dev_err(&data->spi->dev,
+                       "%s - get_sensor_scanning_info failed\n", __func__);
+               return -1;
+       }
+
+       data->cur_firm_rev = get_firmware_rev(data);
+       dev_info(&data->spi->dev, "MCU Firm Rev : New = %8u\n",
+                data->cur_firm_rev);
+
+       return ssp_send_cmd(data, MSG2SSP_AP_MCU_DUMP_CHECK, 0);
+}
+
+static void work_function_firmware_update(struct work_struct *work)
+{
+       struct ssp_data *data = container_of((struct delayed_work *)work,
+                               struct ssp_data, work_firmware);
+       int ret;
+
+       dev_info(&data->spi->dev, "%s, is called\n", __func__);
+
+       ret = forced_to_download_binary(data, KERNEL_BINARY);
+       if (ret < 0) {
+               dev_err(&data->spi->dev,
+                        "%s, forced_to_download_binary failed!\n",
+                       __func__);
+               return;
+       }
+
+       queue_refresh_task(data, SSP_SW_RESET_TIME);
+
+       dev_info(&data->spi->dev, "%s done\n", __func__);
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id ssp_of_match[] = {
+       {
+               .compatible     = "samsung,sensorhub-rinato",
+               .data           = &rinato_info,
+       }, {
+               .compatible     = "samsung,sensorhub-thermostat",
+               .data           = &thermostat_info,
+       },
+};
+MODULE_DEVICE_TABLE(of, ssp_of_match);
+
+static struct ssp_data *ssp_parse_dt(struct device *dev)
+{
+       int ret, len;
+       struct ssp_data *pdata;
+       struct device_node *node = dev->of_node;
+       const void *prop;
+       const struct of_device_id *match;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       pdata->mcu_ap_int = of_get_named_gpio(node, "mcu-ap-int", 0);
+       if (pdata->mcu_ap_int < 0)
+               goto err_free_pd;
+
+       pdata->ap_mcu_int = of_get_named_gpio(node, "ap-mcu-int", 0);
+       if (pdata->ap_mcu_int < 0)
+               goto err_free_pd;
+
+       pdata->mcu_reset = of_get_named_gpio(node, "mcu-reset", 0);
+       if (pdata->mcu_reset < 0)
+               goto err_free_pd;
+
+       ret = devm_gpio_request_one(dev, pdata->ap_mcu_int, GPIOF_OUT_INIT_HIGH,
+                               "ap-mcu-int");
+       if (ret)
+               goto err_free_pd;
+
+       ret = devm_gpio_request_one(dev, pdata->mcu_reset, GPIOF_OUT_INIT_HIGH,
+                       "mcu-reset");
+       if (ret)
+               goto err_ap_mcu;
+
+       match = of_match_node(ssp_of_match, node);
+       if (!match)
+               goto err_ap_mcu;
+
+       pdata->sensorhub_info = (struct sensorhub_info *) match->data;
+
+       ret = of_platform_populate(node, NULL, NULL, dev);
+       if (ret < 0) {
+               dev_err(dev, "of_platform_populate fail\n");
+               goto err_ap_mcu;
+       }
+
+       prop = of_get_property(node, "mag-table", &len);
+       if (IS_ERR(prop)) {
+               dev_err(dev, "get prop failed\n");
+               goto err_mcu_reset;
+       }
+
+       pdata->mag_matrix_size = len;
+
+       pdata->mag_matrix = devm_kzalloc(dev, sizeof(len), GFP_KERNEL);
+       if (pdata->mag_matrix == NULL)
+               goto err_mcu_reset;
+
+       memcpy(pdata->mag_matrix, prop, len);
+
+       return pdata;
+
+err_mcu_reset:
+       devm_gpio_free(dev, pdata->mcu_reset);
+err_ap_mcu:
+       devm_gpio_free(dev, pdata->ap_mcu_int);
+err_free_pd:
+       kfree(pdata);
+       return NULL;
+}
+#else
+static struct ssp_data *ssp_parse_dt(struct platform_device *pdev)
+{
+       return NULL;
+}
+#endif
+
+/**
+ * ssp_register_consumer() - registers iio consumer in ssp framework
+ *
+ * @indio_dev: consumer iio device
+ * @type:      ssp sensor type
+ */
+void ssp_register_consumer(struct iio_dev *indio_dev, enum ssp_sensor_type 
type)
+{
+       /*TODO 3rd level device - hide it*/
+       struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+       data->sensor_devs[type] = indio_dev;
+}
+EXPORT_SYMBOL(ssp_register_consumer);
+
+
+static int ssp_probe(struct spi_device *spi)
+{
+       int ret = 0;
+       struct ssp_data *data;
+
+       data = (struct ssp_data *)spi->dev.platform_data;
+       if (!data) {
+               data = ssp_parse_dt(&spi->dev);
+               if (!data) {
+                       dev_err(&spi->dev,
+                               "%s:Failed to find platform data\n", __func__);
+                       return -ENODEV;
+               }
+       }
+
+       /* FIXME */
+       data->hrm_supply_18 = regulator_get(&spi->dev, "hrm18");
+       if (IS_ERR(data->hrm_supply_18)) {
+               data->hrm_supply_18 = NULL;
+               dev_err(&spi->dev, "Regulator get fail\n");
+       }
+
+       /* FIXME */
+       data->hrm_supply_33 = regulator_get(&spi->dev, "hrm33");
+       if (IS_ERR(data->hrm_supply_33)) {
+               data->hrm_supply_33 = NULL;
+               dev_err(&spi->dev, "Regulator get fail\n");
+       }
+
+       if (data->get_positions) {
+               data->get_positions(&data->accel_position,
+                       &data->mag_position);
+       } else {
+               data->accel_position = 0;
+               data->mag_position = 0;
+       }
+
+       spi->mode = SPI_MODE_1;
+       if (spi_setup(spi)) {
+               dev_err(&spi->dev, "Failed to setup spi\n");
+               goto err_setup;
+       }
+
+       data->fw_dl_state = FW_DL_STATE_NONE;
+       data->spi = spi;
+       spi_set_drvdata(spi, data);
+
+       mutex_init(&data->comm_mutex);
+       mutex_init(&data->pending_mutex);
+
+       initialize_variable(data);
+       INIT_DELAYED_WORK(&data->work_firmware, work_function_firmware_update);
+
+       ret = initialize_wdt_timer(data);
+       if (ret < 0) {
+               dev_err(&spi->dev, "Could not create workqueue\n");
+               goto err_create_workqueue;
+       }
+
+       ret = request_threaded_irq(data->spi->irq, NULL,
+                                  sensordata_irq_thread_fn,
+                                  IRQF_TRIGGER_FALLING|IRQF_ONESHOT,
+                                  "SSP_Int", data);
+       if (ret < 0) {
+               dev_err(&spi->dev, "Irq request fail\n");
+               goto err_setup_irq;
+       }
+
+       disable_irq(data->spi->irq);
+       ssp_enable(data, true);
+
+       data->fw_dl_state = check_fwbl(data);
+
+       if (data->fw_dl_state == FW_DL_STATE_NONE) {
+               ret = initialize_mcu(data);
+               if (ret < 0) {
+                       dev_err(&spi->dev, "Initialize_mcu failed\n");
+                       goto err_read_reg;
+               }
+       }
+
+       if (data->hrm_supply_18) {
+               ret = regulator_enable(data->hrm_supply_18);
+               if (ret < 0) {
+                       dev_err(&spi->dev, "Regulator enable fail\n");
+                       goto err_read_reg;
+               }
+       }
+
+       if (data->hrm_supply_33) {
+               ret = regulator_enable(data->hrm_supply_33);
+               if (ret < 0) {
+                       dev_err(&spi->dev,
+                               "Regulator enable 3.3 V fail\n");
+                       goto err_read_reg;
+               }
+       }
+
+       if (data->fw_dl_state == FW_DL_STATE_NEED_TO_SCHEDULE) {
+               dev_info(&spi->dev, "Firmware update is scheduled\n");
+               queue_delayed_work(system_power_efficient_wq,
+                               &data->work_firmware, msecs_to_jiffies(1000));
+               data->fw_dl_state = FW_DL_STATE_SCHEDULED;
+       } else if (data->fw_dl_state == FW_DL_STATE_FAIL) {
+               data->shut_down = true;
+       }
+
+       return 0;
+
+err_read_reg:
+       free_irq(data->spi->irq, data);
+err_setup_irq:
+       destroy_workqueue(data->wdt_wq);
+err_create_workqueue:
+       mutex_destroy(&data->comm_mutex);
+       mutex_destroy(&data->pending_mutex);
+err_setup:
+       kfree(data);
+       dev_err(&spi->dev, "Probe failed!\n");
+
+       return ret;
+}
+
+static void ssp_shutdown(struct spi_device *spi)
+{
+       struct ssp_data *data = spi_get_drvdata(spi);
+
+       if (ssp_send_cmd(data, MSG2SSP_AP_STATUS_SHUTDOWN, 0) < 0)
+               dev_err(&data->spi->dev, "MSG2SSP_AP_STATUS_SHUTDOWN failed\n");
+
+       ssp_enable(data, false);
+       disable_wdt_timer(data);
+
+       if (data->hrm_supply_18)
+               regulator_put(data->hrm_supply_18);
+
+       if (data->hrm_supply_33)
+               regulator_put(data->hrm_supply_33);
+}
+
+
+static int ssp_remove(struct spi_device *spi)
+{
+       struct ssp_data *data = spi_get_drvdata(spi);
+
+       if (data->fw_dl_state >= FW_DL_STATE_SCHEDULED &&
+               data->fw_dl_state < FW_DL_STATE_DONE) {
+               dev_err(&data->spi->dev,
+                       "cancel_delayed_work_sync state = %d\n",
+                       data->fw_dl_state);
+               cancel_delayed_work_sync(&data->work_firmware);
+       }
+
+       if (ssp_send_cmd(data, MSG2SSP_AP_STATUS_SHUTDOWN, 0) < 0)
+               dev_err(&data->spi->dev, "MSG2SSP_AP_STATUS_SHUTDOWN failed\n");
+
+       ssp_enable(data, false);
+       disable_wdt_timer(data);
+
+       clean_pending_list(data);
+
+       free_irq(data->spi->irq, data);
+
+       del_timer_sync(&data->wdt_timer);
+       cancel_work_sync(&data->work_wdt);
+       destroy_workqueue(data->wdt_wq);
+
+       mutex_destroy(&data->comm_mutex);
+       mutex_destroy(&data->pending_mutex);
+
+       if (data->hrm_supply_18)
+               regulator_put(data->hrm_supply_18);
+
+       if (data->hrm_supply_33)
+               regulator_put(data->hrm_supply_33);
+
+       kfree(data->mag_matrix);
+
+#ifdef CONFIG_OF
+       of_platform_depopulate(&spi->dev);
+#endif
+
+       kfree(data);
+
+       return 0;
+}
+
+static int ssp_suspend(struct device *dev)
+{
+       int ret = 0;
+       struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
+
+       data->last_resume_state = MSG2SSP_AP_STATUS_SUSPEND;
+
+       if (atomic_read(&data->enable_refcount) > 0)
+               disable_wdt_timer(data);
+
+       if (ssp_send_cmd(data, MSG2SSP_AP_STATUS_SUSPEND, 0) < 0)
+               dev_err(&data->spi->dev,
+                       "%s MSG2SSP_AP_STATUS_SUSPEND failed\n", __func__);
+
+       data->time_syncing = false;
+       disable_irq(data->spi->irq);
+
+       if (data->hrm_supply_18) {
+               ret = regulator_disable(data->hrm_supply_18);
+               if (ret < 0) {
+                       dev_err(&data->spi->dev,
+                               "Regulator disable fail\n");
+               }
+       }
+
+       if (data->hrm_supply_33) {
+               ret = regulator_disable(data->hrm_supply_33);
+               if (ret < 0) {
+                       dev_err(&data->spi->dev,
+                               "Regulator disable fail\n");
+               }
+       }
+
+       return ret;
+}
+
+static int ssp_resume(struct device *dev)
+{
+       int ret = 0;
+       struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
+
+       if (data->hrm_supply_18) {
+               ret = regulator_enable(data->hrm_supply_18);
+               if (ret < 0) {
+                       dev_err(&data->spi->dev,
+                               "Regulator enable hrm 1.8 fail\n");
+               } /* do not return jet */
+       }
+
+       if (data->hrm_supply_33) {
+               ret = regulator_enable(data->hrm_supply_33);
+               if (ret < 0) {
+                       dev_err(&data->spi->dev,
+                               "Regulator enable hrm 3.3 fail\n");
+               } /* do not return jet */
+       }
+
+       enable_irq(data->spi->irq);
+
+       if (atomic_read(&data->enable_refcount) > 0)
+               enable_wdt_timer(data);
+
+       if (ssp_send_cmd(data, MSG2SSP_AP_STATUS_RESUME, 0) < 0)
+               dev_err(&data->spi->dev,
+                       "%s MSG2SSP_AP_STATUS_RESUME failed\n", __func__);
+
+       data->last_resume_state = MSG2SSP_AP_STATUS_RESUME;
+
+       return ret;
+}
+
+static const struct dev_pm_ops ssp_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(ssp_suspend, ssp_resume)
+};
+
+static struct spi_driver ssp_driver = {
+       .probe = ssp_probe,
+       .remove = ssp_remove,
+       .shutdown = ssp_shutdown,
+       .driver = {
+               .pm = &ssp_pm_ops,
+               .bus = &spi_bus_type,
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(ssp_of_match),
+               .name = "ssp-spi"
+       },
+};
+
+static int __init ssp_stm_spi_init(void)
+{
+       int ret;
+
+       ret = spi_register_driver(&ssp_driver);
+       if (ret)
+               pr_err("Failed to register ssp-spi %x\n", ret);
+
+       return ret;
+}
+
+static void __exit ssp_stm_spi_exit(void)
+{
+       spi_unregister_driver(&ssp_driver);
+}
+
+module_init(ssp_stm_spi_init);
+module_exit(ssp_stm_spi_exit);
+
+MODULE_DESCRIPTION("ssp spi driver");
+MODULE_AUTHOR("Samsung Electronics");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/sensorhub/ssp_firmware.c 
b/drivers/misc/sensorhub/ssp_firmware.c
new file mode 100644
index 0000000..2554d64
--- /dev/null
+++ b/drivers/misc/sensorhub/ssp_firmware.c
@@ -0,0 +1,571 @@
+/*
+ *  Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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.
+ *
+ */
+
+#include <linux/firmware.h>
+#include "ssp.h"
+
+#define BOOT_SPI_HZ    500000
+#define NORM_SPI_HZ    5000000
+
+#define APP_SLAVE_ADDR                 0x18
+#define BOOTLOADER_SLAVE_ADDR          0x26
+
+/* Bootloader mode status */
+#define BL_WAITING_BOOTLOAD_CMD                0xc0    /* valid 7 6 bit only */
+#define BL_WAITING_FRAME_DATA          0x80    /* valid 7 6 bit only */
+#define BL_FRAME_CRC_CHECK             0x02
+#define BL_FRAME_CRC_FAIL              0x03
+#define BL_FRAME_CRC_PASS              0x04
+#define BL_APP_CRC_FAIL                        0x40    /* valid 7 8 bit only */
+#define BL_BOOT_STATUS_MASK            0x3f
+
+/* Command to unlock bootloader */
+#define BL_UNLOCK_CMD_MSB              0xaa
+#define BL_UNLOCK_CMD_LSB              0xdc
+
+#define SEND_ADDR_LEN  5
+#define BL_SPI_SOF 0x5A
+#define BL_ACK  0x79
+#define BL_ACK2  0xF9
+#define BL_NACK 0x1F
+
+#define STM_MAX_XFER_SIZE 256
+#define STM_MAX_BUFFER_SIZE 260
+#define STM_APP_ADDR   0x08000000
+
+#define BYTE_DELAY_READ 10
+#define BYTE_DELAY_WRITE 8
+
+#define DEF_ACK_ERASE_NUMBER 7000
+#define DEF_ACKCMD_NUMBER    30
+#define DEF_ACKROOF_NUMBER   20
+
+#define WMEM_COMMAND           0x31  /* Write Memory command          */
+#define GO_COMMAND             0x21  /* GO command                    */
+#define EXT_ER_COMMAND         0x44  /* Erase Memory command          */
+
+#define XOR_RMEM_COMMAND       0xEE  /* Read Memory command           */
+
+#define XOR_WMEM_COMMAND       0xCE  /* Write Memory command          */
+#define XOR_GO_COMMAND         0xDE  /* GO command                    */
+#define XOR_EXT_ER_COMMAND     0xBB  /* Erase Memory command          */
+
+#define EXT_ER_DATA_LEN                3
+#define BLMODE_RETRYCOUNT      3
+
+struct stm32fwu_spi_cmd {
+       u8 cmd;
+       u8 xor_cmd;
+       u8 ack_pad; /* Send this when waiting for an ACK */
+       u8 reserved;
+       int status; /* ACK or NACK (or error) */
+       int timeout; /* This is number of retries */
+       int ack_loops;
+};
+
+static int stm32fwu_spi_wait_for_ack(struct spi_device *spi,
+                               struct stm32fwu_spi_cmd *cmd, u8 dummy_bytes)
+{
+       struct spi_message m;
+       char tx_buf = 0x0;
+       char rx_buf = 0x0;
+       struct spi_transfer     t = {
+               .tx_buf         = &tx_buf,
+               .rx_buf         = &rx_buf,
+               .len            = 1,
+               .bits_per_word = 8,
+       };
+       int i = 0;
+       int ret;
+
+       while (i < cmd->timeout) {
+               tx_buf = dummy_bytes;
+               spi_message_init(&m);
+               spi_message_add_tail(&t, &m);
+
+               ret = spi_sync(spi, &m);
+               if (ret < 0) {
+                       dev_err(&spi->dev, "%s: spi_sync error returned %d\n",
+                                               __func__, ret);
+                       return ret;
+               } else  if ((rx_buf == BL_ACK) || (rx_buf == BL_ACK2)) {
+                       cmd->ack_loops = i;
+                       return BL_ACK;
+               } else if (rx_buf == BL_NACK) {
+                       return (int)rx_buf;
+               }
+               usleep_range(1000, 1100);
+               i++;
+       }
+
+       dev_err(&spi->dev, "%s, Timeout after %d loops\n", __func__,
+               cmd->timeout);
+
+       return -EIO;
+}
+
+static int stm32fwu_spi_send_cmd(struct spi_device *spi,
+                                       struct stm32fwu_spi_cmd *cmd)
+{
+       u8 tx_buf[3] = {0,};
+       u8 rx_buf[3] = {0,};
+       u8 dummy_byte = 0;
+       struct spi_message m;
+       int ret;
+       struct spi_transfer     t = {
+               .tx_buf         = tx_buf,
+               .rx_buf         = rx_buf,
+               .len            = 3,
+               .bits_per_word = 8,
+       };
+
+       spi_message_init(&m);
+
+       tx_buf[0] = BL_SPI_SOF;
+       tx_buf[1] = cmd->cmd;
+       tx_buf[2] = cmd->xor_cmd;
+
+       spi_message_add_tail(&t, &m);
+
+       ret = spi_sync(spi, &m);
+       if (ret < 0) {
+               dev_err(&spi->dev, "%s: spi_sync error returned %d\n",
+                                       __func__, ret);
+               return ret;
+       }
+
+       dummy_byte = cmd->ack_pad;
+
+       /* check for ack/nack and loop until found */
+       ret = stm32fwu_spi_wait_for_ack(spi, cmd, dummy_byte);
+       cmd->status = ret;
+       if (ret != BL_ACK) {
+               dev_err(&spi->dev, "%s, Got NAK or Error %d\n", __func__, ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+
+static int stm32fwu_spi_write(struct spi_device *spi,
+                                       const u8 *buffer, ssize_t len)
+{
+       int ret;
+       u8 rx_buf[STM_MAX_BUFFER_SIZE] = {0,};
+       struct spi_message m;
+       struct spi_transfer     t = {
+               .tx_buf         = buffer,
+               .rx_buf         = rx_buf,
+               .len            = len,
+               .bits_per_word = 8,
+       };
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t, &m);
+
+       ret = spi_sync(spi, &m);
+       if (ret < 0) {
+               dev_err(&spi->dev, "%s: spi_sync error returned %d\n",
+                                       __func__, ret);
+               return ret;
+       }
+
+       return len;
+}
+
+static int send_addr(struct spi_device *spi, u32 fw_addr, int send_short)
+{
+       int res;
+       int i = send_short;
+       int len = SEND_ADDR_LEN - send_short;
+       u8 header[SEND_ADDR_LEN];
+       struct stm32fwu_spi_cmd dummy_cmd;
+
+       dummy_cmd.timeout = DEF_ACKROOF_NUMBER;
+
+       header[0] = (u8)((fw_addr >> 24) & 0xFF);
+       header[1] = (u8)((fw_addr >> 16) & 0xFF);
+       header[2] = (u8)((fw_addr >> 8) & 0xFF);
+       header[3] = (u8)(fw_addr & 0xFF);
+       header[4] = header[0] ^ header[1] ^ header[2] ^ header[3];
+
+       res = stm32fwu_spi_write(spi, &header[i], len);
+       if (res <  len) {
+               dev_err(&spi->dev, "%s, spi_write returned %d\n", __func__,
+                       res);
+               return (res > 0) ? -EIO : res;
+       }
+
+       res = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, BL_ACK);
+       if (res != BL_ACK) {
+               dev_err(&spi->dev, "%s, rcv_ack returned 0x%x\n", __func__,
+                       res);
+               return res;
+       }
+       return 0;
+}
+
+static int fw_write_stm(struct spi_device *spi, u32 fw_addr,
+       int len, const u8 *buffer)
+{
+       int res;
+       struct stm32fwu_spi_cmd cmd;
+       struct stm32fwu_spi_cmd dummy_cmd;
+       int i;
+       u8 xor = 0;
+       u8 send_buff[STM_MAX_BUFFER_SIZE] = {0,};
+
+       cmd.cmd = WMEM_COMMAND;
+       cmd.xor_cmd = XOR_WMEM_COMMAND;
+       cmd.timeout = DEF_ACKCMD_NUMBER;
+       cmd.ack_pad = (u8)((fw_addr >> 24) & 0xFF);
+
+       if (len > STM_MAX_XFER_SIZE) {
+               dev_err(&spi->dev,
+                       "Can't send more than 256 bytes per transaction\n");
+               return -EINVAL;
+       }
+
+       send_buff[0] = len - 1;
+       memcpy(&send_buff[1], buffer, len);
+       for (i = 0; i < (len + 1); i++)
+               xor ^= send_buff[i];
+
+       send_buff[len + 1] = xor;
+
+       res = stm32fwu_spi_send_cmd(spi, &cmd);
+       if (res != BL_ACK) {
+               dev_err(&spi->dev, "%s, spi_send_cmd returned %d\n", __func__,
+                       res);
+               return res;
+       }
+
+       if (cmd.ack_loops > 0)
+               res = send_addr(spi, fw_addr, 1);
+       else
+               res = send_addr(spi, fw_addr, 0);
+
+       if (res != 0) {
+               dev_err(&spi->dev, "%s, send_addr returned %d\n", __func__,
+                       res);
+               return res;
+       }
+
+       res = stm32fwu_spi_write(spi, send_buff, len + 2);
+       if (res <  len) {
+               dev_err(&spi->dev, "%s, spi_write returned %d\n", __func__,
+                       res);
+               return ((res > 0) ? -EIO : res);
+       }
+
+       dummy_cmd.timeout = DEF_ACKROOF_NUMBER;
+
+       usleep_range(100, 150);
+
+       res = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, BL_ACK);
+       if (res == BL_ACK) {
+               return len;
+       } else if (res == BL_NACK) {
+               dev_err(&spi->dev,
+                       "Got NAK waiting for WRITE_MEM to complete\n");
+               return -EPROTO;
+       }
+
+       dev_err(&spi->dev, "timeout waiting for ACK for WRITE_MEM command\n");
+       return -ETIME;
+}
+
+static int fw_erase_stm(struct spi_device *spi)
+{
+       struct stm32fwu_spi_cmd cmd;
+       struct stm32fwu_spi_cmd dummy_cmd;
+       int ret;
+       char buff[EXT_ER_DATA_LEN] = {0xff, 0xff, 0x00};
+
+       cmd.cmd = EXT_ER_COMMAND;
+       cmd.xor_cmd = XOR_EXT_ER_COMMAND;
+       cmd.timeout = DEF_ACKCMD_NUMBER;
+       cmd.ack_pad = 0xFF;
+
+       ret = stm32fwu_spi_send_cmd(spi, &cmd);
+       if (ret < 0) {
+               dev_err(&spi->dev, "fw_erase failed (-EIO)\n");
+               return -EIO;
+       } else if (ret != BL_ACK) {
+               return ret;
+       }
+
+       if (cmd.ack_loops == 0)
+               ret = stm32fwu_spi_write(spi, buff, EXT_ER_DATA_LEN);
+       else
+               ret = stm32fwu_spi_write(spi, buff, EXT_ER_DATA_LEN-1);
+
+       if (ret < (EXT_ER_DATA_LEN - cmd.ack_loops))
+               return -EPROTO;
+
+       dummy_cmd.timeout = DEF_ACK_ERASE_NUMBER;
+       ret = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, BL_ACK);
+
+       ssp_info("%s: stm32fwu_spi_wait_for_ack returned %d (0x%x)\n",
+               __func__, ret, ret);
+
+       if (ret == BL_ACK)
+               return 0;
+       else if (ret == BL_NACK)
+               return -EPROTO;
+
+       return -ETIME;
+
+}
+
+static int load_kernel_fw_bootmode(struct spi_device *spi, const char *pfn)
+{
+       int ret, remaining;
+       unsigned int pos = 0;
+       unsigned int fw_addr = STM_APP_ADDR;
+       int block = STM_MAX_XFER_SIZE;
+       int count = 0, err_count = 0, retry_count = 0;
+       const struct firmware *fw = NULL;
+
+       dev_info(&spi->dev, "ssp_load_fw start\n");
+
+       ret = request_firmware(&fw, pfn, &spi->dev);
+       if (ret) {
+               dev_err(&spi->dev, "Unable to open firmware %s\n", pfn);
+               return ret;
+       }
+
+       remaining = fw->size;
+       while (remaining > 0) {
+               if (block > remaining)
+                       block = remaining;
+
+               while (retry_count < 3) {
+                       ret = fw_write_stm(spi, fw_addr, block, fw->data + pos);
+                       if (ret < block) {
+                               dev_err(&spi->dev,
+                                       "Returned %d writing to addr 0x%08X\n",
+                                       ret,
+                                       fw_addr);
+                               retry_count++;
+                               err_count++;
+                       } else {
+                               retry_count = 0;
+                               break;
+                       }
+               }
+
+               if (ret  < block) {
+                       dev_err(&spi->dev,
+                               "Writing MEM failed: %d, retry cont: %d\n",
+                               ret, err_count);
+                       ret = -EIO;
+                       goto out_load_kernel;
+               }
+
+               remaining -= block;
+               pos += block;
+               fw_addr += block;
+               if (count++ == 20) {
+                       dev_info(&spi->dev, "Updated %u bytes / %u bytes\n",
+                                pos, fw->size);
+                       count = 0;
+               }
+       }
+
+       dev_info(&spi->dev,
+                "Firmware download success.(%d bytes, retry %d)\n", pos,
+                err_count);
+
+out_load_kernel:
+       release_firmware(fw);
+       return ret;
+}
+
+static int change_to_bootmode(struct ssp_data *data)
+{
+       int ret, i;
+       char syncb = BL_SPI_SOF;
+       struct stm32fwu_spi_cmd dummy_cmd;
+
+       dev_info(&data->spi->dev, "ssp_change_to_bootmode\n");
+
+       dummy_cmd.timeout = DEF_ACKCMD_NUMBER;
+
+       gpio_set_value(data->mcu_reset, 0);
+       usleep_range(4000, 4100);
+       gpio_set_value(data->mcu_reset, 1);
+       msleep(45);
+
+       for (i = 0; i < 9; i++) {
+               gpio_set_value(data->mcu_reset, 0);
+               usleep_range(4000, 4100);
+               gpio_set_value(data->mcu_reset, 1);
+               usleep_range(15000, 15500);
+       }
+
+       ret = stm32fwu_spi_write(data->spi, &syncb, 1);
+       ssp_info("stm32fwu_spi_write(sync byte) returned %d\n", ret);
+
+       ret = stm32fwu_spi_wait_for_ack(data->spi, &dummy_cmd, BL_ACK);
+       ssp_info("stm32fwu_spi_wait_for_ack returned %d (0x%x)\n", ret, ret);
+
+       return ret;
+}
+
+void toggle_mcu_reset(struct ssp_data *data)
+{
+       gpio_set_value(data->mcu_reset, 0);
+       usleep_range(1000, 1200);
+       gpio_set_value(data->mcu_reset, 1);
+       msleep(50);
+}
+
+static int update_mcu_bin(struct ssp_data *data, int bin_type)
+{
+       int retry = BLMODE_RETRYCOUNT, ret = 0;
+       struct stm32fwu_spi_cmd cmd;
+
+       cmd.cmd = GO_COMMAND;
+       cmd.xor_cmd = XOR_GO_COMMAND;
+       cmd.timeout = 1000;
+       cmd.ack_pad = (u8)((STM_APP_ADDR >> 24) & 0xFF);
+
+       dev_info(&data->spi->dev, "update_mcu_bin\n");
+
+       do {
+               ret = change_to_bootmode(data);
+               dev_info(&data->spi->dev, "bootmode %d, retry = %d\n", ret,
+                        3 - retry);
+       } while (retry-- > 0 && ret != BL_ACK);
+
+       if (ret != BL_ACK) {
+               dev_err(&data->spi->dev, "%s, change_to_bootmode failed %d\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       ret = fw_erase_stm(data->spi);
+       if (ret < 0) {
+               dev_err(&data->spi->dev, "%s, fw_erase_stm failed %d\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       switch (bin_type) {
+       case KERNEL_BINARY:
+               ret = load_kernel_fw_bootmode(data->spi,
+                                             data->sensorhub_info->fw_name);
+               break;
+       case KERNEL_CRASHED_BINARY:
+               ret = load_kernel_fw_bootmode(data->spi,
+                       data->sensorhub_info->fw_crashed_name);
+               break;
+       default:
+               dev_err(&data->spi->dev, "binary type error!!\n");
+       }
+
+       /* STM : GO USER ADDR */
+       stm32fwu_spi_send_cmd(data->spi, &cmd);
+       if (cmd.ack_loops > 0)
+               send_addr(data->spi, STM_APP_ADDR, 1);
+       else
+               send_addr(data->spi, STM_APP_ADDR, 0);
+
+       return ret;
+}
+
+int forced_to_download_binary(struct ssp_data *data, int bin_type)
+{
+       int ret = 0, retry = 3;
+
+       ssp_dbg("%s, mcu binany update!\n", __func__);
+       ssp_enable(data, false);
+
+       data->fw_dl_state = FW_DL_STATE_DOWNLOADING;
+       dev_info(&data->spi->dev, "%s, DL state = %d\n", __func__,
+                data->fw_dl_state);
+
+       data->spi->max_speed_hz = BOOT_SPI_HZ;
+       if (spi_setup(data->spi))
+               dev_err(&data->spi->dev, "failed to setup spi for ssp_boot\n");
+
+       do {
+               dev_info(&data->spi->dev, "%d try\n", 3 - retry);
+               ret = update_mcu_bin(data, bin_type);
+       } while (retry-- > 0 && ret < 0);
+
+       /* FIXME*/
+       data->spi->max_speed_hz = NORM_SPI_HZ;
+       if (spi_setup(data->spi))
+               dev_err(&data->spi->dev, "failed to setup spi for ssp_norm\n");
+
+       if (ret < 0) {
+               ssp_dbg("%s - update_mcu_bin failed!\n", __func__);
+               data->fw_dl_state = FW_DL_STATE_FAIL;
+               return ret;
+       }
+
+       data->fw_dl_state = FW_DL_STATE_SYNC;
+       dev_info(&data->spi->dev, "%s, DL state = %d\n", __func__,
+                data->fw_dl_state);
+
+       ssp_enable(data, true);
+
+       data->fw_dl_state = FW_DL_STATE_DONE;
+       dev_info(&data->spi->dev, "%s, DL state = %d\n", __func__,
+                data->fw_dl_state);
+
+       return ret;
+}
+
+int check_fwbl(struct ssp_data *data)
+{
+       int retries = 0;
+
+       while (retries++ < 5) {
+               data->cur_firm_rev = get_firmware_rev(data);
+               if (data->cur_firm_rev == SSP_INVALID_REVISION
+                       || data->cur_firm_rev == SSP_INVALID_REVISION2
+                       || data->cur_firm_rev < 0) {
+                       pr_warn("Invalid revision, trying %d time\n", retries);
+               } else {
+                       break;
+               }
+       }
+
+       if (data->cur_firm_rev == SSP_INVALID_REVISION
+               || data->cur_firm_rev == SSP_INVALID_REVISION2) {
+               dev_err(&data->spi->dev, "SSP_INVALID_REVISION\n");
+               return FW_DL_STATE_NEED_TO_SCHEDULE;
+
+       } else {
+               if (data->cur_firm_rev != data->sensorhub_info->fw_rev) {
+                       dev_info(&data->spi->dev,
+                                "MCU Firm Rev : Old = %8u, New = %8u\n",
+                                data->cur_firm_rev,
+                                data->sensorhub_info->fw_rev);
+
+                       return FW_DL_STATE_NEED_TO_SCHEDULE;
+               }
+               dev_info(&data->spi->dev,
+                        "MCU Firm Rev : Old = %8u, New = %8u\n",
+                        data->cur_firm_rev,
+                        data->sensorhub_info->fw_rev);
+       }
+
+       return FW_DL_STATE_NONE;
+}
diff --git a/drivers/misc/sensorhub/ssp_spi.c b/drivers/misc/sensorhub/ssp_spi.c
new file mode 100644
index 0000000..c546c4e
--- /dev/null
+++ b/drivers/misc/sensorhub/ssp_spi.c
@@ -0,0 +1,700 @@
+/*
+ *  Copyright (C) 2012, Samsung Electronics Co. Ltd. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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.
+ *
+ */
+
+#include "ssp.h"
+
+#define SSP_DEV (&data->spi->dev)
+#define GET_MESSAGE_TYPE(data) (data & (3 << SSP_SPI))
+
+/* SSP -> AP Instruction */
+#define MSG2AP_INST_BYPASS_DATA                        0x37
+#define MSG2AP_INST_LIBRARY_DATA               0x01
+#define MSG2AP_INST_DEBUG_DATA                 0x03
+#define MSG2AP_INST_BIG_DATA                   0x04
+#define MSG2AP_INST_META_DATA                  0x05
+#define MSG2AP_INST_TIME_SYNC                  0x06
+#define MSG2AP_INST_RESET                      0x07
+
+#define UNINPLEMENTED -1
+
+static const int offset_map[SSP_SENSOR_MAX] = {
+       [SSP_ACCELEROMETER_SENSOR] =            SSP_ACCELEROMETER_SIZE +
+                                               SSP_TIME_SIZE,
+       [SSP_GYROSCOPE_SENSOR] =                SSP_GYROSCOPE_SIZE+
+                                               SSP_TIME_SIZE,
+       [SSP_GEOMAGNETIC_UNCALIB_SENSOR] =      UNINPLEMENTED,
+       [SSP_GEOMAGNETIC_RAW] =                 UNINPLEMENTED,
+       [SSP_GEOMAGNETIC_SENSOR] =              UNINPLEMENTED,
+       [SSP_PRESSURE_SENSOR] =                 UNINPLEMENTED,
+       [SSP_GESTURE_SENSOR] =                  UNINPLEMENTED,
+       [SSP_PROXIMITY_SENSOR] =                UNINPLEMENTED,
+       [SSP_TEMPERATURE_HUMIDITY_SENSOR] =     UNINPLEMENTED,
+       [SSP_LIGHT_SENSOR] =                    UNINPLEMENTED,
+       [SSP_PROXIMITY_RAW] =                   UNINPLEMENTED,
+       [SSP_ORIENTATION_SENSOR] =              UNINPLEMENTED,
+       [SSP_STEP_DETECTOR] =                   UNINPLEMENTED,
+       [SSP_SIG_MOTION_SENSOR] =               UNINPLEMENTED,
+       [SSP_GYRO_UNCALIB_SENSOR] =             UNINPLEMENTED,
+       [SSP_GAME_ROTATION_VECTOR] =            UNINPLEMENTED,
+       [SSP_ROTATION_VECTOR] =                 UNINPLEMENTED,
+       [SSP_STEP_COUNTER] =                    UNINPLEMENTED ,
+       [SSP_BIO_HRM_RAW] =                     SSP_BIO_HRM_RAW_SIZE +
+                                               SSP_TIME_SIZE,
+       [SSP_BIO_HRM_RAW_FAC] =                 SSP_BIO_HRM_RAW_FAC_SIZE +
+                                               SSP_TIME_SIZE,
+       [SSP_BIO_HRM_LIB] =                     SSP_BIO_HRM_LIB_SIZE +
+                                               SSP_TIME_SIZE,
+};
+
+static int print_mcu_debug(char *data_frame, int *data_index,
+                   int received_len)
+{
+       int length = data_frame[(*data_index)++];
+
+       if (length > received_len - *data_index || length <= 0) {
+               ssp_dbg("[SSP]: MSG From MCU-invalid debug length(%d/%d)\n",
+                       length, received_len);
+               return length ? length : -1;
+       }
+
+       ssp_dbg("[SSP]: MSG From MCU - %s\n", &data_frame[*data_index]);
+       *data_index += length;
+       return 0;
+}
+
+static void clean_msg(struct ssp_msg *msg)
+{
+       if (msg->free_buffer)
+               kfree(msg->buffer);
+       kfree(msg);
+}
+
+static int do_transfer(struct ssp_data *data, struct ssp_msg *msg,
+                      struct completion *done, int timeout)
+{
+       int status = 0;
+       int delay_cnt = 0;
+       bool msg_dead = false, ssp_down = false;
+       /*FIXME REVISIT*/
+       const bool use_no_irq = msg->length == 0;
+
+       msg->dead_hook = &msg_dead;
+       msg->dead = false;
+       msg->done = done;
+
+       mutex_lock(&data->comm_mutex);
+
+       gpio_set_value_cansleep(data->ap_mcu_int, 0);
+       while (gpio_get_value_cansleep(data->mcu_ap_int)) {
+               usleep_range(3000, 3500);
+               ssp_down = data->shut_down;
+               if (ssp_down || delay_cnt++ > 500) {
+                       dev_err(SSP_DEV, " %s - timeout 1\n", __func__);
+                       gpio_set_value_cansleep(data->ap_mcu_int, 1);
+                       status = -1;
+                       goto exit;
+               }
+       }
+
+       status = spi_write(data->spi, msg, 9);
+       if (status != 0) {
+               dev_err(SSP_DEV, "%s spi_write fail\n", __func__);
+               gpio_set_value_cansleep(data->ap_mcu_int, 1);
+               status = -1;
+               goto exit;
+       }
+
+       if (!use_no_irq) {
+               mutex_lock(&data->pending_mutex);
+               list_add_tail(&msg->list, &data->pending_list);
+               mutex_unlock(&data->pending_mutex);
+       }
+
+       delay_cnt = 0;
+       gpio_set_value_cansleep(data->ap_mcu_int, 1);
+       while (!gpio_get_value_cansleep(data->mcu_ap_int)) {
+               usleep_range(3000, 3500);
+               ssp_down = data->shut_down;
+               if (ssp_down || delay_cnt++ > 500) {
+                       dev_err(SSP_DEV, "%s - timeout 2\n", __func__);
+                       status = -2;
+                       goto exit;
+               }
+       }
+
+exit:
+       mutex_unlock(&data->comm_mutex);
+
+       if (ssp_down)
+               dev_err(SSP_DEV, "%s ssp down", __func__);
+
+       if (status == -1) {
+               data->time_out_cnt += ssp_down ? 0 : 1;
+               clean_msg(msg);
+               return status;
+       }
+
+       if (status == 0 && done != NULL)
+               if (wait_for_completion_timeout(done,
+                                               msecs_to_jiffies(timeout)) == 0)
+                       status = -2;
+
+       mutex_lock(&data->pending_mutex);
+       if (!msg_dead) {
+               msg->done = NULL;
+               msg->dead_hook = NULL;
+
+               if (status != 0)
+                       msg->dead = true;
+               if (status == -2)
+                       data->time_out_cnt += ssp_down ? 0 : 1;
+       }
+       mutex_unlock(&data->pending_mutex);
+
+       if (use_no_irq)
+               clean_msg(msg);
+
+       return status;
+}
+
+static inline int ssp_spi_async(struct ssp_data *data, struct ssp_msg *msg)
+{
+       return do_transfer(data, msg, NULL, 0);
+}
+
+static int ssp_spi_sync(struct ssp_data *data, struct ssp_msg *msg, int 
timeout)
+{
+       DECLARE_COMPLETION_ONSTACK(done);
+
+       /* len 0 means that we do not expect an answer after an interrupt */
+       if (msg->length == 0) {
+               dev_err(SSP_DEV, "%s length must not be 0\n", __func__);
+               clean_msg(msg);
+               return 0;
+       }
+
+       return do_transfer(data, msg, &done, timeout);
+}
+
+static int handle_big_data(struct ssp_data *data, char *dataframe,
+                          int *idx) {
+       /* FIXME REWORK*/
+       u8 big_type = 0;
+       struct ssp_big *big = kzalloc(sizeof(*big), GFP_KERNEL);
+
+       big->data = data;
+       big_type = dataframe[(*idx)++];
+       memcpy(&big->length, dataframe + *idx, 4);
+       *idx += 4;
+       memcpy(&big->addr, dataframe + *idx, 4);
+       *idx += 4;
+
+       if (big_type >= BIG_TYPE_MAX) {
+               kfree(big);
+               return -EINVAL;
+       }
+
+       INIT_WORK(&big->work, data->ssp_big_task[big_type]);
+       queue_work(data->wdt_wq, &big->work);
+
+       return 0;
+}
+
+static int parse_dataframe(struct ssp_data *data, char *dataframe,
+                               int len)
+{
+       int idx, sd, ret = 0;
+       u16 length = 0;
+       struct timespec ts;
+       struct ssp_sensor_data *sdata;
+       struct iio_dev **indio_devs = data->sensor_devs;
+
+       getnstimeofday(&ts);
+
+       for (idx = 0; idx < len;) {
+               switch (dataframe[idx++]) {
+               case MSG2AP_INST_BYPASS_DATA:
+                       sd = dataframe[idx++];
+                       if ((sd < 0) || (sd >= SSP_SENSOR_MAX)) {
+                               dev_err(SSP_DEV,
+                                       "Mcu data frame1 error %d\n", sd);
+                               return -1;
+                       }
+
+                       if (indio_devs[sd] != NULL) {
+                               sdata = iio_priv(indio_devs[sd]);
+                               if (sdata->process_data)
+                                       sdata->process_data(
+                                                       indio_devs[sd],
+                                                       &dataframe[idx],
+                                                       data->timestamp);
+                       }
+                       idx += offset_map[sd];
+                       break;
+               case MSG2AP_INST_DEBUG_DATA:
+                       sd = print_mcu_debug(dataframe, &idx, len);
+                       if (sd) {
+                               dev_err(SSP_DEV,
+                                       "Mcu data frame3 error %d\n", sd);
+                               return -1;
+                       }
+                       break;
+               case MSG2AP_INST_LIBRARY_DATA:
+                       idx += length;
+                       break;
+               case MSG2AP_INST_BIG_DATA:
+                       handle_big_data(data, dataframe, &idx);
+                       break;
+               case MSG2AP_INST_TIME_SYNC:
+                       data->time_syncing = true;
+                       break;
+               case MSG2AP_INST_RESET:
+                       queue_refresh_task(data, 0);
+                       break;
+               }
+       }
+
+       if (data->time_syncing)
+               data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+
+       return ret;
+}
+
+int select_irq_msg(struct ssp_data *data)
+{
+       bool found = false;
+       char *buffer;
+       unsigned char msg_type = 0;
+       char temp_buf[4] = {-1};
+       int ret = 0;
+       unsigned short length, msg_options;
+       __le16 len, mo;
+       struct ssp_msg *msg, *n;
+
+       ret = spi_read(data->spi, temp_buf, sizeof(temp_buf));
+       if (ret < 0) {
+               dev_err(SSP_DEV, "header read fail\n");
+               return ret;
+       }
+
+       memcpy(&mo, temp_buf, 2);
+       memcpy(&len, &temp_buf[2], 2);
+
+       length = le16_to_cpu(len);
+       msg_options = le16_to_cpu(mo);
+
+       msg_type = GET_MESSAGE_TYPE(msg_options);
+
+       switch (msg_type) {
+       case AP2HUB_READ:
+       case AP2HUB_WRITE:
+               mutex_lock(&data->pending_mutex);
+               if (!list_empty(&data->pending_list)) {
+                       list_for_each_entry_safe(msg, n, &data->pending_list,
+                                                list) {
+                               if (msg->options == msg_options) {
+                                       list_del(&msg->list);
+                                       found = true;
+                                       break;
+                               }
+                       }
+
+                       if (!found) {
+                               dev_err(SSP_DEV, "Not match error %x\n",
+                                       msg_options);
+                               goto exit;
+                       }
+
+                       if (msg->dead && !msg->free_buffer) {
+                               msg->buffer = kzalloc(msg->length, GFP_KERNEL);
+                               msg->free_buffer = 1;
+                       } /* For dead msg, make a temporary buffer to read. */
+
+                       if (msg_type == AP2HUB_READ)
+                               ret = spi_read(data->spi, msg->buffer,
+                                              msg->length);
+                       if (msg_type == AP2HUB_WRITE) {
+                               ret = spi_write(data->spi, msg->buffer,
+                                               msg->length);
+                               if (msg_options & AP2HUB_RETURN) {
+                                       msg->options =
+                                               AP2HUB_READ | AP2HUB_RETURN;
+                                       msg->length = 1;
+                                       list_add_tail(&msg->list,
+                                                     &data->pending_list);
+                                       goto exit;
+                               }
+                       }
+
+                       if (msg->done != NULL && !completion_done(msg->done))
+                               complete(msg->done);
+                       if (msg->dead_hook != NULL)
+                               *(msg->dead_hook) = true;
+
+                       clean_msg(msg);
+               } else
+                       dev_err(SSP_DEV, "List empty error(%d)\n",
+                               msg_type);
+exit:
+               mutex_unlock(&data->pending_mutex);
+               break;
+       case HUB2AP_WRITE:
+               buffer = kzalloc(length, GFP_KERNEL);
+               if (buffer == NULL) {
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               ret = spi_read(data->spi, buffer, length);
+               if (ret < 0) {
+                       dev_err(SSP_DEV, "spi read fail\n");
+                       break;
+               }
+
+               /* FIXME no return ! */
+               parse_dataframe(data, buffer, length);
+
+               kfree(buffer);
+               break;
+
+       default:
+               dev_err(SSP_DEV, "unknown msg type\n");
+               break;
+       }
+
+       return ret;
+}
+
+void clean_pending_list(struct ssp_data *data)
+{
+       struct ssp_msg *msg, *n;
+
+       mutex_lock(&data->pending_mutex);
+       list_for_each_entry_safe(msg, n, &data->pending_list, list) {
+               list_del(&msg->list);
+               if (msg->done != NULL && !completion_done(msg->done))
+                       complete(msg->done);
+               if (msg->dead_hook != NULL)
+                       *(msg->dead_hook) = true;
+
+               clean_msg(msg);
+       }
+       mutex_unlock(&data->pending_mutex);
+}
+
+int ssp_send_cmd(struct ssp_data *data, char command, int arg)
+{
+       struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+       if (msg == NULL)
+               return -ENOMEM;
+
+
+       msg->cmd = command;
+       msg->length = 0;
+       msg->options = AP2HUB_WRITE;
+       msg->data = cpu_to_le32(arg);
+       msg->free_buffer = 0;
+
+       ssp_dbg("%s - command 0x%x %d\n", __func__, command, arg);
+
+       return ssp_spi_async(data, msg);
+}
+
+int ssp_send_instruction(struct ssp_data *data, unsigned char inst,
+                    unsigned char  sensor_type,  unsigned char  *send_buf,
+                    unsigned char length)
+{
+       struct ssp_msg *msg;
+
+       if (data->fw_dl_state == FW_DL_STATE_DOWNLOADING) {
+               dev_err(SSP_DEV, "%s - Skip Inst! DL state = %d\n",
+                       __func__, data->fw_dl_state);
+               return -EBUSY;
+       } else if ((!(data->sensor_state & (1 << sensor_type)))
+                  && (inst <= MSG2SSP_INST_CHANGE_DELAY)) {
+               dev_err(SSP_DEV, "%s - Bypass Inst Skip! - %u\n",
+                       __func__, sensor_type);
+               return -1; /* just fail */
+       }
+
+       msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+       if (msg == NULL)
+               return -ENOMEM;
+
+       msg->cmd = inst;
+       msg->length = length + 2;
+       msg->options = AP2HUB_WRITE;
+       msg->buffer = kzalloc(length + 2, GFP_KERNEL);
+       if (!msg->buffer) {
+               kfree(msg);
+               return -ENOMEM;
+       }
+       msg->free_buffer = 1;
+
+       msg->buffer[0] = sensor_type;
+       memcpy(&msg->buffer[1], send_buf, length);
+
+       ssp_dbg("%s - Inst = 0x%x, Sensor Type = 0x%x, data = %u\n",
+               __func__, command, sensor_type, msg->buffer[1]);
+
+       return ssp_spi_sync(data, msg, 1000);
+}
+
+int flush(struct ssp_data *data, u8 sensor_type)
+{
+       int ret = 0;
+       char buffer = 0;
+
+       struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+       if (!msg)
+               return -ENOMEM;
+
+       msg->cmd = MSG2SSP_AP_MCU_BATCH_FLUSH;
+       msg->length = 1;
+       msg->options = AP2HUB_READ;
+       msg->data = cpu_to_le32(sensor_type);
+       msg->buffer = &buffer;
+       msg->free_buffer = 0;
+
+       ret = ssp_spi_sync(data, msg, 1000);
+       if (ret < 0) {
+               dev_err(SSP_DEV, "%s - fail %d\n", __func__, ret);
+               return ret;
+       }
+
+       ssp_dbg("%s Sensor Type = 0x%x, data = %u\n", __func__, sensor_type,
+                       buffer);
+
+       return buffer ? 0 : -1;
+}
+
+int get_chipid(struct ssp_data *data)
+{
+       int ret, retries = 0;
+       char buffer = 0;
+       struct ssp_msg *msg;
+
+       while (retries++ < 3) {
+               msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+               if (!msg)
+                       return -ENOMEM;
+
+               msg->cmd = MSG2SSP_AP_WHOAMI;
+               msg->length = 1;
+               msg->options = AP2HUB_READ;
+               msg->buffer = &buffer;
+               msg->free_buffer = 0;
+
+               ret = ssp_spi_sync(data, msg, 1000);
+               if (ret < 0) {
+                       dev_err(SSP_DEV, "%s read fail\n", __func__);
+                       continue;
+               }
+
+               if (buffer == DEVICE_ID)
+                       return buffer;
+       }
+
+       dev_err(SSP_DEV, "%s get chip ID fail\n", __func__);
+
+       return -EIO;
+}
+
+
+int set_sensor_position(struct ssp_data *data)
+{
+       int ret = 0;
+
+       struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+       if (!msg)
+               return -ENOMEM;
+
+       msg->cmd = MSG2SSP_AP_SENSOR_FORMATION;
+       msg->length = 3;
+       msg->options = AP2HUB_WRITE;
+       msg->buffer = kzalloc(3, GFP_KERNEL);
+       if (!msg->buffer) {
+               kfree(msg);
+               return -ENOMEM;
+       }
+
+       msg->free_buffer = 1;
+       /*FIXME char = int*/
+       msg->buffer[0] = data->accel_position;
+       msg->buffer[1] = data->accel_position;
+       msg->buffer[2] = data->mag_position;
+
+       ret = ssp_spi_async(data, msg);
+       if (ret < 0) {
+               dev_err(SSP_DEV, "%s -fail to set_sensor_position %d\n",
+                       __func__, ret);
+       } else {
+               dev_info(SSP_DEV,
+                        "Sensor Posision A : %u, G : %u, M: %u, P: %u\n",
+                        data->accel_position, data->accel_position,
+                        data->mag_position, 0);
+       }
+
+       return ret;
+}
+
+int set_magnetic_static_matrix(struct ssp_data *data)
+{
+       struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+       if (msg == NULL)
+               return -ENOMEM;
+
+       msg->cmd = MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX;
+       msg->length = data->mag_matrix_size;
+       msg->options = AP2HUB_WRITE;
+
+       msg->buffer = kzalloc(data->mag_matrix_size, GFP_KERNEL);
+       if (!msg->buffer) {
+               kfree(msg);
+               return -ENOMEM;
+       }
+
+       msg->free_buffer = 1;
+       /* matrix is u8 so do not bother with endianness*/
+       memcpy(msg->buffer, data->mag_matrix, data->mag_matrix_size);
+
+       return ssp_spi_async(data, msg);
+}
+
+unsigned int get_sensor_scanning_info(struct ssp_data *data)
+{
+       int ret, z;
+       __le32 result;
+       u32 cpu_result;
+       char bin[SSP_SENSOR_MAX + 1] = {0};
+
+       struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+       if (!msg)
+               return 0;
+
+       msg->cmd = MSG2SSP_AP_SENSOR_SCANNING;
+       msg->length = 4;
+       msg->options = AP2HUB_READ;
+       msg->buffer = (char *)&result;
+       msg->free_buffer = 0;
+
+       ret = ssp_spi_sync(data, msg, 1000);
+       if (ret < 0) {
+               dev_err(SSP_DEV, "%s - spi read fail %d\n", __func__, ret);
+               return 0;
+       }
+
+       cpu_result = le32_to_cpu(result);
+
+       for (z = 0; z < SSP_SENSOR_MAX; ++z)
+               bin[SSP_SENSOR_MAX - 1 - z] =
+                       (cpu_result & (1 << z)) ? '1' : '0';
+
+       dev_info(SSP_DEV, "%s state: %s\n", __func__, bin);
+
+       return cpu_result;
+}
+
+unsigned int get_firmware_rev(struct ssp_data *data)
+{
+       int ret;
+       __le32 result;
+
+       struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+       if (!msg)
+               return  SSP_INVALID_REVISION;
+
+       msg->cmd = MSG2SSP_AP_FIRMWARE_REV;
+       msg->length = 4;
+       msg->options = AP2HUB_READ;
+       msg->buffer = (char *)&result;
+       msg->free_buffer = 0;
+
+       ret = ssp_spi_sync(data, msg, 1000);
+       if (ret  < 0) {
+               dev_err(SSP_DEV, "%s - transfer fail %d\n", __func__, ret);
+               return SSP_INVALID_REVISION;
+       }
+
+       return le32_to_cpu(result);
+}
+
+int get_fuserom_data(struct ssp_data *data)
+{
+       int ret = 0;
+       char buffer[3] = {0};
+
+       struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+       if (!msg)
+               return -ENOMEM;
+
+       msg->cmd = MSG2SSP_AP_FUSEROM;
+       msg->length = sizeof(buffer);
+       msg->options = AP2HUB_READ;
+       msg->buffer = buffer;
+       msg->free_buffer = 0;
+
+       ret = ssp_spi_sync(data, msg, 1000);
+       if (ret >= 0) {
+               data->fuse_rom_data[0] = buffer[0];
+               data->fuse_rom_data[1] = buffer[1];
+               data->fuse_rom_data[2] = buffer[2];
+       } else {
+               data->fuse_rom_data[0] = 0;
+               data->fuse_rom_data[1] = 0;
+               data->fuse_rom_data[2] = 0;
+               return ret;
+       }
+
+       dev_info(SSP_DEV, "FUSE ROM Data %d , %d, %d\n",
+                data->fuse_rom_data[0], data->fuse_rom_data[1],
+                data->fuse_rom_data[2]);
+
+       return 0;
+}
+
+int ssp_start_big_data(struct ssp_data *data, u8 type, u32 len)
+{
+       __le32 llen;
+
+       struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+       if (!msg)
+               return -ENOMEM;
+
+       msg->cmd = MSG2SSP_AP_START_BIG_DATA;
+       msg->length = 5;
+       msg->options = AP2HUB_WRITE;
+       msg->buffer = kzalloc(msg->length, GFP_KERNEL);
+       if (!msg->buffer) {
+               kfree(msg);
+               return -ENOMEM;
+       }
+
+       msg->free_buffer = 1;
+       msg->buffer[0] = type;
+
+       llen = cpu_to_le32(len);
+       memcpy(&msg->buffer, &llen, sizeof(unsigned int));
+
+       return ssp_spi_async(data, msg);
+}
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to