linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC/PATCH 0/6] iio: Add sensorhub driver
@ 2014-09-03 14:55 Karol Wrona
  2014-09-03 14:55 ` [RFC/PATCH 1/6] iio:sensorhub: Add sensorhub common library Karol Wrona
                   ` (6 more replies)
  0 siblings, 7 replies; 17+ messages in thread
From: Karol Wrona @ 2014-09-03 14:55 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park, Karol Wrona

Hello,

This patchset adds support for sensorhub. It is an external mcu which manages
and collects data from several sensors i.e. on Galaxy Gear 2 watch.

It contains:
- spi driver for sensorhub device
- DT binding for the device
- IIO common utils for ssp sensors, a trigger module
- IIO accelerometer driver
- IIO gyroscope driver

Generally this is an attempt to generalize sensors handling on some Samsung
boards which have multiple sensors IC and also sensorhub does some data
processing itself.

I would like to get your opinion about such solution. The idea is that
data communication layer gives control and data to proper IIO sensor devices.
In this case I chose that sensor drivers are platform ones represented by
child nodes of sensorhub in DT. These compatibles are used mainly to match
sensor to driver. For example at this stage there is one accelerometer but on
other board can have different one with changed data format etc. In this case
ssp_accel_sensor driver could handle several physical devices of accel class.
Unfortunately the firmware gives only an information about used sensor type,
the driver can find out that there is an accelerometer on board but nothing
specific about the sensor.

Other question:
The sensorhub can implement some devices which are non common for IIO and hard
to standardise. These ones need two way communication (raw write will not be
proper for that) and can produce data packets with flexible size to 2KB).
I'm wondering if it is worth to look for solution in IIO and implement
something like IIO_BULK sensor with no standard channel description
or solve this problem using cdev. This is the reason why the part of the 
patchset is intended to be placed in misc.

Regards,
Karol

Karol Wrona (6):
  iio:sensorhub: Add sensorhub common library
  misc: sensorhub: Add sensorhub driver
  sensorhub: Add sensorhub bindings
  iio: sensorhub: Add sensorhub iio commons
  iio: sensorhub: Add sensorhub accelerometer sensor
  iio: sensorhub: Add sensorhub gyroscope sensor

 .../devicetree/bindings/misc/sensorhub.txt         |   51 ++
 drivers/iio/accel/Makefile                         |    2 +
 drivers/iio/accel/ssp_accel_sensor.c               |  240 ++++++
 drivers/iio/common/Kconfig                         |    1 +
 drivers/iio/common/Makefile                        |    1 +
 drivers/iio/common/ssp_sensors/Kconfig             |   15 +
 drivers/iio/common/ssp_sensors/Makefile            |    6 +
 .../iio/common/ssp_sensors/ssp-sensor-trigger.c    |   92 +++
 drivers/iio/common/ssp_sensors/ssp_iio.c           |   35 +
 drivers/iio/common/ssp_sensors/ssp_iio_sensor.h    |   62 ++
 drivers/iio/gyro/Makefile                          |    2 +
 drivers/iio/gyro/ssp_gyro_sensor.c                 |  225 ++++++
 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 ++++++++++++++++++
 include/linux/iio/common/ssp_sensors.h             |   82 ++
 22 files changed, 3274 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/sensorhub.txt
 create mode 100644 drivers/iio/accel/ssp_accel_sensor.c
 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-sensor-trigger.c
 create mode 100644 drivers/iio/common/ssp_sensors/ssp_iio.c
 create mode 100644 drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
 create mode 100644 drivers/iio/gyro/ssp_gyro_sensor.c
 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
 create mode 100644 include/linux/iio/common/ssp_sensors.h

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [RFC/PATCH 1/6] iio:sensorhub: Add sensorhub common library
  2014-09-03 14:55 [RFC/PATCH 0/6] iio: Add sensorhub driver Karol Wrona
@ 2014-09-03 14:55 ` Karol Wrona
  2014-09-14 14:17   ` Jonathan Cameron
  2014-09-03 14:55 ` [RFC/PATCH 2/6] misc: sensorhub: Add sensorhub driver Karol Wrona
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Karol Wrona @ 2014-09-03 14:55 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park, Karol Wrona

This patch adds common sensorhub library for sensorhub driver and iio drivers
which uses sensorhub MCU to communicate with sensors.

Signed-off-by: Karol Wrona <k.wrona@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 include/linux/iio/common/ssp_sensors.h |   82 ++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)
 create mode 100644 include/linux/iio/common/ssp_sensors.h

diff --git a/include/linux/iio/common/ssp_sensors.h b/include/linux/iio/common/ssp_sensors.h
new file mode 100644
index 0000000..ca4bdc4
--- /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_
+
+#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_tyoe - 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.
+ * @destroy:		Callback to destroy and cleanup sensor object.
+ * @prv_data:		Sensor private data.
+ * @type:		Used sensor type.
+ */
+struct ssp_sensor_data {
+	int (*process_data)(struct iio_dev *indio_dev, void *buf,
+			    s64 timestamp);
+	void *prv_data;
+	enum ssp_sensor_type type;
+};
+
+void ssp_register_consumer(struct iio_dev *indio_dev, enum ssp_sensor_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_ */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [RFC/PATCH 2/6] misc: sensorhub: Add sensorhub driver
  2014-09-03 14:55 [RFC/PATCH 0/6] iio: Add sensorhub driver Karol Wrona
  2014-09-03 14:55 ` [RFC/PATCH 1/6] iio:sensorhub: Add sensorhub common library Karol Wrona
@ 2014-09-03 14:55 ` Karol Wrona
  2014-09-14 15:42   ` Jonathan Cameron
  2014-09-03 14:55 ` [RFC/PATCH 3/6] sensorhub: Add sensorhub bindings Karol Wrona
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Karol Wrona @ 2014-09-03 14:55 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park, Karol Wrona

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.wrona@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@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


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [RFC/PATCH 3/6] sensorhub: Add sensorhub bindings
  2014-09-03 14:55 [RFC/PATCH 0/6] iio: Add sensorhub driver Karol Wrona
  2014-09-03 14:55 ` [RFC/PATCH 1/6] iio:sensorhub: Add sensorhub common library Karol Wrona
  2014-09-03 14:55 ` [RFC/PATCH 2/6] misc: sensorhub: Add sensorhub driver Karol Wrona
@ 2014-09-03 14:55 ` Karol Wrona
  2014-09-14 15:45   ` Jonathan Cameron
  2014-09-03 14:55 ` [RFC/PATCH 4/6] iio: sensorhub: Add sensorhub iio commons Karol Wrona
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Karol Wrona @ 2014-09-03 14:55 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park, Karol Wrona

Add sensorhub bindings for sensorhub on Galaxy Gear 2.

Signed-off-by: Karol Wrona <k.wrona@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 .../devicetree/bindings/misc/sensorhub.txt         |   51 ++++++++++++++++++++
 1 file changed, 51 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/sensorhub.txt

diff --git a/Documentation/devicetree/bindings/misc/sensorhub.txt b/Documentation/devicetree/bindings/misc/sensorhub.txt
new file mode 100644
index 0000000..b43c2b7
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/sensorhub.txt
@@ -0,0 +1,50 @@
+Samsung Sensorhub driver
+
+Sensorhub is MCU which manages several sensors and also play the role
+of virtual sensor device.
+
+Required properties:
+- compatible: "samsung,sensorhub-rinato" or "samsung,sensorhub-thermostat"
+- spi-max-frequency: max SPI clock frequency
+- interrupt-parent
+- interrupts: communication interrupt
+- ap-mcu-int: [out] ap to sensorhub line - used during communication
+- mcu-ap-int: [in] sensorhub to ap - used during communication
+- mcu-reset: [out] sensorhub reset
+- mag-table: magnitude table
+
+Optional properties:
+- sensor's nodes:
+  compatible = "samsung,mpu6500-accel"
+  compatible = "samsung,mpu6500-gyro"
+  compatible = "samsung,adpd142"
+
+Sensor compatibles are used to match proper sensor driver to real sensor on
+the board. The firmware does not give such information, so it helps to specify
+some sensors properties. Sensors have "samsung" prefixes because frequently
+they will not have much in common with sensors used without sensorhub because
+it can do some data processing.
+
+Example:
+
+	shub_spi: shub {
+		compatible = "samsung,rinato-ssp-spi";
+		spi-max-frequency = <5000000>;
+		interrupt-parent = <&gpx0>;
+		interrupts = <2 0>;
+		ap-mcu-int = <&gpx0 0 0>;
+		mcu-ap-int = <&gpx0 4 0>;
+		mcu-reset = <&gpx0 5 0>;
+		mag-table = /bits/ 8 <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>;
+		sensor@0 {
+			compatible = "samsung,mpu6500-accel";
+		};
+		sensor@1 {
+			compatible = "samsung,mpu6500-gyro";
+		};
+		sensor@2 {
+			compatible = "samsung,adpd142";
+		};
+	};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [RFC/PATCH 4/6] iio: sensorhub: Add sensorhub iio commons
  2014-09-03 14:55 [RFC/PATCH 0/6] iio: Add sensorhub driver Karol Wrona
                   ` (2 preceding siblings ...)
  2014-09-03 14:55 ` [RFC/PATCH 3/6] sensorhub: Add sensorhub bindings Karol Wrona
@ 2014-09-03 14:55 ` Karol Wrona
  2014-09-14 15:54   ` Jonathan Cameron
  2014-09-03 14:55 ` [RFC/PATCH 5/6] iio: sensorhub: Add sensorhub accelerometer sensor Karol Wrona
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Karol Wrona @ 2014-09-03 14:55 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park, Karol Wrona

This patch adds common library and trigger module for sensorhub iio drivers.

Signed-off-by: Karol Wrona <k.wrona@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/iio/common/Kconfig                         |    1 +
 drivers/iio/common/Makefile                        |    1 +
 drivers/iio/common/ssp_sensors/Kconfig             |   15 ++++
 drivers/iio/common/ssp_sensors/Makefile            |    6 ++
 .../iio/common/ssp_sensors/ssp-sensor-trigger.c    |   92 ++++++++++++++++++++
 drivers/iio/common/ssp_sensors/ssp_iio.c           |   35 ++++++++
 drivers/iio/common/ssp_sensors/ssp_iio_sensor.h    |   62 +++++++++++++
 7 files changed, 212 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-sensor-trigger.c
 create mode 100644 drivers/iio/common/ssp_sensors/ssp_iio.c
 create mode 100644 drivers/iio/common/ssp_sensors/ssp_iio_sensor.h

diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index 0b6e97d..630f9d8 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -4,3 +4,4 @@
 
 source "drivers/iio/common/hid-sensors/Kconfig"
 source "drivers/iio/common/st_sensors/Kconfig"
+source "drivers/iio/common/ssp_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 3112df0..5119642 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -9,3 +9,4 @@
 # When adding new entries keep the list in alphabetical order
 obj-y += hid-sensors/
 obj-y += st_sensors/
+obj-y += ssp_sensors/
diff --git a/drivers/iio/common/ssp_sensors/Kconfig b/drivers/iio/common/ssp_sensors/Kconfig
new file mode 100644
index 0000000..eefabdb
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/Kconfig
@@ -0,0 +1,15 @@
+#
+# Hid Sensor common modules
+#
+menu "SSP Sensor IIO Common"
+
+config SSP_SENSOR_IIO
+	tristate "Common module (trigger) for all SSP Sensor IIO drivers"
+	depends on SENSORS_SSP
+	select IIO_TRIGGER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build trigger support for SSP sensors.
+	  Triggers will be send if all requested attributes were read.
+
+endmenu
diff --git a/drivers/iio/common/ssp_sensors/Makefile b/drivers/iio/common/ssp_sensors/Makefile
new file mode 100644
index 0000000..da47549
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the SSP sensor common modules.
+#
+
+obj-$(CONFIG_SSP_SENSOR_IIO) += ssp-sensor-trigger.o
+obj-$(CONFIG_SSP_SENSOR_IIO) += ssp_iio.o
diff --git a/drivers/iio/common/ssp_sensors/ssp-sensor-trigger.c b/drivers/iio/common/ssp_sensors/ssp-sensor-trigger.c
new file mode 100644
index 0000000..0783cca
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/ssp-sensor-trigger.c
@@ -0,0 +1,92 @@
+/*
+ *  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/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/common/ssp_sensors.h>
+#include "ssp_iio_sensor.h"
+
+static int set_rdy_trigger_state(struct iio_trigger *trigger, bool state)
+{
+	int ret = 0;
+	struct ssp_sensor_data *c = iio_trigger_get_drvdata(trigger);
+	struct ssp_data *data = dev_get_drvdata(trigger->dev.parent->parent);
+
+	if (state)
+		ret = ssp_enable_sensor(data, c->type,
+				ssp_get_sensor_delay(data,
+						     c->type));
+	else
+		ret = ssp_disable_sensor(data, c->type);
+
+	return ret;
+}
+
+static struct iio_trigger_ops ssp_sensor_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = set_rdy_trigger_state,
+};
+
+void ssp_sensor_remove_trigger(struct iio_dev *indio_dev)
+{
+	iio_trigger_unregister(indio_dev->trig);
+	iio_trigger_free(indio_dev->trig);
+	indio_dev->trig = NULL;
+}
+EXPORT_SYMBOL(ssp_sensor_remove_trigger);
+
+int ssp_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
+			     struct ssp_sensor_data *sdata)
+{
+	int ret;
+	struct iio_trigger *trigger;
+
+	trigger = iio_trigger_alloc("%s-dev%d", name, indio_dev->id);
+	if (trigger == NULL) {
+		dev_err(&indio_dev->dev, "Trigger alloc failed\n");
+		return -ENOMEM;
+	}
+
+	trigger->dev.parent = indio_dev->dev.parent;
+	trigger->ops = &ssp_sensor_trigger_ops;
+	iio_trigger_set_drvdata(trigger, sdata);
+
+	ret = iio_trigger_register(trigger);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Trigger register failed\n");
+		goto error_free;
+	}
+	indio_dev->trig = trigger;
+
+	return ret;
+
+error_free:
+	iio_trigger_free(trigger);
+	return ret;
+}
+EXPORT_SYMBOL(ssp_sensor_setup_trigger);
+
+MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
+MODULE_DESCRIPTION("SSP Sensor trigger processing");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/common/ssp_sensors/ssp_iio.c b/drivers/iio/common/ssp_sensors/ssp_iio.c
new file mode 100644
index 0000000..fc88643
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/ssp_iio.c
@@ -0,0 +1,35 @@
+/*
+ *  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/kernel.h>
+#include <linux/iio/common/ssp_sensors.h>
+#include <linux/iio/triggered_buffer.h>
+#include "ssp_iio_sensor.h"
+
+/**
+ * @ssp_common_destroy_sensor() - ssp sensor cleanup
+ *
+ * @indio_dev: iio device
+ */
+void ssp_common_destroy_sensor(struct iio_dev *indio_dev)
+{
+	iio_device_unregister(indio_dev);
+	ssp_sensor_remove_trigger(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	iio_device_free(indio_dev);
+
+}
+EXPORT_SYMBOL(ssp_common_destroy_sensor);
diff --git a/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h b/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
new file mode 100644
index 0000000..1911fbd
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
@@ -0,0 +1,62 @@
+#ifndef __SSP_IIO_SENSOR_H__
+#define __SSP_IIO_SENSOR_H__
+
+#define SSP_CHANNEL_AG(_type, _mod, _index) \
+{ \
+		.type = _type,\
+		.modified = 1,\
+		.channel2 = _mod,\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |\
+					BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+		.scan_index = _index,\
+		.scan_type = {\
+			.sign = 's',\
+			.realbits = 16,\
+			.storagebits = 16,\
+			.shift = 0,\
+			.endianness = IIO_LE,\
+		},\
+}
+
+#define SSP_DIVISOR	1000000ULL
+#define SSP_MS_PER_S	1000
+#define SSP_DIVIDEND	(SSP_DIVISOR * SSP_MS_PER_S)
+
+int ssp_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
+			     struct ssp_sensor_data *data);
+
+void ssp_sensor_remove_trigger(struct iio_dev *indio_dev);
+
+/* Converts time in ms to frequency */
+static inline void ssp_convert_to_freq(u32 time, int *integer_part,
+				       int *fractional)
+{
+	unsigned int value;
+
+	if (time == 0) {
+		*fractional = 0;
+		*integer_part = 0;
+		return;
+	}
+
+	value = SSP_DIVIDEND / time;
+	*fractional = value % SSP_DIVISOR;
+	*integer_part = value / SSP_DIVISOR;
+}
+
+/* Converts frequency to time in ms*/
+static inline int ssp_convert_to_time(int integer_part, int fractional)
+{
+	u64 value;
+
+	value = integer_part * SSP_DIVISOR + fractional;
+	if (value == 0)
+		return 0;
+
+	return  div_u64(SSP_DIVIDEND, value);
+}
+
+void ssp_common_destroy_sensor(struct iio_dev *indio_dev);
+
+#endif /* __SSP_IIO_SENSOR_H__ */
+
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [RFC/PATCH 5/6] iio: sensorhub: Add sensorhub accelerometer sensor
  2014-09-03 14:55 [RFC/PATCH 0/6] iio: Add sensorhub driver Karol Wrona
                   ` (3 preceding siblings ...)
  2014-09-03 14:55 ` [RFC/PATCH 4/6] iio: sensorhub: Add sensorhub iio commons Karol Wrona
@ 2014-09-03 14:55 ` Karol Wrona
  2014-09-14 17:10   ` Jonathan Cameron
  2014-09-03 14:55 ` [RFC/PATCH 6/6] iio: sensorhub: Add sensorhub gyroscope sensor Karol Wrona
  2014-09-03 19:52 ` [RFC/PATCH 0/6] iio: Add sensorhub driver Jonathan Cameron
  6 siblings, 1 reply; 17+ messages in thread
From: Karol Wrona @ 2014-09-03 14:55 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park, Karol Wrona

This patch adds accelerometer iio driver which uses sensorhub as data
provider.

Signed-off-by: Karol Wrona <k.wrona@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/iio/accel/Makefile           |    2 +
 drivers/iio/accel/ssp_accel_sensor.c |  240 ++++++++++++++++++++++++++++++++++
 2 files changed, 242 insertions(+)
 create mode 100644 drivers/iio/accel/ssp_accel_sensor.c

diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index a593996..2ab1ca6 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -16,3 +16,5 @@ st_accel-$(CONFIG_IIO_BUFFER) += st_accel_buffer.o
 
 obj-$(CONFIG_IIO_ST_ACCEL_I2C_3AXIS) += st_accel_i2c.o
 obj-$(CONFIG_IIO_ST_ACCEL_SPI_3AXIS) += st_accel_spi.o
+
+obj-$(CONFIG_SSP_SENSOR_IIO) += ssp_accel_sensor.o
diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c
new file mode 100644
index 0000000..5e7d3e0
--- /dev/null
+++ b/drivers/iio/accel/ssp_accel_sensor.c
@@ -0,0 +1,240 @@
+/*
+ *  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/platform_device.h>
+#include <linux/stat.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/common/ssp_sensors.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/common/ssp_sensors.h>
+#include "../common/ssp_sensors/ssp_iio_sensor.h"
+
+#define CHANNEL_COUNT 3
+
+#define SSP_ACCEL_NAME "ssp-accelrometer"
+static const char ssp_accel_device_name[] = SSP_ACCEL_NAME;
+
+enum accel_3d_channel {
+	CHANNEL_SCAN_INDEX_X,
+	CHANNEL_SCAN_INDEX_Y,
+	CHANNEL_SCAN_INDEX_Z,
+	CHANNEL_SCAN_INDEX_TIME,
+	ACCEL_3D_CHANNEL_MAX,
+};
+
+struct ssp_accel_state {
+	int accel_val;
+	int accel_calibbias;
+	struct ssp_data *data;
+	short calibration_data[CHANNEL_COUNT];
+};
+
+static struct ssp_accel_state accel_state;
+
+static int accel_read_raw(struct iio_dev *indio_dev,
+			  struct iio_chan_spec const *chan,
+			  int *val,
+			  int *val2,
+			  long mask)
+{
+	int ret;
+	u32 t;
+	struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		t = ssp_get_sensor_delay(data, SSP_ACCELEROMETER_SENSOR);
+		ssp_convert_to_freq(t, val, val2);
+		ret = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int accel_write_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan,
+			   int val,
+			   int val2,
+			   long mask)
+{
+	int ret, value;
+	struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		value = ssp_convert_to_time(val, val2);
+		ret = ssp_change_delay(data, SSP_ACCELEROMETER_SENSOR, value);
+		if (ret < 0) {
+			dev_err(&indio_dev->dev, "accel sensor enable fail\n");
+			ret = -EINVAL;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int accel_write_raw_get_fmt(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       long mask)
+{
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static struct iio_info ssp_accel_iio_info = {
+	.read_raw = &accel_read_raw,
+	.write_raw = &accel_write_raw,
+	.write_raw_get_fmt = &accel_write_raw_get_fmt,
+};
+
+static const struct iio_chan_spec acc_channels[] = {
+	SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_X, CHANNEL_SCAN_INDEX_X),
+	SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Y, CHANNEL_SCAN_INDEX_Y),
+	SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Z, CHANNEL_SCAN_INDEX_Z),
+	IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIME),
+};
+
+int accel_open_calibration(struct ssp_data *data)
+{
+	return 0;
+}
+
+static int process_accel_data(struct iio_dev *indio_dev, void *buf,
+			      s64 timestamp)
+{
+	__le64 time = 0;
+	u8 data[16] = {0};
+	const int len = SSP_ACCELEROMETER_SIZE;
+
+	if (indio_dev->scan_bytes == 0)
+		return indio_dev->scan_bytes;
+
+	memcpy(data, buf, SSP_ACCELEROMETER_SIZE);
+
+	if (indio_dev->scan_timestamp) {
+		memcpy(&time, &((char *)(buf))[len], SSP_TIME_SIZE);
+		*(s64 *)((u8 *)data +  ALIGN(len, sizeof(s64))) =
+			timestamp + le64_to_cpu(time) * 1000000;
+	}
+
+	iio_push_to_buffers(indio_dev, (u8 *)data);
+
+	return indio_dev->scan_bytes;
+}
+
+static int ssp_accel_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	ssp_common_destroy_sensor(indio_dev);
+
+	return 0;
+}
+
+static int ssp_accel_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct iio_dev *indio_dev;
+	struct ssp_sensor_data  *spd;
+
+	indio_dev = iio_device_alloc(sizeof(*spd));
+	if (IS_ERR(indio_dev)) {
+		dev_err(&pdev->dev, "device alloc fail\n");
+		return PTR_ERR(indio_dev);
+	}
+
+	spd = iio_priv(indio_dev);
+
+	spd->process_data = process_accel_data;
+	spd->prv_data = &accel_state;
+	spd->type = SSP_ACCELEROMETER_SENSOR;
+
+	indio_dev->name = ssp_accel_device_name;
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->dev.of_node = pdev->dev.of_node;
+	indio_dev->info = &ssp_accel_iio_info;
+
+	if (indio_dev->info == NULL)
+		goto err_iio_alloc;
+
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = acc_channels;
+	indio_dev->num_channels = ARRAY_SIZE(acc_channels);
+
+	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+					 NULL, NULL);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Triggered buffer setup fail\n");
+		goto err_iio_alloc;
+	}
+
+	ret = ssp_sensor_setup_trigger(indio_dev, ssp_accel_device_name, spd);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev, "Trigger setup fail\n");
+		goto err_iio_tbuffer;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto err_ssp_trigger;
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	ssp_register_consumer(indio_dev, SSP_ACCELEROMETER_SENSOR);
+
+	return 0;
+
+err_ssp_trigger:
+	ssp_sensor_remove_trigger(indio_dev);
+err_iio_tbuffer:
+	iio_triggered_buffer_cleanup(indio_dev);
+err_iio_alloc:
+	iio_device_free(indio_dev);
+
+	return ret;
+}
+
+static const struct of_device_id ssp_accel_id_table[] = {
+	{
+		.compatible = "samsung,mpu6500-accel",
+	},
+	{},
+};
+
+static struct platform_driver ssp_accel_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = SSP_ACCEL_NAME,
+		.of_match_table = ssp_accel_id_table,
+	},
+	.probe = ssp_accel_probe,
+	.remove =  ssp_accel_remove,
+};
+
+module_platform_driver(ssp_accel_driver);
+
+MODULE_AUTHOR("Karol Wrona <k.wwrona@samsung.com>");
+MODULE_DESCRIPTION("Samsung sensorhub accelerometers driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [RFC/PATCH 6/6] iio: sensorhub: Add sensorhub gyroscope sensor
  2014-09-03 14:55 [RFC/PATCH 0/6] iio: Add sensorhub driver Karol Wrona
                   ` (4 preceding siblings ...)
  2014-09-03 14:55 ` [RFC/PATCH 5/6] iio: sensorhub: Add sensorhub accelerometer sensor Karol Wrona
@ 2014-09-03 14:55 ` Karol Wrona
  2014-09-14 17:09   ` Jonathan Cameron
  2014-09-03 19:52 ` [RFC/PATCH 0/6] iio: Add sensorhub driver Jonathan Cameron
  6 siblings, 1 reply; 17+ messages in thread
From: Karol Wrona @ 2014-09-03 14:55 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park, Karol Wrona

This patch adds gyroscope iio driver which uses sensorhub as data
provider.

Signed-off-by: Karol Wrona <k.wrona@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/iio/gyro/Makefile          |    2 +
 drivers/iio/gyro/ssp_gyro_sensor.c |  225 ++++++++++++++++++++++++++++++++++++
 2 files changed, 227 insertions(+)
 create mode 100644 drivers/iio/gyro/ssp_gyro_sensor.c

diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
index 2f2752a..b9c57a0 100644
--- a/drivers/iio/gyro/Makefile
+++ b/drivers/iio/gyro/Makefile
@@ -21,3 +21,5 @@ st_gyro-$(CONFIG_IIO_BUFFER) += st_gyro_buffer.o
 
 obj-$(CONFIG_IIO_ST_GYRO_I2C_3AXIS) += st_gyro_i2c.o
 obj-$(CONFIG_IIO_ST_GYRO_SPI_3AXIS) += st_gyro_spi.o
+
+obj-$(CONFIG_SSP_SENSOR_IIO) += ssp_gyro_sensor.o
\ No newline at end of file
diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c
new file mode 100644
index 0000000..b2362a4
--- /dev/null
+++ b/drivers/iio/gyro/ssp_gyro_sensor.c
@@ -0,0 +1,225 @@
+/*
+ *  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/platform_device.h>
+#include <linux/stat.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/common/ssp_sensors.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/common/ssp_sensors.h>
+#include "../common/ssp_sensors/ssp_iio_sensor.h"
+
+#define CHANNEL_COUNT 3
+
+#define SSP_GYROSCOPE_NAME "ssp-gyroscope"
+static const char ssp_gyro_name[] = SSP_GYROSCOPE_NAME;
+
+enum gyro_3d_channel {
+	CHANNEL_SCAN_INDEX_X,
+	CHANNEL_SCAN_INDEX_Y,
+	CHANNEL_SCAN_INDEX_Z,
+	CHANNEL_SCAN_INDEX_TIME,
+	GYRO_3D_CHANNEL_MAX,
+};
+
+static int gyro_read_raw(struct iio_dev *indio_dev,
+			  struct iio_chan_spec const *chan,
+			  int *val,
+			  int *val2,
+			  long mask)
+{
+	int ret;
+	u32 t;
+	struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		t = ssp_get_sensor_delay(data, SSP_GYROSCOPE_SENSOR);
+		ssp_convert_to_freq(t, val, val2);
+		ret = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int gyro_write_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan,
+			   int val,
+			   int val2,
+			   long mask)
+{
+	int ret, value;
+	struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		value = ssp_convert_to_time(val, val2);
+		ret = ssp_change_delay(data, SSP_GYROSCOPE_SENSOR, value);
+		if (ret < 0) {
+			dev_err(&indio_dev->dev, "gyro sensor enable fail\n");
+			ret = -EINVAL;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int gyro_write_raw_get_fmt(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       long mask)
+{
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static struct iio_info ssp_gyro_iio_info = {
+	.read_raw = &gyro_read_raw,
+	.write_raw = &gyro_write_raw,
+	.write_raw_get_fmt = &gyro_write_raw_get_fmt,
+};
+
+static const struct iio_chan_spec gyro_channels[] = {
+	SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_X, CHANNEL_SCAN_INDEX_X),
+	SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Y, CHANNEL_SCAN_INDEX_Y),
+	SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Z, CHANNEL_SCAN_INDEX_Z),
+	IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIME),
+};
+
+static int process_gyro_data(struct iio_dev *indio_dev, void *buf,
+			      s64 timestamp)
+{
+	__le64 time = 0;
+	u8 data[16] = {0};
+	const int len = SSP_GYROSCOPE_SIZE;
+
+	if (indio_dev->scan_bytes == 0)
+		return indio_dev->scan_bytes;
+
+	memcpy(data, buf, SSP_GYROSCOPE_SIZE);
+
+	if (indio_dev->scan_timestamp) {
+		memcpy(&time, &((char *)(buf))[len], SSP_TIME_SIZE);
+		*(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) =
+			timestamp + le64_to_cpu(time) * 1000000;
+	}
+
+	iio_push_to_buffers(indio_dev, (u8 *)data);
+
+	return indio_dev->scan_bytes;
+}
+
+static int ssp_gyro_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	ssp_common_destroy_sensor(indio_dev);
+
+	return 0;
+}
+
+static int ssp_gyro_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct iio_dev *indio_dev;
+	struct ssp_sensor_data  *spd;
+
+	indio_dev = iio_device_alloc(sizeof(*spd));
+	if (IS_ERR(indio_dev)) {
+		dev_err(&pdev->dev, "device alloc fail\n");
+		return PTR_ERR(indio_dev);
+	}
+
+	spd = iio_priv(indio_dev);
+
+	spd->process_data = process_gyro_data;
+	spd->type = SSP_GYROSCOPE_SENSOR;
+
+	indio_dev->name = ssp_gyro_name;
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->info = &ssp_gyro_iio_info;
+
+	if (indio_dev->info == NULL)
+		goto err_iio_alloc;
+
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = gyro_channels;
+	indio_dev->num_channels = ARRAY_SIZE(gyro_channels);
+
+	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+					 NULL, NULL);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Triggered buffer setup fail\n");
+		goto err_iio_alloc;
+	}
+
+	ret = ssp_sensor_setup_trigger(indio_dev, ssp_gyro_name, spd);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev, "Trigger setup fail\n");
+		goto err_iio_tbuffer;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto err_ssp_trigger;
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	ssp_register_consumer(indio_dev, SSP_GYROSCOPE_SENSOR);
+
+	return 0;
+
+err_ssp_trigger:
+	ssp_sensor_remove_trigger(indio_dev);
+err_iio_tbuffer:
+	iio_triggered_buffer_cleanup(indio_dev);
+err_iio_alloc:
+	iio_device_free(indio_dev);
+
+	return ret;
+}
+
+
+static const struct of_device_id ssp_gyro_id_table[] = {
+	{
+		.compatible = "samsung,mpu6500-gyro",
+	},
+	{},
+};
+
+static struct platform_driver ssp_gyro_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = SSP_GYROSCOPE_NAME,
+		.of_match_table = ssp_gyro_id_table,
+	},
+	.probe = ssp_gyro_probe,
+	.remove =  ssp_gyro_remove,
+};
+
+module_platform_driver(ssp_gyro_driver);
+
+MODULE_AUTHOR("Karol Wrona <k.wwrona@samsung.com>");
+MODULE_DESCRIPTION("Samsung sensorhub gyroscopes driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [RFC/PATCH 0/6] iio: Add sensorhub driver
  2014-09-03 14:55 [RFC/PATCH 0/6] iio: Add sensorhub driver Karol Wrona
                   ` (5 preceding siblings ...)
  2014-09-03 14:55 ` [RFC/PATCH 6/6] iio: sensorhub: Add sensorhub gyroscope sensor Karol Wrona
@ 2014-09-03 19:52 ` Jonathan Cameron
  2014-09-08 14:55   ` Karol Wrona
  6 siblings, 1 reply; 17+ messages in thread
From: Jonathan Cameron @ 2014-09-03 19:52 UTC (permalink / raw)
  To: Karol Wrona, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park



On September 3, 2014 3:55:44 PM GMT+01:00, Karol Wrona <k.wrona@samsung.com> wrote:
>Hello,
>
>This patchset adds support for sensorhub. It is an external mcu which
>manages
>and collects data from several sensors i.e. on Galaxy Gear 2 watch.
>
>It contains:
>- spi driver for sensorhub device
>- DT binding for the device
>- IIO common utils for ssp sensors, a trigger module
>- IIO accelerometer driver
>- IIO gyroscope driver
>
>Generally this is an attempt to generalize sensors handling on some
>Samsung
>boards which have multiple sensors IC and also sensorhub does some data
>processing itself.
>
>I would like to get your opinion about such solution. The idea is that
>data communication layer gives control and data to proper IIO sensor
>devices.
>In this case I chose that sensor drivers are platform ones represented
>by
>child nodes of sensorhub in DT. These compatibles are used mainly to
>match
>sensor to driver. For example at this stage there is one accelerometer
>but on
>other board can have different one with changed data format etc. In
>this case
>ssp_accel_sensor driver could handle several physical devices of accel
>class.
>Unfortunately the firmware gives only an information about used sensor
>type,
>the driver can find out that there is an accelerometer on board but
>nothing
>specific about the sensor.
Will look at this later... In the meantime..
>
>Other question:
>The sensorhub can implement some devices which are non common for IIO
>and hard
>to standardise. These ones need two way communication (raw write will
>not be
>proper for that) and can produce data packets with flexible size to
>2KB).
>I'm wondering if it is worth to look for solution in IIO and implement
>something like IIO_BULK sensor with no standard channel description
>or solve this problem using cdev. This is the reason why the part of
>the 
>patchset is intended to be placed in misc.
Out of curiosity, what are these other sensors, roughly?  If they are popular I am
 sure there will be more out there soon!  Ideally we would work out how to standardize these. So convince is it can't be
 done! 
>
>Regards,
>Karol
>
>Karol Wrona (6):
>  iio:sensorhub: Add sensorhub common library
>  misc: sensorhub: Add sensorhub driver
>  sensorhub: Add sensorhub bindings
>  iio: sensorhub: Add sensorhub iio commons
>  iio: sensorhub: Add sensorhub accelerometer sensor
>  iio: sensorhub: Add sensorhub gyroscope sensor
>
> .../devicetree/bindings/misc/sensorhub.txt         |   51 ++
> drivers/iio/accel/Makefile                         |    2 +
> drivers/iio/accel/ssp_accel_sensor.c               |  240 ++++++
> drivers/iio/common/Kconfig                         |    1 +
> drivers/iio/common/Makefile                        |    1 +
> drivers/iio/common/ssp_sensors/Kconfig             |   15 +
> drivers/iio/common/ssp_sensors/Makefile            |    6 +
> .../iio/common/ssp_sensors/ssp-sensor-trigger.c    |   92 +++
> drivers/iio/common/ssp_sensors/ssp_iio.c           |   35 +
> drivers/iio/common/ssp_sensors/ssp_iio_sensor.h    |   62 ++
> drivers/iio/gyro/Makefile                          |    2 +
> drivers/iio/gyro/ssp_gyro_sensor.c                 |  225 ++++++
> 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
>++++++++++++++++++
> include/linux/iio/common/ssp_sensors.h             |   82 ++
> 22 files changed, 3274 insertions(+)
>create mode 100644 Documentation/devicetree/bindings/misc/sensorhub.txt
> create mode 100644 drivers/iio/accel/ssp_accel_sensor.c
> 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-sensor-trigger.c
> create mode 100644 drivers/iio/common/ssp_sensors/ssp_iio.c
> create mode 100644 drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
> create mode 100644 drivers/iio/gyro/ssp_gyro_sensor.c
> 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
> create mode 100644 include/linux/iio/common/ssp_sensors.h

-- 
Sent from my Android phone with K-9 Mail. Please excuse my brevity.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC/PATCH 0/6] iio: Add sensorhub driver
  2014-09-03 19:52 ` [RFC/PATCH 0/6] iio: Add sensorhub driver Jonathan Cameron
@ 2014-09-08 14:55   ` Karol Wrona
  2014-09-11 11:05     ` Octavian Purdila
  0 siblings, 1 reply; 17+ messages in thread
From: Karol Wrona @ 2014-09-08 14:55 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park

On 09/03/2014 09:52 PM, Jonathan Cameron wrote:
>
> On September 3, 2014 3:55:44 PM GMT+01:00, Karol Wrona <k.wrona@samsung.com> wrote:
>> Hello,
>>
>> This patchset adds support for sensorhub. It is an external mcu which
>> manages
>> and collects data from several sensors i.e. on Galaxy Gear 2 watch.
>>
>> It contains:
>> - spi driver for sensorhub device
>> - DT binding for the device
>> - IIO common utils for ssp sensors, a trigger module
>> - IIO accelerometer driver
>> - IIO gyroscope driver
>>
>> Generally this is an attempt to generalize sensors handling on some
>> Samsung
>> boards which have multiple sensors IC and also sensorhub does some data
>> processing itself.
>>
>> I would like to get your opinion about such solution. The idea is that
>> data communication layer gives control and data to proper IIO sensor
>> devices.
>> In this case I chose that sensor drivers are platform ones represented
>> by
>> child nodes of sensorhub in DT. These compatibles are used mainly to
>> match
>> sensor to driver. For example at this stage there is one accelerometer
>> but on
>> other board can have different one with changed data format etc. In
>> this case
>> ssp_accel_sensor driver could handle several physical devices of accel
>> class.
>> Unfortunately the firmware gives only an information about used sensor
>> type,
>> the driver can find out that there is an accelerometer on board but
>> nothing
>> specific about the sensor.
> Will look at this later... In the meantime..
Before code review I want to know your opinion if passing information
about sensors in that way is ok - It's a hack but currently the firmware
do not provide such information and I do not know if I will be able to
change it.
>> Other question:
>> The sensorhub can implement some devices which are non common for IIO
>> and hard
>> to standardise. These ones need two way communication (raw write will
>> not be
>> proper for that) and can produce data packets with flexible size to
>> 2KB).
>> I'm wondering if it is worth to look for solution in IIO and implement
>> something like IIO_BULK sensor with no standard channel description
>> or solve this problem using cdev. This is the reason why the part of
>> the
>> patchset is intended to be placed in misc.
> Out of curiosity, what are these other sensors, roughly?  If they are popular I am
>   sure there will be more out there soon!  Ideally we would work out how to standardize these. So convince is it can't be
>   done!
These are very specific for Galaxy Gear watch like movement detector, 
pedometer,
put down motion etc. The functionalities are mainly implemented by sensorhub
firmware, these ones are not physical chips. My first idea was to use 
IIO for typical
sensors only (I can have there a bunch of thermometers, accelerometers, 
lightsensors ...)
and one cdev for others. In this case data interpretation would be done 
by userspace.
So:
- these specific "sensors" will be only implemented on some Samsung's boards
and depend on firmware.
- there will be a lot of it  - most of them are represented by an 
integer value but there
are some which can send much more data.

>> Regards,
>> Karol
>>
>> Karol Wrona (6):
>>   iio:sensorhub: Add sensorhub common library
>>   misc: sensorhub: Add sensorhub driver
>>   sensorhub: Add sensorhub bindings
>>   iio: sensorhub: Add sensorhub iio commons
>>   iio: sensorhub: Add sensorhub accelerometer sensor
>>   iio: sensorhub: Add sensorhub gyroscope sensor
>>
>> .../devicetree/bindings/misc/sensorhub.txt         |   51 ++
>> drivers/iio/accel/Makefile                         |    2 +
>> drivers/iio/accel/ssp_accel_sensor.c               |  240 ++++++
>> drivers/iio/common/Kconfig                         |    1 +
>> drivers/iio/common/Makefile                        |    1 +
>> drivers/iio/common/ssp_sensors/Kconfig             |   15 +
>> drivers/iio/common/ssp_sensors/Makefile            |    6 +
>> .../iio/common/ssp_sensors/ssp-sensor-trigger.c    |   92 +++
>> drivers/iio/common/ssp_sensors/ssp_iio.c           |   35 +
>> drivers/iio/common/ssp_sensors/ssp_iio_sensor.h    |   62 ++
>> drivers/iio/gyro/Makefile                          |    2 +
>> drivers/iio/gyro/ssp_gyro_sensor.c                 |  225 ++++++
>> 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
>> ++++++++++++++++++
>> include/linux/iio/common/ssp_sensors.h             |   82 ++
>> 22 files changed, 3274 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/misc/sensorhub.txt
>> create mode 100644 drivers/iio/accel/ssp_accel_sensor.c
>> 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-sensor-trigger.c
>> create mode 100644 drivers/iio/common/ssp_sensors/ssp_iio.c
>> create mode 100644 drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
>> create mode 100644 drivers/iio/gyro/ssp_gyro_sensor.c
>> 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
>> create mode 100644 include/linux/iio/common/ssp_sensors.h


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC/PATCH 0/6] iio: Add sensorhub driver
  2014-09-08 14:55   ` Karol Wrona
@ 2014-09-11 11:05     ` Octavian Purdila
  2014-09-14 14:14       ` Jonathan Cameron
  0 siblings, 1 reply; 17+ messages in thread
From: Octavian Purdila @ 2014-09-11 11:05 UTC (permalink / raw)
  To: Karol Wrona
  Cc: Jonathan Cameron, linux-iio, Arnd Bergmann, lkml,
	Bartlomiej Zolnierkiewicz, Kyungmin Park

On Mon, Sep 8, 2014 at 5:55 PM, Karol Wrona <k.wrona@samsung.com> wrote:
> On 09/03/2014 09:52 PM, Jonathan Cameron wrote:
>>
>>
>> On September 3, 2014 3:55:44 PM GMT+01:00, Karol Wrona
>> <k.wrona@samsung.com> wrote:
>>>
>>> Hello,
>>>
>>> This patchset adds support for sensorhub. It is an external mcu which
>>> manages
>>> and collects data from several sensors i.e. on Galaxy Gear 2 watch.
>>>
>>> It contains:
>>> - spi driver for sensorhub device
>>> - DT binding for the device
>>> - IIO common utils for ssp sensors, a trigger module
>>> - IIO accelerometer driver
>>> - IIO gyroscope driver
>>>
>>> Generally this is an attempt to generalize sensors handling on some
>>> Samsung
>>> boards which have multiple sensors IC and also sensorhub does some data
>>> processing itself.
>>>
>>> I would like to get your opinion about such solution. The idea is that
>>> data communication layer gives control and data to proper IIO sensor
>>> devices.
>>> In this case I chose that sensor drivers are platform ones represented
>>> by
>>> child nodes of sensorhub in DT. These compatibles are used mainly to
>>> match
>>> sensor to driver. For example at this stage there is one accelerometer
>>> but on
>>> other board can have different one with changed data format etc. In
>>> this case
>>> ssp_accel_sensor driver could handle several physical devices of accel
>>> class.
>>> Unfortunately the firmware gives only an information about used sensor
>>> type,
>>> the driver can find out that there is an accelerometer on board but
>>> nothing
>>> specific about the sensor.
>>
>> Will look at this later... In the meantime..
>
> Before code review I want to know your opinion if passing information
> about sensors in that way is ok - It's a hack but currently the firmware
> do not provide such information and I do not know if I will be able to
> change it.
>>>
>>> Other question:
>>> The sensorhub can implement some devices which are non common for IIO
>>> and hard
>>> to standardise. These ones need two way communication (raw write will
>>> not be
>>> proper for that) and can produce data packets with flexible size to
>>> 2KB).
>>> I'm wondering if it is worth to look for solution in IIO and implement
>>> something like IIO_BULK sensor with no standard channel description
>>> or solve this problem using cdev. This is the reason why the part of
>>> the
>>> patchset is intended to be placed in misc.
>>
>> Out of curiosity, what are these other sensors, roughly?  If they are
>> popular I am
>>   sure there will be more out there soon!  Ideally we would work out how
>> to standardize these. So convince is it can't be
>>   done!
>
> These are very specific for Galaxy Gear watch like movement detector,
> pedometer,
> put down motion etc. The functionalities are mainly implemented by sensorhub
> firmware, these ones are not physical chips. My first idea was to use IIO
> for typical
> sensors only (I can have there a bunch of thermometers, accelerometers,
> lightsensors ...)
> and one cdev for others. In this case data interpretation would be done by
> userspace.
> So:
> - these specific "sensors" will be only implemented on some Samsung's boards
> and depend on firmware.
> - there will be a lot of it  - most of them are represented by an integer
> value but there
> are some which can send much more data.
>
>

Hi Karol,

With the addition of new sensor types in Android (pedometer,
significant motion) and probably more to come in L-dessert, and with
the proliferation of sensor hub solutions, I am sure that we are going
to see more of these types of sensors coming.

With that in mind, I think it is worth trying to standardize the new
sensors at the kernel level. Having the data interpretation done in
userspace is going to make it hard to write portable application
across different sensors.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC/PATCH 0/6] iio: Add sensorhub driver
  2014-09-11 11:05     ` Octavian Purdila
@ 2014-09-14 14:14       ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2014-09-14 14:14 UTC (permalink / raw)
  To: Octavian Purdila, Karol Wrona
  Cc: linux-iio, Arnd Bergmann, lkml, Bartlomiej Zolnierkiewicz, Kyungmin Park

On 11/09/14 12:05, Octavian Purdila wrote:
> On Mon, Sep 8, 2014 at 5:55 PM, Karol Wrona <k.wrona@samsung.com> wrote:
>> On 09/03/2014 09:52 PM, Jonathan Cameron wrote:
>>>
>>>
>>> On September 3, 2014 3:55:44 PM GMT+01:00, Karol Wrona
>>> <k.wrona@samsung.com> wrote:
>>>>
>>>> Hello,
>>>>
>>>> This patchset adds support for sensorhub. It is an external mcu which
>>>> manages
>>>> and collects data from several sensors i.e. on Galaxy Gear 2 watch.
>>>>
>>>> It contains:
>>>> - spi driver for sensorhub device
>>>> - DT binding for the device
>>>> - IIO common utils for ssp sensors, a trigger module
>>>> - IIO accelerometer driver
>>>> - IIO gyroscope driver
>>>>
>>>> Generally this is an attempt to generalize sensors handling on some
>>>> Samsung
>>>> boards which have multiple sensors IC and also sensorhub does some data
>>>> processing itself.
>>>>
>>>> I would like to get your opinion about such solution. The idea is that
>>>> data communication layer gives control and data to proper IIO sensor
>>>> devices.
>>>> In this case I chose that sensor drivers are platform ones represented
>>>> by
>>>> child nodes of sensorhub in DT. These compatibles are used mainly to
>>>> match
>>>> sensor to driver. For example at this stage there is one accelerometer
>>>> but on
>>>> other board can have different one with changed data format etc. In
>>>> this case
>>>> ssp_accel_sensor driver could handle several physical devices of accel
>>>> class.
>>>> Unfortunately the firmware gives only an information about used sensor
>>>> type,
>>>> the driver can find out that there is an accelerometer on board but
>>>> nothing
>>>> specific about the sensor.
>>>
>>> Will look at this later... In the meantime..
>>
>> Before code review I want to know your opinion if passing information
>> about sensors in that way is ok - It's a hack but currently the firmware
>> do not provide such information and I do not know if I will be able to
>> change it.
>>>>
>>>> Other question:
>>>> The sensorhub can implement some devices which are non common for IIO
>>>> and hard
>>>> to standardise. These ones need two way communication (raw write will
>>>> not be
>>>> proper for that) and can produce data packets with flexible size to
>>>> 2KB).
>>>> I'm wondering if it is worth to look for solution in IIO and implement
>>>> something like IIO_BULK sensor with no standard channel description
>>>> or solve this problem using cdev. This is the reason why the part of
>>>> the
>>>> patchset is intended to be placed in misc.
>>>
>>> Out of curiosity, what are these other sensors, roughly?  If they are
>>> popular I am
>>>   sure there will be more out there soon!  Ideally we would work out how
>>> to standardize these. So convince is it can't be
>>>   done!
>>
>> These are very specific for Galaxy Gear watch like movement detector,
>> pedometer,
>> put down motion etc. The functionalities are mainly implemented by sensorhub
>> firmware, these ones are not physical chips. My first idea was to use IIO
>> for typical
>> sensors only (I can have there a bunch of thermometers, accelerometers,
>> lightsensors ...)
>> and one cdev for others. In this case data interpretation would be done by
>> userspace.
>> So:
>> - these specific "sensors" will be only implemented on some Samsung's boards
>> and depend on firmware.
>> - there will be a lot of it  - most of them are represented by an integer
>> value but there
>> are some which can send much more data.
>>
>>
> 
> Hi Karol,
> 
> With the addition of new sensor types in Android (pedometer,
> significant motion) and probably more to come in L-dessert, and with
> the proliferation of sensor hub solutions, I am sure that we are going
> to see more of these types of sensors coming.
> 
> With that in mind, I think it is worth trying to standardize the new
> sensors at the kernel level. Having the data interpretation done in
> userspace is going to make it hard to write portable application
> across different sensors.
I absolutely agree on this.  We may well need to extend IIO in new directions
to do it, but we need a consistent interface for these signal types.
(might take us a little while to pin down what it is however! Best get
started :)

Jonathan
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC/PATCH 1/6] iio:sensorhub: Add sensorhub common library
  2014-09-03 14:55 ` [RFC/PATCH 1/6] iio:sensorhub: Add sensorhub common library Karol Wrona
@ 2014-09-14 14:17   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2014-09-14 14:17 UTC (permalink / raw)
  To: Karol Wrona, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park

On 03/09/14 15:55, Karol Wrona wrote:
> This patch adds common sensorhub library for sensorhub driver and iio drivers
> which uses sensorhub MCU to communicate with sensors.
> 
> Signed-off-by: Karol Wrona <k.wrona@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  include/linux/iio/common/ssp_sensors.h |   82 ++++++++++++++++++++++++++++++++
>  1 file changed, 82 insertions(+)

There is very little point in a separate patch to introduce a header, please combine this
with the first patch to use it when you respin for V2.

It can't be used on it's own or reviewed on it's own (except very superficially).



>  create mode 100644 include/linux/iio/common/ssp_sensors.h
> 
> diff --git a/include/linux/iio/common/ssp_sensors.h b/include/linux/iio/common/ssp_sensors.h
> new file mode 100644
> index 0000000..ca4bdc4
> --- /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_
> +
> +#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_tyoe - 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.
> + * @destroy:		Callback to destroy and cleanup sensor object.
> + * @prv_data:		Sensor private data.
> + * @type:		Used sensor type.
> + */
> +struct ssp_sensor_data {
> +	int (*process_data)(struct iio_dev *indio_dev, void *buf,
> +			    s64 timestamp);
> +	void *prv_data;
> +	enum ssp_sensor_type type;
> +};
> +
> +void ssp_register_consumer(struct iio_dev *indio_dev, enum ssp_sensor_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_ */
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC/PATCH 2/6] misc: sensorhub: Add sensorhub driver
  2014-09-03 14:55 ` [RFC/PATCH 2/6] misc: sensorhub: Add sensorhub driver Karol Wrona
@ 2014-09-14 15:42   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2014-09-14 15:42 UTC (permalink / raw)
  To: Karol Wrona, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park

On 03/09/14 15:55, 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.
>
> Signed-off-by: Karol Wrona <k.wrona@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>

General comments

* Please use a prefix for pretty much everything as it will avoid possible
  name clashes in future.  This is particularly true for anything that isn't
  static, but applies to all function names and definitions etc.
  ssp_ and SSP_ would be the obvious choices.
* Never do spi transfers using data that can share a cacheline with anything
  else.  Some places in this driver may be fine, but others are definitely not.
* The firmware loader looks like it might be a generic stm32 boatloader
  firmware interface.  If so should probably be factored out so as to allow
  other devices to use it.

A fair number of little bits inline.

This is a complex part, and on the whole your driver is reasonably
easy to follow so keep up the good work!

Jonathan

> ---
>  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
Interesting spacing.
> +#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
We have min for 2G but not 1G or 4G?
> +#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 {
Please make it clear that this first part is used as the buffer for spi
writes.  I don't think this will give you any grief with cacheline corruption
as nothing else should touch the rest of the structure whilst dma is
going on, but it does give me a slight worry...  I'd prefer this split
up or this section at the end then use the magic cacheline alignment
to avoid any possible problems (as an embedded structure to make referencing
it simple)
> +	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,
> +};
> +

I think this structure would benefit from full kernel doc description.
> +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);
Please put parameter names in. Makes it a little easier to review later
patches.
> +
> +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"
Why the separate file?  Not much in here really..
> +
> +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;
No actual need to pack given what is in it...
> +
> +	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;
Do these need to be gpios?  Can't you use interrupts instead and make
things more general?
> +
> +	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);
sizeof(len) rather than len?
> +	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;
No need for the cast - platform_data is a void *.
> +	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 */
Why?
Use regulator_get_optional given you allow it to continue without the regulator.
> +	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)) {
Please don't eat up the error code spi_setup returns.  Pass it onwards.
> +		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,
Spacing around the |
> +				   IRQF_TRIGGER_FALLING|IRQF_ONESHOT,
> +				   "SSP_Int", data);
> +	if (ret < 0) {
> +		dev_err(&spi->dev, "Irq request fail\n");
> +		goto err_setup_irq;
> +	}
> +
There is a window here in which the irq is enabled.  As a general rule,
needing to explicitly disable an irq is a sign that something interesting
is going on in the handling.  Explain why it should be disabled...
> +	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) {
Why wait until here?  Either move the initial get of the regulators to here
or this to the initial get.  No obvious reason to separate them.
> +		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);
You didn't necessarily create data.  Hence I'd suggest making
a copy in the platformdata case so that you are always able to free it.
> +	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);
Hmm. You only allocate data is some paths. Best bet is probably to
copy the platform data into a local copy so that you are always safe
freeing it later.
> +
> +	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 */
> +	}
> +
Either do your suspend and resume so that the orders are an exact mirror
image or comment on why not.
> +	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 */
yet
> +	}
> +
> +	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,
Do we need a separate shutdown rather than just relying on device removal
occuring?  Not sure I've ever seen anyone implement it before!
> +	.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_spi_driver

> +
> +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.
> + *
> + */
> +
Is this a generic stm32 firmware loader?  If so will it work with other
such parts?  If so it should probably be factored out of the driver so
it can be shared with other drivers using this part.

I note some at least of this looks like the protocols in:
http://www.st.com/st-web-ui/static/active/en/resource/technical/document/application_note/DM00081379.pdf

> +#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,};
Now, if it were fine to have these buffers on the stack, you should be filling
tx_buf approriately here, not futher down.

> +	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,
Don't use buffers on the stack.  Will get cacheline corruption from
dma based SPI transfers.
> +		.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;
> +			}
> +		}
> +
Spacing around the <
> +		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 */
A description of these would be good.
> +#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)
> +{
This may be the most complex write sequence I've seen for what
is basically an spi device!  If possible I'd like some comments
saying why it is doing the various steps.
> +	int status = 0;
> +	int delay_cnt = 0;
> +	bool msg_dead = false, ssp_down = false;
> +	/*FIXME REVISIT*/
Why do I get a feeling there might be something wrong in here? :)
> +	const bool use_no_irq = msg->length == 0;
> +
> +	msg->dead_hook = &msg_dead;
> +	msg->dead = false;
> +	msg->done = done;
> +
> +	mutex_lock(&data->comm_mutex);
> +
What's going on here?  What is going to wrench the gpio high
again between the set and the read back? If anything can perhaps
we should be looking at locking to prevent it rather than
effectively doing bus access negotiation using the gpio pin..

Also is this just a chip select control?  If so should be handled
by the spi bus, not this driver.
> +	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) {
Please use slightly less cryptic error messages.
> +			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;
Meaningful error codes please.  I'd keep the result of spi_write in status
and return that.
> +		goto exit;
> +	}
> +
So if using irqs the message needs to be added to a list of messages
we are expecting a response for?
> +	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;
This is another bit that looks like a work around for not having
appropriate locking.
> +	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__);
> +
Please don't mess with the return codes.  Handle the correct ones
or if absolutely necessary introduce another variable to indicate
which ones need this handling.  Here I can't see any reason
not to move this handling up to the cases that can cause it.
> +	if (status == -1) {
data->time_out_cnt += !ssp_down;
> +		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);
> +}
> +
Could do with a descriptive comment.  What is 'big' in this context?
> +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;
> +
temp_buf is not in it's own cacheline and so you might run into problems
with cacheline corruption from the dma transfer.  Use a kmalloc for it
to avoid this.
> +	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);
Just make temp_buf an array of __le16 and then access it directly.
> +
> +	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);
So from this I guess there is no guarantee about the order in which
the sensor hub replies?  Just how many entries might this list have?
Do we need something more sophisticated than a simple linked list
to make the search quick enough?
> +		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");
  Not freeing buffer...
> +			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;
> +
One blank line only please.
> +
> +	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);
> +}
> +
Comment on why we might want to flush.
> +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;
> +
Why would we need retries for this?  To have this in the driver
you need an explanation of why it might validly not work first time.

> +	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;
> +}
> +
> +
Single blank lines are almost always enough so clean these double ones
out.
> +int set_sensor_position(struct ssp_data *data)
> +{
> +	int ret = 0;
No need for the initialization.
> +
No need for a blank line here.
> +	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*/
Please deal with all these FIXME points before next version.
Fair enough in an RFC but not always obvious what you are getting at!
> +	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};
Any need to initialize this other than the last element?
> +
> +	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));
> +			(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);
> +}
> +
Please prefix everything with ssp to avoid possible name clashes in future
> +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;
You put type in buffer[0] here..
> +
> +	llen = cpu_to_le32(len);
> +	memcpy(&msg->buffer, &llen, sizeof(unsigned int));
This is unusual.  You then write the address of buffer with its length
rather than, as I suspect, was intended the first element.  But you've
just written type into the first element...

> +
> +	return ssp_spi_async(data, msg);
> +}
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC/PATCH 3/6] sensorhub: Add sensorhub bindings
  2014-09-03 14:55 ` [RFC/PATCH 3/6] sensorhub: Add sensorhub bindings Karol Wrona
@ 2014-09-14 15:45   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2014-09-14 15:45 UTC (permalink / raw)
  To: Karol Wrona, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park

On 03/09/14 15:55, Karol Wrona wrote:
> Add sensorhub bindings for sensorhub on Galaxy Gear 2.
> 
> Signed-off-by: Karol Wrona <k.wrona@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  .../devicetree/bindings/misc/sensorhub.txt         |   51 ++++++++++++++++++++
>  1 file changed, 51 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/misc/sensorhub.txt
> 
> diff --git a/Documentation/devicetree/bindings/misc/sensorhub.txt b/Documentation/devicetree/bindings/misc/sensorhub.txt
> new file mode 100644
> index 0000000..b43c2b7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/misc/sensorhub.txt
> @@ -0,0 +1,50 @@
> +Samsung Sensorhub driver
> +
> +Sensorhub is MCU which manages several sensors and also play the role
> +of virtual sensor device.
> +
> +Required properties:
> +- compatible: "samsung,sensorhub-rinato" or "samsung,sensorhub-thermostat"
> +- spi-max-frequency: max SPI clock frequency
> +- interrupt-parent
> +- interrupts: communication interrupt
> +- ap-mcu-int: [out] ap to sensorhub line - used during communication
Ah, why call it int?  I assumed when reading some of the code that it was an interrupt line.

> +- mcu-ap-int: [in] sensorhub to ap - used during communication
> +- mcu-reset: [out] sensorhub reset
> +- mag-table: magnitude table
Magnitude of what?
> +
> +Optional properties:
> +- sensor's nodes:
> +  compatible = "samsung,mpu6500-accel"
> +  compatible = "samsung,mpu6500-gyro"
> +  compatible = "samsung,adpd142"
> +
> +Sensor compatibles are used to match proper sensor driver to real sensor on
> +the board. The firmware does not give such information, so it helps to specify
> +some sensors properties. Sensors have "samsung" prefixes because frequently
> +they will not have much in common with sensors used without sensorhub because
> +it can do some data processing.
I wonder if we can make the ssp bit explicit as well.  Chances are samsung might
have another device similar to this (but with a different driver) in future.

> +
> +Example:
> +
> +	shub_spi: shub {
> +		compatible = "samsung,rinato-ssp-spi";
> +		spi-max-frequency = <5000000>;
> +		interrupt-parent = <&gpx0>;
> +		interrupts = <2 0>;
> +		ap-mcu-int = <&gpx0 0 0>;
> +		mcu-ap-int = <&gpx0 4 0>;
> +		mcu-reset = <&gpx0 5 0>;
> +		mag-table = /bits/ 8 <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>;
> +		sensor@0 {
> +			compatible = "samsung,mpu6500-accel";
> +		};
> +		sensor@1 {
> +			compatible = "samsung,mpu6500-gyro";
> +		};
> +		sensor@2 {
> +			compatible = "samsung,adpd142";
> +		};
> +	};
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC/PATCH 4/6] iio: sensorhub: Add sensorhub iio commons
  2014-09-03 14:55 ` [RFC/PATCH 4/6] iio: sensorhub: Add sensorhub iio commons Karol Wrona
@ 2014-09-14 15:54   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2014-09-14 15:54 UTC (permalink / raw)
  To: Karol Wrona, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park

On 03/09/14 15:55, Karol Wrona wrote:
> This patch adds common library and trigger module for sensorhub iio drivers.
>
> Signed-off-by: Karol Wrona <k.wrona@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
A few litle bits inline.
> ---
>  drivers/iio/common/Kconfig                         |    1 +
>  drivers/iio/common/Makefile                        |    1 +
>  drivers/iio/common/ssp_sensors/Kconfig             |   15 ++++
>  drivers/iio/common/ssp_sensors/Makefile            |    6 ++
>  .../iio/common/ssp_sensors/ssp-sensor-trigger.c    |   92 ++++++++++++++++++++
>  drivers/iio/common/ssp_sensors/ssp_iio.c           |   35 ++++++++
>  drivers/iio/common/ssp_sensors/ssp_iio_sensor.h    |   62 +++++++++++++
>  7 files changed, 212 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-sensor-trigger.c
>  create mode 100644 drivers/iio/common/ssp_sensors/ssp_iio.c
>  create mode 100644 drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
>
> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> index 0b6e97d..630f9d8 100644
> --- a/drivers/iio/common/Kconfig
> +++ b/drivers/iio/common/Kconfig
> @@ -4,3 +4,4 @@
>
>  source "drivers/iio/common/hid-sensors/Kconfig"
>  source "drivers/iio/common/st_sensors/Kconfig"
> +source "drivers/iio/common/ssp_sensors/Kconfig"
> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> index 3112df0..5119642 100644
> --- a/drivers/iio/common/Makefile
> +++ b/drivers/iio/common/Makefile
> @@ -9,3 +9,4 @@
>  # When adding new entries keep the list in alphabetical order
>  obj-y += hid-sensors/
>  obj-y += st_sensors/
> +obj-y += ssp_sensors/
> diff --git a/drivers/iio/common/ssp_sensors/Kconfig b/drivers/iio/common/ssp_sensors/Kconfig
> new file mode 100644
> index 0000000..eefabdb
> --- /dev/null
> +++ b/drivers/iio/common/ssp_sensors/Kconfig
> @@ -0,0 +1,15 @@
> +#
> +# Hid Sensor common modules
> +#
> +menu "SSP Sensor IIO Common"
> +
> +config SSP_SENSOR_IIO
> +	tristate "Common module (trigger) for all SSP Sensor IIO drivers"
> +	depends on SENSORS_SSP
> +	select IIO_TRIGGER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	  Say yes here to build trigger support for SSP sensors.
> +	  Triggers will be send if all requested attributes were read.
> +
> +endmenu
> diff --git a/drivers/iio/common/ssp_sensors/Makefile b/drivers/iio/common/ssp_sensors/Makefile
> new file mode 100644
> index 0000000..da47549
> --- /dev/null
> +++ b/drivers/iio/common/ssp_sensors/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for the SSP sensor common modules.
> +#
> +
> +obj-$(CONFIG_SSP_SENSOR_IIO) += ssp-sensor-trigger.o
> +obj-$(CONFIG_SSP_SENSOR_IIO) += ssp_iio.o
> diff --git a/drivers/iio/common/ssp_sensors/ssp-sensor-trigger.c b/drivers/iio/common/ssp_sensors/ssp-sensor-trigger.c
> new file mode 100644
> index 0000000..0783cca
> --- /dev/null
> +++ b/drivers/iio/common/ssp_sensors/ssp-sensor-trigger.c
> @@ -0,0 +1,92 @@
> +/*
> + *  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/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/kfifo_buf.h>
Lots of headers in ehre I can't actually see you using... Please check them
in all the files.
> +#include <linux/iio/common/ssp_sensors.h>
> +#include "ssp_iio_sensor.h"
> +
> +static int set_rdy_trigger_state(struct iio_trigger *trigger, bool state)
> +{
> +	int ret = 0;
> +	struct ssp_sensor_data *c = iio_trigger_get_drvdata(trigger);
> +	struct ssp_data *data = dev_get_drvdata(trigger->dev.parent->parent);
> +
> +	if (state)
> +		ret = ssp_enable_sensor(data, c->type,
> +				ssp_get_sensor_delay(data,
> +						     c->type));
> +	else
> +		ret = ssp_disable_sensor(data, c->type);
> +
> +	return ret;
> +}
> +
> +static struct iio_trigger_ops ssp_sensor_trigger_ops = {
> +	.owner = THIS_MODULE,
> +	.set_trigger_state = set_rdy_trigger_state,
> +};
> +
> +void ssp_sensor_remove_trigger(struct iio_dev *indio_dev)
> +{
> +	iio_trigger_unregister(indio_dev->trig);
> +	iio_trigger_free(indio_dev->trig);
> +	indio_dev->trig = NULL;
This immediately suggests an issue... You should never be explicitly using
indio_dev->trig.  (checks below and finds the matching call which actually
has the problem).
> +}
> +EXPORT_SYMBOL(ssp_sensor_remove_trigger);
> +
> +int ssp_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
> +			     struct ssp_sensor_data *sdata)
Hohum.  I wonder if we would benefit from having a funciton like
this directly in the IIO core?  Certainly looks like it!

Can do that at some later point though...
> +{
> +	int ret;
> +	struct iio_trigger *trigger;
> +
> +	trigger = iio_trigger_alloc("%s-dev%d", name, indio_dev->id);
> +	if (trigger == NULL) {
> +		dev_err(&indio_dev->dev, "Trigger alloc failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	trigger->dev.parent = indio_dev->dev.parent;
> +	trigger->ops = &ssp_sensor_trigger_ops;
> +	iio_trigger_set_drvdata(trigger, sdata);
> +
> +	ret = iio_trigger_register(trigger);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Trigger register failed\n");
> +		goto error_free;
> +	}
> +	indio_dev->trig = trigger;
We keep hitting this.  The above is a bug as it doesn't take a reference
to the trigger.  You need an iio_trigger_get call.  We have a fix set for
this issue queued up that allows
indio_dev->trig = iio_trigger_get(trigger);  which is cleaner.
> +
> +	return ret;
> +
> +error_free:
> +	iio_trigger_free(trigger);
> +	return ret;
> +}
> +EXPORT_SYMBOL(ssp_sensor_setup_trigger);
> +
> +MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
> +MODULE_DESCRIPTION("SSP Sensor trigger processing");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/iio/common/ssp_sensors/ssp_iio.c b/drivers/iio/common/ssp_sensors/ssp_iio.c
> new file mode 100644
> index 0000000..fc88643
> --- /dev/null
> +++ b/drivers/iio/common/ssp_sensors/ssp_iio.c
> @@ -0,0 +1,35 @@
> +/*
> + *  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/kernel.h>
> +#include <linux/iio/common/ssp_sensors.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include "ssp_iio_sensor.h"
> +
> +/**
> + * @ssp_common_destroy_sensor() - ssp sensor cleanup
> + *
> + * @indio_dev: iio device
> + */
> +void ssp_common_destroy_sensor(struct iio_dev *indio_dev)
> +{
> +	iio_device_unregister(indio_dev);
> +	ssp_sensor_remove_trigger(indio_dev);
> +	iio_triggered_buffer_cleanup(indio_dev);
> +	iio_device_free(indio_dev);
No blank line here.  I wonder if it is worth using devm_iio_device_alloc
and loosing the iio_device_free.  Perhaps it will add more confusion than
anything.  Right now I'd argue this function is not worth bothering with
at all as it slightly obscures what is going on in the callers.
> +
> +}
> +EXPORT_SYMBOL(ssp_common_destroy_sensor);
> diff --git a/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h b/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
> new file mode 100644
> index 0000000..1911fbd
> --- /dev/null
> +++ b/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
> @@ -0,0 +1,62 @@
> +#ifndef __SSP_IIO_SENSOR_H__
> +#define __SSP_IIO_SENSOR_H__
> +
I suspect you'll fairly quickly end up moving this nearer to the individual
sub drivers as they will have their own slight differences. However
you can do that when it becomes necessary.  Fine having it here for now.
> +#define SSP_CHANNEL_AG(_type, _mod, _index) \
> +{ \
> +		.type = _type,\
> +		.modified = 1,\
> +		.channel2 = _mod,\
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |\
> +					BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> +		.scan_index = _index,\
> +		.scan_type = {\
> +			.sign = 's',\
> +			.realbits = 16,\
> +			.storagebits = 16,\
> +			.shift = 0,\
> +			.endianness = IIO_LE,\
> +		},\
> +}
> +
> +#define SSP_DIVISOR	1000000ULL
> +#define SSP_MS_PER_S	1000
> +#define SSP_DIVIDEND	(SSP_DIVISOR * SSP_MS_PER_S)
> +
> +int ssp_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
> +			     struct ssp_sensor_data *data);
> +
> +void ssp_sensor_remove_trigger(struct iio_dev *indio_dev);
> +
> +/* Converts time in ms to frequency */
> +static inline void ssp_convert_to_freq(u32 time, int *integer_part,
> +				       int *fractional)
> +{
> +	unsigned int value;
> +
> +	if (time == 0) {
> +		*fractional = 0;
> +		*integer_part = 0;
> +		return;
> +	}
> +
> +	value = SSP_DIVIDEND / time;
> +	*fractional = value % SSP_DIVISOR;
> +	*integer_part = value / SSP_DIVISOR;
> +}
> +
> +/* Converts frequency to time in ms*/
> +static inline int ssp_convert_to_time(int integer_part, int fractional)
> +{
> +	u64 value;
> +
> +	value = integer_part * SSP_DIVISOR + fractional;
> +	if (value == 0)
> +		return 0;
> +
> +	return  div_u64(SSP_DIVIDEND, value);
> +}
> +
> +void ssp_common_destroy_sensor(struct iio_dev *indio_dev);
> +
> +#endif /* __SSP_IIO_SENSOR_H__ */
> +
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC/PATCH 6/6] iio: sensorhub: Add sensorhub gyroscope sensor
  2014-09-03 14:55 ` [RFC/PATCH 6/6] iio: sensorhub: Add sensorhub gyroscope sensor Karol Wrona
@ 2014-09-14 17:09   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2014-09-14 17:09 UTC (permalink / raw)
  To: Karol Wrona, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park

On 03/09/14 15:55, Karol Wrona wrote:
> This patch adds gyroscope iio driver which uses sensorhub as data
> provider.
>
> Signed-off-by: Karol Wrona <k.wrona@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Clearly this is pretty much a clone of the accelerometer driver.
As such all the comments on that apply.

It might be possible to share more with the accelerometer driver but perhaps
it is best to wait until a few more subdrivers are in place so we can
evaluate how to reduce code repitition properly.

Jonathan
> ---
>  drivers/iio/gyro/Makefile          |    2 +
>  drivers/iio/gyro/ssp_gyro_sensor.c |  225 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 227 insertions(+)
>  create mode 100644 drivers/iio/gyro/ssp_gyro_sensor.c
>
> diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
> index 2f2752a..b9c57a0 100644
> --- a/drivers/iio/gyro/Makefile
> +++ b/drivers/iio/gyro/Makefile
> @@ -21,3 +21,5 @@ st_gyro-$(CONFIG_IIO_BUFFER) += st_gyro_buffer.o
>
>  obj-$(CONFIG_IIO_ST_GYRO_I2C_3AXIS) += st_gyro_i2c.o
>  obj-$(CONFIG_IIO_ST_GYRO_SPI_3AXIS) += st_gyro_spi.o
> +
> +obj-$(CONFIG_SSP_SENSOR_IIO) += ssp_gyro_sensor.o
> \ No newline at end of file
> diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c
> new file mode 100644
> index 0000000..b2362a4
> --- /dev/null
> +++ b/drivers/iio/gyro/ssp_gyro_sensor.c
> @@ -0,0 +1,225 @@
> +/*
> + *  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/platform_device.h>
> +#include <linux/stat.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/common/ssp_sensors.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/common/ssp_sensors.h>
> +#include "../common/ssp_sensors/ssp_iio_sensor.h"
> +
> +#define CHANNEL_COUNT 3
> +
> +#define SSP_GYROSCOPE_NAME "ssp-gyroscope"
> +static const char ssp_gyro_name[] = SSP_GYROSCOPE_NAME;
> +
> +enum gyro_3d_channel {
> +	CHANNEL_SCAN_INDEX_X,
> +	CHANNEL_SCAN_INDEX_Y,
> +	CHANNEL_SCAN_INDEX_Z,
> +	CHANNEL_SCAN_INDEX_TIME,
> +	GYRO_3D_CHANNEL_MAX,
> +};
> +
> +static int gyro_read_raw(struct iio_dev *indio_dev,
> +			  struct iio_chan_spec const *chan,
> +			  int *val,
> +			  int *val2,
> +			  long mask)
> +{
> +	int ret;
> +	u32 t;
> +	struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		t = ssp_get_sensor_delay(data, SSP_GYROSCOPE_SENSOR);
> +		ssp_convert_to_freq(t, val, val2);
> +		ret = IIO_VAL_INT_PLUS_MICRO;
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int gyro_write_raw(struct iio_dev *indio_dev,
> +			   struct iio_chan_spec const *chan,
> +			   int val,
> +			   int val2,
> +			   long mask)
> +{
> +	int ret, value;
> +	struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		value = ssp_convert_to_time(val, val2);
> +		ret = ssp_change_delay(data, SSP_GYROSCOPE_SENSOR, value);
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev, "gyro sensor enable fail\n");
> +			ret = -EINVAL;
> +		}
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int gyro_write_raw_get_fmt(struct iio_dev *indio_dev,
> +			       struct iio_chan_spec const *chan,
> +			       long mask)
> +{
> +	return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static struct iio_info ssp_gyro_iio_info = {
> +	.read_raw = &gyro_read_raw,
> +	.write_raw = &gyro_write_raw,
> +	.write_raw_get_fmt = &gyro_write_raw_get_fmt,
> +};
> +
> +static const struct iio_chan_spec gyro_channels[] = {
> +	SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_X, CHANNEL_SCAN_INDEX_X),
> +	SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Y, CHANNEL_SCAN_INDEX_Y),
> +	SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Z, CHANNEL_SCAN_INDEX_Z),
> +	IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIME),
> +};
> +
> +static int process_gyro_data(struct iio_dev *indio_dev, void *buf,
> +			      s64 timestamp)
> +{
> +	__le64 time = 0;
> +	u8 data[16] = {0};
> +	const int len = SSP_GYROSCOPE_SIZE;
> +
> +	if (indio_dev->scan_bytes == 0)
> +		return indio_dev->scan_bytes;
> +
> +	memcpy(data, buf, SSP_GYROSCOPE_SIZE);
> +
> +	if (indio_dev->scan_timestamp) {
> +		memcpy(&time, &((char *)(buf))[len], SSP_TIME_SIZE);
> +		*(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) =
> +			timestamp + le64_to_cpu(time) * 1000000;
> +	}
> +
> +	iio_push_to_buffers(indio_dev, (u8 *)data);
> +
> +	return indio_dev->scan_bytes;
> +}
> +
> +static int ssp_gyro_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> +	ssp_common_destroy_sensor(indio_dev);
> +
> +	return 0;
> +}
> +
> +static int ssp_gyro_probe(struct platform_device *pdev)
> +{
> +	int ret = 0;
> +	struct iio_dev *indio_dev;
> +	struct ssp_sensor_data  *spd;
> +
> +	indio_dev = iio_device_alloc(sizeof(*spd));
> +	if (IS_ERR(indio_dev)) {
> +		dev_err(&pdev->dev, "device alloc fail\n");
> +		return PTR_ERR(indio_dev);
> +	}
> +
> +	spd = iio_priv(indio_dev);
> +
> +	spd->process_data = process_gyro_data;
> +	spd->type = SSP_GYROSCOPE_SENSOR;
> +
> +	indio_dev->name = ssp_gyro_name;
> +	indio_dev->dev.parent = &pdev->dev;
> +	indio_dev->info = &ssp_gyro_iio_info;
> +
> +	if (indio_dev->info == NULL)
> +		goto err_iio_alloc;
> +
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels = gyro_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(gyro_channels);
> +
> +	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
> +					 NULL, NULL);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Triggered buffer setup fail\n");
> +		goto err_iio_alloc;
> +	}
> +
> +	ret = ssp_sensor_setup_trigger(indio_dev, ssp_gyro_name, spd);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev, "Trigger setup fail\n");
> +		goto err_iio_tbuffer;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret < 0)
> +		goto err_ssp_trigger;
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	ssp_register_consumer(indio_dev, SSP_GYROSCOPE_SENSOR);
> +
> +	return 0;
> +
> +err_ssp_trigger:
> +	ssp_sensor_remove_trigger(indio_dev);
> +err_iio_tbuffer:
> +	iio_triggered_buffer_cleanup(indio_dev);
> +err_iio_alloc:
> +	iio_device_free(indio_dev);
> +
> +	return ret;
> +}
> +
> +
> +static const struct of_device_id ssp_gyro_id_table[] = {
> +	{
> +		.compatible = "samsung,mpu6500-gyro",
> +	},
> +	{},
> +};
> +
> +static struct platform_driver ssp_gyro_driver = {
> +	.driver = {
> +		.owner = THIS_MODULE,
> +		.name = SSP_GYROSCOPE_NAME,
> +		.of_match_table = ssp_gyro_id_table,
> +	},
> +	.probe = ssp_gyro_probe,
> +	.remove =  ssp_gyro_remove,
> +};
> +
> +module_platform_driver(ssp_gyro_driver);
> +
> +MODULE_AUTHOR("Karol Wrona <k.wwrona@samsung.com>");
> +MODULE_DESCRIPTION("Samsung sensorhub gyroscopes driver");
> +MODULE_LICENSE("GPL v2");
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC/PATCH 5/6] iio: sensorhub: Add sensorhub accelerometer sensor
  2014-09-03 14:55 ` [RFC/PATCH 5/6] iio: sensorhub: Add sensorhub accelerometer sensor Karol Wrona
@ 2014-09-14 17:10   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2014-09-14 17:10 UTC (permalink / raw)
  To: Karol Wrona, linux-iio, Arnd Bergmann, linux-kernel
  Cc: Bartlomiej Zolnierkiewicz, Kyungmin Park

On 03/09/14 15:55, Karol Wrona wrote:
> This patch adds accelerometer iio driver which uses sensorhub as data
> provider.
>
> Signed-off-by: Karol Wrona <k.wrona@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
A few bits and bobs. The biggest one is that your triggers don't add anything.
It's not necessary to have triggers, you can have buffers directly without
them if they make no sense (as it appears the don't here).

Other bits inline.
> ---
>  drivers/iio/accel/Makefile           |    2 +
>  drivers/iio/accel/ssp_accel_sensor.c |  240 ++++++++++++++++++++++++++++++++++
>  2 files changed, 242 insertions(+)
>  create mode 100644 drivers/iio/accel/ssp_accel_sensor.c
>
> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> index a593996..2ab1ca6 100644
> --- a/drivers/iio/accel/Makefile
> +++ b/drivers/iio/accel/Makefile
> @@ -16,3 +16,5 @@ st_accel-$(CONFIG_IIO_BUFFER) += st_accel_buffer.o
>
>  obj-$(CONFIG_IIO_ST_ACCEL_I2C_3AXIS) += st_accel_i2c.o
>  obj-$(CONFIG_IIO_ST_ACCEL_SPI_3AXIS) += st_accel_spi.o
> +
> +obj-$(CONFIG_SSP_SENSOR_IIO) += ssp_accel_sensor.o
> diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c
> new file mode 100644
> index 0000000..5e7d3e0
> --- /dev/null
> +++ b/drivers/iio/accel/ssp_accel_sensor.c
> @@ -0,0 +1,240 @@
> +/*
> + *  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/platform_device.h>
> +#include <linux/stat.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/common/ssp_sensors.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/common/ssp_sensors.h>
> +#include "../common/ssp_sensors/ssp_iio_sensor.h"
> +
> +#define CHANNEL_COUNT 3
> +
> +#define SSP_ACCEL_NAME "ssp-accelrometer"
> +static const char ssp_accel_device_name[] = SSP_ACCEL_NAME;
> +
> +enum accel_3d_channel {
> +	CHANNEL_SCAN_INDEX_X,
> +	CHANNEL_SCAN_INDEX_Y,
> +	CHANNEL_SCAN_INDEX_Z,
> +	CHANNEL_SCAN_INDEX_TIME,
> +	ACCEL_3D_CHANNEL_MAX,
> +};
> +
> +struct ssp_accel_state {
> +	int accel_val;
> +	int accel_calibbias;
> +	struct ssp_data *data;
> +	short calibration_data[CHANNEL_COUNT];
> +};
> +
> +static struct ssp_accel_state accel_state;
Why not dynamically allocate this and loose the global?
Both more elegant and in theory allows multiple accelerometers on some
future variant of this part (you never know ;)
> +
> +static int accel_read_raw(struct iio_dev *indio_dev,
> +			  struct iio_chan_spec const *chan,
> +			  int *val,
> +			  int *val2,
> +			  long mask)
> +{
> +	int ret;
> +	u32 t;
> +	struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		t = ssp_get_sensor_delay(data, SSP_ACCELEROMETER_SENSOR);
> +		ssp_convert_to_freq(t, val, val2);
> +		ret = IIO_VAL_INT_PLUS_MICRO;
> +		break;
Interesting - no provision for polled data reading.  Fair enough if there
is no hardware support.... However, you have it in the info_mask for the
channels which isn't good.
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int accel_write_raw(struct iio_dev *indio_dev,
> +			   struct iio_chan_spec const *chan,
> +			   int val,
> +			   int val2,
> +			   long mask)
> +{
> +	int ret, value;
> +	struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		value = ssp_convert_to_time(val, val2);
> +		ret = ssp_change_delay(data, SSP_ACCELEROMETER_SENSOR, value);
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev, "accel sensor enable fail\n");
> +			ret = -EINVAL;
> +		}
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int accel_write_raw_get_fmt(struct iio_dev *indio_dev,
> +			       struct iio_chan_spec const *chan,
> +			       long mask)
> +{
> +	return IIO_VAL_INT_PLUS_MICRO;
That's the default. No need to have this function.
> +}
> +
> +static struct iio_info ssp_accel_iio_info = {
> +	.read_raw = &accel_read_raw,
> +	.write_raw = &accel_write_raw,
> +	.write_raw_get_fmt = &accel_write_raw_get_fmt,
> +};
> +
> +static const struct iio_chan_spec acc_channels[] = {
> +	SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_X, CHANNEL_SCAN_INDEX_X),
> +	SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Y, CHANNEL_SCAN_INDEX_Y),
> +	SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Z, CHANNEL_SCAN_INDEX_Z),
> +	IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIME),
> +};
> +
> +int accel_open_calibration(struct ssp_data *data)
> +{
> +	return 0;
What's this?
> +}
> +
> +static int process_accel_data(struct iio_dev *indio_dev, void *buf,
> +			      s64 timestamp)
> +{
> +	__le64 time = 0;
> +	u8 data[16] = {0};
> +	const int len = SSP_ACCELEROMETER_SIZE;
> +
> +	if (indio_dev->scan_bytes == 0)
> +		return indio_dev->scan_bytes;
> +
> +	memcpy(data, buf, SSP_ACCELEROMETER_SIZE);
> +
> +	if (indio_dev->scan_timestamp) {
> +		memcpy(&time, &((char *)(buf))[len], SSP_TIME_SIZE);
> +		*(s64 *)((u8 *)data +  ALIGN(len, sizeof(s64))) =
> +			timestamp + le64_to_cpu(time) * 1000000;
> +	}
> +
> +	iio_push_to_buffers(indio_dev, (u8 *)data);
Use iio_push_to_buffers_with_timestamp though beware that this still needs
there to be enough space in the buffer.
> +
> +	return indio_dev->scan_bytes;
> +}
> +
> +static int ssp_accel_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> +	ssp_common_destroy_sensor(indio_dev);
Don't like this call as it means that, to check ordering etc, I have
to go find that function. It'll be a few more lines, but I'd much
prefer the code being here.
> +
> +	return 0;
> +}
> +
> +static int ssp_accel_probe(struct platform_device *pdev)
> +{
> +	int ret = 0;
> +	struct iio_dev *indio_dev;
> +	struct ssp_sensor_data  *spd;
> +
> +	indio_dev = iio_device_alloc(sizeof(*spd));
> +	if (IS_ERR(indio_dev)) {
> +		dev_err(&pdev->dev, "device alloc fail\n");
> +		return PTR_ERR(indio_dev);
> +	}
> +
> +	spd = iio_priv(indio_dev);
> +
> +	spd->process_data = process_accel_data;
> +	spd->prv_data = &accel_state;
> +	spd->type = SSP_ACCELEROMETER_SENSOR;
> +
> +	indio_dev->name = ssp_accel_device_name;
> +	indio_dev->dev.parent = &pdev->dev;
> +	indio_dev->dev.of_node = pdev->dev.of_node;
> +	indio_dev->info = &ssp_accel_iio_info;
> +
> +	if (indio_dev->info == NULL)
> +		goto err_iio_alloc;
> +
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels = acc_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(acc_channels);
> +
> +	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
> +					 NULL, NULL);
This is interesting.  On a trigger you stash the timestamp but then as
far as I can see never do anything with it?
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Triggered buffer setup fail\n");
> +		goto err_iio_alloc;
> +	}
> +
> +	ret = ssp_sensor_setup_trigger(indio_dev, ssp_accel_device_name, spd);
What does the trigger actually do?  Normally I'd expect it to cause the
poll func elements to be called and ultimately be responsible for causing
data to be pushed into the buffers.  Here that is not the case.

You are simply using the enabling of the trigger to turn on the sensor.
Don't do this.  Drop the trigger and register a buffer directly.  See for
example how adc/ti_am335x.c does it. That way there is no 'fake' trigger
in place.  If there is no means of attaching other devices to the trigger then
there is little point in exposing it.
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev, "Trigger setup fail\n");
> +		goto err_iio_tbuffer;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret < 0)
> +		goto err_ssp_trigger;
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	ssp_register_consumer(indio_dev, SSP_ACCELEROMETER_SENSOR);
> +
> +	return 0;
> +
> +err_ssp_trigger:
> +	ssp_sensor_remove_trigger(indio_dev);
> +err_iio_tbuffer:
> +	iio_triggered_buffer_cleanup(indio_dev);
> +err_iio_alloc:
If you use a devm_iio_device_alloc call this can be dropped.  As mentioned
previously I don't like hiding the remove free in a wrapper function.
> +	iio_device_free(indio_dev);
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id ssp_accel_id_table[] = {
> +	{
> +		.compatible = "samsung,mpu6500-accel",
> +	},
> +	{},
> +};
> +
> +static struct platform_driver ssp_accel_driver = {
> +	.driver = {
> +		.owner = THIS_MODULE,
> +		.name = SSP_ACCEL_NAME,
> +		.of_match_table = ssp_accel_id_table,
> +	},
> +	.probe = ssp_accel_probe,
> +	.remove =  ssp_accel_remove,
> +};
> +
> +module_platform_driver(ssp_accel_driver);
> +
> +MODULE_AUTHOR("Karol Wrona <k.wwrona@samsung.com>");
> +MODULE_DESCRIPTION("Samsung sensorhub accelerometers driver");
> +MODULE_LICENSE("GPL v2");
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2014-09-14 17:10 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-09-03 14:55 [RFC/PATCH 0/6] iio: Add sensorhub driver Karol Wrona
2014-09-03 14:55 ` [RFC/PATCH 1/6] iio:sensorhub: Add sensorhub common library Karol Wrona
2014-09-14 14:17   ` Jonathan Cameron
2014-09-03 14:55 ` [RFC/PATCH 2/6] misc: sensorhub: Add sensorhub driver Karol Wrona
2014-09-14 15:42   ` Jonathan Cameron
2014-09-03 14:55 ` [RFC/PATCH 3/6] sensorhub: Add sensorhub bindings Karol Wrona
2014-09-14 15:45   ` Jonathan Cameron
2014-09-03 14:55 ` [RFC/PATCH 4/6] iio: sensorhub: Add sensorhub iio commons Karol Wrona
2014-09-14 15:54   ` Jonathan Cameron
2014-09-03 14:55 ` [RFC/PATCH 5/6] iio: sensorhub: Add sensorhub accelerometer sensor Karol Wrona
2014-09-14 17:10   ` Jonathan Cameron
2014-09-03 14:55 ` [RFC/PATCH 6/6] iio: sensorhub: Add sensorhub gyroscope sensor Karol Wrona
2014-09-14 17:09   ` Jonathan Cameron
2014-09-03 19:52 ` [RFC/PATCH 0/6] iio: Add sensorhub driver Jonathan Cameron
2014-09-08 14:55   ` Karol Wrona
2014-09-11 11:05     ` Octavian Purdila
2014-09-14 14:14       ` Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).