exynos-linux-stable/drivers/sensorhub/brcm/ssp_dev.c

1129 lines
30 KiB
C

/*
* 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"
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
#endif
#include <linux/sec_debug.h>
#include <linux/sec_batt.h>
//#define DEBUG
#ifdef CONFIG_HAS_EARLYSUSPEND
static void ssp_early_suspend(struct early_suspend *handler);
static void ssp_late_resume(struct early_suspend *handler);
#endif
#define NORMAL_SENSOR_STATE_K 0x3FEFF
#if defined(CONFIG_MUIC_NOTIFIER)
#include <linux/muic/muic.h>
#include <linux/muic/muic_notifier.h>
#endif
#if defined(CONFIG_SSP_MOTOR_CALLBACK)
#include <linux/ssp_motorcallback.h>
#endif
unsigned int bootmode;
EXPORT_SYMBOL(bootmode);
struct mutex shutdown_lock;
bool ssp_debug_time_flag = false;
static int __init bootmode_setup(char *str)
{
get_option(&str, &bootmode);
pr_info("[SSP] %s = %d\n", __func__, bootmode);
return 1;
}
__setup("bootmode=", bootmode_setup);
void ssp_enable(struct ssp_data *data, bool enable)
{
if (enable == !data->bSspShutdown)
return;
pr_info("[SSP] %s, new enable = %d, old enable = %d\n",
__func__, enable, !data->bSspShutdown);
#ifdef CONFIG_SENSORS_SSP_HIFI_BATCHING
if (enable) {
enable_irq(data->irq_shub_int);
} else {
disable_irq(data->irq_shub_int);
}
#endif
if (enable && data->bSspShutdown)
data->bSspShutdown = false;
else if (!enable && !data->bSspShutdown)
data->bSspShutdown = true;
}
u64 get_current_timestamp(void)
{
u64 timestamp;
struct timespec ts;
ts = ktime_to_timespec(ktime_get_boottime());
timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
return timestamp;
}
/*************************************************************************/
/* initialize sensor hub */
/*************************************************************************/
static void initialize_variable(struct ssp_data *data)
{
int iSensorIndex;
#ifdef CONFIG_SENSORS_SSP_HIFI_BATCHING
data->cameraGyroSyncMode = false;
data->ts_stacked_cnt = 0;
data->ts_stacked_offset = 0;
data->ts_irq_last = 0ULL;
#endif
for (iSensorIndex = 0; iSensorIndex < SENSOR_MAX; iSensorIndex++) {
data->adDelayBuf[iSensorIndex] = DEFAULT_POLLING_DELAY;
data->batchLatencyBuf[iSensorIndex] = 0;
data->batchOptBuf[iSensorIndex] = 0;
data->aiCheckStatus[iSensorIndex] = INITIALIZATION_STATE;
data->lastTimestamp[iSensorIndex] = 0;
data->LastSensorTimeforReset[iSensorIndex] = 0;
data->IsBypassMode[iSensorIndex] = 0;
data->reportedData[iSensorIndex] = false;
/* variables for conditional timestamp */
data->first_sensor_data[iSensorIndex] = true;
}
atomic64_set(&data->aSensorEnable, 0);
data->iLibraryLength = 0;
data->uSensorState = NORMAL_SENSOR_STATE_K;
data->uFactoryProxAvg[0] = 0;
data->uMagCntlRegData = 1;
data->uResetCnt = 0;
data->uTimeOutCnt = 0;
data->uComFailCnt = 0;
data->uIrqCnt = 0;
data->bFirstRef = true;
data->bSspShutdown = true;
data->bProximityRawEnabled = false;
data->bGeomagneticRawEnabled = false;
data->bBarcodeEnabled = false;
data->bAccelAlert = false;
data->bTimeSyncing = true;
data->bHandlingIrq = false;
data->resetting = 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->magoffset.x = 0;
data->magoffset.y = 0;
data->magoffset.z = 0;
data->iPressureCal = 0;
#ifdef CONFIG_SENSORS_SSP_PROX_FACTORYCAL
data->uProxCanc = 0;
data->uProxHiThresh = data->uProxHiThresh_default;
data->uProxLoThresh = data->uProxLoThresh_default;
#endif
data->uGyroDps = GYROSCOPE_DPS1000;
data->uIr_Current = DEFAULT_IR_CURRENT;
data->mcu_device = NULL;
data->acc_device = NULL;
data->gyro_device = NULL;
data->mag_device = NULL;
data->prs_device = NULL;
data->prox_device = NULL;
data->light_device = NULL;
data->ges_device = NULL;
data->thermistor_device = NULL;
#ifdef CONFIG_SENSORS_SSP_LIGHT_COLORID
data->hiddenhole_device = NULL;
#endif
data->voice_device = NULL;
data->bMcuDumpMode = ssp_check_sec_dump_mode();
INIT_LIST_HEAD(&data->pending_list);
data->bbd_on_packet_wq =
create_singlethread_workqueue("ssp_bbd_on_packet_wq");
INIT_WORK(&data->work_bbd_on_packet, bbd_on_packet_work_func);
data->bbd_mcu_ready_wq =
create_singlethread_workqueue("ssp_bbd_mcu_ready_wq");
INIT_WORK(&data->work_bbd_mcu_ready, bbd_mcu_ready_work_func);
#ifdef SSP_BBD_USE_SEND_WORK
data->bbd_send_packet_wq =
create_singlethread_workqueue("ssp_bbd_send_packet_wq");
INIT_WORK(&data->work_bbd_send_packet, bbd_send_packet_work_func);
#endif /* SSP_BBD_USE_SEND_WORK */
data->step_count_total = 0;
data->sealevelpressure = 0;
data->gyro_lib_state = GYRO_CALIBRATION_STATE_NOT_YET;
#ifdef CONFIG_SENSORS_SSP_HIFI_BATCHING
/* HIFI batching wakeup */
data->resumeTimestamp = 0;
data->bIsResumed = false;
data->bIsReset = false;
#endif
initialize_function_pointer(data);
data->uNoRespSensorCnt = 0;
data->errorCount = 0;
data->mcuCrashedCnt = 0;
data->pktErrCnt = 0;
data->mcuAbnormal = false;
data->IsMcuCrashed = false;
data->intendedMcuReset = false;
data->timestamp_factor = 10; // initialize for 0.1%
ssp_debug_time_flag = false;
data->dhrAccelScaleRange = 0;
data->skipSensorData = 0;
data->IsGyroselftest = false;
data->IsVDIS_Enabled = false;
data->IsAPsuspend = false;
data->IsNoRespCnt = 0;
data->hall_ic_status = 0;
}
int initialize_mcu(struct ssp_data *data)
{
int iRet = 0;
clean_pending_list(data);
iRet = get_chipid(data);
pr_info("[SSP] MCU device ID = %d, reading ID = %d\n", DEVICE_ID, iRet);
if (iRet != DEVICE_ID) {
if (iRet < 0) {
pr_err("[SSP]: %s - MCU is not working : 0x%x\n",
__func__, iRet);
} else {
pr_err("[SSP]: %s - MCU identification failed\n",
__func__);
iRet = -ENODEV;
}
goto out;
}
iRet = set_sensor_position(data);
if (iRet < 0) {
pr_err("[SSP]: %s - set_sensor_position failed\n", __func__);
goto out;
}
#ifdef CONFIG_SENSORS_MULTIPLE_GLASS_TYPE
iRet = set_glass_type(data);
if (iRet < 0) {
pr_err("[SSP]: %s - set_glass_type failed\n", __func__);
goto out;
}
#endif
/* Hall IC threshold */
if (data->hall_threshold[0]) {
iRet = set_hall_threshold(data);
if (iRet < 0) {
pr_err("[SSP]: %s - set_hall_threshold failed\n",
__func__);
goto out;
}
}
data->uSensorState = get_sensor_scanning_info(data);
if (data->uSensorState == 0) {
pr_err("[SSP]: %s - get_sensor_scanning_info failed\n",
__func__);
iRet = ERROR;
goto out;
}
if (initialize_magnetic_sensor(data) < 0)
pr_err("[SSP]: %s - initialize magnetic sensor failed\n",
__func__);
if (initialize_thermistor_table(data) < 0)
pr_err("[SSP]: %s - initialize thermistor table failed\n",
__func__);
data->uCurFirmRev = get_firmware_rev(data);
pr_info("[SSP] MCU Firm Rev : New = %8u\n",
data->uCurFirmRev);
data->dhrAccelScaleRange = get_accel_range(data);
send_hall_ic_status(data->hall_ic_status);
/* hoi: il dan mak a */
#ifndef CONFIG_SENSORS_SSP_BBD
iRet = ssp_send_cmd(data, MSG2SSP_AP_MCU_DUMP_CHECK, 0);
#endif
out:
return iRet;
}
static bbd_callbacks ssp_bbd_callbacks = {
.on_packet = callback_bbd_on_packet,
.on_packet_alarm = callback_bbd_on_packet_alarm,
.on_control = callback_bbd_on_control,
.on_mcu_ready = callback_bbd_on_mcu_ready,
.on_mcu_reset = callback_bbd_on_mcu_reset
};
#ifdef CONFIG_SENSORS_SSP_HIFI_BATCHING
/* HIFI Sensor */
void ssp_reset_batching_resources(struct ssp_data *data)
{
u64 ts = get_current_timestamp();
pr_err("[SSP_RST] reset triggered %lld\n", ts);
data->ts_stacked_cnt = 0;
data->ts_stacked_offset = 0;
data->ts_irq_last = 0ULL;
data->resumeTimestamp = 0;
data->bIsResumed = false;
memset(data->ts_index_buffer, 0, sizeof(u64)*SENSOR_MAX);
memset(data->ts_prev_index, 0, sizeof(unsigned int)*SENSOR_MAX);
data->ts_last_enable_cmd_time = 0;
memset(data->ts_avg_buffer, 0,
sizeof(u64) * SIZE_MOVING_AVG_BUFFER * SENSOR_MAX);
memset(data->ts_avg_buffer_cnt, 0, sizeof(u8)*SENSOR_MAX);
memset(data->ts_avg_buffer_idx, 0, sizeof(u8)*SENSOR_MAX);
memset(data->ts_avg_buffer_sum, 0, sizeof(u64)*SENSOR_MAX);
data->bIsReset = true;
ts = get_current_timestamp();
pr_err("[SSP_RST] reset finished %lld\n", ts);
}
irqreturn_t ssp_shub_int_handler(int irq, void *device)
{
struct ssp_data *data = (struct ssp_data *) device;
u64 timestamp = get_current_timestamp();
data->ts_stacked_cnt = (data->ts_stacked_cnt + 1) % SIZE_TIMESTAMP_BUFFER;
data->ts_index_buffer[data->ts_stacked_cnt] = timestamp;
//pr_err("[SSP_IRQ] ts_stacked_cnt %d timestamp %llu\n", data->ts_stacked_cnt, timestamp);
return IRQ_HANDLED;
}
#endif
static int ssp_parse_dt(struct device *dev, struct ssp_data *data)
{
struct device_node *np = dev->of_node;
int errorno = 0;
u32 len, temp;
int i;
if (!np) {
pr_err("[SSP] NO dt node!!\n");
return errorno;
}
if (of_property_read_string(np, "ssp-vdd-mcu-1p8", &data->vdd_mcu_1p8_name)) {
data->regulator_vdd_mcu_1p8 = NULL;
} else {
data->regulator_vdd_mcu_1p8 = regulator_get(NULL, data->vdd_mcu_1p8_name);
if (IS_ERR(data->regulator_vdd_mcu_1p8)) {
pr_err("[SSP]: failed to get reulator %s ret: %ld\n",
data->vdd_mcu_1p8_name, PTR_ERR(data->regulator_vdd_mcu_1p8));
}
}
data->shub_en = of_get_named_gpio(np, "ssp-pwr-en", 0);
pr_err("[SSPBBD] ssp-pwr-en=%d\n", data->shub_en);
if (data->shub_en < 0) {
pr_err("[SSPBBD]: GPIO value not correct\n");
} else {
/* Config GPIO */
errorno = gpio_request(data->shub_en, "SHUB EN");
if (errorno)
pr_err("[SSPBBD]: failed to request SHUB EN, ret:%d", errorno);
errorno = gpio_direction_output(data->shub_en, 0);
if (errorno)
pr_err("[SSPBBD]: failed set SHUB EN as output mode, ret:%d", errorno);
}
data->pin_ap_sleep = of_get_named_gpio(np, "ssp-batch-wake-irq", 0);
pr_info("[SSPBBD] of_get_named_gpio ssp-batch-wake-irq=%d to AP SLEEP\n", data->pin_ap_sleep);
if (data->pin_ap_sleep < 0) {
pr_info("[SSPBBD] Failed to of_get_named_gpio 'ssp-batch-wake-irq'\n");
} else {
errorno = gpio_request(data->pin_ap_sleep, "AP SLEEP");
if (errorno) {
pr_info("[SSPBBD] failed to request AP SLEEP, ret:%d", errorno);
}
errorno = gpio_direction_output(data->pin_ap_sleep, 1);
if (errorno)
pr_err("[SSPBBD]: failed set AP SLEEP as output mode, ret:%d", errorno);
}
data->pin_shub_int = of_get_named_gpio(np, "ssp-shub-int", 0);
pr_info("[SSPBBD] of_get_named_gpio ssp-shub-int=%d to SHUB INT\n", data->pin_shub_int);
if (data->pin_shub_int < 0) {
pr_info("[SSPBBD] Failed to of_get_named_gpio 'ssp-shub-int'\n");
} else {
errorno = gpio_request(data->pin_shub_int, "SHUB INT");
if (errorno) {
pr_info("[SSPBBD] failed to request SHUB INT, ret:%d", errorno);
}
errorno = gpio_direction_input(data->pin_shub_int);
if (errorno)
pr_err("[SSPBBD]: failed set SHUB INT as output mode, ret:%d", errorno);
}
if (of_property_read_u32(np, "ssp-acc-position", &data->accel_position))
data->accel_position = 0;
if (of_property_read_u32(np, "ssp-mag-position", &data->mag_position))
data->mag_position = 0;
pr_info("[SSP] acc-posi[%d] mag-posi[%d]\n",
data->accel_position, data->mag_position);
/* acc type */
if (of_property_read_u32(np, "ssp-acc-type", &data->acc_type))
data->acc_type = 0;
pr_info("[SSP] acc-type = %d\n", data->acc_type);
/* mag type */
if (of_property_read_u32(np, "ssp-mag-type", &data->mag_type))
data->mag_type = 0;
pr_info("[SSP] mag-type = %d\n", data->mag_type);
if (of_property_read_u32(np, "ssp-ap-rev", &data->ap_rev))
data->ap_rev = 0;
#if defined(CONFIG_SENSORS_SSP_PROX_AUTOCAL_AMS)
#ifndef CONFIG_SENSORS_SSP_LIGHT_COLORID
if (of_property_read_u32(np, "ssp,prox-hi_thresh",
&data->uProxHiThresh))
data->uProxHiThresh = DEFAULT_HIGH_THRESHOLD;
if (of_property_read_u32(np, "ssp,prox-low_thresh",
&data->uProxLoThresh))
data->uProxLoThresh = DEFAULT_LOW_THRESHOLD;
pr_info("[SSP] hi-thresh[%u] low-thresh[%u]\n",
data->uProxHiThresh, data->uProxLoThresh);
if (of_property_read_u32(np, "ssp,prox-detect_hi_thresh",
&data->uProxHiThresh_detect))
data->uProxHiThresh_detect = DEFAULT_DETECT_HIGH_THRESHOLD;
if (of_property_read_u32(np, "ssp,prox-detect_LOW_thresh",
&data->uProxLoThresh_detect))
data->uProxLoThresh_detect = DEFAULT_DETECT_LOW_THRESHOLD;
pr_info("[SSP] detect-hi[%u] detect-low[%u]\n",
data->uProxHiThresh_detect, data->uProxLoThresh_detect);
#endif
#else /* CONFIG_SENSORS_SSP_PROX_FACTORYCAL */
if (of_property_read_u32(np, "ssp,prox-hi_thresh",
&data->uProxHiThresh_default))
data->uProxHiThresh_default = DEFAULT_HIGH_THRESHOLD;
if (of_property_read_u32(np, "ssp,prox-low_thresh",
&data->uProxLoThresh_default))
data->uProxLoThresh_default = DEFAULT_LOW_THRESHOLD;
pr_info("[SSP] hi-thresh[%u] low-thresh[%u]\n",
data->uProxHiThresh_default, data->uProxLoThresh_default);
if (of_property_read_u32(np, "ssp,prox-cal_hi_thresh",
&data->uProxHiThresh_cal))
data->uProxHiThresh_cal = DEFAULT_CAL_HIGH_THRESHOLD;
if (of_property_read_u32(np, "ssp,prox-cal_LOW_thresh",
&data->uProxLoThresh_cal))
data->uProxLoThresh_cal = DEFAULT_CAL_LOW_THRESHOLD;
pr_info("[SSP] cal-hi[%u] cal-low[%u]\n",
data->uProxHiThresh_cal, data->uProxLoThresh_cal);
#endif
data->uProxAlertHiThresh = DEFAULT_PROX_ALERT_HIGH_THRESHOLD;
#ifdef CONFIG_SENSORS_MULTIPLE_GLASS_TYPE
if (of_property_read_u32(np, "ssp-glass-type", &data->glass_type))
data->glass_type = 0;
#endif
/* magnetic matrix */
if (data->mag_type == 1) {
if (of_property_read_u8_array(np, "ssp-mag-array",
data->pdc_matrix, sizeof(data->pdc_matrix)))
pr_err("no mag-array, set as 0");
} else {
if (!of_get_property(np, "ssp-mag-array", &len)) {
pr_info("[SSP] No static matrix at DT for YAS532!(%pK)\n",
data->static_matrix);
goto dt_exit;
}
if (len/4 != 9) {
pr_err("[SSP] Length/4:%d should be 9 for YAS532!\n", len/4);
goto dt_exit;
}
data->static_matrix = kzalloc(9*sizeof(s16), GFP_KERNEL);
pr_info("[SSP] static matrix Length:%d, Len/4=%d\n", len, len/4);
for (i = 0; i < 9; i++) {
if (of_property_read_u32_index(np, "ssp-mag-array", i, &temp)) {
pr_err("[SSP] %s cannot get u32 of array[%d]!\n",
__func__, i);
goto dt_exit;
}
*(data->static_matrix+i) = (int)temp;
}
}
if (of_property_read_u16_array(np, "ssp-thermi-up",
data->tempTable_up, ARRAY_SIZE(data->tempTable_up)))
pr_err("no thermi up table, set as 0");
if (of_property_read_u16_array(np, "ssp-thermi-sub",
data->tempTable_sub, ARRAY_SIZE(data->tempTable_sub)))
pr_err("no thermi sub table, set as 0");
/* Hall IC threshold */
if (of_property_read_u16_array(np, "ssp-hall-threshold",
data->hall_threshold, ARRAY_SIZE(data->hall_threshold))) {
pr_err("[SSP]: %s - no hall-threshold, set as 0\n", __func__);
} else {
pr_info("[SSP]: %s - hall thr: %d %d %d %d %d\n", __func__,
data->hall_threshold[0], data->hall_threshold[1],
data->hall_threshold[2], data->hall_threshold[3],
data->hall_threshold[4]);
}
return errorno;
dt_exit:
if (data->static_matrix != NULL)
kfree(data->static_matrix);
return errorno;
}
#if defined(CONFIG_MUIC_NOTIFIER)
static int exynos_cpuidle_muic_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct ssp_data *ssp_data;
muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
ssp_data = container_of(nb, struct ssp_data, cpuidle_muic_nb);
switch (attached_dev) {
case ATTACHED_DEV_JIG_UART_OFF_MUIC:
case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC:
case ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC:
case ATTACHED_DEV_JIG_UART_ON_MUIC:
if (action == MUIC_NOTIFY_CMD_DETACH)
ssp_data->jig_is_attached = false;
else if (action == MUIC_NOTIFY_CMD_ATTACH)
ssp_data->jig_is_attached = true;
else
pr_err("[SSP] %s: ACTION Error!\n", __func__);
break;
default:
break;
}
pr_info("[SSP] %s: dev=%d, action=%lu\n",
__func__, attached_dev, action);
return NOTIFY_DONE;
}
#endif
/*
*#if defined (CONFIG_SENSORS_SSP_VLTE)
*static int ssp_hall_ic_notify(struct notifier_block *nb,
* unsigned long action, void *v)
*{
* pr_info("[SSP] %s is called : fold state %lu\n", __func__, action);
* ssp_ckeck_lcd((int) action);
* return 0;
*}
*#endif
*/
#if defined(CONFIG_SSP_MOTOR_CALLBACK)
static struct ssp_data *ssp_data_info;
void set_ssp_data_info(struct ssp_data *data)
{
if (data != NULL)
ssp_data_info = data;
else
pr_info("[SSP] %s : ssp data info is null\n", __func__);
}
int ssp_motor_callback(int state)
{
int iRet = 0;
if (ssp_data_info != NULL) {
ssp_data_info->motor_state = state;
if (ssp_data_info->ssp_motor_wq != NULL)
queue_work(ssp_data_info->ssp_motor_wq, &ssp_data_info->work_ssp_motor);
pr_info("[SSP] %s : Motor state %d\n", __func__, state);
} else {
pr_info("[SSP] %s : ssp do not ready yet.\n", __func__);
}
return iRet;
}
int get_current_motor_state(void)
{
if (ssp_data_info != NULL)
return ssp_data_info->motor_state;
else
return 0;
}
int (*getMotorCallback(void))(int)
{
pr_info("[SSP] %s : called\n", __func__);
return ssp_motor_callback;
}
void ssp_motor_work_func(struct work_struct *work)
{
int iRet = 0;
struct ssp_data *data = container_of(work,
struct ssp_data, work_ssp_motor);
iRet = send_motor_state(data);
pr_info("[SSP] %s : Motor state %d, iRet %d\n", __func__, data->motor_state, iRet);
}
#endif
int send_hall_ic_status(bool enable) {
struct ssp_msg *msg;
int iRet = 0;
msg = kzalloc(sizeof(*msg), GFP_KERNEL);
msg->cmd = MSG2SSP_HALL_IC_ON_OFF;
msg->length = 1;
msg->options = AP2HUB_WRITE;
msg->buffer = kzalloc(1, GFP_KERNEL);
msg->free_buffer = 1;
msg->buffer[0] = enable;
iRet = ssp_spi_async(ssp_data_info, msg);
if (iRet != SUCCESS) {
pr_err("[SSP]: %s - hall ic command, failed %d\n", __func__, iRet);
return iRet;
}
pr_info("[SSP] %s HALL IC ON/OFF, %d enabled %d\n", __func__, iRet, enable);
return iRet;
}
void ssp_timestamp_sync_work_func(struct work_struct *work)
{
struct ssp_data *data = container_of((struct delayed_work *)work,
struct ssp_data, work_ssp_tiemstamp_sync);
struct ssp_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
msg->cmd = MSG2AP_INST_TIMESTAMP_OFFSET;
msg->length = sizeof(data->timestamp_offset);
msg->options = AP2HUB_WRITE;
msg->buffer = kzalloc(sizeof(data->timestamp_offset), GFP_KERNEL);
pr_info("handle_timestamp_sync: %lld\n", data->timestamp_offset);
memcpy(msg->buffer, &(data->timestamp_offset), sizeof(data->timestamp_offset));
ssp_spi_sync(data, msg, 1000);
//pr_info("[SSP] %s : Motor state %d, iRet %d\n",__func__, data->motor_state, iRet);
}
void ssp_reset_work_func(struct work_struct *work)
{
struct ssp_data *data = container_of((struct delayed_work *)work,
struct ssp_data, work_ssp_reset);
u64 current_timestamp = get_current_timestamp();
pr_err("[SSP]: resumetimestamp %lld, current_timestamp %lld\n", data->resumeTimestamp, current_timestamp);
if (data->resetting == false && current_timestamp - data->resumeTimestamp < 3000000000ULL) {
mutex_lock(&data->ssp_enable_mutex);
pr_err("[SSP]: reset scenario, flip cover issue.\n");
reset_mcu(data);
mutex_unlock(&data->ssp_enable_mutex);
}
}
static int ssp_probe(struct spi_device *spi)
{
struct ssp_data *data;
struct ssp_platform_data *pdata;
int iRet = 0;
pr_info("[SSP] %s, is called\n", __func__);
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data == NULL) {
iRet = -ENOMEM;
pr_err("[SSP] %s, failed to allocate memory for data\n",
__func__);
goto exit;
}
if (spi->dev.of_node) {
iRet = ssp_parse_dt(&spi->dev, data);
if (iRet) {
pr_err("[SSP]: %s - Failed to parse DT\n", __func__);
goto err_setup;
}
/* data->ssp_changes = SSP_MCU_L5; *//* K330, MPU L5*/
} else {
pdata = spi->dev.platform_data;
if (pdata == NULL) {
pr_err("[SSP] %s, platform_data is null\n", __func__);
iRet = -ENOMEM;
goto err_setup;
}
/* AP system_rev */
if (pdata->check_ap_rev)
data->ap_rev = pdata->check_ap_rev();
else
data->ap_rev = 0;
/* Get sensor positions */
if (pdata->get_positions) {
pdata->get_positions(&data->accel_position,
&data->mag_position);
} else {
data->accel_position = 0;
data->mag_position = 0;
}
if (pdata->mag_matrix) {
data->mag_matrix_size = pdata->mag_matrix_size;
data->mag_matrix = pdata->mag_matrix;
}
}
spi->mode = SPI_MODE_3;
if (spi_setup(spi)) {
pr_err("[SSP] %s, failed to setup spi\n", __func__);
iRet = -ENODEV;
goto err_setup;
}
data->bProbeIsDone = false;
data->spi = spi;
spi_set_drvdata(spi, data);
#ifdef CONFIG_SENSORS_SSP_SHTC1
mutex_init(&data->cp_temp_adc_lock);
mutex_init(&data->bulk_temp_read_lock);
#endif
mutex_init(&data->comm_mutex);
mutex_init(&data->pending_mutex);
mutex_init(&data->enable_mutex);
mutex_init(&data->ssp_enable_mutex);
if (spi->dev.of_node == NULL) {
pr_err("[SSP] %s, function callback is null\n", __func__);
iRet = -EIO;
goto err_reset_null;
}
pr_info("\n#####################################################\n");
initialize_variable(data);
wake_lock_init(&data->ssp_wake_lock,
WAKE_LOCK_SUSPEND, "ssp_wake_lock");
wake_lock_init(&data->ssp_batch_wake_lock,
WAKE_LOCK_SUSPEND, "ssp_batch_wake_lock");
wake_lock_init(&data->ssp_comm_wake_lock,
WAKE_LOCK_SUSPEND, "ssp_comm_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;
}
iRet = initialize_debug_timer(data);
if (iRet < 0) {
pr_err("[SSP]: %s - could not create workqueue\n", __func__);
goto err_create_workqueue;
}
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_SENSORS_SSP_SENSORHUB
/* init sensorhub device */
iRet = ssp_sensorhub_initialize(data);
if (iRet < 0) {
pr_err("%s: ssp_sensorhub_initialize err(%d)\n",
__func__, iRet);
ssp_sensorhub_remove(data);
}
#endif
#ifdef CONFIG_SENSORS_SSP_HIFI_BATCHING
/* HIFI Sensor + Batch Support */
mutex_init(&data->batch_events_lock);
data->batch_wq = create_singlethread_workqueue("ssp_batch_wq");
if (!data->batch_wq) {
iRet = -1;
pr_err("[SSP]: %s - could not create batch workqueue\n",
__func__);
goto err_create_batch_workqueue;
}
data->irq_shub_int = gpio_to_irq(data->pin_shub_int);
iRet = request_irq(data->irq_shub_int, ssp_shub_int_handler,
IRQF_TRIGGER_FALLING, "ssp-shub-int", data);
if (iRet < 0) {
pr_info("[SSP_IRQ]: request_irq(%d) failed for gpio %d (%d)\n",
data->irq_shub_int,
data->pin_shub_int,
iRet);
iRet = -ENODEV;
}
disable_irq(data->irq_shub_int);
data->ts_stacked_cnt = 0;
#endif
bbd_register(data, &ssp_bbd_callbacks);
#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
#if defined(CONFIG_MUIC_NOTIFIER)
muic_notifier_register(&data->cpuidle_muic_nb,
exynos_cpuidle_muic_notifier, MUIC_NOTIFY_DEV_CPUIDLE);
#endif
/*
*#if defined (CONFIG_SENSORS_SSP_VLTE)
* data->hall_ic_nb.notifier_call = ssp_hall_ic_notify;
* hall_ic_register_notify(&data->hall_ic_nb);
*#endif
*/
pr_info("[SSP]: %s - probe success!\n", __func__);
enable_debug_timer(data);
data->bProbeIsDone = true;
iRet = 0;
mutex_init(&shutdown_lock);
#ifdef CONFIG_SSP_MOTOR_CALLBACK
pr_info("[SSP]: %s motor callback set!", __func__);
set_ssp_data_info(data);
//register motor
setMotorCallback(ssp_motor_callback);
data->ssp_motor_wq =
create_singlethread_workqueue("ssp_motor_wq");
if (!data->ssp_motor_wq) {
iRet = -1;
pr_err("[SSP]: %s - could not create motor workqueue\n",
__func__);
goto err_create_motor_workqueue;
}
INIT_WORK(&data->work_ssp_motor, ssp_motor_work_func);
#endif
INIT_DELAYED_WORK(&data->work_ssp_tiemstamp_sync, ssp_timestamp_sync_work_func);
INIT_DELAYED_WORK(&data->work_ssp_reset, ssp_reset_work_func);
goto exit;
#ifdef CONFIG_SSP_MOTOR_CALLBACK
err_create_motor_workqueue:
destroy_workqueue(data->ssp_motor_wq);
#endif
#ifdef CONFIG_SENSORS_SSP_HIFI_BATCHING
/* Test PIN for Camera - Gyro Sync */
err_create_batch_workqueue:
destroy_workqueue(data->batch_wq);
mutex_destroy(&data->batch_events_lock);
#endif
err_symlink_create:
remove_sysfs(data);
err_sysfs_create:
destroy_workqueue(data->debug_wq);
err_create_workqueue:
remove_input_dev(data);
err_input_register_device:
wake_lock_destroy(&data->ssp_batch_wake_lock);
wake_lock_destroy(&data->ssp_comm_wake_lock);
wake_lock_destroy(&data->ssp_wake_lock);
err_reset_null:
mutex_destroy(&data->comm_mutex);
mutex_destroy(&data->pending_mutex);
mutex_destroy(&data->enable_mutex);
mutex_destroy(&data->ssp_enable_mutex);
#ifdef CONFIG_SENSORS_SSP_SHTC1
mutex_destroy(&data->bulk_temp_read_lock);
mutex_destroy(&data->cp_temp_adc_lock);
#endif
err_setup:
kfree(data);
pr_err("[SSP] %s, probe failed!\n", __func__);
exit:
pr_info("#####################################################\n\n");
return iRet;
}
static void ssp_shutdown(struct spi_device *spi)
{
struct ssp_data *data = spi_get_drvdata(spi);
pr_err("[SSP] lpm %d recovery\n", lpcharge);
func_dbg();
if (data->bProbeIsDone == false)
goto exit;
disable_debug_timer(data);
/*
*hoi
*if (SUCCESS != ssp_send_cmd(data, MSG2SSP_AP_STATUS_SHUTDOWN, 0))
* pr_err("[SSP]: %s MSG2SSP_AP_STATUS_SHUTDOWN failed\n",
* __func__);
*/
/*
*#if defined (CONFIG_SENSORS_SSP_VLTE)
* // hall_ic unregister
* hall_ic_unregister_notify(&data->hall_ic_nb);
*#endif
*/
mutex_lock(&data->ssp_enable_mutex);
ssp_enable(data, false);
clean_pending_list(data);
mutex_unlock(&data->ssp_enable_mutex);
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&data->early_suspend);
#endif
bbd_register(NULL, NULL);
mutex_lock(&shutdown_lock);
cancel_work_sync(&data->work_bbd_on_packet); /* should be cancel before removing iio dev */
destroy_workqueue(data->bbd_on_packet_wq);
cancel_work_sync(&data->work_bbd_mcu_ready);
destroy_workqueue(data->bbd_mcu_ready_wq);
#ifdef CONFIG_SENSORS_SSP_HIFI_BATCHING
destroy_workqueue(data->batch_wq); /* HIFI */
mutex_destroy(&data->batch_events_lock);
#endif
data->bbd_on_packet_wq = NULL;
data->bbd_mcu_ready_wq = NULL;
#ifdef CONFIG_SSP_MOTOR_CALLBACK
cancel_work_sync(&data->work_ssp_motor);
destroy_workqueue(data->ssp_motor_wq);
data->ssp_motor_wq = NULL;
#endif
mutex_unlock(&shutdown_lock);
remove_event_symlink(data);
remove_sysfs(data);
remove_input_dev(data);
#ifdef CONFIG_SENSORS_SSP_SENSORHUB
ssp_sensorhub_remove(data);
#endif
del_timer_sync(&data->debug_timer);
cancel_work_sync(&data->work_debug);
destroy_workqueue(data->debug_wq);
wake_lock_destroy(&data->ssp_comm_wake_lock);
wake_lock_destroy(&data->ssp_wake_lock);
wake_lock_destroy(&data->ssp_batch_wake_lock);
#ifdef CONFIG_SENSORS_SSP_SHTC1
mutex_destroy(&data->bulk_temp_read_lock);
mutex_destroy(&data->cp_temp_adc_lock);
#endif
mutex_destroy(&data->comm_mutex);
mutex_destroy(&data->pending_mutex);
mutex_destroy(&data->enable_mutex);
mutex_destroy(&data->ssp_enable_mutex);
pr_info("[SSP] %s done\n", __func__);
exit:
kfree(data);
}
#ifdef CONFIG_HAS_EARLYSUSPEND
static void ssp_early_suspend(struct early_suspend *handler)
{
struct ssp_data *data;
data = container_of(handler, struct ssp_data, early_suspend);
func_dbg();
disable_debug_timer(data);
#ifdef CONFIG_SENSORS_SSP_SENSORHUB
/* give notice to user that AP goes to sleep */
ssp_sensorhub_report_notice(data, MSG2SSP_AP_STATUS_SLEEP);
ssp_sleep_mode(data);
data->uLastAPState = MSG2SSP_AP_STATUS_SLEEP;
#else
if (atomic64_read(&data->aSensorEnable) > 0)
ssp_sleep_mode(data);
#endif
}
static void ssp_late_resume(struct early_suspend *handler)
{
struct ssp_data *data;
data = container_of(handler, struct ssp_data, early_suspend);
func_dbg();
enable_debug_timer(data);
#ifdef CONFIG_SENSORS_SSP_SENSORHUB
/* give notice to user that AP goes to sleep */
ssp_sensorhub_report_notice(data, MSG2SSP_AP_STATUS_WAKEUP);
ssp_resume_mode(data);
data->uLastAPState = MSG2SSP_AP_STATUS_WAKEUP;
#else
if (atomic64_read(&data->aSensorEnable) > 0)
ssp_resume_mode(data);
#endif
}
#else /* no early suspend */
static int ssp_suspend(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
struct ssp_data *data = spi_get_drvdata(spi);
func_dbg();
gpio_set_value(data->pin_ap_sleep, 0);
pr_err("[SSP]: status of suspend notifier: %d", gpio_get_value(data->pin_ap_sleep));
gpio_set_value(data->pin_ap_sleep, 1);
/*
if (ssp_send_cmd(data, MSG2SSP_AP_STATUS_SUSPEND, 0) != SUCCESS)
pr_err("[SSP]: %s MSG2SSP_AP_STATUS_SUSPEND failed\n",
__func__);
*/
data->uLastResumeState = MSG2SSP_AP_STATUS_SUSPEND;
disable_debug_timer(data);
data->bTimeSyncing = false;
data->IsAPsuspend = true;
return 0;
}
static int ssp_resume(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
struct ssp_data *data = spi_get_drvdata(spi);
func_dbg();
enable_debug_timer(data);
#ifdef CONFIG_SENSORS_SSP_HIFI_BATCHING
data->resumeTimestamp = get_current_timestamp();
data->bIsResumed = true;
#endif
gpio_set_value(data->pin_ap_sleep, 1);
pr_err("[SSP]: status of resume notifier: %d", gpio_get_value(data->pin_ap_sleep));
if (ssp_send_cmd(data, MSG2SSP_AP_STATUS_RESUME, 0) != SUCCESS)
pr_err("[SSP]: %s MSG2SSP_AP_STATUS_RESUME failed\n",
__func__);
data->uLastResumeState = MSG2SSP_AP_STATUS_RESUME;
data->IsAPsuspend = false;
return 0;
}
static const struct dev_pm_ops ssp_pm_ops = {
.suspend = ssp_suspend,
.resume = ssp_resume
};
#endif /* CONFIG_HAS_EARLYSUSPEND */
static const struct spi_device_id ssp_id[] = {
{"ssp-spi", 0},
{}
};
MODULE_DEVICE_TABLE(spi, ssp_id);
static struct spi_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-spi",
},
};
struct spi_driver *pssp_driver = &ssp_driver;
module_spi_driver(ssp_driver);
MODULE_DESCRIPTION("ssp spi driver");
MODULE_AUTHOR("Samsung Electronics");
MODULE_LICENSE("GPL");