aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sensorhub
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sensorhub')
-rw-r--r--drivers/sensorhub/Kconfig133
-rw-r--r--drivers/sensorhub/Makefile21
-rw-r--r--drivers/sensorhub/factory/accel_lsm330.c300
-rw-r--r--drivers/sensorhub/factory/gyro_lsm330.c307
-rw-r--r--drivers/sensorhub/factory/light_cm36651.c74
-rw-r--r--drivers/sensorhub/factory/magnetic_ak8963c.c221
-rw-r--r--drivers/sensorhub/factory/mcu_at32uc3l0128.c257
-rw-r--r--drivers/sensorhub/factory/pressure_bmp182.c190
-rw-r--r--drivers/sensorhub/factory/prox_cm36651.c373
-rw-r--r--drivers/sensorhub/sensors_core.c96
-rw-r--r--drivers/sensorhub/ssp.h410
-rw-r--r--drivers/sensorhub/ssp_ak8963c.c105
-rw-r--r--drivers/sensorhub/ssp_data.c276
-rw-r--r--drivers/sensorhub/ssp_debug.c165
-rw-r--r--drivers/sensorhub/ssp_dev.c457
-rw-r--r--drivers/sensorhub/ssp_firmware.c303
-rw-r--r--drivers/sensorhub/ssp_i2c.c471
-rw-r--r--drivers/sensorhub/ssp_input.c383
-rw-r--r--drivers/sensorhub/ssp_sensorhub.c444
-rw-r--r--drivers/sensorhub/ssp_sysfs.c478
20 files changed, 5464 insertions, 0 deletions
diff --git a/drivers/sensorhub/Kconfig b/drivers/sensorhub/Kconfig
new file mode 100644
index 0000000..609e854
--- /dev/null
+++ b/drivers/sensorhub/Kconfig
@@ -0,0 +1,133 @@
+#
+# 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 I2C
+ 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_LSM330
+ tristate "Sensors ssp lsm330"
+ default n
+ depends on I2C
+ help
+ lsm330 file for factory test in ssp driver.
+ If you say yes here you get lsm330 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_AK8963C
+ tristate "Sensors ssp ak8963c"
+ default n
+ depends on I2C
+ help
+ ak8963c file for factory test in ssp driver.
+ If you say yes here you get ak8963c support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_CM36651
+ tristate "Sensors ssp cm36651"
+ default n
+ depends on I2C
+ help
+ cm36651 file for factory test in ssp driver.
+ If you say yes here you get cm36651 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_BMP182
+ tristate "Sensors ssp bmp182"
+ default n
+ depends on I2C
+ help
+ bmp182 file for factory test in ssp driver.
+ If you say yes here you get bmp182 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_AT32UC3L0128
+ tristate "Sensors ssp at32uc3l0128"
+ default n
+ depends on I2C
+ help
+ at32uc3l0128 file for factory test in ssp driver.
+ If you say yes here you get at32uc3l0128 support for
+ factory test.
+ To compile this driver as a module, choose M here: the
+ module will be called ssp.
+
+config SENSORS_SSP_ACCELEROMETER_POSITION
+ int "Accelerometer Sensor Mounting Position on Board"
+ depends on SENSORS_SSP
+ default "0"
+ help
+ X Y Z axis position.
+ 0: X, Y, Z
+ 1: Y,-X, Z
+ 2: -X,-Y, Z
+ 3: -Y, X, Z
+ 4: -X, Y,-Z
+ 5: Y, X,-Z
+ 6: X,-Y,-Z
+ 7: -Y,-X,-Z
+
+config SENSORS_SSP_GYROSCOPE_POSITION
+ int "Gyroscope Sensor Mounting Position on Board"
+ depends on SENSORS_SSP
+ default "0"
+ help
+ X Y Z axis position.
+ 0: X, Y, Z
+ 1: Y,-X, Z
+ 2: -X,-Y, Z
+ 3: -Y, X, Z
+ 4: -X, Y,-Z
+ 5: Y, X,-Z
+ 6: X,-Y,-Z
+ 7: -Y,-X,-Z
+
+config SENSORS_SSP_MAGNETOMETER_POSITION
+ int "Geomagnetic Sensor Mounting Position on Board"
+ depends on SENSORS_SSP
+ default "0"
+ help
+ X Y Z axis position.
+ 0: X, Y, Z
+ 1: Y,-X, Z
+ 2: -X,-Y, Z
+ 3: -Y, X, Z
+ 4: -X, Y,-Z
+ 5: Y, X,-Z
+ 6: X,-Y,-Z
+ 7: -Y,-X,-Z
+
+config SENSORS_SSP_SENSORHUB
+ tristate "Sensors ssp sensorhub"
+ default n
+ depends on I2C
+ 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/Makefile b/drivers/sensorhub/Makefile
new file mode 100644
index 0000000..11f8e8e
--- /dev/null
+++ b/drivers/sensorhub/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for the sensor drivers.
+#
+
+# Each configuration option enables a list of files.
+obj-$(CONFIG_SENSORS_SSP) += ssp_dev.o ssp_i2c.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_LSM330) += factory/accel_lsm330.o factory/gyro_lsm330.o
+
+obj-$(CONFIG_SENSORS_SSP_CM36651) += factory/light_cm36651.o factory/prox_cm36651.o
+
+obj-$(CONFIG_SENSORS_SSP_AK8963C) += ssp_ak8963c.o factory/magnetic_ak8963c.o
+
+obj-$(CONFIG_SENSORS_SSP_BMP182) += factory/pressure_bmp182.o
+
+obj-$(CONFIG_SENSORS_SSP_AT32UC3L0128) += factory/mcu_at32uc3l0128.o
+
+obj-$(CONFIG_SENSORS_SSP_SENSORHUB) += ssp_sensorhub.o
diff --git a/drivers/sensorhub/factory/accel_lsm330.c b/drivers/sensorhub/factory/accel_lsm330.c
new file mode 100644
index 0000000..71bec9e
--- /dev/null
+++ b/drivers/sensorhub/factory/accel_lsm330.c
@@ -0,0 +1,300 @@
+/*
+ * 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 "LSM330"
+
+#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("[SSP]: 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("[SSP]: 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)) {
+ pr_err("[SSP]: %s - Can't open calibration file\n", __func__);
+ 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)) {
+ pr_err("[SSP]: %s - Can't write the accelcal to file\n",
+ __func__);
+ 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)
+ pr_err("[SSP]: %s - calibration open failed\n", __func__);
+
+ ssp_dbg("[SSP] 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 = strict_strtoll(buf, 10, &dEnable);
+ if (iRet < 0)
+ return iRet;
+
+ iRet = accel_do_calibrate(data, (int)dEnable);
+ if (iRet < 0)
+ pr_err("[SSP]: %s - accel_do_calibrate() failed\n", __func__);
+
+ 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("[SSP]: %s - on\n", __func__);
+ else if (sysfs_streq(buf, "0"))
+ ssp_dbg("[SSP]: %s - off\n", __func__);
+ else if (sysfs_streq(buf, "2")) {
+ ssp_dbg("[SSP]: %s - factory\n", __func__);
+
+ 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)) {
+ pr_err("[SSP]: %s - accel Selftest Timeout!!\n",
+ __func__);
+ goto exit;
+ }
+
+ data->bAccelAlert = data->uFactorydata[0];
+ ssp_dbg("[SSP]: %s factory test success!\n", __func__);
+ } else {
+ pr_err("[SSP]: %s - invalid value %d\n", __func__, *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)
+{
+ struct device *acc_device = NULL;
+
+ sensors_register(acc_device, data, acc_attrs, "accelerometer_sensor");
+}
diff --git a/drivers/sensorhub/factory/gyro_lsm330.c b/drivers/sensorhub/factory/gyro_lsm330.c
new file mode 100644
index 0000000..11b0ac4
--- /dev/null
+++ b/drivers/sensorhub/factory/gyro_lsm330.c
@@ -0,0 +1,307 @@
+/*
+ * 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 "LSM330"
+
+#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("[SSP]: 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("[SSP]: 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)) {
+ pr_err("[SSP]: %s - Can't open calibration file\n", __func__);
+ 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)) {
+ pr_err("[SSP]: %s - Can't write gyro cal to file\n", __func__);
+ 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)
+{
+ ssp_dbg("[SSP]: %s\n", __func__);
+
+ return sprintf(buf, "%d\n", 1);
+}
+
+static ssize_t gyro_power_on(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssp_dbg("[SSP]: %s\n", __func__);
+
+ 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);
+
+ 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)) {
+ pr_err("[SSP]: %s - Gyro Temp Timeout!!\n", __func__);
+ goto exit;
+ }
+
+ chTemp = (char)data->uFactorydata[0];
+ ssp_dbg("[SSP]: %s - %d\n", __func__, 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,};
+ int iZeroRateData[3] = {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++ < 150)
+ && (iRet == SUCCESS))
+ msleep(20);
+
+ if ((iDelayCnt >= 150) || (iRet != SUCCESS)) {
+ pr_err("[SSP]: %s - Gyro Selftest Timeout!!\n", __func__);
+ goto exit;
+ }
+
+ 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]);
+
+ uCalPass = data->uFactorydata[24];
+ uFifoPass = data->uFactorydata[25];
+ uBypassPass = data->uFactorydata[26];
+
+ if (uFifoPass && uBypassPass && uCalPass)
+ save_gyro_caldata(data, iCalData);
+
+exit:
+ ssp_dbg("[SSP]: Gyro Selftest - %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],
+ uFifoPass & uBypassPass & uCalPass, uFifoPass, uCalPass);
+
+ return sprintf(buf, "%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],
+ 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);
+
+ 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)) {
+ pr_err("[SSP]: %s - Gyro Selftest DPS Timeout!!\n", __func__);
+ goto exit;
+ }
+
+ if (data->uFactorydata[0] != SUCCESS) {
+ pr_err("[SSP]: %s - Gyro Selftest DPS Error!!\n", __func__);
+ goto exit;
+ }
+
+ data->uGyroDps = (unsigned int)iNewDps;
+ pr_err("[SSP]: %s - %u dps stored\n", __func__, 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)
+{
+ struct device *gyro_device = NULL;
+
+ sensors_register(gyro_device, data, gyro_attrs, "gyro_sensor");
+}
diff --git a/drivers/sensorhub/factory/light_cm36651.c b/drivers/sensorhub/factory/light_cm36651.c
new file mode 100644
index 0000000..17ad68a
--- /dev/null
+++ b/drivers/sensorhub/factory/light_cm36651.c
@@ -0,0 +1,74 @@
+/*
+ * 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 VENDOR "CAPELLA"
+#define CHIP_ID "CM36651"
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+static ssize_t light_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VENDOR);
+}
+
+static ssize_t light_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", CHIP_ID);
+}
+
+static ssize_t light_lux_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u,%u,%u,%u\n",
+ data->buf[LIGHT_SENSOR].r, data->buf[LIGHT_SENSOR].g,
+ data->buf[LIGHT_SENSOR].b, data->buf[LIGHT_SENSOR].w);
+}
+
+static ssize_t light_data_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u,%u,%u,%u\n",
+ data->buf[LIGHT_SENSOR].r, data->buf[LIGHT_SENSOR].g,
+ data->buf[LIGHT_SENSOR].b, data->buf[LIGHT_SENSOR].w);
+}
+
+static DEVICE_ATTR(vendor, S_IRUGO, light_vendor_show, NULL);
+static DEVICE_ATTR(name, S_IRUGO, light_name_show, NULL);
+static DEVICE_ATTR(lux, S_IRUGO, light_lux_show, NULL);
+static DEVICE_ATTR(raw_data, S_IRUGO, light_data_show, NULL);
+
+static struct device_attribute *light_attrs[] = {
+ &dev_attr_vendor,
+ &dev_attr_name,
+ &dev_attr_lux,
+ &dev_attr_raw_data,
+ NULL,
+};
+
+void initialize_light_factorytest(struct ssp_data *data)
+{
+ struct device *light_device = NULL;
+
+ sensors_register(light_device, data, light_attrs, "light_sensor");
+}
diff --git a/drivers/sensorhub/factory/magnetic_ak8963c.c b/drivers/sensorhub/factory/magnetic_ak8963c.c
new file mode 100644
index 0000000..4a72006
--- /dev/null
+++ b/drivers/sensorhub/factory/magnetic_ak8963c.c
@@ -0,0 +1,221 @@
+/*
+ * 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 "AKM"
+#define CHIP_ID "AK8963C"
+
+static ssize_t magnetic_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VENDOR);
+}
+
+static ssize_t magnetic_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", CHIP_ID);
+}
+
+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[GEOMAGNETIC_SENSOR].x,
+ data->buf[GEOMAGNETIC_SENSOR].y,
+ data->buf[GEOMAGNETIC_SENSOR].z);
+}
+
+static ssize_t adc_data_read(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ u8 chTempbuf[2] = {1, 20};
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (!(atomic_read(&data->aSensorEnable) & (1 << GEOMAGNETIC_SENSOR))) {
+ send_instruction(data, ADD_SENSOR, GEOMAGNETIC_SENSOR,
+ chTempbuf, 2);
+ msleep(200);
+ }
+
+ if ((data->buf[GEOMAGNETIC_SENSOR].x == 0) &&
+ (data->buf[GEOMAGNETIC_SENSOR].y == 0) &&
+ (data->buf[GEOMAGNETIC_SENSOR].z == 0))
+ bSuccess = false;
+ else
+ bSuccess = true;
+
+ if (!(atomic_read(&data->aSensorEnable) & (1 << GEOMAGNETIC_SENSOR)))
+ send_instruction(data, REMOVE_SENSOR, GEOMAGNETIC_SENSOR,
+ chTempbuf, 2);
+
+ pr_info("[SSP]: %s - x = %d, y = %d, z = %d\n", __func__,
+ data->buf[GEOMAGNETIC_SENSOR].x,
+ data->buf[GEOMAGNETIC_SENSOR].y,
+ data->buf[GEOMAGNETIC_SENSOR].z);
+
+ return sprintf(buf, "%s,%d,%d,%d\n", (bSuccess ? "OK" : "NG"),
+ data->buf[GEOMAGNETIC_SENSOR].x,
+ data->buf[GEOMAGNETIC_SENSOR].y,
+ data->buf[GEOMAGNETIC_SENSOR].z);
+}
+
+static ssize_t magnetic_get_asa(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d,%d,%d\n", (s16)data->uFuseRomData[0],
+ (s16)data->uFuseRomData[1], (s16)data->uFuseRomData[2]);
+}
+
+static ssize_t magnetic_get_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if ((data->uFuseRomData[0] == 0) ||
+ (data->uFuseRomData[0] == 0xff) ||
+ (data->uFuseRomData[1] == 0) ||
+ (data->uFuseRomData[1] == 0xff) ||
+ (data->uFuseRomData[2] == 0) ||
+ (data->uFuseRomData[2] == 0xff))
+ bSuccess = false;
+ else
+ bSuccess = true;
+
+ return sprintf(buf, "%s,%u\n", (bSuccess ? "OK" : "NG"), bSuccess);
+}
+
+static ssize_t magnetic_get_selftest(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSelftestPassed = false;
+ s16 iSF_X = 0, iSF_Y = 0, iSF_Z = 0;
+ int iDelayCnt = 0, iRet = 0, iReties = 0;
+ char chTempBuf[2] = { 0, 10 };
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+reties:
+ iDelayCnt = 0;
+ data->uFactorydataReady = 0;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ iRet = send_instruction(data, FACTORY_MODE, GEOMAGNETIC_FACTORY,
+ chTempBuf, 2);
+
+ while (!(data->uFactorydataReady & (1 << GEOMAGNETIC_FACTORY))
+ && (iDelayCnt++ < 50)
+ && (iRet == SUCCESS))
+ msleep(20);
+
+ if ((iDelayCnt >= 50) || (iRet != SUCCESS)) {
+ pr_err("[SSP]: %s - Magnetic Selftest Timeout!!\n", __func__);
+ goto exit;
+ }
+
+ iSF_X = (s16)((data->uFactorydata[0] << 8) + data->uFactorydata[1]);
+ iSF_Y = (s16)((data->uFactorydata[2] << 8) + data->uFactorydata[3]);
+ iSF_Z = (s16)((data->uFactorydata[4] << 8) + data->uFactorydata[5]);
+
+ iSF_X = (s16)(((int)iSF_X * ((int)data->uFuseRomData[0] + 128)) >> 8);
+ iSF_Y = (s16)(((int)iSF_Y * ((int)data->uFuseRomData[1] + 128)) >> 8);
+ iSF_Z = (s16)(((int)iSF_Z * ((int)data->uFuseRomData[2] + 128)) >> 8);
+
+ pr_info("[SSP] %s: self test x = %d, y = %d, z = %d\n",
+ __func__, iSF_X, iSF_Y, iSF_Z);
+ if ((iSF_X >= -200) && (iSF_X <= 200))
+ pr_info("[SSP] x passed self test, expect -200<=x<=200\n");
+ else
+ pr_info("[SSP] x failed self test, expect -200<=x<=200\n");
+ if ((iSF_Y >= -200) && (iSF_Y <= 200))
+ pr_info("[SSP] y passed self test, expect -200<=y<=200\n");
+ else
+ pr_info("[SSP] y failed self test, expect -200<=y<=200\n");
+ if ((iSF_Z >= -3200) && (iSF_Z <= -800))
+ pr_info("[SSP] z passed self test, expect -3200<=z<=-800\n");
+ else
+ pr_info("[SSP] z failed self test, expect -3200<=z<=-800\n");
+
+ if (((iSF_X >= -200) && (iSF_X <= 200)) &&
+ ((iSF_Y >= -200) && (iSF_Y <= 200)) &&
+ ((iSF_Z >= -3200) && (iSF_Z <= -800)))
+ bSelftestPassed = true;
+
+ if ((bSelftestPassed == false) && (iReties++ < 5))
+ goto reties;
+exit:
+ return sprintf(buf, "%u,%d,%d,%d\n",
+ bSelftestPassed, iSF_X, iSF_Y, iSF_Z);
+}
+
+static ssize_t magnetic_check_registers(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u8 uBuf[13] = {0,};
+
+ return sprintf(buf, "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u\n",
+ uBuf[0], uBuf[1], uBuf[2], uBuf[3], uBuf[4], uBuf[5],
+ uBuf[6], uBuf[7], uBuf[8], uBuf[9], uBuf[10], uBuf[11],
+ uBuf[12]);
+}
+
+static ssize_t magnetic_check_cntl(struct device *dev,
+ struct device_attribute *attr, char *strbuf)
+{
+ bool bSuccess = false;
+
+ return sprintf(strbuf, "%s,%d,%d,%d\n",
+ (!bSuccess ? "OK" : "NG"), 0, 0, 0);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, magnetic_name_show, NULL);
+static DEVICE_ATTR(vendor, S_IRUGO, magnetic_vendor_show, NULL);
+static DEVICE_ATTR(raw_data, S_IRUGO, raw_data_read, NULL);
+static DEVICE_ATTR(status, S_IRUGO, magnetic_get_status, NULL);
+static DEVICE_ATTR(adc, S_IRUGO, adc_data_read, NULL);
+static DEVICE_ATTR(dac, S_IRUGO, magnetic_check_cntl, NULL);
+static DEVICE_ATTR(selftest, S_IRUGO, magnetic_get_selftest, NULL);
+static DEVICE_ATTR(ak8963_asa, S_IRUGO, magnetic_get_asa, NULL);
+static DEVICE_ATTR(ak8963_chk_registers, S_IRUGO,
+ magnetic_check_registers, NULL);
+
+static struct device_attribute *mag_attrs[] = {
+ &dev_attr_name,
+ &dev_attr_vendor,
+ &dev_attr_adc,
+ &dev_attr_raw_data,
+ &dev_attr_status,
+ &dev_attr_selftest,
+ &dev_attr_ak8963_asa,
+ &dev_attr_ak8963_chk_registers,
+ &dev_attr_dac,
+ NULL,
+};
+
+void initialize_magnetic_factorytest(struct ssp_data *data)
+{
+ struct device *mag_device = NULL;
+
+ sensors_register(mag_device, data, mag_attrs, "magnetic_sensor");
+}
diff --git a/drivers/sensorhub/factory/mcu_at32uc3l0128.c b/drivers/sensorhub/factory/mcu_at32uc3l0128.c
new file mode 100644
index 0000000..f4549ac
--- /dev/null
+++ b/drivers/sensorhub/factory/mcu_at32uc3l0128.c
@@ -0,0 +1,257 @@
+/*
+ * 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", get_module_rev(),
+ data->uCurFirmRev);
+}
+
+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_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("[SSP]: %s - mcu binany update!\n", __func__);
+
+ disable_irq(data->iIrq);
+ disable_irq_wake(data->iIrq);
+
+ iRet = update_mcu_bin(data);
+ if (iRet < 0) {
+ ssp_dbg("[SSP]: %s - update_mcu_bin failed!\n", __func__);
+ goto exit;
+ }
+
+ iRet = initialize_mcu(data);
+ if (iRet < 0) {
+ ssp_dbg("[SSP]: %s - initialize_mcu failed!\n", __func__);
+ goto exit;
+ }
+
+ sync_sensor_state(data);
+
+ enable_irq(data->iIrq);
+ enable_irq_wake(data->iIrq);
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ ssp_report_sensorhub_notice(data, MSG2SSP_AP_STATUS_RESET);
+#endif
+
+ bSuccess = true;
+exit:
+ return sprintf(buf, "%s\n", (bSuccess ? "OK" : "NG"));
+}
+
+ssize_t mcu_update2_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("[SSP]: %s - mcu binany update!\n", __func__);
+
+ disable_irq(data->iIrq);
+ disable_irq_wake(data->iIrq);
+
+ iRet = update_crashed_mcu_bin(data);
+ if (iRet < 0) {
+ ssp_dbg("[SSP]: %s - update_mcu_bin failed!\n", __func__);
+ goto exit;
+ }
+
+ iRet = initialize_mcu(data);
+ if (iRet < 0) {
+ ssp_dbg("[SSP]: %s - initialize_mcu failed!\n", __func__);
+ goto exit;
+ }
+
+ enable_irq(data->iIrq);
+ enable_irq_wake(data->iIrq);
+
+ if (atomic_read(&data->aSensorEnable) > 0)
+ sync_sensor_state(data);
+
+ bSuccess = true;
+exit:
+ 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 {
+ pr_err("[SSP]: %s - invalid value %d\n", __func__, *buf);
+ return -EINVAL;
+ }
+
+ ssp_dbg("[SSP]: 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->bBinaryChashed == true)
+ sprintf(buf, "NG,NG,NG\n");
+
+ if (data->uFactorydataReady & (1 << MCU_FACTORY)) {
+ ssp_dbg("[SSP] 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 {
+ pr_err("[SSP]: %s - The Sensorhub is not ready\n", __func__);
+ }
+ ssp_dbg("[SSP]: 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 {
+ pr_err("[SSP]: %s - invalid value %d\n", __func__, *buf);
+ return -EINVAL;
+ }
+
+ ssp_dbg("[SSP]: 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))) {
+ pr_err("[SSP]: %s - The Sensorhub is not ready\n", __func__);
+ goto exit;
+ }
+
+ for (iDataIdx = 0; iDataIdx < FACTORY_DATA_MAX;) {
+ iSensorData = (int)data->uFactorydata[iDataIdx++];
+
+ if ((iSensorData < 0) ||
+ (iSensorData >= (SENSOR_MAX - 1))) {
+ pr_err("[SSP]: %s - Mcu data frame error %d\n",
+ __func__, 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("[SSP]: %s Result - "
+ "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%u,%u,%u,%u,%u\n", __func__,
+ 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/factory/pressure_bmp182.c b/drivers/sensorhub/factory/pressure_bmp182.c
new file mode 100644
index 0000000..b3d580a
--- /dev/null
+++ b/drivers/sensorhub/factory/pressure_bmp182.c
@@ -0,0 +1,190 @@
+/*
+ * 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 VENDOR "BOSCH"
+#define CHIP_ID "BMP180"
+
+#define CALIBRATION_FILE_PATH "/efs/FactoryApp/baro_delta"
+
+#define PR_ABS_MAX 8388607 /* 24 bit 2'compl */
+#define PR_ABS_MIN -8388608
+
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+static ssize_t sea_level_pressure_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+ int iNewSeaLevelPressure;
+
+ sscanf(buf, "%d", &iNewSeaLevelPressure);
+
+ if (iNewSeaLevelPressure == 0) {
+ pr_info("%s, our->temperature = 0\n", __func__);
+ iNewSeaLevelPressure = -1;
+ }
+
+ input_report_rel(data->pressure_input_dev, REL_DIAL,
+ iNewSeaLevelPressure);
+ input_sync(data->pressure_input_dev);
+
+ return size;
+}
+
+int pressure_open_calibration(struct ssp_data *data)
+{
+ char chBuf[10] = {0,};
+ int iErr = 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)) {
+ iErr = PTR_ERR(cal_filp);
+ if (iErr != -ENOENT)
+ pr_err("[SSP]: %s - Can't open calibration file(%d)\n",
+ __func__, iErr);
+ set_fs(old_fs);
+ return iErr;
+ }
+ iErr = cal_filp->f_op->read(cal_filp,
+ chBuf, 10 * sizeof(char), &cal_filp->f_pos);
+ if (iErr < 0) {
+ pr_err("[SSP]: %s - Can't read the cal data from file (%d)\n",
+ __func__, iErr);
+ return iErr;
+ }
+ filp_close(cal_filp, current->files);
+ set_fs(old_fs);
+
+ iErr = kstrtoint(chBuf, 10, &data->iPressureCal);
+ if (iErr < 0) {
+ pr_err("[SSP]: %s - kstrtoint failed. %d", __func__, iErr);
+ return iErr;
+ }
+
+ ssp_dbg("[SSP]: open barometer calibration %d\n", data->iPressureCal);
+
+ if (data->iPressureCal < PR_ABS_MIN || data->iPressureCal > PR_ABS_MAX)
+ pr_err("[SSP]: %s - wrong offset value!!!\n", __func__);
+
+ return iErr;
+}
+
+static ssize_t pressure_cabratioin_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+ int iPressureCal = 0, iErr = 0;
+
+ iErr = kstrtoint(buf, 10, &iPressureCal);
+ if (iErr < 0) {
+ pr_err("[SSP]: %s - kstrtoint failed.(%d)", __func__, iErr);
+ return iErr;
+ }
+
+ if (iPressureCal < PR_ABS_MIN || iPressureCal > PR_ABS_MAX)
+ return -EINVAL;
+
+ data->iPressureCal = (s32)iPressureCal;
+
+ return size;
+}
+
+static ssize_t pressure_cabratioin_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ pressure_open_calibration(data);
+
+ return sprintf(buf, "%d\n", data->iPressureCal);
+}
+
+static ssize_t eeprom_check_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool bSuccess = false;
+ char chTempBuf[2] = {0, 10};
+ int iRet, iDelayCnt = 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, PRESSURE_FACTORY,
+ chTempBuf, 2);
+
+ while (!(data->uFactorydataReady & (1 << PRESSURE_FACTORY))
+ && (iDelayCnt++ < 150)
+ && (iRet == SUCCESS))
+ msleep(20);
+
+ if ((iDelayCnt >= 150) || (iRet != SUCCESS)) {
+ pr_err("[SSP]: %s - Pressure Selftest Timeout!!\n",
+ __func__);
+ goto exit;
+ }
+
+ bSuccess = (bool)(!!data->uFactorydata[0]);
+ ssp_dbg("[SSP]: %s - %u\n", __func__, bSuccess);
+
+exit:
+ return snprintf(buf, PAGE_SIZE, "%d", bSuccess);
+}
+
+/* sysfs for vendor & name */
+static ssize_t pressure_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VENDOR);
+}
+
+static ssize_t pressure_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", CHIP_ID);
+}
+
+static DEVICE_ATTR(vendor, S_IRUGO, pressure_vendor_show, NULL);
+static DEVICE_ATTR(name, S_IRUGO, pressure_name_show, NULL);
+static DEVICE_ATTR(eeprom_check, S_IRUGO, eeprom_check_show, NULL);
+static DEVICE_ATTR(calibration, S_IRUGO | S_IWUSR | S_IWGRP,
+ pressure_cabratioin_show, pressure_cabratioin_store);
+static DEVICE_ATTR(sea_level_pressure, S_IRUGO | S_IWUSR | S_IWGRP,
+ NULL, sea_level_pressure_store);
+
+static struct device_attribute *pressure_attrs[] = {
+ &dev_attr_vendor,
+ &dev_attr_name,
+ &dev_attr_calibration,
+ &dev_attr_sea_level_pressure,
+ &dev_attr_eeprom_check,
+ NULL,
+};
+
+void initialize_pressure_factorytest(struct ssp_data *data)
+{
+ struct device *pressure_device = NULL;
+
+ sensors_register(pressure_device, data, pressure_attrs,
+ "barometer_sensor");
+}
diff --git a/drivers/sensorhub/factory/prox_cm36651.c b/drivers/sensorhub/factory/prox_cm36651.c
new file mode 100644
index 0000000..3963952
--- /dev/null
+++ b/drivers/sensorhub/factory/prox_cm36651.c
@@ -0,0 +1,373 @@
+/*
+ * 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 VENDOR "CAPELLA"
+#define CHIP_ID "CM36651"
+
+#define CANCELATION_FILE_PATH "/efs/prox_cal"
+#define LCD_LDI_FILE_PATH "/sys/class/lcd/panel/window_type"
+
+#define LINE_1 '6'
+#define LINE_1_LDI_OTHERS '4'
+#define LINE_1_LDI_GRAY '5'
+#define LINE_1_LDI_WHITE '6'
+
+#define LINE_2 '3'
+#define LINE_2_LDI_OTHERS '2'
+#define LINE_2_LDI_GRAY '3'
+#define LINE_2_LDI_WHITE '4'
+/*************************************************************************/
+/* factory Sysfs */
+/*************************************************************************/
+
+static ssize_t prox_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VENDOR);
+}
+
+static ssize_t prox_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", CHIP_ID);
+}
+
+static ssize_t proximity_avg_show(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[PROXIMITY_RAW].prox[1],
+ data->buf[PROXIMITY_RAW].prox[2],
+ data->buf[PROXIMITY_RAW].prox[3]);
+}
+
+static ssize_t proximity_avg_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ char chTempbuf[2] = { 1, 20};
+ int iRet;
+ int64_t dEnable;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ iRet = strict_strtoll(buf, 10, &dEnable);
+ if (iRet < 0)
+ return iRet;
+
+ if (dEnable) {
+ send_instruction(data, ADD_SENSOR, PROXIMITY_RAW, chTempbuf, 2);
+ data->bProximityRawEnabled = true;
+ } else {
+ send_instruction(data, REMOVE_SENSOR, PROXIMITY_RAW,
+ chTempbuf, 2);
+ data->bProximityRawEnabled = false;
+ }
+
+ return size;
+}
+
+static unsigned char get_proximity_rawdata(struct ssp_data *data)
+{
+ unsigned char uRowdata = 0;
+ char chTempbuf[2] = { 1, 20};
+
+ if (data->bProximityRawEnabled == false) {
+ send_instruction(data, ADD_SENSOR, PROXIMITY_RAW, chTempbuf, 2);
+ msleep(200);
+ uRowdata = data->buf[PROXIMITY_RAW].prox[0];
+ send_instruction(data, REMOVE_SENSOR, PROXIMITY_RAW,
+ chTempbuf, 2);
+ } else {
+ uRowdata = data->buf[PROXIMITY_RAW].prox[0];
+ }
+
+ return uRowdata;
+}
+
+static ssize_t proximity_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", get_proximity_rawdata(data));
+}
+
+static void change_proximity_default_threshold(struct ssp_data *data)
+{
+ if (((data->chLcdLdi[0] == LINE_1)
+ && (data->chLcdLdi[1] == LINE_1_LDI_GRAY))
+ || ((data->chLcdLdi[0] == LINE_2)
+ && (data->chLcdLdi[1] == LINE_2_LDI_GRAY)))
+ data->uProxThresh = GRAY_OCTA_DEFAULT_THRESHOLD;
+ else if (((data->chLcdLdi[0] == LINE_1)
+ && (data->chLcdLdi[1] == LINE_1_LDI_WHITE))
+ || ((data->chLcdLdi[0] == LINE_2)
+ && (data->chLcdLdi[1] == LINE_2_LDI_WHITE)))
+ data->uProxThresh = WHITE_OCTA_DEFAULT_THRESHOLD;
+ else if (((data->chLcdLdi[0] == LINE_1)
+ && (data->chLcdLdi[1] == LINE_1_LDI_OTHERS))
+ || ((data->chLcdLdi[0] == LINE_2)
+ && (data->chLcdLdi[1] == LINE_2_LDI_OTHERS)))
+ data->uProxThresh = OTHERS_OCTA_DEFAULT_THRESHOLD;
+ else
+ data->uProxThresh = DEFAULT_THRESHOLD;
+}
+
+int proximity_open_lcd_ldi(struct ssp_data *data)
+{
+ int iRet = 0;
+ mm_segment_t old_fs;
+ struct file *cancel_filp = NULL;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cancel_filp = filp_open(LCD_LDI_FILE_PATH, O_RDONLY, 0666);
+ if (IS_ERR(cancel_filp)) {
+ iRet = PTR_ERR(cancel_filp);
+ if (iRet != -ENOENT)
+ pr_err("[SSP]: %s - Can't open lcd ldi file\n",
+ __func__);
+ set_fs(old_fs);
+ data->chLcdLdi[0] = 0;
+ data->chLcdLdi[1] = 0;
+ goto exit;
+ }
+
+ iRet = cancel_filp->f_op->read(cancel_filp,
+ (u8 *)data->chLcdLdi, sizeof(u8) * 2, &cancel_filp->f_pos);
+ if (iRet != (sizeof(u8) * 2)) {
+ pr_err("[SSP]: %s - Can't read the lcd ldi data\n", __func__);
+ iRet = -EIO;
+ }
+
+ ssp_dbg("[SSP]: %s - 1st : %c\n", __func__, data->chLcdLdi[0]);
+ ssp_dbg("[SSP]: %s - 1st : %c\n", __func__, data->chLcdLdi[1]);
+
+ filp_close(cancel_filp, current->files);
+ set_fs(old_fs);
+
+exit:
+ change_proximity_default_threshold(data);
+ return iRet;
+}
+
+int proximity_open_calibration(struct ssp_data *data)
+{
+ int iRet = 0;
+ mm_segment_t old_fs;
+ struct file *cancel_filp = NULL;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cancel_filp = filp_open(CANCELATION_FILE_PATH, O_RDONLY, 0666);
+ if (IS_ERR(cancel_filp)) {
+ iRet = PTR_ERR(cancel_filp);
+ if (iRet != -ENOENT)
+ pr_err("[SSP]: %s - Can't open cancelation file\n",
+ __func__);
+ set_fs(old_fs);
+ goto exit;
+ }
+
+ iRet = cancel_filp->f_op->read(cancel_filp,
+ (u8 *)&data->uProxCanc, sizeof(u8), &cancel_filp->f_pos);
+ if (iRet != sizeof(u8)) {
+ pr_err("[SSP]: %s - Can't read the cancel data\n", __func__);
+ iRet = -EIO;
+ }
+
+ if (data->uProxCanc != 0) /*If there is an offset cal data. */
+ data->uProxThresh = CANCELATION_THRESHOLD;
+
+ pr_info("%s: proximity ps_canc = %d, ps_thresh = %d\n",
+ __func__, data->uProxCanc, data->uProxThresh);
+
+ filp_close(cancel_filp, current->files);
+ set_fs(old_fs);
+
+exit:
+ set_proximity_threshold(data);
+
+ return iRet;
+}
+
+static int proximity_store_cancelation(struct ssp_data *data, int iCalCMD)
+{
+ int iRet = 0;
+ mm_segment_t old_fs;
+ struct file *cancel_filp = NULL;
+
+ if (iCalCMD) {
+ data->uProxThresh = CANCELATION_THRESHOLD;
+ data->uProxCanc = get_proximity_rawdata(data);
+ } else {
+ change_proximity_default_threshold(data);
+ data->uProxCanc = 0;
+ }
+
+ set_proximity_threshold(data);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ cancel_filp = filp_open(CANCELATION_FILE_PATH,
+ O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ if (IS_ERR(cancel_filp)) {
+ pr_err("%s: Can't open cancelation file\n", __func__);
+ set_fs(old_fs);
+ iRet = PTR_ERR(cancel_filp);
+ return iRet;
+ }
+
+ iRet = cancel_filp->f_op->write(cancel_filp, (u8 *)&data->uProxCanc,
+ sizeof(u8), &cancel_filp->f_pos);
+ if (iRet != sizeof(u8)) {
+ pr_err("%s: Can't write the cancel data to file\n", __func__);
+ iRet = -EIO;
+ }
+
+ filp_close(cancel_filp, current->files);
+ set_fs(old_fs);
+
+ return iRet;
+}
+
+static ssize_t proximity_cancel_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("[SSP]: uProxThresh = %u, uProxCanc = %u\n",
+ data->uProxThresh, data->uProxCanc);
+
+ return sprintf(buf, "%u,%u\n", data->uProxCanc, data->uProxThresh);
+}
+
+static ssize_t proximity_cancel_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int iCalCMD = 0, iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ if (sysfs_streq(buf, "1")) /* calibrate cancelation value */
+ iCalCMD = 1;
+ else if (sysfs_streq(buf, "0")) /* reset cancelation value */
+ iCalCMD = 0;
+ else {
+ pr_debug("%s: invalid value %d\n", __func__, *buf);
+ return -EINVAL;
+ }
+
+ iRet = proximity_store_cancelation(data, iCalCMD);
+ if (iRet < 0) {
+ pr_err("[SSP]: - %s proximity_store_cancelation() failed\n",
+ __func__);
+ return iRet;
+ }
+
+ ssp_dbg("[SSP]: %s - %u\n", __func__, iCalCMD);
+ return size;
+}
+
+static ssize_t proximity_thresh_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ ssp_dbg("[SSP]: uProxThresh = %u\n", data->uProxThresh);
+
+ return sprintf(buf, "%u\n", data->uProxThresh);
+}
+
+static ssize_t proximity_thresh_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ u8 uNewThresh = 0x09;
+ int iRet = 0;
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ iRet = kstrtou8(buf, 10, &uNewThresh);
+ if (iRet < 0)
+ pr_err("[SSP]: %s - kstrtoint failed.", __func__);
+
+ data->uProxThresh = uNewThresh;
+ set_proximity_threshold(data);
+
+ ssp_dbg("[SSP]: %s - new prox threshold = 0x%x\n",
+ __func__, data->uProxThresh);
+
+ return size;
+}
+
+static ssize_t barcode_emul_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->bBarcodeEnabled);
+}
+
+static ssize_t barcode_emul_enable_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 = strict_strtoll(buf, 10, &dEnable);
+ if (iRet < 0)
+ return iRet;
+
+ if (dEnable)
+ set_proximity_barcode_enable(data, true);
+ else
+ set_proximity_barcode_enable(data, false);
+
+ return size;
+}
+
+static DEVICE_ATTR(vendor, S_IRUGO, prox_vendor_show, NULL);
+static DEVICE_ATTR(name, S_IRUGO, prox_name_show, NULL);
+static DEVICE_ATTR(state, S_IRUGO, proximity_state_show, NULL);
+static DEVICE_ATTR(barcode_emul_en, S_IRUGO | S_IWUSR | S_IWGRP,
+ barcode_emul_enable_show, barcode_emul_enable_store);
+static DEVICE_ATTR(prox_avg, S_IRUGO | S_IWUSR | S_IWGRP,
+ proximity_avg_show, proximity_avg_store);
+static DEVICE_ATTR(prox_cal, S_IRUGO | S_IWUSR | S_IWGRP,
+ proximity_cancel_show, proximity_cancel_store);
+static DEVICE_ATTR(prox_thresh, S_IRUGO | S_IWUSR | S_IWGRP,
+ proximity_thresh_show, proximity_thresh_store);
+
+static struct device_attribute *prox_attrs[] = {
+ &dev_attr_vendor,
+ &dev_attr_name,
+ &dev_attr_state,
+ &dev_attr_prox_avg,
+ &dev_attr_prox_cal,
+ &dev_attr_prox_thresh,
+ &dev_attr_barcode_emul_en,
+ NULL,
+};
+
+void initialize_prox_factorytest(struct ssp_data *data)
+{
+ struct device *prox_device = NULL;
+
+ sensors_register(prox_device, data, prox_attrs, "proximity_sensor");
+}
diff --git a/drivers/sensorhub/sensors_core.c b/drivers/sensorhub/sensors_core.c
new file mode 100644
index 0000000..634ad36
--- /dev/null
+++ b/drivers/sensorhub/sensors_core.c
@@ -0,0 +1,96 @@
+/*
+ * 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>
+
+struct class *sensors_class;
+static atomic_t sensor_count;
+static DEFINE_MUTEX(sensors_mutex);
+
+/*
+ * 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)
+ printk(KERN_INFO "[SENSOR CORE] fail device_create_file"
+ "(dev, attributes[%d])\n", i);
+}
+
+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);
+ }
+
+ mutex_lock(&sensors_mutex);
+
+ dev = device_create(sensors_class, NULL, 0, drvdata, "%s", name);
+
+ if (IS_ERR(dev)) {
+ ret = PTR_ERR(dev);
+ printk(KERN_ERR "[SENSORS CORE] device_create failed!"
+ "[%d]\n", ret);
+ return ret;
+ }
+
+ set_sensor_attr(dev, attributes);
+ atomic_inc(&sensor_count);
+
+ mutex_unlock(&sensors_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sensors_register);
+
+void sensors_unregister(struct device *dev)
+{
+ /* TODO : Unregister device */
+}
+EXPORT_SYMBOL_GPL(sensors_unregister);
+
+static int __init sensors_class_init(void)
+{
+ printk(KERN_INFO "[SENSORS CORE] sensors_class_init\n");
+ sensors_class = class_create(THIS_MODULE, "sensors");
+
+ if (IS_ERR(sensors_class))
+ return PTR_ERR(sensors_class);
+
+ atomic_set(&sensor_count, 0);
+ sensors_class->dev_uevent = NULL;
+
+ return 0;
+}
+
+static void __exit sensors_class_exit(void)
+{
+ class_destroy(sensors_class);
+}
+
+/* exported for the APM Power driver, APM emulation */
+EXPORT_SYMBOL_GPL(sensors_class);
+
+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/ssp.h b/drivers/sensorhub/ssp.h
new file mode 100644
index 0000000..9986c08
--- /dev/null
+++ b/drivers/sensorhub/ssp.h
@@ -0,0 +1,410 @@
+/*
+ * 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/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>
+#include <linux/earlysuspend.h>
+#include <linux/wakelock.h>
+#include <linux/miscdevice.h>
+#include <linux/ssp_platformdata.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/timer.h>
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+#include <linux/completion.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#endif
+
+#define SSP_DBG 1
+
+#define SUCCESS 1
+#define FAIL 0
+#define ERROR -1
+
+#define CANCELATION_THRESHOLD 9
+#define DEFAULT_THRESHOLD 13
+#define OTHERS_OCTA_DEFAULT_THRESHOLD 11
+#define WHITE_OCTA_DEFAULT_THRESHOLD 13
+#define GRAY_OCTA_DEFAULT_THRESHOLD 12
+
+#define FACTORY_DATA_MAX 39
+#if SSP_DBG
+#define SSP_FUNC_DBG 1
+#define SSP_DATA_DBG 0
+
+#define ssp_dbg(dev, format, ...) do { \
+ printk(KERN_INFO dev, format, ##__VA_ARGS__); \
+ } while (0)
+#else
+#define ssp_dbg(dev, format, ...)
+#endif
+
+#if SSP_FUNC_DBG
+#define func_dbg() do { \
+ printk(KERN_INFO "[SSP]: %s\n", __func__); \
+ } while (0)
+#else
+#define func_dbg()
+#endif
+
+#if SSP_DATA_DBG
+#define data_dbg(dev, format, ...) do { \
+ printk(KERN_INFO dev, format, ##__VA_ARGS__); \
+ } while (0)
+#else
+#define data_dbg(dev, format, ...)
+#endif
+
+#define SSP_SW_RESET_TIME 3000
+
+#define DEFUALT_POLLING_DELAY (200 * NSEC_PER_MSEC)
+#define PROX_AVG_READ_NUM 80
+
+/* Sensor Sampling Time Define */
+enum {
+ SENSOR_NS_DELAY_FASTEST = 10000000, /* 10msec */
+ SENSOR_NS_DELAY_GAME = 20000000, /* 20msec */
+ SENSOR_NS_DELAY_UI = 66700000, /* 66.7msec */
+ SENSOR_NS_DELAY_NORMAL = 200000000, /* 200msec */
+};
+
+enum {
+ SENSOR_MS_DELAY_FASTEST = 10, /* 10msec */
+ SENSOR_MS_DELAY_GAME = 20, /* 20msec */
+ SENSOR_MS_DELAY_UI = 66, /* 66.7msec */
+ SENSOR_MS_DELAY_NORMAL = 200, /* 200msec */
+};
+
+enum {
+ SENSOR_CMD_DELAY_FASTEST = 0, /* 10msec */
+ SENSOR_CMD_DELAY_GAME, /* 20msec */
+ SENSOR_CMD_DELAY_UI, /* 66.7msec */
+ SENSOR_CMD_DELAY_NORMAL, /* 200msec */
+};
+
+/*
+ * 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,
+};
+
+/* Gyroscope DPS */
+#define GYROSCOPE_DPS250 250
+#define GYROSCOPE_DPS500 500
+#define GYROSCOPE_DPS2000 2000
+
+/* 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)
+
+/* ioctl command */
+#define AKMIO 0xA1
+#define ECS_IOCTL_GET_FUSEROMDATA _IOR(AKMIO, 0x01, unsigned char[3])
+#define ECS_IOCTL_GET_MAGDATA _IOR(AKMIO, 0x02, unsigned char[8])
+#define ECS_IOCTL_GET_ACCDATA _IOR(AKMIO, 0x03, int[3])
+
+/* 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_SENSOR_SELFTEST 0xA8
+#define MSG2SSP_INST_LIBRARY_ADD 0xB1
+#define MSG2SSP_INST_LIBRARY_REMOVE 0xB2
+
+#define MSG2SSP_AP_STT 0xC8
+#define MSG2SSP_AP_STATUS_WAKEUP 0xD1
+#define MSG2SSP_AP_STATUS_SLEEP 0xD2
+#define MSG2SSP_AP_STATUS_RESET 0xD3
+#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_FUSEROM 0X01
+
+/* AP -> SSP Data Protocol Frame Field */
+#define MSG2SSP_SSP_SLEEP 0xC1
+#define MSG2SSP_STS 0xC2 /* Start to Send */
+#define MSG2SSP_RTS 0xC4 /* Ready to Send */
+#define MSG2SSP_SRM 0xCA /* Start to Read MSG */
+#define MSG2SSP_SSM 0xCB /* Start to Send MSG */
+#define MSG2SSP_SSD 0xCE /* Start to Send Data Type & Length */
+
+/* SSP -> AP ACK about write CMD */
+#define MSG_ACK 0x80 /* ACK from SSP to AP */
+#define MSG_NAK 0x70 /* NAK from SSP to AP */
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+#define SUBCMD_GPIOWAKEUP 0X02
+#define SUBCMD_POWEREUP 0X04
+#define MSG2SSP_STT 0xC8
+#define LIBRARY_MAX_NUM 8
+#endif
+
+/* SSP_INSTRUCTION_CMD */
+enum {
+ REMOVE_SENSOR = 0,
+ ADD_SENSOR,
+ CHANGE_DELAY,
+ GO_SLEEP,
+ FACTORY_MODE,
+ REMOVE_LIBRARY,
+ ADD_LIBRARY,
+};
+
+/* SENSOR_TYPE */
+enum {
+ ACCELEROMETER_SENSOR = 0,
+ GYROSCOPE_SENSOR,
+ GEOMAGNETIC_SENSOR,
+ PRESSURE_SENSOR,
+ GESTURE_SENSOR,
+ PROXIMITY_SENSOR,
+ LIGHT_SENSOR,
+ PROXIMITY_RAW,
+ ORIENTATION_SENSOR,
+ SENSOR_MAX,
+};
+
+/* SENSOR_FACTORY_MODE_TYPE */
+enum {
+ ACCELEROMETER_FACTORY = 0,
+ GYROSCOPE_FACTORY,
+ GEOMAGNETIC_FACTORY,
+ PRESSURE_FACTORY,
+ MCU_FACTORY,
+ GYROSCOPE_TEMP_FACTORY,
+ GYROSCOPE_DPS_FACTORY,
+ MCU_SLEEP_FACTORY,
+ SENSOR_FACTORY_MAX,
+};
+
+struct sensor_value {
+ union {
+ struct {
+ s16 x;
+ s16 y;
+ s16 z;
+ };
+ struct {
+ u16 r;
+ u16 g;
+ u16 b;
+ u16 w;
+ };
+ u8 prox[4];
+ s16 data[4];
+ s32 pressure[3];
+ };
+};
+
+struct calibraion_data {
+ int x;
+ int y;
+ int z;
+};
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+struct sensorhub_event {
+ char *library_data;
+ int library_length;
+ struct list_head list;
+};
+#endif
+
+struct ssp_data {
+ struct input_dev *acc_input_dev;
+ struct input_dev *gyro_input_dev;
+ struct input_dev *pressure_input_dev;
+ struct input_dev *light_input_dev;
+ struct input_dev *prox_input_dev;
+
+ struct i2c_client *client;
+ struct wake_lock ssp_wake_lock;
+ struct miscdevice akmd_device;
+ struct timer_list debug_timer;
+ struct workqueue_struct *debug_wq;
+ struct work_struct work_debug;
+ struct calibraion_data accelcal;
+ struct calibraion_data gyrocal;
+ struct sensor_value buf[SENSOR_MAX];
+ struct device *sen_dev;
+
+ bool bCheckSuspend;
+ bool bDebugEnabled;
+ bool bMcuIRQTestSuccessed;
+ bool bAccelAlert;
+ bool bProximityRawEnabled;
+ bool bBarcodeEnabled;
+ bool bBinaryChashed;
+
+ unsigned char uProxCanc;
+ unsigned char uProxThresh;
+ unsigned char uFuseRomData[3];
+ unsigned char uFactorydata[FACTORY_DATA_MAX];
+ char *pchLibraryBuf;
+ char chLcdLdi[2];
+ int iIrq;
+ int iLibraryLength;
+ int aiCheckStatus[SENSOR_MAX];
+ int iIrqWakeCnt;
+
+ unsigned int uSsdFailCnt;
+ unsigned int uResetCnt;
+ unsigned int uI2cFailCnt;
+ unsigned int uTimeOutCnt;
+ unsigned int uBusyCnt;
+ unsigned int uGyroDps;
+ unsigned int uAliveSensorDebug;
+ unsigned int uCurFirmRev;
+ unsigned int uFactoryProxAvg[4];
+ unsigned int uFactorydataReady;
+ s32 iPressureCal;
+
+ atomic_t aSensorEnable;
+ int64_t adDelayBuf[SENSOR_MAX];
+
+ int (*wakeup_mcu)(void);
+ int (*check_mcu_ready)(void);
+ int (*check_mcu_busy)(void);
+ int (*set_mcu_reset)(int);
+ int (*check_ap_rev)(void);
+ void (*get_sensor_data[SENSOR_MAX])(char *, int *,
+ struct sensor_value *);
+ void (*report_sensor_data[SENSOR_MAX])(struct ssp_data *,
+ struct sensor_value *);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ struct input_dev *sensorhub_input_dev;
+ struct miscdevice sensorhub_device;
+ struct wake_lock sensorhub_wake_lock;
+ struct completion transfer_done;
+ struct task_struct *sensorhub_task;
+ struct sensorhub_event events_head;
+ struct sensorhub_event events[LIBRARY_MAX_NUM];
+ int event_number;
+ int large_library_length;
+ char *large_library_data;
+ wait_queue_head_t sensorhub_waitqueue;
+#endif
+};
+
+int waiting_wakeup_mcu(struct ssp_data *data);
+int ssp_i2c_read(struct ssp_data *data, char *pTxData, u16 uTxLength,
+ char *pRxData, u16 uRxLength);
+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_accel_factorytest(struct ssp_data *);
+void initialize_prox_factorytest(struct ssp_data *);
+void initialize_light_factorytest(struct ssp_data *);
+void initialize_gyro_factorytest(struct ssp_data *);
+void initialize_pressure_factorytest(struct ssp_data *);
+void initialize_magnetic_factorytest(struct ssp_data *);
+void initialize_function_pointer(struct ssp_data *);
+void initialize_magnetic(struct ssp_data *);
+int initialize_event_symlink(struct ssp_data *);
+int accel_open_calibration(struct ssp_data *);
+int gyro_open_calibration(struct ssp_data *);
+int pressure_open_calibration(struct ssp_data *);
+int proximity_open_calibration(struct ssp_data *);
+void check_fwbl(struct ssp_data *);
+int update_mcu_bin(struct ssp_data *);
+int update_crashed_mcu_bin(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_sleep_mode(struct ssp_data *);
+int ssp_resume_mode(struct ssp_data *);
+int send_instruction(struct ssp_data *, u8, u8, u8 *, u8);
+int select_irq_msg(struct ssp_data *);
+int get_chipid(struct ssp_data *);
+int get_fuserom_data(struct ssp_data *);
+int set_sensor_position(struct ssp_data *);
+void sync_sensor_state(struct ssp_data *);
+void set_proximity_threshold(struct ssp_data *);
+void set_proximity_barcode_enable(struct ssp_data *, bool);
+unsigned int get_delay_cmd(u8);
+unsigned int get_msdelay(int64_t);
+unsigned int get_sensor_scanning_info(struct ssp_data *);
+unsigned int get_firmware_rev(struct ssp_data *);
+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 proximity_open_lcd_ldi(struct ssp_data *);
+void report_acc_data(struct ssp_data *, struct sensor_value *);
+void report_gyro_data(struct ssp_data *, struct sensor_value *);
+void report_mag_data(struct ssp_data *, struct sensor_value *);
+void report_gesture_data(struct ssp_data *, struct sensor_value *);
+void report_pressure_data(struct ssp_data *, struct sensor_value *);
+void report_light_data(struct ssp_data *, struct sensor_value *);
+void report_prox_data(struct ssp_data *, struct sensor_value *);
+void report_prox_raw_data(struct ssp_data *, struct sensor_value *);
+void print_mcu_debug(char *, int *);
+unsigned int get_module_rev(void);
+void reset_mcu(struct ssp_data *);
+void convert_acc_data(s16 *);
+int sensors_register(struct device *, void *,
+ struct device_attribute*[], char *);
+ssize_t mcu_reset_show(struct device *, struct device_attribute *, char *);
+ssize_t mcu_revision_show(struct device *, struct device_attribute *, char *);
+ssize_t mcu_update_show(struct device *, struct device_attribute *, char *);
+ssize_t mcu_update2_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);
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+void ssp_report_sensorhub_notice(struct ssp_data *data, char notice);
+int ssp_handle_sensorhub_data(struct ssp_data *data, char *dataframe,
+ int start, int end);
+int ssp_handle_sensorhub_large_data(struct ssp_data *data, u8 sub_cmd);
+int ssp_initialize_sensorhub(struct ssp_data *data);
+void ssp_remove_sensorhub(struct ssp_data *data);
+#endif
+#endif
diff --git a/drivers/sensorhub/ssp_ak8963c.c b/drivers/sensorhub/ssp_ak8963c.c
new file mode 100644
index 0000000..5a462b9
--- /dev/null
+++ b/drivers/sensorhub/ssp_ak8963c.c
@@ -0,0 +1,105 @@
+/*
+ * 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"
+
+/*************************************************************************/
+/* AKM Daemon Library ioctl */
+/*************************************************************************/
+
+static int akmd_copy_in(unsigned int cmd, void __user *argp,
+ void *buf, size_t buf_size)
+{
+ if (!(cmd & IOC_IN))
+ return 0;
+ if (_IOC_SIZE(cmd) > buf_size)
+ return -EINVAL;
+ if (copy_from_user(buf, argp, _IOC_SIZE(cmd)))
+ return -EFAULT;
+ return 0;
+}
+
+static int akmd_copy_out(unsigned int cmd, void __user *argp,
+ void *buf, size_t buf_size)
+{
+ if (!(cmd & IOC_OUT))
+ return 0;
+ if (_IOC_SIZE(cmd) > buf_size)
+ return -EINVAL;
+ if (copy_to_user(argp, buf, _IOC_SIZE(cmd)))
+ return -EFAULT;
+ return 0;
+}
+
+static long akmd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int iRet;
+ void __user *argp = (void __user *)arg;
+ struct ssp_data *data = container_of(file->private_data,
+ struct ssp_data, akmd_device);
+
+ union {
+ u8 uData[8];
+ u8 uMagData[8];
+ u8 uFuseData[3];
+ int iAccData[3];
+ } akmdbuf;
+
+ iRet = akmd_copy_in(cmd, argp, akmdbuf.uData, sizeof(akmdbuf));
+ if (iRet)
+ return iRet;
+
+ switch (cmd) {
+ case ECS_IOCTL_GET_MAGDATA:
+ akmdbuf.uMagData[0] = 1;
+ akmdbuf.uMagData[1] = data->buf[GEOMAGNETIC_SENSOR].x & 0xff;
+ akmdbuf.uMagData[2] = data->buf[GEOMAGNETIC_SENSOR].x >> 8;
+ akmdbuf.uMagData[3] = data->buf[GEOMAGNETIC_SENSOR].y & 0xff;
+ akmdbuf.uMagData[4] = data->buf[GEOMAGNETIC_SENSOR].y >> 8;
+ akmdbuf.uMagData[5] = data->buf[GEOMAGNETIC_SENSOR].z & 0xff;
+ akmdbuf.uMagData[6] = data->buf[GEOMAGNETIC_SENSOR].z >> 8;
+ akmdbuf.uMagData[7] = 0x10;
+ break;
+ case ECS_IOCTL_GET_ACCDATA:
+ akmdbuf.iAccData[0] = data->buf[ACCELEROMETER_SENSOR].x;
+ akmdbuf.iAccData[1] = data->buf[ACCELEROMETER_SENSOR].y;
+ akmdbuf.iAccData[2] = data->buf[ACCELEROMETER_SENSOR].z;
+ break;
+ case ECS_IOCTL_GET_FUSEROMDATA:
+ akmdbuf.uFuseData[0] = data->uFuseRomData[0];
+ akmdbuf.uFuseData[1] = data->uFuseRomData[1];
+ akmdbuf.uFuseData[2] = data->uFuseRomData[2];
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ if (iRet < 0)
+ return iRet;
+
+ return akmd_copy_out(cmd, argp, akmdbuf.uData, sizeof(akmdbuf));
+}
+
+static const struct file_operations akmd_fops = {
+ .owner = THIS_MODULE,
+ .open = nonseekable_open,
+ .unlocked_ioctl = akmd_ioctl,
+};
+
+void initialize_magnetic(struct ssp_data *data)
+{
+ data->akmd_device.minor = MISC_DYNAMIC_MINOR;
+ data->akmd_device.name = "akm8963";
+ data->akmd_device.fops = &akmd_fops;
+}
diff --git a/drivers/sensorhub/ssp_data.c b/drivers/sensorhub/ssp_data.c
new file mode 100644
index 0000000..9ccbca0
--- /dev/null
+++ b/drivers/sensorhub/ssp_data.c
@@ -0,0 +1,276 @@
+/*
+ * 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 0x00
+#define MSG2AP_INST_LIBRARY_DATA 0x01
+#define MSG2AP_INST_SELFTEST_DATA 0x02
+#define MSG2AP_INST_DEBUG_DATA 0x03
+
+/* Factory data length */
+#define ACCEL_FACTORY_DATA_LENGTH 1
+#define GYRO_FACTORY_DATA_LENGTH 27
+#define MAGNETIC_FACTORY_DATA_LENGTH 6
+#define PRESSURE_FACTORY_DATA_LENGTH 1
+#define MCU_FACTORY_DATA_LENGTH 5
+#define GYRO_TEMP_FACTORY_DATA_LENGTH 1
+#define GYRO_DPS_FACTORY_DATA_LENGTH 1
+#define MCU_SLEEP_FACTORY_DATA_LENGTH 39
+
+/*************************************************************************/
+/* SSP parsing the dataframe */
+/*************************************************************************/
+
+static void get_3axis_sensordata(char *pchRcvDataFrame, int *iDataIdx,
+ struct sensor_value *sensorsdata)
+{
+ int iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->x = iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->y = iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->z = iTemp;
+
+ data_dbg("x: %d, y: %d, z: %d\n", sensorsdata->x,
+ sensorsdata->y, sensorsdata->z);
+}
+
+static void get_light_sensordata(char *pchRcvDataFrame, int *iDataIdx,
+ struct sensor_value *sensorsdata)
+{
+ int iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->r = iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->g = iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->b = iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->w = iTemp;
+
+ data_dbg("r: %u, g: %u, b: %u, w: %u\n", sensorsdata->r,
+ sensorsdata->g, sensorsdata->b, sensorsdata->w);
+}
+
+static void get_pressure_sensordata(char *pchRcvDataFrame, int *iDataIdx,
+ struct sensor_value *sensorsdata)
+{
+ int iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 16;
+ sensorsdata->pressure[0] = iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ sensorsdata->pressure[0] += iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->pressure[0] += iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += (int)pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->pressure[1] = (s16)iTemp;
+
+ data_dbg("p : %d, t: %d\n", sensorsdata->pressure[0],
+ sensorsdata->pressure[1]);
+}
+
+static void get_gesture_sensordata(char *pchRcvDataFrame, int *iDataIdx,
+ struct sensor_value *sensorsdata)
+{
+ int iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->data[0] = iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->data[1] = iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->data[2] = iTemp;
+
+ iTemp = (int)pchRcvDataFrame[(*iDataIdx)++];
+ iTemp <<= 8;
+ iTemp += pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->data[3] = iTemp;
+
+ data_dbg("A: %d, B: %d, C: %d, D: %d\n",
+ sensorsdata->data[0], sensorsdata->data[1],
+ sensorsdata->data[2], sensorsdata->data[3]);
+}
+
+static void get_proximity_sensordata(char *pchRcvDataFrame, int *iDataIdx,
+ struct sensor_value *sensorsdata)
+{
+ sensorsdata->prox[0] = (u8)pchRcvDataFrame[(*iDataIdx)++];
+ sensorsdata->prox[1] = (u8)pchRcvDataFrame[(*iDataIdx)++];
+
+ data_dbg("prox : %u, %u\n", sensorsdata->prox[0], sensorsdata->prox[1]);
+}
+
+static void get_proximity_rawdata(char *pchRcvDataFrame, int *iDataIdx,
+ struct sensor_value *sensorsdata)
+{
+ sensorsdata->prox[0] = (u8)pchRcvDataFrame[(*iDataIdx)++];
+
+ data_dbg("proxraw : %u\n", sensorsdata->prox[0]);
+}
+
+static void get_factoty_data(struct ssp_data *data, int iSensorData,
+ char *pchRcvDataFrame, int *iDataIdx)
+{
+ int iIdx, iTotalLenth = 0;
+
+ if (iSensorData == ACCELEROMETER_FACTORY) {
+ data->uFactorydataReady = (1 << ACCELEROMETER_FACTORY);
+ iTotalLenth = ACCEL_FACTORY_DATA_LENGTH;
+ } else if (iSensorData == GYROSCOPE_FACTORY) {
+ data->uFactorydataReady = (1 << GYROSCOPE_FACTORY);
+ iTotalLenth = GYRO_FACTORY_DATA_LENGTH;
+ } else if (iSensorData == GEOMAGNETIC_FACTORY) {
+ data->uFactorydataReady = (1 << GEOMAGNETIC_FACTORY);
+ iTotalLenth = MAGNETIC_FACTORY_DATA_LENGTH;
+ } else if (iSensorData == PRESSURE_FACTORY) {
+ data->uFactorydataReady = (1 << PRESSURE_FACTORY);
+ iTotalLenth = PRESSURE_FACTORY_DATA_LENGTH;
+ } else if (iSensorData == MCU_FACTORY) {
+ data->uFactorydataReady = (1 << MCU_FACTORY);
+ iTotalLenth = MCU_FACTORY_DATA_LENGTH;
+ ssp_dbg("[SSP]: %s - Mcu test data\n", __func__);
+ } else if (iSensorData == GYROSCOPE_TEMP_FACTORY) {
+ data->uFactorydataReady = (1 << GYROSCOPE_TEMP_FACTORY);
+ iTotalLenth = GYRO_TEMP_FACTORY_DATA_LENGTH;
+ } else if (iSensorData == GYROSCOPE_DPS_FACTORY) {
+ data->uFactorydataReady = (1 << GYROSCOPE_DPS_FACTORY);
+ iTotalLenth = GYRO_DPS_FACTORY_DATA_LENGTH;
+ } else if (iSensorData == MCU_SLEEP_FACTORY) {
+ data->uFactorydataReady = (1 << MCU_SLEEP_FACTORY);
+ iTotalLenth = MCU_SLEEP_FACTORY_DATA_LENGTH;
+ }
+
+ for (iIdx = 0; iIdx < iTotalLenth; iIdx++)
+ data->uFactorydata[iIdx] = (u8)pchRcvDataFrame[(*iDataIdx)++];
+}
+
+int parse_dataframe(struct ssp_data *data, char *pchRcvDataFrame, int iLength)
+{
+ int iDataIdx, iSensorData;
+ struct sensor_value *sensorsdata;
+
+ sensorsdata = kzalloc(sizeof(*sensorsdata), GFP_KERNEL);
+ if (sensorsdata == NULL)
+ return ERROR;
+
+ for (iDataIdx = 0; iDataIdx < iLength;) {
+ if (pchRcvDataFrame[iDataIdx] == MSG2AP_INST_BYPASS_DATA) {
+ iDataIdx++;
+ iSensorData = pchRcvDataFrame[iDataIdx++];
+ if ((iSensorData < 0) ||
+ (iSensorData >= (SENSOR_MAX - 1))) {
+ pr_err("[SSP]: %s - Mcu data frame1 error %d\n",
+ __func__, iSensorData);
+ kfree(sensorsdata);
+ return ERROR;
+ }
+
+ data->get_sensor_data[iSensorData](pchRcvDataFrame,
+ &iDataIdx, sensorsdata);
+ data->report_sensor_data[iSensorData](data,
+ sensorsdata);
+ } else if (pchRcvDataFrame[iDataIdx] ==
+ MSG2AP_INST_SELFTEST_DATA) {
+ iDataIdx++;
+ iSensorData = pchRcvDataFrame[iDataIdx++];
+ if ((iSensorData < 0) ||
+ (iSensorData >= SENSOR_FACTORY_MAX)) {
+ pr_err("[SSP]: %s - Mcu data frame2 error %d\n",
+ __func__, iSensorData);
+ kfree(sensorsdata);
+ return ERROR;
+ }
+ get_factoty_data(data, iSensorData, pchRcvDataFrame,
+ &iDataIdx);
+ } else if (pchRcvDataFrame[iDataIdx] ==
+ MSG2AP_INST_DEBUG_DATA) {
+ print_mcu_debug(pchRcvDataFrame + iDataIdx + 1,
+ &iDataIdx);
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ } else if (pchRcvDataFrame[iDataIdx] ==
+ MSG2AP_INST_LIBRARY_DATA) {
+ int ret = ssp_handle_sensorhub_data(data,
+ pchRcvDataFrame, iDataIdx, iLength);
+ if (ret < 0)
+ pr_err("%s: handle sensorhub data(%d) err(%d)",
+ __func__, iDataIdx, ret);
+ break;
+#endif
+ } else
+ iDataIdx++;
+ }
+ kfree(sensorsdata);
+ return SUCCESS;
+}
+
+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;
+ data->get_sensor_data[GEOMAGNETIC_SENSOR] = get_3axis_sensordata;
+ data->get_sensor_data[PRESSURE_SENSOR] = get_pressure_sensordata;
+ data->get_sensor_data[GESTURE_SENSOR] = get_gesture_sensordata;
+ data->get_sensor_data[PROXIMITY_SENSOR] = get_proximity_sensordata;
+ data->get_sensor_data[PROXIMITY_RAW] = get_proximity_rawdata;
+ data->get_sensor_data[LIGHT_SENSOR] = get_light_sensordata;
+
+ data->report_sensor_data[ACCELEROMETER_SENSOR] = report_acc_data;
+ data->report_sensor_data[GYROSCOPE_SENSOR] = report_gyro_data;
+ data->report_sensor_data[GEOMAGNETIC_SENSOR] = report_mag_data;
+ data->report_sensor_data[PRESSURE_SENSOR] = report_pressure_data;
+ data->report_sensor_data[GESTURE_SENSOR] = report_gesture_data;
+ data->report_sensor_data[PROXIMITY_SENSOR] = report_prox_data;
+ data->report_sensor_data[PROXIMITY_RAW] = report_prox_raw_data;
+ data->report_sensor_data[LIGHT_SENSOR] = report_light_data;
+}
diff --git a/drivers/sensorhub/ssp_debug.c b/drivers/sensorhub/ssp_debug.c
new file mode 100644
index 0000000..1e1151e
--- /dev/null
+++ b/drivers/sensorhub/ssp_debug.c
@@ -0,0 +1,165 @@
+/*
+ * 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_DEBUG_TIMER_SEC (10 * HZ)
+
+/*************************************************************************/
+/* SSP Debug timer function */
+/*************************************************************************/
+
+void reset_mcu(struct ssp_data *data)
+{
+ disable_irq(data->iIrq);
+ disable_irq_wake(data->iIrq);
+
+ toggle_mcu_reset(data);
+ msleep(SSP_SW_RESET_TIME);
+ initialize_mcu(data);
+
+ sync_sensor_state(data);
+
+ enable_irq(data->iIrq);
+ enable_irq_wake(data->iIrq);
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ ssp_report_sensorhub_notice(data, MSG2SSP_AP_STATUS_RESET);
+#endif
+}
+
+void sync_sensor_state(struct ssp_data *data)
+{
+ unsigned char uBuf[2] = {0,};
+ unsigned int uSensorCnt;
+
+ set_proximity_threshold(data);
+
+ udelay(10);
+
+ for (uSensorCnt = 0; uSensorCnt < (SENSOR_MAX - 1); uSensorCnt++) {
+ if (atomic_read(&data->aSensorEnable) & (1 << uSensorCnt)) {
+ uBuf[1] = (u8)get_msdelay(data->adDelayBuf[uSensorCnt]);
+ uBuf[0] = (u8)get_delay_cmd(uBuf[1]);
+ send_instruction(data, ADD_SENSOR, uSensorCnt, uBuf, 2);
+ udelay(10);
+ }
+ }
+
+ data->uTimeOutCnt = 0;
+ data->uBusyCnt = 0;
+}
+
+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;
+ case LIGHT_SENSOR:
+ ssp_dbg(" %u : %u, %u, %u, %u (%ums)\n", uSensor,
+ data->buf[uSensor].r, data->buf[uSensor].g,
+ data->buf[uSensor].b, data->buf[uSensor].w,
+ get_msdelay(data->adDelayBuf[uSensor]));
+ break;
+ case PRESSURE_SENSOR:
+ ssp_dbg(" %u : %d, %d (%ums)\n", uSensor,
+ data->buf[uSensor].pressure[0],
+ data->buf[uSensor].pressure[1],
+ get_msdelay(data->adDelayBuf[uSensor]));
+ break;
+ case GESTURE_SENSOR:
+ ssp_dbg(" %u : %d %d %d %d (%ums)\n", uSensor,
+ data->buf[uSensor].data[0], data->buf[uSensor].data[1],
+ data->buf[uSensor].data[2], data->buf[uSensor].data[3],
+ get_msdelay(data->adDelayBuf[uSensor]));
+ break;
+ case PROXIMITY_SENSOR:
+ ssp_dbg(" %u : %d %d(%ums)\n", uSensor,
+ data->buf[uSensor].prox[0], data->buf[uSensor].prox[1],
+ get_msdelay(data->adDelayBuf[uSensor]));
+ }
+}
+
+void print_mcu_debug(char *pchRcvDataFrame, int *pDataIdx)
+{
+ int iLength;
+
+ iLength = pchRcvDataFrame[0];
+ pchRcvDataFrame[iLength] = 0;
+ *pDataIdx = *pDataIdx + iLength + 2;
+
+ ssp_dbg("[SSP] MSG From MCU : %s\n", pchRcvDataFrame + 1);
+}
+
+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("[SSP]: %s - Sensor state: 0x%x, TO: %u, BC: %u, RC: %u\n",
+ __func__, data->uAliveSensorDebug, data->uTimeOutCnt,
+ data->uBusyCnt, data->uResetCnt);
+ for (uSensorCnt = 0; uSensorCnt < (SENSOR_MAX - 1); uSensorCnt++)
+ if (atomic_read(&data->aSensorEnable) & (1 << uSensorCnt))
+ print_sensordata(data, uSensorCnt);
+
+ if ((data->uSsdFailCnt >= 3) || (data->uI2cFailCnt >= 1)) {
+ if (data->uResetCnt < 20) {
+ reset_mcu(data);
+ data->uResetCnt++;
+ }
+
+ data->uSsdFailCnt = 0;
+ data->uI2cFailCnt = 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;
+}
diff --git a/drivers/sensorhub/ssp_dev.c b/drivers/sensorhub/ssp_dev.c
new file mode 100644
index 0000000..275b8a9
--- /dev/null
+++ b/drivers/sensorhub/ssp_dev.c
@@ -0,0 +1,457 @@
+/*
+ * 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 mcu device ID */
+#define DEVICE_ID 0x55
+
+static void ssp_early_suspend(struct early_suspend *handler);
+static void ssp_late_resume(struct early_suspend *handler);
+
+/************************************************************************/
+/* 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;
+
+ data_dbg("%s\n", __func__);
+ select_irq_msg(data);
+
+ 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->aiCheckStatus[iSensorIndex] = INITIALIZATION_STATE;
+ }
+
+ /* AKM Daemon Library */
+ data->aiCheckStatus[GEOMAGNETIC_SENSOR] = NO_SENSOR_STATE;
+ data->aiCheckStatus[ORIENTATION_SENSOR] = NO_SENSOR_STATE;
+ memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX);
+
+ atomic_set(&data->aSensorEnable, 0);
+ data->iLibraryLength = 0;
+ data->uAliveSensorDebug = 0;
+ data->uFactorydataReady = 0;
+ data->uFactoryProxAvg[0] = 0;
+
+ data->uResetCnt = 0;
+ data->uI2cFailCnt = 0;
+ data->uTimeOutCnt = 0;
+ data->uSsdFailCnt = 0;
+ data->uBusyCnt = 0;
+
+ data->bCheckSuspend = false;
+ data->bDebugEnabled = false;
+ data->bProximityRawEnabled = false;
+ data->bMcuIRQTestSuccessed = false;
+ data->bBarcodeEnabled = false;
+ data->bBinaryChashed = false;
+
+ 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->iPressureCal = 0;
+ data->uProxCanc = 0;
+ data->uProxThresh = DEFAULT_THRESHOLD;
+ data->uGyroDps = GYROSCOPE_DPS500;
+
+ initialize_function_pointer(data);
+}
+
+int initialize_mcu(struct ssp_data *data)
+{
+ int iRet = 0;
+
+ iRet = get_chipid(data);
+ pr_info("[SSP] MPU device ID = %d, reading ID = %d\n", DEVICE_ID, iRet);
+ if (iRet != DEVICE_ID) {
+ if (iRet < 0)
+ pr_err("[SSP]: %s - i2c for reading chip id failed\n",
+ __func__);
+ else {
+ pr_err("[SSP]: %s - Device identification failed\n",
+ __func__);
+ iRet = -ENODEV;
+ }
+ return iRet;
+ }
+
+ iRet = set_sensor_position(data);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - set_sensor_position failed\n", __func__);
+ return iRet;
+ }
+
+ iRet = get_fuserom_data(data);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - get_fuserom_data failed\n", __func__);
+ return iRet;
+ }
+
+ data->uAliveSensorDebug = get_sensor_scanning_info(data);
+ if (data->uAliveSensorDebug == 0) {
+ pr_err("[SSP]: %s - get_sensor_scanning_info failed\n",
+ __func__);
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+static int initialize_irq(struct ssp_data *data)
+{
+ int iRet, iIrq;
+
+ iRet = gpio_request(data->client->irq, "mpu_ap_int1");
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - gpio %d request failed (%d)\n",
+ __func__, data->client->irq, iRet);
+ return iRet;
+ }
+
+ iRet = gpio_direction_input(data->client->irq);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - failed to set gpio %d as input (%d)\n",
+ __func__, data->client->irq, iRet);
+ goto err_irq_direction_input;
+ }
+
+ iIrq = gpio_to_irq(data->client->irq);
+
+ pr_info("[SSP]: requesting IRQ %d\n", iIrq);
+ iRet = request_threaded_irq(iIrq, NULL, sensordata_irq_thread_fn,
+ IRQF_TRIGGER_FALLING, "SSP_Int", data);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - request_irq(%d) failed for gpio %d (%d)\n",
+ __func__, iIrq, iIrq, iRet);
+ goto err_request_irq;
+ }
+
+ /* start with interrupts disabled */
+ data->iIrq = iIrq;
+ disable_irq(data->iIrq);
+ return 0;
+
+err_request_irq:
+err_irq_direction_input:
+ gpio_free(data->client->irq);
+ return iRet;
+}
+
+static int ssp_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ int iRet = 0;
+ struct ssp_data *data;
+ struct ssp_platform_data *pdata = client->dev.platform_data;
+
+ if (pdata == NULL) {
+ pr_err("[SSP]: %s - platform_data is null..\n", __func__);
+ iRet = -ENOMEM;
+ goto exit;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (data == NULL) {
+ pr_err("[SSP]: %s - failed to allocate memory for module data\n",
+ __func__);
+ iRet = -ENOMEM;
+ goto exit;
+ }
+
+ data->client = client;
+ data->client->adapter->timeout = HZ;
+ i2c_set_clientdata(client, data);
+
+ data->wakeup_mcu = pdata->wakeup_mcu;
+ data->check_mcu_ready = pdata->check_mcu_ready;
+ data->check_mcu_busy = pdata->check_mcu_busy;
+ data->set_mcu_reset = pdata->set_mcu_reset;
+ data->check_ap_rev = pdata->check_ap_rev;
+
+ if ((data->wakeup_mcu == NULL)
+ || (data->check_mcu_ready == NULL)
+ || (data->check_mcu_busy == NULL)
+ || (data->set_mcu_reset == NULL)
+ || (data->check_ap_rev == NULL)) {
+ pr_err("[SSP]: %s - function callback is null\n", __func__);
+ iRet = -EIO;
+ goto err_reset_null;
+ }
+
+ pr_info("\n#####################################################\n");
+
+ /* check boot loader binary */
+ check_fwbl(data);
+
+ initialize_variable(data);
+
+ iRet = initialize_mcu(data);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - initialize_mcu failed\n", __func__);
+ goto err_read_reg;
+ }
+
+ wake_lock_init(&data->ssp_wake_lock,
+ WAKE_LOCK_SUSPEND, "ssp_wake_lock");
+
+ iRet = initialize_input_dev(data);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - could not create input device\n", __func__);
+ goto err_input_register_device;
+ }
+
+ initialize_magnetic(data);
+
+ iRet = misc_register(&data->akmd_device);
+ if (iRet)
+ goto err_akmd_device_register;
+
+ iRet = initialize_debug_timer(data);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - could not create workqueue\n", __func__);
+ goto err_create_workqueue;
+ }
+
+ iRet = initialize_irq(data);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - could not create sysfs\n", __func__);
+ goto err_setup_irq;
+ }
+
+ iRet = initialize_sysfs(data);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - could not create sysfs\n", __func__);
+ goto err_sysfs_create;
+ }
+
+ iRet = initialize_event_symlink(data);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - could not create symlink\n", __func__);
+ goto err_symlink_create;
+ }
+
+#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
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ /* init sensorhub device */
+ iRet = ssp_initialize_sensorhub(data);
+ if (iRet < 0) {
+ pr_err("%s: ssp_initialize_sensorhub err(%d)", __func__, iRet);
+ ssp_remove_sensorhub(data);
+ }
+#endif
+
+ enable_irq(data->iIrq);
+ enable_irq_wake(data->iIrq);
+ pr_info("[SSP] probe success!\n");
+
+ enable_debug_timer(data);
+
+ iRet = 0;
+ goto exit;
+
+err_symlink_create:
+ remove_sysfs(data);
+err_sysfs_create:
+ free_irq(data->iIrq, data);
+ gpio_free(data->client->irq);
+err_setup_irq:
+ destroy_workqueue(data->debug_wq);
+err_create_workqueue:
+ misc_deregister(&data->akmd_device);
+err_akmd_device_register:
+ remove_input_dev(data);
+err_input_register_device:
+ wake_lock_destroy(&data->ssp_wake_lock);
+err_read_reg:
+err_reset_null:
+ kfree(data);
+ pr_err("[SSP]: %s - probe failed!\n", __func__);
+exit:
+ pr_info("#####################################################\n\n");
+ return iRet;
+}
+
+static void ssp_shutdown(struct i2c_client *client)
+{
+ struct ssp_data *data = i2c_get_clientdata(client);
+
+ func_dbg();
+
+ disable_debug_timer(data);
+
+ disable_irq_wake(data->iIrq);
+ disable_irq(data->iIrq);
+ free_irq(data->iIrq, data);
+ gpio_free(data->client->irq);
+
+ toggle_mcu_reset(data);
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ ssp_remove_sensorhub(data);
+#endif
+ remove_event_symlink(data);
+ remove_sysfs(data);
+ remove_input_dev(data);
+
+ misc_deregister(&data->akmd_device);
+
+ del_timer_sync(&data->debug_timer);
+ cancel_work_sync(&data->work_debug);
+ destroy_workqueue(data->debug_wq);
+
+ wake_lock_destroy(&data->ssp_wake_lock);
+ 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_report_sensorhub_notice(data, MSG2SSP_AP_STATUS_SLEEP);
+ ssp_sleep_mode(data);
+#else
+ if (atomic_read(&data->aSensorEnable) > 0)
+ ssp_sleep_mode(data);
+#endif
+
+ data->bCheckSuspend = true;
+}
+
+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);
+
+ data->bCheckSuspend = false;
+
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ /* give notice to user that AP goes to sleep */
+ ssp_report_sensorhub_notice(data, MSG2SSP_AP_STATUS_WAKEUP);
+ ssp_resume_mode(data);
+#else
+ if (atomic_read(&data->aSensorEnable) > 0)
+ ssp_resume_mode(data);
+#endif
+}
+#else
+
+static int ssp_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ssp_data *data = i2c_get_clientdata(client);
+
+ func_dbg();
+ if (data->bDebugEnabled)
+ disable_debug_timer(data);
+
+ if (atomic_read(&data->aSensorEnable) > 0)
+ ssp_sleep_mode(data);
+
+ data->bCheckSuspend = true;
+ return 0;
+}
+
+static int ssp_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ssp_data *data = i2c_get_clientdata(client);
+
+ func_dbg();
+ if (data->bDebugEnabled)
+ enable_debug_timer(data);
+
+ data->bCheckSuspend = false;
+
+ if (atomic_read(&data->aSensorEnable) > 0)
+ ssp_resume_mode(data);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ssp_pm_ops = {
+ .suspend = ssp_suspend,
+ .resume = ssp_resume
+};
+#endif
+
+static const struct i2c_device_id ssp_id[] = {
+ {"ssp", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ssp_id);
+
+static struct i2c_driver ssp_driver = {
+ .probe = ssp_probe,
+ .shutdown = ssp_shutdown,
+ .id_table = ssp_id,
+ .driver = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .pm = &ssp_pm_ops,
+#endif
+ .owner = THIS_MODULE,
+ .name = "ssp"
+ },
+};
+
+static int __init ssp_init(void)
+{
+ return i2c_add_driver(&ssp_driver);
+}
+
+static void __exit ssp_exit(void)
+{
+ i2c_del_driver(&ssp_driver);
+}
+
+module_init(ssp_init);
+module_exit(ssp_exit);
+
+MODULE_DESCRIPTION("ssp driver");
+MODULE_AUTHOR("Kyusung Kim <gs0816.kim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/sensorhub/ssp_firmware.c b/drivers/sensorhub/ssp_firmware.c
new file mode 100644
index 0000000..ae04287
--- /dev/null
+++ b/drivers/sensorhub/ssp_firmware.c
@@ -0,0 +1,303 @@
+/*
+ * 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 90100
+
+/* Bootload mode cmd */
+#define BL_FW_NAME "ssp.fw"
+#define BL_CRASHED_FW_NAME "ssp_crashed.fw"
+
+#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
+
+unsigned int get_module_rev(void)
+{
+ return SSP_FIRMWARE_REVISION;
+}
+
+static int check_bootloader(struct i2c_client *client, unsigned int uState)
+{
+ u8 uVal;
+ u8 uTemp;
+
+recheck:
+ if (i2c_master_recv(client, &uVal, 1) != 1)
+ return -EIO;
+
+ if (uVal & 0x20) {
+ if (i2c_master_recv(client, &uTemp, 1) != 1) {
+ pr_err("[SSP]: %s - i2c recv fail\n", __func__);
+ return -EIO;
+ }
+
+ if (i2c_master_recv(client, &uTemp, 1) != 1) {
+ pr_err("[SSP]: %s - i2c recv fail\n", __func__);
+ return -EIO;
+ }
+
+ uVal &= ~0x20;
+ }
+
+ if ((uVal & 0xF0) == BL_APP_CRC_FAIL) {
+ pr_info("[SSP] SSP_APP_CRC_FAIL - There is no bootloader.\n");
+ if (i2c_master_recv(client, &uVal, 1) != 1) {
+ pr_err("[SSP]: %s - i2c recv fail\n", __func__);
+ return -EIO;
+ }
+
+ if (uVal & 0x20) {
+ if (i2c_master_recv(client, &uTemp, 1) != 1) {
+ pr_err("[SSP]: %s - i2c recv fail\n", __func__);
+ return -EIO;
+ }
+
+ if (i2c_master_recv(client, &uTemp, 1) != 1) {
+ pr_err("[SSP]: %s - i2c recv fail\n", __func__);
+ return -EIO;
+ }
+
+ uVal &= ~0x20;
+ }
+ }
+
+ switch (uState) {
+ case BL_WAITING_BOOTLOAD_CMD:
+ case BL_WAITING_FRAME_DATA:
+ uVal &= ~BL_BOOT_STATUS_MASK;
+ break;
+ case BL_FRAME_CRC_PASS:
+ if (uVal == BL_FRAME_CRC_CHECK)
+ goto recheck;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (uVal != uState) {
+ pr_err("[SSP]: %s - Unvalid bootloader mode state\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int unlock_bootloader(struct i2c_client *client)
+{
+ u8 uBuf[2];
+
+ uBuf[0] = BL_UNLOCK_CMD_LSB;
+ uBuf[1] = BL_UNLOCK_CMD_MSB;
+
+ if (i2c_master_send(client, uBuf, 2) != 2) {
+ pr_err("[SSP]: %s - i2c send failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int fw_write(struct i2c_client *client,
+ const u8 *pData, unsigned int uFrameSize)
+{
+ if (i2c_master_send(client, pData, uFrameSize) != uFrameSize) {
+ pr_err("[SSP]: %s - i2c send failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int load_fw_bootmode(struct i2c_client *client, const char *pFn)
+{
+ const struct firmware *fw = NULL;
+ unsigned int uFrameSize;
+ unsigned int uPos = 0;
+ int iRet;
+ int iCheckFrameCrcError = 0;
+ int iCheckWatingFrameDataError = 0;
+
+ pr_info("[SSP] ssp_load_fw start!!!\n");
+
+ iRet = request_firmware(&fw, pFn, &client->dev);
+ if (iRet) {
+ pr_err("[SSP]: %s - Unable to open firmware %s\n",
+ __func__, pFn);
+ return iRet;
+ }
+
+ /* Unlock bootloader */
+ unlock_bootloader(client);
+
+ while (uPos < fw->size) {
+ iRet = check_bootloader(client, BL_WAITING_FRAME_DATA);
+ if (iRet) {
+ iCheckWatingFrameDataError++;
+ if (iCheckWatingFrameDataError > 10) {
+ pr_err("[SSP]: %s - F/W update fail\n",
+ __func__);
+ goto out;
+ } else {
+ pr_err("[SSP]: %s - F/W data_error %d, retry\n",
+ __func__, iCheckWatingFrameDataError);
+ continue;
+ }
+ }
+
+ uFrameSize = ((*(fw->data + uPos) << 8) |
+ *(fw->data + uPos + 1));
+
+ /* We should add 2 at frame size as the the firmware data is not
+ * included the CRC bytes.
+ */
+ uFrameSize += 2;
+
+ /* Write one frame to device */
+ fw_write(client, fw->data + uPos, uFrameSize);
+ iRet = check_bootloader(client, BL_FRAME_CRC_PASS);
+ if (iRet) {
+ iCheckFrameCrcError++;
+ if (iCheckFrameCrcError > 10) {
+ pr_err("[SSP]: %s - F/W Update Fail. crc err\n",
+ __func__);
+ goto out;
+ } else {
+ pr_err("[SSP]: %s - F/W crc_error %d, retry\n",
+ __func__, iCheckFrameCrcError);
+ continue;
+ }
+ }
+
+ uPos += uFrameSize;
+
+ pr_info("[SSP] Updated %d bytes / %zd bytes\n", uPos, fw->size);
+ mdelay(1);
+ }
+
+out:
+ release_firmware(fw);
+ return iRet;
+}
+
+static void change_to_bootmode(struct ssp_data *data)
+{
+ int iCnt = 0;
+
+ for (iCnt = 0; iCnt < 10; iCnt++) {
+ data->set_mcu_reset(0);
+ udelay(10);
+
+ data->set_mcu_reset(1);
+ msleep(100);
+ }
+
+ msleep(50);
+}
+
+void toggle_mcu_reset(struct ssp_data *data)
+{
+ u8 uBuf[2];
+
+ uBuf[0] = 0x00;
+ uBuf[1] = 0x00;
+
+ data->set_mcu_reset(0);
+ udelay(10);
+ data->set_mcu_reset(1);
+ mdelay(50);
+
+ data->client->addr = BOOTLOADER_SLAVE_ADDR;
+
+ if (i2c_master_send(data->client, uBuf, 2) != 2)
+ pr_err("[SSP]: %s - ssp_Normal Mode\n", __func__);
+ else
+ pr_err("[SSP]: %s - ssp_load_fw_bootmode\n", __func__);
+
+ data->client->addr = APP_SLAVE_ADDR;
+}
+
+int update_mcu_bin(struct ssp_data *data)
+{
+ int iRet = 0;
+
+ pr_info("[SSP] ssp_change_to_bootmode\n");
+ change_to_bootmode(data);
+ data->client->addr = BOOTLOADER_SLAVE_ADDR;
+ iRet = load_fw_bootmode(data->client, BL_FW_NAME);
+
+ msleep(SSP_SW_RESET_TIME);
+
+ data->client->addr = APP_SLAVE_ADDR;
+
+ if (iRet < 0)
+ data->bBinaryChashed = true;
+
+ return iRet;
+}
+
+int update_crashed_mcu_bin(struct ssp_data *data)
+{
+ int iRet = 0;
+ pr_info("[SSP] ssp_change_to_bootmode\n");
+ change_to_bootmode(data);
+ data->client->addr = BOOTLOADER_SLAVE_ADDR;
+ iRet = load_fw_bootmode(data->client, BL_CRASHED_FW_NAME);
+
+ msleep(SSP_SW_RESET_TIME);
+
+ data->client->addr = APP_SLAVE_ADDR;
+ data->bBinaryChashed = true;
+ return iRet;
+}
+
+void check_fwbl(struct ssp_data *data)
+{
+ int iRet;
+
+ data->client->addr = BOOTLOADER_SLAVE_ADDR;
+ iRet = check_bootloader(data->client, BL_WAITING_BOOTLOAD_CMD);
+
+ if (iRet >= 0) {
+ pr_info("[SSP] ssp_load_fw_bootmode\n");
+ load_fw_bootmode(data->client, BL_FW_NAME);
+ msleep(SSP_SW_RESET_TIME);
+ } else {
+ data->client->addr = APP_SLAVE_ADDR;
+ data->uCurFirmRev = get_firmware_rev(data);
+ if (data->uCurFirmRev != SSP_FIRMWARE_REVISION) {
+ pr_info("[SSP] MPU Firm Rev. : Old = %8u, New = %8u\n",
+ data->uCurFirmRev, SSP_FIRMWARE_REVISION);
+ update_mcu_bin(data);
+ }
+ }
+
+ data->client->addr = APP_SLAVE_ADDR;
+ data->uCurFirmRev = get_firmware_rev(data);
+ pr_info("[SSP] MPU Firm Rev. : Old = %8u, New = %8u\n",
+ data->uCurFirmRev, SSP_FIRMWARE_REVISION);
+}
diff --git a/drivers/sensorhub/ssp_i2c.c b/drivers/sensorhub/ssp_i2c.c
new file mode 100644
index 0000000..f18af89
--- /dev/null
+++ b/drivers/sensorhub/ssp_i2c.c
@@ -0,0 +1,471 @@
+/*
+ * 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"
+
+int waiting_wakeup_mcu(struct ssp_data *data)
+{
+ int iDelaycnt = 0;
+
+ while (!data->check_mcu_busy() && (iDelaycnt++ < 100))
+ mdelay(5);
+
+ if (iDelaycnt >= 100) {
+ pr_err("[SSP]: %s - MCU Irq Timeout!!\n", __func__);
+ data->uBusyCnt++;
+ }
+
+ iDelaycnt = 0;
+ data->wakeup_mcu();
+ while (data->check_mcu_ready() && (iDelaycnt++ < 40))
+ udelay(50);
+
+ if (iDelaycnt >= 40) {
+ pr_err("[SSP]: %s - MCU Wakeup Timeout!!\n", __func__);
+ data->uTimeOutCnt++;
+ return ERROR;
+ }
+
+ return SUCCESS;
+}
+
+int ssp_i2c_read(struct ssp_data *data, char *pTxData, u16 uTxLength,
+ char *pRxData, u16 uRxLength)
+{
+ int iRetries = 3, iRet = 0;
+ struct i2c_client *client = data->client;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = uTxLength,
+ .buf = pTxData,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = uRxLength,
+ .buf = pRxData,
+ },
+ };
+
+ while (iRetries--) {
+ iRet = i2c_transfer(client->adapter, msgs, 2);
+ if (iRet < 0) {
+ pr_err("[SSP]: %s - i2c transfer error %d! retry...\n",
+ __func__, iRet);
+ mdelay(10);
+ } else {
+ return SUCCESS;
+ }
+ }
+ return ERROR;
+}
+
+int ssp_sleep_mode(struct ssp_data *data)
+{
+ char chRxData = 0;
+ char chTxData = MSG2SSP_AP_STATUS_SLEEP;
+ int iRet = 0;
+
+ waiting_wakeup_mcu(data);
+
+ /* send to AP_STATUS_SLEEP */
+ iRet = ssp_i2c_read(data, &chTxData, 1, &chRxData, 1);
+ if ((chRxData != MSG_ACK) || (iRet != SUCCESS)) {
+ pr_err("[SSP]: %s - MSG2SSP_AP_STATUS_SLEEP CMD fail %d\n",
+ __func__, iRet);
+ return ERROR;
+ }
+ ssp_dbg("[SSP]: %s - MSG2SSP_AP_STATUS_SLEEP CMD\n", __func__);
+
+ return SUCCESS;
+}
+
+int ssp_resume_mode(struct ssp_data *data)
+{
+ char chRxData = 0;
+ char chTxData = MSG2SSP_AP_STATUS_WAKEUP;
+ int iRet = 0;
+
+ waiting_wakeup_mcu(data);
+
+ /* send to AP_STATUS_WAKEUP */
+ iRet = ssp_i2c_read(data, &chTxData, 1, &chRxData, 1);
+ if ((chRxData != MSG_ACK) || (iRet != SUCCESS)) {
+ pr_err("[SSP]: %s - MSG2SSP_AP_STATUS_WAKEUP CMD fail %d\n",
+ __func__, iRet);
+ return ERROR;
+ }
+ ssp_dbg("[SSP]: %s - MSG2SSP_AP_STATUS_WAKEUP CMD\n", __func__);
+
+ return SUCCESS;
+}
+
+int send_instruction(struct ssp_data *data, u8 uInst,
+ u8 uSensorType, u8 *uSendBuf, u8 uLength)
+{
+ char aTxData[uLength + 5];
+ char chRxbuf = 0;
+ int iRet = 0, iRetries = 3;
+
+ waiting_wakeup_mcu(data);
+
+ aTxData[0] = MSG2SSP_STS;
+ aTxData[1] = 3 + uLength;
+ aTxData[2] = MSG2SSP_SSM;
+
+ switch (uInst) {
+ case REMOVE_SENSOR:
+ aTxData[3] = MSG2SSP_INST_BYPASS_SENSOR_REMOVE;
+ break;
+ case ADD_SENSOR:
+ aTxData[3] = MSG2SSP_INST_BYPASS_SENSOR_ADD;
+ break;
+ case CHANGE_DELAY:
+ aTxData[3] = MSG2SSP_INST_CHANGE_DELAY;
+ break;
+ case GO_SLEEP:
+ aTxData[3] = MSG2SSP_AP_STATUS_SLEEP;
+ break;
+ case FACTORY_MODE:
+ aTxData[3] = MSG2SSP_INST_SENSOR_SELFTEST;
+ break;
+ case REMOVE_LIBRARY:
+ aTxData[3] = MSG2SSP_INST_LIBRARY_REMOVE;
+ break;
+ case ADD_LIBRARY:
+ aTxData[3] = MSG2SSP_INST_LIBRARY_ADD;
+ break;
+ default:
+ aTxData[3] = uInst;
+ break;
+ }
+
+ aTxData[4] = uSensorType;
+ memcpy(&aTxData[5], uSendBuf, uLength);
+
+#if 0
+ iRet = ssp_i2c_read(data, aTxData, 2, &chRxbuf, 1);
+ if (iRet != SUCCESS) {
+ pr_err("[SSP]: %s - MSG2SSP_STS - i2c fail %d\n",
+ __func__, iRet);
+ return ERROR;
+ } else if (chRxbuf != MSG_ACK) {
+ while (iRetries--) {
+ mdelay(10);
+ pr_err("[SSP]: %s - MSG2SSP_STS retry...\n", __func__);
+ iRet = ssp_i2c_read(data, aTxData, 2, &chRxbuf, 1);
+ if ((iRet == SUCCESS) && (chRxbuf == MSG_ACK))
+ break;
+ }
+
+ if (iRetries <= 0)
+ return FAIL;
+ }
+#endif
+
+ chRxbuf = 0;
+ iRetries = 3;
+ iRet = ssp_i2c_read(data, &(aTxData[2]), (u16)aTxData[1], &chRxbuf, 1);
+ if (iRet != SUCCESS) {
+ pr_err("[SSP]: %s - Instruction CMD Fail %d\n",
+ __func__, iRet);
+ return ERROR;
+ } else if (chRxbuf != MSG_ACK) {
+ while (iRetries--) {
+ mdelay(10);
+ pr_err("[SSP]: %s - Instruction CMD retry...\n",
+ __func__);
+ iRet = ssp_i2c_read(data, &(aTxData[2]),
+ (u16)aTxData[1], &chRxbuf, 1);
+ if ((iRet == SUCCESS) && (chRxbuf == MSG_ACK))
+ break;
+ }
+
+ if (iRetries <= 0) {
+ data->uI2cFailCnt++;
+ return FAIL;
+ }
+ }
+
+ data->uI2cFailCnt = 0;
+ ssp_dbg("[SSP]: %s - Inst = 0x%x, Sensor Type = 0x%x, "
+ "data = %u, %u\n", __func__, aTxData[3], aTxData[4],
+ aTxData[5], aTxData[6]);
+ return SUCCESS;
+}
+
+static int ssp_recieve_msg(struct ssp_data *data, u8 uLength)
+{
+ char chTxBuf = 0;
+ char *pchRcvDataFrame; /* SSP-AP Massage data buffer */
+ int iRet = 0;
+
+ if (uLength > 0) {
+ pchRcvDataFrame = kzalloc((uLength * sizeof(char)), GFP_KERNEL);
+ chTxBuf = MSG2SSP_SRM;
+ iRet = ssp_i2c_read(data, &chTxBuf, 1, pchRcvDataFrame,
+ (u16)uLength);
+ if (iRet != SUCCESS) {
+ pr_err("[SSP]: %s - Fail to recieve data %d\n",
+ __func__, iRet);
+ kfree(pchRcvDataFrame);
+ return ERROR;
+ }
+ } else {
+ pr_err("[SSP]: %s - No ready data. length = %d\n",
+ __func__, uLength);
+ return FAIL;
+ }
+
+ parse_dataframe(data, pchRcvDataFrame, uLength);
+
+ kfree(pchRcvDataFrame);
+ return uLength;
+}
+
+int get_chipid(struct ssp_data *data)
+{
+ int iRet;
+
+ waiting_wakeup_mcu(data);
+
+ /* read chip id */
+ iRet = i2c_smbus_read_byte_data(data->client, MSG2SSP_AP_WHOAMI);
+
+ return iRet;
+}
+
+int set_sensor_position(struct ssp_data *data)
+{
+ char chTxBuf[5] = { 0, };
+ char chRxData = 0;
+ int iRet = 0;
+
+ waiting_wakeup_mcu(data);
+
+ chTxBuf[0] = MSG2SSP_AP_SENSOR_FORMATION;
+
+ chTxBuf[1] = CONFIG_SENSORS_SSP_ACCELEROMETER_POSITION;
+ chTxBuf[2] = CONFIG_SENSORS_SSP_GYROSCOPE_POSITION;
+ chTxBuf[3] = CONFIG_SENSORS_SSP_MAGNETOMETER_POSITION;
+ chTxBuf[4] = 0;
+
+#if defined(CONFIG_MACH_T0_EUR_OPEN)
+ if (data->check_ap_rev() == 0x03) {
+ chTxBuf[1] = 7;
+ chTxBuf[2] = 7;
+ chTxBuf[3] = CONFIG_SENSORS_SSP_MAGNETOMETER_POSITION;
+ }
+#endif
+
+#if defined(CONFIG_MACH_T0_USA_SPR) || defined(CONFIG_MACH_T0_USA_USCC)\
+ || defined(CONFIG_MACH_T0_USA_VZW)
+ if (data->check_ap_rev() <= 0x04) {
+ chTxBuf[1] = 4;
+ chTxBuf[2] = 4;
+ chTxBuf[3] = CONFIG_SENSORS_SSP_MAGNETOMETER_POSITION;
+ }
+#endif
+
+ pr_info("[SSP] Sensor Posision A : %u, G : %u, M: %u, P: %u\n",
+ chTxBuf[1], chTxBuf[2], chTxBuf[3], chTxBuf[4]);
+
+ iRet = ssp_i2c_read(data, chTxBuf, 5, &chRxData, 1);
+ if ((chRxData != MSG_ACK) || (iRet != SUCCESS)) {
+ pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet);
+ iRet = ERROR;
+ }
+
+ return iRet;
+}
+
+void set_proximity_threshold(struct ssp_data *data)
+{
+ char chTxBuf[3] = { 0, };
+ char chRxData = 0;
+ int iRet = 0;
+
+ waiting_wakeup_mcu(data);
+
+ chTxBuf[0] = MSG2SSP_AP_SENSOR_PROXTHRESHOLD;
+ chTxBuf[1] = data->uProxThresh;
+ chTxBuf[2] = data->uProxCanc;
+
+ pr_info("[SSP] Proximity Threshold : %u, Cancelation : %u\n",
+ data->uProxThresh, data->uProxCanc);
+
+ iRet = ssp_i2c_read(data, chTxBuf, 3, &chRxData, 1);
+ if ((chRxData != MSG_ACK) || (iRet != SUCCESS))
+ pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet);
+}
+
+void set_proximity_barcode_enable(struct ssp_data *data, bool bEnable)
+{
+ char chTxBuf[2] = { 0, };
+ char chRxData = 0;
+ int iRet = 0;
+
+ waiting_wakeup_mcu(data);
+
+ chTxBuf[0] = MSG2SSP_AP_SENSOR_BARCODE_EMUL;
+ chTxBuf[1] = bEnable;
+
+ data->bBarcodeEnabled = bEnable;
+ pr_info("[SSP] Proximity Barcode En : %u\n", bEnable);
+
+ iRet = ssp_i2c_read(data, chTxBuf, 2, &chRxData, 1);
+ if ((chRxData != MSG_ACK) || (iRet != SUCCESS))
+ pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet);
+}
+
+unsigned int get_sensor_scanning_info(struct ssp_data *data)
+{
+ char chTxBuf = MSG2SSP_AP_SENSOR_SCANNING;
+ char chRxData[2] = {0,};
+ int iRet = 0;
+
+ waiting_wakeup_mcu(data);
+
+ iRet = ssp_i2c_read(data, &chTxBuf, 1, chRxData, 2);
+ if (iRet != SUCCESS) {
+ pr_err("[SSP]: %s - i2c failed %d\n", __func__, iRet);
+ return 0;
+ }
+ return ((unsigned int)chRxData[0] << 8) | chRxData[1];
+}
+
+unsigned int get_firmware_rev(struct ssp_data *data)
+{
+ char chTxData = MSG2SSP_AP_FIRMWARE_REV;
+ char chRxBuf[3] = { 0, };
+ unsigned int uRev = 99999;
+ int iRet;
+
+ waiting_wakeup_mcu(data);
+
+ iRet = ssp_i2c_read(data, &chTxData, 1, chRxBuf, 3);
+ if (iRet != SUCCESS)
+ pr_err("[SSP]: %s - i2c fail %d\n", __func__, iRet);
+ else
+ uRev = ((unsigned int)chRxBuf[0] << 16)
+ | ((unsigned int)chRxBuf[1] << 8) | chRxBuf[2];
+ return uRev;
+}
+
+int get_fuserom_data(struct ssp_data *data)
+{
+ char chTxBuf[2] = { 0, };
+ char chRxBuf[2] = { 0, };
+ int iRet = 0;
+ unsigned int uLength = 0;
+
+ waiting_wakeup_mcu(data);
+
+ chTxBuf[0] = MSG2SSP_AP_STT;
+ chTxBuf[1] = MSG2SSP_AP_FUSEROM;
+
+ /* chRxBuf is Two Byte because msg is large */
+ iRet = ssp_i2c_read(data, chTxBuf, 2, chRxBuf, 2);
+ uLength = ((unsigned int)chRxBuf[0] << 8) + (unsigned int)chRxBuf[1];
+
+ if (iRet != SUCCESS) {
+ pr_err("[SSP]: %s - MSG2SSP_AP_STT - i2c fail %d\n",
+ __func__, iRet);
+ goto err_read_fuserom;
+ } else if (uLength <= 0) {
+ pr_err("[SSP]: %s - No ready data. length = %u\n",
+ __func__, uLength);
+ goto err_read_fuserom;
+ } else {
+ if (data->iLibraryLength != 0)
+ kfree(data->pchLibraryBuf);
+
+ data->iLibraryLength = (int)uLength;
+ data->pchLibraryBuf =
+ kzalloc((data->iLibraryLength * sizeof(char)), GFP_KERNEL);
+
+ chTxBuf[0] = MSG2SSP_SRM;
+ iRet = ssp_i2c_read(data, chTxBuf, 1, data->pchLibraryBuf,
+ (u16)data->iLibraryLength);
+ if (iRet != SUCCESS) {
+ pr_err("[SSP]: %s - Fail to recieve SSP data %d\n",
+ __func__, iRet);
+ kfree(data->pchLibraryBuf);
+ data->iLibraryLength = 0;
+ goto err_read_fuserom;
+ }
+ }
+
+ data->uFuseRomData[0] = data->pchLibraryBuf[0];
+ data->uFuseRomData[1] = data->pchLibraryBuf[1];
+ data->uFuseRomData[2] = data->pchLibraryBuf[2];
+
+ pr_info("[SSP] FUSE ROM Data %d , %d, %d\n", data->uFuseRomData[0],
+ data->uFuseRomData[1], data->uFuseRomData[2]);
+
+ data->iLibraryLength = 0;
+ kfree(data->pchLibraryBuf);
+ return SUCCESS;
+
+err_read_fuserom:
+ data->uFuseRomData[0] = 0;
+ data->uFuseRomData[1] = 0;
+ data->uFuseRomData[2] = 0;
+
+ return FAIL;
+}
+
+int select_irq_msg(struct ssp_data *data)
+{
+ u8 chLength = 0;
+ char chTxBuf = 0;
+ char chRxBuf[2] = { 0, };
+ int iRet = 0;
+
+ chTxBuf = MSG2SSP_SSD;
+ iRet = ssp_i2c_read(data, &chTxBuf, 1, chRxBuf, 2);
+
+ if (iRet != SUCCESS) {
+ pr_err("[SSP]: %s - MSG2SSP_SSD error %d\n", __func__, iRet);
+ return ERROR;
+ } else {
+ if (chRxBuf[0] == MSG2SSP_RTS) {
+ chLength = (u8)chRxBuf[1];
+ ssp_recieve_msg(data, chLength);
+ data->uSsdFailCnt = 0;
+ }
+#ifdef CONFIG_SENSORS_SSP_SENSORHUB
+ else if (chRxBuf[0] == MSG2SSP_STT) {
+ pr_info("%s: MSG2SSP_STT irq", __func__);
+ iRet = ssp_handle_sensorhub_large_data(data,
+ (u8)chRxBuf[1]);
+ if (iRet < 0) {
+ pr_err("%s: ssp_handle_sensorhub_data(%d)",
+ __func__, iRet);
+ }
+ data->uSsdFailCnt = 0;
+ }
+#endif
+ else {
+ pr_err("[SSP]: %s - MSG2SSP_SSD Data fail "
+ "[0]: 0x%x, [1]: 0x%x\n", __func__,
+ chRxBuf[0], chRxBuf[1]);
+ if ((chRxBuf[0] == 0) && (chRxBuf[1] == 0))
+ data->uSsdFailCnt++;
+ }
+ }
+ return SUCCESS;
+}
diff --git a/drivers/sensorhub/ssp_input.c b/drivers/sensorhub/ssp_input.c
new file mode 100644
index 0000000..f14f9f4
--- /dev/null
+++ b/drivers/sensorhub/ssp_input.c
@@ -0,0 +1,383 @@
+/*
+ * 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 > 2048)
+ *iValue = ((4096 - *iValue)) * (-1);
+}
+
+void report_acc_data(struct ssp_data *data, struct sensor_value *accdata)
+{
+ convert_acc_data(&accdata->x);
+ convert_acc_data(&accdata->y);
+ convert_acc_data(&accdata->z);
+
+ data->buf[ACCELEROMETER_SENSOR].x = accdata->x - data->accelcal.x;
+ data->buf[ACCELEROMETER_SENSOR].y = accdata->y - data->accelcal.y;
+ data->buf[ACCELEROMETER_SENSOR].z = accdata->z - data->accelcal.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->gyrocal.x;
+ data->buf[GYROSCOPE_SENSOR].y = gyrodata->y - data->gyrocal.y;
+ data->buf[GYROSCOPE_SENSOR].z = gyrodata->z - data->gyrocal.z;
+
+ if (data->uGyroDps == GYROSCOPE_DPS250) {
+ 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 if (data->uGyroDps == GYROSCOPE_DPS2000) {
+ lTemp[0] = (long)data->buf[GYROSCOPE_SENSOR].x * 4;
+ lTemp[1] = (long)data->buf[GYROSCOPE_SENSOR].y * 4;
+ lTemp[2] = (long)data->buf[GYROSCOPE_SENSOR].z * 4;
+ } 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);
+}
+
+void report_mag_data(struct ssp_data *data, struct sensor_value *magdata)
+{
+ data->buf[GEOMAGNETIC_SENSOR].x = magdata->x;
+ data->buf[GEOMAGNETIC_SENSOR].y = magdata->y;
+ data->buf[GEOMAGNETIC_SENSOR].z = magdata->z;
+}
+
+void report_gesture_data(struct ssp_data *data, struct sensor_value *gesdata)
+{
+ /* todo: remove func*/
+}
+
+void report_pressure_data(struct ssp_data *data, struct sensor_value *predata)
+{
+ data->buf[PRESSURE_SENSOR].pressure[0] =
+ predata->pressure[0] - data->iPressureCal;
+ data->buf[PRESSURE_SENSOR].pressure[1] = predata->pressure[1];
+
+ /* pressure */
+ input_report_rel(data->pressure_input_dev, REL_HWHEEL,
+ data->buf[PRESSURE_SENSOR].pressure[0]);
+ /* temperature */
+ input_report_rel(data->pressure_input_dev, REL_WHEEL,
+ data->buf[PRESSURE_SENSOR].pressure[1]);
+ input_sync(data->pressure_input_dev);
+}
+
+void report_light_data(struct ssp_data *data, struct sensor_value *lightdata)
+{
+ data->buf[LIGHT_SENSOR].r = lightdata->r;
+ data->buf[LIGHT_SENSOR].g = lightdata->g;
+ data->buf[LIGHT_SENSOR].b = lightdata->b;
+ data->buf[LIGHT_SENSOR].w = lightdata->w;
+
+ input_report_rel(data->light_input_dev, REL_HWHEEL,
+ data->buf[LIGHT_SENSOR].r + 1);
+ input_report_rel(data->light_input_dev, REL_DIAL,
+ data->buf[LIGHT_SENSOR].g + 1);
+ input_report_rel(data->light_input_dev, REL_WHEEL,
+ data->buf[LIGHT_SENSOR].b + 1);
+ input_report_rel(data->light_input_dev, REL_MISC,
+ data->buf[LIGHT_SENSOR].w + 1);
+ input_sync(data->light_input_dev);
+}
+
+void report_prox_data(struct ssp_data *data, struct sensor_value *proxdata)
+{
+ ssp_dbg("[SSP] Proximity Sensor Detect : %u, raw : %u\n",
+ proxdata->prox[0], proxdata->prox[1]);
+
+ data->buf[PROXIMITY_SENSOR].prox[0] = proxdata->prox[0];
+ data->buf[PROXIMITY_SENSOR].prox[1] = proxdata->prox[1];
+
+#if defined(CONFIG_MACH_T0_USA_SPR) || defined(CONFIG_MACH_T0_USA_USCC)\
+ || defined(CONFIG_MACH_T0_USA_VZW) || defined(CONFIG_MACH_T0_USA_TMO)\
+ || defined(CONFIG_MACH_T0_USA_ATT)
+ if (data->check_ap_rev() == 0x04)
+ proxdata->prox[0] = 0;
+#endif
+
+#if defined(CONFIG_MACH_T0_USA_TMO)
+ if (data->check_ap_rev() == 0x05)
+ proxdata->prox[0] = 0;
+#endif
+
+ input_report_abs(data->prox_input_dev, ABS_DISTANCE,
+ (!proxdata->prox[0]));
+ input_sync(data->prox_input_dev);
+
+ wake_lock_timeout(&data->ssp_wake_lock, 3 * HZ);
+}
+
+void report_prox_raw_data(struct ssp_data *data,
+ struct sensor_value *proxrawdata)
+{
+ if (data->uFactoryProxAvg[0]++ >= PROX_AVG_READ_NUM) {
+ data->uFactoryProxAvg[2] /= PROX_AVG_READ_NUM;
+ data->buf[PROXIMITY_RAW].prox[1] = (u8)data->uFactoryProxAvg[1];
+ data->buf[PROXIMITY_RAW].prox[2] = (u8)data->uFactoryProxAvg[2];
+ data->buf[PROXIMITY_RAW].prox[3] = (u8)data->uFactoryProxAvg[3];
+
+ data->uFactoryProxAvg[0] = 0;
+ data->uFactoryProxAvg[1] = 0;
+ data->uFactoryProxAvg[2] = 0;
+ data->uFactoryProxAvg[3] = 0;
+ } else {
+ data->uFactoryProxAvg[2] += proxrawdata->prox[0];
+
+ if (data->uFactoryProxAvg[0] == 1)
+ data->uFactoryProxAvg[1] = proxrawdata->prox[0];
+ else if (proxrawdata->prox[0] < data->uFactoryProxAvg[1])
+ data->uFactoryProxAvg[1] = proxrawdata->prox[0];
+
+ if (proxrawdata->prox[0] > data->uFactoryProxAvg[3])
+ data->uFactoryProxAvg[3] = proxrawdata->prox[0];
+ }
+
+ data->buf[PROXIMITY_RAW].prox[0] = proxrawdata->prox[0];
+}
+
+int initialize_event_symlink(struct ssp_data *data)
+{
+ int iRet = 0;
+ struct class *event_class = NULL;
+
+ event_class = class_create(THIS_MODULE, "sensor_event");
+ data->sen_dev = device_create(event_class, NULL, 0, NULL,
+ "%s", "symlink");
+
+ iRet = sysfs_create_link(&data->sen_dev->kobj,
+ &data->acc_input_dev->dev.kobj,
+ data->acc_input_dev->name);
+ if (iRet < 0)
+ goto iRet_acc_sysfs_create_link;
+
+ iRet = sysfs_create_link(&data->sen_dev->kobj,
+ &data->gyro_input_dev->dev.kobj,
+ data->gyro_input_dev->name);
+ if (iRet < 0)
+ goto iRet_gyro_sysfs_create_link;
+
+ iRet = sysfs_create_link(&data->sen_dev->kobj,
+ &data->pressure_input_dev->dev.kobj,
+ data->pressure_input_dev->name);
+ if (iRet < 0)
+ goto iRet_prs_sysfs_create_link;
+
+ iRet = sysfs_create_link(&data->sen_dev->kobj,
+ &data->light_input_dev->dev.kobj,
+ data->light_input_dev->name);
+ if (iRet < 0)
+ goto iRet_light_sysfs_create_link;
+
+ iRet = sysfs_create_link(&data->sen_dev->kobj,
+ &data->prox_input_dev->dev.kobj,
+ data->prox_input_dev->name);
+ if (iRet < 0)
+ goto iRet_prox_sysfs_create_link;
+
+ return SUCCESS;
+
+iRet_prox_sysfs_create_link:
+ sysfs_delete_link(&data->sen_dev->kobj,
+ &data->light_input_dev->dev.kobj,
+ data->light_input_dev->name);
+iRet_light_sysfs_create_link:
+ sysfs_delete_link(&data->sen_dev->kobj,
+ &data->pressure_input_dev->dev.kobj,
+ data->pressure_input_dev->name);
+iRet_prs_sysfs_create_link:
+ sysfs_delete_link(&data->sen_dev->kobj,
+ &data->gyro_input_dev->dev.kobj,
+ data->gyro_input_dev->name);
+iRet_gyro_sysfs_create_link:
+ sysfs_delete_link(&data->sen_dev->kobj,
+ &data->acc_input_dev->dev.kobj,
+ data->acc_input_dev->name);
+iRet_acc_sysfs_create_link:
+ pr_err("[SSP]: %s - could not create event symlink\n", __func__);
+
+ return FAIL;
+}
+
+void remove_event_symlink(struct ssp_data *data)
+{
+ sysfs_delete_link(&data->sen_dev->kobj,
+ &data->acc_input_dev->dev.kobj,
+ data->acc_input_dev->name);
+ sysfs_delete_link(&data->sen_dev->kobj,
+ &data->gyro_input_dev->dev.kobj,
+ data->gyro_input_dev->name);
+ sysfs_delete_link(&data->sen_dev->kobj,
+ &data->pressure_input_dev->dev.kobj,
+ data->pressure_input_dev->name);
+ sysfs_delete_link(&data->sen_dev->kobj,
+ &data->light_input_dev->dev.kobj,
+ data->light_input_dev->name);
+ sysfs_delete_link(&data->sen_dev->kobj,
+ &data->prox_input_dev->dev.kobj,
+ data->prox_input_dev->name);
+}
+
+int initialize_input_dev(struct ssp_data *data)
+{
+ int iRet = 0;
+ struct input_dev *acc_input_dev, *gyro_input_dev, *pressure_input_dev,
+ *light_input_dev, *prox_input_dev;
+
+ /* allocate input_device */
+ acc_input_dev = input_allocate_device();
+ if (acc_input_dev == NULL)
+ goto iRet_acc_input_free_device;
+
+ gyro_input_dev = input_allocate_device();
+ if (gyro_input_dev == NULL)
+ goto iRet_gyro_input_free_device;
+
+ pressure_input_dev = input_allocate_device();
+ if (pressure_input_dev == NULL)
+ goto iRet_pressure_input_free_device;
+
+ light_input_dev = input_allocate_device();
+ if (light_input_dev == NULL)
+ goto iRet_light_input_free_device;
+
+ prox_input_dev = input_allocate_device();
+ if (prox_input_dev == NULL)
+ goto iRet_proximity_input_free_device;
+
+ input_set_drvdata(acc_input_dev, data);
+ input_set_drvdata(gyro_input_dev, data);
+ input_set_drvdata(pressure_input_dev, data);
+ input_set_drvdata(light_input_dev, data);
+ input_set_drvdata(prox_input_dev, data);
+
+ acc_input_dev->name = "accelerometer_sensor";
+ gyro_input_dev->name = "gyro_sensor";
+ pressure_input_dev->name = "pressure_sensor";
+ light_input_dev->name = "light_sensor";
+ prox_input_dev->name = "proximity_sensor";
+
+ input_set_capability(acc_input_dev, EV_REL, REL_X);
+ input_set_capability(acc_input_dev, EV_REL, REL_Y);
+ input_set_capability(acc_input_dev, EV_REL, REL_Z);
+
+ input_set_capability(gyro_input_dev, EV_REL, REL_RX);
+ input_set_capability(gyro_input_dev, EV_REL, REL_RY);
+ input_set_capability(gyro_input_dev, EV_REL, REL_RZ);
+
+ input_set_capability(pressure_input_dev, EV_REL, REL_HWHEEL);
+ input_set_capability(pressure_input_dev, EV_REL, REL_DIAL);
+ input_set_capability(pressure_input_dev, EV_REL, REL_WHEEL);
+
+ input_set_capability(light_input_dev, EV_REL, REL_HWHEEL);
+ input_set_capability(light_input_dev, EV_REL, REL_DIAL);
+ input_set_capability(light_input_dev, EV_REL, REL_WHEEL);
+ input_set_capability(light_input_dev, EV_REL, REL_MISC);
+
+ input_set_capability(prox_input_dev, EV_ABS, ABS_DISTANCE);
+ input_set_abs_params(prox_input_dev, ABS_DISTANCE, 0, 1, 0, 0);
+
+ /* register input_device */
+ iRet = input_register_device(acc_input_dev);
+ if (iRet < 0)
+ goto iRet_acc_input_unreg_device;
+
+ iRet = input_register_device(gyro_input_dev);
+ if (iRet < 0) {
+ input_free_device(gyro_input_dev);
+ goto iRet_gyro_input_unreg_device;
+ }
+
+ iRet = input_register_device(pressure_input_dev);
+ if (iRet < 0) {
+ input_free_device(pressure_input_dev);
+ goto iRet_pressure_input_unreg_device;
+ }
+
+ iRet = input_register_device(light_input_dev);
+ if (iRet < 0) {
+ input_free_device(light_input_dev);
+ goto iRet_light_input_unreg_device;
+ }
+
+ iRet = input_register_device(prox_input_dev);
+ if (iRet < 0) {
+ input_free_device(prox_input_dev);
+ goto iRet_proximity_input_unreg_device;
+ }
+
+ data->acc_input_dev = acc_input_dev;
+ data->gyro_input_dev = gyro_input_dev;
+ data->pressure_input_dev = pressure_input_dev;
+ data->light_input_dev = light_input_dev;
+ data->prox_input_dev = prox_input_dev;
+
+ return SUCCESS;
+
+iRet_proximity_input_unreg_device:
+ input_unregister_device(light_input_dev);
+iRet_light_input_unreg_device:
+ input_unregister_device(pressure_input_dev);
+iRet_pressure_input_unreg_device:
+ input_unregister_device(gyro_input_dev);
+iRet_gyro_input_unreg_device:
+ input_unregister_device(acc_input_dev);
+ return -1;
+
+iRet_acc_input_unreg_device:
+ pr_err("[SSP]: %s - could not register input device\n", __func__);
+ input_free_device(prox_input_dev);
+iRet_proximity_input_free_device:
+ input_free_device(light_input_dev);
+iRet_light_input_free_device:
+ input_free_device(pressure_input_dev);
+iRet_pressure_input_free_device:
+ input_free_device(gyro_input_dev);
+iRet_gyro_input_free_device:
+ input_free_device(acc_input_dev);
+iRet_acc_input_free_device:
+ pr_err("[SSP]: %s - could not allocate input device\n", __func__);
+ return -1;
+}
+
+void remove_input_dev(struct ssp_data *data)
+{
+ input_unregister_device(data->acc_input_dev);
+ input_unregister_device(data->gyro_input_dev);
+ input_unregister_device(data->pressure_input_dev);
+ input_unregister_device(data->light_input_dev);
+ input_unregister_device(data->prox_input_dev);
+}
diff --git a/drivers/sensorhub/ssp_sensorhub.c b/drivers/sensorhub/ssp_sensorhub.c
new file mode 100644
index 0000000..8330beb
--- /dev/null
+++ b/drivers/sensorhub/ssp_sensorhub.c
@@ -0,0 +1,444 @@
+/*
+ * 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"
+
+/* sensorhub ioctl command */
+#define SENSORHUB_IOCTL_MAGIC 'S'
+#define IOCTL_READ_CONTEXT_DATA _IOR(SENSORHUB_IOCTL_MAGIC, 3, char *)
+
+
+static ssize_t ssp_sensorhub_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct ssp_data *data = container_of(file->private_data,
+ struct ssp_data, sensorhub_device);
+ int ret = 0;
+ int i;
+ u8 instruction = buf[0];
+
+ if (count <= 0) {
+ pr_err("%s: library command length err(%d)", __func__, count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++)
+ pr_info("%s[%d] = %d", __func__, i, buf[i]);
+
+ if (buf[0] == MSG2SSP_INST_LIBRARY_REMOVE)
+ instruction = REMOVE_LIBRARY;
+ else if (buf[0] == MSG2SSP_INST_LIBRARY_ADD)
+ instruction = ADD_LIBRARY;
+
+ ret = send_instruction(data, instruction,
+ (u8)buf[1], (u8 *)(buf+2), count-2);
+ if (ret < 0)
+ pr_err("%s: send library command err(%d)", __func__, ret);
+
+ return ret;
+}
+
+static long ssp_sensorhub_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct ssp_data *data = container_of(file->private_data,
+ struct ssp_data, sensorhub_device);
+ struct sensorhub_event *event;
+ int ret = 0;
+ int i;
+
+ switch (cmd) {
+ case IOCTL_READ_CONTEXT_DATA:
+ /* for receive_msg */
+ if (!data->large_library_length &&
+ data->large_library_data == NULL) {
+ if (list_empty(&data->events_head.list)) {
+ pr_err("%s: list empty!", __func__);
+ complete(&data->transfer_done);
+ goto exit;
+ }
+
+ event = list_first_entry(&data->events_head.list,
+ struct sensorhub_event, list);
+ if (IS_ERR(event)) {
+ pr_err("%s: no sensor event entry", __func__);
+ complete(&data->transfer_done);
+ goto exit;
+ }
+
+ ret = copy_to_user(argp,
+ event->library_data, event->library_length);
+ if (ret < 0) {
+ pr_err("%s: send library datar err(%d)",
+ __func__, ret);
+ complete(&data->transfer_done);
+ goto exit;
+ }
+
+ for (i = 0; i < event->library_length; i++) {
+ pr_info("%s[%d] = %d",
+ __func__, i, event->library_data[i]);
+ }
+
+ list_del(&event->list);
+ complete(&data->transfer_done);
+
+ /* for receive_large_msg */
+ } else {
+ pr_info("%s: receive_large_msg ioctl", __func__);
+ ret = copy_to_user(argp, data->large_library_data,
+ data->large_library_length);
+ if (ret < 0) {
+ pr_err("%s: send large library data err(%d)",
+ __func__, ret);
+ goto exit;
+ }
+
+ kfree(data->large_library_data);
+ data->large_library_length = 0;
+ }
+ break;
+
+ default:
+ pr_err("%s: icotl cmd err(%d)", __func__, cmd);
+ ret = -EINVAL;
+ }
+
+exit:
+ return ret;
+}
+
+static const struct file_operations ssp_sensorhub_fops = {
+ .owner = THIS_MODULE,
+ .open = nonseekable_open,
+ .write = ssp_sensorhub_write,
+ .unlocked_ioctl = ssp_sensorhub_ioctl,
+};
+
+void ssp_report_sensorhub_notice(struct ssp_data *data, char notice)
+{
+ input_report_rel(data->sensorhub_input_dev, REL_RY, notice);
+ input_sync(data->sensorhub_input_dev);
+
+ if (notice == MSG2SSP_AP_STATUS_WAKEUP)
+ pr_info("%s: wake up", __func__);
+ else if (notice == MSG2SSP_AP_STATUS_SLEEP)
+ pr_info("%s: sleep", __func__);
+ else if (notice == MSG2SSP_AP_STATUS_RESET)
+ pr_info("%s: reset", __func__);
+}
+
+static void ssp_report_sensorhub_length(struct ssp_data *data,
+ int library_length)
+{
+ input_report_rel(data->sensorhub_input_dev, REL_RX, library_length);
+ input_sync(data->sensorhub_input_dev);
+ pr_info("%s = %d", __func__, library_length);
+}
+
+static int ssp_queue_sensorhub_events(struct ssp_data *data,
+ char *dataframe, int start, int end)
+{
+ int length = end - start;
+ int i = 0;
+
+ if (length <= 0) {
+ pr_err("%s: library length err(%d)", __func__, length);
+ return -EINVAL;
+ }
+
+ /* allocate memory for new event */
+ if (data->events[data->event_number].library_data != NULL)
+ kfree(data->events[data->event_number].library_data);
+
+ data->events[data->event_number].library_data
+ = kzalloc(length * sizeof(char), GFP_KERNEL);
+ if (data->events[data->event_number].library_data == NULL) {
+ pr_err("%s: allocate memory for library data err", __func__);
+ return -ENOMEM;
+ }
+
+ /* copy sensorhub event into queue */
+ while (start < end) {
+ data->events[data->event_number].library_data[i++]
+ = dataframe[start++];
+ pr_info("%s[%d] = %d", __func__, i-1,
+ data->events[data->event_number].library_data[i-1]);
+ }
+ data->events[data->event_number].library_length = length;
+
+ /* add new sensorhug event at the end of queue */
+ list_add_tail(&data->events[data->event_number].list,
+ &data->events_head.list);
+
+ /* do not exceed max queue number */
+ if (data->event_number++ >= LIBRARY_MAX_NUM - 1)
+ data->event_number = 0;
+
+ return length;
+}
+
+static int ssp_receive_large_msg(struct ssp_data *data, u8 sub_cmd)
+{
+ char send_data[2] = { 0, };
+ char receive_data[2] = { 0, };
+ char *large_msg_data; /* Nth large msg data */
+ int length = 0; /* length of Nth large msg */
+ int data_locater = 0; /* large_library_data current position */
+ int total_msg_number; /* total number of large msg */
+ int msg_number; /* current number of large msg */
+ int ret = 0;
+
+ waiting_wakeup_mcu(data);
+
+ /* receive the first msg length */
+ send_data[0] = MSG2SSP_STT;
+ send_data[1] = sub_cmd;
+
+ /* receive_data(msg length) is two byte because msg is large */
+ ret = ssp_i2c_read(data, send_data, 2, receive_data, 2);
+ if (ret < 0) {
+ pr_err("%s: MSG2SSP_STT i2c err(%d)", __func__, ret);
+ return ret;
+ }
+
+ /* get the first msg length */
+ length = ((unsigned int)receive_data[0] << 8)
+ + (unsigned int)receive_data[1];
+ if (length < 3) {
+ /* do not print err message with power-up */
+ if (sub_cmd != SUBCMD_POWEREUP)
+ pr_err("%s: 1st large msg data not ready(length=%d)",
+ __func__, length);
+ return -EINVAL;
+ }
+
+ /* receive the first msg data */
+ send_data[0] = MSG2SSP_SRM;
+ large_msg_data = kzalloc((length * sizeof(char)), GFP_KERNEL);
+ ret = ssp_i2c_read(data, send_data, 1,
+ large_msg_data, length);
+ if (ret < 0) {
+ pr_err("%s: receive 1st large msg err(%d)", __func__, ret);
+ kfree(large_msg_data);
+ return ret;
+ }
+
+ /* empty the previous large library data */
+ if (data->large_library_length != 0)
+ kfree(data->large_library_data);
+
+ /* large_msg_data[0] of the first msg: total number of large msg
+ * large_msg_data[1-2] of the first msg: total msg length
+ * large_msg_data[3-N] of the first msg: the first msg data itself */
+ total_msg_number = large_msg_data[0];
+ data->large_library_length = (int)((unsigned int)large_msg_data[1] << 8)
+ + (unsigned int)large_msg_data[2];
+ data->large_library_data
+ = kzalloc((data->large_library_length * sizeof(char)),
+ GFP_KERNEL);
+
+ /* copy the fist msg data into large_library_data */
+ memcpy(data->large_library_data, &large_msg_data[3],
+ (length - 3) * sizeof(char));
+ kfree(large_msg_data);
+
+ data_locater = length - 3;
+
+ /* 2nd, 3rd,...Nth msg */
+ for (msg_number = 0; msg_number < total_msg_number; msg_number++) {
+ /* receive Nth msg length */
+ send_data[0] = MSG2SSP_STT;
+ send_data[1] = 0x81 + msg_number;
+
+ /* receive_data(msg length) is two byte because msg is large */
+ ret = ssp_i2c_read(data, send_data, 2, receive_data, 2);
+ if (ret < 0) {
+ pr_err("%s: MSG2SSP_STT i2c err(%d)",
+ __func__, ret);
+ return ret;
+ }
+
+ /* get the Nth msg length */
+ length = ((unsigned int)receive_data[0] << 8)
+ + (unsigned int)receive_data[1];
+ if (length <= 0) {
+ pr_err("%s: %dth large msg data not ready(length=%d)",
+ __func__, msg_number + 2, length);
+ return -EINVAL;
+ }
+
+ large_msg_data = kzalloc((length * sizeof(char)),
+ GFP_KERNEL);
+
+ /* receive Nth msg data */
+ send_data[0] = MSG2SSP_SRM;
+ ret = ssp_i2c_read(data, send_data, 1,
+ large_msg_data, length);
+ if (ret < 0) {
+ pr_err("%s: recieve %dth large msg err(%d)",
+ __func__, msg_number + 2, ret);
+ kfree(large_msg_data);
+ return ret;
+ }
+
+ /* copy(append) Nth msg data into large_library_data */
+ memcpy(&data->large_library_data[data_locater],
+ large_msg_data, length * sizeof(char));
+ data_locater += length;
+ kfree(large_msg_data);
+ }
+
+ return data->large_library_length;
+}
+
+static int ssp_senosrhub_thread_func(void *arg)
+{
+ struct ssp_data *data = (struct ssp_data *)arg;
+ struct sensorhub_event *event;
+ int ret = 0;
+
+ while (!kthread_should_stop() || !list_empty(&data->events_head.list)) {
+ /* run if only event queue is not empty */
+ wait_event_interruptible(data->sensorhub_waitqueue,
+ kthread_should_stop() ||
+ !list_empty(&data->events_head.list));
+
+ /* first in first out */
+ event = list_first_entry(&data->events_head.list,
+ struct sensorhub_event, list);
+ if (IS_ERR(event)) {
+ pr_err("%s: no sensor event entry", __func__);
+ continue;
+ }
+
+ /* report sensorhub event to user */
+ ssp_report_sensorhub_length(data, event->library_length);
+ wake_lock_timeout(&data->sensorhub_wake_lock, 5*HZ);
+
+ /* wait until user gets data */
+ ret = wait_for_completion_timeout(&data->transfer_done, 3*HZ);
+ if (ret == 0) {
+ pr_err("%s: wait timed out", __func__);
+ } else if (ret < 0) {
+ pr_err("%s: wait_for_completion_timeout err(%d)",
+ __func__, ret);
+ }
+ }
+
+ return ret;
+}
+
+int ssp_handle_sensorhub_data(struct ssp_data *data, char *dataframe,
+ int start, int end)
+{
+ /* add new sensorhub event into queue */
+ int ret = ssp_queue_sensorhub_events(data, dataframe, start, end);
+ if (ret < 0)
+ pr_err("%s: ssp_queue_sensorhub_events err(%d)", __func__, ret);
+ wake_up(&data->sensorhub_waitqueue);
+
+ return ret;
+}
+
+int ssp_handle_sensorhub_large_data(struct ssp_data *data, u8 sub_cmd)
+{
+ /* receive large size of library data */
+ int ret = ssp_receive_large_msg(data, sub_cmd);
+ if (ret >= 0) {
+ ssp_report_sensorhub_length(data, data->large_library_length);
+ wake_lock_timeout(&data->sensorhub_wake_lock, 3*HZ);
+ } else {
+ pr_err("%s: ssp_receive_large_msg err(%d)", __func__, ret);
+ }
+
+ return ret;
+}
+
+int ssp_initialize_sensorhub(struct ssp_data *data)
+{
+ int ret;
+
+ /* allocate sensorhub input devices */
+ data->sensorhub_input_dev = input_allocate_device();
+ if (!data->sensorhub_input_dev) {
+ pr_err("%s: allocate sensorhub input devices err", __func__);
+ ret = -ENOMEM;
+ goto err_input_allocate_device_sensorhub;
+ }
+
+ wake_lock_init(&data->sensorhub_wake_lock, WAKE_LOCK_SUSPEND,
+ "sensorhub_wake_lock");
+ INIT_LIST_HEAD(&data->events_head.list);
+ init_waitqueue_head(&data->sensorhub_waitqueue);
+ init_completion(&data->transfer_done);
+
+ ret = input_register_device(data->sensorhub_input_dev);
+ if (ret < 0) {
+ pr_err("%s: could not register sensorhub input device(%d)",
+ __func__, ret);
+ input_free_device(data->sensorhub_input_dev);
+ goto err_input_register_device_sensorhub;
+ }
+
+ data->sensorhub_input_dev->name = "ssp_context";
+ input_set_drvdata(data->sensorhub_input_dev, data);
+ input_set_capability(data->sensorhub_input_dev, EV_REL, REL_RX);
+ input_set_capability(data->sensorhub_input_dev, EV_REL, REL_RY);
+
+ /* create sensorhub device node */
+ data->sensorhub_device.minor = MISC_DYNAMIC_MINOR;
+ data->sensorhub_device.name = "ssp_sensorhub";
+ data->sensorhub_device.fops = &ssp_sensorhub_fops;
+
+ ret = misc_register(&data->sensorhub_device);
+ if (ret < 0) {
+ pr_err("%s: misc_register() failed", __func__);
+ goto err_misc_register;
+ }
+
+ data->sensorhub_task = kthread_run(ssp_senosrhub_thread_func,
+ (void *)data, "ssp_sensorhub_task");
+ if (IS_ERR(data->sensorhub_task)) {
+ ret = PTR_ERR(data->sensorhub_task);
+ goto err_kthread_create;
+ }
+
+ return 0;
+
+err_kthread_create:
+ misc_deregister(&data->sensorhub_device);
+err_misc_register:
+ input_unregister_device(data->sensorhub_input_dev);
+err_input_register_device_sensorhub:
+ complete_all(&data->transfer_done);
+ wake_lock_destroy(&data->sensorhub_wake_lock);
+err_input_allocate_device_sensorhub:
+ return ret;
+}
+
+void ssp_remove_sensorhub(struct ssp_data *data)
+{
+ complete_all(&data->transfer_done);
+ kthread_stop(data->sensorhub_task);
+ misc_deregister(&data->sensorhub_device);
+ input_unregister_device(data->sensorhub_input_dev);
+ wake_lock_destroy(&data->sensorhub_wake_lock);
+}
+
+MODULE_DESCRIPTION("Samsung Sensor Platform(SSP) sensorhub driver");
+MODULE_AUTHOR("Samsung Electronics");
+MODULE_LICENSE("GPL");
diff --git a/drivers/sensorhub/ssp_sysfs.c b/drivers/sensorhub/ssp_sysfs.c
new file mode 100644
index 0000000..fb1ad06
--- /dev/null
+++ b/drivers/sensorhub/ssp_sysfs.c
@@ -0,0 +1,478 @@
+/*
+ * 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 */
+/*************************************************************************/
+
+unsigned int get_msdelay(int64_t dDelayRate)
+{
+ if (dDelayRate <= SENSOR_NS_DELAY_FASTEST)
+ return SENSOR_MS_DELAY_FASTEST;
+ else if (dDelayRate <= SENSOR_NS_DELAY_GAME)
+ return SENSOR_MS_DELAY_GAME;
+ else if (dDelayRate <= SENSOR_NS_DELAY_UI)
+ return SENSOR_MS_DELAY_UI;
+ else
+ return SENSOR_MS_DELAY_NORMAL;
+}
+
+unsigned int get_delay_cmd(u8 uDelayRate)
+{
+ if (uDelayRate <= SENSOR_MS_DELAY_FASTEST)
+ return SENSOR_CMD_DELAY_FASTEST;
+ else if (uDelayRate <= SENSOR_MS_DELAY_GAME)
+ return SENSOR_CMD_DELAY_GAME;
+ else if (uDelayRate <= SENSOR_MS_DELAY_UI)
+ return SENSOR_CMD_DELAY_UI;
+ else
+ return SENSOR_CMD_DELAY_NORMAL;
+}
+
+static void change_sensor_delay(struct ssp_data *data,
+ int iSensorType, int64_t dNewDelay)
+{
+ u8 uBuf[2];
+ int64_t dTempDelay = data->adDelayBuf[iSensorType];
+
+ data->adDelayBuf[iSensorType] = dNewDelay;
+
+ if (iSensorType == ORIENTATION_SENSOR)
+ iSensorType = ACCELEROMETER_SENSOR;
+
+ switch (data->aiCheckStatus[iSensorType]) {
+ case ADD_SENSOR_STATE:
+ ssp_dbg("[SSP]: %s - add %u, New = %lldns\n",
+ __func__, 1 << iSensorType, dNewDelay);
+
+ uBuf[1] = (u8)get_msdelay(dNewDelay);
+ uBuf[0] = (u8)get_delay_cmd(uBuf[1]);
+ send_instruction(data, ADD_SENSOR, iSensorType, uBuf, 2);
+
+ data->aiCheckStatus[iSensorType] = RUNNING_SENSOR_STATE;
+
+ if (iSensorType == PROXIMITY_SENSOR) {
+ input_report_abs(data->prox_input_dev, ABS_DISTANCE, 1);
+ input_sync(data->prox_input_dev);
+ }
+ break;
+ case RUNNING_SENSOR_STATE:
+ if (dTempDelay == data->adDelayBuf[iSensorType])
+ break;
+
+ ssp_dbg("[SSP]: %s - Change %u, New = %lldns\n",
+ __func__, 1 << iSensorType, dNewDelay);
+
+ uBuf[1] = (u8)get_msdelay(dNewDelay);
+ uBuf[0] = (u8)get_delay_cmd(uBuf[1]);
+ send_instruction(data, CHANGE_DELAY, iSensorType, uBuf, 2);
+ break;
+ default:
+ data->aiCheckStatus[iSensorType] = ADD_SENSOR_STATE;
+ }
+}
+
+/*************************************************************************/
+/* SSP data enable function */
+/*************************************************************************/
+
+static int ssp_add_sensor(struct ssp_data *data, unsigned int uChangedSensor)
+{
+ if ((data->aiCheckStatus[uChangedSensor] != INITIALIZATION_STATE)
+ && (!atomic_read(&data->aSensorEnable))) {
+ if (data->bCheckSuspend == false)
+ data->bDebugEnabled = true;
+ }
+
+ return 0;
+}
+
+static int ssp_remove_sensor(struct ssp_data *data,
+ unsigned int uChangedSensor, unsigned int uNewEnable)
+{
+ u8 uBuf[2];
+ int64_t dSensorDelay = data->adDelayBuf[uChangedSensor];
+
+ ssp_dbg("[SSP]: %s - remove sensor = %d, current state = %d\n",
+ __func__, (1 << uChangedSensor), uNewEnable);
+
+ data->adDelayBuf[uChangedSensor] = DEFUALT_POLLING_DELAY;
+
+ if (data->aiCheckStatus[uChangedSensor] == INITIALIZATION_STATE) {
+ data->aiCheckStatus[uChangedSensor] = NO_SENSOR_STATE;
+ if (uChangedSensor == ACCELEROMETER_SENSOR)
+ accel_open_calibration(data);
+ else if (uChangedSensor == GYROSCOPE_SENSOR)
+ gyro_open_calibration(data);
+ else if (uChangedSensor == PRESSURE_SENSOR)
+ pressure_open_calibration(data);
+ else if (uChangedSensor == PROXIMITY_SENSOR) {
+ proximity_open_lcd_ldi(data);
+ proximity_open_calibration(data);
+ }
+ return 0;
+ } else 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 (!uNewEnable) {
+ if (data->bCheckSuspend == false)
+ data->bDebugEnabled = false;
+ }
+
+ uBuf[1] = (u8)get_msdelay(dSensorDelay);
+ uBuf[0] = (u8)get_delay_cmd(uBuf[1]);
+
+ send_instruction(data, REMOVE_SENSOR, uChangedSensor, uBuf, 2);
+ data->aiCheckStatus[uChangedSensor] = NO_SENSOR_STATE;
+ return 0;
+}
+
+/*************************************************************************/
+/* ssp Sysfs */
+/*************************************************************************/
+
+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("[SSP]: %s - cur_enable = %d\n", __func__,
+ atomic_read(&data->aSensorEnable));
+
+ return sprintf(buf, "%10u", 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 (strict_strtoll(buf, 10, &dTemp) < 0)
+ return -1;
+
+ uNewEnable = (unsigned int)dTemp;
+ ssp_dbg("[SSP]: %s - new_enable = %u, old_enable = %u\n", __func__,
+ uNewEnable, atomic_read(&data->aSensorEnable));
+
+ if (uNewEnable == atomic_read(&data->aSensorEnable))
+ return 0;
+
+ for (uChangedSensor = 0; uChangedSensor < SENSOR_MAX; uChangedSensor++)
+ if ((atomic_read(&data->aSensorEnable) & (1 << uChangedSensor))
+ != (uNewEnable & (1 << uChangedSensor))) {
+
+ if (uNewEnable & (1 << uChangedSensor))
+ ssp_add_sensor(data, uChangedSensor);
+ else
+ ssp_remove_sensor(data, uChangedSensor,
+ uNewEnable);
+ break;
+ }
+
+ atomic_set(&data->aSensorEnable, uNewEnable);
+
+ 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 (strict_strtoll(buf, 10, &dNewDelay) < 0)
+ return -1;
+
+ 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_ori_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[ORIENTATION_SENSOR]);
+}
+
+static ssize_t set_ori_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 (strict_strtoll(buf, 10, &dNewDelay) < 0)
+ return -1;
+
+ if (data->aiCheckStatus[ACCELEROMETER_SENSOR] == NO_SENSOR_STATE) {
+ data->aiCheckStatus[ACCELEROMETER_SENSOR] = ADD_SENSOR_STATE;
+ change_sensor_delay(data, ORIENTATION_SENSOR, dNewDelay);
+ } else if (data->aiCheckStatus[ACCELEROMETER_SENSOR] ==
+ RUNNING_SENSOR_STATE) {
+ if (dNewDelay < data->adDelayBuf[ACCELEROMETER_SENSOR])
+ change_sensor_delay(data,
+ ORIENTATION_SENSOR, dNewDelay);
+ else
+ data->adDelayBuf[ORIENTATION_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 (strict_strtoll(buf, 10, &dNewDelay) < 0)
+ return -1;
+
+ change_sensor_delay(data, GYROSCOPE_SENSOR, dNewDelay);
+ return size;
+}
+
+static ssize_t show_mag_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[GEOMAGNETIC_SENSOR]);
+}
+
+static ssize_t set_mag_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 (strict_strtoll(buf, 10, &dNewDelay) < 0)
+ return -1;
+
+ change_sensor_delay(data, GEOMAGNETIC_SENSOR, dNewDelay);
+
+ return size;
+}
+
+static ssize_t show_pressure_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[PRESSURE_SENSOR]);
+}
+
+static ssize_t set_pressure_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 (strict_strtoll(buf, 10, &dNewDelay) < 0)
+ return -1;
+
+ change_sensor_delay(data, PRESSURE_SENSOR, dNewDelay);
+ return size;
+}
+
+static ssize_t show_light_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[LIGHT_SENSOR]);
+}
+
+static ssize_t set_light_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 (strict_strtoll(buf, 10, &dNewDelay) < 0)
+ return -1;
+
+ change_sensor_delay(data, LIGHT_SENSOR, dNewDelay);
+ return size;
+}
+
+static ssize_t show_prox_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[PROXIMITY_SENSOR]);
+}
+
+static ssize_t set_prox_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 (strict_strtoll(buf, 10, &dNewDelay) < 0)
+ return -1;
+
+ change_sensor_delay(data, PROXIMITY_SENSOR, dNewDelay);
+ return size;
+}
+
+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_show, NULL);
+static DEVICE_ATTR(mcu_update2, S_IRUGO, mcu_update2_show, NULL);
+static DEVICE_ATTR(mcu_reset, S_IRUGO, mcu_reset_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(acc_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);
+static DEVICE_ATTR(mag_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_mag_delay, set_mag_delay);
+static DEVICE_ATTR(ori_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_ori_delay, set_ori_delay);
+static DEVICE_ATTR(pressure_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_pressure_delay, set_pressure_delay);
+static DEVICE_ATTR(light_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_light_delay, set_light_delay);
+static DEVICE_ATTR(prox_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_prox_delay, set_prox_delay);
+
+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_update,
+ &dev_attr_mcu_update2,
+ &dev_attr_mcu_sleep_test,
+ &dev_attr_mag_poll_delay,
+ &dev_attr_ori_poll_delay,
+ NULL,
+};
+
+static void initialize_mcu_factorytest(struct ssp_data *data)
+{
+ struct device *mcu_device = NULL;
+
+ sensors_register(mcu_device, data, mcu_attrs, "ssp_sensor");
+}
+
+int initialize_sysfs(struct ssp_data *data)
+{
+ if (device_create_file(&data->acc_input_dev->dev,
+ &dev_attr_acc_poll_delay))
+ goto err_acc_input_dev;
+
+ if (device_create_file(&data->gyro_input_dev->dev,
+ &dev_attr_gyro_poll_delay))
+ goto err_gyro_input_dev;
+
+ if (device_create_file(&data->pressure_input_dev->dev,
+ &dev_attr_pressure_poll_delay))
+ goto err_pressure_input_dev;
+
+ if (device_create_file(&data->light_input_dev->dev,
+ &dev_attr_light_poll_delay))
+ goto err_light_input_dev;
+
+ if (device_create_file(&data->prox_input_dev->dev,
+ &dev_attr_prox_poll_delay))
+ goto err_prox_input_dev;
+
+ initialize_accel_factorytest(data);
+ initialize_gyro_factorytest(data);
+ initialize_prox_factorytest(data);
+ initialize_light_factorytest(data);
+ initialize_pressure_factorytest(data);
+ initialize_magnetic_factorytest(data);
+ initialize_mcu_factorytest(data);
+
+ return SUCCESS;
+
+err_prox_input_dev:
+ device_remove_file(&data->light_input_dev->dev,
+ &dev_attr_light_poll_delay);
+err_light_input_dev:
+ device_remove_file(&data->pressure_input_dev->dev,
+ &dev_attr_pressure_poll_delay);
+err_pressure_input_dev:
+ device_remove_file(&data->gyro_input_dev->dev,
+ &dev_attr_gyro_poll_delay);
+err_gyro_input_dev:
+ device_remove_file(&data->acc_input_dev->dev,
+ &dev_attr_acc_poll_delay);
+err_acc_input_dev:
+ return ERROR;
+}
+
+void remove_sysfs(struct ssp_data *data)
+{
+ device_remove_file(&data->acc_input_dev->dev,
+ &dev_attr_acc_poll_delay);
+ device_remove_file(&data->gyro_input_dev->dev,
+ &dev_attr_gyro_poll_delay);
+ device_remove_file(&data->pressure_input_dev->dev,
+ &dev_attr_pressure_poll_delay);
+ device_remove_file(&data->light_input_dev->dev,
+ &dev_attr_light_poll_delay);
+ device_remove_file(&data->prox_input_dev->dev,
+ &dev_attr_prox_poll_delay);
+}