116216333SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2bebb8a2bSYoichi Yuasa /* 3bebb8a2bSYoichi Yuasa * Cobalt button interface driver. 4bebb8a2bSYoichi Yuasa * 5ada8e951SYoichi Yuasa * Copyright (C) 2007-2008 Yoichi Yuasa <yuasa@linux-mips.org> 6bebb8a2bSYoichi Yuasa */ 73d29cdffSDmitry Torokhov #include <linux/input-polldev.h> 84d69ca90SDmitry Torokhov #include <linux/io.h> 9bebb8a2bSYoichi Yuasa #include <linux/ioport.h> 10bebb8a2bSYoichi Yuasa #include <linux/module.h> 11bebb8a2bSYoichi Yuasa #include <linux/platform_device.h> 125a0e3ad6STejun Heo #include <linux/slab.h> 13bebb8a2bSYoichi Yuasa 14bebb8a2bSYoichi Yuasa #define BUTTONS_POLL_INTERVAL 30 /* msec */ 15bebb8a2bSYoichi Yuasa #define BUTTONS_COUNT_THRESHOLD 3 16bebb8a2bSYoichi Yuasa #define BUTTONS_STATUS_MASK 0xfe000000 17bebb8a2bSYoichi Yuasa 18b037b08eSDmitry Torokhov static const unsigned short cobalt_map[] = { 19b037b08eSDmitry Torokhov KEY_RESERVED, 20b037b08eSDmitry Torokhov KEY_RESTART, 21b037b08eSDmitry Torokhov KEY_LEFT, 22b037b08eSDmitry Torokhov KEY_UP, 23b037b08eSDmitry Torokhov KEY_DOWN, 24b037b08eSDmitry Torokhov KEY_RIGHT, 25b037b08eSDmitry Torokhov KEY_ENTER, 26b037b08eSDmitry Torokhov KEY_SELECT 27b037b08eSDmitry Torokhov }; 28b037b08eSDmitry Torokhov 29bebb8a2bSYoichi Yuasa struct buttons_dev { 30b037b08eSDmitry Torokhov unsigned short keymap[ARRAY_SIZE(cobalt_map)]; 31b037b08eSDmitry Torokhov int count[ARRAY_SIZE(cobalt_map)]; 32bebb8a2bSYoichi Yuasa void __iomem *reg; 33bebb8a2bSYoichi Yuasa }; 34bebb8a2bSYoichi Yuasa 353d29cdffSDmitry Torokhov static void handle_buttons(struct input_polled_dev *dev) 36bebb8a2bSYoichi Yuasa { 373d29cdffSDmitry Torokhov struct buttons_dev *bdev = dev->private; 383d29cdffSDmitry Torokhov struct input_dev *input = dev->input; 39bebb8a2bSYoichi Yuasa uint32_t status; 40bebb8a2bSYoichi Yuasa int i; 41bebb8a2bSYoichi Yuasa 42b037b08eSDmitry Torokhov status = ~readl(bdev->reg) >> 24; 43bebb8a2bSYoichi Yuasa 44b037b08eSDmitry Torokhov for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) { 453c514387SYoichi Yuasa if (status & (1U << i)) { 46b037b08eSDmitry Torokhov if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) { 47b037b08eSDmitry Torokhov input_event(input, EV_MSC, MSC_SCAN, i); 48b037b08eSDmitry Torokhov input_report_key(input, bdev->keymap[i], 1); 49b037b08eSDmitry Torokhov input_sync(input); 50b037b08eSDmitry Torokhov } 51bebb8a2bSYoichi Yuasa } else { 52b037b08eSDmitry Torokhov if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) { 53b037b08eSDmitry Torokhov input_event(input, EV_MSC, MSC_SCAN, i); 54b037b08eSDmitry Torokhov input_report_key(input, bdev->keymap[i], 0); 553d29cdffSDmitry Torokhov input_sync(input); 56bebb8a2bSYoichi Yuasa } 57b037b08eSDmitry Torokhov bdev->count[i] = 0; 58bebb8a2bSYoichi Yuasa } 59bebb8a2bSYoichi Yuasa } 60bebb8a2bSYoichi Yuasa } 61bebb8a2bSYoichi Yuasa 625298cc4cSBill Pemberton static int cobalt_buttons_probe(struct platform_device *pdev) 63bebb8a2bSYoichi Yuasa { 64bebb8a2bSYoichi Yuasa struct buttons_dev *bdev; 653d29cdffSDmitry Torokhov struct input_polled_dev *poll_dev; 66bebb8a2bSYoichi Yuasa struct input_dev *input; 67bebb8a2bSYoichi Yuasa struct resource *res; 68bebb8a2bSYoichi Yuasa int error, i; 69bebb8a2bSYoichi Yuasa 704d69ca90SDmitry Torokhov bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL); 714d69ca90SDmitry Torokhov if (!bdev) 724d69ca90SDmitry Torokhov return -ENOMEM; 734d69ca90SDmitry Torokhov 744d69ca90SDmitry Torokhov res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 754d69ca90SDmitry Torokhov if (!res) 764d69ca90SDmitry Torokhov return -EBUSY; 774d69ca90SDmitry Torokhov 784d69ca90SDmitry Torokhov bdev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res)); 794d69ca90SDmitry Torokhov if (!bdev->reg) 804d69ca90SDmitry Torokhov return -ENOMEM; 81bebb8a2bSYoichi Yuasa 82b037b08eSDmitry Torokhov memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap)); 83b037b08eSDmitry Torokhov 844d69ca90SDmitry Torokhov poll_dev = devm_input_allocate_polled_device(&pdev->dev); 854d69ca90SDmitry Torokhov if (!poll_dev) 864d69ca90SDmitry Torokhov return -ENOMEM; 874d69ca90SDmitry Torokhov 883d29cdffSDmitry Torokhov poll_dev->private = bdev; 893d29cdffSDmitry Torokhov poll_dev->poll = handle_buttons; 903d29cdffSDmitry Torokhov poll_dev->poll_interval = BUTTONS_POLL_INTERVAL; 913d29cdffSDmitry Torokhov 923d29cdffSDmitry Torokhov input = poll_dev->input; 93bebb8a2bSYoichi Yuasa input->name = "Cobalt buttons"; 94bebb8a2bSYoichi Yuasa input->phys = "cobalt/input0"; 95bebb8a2bSYoichi Yuasa input->id.bustype = BUS_HOST; 96bebb8a2bSYoichi Yuasa 973c514387SYoichi Yuasa input->keycode = bdev->keymap; 983c514387SYoichi Yuasa input->keycodemax = ARRAY_SIZE(bdev->keymap); 99b037b08eSDmitry Torokhov input->keycodesize = sizeof(unsigned short); 100b037b08eSDmitry Torokhov 101b037b08eSDmitry Torokhov input_set_capability(input, EV_MSC, MSC_SCAN); 102b037b08eSDmitry Torokhov __set_bit(EV_KEY, input->evbit); 1033c514387SYoichi Yuasa for (i = 0; i < ARRAY_SIZE(cobalt_map); i++) 1043c514387SYoichi Yuasa __set_bit(bdev->keymap[i], input->keybit); 105b037b08eSDmitry Torokhov __clear_bit(KEY_RESERVED, input->keybit); 106bebb8a2bSYoichi Yuasa 1073d29cdffSDmitry Torokhov error = input_register_polled_device(poll_dev); 108bebb8a2bSYoichi Yuasa if (error) 109bebb8a2bSYoichi Yuasa return error; 110bebb8a2bSYoichi Yuasa 111bebb8a2bSYoichi Yuasa return 0; 112bebb8a2bSYoichi Yuasa } 113bebb8a2bSYoichi Yuasa 114ada8e951SYoichi Yuasa MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>"); 11526135ed3SMartin Michlmayr MODULE_DESCRIPTION("Cobalt button interface driver"); 11626135ed3SMartin Michlmayr MODULE_LICENSE("GPL"); 117d7b5247bSKay Sievers /* work with hotplug and coldplug */ 118d7b5247bSKay Sievers MODULE_ALIAS("platform:Cobalt buttons"); 119d7b5247bSKay Sievers 120bebb8a2bSYoichi Yuasa static struct platform_driver cobalt_buttons_driver = { 121bebb8a2bSYoichi Yuasa .probe = cobalt_buttons_probe, 122bebb8a2bSYoichi Yuasa .driver = { 123bebb8a2bSYoichi Yuasa .name = "Cobalt buttons", 124bebb8a2bSYoichi Yuasa }, 125bebb8a2bSYoichi Yuasa }; 126840a746bSJJ Ding module_platform_driver(cobalt_buttons_driver); 127