diff options
| author | Doug Zobel <dzobel1@motorola.com> | 2013-11-15 14:29:07 -0600 |
|---|---|---|
| committer | James Wylder <jwylder@motorola.com> | 2014-03-05 17:46:52 -0600 |
| commit | d2a782003a6047da120a33e6f8ee6fd33bb825d6 (patch) | |
| tree | 8d20bd4ecda62a06e98993c4108456bc1acb0d0b /drivers/misc/m4sensorhub_stillmode.c | |
| parent | 32fd2d36d2464056d4522a9c02797b7c2b2e884f (diff) | |
| download | olio-linux-3.10-d2a782003a6047da120a33e6f8ee6fd33bb825d6.tar.xz olio-linux-3.10-d2a782003a6047da120a33e6f8ee6fd33bb825d6.zip | |
CW integration and minnow bringup
* create minnow machine type
* create Android makefile
* add pre-commit syntax check
* enable -Werror
* Add drivers: CPCAP, TPS65xxx, m4sensorhub, atmxt, lm3535,
usb gadget, minnow display, TI 12xx wireless
Change-Id: I7962f5e1256715f2452aed5a62a4f2f2383d5046
Diffstat (limited to 'drivers/misc/m4sensorhub_stillmode.c')
| -rw-r--r-- | drivers/misc/m4sensorhub_stillmode.c | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/drivers/misc/m4sensorhub_stillmode.c b/drivers/misc/m4sensorhub_stillmode.c new file mode 100644 index 00000000000..3fa61219245 --- /dev/null +++ b/drivers/misc/m4sensorhub_stillmode.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2012 Motorola, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Adds ability to program periodic interrupts from user space that + * can wake the phone out of low power modes. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/miscdevice.h> +#include <linux/input.h> +#include <linux/wakelock.h> +#include <linux/workqueue.h> +#include <linux/m4sensorhub.h> +#include <linux/slab.h> + +#define STILLMODE_CLIENT_DRIVER_NAME "m4sensorhub_stillmode" +#define STILLMODE_DEFAULT_TIMEOUT 600 /* 10 minutes */ + +static DEFINE_MUTEX(state_access); + +enum m4_stillmode_type { + MOTION, + STILL, +}; + +struct stillmode_client { + struct m4sensorhub_data *m4sensorhub; + struct input_dev *input_dev; + enum m4_stillmode_type state; + struct wake_lock wakelock; + struct work_struct queued_work; + u16 timeout; +}; + +static struct stillmode_client *g_stillmode_data; + +static int stillmode_set_timeout(struct stillmode_client *stillmode_client_data, + u16 timeout) +{ + int ret; + + ret = m4sensorhub_reg_write(stillmode_client_data->m4sensorhub, + M4SH_REG_POWER_STILLMODETIMEOUT, + (char *)&timeout, m4sh_no_mask); + if (ret == m4sensorhub_reg_getsize(stillmode_client_data->m4sensorhub, + M4SH_REG_POWER_STILLMODETIMEOUT)) { + stillmode_client_data->timeout = timeout; + ret = 0; + } else + ret = -EIO; + + return ret; + +} + +static void stillmode_set_state(struct stillmode_client *stillmode_client_data, + enum m4_stillmode_type state) +{ + mutex_lock(&state_access); + if (stillmode_client_data->state == state) { + mutex_unlock(&state_access); + printk(KERN_WARNING "M4SH duplicate stillmode update (%s)\n", + (state == STILL) ? "still" : "moving"); + } else { + stillmode_client_data->state = state; + mutex_unlock(&state_access); + + /* Hold a 500ms wakelock to let data get to KineticManager */ + wake_lock_timeout(&stillmode_client_data->wakelock, 0.5 * HZ); + + input_report_switch(stillmode_client_data->input_dev, + SW_STILL_MODE, + (stillmode_client_data->state == STILL)); + input_sync(stillmode_client_data->input_dev); + printk(KERN_INFO "stillmode state changed to %s (%d)\n", + (state == STILL) ? "still" : "moving", state); + } +} + +static int m4_stillmode_exit(void) +{ + struct stillmode_client *stillmode_client_data = g_stillmode_data; + int ret = 0; + + KDEBUG(M4SH_INFO, "Resetting stillmode timer\n"); + + /* writing timeout value to M4 resets its timer */ + ret = stillmode_set_timeout(stillmode_client_data, + stillmode_client_data->timeout); + if (ret == 0) { + if (stillmode_client_data->state == STILL) + stillmode_set_state(stillmode_client_data, MOTION); + } else + KDEBUG(M4SH_ERROR, "M4SH Error setting timeout (%d)\n", ret); + + return ret; +} + + +int m4sensorhub_stillmode_exit(void) +{ + return m4_stillmode_exit(); +} +EXPORT_SYMBOL_GPL(m4sensorhub_stillmode_exit); + + +static void m4sensorhub_stillmode_work(struct work_struct *work) +{ + m4sensorhub_stillmode_exit(); +} + +static void m4_handle_stillmode_irq(enum m4sensorhub_irqs int_event, + void *stillmode_data) +{ + struct stillmode_client *stillmode_client_data = stillmode_data; + enum m4_stillmode_type new_state; + + KDEBUG(M4SH_INFO, "%s() got irq %d (%s)\n", __func__, int_event, + int_event == M4SH_IRQ_STILL_DETECTED ? "STILL_MODE" : "MOTION_MODE"); + + switch (int_event) { + case (M4SH_IRQ_STILL_DETECTED): + new_state = STILL; + break; + case (M4SH_IRQ_MOTION_DETECTED): + new_state = MOTION; + break; + default: + printk(KERN_ERR "%s() Unexpected irq: %d\n", + __func__, int_event); + return; + break; + } + + stillmode_set_state(stillmode_client_data, new_state); +} + +static ssize_t m4_stillmode_getstate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stillmode_client *stillmode_client_data = + platform_get_drvdata(pdev); + + return sprintf(buf, "%d \n", stillmode_client_data->state); +} + +static ssize_t m4_stillmode_setstate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stillmode_client *stillmode_client_data = + platform_get_drvdata(pdev); + long value; + int ret = size; + + if (((strict_strtoul(buf, 10, &value)) < 0) || + (value != MOTION)) { + KDEBUG(M4SH_ERROR, "M4SH stillmode invalid value: %ld. Only " + "%d is allowed\n", value, MOTION); + return -EINVAL; + } + + if (value != stillmode_client_data->state) + return m4sensorhub_stillmode_exit(); + + return ret; +} + +static ssize_t m4_stillmode_get_timeout(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stillmode_client *stillmode_client_data = + platform_get_drvdata(pdev); + + return sprintf(buf, "%d \n", stillmode_client_data->timeout); +} + +static ssize_t m4_stillmode_set_timeout(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stillmode_client *stillmode_client_data = + platform_get_drvdata(pdev); + long value; + int ret; + + if (((strict_strtoul(buf, 10, &value)) < 0) || + (value < 0) || (value > USHRT_MAX)) { + KDEBUG(M4SH_ERROR, "M4SH stillmode invalid timeout: %ld\n", + value); + return -EINVAL; + } + + KDEBUG(M4SH_DEBUG, "%s() setting timeout to %ld\n", __func__, value); + + ret = stillmode_set_timeout(stillmode_client_data, value); + + return ((ret == 0) ? size : ret); +} + +static DEVICE_ATTR(state, 0664, m4_stillmode_getstate, + m4_stillmode_setstate); +static DEVICE_ATTR(timeout, 0664, m4_stillmode_get_timeout, + m4_stillmode_set_timeout); + +static int stillmode_client_probe(struct platform_device *pdev) +{ + int ret = -1; + struct stillmode_client *stillmode_client_data; + struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata(); + + if (!m4sensorhub) + return -EFAULT; + + stillmode_client_data = kzalloc(sizeof(*stillmode_client_data), + GFP_KERNEL); + if (!stillmode_client_data) + return -ENOMEM; + + g_stillmode_data = stillmode_client_data; + stillmode_client_data->m4sensorhub = m4sensorhub; + platform_set_drvdata(pdev, stillmode_client_data); + stillmode_client_data->state = MOTION; + stillmode_client_data->timeout = STILLMODE_DEFAULT_TIMEOUT; + + stillmode_client_data->input_dev = input_allocate_device(); + if (!stillmode_client_data->input_dev) { + ret = -ENOMEM; + KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n", + __func__, ret); + goto free_memory; + } + + stillmode_client_data->input_dev->name = STILLMODE_CLIENT_DRIVER_NAME; + set_bit(EV_SW, stillmode_client_data->input_dev->evbit); + set_bit(SW_STILL_MODE, stillmode_client_data->input_dev->swbit); + + if (input_register_device(stillmode_client_data->input_dev)) { + KDEBUG(M4SH_ERROR, "%s: input device register failed\n", + __func__); + input_free_device(stillmode_client_data->input_dev); + goto free_memory; + } + + wake_lock_init(&stillmode_client_data->wakelock, WAKE_LOCK_SUSPEND, + STILLMODE_CLIENT_DRIVER_NAME); + + INIT_WORK(&stillmode_client_data->queued_work, + m4sensorhub_stillmode_work); + + ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_STILL_DETECTED, + m4_handle_stillmode_irq, + stillmode_client_data); + if (ret < 0) { + KDEBUG(M4SH_ERROR, "Error registering still mode IRQ: " + "%d\n", ret); + goto destroy_wakelock; + } + ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_STILL_DETECTED); + if (ret < 0) { + KDEBUG(M4SH_ERROR, "Error enabling still mode int: " + "%d\n", ret); + goto unregister_still_irq; + } + + ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_MOTION_DETECTED, + m4_handle_stillmode_irq, + stillmode_client_data); + if (ret < 0) { + KDEBUG(M4SH_ERROR, "Error registering moving mode IRQ: " + "%d\n", ret); + goto disable_still_irq; + } + ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_MOTION_DETECTED); + if (ret < 0) { + KDEBUG(M4SH_ERROR, "Error enabling moving mode int: " + "%d\n", ret); + goto unregister_moving_irq; + } + + if (device_create_file(&pdev->dev, &dev_attr_state)) { + KDEBUG(M4SH_ERROR, "Error creating stillmode sys entry\n"); + ret = -1; + goto disable_moving_irq; + } + + if (device_create_file(&pdev->dev, &dev_attr_timeout)) { + KDEBUG(M4SH_ERROR, "Error creating timeout sys entry\n"); + ret = -1; + goto remove_stillmode_sysfs; + } + + /* initialize timer on M4 */ + m4sensorhub_stillmode_exit(); + + KDEBUG(M4SH_INFO, "Initialized %s driver\n", + STILLMODE_CLIENT_DRIVER_NAME); + + return 0; + +remove_stillmode_sysfs: + device_remove_file(&pdev->dev, &dev_attr_state); +disable_moving_irq: + m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_MOTION_DETECTED); +unregister_moving_irq: + m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_MOTION_DETECTED); +disable_still_irq: + m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_STILL_DETECTED); +unregister_still_irq: + m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_STILL_DETECTED); +destroy_wakelock: + wake_lock_destroy(&stillmode_client_data->wakelock); + input_unregister_device(stillmode_client_data->input_dev); +free_memory: + platform_set_drvdata(pdev, NULL); + m4sensorhub->pdev->stillmode_exit = NULL; + stillmode_client_data->m4sensorhub = NULL; + kfree(stillmode_client_data); + g_stillmode_data = NULL; + + return ret; +} + +static int __exit stillmode_client_remove(struct platform_device *pdev) +{ + struct stillmode_client *stillmode_client_data = + platform_get_drvdata(pdev); + struct m4sensorhub_data *m4sensorhub = + stillmode_client_data->m4sensorhub; + + device_remove_file(&pdev->dev, &dev_attr_timeout); + device_remove_file(&pdev->dev, &dev_attr_state); + m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_MOTION_DETECTED); + m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_MOTION_DETECTED); + m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_STILL_DETECTED); + m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_STILL_DETECTED); + wake_lock_destroy(&stillmode_client_data->wakelock); + input_unregister_device(stillmode_client_data->input_dev); + platform_set_drvdata(pdev, NULL); + m4sensorhub->pdev->stillmode_exit = NULL; + stillmode_client_data->m4sensorhub = NULL; + kfree(stillmode_client_data); + g_stillmode_data = NULL; + + return 0; +} + +static void stillmode_client_shutdown(struct platform_device *pdev) +{ + return; +} +#ifdef CONFIG_PM +static int stillmode_client_suspend(struct platform_device *pdev, + pm_message_t message) +{ + return 0; +} + +static int stillmode_client_resume(struct platform_device *pdev) +{ + return 0; +} +#else +#define stillmode_client_suspend NULL +#define stillmode_client_resume NULL +#endif + + +static struct of_device_id m4stillmode_match_tbl[] = { + { .compatible = "mot,m4stillmode" }, + {}, +}; + +static struct platform_driver stillmode_client_driver = { + .probe = stillmode_client_probe, + .remove = __exit_p(stillmode_client_remove), + .shutdown = stillmode_client_shutdown, + .suspend = stillmode_client_suspend, + .resume = stillmode_client_resume, + .driver = { + .name = STILLMODE_CLIENT_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(m4stillmode_match_tbl), + }, +}; + +static int __init stillmode_client_init(void) +{ + return platform_driver_register(&stillmode_client_driver); +} + +static void __exit stillmode_client_exit(void) +{ + platform_driver_unregister(&stillmode_client_driver); +} + +module_init(stillmode_client_init); +module_exit(stillmode_client_exit); + +MODULE_ALIAS("platform:stillmode_client"); +MODULE_DESCRIPTION("M4 sensorhub still mode client driver"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); + |