xref: /openbmc/linux/drivers/input/misc/sgi_btns.c (revision 9e085dd0)
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  */
748ad88b1SThomas Bogendoerfer #include <linux/input-polldev.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 
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 
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 
5248ad88b1SThomas Bogendoerfer static void handle_buttons(struct input_polled_dev *dev)
5348ad88b1SThomas Bogendoerfer {
5448ad88b1SThomas Bogendoerfer 	struct buttons_dev *bdev = dev->private;
5548ad88b1SThomas Bogendoerfer 	struct input_dev *input = dev->input;
5648ad88b1SThomas Bogendoerfer 	u8 status;
5748ad88b1SThomas Bogendoerfer 	int i;
5848ad88b1SThomas Bogendoerfer 
5948ad88b1SThomas Bogendoerfer 	status = button_status();
6048ad88b1SThomas Bogendoerfer 
6148ad88b1SThomas Bogendoerfer 	for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
6248ad88b1SThomas Bogendoerfer 		if (status & (1U << i)) {
6348ad88b1SThomas Bogendoerfer 			if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
6448ad88b1SThomas Bogendoerfer 				input_event(input, EV_MSC, MSC_SCAN, i);
6548ad88b1SThomas Bogendoerfer 				input_report_key(input, bdev->keymap[i], 1);
6648ad88b1SThomas Bogendoerfer 				input_sync(input);
6748ad88b1SThomas Bogendoerfer 			}
6848ad88b1SThomas Bogendoerfer 		} else {
6948ad88b1SThomas Bogendoerfer 			if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
7048ad88b1SThomas Bogendoerfer 				input_event(input, EV_MSC, MSC_SCAN, i);
7148ad88b1SThomas Bogendoerfer 				input_report_key(input, bdev->keymap[i], 0);
7248ad88b1SThomas Bogendoerfer 				input_sync(input);
7348ad88b1SThomas Bogendoerfer 			}
7448ad88b1SThomas Bogendoerfer 			bdev->count[i] = 0;
7548ad88b1SThomas Bogendoerfer 		}
7648ad88b1SThomas Bogendoerfer 	}
7748ad88b1SThomas Bogendoerfer }
7848ad88b1SThomas Bogendoerfer 
795298cc4cSBill Pemberton static int sgi_buttons_probe(struct platform_device *pdev)
8048ad88b1SThomas Bogendoerfer {
8148ad88b1SThomas Bogendoerfer 	struct buttons_dev *bdev;
8248ad88b1SThomas Bogendoerfer 	struct input_polled_dev *poll_dev;
8348ad88b1SThomas Bogendoerfer 	struct input_dev *input;
8448ad88b1SThomas Bogendoerfer 	int error, i;
8548ad88b1SThomas Bogendoerfer 
869e085dd0SDmitry Torokhov 	bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL);
879e085dd0SDmitry Torokhov 	if (!bdev)
889e085dd0SDmitry Torokhov 		return -ENOMEM;
899e085dd0SDmitry Torokhov 
909e085dd0SDmitry Torokhov 	poll_dev = devm_input_allocate_polled_device(&pdev->dev);
919e085dd0SDmitry Torokhov 	if (!poll_dev)
929e085dd0SDmitry Torokhov 		return -ENOMEM;
9348ad88b1SThomas Bogendoerfer 
9448ad88b1SThomas Bogendoerfer 	memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap));
9548ad88b1SThomas Bogendoerfer 
9648ad88b1SThomas Bogendoerfer 	poll_dev->private = bdev;
9748ad88b1SThomas Bogendoerfer 	poll_dev->poll = handle_buttons;
9848ad88b1SThomas Bogendoerfer 	poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
9948ad88b1SThomas Bogendoerfer 
10048ad88b1SThomas Bogendoerfer 	input = poll_dev->input;
10148ad88b1SThomas Bogendoerfer 	input->name = "SGI buttons";
10248ad88b1SThomas Bogendoerfer 	input->phys = "sgi/input0";
10348ad88b1SThomas Bogendoerfer 	input->id.bustype = BUS_HOST;
10448ad88b1SThomas Bogendoerfer 
10548ad88b1SThomas Bogendoerfer 	input->keycode = bdev->keymap;
10648ad88b1SThomas Bogendoerfer 	input->keycodemax = ARRAY_SIZE(bdev->keymap);
10748ad88b1SThomas Bogendoerfer 	input->keycodesize = sizeof(unsigned short);
10848ad88b1SThomas Bogendoerfer 
10948ad88b1SThomas Bogendoerfer 	input_set_capability(input, EV_MSC, MSC_SCAN);
11048ad88b1SThomas Bogendoerfer 	__set_bit(EV_KEY, input->evbit);
11148ad88b1SThomas Bogendoerfer 	for (i = 0; i < ARRAY_SIZE(sgi_map); i++)
11248ad88b1SThomas Bogendoerfer 		__set_bit(bdev->keymap[i], input->keybit);
11348ad88b1SThomas Bogendoerfer 	__clear_bit(KEY_RESERVED, input->keybit);
11448ad88b1SThomas Bogendoerfer 
11548ad88b1SThomas Bogendoerfer 	error = input_register_polled_device(poll_dev);
11648ad88b1SThomas Bogendoerfer 	if (error)
11748ad88b1SThomas Bogendoerfer 		return error;
11848ad88b1SThomas Bogendoerfer 
11948ad88b1SThomas Bogendoerfer 	return 0;
12048ad88b1SThomas Bogendoerfer }
12148ad88b1SThomas Bogendoerfer 
12248ad88b1SThomas Bogendoerfer static struct platform_driver sgi_buttons_driver = {
12348ad88b1SThomas Bogendoerfer 	.probe	= sgi_buttons_probe,
12448ad88b1SThomas Bogendoerfer 	.driver	= {
12548ad88b1SThomas Bogendoerfer 		.name	= "sgibtns",
12648ad88b1SThomas Bogendoerfer 	},
12748ad88b1SThomas Bogendoerfer };
128840a746bSJJ Ding module_platform_driver(sgi_buttons_driver);
12948ad88b1SThomas Bogendoerfer 
1304c2bdcdcSDmitri Vorobiev MODULE_LICENSE("GPL");
131