push-switch.c (2973B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Generic push-switch framework 4 * 5 * Copyright (C) 2006 Paul Mundt 6 */ 7#include <linux/init.h> 8#include <linux/slab.h> 9#include <linux/module.h> 10#include <linux/interrupt.h> 11#include <linux/platform_device.h> 12#include <asm/push-switch.h> 13 14#define DRV_NAME "push-switch" 15#define DRV_VERSION "0.1.1" 16 17static ssize_t switch_show(struct device *dev, 18 struct device_attribute *attr, 19 char *buf) 20{ 21 struct push_switch_platform_info *psw_info = dev->platform_data; 22 return sprintf(buf, "%s\n", psw_info->name); 23} 24static DEVICE_ATTR_RO(switch); 25 26static void switch_timer(struct timer_list *t) 27{ 28 struct push_switch *psw = from_timer(psw, t, debounce); 29 30 schedule_work(&psw->work); 31} 32 33static void switch_work_handler(struct work_struct *work) 34{ 35 struct push_switch *psw = container_of(work, struct push_switch, work); 36 struct platform_device *pdev = psw->pdev; 37 38 psw->state = 0; 39 40 kobject_uevent(&pdev->dev.kobj, KOBJ_CHANGE); 41} 42 43static int switch_drv_probe(struct platform_device *pdev) 44{ 45 struct push_switch_platform_info *psw_info; 46 struct push_switch *psw; 47 int ret, irq; 48 49 psw = kzalloc(sizeof(struct push_switch), GFP_KERNEL); 50 if (unlikely(!psw)) 51 return -ENOMEM; 52 53 irq = platform_get_irq(pdev, 0); 54 if (unlikely(irq < 0)) { 55 ret = -ENODEV; 56 goto err; 57 } 58 59 psw_info = pdev->dev.platform_data; 60 BUG_ON(!psw_info); 61 62 ret = request_irq(irq, psw_info->irq_handler, 63 psw_info->irq_flags, 64 psw_info->name ? psw_info->name : DRV_NAME, pdev); 65 if (unlikely(ret < 0)) 66 goto err; 67 68 if (psw_info->name) { 69 ret = device_create_file(&pdev->dev, &dev_attr_switch); 70 if (unlikely(ret)) { 71 dev_err(&pdev->dev, "Failed creating device attrs\n"); 72 ret = -EINVAL; 73 goto err_irq; 74 } 75 } 76 77 INIT_WORK(&psw->work, switch_work_handler); 78 timer_setup(&psw->debounce, switch_timer, 0); 79 80 /* Workqueue API brain-damage */ 81 psw->pdev = pdev; 82 83 platform_set_drvdata(pdev, psw); 84 85 return 0; 86 87err_irq: 88 free_irq(irq, pdev); 89err: 90 kfree(psw); 91 return ret; 92} 93 94static int switch_drv_remove(struct platform_device *pdev) 95{ 96 struct push_switch *psw = platform_get_drvdata(pdev); 97 struct push_switch_platform_info *psw_info = pdev->dev.platform_data; 98 int irq = platform_get_irq(pdev, 0); 99 100 if (psw_info->name) 101 device_remove_file(&pdev->dev, &dev_attr_switch); 102 103 platform_set_drvdata(pdev, NULL); 104 flush_work(&psw->work); 105 del_timer_sync(&psw->debounce); 106 free_irq(irq, pdev); 107 108 kfree(psw); 109 110 return 0; 111} 112 113static struct platform_driver switch_driver = { 114 .probe = switch_drv_probe, 115 .remove = switch_drv_remove, 116 .driver = { 117 .name = DRV_NAME, 118 }, 119}; 120 121static int __init switch_init(void) 122{ 123 printk(KERN_NOTICE DRV_NAME ": version %s loaded\n", DRV_VERSION); 124 return platform_driver_register(&switch_driver); 125} 126 127static void __exit switch_exit(void) 128{ 129 platform_driver_unregister(&switch_driver); 130} 131module_init(switch_init); 132module_exit(switch_exit); 133 134MODULE_VERSION(DRV_VERSION); 135MODULE_AUTHOR("Paul Mundt"); 136MODULE_LICENSE("GPL v2");