Root/
1 | /* |
2 | * Marvell 88PM80x ONKEY driver |
3 | * |
4 | * Copyright (C) 2012 Marvell International Ltd. |
5 | * Haojian Zhuang <haojian.zhuang@marvell.com> |
6 | * Qiao Zhou <zhouqiao@marvell.com> |
7 | * |
8 | * This file is subject to the terms and conditions of the GNU General |
9 | * Public License. See the file "COPYING" in the main directory of this |
10 | * archive for more details. |
11 | * |
12 | * This program is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * GNU General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU General Public License |
18 | * along with this program; if not, write to the Free Software |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | */ |
21 | |
22 | #include <linux/kernel.h> |
23 | #include <linux/module.h> |
24 | #include <linux/input.h> |
25 | #include <linux/mfd/88pm80x.h> |
26 | #include <linux/regmap.h> |
27 | #include <linux/slab.h> |
28 | |
29 | #define PM800_LONG_ONKEY_EN (1 << 0) |
30 | #define PM800_LONG_KEY_DELAY (8) /* 1 .. 16 seconds */ |
31 | #define PM800_LONKEY_PRESS_TIME ((PM800_LONG_KEY_DELAY-1) << 4) |
32 | #define PM800_LONKEY_PRESS_TIME_MASK (0xF0) |
33 | #define PM800_SW_PDOWN (1 << 5) |
34 | |
35 | struct pm80x_onkey_info { |
36 | struct input_dev *idev; |
37 | struct pm80x_chip *pm80x; |
38 | struct regmap *map; |
39 | int irq; |
40 | }; |
41 | |
42 | /* 88PM80x gives us an interrupt when ONKEY is held */ |
43 | static irqreturn_t pm80x_onkey_handler(int irq, void *data) |
44 | { |
45 | struct pm80x_onkey_info *info = data; |
46 | int ret = 0; |
47 | unsigned int val; |
48 | |
49 | ret = regmap_read(info->map, PM800_STATUS_1, &val); |
50 | if (ret < 0) { |
51 | dev_err(info->idev->dev.parent, "failed to read status: %d\n", ret); |
52 | return IRQ_NONE; |
53 | } |
54 | val &= PM800_ONKEY_STS1; |
55 | |
56 | input_report_key(info->idev, KEY_POWER, val); |
57 | input_sync(info->idev); |
58 | |
59 | return IRQ_HANDLED; |
60 | } |
61 | |
62 | static SIMPLE_DEV_PM_OPS(pm80x_onkey_pm_ops, pm80x_dev_suspend, |
63 | pm80x_dev_resume); |
64 | |
65 | static int pm80x_onkey_probe(struct platform_device *pdev) |
66 | { |
67 | |
68 | struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); |
69 | struct pm80x_onkey_info *info; |
70 | int err; |
71 | |
72 | info = kzalloc(sizeof(struct pm80x_onkey_info), GFP_KERNEL); |
73 | if (!info) |
74 | return -ENOMEM; |
75 | |
76 | info->pm80x = chip; |
77 | |
78 | info->irq = platform_get_irq(pdev, 0); |
79 | if (info->irq < 0) { |
80 | dev_err(&pdev->dev, "No IRQ resource!\n"); |
81 | err = -EINVAL; |
82 | goto out; |
83 | } |
84 | |
85 | info->map = info->pm80x->regmap; |
86 | if (!info->map) { |
87 | dev_err(&pdev->dev, "no regmap!\n"); |
88 | err = -EINVAL; |
89 | goto out; |
90 | } |
91 | |
92 | info->idev = input_allocate_device(); |
93 | if (!info->idev) { |
94 | dev_err(&pdev->dev, "Failed to allocate input dev\n"); |
95 | err = -ENOMEM; |
96 | goto out; |
97 | } |
98 | |
99 | info->idev->name = "88pm80x_on"; |
100 | info->idev->phys = "88pm80x_on/input0"; |
101 | info->idev->id.bustype = BUS_I2C; |
102 | info->idev->dev.parent = &pdev->dev; |
103 | info->idev->evbit[0] = BIT_MASK(EV_KEY); |
104 | __set_bit(KEY_POWER, info->idev->keybit); |
105 | |
106 | err = pm80x_request_irq(info->pm80x, info->irq, pm80x_onkey_handler, |
107 | IRQF_ONESHOT, "onkey", info); |
108 | if (err < 0) { |
109 | dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n", |
110 | info->irq, err); |
111 | goto out_reg; |
112 | } |
113 | |
114 | err = input_register_device(info->idev); |
115 | if (err) { |
116 | dev_err(&pdev->dev, "Can't register input device: %d\n", err); |
117 | goto out_irq; |
118 | } |
119 | |
120 | platform_set_drvdata(pdev, info); |
121 | |
122 | /* Enable long onkey detection */ |
123 | regmap_update_bits(info->map, PM800_RTC_MISC4, PM800_LONG_ONKEY_EN, |
124 | PM800_LONG_ONKEY_EN); |
125 | /* Set 8-second interval */ |
126 | regmap_update_bits(info->map, PM800_RTC_MISC3, |
127 | PM800_LONKEY_PRESS_TIME_MASK, |
128 | PM800_LONKEY_PRESS_TIME); |
129 | |
130 | device_init_wakeup(&pdev->dev, 1); |
131 | return 0; |
132 | |
133 | out_irq: |
134 | pm80x_free_irq(info->pm80x, info->irq, info); |
135 | out_reg: |
136 | input_free_device(info->idev); |
137 | out: |
138 | kfree(info); |
139 | return err; |
140 | } |
141 | |
142 | static int pm80x_onkey_remove(struct platform_device *pdev) |
143 | { |
144 | struct pm80x_onkey_info *info = platform_get_drvdata(pdev); |
145 | |
146 | device_init_wakeup(&pdev->dev, 0); |
147 | pm80x_free_irq(info->pm80x, info->irq, info); |
148 | input_unregister_device(info->idev); |
149 | kfree(info); |
150 | return 0; |
151 | } |
152 | |
153 | static struct platform_driver pm80x_onkey_driver = { |
154 | .driver = { |
155 | .name = "88pm80x-onkey", |
156 | .owner = THIS_MODULE, |
157 | .pm = &pm80x_onkey_pm_ops, |
158 | }, |
159 | .probe = pm80x_onkey_probe, |
160 | .remove = pm80x_onkey_remove, |
161 | }; |
162 | |
163 | module_platform_driver(pm80x_onkey_driver); |
164 | |
165 | MODULE_LICENSE("GPL"); |
166 | MODULE_DESCRIPTION("Marvell 88PM80x ONKEY driver"); |
167 | MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>"); |
168 | MODULE_ALIAS("platform:88pm80x-onkey"); |
169 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9