1 /** 2 * max8925_onkey.c - MAX8925 ONKEY driver 3 * 4 * Copyright (C) 2009 Marvell International Ltd. 5 * Haojian Zhuang <haojian.zhuang@marvell.com> 6 * 7 * This file is subject to the terms and conditions of the GNU General 8 * Public License. See the file "COPYING" in the main directory of this 9 * archive for more details. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 #include <linux/kernel.h> 22 #include <linux/module.h> 23 #include <linux/platform_device.h> 24 #include <linux/i2c.h> 25 #include <linux/input.h> 26 #include <linux/interrupt.h> 27 #include <linux/mfd/max8925.h> 28 #include <linux/slab.h> 29 30 #define HARDRESET_EN (1 << 7) 31 #define PWREN_EN (1 << 7) 32 33 struct max8925_onkey_info { 34 struct input_dev *idev; 35 struct i2c_client *i2c; 36 int irq; 37 }; 38 39 /* 40 * MAX8925 gives us an interrupt when ONKEY is held for 3 seconds. 41 * max8925_set_bits() operates I2C bus and may sleep. So implement 42 * it in thread IRQ handler. 43 */ 44 static irqreturn_t max8925_onkey_handler(int irq, void *data) 45 { 46 struct max8925_onkey_info *info = data; 47 48 input_report_key(info->idev, KEY_POWER, 1); 49 input_sync(info->idev); 50 51 /* Enable hardreset to halt if system isn't shutdown on time */ 52 max8925_set_bits(info->i2c, MAX8925_SYSENSEL, 53 HARDRESET_EN, HARDRESET_EN); 54 55 return IRQ_HANDLED; 56 } 57 58 static int __devinit max8925_onkey_probe(struct platform_device *pdev) 59 { 60 struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); 61 struct max8925_onkey_info *info; 62 int error; 63 64 info = kzalloc(sizeof(struct max8925_onkey_info), GFP_KERNEL); 65 if (!info) 66 return -ENOMEM; 67 68 info->i2c = chip->i2c; 69 info->irq = chip->irq_base + MAX8925_IRQ_GPM_SW_3SEC; 70 71 info->idev = input_allocate_device(); 72 if (!info->idev) { 73 dev_err(chip->dev, "Failed to allocate input dev\n"); 74 error = -ENOMEM; 75 goto out_input; 76 } 77 78 info->idev->name = "max8925_on"; 79 info->idev->phys = "max8925_on/input0"; 80 info->idev->id.bustype = BUS_I2C; 81 info->idev->dev.parent = &pdev->dev; 82 info->idev->evbit[0] = BIT_MASK(EV_KEY); 83 info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); 84 85 error = request_threaded_irq(info->irq, NULL, max8925_onkey_handler, 86 IRQF_ONESHOT, "onkey", info); 87 if (error < 0) { 88 dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", 89 info->irq, error); 90 goto out_irq; 91 } 92 93 error = input_register_device(info->idev); 94 if (error) { 95 dev_err(chip->dev, "Can't register input device: %d\n", error); 96 goto out; 97 } 98 99 platform_set_drvdata(pdev, info); 100 101 return 0; 102 103 out: 104 free_irq(info->irq, info); 105 out_irq: 106 input_free_device(info->idev); 107 out_input: 108 kfree(info); 109 return error; 110 } 111 112 static int __devexit max8925_onkey_remove(struct platform_device *pdev) 113 { 114 struct max8925_onkey_info *info = platform_get_drvdata(pdev); 115 116 free_irq(info->irq, info); 117 input_unregister_device(info->idev); 118 kfree(info); 119 120 platform_set_drvdata(pdev, NULL); 121 122 return 0; 123 } 124 125 static struct platform_driver max8925_onkey_driver = { 126 .driver = { 127 .name = "max8925-onkey", 128 .owner = THIS_MODULE, 129 }, 130 .probe = max8925_onkey_probe, 131 .remove = __devexit_p(max8925_onkey_remove), 132 }; 133 134 static int __init max8925_onkey_init(void) 135 { 136 return platform_driver_register(&max8925_onkey_driver); 137 } 138 module_init(max8925_onkey_init); 139 140 static void __exit max8925_onkey_exit(void) 141 { 142 platform_driver_unregister(&max8925_onkey_driver); 143 } 144 module_exit(max8925_onkey_exit); 145 146 MODULE_DESCRIPTION("Maxim MAX8925 ONKEY driver"); 147 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); 148 MODULE_LICENSE("GPL"); 149