xref: /openbmc/linux/drivers/input/misc/sgi_btns.c (revision aede7a1e)
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  */
7aede7a1eSDmitry 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)52aede7a1eSDmitry Torokhov static void handle_buttons(struct input_dev *input)
5348ad88b1SThomas Bogendoerfer {
54aede7a1eSDmitry 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 
88aede7a1eSDmitry Torokhov 	input = devm_input_allocate_device(&pdev->dev);
89aede7a1eSDmitry Torokhov 	if (!input)
909e085dd0SDmitry Torokhov 		return -ENOMEM;
9148ad88b1SThomas Bogendoerfer 
9248ad88b1SThomas Bogendoerfer 	memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap));
9348ad88b1SThomas Bogendoerfer 
94aede7a1eSDmitry 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 
110aede7a1eSDmitry Torokhov 	error = input_setup_polling(input, handle_buttons);
111aede7a1eSDmitry Torokhov 	if (error)
112aede7a1eSDmitry Torokhov 		return error;
113aede7a1eSDmitry Torokhov 
114aede7a1eSDmitry Torokhov 	input_set_poll_interval(input, BUTTONS_POLL_INTERVAL);
115aede7a1eSDmitry Torokhov 
116aede7a1eSDmitry 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