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