Merge tag 'for-linus-20190524' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / drivers / input / misc / cobalt_btns.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  Cobalt button interface driver.
4  *
5  *  Copyright (C) 2007-2008  Yoichi Yuasa <yuasa@linux-mips.org>
6  */
7 #include <linux/input-polldev.h>
8 #include <linux/ioport.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/slab.h>
12
13 #define BUTTONS_POLL_INTERVAL   30      /* msec */
14 #define BUTTONS_COUNT_THRESHOLD 3
15 #define BUTTONS_STATUS_MASK     0xfe000000
16
17 static const unsigned short cobalt_map[] = {
18         KEY_RESERVED,
19         KEY_RESTART,
20         KEY_LEFT,
21         KEY_UP,
22         KEY_DOWN,
23         KEY_RIGHT,
24         KEY_ENTER,
25         KEY_SELECT
26 };
27
28 struct buttons_dev {
29         struct input_polled_dev *poll_dev;
30         unsigned short keymap[ARRAY_SIZE(cobalt_map)];
31         int count[ARRAY_SIZE(cobalt_map)];
32         void __iomem *reg;
33 };
34
35 static void handle_buttons(struct input_polled_dev *dev)
36 {
37         struct buttons_dev *bdev = dev->private;
38         struct input_dev *input = dev->input;
39         uint32_t status;
40         int i;
41
42         status = ~readl(bdev->reg) >> 24;
43
44         for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
45                 if (status & (1U << i)) {
46                         if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
47                                 input_event(input, EV_MSC, MSC_SCAN, i);
48                                 input_report_key(input, bdev->keymap[i], 1);
49                                 input_sync(input);
50                         }
51                 } else {
52                         if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
53                                 input_event(input, EV_MSC, MSC_SCAN, i);
54                                 input_report_key(input, bdev->keymap[i], 0);
55                                 input_sync(input);
56                         }
57                         bdev->count[i] = 0;
58                 }
59         }
60 }
61
62 static int cobalt_buttons_probe(struct platform_device *pdev)
63 {
64         struct buttons_dev *bdev;
65         struct input_polled_dev *poll_dev;
66         struct input_dev *input;
67         struct resource *res;
68         int error, i;
69
70         bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
71         poll_dev = input_allocate_polled_device();
72         if (!bdev || !poll_dev) {
73                 error = -ENOMEM;
74                 goto err_free_mem;
75         }
76
77         memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap));
78
79         poll_dev->private = bdev;
80         poll_dev->poll = handle_buttons;
81         poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
82
83         input = poll_dev->input;
84         input->name = "Cobalt buttons";
85         input->phys = "cobalt/input0";
86         input->id.bustype = BUS_HOST;
87         input->dev.parent = &pdev->dev;
88
89         input->keycode = bdev->keymap;
90         input->keycodemax = ARRAY_SIZE(bdev->keymap);
91         input->keycodesize = sizeof(unsigned short);
92
93         input_set_capability(input, EV_MSC, MSC_SCAN);
94         __set_bit(EV_KEY, input->evbit);
95         for (i = 0; i < ARRAY_SIZE(cobalt_map); i++)
96                 __set_bit(bdev->keymap[i], input->keybit);
97         __clear_bit(KEY_RESERVED, input->keybit);
98
99         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
100         if (!res) {
101                 error = -EBUSY;
102                 goto err_free_mem;
103         }
104
105         bdev->poll_dev = poll_dev;
106         bdev->reg = ioremap(res->start, resource_size(res));
107         dev_set_drvdata(&pdev->dev, bdev);
108
109         error = input_register_polled_device(poll_dev);
110         if (error)
111                 goto err_iounmap;
112
113         return 0;
114
115  err_iounmap:
116         iounmap(bdev->reg);
117  err_free_mem:
118         input_free_polled_device(poll_dev);
119         kfree(bdev);
120         return error;
121 }
122
123 static int cobalt_buttons_remove(struct platform_device *pdev)
124 {
125         struct device *dev = &pdev->dev;
126         struct buttons_dev *bdev = dev_get_drvdata(dev);
127
128         input_unregister_polled_device(bdev->poll_dev);
129         input_free_polled_device(bdev->poll_dev);
130         iounmap(bdev->reg);
131         kfree(bdev);
132
133         return 0;
134 }
135
136 MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
137 MODULE_DESCRIPTION("Cobalt button interface driver");
138 MODULE_LICENSE("GPL");
139 /* work with hotplug and coldplug */
140 MODULE_ALIAS("platform:Cobalt buttons");
141
142 static struct platform_driver cobalt_buttons_driver = {
143         .probe  = cobalt_buttons_probe,
144         .remove = cobalt_buttons_remove,
145         .driver = {
146                 .name   = "Cobalt buttons",
147         },
148 };
149 module_platform_driver(cobalt_buttons_driver);