aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sensorhub/stm/ssp_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sensorhub/stm/ssp_dev.c')
-rwxr-xr-xdrivers/sensorhub/stm/ssp_dev.c566
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");