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