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.h> |
8 | #include <linux/io.h> |
9 | #include <linux/ioport.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #define BUTTONS_POLL_INTERVAL 30 /* msec */ |
15 | #define BUTTONS_COUNT_THRESHOLD 3 |
16 | #define BUTTONS_STATUS_MASK 0xfe000000 |
17 | |
18 | static const unsigned short cobalt_map[] = { |
19 | KEY_RESERVED, |
20 | KEY_RESTART, |
21 | KEY_LEFT, |
22 | KEY_UP, |
23 | KEY_DOWN, |
24 | KEY_RIGHT, |
25 | KEY_ENTER, |
26 | KEY_SELECT |
27 | }; |
28 | |
29 | struct buttons_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_dev *input) |
36 | { |
37 | struct buttons_dev *bdev = input_get_drvdata(dev: input); |
38 | uint32_t status; |
39 | int i; |
40 | |
41 | status = ~readl(addr: bdev->reg) >> 24; |
42 | |
43 | for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) { |
44 | if (status & (1U << i)) { |
45 | if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) { |
46 | input_event(dev: input, EV_MSC, MSC_SCAN, value: i); |
47 | input_report_key(dev: input, code: bdev->keymap[i], value: 1); |
48 | input_sync(dev: input); |
49 | } |
50 | } else { |
51 | if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) { |
52 | input_event(dev: input, EV_MSC, MSC_SCAN, value: i); |
53 | input_report_key(dev: input, code: bdev->keymap[i], value: 0); |
54 | input_sync(dev: input); |
55 | } |
56 | bdev->count[i] = 0; |
57 | } |
58 | } |
59 | } |
60 | |
61 | static int cobalt_buttons_probe(struct platform_device *pdev) |
62 | { |
63 | struct buttons_dev *bdev; |
64 | struct input_dev *input; |
65 | struct resource *res; |
66 | int error, i; |
67 | |
68 | bdev = devm_kzalloc(dev: &pdev->dev, size: sizeof(*bdev), GFP_KERNEL); |
69 | if (!bdev) |
70 | return -ENOMEM; |
71 | |
72 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
73 | if (!res) |
74 | return -EBUSY; |
75 | |
76 | bdev->reg = devm_ioremap(dev: &pdev->dev, offset: res->start, size: resource_size(res)); |
77 | if (!bdev->reg) |
78 | return -ENOMEM; |
79 | |
80 | memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap)); |
81 | |
82 | input = devm_input_allocate_device(&pdev->dev); |
83 | if (!input) |
84 | return -ENOMEM; |
85 | |
86 | input_set_drvdata(dev: input, data: bdev); |
87 | |
88 | input->name = "Cobalt buttons" ; |
89 | input->phys = "cobalt/input0" ; |
90 | input->id.bustype = BUS_HOST; |
91 | |
92 | input->keycode = bdev->keymap; |
93 | input->keycodemax = ARRAY_SIZE(bdev->keymap); |
94 | input->keycodesize = sizeof(unsigned short); |
95 | |
96 | input_set_capability(dev: input, EV_MSC, MSC_SCAN); |
97 | __set_bit(EV_KEY, input->evbit); |
98 | for (i = 0; i < ARRAY_SIZE(cobalt_map); i++) |
99 | __set_bit(bdev->keymap[i], input->keybit); |
100 | __clear_bit(KEY_RESERVED, input->keybit); |
101 | |
102 | |
103 | error = input_setup_polling(dev: input, poll_fn: handle_buttons); |
104 | if (error) |
105 | return error; |
106 | |
107 | input_set_poll_interval(dev: input, BUTTONS_POLL_INTERVAL); |
108 | |
109 | error = input_register_device(input); |
110 | if (error) |
111 | return error; |
112 | |
113 | return 0; |
114 | } |
115 | |
116 | MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>" ); |
117 | MODULE_DESCRIPTION("Cobalt button interface driver" ); |
118 | MODULE_LICENSE("GPL" ); |
119 | /* work with hotplug and coldplug */ |
120 | MODULE_ALIAS("platform:Cobalt buttons" ); |
121 | |
122 | static struct platform_driver cobalt_buttons_driver = { |
123 | .probe = cobalt_buttons_probe, |
124 | .driver = { |
125 | .name = "Cobalt buttons" , |
126 | }, |
127 | }; |
128 | module_platform_driver(cobalt_buttons_driver); |
129 | |