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