diff options
Diffstat (limited to 'drivers/sensorhub/stm/ssp_dev.c')
-rwxr-xr-x | drivers/sensorhub/stm/ssp_dev.c | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/drivers/sensorhub/stm/ssp_dev.c b/drivers/sensorhub/stm/ssp_dev.c new file mode 100755 index 00000000000..e7ee067434f --- /dev/null +++ b/drivers/sensorhub/stm/ssp_dev.c @@ -0,0 +1,566 @@ +/* + * 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 + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void ssp_early_suspend(struct early_suspend *handler); +static void ssp_late_resume(struct early_suspend *handler); +#endif + +void ssp_enable(struct ssp_data *data, bool enable) +{ + pr_info("%s, enable = %d, old enable = %d\n", + __func__, enable, data->bSspShutdown); + + if (enable && data->bSspShutdown) { + data->bSspShutdown = false; + enable_irq(data->iIrq); + enable_irq_wake(data->iIrq); + } else if (!enable && !data->bSspShutdown) { + data->bSspShutdown = true; + disable_irq(data->iIrq); + disable_irq_wake(data->iIrq); + } else + pr_err("%s, error / enable = %d, old enable = %d\n", + __func__, enable, data->bSspShutdown); +} +/************************************************************************/ +/* interrupt happened due to transition/change of SSP MCU */ +/************************************************************************/ + +static irqreturn_t sensordata_irq_thread_fn(int iIrq, void *dev_id) +{ + struct ssp_data *data = dev_id; + + select_irq_msg(data); + data->uIrqCnt++; + + return IRQ_HANDLED; +} + +/*************************************************************************/ +/* initialize sensor hub */ +/*************************************************************************/ + +static void initialize_variable(struct ssp_data *data) +{ + int iSensorIndex; + + for (iSensorIndex = 0; iSensorIndex < SENSOR_MAX; iSensorIndex++) { + data->adDelayBuf[iSensorIndex] = DEFUALT_POLLING_DELAY; + data->aiCheckStatus[iSensorIndex] = INITIALIZATION_STATE; + } + + memset(data->uFactorydata, 0, sizeof(char) * FACTORY_DATA_MAX); + + atomic_set(&data->aSensorEnable, 0); + data->iLibraryLength = 0; + data->uSensorState = 0; + data->uFactorydataReady = 0; + data->uFactoryProxAvg[0] = 0; + + data->uResetCnt = 0; + data->uInstFailCnt = 0; + data->uTimeOutCnt = 0; + data->uSsdFailCnt = 0; + data->uBusyCnt = 0; + data->uIrqCnt = 0; + data->uIrqFailCnt = 0; + data->uMissSensorCnt = 0; + + data->bCheckSuspend = false; + data->bSspShutdown = true; + data->bDebugEnabled = false; + data->bProximityRawEnabled = false; + data->bGeomagneticRawEnabled = false; + data->bMcuIRQTestSuccessed = false; + data->bBarcodeEnabled = false; + data->bAccelAlert = 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; + data->uProxCanc = 0; + data->uProxHiThresh = 0; + data->uProxLoThresh = 0; + data->uGyroDps = GYROSCOPE_DPS500; + data->uIr_Current = DEFUALT_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; + + initialize_function_pointer(data); +} + +int initialize_mcu(struct ssp_data *data) +{ + int iRet = 0; + + 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; + } + + 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; + } + + iRet = SUCCESS; +out: + return iRet; +} + +static int initialize_irq(struct ssp_data *data) +{ + int iRet, iIrq; + + iRet = gpio_request(data->spi->irq, "mpu_ap_int"); + if (iRet < 0) { + pr_err("[SSP]: %s - gpio %d request failed (%d)\n", + __func__, data->spi->irq, iRet); + return iRet; + } + + iRet = gpio_direction_input(data->spi->irq); + if (iRet < 0) { + pr_err("[SSP]: %s - failed to set gpio %d as input (%d)\n", + __func__, data->spi->irq, iRet); + goto err_irq_direction_input; + } + + iIrq = gpio_to_irq(data->spi->irq); + + pr_info("[SSP]: requesting IRQ %d, %d\n", iIrq, data->spi->irq); + 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->spi->irq); + return iRet; +} + +static void work_function_firmware_update(struct work_struct *work) +{ + struct ssp_data *data = container_of((struct delayed_work *)work, + struct ssp_data, work_firmware); + int iRet = 0; + + pr_info("[SSP] : %s\n", __func__); + + iRet = forced_to_download_binary(data, KERNEL_BINARY); + if (iRet < 0) { + ssp_dbg("[SSP]: %s - forced_to_download_binary failed!\n", + __func__); + return; + } + + data->uCurFirmRev = get_firmware_rev(data); + pr_info("[SSP] MCU Firm Rev : New = %8u\n", + data->uCurFirmRev); +} + +static int ssp_probe(struct spi_device *spi) +{ + int iRet = 0; + struct ssp_data *data; + struct ssp_platform_data *pdata = spi->dev.platform_data; + + pr_info("\n#####################################################\n"); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL || pdata == NULL) { + pr_err("[SSP]: %s - failed to allocate memory for data\n", + __func__); + iRet = -ENOMEM; + goto exit; + } + + 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; + + if ((data->wakeup_mcu == NULL) + || (data->check_mcu_ready == NULL) + || (data->check_mcu_busy == NULL) + || (data->set_mcu_reset == NULL)) { + pr_err("[SSP]: %s - function callback is null\n", __func__); + iRet = -EIO; + goto exit; + } + + /* 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; + } + +#ifdef CONFIG_SENSORS_SSP_SHTC1 + /* Get pdata for cp thermister */ + if (pdata->cp_thm_adc_table) { + data->cp_thm_adc_channel = pdata->cp_thm_adc_channel; + data->cp_thm_adc_arr_size= pdata->cp_thm_adc_arr_size; + data->cp_thm_adc_table = pdata->cp_thm_adc_table; + } + mutex_init(&data->cp_temp_adc_lock); +#endif + + mutex_init(&data->comm_mutex); + + if (spi_setup(spi)) { + pr_err("failed to setup spi for ssp_spi\n"); + goto err_setup; + } + + data->bProbeIsDone = false; + data->fw_dl_state = FW_DL_STATE_NONE; + data->spi = spi; + spi_set_drvdata(spi, data); + + INIT_DELAYED_WORK(&data->work_firmware, work_function_firmware_update); + /* check boot loader binary */ + data->fw_dl_state = check_fwbl(data); + + initialize_variable(data); + + if (data->fw_dl_state == FW_DL_STATE_NONE) { + iRet = initialize_mcu(data); + if (iRet == ERROR) { + data->uResetCnt++; + toggle_mcu_reset(data); + msleep(SSP_SW_RESET_TIME); + initialize_mcu(data); + } else if (iRet < ERROR) { + 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; + } + + 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 irq\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_sensorhub_initialize(data); + if (iRet < 0) { + pr_err("%s: ssp_sensorhub_initialize err(%d)", __func__, iRet); + ssp_sensorhub_remove(data); + } +#endif + + ssp_enable(data, true); + pr_info("[SSP]: %s - probe success!\n", __func__); + + enable_debug_timer(data); + + iRet = 0; + if (data->fw_dl_state == FW_DL_STATE_NEED_TO_SCHEDULE) { + pr_info("[SSP]: Firmware update is scheduled\n"); + schedule_delayed_work(&data->work_firmware, + msecs_to_jiffies(1000)); + data->fw_dl_state = FW_DL_STATE_SCHEDULED; + } else if (data->fw_dl_state == FW_DL_STATE_FAIL) { + data->bSspShutdown = true; + } + + data->bProbeIsDone = true; + + goto exit; + +err_symlink_create: + remove_sysfs(data); +err_sysfs_create: + free_irq(data->iIrq, data); + gpio_free(data->spi->irq); +err_setup_irq: + destroy_workqueue(data->debug_wq); +err_create_workqueue: + remove_input_dev(data); +err_input_register_device: + wake_lock_destroy(&data->ssp_wake_lock); +err_read_reg: +err_reset_null: +err_setup: +#ifdef CONFIG_SENSORS_SSP_SHTC1 + mutex_destroy(&data->cp_temp_adc_lock); +#endif + mutex_destroy(&data->comm_mutex); + 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); + + func_dbg(); + if (data->bProbeIsDone == false) + goto exit; + + if (data->fw_dl_state >= FW_DL_STATE_SCHEDULED && + data->fw_dl_state < FW_DL_STATE_DONE) { + pr_err("%s, cancel_delayed_work_sync state = %d\n", + __func__, data->fw_dl_state); + cancel_delayed_work_sync(&data->work_firmware); + } + + ssp_enable(data, false); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&data->early_suspend); +#endif + + disable_debug_timer(data); + + free_irq(data->iIrq, data); + gpio_free(data->spi->irq); + + remove_sysfs(data); + remove_event_symlink(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_wake_lock); +#ifdef CONFIG_SENSORS_SSP_SHTC1 + mutex_destroy(&data->cp_temp_adc_lock); +#endif + toggle_mcu_reset(data); +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); +#else + if (atomic_read(&data->aSensorEnable) > 0) + ssp_sleep_mode(data); +#endif +} + +static void ssp_late_resume(struct early_suspend *handler) +{ + struct ssp_data *data; + data = container_of(handler, struct ssp_data, early_suspend); + + func_dbg(); + enable_debug_timer(data); + +#ifdef CONFIG_SENSORS_SSP_SENSORHUB + /* give notice to user that AP goes to sleep */ + ssp_sensorhub_report_notice(data, MSG2SSP_AP_STATUS_WAKEUP); + ssp_resume_mode(data); +#else + if (atomic_read(&data->aSensorEnable) > 0) + ssp_resume_mode(data); +#endif +} + +#else /* CONFIG_HAS_EARLYSUSPEND */ + +static int ssp_suspend(struct device *dev) +{ +#if 0 + struct spi_device *spi_dev = to_spi_device(dev); + struct ssp_data *data = spi_get_drvdata(spi_dev); + + if (ssp_send_cmd(data, MSG2SSP_AP_STATUS_SUSPEND) != SUCCESS) + pr_err("[SSP]: %s MSG2SSP_AP_STATUS_SUSPEND failed\n", + __func__); +#endif + func_dbg(); + + return 0; +} + +static int ssp_resume(struct device *dev) +{ +#if 0 + struct spi_device *spi_dev = to_spi_device(dev); + struct ssp_data *data = spi_get_drvdata(spi_dev); + + if (ssp_send_cmd(data, MSG2SSP_AP_STATUS_RESUME) != SUCCESS) + pr_err("[SSP]: %s MSG2SSP_AP_STATUS_RESUME failed\n", + __func__); +#endif + func_dbg(); + + 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 + .bus = &spi_bus_type, + .owner = THIS_MODULE, + .name = "ssp-spi" + }, +}; + +static int __init ssp_stm_spi_init(void) +{ + int ret; + + pr_info("[SSP] %s\n", __func__); + ret = spi_register_driver(&ssp_driver); + + if (ret) + pr_err("[SSP]failed to register s5c73mc fw - %x\n", ret); + + return ret; +} + +static void __exit ssp_stm_spi_exit(void) +{ + spi_unregister_driver(&ssp_driver); +} + +module_init(ssp_stm_spi_init); +module_exit(ssp_stm_spi_exit); + +MODULE_DESCRIPTION("ssp spi driver"); +MODULE_AUTHOR("Samsung Electronics"); +MODULE_LICENSE("GPL"); |