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