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