Path: blob/master/drivers/input/keyboard/pxa930_rotary.c
15111 views
/*1* Driver for the enhanced rotary controller on pxa930 and pxa9352*3* This program is free software; you can redistribute it and/or modify4* it under the terms of the GNU General Public License version 2 as5* published by the Free Software Foundation.6*/78#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/init.h>11#include <linux/interrupt.h>12#include <linux/input.h>13#include <linux/platform_device.h>14#include <linux/io.h>15#include <linux/slab.h>1617#include <mach/pxa930_rotary.h>1819#define SBCR (0x04)20#define ERCR (0x0c)2122#define SBCR_ERSB (1 << 5)2324struct pxa930_rotary {25struct input_dev *input_dev;26void __iomem *mmio_base;27int last_ercr;2829struct pxa930_rotary_platform_data *pdata;30};3132static void clear_sbcr(struct pxa930_rotary *r)33{34uint32_t sbcr = __raw_readl(r->mmio_base + SBCR);3536__raw_writel(sbcr | SBCR_ERSB, r->mmio_base + SBCR);37__raw_writel(sbcr & ~SBCR_ERSB, r->mmio_base + SBCR);38}3940static irqreturn_t rotary_irq(int irq, void *dev_id)41{42struct pxa930_rotary *r = dev_id;43struct pxa930_rotary_platform_data *pdata = r->pdata;44int ercr, delta, key;4546ercr = __raw_readl(r->mmio_base + ERCR) & 0xf;47clear_sbcr(r);4849delta = ercr - r->last_ercr;50if (delta == 0)51return IRQ_HANDLED;5253r->last_ercr = ercr;5455if (pdata->up_key && pdata->down_key) {56key = (delta > 0) ? pdata->up_key : pdata->down_key;57input_report_key(r->input_dev, key, 1);58input_sync(r->input_dev);59input_report_key(r->input_dev, key, 0);60} else61input_report_rel(r->input_dev, pdata->rel_code, delta);6263input_sync(r->input_dev);6465return IRQ_HANDLED;66}6768static int pxa930_rotary_open(struct input_dev *dev)69{70struct pxa930_rotary *r = input_get_drvdata(dev);7172clear_sbcr(r);7374return 0;75}7677static void pxa930_rotary_close(struct input_dev *dev)78{79struct pxa930_rotary *r = input_get_drvdata(dev);8081clear_sbcr(r);82}8384static int __devinit pxa930_rotary_probe(struct platform_device *pdev)85{86struct pxa930_rotary_platform_data *pdata = pdev->dev.platform_data;87struct pxa930_rotary *r;88struct input_dev *input_dev;89struct resource *res;90int irq;91int err;9293irq = platform_get_irq(pdev, 0);94if (irq < 0) {95dev_err(&pdev->dev, "no irq for rotary controller\n");96return -ENXIO;97}9899res = platform_get_resource(pdev, IORESOURCE_MEM, 0);100if (!res) {101dev_err(&pdev->dev, "no I/O memory defined\n");102return -ENXIO;103}104105if (!pdata) {106dev_err(&pdev->dev, "no platform data defined\n");107return -EINVAL;108}109110r = kzalloc(sizeof(struct pxa930_rotary), GFP_KERNEL);111if (!r)112return -ENOMEM;113114r->mmio_base = ioremap_nocache(res->start, resource_size(res));115if (r->mmio_base == NULL) {116dev_err(&pdev->dev, "failed to remap IO memory\n");117err = -ENXIO;118goto failed_free;119}120121r->pdata = pdata;122platform_set_drvdata(pdev, r);123124/* allocate and register the input device */125input_dev = input_allocate_device();126if (!input_dev) {127dev_err(&pdev->dev, "failed to allocate input device\n");128err = -ENOMEM;129goto failed_free_io;130}131132input_dev->name = pdev->name;133input_dev->id.bustype = BUS_HOST;134input_dev->open = pxa930_rotary_open;135input_dev->close = pxa930_rotary_close;136input_dev->dev.parent = &pdev->dev;137138if (pdata->up_key && pdata->down_key) {139__set_bit(pdata->up_key, input_dev->keybit);140__set_bit(pdata->down_key, input_dev->keybit);141__set_bit(EV_KEY, input_dev->evbit);142} else {143__set_bit(pdata->rel_code, input_dev->relbit);144__set_bit(EV_REL, input_dev->evbit);145}146147r->input_dev = input_dev;148input_set_drvdata(input_dev, r);149150err = request_irq(irq, rotary_irq, IRQF_DISABLED,151"enhanced rotary", r);152if (err) {153dev_err(&pdev->dev, "failed to request IRQ\n");154goto failed_free_input;155}156157err = input_register_device(input_dev);158if (err) {159dev_err(&pdev->dev, "failed to register input device\n");160goto failed_free_irq;161}162163return 0;164165failed_free_irq:166free_irq(irq, r);167failed_free_input:168input_free_device(input_dev);169failed_free_io:170iounmap(r->mmio_base);171failed_free:172kfree(r);173return err;174}175176static int __devexit pxa930_rotary_remove(struct platform_device *pdev)177{178struct pxa930_rotary *r = platform_get_drvdata(pdev);179180free_irq(platform_get_irq(pdev, 0), r);181input_unregister_device(r->input_dev);182iounmap(r->mmio_base);183platform_set_drvdata(pdev, NULL);184kfree(r);185186return 0;187}188189static struct platform_driver pxa930_rotary_driver = {190.driver = {191.name = "pxa930-rotary",192.owner = THIS_MODULE,193},194.probe = pxa930_rotary_probe,195.remove = __devexit_p(pxa930_rotary_remove),196};197198static int __init pxa930_rotary_init(void)199{200return platform_driver_register(&pxa930_rotary_driver);201}202module_init(pxa930_rotary_init);203204static void __exit pxa930_rotary_exit(void)205{206platform_driver_unregister(&pxa930_rotary_driver);207}208module_exit(pxa930_rotary_exit);209210MODULE_LICENSE("GPL");211MODULE_DESCRIPTION("Driver for PXA93x Enhanced Rotary Controller");212MODULE_AUTHOR("Yao Yong <[email protected]>");213214215