13734574cSHaojian Zhuang /** 23734574cSHaojian Zhuang * max8925_onkey.c - MAX8925 ONKEY driver 33734574cSHaojian Zhuang * 43734574cSHaojian Zhuang * Copyright (C) 2009 Marvell International Ltd. 53734574cSHaojian Zhuang * Haojian Zhuang <haojian.zhuang@marvell.com> 63734574cSHaojian Zhuang * 73734574cSHaojian Zhuang * This file is subject to the terms and conditions of the GNU General 83734574cSHaojian Zhuang * Public License. See the file "COPYING" in the main directory of this 93734574cSHaojian Zhuang * archive for more details. 103734574cSHaojian Zhuang * 113734574cSHaojian Zhuang * This program is distributed in the hope that it will be useful, 123734574cSHaojian Zhuang * but WITHOUT ANY WARRANTY; without even the implied warranty of 133734574cSHaojian Zhuang * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 143734574cSHaojian Zhuang * GNU General Public License for more details. 153734574cSHaojian Zhuang * 163734574cSHaojian Zhuang * You should have received a copy of the GNU General Public License 173734574cSHaojian Zhuang * along with this program; if not, write to the Free Software 183734574cSHaojian Zhuang * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 193734574cSHaojian Zhuang */ 203734574cSHaojian Zhuang 213734574cSHaojian Zhuang #include <linux/kernel.h> 223734574cSHaojian Zhuang #include <linux/module.h> 233734574cSHaojian Zhuang #include <linux/platform_device.h> 243734574cSHaojian Zhuang #include <linux/i2c.h> 253734574cSHaojian Zhuang #include <linux/input.h> 263734574cSHaojian Zhuang #include <linux/interrupt.h> 273734574cSHaojian Zhuang #include <linux/mfd/max8925.h> 283734574cSHaojian Zhuang #include <linux/slab.h> 293734574cSHaojian Zhuang 303734574cSHaojian Zhuang #define HARDRESET_EN (1 << 7) 313734574cSHaojian Zhuang #define PWREN_EN (1 << 7) 323734574cSHaojian Zhuang 333734574cSHaojian Zhuang struct max8925_onkey_info { 343734574cSHaojian Zhuang struct input_dev *idev; 353734574cSHaojian Zhuang struct i2c_client *i2c; 363734574cSHaojian Zhuang int irq; 373734574cSHaojian Zhuang }; 383734574cSHaojian Zhuang 393734574cSHaojian Zhuang /* 403734574cSHaojian Zhuang * MAX8925 gives us an interrupt when ONKEY is held for 3 seconds. 413734574cSHaojian Zhuang * max8925_set_bits() operates I2C bus and may sleep. So implement 423734574cSHaojian Zhuang * it in thread IRQ handler. 433734574cSHaojian Zhuang */ 443734574cSHaojian Zhuang static irqreturn_t max8925_onkey_handler(int irq, void *data) 453734574cSHaojian Zhuang { 463734574cSHaojian Zhuang struct max8925_onkey_info *info = data; 473734574cSHaojian Zhuang 483734574cSHaojian Zhuang input_report_key(info->idev, KEY_POWER, 1); 493734574cSHaojian Zhuang input_sync(info->idev); 503734574cSHaojian Zhuang 513734574cSHaojian Zhuang /* Enable hardreset to halt if system isn't shutdown on time */ 523734574cSHaojian Zhuang max8925_set_bits(info->i2c, MAX8925_SYSENSEL, 533734574cSHaojian Zhuang HARDRESET_EN, HARDRESET_EN); 543734574cSHaojian Zhuang 553734574cSHaojian Zhuang return IRQ_HANDLED; 563734574cSHaojian Zhuang } 573734574cSHaojian Zhuang 583734574cSHaojian Zhuang static int __devinit max8925_onkey_probe(struct platform_device *pdev) 593734574cSHaojian Zhuang { 603734574cSHaojian Zhuang struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); 613734574cSHaojian Zhuang struct max8925_onkey_info *info; 623734574cSHaojian Zhuang int error; 633734574cSHaojian Zhuang 643734574cSHaojian Zhuang info = kzalloc(sizeof(struct max8925_onkey_info), GFP_KERNEL); 653734574cSHaojian Zhuang if (!info) 663734574cSHaojian Zhuang return -ENOMEM; 673734574cSHaojian Zhuang 683734574cSHaojian Zhuang info->i2c = chip->i2c; 693734574cSHaojian Zhuang info->irq = chip->irq_base + MAX8925_IRQ_GPM_SW_3SEC; 703734574cSHaojian Zhuang 713734574cSHaojian Zhuang info->idev = input_allocate_device(); 723734574cSHaojian Zhuang if (!info->idev) { 733734574cSHaojian Zhuang dev_err(chip->dev, "Failed to allocate input dev\n"); 743734574cSHaojian Zhuang error = -ENOMEM; 753734574cSHaojian Zhuang goto out_input; 763734574cSHaojian Zhuang } 773734574cSHaojian Zhuang 783734574cSHaojian Zhuang info->idev->name = "max8925_on"; 793734574cSHaojian Zhuang info->idev->phys = "max8925_on/input0"; 803734574cSHaojian Zhuang info->idev->id.bustype = BUS_I2C; 813734574cSHaojian Zhuang info->idev->dev.parent = &pdev->dev; 823734574cSHaojian Zhuang info->idev->evbit[0] = BIT_MASK(EV_KEY); 833734574cSHaojian Zhuang info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); 843734574cSHaojian Zhuang 853734574cSHaojian Zhuang error = request_threaded_irq(info->irq, NULL, max8925_onkey_handler, 863734574cSHaojian Zhuang IRQF_ONESHOT, "onkey", info); 873734574cSHaojian Zhuang if (error < 0) { 883734574cSHaojian Zhuang dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", 893734574cSHaojian Zhuang info->irq, error); 903734574cSHaojian Zhuang goto out_irq; 913734574cSHaojian Zhuang } 923734574cSHaojian Zhuang 933734574cSHaojian Zhuang error = input_register_device(info->idev); 943734574cSHaojian Zhuang if (error) { 953734574cSHaojian Zhuang dev_err(chip->dev, "Can't register input device: %d\n", error); 963734574cSHaojian Zhuang goto out; 973734574cSHaojian Zhuang } 983734574cSHaojian Zhuang 993734574cSHaojian Zhuang platform_set_drvdata(pdev, info); 1003734574cSHaojian Zhuang 1013734574cSHaojian Zhuang return 0; 1023734574cSHaojian Zhuang 1033734574cSHaojian Zhuang out: 1043734574cSHaojian Zhuang free_irq(info->irq, info); 1053734574cSHaojian Zhuang out_irq: 1063734574cSHaojian Zhuang input_free_device(info->idev); 1073734574cSHaojian Zhuang out_input: 1083734574cSHaojian Zhuang kfree(info); 1093734574cSHaojian Zhuang return error; 1103734574cSHaojian Zhuang } 1113734574cSHaojian Zhuang 1123734574cSHaojian Zhuang static int __devexit max8925_onkey_remove(struct platform_device *pdev) 1133734574cSHaojian Zhuang { 1143734574cSHaojian Zhuang struct max8925_onkey_info *info = platform_get_drvdata(pdev); 1153734574cSHaojian Zhuang 1163734574cSHaojian Zhuang free_irq(info->irq, info); 1173734574cSHaojian Zhuang input_unregister_device(info->idev); 1183734574cSHaojian Zhuang kfree(info); 1193734574cSHaojian Zhuang 1203734574cSHaojian Zhuang platform_set_drvdata(pdev, NULL); 1213734574cSHaojian Zhuang 1223734574cSHaojian Zhuang return 0; 1233734574cSHaojian Zhuang } 1243734574cSHaojian Zhuang 1253734574cSHaojian Zhuang static struct platform_driver max8925_onkey_driver = { 1263734574cSHaojian Zhuang .driver = { 1273734574cSHaojian Zhuang .name = "max8925-onkey", 1283734574cSHaojian Zhuang .owner = THIS_MODULE, 1293734574cSHaojian Zhuang }, 1303734574cSHaojian Zhuang .probe = max8925_onkey_probe, 1313734574cSHaojian Zhuang .remove = __devexit_p(max8925_onkey_remove), 1323734574cSHaojian Zhuang }; 1333734574cSHaojian Zhuang 1343734574cSHaojian Zhuang static int __init max8925_onkey_init(void) 1353734574cSHaojian Zhuang { 1363734574cSHaojian Zhuang return platform_driver_register(&max8925_onkey_driver); 1373734574cSHaojian Zhuang } 1383734574cSHaojian Zhuang module_init(max8925_onkey_init); 1393734574cSHaojian Zhuang 1403734574cSHaojian Zhuang static void __exit max8925_onkey_exit(void) 1413734574cSHaojian Zhuang { 1423734574cSHaojian Zhuang platform_driver_unregister(&max8925_onkey_driver); 1433734574cSHaojian Zhuang } 1443734574cSHaojian Zhuang module_exit(max8925_onkey_exit); 1453734574cSHaojian Zhuang 1463734574cSHaojian Zhuang MODULE_DESCRIPTION("Maxim MAX8925 ONKEY driver"); 1473734574cSHaojian Zhuang MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); 1483734574cSHaojian Zhuang MODULE_LICENSE("GPL"); 149