Path: blob/master/drivers/input/misc/bfin_rotary.c
15109 views
/*1* Rotary counter driver for Analog Devices Blackfin Processors2*3* Copyright 2008-2009 Analog Devices Inc.4* Licensed under the GPL-2 or later.5*/67#include <linux/module.h>8#include <linux/version.h>9#include <linux/init.h>10#include <linux/interrupt.h>11#include <linux/irq.h>12#include <linux/pm.h>13#include <linux/platform_device.h>14#include <linux/input.h>15#include <linux/slab.h>1617#include <asm/portmux.h>18#include <asm/bfin_rotary.h>1920static const u16 per_cnt[] = {21P_CNT_CUD,22P_CNT_CDG,23P_CNT_CZM,24025};2627struct bfin_rot {28struct input_dev *input;29int irq;30unsigned int up_key;31unsigned int down_key;32unsigned int button_key;33unsigned int rel_code;34unsigned short cnt_config;35unsigned short cnt_imask;36unsigned short cnt_debounce;37};3839static void report_key_event(struct input_dev *input, int keycode)40{41/* simulate a press-n-release */42input_report_key(input, keycode, 1);43input_sync(input);44input_report_key(input, keycode, 0);45input_sync(input);46}4748static void report_rotary_event(struct bfin_rot *rotary, int delta)49{50struct input_dev *input = rotary->input;5152if (rotary->up_key) {53report_key_event(input,54delta > 0 ? rotary->up_key : rotary->down_key);55} else {56input_report_rel(input, rotary->rel_code, delta);57input_sync(input);58}59}6061static irqreturn_t bfin_rotary_isr(int irq, void *dev_id)62{63struct platform_device *pdev = dev_id;64struct bfin_rot *rotary = platform_get_drvdata(pdev);65int delta;6667switch (bfin_read_CNT_STATUS()) {6869case ICII:70break;7172case UCII:73case DCII:74delta = bfin_read_CNT_COUNTER();75if (delta)76report_rotary_event(rotary, delta);77break;7879case CZMII:80report_key_event(rotary->input, rotary->button_key);81break;8283default:84break;85}8687bfin_write_CNT_COMMAND(W1LCNT_ZERO); /* Clear COUNTER */88bfin_write_CNT_STATUS(-1); /* Clear STATUS */8990return IRQ_HANDLED;91}9293static int __devinit bfin_rotary_probe(struct platform_device *pdev)94{95struct bfin_rotary_platform_data *pdata = pdev->dev.platform_data;96struct bfin_rot *rotary;97struct input_dev *input;98int error;99100/* Basic validation */101if ((pdata->rotary_up_key && !pdata->rotary_down_key) ||102(!pdata->rotary_up_key && pdata->rotary_down_key)) {103return -EINVAL;104}105106error = peripheral_request_list(per_cnt, dev_name(&pdev->dev));107if (error) {108dev_err(&pdev->dev, "requesting peripherals failed\n");109return error;110}111112rotary = kzalloc(sizeof(struct bfin_rot), GFP_KERNEL);113input = input_allocate_device();114if (!rotary || !input) {115error = -ENOMEM;116goto out1;117}118119rotary->input = input;120121rotary->up_key = pdata->rotary_up_key;122rotary->down_key = pdata->rotary_down_key;123rotary->button_key = pdata->rotary_button_key;124rotary->rel_code = pdata->rotary_rel_code;125126error = rotary->irq = platform_get_irq(pdev, 0);127if (error < 0)128goto out1;129130input->name = pdev->name;131input->phys = "bfin-rotary/input0";132input->dev.parent = &pdev->dev;133134input_set_drvdata(input, rotary);135136input->id.bustype = BUS_HOST;137input->id.vendor = 0x0001;138input->id.product = 0x0001;139input->id.version = 0x0100;140141if (rotary->up_key) {142__set_bit(EV_KEY, input->evbit);143__set_bit(rotary->up_key, input->keybit);144__set_bit(rotary->down_key, input->keybit);145} else {146__set_bit(EV_REL, input->evbit);147__set_bit(rotary->rel_code, input->relbit);148}149150if (rotary->button_key) {151__set_bit(EV_KEY, input->evbit);152__set_bit(rotary->button_key, input->keybit);153}154155error = request_irq(rotary->irq, bfin_rotary_isr,1560, dev_name(&pdev->dev), pdev);157if (error) {158dev_err(&pdev->dev,159"unable to claim irq %d; error %d\n",160rotary->irq, error);161goto out1;162}163164error = input_register_device(input);165if (error) {166dev_err(&pdev->dev,167"unable to register input device (%d)\n", error);168goto out2;169}170171if (pdata->rotary_button_key)172bfin_write_CNT_IMASK(CZMIE);173174if (pdata->mode & ROT_DEBE)175bfin_write_CNT_DEBOUNCE(pdata->debounce & DPRESCALE);176177if (pdata->mode)178bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() |179(pdata->mode & ~CNTE));180181bfin_write_CNT_IMASK(bfin_read_CNT_IMASK() | UCIE | DCIE);182bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | CNTE);183184platform_set_drvdata(pdev, rotary);185device_init_wakeup(&pdev->dev, 1);186187return 0;188189out2:190free_irq(rotary->irq, pdev);191out1:192input_free_device(input);193kfree(rotary);194peripheral_free_list(per_cnt);195196return error;197}198199static int __devexit bfin_rotary_remove(struct platform_device *pdev)200{201struct bfin_rot *rotary = platform_get_drvdata(pdev);202203bfin_write_CNT_CONFIG(0);204bfin_write_CNT_IMASK(0);205206free_irq(rotary->irq, pdev);207input_unregister_device(rotary->input);208peripheral_free_list(per_cnt);209210kfree(rotary);211platform_set_drvdata(pdev, NULL);212213return 0;214}215216#ifdef CONFIG_PM217static int bfin_rotary_suspend(struct device *dev)218{219struct platform_device *pdev = to_platform_device(dev);220struct bfin_rot *rotary = platform_get_drvdata(pdev);221222rotary->cnt_config = bfin_read_CNT_CONFIG();223rotary->cnt_imask = bfin_read_CNT_IMASK();224rotary->cnt_debounce = bfin_read_CNT_DEBOUNCE();225226if (device_may_wakeup(&pdev->dev))227enable_irq_wake(rotary->irq);228229return 0;230}231232static int bfin_rotary_resume(struct device *dev)233{234struct platform_device *pdev = to_platform_device(dev);235struct bfin_rot *rotary = platform_get_drvdata(pdev);236237bfin_write_CNT_DEBOUNCE(rotary->cnt_debounce);238bfin_write_CNT_IMASK(rotary->cnt_imask);239bfin_write_CNT_CONFIG(rotary->cnt_config & ~CNTE);240241if (device_may_wakeup(&pdev->dev))242disable_irq_wake(rotary->irq);243244if (rotary->cnt_config & CNTE)245bfin_write_CNT_CONFIG(rotary->cnt_config);246247return 0;248}249250static const struct dev_pm_ops bfin_rotary_pm_ops = {251.suspend = bfin_rotary_suspend,252.resume = bfin_rotary_resume,253};254#endif255256static struct platform_driver bfin_rotary_device_driver = {257.probe = bfin_rotary_probe,258.remove = __devexit_p(bfin_rotary_remove),259.driver = {260.name = "bfin-rotary",261.owner = THIS_MODULE,262#ifdef CONFIG_PM263.pm = &bfin_rotary_pm_ops,264#endif265},266};267268static int __init bfin_rotary_init(void)269{270return platform_driver_register(&bfin_rotary_device_driver);271}272module_init(bfin_rotary_init);273274static void __exit bfin_rotary_exit(void)275{276platform_driver_unregister(&bfin_rotary_device_driver);277}278module_exit(bfin_rotary_exit);279280MODULE_LICENSE("GPL");281MODULE_AUTHOR("Michael Hennerich <[email protected]>");282MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors");283MODULE_ALIAS("platform:bfin-rotary");284285286