diff options
Diffstat (limited to 'drivers/sensorhub/ssp_dev.c')
-rw-r--r-- | drivers/sensorhub/ssp_dev.c | 457 |
1 files changed, 457 insertions, 0 deletions
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 <gs0816.kim@samsung.com>"); +MODULE_LICENSE("GPL"); |