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 17 static 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 } 24 static DEVICE_ATTR_RO(switch); 25 26 static 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 33 static 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 43 static 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 87 err_irq: 88 free_irq(irq, pdev); 89 err: 90 kfree(psw); 91 return ret; 92 } 93 94 static 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 timer_shutdown_sync(&psw->debounce); 105 flush_work(&psw->work); 106 free_irq(irq, pdev); 107 108 kfree(psw); 109 110 return 0; 111 } 112 113 static struct platform_driver switch_driver = { 114 .probe = switch_drv_probe, 115 .remove = switch_drv_remove, 116 .driver = { 117 .name = DRV_NAME, 118 }, 119 }; 120 121 static 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 127 static void __exit switch_exit(void) 128 { 129 platform_driver_unregister(&switch_driver); 130 } 131 module_init(switch_init); 132 module_exit(switch_exit); 133 134 MODULE_VERSION(DRV_VERSION); 135 MODULE_AUTHOR("Paul Mundt"); 136 MODULE_LICENSE("GPL v2"); 137