13bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer/* 248ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer * SGI Volume Button interface driver 33bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * 43bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * Copyright (C) 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de> 53bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * 63bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * This program is free software; you can redistribute it and/or modify 73bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * it under the terms of the GNU General Public License as published by 83bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * the Free Software Foundation; either version 2 of the License, or 93bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * (at your option) any later version. 103bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * 113bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * This program is distributed in the hope that it will be useful, 123bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * but WITHOUT ANY WARRANTY; without even the implied warranty of 133bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 143bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * GNU General Public License for more details. 153bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * 163bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * You should have received a copy of the GNU General Public License 173bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * along with this program; if not, write to the Free Software 183bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 193bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer */ 203bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer#include <linux/init.h> 213bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer#include <linux/input-polldev.h> 223bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer#include <linux/ioport.h> 233bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer#include <linux/module.h> 243bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer#include <linux/platform_device.h> 255a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 263bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 2748ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer#ifdef CONFIG_SGI_IP22 2848ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer#include <asm/sgi/ioc.h> 2948ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer 3048ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerferstatic inline u8 button_status(void) 3148ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer{ 3248ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer u8 status; 3348ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer 3448ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer status = readb(&sgioc->panel) ^ 0xa0; 3548ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer return ((status & 0x80) >> 6) | ((status & 0x20) >> 5); 3648ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer} 3748ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer#endif 3848ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer 3948ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer#ifdef CONFIG_SGI_IP32 403bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer#include <asm/ip32/mace.h> 413bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 4248ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerferstatic inline u8 button_status(void) 4348ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer{ 4448ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer u64 status; 4548ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer 4648ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer status = readq(&mace->perif.audio.control); 4748ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer writeq(status & ~(3U << 23), &mace->perif.audio.control); 4848ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer 4948ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer return (status >> 23) & 3; 5048ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer} 5148ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer#endif 5248ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer 533bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer#define BUTTONS_POLL_INTERVAL 30 /* msec */ 543bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer#define BUTTONS_COUNT_THRESHOLD 3 553bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 5648ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerferstatic const unsigned short sgi_map[] = { 5748ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer KEY_VOLUMEDOWN, 5848ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer KEY_VOLUMEUP 593bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer}; 603bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 613bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerferstruct buttons_dev { 623bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer struct input_polled_dev *poll_dev; 6348ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer unsigned short keymap[ARRAY_SIZE(sgi_map)]; 6448ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer int count[ARRAY_SIZE(sgi_map)]; 653bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer}; 663bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 673bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerferstatic void handle_buttons(struct input_polled_dev *dev) 683bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer{ 693bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer struct buttons_dev *bdev = dev->private; 703bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer struct input_dev *input = dev->input; 7148ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer u8 status; 723bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer int i; 733bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 7448ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer status = button_status(); 753bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 763bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) { 773bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer if (status & (1U << i)) { 783bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) { 793bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input_event(input, EV_MSC, MSC_SCAN, i); 803bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input_report_key(input, bdev->keymap[i], 1); 813bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input_sync(input); 823bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer } 833bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer } else { 843bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) { 853bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input_event(input, EV_MSC, MSC_SCAN, i); 863bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input_report_key(input, bdev->keymap[i], 0); 873bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input_sync(input); 883bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer } 893bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer bdev->count[i] = 0; 903bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer } 913bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer } 923bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer} 933bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 9448ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerferstatic int __devinit sgi_buttons_probe(struct platform_device *pdev) 953bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer{ 963bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer struct buttons_dev *bdev; 973bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer struct input_polled_dev *poll_dev; 983bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer struct input_dev *input; 993bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer int error, i; 1003bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1013bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL); 1023bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer poll_dev = input_allocate_polled_device(); 1033bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer if (!bdev || !poll_dev) { 1043bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer error = -ENOMEM; 1053bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer goto err_free_mem; 1063bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer } 1073bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 10848ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap)); 1093bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1103bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer poll_dev->private = bdev; 1113bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer poll_dev->poll = handle_buttons; 1123bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer poll_dev->poll_interval = BUTTONS_POLL_INTERVAL; 1133bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1143bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input = poll_dev->input; 11548ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer input->name = "SGI buttons"; 11648ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer input->phys = "sgi/input0"; 1173bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input->id.bustype = BUS_HOST; 1183bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input->dev.parent = &pdev->dev; 1193bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1203bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input->keycode = bdev->keymap; 1213bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input->keycodemax = ARRAY_SIZE(bdev->keymap); 1223bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input->keycodesize = sizeof(unsigned short); 1233bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1243bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input_set_capability(input, EV_MSC, MSC_SCAN); 1253bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer __set_bit(EV_KEY, input->evbit); 12648ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer for (i = 0; i < ARRAY_SIZE(sgi_map); i++) 1273bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer __set_bit(bdev->keymap[i], input->keybit); 1283bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer __clear_bit(KEY_RESERVED, input->keybit); 1293bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1303bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer bdev->poll_dev = poll_dev; 1313bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer dev_set_drvdata(&pdev->dev, bdev); 1323bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1333bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer error = input_register_polled_device(poll_dev); 1343bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer if (error) 1353bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer goto err_free_mem; 1363bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1373bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer return 0; 1383bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1393bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer err_free_mem: 1403bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input_free_polled_device(poll_dev); 1413bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer kfree(bdev); 1423bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer dev_set_drvdata(&pdev->dev, NULL); 1433bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer return error; 1443bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer} 1453bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 14648ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerferstatic int __devexit sgi_buttons_remove(struct platform_device *pdev) 1473bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer{ 1483bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer struct device *dev = &pdev->dev; 1493bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer struct buttons_dev *bdev = dev_get_drvdata(dev); 1503bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1513bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input_unregister_polled_device(bdev->poll_dev); 1523bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer input_free_polled_device(bdev->poll_dev); 1533bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer kfree(bdev); 1543bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer dev_set_drvdata(dev, NULL); 1553bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1563bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer return 0; 1573bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer} 1583bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 15948ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerferstatic struct platform_driver sgi_buttons_driver = { 16048ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer .probe = sgi_buttons_probe, 16148ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer .remove = __devexit_p(sgi_buttons_remove), 1623bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer .driver = { 16348ad88b1f2caf87e3d02c34e1d7de2ce68370e27Thomas Bogendoerfer .name = "sgibtns", 1643bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer .owner = THIS_MODULE, 1653bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer }, 1663bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer}; 167840a746be2beddd2ada0e5ba772147316d071f25JJ Dingmodule_platform_driver(sgi_buttons_driver); 1683bee2a04cf14f599e094a37445f2eb4e6bb316bcThomas Bogendoerfer 1694c2bdcdc62e7a07bd0786fd2048e4ac97ae74e6eDmitri VorobievMODULE_LICENSE("GPL"); 170