1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * SGI Volume Button interface driver |
4 | * |
5 | * Copyright (C) 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de> |
6 | */ |
7 | #include <linux/input.h> |
8 | #include <linux/ioport.h> |
9 | #include <linux/module.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/slab.h> |
12 | |
13 | #ifdef CONFIG_SGI_IP22 |
14 | #include <asm/sgi/ioc.h> |
15 | |
16 | static inline u8 button_status(void) |
17 | { |
18 | u8 status; |
19 | |
20 | status = readb(&sgioc->panel) ^ 0xa0; |
21 | return ((status & 0x80) >> 6) | ((status & 0x20) >> 5); |
22 | } |
23 | #endif |
24 | |
25 | #ifdef CONFIG_SGI_IP32 |
26 | #include <asm/ip32/mace.h> |
27 | |
28 | static inline u8 button_status(void) |
29 | { |
30 | u64 status; |
31 | |
32 | status = readq(&mace->perif.audio.control); |
33 | writeq(status & ~(3U << 23), &mace->perif.audio.control); |
34 | |
35 | return (status >> 23) & 3; |
36 | } |
37 | #endif |
38 | |
39 | #define BUTTONS_POLL_INTERVAL 30 /* msec */ |
40 | #define BUTTONS_COUNT_THRESHOLD 3 |
41 | |
42 | static const unsigned short sgi_map[] = { |
43 | KEY_VOLUMEDOWN, |
44 | KEY_VOLUMEUP |
45 | }; |
46 | |
47 | struct buttons_dev { |
48 | unsigned short keymap[ARRAY_SIZE(sgi_map)]; |
49 | int count[ARRAY_SIZE(sgi_map)]; |
50 | }; |
51 | |
52 | static void handle_buttons(struct input_dev *input) |
53 | { |
54 | struct buttons_dev *bdev = input_get_drvdata(dev: input); |
55 | u8 status; |
56 | int i; |
57 | |
58 | status = button_status(); |
59 | |
60 | for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) { |
61 | if (status & (1U << i)) { |
62 | if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) { |
63 | input_event(dev: input, EV_MSC, MSC_SCAN, value: i); |
64 | input_report_key(dev: input, code: bdev->keymap[i], value: 1); |
65 | input_sync(dev: input); |
66 | } |
67 | } else { |
68 | if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) { |
69 | input_event(dev: input, EV_MSC, MSC_SCAN, value: i); |
70 | input_report_key(dev: input, code: bdev->keymap[i], value: 0); |
71 | input_sync(dev: input); |
72 | } |
73 | bdev->count[i] = 0; |
74 | } |
75 | } |
76 | } |
77 | |
78 | static int sgi_buttons_probe(struct platform_device *pdev) |
79 | { |
80 | struct buttons_dev *bdev; |
81 | struct input_dev *input; |
82 | int error, i; |
83 | |
84 | bdev = devm_kzalloc(dev: &pdev->dev, size: sizeof(*bdev), GFP_KERNEL); |
85 | if (!bdev) |
86 | return -ENOMEM; |
87 | |
88 | input = devm_input_allocate_device(&pdev->dev); |
89 | if (!input) |
90 | return -ENOMEM; |
91 | |
92 | memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap)); |
93 | |
94 | input_set_drvdata(dev: input, data: bdev); |
95 | |
96 | input->name = "SGI buttons" ; |
97 | input->phys = "sgi/input0" ; |
98 | input->id.bustype = BUS_HOST; |
99 | |
100 | input->keycode = bdev->keymap; |
101 | input->keycodemax = ARRAY_SIZE(bdev->keymap); |
102 | input->keycodesize = sizeof(unsigned short); |
103 | |
104 | input_set_capability(dev: input, EV_MSC, MSC_SCAN); |
105 | __set_bit(EV_KEY, input->evbit); |
106 | for (i = 0; i < ARRAY_SIZE(sgi_map); i++) |
107 | __set_bit(bdev->keymap[i], input->keybit); |
108 | __clear_bit(KEY_RESERVED, input->keybit); |
109 | |
110 | error = input_setup_polling(dev: input, poll_fn: handle_buttons); |
111 | if (error) |
112 | return error; |
113 | |
114 | input_set_poll_interval(dev: input, BUTTONS_POLL_INTERVAL); |
115 | |
116 | error = input_register_device(input); |
117 | if (error) |
118 | return error; |
119 | |
120 | return 0; |
121 | } |
122 | |
123 | static struct platform_driver sgi_buttons_driver = { |
124 | .probe = sgi_buttons_probe, |
125 | .driver = { |
126 | .name = "sgibtns" , |
127 | }, |
128 | }; |
129 | module_platform_driver(sgi_buttons_driver); |
130 | |
131 | MODULE_LICENSE("GPL" ); |
132 | |