summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaewon Kim <jaewon02.kim@samsung.com>2014-07-15 16:05:25 +0900
committerChanho Park <chanho61.park@samsung.com>2014-08-07 15:17:53 +0900
commit86629f2a48e66f4bc5f1c5a8071ee8c776e7585f (patch)
treeba991e5aac40e8bc06a85e8b01c670127d8e4685
parent11c71d8535941d9a115673ce74a3b03185db17de (diff)
downloadlinux-3.10-86629f2a48e66f4bc5f1c5a8071ee8c776e7585f.tar.gz
linux-3.10-86629f2a48e66f4bc5f1c5a8071ee8c776e7585f.tar.bz2
linux-3.10-86629f2a48e66f4bc5f1c5a8071ee8c776e7585f.zip
sensorhub: add sensorhub driver
This patch add sensorhub driver to communicate MCU called sensorhub. It require firmware driver for MCU. Signed-off-by: Jaewon Kim <jaewon02.kim@samsung.com>
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile3
-rw-r--r--drivers/sensorhub/Kconfig5
-rw-r--r--drivers/sensorhub/Makefile5
-rw-r--r--drivers/sensorhub/stm/Kconfig164
-rw-r--r--drivers/sensorhub/stm/Makefile16
-rw-r--r--drivers/sensorhub/stm/factory/accel_k330.c309
-rw-r--r--drivers/sensorhub/stm/factory/accel_mpu6050.c304
-rw-r--r--drivers/sensorhub/stm/factory/accel_mpu6500.c406
-rw-r--r--drivers/sensorhub/stm/factory/gyro_k330.c331
-rw-r--r--drivers/sensorhub/stm/factory/gyro_mpu6050.c520
-rw-r--r--drivers/sensorhub/stm/factory/gyro_mpu6500.c615
-rw-r--r--drivers/sensorhub/stm/factory/hrm_adpd142.c122
-rw-r--r--drivers/sensorhub/stm/factory/mcu_at32uc3l0128.c252
-rw-r--r--drivers/sensorhub/stm/factory/mcu_atuc128l5har.c312
-rw-r--r--drivers/sensorhub/stm/factory/mcu_stm32f401.c355
-rw-r--r--drivers/sensorhub/stm/sensors_core.c172
-rw-r--r--drivers/sensorhub/stm/ssp.h648
-rw-r--r--drivers/sensorhub/stm/ssp_data.c258
-rw-r--r--drivers/sensorhub/stm/ssp_debug.c295
-rw-r--r--drivers/sensorhub/stm/ssp_dev.c675
-rw-r--r--drivers/sensorhub/stm/ssp_firmware.c822
-rw-r--r--drivers/sensorhub/stm/ssp_input.c269
-rw-r--r--drivers/sensorhub/stm/ssp_sensorhub.c707
-rw-r--r--drivers/sensorhub/stm/ssp_sensorhub.h72
-rw-r--r--drivers/sensorhub/stm/ssp_spi.c775
-rw-r--r--drivers/sensorhub/stm/ssp_sysfs.c531
-rw-r--r--include/linux/ssp_platformdata.h38
28 files changed, 8983 insertions, 0 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 2943fb6c038..20896ff9f3b 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -168,4 +168,6 @@ source "drivers/reset/Kconfig"
source "drivers/phy/Kconfig"
+source "drivers/sensorhub/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 46c99a1f1a9..affabdbec51 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -154,3 +154,6 @@ obj-$(CONFIG_IIO) += iio/
obj-$(CONFIG_VME_BUS) += vme/
obj-$(CONFIG_IPACK_BUS) += ipack/
obj-$(CONFIG_NTB) += ntb/
+
+# sensorhub drivers
+obj-$(CONFIG_SENSORS_SSP) += sensorhub/
diff --git a/drivers/sensorhub/Kconfig b/drivers/sensorhub/Kconfig
new file mode 100644
index 00000000000..a1abcce2ef2
--- /dev/null
+++ b/drivers/sensorhub/Kconfig
@@ -0,0 +1,5 @@
+menu "Sensor HUB Drivers"
+
+source "drivers/sensorhub/stm/Kconfig"
+
+endmenu
diff --git a/drivers/sensorhub/Makefile b/drivers/sensorhub/Makefile
new file mode 100644
index 00000000000..57cb83bfbb8
--- /dev/null
+++ b/drivers/sensorhub/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the sensor hub.
+#
+
+obj-$(CONFIG_SENSORS_SSP_STM) += stm/
diff --git a/drivers/sensorhub/stm/Kconfig b/drivers/sensorhub/stm/Kconfig
new file mode 100644
index 00000000000..51e33e222d3
--- /dev/null
+++ b/drivers/sensorhub/stm/Kconfig
@@ -0,0 +1,164 @@
+#
+# sensor drivers configuration
+#
+config SENSORS_SYSFS
+ tristate "Sensors sysfs"
+ help
+ Support sysfs for sensors.
+ If you say yes here you get sysfs support for
+ sensor factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called sensors_core.
+
+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.
+
+config SENSORS_SSP_STM
+ tristate "Sensors ssp stm"
+ 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.
+
+config SENSORS_SSP_MPU6500
+ tristate "Sensors ssp mpu6500"
+ default n
+ depends on SPI
+ help
+ mpu6500 file for factory test in ssp driver.
+ If you say yes here you get mpu6500 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_AK09911
+ tristate "Sensors ssp ak09911"
+ default n
+ depends on SPI
+ help
+ ak09911 file for factory test and doeplus in ssp driver.
+ If you say yes here you get ak09911 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_YAS532
+ tristate "Sensors ssp yas532"
+ default n
+ depends on SPI
+ help
+ yas532 file for factory test in ssp driver.
+ If you say yes here you get yas532 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_CM3320
+ tristate "Sensors ssp cm3320"
+ default n
+ depends on SPI
+ help
+ cm3320 file for factory test in ssp driver.
+ If you say yes here you get cm3320 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_MAX88920
+ tristate "Sensors ssp max88920"
+ default n
+ depends on SPI
+ help
+ max88920 file for factory test in ssp driver.
+ If you say yes here you get max88920 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_MAX88921
+ tristate "Sensors ssp max88921"
+ default n
+ depends on SPI
+ help
+ max88921 file for factory test in ssp driver.
+ If you say yes here you get max88921 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_LPS25H
+ tristate "Sensors ssp lps25h"
+ default n
+ depends on SPI
+ help
+ lps25h file for factory test in ssp driver.
+ If you say yes here you get lps25h support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_SHTC1
+ tristate "Sensors ssp shtc1"
+ default n
+ depends on SPI
+ help
+ shtc1 file for factory test in ssp driver.
+ If you say yes here you get shtc1 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_ADPD142
+ tristate "Sensors ssp adpd142"
+ default n
+ depends on SPI
+ help
+ adpd142 file for factory test in ssp driver.
+ If you say yes here you get adpd142 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_SHTC1_VER
+ string "Sensors shtc1 version name"
+ default "No_Version_Name"
+ depends on SENSORS_SSP_SHTC1
+ help
+ shtc1 version for temphumid factory test in ssp driver.
+ If you give version name here you get shtc1 version for
+ lcd test.
+
+config SENSORS_SSP_STM32F401
+ tristate "Sensors ssp stm32f401"
+ default n
+ depends on SPI
+ help
+ stm32f401 file for factory test in ssp driver.
+ If you say yes here you get stm32f401 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_SENSORHUB
+ tristate "Sensors ssp sensorhub"
+ default n
+ depends on SPI
+ help
+ ssp sensor hub 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.
+
diff --git a/drivers/sensorhub/stm/Makefile b/drivers/sensorhub/stm/Makefile
new file mode 100644
index 00000000000..8b1dd6bd767
--- /dev/null
+++ b/drivers/sensorhub/stm/Makefile
@@ -0,0 +1,16 @@
+#
+# 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_sysfs.o\
+ ssp_input.o ssp_firmware.o ssp_debug.o
+obj-$(CONFIG_SENSORS_SYSFS) += sensors_core.o
+
+obj-$(CONFIG_SENSORS_SSP_MPU6500) += factory/accel_mpu6500.o factory/gyro_mpu6500.o
+
+obj-$(CONFIG_SENSORS_SSP_ADPD142) += factory/hrm_adpd142.o
+
+obj-$(CONFIG_SENSORS_SSP_STM32F401) += factory/mcu_stm32f401.o
+
+obj-$(CONFIG_SENSORS_SSP_SENSORHUB) += ssp_sensorhub.o
diff --git a/drivers/sensorhub/stm/factory/accel_k330.c b/drivers/sensorhub/stm/factory/accel_k330.c
new file mode 100644
index 00000000000..8ab4e4d3bd0
--- /dev/null
+++ b/drivers/sensorhub/stm/factory/accel_k330.c
@@ -0,0 +1,309 @@
+/*
+ * 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"
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+#define VENDOR "STM"
+#define CHIP_ID "K330"
+
+#define CALIBRATION_FILE_PATH "/efs/calibration_data"
+#define CALIBRATION_DATA_AMOUNT 20
+
+static ssize_t accel_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VENDOR);
+}
+
+static ssize_t accel_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", CHIP_ID);
+}
+
+int accel_open_calibration(struct ssp_data *data)
+{
+ int iRet = 0;
+ mm_segment_t old_fs;
+ struct file *cal_filp = NULL;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+
+ data->accelcal.x = 0;
+ data->accelcal.y = 0;
+ data->accelcal.z = 0;
+
+ return iRet;
+ }
+
+ iRet = cal_filp->f_op->read(cal_filp, (char *)&data->accelcal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int))
+ iRet = -EIO;
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ ssp_dbg("open accel calibration %d, %d, %d\n",
+ data->accelcal.x, data->accelcal.y, data->accelcal.z);
+
+ if ((data->accelcal.x == 0) && (data->accelcal.y == 0)
+ && (data->accelcal.z == 0))
+ return ERROR;
+
+ return iRet;
+}
+
+static int enable_accel_for_cal(struct ssp_data *data)
+{
+ u8 uBuf[2] = {0, 10};
+
+ if (atomic_read(&data->aSensorEnable) & (1 << ACCELEROMETER_SENSOR)) {
+ if (get_msdelay(data->adDelayBuf[ACCELEROMETER_SENSOR]) != 10) {
+ send_instruction(data, CHANGE_DELAY,
+ ACCELEROMETER_SENSOR, uBuf, 2);
+ return SUCCESS;
+ }
+ } else {
+ send_instruction(data, ADD_SENSOR,
+ ACCELEROMETER_SENSOR, uBuf, 2);
+ }
+
+ return FAIL;
+}
+
+static void disable_accel_for_cal(struct ssp_data *data, int iDelayChanged)
+{
+ u8 uBuf[2] = {0, 10};
+
+ if (atomic_read(&data->aSensorEnable) & (1 << ACCELEROMETER_SENSOR)) {
+ uBuf[1] = get_msdelay(data->adDelayBuf[ACCELEROMETER_SENSOR]);
+ uBuf[0] = get_delay_cmd(uBuf[1]);
+ if (iDelayChanged)
+ send_instruction(data, CHANGE_DELAY,
+ ACCELEROMETER_SENSOR, uBuf, 2);
+ } else {
+ send_instruction(data, REMOVE_SENSOR,
+ ACCELEROMETER_SENSOR, uBuf, 2);
+ }
+}
+
+static int accel_do_calibrate(struct ssp_data *data, int iEnable)
+{
+ int iSum[3] = { 0, };
+ int iRet = 0, iCount;
+ struct file *cal_filp = NULL;
+ mm_segment_t old_fs;
+
+ if (iEnable) {
+ data->accelcal.x = 0;
+ data->accelcal.y = 0;
+ data->accelcal.z = 0;
+
+ iRet = enable_accel_for_cal(data);
+ msleep(300);
+
+ for (iCount = 0; iCount < CALIBRATION_DATA_AMOUNT; iCount++) {
+ iSum[0] += data->buf[ACCELEROMETER_SENSOR].x;
+ iSum[1] += data->buf[ACCELEROMETER_SENSOR].y;
+ iSum[2] += data->buf[ACCELEROMETER_SENSOR].z;
+ mdelay(10);
+ }
+ disable_accel_for_cal(data, iRet);
+
+ data->accelcal.x = (iSum[0] / CALIBRATION_DATA_AMOUNT);
+ data->accelcal.y = (iSum[1] / CALIBRATION_DATA_AMOUNT);
+ data->accelcal.z = (iSum[2] / CALIBRATION_DATA_AMOUNT);
+
+ if (data->accelcal.z > 0)
+ data->accelcal.z -= MAX_ACCEL_1G;
+ else if (data->accelcal.z < 0)
+ data->accelcal.z += MAX_ACCEL_1G;
+ } else {
+ data->accelcal.x = 0;
+ data->accelcal.y = 0;
+ data->accelcal.z = 0;
+ }
+
+ ssp_dbg("do accel calibrate %d, %d, %d\n",
+ data->accelcal.x, data->accelcal.y, data->accelcal.z);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH,
+ O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ ssp_err("Can't open calibration file\n");
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+ return iRet;
+ }
+
+ iRet = cal_filp->f_op->write(cal_filp, (char *)&data->accelcal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int)) {
+ ssp_err("Can't write the accelcal to file\n");
+ iRet = -EIO;
+ }
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ return iRet;
+}
+
+static ssize_t accel_calibration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int iRet;
+ int iCount = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ iRet = accel_open_calibration(data);
+ if (iRet < 0)
+ ssp_err("calibration open failed(%d)\n", iRet);
+
+ ssp_dbg("Cal data : %d %d %d - %d\n",
+ data->accelcal.x, data->accelcal.y, data->accelcal.z, iRet);
+
+ iCount = sprintf(buf, "%d %d %d %d\n", iRet, data->accelcal.x,
+ data->accelcal.y, data->accelcal.z);
+ return iCount;
+}
+
+static ssize_t accel_calibration_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int iRet;
+ int64_t dEnable;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ iRet = kstrtoll(buf, 10, &dEnable);
+ if (iRet < 0)
+ return iRet;
+
+ iRet = accel_do_calibrate(data, (int)dEnable);
+ if (iRet < 0)
+ ssp_err("accel_do_calibrate() failed\n");
+
+ return size;
+}
+
+static ssize_t raw_data_read(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
+ data->buf[ACCELEROMETER_SENSOR].x,
+ data->buf[ACCELEROMETER_SENSOR].y,
+ data->buf[ACCELEROMETER_SENSOR].z);
+}
+
+static ssize_t accel_reactive_alert_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ char chTempBuf[2] = {0, 10};
+ int iRet, iDelayCnt = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (sysfs_streq(buf, "1"))
+ ssp_dbg("on\n");
+ else if (sysfs_streq(buf, "0"))
+ ssp_dbg("off\n");
+ else if (sysfs_streq(buf, "2")) {
+ ssp_dbg("factory\n");
+
+ data->uFactorydataReady = 0;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ data->bAccelAlert = false;
+ iRet = send_instruction(data, FACTORY_MODE,
+ ACCELEROMETER_FACTORY, chTempBuf, 2);
+
+ while (!(data->uFactorydataReady & (1 << ACCELEROMETER_FACTORY))
+ && (iDelayCnt++ < 150)
+ && (iRet == SUCCESS))
+ msleep(20);
+
+ if ((iDelayCnt >= 150) || (iRet != SUCCESS)) {
+ ssp_err("accel Selftest Timeout!!\n");
+ goto exit;
+ }
+
+ mdelay(5);
+
+ data->bAccelAlert = data->uFactorydata[0];
+ ssp_dbg("factory test success!\n");
+ } else {
+ ssp_err("invalid value %d\n", *buf);
+ return -EINVAL;
+ }
+exit:
+ return size;
+}
+
+static ssize_t accel_reactive_alert_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (data->bAccelAlert == true)
+ bSuccess = true;
+ else
+ bSuccess = false;
+
+ data->bAccelAlert = false;
+ return sprintf(buf, "%u\n", bSuccess);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, accel_name_show, NULL);
+static DEVICE_ATTR(vendor, S_IRUGO, accel_vendor_show, NULL);
+static DEVICE_ATTR(calibration, S_IRUGO | S_IWUSR | S_IWGRP,
+ accel_calibration_show, accel_calibration_store);
+static DEVICE_ATTR(raw_data, S_IRUGO, raw_data_read, NULL);
+static DEVICE_ATTR(reactive_alert, S_IRUGO | S_IWUSR | S_IWGRP,
+ accel_reactive_alert_show, accel_reactive_alert_store);
+
+static struct device_attribute *acc_attrs[] = {
+ &dev_attr_name,
+ &dev_attr_vendor,
+ &dev_attr_calibration,
+ &dev_attr_raw_data,
+ &dev_attr_reactive_alert,
+ NULL,
+};
+
+void initialize_accel_factorytest(struct ssp_data *data)
+{
+ sensors_register(data->acc_device, data, acc_attrs,
+ "accelerometer_sensor");
+}
+
+void remove_accel_factorytest(struct ssp_data *data)
+{
+ sensors_unregister(data->acc_device, acc_attrs);
+}
diff --git a/drivers/sensorhub/stm/factory/accel_mpu6050.c b/drivers/sensorhub/stm/factory/accel_mpu6050.c
new file mode 100644
index 00000000000..1d54924b1fc
--- /dev/null
+++ b/drivers/sensorhub/stm/factory/accel_mpu6050.c
@@ -0,0 +1,304 @@
+/*
+ * 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"
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+#define VENDOR "INVENSENSE"
+#define CHIP_ID "MPU6050"
+
+#define CALIBRATION_FILE_PATH "/efs/calibration_data"
+#define CALIBRATION_DATA_AMOUNT 20
+
+static ssize_t accel_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VENDOR);
+}
+
+static ssize_t accel_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", CHIP_ID);
+}
+
+int accel_open_calibration(struct ssp_data *data)
+{
+ int iRet = 0;
+ mm_segment_t old_fs;
+ struct file *cal_filp = NULL;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+
+ data->accelcal.x = 0;
+ data->accelcal.y = 0;
+ data->accelcal.z = 0;
+
+ return iRet;
+ }
+
+ iRet = cal_filp->f_op->read(cal_filp, (char *)&data->accelcal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int))
+ iRet = -EIO;
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ ssp_dbg("open accel calibration %d, %d, %d\n",
+ data->accelcal.x, data->accelcal.y, data->accelcal.z);
+
+ if ((data->accelcal.x == 0) && (data->accelcal.y == 0)
+ && (data->accelcal.z == 0))
+ return ERROR;
+
+ return iRet;
+}
+
+static int enable_accel_for_cal(struct ssp_data *data)
+{
+ u8 uBuf[2] = {0, 10};
+
+ if (atomic_read(&data->aSensorEnable) & (1 << ACCELEROMETER_SENSOR)) {
+ if (get_msdelay(data->adDelayBuf[ACCELEROMETER_SENSOR]) != 10) {
+ send_instruction(data, CHANGE_DELAY,
+ ACCELEROMETER_SENSOR, uBuf, 2);
+ return SUCCESS;
+ }
+ } else {
+ send_instruction(data, ADD_SENSOR,
+ ACCELEROMETER_SENSOR, uBuf, 2);
+ }
+
+ return FAIL;
+}
+
+static void disable_accel_for_cal(struct ssp_data *data, int iDelayChanged)
+{
+ u8 uBuf[2] = {0, 10};
+
+ if (atomic_read(&data->aSensorEnable) & (1 << ACCELEROMETER_SENSOR)) {
+ uBuf[1] = get_msdelay(data->adDelayBuf[ACCELEROMETER_SENSOR]);
+ uBuf[0] = get_delay_cmd(uBuf[1]);
+ if (iDelayChanged)
+ send_instruction(data, CHANGE_DELAY,
+ ACCELEROMETER_SENSOR, uBuf, 2);
+ } else {
+ send_instruction(data, REMOVE_SENSOR,
+ ACCELEROMETER_SENSOR, uBuf, 2);
+ }
+}
+
+static int accel_do_calibrate(struct ssp_data *data, int iEnable)
+{
+ int iSum[3] = { 0, };
+ int iRet = 0, iCount;
+ struct file *cal_filp = NULL;
+ mm_segment_t old_fs;
+
+ if (iEnable) {
+ data->accelcal.x = 0;
+ data->accelcal.y = 0;
+ data->accelcal.z = 0;
+
+ iRet = enable_accel_for_cal(data);
+ msleep(300);
+
+ for (iCount = 0; iCount < CALIBRATION_DATA_AMOUNT; iCount++) {
+ iSum[0] += data->buf[ACCELEROMETER_SENSOR].x;
+ iSum[1] += data->buf[ACCELEROMETER_SENSOR].y;
+ iSum[2] += (data->buf[ACCELEROMETER_SENSOR].z - 1024);
+ mdelay(10);
+ }
+ disable_accel_for_cal(data, iRet);
+
+ data->accelcal.x = (iSum[0] / CALIBRATION_DATA_AMOUNT);
+ data->accelcal.y = (iSum[1] / CALIBRATION_DATA_AMOUNT);
+ data->accelcal.z = (iSum[2] / CALIBRATION_DATA_AMOUNT);
+ } else {
+ data->accelcal.x = 0;
+ data->accelcal.y = 0;
+ data->accelcal.z = 0;
+ }
+
+ ssp_dbg("do accel calibrate %d, %d, %d\n",
+ data->accelcal.x, data->accelcal.y, data->accelcal.z);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH,
+ O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ ssp_err("Can't open calibration file\n");
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+ return iRet;
+ }
+
+ iRet = cal_filp->f_op->write(cal_filp, (char *)&data->accelcal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int)) {
+ ssp_err("Can't write the accelcal to file\n");
+ iRet = -EIO;
+ }
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ return iRet;
+}
+
+static ssize_t accel_calibration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int iRet;
+ int iCount = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ iRet = accel_open_calibration(data);
+ if (iRet < 0)
+ ssp_err("calibration open failed\n");
+
+ ssp_dbg("Cal data : %d %d %d - %d\n",
+ data->accelcal.x, data->accelcal.y, data->accelcal.z, iRet);
+
+ iCount = sprintf(buf, "%d %d %d %d\n", iRet, data->accelcal.x,
+ data->accelcal.y, data->accelcal.z);
+ return iCount;
+}
+
+static ssize_t accel_calibration_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int iRet;
+ int64_t dEnable;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ iRet = kstrtoll(buf, 10, &dEnable);
+ if (iRet < 0)
+ return iRet;
+
+ iRet = accel_do_calibrate(data, (int)dEnable);
+ if (iRet < 0)
+ ssp_err("accel_do_calibrate() failed\n");
+
+ return size;
+}
+
+static ssize_t raw_data_read(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
+ data->buf[ACCELEROMETER_SENSOR].x,
+ data->buf[ACCELEROMETER_SENSOR].y,
+ data->buf[ACCELEROMETER_SENSOR].z);
+}
+
+static ssize_t accel_reactive_alert_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ char chTempBuf[2] = {0, 10};
+ int iRet, iDelayCnt = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (sysfs_streq(buf, "1"))
+ ssp_dbg("on\n");
+ else if (sysfs_streq(buf, "0"))
+ ssp_dbg("off\n");
+ else if (sysfs_streq(buf, "2")) {
+ ssp_dbg("factory\n");
+
+ data->uFactorydataReady = 0;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ data->bAccelAlert = false;
+ iRet = send_instruction(data, FACTORY_MODE,
+ ACCELEROMETER_FACTORY, chTempBuf, 2);
+
+ while (!(data->uFactorydataReady & (1 << ACCELEROMETER_FACTORY))
+ && (iDelayCnt++ < 150)
+ && (iRet == SUCCESS))
+ msleep(20);
+
+ if ((iDelayCnt >= 150) || (iRet != SUCCESS)) {
+ ssp_err("accel Selftest Timeout!!\n");
+ goto exit;
+ }
+
+ mdelay(5);
+
+ data->bAccelAlert = data->uFactorydata[0];
+ ssp_dbg("factory test success!\n");
+ } else {
+ ssp_err("invalid value %d\n", *buf);
+ return -EINVAL;
+ }
+exit:
+ return size;
+}
+
+static ssize_t accel_reactive_alert_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (data->bAccelAlert == true)
+ bSuccess = true;
+ else
+ bSuccess = false;
+
+ data->bAccelAlert = false;
+ return sprintf(buf, "%u\n", bSuccess);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, accel_name_show, NULL);
+static DEVICE_ATTR(vendor, S_IRUGO, accel_vendor_show, NULL);
+static DEVICE_ATTR(calibration, S_IRUGO | S_IWUSR | S_IWGRP,
+ accel_calibration_show, accel_calibration_store);
+static DEVICE_ATTR(raw_data, S_IRUGO, raw_data_read, NULL);
+static DEVICE_ATTR(reactive_alert, S_IRUGO | S_IWUSR | S_IWGRP,
+ accel_reactive_alert_show, accel_reactive_alert_store);
+
+static struct device_attribute *acc_attrs[] = {
+ &dev_attr_name,
+ &dev_attr_vendor,
+ &dev_attr_calibration,
+ &dev_attr_raw_data,
+ &dev_attr_reactive_alert,
+ NULL,
+};
+
+void initialize_accel_factorytest(struct ssp_data *data)
+{
+ sensors_register(data->acc_device, data, acc_attrs,
+ "accelerometer_sensor");
+}
+
+void remove_accel_factorytest(struct ssp_data *data)
+{
+ sensors_unregister(data->acc_device, acc_attrs);
+}
diff --git a/drivers/sensorhub/stm/factory/accel_mpu6500.c b/drivers/sensorhub/stm/factory/accel_mpu6500.c
new file mode 100644
index 00000000000..06d8fa14c5b
--- /dev/null
+++ b/drivers/sensorhub/stm/factory/accel_mpu6500.c
@@ -0,0 +1,406 @@
+/*
+ * 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"
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+#define VENDOR "INVENSENSE"
+#define CHIP_ID "MPU6500"
+
+#define CALIBRATION_FILE_PATH "/csa/sensor/accel_cal_data"
+#define CALIBRATION_DATA_AMOUNT 20
+
+static ssize_t accel_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VENDOR);
+}
+
+static ssize_t accel_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", CHIP_ID);
+}
+
+int accel_open_calibration(struct ssp_data *data)
+{
+ int iRet = 0;
+ mm_segment_t old_fs;
+ struct file *cal_filp = NULL;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+
+ data->accelcal.x = 0;
+ data->accelcal.y = 0;
+ data->accelcal.z = 0;
+
+ return iRet;
+ }
+
+ iRet = cal_filp->f_op->read(cal_filp, (char *)&data->accelcal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int))
+ iRet = -EIO;
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ ssp_dbg("open accel calibration %d, %d, %d\n",
+ data->accelcal.x, data->accelcal.y, data->accelcal.z);
+
+ if ((data->accelcal.x == 0) && (data->accelcal.y == 0)
+ && (data->accelcal.z == 0))
+ return ERROR;
+
+ return iRet;
+}
+
+int set_accel_cal(struct ssp_data *data)
+{
+ int iRet = 0;
+ struct ssp_msg *msg;
+ s16 accel_cal[3];
+
+ if (!(data->uSensorState & (1 << ACCELEROMETER_SENSOR))) {
+ ssp_dbg("Skip this function!!!, accel sensor is not connected(0x%x)\n",
+ data->uSensorState);
+ return iRet;
+ }
+ accel_cal[0] = data->accelcal.x;
+ accel_cal[1] = data->accelcal.y;
+ accel_cal[2] = data->accelcal.z;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_MCU_SET_ACCEL_CAL;
+ msg->length = 6;
+ msg->options = AP2HUB_WRITE;
+ msg->buffer = (char*) kzalloc(6, GFP_KERNEL);
+
+ msg->free_buffer = 1;
+ memcpy(msg->buffer, accel_cal, 6);
+
+ iRet = ssp_spi_async(data, msg);
+
+ if (iRet != SUCCESS) {
+ ssp_err("i2c fail %d\n", iRet);
+ iRet = ERROR;
+ }
+
+ ssp_dbg("Set accel cal data %d, %d, %d\n", accel_cal[0], accel_cal[1], accel_cal[2]);
+ return iRet;
+}
+
+static int enable_accel_for_cal(struct ssp_data *data)
+{
+ u8 uBuf[9] = { 0, };
+ s32 dMsDelay = 10;//get_msdelay(data->adDelayBuf[ACCELEROMETER_SENSOR]);
+ memcpy(&uBuf[0], &dMsDelay, 4);
+
+ if (atomic_read(&data->aSensorEnable) & (1 << ACCELEROMETER_SENSOR)) {
+ if (get_msdelay(data->adDelayBuf[ACCELEROMETER_SENSOR]) != 10) {
+ send_instruction(data, CHANGE_DELAY,
+ ACCELEROMETER_SENSOR, uBuf, 9);
+ return SUCCESS;
+ }
+ } else {
+ send_instruction(data, ADD_SENSOR,
+ ACCELEROMETER_SENSOR, uBuf, 9);
+ }
+
+ return FAIL;
+}
+
+static void disable_accel_for_cal(struct ssp_data *data, int iDelayChanged)
+{
+ u8 uBuf[9] = { 0, };
+ s32 dMsDelay = get_msdelay(data->adDelayBuf[ACCELEROMETER_SENSOR]);
+ memcpy(&uBuf[0], &dMsDelay, 4);
+
+ if (atomic_read(&data->aSensorEnable) & (1 << ACCELEROMETER_SENSOR)) {
+ if (iDelayChanged)
+ send_instruction(data, CHANGE_DELAY,
+ ACCELEROMETER_SENSOR, uBuf, 9);
+ } else {
+ send_instruction(data, REMOVE_SENSOR,
+ ACCELEROMETER_SENSOR, uBuf, 9);
+ }
+}
+
+static int accel_do_calibrate(struct ssp_data *data, int iEnable)
+{
+ int iSum[3] = { 0, };
+ int iRet = 0, iCount;
+ struct file *cal_filp = NULL;
+ mm_segment_t old_fs;
+
+ if (iEnable) {
+ data->accelcal.x = 0;
+ data->accelcal.y = 0;
+ data->accelcal.z = 0;
+ set_accel_cal(data);
+ iRet = enable_accel_for_cal(data);
+ msleep(300);
+
+ for (iCount = 0; iCount < CALIBRATION_DATA_AMOUNT; iCount++) {
+ iSum[0] += data->buf[ACCELEROMETER_SENSOR].x;
+ iSum[1] += data->buf[ACCELEROMETER_SENSOR].y;
+ iSum[2] += data->buf[ACCELEROMETER_SENSOR].z;
+ mdelay(10);
+ }
+ disable_accel_for_cal(data, iRet);
+
+ data->accelcal.x = (iSum[0] / CALIBRATION_DATA_AMOUNT);
+ data->accelcal.y = (iSum[1] / CALIBRATION_DATA_AMOUNT);
+ data->accelcal.z = (iSum[2] / CALIBRATION_DATA_AMOUNT);
+
+ if (data->accelcal.z > 0)
+ data->accelcal.z -= (MAX_ACCEL_1G >> 2);
+ else if (data->accelcal.z < 0)
+ data->accelcal.z += (MAX_ACCEL_1G >> 2);
+ } else {
+ data->accelcal.x = 0;
+ data->accelcal.y = 0;
+ data->accelcal.z = 0;
+ }
+
+ ssp_dbg("do accel calibrate %d, %d, %d\n",
+ data->accelcal.x, data->accelcal.y, data->accelcal.z);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH,
+ O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ ssp_err("Can't open calibration file\n");
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+ return iRet;
+ }
+
+ iRet = cal_filp->f_op->write(cal_filp, (char *)&data->accelcal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int)) {
+ ssp_err("Can't write the accelcal to file\n");
+ iRet = -EIO;
+ }
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+ set_accel_cal(data);
+ return iRet;
+}
+
+static ssize_t accel_calibration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int iRet;
+ int iCount = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ iRet = accel_open_calibration(data);
+ if (iRet < 0)
+ ssp_err("calibration open failed(%d)\n", iRet);
+
+ ssp_dbg("Cal data : %d %d %d - %d\n",
+ data->accelcal.x, data->accelcal.y, data->accelcal.z, iRet);
+
+ if (!data->accelcal.x && !data->accelcal.y && !data->accelcal.z)
+ iRet = -1;
+
+ iCount = sprintf(buf, "%d %d %d %d\n", iRet, data->accelcal.x,
+ data->accelcal.y, data->accelcal.z);
+
+ return iCount;
+}
+
+static ssize_t accel_calibration_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int iRet;
+ int64_t dEnable;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ iRet = kstrtoll(buf, 10, &dEnable);
+ if (iRet < 0)
+ return iRet;
+
+ iRet = accel_do_calibrate(data, (int)dEnable);
+ if (iRet < 0)
+ ssp_err("accel_do_calibrate() failed\n");
+
+ return size;
+}
+
+static ssize_t raw_data_read(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
+ data->buf[ACCELEROMETER_SENSOR].x,
+ data->buf[ACCELEROMETER_SENSOR].y,
+ data->buf[ACCELEROMETER_SENSOR].z);
+}
+
+static ssize_t accel_reactive_alert_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int iRet = 0;
+ char chTempBuf = 1;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ struct ssp_msg *msg;
+
+ if (sysfs_streq(buf, "1"))
+ ssp_dbg("on\n");
+ else if (sysfs_streq(buf, "0"))
+ ssp_dbg("off\n");
+ else if (sysfs_streq(buf, "2")) {
+ ssp_dbg("factory\n");
+
+ data->bAccelAlert = 0;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = ACCELEROMETER_FACTORY;
+ msg->length = 1;
+ msg->options = AP2HUB_READ;
+ msg->data = chTempBuf;
+ msg->buffer = &chTempBuf;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_sync(data, msg, 3000);
+ data->bAccelAlert = chTempBuf;
+
+ if (iRet != SUCCESS) {
+ ssp_err(" accel Selftest Timeout!!\n");
+ goto exit;
+ }
+
+ ssp_dbg("factory test success!\n");
+ } else {
+ ssp_err("invalid value %d\n", *buf);
+ return -EINVAL;
+ }
+exit:
+ return size;
+}
+
+static ssize_t accel_reactive_alert_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (data->bAccelAlert == true)
+ bSuccess = true;
+ else
+ bSuccess = false;
+
+ data->bAccelAlert = false;
+ return sprintf(buf, "%u\n", bSuccess);
+}
+
+static ssize_t accel_hw_selftest_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char chTempBuf[8] = { 2, 0, };
+ s8 init_status = 0, result = -1;
+ s16 shift_ratio[3] = { 0, };
+ int iRet;
+ struct ssp_data *data = dev_get_drvdata(dev);
+ struct ssp_msg *msg;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ goto exit;
+ }
+ msg->cmd = ACCELEROMETER_FACTORY;
+ msg->length = 8;
+ msg->options = AP2HUB_READ;
+ msg->data = chTempBuf[0];
+ msg->buffer = chTempBuf;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_sync(data, msg, 3000);
+ if (iRet != SUCCESS) {
+ ssp_err("accel hw selftest Timeout!!\n");
+ goto exit;
+ }
+
+ init_status = chTempBuf[0];
+ shift_ratio[0] = (s16)((chTempBuf[2] << 8) + chTempBuf[1]);
+ shift_ratio[1] = (s16)((chTempBuf[4] << 8) + chTempBuf[3]);
+ shift_ratio[2] = (s16)((chTempBuf[6] << 8) + chTempBuf[5]);
+ result = chTempBuf[7];
+
+ ssp_info("%d, %d, %d, %d, %d\n", init_status, result,
+ shift_ratio[0], shift_ratio[1], shift_ratio[2]);
+
+ return sprintf(buf, "%d,%d.%d,%d.%d,%d.%d\n", result,
+ shift_ratio[0] / 10, shift_ratio[0] % 10,
+ shift_ratio[1] / 10, shift_ratio[1] % 10,
+ shift_ratio[2] / 10, shift_ratio[2] % 10);
+exit:
+ return sprintf(buf, "%d,%d,%d,%d\n", -5, 0, 0, 0);
+}
+
+
+static DEVICE_ATTR(name, S_IRUGO, accel_name_show, NULL);
+static DEVICE_ATTR(vendor, S_IRUGO, accel_vendor_show, NULL);
+static DEVICE_ATTR(calibration, S_IRUGO | S_IWUSR | S_IWGRP,
+ accel_calibration_show, accel_calibration_store);
+static DEVICE_ATTR(raw_data, S_IRUGO, raw_data_read, NULL);
+static DEVICE_ATTR(reactive_alert, S_IRUGO | S_IWUSR | S_IWGRP,
+ accel_reactive_alert_show, accel_reactive_alert_store);
+static DEVICE_ATTR(selftest, S_IRUGO, accel_hw_selftest_show, NULL);
+
+static struct device_attribute *acc_attrs[] = {
+ &dev_attr_name,
+ &dev_attr_vendor,
+ &dev_attr_calibration,
+ &dev_attr_raw_data,
+ &dev_attr_reactive_alert,
+ &dev_attr_selftest,
+ NULL,
+};
+
+void initialize_accel_factorytest(struct ssp_data *data)
+{
+ sensors_register(data->acc_device, data, acc_attrs,
+ "accelerometer_sensor");
+}
+
+void remove_accel_factorytest(struct ssp_data *data)
+{
+ sensors_unregister(data->acc_device, acc_attrs);
+}
diff --git a/drivers/sensorhub/stm/factory/gyro_k330.c b/drivers/sensorhub/stm/factory/gyro_k330.c
new file mode 100644
index 00000000000..c4a73ea5468
--- /dev/null
+++ b/drivers/sensorhub/stm/factory/gyro_k330.c
@@ -0,0 +1,331 @@
+/*
+ * 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"
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+#define VENDOR "STM"
+#define CHIP_ID "K330"
+
+#define CALIBRATION_FILE_PATH "/efs/gyro_cal_data"
+#define CALIBRATION_DATA_AMOUNT 20
+
+static ssize_t gyro_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VENDOR);
+}
+
+static ssize_t gyro_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", CHIP_ID);
+}
+
+int gyro_open_calibration(struct ssp_data *data)
+{
+ int iRet = 0;
+ mm_segment_t old_fs;
+ struct file *cal_filp = NULL;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+
+ data->gyrocal.x = 0;
+ data->gyrocal.y = 0;
+ data->gyrocal.z = 0;
+
+ return iRet;
+ }
+
+ iRet = cal_filp->f_op->read(cal_filp, (char *)&data->gyrocal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int))
+ iRet = -EIO;
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ ssp_dbg("open gyro calibration %d, %d, %d\n",
+ data->gyrocal.x, data->gyrocal.y, data->gyrocal.z);
+ return iRet;
+}
+
+static int save_gyro_caldata(struct ssp_data *data, s16 *iCalData)
+{
+ int iRet = 0;
+ struct file *cal_filp = NULL;
+ mm_segment_t old_fs;
+
+ data->gyrocal.x = iCalData[0];
+ data->gyrocal.y = iCalData[1];
+ data->gyrocal.z = iCalData[2];
+
+ ssp_dbg("do gyro calibrate %d, %d, %d\n",
+ data->gyrocal.x, data->gyrocal.y, data->gyrocal.z);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH,
+ O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ ssp_err("Can't open calibration file\n");
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+ return -EIO;
+ }
+
+ iRet = cal_filp->f_op->write(cal_filp, (char *)&data->gyrocal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int)) {
+ ssp_err("Can't write gyro cal to file\n");
+ iRet = -EIO;
+ }
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ return iRet;
+}
+
+static ssize_t gyro_power_off(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ func_dbg();
+
+ return sprintf(buf, "%d\n", 1);
+}
+
+static ssize_t gyro_power_on(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ func_dbg();
+
+ return sprintf(buf, "%d\n", 1);
+}
+
+static ssize_t gyro_get_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char chTempBuf[2] = { 0, 10}, chTemp = 0;
+ int iDelayCnt = 0, iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (!(data->uSensorState & (1 << GYROSCOPE_SENSOR)))
+ goto exit;
+
+ data->uFactorydataReady = 0;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ iRet = send_instruction(data, FACTORY_MODE, GYROSCOPE_TEMP_FACTORY,
+ chTempBuf, 2);
+
+ while (!(data->uFactorydataReady & (1 << GYROSCOPE_TEMP_FACTORY))
+ && (iDelayCnt++ < 150)
+ && (iRet == SUCCESS))
+ msleep(20);
+
+ if ((iDelayCnt >= 150) || (iRet != SUCCESS)) {
+ ssp_err("Gyro Temp Timeout!!\n");
+ goto exit;
+ }
+
+ mdelay(5);
+
+ chTemp = (char)data->uFactorydata[0];
+ ssp_dbg("%d\n", chTemp);
+exit:
+ return sprintf(buf, "%d\n", chTemp);
+}
+
+static ssize_t gyro_selftest_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char chTempBuf[2] = { 3, 200};
+ u8 uFifoPass = 2;
+ u8 uBypassPass = 2;
+ u8 uCalPass = 0;
+ s16 iNOST[3] = {0,}, iST[3] = {0,}, iCalData[3] = {0,};
+ s16 iZeroRateData[3] = {0,}, fifo_data[4] = {0,};
+ int iDelayCnt = 0, iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ data->uFactorydataReady = 0;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ iRet = send_instruction(data, FACTORY_MODE, GYROSCOPE_FACTORY,
+ chTempBuf, 2);
+
+ while (!(data->uFactorydataReady & (1 << GYROSCOPE_FACTORY))
+ && (iDelayCnt++ < 250)
+ && (iRet == SUCCESS))
+ msleep(20);
+
+ if ((iDelayCnt >= 250) || (iRet != SUCCESS)) {
+ ssp_err(" Gyro Selftest Timeout!!\n");
+ goto exit;
+ }
+ mdelay(5);
+
+ iNOST[0] = (s16)((data->uFactorydata[0] << 8) + data->uFactorydata[1]);
+ iNOST[1] = (s16)((data->uFactorydata[2] << 8) + data->uFactorydata[3]);
+ iNOST[2] = (s16)((data->uFactorydata[4] << 8) + data->uFactorydata[5]);
+
+ iST[0] = (s16)((data->uFactorydata[6] << 8) + data->uFactorydata[7]);
+ iST[1] = (s16)((data->uFactorydata[8] << 8) + data->uFactorydata[9]);
+ iST[2] = (s16)((data->uFactorydata[10] << 8) + data->uFactorydata[11]);
+
+ iCalData[0] =
+ (s16)((data->uFactorydata[12] << 8) + data->uFactorydata[13]);
+ iCalData[1] =
+ (s16)((data->uFactorydata[14] << 8) + data->uFactorydata[15]);
+ iCalData[2] =
+ (s16)((data->uFactorydata[16] << 8) + data->uFactorydata[17]);
+
+ iZeroRateData[0] =
+ (s16)((data->uFactorydata[18] << 8) + data->uFactorydata[19]);
+ iZeroRateData[1] =
+ (s16)((data->uFactorydata[20] << 8) + data->uFactorydata[21]);
+ iZeroRateData[2] =
+ (s16)((data->uFactorydata[22] << 8) + data->uFactorydata[23]);
+
+ fifo_data[0] = data->uFactorydata[24];
+ fifo_data[1] =
+ (s16)((data->uFactorydata[25] << 8) + data->uFactorydata[26]);
+ fifo_data[2] =
+ (s16)((data->uFactorydata[27] << 8) + data->uFactorydata[28]);
+ fifo_data[3] =
+ (s16)((data->uFactorydata[29] << 8) + data->uFactorydata[30]);
+
+ uCalPass = data->uFactorydata[31];
+ uFifoPass = data->uFactorydata[32];
+ uBypassPass = data->uFactorydata[33];
+
+ if (uFifoPass && uBypassPass && uCalPass)
+ save_gyro_caldata(data, iCalData);
+
+exit:
+ ssp_dbg("Gyro Selftest - %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ iNOST[0], iNOST[1], iNOST[2], iST[0], iST[1], iST[2],
+ iZeroRateData[0], iZeroRateData[1], iZeroRateData[2],
+ fifo_data[0], fifo_data[1], fifo_data[2], fifo_data[3],
+ uFifoPass & uBypassPass & uCalPass, uFifoPass, uCalPass);
+
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ iNOST[0], iNOST[1], iNOST[2], iST[0], iST[1], iST[2],
+ iZeroRateData[0], iZeroRateData[1], iZeroRateData[2],
+ fifo_data[0], fifo_data[1], fifo_data[2], fifo_data[3],
+ uFifoPass & uBypassPass & uCalPass, uFifoPass, uCalPass);
+}
+
+static ssize_t gyro_selftest_dps_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int iNewDps = 0;
+ int iDelayCnt = 0, iRet = 0;
+ char chTempBuf[2] = { 0, 10 };
+
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (!(data->uSensorState & (1 << GYROSCOPE_SENSOR)))
+ goto exit;
+
+ sscanf(buf, "%d", &iNewDps);
+
+ if (iNewDps == GYROSCOPE_DPS250)
+ chTempBuf[0] = 0;
+ else if (iNewDps == GYROSCOPE_DPS500)
+ chTempBuf[0] = 1;
+ else if (iNewDps == GYROSCOPE_DPS2000)
+ chTempBuf[0] = 2;
+ else {
+ chTempBuf[0] = 1;
+ iNewDps = GYROSCOPE_DPS500;
+ }
+
+ data->uFactorydataReady = 0;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ iRet = send_instruction(data, FACTORY_MODE, GYROSCOPE_DPS_FACTORY,
+ chTempBuf, 2);
+
+ while (!(data->uFactorydataReady & (1 << GYROSCOPE_DPS_FACTORY))
+ && (iDelayCnt++ < 150)
+ && (iRet == SUCCESS))
+ msleep(20);
+
+ if ((iDelayCnt >= 150) || (iRet != SUCCESS)) {
+ ssp_err("Gyro Selftest DPS Timeout!!\n");
+ goto exit;
+ }
+
+ mdelay(5);
+
+ if (data->uFactorydata[0] != SUCCESS) {
+ ssp_err("Gyro Selftest DPS Error!!\n");
+ goto exit;
+ }
+
+ data->uGyroDps = (unsigned int)iNewDps;
+ ssp_err("%u dps stored\n", data->uGyroDps);
+exit:
+ return count;
+}
+
+static ssize_t gyro_selftest_dps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->uGyroDps);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, gyro_name_show, NULL);
+static DEVICE_ATTR(vendor, S_IRUGO, gyro_vendor_show, NULL);
+static DEVICE_ATTR(power_off, S_IRUGO, gyro_power_off, NULL);
+static DEVICE_ATTR(power_on, S_IRUGO, gyro_power_on, NULL);
+static DEVICE_ATTR(temperature, S_IRUGO, gyro_get_temp, NULL);
+static DEVICE_ATTR(selftest, S_IRUGO, gyro_selftest_show, NULL);
+static DEVICE_ATTR(selftest_dps, S_IRUGO | S_IWUSR | S_IWGRP,
+ gyro_selftest_dps_show, gyro_selftest_dps_store);
+
+static struct device_attribute *gyro_attrs[] = {
+ &dev_attr_name,
+ &dev_attr_vendor,
+ &dev_attr_selftest,
+ &dev_attr_power_on,
+ &dev_attr_power_off,
+ &dev_attr_temperature,
+ &dev_attr_selftest_dps,
+ NULL,
+};
+
+void initialize_gyro_factorytest(struct ssp_data *data)
+{
+ sensors_register(data->gyro_device, data, gyro_attrs, "gyro_sensor");
+}
+
+void remove_gyro_factorytest(struct ssp_data *data)
+{
+ sensors_unregister(data->gyro_device, gyro_attrs);
+}
diff --git a/drivers/sensorhub/stm/factory/gyro_mpu6050.c b/drivers/sensorhub/stm/factory/gyro_mpu6050.c
new file mode 100644
index 00000000000..e3ba3dd6226
--- /dev/null
+++ b/drivers/sensorhub/stm/factory/gyro_mpu6050.c
@@ -0,0 +1,520 @@
+/*
+ * 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/kernel.h>
+#include "../ssp.h"
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+#define VENDOR "INVENSENSE"
+#define CHIP_ID "MPU6050"
+
+#define CALIBRATION_FILE_PATH "/efs/gyro_cal_data"
+#define VERBOSE_OUT 1
+#define CALIBRATION_DATA_AMOUNT 20
+#define DEF_GYRO_FULLSCALE 2000
+#define DEF_GYRO_SENS (32768 / DEF_GYRO_FULLSCALE)
+#define DEF_BIAS_LSB_THRESH_SELF (20 * DEF_GYRO_SENS)
+#define DEF_RMS_LSB_TH_SELF (5 * DEF_GYRO_SENS)
+#define DEF_RMS_THRESH ((DEF_RMS_LSB_TH_SELF) * (DEF_RMS_LSB_TH_SELF))
+#define DEF_SCALE_FOR_FLOAT (1000)
+#define DEF_RMS_SCALE_FOR_RMS (10000)
+#define DEF_SQRT_SCALE_FOR_RMS (100)
+
+static ssize_t gyro_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VENDOR);
+}
+
+static ssize_t gyro_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", CHIP_ID);
+}
+
+int gyro_open_calibration(struct ssp_data *data)
+{
+ int iRet = 0;
+ mm_segment_t old_fs;
+ struct file *cal_filp = NULL;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+
+ data->gyrocal.x = 0;
+ data->gyrocal.y = 0;
+ data->gyrocal.z = 0;
+
+ return iRet;
+ }
+
+ iRet = cal_filp->f_op->read(cal_filp, (char *)&data->gyrocal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int))
+ iRet = -EIO;
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ ssp_dbg("open gyro calibration %d, %d, %d\n",
+ data->gyrocal.x, data->gyrocal.y, data->gyrocal.z);
+ return iRet;
+}
+
+static int save_gyro_caldata(struct ssp_data *data, s16 *iCalData)
+{
+ int iRet = 0;
+ struct file *cal_filp = NULL;
+ mm_segment_t old_fs;
+
+ data->gyrocal.x = iCalData[0];
+ data->gyrocal.y = iCalData[1];
+ data->gyrocal.z = iCalData[2];
+
+ ssp_dbg("do gyro calibrate %d, %d, %d\n",
+ data->gyrocal.x, data->gyrocal.y, data->gyrocal.z);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH,
+ O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ ssp_err("Can't open calibration file\n");
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+ return -EIO;
+ }
+
+ iRet = cal_filp->f_op->write(cal_filp, (char *)&data->gyrocal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int)) {
+ ssp_err("Can't write gyro cal to file\n");
+ iRet = -EIO;
+ }
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ return iRet;
+}
+
+static ssize_t gyro_power_off(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ func_dbg();
+ return sprintf(buf, "%d\n", 1);
+}
+
+static ssize_t gyro_power_on(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ func_dbg();
+ return sprintf(buf, "%d\n", 1);
+}
+
+static ssize_t gyro_get_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char chTempBuf[2] = { 0, 10};
+ unsigned char reg[2];
+ short temperature = 0;
+ int iDelayCnt = 0, iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ data->uFactorydataReady = 0;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ iRet = send_instruction(data, FACTORY_MODE, GYROSCOPE_TEMP_FACTORY,
+ chTempBuf, 2);
+
+ while (!(data->uFactorydataReady & (1 << GYROSCOPE_TEMP_FACTORY))
+ && (iDelayCnt++ < 150)
+ && (iRet == SUCCESS))
+ msleep(20);
+
+ if ((iDelayCnt >= 150) || (iRet != SUCCESS)) {
+ ssp_err("Gyro Temp Timeout!!\n");
+ goto exit;
+ }
+ reg[0] = data->uFactorydata[1];
+ reg[1] = data->uFactorydata[0];
+ temperature = (short) (((reg[0]) << 8) | reg[1]);
+ temperature = (((temperature + 521) / 340) + 35);
+ ssp_dbg("%d\n", temperature);
+exit:
+ return sprintf(buf, "%d\n", temperature);
+}
+
+
+u32 mpu6050_selftest_sqrt(u32 sqsum)
+{
+ u32 sq_rt;
+ u32 g0, g1, g2, g3, g4;
+ u32 seed;
+ u32 next;
+ u32 step;
+
+ g4 = sqsum / 100000000;
+ g3 = (sqsum - g4 * 100000000) / 1000000;
+ g2 = (sqsum - g4 * 100000000 - g3 * 1000000) / 10000;
+ g1 = (sqsum - g4 * 100000000 - g3 * 1000000 - g2 * 10000) / 100;
+ g0 = (sqsum - g4 * 100000000 - g3 * 1000000 - g2 * 10000 - g1 * 100);
+
+ next = g4;
+ step = 0;
+ seed = 0;
+ while (((seed + 1) * (step + 1)) <= next) {
+ step++;
+ seed++;
+ }
+
+ sq_rt = seed * 10000;
+ next = (next - (seed * step)) * 100 + g3;
+
+ step = 0;
+ seed = 2 * seed * 10;
+ while (((seed + 1) * (step + 1)) <= next) {
+ step++;
+ seed++;
+ }
+
+ sq_rt = sq_rt + step * 1000;
+ next = (next - seed * step) * 100 + g2;
+ seed = (seed + step) * 10;
+ step = 0;
+ while (((seed + 1) * (step + 1)) <= next) {
+ step++;
+ seed++;
+ }
+
+ sq_rt = sq_rt + step * 100;
+ next = (next - seed * step) * 100 + g1;
+ seed = (seed + step) * 10;
+ step = 0;
+
+ while (((seed + 1) * (step + 1)) <= next) {
+ step++;
+ seed++;
+ }
+
+ sq_rt = sq_rt + step * 10;
+ next = (next - seed * step) * 100 + g0;
+ seed = (seed + step) * 10;
+ step = 0;
+
+ while (((seed + 1) * (step + 1)) <= next) {
+ step++;
+ seed++;
+ }
+
+ sq_rt = sq_rt + step;
+
+ return sq_rt;
+}
+
+
+static ssize_t gyro_selftest_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char chTempBuf[2] = { 3, 200};
+ u8 initialized = 0;
+ int i = 0, j = 0, total_count = 0, ret_val = 0;
+ long avg[3] = {0,}, rms[3] = {0,};
+ int gyro_bias[3] = {0,}, gyro_rms[3] = {0,};
+ s16 iCalData[3] = {0,};
+ char a_name[3][2] = { "X", "Y", "Z" };
+ int iDelayCnt = 0, iRet = 0;
+ int dps_rms[3] = { 0, };
+ u32 temp = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ data->uFactorydataReady = 0;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ iRet = send_instruction(data, FACTORY_MODE, GYROSCOPE_FACTORY,
+ chTempBuf, 2);
+
+ while (!(data->uFactorydataReady & (1 << GYROSCOPE_FACTORY))
+ && (iDelayCnt++ < 150)
+ && (iRet == SUCCESS))
+ msleep(20);
+
+ if ((iDelayCnt >= 150) || (iRet != SUCCESS)) {
+ ssp_err("Gyro Selftest Timeout!!\n");
+ goto exit;
+ }
+ for (i = 0; i < 29; i++)
+ ssp_err("uFactorydata[%d] = 0x%x\n", i,
+ data->uFactorydata[i]);
+
+ initialized = data->uFactorydata[0];
+ total_count = (int)((data->uFactorydata[4] << 24) +
+ (data->uFactorydata[3] << 16) +
+ (data->uFactorydata[2] << 8) +
+ data->uFactorydata[1]);
+ avg[0] = (long)((data->uFactorydata[8] << 24) +
+ (data->uFactorydata[7] << 16) +
+ (data->uFactorydata[6] << 8) +
+ data->uFactorydata[5]);
+ avg[1] = (long)((data->uFactorydata[12] << 24) +
+ (data->uFactorydata[11] << 16) +
+ (data->uFactorydata[10] << 8) +
+ data->uFactorydata[9]);
+ avg[2] = (long)((data->uFactorydata[16] << 24) +
+ (data->uFactorydata[15] << 16) +
+ (data->uFactorydata[14] << 8) +
+ data->uFactorydata[13]);
+ rms[0] = (long)((data->uFactorydata[20] << 24) +
+ (data->uFactorydata[19] << 16) +
+ (data->uFactorydata[18] << 8) +
+ data->uFactorydata[17]);
+ rms[1] = (long)((data->uFactorydata[24] << 24) +
+ (data->uFactorydata[23] << 16) +
+ (data->uFactorydata[22] << 8) +
+ data->uFactorydata[21]);
+ rms[2] = (long)((data->uFactorydata[28] << 24) +
+ (data->uFactorydata[27] << 16) +
+ (data->uFactorydata[26] << 8) +
+ data->uFactorydata[25]);
+ ssp_info("init: %d, total cnt: %d", initialized, total_count);
+ ssp_info("avg %+8ld %+8ld %+8ld (LSB)\n", avg[0], avg[1], avg[2]);
+ ssp_info("rms %+8ld %+8ld %+8ld (LSB)\n", rms[0], rms[1], rms[2]);
+
+ ssp_dbg("bias : %+8ld %+8ld %+8ld (LSB)\n", avg[0], avg[1], avg[2]);
+
+ gyro_bias[0] = (avg[0] * DEF_SCALE_FOR_FLOAT) / DEF_GYRO_SENS;
+ gyro_bias[1] = (avg[1] * DEF_SCALE_FOR_FLOAT) / DEF_GYRO_SENS;
+ gyro_bias[2] = (avg[2] * DEF_SCALE_FOR_FLOAT) / DEF_GYRO_SENS;
+ iCalData[0] = (s16)avg[0];
+ iCalData[1] = (s16)avg[1];
+ iCalData[2] = (s16)avg[2];
+
+ if (VERBOSE_OUT) {
+ ssp_info("abs bias : %+8d.%03d %+8d.%03d %+8d.%03d (dps)\n",
+ (int)abs(gyro_bias[0]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_bias[0]) % DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_bias[1]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_bias[1]) % DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_bias[2]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_bias[2]) % DEF_SCALE_FOR_FLOAT);
+ }
+ for (j = 0; j < 3; j++) {
+ if (abs(avg[j]) > DEF_BIAS_LSB_THRESH_SELF) {
+ ssp_info("Gyro bias (%ld) exceeded threshold "
+ "(threshold = %d LSB)\n", a_name[j],
+ avg[j], DEF_BIAS_LSB_THRESH_SELF);
+ ret_val |= 1 << (3 + j);
+ }
+ }
+ /* 3rd, check RMS for dead gyros
+ If any of the RMS noise value returns zero,
+ then we might have dead gyro or FIFO/register failure,
+ the part is sleeping, or the part is not responsive */
+ if (rms[0] == 0 || rms[1] == 0 || rms[2] == 0)
+ ret_val |= 1 << 6;
+
+ if (VERBOSE_OUT) {
+ ssp_info("RMS ^ 2 : %+8ld %+8ld %+8ld\n",
+ (long)rms[0] / total_count,
+ (long)rms[1] / total_count, (long)rms[2] / total_count);
+ }
+
+ for (j = 0; j < 3; j++) {
+ if (rms[j] / total_count > DEF_RMS_THRESH) {
+ ssp_info("Gyro rms (%ld) exceeded threshold "
+ "(threshold = %d LSB)\n", a_name[j],
+ rms[j] / total_count, DEF_RMS_THRESH);
+ ret_val |= 1 << (7 + j);
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (rms[i] > 10000) {
+ temp =
+ ((u32) (rms[i] / total_count)) *
+ DEF_RMS_SCALE_FOR_RMS;
+ } else {
+ temp =
+ ((u32) (rms[i] * DEF_RMS_SCALE_FOR_RMS)) /
+ total_count;
+ }
+ if (rms[i] < 0)
+ temp = 1 << 31;
+
+ dps_rms[i] = mpu6050_selftest_sqrt(temp) / DEF_GYRO_SENS;
+
+ gyro_rms[i] =
+ dps_rms[i] * DEF_SCALE_FOR_FLOAT / DEF_SQRT_SCALE_FOR_RMS;
+ }
+
+ ssp_info("RMS : %+8d.%03d %+8d.%03d %+8d.%03d (dps)\n",
+ (int)abs(gyro_rms[0]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_rms[0]) % DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_rms[1]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_rms[1]) % DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_rms[2]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_rms[2]) % DEF_SCALE_FOR_FLOAT);
+
+ if (!ret_val) {
+ ssp_err("ret_val == 0\n");
+ save_gyro_caldata(data, iCalData);
+ } else {
+ ssp_err("ret_val != 0\n");
+ data->gyrocal.x = 0;
+ data->gyrocal.y = 0;
+ data->gyrocal.z = 0;
+ /*initialized = -1;*/
+ }
+
+exit:
+ ssp_dbg("Gyro Selftest - %d,"
+ "%d.%03d,%d.%03d,%d.%03d,"
+ "%d.%03d,%d.%03d,%d.%03d,"
+ "%d,%d,%d\n",
+ ret_val,
+ (int)abs(gyro_bias[0]/1000),
+ (int)abs(gyro_bias[0])%1000,
+ (int)abs(gyro_bias[1]/1000),
+ (int)abs(gyro_bias[1])%1000,
+ (int)abs(gyro_bias[2]/1000),
+ (int)abs(gyro_bias[2])%1000,
+ gyro_rms[0]/1000,
+ (int)abs(gyro_rms[0])%1000,
+ gyro_rms[1]/1000,
+ (int)abs(gyro_rms[1])%1000,
+ gyro_rms[2]/1000,
+ (int)abs(gyro_rms[2])%1000,
+ (int)(total_count/3),
+ (int)(total_count/3),
+ (int)(total_count/3));
+
+ return sprintf(buf, "%d,"
+ "%d.%03d,%d.%03d,%d.%03d,"
+ "%d.%03d,%d.%03d,%d.%03d,"
+ "%d,%d,%d\n",
+ ret_val,
+ (int)abs(gyro_bias[0]/1000),
+ (int)abs(gyro_bias[0])%1000,
+ (int)abs(gyro_bias[1]/1000),
+ (int)abs(gyro_bias[1])%1000,
+ (int)abs(gyro_bias[2]/1000),
+ (int)abs(gyro_bias[2])%1000,
+ gyro_rms[0]/1000,
+ (int)abs(gyro_rms[0])%1000,
+ gyro_rms[1]/1000,
+ (int)abs(gyro_rms[1])%1000,
+ gyro_rms[2]/1000,
+ (int)abs(gyro_rms[2])%1000,
+ (int)(total_count/3),
+ (int)(total_count/3),
+ (int)(total_count/3));
+}
+
+static ssize_t gyro_selftest_dps_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int iNewDps = 0;
+ int iDelayCnt = 0, iRet = 0;
+ char chTempBuf[2] = { 0, 10 };
+
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ sscanf(buf, "%d", &iNewDps);
+
+ if (iNewDps == GYROSCOPE_DPS250)
+ chTempBuf[0] = 0;
+ else if (iNewDps == GYROSCOPE_DPS500)
+ chTempBuf[0] = 1;
+ else if (iNewDps == GYROSCOPE_DPS2000)
+ chTempBuf[0] = 2;
+ else {
+ chTempBuf[0] = 1;
+ iNewDps = GYROSCOPE_DPS500;
+ }
+
+ data->uFactorydataReady = 0;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ iRet = send_instruction(data, FACTORY_MODE, GYROSCOPE_DPS_FACTORY,
+ chTempBuf, 2);
+
+ while (!(data->uFactorydataReady & (1 << GYROSCOPE_DPS_FACTORY))
+ && (iDelayCnt++ < 150)
+ && (iRet == SUCCESS))
+ msleep(20);
+
+ if ((iDelayCnt >= 150) || (iRet != SUCCESS)) {
+ ssp_err("Gyro Selftest DPS Timeout!!\n");
+ goto exit;
+ }
+
+ mdelay(5);
+
+ if (data->uFactorydata[0] != SUCCESS) {
+ ssp_err("Gyro Selftest DPS Error!!\n");
+ goto exit;
+ }
+
+ data->uGyroDps = (unsigned int)iNewDps;
+ ssp_err("%u dps stored\n", data->uGyroDps);
+exit:
+ return count;
+}
+
+static ssize_t gyro_selftest_dps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->uGyroDps);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, gyro_name_show, NULL);
+static DEVICE_ATTR(vendor, S_IRUGO, gyro_vendor_show, NULL);
+static DEVICE_ATTR(power_off, S_IRUGO, gyro_power_off, NULL);
+static DEVICE_ATTR(power_on, S_IRUGO, gyro_power_on, NULL);
+static DEVICE_ATTR(temperature, S_IRUGO, gyro_get_temp, NULL);
+static DEVICE_ATTR(selftest, S_IRUGO, gyro_selftest_show, NULL);
+static DEVICE_ATTR(selftest_dps, S_IRUGO | S_IWUSR | S_IWGRP,
+ gyro_selftest_dps_show, gyro_selftest_dps_store);
+
+static struct device_attribute *gyro_attrs[] = {
+ &dev_attr_name,
+ &dev_attr_vendor,
+ &dev_attr_selftest,
+ &dev_attr_power_on,
+ &dev_attr_power_off,
+ &dev_attr_temperature,
+ &dev_attr_selftest_dps,
+ NULL,
+};
+
+void initialize_gyro_factorytest(struct ssp_data *data)
+{
+ sensors_register(data->gyro_device, data, gyro_attrs, "gyro_sensor");
+}
+
+void remove_gyro_factorytest(struct ssp_data *data)
+{
+ sensors_unregister(data->gyro_device, gyro_attrs);
+}
diff --git a/drivers/sensorhub/stm/factory/gyro_mpu6500.c b/drivers/sensorhub/stm/factory/gyro_mpu6500.c
new file mode 100644
index 00000000000..0601070038c
--- /dev/null
+++ b/drivers/sensorhub/stm/factory/gyro_mpu6500.c
@@ -0,0 +1,615 @@
+/*
+ * 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/kernel.h>
+#include "../ssp.h"
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+#define VENDOR "INVENSENSE"
+#define CHIP_ID "MPU6500"
+
+#define CALIBRATION_FILE_PATH "/csa/sensor/gyro_cal_data"
+#define VERBOSE_OUT 1
+#define CALIBRATION_DATA_AMOUNT 20
+#define DEF_GYRO_FULLSCALE 2000
+#define DEF_GYRO_SENS (32768 / DEF_GYRO_FULLSCALE)
+#define DEF_BIAS_LSB_THRESH_SELF (20 * DEF_GYRO_SENS)
+#define DEF_BIAS_LSB_THRESH_SELF_6500 (30 * DEF_GYRO_SENS)
+#define DEF_RMS_LSB_TH_SELF (5 * DEF_GYRO_SENS)
+#define DEF_RMS_THRESH ((DEF_RMS_LSB_TH_SELF) * (DEF_RMS_LSB_TH_SELF))
+#define DEF_SCALE_FOR_FLOAT (1000)
+#define DEF_RMS_SCALE_FOR_RMS (10000)
+#define DEF_SQRT_SCALE_FOR_RMS (100)
+
+static ssize_t gyro_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VENDOR);
+}
+
+static ssize_t gyro_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", CHIP_ID);
+}
+
+int gyro_open_calibration(struct ssp_data *data)
+{
+ int iRet = 0;
+ mm_segment_t old_fs;
+ struct file *cal_filp = NULL;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+
+ data->gyrocal.x = 0;
+ data->gyrocal.y = 0;
+ data->gyrocal.z = 0;
+
+ return iRet;
+ }
+
+ iRet = cal_filp->f_op->read(cal_filp, (char *)&data->gyrocal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int))
+ iRet = -EIO;
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ ssp_dbg("open gyro calibration %d, %d, %d\n",
+ data->gyrocal.x, data->gyrocal.y, data->gyrocal.z);
+ return iRet;
+}
+
+static int save_gyro_caldata(struct ssp_data *data, s16 *iCalData)
+{
+ int iRet = 0;
+ struct file *cal_filp = NULL;
+ mm_segment_t old_fs;
+
+ data->gyrocal.x = iCalData[0] << 2;
+ data->gyrocal.y = iCalData[1] << 2;
+ data->gyrocal.z = iCalData[2] << 2;
+
+ ssp_dbg("do gyro calibrate %d, %d, %d\n",
+ data->gyrocal.x, data->gyrocal.y, data->gyrocal.z);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cal_filp = filp_open(CALIBRATION_FILE_PATH,
+ O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ if (IS_ERR(cal_filp)) {
+ ssp_err("Can't open calibration file\n");
+ set_fs(old_fs);
+ iRet = PTR_ERR(cal_filp);
+ return -EIO;
+ }
+
+ iRet = cal_filp->f_op->write(cal_filp, (char *)&data->gyrocal,
+ 3 * sizeof(int), &cal_filp->f_pos);
+ if (iRet != 3 * sizeof(int)) {
+ ssp_err("Can't write gyro cal to file\n");
+ iRet = -EIO;
+ }
+
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ return iRet;
+}
+
+int set_gyro_cal(struct ssp_data *data)
+{
+ int iRet = 0;
+ struct ssp_msg *msg;
+ s16 gyro_cal[3];
+ if (!(data->uSensorState & (1 << GYROSCOPE_SENSOR))) {
+ ssp_info("Skip this function!!!"\
+ ", gyro sensor is not connected(0x%x)\n",
+ data->uSensorState);
+ return iRet;
+ }
+
+ gyro_cal[0] = data->gyrocal.x;
+ gyro_cal[1] = data->gyrocal.y;
+ gyro_cal[2] = data->gyrocal.z;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_MCU_SET_GYRO_CAL;
+ msg->length = 6;
+ msg->options = AP2HUB_WRITE;
+ msg->buffer = (char*) kzalloc(6, GFP_KERNEL);
+
+ msg->free_buffer = 1;
+ memcpy(msg->buffer, gyro_cal, 6);
+
+ iRet = ssp_spi_async(data, msg);
+
+ if (iRet != SUCCESS) {
+ ssp_err("i2c fail %d\n", iRet);
+ iRet = ERROR;
+ }
+
+ ssp_dbg("Set gyro cal data %d, %d, %d\n", gyro_cal[0], gyro_cal[1], gyro_cal[2]);
+ return iRet;
+}
+
+static ssize_t gyro_power_off(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ func_dbg();
+
+ return sprintf(buf, "%d\n", 1);
+}
+
+static ssize_t gyro_power_on(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ func_dbg();
+
+ return sprintf(buf, "%d\n", 1);
+}
+
+short mpu6500_gyro_get_temp(struct ssp_data *data)
+{
+ char chTempBuf[2] = { 0};
+ unsigned char reg[2];
+ short temperature = 0;
+ int iRet = 0;
+
+ struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ goto exit;
+ }
+ msg->cmd = GYROSCOPE_TEMP_FACTORY;
+ msg->length = 2;
+ msg->options = AP2HUB_READ;
+ msg->buffer = chTempBuf;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_sync(data, msg, 3000);
+
+ if (iRet != SUCCESS) {
+ ssp_err("Gyro Temp Timeout!!\n");
+ goto exit;
+ }
+
+ reg[0] = chTempBuf[1];
+ reg[1] = chTempBuf[0];
+ temperature = (short) (((reg[0]) << 8) | reg[1]);
+ ssp_dbg("%d\n", temperature);
+
+ exit:
+ return temperature;
+}
+
+
+static ssize_t gyro_get_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ short temperature = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ temperature = mpu6500_gyro_get_temp(data);
+ return sprintf(buf, "%d\n", temperature);
+}
+
+u32 mpu6050_selftest_sqrt(u32 sqsum)
+{
+ u32 sq_rt;
+ u32 g0, g1, g2, g3, g4;
+ u32 seed;
+ u32 next;
+ u32 step;
+
+ g4 = sqsum / 100000000;
+ g3 = (sqsum - g4 * 100000000) / 1000000;
+ g2 = (sqsum - g4 * 100000000 - g3 * 1000000) / 10000;
+ g1 = (sqsum - g4 * 100000000 - g3 * 1000000 - g2 * 10000) / 100;
+ g0 = (sqsum - g4 * 100000000 - g3 * 1000000 - g2 * 10000 - g1 * 100);
+
+ next = g4;
+ step = 0;
+ seed = 0;
+ while (((seed + 1) * (step + 1)) <= next) {
+ step++;
+ seed++;
+ }
+
+ sq_rt = seed * 10000;
+ next = (next - (seed * step)) * 100 + g3;
+
+ step = 0;
+ seed = 2 * seed * 10;
+ while (((seed + 1) * (step + 1)) <= next) {
+ step++;
+ seed++;
+ }
+
+ sq_rt = sq_rt + step * 1000;
+ next = (next - seed * step) * 100 + g2;
+ seed = (seed + step) * 10;
+ step = 0;
+ while (((seed + 1) * (step + 1)) <= next) {
+ step++;
+ seed++;
+ }
+
+ sq_rt = sq_rt + step * 100;
+ next = (next - seed * step) * 100 + g1;
+ seed = (seed + step) * 10;
+ step = 0;
+
+ while (((seed + 1) * (step + 1)) <= next) {
+ step++;
+ seed++;
+ }
+
+ sq_rt = sq_rt + step * 10;
+ next = (next - seed * step) * 100 + g0;
+ seed = (seed + step) * 10;
+ step = 0;
+
+ while (((seed + 1) * (step + 1)) <= next) {
+ step++;
+ seed++;
+ }
+
+ sq_rt = sq_rt + step;
+
+ return sq_rt;
+}
+
+static ssize_t mpu6500_gyro_selftest(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char chTempBuf[36] = { 0,};
+ u8 initialized = 0;
+ s8 hw_result = 0;
+ int i = 0, j = 0, total_count = 0, ret_val = 0;
+ long avg[3] = {0,}, rms[3] = {0,};
+ int gyro_bias[3] = {0,}, gyro_rms[3] = {0,};
+ s16 shift_ratio[3] = {0,};
+ s16 iCalData[3] = {0,};
+ char a_name[3][2] = { "X", "Y", "Z" };
+ int iRet = 0;
+ int dps_rms[3] = { 0, };
+ u32 temp = 0;
+ int bias_thresh = DEF_BIAS_LSB_THRESH_SELF_6500;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ goto exit;
+ }
+ msg->cmd = GYROSCOPE_FACTORY;
+ msg->length = 36;
+ msg->options = AP2HUB_READ;
+ msg->buffer = chTempBuf;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_sync(data, msg, 7000);
+
+ if (iRet != SUCCESS) {
+ ssp_err("Gyro Selftest Timeout!!\n");
+ ret_val = 1;
+ goto exit;
+ }
+
+ data->uTimeOutCnt = 0;
+
+ ssp_err("%d %d %d %d %d %d %d %d %d %d %d %d", chTempBuf[0],
+ chTempBuf[1], chTempBuf[2], chTempBuf[3], chTempBuf[4],
+ chTempBuf[5], chTempBuf[6], chTempBuf[7], chTempBuf[8],
+ chTempBuf[9], chTempBuf[10], chTempBuf[11]);
+
+ initialized = chTempBuf[0];
+ shift_ratio[0] = (s16)((chTempBuf[2] << 8) +
+ chTempBuf[1]);
+ shift_ratio[1] = (s16)((chTempBuf[4] << 8) +
+ chTempBuf[3]);
+ shift_ratio[2] = (s16)((chTempBuf[6] << 8) +
+ chTempBuf[5]);
+ hw_result = (s8)chTempBuf[7];
+ total_count = (int)((chTempBuf[11] << 24) +
+ (chTempBuf[10] << 16) +
+ (chTempBuf[9] << 8) +
+ chTempBuf[8]);
+ avg[0] = (long)((chTempBuf[15] << 24) +
+ (chTempBuf[14] << 16) +
+ (chTempBuf[13] << 8) +
+ chTempBuf[12]);
+ avg[1] = (long)((chTempBuf[19] << 24) +
+ (chTempBuf[18] << 16) +
+ (chTempBuf[17] << 8) +
+ chTempBuf[16]);
+ avg[2] = (long)((chTempBuf[23] << 24) +
+ (chTempBuf[22] << 16) +
+ (chTempBuf[21] << 8) +
+ chTempBuf[20]);
+ rms[0] = (long)((chTempBuf[27] << 24) +
+ (chTempBuf[26] << 16) +
+ (chTempBuf[25] << 8) +
+ chTempBuf[24]);
+ rms[1] = (long)((chTempBuf[31] << 24) +
+ (chTempBuf[30] << 16) +
+ (chTempBuf[29] << 8) +
+ chTempBuf[28]);
+ rms[2] = (long)((chTempBuf[35] << 24) +
+ (chTempBuf[34] << 16) +
+ (chTempBuf[33] << 8) +
+ chTempBuf[32]);
+ ssp_info("init: %d, total cnt: %d\n", initialized, total_count);
+ ssp_info("hw_result: %d, %d, %d, %d\n", hw_result,
+ shift_ratio[0], shift_ratio[1], shift_ratio[2]);
+ ssp_info("avg %+8ld %+8ld %+8ld (LSB)\n", avg[0], avg[1], avg[2]);
+ ssp_info("rms %+8ld %+8ld %+8ld (LSB)\n", rms[0], rms[1], rms[2]);
+
+ if (total_count == 0) {
+ ssp_err("total_count is 0. goto exit\n");
+ ret_val = 2;
+ goto exit;
+ }
+
+ if (hw_result < 0) {
+ ssp_err("hw selftest fail(%d), sw selftest skip\n", hw_result);
+ return sprintf(buf, "-1,0,0,0,0,0,0,%d.%d,%d.%d,%d.%d,0,0,0\n",
+ shift_ratio[0] / 10, shift_ratio[0] % 10,
+ shift_ratio[1] / 10, shift_ratio[1] % 10,
+ shift_ratio[2] / 10, shift_ratio[2] % 10);
+ }
+ gyro_bias[0] = (avg[0] * DEF_SCALE_FOR_FLOAT) / DEF_GYRO_SENS;
+ gyro_bias[1] = (avg[1] * DEF_SCALE_FOR_FLOAT) / DEF_GYRO_SENS;
+ gyro_bias[2] = (avg[2] * DEF_SCALE_FOR_FLOAT) / DEF_GYRO_SENS;
+ iCalData[0] = (s16)avg[0];
+ iCalData[1] = (s16)avg[1];
+ iCalData[2] = (s16)avg[2];
+
+ if (VERBOSE_OUT) {
+ ssp_info("abs bias : %+8d.%03d %+8d.%03d %+8d.%03d (dps)\n",
+ (int)abs(gyro_bias[0]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_bias[0]) % DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_bias[1]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_bias[1]) % DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_bias[2]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_bias[2]) % DEF_SCALE_FOR_FLOAT);
+ }
+
+ for (j = 0; j < 3; j++) {
+ if (unlikely(abs(avg[j]) > bias_thresh)) {
+ ssp_err("%s Gyro bias (%ld) exceeded threshold "\
+ "(threshold = %d LSB)\n", a_name[j],
+ avg[j], bias_thresh);
+ ret_val |= 1 << (3 + j);
+ }
+ }
+ /* 3rd, check RMS for dead gyros
+ If any of the RMS noise value returns zero,
+ then we might have dead gyro or FIFO/register failure,
+ the part is sleeping, or the part is not responsive */
+ if (rms[0] == 0 || rms[1] == 0 || rms[2] == 0)
+ ret_val |= 1 << 6;
+
+ if (VERBOSE_OUT) {
+ ssp_info("RMS ^ 2 : %+8ld %+8ld %+8ld\n",
+ (long)rms[0] / total_count,
+ (long)rms[1] / total_count, (long)rms[2] / total_count);
+ }
+
+ for (j = 0; j < 3; j++) {
+ if (unlikely(rms[j] / total_count > DEF_RMS_THRESH)) {
+ ssp_err("%s Gyro rms (%ld) exceeded threshold "\
+ "(threshold = %d LSB)\n", a_name[j],
+ rms[j] / total_count, DEF_RMS_THRESH);
+ ret_val |= 1 << (7 + j);
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (rms[i] > 10000) {
+ temp =
+ ((u32) (rms[i] / total_count)) *
+ DEF_RMS_SCALE_FOR_RMS;
+ } else {
+ temp =
+ ((u32) (rms[i] * DEF_RMS_SCALE_FOR_RMS)) /
+ total_count;
+ }
+ if (rms[i] < 0)
+ temp = 1 << 31;
+
+ dps_rms[i] = mpu6050_selftest_sqrt(temp) / DEF_GYRO_SENS;
+
+ gyro_rms[i] =
+ dps_rms[i] * DEF_SCALE_FOR_FLOAT / DEF_SQRT_SCALE_FOR_RMS;
+ }
+
+ ssp_info("RMS : %+8d.%03d %+8d.%03d %+8d.%03d (dps)\n",
+ (int)abs(gyro_rms[0]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_rms[0]) % DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_rms[1]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_rms[1]) % DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_rms[2]) / DEF_SCALE_FOR_FLOAT,
+ (int)abs(gyro_rms[2]) % DEF_SCALE_FOR_FLOAT);
+
+ if (likely(!ret_val)) {
+ save_gyro_caldata(data, iCalData);
+ } else {
+ ssp_err("ret_val != 0, gyrocal is 0 at all axis\n");
+ data->gyrocal.x = 0;
+ data->gyrocal.y = 0;
+ data->gyrocal.z = 0;
+ }
+
+exit:
+ ssp_dbg("%d,"
+ "%d.%03d,%d.%03d,%d.%03d,"
+ "%d.%03d,%d.%03d,%d.%03d,"
+ "%d.%d,%d.%d,%d.%d,"
+ "%d,%d,%d\n",
+ ret_val,
+ (int)abs(gyro_bias[0]/1000),
+ (int)abs(gyro_bias[0])%1000,
+ (int)abs(gyro_bias[1]/1000),
+ (int)abs(gyro_bias[1])%1000,
+ (int)abs(gyro_bias[2]/1000),
+ (int)abs(gyro_bias[2])%1000,
+ gyro_rms[0]/1000,
+ (int)abs(gyro_rms[0])%1000,
+ gyro_rms[1]/1000,
+ (int)abs(gyro_rms[1])%1000,
+ gyro_rms[2]/1000,
+ (int)abs(gyro_rms[2])%1000,
+ shift_ratio[0] / 10, shift_ratio[0] % 10,
+ shift_ratio[1] / 10, shift_ratio[1] % 10,
+ shift_ratio[2] / 10, shift_ratio[2] % 10,
+ (int)(total_count/3),
+ (int)(total_count/3),
+ (int)(total_count/3));
+
+ return sprintf(buf, "%d,"
+ "%d.%03d,%d.%03d,%d.%03d,"
+ "%d.%03d,%d.%03d,%d.%03d,"
+ "%d.%d,%d.%d,%d.%d,"
+ "%d,%d,%d\n",
+ ret_val,
+ (int)abs(gyro_bias[0]/1000),
+ (int)abs(gyro_bias[0])%1000,
+ (int)abs(gyro_bias[1]/1000),
+ (int)abs(gyro_bias[1])%1000,
+ (int)abs(gyro_bias[2]/1000),
+ (int)abs(gyro_bias[2])%1000,
+ gyro_rms[0]/1000,
+ (int)abs(gyro_rms[0])%1000,
+ gyro_rms[1]/1000,
+ (int)abs(gyro_rms[1])%1000,
+ gyro_rms[2]/1000,
+ (int)abs(gyro_rms[2])%1000,
+ shift_ratio[0] / 10, shift_ratio[0] % 10,
+ shift_ratio[1] / 10, shift_ratio[1] % 10,
+ shift_ratio[2] / 10, shift_ratio[2] % 10,
+ (int)(total_count/3),
+ (int)(total_count/3),
+ (int)(total_count/3));
+}
+
+static ssize_t gyro_selftest_dps_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int iNewDps = 0;
+ int iRet = 0;
+ char chTempBuf = 0;
+
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ struct ssp_msg *msg;
+
+ if (!(data->uSensorState & (1 << GYROSCOPE_SENSOR)))
+ goto exit;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ goto exit;
+ }
+ msg->cmd = GYROSCOPE_DPS_FACTORY;
+ msg->length = 1;
+ msg->options = AP2HUB_READ;
+ msg->buffer = &chTempBuf;
+ msg->free_buffer = 0;
+
+ sscanf(buf, "%d", &iNewDps);
+
+ if (iNewDps == GYROSCOPE_DPS250)
+ msg->options |= 0 << SSP_GYRO_DPS;
+ else if (iNewDps == GYROSCOPE_DPS500)
+ msg->options |= 1 << SSP_GYRO_DPS;
+ else if (iNewDps == GYROSCOPE_DPS2000)
+ msg->options |= 2 << SSP_GYRO_DPS;
+ else {
+ msg->options |= 1 << SSP_GYRO_DPS;
+ iNewDps = GYROSCOPE_DPS500;
+ }
+
+ iRet = ssp_spi_sync(data, msg, 3000);
+
+ if (iRet != SUCCESS) {
+ ssp_err("Gyro Selftest DPS Timeout!!\n");
+ goto exit;
+ }
+
+ if (chTempBuf != SUCCESS) {
+ ssp_err("Gyro Selftest DPS Error!!\n");
+ goto exit;
+ }
+
+ data->uGyroDps = (unsigned int)iNewDps;
+ ssp_err("%u dps stored\n", data->uGyroDps);
+exit:
+ return count;
+}
+
+static ssize_t gyro_selftest_dps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->uGyroDps);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, gyro_name_show, NULL);
+static DEVICE_ATTR(vendor, S_IRUGO, gyro_vendor_show, NULL);
+static DEVICE_ATTR(power_off, S_IRUGO, gyro_power_off, NULL);
+static DEVICE_ATTR(power_on, S_IRUGO, gyro_power_on, NULL);
+static DEVICE_ATTR(temperature, S_IRUGO, gyro_get_temp, NULL);
+static DEVICE_ATTR(selftest, S_IRUGO, mpu6500_gyro_selftest, NULL);
+static DEVICE_ATTR(selftest_dps, S_IRUGO | S_IWUSR | S_IWGRP,
+ gyro_selftest_dps_show, gyro_selftest_dps_store);
+
+static struct device_attribute *gyro_attrs[] = {
+ &dev_attr_name,
+ &dev_attr_vendor,
+ &dev_attr_selftest,
+ &dev_attr_power_on,
+ &dev_attr_power_off,
+ &dev_attr_temperature,
+ &dev_attr_selftest_dps,
+ NULL,
+};
+
+void initialize_gyro_factorytest(struct ssp_data *data)
+{
+ sensors_register(data->gyro_device, data, gyro_attrs, "gyro_sensor");
+}
+
+void remove_gyro_factorytest(struct ssp_data *data)
+{
+ sensors_unregister(data->gyro_device, gyro_attrs);
+}
diff --git a/drivers/sensorhub/stm/factory/hrm_adpd142.c b/drivers/sensorhub/stm/factory/hrm_adpd142.c
new file mode 100644
index 00000000000..adf885401f1
--- /dev/null
+++ b/drivers/sensorhub/stm/factory/hrm_adpd142.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c)2013 Maxim Integrated Products, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include "../ssp.h"
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+#define VENDOR "ADI"
+#define CHIP_ID "ADPD142"
+
+static ssize_t hrm_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VENDOR);
+}
+
+static ssize_t hrm_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", CHIP_ID);
+}
+
+static ssize_t hrm_eol_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int iCount = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ iCount = sprintf(buf,"%d %d %d %d %d %d %d\n",
+ data->buf[BIO_HRM_RAW_FAC].frequency,
+ data->buf[BIO_HRM_RAW_FAC].noise_value,
+ data->buf[BIO_HRM_RAW_FAC].dc_value,
+ data->buf[BIO_HRM_RAW_FAC].ac_value,
+ data->buf[BIO_HRM_RAW_FAC].perfusion_rate,
+ data->buf[BIO_HRM_RAW_FAC].snrac,
+ data->buf[BIO_HRM_RAW_FAC].snrdc);
+
+ return iCount;
+}
+
+static ssize_t hrm_eol_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int iRet;
+ int64_t dEnable;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ iRet = kstrtoll(buf, 10, &dEnable);
+ if (iRet < 0)
+ return iRet;
+
+ if (dEnable)
+ atomic_set(&data->eol_enable, 1);
+ else
+ atomic_set(&data->eol_enable, 0);
+
+ return size;
+}
+
+static ssize_t hrm_raw_data_read(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d,%d\n",
+ data->buf[BIO_HRM_RAW].ch_a,
+ data->buf[BIO_HRM_RAW].ch_b);
+}
+
+static ssize_t hrm_lib_data_read(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
+ data->buf[BIO_HRM_LIB].hr,
+ data->buf[BIO_HRM_LIB].rri,
+ data->buf[BIO_HRM_LIB].snr);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, hrm_name_show, NULL);
+static DEVICE_ATTR(vendor, S_IRUGO, hrm_vendor_show, NULL);
+static DEVICE_ATTR(hrm_eol, S_IRUGO | S_IWUSR | S_IWGRP, hrm_eol_show, hrm_eol_store);
+static DEVICE_ATTR(hrm_raw, S_IRUGO, hrm_raw_data_read, NULL);
+static DEVICE_ATTR(hrm_lib, S_IRUGO, hrm_lib_data_read, NULL);
+
+static struct device_attribute *hrm_attrs[] = {
+ &dev_attr_name,
+ &dev_attr_vendor,
+ &dev_attr_hrm_eol,
+ &dev_attr_hrm_raw,
+ &dev_attr_hrm_lib,
+ NULL,
+};
+
+void initialize_hrm_factorytest(struct ssp_data *data)
+{
+ sensors_register(data->hrm_device, data, hrm_attrs,
+ "hrm_sensor");
+}
+
+void remove_hrm_factorytest(struct ssp_data *data)
+{
+ sensors_unregister(data->hrm_device, hrm_attrs);
+}
diff --git a/drivers/sensorhub/stm/factory/mcu_at32uc3l0128.c b/drivers/sensorhub/stm/factory/mcu_at32uc3l0128.c
new file mode 100644
index 00000000000..d84fc06694d
--- /dev/null
+++ b/drivers/sensorhub/stm/factory/mcu_at32uc3l0128.c
@@ -0,0 +1,252 @@
+/*
+ * 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"
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+#define MODEL_NAME "AT32UC3L0128"
+
+ssize_t mcu_revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "AT01120%u,AT01120%u\n", data->uCurFirmRev,
+ get_module_rev(data));
+}
+
+ssize_t mcu_model_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", MODEL_NAME);
+}
+
+ssize_t mcu_update_kernel_bin_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ int iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("mcu binany update!\n");
+
+ iRet = forced_to_download_binary(data, UMS_BINARY);
+ if (iRet == SUCCESS) {
+ bSuccess = true;
+ goto out;
+ }
+
+ iRet = forced_to_download_binary(data, KERNEL_BINARY);
+ if (iRet == SUCCESS)
+ bSuccess = true;
+ else
+ bSuccess = false;
+out:
+ return sprintf(buf, "%s\n", (bSuccess ? "OK" : "NG"));
+}
+
+ssize_t mcu_update_kernel_crashed_bin_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ int iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("MCU binany update!\n");
+
+ iRet = forced_to_download_binary(data, UMS_BINARY);
+ if (iRet == SUCCESS) {
+ bSuccess = true;
+ goto out;
+ }
+
+ iRet = forced_to_download_binary(data, KERNEL_CRASHED_BINARY);
+ if (iRet == SUCCESS)
+ bSuccess = true;
+ else
+ bSuccess = false;
+out:
+ return sprintf(buf, "%s\n", (bSuccess ? "OK" : "NG"));
+}
+
+ssize_t mcu_update_ums_bin_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ int iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("MCU binany update!\n");
+
+ iRet = forced_to_download_binary(data, UMS_BINARY);
+ if (iRet == SUCCESS)
+ bSuccess = true;
+ else
+ bSuccess = false;
+
+ return sprintf(buf, "%s\n", (bSuccess ? "OK" : "NG"));
+}
+
+ssize_t mcu_reset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ reset_mcu(data);
+
+ return sprintf(buf, "OK\n");
+}
+
+ssize_t mcu_factorytest_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+ char chTempBuf[2] = {0, 10};
+ int iRet = 0;
+
+ if (sysfs_streq(buf, "1")) {
+ data->uFactorydataReady = 0;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ data->bMcuIRQTestSuccessed = false;
+ data->uTimeOutCnt = 0;
+
+ iRet = send_instruction(data, FACTORY_MODE,
+ MCU_FACTORY, chTempBuf, 2);
+ if (data->uTimeOutCnt == 0)
+ data->bMcuIRQTestSuccessed = true;
+ } else {
+ ssp_err("invalid value %d\n", *buf);
+ return -EINVAL;
+ }
+
+ ssp_dbg("MCU Factory Test Start! - %d\n", iRet);
+
+ return size;
+}
+
+ssize_t mcu_factorytest_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bMcuTestSuccessed = false;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (data->bSspShutdown == true) {
+ ssp_dbg("MCU Bin is crashed\n");
+ return sprintf(buf, "NG,NG,NG\n");
+ }
+
+ if (data->uFactorydataReady & (1 << MCU_FACTORY)) {
+ ssp_dbg("MCU Factory Test Data : %u, %u, %u, %u, %u\n",
+ data->uFactorydata[0], data->uFactorydata[1],
+ data->uFactorydata[2], data->uFactorydata[3],
+ data->uFactorydata[4]);
+
+ /* system clock, RTC, I2C Master, I2C Slave, externel pin */
+ if ((data->uFactorydata[0] == SUCCESS)
+ && (data->uFactorydata[1] == SUCCESS)
+ && (data->uFactorydata[2] == SUCCESS)
+ && (data->uFactorydata[3] == SUCCESS)
+ && (data->uFactorydata[4] == SUCCESS))
+ bMcuTestSuccessed = true;
+ } else {
+ ssp_err("The Sensorhub is not ready %u\n",
+ data->uFactorydataReady);
+ }
+
+ ssp_dbg("MCU Factory Test Result - %s, %s, %s\n", MODEL_NAME,
+ (data->bMcuIRQTestSuccessed ? "OK" : "NG"),
+ (bMcuTestSuccessed ? "OK" : "NG"));
+
+ return sprintf(buf, "%s,%s,%s\n", MODEL_NAME,
+ (data->bMcuIRQTestSuccessed ? "OK" : "NG"),
+ (bMcuTestSuccessed ? "OK" : "NG"));
+}
+
+ssize_t mcu_sleep_factorytest_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+ char chTempBuf[2] = {0, 10};
+ int iRet = 0;
+
+ if (sysfs_streq(buf, "1")) {
+ data->uFactorydataReady = 0;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ iRet = send_instruction(data, FACTORY_MODE,
+ MCU_SLEEP_FACTORY, chTempBuf, 2);
+ } else {
+ ssp_err("%s - invalid value %d\n", *buf);
+ return -EINVAL;
+ }
+
+ ssp_dbg("MCU Sleep Factory Test Start! - %d\n", iRet);
+
+ return size;
+}
+
+ssize_t mcu_sleep_factorytest_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int iDataIdx, iSensorData = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+ struct sensor_value fsb[SENSOR_MAX];
+
+ if (!(data->uFactorydataReady & (1 << MCU_SLEEP_FACTORY))) {
+ ssp_err("The Sensorhub is not ready\n");
+ goto exit;
+ }
+
+ for (iDataIdx = 0; iDataIdx < FACTORY_DATA_MAX;) {
+ iSensorData = (int)data->uFactorydata[iDataIdx++];
+ if ((iSensorData < 0) ||
+ (iSensorData >= (SENSOR_MAX - 1))) {
+ ssp_err("MCU data frame error %d\n", iSensorData);
+ goto exit;
+ }
+
+ data->get_sensor_data[iSensorData]((char *)data->uFactorydata,
+ &iDataIdx, &(fsb[iSensorData]));
+ }
+
+ convert_acc_data(&fsb[ACCELEROMETER_SENSOR].x);
+ convert_acc_data(&fsb[ACCELEROMETER_SENSOR].y);
+ convert_acc_data(&fsb[ACCELEROMETER_SENSOR].z);
+
+exit:
+ ssp_dbg("Result - "\
+ "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%u,%u,%u,%u,%u\n",
+ fsb[ACCELEROMETER_SENSOR].x, fsb[ACCELEROMETER_SENSOR].y,
+ fsb[ACCELEROMETER_SENSOR].z, fsb[GYROSCOPE_SENSOR].x,
+ fsb[GYROSCOPE_SENSOR].y, fsb[GYROSCOPE_SENSOR].z,
+ fsb[GEOMAGNETIC_SENSOR].x, fsb[GEOMAGNETIC_SENSOR].y,
+ fsb[GEOMAGNETIC_SENSOR].z, fsb[PRESSURE_SENSOR].pressure[0],
+ fsb[PRESSURE_SENSOR].pressure[1], fsb[PROXIMITY_SENSOR].prox[1],
+ fsb[LIGHT_SENSOR].r, fsb[LIGHT_SENSOR].g, fsb[LIGHT_SENSOR].b,
+ fsb[LIGHT_SENSOR].w);
+
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%u,%u,%u,%u,%u\n",
+ fsb[ACCELEROMETER_SENSOR].x, fsb[ACCELEROMETER_SENSOR].y,
+ fsb[ACCELEROMETER_SENSOR].z, fsb[GYROSCOPE_SENSOR].x,
+ fsb[GYROSCOPE_SENSOR].y, fsb[GYROSCOPE_SENSOR].z,
+ fsb[GEOMAGNETIC_SENSOR].x, fsb[GEOMAGNETIC_SENSOR].y,
+ fsb[GEOMAGNETIC_SENSOR].z, fsb[PRESSURE_SENSOR].pressure[0],
+ fsb[PRESSURE_SENSOR].pressure[1], fsb[PROXIMITY_SENSOR].prox[1],
+ fsb[LIGHT_SENSOR].r, fsb[LIGHT_SENSOR].g, fsb[LIGHT_SENSOR].b,
+ fsb[LIGHT_SENSOR].w);
+}
diff --git a/drivers/sensorhub/stm/factory/mcu_atuc128l5har.c b/drivers/sensorhub/stm/factory/mcu_atuc128l5har.c
new file mode 100644
index 00000000000..5d45d8167d5
--- /dev/null
+++ b/drivers/sensorhub/stm/factory/mcu_atuc128l5har.c
@@ -0,0 +1,312 @@
+/*
+ * 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"
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+#define MODEL_NAME "STM32F401CCY6B"
+
+ssize_t mcu_revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "ST01%u,ST01%u\n", data->uCurFirmRev,
+ get_module_rev(data));
+}
+
+ssize_t mcu_model_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", MODEL_NAME);
+}
+
+ssize_t mcu_update_kernel_bin_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ int iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("MCU binany update!\n");
+
+ iRet = forced_to_download_binary(data, UMS_BINARY);
+ if (iRet == SUCCESS) {
+ bSuccess = true;
+ goto out;
+ }
+
+ iRet = forced_to_download_binary(data, KERNEL_BINARY);
+ if (iRet == SUCCESS)
+ bSuccess = true;
+ else
+ bSuccess = false;
+out:
+ return sprintf(buf, "%s\n", (bSuccess ? "OK" : "NG"));
+}
+
+ssize_t mcu_update_kernel_crashed_bin_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ int iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("MCU binany update!\n");
+
+ iRet = forced_to_download_binary(data, UMS_BINARY);
+ if (iRet == SUCCESS) {
+ bSuccess = true;
+ goto out;
+ }
+
+ iRet = forced_to_download_binary(data, KERNEL_CRASHED_BINARY);
+ if (iRet == SUCCESS)
+ bSuccess = true;
+ else
+ bSuccess = false;
+out:
+ return sprintf(buf, "%s\n", (bSuccess ? "OK" : "NG"));
+}
+
+ssize_t mcu_update_ums_bin_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ int iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("MCU binany update!\n");
+
+ iRet = forced_to_download_binary(data, UMS_BINARY);
+ if (iRet == SUCCESS)
+ bSuccess = true;
+ else
+ bSuccess = false;
+
+ return sprintf(buf, "%s\n", (bSuccess ? "OK" : "NG"));
+}
+
+ssize_t mcu_reset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ reset_mcu(data);
+
+ return sprintf(buf, "OK\n");
+}
+
+ssize_t mcu_dump_show(struct device *dev, struct device_attribute *attr,
+ char *buf) {
+ struct ssp_data *data = dev_get_drvdata(dev);
+ int status = 1, iDelaycnt = 0;
+
+ data->bDumping = true;
+ set_big_data_start(data, BIG_TYPE_DUMP, 0);
+ msleep(300);
+ while (data->bDumping) {
+ mdelay(10);
+ if (iDelaycnt++ > 1000) {
+ status = 0;
+ break;
+ }
+ }
+ return sprintf(buf, "%s\n", status ? "OK" : "NG");
+}
+
+static char buffer[FACTORY_DATA_MAX];
+
+ssize_t mcu_factorytest_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+ int iRet = 0;
+ struct ssp_msg *msg;
+
+ if (sysfs_streq(buf, "1")) {
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ msg->cmd = MCU_FACTORY;
+ msg->length = 5;
+ msg->options = AP2HUB_READ;
+ msg->buffer = buffer;
+ msg->free_buffer = 0;
+
+ memset(msg->buffer, 0, 5);
+
+ iRet = ssp_spi_async(data, msg);
+
+ } else {
+ ssp_err("invalid value %d\n", *buf);
+ return -EINVAL;
+ }
+
+ ssp_dbg("MCU Factory Test Start! - %d\n", iRet);
+
+ return size;
+}
+
+ssize_t mcu_factorytest_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bMcuTestSuccessed = false;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (data->bSspShutdown == true) {
+ ssp_dbg(" MCU Bin is crashed\n");
+ return sprintf(buf, "NG,NG,NG\n");
+ }
+
+ ssp_dbg("MCU Factory Test Data : %u, %u, %u, %u, %u\n", buffer[0],
+ buffer[1], buffer[2], buffer[3], buffer[4]);
+
+ /* system clock, RTC, I2C Master, I2C Slave, externel pin */
+ if ((buffer[0] == SUCCESS)
+ && (buffer[1] == SUCCESS)
+ && (buffer[2] == SUCCESS)
+ && (buffer[3] == SUCCESS)
+ && (buffer[4] == SUCCESS))
+ bMcuTestSuccessed = true;
+
+ ssp_dbg("MCU Factory Test Result - %s, %s, %s\n", MODEL_NAME,
+ (bMcuTestSuccessed ? "OK" : "NG"), "OK");
+
+ return sprintf(buf, "%s,%s,%s\n", MODEL_NAME,
+ (bMcuTestSuccessed ? "OK" : "NG"), "OK");
+}
+
+ssize_t mcu_sleep_factorytest_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+ int iRet = 0;
+ struct ssp_msg *msg;
+
+ if (sysfs_streq(buf, "1")) {
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ msg->cmd = MCU_SLEEP_FACTORY;
+ msg->length = FACTORY_DATA_MAX;
+ msg->options = AP2HUB_READ;
+ msg->buffer = buffer;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_async(data, msg);
+
+ } else {
+ ssp_err("invalid value %d\n", *buf);
+ return -EINVAL;
+ }
+
+ ssp_dbg("MCU Sleep Factory Test Start! - %d\n", iRet);
+
+ return size;
+}
+
+ssize_t mcu_sleep_factorytest_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int iDataIdx, iSensorData = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+ struct sensor_value fsb[SENSOR_MAX];
+ u16 chLength = 0;
+
+ memcpy(&chLength, buffer, 2);
+ memset(fsb, 0, sizeof(struct sensor_value) * SENSOR_MAX);
+
+ for (iDataIdx = 2; iDataIdx < chLength + 2;) {
+ iSensorData = (int)buffer[iDataIdx++];
+
+ if ((iSensorData < 0) ||
+ (iSensorData >= (SENSOR_MAX - 1))) {
+ ssp_err("Mcu data frame error %d\n",
+ iSensorData);
+ goto exit;
+ }
+
+ data->get_sensor_data[iSensorData]((char *)buffer,
+ &iDataIdx, &(fsb[iSensorData]));
+ }
+
+ convert_acc_data(&fsb[ACCELEROMETER_SENSOR].x);
+ convert_acc_data(&fsb[ACCELEROMETER_SENSOR].y);
+ convert_acc_data(&fsb[ACCELEROMETER_SENSOR].z);
+
+ fsb[ACCELEROMETER_SENSOR].x -= data->accelcal.x;
+ fsb[ACCELEROMETER_SENSOR].y -= data->accelcal.y;
+ fsb[ACCELEROMETER_SENSOR].z -= data->accelcal.z;
+
+ fsb[GYROSCOPE_SENSOR].x -= data->gyrocal.x;
+ fsb[GYROSCOPE_SENSOR].y -= data->gyrocal.y;
+ fsb[GYROSCOPE_SENSOR].z -= data->gyrocal.z;
+
+ fsb[PRESSURE_SENSOR].pressure[0] -= data->iPressureCal;
+
+exit:
+ ssp_dbg("Result\n"
+ "accel %d,%d,%d\n"
+ "gyro %d,%d,%d\n"
+ "mag %d,%d,%d\n"
+ "baro %d,%d\n"
+ "ges %d,%d,%d,%d\n"
+ "prox %u,%u\n"
+ "temp %d,%d,%d\n"
+#ifdef CONFIG_SENSORS_SSP_MAX88921
+ "light %u,%u,%u,%u,%u,%u\n",
+#else
+ "light %u,%u,%u,%u\n",
+#endif
+ fsb[ACCELEROMETER_SENSOR].x, fsb[ACCELEROMETER_SENSOR].y,
+ fsb[ACCELEROMETER_SENSOR].z, fsb[GYROSCOPE_SENSOR].x,
+ fsb[GYROSCOPE_SENSOR].y, fsb[GYROSCOPE_SENSOR].z,
+ fsb[GEOMAGNETIC_SENSOR].x, fsb[GEOMAGNETIC_SENSOR].y,
+ fsb[GEOMAGNETIC_SENSOR].z, fsb[PRESSURE_SENSOR].pressure[0],
+ fsb[PRESSURE_SENSOR].pressure[1],
+ fsb[GESTURE_SENSOR].data[0], fsb[GESTURE_SENSOR].data[1],
+ fsb[GESTURE_SENSOR].data[2], fsb[GESTURE_SENSOR].data[3],
+ fsb[PROXIMITY_SENSOR].prox[0], fsb[PROXIMITY_SENSOR].prox[1],
+ fsb[TEMPERATURE_HUMIDITY_SENSOR].data[0],
+ fsb[TEMPERATURE_HUMIDITY_SENSOR].data[1],
+ fsb[TEMPERATURE_HUMIDITY_SENSOR].data[2],
+ fsb[LIGHT_SENSOR].r, fsb[LIGHT_SENSOR].g, fsb[LIGHT_SENSOR].b,
+ fsb[LIGHT_SENSOR].w
+#ifdef CONFIG_SENSORS_SSP_MAX88921
+ , fsb[LIGHT_SENSOR].ir_cmp, fsb[LIGHT_SENSOR].amb_pga
+#endif
+ );
+
+ return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%u,"
+//#ifdef CONFIG_SENSORS_SSP_MAX88921
+// "%u,%u,%u,%u,%u,%u,%d,%d,%d,%d,%d,%d\n",
+//#else
+ "%u,%u,%u,%u,%d,%d,%d,%d,%d,%d\n",
+//#endif
+ fsb[ACCELEROMETER_SENSOR].x, fsb[ACCELEROMETER_SENSOR].y,
+ fsb[ACCELEROMETER_SENSOR].z, fsb[GYROSCOPE_SENSOR].x,
+ fsb[GYROSCOPE_SENSOR].y, fsb[GYROSCOPE_SENSOR].z,
+ fsb[GEOMAGNETIC_SENSOR].x, fsb[GEOMAGNETIC_SENSOR].y,
+ fsb[GEOMAGNETIC_SENSOR].z, fsb[PRESSURE_SENSOR].pressure[0],
+ fsb[PRESSURE_SENSOR].pressure[1], fsb[PROXIMITY_SENSOR].prox[1],
+ fsb[LIGHT_SENSOR].r, fsb[LIGHT_SENSOR].g, fsb[LIGHT_SENSOR].b,
+ fsb[LIGHT_SENSOR].w,
+//#ifdef CONFIG_SENSORS_SSP_MAX88921
+// fsb[LIGHT_SENSOR].ir_cmp, fsb[LIGHT_SENSOR].amb_pga,
+//#endif
+ fsb[GESTURE_SENSOR].data[0], fsb[GESTURE_SENSOR].data[1],
+ fsb[GESTURE_SENSOR].data[2], fsb[GESTURE_SENSOR].data[3],
+ fsb[TEMPERATURE_HUMIDITY_SENSOR].data[0],
+ fsb[TEMPERATURE_HUMIDITY_SENSOR].data[1]);
+}
diff --git a/drivers/sensorhub/stm/factory/mcu_stm32f401.c b/drivers/sensorhub/stm/factory/mcu_stm32f401.c
new file mode 100644
index 00000000000..4b34079b521
--- /dev/null
+++ b/drivers/sensorhub/stm/factory/mcu_stm32f401.c
@@ -0,0 +1,355 @@
+/*
+ * 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"
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+#define MODEL_NAME "STM32F401CCY6B"
+#define SMART_ALERT_MOTION 8
+
+
+ssize_t mcu_revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "ST01%u,ST01%u\n", data->uCurFirmRev,
+ get_module_rev(data));
+}
+
+ssize_t mcu_model_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", MODEL_NAME);
+}
+
+ssize_t mcu_update_kernel_bin_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ int iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("MCU binany update!\n");
+
+ iRet = forced_to_download_binary(data, UMS_BINARY);
+ if (iRet == SUCCESS) {
+ bSuccess = true;
+ goto out;
+ }
+
+ iRet = forced_to_download_binary(data, KERNEL_BINARY);
+ if (iRet == SUCCESS)
+ bSuccess = true;
+ else
+ bSuccess = false;
+out:
+ return sprintf(buf, "%s\n", (bSuccess ? "OK" : "NG"));
+}
+
+ssize_t mcu_update_kernel_crashed_bin_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ int iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("MCU binany update!\n");
+
+ iRet = forced_to_download_binary(data, UMS_BINARY);
+ if (iRet == SUCCESS) {
+ bSuccess = true;
+ goto out;
+ }
+
+ iRet = forced_to_download_binary(data, KERNEL_CRASHED_BINARY);
+ if (iRet == SUCCESS)
+ bSuccess = true;
+ else
+ bSuccess = false;
+out:
+ return sprintf(buf, "%s\n", (bSuccess ? "OK" : "NG"));
+}
+
+ssize_t mcu_update_ums_bin_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ int iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("MCU binany update!\n");
+
+ iRet = forced_to_download_binary(data, UMS_BINARY);
+ if (iRet == SUCCESS)
+ bSuccess = true;
+ else
+ bSuccess = false;
+
+ return sprintf(buf, "%s\n", (bSuccess ? "OK" : "NG"));
+}
+
+ssize_t mcu_reset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ reset_mcu(data);
+
+ return sprintf(buf, "OK\n");
+}
+
+ssize_t mcu_dump_show(struct device *dev, struct device_attribute *attr,
+ char *buf) {
+ struct ssp_data *data = dev_get_drvdata(dev);
+ int status = 1, iDelaycnt = 0;
+
+ data->bDumping = true;
+ set_big_data_start(data, BIG_TYPE_DUMP, 0);
+ msleep(300);
+ while (data->bDumping) {
+ mdelay(10);
+ if (iDelaycnt++ > 1000) {
+ status = 0;
+ break;
+ }
+ }
+ return sprintf(buf, "%s\n", status ? "OK" : "NG");
+}
+
+static char buffer[FACTORY_DATA_MAX];
+
+ssize_t mcu_factorytest_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+ int iRet = 0;
+ struct ssp_msg *msg;
+
+ if (sysfs_streq(buf, "1")) {
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MCU_FACTORY;
+ msg->length = 5;
+ msg->options = AP2HUB_READ;
+ msg->buffer = buffer;
+ msg->free_buffer = 0;
+
+ memset(msg->buffer, 0, 5);
+
+ iRet = ssp_spi_async(data, msg);
+
+ } else {
+ ssp_err("invalid value %d\n", *buf);
+ return -EINVAL;
+ }
+
+ ssp_dbg("MCU Factory Test Start! - %d\n", iRet);
+
+ return size;
+}
+
+ssize_t mcu_factorytest_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bMcuTestSuccessed = false;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (data->bSspShutdown == true) {
+ ssp_dbg("MCU Bin is crashed\n");
+ return sprintf(buf, "NG,NG,NG\n");
+ }
+
+ ssp_dbg("MCU Factory Test Data : %u, %u, %u, %u, %u\n", buffer[0],
+ buffer[1], buffer[2], buffer[3], buffer[4]);
+
+ /* system clock, RTC, I2C Master, I2C Slave, externel pin */
+ if ((buffer[0] == SUCCESS)
+ && (buffer[1] == SUCCESS)
+ && (buffer[2] == SUCCESS)
+ && (buffer[3] == SUCCESS)
+ && (buffer[4] == SUCCESS))
+ bMcuTestSuccessed = true;
+
+ ssp_dbg("MCU Factory Test Result - %s, %s, %s\n", MODEL_NAME,
+ (bMcuTestSuccessed ? "OK" : "NG"), "OK");
+
+ return sprintf(buf, "%s,%s,%s\n", MODEL_NAME,
+ (bMcuTestSuccessed ? "OK" : "NG"), "OK");
+}
+
+ssize_t mcu_sleep_factorytest_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+ int iRet = 0;
+ struct ssp_msg *msg;
+
+ if (sysfs_streq(buf, "1")) {
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MCU_SLEEP_FACTORY;
+ msg->length = FACTORY_DATA_MAX;
+ msg->options = AP2HUB_READ;
+ msg->buffer = buffer;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_async(data, msg);
+
+ } else {
+ ssp_err("invalid value %d\n", *buf);
+ return -EINVAL;
+ }
+
+ ssp_dbg("MCU Sleep Factory Test Start! - %d\n", iRet);
+
+ return size;
+}
+
+ssize_t mcu_sleep_factorytest_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int iDataIdx, iSensorData = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+ struct sensor_value *fsb;
+ u16 chLength = 0;
+ int ret = 0;
+
+ fsb = kzalloc(sizeof(struct sensor_value)*SENSOR_MAX, GFP_KERNEL);
+
+ memcpy(&chLength, buffer, 2);
+ memset(fsb, 0, sizeof(struct sensor_value) * SENSOR_MAX);
+
+ for (iDataIdx = 2; iDataIdx < chLength + 2;) {
+ iSensorData = (int)buffer[iDataIdx++];
+
+ if ((iSensorData < 0) ||
+ (iSensorData >= (SENSOR_MAX - 1))) {
+ ssp_err("MCU data frame error %d\n",
+ iSensorData);
+ goto exit;
+ }
+
+ data->get_sensor_data[iSensorData]((char *)buffer,
+ &iDataIdx, &(fsb[iSensorData]));
+ }
+
+exit:
+ ssp_dbg("Result\n"
+ "accel %d,%d,%d\n"
+ "gyro %d,%d,%d\n"
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ "hrm_raw %d,%d\n"
+ "hrm_lib %d,%d,%d\n"
+#endif
+ ,
+ fsb[ACCELEROMETER_SENSOR].x, fsb[ACCELEROMETER_SENSOR].y,
+ fsb[ACCELEROMETER_SENSOR].z, fsb[GYROSCOPE_SENSOR].x,
+ fsb[GYROSCOPE_SENSOR].y, fsb[GYROSCOPE_SENSOR].z
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ , fsb[BIO_HRM_RAW].ch_a, fsb[BIO_HRM_RAW].ch_b
+ , fsb[BIO_HRM_LIB].hr, fsb[BIO_HRM_LIB].rri, fsb[BIO_HRM_RAW].snr
+#endif
+ );
+
+ ret = sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%u,"
+ "%u,%u,%u,%u,%d,%d,%d,%d,%d,%d"
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ ",%d, %d, %d, %d, %d"
+#endif
+ "\n",
+ fsb[ACCELEROMETER_SENSOR].x, fsb[ACCELEROMETER_SENSOR].y,
+ fsb[ACCELEROMETER_SENSOR].z, fsb[GYROSCOPE_SENSOR].x,
+ fsb[GYROSCOPE_SENSOR].y, fsb[GYROSCOPE_SENSOR].z,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ , fsb[BIO_HRM_RAW].ch_a, fsb[BIO_HRM_RAW].ch_b
+ , fsb[BIO_HRM_LIB].hr, fsb[BIO_HRM_LIB].rri, fsb[BIO_HRM_RAW].snr
+#endif
+ );
+
+ kfree(fsb);
+
+ return ret;
+}
+
+int ssp_charging_motion(struct ssp_data *data, int iEnable)
+{
+ u8 uBuf[2] = {0, 0};
+
+ if (iEnable == 1) {
+ send_instruction(data, ADD_LIBRARY,
+ SMART_ALERT_MOTION, uBuf, 2);
+ } else {
+ send_instruction(data, REMOVE_LIBRARY,
+ SMART_ALERT_MOTION, uBuf, 2);
+ }
+
+ return 0;
+}
+
+int ssp_parse_motion(struct ssp_data *data, char *dataframe, int start, int end)
+{
+ int length = end - start;
+ char *buf = dataframe + start;;
+
+ if (length != 4)
+ return FAIL;
+
+ if ((buf[0] == 1) && (buf[1] == 1) && (buf[2] == SMART_ALERT_MOTION)) {
+ ssp_dbg("LP MODE WAKEUP\n");
+ queue_work(data->lpm_motion_wq, &data->work_lpm_motion);
+ //report_key_event(data);
+ return SUCCESS;
+ }
+
+ return FAIL;
+}
+
+static void lpm_motion_work_func(struct work_struct *work)
+{
+ struct ssp_data *data =
+ container_of(work, struct ssp_data, work_lpm_motion);
+
+ input_event(data->key_input_dev, EV_KEY, KEY_HOMEPAGE, 1);
+ input_sync(data->key_input_dev);
+ ssp_charging_motion(data, 0);
+
+ msleep(10);
+
+ input_event(data->key_input_dev, EV_KEY, KEY_HOMEPAGE, 0);
+ input_sync(data->key_input_dev);
+ ssp_charging_motion(data, 1);
+
+}
+
+int intialize_lpm_motion(struct ssp_data *data)
+{
+ data->lpm_motion_wq = create_singlethread_workqueue("ssp_lpm_motion_wq");
+ if (!data->lpm_motion_wq)
+ return ERROR;
+
+ INIT_WORK(&data->work_lpm_motion, lpm_motion_work_func);
+ return SUCCESS;
+}
+
diff --git a/drivers/sensorhub/stm/sensors_core.c b/drivers/sensorhub/stm/sensors_core.c
new file mode 100644
index 00000000000..ca0dcf95bd1
--- /dev/null
+++ b/drivers/sensorhub/stm/sensors_core.c
@@ -0,0 +1,172 @@
+/*
+ * Universal sensors core class
+ *
+ * Author : Ryunkyun Park <ryun.park@samsung.com>
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/input.h>
+
+struct class *sensors_class;
+EXPORT_SYMBOL_GPL(sensors_class);
+struct class *sensors_event_class;
+EXPORT_SYMBOL_GPL(sensors_event_class);
+static atomic_t sensor_count;
+static struct device *symlink_dev;
+
+/*
+ * Create sysfs interface
+ */
+static void set_sensor_attr(struct device *dev,
+ struct device_attribute *attributes[])
+{
+ int i;
+
+ for (i = 0; attributes[i] != NULL; i++)
+ if ((device_create_file(dev, attributes[i])) < 0)
+ pr_err("[SENSOR CORE] fail device_create_file"\
+ "(dev, attributes[%d])\n", i);
+}
+
+int sensors_create_symlink(struct input_dev *inputdev)
+{
+ int err = 0;
+
+ if (symlink_dev == NULL) {
+ pr_err("%s, symlink_dev is NULL!!!\n", __func__);
+ return err ;
+ }
+
+ err = sysfs_create_link(&symlink_dev->kobj, &inputdev->dev.kobj, inputdev->name);
+
+ if (err < 0) {
+ pr_err("%s, %s failed!(%d)\n", __func__, inputdev->name, err);
+ return err;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(sensors_create_symlink);
+
+void sensors_remove_symlink(struct input_dev *inputdev)
+{
+
+ if (symlink_dev == NULL) {
+ pr_err("%s, symlink_dev is NULL!!!\n", __func__);
+ return;
+ }
+
+ sysfs_delete_link(&symlink_dev->kobj, &inputdev->dev.kobj, inputdev->name);
+}
+EXPORT_SYMBOL_GPL(sensors_remove_symlink);
+
+
+int sensors_register(struct device *dev, void *drvdata,
+ struct device_attribute *attributes[], char *name)
+{
+ int ret = 0;
+
+ if (!sensors_class) {
+ sensors_class = class_create(THIS_MODULE, "sensors");
+ if (IS_ERR(sensors_class))
+ return PTR_ERR(sensors_class);
+ }
+
+ dev = device_create(sensors_class, NULL, 0, drvdata, "%s", name);
+
+ if (IS_ERR(dev)) {
+ ret = PTR_ERR(dev);
+ pr_err("[SENSORS CORE] device_create failed!"\
+ "[%d]\n", ret);
+ return ret;
+ }
+
+ set_sensor_attr(dev, attributes);
+ atomic_inc(&sensor_count);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sensors_register);
+
+void sensors_unregister(struct device *dev,
+ struct device_attribute *attributes[])
+{
+ int i;
+
+ for (i = 0; attributes[i] != NULL; i++)
+ device_remove_file(dev, attributes[i]);
+}
+EXPORT_SYMBOL_GPL(sensors_unregister);
+
+void destroy_sensor_class(void)
+{
+ if (sensors_class) {
+ class_destroy(sensors_class);
+ sensors_class = NULL;
+ }
+
+ if (sensors_event_class) {
+ device_destroy(sensors_event_class, symlink_dev->devt);
+ class_destroy(sensors_event_class);
+ symlink_dev = NULL;
+ sensors_event_class = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(destroy_sensor_class);
+
+static int __init sensors_class_init(void)
+{
+ pr_info("[SENSORS CORE] sensors_class_init\n");
+ sensors_class = class_create(THIS_MODULE, "sensors");
+
+ if (IS_ERR(sensors_class)) {
+ pr_err("%s, create sensors_class is failed.(err=%ld)\n",
+ __func__, IS_ERR(sensors_class));
+ return PTR_ERR(sensors_class);
+ }
+
+ /* For symbolic link */
+ sensors_event_class = class_create(THIS_MODULE, "sensor_event");
+ if (IS_ERR(sensors_event_class)) {
+ pr_err("%s, create sensors_class is failed.(err=%ld)\n",
+ __func__, IS_ERR(sensors_event_class));
+ return PTR_ERR(sensors_event_class);
+ }
+
+ symlink_dev = device_create(sensors_event_class, NULL, 0, NULL,
+ "%s", "symlink");
+
+ if (IS_ERR(symlink_dev)) {
+ pr_err("[SENSORS CORE] symlink_dev create failed!"\
+ "[%ld]\n", IS_ERR(symlink_dev));
+ return PTR_ERR(symlink_dev);
+ }
+
+ atomic_set(&sensor_count, 0);
+ sensors_class->dev_uevent = NULL;
+ pr_info("[SENSORS CORE] sensors_class_init succcess\n");
+
+ return 0;
+}
+
+static void __exit sensors_class_exit(void)
+{
+ if (sensors_class || sensors_event_class) {
+ class_destroy(sensors_class);
+ sensors_class = NULL;
+ class_destroy(sensors_event_class);
+ sensors_event_class = NULL;
+ }
+}
+
+subsys_initcall(sensors_class_init);
+module_exit(sensors_class_exit);
+
+MODULE_DESCRIPTION("Universal sensors core class");
+MODULE_AUTHOR("Ryunkyun Park <ryun.park@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/sensorhub/stm/ssp.h b/drivers/sensorhub/stm/ssp.h
new file mode 100644
index 00000000000..49cdc80cb92
--- /dev/null
+++ b/drivers/sensorhub/stm/ssp.h
@@ -0,0 +1,648 @@
+/*
+ * 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_PRJ_H__
+#define __SSP_PRJ_H__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/rtc.h>
+#include <linux/regulator/consumer.h>
+#include <linux/ssp_platformdata.h>
+#ifdef CONFIG_SENSORS_SSP_STM
+#include <linux/spi/spi.h>
+#endif
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+#include "ssp_sensorhub.h"
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#undef CONFIG_HAS_EARLYSUSPEND
+#endif
+
+#define SUCCESS 1
+#define FAIL 0
+#define ERROR -1
+
+#define FACTORY_DATA_MAX 99
+
+#undef SAVE_MAG_LOG /* Magnetic sensor data logging flag */
+
+#define SSP_FUNC_DBG 1
+#define SSP_DBG 0
+
+/* ssp mcu device ID */
+#define DEVICE_ID 0x55
+
+#if SSP_DBG
+#define ssp_dbg(format, ...)\
+ do {\
+ pr_info("[SSP] %s - " format, __func__, ##__VA_ARGS__); \
+ } while (0)
+#else
+#define ssp_dbg(format, ...)
+#endif
+
+#define ssp_info(format, ...)\
+ do {\
+ pr_info("[SSP] %s - " format, __func__, ##__VA_ARGS__); \
+ }while(0)
+
+#define ssp_err(format, ...)\
+ do {\
+ pr_err("[SSP] %s - " format, __func__, ##__VA_ARGS__); \
+ } while (0)
+
+#if SSP_FUNC_DBG
+#define func_dbg()\
+ do {\
+ pr_info("[SSP] %s is called!\n", __func__);\
+ } while (0)
+#else
+#define func_dbg()
+#endif
+
+#define SSP_SW_RESET_TIME 3000
+#define DEFUALT_POLLING_DELAY (200 * NSEC_PER_MSEC)
+#define PROX_AVG_READ_NUM 80
+#define DEFAULT_RETRIES 3
+#define DATA_PACKET_SIZE 960
+
+/* SSP Binary Type */
+enum {
+ KERNEL_BINARY = 0,
+ KERNEL_CRASHED_BINARY,
+ UMS_BINARY,
+};
+
+/*
+ * SENSOR_DELAY_SET_STATE
+ * Check delay set to avoid sending ADD instruction twice
+ */
+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,
+};
+
+/* for MSG2SSP_AP_GET_THERM */
+enum {
+ ADC_BATT = 0,
+ ADC_CHG,
+};
+
+enum {
+ SENSORS_BATCH_DRY_RUN = 0x00000001,
+ SENSORS_BATCH_WAKE_UPON_FIFO_FULL = 0x00000002
+};
+
+enum {
+ META_DATA_FLUSH_COMPLETE = 1,
+};
+
+#define SSP_INVALID_REVISION 99999
+#define SSP_INVALID_REVISION2 0xFFFFFF
+
+/* Gyroscope DPS */
+#define GYROSCOPE_DPS250 250
+#define GYROSCOPE_DPS500 500
+#define GYROSCOPE_DPS2000 2000
+
+/* Gesture Sensor Current */
+#define DEFUALT_IR_CURRENT 100 /* 0xF0 */
+
+/* kernel -> ssp manager cmd*/
+#define SSP_LIBRARY_SLEEP_CMD (1 << 5)
+#define SSP_LIBRARY_LARGE_DATA_CMD (1 << 6)
+#define SSP_LIBRARY_WAKEUP_CMD (1 << 7)
+
+/* 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
+
+#define MAX_COMP_BUFF 60
+
+/* temphumidity sensor*/
+struct shtc1_buffer {
+ u16 batt[MAX_COMP_BUFF];
+ u16 chg[MAX_COMP_BUFF];
+ s16 temp[MAX_COMP_BUFF];
+ u16 humidity[MAX_COMP_BUFF];
+ u16 baro[MAX_COMP_BUFF];
+ u16 gyro[MAX_COMP_BUFF];
+ char len;
+};
+
+/* SSP_INSTRUCTION_CMD */
+enum {
+ REMOVE_SENSOR = 0,
+ ADD_SENSOR,
+ CHANGE_DELAY,
+ GO_SLEEP,
+ REMOVE_LIBRARY,
+ ADD_LIBRARY,
+ GET_LOGGING,
+};
+
+/* SENSOR_TYPE */
+enum {
+ ACCELEROMETER_SENSOR = 0,
+ GYROSCOPE_SENSOR,
+ GEOMAGNETIC_UNCALIB_SENSOR,
+ GEOMAGNETIC_RAW,
+ GEOMAGNETIC_SENSOR,
+ PRESSURE_SENSOR,
+ GESTURE_SENSOR,
+ PROXIMITY_SENSOR,
+ TEMPERATURE_HUMIDITY_SENSOR,
+ LIGHT_SENSOR,
+ PROXIMITY_RAW,
+ ORIENTATION_SENSOR,
+ STEP_DETECTOR = 12,
+ SIG_MOTION_SENSOR,
+ GYRO_UNCALIB_SENSOR,
+ GAME_ROTATION_VECTOR = 15,
+ ROTATION_VECTOR,
+ STEP_COUNTER,
+ BIO_HRM_RAW,
+ BIO_HRM_RAW_FAC,
+ BIO_HRM_LIB,
+ SENSOR_MAX, /* = 21 */
+};
+
+struct meta_data_event {
+ s32 what;
+ s32 sensor;
+} __attribute__((__packed__));
+
+struct sensor_value {
+ union {
+ struct {
+ s16 x;
+ s16 y;
+ s16 z;
+ };
+ struct { /*calibrated mag, gyro*/
+ s16 cal_x;
+ s16 cal_y;
+ s16 cal_z;
+ u8 accuracy;
+ };
+ struct { /*uncalibrated mag, gyro*/
+ s16 uncal_x;
+ s16 uncal_y;
+ s16 uncal_z;
+ s16 offset_x;
+ s16 offset_y;
+ s16 offset_z;
+ };
+ struct { /* rotation vector */
+ s32 quat_a;
+ s32 quat_b;
+ s32 quat_c;
+ s32 quat_d;
+ u8 acc_rot;
+ };
+ struct {
+ u16 r;
+ u16 g;
+ u16 b;
+ u16 w;
+#ifdef CONFIG_SENSORS_SSP_MAX88921
+ u16 ir_cmp;
+ u16 amb_pga;
+#endif
+ };
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ struct {
+ u32 ch_a;
+ u32 ch_b;
+ u32 frequency;
+ u32 noise_value;
+ u32 dc_value;
+ u32 ac_value;
+ u32 perfusion_rate;
+ u32 snrac;
+ u32 snrdc;
+ };
+ struct {
+ s16 hr;
+ s16 rri;
+ s32 snr;
+ };
+#endif
+ s16 data[19];
+#ifdef SAVE_MAG_LOG
+ u8 log_data[20];
+#endif
+ };
+ u64 timestamp;
+} __attribute__((__packed__));
+
+extern struct class *sensors_event_class;
+
+struct calibraion_data {
+ s16 x;
+ s16 y;
+ s16 z;
+};
+
+struct hw_offset_data {
+ char x;
+ char y;
+ char 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;
+ u16 length;
+ u16 options;
+ u32 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,
+ /*+snamy.jeong 0706 for voice model download & pcm dump*/
+ BIG_TYPE_VOICE_NET,
+ BIG_TYPE_VOICE_GRAM,
+ BIG_TYPE_VOICE_PCM,
+ /*-snamy.jeong 0706 for voice model download & pcm dump*/
+ BIG_TYPE_TEMP,
+ BIG_TYPE_MAX,
+};
+
+struct ssp_data {
+ struct input_dev *acc_input_dev;
+ struct input_dev *gyro_input_dev;
+ struct input_dev *key_input_dev;
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ struct input_dev *hrm_raw_input_dev;
+ struct input_dev *hrm_lib_input_dev;
+#endif
+
+#ifdef CONFIG_SENSORS_SSP_STM
+ struct spi_device *spi;
+#endif
+ struct i2c_client *client;
+ struct timer_list debug_timer;
+ struct workqueue_struct *debug_wq;
+ struct workqueue_struct *lpm_motion_wq;
+ struct work_struct work_debug;
+ struct work_struct work_lpm_motion;
+ struct calibraion_data accelcal;
+ struct calibraion_data gyrocal;
+ struct hw_offset_data magoffset;
+ struct sensor_value buf[SENSOR_MAX];
+ struct device *sen_dev;
+ struct device *mcu_device;
+ struct device *acc_device;
+ struct device *gyro_device;
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ struct device *hrm_device;
+#endif
+ struct delayed_work work_firmware;
+ struct delayed_work work_refresh;
+ struct miscdevice shtc1_device;
+
+/*snamy.jeong@samsung.com temporary code for voice data sending to mcu*/
+ struct device *voice_device;
+
+ bool bSspShutdown;
+ bool bAccelAlert;
+ bool bMcuDumpMode;
+ bool bBinaryChashed;
+ bool bProbeIsDone;
+ bool bDumping;
+ bool bLpModeEnabled;
+ bool bTimeSyncing;
+
+ unsigned int uIr_Current;
+ unsigned char uFuseRomData[3];
+ unsigned char uMagCntlRegData;
+ char *pchLibraryBuf;
+ char chLcdLdi[2];
+ int iIrq;
+ int iLibraryLength;
+ int aiCheckStatus[SENSOR_MAX];
+
+ unsigned int uComFailCnt;
+ unsigned int uResetCnt;
+ unsigned int uTimeOutCnt;
+ unsigned int uIrqCnt;
+ unsigned int uDumpCnt;
+
+ unsigned int uGyroDps;
+ unsigned int uSensorState;
+ unsigned int uCurFirmRev;
+
+ char uLastResumeState;
+ char uLastAPState;
+
+ atomic_t aSensorEnable;
+ int64_t adDelayBuf[SENSOR_MAX];
+ s32 batchLatencyBuf[SENSOR_MAX];
+ s8 batchOptBuf[SENSOR_MAX];
+
+ void (*get_sensor_data[SENSOR_MAX])(char *, int *,
+ struct sensor_value *);
+ void (*report_sensor_data[SENSOR_MAX])(struct ssp_data *,
+ struct sensor_value *);
+ int (*check_lpmode)(void);
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ int (*hrm_sensor_power)(int);
+ atomic_t eol_enable;
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ struct ssp_sensorhub_data *hub_data;
+#endif
+ int ap_rev;
+ int accel_position;
+ int mag_position;
+ int fw_dl_state;
+ u8 mag_matrix_size;
+ u8 *mag_matrix;
+
+#ifdef CONFIG_SENSORS_SSP_STM
+ struct mutex comm_mutex;
+ struct mutex pending_mutex;
+#endif
+ int rst;
+ int ap_int;
+ int mcu_int1;
+ int mcu_int2;
+ struct list_head pending_list;
+ void (*ssp_big_task[BIG_TYPE_MAX])(struct work_struct *);
+ u64 timestamp;
+};
+
+struct ssp_big {
+ struct ssp_data* data;
+ struct work_struct work;
+ u32 length;
+ u32 addr;
+};
+
+void ssp_enable(struct ssp_data *, bool);
+int ssp_spi_async(struct ssp_data *, struct ssp_msg *);
+int ssp_spi_sync(struct ssp_data *, struct ssp_msg *, int);
+void clean_pending_list(struct ssp_data *);
+void toggle_mcu_reset(struct ssp_data *);
+int initialize_mcu(struct ssp_data *);
+int initialize_input_dev(struct ssp_data *);
+int initialize_sysfs(struct ssp_data *);
+void initialize_function_pointer(struct ssp_data *);
+void initialize_accel_factorytest(struct ssp_data *);
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+void initialize_hrm_factorytest(struct ssp_data *);
+#endif
+void initialize_gyro_factorytest(struct ssp_data *);
+void remove_accel_factorytest(struct ssp_data *);
+void remove_gyro_factorytest(struct ssp_data *);
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+void remove_hrm_factorytest(struct ssp_data *);
+#endif
+void sensors_remove_symlink(struct input_dev *);
+void destroy_sensor_class(void);
+int initialize_event_symlink(struct ssp_data *);
+int sensors_create_symlink(struct input_dev *);
+int accel_open_calibration(struct ssp_data *);
+int gyro_open_calibration(struct ssp_data *);
+int check_fwbl(struct ssp_data *);
+void remove_input_dev(struct ssp_data *);
+void remove_sysfs(struct ssp_data *);
+void remove_event_symlink(struct ssp_data *);
+int ssp_send_cmd(struct ssp_data *, char, int);
+int send_instruction(struct ssp_data *, u8, u8, u8 *, u8);
+int send_instruction_sync(struct ssp_data *, u8, u8, u8 *, u8);
+int flush(struct ssp_data *, u8);
+int get_batch_count(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_hw_offset(struct ssp_data *);
+int get_hw_offset(struct ssp_data *);
+int set_gyro_cal(struct ssp_data *);
+int set_accel_cal(struct ssp_data *);
+int set_sensor_position(struct ssp_data *);
+int set_magnetic_static_matrix(struct ssp_data *);
+void sync_sensor_state(struct ssp_data *);
+int get_msdelay(int64_t);
+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 parse_dataframe(struct ssp_data *, char *, int);
+void enable_debug_timer(struct ssp_data *);
+void disable_debug_timer(struct ssp_data *);
+int initialize_debug_timer(struct ssp_data *);
+int intialize_lpm_motion(struct ssp_data *);
+void report_acc_data(struct ssp_data *, struct sensor_value *);
+void report_gyro_data(struct ssp_data *, struct sensor_value *);
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+void report_hrm_raw_data(struct ssp_data *, struct sensor_value *);
+void report_hrm_raw_fac_data(struct ssp_data *, struct sensor_value *);
+void report_hrm_lib_data(struct ssp_data *, struct sensor_value *);
+#endif
+int print_mcu_debug(char *, int *, int);
+void report_temp_humidity_data(struct ssp_data *, struct sensor_value *);
+void report_bulk_comp_data(struct ssp_data *data);
+unsigned int get_module_rev(struct ssp_data *data);
+void reset_mcu(struct ssp_data *);
+int queue_refresh_task(struct ssp_data *data, int delay);
+void convert_acc_data(s16 *);
+int sensors_register(struct device *, void *,
+ struct device_attribute*[], char *);
+void sensors_unregister(struct device *,
+ struct device_attribute*[]);
+int ssp_charging_motion(struct ssp_data *, int);
+void report_key_event(struct ssp_data *);
+int ssp_parse_motion(struct ssp_data *, char *, int, int);
+ssize_t mcu_reset_show(struct device *, struct device_attribute *, char *);
+ssize_t mcu_dump_show(struct device *, struct device_attribute *, char *);
+ssize_t mcu_revision_show(struct device *, struct device_attribute *, char *);
+ssize_t mcu_update_ums_bin_show(struct device *,
+ struct device_attribute *, char *);
+ssize_t mcu_update_kernel_bin_show(struct device *,
+ struct device_attribute *, char *);
+ssize_t mcu_update_kernel_crashed_bin_show(struct device *,
+ struct device_attribute *, char *);
+ssize_t mcu_factorytest_store(struct device *, struct device_attribute *,
+ const char *, size_t);
+ssize_t mcu_factorytest_show(struct device *,
+ struct device_attribute *, char *);
+ssize_t mcu_model_name_show(struct device *,
+ struct device_attribute *, char *);
+ssize_t mcu_sleep_factorytest_show(struct device *,
+ struct device_attribute *, char *);
+ssize_t mcu_sleep_factorytest_store(struct device *,
+ struct device_attribute *, const char *, size_t);
+unsigned int ssp_check_sec_dump_mode(void);
+
+#ifdef CONFIG_SENSORS_SSP_STM
+void ssp_dump_task(struct work_struct *work);
+void ssp_read_big_library_task(struct work_struct *work);
+void ssp_send_big_library_task(struct work_struct *work);
+void ssp_pcm_dump_task(struct work_struct *work);
+void ssp_temp_task(struct work_struct *work);
+#endif
+int set_time(struct ssp_data *);
+int get_time(struct ssp_data *);
+#endif
diff --git a/drivers/sensorhub/stm/ssp_data.c b/drivers/sensorhub/stm/ssp_data.c
new file mode 100644
index 00000000000..a25bcf56b8a
--- /dev/null
+++ b/drivers/sensorhub/stm/ssp_data.c
@@ -0,0 +1,258 @@
+/*
+ * 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"
+
+/* 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
+
+/*************************************************************************/
+/* SSP parsing the dataframe */
+/*************************************************************************/
+
+static void get_timestamp(struct ssp_data *data, char *pchRcvDataFrame,
+ int *iDataIdx, struct sensor_value *sensorsdata)
+{
+ s32 otimestamp = 0;
+ s64 ctimestamp = 0;
+
+ memcpy(&otimestamp, pchRcvDataFrame + *iDataIdx, 4);
+ *iDataIdx += 4;
+
+ ctimestamp = (s64) otimestamp * 1000000;
+ sensorsdata->timestamp = data->timestamp + ctimestamp;
+}
+
+static void get_3axis_sensordata(char *pchRcvDataFrame, int *iDataIdx,
+ struct sensor_value *sensorsdata)
+{
+ memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 6);
+ *iDataIdx += 6;
+}
+
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+static void get_hrm_raw_sensordata(char *pchRcvDataFrame, int *iDataIdx,
+ struct sensor_value *sensorsdata)
+{
+ memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 8);
+ *iDataIdx += 8;
+}
+
+static void get_hrm_raw_fac_sensordata(char *pchRcvDataFrame, int *iDataIdx,
+ struct sensor_value *sensorsdata)
+{
+ memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 36);
+ *iDataIdx += 36;
+}
+
+static void get_hrm_lib_sensordata(char *pchRcvDataFrame, int *iDataIdx,
+ struct sensor_value *sensorsdata)
+{
+ memcpy(sensorsdata, pchRcvDataFrame + *iDataIdx, 8);
+ *iDataIdx += 8;
+}
+#endif
+
+int handle_big_data(struct ssp_data *data, char *pchRcvDataFrame, int *pDataIdx) {
+ u8 bigType = 0;
+ struct ssp_big *big = kzalloc(sizeof(*big), GFP_KERNEL);
+ big->data = data;
+ bigType = pchRcvDataFrame[(*pDataIdx)++];
+ memcpy(&big->length, pchRcvDataFrame + *pDataIdx, 4);
+ *pDataIdx += 4;
+ memcpy(&big->addr, pchRcvDataFrame + *pDataIdx, 4);
+ *pDataIdx += 4;
+
+ if (bigType >= BIG_TYPE_MAX) {
+ kfree(big);
+ return FAIL;
+ }
+
+ INIT_WORK(&big->work, data->ssp_big_task[bigType]);
+ queue_work(data->debug_wq, &big->work);
+ return SUCCESS;
+}
+
+void refresh_task(struct work_struct *work)
+{
+ struct ssp_data *data = container_of((struct delayed_work *)work,
+ struct ssp_data, work_refresh);
+
+ func_dbg();
+ data->uResetCnt++;
+ if (initialize_mcu(data) > 0) {
+ sync_sensor_state(data);
+ ssp_sensorhub_report_notice(data, MSG2SSP_AP_STATUS_RESET);
+ if (data->uLastAPState != 0)
+ ssp_send_cmd(data, data->uLastAPState, 0);
+ if (data->uLastResumeState != 0)
+ ssp_send_cmd(data, data->uLastResumeState, 0);
+ data->uTimeOutCnt = 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);
+ queue_delayed_work(data->debug_wq, &data->work_refresh,
+ msecs_to_jiffies(delay));
+ return SUCCESS;
+}
+
+int parse_dataframe(struct ssp_data *data, char *pchRcvDataFrame, int iLength)
+{
+ int iDataIdx, iSensorData;
+ u16 length = 0;
+ struct sensor_value sensorsdata;
+ struct timespec ts;
+ int iRet = FAIL;
+
+ getnstimeofday(&ts);
+
+ for (iDataIdx = 0; iDataIdx < iLength;) {
+ switch (pchRcvDataFrame[iDataIdx++]) {
+ case MSG2AP_INST_BYPASS_DATA:
+ iSensorData =
+ pchRcvDataFrame[iDataIdx++];
+ if ((iSensorData < 0) || (iSensorData >= SENSOR_MAX)) {
+ ssp_err("MCU data frame1 error %d\n", iSensorData);
+ return ERROR;
+ }
+ data->get_sensor_data[iSensorData](pchRcvDataFrame,
+ &iDataIdx, &sensorsdata);
+ get_timestamp(data, pchRcvDataFrame, &iDataIdx,
+ &sensorsdata);
+ data->report_sensor_data[iSensorData](data,
+ &sensorsdata);
+ break;
+ case MSG2AP_INST_DEBUG_DATA:
+ iSensorData =
+ print_mcu_debug(pchRcvDataFrame, &iDataIdx, iLength);
+ if (iSensorData) {
+ ssp_err("MCU data frame3 error %d\n", iSensorData);
+ return ERROR;
+ }
+ break;
+ case MSG2AP_INST_LIBRARY_DATA:
+ memcpy(&length, pchRcvDataFrame + iDataIdx, 2);
+ iDataIdx += 2;
+ if (data->bLpModeEnabled == true)
+ iRet = ssp_parse_motion(data, pchRcvDataFrame,
+ iDataIdx, iDataIdx + length);
+ if (iRet == FAIL)
+ ssp_sensorhub_handle_data(data,
+ pchRcvDataFrame, iDataIdx,
+ iDataIdx + length);
+ iDataIdx += length;
+ break;
+ case MSG2AP_INST_BIG_DATA:
+ handle_big_data(data, pchRcvDataFrame, &iDataIdx);
+ break;
+ case MSG2AP_INST_TIME_SYNC:
+ data->bTimeSyncing = true;
+ break;
+ }
+ }
+
+ if (data->bTimeSyncing)
+ data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+
+ return SUCCESS;
+}
+
+static void get_dummy_sensordata(char *pchRcvDataFrame, int *iDataIdx,
+ struct sensor_value *sensorsdata)
+{
+ ssp_dbg("not supported\n");
+}
+
+void ssp_temp_task(struct work_struct *temp)
+{
+ ssp_dbg("not supported\n");
+}
+
+void report_dummy_data(struct ssp_data *data, struct sensor_value *value)
+{
+ ssp_dbg("not supported\n");
+}
+
+void initialize_function_pointer(struct ssp_data *data)
+{
+ data->get_sensor_data[ACCELEROMETER_SENSOR] = get_3axis_sensordata;
+ data->get_sensor_data[GYROSCOPE_SENSOR] = get_3axis_sensordata;
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ data->get_sensor_data[BIO_HRM_RAW] = get_hrm_raw_sensordata;
+ data->get_sensor_data[BIO_HRM_RAW_FAC] = get_hrm_raw_fac_sensordata;
+ data->get_sensor_data[BIO_HRM_LIB] = get_hrm_lib_sensordata;
+#endif
+ data->get_sensor_data[GEOMAGNETIC_UNCALIB_SENSOR] =
+ get_dummy_sensordata;
+ data->get_sensor_data[GEOMAGNETIC_RAW] = get_dummy_sensordata;
+ data->get_sensor_data[GEOMAGNETIC_SENSOR] =
+ get_dummy_sensordata;
+ data->get_sensor_data[PRESSURE_SENSOR] = get_dummy_sensordata;
+ data->get_sensor_data[GESTURE_SENSOR] = get_dummy_sensordata;
+ data->get_sensor_data[PROXIMITY_SENSOR] = get_dummy_sensordata;
+ data->get_sensor_data[PROXIMITY_RAW] = get_dummy_sensordata;
+ data->get_sensor_data[LIGHT_SENSOR] = get_dummy_sensordata;
+ data->get_sensor_data[TEMPERATURE_HUMIDITY_SENSOR] =
+ get_dummy_sensordata;
+ data->get_sensor_data[ROTATION_VECTOR] = get_dummy_sensordata;
+ data->get_sensor_data[GAME_ROTATION_VECTOR] = get_dummy_sensordata;
+ data->get_sensor_data[STEP_DETECTOR] = get_dummy_sensordata;
+ data->get_sensor_data[SIG_MOTION_SENSOR] = get_dummy_sensordata;
+ data->get_sensor_data[GYRO_UNCALIB_SENSOR] = get_dummy_sensordata;
+ data->get_sensor_data[STEP_COUNTER] = get_dummy_sensordata;
+
+ data->report_sensor_data[ACCELEROMETER_SENSOR] = report_acc_data;
+ data->report_sensor_data[GYROSCOPE_SENSOR] = report_gyro_data;
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ data->report_sensor_data[BIO_HRM_RAW] = report_hrm_raw_data;
+ data->report_sensor_data[BIO_HRM_RAW_FAC] = report_hrm_raw_fac_data;
+ data->report_sensor_data[BIO_HRM_LIB] = report_hrm_lib_data;
+#endif
+ data->report_sensor_data[GEOMAGNETIC_UNCALIB_SENSOR] =
+ report_dummy_data;
+ data->report_sensor_data[GEOMAGNETIC_RAW] = report_dummy_data;
+ data->report_sensor_data[GEOMAGNETIC_SENSOR] =
+ report_dummy_data;
+ data->report_sensor_data[PRESSURE_SENSOR] = report_dummy_data;
+ data->report_sensor_data[GESTURE_SENSOR] = report_dummy_data;
+ data->report_sensor_data[PROXIMITY_SENSOR] = report_dummy_data;
+ data->report_sensor_data[PROXIMITY_RAW] = report_dummy_data;
+ data->report_sensor_data[LIGHT_SENSOR] = report_dummy_data;
+ data->report_sensor_data[TEMPERATURE_HUMIDITY_SENSOR] =
+ report_dummy_data;
+ data->report_sensor_data[ROTATION_VECTOR] = report_dummy_data;
+ data->report_sensor_data[GAME_ROTATION_VECTOR] = report_dummy_data;
+ data->report_sensor_data[STEP_DETECTOR] = report_dummy_data;
+ data->report_sensor_data[SIG_MOTION_SENSOR] = report_dummy_data;
+ data->report_sensor_data[GYRO_UNCALIB_SENSOR] = report_dummy_data;
+ data->report_sensor_data[STEP_COUNTER] = report_dummy_data;
+
+ data->ssp_big_task[BIG_TYPE_DUMP] = ssp_dump_task;
+ data->ssp_big_task[BIG_TYPE_READ_LIB] = ssp_read_big_library_task;
+ data->ssp_big_task[BIG_TYPE_VOICE_NET] = ssp_send_big_library_task;
+ data->ssp_big_task[BIG_TYPE_VOICE_GRAM] = ssp_send_big_library_task;
+ data->ssp_big_task[BIG_TYPE_VOICE_PCM] = ssp_pcm_dump_task;
+ data->ssp_big_task[BIG_TYPE_TEMP] = ssp_temp_task;
+}
diff --git a/drivers/sensorhub/stm/ssp_debug.c b/drivers/sensorhub/stm/ssp_debug.c
new file mode 100644
index 00000000000..f7ac63a3187
--- /dev/null
+++ b/drivers/sensorhub/stm/ssp_debug.c
@@ -0,0 +1,295 @@
+/*
+ * 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"
+#include <linux/fs.h>
+
+
+
+#define SSP_DEBUG_TIMER_SEC (10 * HZ)
+
+#define LIMIT_RESET_CNT 20
+#define LIMIT_TIMEOUT_CNT 3
+
+#define DUMP_FILE_PATH "/data/log/MCU_DUMP"
+
+void ssp_dump_task(struct work_struct *work) {
+ struct ssp_big *big;
+ struct file *dump_file;
+ struct ssp_msg *msg;
+ char *buffer;
+ char strFilePath[60];
+ struct timeval cur_time;
+ int iTimeTemp;
+ mm_segment_t fs;
+ int buf_len, packet_len, residue;
+ int iRet = 0, index = 0, iRetTrans = 0, iRetWrite = 0;
+
+ big = container_of(work, struct ssp_big, work);
+ ssp_info("start ssp dumping (%d)(%d)\n",
+ big->data->bMcuDumpMode, big->data->uDumpCnt);
+ big->data->uDumpCnt++;
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ if (big->data->bMcuDumpMode == true) {
+ do_gettimeofday(&cur_time);
+ iTimeTemp = (int) cur_time.tv_sec;
+
+ sprintf(strFilePath, "%s%d.txt", DUMP_FILE_PATH, iTimeTemp);
+
+ dump_file = filp_open(strFilePath,
+ O_RDWR | O_CREAT | O_APPEND, 0666);
+ if (IS_ERR(dump_file)) {
+ ssp_err("Can't open dump file\n");
+ set_fs(fs);
+ iRet = PTR_ERR(dump_file);
+ kfree(big);
+ return;
+ }
+ } else
+ dump_file = NULL;
+
+ buf_len = big->length > DATA_PACKET_SIZE ? DATA_PACKET_SIZE : big->length;
+ buffer = kzalloc(buf_len, GFP_KERNEL);
+ residue = big->length;
+
+ while (residue > 0) {
+ packet_len = residue > DATA_PACKET_SIZE ? DATA_PACKET_SIZE : residue;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ msg->cmd = MSG2SSP_AP_GET_BIG_DATA;
+ msg->length = packet_len;
+ msg->options = AP2HUB_READ | (index++ << SSP_INDEX);
+ msg->data = big->addr;
+ msg->buffer = buffer;
+ msg->free_buffer = 0;
+
+ iRetTrans = ssp_spi_sync(big->data, msg, 1000);
+ if (iRetTrans != SUCCESS) {
+ ssp_err("Fail to receive data %d (%d)\n", iRetTrans, residue);
+ break;
+ }
+ if (big->data->bMcuDumpMode == true) {
+ iRetWrite = vfs_write(dump_file,
+ (char __user *)buffer, packet_len,
+ &dump_file->f_pos);
+ if (iRetWrite < 0) {
+ ssp_err("Can't write dump to file\n");
+ break;
+ }
+ }
+ residue -= packet_len;
+ }
+
+ if (big->data->bMcuDumpMode == true &&
+ (iRetTrans != SUCCESS || iRetWrite < 0)) {
+ char FAILSTRING[100];
+ sprintf(FAILSTRING, "FAIL OCCURED(%d)(%d)(%d)",
+ iRetTrans, iRetWrite, big->length);
+ vfs_write(dump_file, (char __user *) FAILSTRING,
+ strlen(FAILSTRING), &dump_file->f_pos);
+ }
+
+ big->data->bDumping = false;
+ if(big->data->bMcuDumpMode == true)
+ filp_close(dump_file, current->files);
+
+ set_fs(fs);
+
+ kfree(buffer);
+ kfree(big);
+
+ ssp_info("done\n");
+}
+
+/*
+ SSP Debug timer function
+*/
+
+int print_mcu_debug(char *pchRcvDataFrame, int *pDataIdx,
+ int iRcvDataFrameLength)
+{
+ int iLength = pchRcvDataFrame[(*pDataIdx)++];
+
+ if (iLength > iRcvDataFrameLength - *pDataIdx || iLength <= 0) {
+ ssp_dbg("MSG From MCU - invalid debug length(%d/%d/%d)\n",
+ iLength, iRcvDataFrameLength, cur);
+ return iLength ? iLength : ERROR;
+ }
+
+ ssp_dbg("MSG From MCU - %s\n", &pchRcvDataFrame[*pDataIdx]);
+ *pDataIdx += iLength;
+ return 0;
+}
+
+void reset_mcu(struct ssp_data *data)
+{
+ func_dbg();
+ ssp_enable(data, false);
+ clean_pending_list(data);
+ toggle_mcu_reset(data);
+ ssp_enable(data, true);
+}
+
+void sync_sensor_state(struct ssp_data *data)
+{
+ unsigned char uBuf[9] = {0,};
+ unsigned int uSensorCnt;
+ int iRet = 0;
+
+ iRet = set_gyro_cal(data);
+ if (iRet < 0)
+ ssp_err("set_gyro_cal failed\n");
+
+ iRet = set_accel_cal(data);
+ if (iRet < 0)
+ ssp_err("set_accel_cal failed\n");
+
+ udelay(10);
+
+ for (uSensorCnt = 0; uSensorCnt < (SENSOR_MAX); uSensorCnt++) {
+ if (atomic_read(&data->aSensorEnable) & (1 << uSensorCnt)) {
+ s32 dMsDelay =
+ get_msdelay(data->adDelayBuf[uSensorCnt]);
+
+ memcpy(&uBuf[0], &dMsDelay, 4);
+ memcpy(&uBuf[4], &data->batchLatencyBuf[uSensorCnt], 4);
+ uBuf[8] = data->batchOptBuf[uSensorCnt];
+ send_instruction(data, ADD_SENSOR, uSensorCnt, uBuf, 9);
+ udelay(10);
+ }
+ }
+
+ data->bMcuDumpMode = ssp_check_sec_dump_mode();
+ iRet = ssp_send_cmd(data, MSG2SSP_AP_MCU_SET_DUMPMODE,
+ data->bMcuDumpMode);
+ if (iRet < 0) {
+ ssp_err("MSG2SSP_AP_MCU_SET_DUMPMODE failed\n");
+ }
+}
+
+static void print_sensordata(struct ssp_data *data, unsigned int uSensor)
+{
+ switch (uSensor) {
+ case ACCELEROMETER_SENSOR:
+ case GYROSCOPE_SENSOR:
+ case GEOMAGNETIC_SENSOR:
+ ssp_dbg("%u : %d, %d, %d (%ums)\n", uSensor,
+ data->buf[uSensor].x, data->buf[uSensor].y,
+ data->buf[uSensor].z,
+ get_msdelay(data->adDelayBuf[uSensor]));
+ break;
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ case BIO_HRM_RAW:
+ case BIO_HRM_RAW_FAC:
+ ssp_dbg("%u : %d, %d (%ums)\n", uSensor,
+ data->buf[uSensor].ch_a , data->buf[uSensor].ch_b,
+ get_msdelay(data->adDelayBuf[uSensor]));
+ break;
+
+ case BIO_HRM_LIB:
+ ssp_dbg("%u : %d, %d, %d (%ums)\n", uSensor,
+ data->buf[uSensor].hr , data->buf[uSensor].rri,
+ data->buf[uSensor].snr,
+ get_msdelay(data->adDelayBuf[uSensor]));
+ break;
+#endif
+ default:
+ ssp_err("Wrong sensorCnt: %u\n", uSensor);
+ break;
+ }
+}
+
+static void debug_work_func(struct work_struct *work)
+{
+ unsigned int uSensorCnt;
+ struct ssp_data *data = container_of(work, struct ssp_data, work_debug);
+
+ ssp_dbg("(%u) - Sensor state: 0x%x, RC: %u, CC: %u DC: %u\n",
+ data->uIrqCnt, data->uSensorState, data->uResetCnt,
+ data->uComFailCnt,data->uDumpCnt);
+
+ switch (data->fw_dl_state) {
+ case FW_DL_STATE_FAIL:
+ case FW_DL_STATE_DOWNLOADING:
+ case FW_DL_STATE_SYNC:
+ ssp_dbg("firmware downloading state = %d\n", data->fw_dl_state);
+ return;
+ }
+
+ for (uSensorCnt = 0; uSensorCnt < (SENSOR_MAX); uSensorCnt++)
+ if (atomic_read(&data->aSensorEnable) & (1 << uSensorCnt))
+ print_sensordata(data, uSensorCnt);
+
+ if (data->uTimeOutCnt > LIMIT_TIMEOUT_CNT) {
+ if (data->uComFailCnt < LIMIT_RESET_CNT) {
+ ssp_dbg("uTimeOutCnt(%u), pending(%u)\n",
+ data->uTimeOutCnt, !list_empty(&data->pending_list));
+ data->uComFailCnt++;
+ reset_mcu(data);
+ } else
+ ssp_enable(data, false);
+ data->uTimeOutCnt = 0;
+ }
+
+ data->uIrqCnt = 0;
+}
+
+static void debug_timer_func(unsigned long ptr)
+{
+ struct ssp_data *data = (struct ssp_data *)ptr;
+
+ queue_work(data->debug_wq, &data->work_debug);
+ mod_timer(&data->debug_timer,
+ round_jiffies_up(jiffies + SSP_DEBUG_TIMER_SEC));
+}
+
+void enable_debug_timer(struct ssp_data *data)
+{
+ mod_timer(&data->debug_timer,
+ round_jiffies_up(jiffies + SSP_DEBUG_TIMER_SEC));
+}
+
+void disable_debug_timer(struct ssp_data *data)
+{
+ del_timer_sync(&data->debug_timer);
+ cancel_work_sync(&data->work_debug);
+}
+
+int initialize_debug_timer(struct ssp_data *data)
+{
+ setup_timer(&data->debug_timer, debug_timer_func,
+ (unsigned long)data);
+
+ data->debug_wq = create_singlethread_workqueue("ssp_debug_wq");
+ if (!data->debug_wq)
+ return ERROR;
+
+ INIT_WORK(&data->work_debug, debug_work_func);
+ return SUCCESS;
+}
+
+unsigned int ssp_check_sec_dump_mode()
+{
+ /* if returns true dump mode on */
+ /*
+ if (sec_debug_level.en.kernel_fault == 1)
+ return 1;
+ else
+ return 0;
+ */
+ return 0;
+}
diff --git a/drivers/sensorhub/stm/ssp_dev.c b/drivers/sensorhub/stm/ssp_dev.c
new file mode 100644
index 00000000000..d551aa84b76
--- /dev/null
+++ b/drivers/sensorhub/stm/ssp_dev.c
@@ -0,0 +1,675 @@
+/*
+ * 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"
+#include <linux/of_gpio.h>
+
+extern unsigned int system_rev;
+
+u8 ssp_magnetic_pdc[] = {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};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ssp_early_suspend(struct early_suspend *handler);
+static void ssp_late_resume(struct early_suspend *handler);
+#endif
+
+void ssp_enable(struct ssp_data *data, bool enable)
+{
+ ssp_dbg("enable = %d, old enable = %d\n",enable, data->bSspShutdown);
+
+ if (enable && data->bSspShutdown) {
+ data->bSspShutdown = false;
+ enable_irq(data->iIrq);
+ enable_irq_wake(data->iIrq);
+ } else if (!enable && !data->bSspShutdown) {
+ data->bSspShutdown = true;
+ disable_irq(data->iIrq);
+ disable_irq_wake(data->iIrq);
+ } else {
+ ssp_err("error / enable = %d, old enable = %d\n",
+ enable, data->bSspShutdown);
+ }
+}
+/************************************************************************/
+/* interrupt happened due to transition/change of SSP MCU */
+/************************************************************************/
+
+static irqreturn_t sensordata_irq_thread_fn(int iIrq, void *dev_id)
+{
+ struct ssp_data *data = dev_id;
+
+ select_irq_msg(data);
+ data->uIrqCnt++;
+
+ return IRQ_HANDLED;
+}
+
+/*************************************************************************/
+/* initialize sensor hub */
+/*************************************************************************/
+
+static void initialize_variable(struct ssp_data *data)
+{
+ int iSensorIndex;
+
+ for (iSensorIndex = 0; iSensorIndex < SENSOR_MAX; iSensorIndex++) {
+ data->adDelayBuf[iSensorIndex] = DEFUALT_POLLING_DELAY;
+ data->batchLatencyBuf[iSensorIndex] = 0;
+ data->batchOptBuf[iSensorIndex] = 0;
+ data->aiCheckStatus[iSensorIndex] = INITIALIZATION_STATE;
+ }
+
+ data->adDelayBuf[BIO_HRM_LIB] = (100 * NSEC_PER_MSEC);
+
+ atomic_set(&data->aSensorEnable, 0);
+ data->iLibraryLength = 0;
+ data->uSensorState = 0;
+
+ data->uResetCnt = 0;
+ data->uTimeOutCnt = 0;
+ data->uComFailCnt = 0;
+ data->uIrqCnt = 0;
+
+ data->bSspShutdown = true;
+ data->bAccelAlert = false;
+ data->bLpModeEnabled = false;
+ data->bTimeSyncing = true;
+
+ data->accelcal.x = 0;
+ data->accelcal.y = 0;
+ data->accelcal.z = 0;
+
+ data->gyrocal.x = 0;
+ data->gyrocal.y = 0;
+ data->gyrocal.z = 0;
+
+ data->uGyroDps = GYROSCOPE_DPS500;
+ data->uIr_Current = DEFUALT_IR_CURRENT;
+
+ data->mcu_device = NULL;
+ data->acc_device = NULL;
+ data->gyro_device = NULL;
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ data->hrm_device = NULL;
+#endif
+
+ data->voice_device = NULL;
+ data->bMcuDumpMode = ssp_check_sec_dump_mode();
+ INIT_LIST_HEAD(&data->pending_list);
+
+ initialize_function_pointer(data);
+}
+
+int initialize_mcu(struct ssp_data *data)
+{
+ int iRet = 0;
+
+ clean_pending_list(data);
+
+ iRet = get_chipid(data);
+ ssp_info("MCU device ID = %d, reading ID = %d\n",
+ DEVICE_ID, iRet);
+ if (iRet != DEVICE_ID) {
+ if (iRet < 0) {
+ ssp_err("MCU is not working : 0x%x\n", iRet);
+ } else {
+ ssp_err("MCU identification failed\n");
+ iRet = -ENODEV;
+ }
+ goto out;
+ }
+
+ iRet = set_sensor_position(data);
+ if (iRet < 0) {
+ ssp_err("set_sensor_position failed\n");
+ goto out;
+ }
+
+ iRet = set_magnetic_static_matrix(data);
+ if (iRet < 0)
+ ssp_err("set_magnetic_static_matrix failed\n");
+
+ iRet = get_fuserom_data(data);
+ if (iRet < 0)
+ ssp_err("get_fuserom_data failed\n");
+
+ data->uSensorState = get_sensor_scanning_info(data);
+ if (data->uSensorState == 0) {
+ ssp_err("get_sensor_scanning_info failed\n");
+ iRet = ERROR;
+ goto out;
+ }
+ data->uCurFirmRev = get_firmware_rev(data);
+ ssp_info("MCU Firm Rev : New = %8u\n", data->uCurFirmRev);
+
+ iRet = ssp_send_cmd(data, MSG2SSP_AP_MCU_DUMP_CHECK, 0);
+out:
+ return iRet;
+}
+
+static int initialize_irq(struct ssp_data *data)
+{
+ int iRet;
+
+ data->iIrq = gpio_to_irq(data->mcu_int1);
+ if (data->iIrq < 0) {
+ ssp_err("Failed to requesting IRQ\n");
+ return -ENXIO;
+ }
+
+ iRet = request_threaded_irq(data->iIrq, NULL, sensordata_irq_thread_fn,
+ IRQF_TRIGGER_FALLING|IRQF_ONESHOT,
+ "SSP_Int", data);
+ if (iRet < 0) {
+ ssp_err("request_irq(%d) failed for gpio %d (%d)\n",
+ data->iIrq, data->iIrq, iRet);
+ goto err_request_irq;
+ }
+
+ /* start with interrupts disabled */
+ disable_irq(data->iIrq);
+ return 0;
+
+err_request_irq:
+ return iRet;
+}
+
+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 iRet;
+
+ ssp_info("firmware update...\n");
+
+ iRet = forced_to_download_binary(data, KERNEL_BINARY);
+ if (iRet < 0) {
+ ssp_err("forced_to_download_binary failed!\n");
+ return;
+ }
+
+ queue_refresh_task(data, SSP_SW_RESET_TIME);
+
+ if (data->check_lpmode() == true) {
+ data->bLpModeEnabled = true;
+ ssp_dbg("LPM Charging...\n");
+ } else {
+ data->bLpModeEnabled = false;
+ ssp_dbg("Normal Booting OK\n");
+ }
+
+ ssp_info("firmware update done!\n");
+}
+
+static int check_ap_rev(void)
+{
+ return system_rev;
+}
+
+static int ssp_check_lpmode(void)
+{
+ /* FIXME: sensor-hub must check lpmode for low-power mode.
+ * but, the following code was hardcoded. So, following
+ * comment have to be modified after resolving the hardcoded issue.
+ */
+ /*
+ if (lpcharge == 0x01)
+ return true;
+ else
+ return false;
+ */
+ return false;
+}
+
+static void ssp_get_positions(int *acc, int *mag)
+{
+ *acc = 7;
+ *mag = 0;
+
+ ssp_dbg("position acc : %d, mag = %d\n", *acc, *mag);
+}
+
+static int initialize_gpio(struct device *dev, struct ssp_data *pdata)
+{
+ int ret;
+
+ ret = devm_gpio_request_one(dev,
+ pdata->mcu_int1, GPIOF_IN, "mcu-ap-int1");
+ if (ret) {
+ ssp_err("Cannot request mcu-int1 gpio\n");
+ return ret;
+ }
+
+ ret = devm_gpio_request_one(dev,
+ pdata->mcu_int2, GPIOF_IN, "mcu-ap-int2");
+ if (ret) {
+ ssp_err("Cannot request mcu-int2 gpio\n");
+ return ret;
+ }
+
+ ret = devm_gpio_request_one(dev,
+ pdata->ap_int, GPIOF_OUT_INIT_HIGH, "ap-mcu-int");
+ if (ret) {
+ ssp_err("Cannot request ap-int gpio\n");
+ return ret;
+ }
+
+ ret = devm_gpio_request_one(dev,
+ pdata->rst, GPIOF_OUT_INIT_HIGH, "mcu-reset");
+ if (ret) {
+ ssp_err("Cannot request rst gpio\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ssp_parse_dt(struct device *dev, struct ssp_data *pdata)
+{
+ struct device_node *node = dev->of_node;
+
+ pdata->mcu_int1 = of_get_named_gpio(node, "mcu-ap-int1", 0);
+ if (pdata->mcu_int1 < 0) {
+ ssp_err("Cannot get mcu-ap-int1 gpio\n");
+ return -EINVAL;
+ }
+
+ pdata->mcu_int2 = of_get_named_gpio(node, "mcu-ap-int2", 0);
+ if (pdata->mcu_int2 < 0) {
+ ssp_err("Cannot get mcu-ap-int2 gpio\n");
+ return -EINVAL;
+ }
+
+ pdata->ap_int = of_get_named_gpio(node, "ap-mcu-int", 0);
+ if (pdata->ap_int < 0) {
+ ssp_err("Cannot get ap-mcu-int gpio\n");
+ return -EINVAL;
+ }
+
+ pdata->rst = of_get_named_gpio(node, "mcu-reset", 0);
+ if (pdata->rst < 0) {
+ ssp_err("Cannot get mcu-reset gpio\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ssp_probe(struct spi_device *spi)
+{
+ struct ssp_data *data;
+ struct ssp_platform_data *pdata;
+ int iRet = 0;
+
+ ssp_info(" is called\n");
+
+ data = devm_kzalloc(&spi->dev, sizeof(struct ssp_data), GFP_KERNEL);
+ if (!data) {
+ ssp_err("failed to allocate memory for data\n");
+ goto err_setup;
+ }
+
+ if (spi->dev.of_node) {
+ iRet = ssp_parse_dt(&spi->dev, data);
+ if (iRet) {
+ ssp_err("Failed to parse dt\n");
+ goto err_setup;
+ }
+
+ iRet = initialize_gpio(&spi->dev, data);
+ if (iRet) {
+ ssp_err("Failed to initialize gpio\n");
+ goto err_setup;
+ }
+
+ data->check_lpmode = ssp_check_lpmode;
+ data->ap_rev = check_ap_rev();
+ ssp_info("ap_rev = %d", data->ap_rev);
+
+ /* Get sensor positon */
+ ssp_get_positions(&data->accel_position, &data->mag_position);
+ data->mag_matrix_size = ARRAY_SIZE(ssp_magnetic_pdc);
+ data->mag_matrix = ssp_magnetic_pdc;
+
+ } else {
+ pdata = spi->dev.platform_data;
+ if (pdata == NULL) {
+ ssp_err("platform_data is null\n");
+ return -ENOMEM;
+ }
+
+ data->rst = pdata->rst;
+ data->ap_int = pdata->ap_int;
+ data->mcu_int1 = pdata->mcu_int1;
+ data->mcu_int2 = pdata->mcu_int2;
+ data->iIrq = pdata->irq;
+ data->check_lpmode = pdata->check_lpmode;
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ data->hrm_sensor_power = pdata->hrm_sensor_power;
+#endif
+
+ ssp_dbg("rst = %d, ap_int = %d,"\
+ " mcu_int1 = %d, mcu_int2 = %d\n",
+ (int)data->rst, (int)data->ap_int,
+ (int)data->mcu_int1, (int)data->mcu_int2);
+
+ /* AP system_rev */
+ if (pdata->check_ap_rev)
+ data->ap_rev = pdata->check_ap_rev();
+ else
+ data->ap_rev = 0;
+ ssp_info("system Rev = 0x%x\n", data->ap_rev);
+
+ /* Get sensor positions */
+ if (pdata->get_positions) {
+ pdata->get_positions(&data->accel_position,
+ &data->mag_position);
+ } else {
+ data->accel_position = 0;
+ data->mag_position = 0;
+ }
+ if (pdata->mag_matrix) {
+ data->mag_matrix_size = pdata->mag_matrix_size;
+ data->mag_matrix = pdata->mag_matrix;
+ }
+ }
+
+ spi->mode = SPI_MODE_1;
+ if (spi_setup(spi)) {
+ ssp_err("failed to setup spi\n");
+ goto err_setup;
+ }
+
+ data->bProbeIsDone = false;
+ data->fw_dl_state = FW_DL_STATE_NONE;
+ data->spi = spi;
+ spi_set_drvdata(spi, data);
+
+#ifdef CONFIG_SENSORS_SSP_STM
+ mutex_init(&data->comm_mutex);
+ mutex_init(&data->pending_mutex);
+#endif
+
+ initialize_variable(data);
+ INIT_DELAYED_WORK(&data->work_firmware, work_function_firmware_update);
+
+ iRet = initialize_input_dev(data);
+ if (iRet < 0) {
+ ssp_err("could not create input device\n");
+ goto err_input_register_device;
+ }
+
+ iRet = initialize_debug_timer(data);
+ if (iRet < 0) {
+ ssp_err("could not create workqueue\n");
+ goto err_create_workqueue;
+ }
+
+ iRet = intialize_lpm_motion(data);
+ if (iRet < 0) {
+ ssp_err("could not create workqueue\n");
+ goto err_create_lpm_motion;
+ }
+
+ iRet = initialize_irq(data);
+ if (iRet < 0) {
+ ssp_err("could not create irq\n");
+ goto err_setup_irq;
+ }
+
+ iRet = initialize_sysfs(data);
+ if (iRet < 0) {
+ ssp_err("could not create sysfs\n");
+ goto err_sysfs_create;
+ }
+
+ iRet = initialize_event_symlink(data);
+ if (iRet < 0) {
+ ssp_err("could not create symlink\n");
+ goto err_symlink_create;
+ }
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ /* init sensorhub device */
+ iRet = ssp_sensorhub_initialize(data);
+ if (iRet < 0) {
+ ssp_err("ssp_sensorhub_initialize err(%d)", iRet);
+ ssp_sensorhub_remove(data);
+ }
+#endif
+
+ ssp_enable(data, true);
+ /* check boot loader binary */
+ data->fw_dl_state = check_fwbl(data);
+
+ if (data->fw_dl_state == FW_DL_STATE_NONE) {
+ iRet = initialize_mcu(data);
+ if (iRet == ERROR) {
+ toggle_mcu_reset(data);
+ } else if (iRet < ERROR) {
+ ssp_err("initialize_mcu failed\n");
+ goto err_read_reg;
+ }
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ data->early_suspend.suspend = ssp_early_suspend;
+ data->early_suspend.resume = ssp_late_resume;
+ register_early_suspend(&data->early_suspend);
+#endif
+
+ ssp_info("probe success!\n");
+
+ enable_debug_timer(data);
+
+ if (data->fw_dl_state == FW_DL_STATE_NEED_TO_SCHEDULE) {
+ ssp_info("Firmware update is scheduled\n");
+ schedule_delayed_work(&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->bSspShutdown = true;
+ }
+
+ data->bProbeIsDone = true;
+ iRet = 0;
+
+ if (data->check_lpmode() == true) {
+ ssp_charging_motion(data, 1);
+ data->bLpModeEnabled = true;
+ ssp_dbg("LPM Charging...\n");
+ } else {
+ data->bLpModeEnabled = false;
+ ssp_dbg("Normal Booting OK\n");
+ }
+
+ goto exit;
+
+err_read_reg:
+err_symlink_create:
+ remove_sysfs(data);
+err_sysfs_create:
+ free_irq(data->iIrq, data);
+err_setup_irq:
+ destroy_workqueue(data->lpm_motion_wq);
+err_create_lpm_motion:
+ destroy_workqueue(data->debug_wq);
+err_create_workqueue:
+ remove_input_dev(data);
+err_input_register_device:
+#ifdef CONFIG_SENSORS_SSP_STM
+ mutex_destroy(&data->comm_mutex);
+ mutex_destroy(&data->pending_mutex);
+#endif
+
+err_setup:
+ ssp_err("probe failed!\n");
+exit:
+ return iRet;
+}
+
+static void ssp_shutdown(struct spi_device *spi)
+{
+ struct ssp_data *data = spi_get_drvdata(spi);
+
+ func_dbg();
+ if (data->bProbeIsDone == false)
+ goto exit;
+
+ if (data->fw_dl_state >= FW_DL_STATE_SCHEDULED &&
+ data->fw_dl_state < FW_DL_STATE_DONE) {
+ ssp_err("cancel_delayed_work_sync state = %d\n",
+ data->fw_dl_state);
+ cancel_delayed_work_sync(&data->work_firmware);
+ }
+
+ if (SUCCESS != ssp_send_cmd(data, MSG2SSP_AP_STATUS_SHUTDOWN, 0))
+ ssp_err("MSG2SSP_AP_STATUS_SHUTDOWN failed\n");
+
+ ssp_enable(data, false);
+ disable_debug_timer(data);
+
+ clean_pending_list(data);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&data->early_suspend);
+#endif
+
+ free_irq(data->iIrq, data);
+
+ remove_event_symlink(data);
+ remove_sysfs(data);
+ remove_input_dev(data);
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ ssp_sensorhub_remove(data);
+#endif
+
+ del_timer_sync(&data->debug_timer);
+ cancel_work_sync(&data->work_debug);
+ cancel_work_sync(&data->work_lpm_motion);
+ destroy_workqueue(data->lpm_motion_wq);
+ destroy_workqueue(data->debug_wq);
+
+#ifdef CONFIG_SENSORS_SSP_STM
+ mutex_destroy(&data->comm_mutex);
+ mutex_destroy(&data->pending_mutex);
+#endif
+ toggle_mcu_reset(data);
+ ssp_info(" done\n");
+exit:
+ kfree(data);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ssp_early_suspend(struct early_suspend *handler)
+{
+ struct ssp_data *data;
+ data = container_of(handler, struct ssp_data, early_suspend);
+
+ func_dbg();
+ disable_debug_timer(data);
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ /* give notice to user that AP goes to sleep */
+ ssp_sensorhub_report_notice(data, MSG2SSP_AP_STATUS_SLEEP);
+ ssp_sleep_mode(data);
+ data->uLastAPState = MSG2SSP_AP_STATUS_SLEEP;
+#else
+ if (atomic_read(&data->aSensorEnable) > 0)
+ ssp_sleep_mode(data);
+#endif
+}
+
+static void ssp_late_resume(struct early_suspend *handler)
+{
+ struct ssp_data *data;
+ data = container_of(handler, struct ssp_data, early_suspend);
+
+ func_dbg();
+ enable_debug_timer(data);
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ /* give notice to user that AP goes to sleep */
+ ssp_sensorhub_report_notice(data, MSG2SSP_AP_STATUS_WAKEUP);
+ ssp_resume_mode(data);
+ data->uLastAPState = MSG2SSP_AP_STATUS_WAKEUP;
+#else
+ if (atomic_read(&data->aSensorEnable) > 0)
+ ssp_resume_mode(data);
+#endif
+}
+
+#else /* no early suspend */
+
+static int ssp_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct ssp_data *data = spi_get_drvdata(spi);
+
+ func_dbg();
+ data->uLastResumeState = MSG2SSP_AP_STATUS_SUSPEND;
+ disable_debug_timer(data);
+
+ if (SUCCESS != ssp_send_cmd(data, MSG2SSP_AP_STATUS_SUSPEND, 0))
+ ssp_err("MSG2SSP_AP_STATUS_SUSPEND failed\n");
+ data->bTimeSyncing = false;
+ disable_irq(data->iIrq);
+ return 0;
+}
+
+static int ssp_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct ssp_data *data = spi_get_drvdata(spi);
+ enable_irq(data->iIrq);
+ func_dbg();
+ enable_debug_timer(data);
+
+ if (SUCCESS != ssp_send_cmd(data, MSG2SSP_AP_STATUS_RESUME, 0))
+ ssp_err("MSG2SSP_AP_STATUS_RESUME failed\n");
+ data->uLastResumeState = MSG2SSP_AP_STATUS_RESUME;
+
+ return 0;
+}
+
+static const struct dev_pm_ops ssp_pm_ops = {
+ .suspend = ssp_suspend,
+ .resume = ssp_resume
+};
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+static struct of_device_id ssp_of_match[] = {
+ { .compatible = "samsung,ssp-spi", },
+};
+
+static struct spi_driver ssp_driver = {
+ .probe = ssp_probe,
+ .shutdown = ssp_shutdown,
+ .driver = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .pm = &ssp_pm_ops,
+#endif
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ssp_of_match),
+ .name = "ssp-spi"
+ },
+};
+
+module_spi_driver(ssp_driver);
+MODULE_DESCRIPTION("ssp spi driver");
+MODULE_AUTHOR("Samsung Electronics");
+MODULE_LICENSE("GPL");
diff --git a/drivers/sensorhub/stm/ssp_firmware.c b/drivers/sensorhub/stm/ssp_firmware.c
new file mode 100644
index 00000000000..d692a1f90e6
--- /dev/null
+++ b/drivers/sensorhub/stm/ssp_firmware.c
@@ -0,0 +1,822 @@
+/*
+ * 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_FIRMWARE_REVISION_STM 14052300
+
+#define BOOT_SPI_HZ 500000
+#define NORM_SPI_HZ 5000000
+
+/* Bootload mode cmd */
+#define BL_FW_NAME "ssp_B2.fw"
+#define BL_UMS_FW_NAME "ssp_B2.bin"
+#define BL_CRASHED_FW_NAME "ssp_crashed.fw"
+
+#define BL_UMS_FW_PATH 255
+
+#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
+
+/* STM */
+#define STM_SHOULD_BE_IMPLEMENT 0
+
+#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
+#define BYTETOBYTE_USED 0
+
+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;
+};
+
+unsigned int get_module_rev(struct ssp_data *data)
+{
+ return SSP_FIRMWARE_REVISION_STM;
+}
+
+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;
+
+ ssp_dbg("dummy byte = 0x%02hhx\n", dummy_bytes);
+
+ 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) {
+ ssp_err("spi_sync error returned %d\n", 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++;
+ }
+ ssp_err("Timeout after %d loops\n", 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;
+#if BYTETOBYTE_USED
+ int i;
+ struct spi_transfer t[STM_MAX_BUFFER_SIZE];
+ memset(t, 0, STM_MAX_BUFFER_SIZE * sizeof(struct spi_transfer));
+#else
+ struct spi_transfer t = {
+ .tx_buf = tx_buf,
+ .rx_buf = rx_buf,
+ .len = 3,
+ .bits_per_word = 8,
+ };
+#endif
+
+ spi_message_init(&m);
+
+ tx_buf[0] = BL_SPI_SOF;
+ tx_buf[1] = cmd->cmd;
+ tx_buf[2] = cmd->xor_cmd;
+
+#if BYTETOBYTE_USED
+ for (i = 0; i < 3; i++) {
+ t[i].tx_buf = &tx_buf[i];
+ t[i].rx_buf = &rx_buf[i];
+ t[i].len = 1;
+ t[i].bits_per_word = 8;
+ t[i].delay_usecs = BYTE_DELAY_WRITE;
+ spi_message_add_tail(&t[i], &m);
+ }
+#else
+ spi_message_add_tail(&t, &m);
+#endif
+
+ ret = spi_sync(spi, &m);
+ if (ret < 0) {
+ ssp_err("spi_sync error returned %d\n", 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) {
+ ssp_err("Got NAK or Error %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+#if STM_SHOULD_BE_IMPLEMENT
+static int stm32fwu_spi_read(struct spi_device *spi,
+ u8 *buffer, ssize_t len)
+{
+ int ret;
+ int i;
+ u8 tx_buf[STM_MAX_BUFFER_SIZE] = {0,};
+ struct spi_message m;
+ struct spi_transfer t[STM_MAX_BUFFER_SIZE];
+
+ memset(t, 0, STM_MAX_BUFFER_SIZE * sizeof(struct spi_transfer));
+
+ spi_message_init(&m);
+
+ for (i = 0; i < len; i++) {
+ t[i].tx_buf = tx_buf;
+ t[i].rx_buf = &buffer[i];
+ t[i].len = 1;
+ t[i].bits_per_word = 8;
+ t[i].delay_usecs = BYTE_DELAY_READ;
+ spi_message_add_tail(&t[i], &m);
+ }
+
+ ret = spi_sync(spi, &m);
+ if (ret < 0) {
+ ssp_err("spi_sync error returned %d\n", ret);
+ return ret;
+ }
+
+ return len;
+}
+#endif
+
+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;
+#if BYTETOBYTE_USED
+ struct spi_transfer t[STM_MAX_BUFFER_SIZE];
+ memset(t, 0, STM_MAX_BUFFER_SIZE * sizeof(struct spi_transfer));
+ int i;
+#else
+ struct spi_transfer t = {
+ .tx_buf = buffer,
+ .rx_buf = rx_buf,
+ .len = len,
+ .bits_per_word = 8,
+ };
+#endif
+
+ spi_message_init(&m);
+
+#if BYTETOBYTE_USED
+ for (i = 0; i < len; i++) {
+ t[i].tx_buf = &buffer[i];
+ t[i].rx_buf = &rx_buf[i];
+ t[i].len = 1;
+ t[i].bits_per_word = 8;
+ t[i].delay_usecs = BYTE_DELAY_WRITE;
+ spi_message_add_tail(&t[i], &m);
+ }
+#else
+ spi_message_add_tail(&t, &m);
+#endif
+
+ ret = spi_sync(spi, &m);
+ if (ret < 0) {
+ ssp_err("spi_sync error returned %d\n", 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) {
+ ssp_err("spi_write returned %d\n", res);
+ return ((res > 0) ? -EIO : res);
+ }
+
+ res = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, BL_ACK);
+ if (res != BL_ACK) {
+ ssp_err("rcv_ack returned 0x%x\n", res);
+ return res;
+ }
+ return 0;
+}
+
+#if STM_SHOULD_BE_IMPLEMENT
+static int send_byte_count(struct spi_device *spi, int bytes, int get_ack)
+{
+ int res;
+ uchar bbuff[3];
+ struct stm32fwu_spi_cmd dummy_cmd;
+
+ if (bytes > 256)
+ return -EINVAL;
+
+ bbuff[0] = bytes - 1;
+ bbuff[1] = ~bbuff[0];
+
+ res = stm32fwu_spi_write(spi, bbuff, 2);
+ if (res < 2)
+ return -EPROTO;
+
+ if (get_ack) {
+ dummy_cmd.timeout = DEF_ACKROOF_NUMBER;
+ res = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, BL_ACK);
+ if (res != BL_ACK)
+ return -EPROTO;
+ }
+ return 0;
+}
+
+static int fw_read_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_RMEM_COMMAND;
+ cmd.timeout = DEF_ACKCMD_NUMBER;
+ cmd.ack_pad = (u8)((fw_addr >> 24) & 0xFF);
+
+ res = stm32fwu_spi_send_cmd(spi, &cmd);
+ if (res != BL_ACK) {
+ ssp_err("spi_send_cmd returned %d\n", 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) {
+ ssp_err("send_addr returned %d\n", res);
+ return res;
+ }
+
+ res = send_byte_count(spi, len, 1);
+ if (res != 0)
+ return -EPROTO;
+
+ res = stm32fwu_spi_read(spi, buffer, len);
+ if (res < len)
+ return -EIO;
+
+ return len;
+}
+#endif
+
+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);
+ ssp_dbg("sending WMEM_COMMAND\n");
+
+ if (len > STM_MAX_XFER_SIZE) {
+ ssp_err("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) {
+ ssp_err("spi_send_cmd returned %d\n", 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) {
+ ssp_err("send_addr returned %d\n", res);
+ return res;
+ }
+
+ res = stm32fwu_spi_write(spi, send_buff, len + 2);
+ if (res < len) {
+ ssp_err("spi_write returned %d\n", res);
+ return ((res > 0) ? -EIO : res);
+ }
+
+ dummy_cmd.timeout = DEF_ACKROOF_NUMBER;
+ usleep_range(100, 150); /* Samsung added */
+ res = stm32fwu_spi_wait_for_ack(spi, &dummy_cmd, BL_ACK);
+ if (res == BL_ACK) {
+ return len;
+ } else if (res == BL_NACK) {
+ ssp_err("Got NAK waiting for WRITE_MEM to complete\n");
+ return -EPROTO;
+ }
+
+ ssp_err("timeout waiting for ACK for WRITE_MEM command\n");
+ return -ETIME;
+}
+
+static int load_ums_fw_bootmode(struct spi_device *spi, const char *pFn)
+{
+ const u8 *buff = NULL;
+ char fw_path[BL_UMS_FW_PATH+1];
+ unsigned int uFSize = 0, uNRead = 0;
+ unsigned int uPos = 0;
+ int iRet = SUCCESS;
+ int remaining;
+ int block = STM_MAX_XFER_SIZE;
+ unsigned int fw_addr = STM_APP_ADDR;
+ int retry_count = 0;
+ int err_count = 0;
+ int count = 0;
+ struct file *fp = NULL;
+ mm_segment_t old_fs = get_fs();
+
+ ssp_info("ssp_load_ums_fw start!!!\n");
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ snprintf(fw_path, BL_UMS_FW_PATH, "/sdcard/ssp/%s", pFn);
+
+ fp = filp_open(fw_path, O_RDONLY, 0);
+ if (IS_ERR(fp)) {
+ iRet = ERROR;
+ ssp_err("file %s open error : %d\n", fw_path, (s32)fp);
+ goto err_open;
+ }
+
+ uFSize = (unsigned int)fp->f_path.dentry->d_inode->i_size;
+ ssp_info("load_ums_firmware size : %u\n", uFSize);
+
+ buff = kzalloc((size_t)uFSize, GFP_KERNEL);
+ if (!buff) {
+ iRet = ERROR;
+ ssp_err("fail to alloc buffer for ums fw\n");
+ goto err_alloc;
+ }
+
+ uNRead = (unsigned int)vfs_read(fp, (char __user *)buff,
+ (unsigned int)uFSize, &fp->f_pos);
+ if (uNRead != uFSize) {
+ iRet = ERROR;
+ ssp_err("fail to read file %s (nread = %u)\n", fw_path, uNRead);
+ goto err_fw_size;
+ }
+
+ remaining = uFSize;
+ while (remaining > 0) {
+ if (block > remaining)
+ block = remaining;
+
+ while (retry_count < 3) {
+ iRet = fw_write_stm(spi, fw_addr, block, buff + uPos);
+ if (iRet < block) {
+ ssp_err("Error writing to addr 0x%08X\n", fw_addr);
+ if (iRet < 0) {
+ ssp_err("Erro was %d\n", iRet);
+ } else {
+ ssp_err("Incomplete write of %d bytes\n", iRet);
+ iRet = -EIO;
+ }
+ retry_count++;
+ err_count++;
+ } else {
+ retry_count = 0;
+ break;
+ }
+ }
+ if (iRet < 0) {
+ ssp_err("Writing MEM failed: %d, retry cont: %d\n",
+ iRet, err_count);
+ goto out;
+ }
+ remaining -= block;
+ uPos += block;
+ fw_addr += block;
+ if (count++ == 20) {
+ ssp_info("Updated %u bytes / %u bytes\n", uPos, uFSize);
+ count = 0;
+ }
+ }
+
+ ssp_info("Firm up(UMS) success(%d bytes, retry %d)\n", uPos, err_count);
+
+out:
+err_fw_size:
+ kfree(buff);
+err_alloc:
+ filp_close(fp, NULL);
+err_open:
+ set_fs(old_fs);
+
+ return iRet;
+}
+
+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) {
+ ssp_err("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_dbg("stm32fwu_spi_wait_for_ack returned %d (0x%x)\n", ret, ret);
+
+ if (ret == BL_ACK)
+ return 0;
+ else if (ret == BL_NACK)
+ return -EPROTO;
+ else
+ return -ETIME;
+
+}
+
+static int load_kernel_fw_bootmode(struct spi_device *spi, const char *pFn)
+{
+ const struct firmware *fw = NULL;
+ int remaining;
+ unsigned int uPos = 0;
+ unsigned int fw_addr = STM_APP_ADDR;
+ int iRet;
+ int block = STM_MAX_XFER_SIZE;
+ int count = 0;
+ int err_count = 0;
+ int retry_count = 0;
+
+ ssp_info("ssp_load_fw start!!!\n");
+
+ iRet = request_firmware(&fw, pFn, &spi->dev);
+ if (iRet) {
+ ssp_err("Unable to open firmware %s\n", pFn);
+ return iRet;
+ }
+
+ remaining = fw->size;
+ while (remaining > 0) {
+ if (block > remaining)
+ block = remaining;
+
+ while (retry_count < 3) {
+ iRet = fw_write_stm(spi, fw_addr, block, fw->data + uPos);
+ if (iRet < block) {
+ ssp_err("Error writing to addr 0x%08X\n", fw_addr);
+ if (iRet < 0) {
+ ssp_err("Erro was %d\n", iRet);
+ } else {
+ ssp_err("Incomplete write of %d bytes\n", iRet);
+ iRet = -EIO;
+ }
+ retry_count++;
+ err_count++;
+ } else {
+ retry_count = 0;
+ break;
+ }
+ }
+
+ if (iRet < 0) {
+ ssp_err("Writing MEM failed: %d, retry cont: %d\n",
+ iRet, err_count);
+ goto out_load_kernel;
+ }
+ remaining -= block;
+ uPos += block;
+ fw_addr += block;
+ if (count++ == 20) {
+ ssp_info("Updated %u bytes / %u bytes\n", uPos, fw->size);
+ count = 0;
+ }
+ }
+
+ ssp_info("Firmware download is success.(%d bytes, retry %d)\n", uPos, err_count);
+
+out_load_kernel:
+ release_firmware(fw);
+ return iRet;
+}
+
+static int change_to_bootmode(struct ssp_data *data)
+{
+ int iCnt;
+ int ret;
+ char syncb = BL_SPI_SOF;
+ struct stm32fwu_spi_cmd dummy_cmd;
+
+ ssp_dbg("ssp_change_to_bootmode\n");
+
+ dummy_cmd.timeout = DEF_ACKCMD_NUMBER;
+
+ gpio_set_value(data->rst, 0);
+ mdelay(4);
+ gpio_set_value(data->rst, 1);
+ usleep_range(45000, 47000);
+
+ for (iCnt = 0; iCnt < 9; iCnt++) {
+ gpio_set_value(data->rst, 0);
+ mdelay(4);
+ gpio_set_value(data->rst, 1);
+ usleep_range(15000, 15500);
+ }
+
+ ret = stm32fwu_spi_write(data->spi, &syncb, 1);
+ ssp_dbg("stm32fwu_spi_write(sync byte) returned %d\n", ret);
+ ret = stm32fwu_spi_wait_for_ack(data->spi, &dummy_cmd, BL_ACK);
+ ssp_dbg("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->rst, 0);
+ usleep_range(1000, 1200);
+ gpio_set_value(data->rst, 1);
+}
+
+static int update_mcu_bin(struct ssp_data *data, int iBinType)
+{
+ int retry = BLMODE_RETRYCOUNT;
+ int iRet = SUCCESS;
+ 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);
+
+ ssp_info("update_mcu_bin\n");
+ do {
+ iRet = change_to_bootmode(data);
+ ssp_dbg("bootmode %d, retry = %d\n", iRet, 3 - retry);
+ } while (retry-- > 0 && iRet != BL_ACK);
+
+ if(iRet != BL_ACK) {
+ ssp_err("change_to_bootmode failed %d\n", iRet);
+ return iRet;
+ }
+
+ iRet = fw_erase_stm(data->spi);
+ if (iRet < 0) {
+ ssp_err("fw_erase_stm failed %d\n", iRet);
+ return iRet;
+ }
+
+ switch (iBinType) {
+ case KERNEL_BINARY:
+ iRet = load_kernel_fw_bootmode(data->spi,BL_FW_NAME);
+ break;
+ case KERNEL_CRASHED_BINARY:
+ iRet = load_kernel_fw_bootmode(data->spi,
+ BL_CRASHED_FW_NAME);
+ break;
+ case UMS_BINARY:
+ iRet = load_ums_fw_bootmode(data->spi,
+ BL_UMS_FW_NAME);
+ break;
+
+ default:
+ ssp_err("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 iRet;
+}
+
+int forced_to_download_binary(struct ssp_data *data, int iBinType)
+{
+ int iRet;
+ int retry = 3;
+
+ ssp_dbg("mcu binany update!\n");
+ ssp_enable(data, false);
+
+ data->fw_dl_state = FW_DL_STATE_DOWNLOADING;
+ ssp_dbg("Download state = %d\n", data->fw_dl_state);
+
+ data->spi->max_speed_hz = BOOT_SPI_HZ;
+ if (spi_setup(data->spi))
+ ssp_err("failed to setup spi for ssp_boot\n");
+
+ do {
+ ssp_info("%d try\n", 3 - retry);
+ iRet = update_mcu_bin(data, iBinType);
+ } while (retry -- > 0 && iRet < 0);
+
+ data->spi->max_speed_hz = NORM_SPI_HZ;
+ if (spi_setup(data->spi))
+ ssp_err("failed to setup spi for ssp_norm\n");
+
+ if (iRet < 0) {
+ ssp_err("update_mcu_bin failed!\n");
+ goto out;
+ }
+
+ data->fw_dl_state = FW_DL_STATE_SYNC;
+ ssp_dbg("Download state = %d\n", data->fw_dl_state);
+ ssp_enable(data, true);
+
+ /* we should reload cal data after updating firmware on boooting */
+ accel_open_calibration(data);
+ gyro_open_calibration(data);
+
+ data->fw_dl_state = FW_DL_STATE_DONE;
+ ssp_dbg("Download state = %d\n", data->fw_dl_state);
+
+ iRet = SUCCESS;
+out:
+ return iRet;
+}
+
+int check_fwbl(struct ssp_data *data)
+{
+ int retries = 0;
+
+ while (retries++ < 5) {
+ data->uCurFirmRev = get_firmware_rev(data);
+ if (data->uCurFirmRev == SSP_INVALID_REVISION
+ || data->uCurFirmRev == SSP_INVALID_REVISION2 ) {
+ ssp_err("Invalid revision, tring %d time\n", retries);
+ } else {
+ break;
+ }
+ }
+
+ if ((data->uCurFirmRev == SSP_INVALID_REVISION) \
+ || (data->uCurFirmRev == SSP_INVALID_REVISION2)) {
+#if STM_SHOULD_BE_IMPLEMENT
+ data->client->addr = BOOTLOADER_SLAVE_ADDR;
+ iRet = check_bootloader(data->client, BL_WAITING_BOOTLOAD_CMD);
+
+ if (iRet >= 0)
+ ssp_info("ssp_load_fw_bootmode\n");
+ else {
+ ssp_err("Firm Rev is invalid(%8u). Retry.\n",
+ data->uCurFirmRev);
+ data->client->addr = APP_SLAVE_ADDR;
+ data->uCurFirmRev = get_firmware_rev(data);
+ if (data->uCurFirmRev == SSP_INVALID_REVISION ||\
+ data->uCurFirmRev == ERROR) {
+ ssp_err("MCU is not working, FW download failed\n");
+ return FW_DL_STATE_FAIL;
+ }
+ }
+#endif
+ data->uCurFirmRev = SSP_INVALID_REVISION;
+ ssp_err("SSP_INVALID_REVISION\n");
+ return FW_DL_STATE_NEED_TO_SCHEDULE;
+
+ } else {
+ if (data->uCurFirmRev != SSP_FIRMWARE_REVISION_STM) {
+ ssp_info("MCU Firm Rev : Old = %8u, New = %8u\n",
+ data->uCurFirmRev,
+ SSP_FIRMWARE_REVISION_STM);
+
+ return FW_DL_STATE_NEED_TO_SCHEDULE;
+ }
+ ssp_info("MCU Firm Rev : Old = %8u, New = %8u\n",
+ data->uCurFirmRev,
+ SSP_FIRMWARE_REVISION_STM);
+ }
+ return FW_DL_STATE_NONE;
+}
diff --git a/drivers/sensorhub/stm/ssp_input.c b/drivers/sensorhub/stm/ssp_input.c
new file mode 100644
index 00000000000..072f573a019
--- /dev/null
+++ b/drivers/sensorhub/stm/ssp_input.c
@@ -0,0 +1,269 @@
+/*
+ * 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"
+
+/*************************************************************************/
+/* SSP Kernel -> HAL input evnet function */
+/*************************************************************************/
+void convert_acc_data(s16 *iValue)
+{
+ if (*iValue > MAX_ACCEL_2G)
+ *iValue = ((MAX_ACCEL_4G - *iValue)) * (-1);
+}
+
+void report_acc_data(struct ssp_data *data, struct sensor_value *accdata)
+{
+ data->buf[ACCELEROMETER_SENSOR].x = accdata->x;
+ data->buf[ACCELEROMETER_SENSOR].y = accdata->y;
+ data->buf[ACCELEROMETER_SENSOR].z = accdata->z;
+
+ input_report_rel(data->acc_input_dev, REL_X,
+ data->buf[ACCELEROMETER_SENSOR].x);
+ input_report_rel(data->acc_input_dev, REL_Y,
+ data->buf[ACCELEROMETER_SENSOR].y);
+ input_report_rel(data->acc_input_dev, REL_Z,
+ data->buf[ACCELEROMETER_SENSOR].z);
+ input_sync(data->acc_input_dev);
+}
+
+void report_gyro_data(struct ssp_data *data, struct sensor_value *gyrodata)
+{
+ long lTemp[3] = {0,};
+ data->buf[GYROSCOPE_SENSOR].x = gyrodata->x;
+ data->buf[GYROSCOPE_SENSOR].y = gyrodata->y;
+ data->buf[GYROSCOPE_SENSOR].z = gyrodata->z;
+
+ if (data->uGyroDps == GYROSCOPE_DPS500) {
+ lTemp[0] = (long)data->buf[GYROSCOPE_SENSOR].x;
+ lTemp[1] = (long)data->buf[GYROSCOPE_SENSOR].y;
+ lTemp[2] = (long)data->buf[GYROSCOPE_SENSOR].z;
+ } else if (data->uGyroDps == GYROSCOPE_DPS250) {
+ lTemp[0] = (long)data->buf[GYROSCOPE_SENSOR].x >> 1;
+ lTemp[1] = (long)data->buf[GYROSCOPE_SENSOR].y >> 1;
+ lTemp[2] = (long)data->buf[GYROSCOPE_SENSOR].z >> 1;
+ } else if (data->uGyroDps == GYROSCOPE_DPS2000) {
+ lTemp[0] = (long)data->buf[GYROSCOPE_SENSOR].x << 2;
+ lTemp[1] = (long)data->buf[GYROSCOPE_SENSOR].y << 2;
+ lTemp[2] = (long)data->buf[GYROSCOPE_SENSOR].z << 2;
+ } else {
+ lTemp[0] = (long)data->buf[GYROSCOPE_SENSOR].x;
+ lTemp[1] = (long)data->buf[GYROSCOPE_SENSOR].y;
+ lTemp[2] = (long)data->buf[GYROSCOPE_SENSOR].z;
+ }
+
+ input_report_rel(data->gyro_input_dev, REL_RX, lTemp[0]);
+ input_report_rel(data->gyro_input_dev, REL_RY, lTemp[1]);
+ input_report_rel(data->gyro_input_dev, REL_RZ, lTemp[2]);
+ input_sync(data->gyro_input_dev);
+}
+
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+void report_hrm_raw_data(struct ssp_data *data, struct sensor_value *hrmdata)
+{
+ data->buf[BIO_HRM_RAW].ch_a = hrmdata->ch_a;
+ data->buf[BIO_HRM_RAW].ch_b = hrmdata->ch_b;
+ input_report_rel(data->hrm_raw_input_dev, REL_X, data->buf[BIO_HRM_RAW].ch_a + 1);
+ input_report_rel(data->hrm_raw_input_dev, REL_Y, data->buf[BIO_HRM_RAW].ch_b + 1);
+ input_report_rel(data->hrm_raw_input_dev, REL_Z, 1); // Dummy code
+ input_sync(data->hrm_raw_input_dev);
+}
+
+void report_hrm_raw_fac_data(struct ssp_data *data, struct sensor_value *hrmdata)
+{
+ data->buf[BIO_HRM_RAW_FAC].ch_a = hrmdata->ch_a;
+ data->buf[BIO_HRM_RAW_FAC].ch_b = hrmdata->ch_b;
+ data->buf[BIO_HRM_RAW_FAC].frequency = hrmdata->frequency;
+ data->buf[BIO_HRM_RAW_FAC].noise_value = hrmdata->noise_value;
+ data->buf[BIO_HRM_RAW_FAC].dc_value = hrmdata->dc_value;
+ data->buf[BIO_HRM_RAW_FAC].ac_value = hrmdata->ac_value;
+ data->buf[BIO_HRM_RAW_FAC].perfusion_rate = hrmdata->perfusion_rate;
+ data->buf[BIO_HRM_RAW_FAC].snrac = hrmdata->snrac;
+ data->buf[BIO_HRM_RAW_FAC].snrdc = hrmdata->snrdc;
+ input_report_rel(data->hrm_raw_input_dev, REL_X, data->buf[BIO_HRM_RAW_FAC].ch_a + 1);
+ input_report_rel(data->hrm_raw_input_dev, REL_Y, data->buf[BIO_HRM_RAW_FAC].ch_b + 1);
+ input_report_rel(data->hrm_raw_input_dev, REL_Z, 1); // Dummy code
+ input_sync(data->hrm_raw_input_dev);
+}
+
+void report_hrm_lib_data(struct ssp_data *data, struct sensor_value *hrmdata)
+{
+ data->buf[BIO_HRM_LIB].hr = hrmdata->hr;
+ data->buf[BIO_HRM_LIB].rri = hrmdata->rri;
+ data->buf[BIO_HRM_LIB].snr = hrmdata->snr;
+
+ input_report_rel(data->hrm_lib_input_dev, REL_X, data->buf[BIO_HRM_LIB].hr + 1);
+ input_report_rel(data->hrm_lib_input_dev, REL_Y, data->buf[BIO_HRM_LIB].rri + 1);
+ input_report_rel(data->hrm_lib_input_dev, REL_Z, data->buf[BIO_HRM_LIB].snr + 1);
+ input_sync(data->hrm_lib_input_dev);
+}
+#endif
+
+int initialize_event_symlink(struct ssp_data *data)
+{
+ int iRet = 0;
+
+ iRet = sensors_create_symlink(data->acc_input_dev);
+ if (iRet < 0)
+ goto iRet_acc_sysfs_create_link;
+
+ iRet = sensors_create_symlink(data->gyro_input_dev);
+ if (iRet < 0)
+ goto iRet_gyro_sysfs_create_link;
+
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ iRet = sensors_create_symlink(data->hrm_raw_input_dev);
+ if (iRet < 0)
+ goto iRet_hrm_raw_sysfs_create_link;
+
+ iRet = sensors_create_symlink(data->hrm_lib_input_dev);
+ if (iRet < 0)
+ goto iRet_hrm_lib_sysfs_create_link;
+#endif
+
+ return SUCCESS;
+
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+iRet_hrm_lib_sysfs_create_link:
+ sensors_remove_symlink(data->hrm_raw_input_dev);
+iRet_hrm_raw_sysfs_create_link:
+ sensors_remove_symlink(data->gyro_input_dev);
+#endif
+iRet_gyro_sysfs_create_link:
+ sensors_remove_symlink(data->acc_input_dev);
+iRet_acc_sysfs_create_link:
+ ssp_err("could not create event symlink\n");
+ return FAIL;
+}
+
+void remove_event_symlink(struct ssp_data *data)
+{
+ sensors_remove_symlink(data->acc_input_dev);
+ sensors_remove_symlink(data->gyro_input_dev);
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ sensors_remove_symlink(data->hrm_raw_input_dev);
+ sensors_remove_symlink(data->hrm_lib_input_dev);
+#endif
+}
+
+int initialize_input_dev(struct ssp_data *data)
+{
+ int iRet = 0;
+
+ data->acc_input_dev = input_allocate_device();
+ if (data->acc_input_dev == NULL)
+ goto err_initialize_acc_input_dev;
+
+ data->acc_input_dev->name = "accelerometer_sensor";
+ input_set_capability(data->acc_input_dev, EV_REL, REL_X);
+ input_set_capability(data->acc_input_dev, EV_REL, REL_Y);
+ input_set_capability(data->acc_input_dev, EV_REL, REL_Z);
+
+ iRet = input_register_device(data->acc_input_dev);
+ if (iRet < 0) {
+ input_free_device(data->acc_input_dev);
+ goto err_initialize_acc_input_dev;
+ }
+ input_set_drvdata(data->acc_input_dev, data);
+
+ data->gyro_input_dev = input_allocate_device();
+ if (data->gyro_input_dev == NULL)
+ goto err_initialize_gyro_input_dev;
+
+ data->gyro_input_dev->name = "gyro_sensor";
+ input_set_capability(data->gyro_input_dev, EV_REL, REL_RX);
+ input_set_capability(data->gyro_input_dev, EV_REL, REL_RY);
+ input_set_capability(data->gyro_input_dev, EV_REL, REL_RZ);
+
+ iRet = input_register_device(data->gyro_input_dev);
+ if (iRet < 0) {
+ input_free_device(data->gyro_input_dev);
+ goto err_initialize_gyro_input_dev;
+ }
+ input_set_drvdata(data->gyro_input_dev, data);
+
+ data->key_input_dev = input_allocate_device();
+ if (data->key_input_dev == NULL)
+ goto err_initialize_key_input_dev;
+
+ data->key_input_dev->name = "LPM_MOTION";
+ input_set_capability(data->key_input_dev, EV_KEY, KEY_HOMEPAGE);
+
+ iRet = input_register_device(data->key_input_dev);
+ if (iRet < 0) {
+ input_free_device(data->key_input_dev);
+ goto err_initialize_key_input_dev;
+ }
+ input_set_drvdata(data->key_input_dev, data);
+
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ data->hrm_raw_input_dev = input_allocate_device();
+ if (data->hrm_raw_input_dev == NULL)
+ goto err_initialize_hrm_raw_input_dev;
+
+ data->hrm_raw_input_dev->name = "hrm_raw_sensor";
+ input_set_capability(data->hrm_raw_input_dev, EV_REL, REL_X);
+ input_set_capability(data->hrm_raw_input_dev, EV_REL, REL_Y);
+ input_set_capability(data->hrm_raw_input_dev, EV_REL, REL_Z);
+
+ iRet = input_register_device(data->hrm_raw_input_dev);
+ if (iRet < 0) {
+ input_free_device(data->hrm_raw_input_dev);
+ goto err_initialize_hrm_raw_input_dev;
+ }
+ input_set_drvdata(data->hrm_raw_input_dev, data);
+
+ data->hrm_lib_input_dev = input_allocate_device();
+ if (data->hrm_lib_input_dev == NULL)
+ goto err_initialize_hrm_lib_input_dev;
+
+ data->hrm_lib_input_dev->name = "hrm_lib_sensor";
+ input_set_capability(data->hrm_lib_input_dev, EV_REL, REL_X);
+ input_set_capability(data->hrm_lib_input_dev, EV_REL, REL_Y);
+ input_set_capability(data->hrm_lib_input_dev, EV_REL, REL_Z);
+
+ iRet = input_register_device(data->hrm_lib_input_dev);
+ if (iRet < 0) {
+ input_free_device(data->hrm_lib_input_dev);
+ goto err_initialize_hrm_lib_input_dev;
+ }
+ input_set_drvdata(data->hrm_lib_input_dev, data);
+#endif
+
+ return SUCCESS;
+
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+err_initialize_hrm_lib_input_dev:
+ ssp_err("could not allocate hrm lib input device\n");
+ input_unregister_device(data->hrm_raw_input_dev);
+err_initialize_hrm_raw_input_dev:
+ ssp_err("could not allocate hrm raw input device\n");
+ input_unregister_device(data->key_input_dev);
+#endif
+err_initialize_key_input_dev:
+ ssp_err("could not allocate key input device\n");
+ input_unregister_device(data->gyro_input_dev);
+err_initialize_gyro_input_dev:
+ ssp_err("could not allocate gyro input device\n");
+ input_unregister_device(data->acc_input_dev);
+err_initialize_acc_input_dev:
+ ssp_err("could not allocate acc input device\n");
+ return ERROR;
+}
+
+void remove_input_dev(struct ssp_data *data)
+{
+ input_unregister_device(data->acc_input_dev);
+ input_unregister_device(data->gyro_input_dev);
+}
diff --git a/drivers/sensorhub/stm/ssp_sensorhub.c b/drivers/sensorhub/stm/ssp_sensorhub.c
new file mode 100644
index 00000000000..dfeeb4adc59
--- /dev/null
+++ b/drivers/sensorhub/stm/ssp_sensorhub.c
@@ -0,0 +1,707 @@
+/*
+ * Copyright (C) 2013, 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_sensorhub.h"
+
+static void ssp_sensorhub_log(const char *func_name,
+ const char *data, int length)
+{
+ char buf[6];
+ char *log_str;
+ int log_size;
+ int i;
+
+ if (likely(length <= BIG_DATA_SIZE))
+ log_size = length;
+ else
+ log_size = PRINT_TRUNCATE * 2 + 1;
+
+ log_size = sizeof(buf) * log_size + 1;
+ log_str = kzalloc(log_size, GFP_ATOMIC);
+ if (unlikely(!log_str)) {
+ ssp_err("allocate memory for data log err");
+ return;
+ }
+
+ for (i = 0; i < length; i++) {
+ if (length < BIG_DATA_SIZE ||
+ i < PRINT_TRUNCATE || i >= length - PRINT_TRUNCATE) {
+ snprintf(buf, sizeof(buf), "%d", (signed char)data[i]);
+ strlcat(log_str, buf, log_size);
+ }
+ if (length < BIG_DATA_SIZE ||
+ i < PRINT_TRUNCATE || i >= length - PRINT_TRUNCATE) {
+ if (i < length - 1)
+ strlcat(log_str, ", ", log_size);
+ }
+ if (length > BIG_DATA_SIZE && i == PRINT_TRUNCATE)
+ strlcat(log_str, "..., ", log_size);
+ }
+
+ ssp_dbg("sensorhub - %s:%s (%d)\n", func_name, log_str, length);
+ kfree(log_str);
+}
+
+static int ssp_sensorhub_send_big_data(struct ssp_sensorhub_data *hub_data,
+ const char *buf, int count)
+{
+ int length = count - 3;
+ int ret = 0;
+
+ /* only support voice service for the moment */
+ if (buf[1] != TYPE_WAKE_UP_VOICE_SERVICE) {
+ ssp_err("not voice service(%d)", buf[1]);
+ return -EINVAL;
+ }
+
+ /* am or grammer data? */
+ if (buf[2] != TYPE_WAKE_UP_VOICE_SOUND_SOURCE_AM &&
+ buf[2] != TYPE_WAKE_UP_VOICE_SOUND_SOURCE_GRAMMER) {
+ ssp_err("voice data type err(%d)", buf[2]);
+ return -EINVAL;
+ }
+
+ hub_data->big_send_events.library_data
+ = kzalloc(length * sizeof(char), GFP_KERNEL);
+ if (unlikely(!hub_data->big_send_events.library_data)) {
+ ssp_err("allocate memory for big library err");
+ return -ENOMEM;
+ }
+
+ memcpy(hub_data->big_send_events.library_data, buf+3, length);
+ hub_data->big_send_events.library_length = length;
+
+ /* trigger big data transfer */
+ ret = set_big_data_start(hub_data->ssp_data, buf[2]+1, length);
+ if (ret != SUCCESS) {
+ ssp_err("set_big_data_start err(%d)", ret);
+ goto exit;
+ }
+
+ /* wait until write operation is done */
+ ret = wait_for_completion_timeout(&hub_data->big_write_done,
+ COMPLETION_TIMEOUT + 1*HZ);
+ if (unlikely(!ret)) {
+ ssp_err("wait timed out");
+ ret = -EBUSY;
+ } else if (unlikely(ret < 0)) {
+ ssp_err("wait for completion err(%d)", ret);
+ ret = -EIO;
+ }
+
+exit:
+ kfree(hub_data->big_send_events.library_data);
+ return !ret ? ret + 1 : ret;
+}
+
+static int ssp_sensorhub_send_cmd(struct ssp_sensorhub_data *hub_data,
+ const char *buf, int count)
+{
+ int ret = 0;
+
+ if (buf[2] < MSG2SSP_AP_STATUS_WAKEUP ||
+ buf[2] >= MSG2SSP_AP_TEMPHUMIDITY_CAL_DONE) {
+ ssp_err("MSG2SSP_INST_LIB_NOTI err(%d)", buf[2]);
+ return -EINVAL;
+ }
+
+ ret = ssp_send_cmd(hub_data->ssp_data, buf[2], 0);
+
+ if (buf[2] == MSG2SSP_AP_STATUS_WAKEUP ||
+ buf[2] == MSG2SSP_AP_STATUS_SLEEP)
+ hub_data->ssp_data->uLastAPState = buf[2];
+
+ if (buf[2] == MSG2SSP_AP_STATUS_SUSPEND ||
+ buf[2] == MSG2SSP_AP_STATUS_RESUME)
+ hub_data->ssp_data->uLastResumeState = buf[2];
+
+ return ret;
+}
+
+static int ssp_sensorhub_send_instruction(struct ssp_sensorhub_data *hub_data,
+ const char *buf, int count)
+{
+ unsigned char instruction = buf[0];
+
+ if (buf[0] == MSG2SSP_INST_LIBRARY_REMOVE)
+ instruction = REMOVE_LIBRARY;
+ else if (buf[0] == MSG2SSP_INST_LIBRARY_ADD)
+ instruction = ADD_LIBRARY;
+
+ return send_instruction(hub_data->ssp_data, instruction,
+ (unsigned char)buf[1], (unsigned char *)(buf+2), count-2);
+}
+
+static ssize_t ssp_sensorhub_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct ssp_sensorhub_data *hub_data
+ = container_of(file->private_data,
+ struct ssp_sensorhub_data, sensorhub_device);
+ int ret = 0;
+
+ if (unlikely(count < 2)) {
+ ssp_err("library data length err(%d)", count);
+ return -EINVAL;
+ }
+
+ ssp_sensorhub_log(__func__, buf, count);
+
+ if (unlikely(hub_data->ssp_data->bSspShutdown)) {
+ ssp_err("stop sending library data(shutdown)");
+ return -EBUSY;
+ }
+
+ if (buf[0] == MSG2SSP_INST_LIB_DATA && count >= BIG_DATA_SIZE)
+ ret = ssp_sensorhub_send_big_data(hub_data, buf, count);
+ else if (buf[0] == MSG2SSP_INST_LIB_NOTI)
+ ret = ssp_sensorhub_send_cmd(hub_data, buf, count);
+ else
+ ret = ssp_sensorhub_send_instruction(hub_data, buf, count);
+
+ if (unlikely(ret <= 0)) {
+ ssp_err("send library data err(%d)", ret);
+ /* i2c transfer fail */
+ if (ret == ERROR)
+ return -EIO;
+ /* i2c transfer done but no ack from MCU */
+ else if (ret == FAIL)
+ return -EAGAIN;
+ else
+ return ret;
+ }
+
+ return count;
+}
+
+static ssize_t ssp_sensorhub_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct ssp_sensorhub_data *hub_data
+ = container_of(file->private_data,
+ struct ssp_sensorhub_data, sensorhub_device);
+ struct sensorhub_event *event;
+ int retries = MAX_DATA_COPY_TRY;
+ int length = 0;
+ int ret = 0;
+
+ spin_lock_bh(&hub_data->sensorhub_lock);
+ if (unlikely(kfifo_is_empty(&hub_data->fifo))) {
+ ssp_err("no library data");
+ goto err;
+ }
+
+ /* first in first out */
+ ret = kfifo_out_peek(&hub_data->fifo, &event, sizeof(void *));
+ if (unlikely(!ret)) {
+ ssp_err("kfifo out peek err(%d)", ret);
+ ret = EIO;
+ goto err;
+ }
+
+ length = event->library_length;
+
+ while (retries--) {
+ ret = copy_to_user(buf,
+ event->library_data, event->library_length);
+ if (likely(!ret))
+ break;
+ }
+ if (unlikely(ret)) {
+ ssp_err("read library data err(%d)", ret);
+ goto err;
+ }
+
+ ssp_sensorhub_log(__func__,
+ event->library_data, event->library_length);
+
+ /* remove first event from the list */
+ ret = kfifo_out(&hub_data->fifo, &event, sizeof(void *));
+ if (unlikely(ret != sizeof(void *))) {
+ ssp_err("kfifo out err(%d)", ret);
+ ret = EIO;
+ goto err;
+ }
+
+ complete(&hub_data->read_done);
+ spin_unlock_bh(&hub_data->sensorhub_lock);
+
+ return length;
+
+err:
+ spin_unlock_bh(&hub_data->sensorhub_lock);
+ return ret ? -ret : 0;
+}
+
+static long ssp_sensorhub_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct ssp_sensorhub_data *hub_data
+ = container_of(file->private_data,
+ struct ssp_sensorhub_data, sensorhub_device);
+ void __user *argp = (void __user *)arg;
+ int retries = MAX_DATA_COPY_TRY;
+ int length = hub_data->big_events.library_length;
+ int ret = 0;
+
+ switch (cmd) {
+ case IOCTL_READ_BIG_CONTEXT_DATA:
+ if (unlikely(!hub_data->big_events.library_data
+ || !hub_data->big_events.library_length)) {
+ ssp_err("no big library data");
+ return 0;
+ }
+
+ while (retries--) {
+ ret = copy_to_user(argp,
+ hub_data->big_events.library_data,
+ hub_data->big_events.library_length);
+ if (likely(!ret))
+ break;
+ }
+ if (unlikely(ret)) {
+ ssp_err("read big library data err(%d)", ret);
+ return -ret;
+ }
+
+ ssp_sensorhub_log(__func__,
+ hub_data->big_events.library_data,
+ hub_data->big_events.library_length);
+
+ hub_data->is_big_event = false;
+ kfree(hub_data->big_events.library_data);
+ hub_data->big_events.library_length = 0;
+ complete(&hub_data->big_read_done);
+ break;
+
+ default:
+ ssp_err("ioctl cmd err(%d)", cmd);
+ return -EINVAL;
+ }
+
+ return length;
+}
+
+static struct file_operations ssp_sensorhub_fops = {
+ .owner = THIS_MODULE,
+ .open = nonseekable_open,
+ .write = ssp_sensorhub_write,
+ .read = ssp_sensorhub_read,
+ .unlocked_ioctl = ssp_sensorhub_ioctl,
+};
+
+void ssp_sensorhub_report_notice(struct ssp_data *ssp_data, char notice)
+{
+ struct ssp_sensorhub_data *hub_data = ssp_data->hub_data;
+
+ input_report_rel(hub_data->sensorhub_input_dev, NOTICE, notice);
+ input_sync(hub_data->sensorhub_input_dev);
+
+ if (notice == MSG2SSP_AP_STATUS_WAKEUP)
+ ssp_info("wake up");
+ else if (notice == MSG2SSP_AP_STATUS_SLEEP)
+ ssp_info("sleep");
+ else if (notice == MSG2SSP_AP_STATUS_RESET)
+ ssp_info("reset");
+ else
+ ssp_err("invalid notice(0x%x)", notice);
+}
+
+static void ssp_sensorhub_report_library(struct ssp_sensorhub_data *hub_data)
+{
+ input_report_rel(hub_data->sensorhub_input_dev, DATA, DATA);
+ input_sync(hub_data->sensorhub_input_dev);
+}
+
+static void ssp_sensorhub_report_big_library(
+ struct ssp_sensorhub_data *hub_data)
+{
+ input_report_rel(hub_data->sensorhub_input_dev, BIG_DATA, BIG_DATA);
+ input_sync(hub_data->sensorhub_input_dev);
+}
+
+static int ssp_sensorhub_list(struct ssp_sensorhub_data *hub_data,
+ char *dataframe, int length)
+{
+ struct sensorhub_event *event;
+ int ret = 0;
+
+ if (unlikely(length <= 0 || length >= PAGE_SIZE)) {
+ ssp_err("library length err(%d)", length);
+ return -EINVAL;
+ }
+
+ ssp_sensorhub_log(__func__, dataframe, length);
+
+ /* overwrite new event if list is full */
+ if (unlikely(kfifo_is_full(&hub_data->fifo))) {
+ ret = kfifo_out(&hub_data->fifo, &event, sizeof(void *));
+ if (unlikely(ret != sizeof(void *))) {
+ ssp_err("kfifo out err(%d)", ret);
+ return -EIO;
+ }
+ ssp_dbg("overwrite event");
+ }
+
+ /* allocate memory for new event */
+ kfree(hub_data->events[hub_data->event_number].library_data);
+ hub_data->events[hub_data->event_number].library_data
+ = kzalloc(length * sizeof(char), GFP_ATOMIC);
+ if (unlikely(!hub_data->events[hub_data->event_number].library_data)) {
+ ssp_dbg("allocate memory for library err");
+ return -ENOMEM;
+ }
+
+ /* copy new event into memory */
+ memcpy(hub_data->events[hub_data->event_number].library_data,
+ dataframe, length);
+ hub_data->events[hub_data->event_number].library_length = length;
+
+ /* add new event into the end of list */
+ event = &hub_data->events[hub_data->event_number];
+ ret = kfifo_in(&hub_data->fifo, &event, sizeof(void *));
+ if (unlikely(ret != sizeof(void *))) {
+ ssp_err("kfifo in err(%d)", ret);
+ return -EIO;
+ }
+
+ /* not to overflow max list capacity */
+ if (hub_data->event_number++ >= LIST_SIZE - 1)
+ hub_data->event_number = 0;
+
+ return kfifo_len(&hub_data->fifo) / sizeof(void *);
+}
+
+int ssp_sensorhub_handle_data(struct ssp_data *ssp_data, char *dataframe,
+ int start, int end)
+{
+ struct ssp_sensorhub_data *hub_data = ssp_data->hub_data;
+ int ret = 0;
+
+ /* add new sensorhub event into list */
+ spin_lock_bh(&hub_data->sensorhub_lock);
+ ret = ssp_sensorhub_list(hub_data, dataframe+start, end-start);
+ spin_unlock_bh(&hub_data->sensorhub_lock);
+
+ if (ret < 0)
+ ssp_err("sensorhub list err(%d)", ret);
+ else
+ wake_up(&hub_data->sensorhub_wq);
+
+ return ret;
+}
+
+static int ssp_sensorhub_thread(void *arg)
+{
+ struct ssp_sensorhub_data *hub_data = (struct ssp_sensorhub_data *)arg;
+ int ret = 0;
+
+ while (likely(!kthread_should_stop())) {
+ /* run thread if list is not empty */
+ wait_event_interruptible(hub_data->sensorhub_wq,
+ kthread_should_stop() ||
+ !kfifo_is_empty(&hub_data->fifo) ||
+ hub_data->is_big_event);
+
+ /* exit thread if kthread should stop */
+ if (unlikely(kthread_should_stop())) {
+ ssp_dbg("kthread_stop()");
+ break;
+ }
+
+ if (likely(!kfifo_is_empty(&hub_data->fifo))) {
+ /* report sensorhub event to user */
+ ssp_sensorhub_report_library(hub_data);
+ /* wait until transfer finished */
+ ret = wait_for_completion_timeout(
+ &hub_data->read_done, COMPLETION_TIMEOUT);
+ if (unlikely(!ret))
+ ssp_err("wait for read timed out");
+ else if (unlikely(ret < 0))
+ ssp_err("read completion err(%d)", ret);
+ }
+
+ if (unlikely(hub_data->is_big_event)) {
+ /* report big sensorhub event to user */
+ ssp_sensorhub_report_big_library(hub_data);
+ /* wait until transfer finished */
+ ret = wait_for_completion_timeout(
+ &hub_data->big_read_done, COMPLETION_TIMEOUT);
+ if (unlikely(!ret))
+ ssp_err("wait for big read timed out");
+ else if (unlikely(ret < 0))
+ ssp_err("big read completion err(%d)",
+ ret);
+ }
+ }
+
+ return 0;
+}
+
+void ssp_read_big_library_task(struct work_struct *work)
+{
+ struct ssp_big *big = container_of(work, struct ssp_big, work);
+ struct ssp_sensorhub_data *hub_data = big->data->hub_data;
+ struct ssp_msg *msg;
+ int buf_len, residue = big->length, ret = 0, index = 0, pos = 0;
+
+ hub_data->big_events.library_length = big->length;
+ hub_data->big_events.library_data = kzalloc(big->length, GFP_KERNEL);
+
+ while (residue > 0) {
+ buf_len = residue > DATA_PACKET_SIZE
+ ? DATA_PACKET_SIZE : residue;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ msg->cmd = MSG2SSP_AP_GET_BIG_DATA;
+ msg->length = buf_len;
+ msg->options = AP2HUB_READ | (index++ << SSP_INDEX);
+ msg->data = big->addr;
+ msg->buffer = hub_data->big_events.library_data + pos;
+ msg->free_buffer = 0;
+
+ ret = ssp_spi_sync(big->data, msg, 1000);
+ if (ret != SUCCESS) {
+ ssp_err("read big data err(%d)", ret);
+ break;
+ }
+
+ pos += buf_len;
+ residue -= buf_len;
+
+ ssp_dbg("read big data (%5d / %5d)", pos, big->length);
+ }
+
+ hub_data->is_big_event = true;
+ wake_up(&hub_data->sensorhub_wq);
+ kfree(big);
+}
+
+void ssp_send_big_library_task(struct work_struct *work)
+{
+ struct ssp_big *big = container_of(work, struct ssp_big, work);
+ struct ssp_sensorhub_data *hub_data = big->data->hub_data;
+ struct ssp_msg *msg;
+ int buf_len, residue = big->length, ret = 0, index = 0, pos = 0;
+
+ while (residue > 0) {
+ buf_len = residue > DATA_PACKET_SIZE
+ ? DATA_PACKET_SIZE : residue;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ msg->cmd = MSG2SSP_AP_SET_BIG_DATA;
+ msg->length = buf_len;
+ msg->options = AP2HUB_WRITE | (index++ << SSP_INDEX);
+ msg->data = big->addr;
+ msg->buffer = hub_data->big_send_events.library_data + pos;
+ msg->free_buffer = 0;
+
+ ret = ssp_spi_sync(big->data, msg, 1000);
+ if (ret != SUCCESS) {
+ ssp_err("send big data err(%d)", ret);
+ break;
+ }
+
+ pos += buf_len;
+ residue -= buf_len;
+
+ ssp_dbg("send big data (%5d / %5d)", pos, big->length);
+ }
+
+ complete(&hub_data->big_write_done);
+ kfree(big);
+}
+
+void ssp_pcm_dump_task(struct work_struct *work)
+{
+ struct ssp_big *big = container_of(work, struct ssp_big, work);
+ struct ssp_sensorhub_data *hub_data = big->data->hub_data;
+ struct ssp_msg *msg;
+ int buf_len, residue = big->length, ret = 0, index = 0;
+
+ mm_segment_t old_fs;
+ struct file *voice_filp;
+ char pcm_path[BIN_PATH_SIZE+1];
+ char *buff;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ snprintf(pcm_path, BIN_PATH_SIZE,
+ "/data/voice%d.pcm", hub_data->pcm_cnt);
+ voice_filp = filp_open(pcm_path, O_RDWR | O_CREAT | O_APPEND, 0666);
+ if (IS_ERR(voice_filp)) {
+ ssp_err("open pcm dump file err");
+ goto exit;
+ }
+
+ buf_len = big->length > DATA_PACKET_SIZE
+ ? DATA_PACKET_SIZE : big->length;
+ buff = kzalloc(buf_len, GFP_KERNEL);
+
+ while (residue > 0) {
+ buf_len = residue > DATA_PACKET_SIZE
+ ? DATA_PACKET_SIZE : residue;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ msg->cmd = MSG2SSP_AP_GET_BIG_DATA;
+ msg->length = buf_len;
+ msg->options = AP2HUB_READ | (index++ << SSP_INDEX);
+ msg->data = big->addr;
+ msg->buffer = buff;
+ msg->free_buffer = 0;
+
+ ret = ssp_spi_sync(big->data, msg, 1000);
+ if (ret != SUCCESS) {
+ ssp_err("receive pcm dump err(%d)", ret);
+ break;
+ }
+
+ ret = voice_filp->f_op->write(voice_filp, (char __user *)buff,
+ buf_len, &voice_filp->f_pos);
+ if (ret < 0) {
+ ssp_err("write pcm dump to file err(%d)", ret);
+ break;
+ }
+
+ residue -= buf_len;
+ ssp_dbg("write pcm dump...");
+ }
+
+ filp_close(voice_filp, current->files);
+ kfree(buff);
+
+exit:
+ set_fs(old_fs);
+ kfree(big);
+}
+
+int ssp_sensorhub_pcm_dump(struct ssp_sensorhub_data *hub_data)
+{
+ hub_data->pcm_cnt++;
+ return set_big_data_start(hub_data->ssp_data, BIG_TYPE_VOICE_PCM, 0);
+}
+
+int ssp_sensorhub_initialize(struct ssp_data *ssp_data)
+{
+ struct ssp_sensorhub_data *hub_data;
+ int ret;
+
+ /* allocate memory for sensorhub data */
+ hub_data = kzalloc(sizeof(*hub_data), GFP_KERNEL);
+ if (!hub_data) {
+ ssp_err("allocate memory for sensorhub data err");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ hub_data->ssp_data = ssp_data;
+ ssp_data->hub_data = hub_data;
+
+ /* init list, waitqueue, completion and spinlock */
+ init_waitqueue_head(&hub_data->sensorhub_wq);
+ init_completion(&hub_data->read_done);
+ init_completion(&hub_data->big_read_done);
+ init_completion(&hub_data->big_write_done);
+ spin_lock_init(&hub_data->sensorhub_lock);
+
+ /* allocate sensorhub input device */
+ hub_data->sensorhub_input_dev = input_allocate_device();
+ if (!hub_data->sensorhub_input_dev) {
+ ssp_err("allocate sensorhub input device err");
+ ret = -ENOMEM;
+ goto err_input_allocate_device_sensorhub;
+ }
+
+ /* set sensorhub input device */
+ input_set_drvdata(hub_data->sensorhub_input_dev, hub_data);
+ hub_data->sensorhub_input_dev->name = "ssp_context";
+ input_set_capability(hub_data->sensorhub_input_dev, EV_REL, DATA);
+ input_set_capability(hub_data->sensorhub_input_dev, EV_REL, BIG_DATA);
+ input_set_capability(hub_data->sensorhub_input_dev, EV_REL, NOTICE);
+
+ /* register sensorhub input device */
+ ret = input_register_device(hub_data->sensorhub_input_dev);
+ if (ret < 0) {
+ ssp_err("register sensorhub input device err(%d)", ret);
+ input_free_device(hub_data->sensorhub_input_dev);
+ goto err_input_register_device_sensorhub;
+ }
+
+ /* register sensorhub misc device */
+ hub_data->sensorhub_device.minor = MISC_DYNAMIC_MINOR;
+ hub_data->sensorhub_device.name = "ssp_sensorhub";
+ hub_data->sensorhub_device.fops = &ssp_sensorhub_fops;
+
+ ret = misc_register(&hub_data->sensorhub_device);
+ if (ret < 0) {
+ ssp_err("register sensorhub misc device err(%d)", ret);
+ goto err_misc_register;
+ }
+
+ /* allocate fifo */
+ ret = kfifo_alloc(&hub_data->fifo,
+ sizeof(void *) * LIST_SIZE, GFP_KERNEL);
+ if (ret) {
+ ssp_err("kfifo allocate err(%d)", ret);
+ goto err_kfifo_alloc;
+ }
+
+ /* create and run sensorhub thread */
+ hub_data->sensorhub_task = kthread_run(ssp_sensorhub_thread,
+ (void *)hub_data, "ssp_sensorhub_thread");
+ if (IS_ERR(hub_data->sensorhub_task)) {
+ ret = PTR_ERR(hub_data->sensorhub_task);
+ goto err_kthread_run;
+ }
+
+ return 0;
+
+err_kthread_run:
+ kfifo_free(&hub_data->fifo);
+err_kfifo_alloc:
+ misc_deregister(&hub_data->sensorhub_device);
+err_misc_register:
+ input_unregister_device(hub_data->sensorhub_input_dev);
+err_input_register_device_sensorhub:
+err_input_allocate_device_sensorhub:
+ complete_all(&hub_data->big_write_done);
+ complete_all(&hub_data->big_read_done);
+ complete_all(&hub_data->read_done);
+ kfree(hub_data);
+exit:
+ return ret;
+}
+
+void ssp_sensorhub_remove(struct ssp_data *ssp_data)
+{
+ struct ssp_sensorhub_data *hub_data = ssp_data->hub_data;
+
+ ssp_sensorhub_fops.write = NULL;
+ ssp_sensorhub_fops.read = NULL;
+ ssp_sensorhub_fops.unlocked_ioctl = NULL;
+
+ kthread_stop(hub_data->sensorhub_task);
+ kfifo_free(&hub_data->fifo);
+ misc_deregister(&hub_data->sensorhub_device);
+ input_unregister_device(hub_data->sensorhub_input_dev);
+ complete_all(&hub_data->big_write_done);
+ complete_all(&hub_data->big_read_done);
+ complete_all(&hub_data->read_done);
+ kfree(hub_data);
+}
+
+MODULE_DESCRIPTION("Seamless Sensor Platform(SSP) sensorhub driver");
+MODULE_AUTHOR("Samsung Electronics");
+MODULE_LICENSE("GPL");
diff --git a/drivers/sensorhub/stm/ssp_sensorhub.h b/drivers/sensorhub/stm/ssp_sensorhub.h
new file mode 100644
index 00000000000..a3320fc53a7
--- /dev/null
+++ b/drivers/sensorhub/stm/ssp_sensorhub.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013, 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__
+#define __SSP_SENSORHUB__
+
+#include <linux/completion.h>
+#include <linux/kfifo.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include "ssp.h"
+
+/* 'LIST_SIZE' should be be rounded-up to a power of 2 */
+#define LIST_SIZE 4
+#define MAX_DATA_COPY_TRY 2
+#define WAKE_LOCK_TIMEOUT (0.5*HZ)
+#define COMPLETION_TIMEOUT (2*HZ)
+#define DATA REL_RX
+#define BIG_DATA REL_RY
+#define NOTICE REL_RZ
+#define BIG_DATA_SIZE 256
+#define PRINT_TRUNCATE 6
+#define BIN_PATH_SIZE 100
+
+#define SENSORHUB_IOCTL_MAGIC 'S'
+#define IOCTL_READ_BIG_CONTEXT_DATA _IOR(SENSORHUB_IOCTL_MAGIC, 3, char *)
+
+struct sensorhub_event {
+ char *library_data;
+ int library_length;
+};
+
+struct ssp_sensorhub_data {
+ struct ssp_data *ssp_data;
+ struct input_dev *sensorhub_input_dev;
+ struct task_struct *sensorhub_task;
+ struct miscdevice sensorhub_device;
+ struct completion read_done;
+ struct completion big_read_done;
+ struct completion big_write_done;
+ struct sensorhub_event events[LIST_SIZE];
+ struct sensorhub_event big_events;
+ struct sensorhub_event big_send_events;
+ struct kfifo fifo;
+ bool is_big_event;
+ int event_number;
+ int pcm_cnt;
+ wait_queue_head_t sensorhub_wq;
+ spinlock_t sensorhub_lock;
+};
+
+int ssp_sensorhub_pcm_dump(struct ssp_sensorhub_data *hub_data);
+void ssp_sensorhub_report_notice(struct ssp_data *ssp_data, char notice);
+int ssp_sensorhub_handle_data(struct ssp_data *ssp_data, char *dataframe,
+ int start, int end);
+int ssp_sensorhub_initialize(struct ssp_data *ssp_data);
+void ssp_sensorhub_remove(struct ssp_data *ssp_data);
+
+#endif
diff --git a/drivers/sensorhub/stm/ssp_spi.c b/drivers/sensorhub/stm/ssp_spi.c
new file mode 100644
index 00000000000..039624c66a9
--- /dev/null
+++ b/drivers/sensorhub/stm/ssp_spi.c
@@ -0,0 +1,775 @@
+/*
+ * 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 LIMIT_DELAY_CNT 200
+#define RECEIVEBUFFERSIZE 12
+#define DEBUG_SHOW_DATA 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 iDelaycnt = 0;
+ bool msg_dead = false, ssp_down = false;
+ 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_int, 0);
+ while (gpio_get_value_cansleep(data->mcu_int2)) {
+ mdelay(3);
+ ssp_down = data->bSspShutdown;
+ if (ssp_down || iDelaycnt++ > 500) {
+ ssp_err("exit1 - Time out!!\n");
+ gpio_set_value_cansleep(data->ap_int, 1);
+ status = -1;
+ goto exit;
+ }
+ }
+
+ status = spi_write(data->spi, msg, 9) >= 0;
+ if (status == 0) {
+ ssp_err("spi_write fail!!\n");
+ gpio_set_value_cansleep(data->ap_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);
+ }
+
+ iDelaycnt = 0;
+ gpio_set_value_cansleep(data->ap_int, 1);
+ while (!gpio_get_value_cansleep(data->mcu_int2)) {
+ mdelay(3);
+ ssp_down = data->bSspShutdown;
+ if (ssp_down || iDelaycnt++ > 500) {
+ ssp_err("exit2 - Time out!!\n");
+ status = -2;
+ goto exit;
+ }
+ }
+
+exit:
+ mutex_unlock(&data->comm_mutex);
+
+ if (ssp_down)
+ ssp_err("ssp down");
+
+ if (status == -1) {
+ data->uTimeOutCnt += ssp_down ? 0 : 1;
+ clean_msg(msg);
+ return status;
+ }
+
+ if (status == 1 && 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 != 1)
+ msg->dead = true;
+ if (status == -2)
+ data->uTimeOutCnt += ssp_down ? 0 : 1;
+ }
+ mutex_unlock(&data->pending_mutex);
+
+ if (use_no_irq)
+ clean_msg(msg);
+
+ return status;
+}
+
+int ssp_spi_async(struct ssp_data *data, struct ssp_msg *msg)
+{
+ int status = 0;
+
+ status = do_transfer(data, msg, NULL, 0);
+
+ return status;
+}
+
+int ssp_spi_sync(struct ssp_data *data, struct ssp_msg *msg, int timeout)
+{
+ DECLARE_COMPLETION_ONSTACK(done);
+ int status = 0;
+
+ if (msg->length == 0) {
+ ssp_err("length must not be 0\n");
+ clean_msg(msg);
+ return status;
+ }
+
+ status = do_transfer(data, msg, &done, timeout);
+
+ return status;
+}
+
+int select_irq_msg(struct ssp_data *data)
+{
+ struct ssp_msg *msg, *n;
+ bool found = false;
+ u16 chLength = 0, msg_options = 0;
+ u8 msg_type = 0;
+ int iRet = 0;
+ char* buffer;
+ char chTempBuf[4] = { -1 };
+
+ iRet = spi_read(data->spi, chTempBuf, sizeof(chTempBuf));
+ if (iRet < 0) {
+ ssp_err("spi_read fail!!\n");
+ return ERROR;
+ }
+
+ memcpy(&msg_options, &chTempBuf[0], 2);
+ msg_type = msg_options & SSP_SPI_MASK;
+ memcpy(&chLength, &chTempBuf[2], 2);
+
+ 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) {
+ ssp_err("%d - Not match error\n", msg_options);
+ goto exit;
+ }
+
+ if (msg->dead && !msg->free_buffer) {
+ msg->buffer = (char*) kzalloc(msg->length, GFP_KERNEL);
+ msg->free_buffer = 1;
+ } /* For dead msg, make a temporary buffer to read. */
+
+ if (msg_type == AP2HUB_READ)
+ iRet = spi_read(data->spi, msg->buffer, msg->length);
+ if (msg_type == AP2HUB_WRITE) {
+ iRet = 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
+ ssp_err("List empty error(%d)\n", msg_type);
+exit:
+ mutex_unlock(&data->pending_mutex);
+ break;
+ case HUB2AP_WRITE:
+ buffer = (char*) kzalloc(chLength, GFP_KERNEL);
+ if (buffer == NULL) {
+ ssp_err("failed to alloc memory for buffer\n");
+ iRet = -ENOMEM;
+ break;
+ }
+ iRet = spi_read(data->spi, buffer, chLength);
+ if (iRet < 0)
+ ssp_err("spi_read fail\n");
+ else
+ parse_dataframe(data, buffer, chLength);
+ kfree(buffer);
+ break;
+ default:
+ ssp_err("No type error(%d)\n", msg_type);
+ break;
+ }
+
+ if (iRet < 0) {
+ ssp_err("MSG2SSP_SSD error %d\n", iRet);
+ return ERROR;
+ }
+
+ return SUCCESS;
+}
+
+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)
+{
+ int iRet = 0;
+
+ struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = command;
+ msg->length = 0;
+ msg->options = AP2HUB_WRITE;
+ msg->data = arg;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_async(data, msg);
+ if (iRet != SUCCESS) {
+ ssp_err("command 0x%x failed %d\n", command, iRet);
+ return ERROR;
+ }
+
+ ssp_dbg("command 0x%x %d\n", command, arg);
+
+ return SUCCESS;
+}
+
+int send_instruction(struct ssp_data *data, u8 uInst,
+ u8 uSensorType, u8 *uSendBuf, u8 uLength)
+{
+ char command;
+ int iRet = 0;
+ struct ssp_msg *msg;
+
+ if (data->fw_dl_state == FW_DL_STATE_DOWNLOADING) {
+ ssp_err("Skip Inst! DL state = %d\n", data->fw_dl_state);
+ return SUCCESS;
+ } else if ((!(data->uSensorState & (1 << uSensorType)))
+ && (uInst <= CHANGE_DELAY)) {
+ ssp_err("Bypass Inst Skip! - %u\n", uSensorType);
+ return FAIL;
+ }
+
+ switch (uInst) {
+ case REMOVE_SENSOR:
+ command = MSG2SSP_INST_BYPASS_SENSOR_REMOVE;
+ break;
+ case ADD_SENSOR:
+ case GET_LOGGING:
+ command = MSG2SSP_INST_BYPASS_SENSOR_ADD;
+ break;
+ case CHANGE_DELAY:
+ command = MSG2SSP_INST_CHANGE_DELAY;
+ break;
+ case GO_SLEEP:
+ command = MSG2SSP_AP_STATUS_SLEEP;
+ data->uLastAPState = MSG2SSP_AP_STATUS_SLEEP;
+ break;
+ case REMOVE_LIBRARY:
+ command = MSG2SSP_INST_LIBRARY_REMOVE;
+ break;
+ case ADD_LIBRARY:
+ command = MSG2SSP_INST_LIBRARY_ADD;
+ break;
+ default:
+ command = uInst;
+ break;
+ }
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = command;
+ msg->length = uLength + 2;
+ msg->options = AP2HUB_WRITE;
+ msg->buffer = (char*) kzalloc(uLength + 2, GFP_KERNEL);
+ msg->free_buffer = 1;
+
+ msg->buffer[0] = uSensorType;
+ memcpy(&msg->buffer[1], uSendBuf, uLength);
+ if (uSensorType == BIO_HRM_RAW_FAC)
+ msg->buffer[10] = 1;
+
+ ssp_dbg("Inst = 0x%x, Sensor Type = 0x%x, data = %u\n",
+ command, uSensorType, msg->buffer[1]);
+
+ iRet = ssp_spi_async(data, msg);
+
+ if (iRet != SUCCESS) {
+ ssp_err("Instruction CMD Fail %d\n", iRet);
+ return ERROR;
+ }
+
+ return iRet;
+}
+
+int send_instruction_sync(struct ssp_data *data, u8 uInst,
+ u8 uSensorType, u8 *uSendBuf, u8 uLength)
+{
+ char command;
+ int iRet = 0;
+ char buffer[10] = { 0, };
+ struct ssp_msg *msg;
+
+ if (data->fw_dl_state == FW_DL_STATE_DOWNLOADING) {
+ ssp_err("Skip Inst! DL state = %d\n", data->fw_dl_state);
+ return SUCCESS;
+ } else if ((!(data->uSensorState & (1 << uSensorType)))
+ && (uInst <= CHANGE_DELAY)) {
+ ssp_err("Bypass Inst Skip! - %u\n", uSensorType);
+ return FAIL;
+ }
+
+ switch (uInst) {
+ case REMOVE_SENSOR:
+ command = MSG2SSP_INST_BYPASS_SENSOR_REMOVE;
+ break;
+ case ADD_SENSOR:
+ command = MSG2SSP_INST_BYPASS_SENSOR_ADD;
+ break;
+ case CHANGE_DELAY:
+ command = MSG2SSP_INST_CHANGE_DELAY;
+ break;
+ case GO_SLEEP:
+ command = MSG2SSP_AP_STATUS_SLEEP;
+ data->uLastAPState = MSG2SSP_AP_STATUS_SLEEP;
+ break;
+ case REMOVE_LIBRARY:
+ command = MSG2SSP_INST_LIBRARY_REMOVE;
+ break;
+ case ADD_LIBRARY:
+ command = MSG2SSP_INST_LIBRARY_ADD;
+ break;
+ default:
+ command = uInst;
+ break;
+ }
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = command;
+ msg->length = uLength + 1;
+ msg->options = AP2HUB_WRITE | AP2HUB_RETURN;
+ msg->buffer = buffer;
+ msg->free_buffer = 0;
+
+ msg->buffer[0] = uSensorType;
+ memcpy(&msg->buffer[1], uSendBuf, uLength);
+
+ ssp_dbg("Inst = 0x%x, Sensor Type = %u, data = %u\n",
+ command, uSensorType, msg->buffer[0]);
+
+ iRet = ssp_spi_sync(data, msg, 1000);
+
+ if (iRet != SUCCESS) {
+ ssp_err("Instruction CMD Fail %d\n", iRet);
+ return ERROR;
+ }
+
+ return buffer[0];
+}
+
+int flush(struct ssp_data *data, u8 uSensorType)
+{
+ int iRet = 0;
+ char buffer = 0;
+
+ struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_MCU_BATCH_FLUSH;
+ msg->length = 1;
+ msg->options = AP2HUB_READ;
+ msg->data = uSensorType;
+ msg->buffer = &buffer;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_sync(data, msg, 1000);
+
+ if (iRet != SUCCESS) {
+ ssp_err("fail %d\n", iRet);
+ return ERROR;
+ }
+
+ ssp_dbg("Sensor Type = 0x%x, data = %u\n", uSensorType, buffer);
+
+ return buffer ? 0 : -1;
+}
+
+int get_batch_count(struct ssp_data *data, u8 uSensorType)
+{
+ int iRet = 0;
+ s32 result = 0;
+ char buffer[4] = { 0, };
+
+ struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_MCU_BATCH_COUNT;
+ msg->length = 4;
+ msg->options = AP2HUB_READ;
+ msg->data = uSensorType;
+ msg->buffer = buffer;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_sync(data, msg, 1000);
+
+ if (iRet != SUCCESS) {
+ ssp_err("fail %d\n", iRet);
+ return ERROR;
+ }
+
+ memcpy(&result, buffer, 4);
+
+ ssp_dbg("Sensor Type = 0x%x, data = %u\n", uSensorType, result);
+
+ return result;
+}
+
+int get_chipid(struct ssp_data *data)
+{
+ int iRet, iReties = 0;
+ char buffer = 0;
+ struct ssp_msg *msg;
+
+retries:
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_WHOAMI;
+ msg->length = 1;
+ msg->options = AP2HUB_READ;
+ msg->buffer = &buffer;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_sync(data, msg, 1000);
+
+ if (buffer != DEVICE_ID && iReties++ < 2) {
+ mdelay(5);
+ ssp_err("get chip ID retry\n");
+ goto retries;
+ }
+
+ if (iRet == SUCCESS)
+ return buffer;
+
+ ssp_err("get chip ID failed %d\n", iRet);
+ return ERROR;
+}
+
+int set_sensor_position(struct ssp_data *data)
+{
+ int iRet = 0;
+
+ struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_SENSOR_FORMATION;
+ msg->length = 3;
+ msg->options = AP2HUB_WRITE;
+ msg->buffer = (char*) kzalloc(3, GFP_KERNEL);
+ msg->free_buffer = 1;
+
+ msg->buffer[0] = data->accel_position;
+ msg->buffer[1] = data->accel_position;
+ msg->buffer[2] = data->mag_position;
+
+ iRet = ssp_spi_async(data, msg);
+
+ ssp_dbg("Sensor Posision A : %u, G : %u, M: %u, P: %u\n",
+ data->accel_position, data->accel_position, data->mag_position, 0);
+
+ if (iRet != SUCCESS) {
+ ssp_err("fail to set_sensor_position %d\n", iRet);
+ iRet = ERROR;
+ }
+
+ return iRet;
+}
+
+int set_magnetic_static_matrix(struct ssp_data *data)
+{
+ int iRet = 0;
+
+ struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX;
+ msg->length = data->mag_matrix_size;
+ msg->options = AP2HUB_WRITE;
+ msg->buffer = (char*) kzalloc(data->mag_matrix_size, GFP_KERNEL);
+ msg->free_buffer = 1;
+
+ memcpy(msg->buffer, data->mag_matrix, data->mag_matrix_size);
+
+ iRet = ssp_spi_async(data, msg);
+ if (iRet != SUCCESS) {
+ ssp_err("fail to set_magnetic_static_matrix %d\n", iRet);
+ iRet = ERROR;
+ }
+
+ return iRet;
+}
+
+unsigned int get_sensor_scanning_info(struct ssp_data *data)
+{
+ int iRet = 0, z = 0;
+ u32 result = 0;
+ char bin[SENSOR_MAX + 1];
+
+ struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_SENSOR_SCANNING;
+ msg->length = 4;
+ msg->options = AP2HUB_READ;
+ msg->buffer = (char*) &result;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_sync(data, msg, 1000);
+
+ if (iRet != SUCCESS)
+ ssp_err("i2c fail %d\n", iRet);
+
+ bin[SENSOR_MAX] = '\0';
+ for (z = 0; z < SENSOR_MAX; z++)
+ bin[SENSOR_MAX - 1 - z] = (result & (1 << z)) ? '1' : '0';
+ ssp_err("state: %s\n", bin);
+
+ return result;
+}
+
+unsigned int get_firmware_rev(struct ssp_data *data)
+{
+ int iRet;
+ u32 result = SSP_INVALID_REVISION;
+
+ struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_FIRMWARE_REV;
+ msg->length = 4;
+ msg->options = AP2HUB_READ;
+ msg->buffer = (char*) &result;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_sync(data, msg, 1000);
+ if (iRet != SUCCESS)
+ ssp_err("transfer fail %d\n", iRet);
+
+ return result;
+}
+
+int get_fuserom_data(struct ssp_data *data)
+{
+ int iRet = 0;
+ char buffer[3] = { 0, };
+
+ struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_FUSEROM;
+ msg->length = 3;
+ msg->options = AP2HUB_READ;
+ msg->buffer = buffer;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_sync(data, msg, 1000);
+
+ if (iRet) {
+ data->uFuseRomData[0] = buffer[0];
+ data->uFuseRomData[1] = buffer[1];
+ data->uFuseRomData[2] = buffer[2];
+ } else {
+ data->uFuseRomData[0] = 0;
+ data->uFuseRomData[1] = 0;
+ data->uFuseRomData[2] = 0;
+ return FAIL;
+ }
+
+ ssp_info("FUSE ROM Data %d , %d, %d\n", data->uFuseRomData[0],
+ data->uFuseRomData[1], data->uFuseRomData[2]);
+
+ return SUCCESS;
+}
+
+int set_big_data_start(struct ssp_data *data, u8 type, u32 length)
+{
+ int iRet = 0;
+
+ struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_START_BIG_DATA;
+ msg->length = 5;
+ msg->options = AP2HUB_WRITE;
+ msg->buffer = (char*) kzalloc(5, GFP_KERNEL);
+ msg->free_buffer = 1;
+
+ msg->buffer[0] = type;
+ memcpy(&msg->buffer[1], &length, 4);
+
+ iRet = ssp_spi_async(data, msg);
+
+ if (iRet != SUCCESS) {
+ ssp_err("i2c fail %d\n", iRet);
+ iRet = ERROR;
+ }
+
+ return iRet;
+}
+
+int set_time(struct ssp_data *data)
+{
+ int iRet;
+ struct ssp_msg *msg;
+ struct timespec ts;
+ struct rtc_time tm;
+
+ getnstimeofday(&ts);
+ rtc_time_to_tm(ts.tv_sec, &tm);
+ ssp_dbg("%d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_MCU_SET_TIME;
+ msg->length = 12;
+ msg->options = AP2HUB_WRITE;
+ msg->buffer = (char*) kzalloc(12, GFP_KERNEL);
+ msg->free_buffer = 1;
+
+ msg->buffer[0] = tm.tm_hour;
+ msg->buffer[1] = tm.tm_min;
+ msg->buffer[2] = tm.tm_sec;
+ msg->buffer[3] = tm.tm_hour > 11 ? 64 : 0;
+ msg->buffer[4] = tm.tm_wday;
+ msg->buffer[5] = tm.tm_mon + 1;
+ msg->buffer[6] = tm.tm_mday;
+ msg->buffer[7] = tm.tm_year % 100;
+ memcpy(&msg->buffer[8], &ts.tv_nsec, 4);
+
+ iRet = ssp_spi_async(data, msg);
+
+ if (iRet != SUCCESS) {
+ ssp_err("i2c fail %d\n", iRet);
+ iRet = ERROR;
+ }
+
+ return iRet;
+}
+
+int get_time(struct ssp_data *data)
+{
+ int iRet;
+ char buffer[12] = { 0, };
+ struct ssp_msg *msg;
+ struct timespec ts;
+ struct rtc_time tm;
+
+ getnstimeofday(&ts);
+ rtc_time_to_tm(ts.tv_sec, &tm);
+ ssp_dbg("ap %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (msg == NULL) {
+ ssp_err("failed to alloc memory for ssp_msg\n");
+ return -ENOMEM;
+ }
+ msg->cmd = MSG2SSP_AP_MCU_GET_TIME;
+ msg->length = 12;
+ msg->options = AP2HUB_READ;
+ msg->buffer = buffer;
+ msg->free_buffer = 0;
+
+ iRet = ssp_spi_sync(data, msg, 1000);
+
+ if (iRet != SUCCESS) {
+ ssp_err("i2c failed %d\n", iRet);
+ return 0;
+ }
+
+ tm.tm_hour = buffer[0];
+ tm.tm_min = buffer[1];
+ tm.tm_sec = buffer[2];
+ tm.tm_mon = msg->buffer[5] - 1;
+ tm.tm_mday = buffer[6];
+ tm.tm_year = buffer[7] + 100;
+ rtc_tm_to_time(&tm, &ts.tv_sec);
+ memcpy(&ts.tv_nsec, &msg->buffer[8], 4);
+
+ rtc_time_to_tm(ts.tv_sec, &tm);
+ ssp_dbg("mcu %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
+
+ return iRet;
+}
diff --git a/drivers/sensorhub/stm/ssp_sysfs.c b/drivers/sensorhub/stm/ssp_sysfs.c
new file mode 100644
index 00000000000..0501f7b4539
--- /dev/null
+++ b/drivers/sensorhub/stm/ssp_sysfs.c
@@ -0,0 +1,531 @@
+/*
+ * 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"
+
+/*************************************************************************/
+/* SSP data delay function */
+/*************************************************************************/
+
+int get_msdelay(int64_t dDelayRate) {
+ return div_s64(dDelayRate, 1000000);
+}
+
+static void enable_sensor(struct ssp_data *data,
+ int iSensorType, int64_t dNewDelay)
+{
+ u8 uBuf[9];
+ unsigned int uNewEnable = 0;
+ s32 maxBatchReportLatency = 0;
+ s8 batchOptions = 0;
+ int64_t dTempDelay = data->adDelayBuf[iSensorType];
+ s32 dMsDelay = get_msdelay(dNewDelay);
+ int ret = 0;
+
+ data->adDelayBuf[iSensorType] = dNewDelay;
+ maxBatchReportLatency = data->batchLatencyBuf[iSensorType];
+ batchOptions = data->batchOptBuf[iSensorType];
+
+ switch (data->aiCheckStatus[iSensorType]) {
+ case ADD_SENSOR_STATE:
+ ssp_dbg("add %u, New = %lldns\n", 1 << iSensorType, dNewDelay);
+
+ memcpy(&uBuf[0], &dMsDelay, 4);
+ memcpy(&uBuf[4], &maxBatchReportLatency, 4);
+ uBuf[8] = batchOptions;
+
+ ret = send_instruction(data, ADD_SENSOR, iSensorType, uBuf, 9);
+ ssp_info("delay %d, timeout %d, flag=%d, ret%d\n",
+ dMsDelay, maxBatchReportLatency, uBuf[8], ret);
+ if (ret <= 0) {
+ uNewEnable =
+ (unsigned int)atomic_read(&data->aSensorEnable)
+ & (~(unsigned int)(1 << iSensorType));
+ atomic_set(&data->aSensorEnable, uNewEnable);
+
+ data->aiCheckStatus[iSensorType] = NO_SENSOR_STATE;
+ break;
+ }
+
+ data->aiCheckStatus[iSensorType] = RUNNING_SENSOR_STATE;
+ break;
+ case RUNNING_SENSOR_STATE:
+ if (get_msdelay(dTempDelay)
+ == get_msdelay(data->adDelayBuf[iSensorType]))
+ break;
+
+ ssp_dbg("Change %u, New = %lldns\n",
+ 1 << iSensorType, dNewDelay);
+
+ memcpy(&uBuf[0], &dMsDelay, 4);
+ memcpy(&uBuf[4], &maxBatchReportLatency, 4);
+ uBuf[8] = batchOptions;
+ send_instruction(data, CHANGE_DELAY, iSensorType, uBuf, 9);
+
+ break;
+ default:
+ data->aiCheckStatus[iSensorType] = ADD_SENSOR_STATE;
+ }
+}
+
+static void change_sensor_delay(struct ssp_data *data,
+ int iSensorType, int64_t dNewDelay)
+{
+ u8 uBuf[9];
+ s32 maxBatchReportLatency = 0;
+ s8 batchOptions = 0;
+ int64_t dTempDelay = data->adDelayBuf[iSensorType];
+ s32 dMsDelay = get_msdelay(dNewDelay);
+
+ data->adDelayBuf[iSensorType] = dNewDelay;
+ data->batchLatencyBuf[iSensorType] = maxBatchReportLatency;
+ data->batchOptBuf[iSensorType] = batchOptions;
+
+ switch (data->aiCheckStatus[iSensorType]) {
+ case RUNNING_SENSOR_STATE:
+ if (get_msdelay(dTempDelay)
+ == get_msdelay(data->adDelayBuf[iSensorType]))
+ break;
+
+ ssp_dbg("Change %u, New = %lldns\n",
+ 1 << iSensorType, dNewDelay);
+
+ memcpy(&uBuf[0], &dMsDelay, 4);
+ memcpy(&uBuf[4], &maxBatchReportLatency, 4);
+ uBuf[8] = batchOptions;
+ send_instruction(data, CHANGE_DELAY, iSensorType, uBuf, 9);
+
+ break;
+ default:
+ break;
+ }
+}
+
+/*************************************************************************/
+/* SSP data enable function */
+/*************************************************************************/
+
+static int ssp_remove_sensor(struct ssp_data *data,
+ unsigned int uChangedSensor, unsigned int uNewEnable)
+{
+ u8 uBuf[4];
+ int64_t dSensorDelay = data->adDelayBuf[uChangedSensor];
+
+ ssp_dbg("remove sensor = %d, current state = %d\n",
+ (1 << uChangedSensor), uNewEnable);
+
+ data->adDelayBuf[uChangedSensor] = DEFUALT_POLLING_DELAY;
+ data->batchLatencyBuf[uChangedSensor] = 0;
+ data->batchOptBuf[uChangedSensor] = 0;
+
+ if (uChangedSensor == ORIENTATION_SENSOR) {
+ if (!(atomic_read(&data->aSensorEnable)
+ & (1 << ACCELEROMETER_SENSOR))) {
+ uChangedSensor = ACCELEROMETER_SENSOR;
+ } else {
+ change_sensor_delay(data, ACCELEROMETER_SENSOR,
+ data->adDelayBuf[ACCELEROMETER_SENSOR]);
+ return 0;
+ }
+ } else if (uChangedSensor == ACCELEROMETER_SENSOR) {
+ if (atomic_read(&data->aSensorEnable)
+ & (1 << ORIENTATION_SENSOR)) {
+ change_sensor_delay(data, ORIENTATION_SENSOR,
+ data->adDelayBuf[ORIENTATION_SENSOR]);
+ return 0;
+ }
+ }
+
+ if (!data->bSspShutdown)
+ if (atomic_read(&data->aSensorEnable) & (1 << uChangedSensor)) {
+ s32 dMsDelay = get_msdelay(dSensorDelay);
+ memcpy(&uBuf[0], &dMsDelay, 4);
+
+ send_instruction(data, REMOVE_SENSOR, uChangedSensor, uBuf, 4);
+ }
+ data->aiCheckStatus[uChangedSensor] = NO_SENSOR_STATE;
+
+ return 0;
+}
+
+/*************************************************************************/
+/* ssp Sysfs */
+/*************************************************************************/
+
+static ssize_t show_enable_irq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("%d\n", !data->bSspShutdown);
+
+ return sprintf(buf, "%d\n", !data->bSspShutdown);
+}
+
+static ssize_t set_enable_irq(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ u8 dTemp;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (kstrtou8(buf, 10, &dTemp) < 0)
+ return -1;
+
+ ssp_dbg("%d start\n", dTemp);
+ if (dTemp) {
+ reset_mcu(data);
+ enable_debug_timer(data);
+ } else if (!dTemp) {
+ disable_debug_timer(data);
+ ssp_enable(data, 0);
+ } else
+ ssp_err("invalid value\n");
+ ssp_info("%d end\n", dTemp);
+ return size;
+}
+
+static ssize_t show_sensors_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("cur_enable = %d\n", atomic_read(&data->aSensorEnable));
+
+ return sprintf(buf, "%9u\n", atomic_read(&data->aSensorEnable));
+}
+
+static ssize_t set_sensors_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int64_t dTemp;
+ unsigned int uNewEnable = 0, uChangedSensor = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (kstrtoll(buf, 10, &dTemp) < 0)
+ return -EINVAL;
+
+ uNewEnable = (unsigned int)dTemp;
+ ssp_dbg("new_enable = %u, old_enable = %u\n",
+ uNewEnable, atomic_read(&data->aSensorEnable));
+
+ if (uNewEnable == atomic_read(&data->aSensorEnable))
+ return size;
+
+ for (uChangedSensor = 0; uChangedSensor < SENSOR_MAX; uChangedSensor++) {
+ if ((atomic_read(&data->aSensorEnable) & (1 << uChangedSensor))
+ != (uNewEnable & (1 << uChangedSensor))) {
+
+ if (!(uNewEnable & (1 << uChangedSensor))) {
+ ssp_remove_sensor(data, uChangedSensor,
+ uNewEnable); /* disable */
+ } else { /* Change to ADD_SENSOR_STATE from KitKat */
+ if (data->aiCheckStatus[uChangedSensor]
+ == INITIALIZATION_STATE) {
+ if (uChangedSensor == ACCELEROMETER_SENSOR) {
+ accel_open_calibration(data);
+ set_accel_cal(data);
+ } else if (uChangedSensor == GYROSCOPE_SENSOR) {
+ gyro_open_calibration(data);
+ set_gyro_cal(data);
+ }
+ }
+ data->aiCheckStatus[uChangedSensor] = ADD_SENSOR_STATE;
+ enable_sensor(data, uChangedSensor,
+ data->adDelayBuf[uChangedSensor]);
+ }
+ }
+ }
+ atomic_set(&data->aSensorEnable, uNewEnable);
+
+ return size;
+}
+
+
+
+static ssize_t set_flush(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int64_t dTemp;
+ u8 sensor_type = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (kstrtoll(buf, 10, &dTemp) < 0)
+ return -EINVAL;
+
+ sensor_type = (u8)dTemp;
+ if (!(atomic_read(&data->aSensorEnable) & (1 << sensor_type)))
+ return -EINVAL;
+
+ if (flush(data, sensor_type) < 0) {
+ ssp_err("ssp returns error for flush(%x)", sensor_type);
+ return -EINVAL;
+ }
+ return size;
+}
+
+static ssize_t show_acc_delay(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld\n", data->adDelayBuf[ACCELEROMETER_SENSOR]);
+}
+
+static ssize_t set_acc_delay(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int64_t dNewDelay;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (kstrtoll(buf, 10, &dNewDelay) < 0)
+ return -EINVAL;
+
+ if ((atomic_read(&data->aSensorEnable) & (1 << ORIENTATION_SENSOR)) &&
+ (data->adDelayBuf[ORIENTATION_SENSOR] < dNewDelay))
+ data->adDelayBuf[ACCELEROMETER_SENSOR] = dNewDelay;
+ else
+ change_sensor_delay(data, ACCELEROMETER_SENSOR, dNewDelay);
+
+ return size;
+}
+
+static ssize_t show_gyro_delay(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld\n", data->adDelayBuf[GYROSCOPE_SENSOR]);
+}
+
+static ssize_t set_gyro_delay(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int64_t dNewDelay;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (kstrtoll(buf, 10, &dNewDelay) < 0)
+ return -EINVAL;
+
+ change_sensor_delay(data, GYROSCOPE_SENSOR, dNewDelay);
+ return size;
+}
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+static ssize_t show_hrm_delay(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld\n", data->adDelayBuf[BIO_HRM_RAW]);
+}
+
+static ssize_t set_hrm_delay(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int64_t dNewDelay;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (kstrtoll(buf, 10, &dNewDelay) < 0)
+ return -EINVAL;
+
+ change_sensor_delay(data, BIO_HRM_RAW, dNewDelay);
+ change_sensor_delay(data, BIO_HRM_RAW_FAC, dNewDelay);
+
+ return size;
+}
+#endif
+
+ssize_t ssp_sensorhub_voicel_pcmdump_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+ int status = ssp_sensorhub_pcm_dump(data->hub_data);
+
+ return sprintf(buf, "%s\n", (status ? "OK" : "NG"));
+}
+
+static DEVICE_ATTR(voice_pcmdump, S_IRUGO,
+ ssp_sensorhub_voicel_pcmdump_show, NULL);
+
+static struct device_attribute *voice_attrs[] = {
+ &dev_attr_voice_pcmdump,
+ NULL,
+};
+
+static void initialize_voice_sysfs(struct ssp_data *data)
+{
+ sensors_register(data->voice_device, data, voice_attrs, "ssp_voice");
+}
+
+static void remove_voice_sysfs(struct ssp_data *data)
+{
+ sensors_unregister(data->voice_device, voice_attrs);
+}
+
+static DEVICE_ATTR(mcu_rev, S_IRUGO, mcu_revision_show, NULL);
+static DEVICE_ATTR(mcu_name, S_IRUGO, mcu_model_name_show, NULL);
+static DEVICE_ATTR(mcu_update, S_IRUGO, mcu_update_kernel_bin_show, NULL);
+static DEVICE_ATTR(mcu_update2, S_IRUGO,
+ mcu_update_kernel_crashed_bin_show, NULL);
+static DEVICE_ATTR(mcu_update_ums, S_IRUGO, mcu_update_ums_bin_show, NULL);
+static DEVICE_ATTR(mcu_reset, S_IRUGO, mcu_reset_show, NULL);
+static DEVICE_ATTR(mcu_dump, S_IRUGO, mcu_dump_show, NULL);
+
+static DEVICE_ATTR(mcu_test, S_IRUGO | S_IWUSR | S_IWGRP,
+ mcu_factorytest_show, mcu_factorytest_store);
+static DEVICE_ATTR(mcu_sleep_test, S_IRUGO | S_IWUSR | S_IWGRP,
+ mcu_sleep_factorytest_show, mcu_sleep_factorytest_store);
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_sensors_enable, set_sensors_enable);
+static DEVICE_ATTR(enable_irq, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_enable_irq, set_enable_irq);
+static DEVICE_ATTR(accel_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_acc_delay, set_acc_delay);
+static DEVICE_ATTR(gyro_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_gyro_delay, set_gyro_delay);
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+static DEVICE_ATTR(hrm_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_hrm_delay, set_hrm_delay);
+#endif
+static DEVICE_ATTR(ssp_flush, S_IWUSR | S_IWGRP,
+ NULL, set_flush);
+
+static struct device_attribute *mcu_attrs[] = {
+ &dev_attr_enable,
+ &dev_attr_mcu_rev,
+ &dev_attr_mcu_name,
+ &dev_attr_mcu_test,
+ &dev_attr_mcu_reset,
+ &dev_attr_mcu_dump,
+ &dev_attr_mcu_update,
+ &dev_attr_mcu_update2,
+ &dev_attr_mcu_update_ums,
+ &dev_attr_mcu_sleep_test,
+ &dev_attr_enable_irq,
+ &dev_attr_accel_poll_delay,
+ &dev_attr_gyro_poll_delay,
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ &dev_attr_hrm_poll_delay,
+#endif
+ &dev_attr_ssp_flush,
+ NULL,
+};
+
+static struct device_attribute dev_attr_input_accel_poll_delay
+ = __ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_acc_delay, set_acc_delay);
+
+static struct device_attribute dev_attr_input_gyro_poll_delay
+ = __ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_gyro_delay, set_gyro_delay);
+
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+static struct device_attribute dev_attr_input_hrm_poll_delay
+ = __ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_hrm_delay, set_hrm_delay);
+#endif
+
+static void initialize_mcu_factorytest(struct ssp_data *data)
+{
+ sensors_register(data->mcu_device, data, mcu_attrs, "ssp_sensor");
+}
+
+static void remove_mcu_factorytest(struct ssp_data *data)
+{
+ sensors_unregister(data->mcu_device, mcu_attrs);
+}
+
+static int initialize_input_poll_delay_sysfs(struct ssp_data *data)
+{
+ int iRet;
+
+ iRet = device_create_file(&data->acc_input_dev->dev,
+ &dev_attr_input_accel_poll_delay);
+
+ if (iRet < 0)
+ goto err_create_acc_poll_delay;
+
+ iRet = device_create_file(&data->gyro_input_dev->dev,
+ &dev_attr_input_gyro_poll_delay);
+
+ if (iRet < 0)
+ goto err_create_gyro_poll_delay;
+
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ iRet = device_create_file(&data->hrm_raw_input_dev->dev,
+ &dev_attr_input_hrm_poll_delay);
+
+ if (iRet < 0)
+ goto err_create_hrm_poll_delay;
+#endif
+ return SUCCESS;
+
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+err_create_hrm_poll_delay:
+ ssp_err("could not create hrm poll delay node\n");
+ device_remove_file(&data->gyro_input_dev->dev,
+ &dev_attr_gyro_poll_delay);
+#endif
+err_create_gyro_poll_delay:
+ ssp_err("could not create gyro poll delay node\n");
+ device_remove_file(&data->acc_input_dev->dev,
+ &dev_attr_accel_poll_delay);
+err_create_acc_poll_delay:
+ ssp_err("could not create accel poll delay node\n");
+
+ return ERROR;
+}
+
+static void remove_input_poll_delay_sysfs(struct ssp_data *data)
+{
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ device_remove_file(&data->hrm_raw_input_dev->dev,
+ &dev_attr_hrm_poll_delay);
+#endif
+ device_remove_file(&data->gyro_input_dev->dev,
+ &dev_attr_gyro_poll_delay);
+ device_remove_file(&data->acc_input_dev->dev,
+ &dev_attr_accel_poll_delay);
+}
+
+int initialize_sysfs(struct ssp_data *data)
+{
+ initialize_accel_factorytest(data);
+ initialize_gyro_factorytest(data);
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ initialize_hrm_factorytest(data);
+#endif
+ initialize_mcu_factorytest(data);
+
+ /*snamy.jeong_0630 voice dump & data*/
+ initialize_voice_sysfs(data);
+
+ initialize_input_poll_delay_sysfs(data);
+
+ return SUCCESS;
+}
+
+void remove_sysfs(struct ssp_data *data)
+{
+ remove_accel_factorytest(data);
+ remove_gyro_factorytest(data);
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ remove_hrm_factorytest(data);
+#endif
+ remove_mcu_factorytest(data);
+ /*snamy.jeong_0630 voice dump & data*/
+ remove_voice_sysfs(data);
+ remove_input_poll_delay_sysfs(data);
+
+ destroy_sensor_class();
+}
diff --git a/include/linux/ssp_platformdata.h b/include/linux/ssp_platformdata.h
new file mode 100644
index 00000000000..fac0b909af3
--- /dev/null
+++ b/include/linux/ssp_platformdata.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics. 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
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef _SSP_PLATFORMDATA_H_
+#define _SSP_PLATFORMDATA_H_
+
+struct ssp_platform_data {
+ int (*set_mcu_reset)(struct device *, int, int);
+ int (*check_ap_rev)(void);
+ void (*get_positions)(int *, int *);
+ int (*check_lpmode)(void);
+#ifdef CONFIG_SENSORS_SSP_ADPD142
+ int (*hrm_sensor_power)(int);
+#endif
+ u8 mag_matrix_size;
+ u8 *mag_matrix;
+ int ap_int;
+ int mcu_int1;
+ int mcu_int2;
+ int rst;
+ int irq;
+};
+#endif