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