148ad88b1SThomas Bogendoerfer /* 248ad88b1SThomas Bogendoerfer * SGI Volume Button interface driver 348ad88b1SThomas Bogendoerfer * 448ad88b1SThomas Bogendoerfer * Copyright (C) 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de> 548ad88b1SThomas Bogendoerfer * 648ad88b1SThomas Bogendoerfer * This program is free software; you can redistribute it and/or modify 748ad88b1SThomas Bogendoerfer * it under the terms of the GNU General Public License as published by 848ad88b1SThomas Bogendoerfer * the Free Software Foundation; either version 2 of the License, or 948ad88b1SThomas Bogendoerfer * (at your option) any later version. 1048ad88b1SThomas Bogendoerfer * 1148ad88b1SThomas Bogendoerfer * This program is distributed in the hope that it will be useful, 1248ad88b1SThomas Bogendoerfer * but WITHOUT ANY WARRANTY; without even the implied warranty of 1348ad88b1SThomas Bogendoerfer * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1448ad88b1SThomas Bogendoerfer * GNU General Public License for more details. 1548ad88b1SThomas Bogendoerfer * 1648ad88b1SThomas Bogendoerfer * You should have received a copy of the GNU General Public License 1748ad88b1SThomas Bogendoerfer * along with this program; if not, write to the Free Software 1848ad88b1SThomas Bogendoerfer * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 1948ad88b1SThomas Bogendoerfer */ 2048ad88b1SThomas Bogendoerfer #include <linux/init.h> 2148ad88b1SThomas Bogendoerfer #include <linux/input-polldev.h> 2248ad88b1SThomas Bogendoerfer #include <linux/ioport.h> 2348ad88b1SThomas Bogendoerfer #include <linux/module.h> 2448ad88b1SThomas Bogendoerfer #include <linux/platform_device.h> 2548ad88b1SThomas Bogendoerfer 2648ad88b1SThomas Bogendoerfer #ifdef CONFIG_SGI_IP22 2748ad88b1SThomas Bogendoerfer #include <asm/sgi/ioc.h> 2848ad88b1SThomas Bogendoerfer 2948ad88b1SThomas Bogendoerfer static inline u8 button_status(void) 3048ad88b1SThomas Bogendoerfer { 3148ad88b1SThomas Bogendoerfer u8 status; 3248ad88b1SThomas Bogendoerfer 3348ad88b1SThomas Bogendoerfer status = readb(&sgioc->panel) ^ 0xa0; 3448ad88b1SThomas Bogendoerfer return ((status & 0x80) >> 6) | ((status & 0x20) >> 5); 3548ad88b1SThomas Bogendoerfer } 3648ad88b1SThomas Bogendoerfer #endif 3748ad88b1SThomas Bogendoerfer 3848ad88b1SThomas Bogendoerfer #ifdef CONFIG_SGI_IP32 3948ad88b1SThomas Bogendoerfer #include <asm/ip32/mace.h> 4048ad88b1SThomas Bogendoerfer 4148ad88b1SThomas Bogendoerfer static inline u8 button_status(void) 4248ad88b1SThomas Bogendoerfer { 4348ad88b1SThomas Bogendoerfer u64 status; 4448ad88b1SThomas Bogendoerfer 4548ad88b1SThomas Bogendoerfer status = readq(&mace->perif.audio.control); 4648ad88b1SThomas Bogendoerfer writeq(status & ~(3U << 23), &mace->perif.audio.control); 4748ad88b1SThomas Bogendoerfer 4848ad88b1SThomas Bogendoerfer return (status >> 23) & 3; 4948ad88b1SThomas Bogendoerfer } 5048ad88b1SThomas Bogendoerfer #endif 5148ad88b1SThomas Bogendoerfer 5248ad88b1SThomas Bogendoerfer #define BUTTONS_POLL_INTERVAL 30 /* msec */ 5348ad88b1SThomas Bogendoerfer #define BUTTONS_COUNT_THRESHOLD 3 5448ad88b1SThomas Bogendoerfer 5548ad88b1SThomas Bogendoerfer static const unsigned short sgi_map[] = { 5648ad88b1SThomas Bogendoerfer KEY_VOLUMEDOWN, 5748ad88b1SThomas Bogendoerfer KEY_VOLUMEUP 5848ad88b1SThomas Bogendoerfer }; 5948ad88b1SThomas Bogendoerfer 6048ad88b1SThomas Bogendoerfer struct buttons_dev { 6148ad88b1SThomas Bogendoerfer struct input_polled_dev *poll_dev; 6248ad88b1SThomas Bogendoerfer unsigned short keymap[ARRAY_SIZE(sgi_map)]; 6348ad88b1SThomas Bogendoerfer int count[ARRAY_SIZE(sgi_map)]; 6448ad88b1SThomas Bogendoerfer }; 6548ad88b1SThomas Bogendoerfer 6648ad88b1SThomas Bogendoerfer static void handle_buttons(struct input_polled_dev *dev) 6748ad88b1SThomas Bogendoerfer { 6848ad88b1SThomas Bogendoerfer struct buttons_dev *bdev = dev->private; 6948ad88b1SThomas Bogendoerfer struct input_dev *input = dev->input; 7048ad88b1SThomas Bogendoerfer u8 status; 7148ad88b1SThomas Bogendoerfer int i; 7248ad88b1SThomas Bogendoerfer 7348ad88b1SThomas Bogendoerfer status = button_status(); 7448ad88b1SThomas Bogendoerfer 7548ad88b1SThomas Bogendoerfer for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) { 7648ad88b1SThomas Bogendoerfer if (status & (1U << i)) { 7748ad88b1SThomas Bogendoerfer if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) { 7848ad88b1SThomas Bogendoerfer input_event(input, EV_MSC, MSC_SCAN, i); 7948ad88b1SThomas Bogendoerfer input_report_key(input, bdev->keymap[i], 1); 8048ad88b1SThomas Bogendoerfer input_sync(input); 8148ad88b1SThomas Bogendoerfer } 8248ad88b1SThomas Bogendoerfer } else { 8348ad88b1SThomas Bogendoerfer if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) { 8448ad88b1SThomas Bogendoerfer input_event(input, EV_MSC, MSC_SCAN, i); 8548ad88b1SThomas Bogendoerfer input_report_key(input, bdev->keymap[i], 0); 8648ad88b1SThomas Bogendoerfer input_sync(input); 8748ad88b1SThomas Bogendoerfer } 8848ad88b1SThomas Bogendoerfer bdev->count[i] = 0; 8948ad88b1SThomas Bogendoerfer } 9048ad88b1SThomas Bogendoerfer } 9148ad88b1SThomas Bogendoerfer } 9248ad88b1SThomas Bogendoerfer 9348ad88b1SThomas Bogendoerfer static int __devinit sgi_buttons_probe(struct platform_device *pdev) 9448ad88b1SThomas Bogendoerfer { 9548ad88b1SThomas Bogendoerfer struct buttons_dev *bdev; 9648ad88b1SThomas Bogendoerfer struct input_polled_dev *poll_dev; 9748ad88b1SThomas Bogendoerfer struct input_dev *input; 9848ad88b1SThomas Bogendoerfer int error, i; 9948ad88b1SThomas Bogendoerfer 10048ad88b1SThomas Bogendoerfer bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL); 10148ad88b1SThomas Bogendoerfer poll_dev = input_allocate_polled_device(); 10248ad88b1SThomas Bogendoerfer if (!bdev || !poll_dev) { 10348ad88b1SThomas Bogendoerfer error = -ENOMEM; 10448ad88b1SThomas Bogendoerfer goto err_free_mem; 10548ad88b1SThomas Bogendoerfer } 10648ad88b1SThomas Bogendoerfer 10748ad88b1SThomas Bogendoerfer memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap)); 10848ad88b1SThomas Bogendoerfer 10948ad88b1SThomas Bogendoerfer poll_dev->private = bdev; 11048ad88b1SThomas Bogendoerfer poll_dev->poll = handle_buttons; 11148ad88b1SThomas Bogendoerfer poll_dev->poll_interval = BUTTONS_POLL_INTERVAL; 11248ad88b1SThomas Bogendoerfer 11348ad88b1SThomas Bogendoerfer input = poll_dev->input; 11448ad88b1SThomas Bogendoerfer input->name = "SGI buttons"; 11548ad88b1SThomas Bogendoerfer input->phys = "sgi/input0"; 11648ad88b1SThomas Bogendoerfer input->id.bustype = BUS_HOST; 11748ad88b1SThomas Bogendoerfer input->dev.parent = &pdev->dev; 11848ad88b1SThomas Bogendoerfer 11948ad88b1SThomas Bogendoerfer input->keycode = bdev->keymap; 12048ad88b1SThomas Bogendoerfer input->keycodemax = ARRAY_SIZE(bdev->keymap); 12148ad88b1SThomas Bogendoerfer input->keycodesize = sizeof(unsigned short); 12248ad88b1SThomas Bogendoerfer 12348ad88b1SThomas Bogendoerfer input_set_capability(input, EV_MSC, MSC_SCAN); 12448ad88b1SThomas Bogendoerfer __set_bit(EV_KEY, input->evbit); 12548ad88b1SThomas Bogendoerfer for (i = 0; i < ARRAY_SIZE(sgi_map); i++) 12648ad88b1SThomas Bogendoerfer __set_bit(bdev->keymap[i], input->keybit); 12748ad88b1SThomas Bogendoerfer __clear_bit(KEY_RESERVED, input->keybit); 12848ad88b1SThomas Bogendoerfer 12948ad88b1SThomas Bogendoerfer bdev->poll_dev = poll_dev; 13048ad88b1SThomas Bogendoerfer dev_set_drvdata(&pdev->dev, bdev); 13148ad88b1SThomas Bogendoerfer 13248ad88b1SThomas Bogendoerfer error = input_register_polled_device(poll_dev); 13348ad88b1SThomas Bogendoerfer if (error) 13448ad88b1SThomas Bogendoerfer goto err_free_mem; 13548ad88b1SThomas Bogendoerfer 13648ad88b1SThomas Bogendoerfer return 0; 13748ad88b1SThomas Bogendoerfer 13848ad88b1SThomas Bogendoerfer err_free_mem: 13948ad88b1SThomas Bogendoerfer input_free_polled_device(poll_dev); 14048ad88b1SThomas Bogendoerfer kfree(bdev); 14148ad88b1SThomas Bogendoerfer dev_set_drvdata(&pdev->dev, NULL); 14248ad88b1SThomas Bogendoerfer return error; 14348ad88b1SThomas Bogendoerfer } 14448ad88b1SThomas Bogendoerfer 14548ad88b1SThomas Bogendoerfer static int __devexit sgi_buttons_remove(struct platform_device *pdev) 14648ad88b1SThomas Bogendoerfer { 14748ad88b1SThomas Bogendoerfer struct device *dev = &pdev->dev; 14848ad88b1SThomas Bogendoerfer struct buttons_dev *bdev = dev_get_drvdata(dev); 14948ad88b1SThomas Bogendoerfer 15048ad88b1SThomas Bogendoerfer input_unregister_polled_device(bdev->poll_dev); 15148ad88b1SThomas Bogendoerfer input_free_polled_device(bdev->poll_dev); 15248ad88b1SThomas Bogendoerfer kfree(bdev); 15348ad88b1SThomas Bogendoerfer dev_set_drvdata(dev, NULL); 15448ad88b1SThomas Bogendoerfer 15548ad88b1SThomas Bogendoerfer return 0; 15648ad88b1SThomas Bogendoerfer } 15748ad88b1SThomas Bogendoerfer 15848ad88b1SThomas Bogendoerfer static struct platform_driver sgi_buttons_driver = { 15948ad88b1SThomas Bogendoerfer .probe = sgi_buttons_probe, 16048ad88b1SThomas Bogendoerfer .remove = __devexit_p(sgi_buttons_remove), 16148ad88b1SThomas Bogendoerfer .driver = { 16248ad88b1SThomas Bogendoerfer .name = "sgibtns", 16348ad88b1SThomas Bogendoerfer .owner = THIS_MODULE, 16448ad88b1SThomas Bogendoerfer }, 16548ad88b1SThomas Bogendoerfer }; 16648ad88b1SThomas Bogendoerfer 16748ad88b1SThomas Bogendoerfer static int __init sgi_buttons_init(void) 16848ad88b1SThomas Bogendoerfer { 16948ad88b1SThomas Bogendoerfer return platform_driver_register(&sgi_buttons_driver); 17048ad88b1SThomas Bogendoerfer } 17148ad88b1SThomas Bogendoerfer 17248ad88b1SThomas Bogendoerfer static void __exit sgi_buttons_exit(void) 17348ad88b1SThomas Bogendoerfer { 17448ad88b1SThomas Bogendoerfer platform_driver_unregister(&sgi_buttons_driver); 17548ad88b1SThomas Bogendoerfer } 17648ad88b1SThomas Bogendoerfer 17748ad88b1SThomas Bogendoerfer module_init(sgi_buttons_init); 17848ad88b1SThomas Bogendoerfer module_exit(sgi_buttons_exit); 179