116216333SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2bebb8a2bSYoichi Yuasa /*
3bebb8a2bSYoichi Yuasa * Cobalt button interface driver.
4bebb8a2bSYoichi Yuasa *
5ada8e951SYoichi Yuasa * Copyright (C) 2007-2008 Yoichi Yuasa <yuasa@linux-mips.org>
6bebb8a2bSYoichi Yuasa */
7*5d96738dSDmitry Torokhov #include <linux/input.h>
84d69ca90SDmitry Torokhov #include <linux/io.h>
9bebb8a2bSYoichi Yuasa #include <linux/ioport.h>
10bebb8a2bSYoichi Yuasa #include <linux/module.h>
11bebb8a2bSYoichi Yuasa #include <linux/platform_device.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
13bebb8a2bSYoichi Yuasa
14bebb8a2bSYoichi Yuasa #define BUTTONS_POLL_INTERVAL 30 /* msec */
15bebb8a2bSYoichi Yuasa #define BUTTONS_COUNT_THRESHOLD 3
16bebb8a2bSYoichi Yuasa #define BUTTONS_STATUS_MASK 0xfe000000
17bebb8a2bSYoichi Yuasa
18b037b08eSDmitry Torokhov static const unsigned short cobalt_map[] = {
19b037b08eSDmitry Torokhov KEY_RESERVED,
20b037b08eSDmitry Torokhov KEY_RESTART,
21b037b08eSDmitry Torokhov KEY_LEFT,
22b037b08eSDmitry Torokhov KEY_UP,
23b037b08eSDmitry Torokhov KEY_DOWN,
24b037b08eSDmitry Torokhov KEY_RIGHT,
25b037b08eSDmitry Torokhov KEY_ENTER,
26b037b08eSDmitry Torokhov KEY_SELECT
27b037b08eSDmitry Torokhov };
28b037b08eSDmitry Torokhov
29bebb8a2bSYoichi Yuasa struct buttons_dev {
30b037b08eSDmitry Torokhov unsigned short keymap[ARRAY_SIZE(cobalt_map)];
31b037b08eSDmitry Torokhov int count[ARRAY_SIZE(cobalt_map)];
32bebb8a2bSYoichi Yuasa void __iomem *reg;
33bebb8a2bSYoichi Yuasa };
34bebb8a2bSYoichi Yuasa
handle_buttons(struct input_dev * input)35*5d96738dSDmitry Torokhov static void handle_buttons(struct input_dev *input)
36bebb8a2bSYoichi Yuasa {
37*5d96738dSDmitry Torokhov struct buttons_dev *bdev = input_get_drvdata(input);
38bebb8a2bSYoichi Yuasa uint32_t status;
39bebb8a2bSYoichi Yuasa int i;
40bebb8a2bSYoichi Yuasa
41b037b08eSDmitry Torokhov status = ~readl(bdev->reg) >> 24;
42bebb8a2bSYoichi Yuasa
43b037b08eSDmitry Torokhov for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
443c514387SYoichi Yuasa if (status & (1U << i)) {
45b037b08eSDmitry Torokhov if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
46b037b08eSDmitry Torokhov input_event(input, EV_MSC, MSC_SCAN, i);
47b037b08eSDmitry Torokhov input_report_key(input, bdev->keymap[i], 1);
48b037b08eSDmitry Torokhov input_sync(input);
49b037b08eSDmitry Torokhov }
50bebb8a2bSYoichi Yuasa } else {
51b037b08eSDmitry Torokhov if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
52b037b08eSDmitry Torokhov input_event(input, EV_MSC, MSC_SCAN, i);
53b037b08eSDmitry Torokhov input_report_key(input, bdev->keymap[i], 0);
543d29cdffSDmitry Torokhov input_sync(input);
55bebb8a2bSYoichi Yuasa }
56b037b08eSDmitry Torokhov bdev->count[i] = 0;
57bebb8a2bSYoichi Yuasa }
58bebb8a2bSYoichi Yuasa }
59bebb8a2bSYoichi Yuasa }
60bebb8a2bSYoichi Yuasa
cobalt_buttons_probe(struct platform_device * pdev)615298cc4cSBill Pemberton static int cobalt_buttons_probe(struct platform_device *pdev)
62bebb8a2bSYoichi Yuasa {
63bebb8a2bSYoichi Yuasa struct buttons_dev *bdev;
64bebb8a2bSYoichi Yuasa struct input_dev *input;
65bebb8a2bSYoichi Yuasa struct resource *res;
66bebb8a2bSYoichi Yuasa int error, i;
67bebb8a2bSYoichi Yuasa
684d69ca90SDmitry Torokhov bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL);
694d69ca90SDmitry Torokhov if (!bdev)
704d69ca90SDmitry Torokhov return -ENOMEM;
714d69ca90SDmitry Torokhov
724d69ca90SDmitry Torokhov res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
734d69ca90SDmitry Torokhov if (!res)
744d69ca90SDmitry Torokhov return -EBUSY;
754d69ca90SDmitry Torokhov
764d69ca90SDmitry Torokhov bdev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
774d69ca90SDmitry Torokhov if (!bdev->reg)
784d69ca90SDmitry Torokhov return -ENOMEM;
79bebb8a2bSYoichi Yuasa
80b037b08eSDmitry Torokhov memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap));
81b037b08eSDmitry Torokhov
82*5d96738dSDmitry Torokhov input = devm_input_allocate_device(&pdev->dev);
83*5d96738dSDmitry Torokhov if (!input)
844d69ca90SDmitry Torokhov return -ENOMEM;
854d69ca90SDmitry Torokhov
86*5d96738dSDmitry Torokhov input_set_drvdata(input, bdev);
873d29cdffSDmitry Torokhov
88bebb8a2bSYoichi Yuasa input->name = "Cobalt buttons";
89bebb8a2bSYoichi Yuasa input->phys = "cobalt/input0";
90bebb8a2bSYoichi Yuasa input->id.bustype = BUS_HOST;
91bebb8a2bSYoichi Yuasa
923c514387SYoichi Yuasa input->keycode = bdev->keymap;
933c514387SYoichi Yuasa input->keycodemax = ARRAY_SIZE(bdev->keymap);
94b037b08eSDmitry Torokhov input->keycodesize = sizeof(unsigned short);
95b037b08eSDmitry Torokhov
96b037b08eSDmitry Torokhov input_set_capability(input, EV_MSC, MSC_SCAN);
97b037b08eSDmitry Torokhov __set_bit(EV_KEY, input->evbit);
983c514387SYoichi Yuasa for (i = 0; i < ARRAY_SIZE(cobalt_map); i++)
993c514387SYoichi Yuasa __set_bit(bdev->keymap[i], input->keybit);
100b037b08eSDmitry Torokhov __clear_bit(KEY_RESERVED, input->keybit);
101bebb8a2bSYoichi Yuasa
102*5d96738dSDmitry Torokhov
103*5d96738dSDmitry Torokhov error = input_setup_polling(input, handle_buttons);
104*5d96738dSDmitry Torokhov if (error)
105*5d96738dSDmitry Torokhov return error;
106*5d96738dSDmitry Torokhov
107*5d96738dSDmitry Torokhov input_set_poll_interval(input, BUTTONS_POLL_INTERVAL);
108*5d96738dSDmitry Torokhov
109*5d96738dSDmitry Torokhov error = input_register_device(input);
110bebb8a2bSYoichi Yuasa if (error)
111bebb8a2bSYoichi Yuasa return error;
112bebb8a2bSYoichi Yuasa
113bebb8a2bSYoichi Yuasa return 0;
114bebb8a2bSYoichi Yuasa }
115bebb8a2bSYoichi Yuasa
116ada8e951SYoichi Yuasa MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
11726135ed3SMartin Michlmayr MODULE_DESCRIPTION("Cobalt button interface driver");
11826135ed3SMartin Michlmayr MODULE_LICENSE("GPL");
119d7b5247bSKay Sievers /* work with hotplug and coldplug */
120d7b5247bSKay Sievers MODULE_ALIAS("platform:Cobalt buttons");
121d7b5247bSKay Sievers
122bebb8a2bSYoichi Yuasa static struct platform_driver cobalt_buttons_driver = {
123bebb8a2bSYoichi Yuasa .probe = cobalt_buttons_probe,
124bebb8a2bSYoichi Yuasa .driver = {
125bebb8a2bSYoichi Yuasa .name = "Cobalt buttons",
126bebb8a2bSYoichi Yuasa },
127bebb8a2bSYoichi Yuasa };
128840a746bSJJ Ding module_platform_driver(cobalt_buttons_driver);
129