On 28/01/15 14:05, Karol Wrona wrote:
> 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.
> 
> Adds common sensorhub library for sensorhub driver and iio drivers
> which uses sensorhub MCU to communicate with sensors.
> 
> Signed-off-by: Karol Wrona <k.wr...@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.p...@samsung.com>
A couple of build errors from this one...

I've fixed them up and applied to the togreg branch of iio.git.
Pushed out as testing.  If you get a chance to check I didn't
mess anything up that would be great.
> ---
>  drivers/iio/common/Kconfig               |    1 +
>  drivers/iio/common/Makefile              |    1 +
>  drivers/iio/common/ssp_sensors/Kconfig   |   26 ++
>  drivers/iio/common/ssp_sensors/Makefile  |    8 +
>  drivers/iio/common/ssp_sensors/ssp.h     |  257 +++++++++++
>  drivers/iio/common/ssp_sensors/ssp_dev.c |  712 
> ++++++++++++++++++++++++++++++
>  drivers/iio/common/ssp_sensors/ssp_spi.c |  608 +++++++++++++++++++++++++
>  include/linux/iio/common/ssp_sensors.h   |   82 ++++
>  8 files changed, 1695 insertions(+)
>  create mode 100644 drivers/iio/common/ssp_sensors/Kconfig
>  create mode 100644 drivers/iio/common/ssp_sensors/Makefile
>  create mode 100644 drivers/iio/common/ssp_sensors/ssp.h
>  create mode 100644 drivers/iio/common/ssp_sensors/ssp_dev.c
>  create mode 100644 drivers/iio/common/ssp_sensors/ssp_spi.c
>  create mode 100644 include/linux/iio/common/ssp_sensors.h
> 
> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> index 0b6e97d..790f106 100644
> --- a/drivers/iio/common/Kconfig
> +++ b/drivers/iio/common/Kconfig
> @@ -3,4 +3,5 @@
>  #
>  
>  source "drivers/iio/common/hid-sensors/Kconfig"
> +source "drivers/iio/common/ssp_sensors/Kconfig"
>  source "drivers/iio/common/st_sensors/Kconfig"
> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> index 3112df0..b1e4d9c 100644
> --- a/drivers/iio/common/Makefile
> +++ b/drivers/iio/common/Makefile
> @@ -8,4 +8,5 @@
>  
>  # When adding new entries keep the list in alphabetical order
>  obj-y += hid-sensors/
> +obj-y += ssp_sensors/
>  obj-y += st_sensors/
> diff --git a/drivers/iio/common/ssp_sensors/Kconfig 
> b/drivers/iio/common/ssp_sensors/Kconfig
> new file mode 100644
> index 0000000..0ea4faf
> --- /dev/null
> +++ b/drivers/iio/common/ssp_sensors/Kconfig
> @@ -0,0 +1,26 @@
> +#
> +# SSP sensor drivers and commons configuration
> +#
> +menu "SSP Sensor Common"
> +
> +config IIO_SSP_SENSORS_COMMONS
> +     tristate "Commons for all SSP Sensor IIO drivers"
> +     depends on IIO_SSP_SENSORHUB
> +     select IIO_BUFFER
> +     select IIO_KFIFO_BUF
> +     help
> +       Say yes here to build commons for SSP sensors.
> +       To compile this as a module, choose M here: the module
> +       will be called ssp_iio.
> +
> +config IIO_SSP_SENSORHUB
> +     tristate "Samsung Sensorhub driver"
> +     depends on SPI
> +     select MFD_CORE
> +     help
> +       SSP driver for sensorhub. +     If you say yes here you get ssp 
> support for sensorhub.
> +       To compile this driver as a module, choose M here: the
> +       module will be called sensorhub.
> +
> +endmenu
> diff --git a/drivers/iio/common/ssp_sensors/Makefile 
> b/drivers/iio/common/ssp_sensors/Makefile
> new file mode 100644
> index 0000000..1e0389e
> --- /dev/null
> +++ b/drivers/iio/common/ssp_sensors/Makefile
> @@ -0,0 +1,8 @@
> +#
> +# Makefile for SSP sensor drivers and commons.
> +#
> +
> +sensorhub-objs                               := ssp_dev.o ssp_spi.o
> +obj-$(CONFIG_IIO_SSP_SENSORHUB)              += sensorhub.o
> +
> +obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS)        += ssp_iio.o
This file isn't in this patch.
> diff --git a/drivers/iio/common/ssp_sensors/ssp.h 
> b/drivers/iio/common/ssp_sensors/ssp.h
> new file mode 100644
> index 0000000..b910e91
> --- /dev/null
> +++ b/drivers/iio/common/ssp_sensors/ssp.h
> @@ -0,0 +1,257 @@
> +/*
> + *  Copyright (C) 2014, 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/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/iio/common/ssp_sensors.h>
> +#include <linux/iio/iio.h>
> +#include <linux/spi/spi.h>
> +
> +#define SSP_DEVICE_ID                0x55
> +
> +#ifdef SSP_DBG
> +#define ssp_dbg(format, ...) pr_info("[SSP] "format, ##__VA_ARGS__)
> +#else
> +#define ssp_dbg(format, ...)
> +#endif
> +
> +#define SSP_SW_RESET_TIME            3000
> +/* Sensor polling in ms */
> +#define SSP_DEFAULT_POLLING_DELAY    200
> +#define SSP_DEFAULT_RETRIES          3
> +#define SSP_DATA_PACKET_SIZE         960
> +#define SSP_HEADER_BUFFER_SIZE               4
> +
> +enum {
> +     SSP_KERNEL_BINARY = 0,
> +     SSP_KERNEL_CRASHED_BINARY,
> +};
> +
> +enum {
> +     SSP_INITIALIZATION_STATE = 0,
> +     SSP_NO_SENSOR_STATE,
> +     SSP_ADD_SENSOR_STATE,
> +     SSP_RUNNING_SENSOR_STATE,
> +};
> +
> +/* Firmware download STATE */
> +enum {
> +     SSP_FW_DL_STATE_FAIL = -1,
> +     SSP_FW_DL_STATE_NONE = 0,
> +     SSP_FW_DL_STATE_NEED_TO_SCHEDULE,
> +     SSP_FW_DL_STATE_SCHEDULED,
> +     SSP_FW_DL_STATE_DOWNLOADING,
> +     SSP_FW_DL_STATE_SYNC,
> +     SSP_FW_DL_STATE_DONE,
> +};
> +
> +#define SSP_INVALID_REVISION                 99999
> +#define SSP_INVALID_REVISION2                        0xffffff
> +
> +/* AP -> SSP Instruction */
> +#define SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD   0xa1
> +#define SSP_MSG2SSP_INST_BYPASS_SENSOR_RM    0xa2
> +#define SSP_MSG2SSP_INST_REMOVE_ALL          0xa3
> +#define SSP_MSG2SSP_INST_CHANGE_DELAY                0xa4
> +#define SSP_MSG2SSP_INST_LIBRARY_ADD         0xb1
> +#define SSP_MSG2SSP_INST_LIBRARY_REMOVE              0xb2
> +#define SSP_MSG2SSP_INST_LIB_NOTI            0xb4
> +#define SSP_MSG2SSP_INST_LIB_DATA            0xc1
> +
> +#define SSP_MSG2SSP_AP_MCU_SET_GYRO_CAL              0xcd
> +#define SSP_MSG2SSP_AP_MCU_SET_ACCEL_CAL     0xce
> +#define SSP_MSG2SSP_AP_STATUS_SHUTDOWN               0xd0
> +#define SSP_MSG2SSP_AP_STATUS_WAKEUP         0xd1
> +#define SSP_MSG2SSP_AP_STATUS_SLEEP          0xd2
> +#define SSP_MSG2SSP_AP_STATUS_RESUME         0xd3
> +#define SSP_MSG2SSP_AP_STATUS_SUSPEND                0xd4
> +#define SSP_MSG2SSP_AP_STATUS_RESET          0xd5
> +#define SSP_MSG2SSP_AP_STATUS_POW_CONNECTED  0xd6
> +#define SSP_MSG2SSP_AP_STATUS_POW_DISCONNECTED       0xd7
> +#define SSP_MSG2SSP_AP_TEMPHUMIDITY_CAL_DONE 0xda
> +#define SSP_MSG2SSP_AP_MCU_SET_DUMPMODE              0xdb
> +#define SSP_MSG2SSP_AP_MCU_DUMP_CHECK                0xdc
> +#define SSP_MSG2SSP_AP_MCU_BATCH_FLUSH               0xdd
> +#define SSP_MSG2SSP_AP_MCU_BATCH_COUNT               0xdf
> +
> +#define SSP_MSG2SSP_AP_WHOAMI                                0x0f
> +#define SSP_MSG2SSP_AP_FIRMWARE_REV                  0xf0
> +#define SSP_MSG2SSP_AP_SENSOR_FORMATION                      0xf1
> +#define SSP_MSG2SSP_AP_SENSOR_PROXTHRESHOLD          0xf2
> +#define SSP_MSG2SSP_AP_SENSOR_BARCODE_EMUL           0xf3
> +#define SSP_MSG2SSP_AP_SENSOR_SCANNING                       0xf4
> +#define SSP_MSG2SSP_AP_SET_MAGNETIC_HWOFFSET         0xf5
> +#define SSP_MSG2SSP_AP_GET_MAGNETIC_HWOFFSET         0xf6
> +#define SSP_MSG2SSP_AP_SENSOR_GESTURE_CURRENT                0xf7
> +#define SSP_MSG2SSP_AP_GET_THERM                     0xf8
> +#define SSP_MSG2SSP_AP_GET_BIG_DATA                  0xf9
> +#define SSP_MSG2SSP_AP_SET_BIG_DATA                  0xfa
> +#define SSP_MSG2SSP_AP_START_BIG_DATA                        0xfb
> +#define SSP_MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX    0xfd
> +#define SSP_MSG2SSP_AP_SENSOR_TILT                   0xea
> +#define SSP_MSG2SSP_AP_MCU_SET_TIME                  0xfe
> +#define SSP_MSG2SSP_AP_MCU_GET_TIME                  0xff
> +
> +#define SSP_MSG2SSP_AP_FUSEROM                               0x01
> +
> +/* voice data */
> +#define SSP_TYPE_WAKE_UP_VOICE_SERVICE                       0x01
> +#define SSP_TYPE_WAKE_UP_VOICE_SOUND_SOURCE_AM               0x01
> +#define SSP_TYPE_WAKE_UP_VOICE_SOUND_SOURCE_GRAMMER  0x02
> +
> +/* Factory Test */
> +#define SSP_ACCELEROMETER_FACTORY                    0x80
> +#define SSP_GYROSCOPE_FACTORY                                0x81
> +#define SSP_GEOMAGNETIC_FACTORY                              0x82
> +#define SSP_PRESSURE_FACTORY                         0x85
> +#define SSP_GESTURE_FACTORY                          0x86
> +#define SSP_TEMPHUMIDITY_CRC_FACTORY                 0x88
> +#define SSP_GYROSCOPE_TEMP_FACTORY                   0x8a
> +#define SSP_GYROSCOPE_DPS_FACTORY                    0x8b
> +#define SSP_MCU_FACTORY                                      0x8c
> +#define SSP_MCU_SLEEP_FACTORY                                0x8d
> +
> +/* SSP -> AP ACK about write CMD */
> +#define SSP_MSG_ACK          0x80    /* ACK from SSP to AP */
> +#define SSP_MSG_NAK          0x70    /* NAK from SSP to AP */
> +
> +struct ssp_sensorhub_info {
> +     char *fw_name;
> +     char *fw_crashed_name;
> +     unsigned int fw_rev;
> +     const u8 * const mag_table;
> +     const unsigned int mag_length;
> +};
> +
> +/* ssp_msg options bit */
> +#define SSP_RW               0
> +#define SSP_INDEX    3
> +
> +#define SSP_AP2HUB_READ              0
> +#define SSP_AP2HUB_WRITE     1
> +#define SSP_HUB2AP_WRITE     2
> +#define SSP_AP2HUB_READY     3
> +#define SSP_AP2HUB_RETURN    4
> +
> +/**
> + * struct ssp_data - ssp platformdata structure
> + * @spi:             spi device
> + * @sensorhub_info:  info about sensorhub board specific features
> + * @wdt_timer:               watchdog timer
> + * @work_wdt:                watchdog work
> + * @work_firmware:   firmware upgrade work queue
> + * @work_refresh:    refresh work queue for reset request from MCU
> + * @shut_down:               shut down flag
> + * @mcu_dump_mode:   mcu dump mode for debug
> + * @time_syncing:    time syncing indication flag
> + * @timestamp:               previous time in ns calculated for time syncing
> + * @check_status:    status table for each sensor
> + * @com_fail_cnt:    communication fail count
> + * @reset_cnt:               reset count
> + * @timeout_cnt:     timeout count
> + * @available_sensors:       available sensors seen by sensorhub (bit array)
> + * @cur_firm_rev:    cached current firmware revision
> + * @last_resume_state:       last AP resume/suspend state used to handle the 
> PM
> + *                      state of ssp
> + * @last_ap_state:   (obsolete) sleep notification for MCU
> + * @sensor_enable:   sensor enable mask
> + * @delay_buf:               data acquisition intervals table
> + * @batch_latency_buf:       yet unknown but existing in communication 
> protocol
> + * @batch_opt_buf:   yet unknown but existing in communication protocol
> + * @accel_position:  yet unknown but existing in communication protocol
> + * @mag_position:    yet unknown but existing in communication protocol
> + * @fw_dl_state:     firmware download state
> + * @comm_lock:               lock protecting the handshake
> + * @pending_lock:    lock protecting pending list and completion
> + * @mcu_reset_gpio:  mcu reset line
> + * @ap_mcu_gpio:     ap to mcu gpio line
> + * @mcu_ap_gpio:     mcu to ap gpio line
> + * @pending_list:    pending list for messages queued to be sent/read
> + * @sensor_devs:     registered IIO devices table
> + * @enable_refcount: enable reference count for wdt (watchdog timer)
> + * @header_buffer:   cache aligned buffer for packet header
> + */
> +struct ssp_data {
> +     struct spi_device *spi;
> +     struct ssp_sensorhub_info *sensorhub_info;
> +     struct timer_list wdt_timer;
> +     struct work_struct work_wdt;
> +     struct delayed_work work_refresh;
> +
> +     bool shut_down;
> +     bool mcu_dump_mode;
> +     bool time_syncing;
> +     int64_t timestamp;
> +
> +     int check_status[SSP_SENSOR_MAX];
> +
> +     unsigned int com_fail_cnt;
> +     unsigned int reset_cnt;
> +     unsigned int timeout_cnt;
> +
> +     unsigned int available_sensors;
> +     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;
> +
> +     struct mutex comm_lock;
> +     struct mutex pending_lock;
> +
> +     int mcu_reset_gpio;
> +     int ap_mcu_gpio;
> +     int mcu_ap_gpio;
> +
> +     struct list_head pending_list;
> +
> +     struct iio_dev *sensor_devs[SSP_SENSOR_MAX];
> +     atomic_t enable_refcount;
> +
> +     __le16 header_buffer[SSP_HEADER_BUFFER_SIZE / sizeof(__le16)]
> +             ____cacheline_aligned;
> +};
> +
> +void ssp_clean_pending_list(struct ssp_data *data);
> +
> +int ssp_command(struct ssp_data *data, char command, int arg);
> +
> +int ssp_send_instruction(struct ssp_data *data, u8 inst, u8 sensor_type,
> +                      u8 *send_buf, u8 length);
> +
> +int ssp_irq_msg(struct ssp_data *data);
> +
> +int ssp_get_chipid(struct ssp_data *data);
> +
> +int ssp_set_magnetic_matrix(struct ssp_data *data);
> +
> +unsigned int ssp_get_sensor_scanning_info(struct ssp_data *data);
> +
> +unsigned int ssp_get_firmware_rev(struct ssp_data *data);
> +
> +int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay);
> +
> +#endif /* __SSP_SENSORHUB_H__ */
> diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c 
> b/drivers/iio/common/ssp_sensors/ssp_dev.c
> new file mode 100644
> index 0000000..6b22115
> --- /dev/null
> +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c
> @@ -0,0 +1,712 @@
> +/*
> + *  Copyright (C) 2014, 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/iio/iio.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_platform.h>
> +#include "ssp.h"
> +
> +#define SSP_WDT_TIME                 10000
> +#define SSP_LIMIT_RESET_CNT          20
> +#define SSP_LIMIT_TIMEOUT_CNT                3
> +
> +/* It is possible that it is max clk rate for version 1.0 of bootcode */
> +#define SSP_BOOT_SPI_HZ      400000
> +
> +/*
> + * These fields can look enigmatic but this structure is used mainly to flat
> + * some values and depends on command type.
> + */
> +struct ssp_instruction {
> +     __le32 a;
> +     __le32 b;
> +     u8 c;
> +} __attribute__((__packed__));
> +
> +static const u8 ssp_magnitude_table[] = {110, 85, 171, 71, 203, 195, 0, 67,
> +     208, 56, 175, 244, 206, 213, 0, 92, 250, 0, 55, 48, 189, 252, 171,
> +     243, 13, 45, 250};
> +
> +static const struct ssp_sensorhub_info ssp_rinato_info = {
> +     .fw_name = "ssp_B2.fw",
> +     .fw_crashed_name = "ssp_crashed.fw",
> +     .fw_rev = 14052300,
> +     .mag_table = ssp_magnitude_table,
> +     .mag_length = ARRAY_SIZE(ssp_magnitude_table),
> +};
> +
> +static const struct ssp_sensorhub_info ssp_thermostat_info = {
> +     .fw_name = "thermostat_B2.fw",
> +     .fw_crashed_name = "ssp_crashed.fw",
> +     .fw_rev = 14080600,
> +     .mag_table = ssp_magnitude_table,
> +     .mag_length = ARRAY_SIZE(ssp_magnitude_table),
> +};
> +
> +static const struct mfd_cell sensorhub_sensor_devs[] = {
> +     {
> +             .name = "ssp-accelerometer",
> +     },
> +     {
> +             .name = "ssp-gyroscope",
> +     },
> +};
> +
> +static void ssp_toggle_mcu_reset_gpio(struct ssp_data *data)
> +{
> +     gpio_set_value(data->mcu_reset_gpio, 0);
> +     usleep_range(1000, 1200);
> +     gpio_set_value(data->mcu_reset_gpio, 1);
> +     msleep(50);
> +}
> +
> +static void ssp_sync_available_sensors(struct ssp_data *data)
> +{
> +     int i, ret;
> +
> +     for (i = 0; i < SSP_SENSOR_MAX; ++i) {
> +             if (data->available_sensors & BIT(i)) {
> +                     ret = ssp_enable_sensor(data, i, data->delay_buf[i]);
> +                     if (ret < 0) {
> +                             dev_err(&data->spi->dev,
> +                                     "Sync sensor nr: %d fail\n", i);
> +                             continue;
> +                     }
> +             }
> +     }
> +
> +     ret = ssp_command(data, SSP_MSG2SSP_AP_MCU_SET_DUMPMODE,
> +                       data->mcu_dump_mode);
> +     if (ret < 0)
> +             dev_err(&data->spi->dev,
> +                     "SSP_MSG2SSP_AP_MCU_SET_DUMPMODE failed\n");
> +}
> +
> +static void ssp_enable_mcu(struct ssp_data *data, bool enable)
> +{
> +     dev_info(&data->spi->dev, "current shutdown = %d, old = %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_warn(&data->spi->dev, "current shutdown = %d, old = %d\n",
> +                      enable, data->shut_down);
> +     }
> +}
> +
> +/*
> + * This function is the first one which communicates with the mcu so it is
> + * possible that the first attempt will fail
> + */
> +static int ssp_check_fwbl(struct ssp_data *data)
> +{
> +     int retries = 0;
> +
> +     while (retries++ < 5) {
> +             data->cur_firm_rev = ssp_get_firmware_rev(data);
> +             if (data->cur_firm_rev == SSP_INVALID_REVISION ||
> +                 data->cur_firm_rev == SSP_INVALID_REVISION2) {
> +                     dev_warn(&data->spi->dev,
> +                              "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 SSP_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);
> +
> +     if (data->cur_firm_rev != data->sensorhub_info->fw_rev)
> +             return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;
> +
> +     return SSP_FW_DL_STATE_NONE;
> +}
> +
> +static void ssp_reset_mcu(struct ssp_data *data)
> +{
> +     ssp_enable_mcu(data, false);
> +     ssp_clean_pending_list(data);
> +     ssp_toggle_mcu_reset_gpio(data);
> +     ssp_enable_mcu(data, true);
> +}
> +
> +static void ssp_wdt_work_func(struct work_struct *work)
> +{
> +     struct ssp_data *data = container_of(work, struct ssp_data, work_wdt);
> +
> +     dev_err(&data->spi->dev, "%s - Sensor state: 0x%x, RC: %u, CC: %u\n",
> +             __func__, data->available_sensors, data->reset_cnt,
> +             data->com_fail_cnt);
> +
> +     ssp_reset_mcu(data);
> +     data->com_fail_cnt = 0;
> +     data->timeout_cnt = 0;
> +}
> +
> +static void ssp_wdt_timer_func(unsigned long ptr)
> +{
> +     struct ssp_data *data = (struct ssp_data *)ptr;
> +
> +     switch (data->fw_dl_state) {
> +     case SSP_FW_DL_STATE_FAIL:
> +     case SSP_FW_DL_STATE_DOWNLOADING:
> +     case SSP_FW_DL_STATE_SYNC:
> +             goto _mod;
> +     }
> +
> +     if (data->timeout_cnt > SSP_LIMIT_TIMEOUT_CNT ||
> +         data->com_fail_cnt > SSP_LIMIT_RESET_CNT)
> +             queue_work(system_power_efficient_wq, &data->work_wdt);
> +_mod:
> +     mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
> +}
> +
> +static void ssp_enable_wdt_timer(struct ssp_data *data)
> +{
> +     mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
> +}
> +
> +static void ssp_disable_wdt_timer(struct ssp_data *data)
> +{
> +     del_timer_sync(&data->wdt_timer);
> +     cancel_work_sync(&data->work_wdt);
> +}
> +
> +/**
> + * 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
> + *
> + * Returns 0 or negative value in case of error
> + */
> +int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,
> +                   u32 delay)
> +{
> +     int ret;
> +     struct ssp_instruction 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 SSP_INITIALIZATION_STATE:
> +             /* do calibration step, now just enable */
> +     case SSP_ADD_SENSOR_STATE:
> +             ret = ssp_send_instruction(data,
> +                                        SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD,
> +                                        type,
> +                                        (u8 *)&to_send, sizeof(to_send));
> +             if (ret < 0) {
> +                     dev_err(&data->spi->dev, "Enabling sensor failed\n");
> +                     data->check_status[type] = SSP_NO_SENSOR_STATE;
> +                     goto derror;
> +             }
> +
> +             data->sensor_enable |= BIT(type);
> +             data->check_status[type] = SSP_RUNNING_SENSOR_STATE;
> +             break;
> +     case SSP_RUNNING_SENSOR_STATE:
> +             ret = ssp_send_instruction(data,
> +                                        SSP_MSG2SSP_INST_CHANGE_DELAY, type,
> +                                        (u8 *)&to_send, sizeof(to_send));
> +             if (ret < 0) {
> +                     dev_err(&data->spi->dev,
> +                             "Changing sensor delay failed\n");
> +                     goto derror;
> +             }
> +             break;
> +     default:
> +             data->check_status[type] = SSP_ADD_SENSOR_STATE;
> +             break;
> +     }
> +
> +     data->delay_buf[type] = delay;
> +
> +     if (atomic_inc_return(&data->enable_refcount) == 1)
> +             ssp_enable_wdt_timer(data);
> +
> +     return 0;
> +
> +derror:
> +     return ret;
> +}
> +EXPORT_SYMBOL(ssp_enable_sensor);
> +
> +/**
> + * ssp_change_delay() - changes data acquisition for sensor
> + * @data:    sensorhub structure
> + * @type:    SSP sensor type
> + * @delay:   delay in ms
> + *
> + * Returns 0 or negative value in case of error
> + */
> +int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type,
> +                  u32 delay)
> +{
> +     int ret;
> +     struct ssp_instruction 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, SSP_MSG2SSP_INST_CHANGE_DELAY, type,
> +                                (u8 *)&to_send, sizeof(to_send));
> +     if (ret < 0) {
> +             dev_err(&data->spi->dev, "Changing sensor delay failed\n");
> +             return ret;
> +     }
> +
> +     data->delay_buf[type] = delay;
> +
> +     return 0;
> +}
> +EXPORT_SYMBOL(ssp_change_delay);
> +
> +/**
> + * ssp_disable_sensor() - disables sensor
> + *
> + * @data:    sensorhub structure
> + * @type:    SSP sensor type
> + *
> + * Returns 0 or negative value in case of error
> + */
> +int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type)
> +{
> +     int ret;
> +     __le32 command;
> +
> +     if (data->sensor_enable & BIT(type)) {
> +             command = cpu_to_le32(data->delay_buf[type]);
> +
> +             ret = ssp_send_instruction(data,
> +                                        SSP_MSG2SSP_INST_BYPASS_SENSOR_RM,
> +                                        type, (u8 *)&command,
> +                                        sizeof(command));
> +             if (ret < 0) {
> +                     dev_err(&data->spi->dev, "Remove sensor fail\n");
> +                     return ret;
> +             }
> +
> +             data->sensor_enable &= ~BIT(type);
> +     }
> +
> +     data->check_status[type] = SSP_ADD_SENSOR_STATE;
> +
> +     if (atomic_dec_and_test(&data->enable_refcount))
> +             ssp_disable_wdt_timer(data);
> +
> +     return 0;
> +}
> +EXPORT_SYMBOL(ssp_disable_sensor);
> +
> +static irqreturn_t ssp_irq_thread_fn(int irq, void *dev_id)
> +{
> +     struct ssp_data *data = dev_id;
> +
> +     /*
> +      * This wrapper is done to preserve error path for ssp_irq_msg, also
> +      * it is defined in different file.
> +      */
> +     ssp_irq_msg(data);
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static int ssp_initialize_mcu(struct ssp_data *data)
> +{
> +     int ret;
> +
> +     ssp_clean_pending_list(data);
> +
> +     ret = ssp_get_chipid(data);
> +     if (ret != SSP_DEVICE_ID) {
> +             dev_err(&data->spi->dev, "%s - MCU %s ret = %d\n", __func__,
> +                     ret < 0 ? "is not working" : "identification failed",
> +                     ret);
> +             return ret < 0 ? ret : -ENODEV;
> +     }
> +
> +     dev_info(&data->spi->dev, "MCU device ID = %d\n", ret);
> +
> +     /*
> +      * needs clarification, for now do not want to export all transfer
> +      * methods to sensors' drivers
> +      */
> +     ret = ssp_set_magnetic_matrix(data);
> +     if (ret < 0) {
> +             dev_err(&data->spi->dev,
> +                     "%s - ssp_set_magnetic_matrix failed\n", __func__);
> +             return ret;
> +     }
> +
> +     data->available_sensors = ssp_get_sensor_scanning_info(data);
> +     if (data->available_sensors == 0) {
> +             dev_err(&data->spi->dev,
> +                     "%s - ssp_get_sensor_scanning_info failed\n", __func__);
> +             return -EIO;
> +     }
> +
> +     data->cur_firm_rev = ssp_get_firmware_rev(data);
> +     dev_info(&data->spi->dev, "MCU Firm Rev : New = %8u\n",
> +              data->cur_firm_rev);
> +
> +     return ssp_command(data, SSP_MSG2SSP_AP_MCU_DUMP_CHECK, 0);
> +}
> +
> +/*
> + * sensorhub can request its reinitialization as some brutal and rare error
> + * handling. It can be requested from the MCU.
> + */
> +static void ssp_refresh_task(struct work_struct *work)
> +{
> +     struct ssp_data *data = container_of((struct delayed_work *)work,
> +                                          struct ssp_data, work_refresh);
> +
> +     dev_info(&data->spi->dev, "refreshing\n");
> +
> +     data->reset_cnt++;
> +
> +     if (ssp_initialize_mcu(data) >= 0) {
> +             ssp_sync_available_sensors(data);
> +             if (data->last_ap_state != 0)
> +                     ssp_command(data, data->last_ap_state, 0);
> +
> +             if (data->last_resume_state != 0)
> +                     ssp_command(data, data->last_resume_state, 0);
> +
> +             data->timeout_cnt = 0;
> +             data->com_fail_cnt = 0;
> +     }
> +}
> +
> +int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay)
> +{
> +     cancel_delayed_work_sync(&data->work_refresh);
> +
> +     return queue_delayed_work(system_power_efficient_wq,
> +                               &data->work_refresh,
> +                               msecs_to_jiffies(delay));
> +}
> +
> +#ifdef CONFIG_OF
> +static struct of_device_id ssp_of_match[] = {
> +     {
> +             .compatible     = "samsung,sensorhub-rinato",
> +             .data           = &ssp_rinato_info,
> +     }, {
> +             .compatible     = "samsung,sensorhub-thermostat",
> +             .data           = &ssp_thermostat_info,
> +     },
> +     {},
> +};
> +MODULE_DEVICE_TABLE(of, ssp_of_match);
> +
> +static struct ssp_data *ssp_parse_dt(struct device *dev)
> +{
> +     int ret;
> +     struct ssp_data *data;
> +     struct device_node *node = dev->of_node;
> +     const struct of_device_id *match;
> +
> +     data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> +     if (!data)
> +             return NULL;
> +
> +     data->mcu_ap_gpio = of_get_named_gpio(node, "mcu-ap-gpios", 0);
> +     if (data->mcu_ap_gpio < 0)
> +             goto err_free_pd;
> +
> +     data->ap_mcu_gpio = of_get_named_gpio(node, "ap-mcu-gpios", 0);
> +     if (data->ap_mcu_gpio < 0)
> +             goto err_free_pd;
> +
> +     data->mcu_reset_gpio = of_get_named_gpio(node, "mcu-reset-gpios", 0);
> +     if (data->mcu_reset_gpio < 0)
> +             goto err_free_pd;
> +
> +     ret = devm_gpio_request_one(dev, data->ap_mcu_gpio, GPIOF_OUT_INIT_HIGH,
> +                                 "ap-mcu-gpios");
> +     if (ret)
> +             goto err_free_pd;
> +
> +     ret = devm_gpio_request_one(dev, data->mcu_reset_gpio,
> +                                 GPIOF_OUT_INIT_HIGH, "mcu-reset-gpios");
> +     if (ret)
> +             goto err_ap_mcu;
> +
> +     match = of_match_node(ssp_of_match, node);
> +     if (!match)
> +             goto err_mcu_reset_gpio;
> +
> +     data->sensorhub_info = (struct ssp_sensorhub_info *)match->data;
> +
> +     dev_set_drvdata(dev, data);
> +
> +     return data;
> +
> +err_mcu_reset_gpio:
> +     devm_gpio_free(dev, data->mcu_reset_gpio);
> +err_ap_mcu:
> +     devm_gpio_free(dev, data->ap_mcu_gpio);
> +err_free_pd:
> +     devm_kfree(dev, data);
> +     return NULL;
> +}
> +#else
> +static struct ssp_data *ssp_parse_dt(struct platform_device *pdev)
> +{
This has incorrect arguements.. So causes a build error.
> +     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)
> +{
> +     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, i;
> +     struct ssp_data *data;
> +
> +     data = ssp_parse_dt(&spi->dev);
> +     if (!data) {
> +             dev_err(&spi->dev, "Failed to find platform data\n");
> +             return -ENODEV;
> +     }
> +
> +     ret = mfd_add_devices(&spi->dev, -1, sensorhub_sensor_devs,
> +                           ARRAY_SIZE(sensorhub_sensor_devs), NULL, 0, NULL);
> +     if (ret < 0) {
> +             dev_err(&spi->dev, "mfd add devices fail\n");
> +             return ret;
> +     }
> +
> +     spi->mode = SPI_MODE_1;
> +     ret = spi_setup(spi);
> +     if (ret < 0) {
> +             dev_err(&spi->dev, "Failed to setup spi\n");
> +             return ret;
> +     }
> +
> +     data->fw_dl_state = SSP_FW_DL_STATE_NONE;
> +     data->spi = spi;
> +     spi_set_drvdata(spi, data);
> +
> +     mutex_init(&data->comm_lock);
> +
> +     for (i = 0; i < SSP_SENSOR_MAX; ++i) {
> +             data->delay_buf[i] = SSP_DEFAULT_POLLING_DELAY;
> +             data->batch_latency_buf[i] = 0;
> +             data->batch_opt_buf[i] = 0;
> +             data->check_status[i] = SSP_INITIALIZATION_STATE;
> +     }
> +
> +     data->delay_buf[SSP_BIO_HRM_LIB] = 100;
> +
> +     data->time_syncing = true;
> +
> +     mutex_init(&data->pending_lock);
> +     INIT_LIST_HEAD(&data->pending_list);
> +
> +     atomic_set(&data->enable_refcount, 0);
> +
> +     INIT_WORK(&data->work_wdt, ssp_wdt_work_func);
> +     INIT_DELAYED_WORK(&data->work_refresh, ssp_refresh_task);
> +
> +     setup_timer(&data->wdt_timer, ssp_wdt_timer_func, (unsigned long)data);
> +
> +     ret = request_threaded_irq(data->spi->irq, NULL,
> +                                ssp_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;
> +     }
> +
> +     /* Let's start with enabled one so irq balance could be ok */
> +     data->shut_down = false;
> +
> +     /* just to avoid unbalanced irq set wake up */
> +     enable_irq_wake(data->spi->irq);
> +
> +     data->fw_dl_state = ssp_check_fwbl(data);
> +     if (data->fw_dl_state == SSP_FW_DL_STATE_NONE) {
> +             ret = ssp_initialize_mcu(data);
> +             if (ret < 0) {
> +                     dev_err(&spi->dev, "Initialize_mcu failed\n");
> +                     goto err_read_reg;
> +             }
> +     } else {
> +             dev_err(&spi->dev, "Firmware version not supported\n");
> +             ret = -EPERM;
> +             goto err_read_reg;
> +     }
> +
> +     return 0;
> +
> +err_read_reg:
> +     free_irq(data->spi->irq, data);
> +err_setup_irq:
> +     mutex_destroy(&data->pending_lock);
> +     mutex_destroy(&data->comm_lock);
> +
> +     dev_err(&spi->dev, "Probe failed!\n");
> +
> +     return ret;
> +}
> +
> +static int ssp_remove(struct spi_device *spi)
> +{
> +     struct ssp_data *data = spi_get_drvdata(spi);
> +
> +     if (ssp_command(data, SSP_MSG2SSP_AP_STATUS_SHUTDOWN, 0) < 0)
> +             dev_err(&data->spi->dev,
> +                     "SSP_MSG2SSP_AP_STATUS_SHUTDOWN failed\n");
> +
> +     ssp_enable_mcu(data, false);
> +     ssp_disable_wdt_timer(data);
> +
> +     ssp_clean_pending_list(data);
> +
> +     free_irq(data->spi->irq, data);
> +
> +     del_timer_sync(&data->wdt_timer);
> +     cancel_work_sync(&data->work_wdt);
> +
> +     mutex_destroy(&data->comm_lock);
> +     mutex_destroy(&data->pending_lock);
> +
> +     mfd_remove_devices(&spi->dev);
> +
> +     return 0;
> +}
> +
> +static int ssp_suspend(struct device *dev)
> +{
> +     int ret;
> +     struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
> +
> +     data->last_resume_state = SSP_MSG2SSP_AP_STATUS_SUSPEND;
> +
> +     if (atomic_read(&data->enable_refcount) > 0)
> +             ssp_disable_wdt_timer(data);
> +
> +     ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_SUSPEND, 0);
> +     if (ret < 0) {
> +             dev_err(&data->spi->dev,
> +                     "%s SSP_MSG2SSP_AP_STATUS_SUSPEND failed\n", __func__);
> +
> +             ssp_enable_wdt_timer(data);
> +             return ret;
> +     }
> +
> +     data->time_syncing = false;
> +     disable_irq(data->spi->irq);
> +
> +     return 0;
> +}
> +
> +static int ssp_resume(struct device *dev)
> +{
> +     int ret;
> +     struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
> +
> +     enable_irq(data->spi->irq);
> +
> +     if (atomic_read(&data->enable_refcount) > 0)
> +             ssp_enable_wdt_timer(data);
> +
> +     ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_RESUME, 0);
> +     if (ret < 0) {
> +             dev_err(&data->spi->dev,
> +                     "%s SSP_MSG2SSP_AP_STATUS_RESUME failed\n", __func__);
> +             ssp_disable_wdt_timer(data);
> +             return ret;
> +     }
> +
> +     /* timesyncing is set by MCU */
> +     data->last_resume_state = SSP_MSG2SSP_AP_STATUS_RESUME;
> +
> +     return 0;
> +}
> +
> +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,
> +     .driver = {
> +             .pm = &ssp_pm_ops,
> +             .bus = &spi_bus_type,
> +             .owner = THIS_MODULE,
> +             .of_match_table = of_match_ptr(ssp_of_match),
> +             .name = "sensorhub"
> +     },
> +};
> +
> +module_spi_driver(ssp_driver);
> +
> +MODULE_DESCRIPTION("ssp sensorhub driver");
> +MODULE_AUTHOR("Samsung Electronics");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/iio/common/ssp_sensors/ssp_spi.c 
> b/drivers/iio/common/ssp_sensors/ssp_spi.c
> new file mode 100644
> index 0000000..704284a
> --- /dev/null
> +++ b/drivers/iio/common/ssp_sensors/ssp_spi.c
> @@ -0,0 +1,608 @@
> +/*
> + *  Copyright (C) 2014, 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 SSP_GET_MESSAGE_TYPE(data) (data & (3 << SSP_RW))
> +
> +/*
> + * SSP -> AP Instruction
> + * They tell what packet type can be expected. In the future there will
> + * be less of them. BYPASS means common sensor packets with accel, gyro,
> + * hrm etc. data. LIBRARY and META are mock-up's for now.
> + */
> +#define SSP_MSG2AP_INST_BYPASS_DATA          0x37
> +#define SSP_MSG2AP_INST_LIBRARY_DATA         0x01
> +#define SSP_MSG2AP_INST_DEBUG_DATA           0x03
> +#define SSP_MSG2AP_INST_BIG_DATA             0x04
> +#define SSP_MSG2AP_INST_META_DATA            0x05
> +#define SSP_MSG2AP_INST_TIME_SYNC            0x06
> +#define SSP_MSG2AP_INST_RESET                        0x07
> +
> +#define SSP_UNIMPLEMENTED -1
> +
> +struct ssp_msg_header {
> +     u8 cmd;
> +     __le16 length;
> +     __le16 options;
> +     __le32 data;
> +} __attribute__((__packed__));
> +
> +struct ssp_msg {
> +     u16 length;
> +     u16 options;
> +     struct list_head list;
> +     struct completion *done;
> +     struct ssp_msg_header *h;
> +     char *buffer;
> +};
> +
> +static const int ssp_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] =      SSP_UNIMPLEMENTED,
> +     [SSP_GEOMAGNETIC_RAW] =                 SSP_UNIMPLEMENTED,
> +     [SSP_GEOMAGNETIC_SENSOR] =              SSP_UNIMPLEMENTED,
> +     [SSP_PRESSURE_SENSOR] =                 SSP_UNIMPLEMENTED,
> +     [SSP_GESTURE_SENSOR] =                  SSP_UNIMPLEMENTED,
> +     [SSP_PROXIMITY_SENSOR] =                SSP_UNIMPLEMENTED,
> +     [SSP_TEMPERATURE_HUMIDITY_SENSOR] =     SSP_UNIMPLEMENTED,
> +     [SSP_LIGHT_SENSOR] =                    SSP_UNIMPLEMENTED,
> +     [SSP_PROXIMITY_RAW] =                   SSP_UNIMPLEMENTED,
> +     [SSP_ORIENTATION_SENSOR] =              SSP_UNIMPLEMENTED,
> +     [SSP_STEP_DETECTOR] =                   SSP_UNIMPLEMENTED,
> +     [SSP_SIG_MOTION_SENSOR] =               SSP_UNIMPLEMENTED,
> +     [SSP_GYRO_UNCALIB_SENSOR] =             SSP_UNIMPLEMENTED,
> +     [SSP_GAME_ROTATION_VECTOR] =            SSP_UNIMPLEMENTED,
> +     [SSP_ROTATION_VECTOR] =                 SSP_UNIMPLEMENTED,
> +     [SSP_STEP_COUNTER] =                    SSP_UNIMPLEMENTED,
> +     [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,
> +};
> +
> +#define SSP_HEADER_SIZE              (sizeof(struct ssp_msg_header))
> +#define SSP_HEADER_SIZE_ALIGNED      (ALIGN(SSP_HEADER_SIZE, 4))
> +
> +static struct ssp_msg *ssp_create_msg(u8 cmd, u16 len, u16 opt, u32 data)
> +{
> +     struct ssp_msg_header h;
> +     struct ssp_msg *msg;
> +
> +     msg = kzalloc(sizeof(*msg), GFP_KERNEL);
> +     if (!msg)
> +             return NULL;
> +
> +     h.cmd = cmd;
> +     h.length = cpu_to_le16(len);
> +     h.options = cpu_to_le16(opt);
> +     h.data = cpu_to_le32(data);
> +
> +     msg->buffer = kzalloc(SSP_HEADER_SIZE_ALIGNED + len,
> +                           GFP_KERNEL | GFP_DMA);
> +     if (!msg->buffer) {
> +             kfree(msg);
> +             return NULL;
> +     }
> +
> +     msg->length = len;
> +     msg->options = opt;
> +
> +     memcpy(msg->buffer, &h, SSP_HEADER_SIZE);
> +
> +     return msg;
> +}
> +
> +/*
> + * It is a bit heavy to do it this way but often the function is used to 
> compose
> + * the message from smaller chunks which are placed on the stack.  Often the
> + * chunks are small so memcpy should be optimalized.
> + */
> +static inline void ssp_fill_buffer(struct ssp_msg *m, unsigned int offset,
> +                                const void *src, unsigned int len)
> +{
> +     memcpy(&m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], src, len);
> +}
> +
> +static inline void ssp_get_buffer(struct ssp_msg *m, unsigned int offset,
> +                               void *dest, unsigned int len)
> +{
> +     memcpy(dest, &m->buffer[SSP_HEADER_SIZE_ALIGNED + offset],  len);
> +}
> +
> +#define SSP_GET_BUFFER_AT_INDEX(m, index) \
> +     (m->buffer[SSP_HEADER_SIZE_ALIGNED + index])
> +#define SSP_SET_BUFFER_AT_INDEX(m, index, val) \
> +     (m->buffer[SSP_HEADER_SIZE_ALIGNED + index] = val)
> +
> +static void ssp_clean_msg(struct ssp_msg *m)
> +{
> +     kfree(m->buffer);
> +     kfree(m);
> +}
> +
> +static int ssp_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 : -EPROTO;
> +     }
> +
> +     ssp_dbg("[SSP]: MSG From MCU - %s\n", &data_frame[*data_index]);
> +
> +     *data_index += length;
> +
> +     return 0;
> +}
> +
> +/*
> + * It was designed that way - additional lines to some kind of handshake,
> + * please do not ask why - only the firmware guy can know it.
> + */
> +static int ssp_check_lines(struct ssp_data *data, bool state)
> +{
> +     int delay_cnt = 0;
> +
> +     gpio_set_value_cansleep(data->ap_mcu_gpio, state);
> +
> +     while (gpio_get_value_cansleep(data->mcu_ap_gpio) != state) {
> +             usleep_range(3000, 3500);
> +
> +             if (data->shut_down || delay_cnt++ > 500) {
> +                     dev_err(SSP_DEV, "%s:timeout, hw ack wait fail %d\n",
> +                             __func__, state);
> +
> +                     if (!state)
> +                             gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
> +
> +                     return -ETIMEDOUT;
> +             }
> +     }
> +
> +     return 0;
> +}
> +
> +static int ssp_do_transfer(struct ssp_data *data, struct ssp_msg *msg,
> +                        struct completion *done, int timeout)
> +{
> +     int status;
> +     /*
> +      * check if this is a short one way message or the whole transfer has
> +      * second part after an interrupt
> +      */
> +     const bool use_no_irq = msg->length == 0;
> +
> +     if (data->shut_down)
> +             return -EPERM;
> +
> +     msg->done = done;
> +
> +     mutex_lock(&data->comm_lock);
> +
> +     status = ssp_check_lines(data, false);
> +     if (status < 0)
> +             goto _error_locked;
> +
> +     status = spi_write(data->spi, msg->buffer, SSP_HEADER_SIZE);
> +     if (status < 0) {
> +             gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
> +             dev_err(SSP_DEV, "%s spi_write fail\n", __func__);
> +             goto _error_locked;
> +     }
> +
> +     if (!use_no_irq) {
> +             mutex_lock(&data->pending_lock);
> +             list_add_tail(&msg->list, &data->pending_list);
> +             mutex_unlock(&data->pending_lock);
> +     }
> +
> +     status = ssp_check_lines(data, true);
> +     if (status < 0) {
> +             if (!use_no_irq) {
> +                     mutex_lock(&data->pending_lock);
> +                     list_del(&msg->list);
> +                     mutex_unlock(&data->pending_lock);
> +             }
> +             goto _error_locked;
> +     }
> +
> +     mutex_unlock(&data->comm_lock);
> +
> +     if (!use_no_irq && done)
> +             if (wait_for_completion_timeout(done,
> +                                             msecs_to_jiffies(timeout)) ==
> +                 0) {
> +                     mutex_lock(&data->pending_lock);
> +                     list_del(&msg->list);
> +                     mutex_unlock(&data->pending_lock);
> +
> +                     data->timeout_cnt++;
> +                     return -ETIMEDOUT;
> +             }
> +
> +     return 0;
> +
> +_error_locked:
> +     mutex_unlock(&data->comm_lock);
> +     data->timeout_cnt++;
> +     return status;
> +}
> +
> +static inline int ssp_spi_sync_command(struct ssp_data *data,
> +                                    struct ssp_msg *msg)
> +{
> +     return ssp_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);
> +
> +     if (WARN_ON(!msg->length))
> +             return -EPERM;
> +
> +     return ssp_do_transfer(data, msg, &done, timeout);
> +}
> +
> +static int ssp_handle_big_data(struct ssp_data *data, char *dataframe, int 
> *idx)
> +{
> +     /* mock-up, it will be changed with adding another sensor types */
> +     *idx += 8;
> +     return 0;
> +}
> +
> +static int ssp_parse_dataframe(struct ssp_data *data, char *dataframe, int 
> len)
> +{
> +     int idx, sd;
> +     struct timespec ts;
> +     struct ssp_sensor_data *spd;
> +     struct iio_dev **indio_devs = data->sensor_devs;
> +
> +     getnstimeofday(&ts);
> +
> +     for (idx = 0; idx < len;) {
> +             switch (dataframe[idx++]) {
> +             case SSP_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 -EPROTO;
> +                     }
> +
> +                     if (indio_devs[sd]) {
> +                             spd = iio_priv(indio_devs[sd]);
> +                             if (spd->process_data)
> +                                     spd->process_data(indio_devs[sd],
> +                                                       &dataframe[idx],
> +                                                       data->timestamp);
> +                     } else {
> +                             dev_err(SSP_DEV, "no client for frame\n");
> +                     }
> +
> +                     idx += ssp_offset_map[sd];
> +                     break;
> +             case SSP_MSG2AP_INST_DEBUG_DATA:
> +                     sd = ssp_print_mcu_debug(dataframe, &idx, len);
> +                     if (sd) {
> +                             dev_err(SSP_DEV,
> +                                     "Mcu data frame3 error %d\n", sd);
> +                             return sd;
> +                     }
> +                     break;
> +             case SSP_MSG2AP_INST_LIBRARY_DATA:
> +                     idx += len;
> +                     break;
> +             case SSP_MSG2AP_INST_BIG_DATA:
> +                     ssp_handle_big_data(data, dataframe, &idx);
> +                     break;
> +             case SSP_MSG2AP_INST_TIME_SYNC:
> +                     data->time_syncing = true;
> +                     break;
> +             case SSP_MSG2AP_INST_RESET:
> +                     ssp_queue_ssp_refresh_task(data, 0);
> +                     break;
> +             }
> +     }
> +
> +     if (data->time_syncing)
> +             data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
> +
> +     return 0;
> +}
> +
> +/* threaded irq */
> +int ssp_irq_msg(struct ssp_data *data)
> +{
> +     bool found = false;
> +     char *buffer;
> +     u8 msg_type;
> +     int ret;
> +     u16 length, msg_options;
> +     struct ssp_msg *msg, *n;
> +
> +     ret = spi_read(data->spi, data->header_buffer, SSP_HEADER_BUFFER_SIZE);
> +     if (ret < 0) {
> +             dev_err(SSP_DEV, "header read fail\n");
> +             return ret;
> +     }
> +
> +     length = le16_to_cpu(data->header_buffer[1]);
> +     msg_options = le16_to_cpu(data->header_buffer[0]);
> +
> +     if (length == 0) {
> +             dev_err(SSP_DEV, "length received from mcu is 0\n");
> +             return -EINVAL;
> +     }
> +
> +     msg_type = SSP_GET_MESSAGE_TYPE(msg_options);
> +
> +     switch (msg_type) {
> +     case SSP_AP2HUB_READ:
> +     case SSP_AP2HUB_WRITE:
> +             /*
> +              * this is a small list, a few elements - the packets can be
> +              * received with no order
> +              */
> +             mutex_lock(&data->pending_lock);
> +             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) {
> +                     /*
> +                      * here can be implemented dead messages handling
> +                      * but the slave should not send such ones - it is to
> +                      * check but let's handle this
> +                      */
> +                     buffer = kmalloc(length, GFP_KERNEL | GFP_DMA);
> +                     if (!buffer) {
> +                             ret = -ENOMEM;
> +                             goto _unlock;
> +                     }
> +
> +                     /* got dead packet so it is always an error */
> +                     ret = spi_read(data->spi, buffer, length);
> +                     if (ret >= 0)
> +                             ret = -EPROTO;
> +
> +                     kfree(buffer);
> +
> +                     dev_err(SSP_DEV, "No match error %x\n",
> +                             msg_options);
> +
> +                     goto _unlock;
> +             }
> +
> +             if (msg_type == SSP_AP2HUB_READ)
> +                     ret = spi_read(data->spi,
> +                                    &msg->buffer[SSP_HEADER_SIZE_ALIGNED],
> +                                    msg->length);
> +
> +             if (msg_type == SSP_AP2HUB_WRITE) {
> +                     ret = spi_write(data->spi,
> +                                     &msg->buffer[SSP_HEADER_SIZE_ALIGNED],
> +                                     msg->length);
> +                     if (msg_options & SSP_AP2HUB_RETURN) {
> +                             msg->options =
> +                                     SSP_AP2HUB_READ | SSP_AP2HUB_RETURN;
> +                             msg->length = 1;
> +
> +                             list_add_tail(&msg->list, &data->pending_list);
> +                             goto _unlock;
> +                     }
> +             }
> +
> +             if (msg->done)
> +                     if (!completion_done(msg->done))
> +                             complete(msg->done);
> +_unlock:
> +             mutex_unlock(&data->pending_lock);
> +             break;
> +     case SSP_HUB2AP_WRITE:
> +             buffer = kzalloc(length, GFP_KERNEL | GFP_DMA);
> +             if (!buffer)
> +                     return -ENOMEM;
> +
> +             ret = spi_read(data->spi, buffer, length);
> +             if (ret < 0) {
> +                     dev_err(SSP_DEV, "spi read fail\n");
> +                     kfree(buffer);
> +                     break;
> +             }
> +
> +             ret = ssp_parse_dataframe(data, buffer, length);
> +
> +             kfree(buffer);
> +             break;
> +
> +     default:
> +             dev_err(SSP_DEV, "unknown msg type\n");
> +             return -EPROTO;
> +     }
> +
> +     return ret;
> +}
> +
> +void ssp_clean_pending_list(struct ssp_data *data)
> +{
> +     struct ssp_msg *msg, *n;
> +
> +     mutex_lock(&data->pending_lock);
> +     list_for_each_entry_safe(msg, n, &data->pending_list, list) {
> +             list_del(&msg->list);
> +
> +             if (msg->done)
> +                     if (!completion_done(msg->done))
> +                             complete(msg->done);
> +     }
> +     mutex_unlock(&data->pending_lock);
> +}
> +
> +int ssp_command(struct ssp_data *data, char command, int arg)
> +{
> +     int ret;
> +     struct ssp_msg *msg;
> +
> +     msg = ssp_create_msg(command, 0, SSP_AP2HUB_WRITE, arg);
> +     if (!msg)
> +             return -ENOMEM;
> +
> +     ssp_dbg("%s - command 0x%x %d\n", __func__, command, arg);
> +
> +     ret = ssp_spi_sync_command(data, msg);
> +     ssp_clean_msg(msg);
> +
> +     return ret;
> +}
> +
> +int ssp_send_instruction(struct ssp_data *data, u8 inst, u8 sensor_type,
> +                      u8 *send_buf, u8 length)
> +{
> +     int ret;
> +     struct ssp_msg *msg;
> +
> +     if (data->fw_dl_state == SSP_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->available_sensors & BIT(sensor_type)) &&
> +                (inst <= SSP_MSG2SSP_INST_CHANGE_DELAY)) {
> +             dev_err(SSP_DEV, "%s - Bypass Inst Skip! - %u\n",
> +                     __func__, sensor_type);
> +             return -EIO; /* just fail */
> +     }
> +
> +     msg = ssp_create_msg(inst, length + 2, SSP_AP2HUB_WRITE, 0);
> +     if (!msg)
> +             return -ENOMEM;
> +
> +     ssp_fill_buffer(msg, 0, &sensor_type, 1);
> +     ssp_fill_buffer(msg, 1, send_buf, length);
> +
> +     ssp_dbg("%s - Inst = 0x%x, Sensor Type = 0x%x, data = %u\n",
> +             __func__, inst, sensor_type, send_buf[1]);
> +
> +     ret = ssp_spi_sync(data, msg, 1000);
> +     ssp_clean_msg(msg);
> +
> +     return ret;
> +}
> +
> +int ssp_get_chipid(struct ssp_data *data)
> +{
> +     int ret;
> +     char buffer;
> +     struct ssp_msg *msg;
> +
> +     msg = ssp_create_msg(SSP_MSG2SSP_AP_WHOAMI, 1, SSP_AP2HUB_READ, 0);
> +     if (!msg)
> +             return -ENOMEM;
> +
> +     ret = ssp_spi_sync(data, msg, 1000);
> +
> +     buffer = SSP_GET_BUFFER_AT_INDEX(msg, 0);
> +
> +     ssp_clean_msg(msg);
> +
> +     return ret < 0 ? ret : buffer;
> +}
> +
> +int ssp_set_magnetic_matrix(struct ssp_data *data)
> +{
> +     int ret;
> +     struct ssp_msg *msg;
> +
> +     msg = ssp_create_msg(SSP_MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX,
> +                          data->sensorhub_info->mag_length, SSP_AP2HUB_WRITE,
> +                          0);
> +     if (!msg)
> +             return -ENOMEM;
> +
> +     ssp_fill_buffer(msg, 0, data->sensorhub_info->mag_table,
> +                     data->sensorhub_info->mag_length);
> +
> +     ret = ssp_spi_sync(data, msg, 1000);
> +     ssp_clean_msg(msg);
> +
> +     return ret;
> +}
> +
> +unsigned int ssp_get_sensor_scanning_info(struct ssp_data *data)
> +{
> +     int ret;
> +     __le32 result;
> +     u32 cpu_result = 0;
> +
> +     struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_SENSOR_SCANNING, 4,
> +                                          SSP_AP2HUB_READ, 0);
> +     if (!msg)
> +             return 0;
> +
> +     ret = ssp_spi_sync(data, msg, 1000);
> +     if (ret < 0) {
> +             dev_err(SSP_DEV, "%s - spi read fail %d\n", __func__, ret);
> +             goto _exit;
> +     }
> +
> +     ssp_get_buffer(msg, 0, &result, 4);
> +     cpu_result = le32_to_cpu(result);
> +
> +     dev_info(SSP_DEV, "%s state: 0x%08x\n", __func__, cpu_result);
> +
> +_exit:
> +     ssp_clean_msg(msg);
> +     return cpu_result;
> +}
> +
> +unsigned int ssp_get_firmware_rev(struct ssp_data *data)
> +{
> +     int ret;
> +     __le32 result;
> +
> +     struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_FIRMWARE_REV, 4,
> +                                          SSP_AP2HUB_READ, 0);
> +     if (!msg)
> +             return SSP_INVALID_REVISION;
> +
> +     ret = ssp_spi_sync(data, msg, 1000);
> +     if (ret < 0) {
> +             dev_err(SSP_DEV, "%s - transfer fail %d\n", __func__, ret);
> +             ret = SSP_INVALID_REVISION;
> +             goto _exit;
> +     }
> +
> +     ssp_get_buffer(msg, 0, &result, 4);
> +     ret = le32_to_cpu(result);
> +
> +_exit:
> +     ssp_clean_msg(msg);
> +     return ret;
> +}
> diff --git a/include/linux/iio/common/ssp_sensors.h 
> b/include/linux/iio/common/ssp_sensors.h
> new file mode 100644
> index 0000000..f4d1b0e
> --- /dev/null
> +++ b/include/linux/iio/common/ssp_sensors.h
> @@ -0,0 +1,82 @@
> +/*
> + *  Copyright (C) 2014, 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_SENSORS_H_
> +#define _SSP_SENSORS_H_
> +
> +#include <linux/iio/iio.h>
> +
> +#define SSP_TIME_SIZE                                4
> +#define SSP_ACCELEROMETER_SIZE                       6
> +#define SSP_GYROSCOPE_SIZE                   6
> +#define SSP_BIO_HRM_RAW_SIZE                 8
> +#define SSP_BIO_HRM_RAW_FAC_SIZE             36
> +#define SSP_BIO_HRM_LIB_SIZE                 8
> +
> +/**
> + * enum ssp_sensor_type - SSP sensor type
> + */
> +enum ssp_sensor_type {
> +     SSP_ACCELEROMETER_SENSOR = 0,
> +     SSP_GYROSCOPE_SENSOR,
> +     SSP_GEOMAGNETIC_UNCALIB_SENSOR,
> +     SSP_GEOMAGNETIC_RAW,
> +     SSP_GEOMAGNETIC_SENSOR,
> +     SSP_PRESSURE_SENSOR,
> +     SSP_GESTURE_SENSOR,
> +     SSP_PROXIMITY_SENSOR,
> +     SSP_TEMPERATURE_HUMIDITY_SENSOR,
> +     SSP_LIGHT_SENSOR,
> +     SSP_PROXIMITY_RAW,
> +     SSP_ORIENTATION_SENSOR,
> +     SSP_STEP_DETECTOR,
> +     SSP_SIG_MOTION_SENSOR,
> +     SSP_GYRO_UNCALIB_SENSOR,
> +     SSP_GAME_ROTATION_VECTOR,
> +     SSP_ROTATION_VECTOR,
> +     SSP_STEP_COUNTER,
> +     SSP_BIO_HRM_RAW,
> +     SSP_BIO_HRM_RAW_FAC,
> +     SSP_BIO_HRM_LIB,
> +     SSP_SENSOR_MAX,
> +};
> +
> +struct ssp_data;
> +
> +/**
> + * struct ssp_sensor_data - Sensor object
> + * @process_data:    Callback to feed sensor data.
> + * @type:            Used sensor type.
> + * @buffer:          Received data buffer.
> + */
> +struct ssp_sensor_data {
> +     int (*process_data)(struct iio_dev *indio_dev, void *buf,
> +                         int64_t timestamp);
> +     enum ssp_sensor_type type;
> +     u8 *buffer;
> +};
> +
> +void ssp_register_consumer(struct iio_dev *indio_dev,
> +                        enum ssp_sensor_type type);
> +
> +int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,
> +                   u32 delay);
> +
> +int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type);
> +
> +u32 ssp_get_sensor_delay(struct ssp_data *data, enum ssp_sensor_type);
> +
> +int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type,
> +                  u32 delay);
> +#endif /* _SSP_SENSORS_H_ */
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to