diff options
Diffstat (limited to 'drivers/input/misc/stmp3xxx_rotdec.c')
-rw-r--r-- | drivers/input/misc/stmp3xxx_rotdec.c | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/drivers/input/misc/stmp3xxx_rotdec.c b/drivers/input/misc/stmp3xxx_rotdec.c new file mode 100644 index 000000000000..621db95dba8c --- /dev/null +++ b/drivers/input/misc/stmp3xxx_rotdec.c @@ -0,0 +1,174 @@ +/* + * Freescale STMP3XXX Rotary Encoder Driver + * + * Author: Drew Benedetti <drewb@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/input-polldev.h> +#include <mach/regs-timrot.h> +#include <mach/rotdec.h> +#include <mach/platform.h> + +static int relative; +static unsigned int poll_interval = 500; + +void stmp3xxx_rotdec_flush(struct input_polled_dev *dev) +{ + /* in relative mode, reading the counter resets it */ + if (relative) + __raw_readl(REGS_TIMROT_BASE + HW_TIMROT_ROTCOUNT); +} + +void stmp3xxx_rotdec_poll(struct input_polled_dev *dev) +{ + s16 cnt = __raw_readl(REGS_TIMROT_BASE + HW_TIMROT_ROTCOUNT) & BM_TIMROT_ROTCOUNT_UPDOWN; + if (relative) + input_report_rel(dev->input, REL_WHEEL, cnt); + else + input_report_abs(dev->input, ABS_WHEEL, cnt); +} + +struct input_polled_dev *rotdec; +static u32 rotctrl; + +static int stmp3xxx_rotdec_probe(struct platform_device *pdev) +{ + int rc = 0; + + /* save original state of HW_TIMROT_ROTCTRL */ + rotctrl = __raw_readl(REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL); + + if (!(rotctrl & BM_TIMROT_ROTCTRL_ROTARY_PRESENT)) { + dev_info(&pdev->dev, "No rotary decoder present\n"); + rc = -ENODEV; + goto err_rotdec_present; + } else { + /* I had to add some extra line breaks in here + * to avoid lines >80 chars wide + */ + __raw_writel( + BF(0x0, TIMROT_ROTCTRL_DIVIDER) | /* 32kHz divider - 1 */ + BF(BV_TIMROT_ROTCTRL_OVERSAMPLE__2X, + TIMROT_ROTCTRL_OVERSAMPLE) | + BF(BV_TIMROT_ROTCTRL_SELECT_B__ROTARYB, + TIMROT_ROTCTRL_SELECT_B) | + BF(BV_TIMROT_ROTCTRL_SELECT_A__ROTARYA, + TIMROT_ROTCTRL_SELECT_A) + , REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL); + __raw_writel( + BM_TIMROT_ROTCTRL_POLARITY_B | + BM_TIMROT_ROTCTRL_POLARITY_A + , REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL_CLR); + + if (relative) + __raw_writel(BM_TIMROT_ROTCTRL_RELATIVE, + REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL_SET); + else + __raw_writel(BM_TIMROT_ROTCTRL_RELATIVE, + REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL_CLR); + + rc = rotdec_pinmux_request(); + if (rc) { + dev_err(&pdev->dev, + "Pin request failed (err=%d)\n", rc); + goto err_pinmux; + } + + /* set up input_polled_dev */ + rotdec = input_allocate_polled_device(); + if (!rotdec) { + dev_err(&pdev->dev, + "Unable to allocate polled device\n"); + rc = -ENOMEM; + goto err_alloc_polldev; + } + rotdec->flush = stmp3xxx_rotdec_flush; + rotdec->poll = stmp3xxx_rotdec_poll; + rotdec->poll_interval = poll_interval; /* msec */ + + rotdec->input->name = "stmp3xxx-rotdec"; + if (relative) + input_set_capability(rotdec->input, EV_REL, REL_WHEEL); + else { + input_set_capability(rotdec->input, EV_ABS, ABS_WHEEL); + input_set_abs_params(rotdec->input, ABS_WHEEL, + -32768, 32767, 0, 0); + } + + rc = input_register_polled_device(rotdec); + if (rc) { + dev_err(&pdev->dev, + "Unable to register rotary decoder (err=%d)\n", + rc); + goto err_reg_polldev; + } + } + + return 0; + +err_reg_polldev: + input_free_polled_device(rotdec); +err_alloc_polldev: + rotdec_pinmux_free(); +err_pinmux: + /* restore original register state */ + __raw_writel(rotctrl, REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL); + +err_rotdec_present: + return rc; +} + +static int stmp3xxx_rotdec_remove(struct platform_device *pdev) +{ + input_unregister_polled_device(rotdec); + input_free_polled_device(rotdec); + + rotdec_pinmux_free(); + + /* restore original register state */ + __raw_writel(rotctrl, REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL); + + return 0; +} + +static struct platform_driver stmp3xxx_rotdec_driver = { + .probe = stmp3xxx_rotdec_probe, + .remove = stmp3xxx_rotdec_remove, + .driver = { + .name = "stmp3xxx-rotdec", + }, +}; + +static int __init stmp3xxx_rotdec_init(void) +{ + return platform_driver_register(&stmp3xxx_rotdec_driver); +} + +static void __exit stmp3xxx_rotdec_exit(void) +{ + platform_driver_unregister(&stmp3xxx_rotdec_driver); +} + +module_init(stmp3xxx_rotdec_init); +module_exit(stmp3xxx_rotdec_exit); + +module_param(relative, bool, 0600); +module_param(poll_interval, uint, 0600); + +MODULE_AUTHOR("Drew Benedetti <drewb@embeddedalley.com>"); +MODULE_DESCRIPTION("STMP3xxx rotary decoder driver"); +MODULE_LICENSE("GPL"); |