Path: blob/master/drivers/input/misc/pmic8xxx-pwrkey.c
15111 views
/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.1*2* This program is free software; you can redistribute it and/or modify3* it under the terms of the GNU General Public License version 2 and4* only version 2 as published by the Free Software Foundation.5*6* This program is distributed in the hope that it will be useful,7* but WITHOUT ANY WARRANTY; without even the implied warranty of8* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9* GNU General Public License for more details.10*/1112#include <linux/module.h>13#include <linux/init.h>14#include <linux/kernel.h>15#include <linux/errno.h>16#include <linux/slab.h>17#include <linux/input.h>18#include <linux/interrupt.h>19#include <linux/platform_device.h>20#include <linux/log2.h>2122#include <linux/mfd/pm8xxx/core.h>23#include <linux/input/pmic8xxx-pwrkey.h>2425#define PON_CNTL_1 0x1C26#define PON_CNTL_PULL_UP BIT(7)27#define PON_CNTL_TRIG_DELAY_MASK (0x7)2829/**30* struct pmic8xxx_pwrkey - pmic8xxx pwrkey information31* @key_press_irq: key press irq number32*/33struct pmic8xxx_pwrkey {34struct input_dev *pwr;35int key_press_irq;36};3738static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey)39{40struct pmic8xxx_pwrkey *pwrkey = _pwrkey;4142input_report_key(pwrkey->pwr, KEY_POWER, 1);43input_sync(pwrkey->pwr);4445return IRQ_HANDLED;46}4748static irqreturn_t pwrkey_release_irq(int irq, void *_pwrkey)49{50struct pmic8xxx_pwrkey *pwrkey = _pwrkey;5152input_report_key(pwrkey->pwr, KEY_POWER, 0);53input_sync(pwrkey->pwr);5455return IRQ_HANDLED;56}5758#ifdef CONFIG_PM_SLEEP59static int pmic8xxx_pwrkey_suspend(struct device *dev)60{61struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);6263if (device_may_wakeup(dev))64enable_irq_wake(pwrkey->key_press_irq);6566return 0;67}6869static int pmic8xxx_pwrkey_resume(struct device *dev)70{71struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);7273if (device_may_wakeup(dev))74disable_irq_wake(pwrkey->key_press_irq);7576return 0;77}78#endif7980static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops,81pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume);8283static int __devinit pmic8xxx_pwrkey_probe(struct platform_device *pdev)84{85struct input_dev *pwr;86int key_release_irq = platform_get_irq(pdev, 0);87int key_press_irq = platform_get_irq(pdev, 1);88int err;89unsigned int delay;90u8 pon_cntl;91struct pmic8xxx_pwrkey *pwrkey;92const struct pm8xxx_pwrkey_platform_data *pdata =93dev_get_platdata(&pdev->dev);9495if (!pdata) {96dev_err(&pdev->dev, "power key platform data not supplied\n");97return -EINVAL;98}99100if (pdata->kpd_trigger_delay_us > 62500) {101dev_err(&pdev->dev, "invalid power key trigger delay\n");102return -EINVAL;103}104105pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL);106if (!pwrkey)107return -ENOMEM;108109pwr = input_allocate_device();110if (!pwr) {111dev_dbg(&pdev->dev, "Can't allocate power button\n");112err = -ENOMEM;113goto free_pwrkey;114}115116input_set_capability(pwr, EV_KEY, KEY_POWER);117118pwr->name = "pmic8xxx_pwrkey";119pwr->phys = "pmic8xxx_pwrkey/input0";120pwr->dev.parent = &pdev->dev;121122delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC;123delay = 1 + ilog2(delay);124125err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl);126if (err < 0) {127dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err);128goto free_input_dev;129}130131pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK;132pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK);133if (pdata->pull_up)134pon_cntl |= PON_CNTL_PULL_UP;135else136pon_cntl &= ~PON_CNTL_PULL_UP;137138err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl);139if (err < 0) {140dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err);141goto free_input_dev;142}143144err = input_register_device(pwr);145if (err) {146dev_dbg(&pdev->dev, "Can't register power key: %d\n", err);147goto free_input_dev;148}149150pwrkey->key_press_irq = key_press_irq;151pwrkey->pwr = pwr;152153platform_set_drvdata(pdev, pwrkey);154155err = request_irq(key_press_irq, pwrkey_press_irq,156IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey);157if (err < 0) {158dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",159key_press_irq, err);160goto unreg_input_dev;161}162163err = request_irq(key_release_irq, pwrkey_release_irq,164IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwrkey);165if (err < 0) {166dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",167key_release_irq, err);168169goto free_press_irq;170}171172device_init_wakeup(&pdev->dev, pdata->wakeup);173174return 0;175176free_press_irq:177free_irq(key_press_irq, NULL);178unreg_input_dev:179platform_set_drvdata(pdev, NULL);180input_unregister_device(pwr);181pwr = NULL;182free_input_dev:183input_free_device(pwr);184free_pwrkey:185kfree(pwrkey);186return err;187}188189static int __devexit pmic8xxx_pwrkey_remove(struct platform_device *pdev)190{191struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev);192int key_release_irq = platform_get_irq(pdev, 0);193int key_press_irq = platform_get_irq(pdev, 1);194195device_init_wakeup(&pdev->dev, 0);196197free_irq(key_press_irq, pwrkey);198free_irq(key_release_irq, pwrkey);199input_unregister_device(pwrkey->pwr);200platform_set_drvdata(pdev, NULL);201kfree(pwrkey);202203return 0;204}205206static struct platform_driver pmic8xxx_pwrkey_driver = {207.probe = pmic8xxx_pwrkey_probe,208.remove = __devexit_p(pmic8xxx_pwrkey_remove),209.driver = {210.name = PM8XXX_PWRKEY_DEV_NAME,211.owner = THIS_MODULE,212.pm = &pm8xxx_pwr_key_pm_ops,213},214};215216static int __init pmic8xxx_pwrkey_init(void)217{218return platform_driver_register(&pmic8xxx_pwrkey_driver);219}220module_init(pmic8xxx_pwrkey_init);221222static void __exit pmic8xxx_pwrkey_exit(void)223{224platform_driver_unregister(&pmic8xxx_pwrkey_driver);225}226module_exit(pmic8xxx_pwrkey_exit);227228MODULE_ALIAS("platform:pmic8xxx_pwrkey");229MODULE_DESCRIPTION("PMIC8XXX Power Key driver");230MODULE_LICENSE("GPL v2");231MODULE_AUTHOR("Trilok Soni <[email protected]>");232233234