116216333SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
248ad88b1SThomas Bogendoerfer /*
348ad88b1SThomas Bogendoerfer * SGI Volume Button interface driver
448ad88b1SThomas Bogendoerfer *
548ad88b1SThomas Bogendoerfer * Copyright (C) 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
648ad88b1SThomas Bogendoerfer */
7*aede7a1eSDmitry Torokhov #include <linux/input.h>
848ad88b1SThomas Bogendoerfer #include <linux/ioport.h>
948ad88b1SThomas Bogendoerfer #include <linux/module.h>
1048ad88b1SThomas Bogendoerfer #include <linux/platform_device.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
1248ad88b1SThomas Bogendoerfer
1348ad88b1SThomas Bogendoerfer #ifdef CONFIG_SGI_IP22
1448ad88b1SThomas Bogendoerfer #include <asm/sgi/ioc.h>
1548ad88b1SThomas Bogendoerfer
button_status(void)1648ad88b1SThomas Bogendoerfer static inline u8 button_status(void)
1748ad88b1SThomas Bogendoerfer {
1848ad88b1SThomas Bogendoerfer u8 status;
1948ad88b1SThomas Bogendoerfer
2048ad88b1SThomas Bogendoerfer status = readb(&sgioc->panel) ^ 0xa0;
2148ad88b1SThomas Bogendoerfer return ((status & 0x80) >> 6) | ((status & 0x20) >> 5);
2248ad88b1SThomas Bogendoerfer }
2348ad88b1SThomas Bogendoerfer #endif
2448ad88b1SThomas Bogendoerfer
2548ad88b1SThomas Bogendoerfer #ifdef CONFIG_SGI_IP32
2648ad88b1SThomas Bogendoerfer #include <asm/ip32/mace.h>
2748ad88b1SThomas Bogendoerfer
button_status(void)2848ad88b1SThomas Bogendoerfer static inline u8 button_status(void)
2948ad88b1SThomas Bogendoerfer {
3048ad88b1SThomas Bogendoerfer u64 status;
3148ad88b1SThomas Bogendoerfer
3248ad88b1SThomas Bogendoerfer status = readq(&mace->perif.audio.control);
3348ad88b1SThomas Bogendoerfer writeq(status & ~(3U << 23), &mace->perif.audio.control);
3448ad88b1SThomas Bogendoerfer
3548ad88b1SThomas Bogendoerfer return (status >> 23) & 3;
3648ad88b1SThomas Bogendoerfer }
3748ad88b1SThomas Bogendoerfer #endif
3848ad88b1SThomas Bogendoerfer
3948ad88b1SThomas Bogendoerfer #define BUTTONS_POLL_INTERVAL 30 /* msec */
4048ad88b1SThomas Bogendoerfer #define BUTTONS_COUNT_THRESHOLD 3
4148ad88b1SThomas Bogendoerfer
4248ad88b1SThomas Bogendoerfer static const unsigned short sgi_map[] = {
4348ad88b1SThomas Bogendoerfer KEY_VOLUMEDOWN,
4448ad88b1SThomas Bogendoerfer KEY_VOLUMEUP
4548ad88b1SThomas Bogendoerfer };
4648ad88b1SThomas Bogendoerfer
4748ad88b1SThomas Bogendoerfer struct buttons_dev {
4848ad88b1SThomas Bogendoerfer unsigned short keymap[ARRAY_SIZE(sgi_map)];
4948ad88b1SThomas Bogendoerfer int count[ARRAY_SIZE(sgi_map)];
5048ad88b1SThomas Bogendoerfer };
5148ad88b1SThomas Bogendoerfer
handle_buttons(struct input_dev * input)52*aede7a1eSDmitry Torokhov static void handle_buttons(struct input_dev *input)
5348ad88b1SThomas Bogendoerfer {
54*aede7a1eSDmitry Torokhov struct buttons_dev *bdev = input_get_drvdata(input);
5548ad88b1SThomas Bogendoerfer u8 status;
5648ad88b1SThomas Bogendoerfer int i;
5748ad88b1SThomas Bogendoerfer
5848ad88b1SThomas Bogendoerfer status = button_status();
5948ad88b1SThomas Bogendoerfer
6048ad88b1SThomas Bogendoerfer for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
6148ad88b1SThomas Bogendoerfer if (status & (1U << i)) {
6248ad88b1SThomas Bogendoerfer if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
6348ad88b1SThomas Bogendoerfer input_event(input, EV_MSC, MSC_SCAN, i);
6448ad88b1SThomas Bogendoerfer input_report_key(input, bdev->keymap[i], 1);
6548ad88b1SThomas Bogendoerfer input_sync(input);
6648ad88b1SThomas Bogendoerfer }
6748ad88b1SThomas Bogendoerfer } else {
6848ad88b1SThomas Bogendoerfer if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
6948ad88b1SThomas Bogendoerfer input_event(input, EV_MSC, MSC_SCAN, i);
7048ad88b1SThomas Bogendoerfer input_report_key(input, bdev->keymap[i], 0);
7148ad88b1SThomas Bogendoerfer input_sync(input);
7248ad88b1SThomas Bogendoerfer }
7348ad88b1SThomas Bogendoerfer bdev->count[i] = 0;
7448ad88b1SThomas Bogendoerfer }
7548ad88b1SThomas Bogendoerfer }
7648ad88b1SThomas Bogendoerfer }
7748ad88b1SThomas Bogendoerfer
sgi_buttons_probe(struct platform_device * pdev)785298cc4cSBill Pemberton static int sgi_buttons_probe(struct platform_device *pdev)
7948ad88b1SThomas Bogendoerfer {
8048ad88b1SThomas Bogendoerfer struct buttons_dev *bdev;
8148ad88b1SThomas Bogendoerfer struct input_dev *input;
8248ad88b1SThomas Bogendoerfer int error, i;
8348ad88b1SThomas Bogendoerfer
849e085dd0SDmitry Torokhov bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL);
859e085dd0SDmitry Torokhov if (!bdev)
869e085dd0SDmitry Torokhov return -ENOMEM;
879e085dd0SDmitry Torokhov
88*aede7a1eSDmitry Torokhov input = devm_input_allocate_device(&pdev->dev);
89*aede7a1eSDmitry Torokhov if (!input)
909e085dd0SDmitry Torokhov return -ENOMEM;
9148ad88b1SThomas Bogendoerfer
9248ad88b1SThomas Bogendoerfer memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap));
9348ad88b1SThomas Bogendoerfer
94*aede7a1eSDmitry Torokhov input_set_drvdata(input, bdev);
9548ad88b1SThomas Bogendoerfer
9648ad88b1SThomas Bogendoerfer input->name = "SGI buttons";
9748ad88b1SThomas Bogendoerfer input->phys = "sgi/input0";
9848ad88b1SThomas Bogendoerfer input->id.bustype = BUS_HOST;
9948ad88b1SThomas Bogendoerfer
10048ad88b1SThomas Bogendoerfer input->keycode = bdev->keymap;
10148ad88b1SThomas Bogendoerfer input->keycodemax = ARRAY_SIZE(bdev->keymap);
10248ad88b1SThomas Bogendoerfer input->keycodesize = sizeof(unsigned short);
10348ad88b1SThomas Bogendoerfer
10448ad88b1SThomas Bogendoerfer input_set_capability(input, EV_MSC, MSC_SCAN);
10548ad88b1SThomas Bogendoerfer __set_bit(EV_KEY, input->evbit);
10648ad88b1SThomas Bogendoerfer for (i = 0; i < ARRAY_SIZE(sgi_map); i++)
10748ad88b1SThomas Bogendoerfer __set_bit(bdev->keymap[i], input->keybit);
10848ad88b1SThomas Bogendoerfer __clear_bit(KEY_RESERVED, input->keybit);
10948ad88b1SThomas Bogendoerfer
110*aede7a1eSDmitry Torokhov error = input_setup_polling(input, handle_buttons);
111*aede7a1eSDmitry Torokhov if (error)
112*aede7a1eSDmitry Torokhov return error;
113*aede7a1eSDmitry Torokhov
114*aede7a1eSDmitry Torokhov input_set_poll_interval(input, BUTTONS_POLL_INTERVAL);
115*aede7a1eSDmitry Torokhov
116*aede7a1eSDmitry Torokhov error = input_register_device(input);
11748ad88b1SThomas Bogendoerfer if (error)
11848ad88b1SThomas Bogendoerfer return error;
11948ad88b1SThomas Bogendoerfer
12048ad88b1SThomas Bogendoerfer return 0;
12148ad88b1SThomas Bogendoerfer }
12248ad88b1SThomas Bogendoerfer
12348ad88b1SThomas Bogendoerfer static struct platform_driver sgi_buttons_driver = {
12448ad88b1SThomas Bogendoerfer .probe = sgi_buttons_probe,
12548ad88b1SThomas Bogendoerfer .driver = {
12648ad88b1SThomas Bogendoerfer .name = "sgibtns",
12748ad88b1SThomas Bogendoerfer },
12848ad88b1SThomas Bogendoerfer };
129840a746bSJJ Ding module_platform_driver(sgi_buttons_driver);
13048ad88b1SThomas Bogendoerfer
1314c2bdcdcSDmitri Vorobiev MODULE_LICENSE("GPL");
132