Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Marvell 88PM80x ONKEY driver
0003  *
0004  * Copyright (C) 2012 Marvell International Ltd.
0005  * Haojian Zhuang <haojian.zhuang@marvell.com>
0006  * Qiao Zhou <zhouqiao@marvell.com>
0007  *
0008  * This file is subject to the terms and conditions of the GNU General
0009  * Public License. See the file "COPYING" in the main directory of this
0010  * archive for more details.
0011  *
0012  * This program is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015  * GNU General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU General Public License
0018  * along with this program; if not, write to the Free Software
0019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0020  */
0021 
0022 #include <linux/kernel.h>
0023 #include <linux/module.h>
0024 #include <linux/input.h>
0025 #include <linux/mfd/88pm80x.h>
0026 #include <linux/regmap.h>
0027 #include <linux/slab.h>
0028 
0029 #define PM800_LONG_ONKEY_EN     (1 << 0)
0030 #define PM800_LONG_KEY_DELAY        (8) /* 1 .. 16 seconds */
0031 #define PM800_LONKEY_PRESS_TIME     ((PM800_LONG_KEY_DELAY-1) << 4)
0032 #define PM800_LONKEY_PRESS_TIME_MASK    (0xF0)
0033 #define PM800_SW_PDOWN          (1 << 5)
0034 
0035 struct pm80x_onkey_info {
0036     struct input_dev *idev;
0037     struct pm80x_chip *pm80x;
0038     struct regmap *map;
0039     int irq;
0040 };
0041 
0042 /* 88PM80x gives us an interrupt when ONKEY is held */
0043 static irqreturn_t pm80x_onkey_handler(int irq, void *data)
0044 {
0045     struct pm80x_onkey_info *info = data;
0046     int ret = 0;
0047     unsigned int val;
0048 
0049     ret = regmap_read(info->map, PM800_STATUS_1, &val);
0050     if (ret < 0) {
0051         dev_err(info->idev->dev.parent, "failed to read status: %d\n", ret);
0052         return IRQ_NONE;
0053     }
0054     val &= PM800_ONKEY_STS1;
0055 
0056     input_report_key(info->idev, KEY_POWER, val);
0057     input_sync(info->idev);
0058 
0059     return IRQ_HANDLED;
0060 }
0061 
0062 static SIMPLE_DEV_PM_OPS(pm80x_onkey_pm_ops, pm80x_dev_suspend,
0063              pm80x_dev_resume);
0064 
0065 static int pm80x_onkey_probe(struct platform_device *pdev)
0066 {
0067 
0068     struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
0069     struct pm80x_onkey_info *info;
0070     int err;
0071 
0072     info = kzalloc(sizeof(struct pm80x_onkey_info), GFP_KERNEL);
0073     if (!info)
0074         return -ENOMEM;
0075 
0076     info->pm80x = chip;
0077 
0078     info->irq = platform_get_irq(pdev, 0);
0079     if (info->irq < 0) {
0080         err = -EINVAL;
0081         goto out;
0082     }
0083 
0084     info->map = info->pm80x->regmap;
0085     if (!info->map) {
0086         dev_err(&pdev->dev, "no regmap!\n");
0087         err = -EINVAL;
0088         goto out;
0089     }
0090 
0091     info->idev = input_allocate_device();
0092     if (!info->idev) {
0093         dev_err(&pdev->dev, "Failed to allocate input dev\n");
0094         err = -ENOMEM;
0095         goto out;
0096     }
0097 
0098     info->idev->name = "88pm80x_on";
0099     info->idev->phys = "88pm80x_on/input0";
0100     info->idev->id.bustype = BUS_I2C;
0101     info->idev->dev.parent = &pdev->dev;
0102     info->idev->evbit[0] = BIT_MASK(EV_KEY);
0103     __set_bit(KEY_POWER, info->idev->keybit);
0104 
0105     err = pm80x_request_irq(info->pm80x, info->irq, pm80x_onkey_handler,
0106                         IRQF_ONESHOT, "onkey", info);
0107     if (err < 0) {
0108         dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n",
0109             info->irq, err);
0110         goto out_reg;
0111     }
0112 
0113     err = input_register_device(info->idev);
0114     if (err) {
0115         dev_err(&pdev->dev, "Can't register input device: %d\n", err);
0116         goto out_irq;
0117     }
0118 
0119     platform_set_drvdata(pdev, info);
0120 
0121     /* Enable long onkey detection */
0122     regmap_update_bits(info->map, PM800_RTC_MISC4, PM800_LONG_ONKEY_EN,
0123                PM800_LONG_ONKEY_EN);
0124     /* Set 8-second interval */
0125     regmap_update_bits(info->map, PM800_RTC_MISC3,
0126                PM800_LONKEY_PRESS_TIME_MASK,
0127                PM800_LONKEY_PRESS_TIME);
0128 
0129     device_init_wakeup(&pdev->dev, 1);
0130     return 0;
0131 
0132 out_irq:
0133     pm80x_free_irq(info->pm80x, info->irq, info);
0134 out_reg:
0135     input_free_device(info->idev);
0136 out:
0137     kfree(info);
0138     return err;
0139 }
0140 
0141 static int pm80x_onkey_remove(struct platform_device *pdev)
0142 {
0143     struct pm80x_onkey_info *info = platform_get_drvdata(pdev);
0144 
0145     pm80x_free_irq(info->pm80x, info->irq, info);
0146     input_unregister_device(info->idev);
0147     kfree(info);
0148     return 0;
0149 }
0150 
0151 static struct platform_driver pm80x_onkey_driver = {
0152     .driver = {
0153            .name = "88pm80x-onkey",
0154            .pm = &pm80x_onkey_pm_ops,
0155            },
0156     .probe = pm80x_onkey_probe,
0157     .remove = pm80x_onkey_remove,
0158 };
0159 
0160 module_platform_driver(pm80x_onkey_driver);
0161 
0162 MODULE_LICENSE("GPL");
0163 MODULE_DESCRIPTION("Marvell 88PM80x ONKEY driver");
0164 MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
0165 MODULE_ALIAS("platform:88pm80x-onkey");