sensorhub: add sensorhub driver
authorJaewon Kim <jaewon02.kim@samsung.com>
Tue, 15 Jul 2014 07:05:25 +0000 (16:05 +0900)
committerChanho Park <chanho61.park@samsung.com>
Tue, 18 Nov 2014 03:00:17 +0000 (12:00 +0900)
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>
28 files changed:
drivers/Kconfig
drivers/Makefile
drivers/sensorhub/Kconfig [new file with mode: 0644]
drivers/sensorhub/Makefile [new file with mode: 0644]
drivers/sensorhub/stm/Kconfig [new file with mode: 0644]
drivers/sensorhub/stm/Makefile [new file with mode: 0644]
drivers/sensorhub/stm/factory/accel_k330.c [new file with mode: 0644]
drivers/sensorhub/stm/factory/accel_mpu6050.c [new file with mode: 0644]
drivers/sensorhub/stm/factory/accel_mpu6500.c [new file with mode: 0644]
drivers/sensorhub/stm/factory/gyro_k330.c [new file with mode: 0644]
drivers/sensorhub/stm/factory/gyro_mpu6050.c [new file with mode: 0644]
drivers/sensorhub/stm/factory/gyro_mpu6500.c [new file with mode: 0644]
drivers/sensorhub/stm/factory/hrm_adpd142.c [new file with mode: 0644]
drivers/sensorhub/stm/factory/mcu_at32uc3l0128.c [new file with mode: 0644]
drivers/sensorhub/stm/factory/mcu_atuc128l5har.c [new file with mode: 0644]
drivers/sensorhub/stm/factory/mcu_stm32f401.c [new file with mode: 0644]
drivers/sensorhub/stm/sensors_core.c [new file with mode: 0644]
drivers/sensorhub/stm/ssp.h [new file with mode: 0644]
drivers/sensorhub/stm/ssp_data.c [new file with mode: 0644]
drivers/sensorhub/stm/ssp_debug.c [new file with mode: 0644]
drivers/sensorhub/stm/ssp_dev.c [new file with mode: 0644]
drivers/sensorhub/stm/ssp_firmware.c [new file with mode: 0644]
drivers/sensorhub/stm/ssp_input.c [new file with mode: 0644]
drivers/sensorhub/stm/ssp_sensorhub.c [new file with mode: 0644]
drivers/sensorhub/stm/ssp_sensorhub.h [new file with mode: 0644]
drivers/sensorhub/stm/ssp_spi.c [new file with mode: 0644]
drivers/sensorhub/stm/ssp_sysfs.c [new file with mode: 0644]
include/linux/ssp_platformdata.h [new file with mode: 0644]

index 2943fb6..20896ff 100644 (file)
@@ -168,4 +168,6 @@ source "drivers/reset/Kconfig"
 
 source "drivers/phy/Kconfig"
 
+source "drivers/sensorhub/Kconfig"
+
 endmenu
index 46c99a1..affabdb 100644 (file)
@@ -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 (file)
index 0000000..a1abcce
--- /dev/null
@@ -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 (file)
index 0000000..57cb83b
--- /dev/null
@@ -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 (file)
index 0000000..51e33e2
--- /dev/null
@@ -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 (file)
index 0000000..8b1dd6b
--- /dev/null
@@ -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 (file)
index 0000000..8ab4e4d
--- /dev/null
@@ -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 (file)
index 0000000..1d54924
--- /dev/null
@@ -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 (file)
index 0000000..06d8fa1
--- /dev/null
@@ -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 (file)
index 0000000..c4a73ea
--- /dev/null
@@ -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 (file)
index 0000000..e3ba3dd
--- /dev/null
@@ -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 (file)
index 0000000..0601070
--- /dev/null
@@ -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 (file)
index 0000000..adf8854
--- /dev/null
@@ -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 (file)
index 0000000..d84fc06
--- /dev/null
@@ -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 (file)
index 0000000..5d45d81
--- /dev/null
@@ -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 (file)
index 0000000..4b34079
--- /dev/null
@@ -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 (file)
index 0000000..ca0dcf9
--- /dev/null
@@ -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 (file)
index 0000000..49cdc80
--- /dev/null
@@ -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 (file)
index 0000000..a25bcf5
--- /dev/null
@@ -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 (file)
index 0000000..f7ac63a
--- /dev/null
@@ -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 (file)
index 0000000..d551aa8
--- /dev/null
@@ -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 (file)
index 0000000..d692a1f
--- /dev/null
@@ -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 (file)
index 0000000..072f573
--- /dev/null
@@ -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 (file)
index 0000000..dfeeb4a
--- /dev/null
@@ -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 (file)
index 0000000..a3320fc
--- /dev/null
@@ -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 (file)
index 0000000..039624c
--- /dev/null
@@ -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 (file)
index 0000000..0501f7b
--- /dev/null
@@ -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 (file)
index 0000000..fac0b90
--- /dev/null
@@ -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