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