Root/
1 | /* |
2 | * Copyright (C) 2011 Sony Ericsson Mobile Communications Inc. |
3 | * |
4 | * Author: Courtney Cavin <courtney.cavin@sonyericsson.com> |
5 | * Prepared for up-stream by: Oskar Andero <oskar.andero@sonyericsson.com> |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2, as |
9 | * published by the Free Software Foundation. |
10 | */ |
11 | |
12 | #include <linux/i2c.h> |
13 | #include <linux/irq.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/input.h> |
16 | #include <linux/module.h> |
17 | #include <linux/interrupt.h> |
18 | #include <linux/gpio.h> |
19 | #include <linux/delay.h> |
20 | #include <linux/input/gp2ap002a00f.h> |
21 | |
22 | struct gp2a_data { |
23 | struct input_dev *input; |
24 | const struct gp2a_platform_data *pdata; |
25 | struct i2c_client *i2c_client; |
26 | }; |
27 | |
28 | enum gp2a_addr { |
29 | GP2A_ADDR_PROX = 0x0, |
30 | GP2A_ADDR_GAIN = 0x1, |
31 | GP2A_ADDR_HYS = 0x2, |
32 | GP2A_ADDR_CYCLE = 0x3, |
33 | GP2A_ADDR_OPMOD = 0x4, |
34 | GP2A_ADDR_CON = 0x6 |
35 | }; |
36 | |
37 | enum gp2a_controls { |
38 | /* Software Shutdown control: 0 = shutdown, 1 = normal operation */ |
39 | GP2A_CTRL_SSD = 0x01 |
40 | }; |
41 | |
42 | static int gp2a_report(struct gp2a_data *dt) |
43 | { |
44 | int vo = gpio_get_value(dt->pdata->vout_gpio); |
45 | |
46 | input_report_switch(dt->input, SW_FRONT_PROXIMITY, !vo); |
47 | input_sync(dt->input); |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | static irqreturn_t gp2a_irq(int irq, void *handle) |
53 | { |
54 | struct gp2a_data *dt = handle; |
55 | |
56 | gp2a_report(dt); |
57 | |
58 | return IRQ_HANDLED; |
59 | } |
60 | |
61 | static int gp2a_enable(struct gp2a_data *dt) |
62 | { |
63 | return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD, |
64 | GP2A_CTRL_SSD); |
65 | } |
66 | |
67 | static int gp2a_disable(struct gp2a_data *dt) |
68 | { |
69 | return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD, |
70 | 0x00); |
71 | } |
72 | |
73 | static int gp2a_device_open(struct input_dev *dev) |
74 | { |
75 | struct gp2a_data *dt = input_get_drvdata(dev); |
76 | int error; |
77 | |
78 | error = gp2a_enable(dt); |
79 | if (error < 0) { |
80 | dev_err(&dt->i2c_client->dev, |
81 | "unable to activate, err %d\n", error); |
82 | return error; |
83 | } |
84 | |
85 | gp2a_report(dt); |
86 | |
87 | return 0; |
88 | } |
89 | |
90 | static void gp2a_device_close(struct input_dev *dev) |
91 | { |
92 | struct gp2a_data *dt = input_get_drvdata(dev); |
93 | int error; |
94 | |
95 | error = gp2a_disable(dt); |
96 | if (error < 0) |
97 | dev_err(&dt->i2c_client->dev, |
98 | "unable to deactivate, err %d\n", error); |
99 | } |
100 | |
101 | static int gp2a_initialize(struct gp2a_data *dt) |
102 | { |
103 | int error; |
104 | |
105 | error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_GAIN, |
106 | 0x08); |
107 | if (error < 0) |
108 | return error; |
109 | |
110 | error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_HYS, |
111 | 0xc2); |
112 | if (error < 0) |
113 | return error; |
114 | |
115 | error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_CYCLE, |
116 | 0x04); |
117 | if (error < 0) |
118 | return error; |
119 | |
120 | error = gp2a_disable(dt); |
121 | |
122 | return error; |
123 | } |
124 | |
125 | static int gp2a_probe(struct i2c_client *client, |
126 | const struct i2c_device_id *id) |
127 | { |
128 | const struct gp2a_platform_data *pdata = client->dev.platform_data; |
129 | struct gp2a_data *dt; |
130 | int error; |
131 | |
132 | if (!pdata) |
133 | return -EINVAL; |
134 | |
135 | if (pdata->hw_setup) { |
136 | error = pdata->hw_setup(client); |
137 | if (error < 0) |
138 | return error; |
139 | } |
140 | |
141 | error = gpio_request_one(pdata->vout_gpio, GPIOF_IN, GP2A_I2C_NAME); |
142 | if (error) |
143 | goto err_hw_shutdown; |
144 | |
145 | dt = kzalloc(sizeof(struct gp2a_data), GFP_KERNEL); |
146 | if (!dt) { |
147 | error = -ENOMEM; |
148 | goto err_free_gpio; |
149 | } |
150 | |
151 | dt->pdata = pdata; |
152 | dt->i2c_client = client; |
153 | |
154 | error = gp2a_initialize(dt); |
155 | if (error < 0) |
156 | goto err_free_mem; |
157 | |
158 | dt->input = input_allocate_device(); |
159 | if (!dt->input) { |
160 | error = -ENOMEM; |
161 | goto err_free_mem; |
162 | } |
163 | |
164 | input_set_drvdata(dt->input, dt); |
165 | |
166 | dt->input->open = gp2a_device_open; |
167 | dt->input->close = gp2a_device_close; |
168 | dt->input->name = GP2A_I2C_NAME; |
169 | dt->input->id.bustype = BUS_I2C; |
170 | dt->input->dev.parent = &client->dev; |
171 | |
172 | input_set_capability(dt->input, EV_SW, SW_FRONT_PROXIMITY); |
173 | |
174 | error = request_threaded_irq(client->irq, NULL, gp2a_irq, |
175 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | |
176 | IRQF_ONESHOT, |
177 | GP2A_I2C_NAME, dt); |
178 | if (error) { |
179 | dev_err(&client->dev, "irq request failed\n"); |
180 | goto err_free_input_dev; |
181 | } |
182 | |
183 | error = input_register_device(dt->input); |
184 | if (error) { |
185 | dev_err(&client->dev, "device registration failed\n"); |
186 | goto err_free_irq; |
187 | } |
188 | |
189 | device_init_wakeup(&client->dev, pdata->wakeup); |
190 | i2c_set_clientdata(client, dt); |
191 | |
192 | return 0; |
193 | |
194 | err_free_irq: |
195 | free_irq(client->irq, dt); |
196 | err_free_input_dev: |
197 | input_free_device(dt->input); |
198 | err_free_mem: |
199 | kfree(dt); |
200 | err_free_gpio: |
201 | gpio_free(pdata->vout_gpio); |
202 | err_hw_shutdown: |
203 | if (pdata->hw_shutdown) |
204 | pdata->hw_shutdown(client); |
205 | return error; |
206 | } |
207 | |
208 | static int gp2a_remove(struct i2c_client *client) |
209 | { |
210 | struct gp2a_data *dt = i2c_get_clientdata(client); |
211 | const struct gp2a_platform_data *pdata = dt->pdata; |
212 | |
213 | device_init_wakeup(&client->dev, false); |
214 | |
215 | free_irq(client->irq, dt); |
216 | |
217 | input_unregister_device(dt->input); |
218 | kfree(dt); |
219 | |
220 | gpio_free(pdata->vout_gpio); |
221 | |
222 | if (pdata->hw_shutdown) |
223 | pdata->hw_shutdown(client); |
224 | |
225 | return 0; |
226 | } |
227 | |
228 | #ifdef CONFIG_PM_SLEEP |
229 | static int gp2a_suspend(struct device *dev) |
230 | { |
231 | struct i2c_client *client = to_i2c_client(dev); |
232 | struct gp2a_data *dt = i2c_get_clientdata(client); |
233 | int retval = 0; |
234 | |
235 | if (device_may_wakeup(&client->dev)) { |
236 | enable_irq_wake(client->irq); |
237 | } else { |
238 | mutex_lock(&dt->input->mutex); |
239 | if (dt->input->users) |
240 | retval = gp2a_disable(dt); |
241 | mutex_unlock(&dt->input->mutex); |
242 | } |
243 | |
244 | return retval; |
245 | } |
246 | |
247 | static int gp2a_resume(struct device *dev) |
248 | { |
249 | struct i2c_client *client = to_i2c_client(dev); |
250 | struct gp2a_data *dt = i2c_get_clientdata(client); |
251 | int retval = 0; |
252 | |
253 | if (device_may_wakeup(&client->dev)) { |
254 | disable_irq_wake(client->irq); |
255 | } else { |
256 | mutex_lock(&dt->input->mutex); |
257 | if (dt->input->users) |
258 | retval = gp2a_enable(dt); |
259 | mutex_unlock(&dt->input->mutex); |
260 | } |
261 | |
262 | return retval; |
263 | } |
264 | #endif |
265 | |
266 | static SIMPLE_DEV_PM_OPS(gp2a_pm, gp2a_suspend, gp2a_resume); |
267 | |
268 | static const struct i2c_device_id gp2a_i2c_id[] = { |
269 | { GP2A_I2C_NAME, 0 }, |
270 | { } |
271 | }; |
272 | |
273 | static struct i2c_driver gp2a_i2c_driver = { |
274 | .driver = { |
275 | .name = GP2A_I2C_NAME, |
276 | .owner = THIS_MODULE, |
277 | .pm = &gp2a_pm, |
278 | }, |
279 | .probe = gp2a_probe, |
280 | .remove = gp2a_remove, |
281 | .id_table = gp2a_i2c_id, |
282 | }; |
283 | |
284 | module_i2c_driver(gp2a_i2c_driver); |
285 | |
286 | MODULE_AUTHOR("Courtney Cavin <courtney.cavin@sonyericsson.com>"); |
287 | MODULE_DESCRIPTION("Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"); |
288 | MODULE_LICENSE("GPL v2"); |
289 |
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