Commit | Line | Data |
---|---|---|
e590d451 BS |
1 | /* |
2 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | |
3 | * Author: Beomho Seo <beomho.seo@samsung.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2, as published | |
7 | * by the Free Software Foundation. | |
8 | */ | |
9 | ||
10 | #include <linux/delay.h> | |
11 | #include <linux/err.h> | |
12 | #include <linux/i2c.h> | |
13 | #include <linux/mutex.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/interrupt.h> | |
16 | #include <linux/regulator/consumer.h> | |
17 | #include <linux/iio/iio.h> | |
18 | #include <linux/iio/sysfs.h> | |
19 | #include <linux/iio/events.h> | |
20 | ||
21 | /* Slave address 0x19 for PS of 7 bit addressing protocol for I2C */ | |
22 | #define CM36651_I2C_ADDR_PS 0x19 | |
23 | /* Alert Response Address */ | |
24 | #define CM36651_ARA 0x0C | |
25 | ||
26 | /* Ambient light sensor */ | |
27 | #define CM36651_CS_CONF1 0x00 | |
28 | #define CM36651_CS_CONF2 0x01 | |
29 | #define CM36651_ALS_WH_M 0x02 | |
30 | #define CM36651_ALS_WH_L 0x03 | |
31 | #define CM36651_ALS_WL_M 0x04 | |
32 | #define CM36651_ALS_WL_L 0x05 | |
33 | #define CM36651_CS_CONF3 0x06 | |
34 | #define CM36651_CS_CONF_REG_NUM 0x02 | |
35 | ||
36 | /* Proximity sensor */ | |
37 | #define CM36651_PS_CONF1 0x00 | |
38 | #define CM36651_PS_THD 0x01 | |
39 | #define CM36651_PS_CANC 0x02 | |
40 | #define CM36651_PS_CONF2 0x03 | |
41 | #define CM36651_PS_REG_NUM 0x04 | |
42 | ||
43 | /* CS_CONF1 command code */ | |
44 | #define CM36651_ALS_ENABLE 0x00 | |
45 | #define CM36651_ALS_DISABLE 0x01 | |
46 | #define CM36651_ALS_INT_EN 0x02 | |
47 | #define CM36651_ALS_THRES 0x04 | |
48 | ||
49 | /* CS_CONF2 command code */ | |
50 | #define CM36651_CS_CONF2_DEFAULT_BIT 0x08 | |
51 | ||
52 | /* CS_CONF3 channel integration time */ | |
53 | #define CM36651_CS_IT1 0x00 /* Integration time 80000 usec */ | |
54 | #define CM36651_CS_IT2 0x40 /* Integration time 160000 usec */ | |
55 | #define CM36651_CS_IT3 0x80 /* Integration time 320000 usec */ | |
56 | #define CM36651_CS_IT4 0xC0 /* Integration time 640000 usec */ | |
57 | ||
58 | /* PS_CONF1 command code */ | |
59 | #define CM36651_PS_ENABLE 0x00 | |
60 | #define CM36651_PS_DISABLE 0x01 | |
61 | #define CM36651_PS_INT_EN 0x02 | |
62 | #define CM36651_PS_PERS2 0x04 | |
63 | #define CM36651_PS_PERS3 0x08 | |
64 | #define CM36651_PS_PERS4 0x0C | |
65 | ||
66 | /* PS_CONF1 command code: integration time */ | |
67 | #define CM36651_PS_IT1 0x00 /* Integration time 320 usec */ | |
68 | #define CM36651_PS_IT2 0x10 /* Integration time 420 usec */ | |
69 | #define CM36651_PS_IT3 0x20 /* Integration time 520 usec */ | |
70 | #define CM36651_PS_IT4 0x30 /* Integration time 640 usec */ | |
71 | ||
72 | /* PS_CONF1 command code: duty ratio */ | |
73 | #define CM36651_PS_DR1 0x00 /* Duty ratio 1/80 */ | |
74 | #define CM36651_PS_DR2 0x40 /* Duty ratio 1/160 */ | |
75 | #define CM36651_PS_DR3 0x80 /* Duty ratio 1/320 */ | |
76 | #define CM36651_PS_DR4 0xC0 /* Duty ratio 1/640 */ | |
77 | ||
78 | /* PS_THD command code */ | |
79 | #define CM36651_PS_INITIAL_THD 0x05 | |
80 | ||
81 | /* PS_CANC command code */ | |
82 | #define CM36651_PS_CANC_DEFAULT 0x00 | |
83 | ||
84 | /* PS_CONF2 command code */ | |
85 | #define CM36651_PS_HYS1 0x00 | |
86 | #define CM36651_PS_HYS2 0x01 | |
87 | #define CM36651_PS_SMART_PERS_EN 0x02 | |
88 | #define CM36651_PS_DIR_INT 0x04 | |
89 | #define CM36651_PS_MS 0x10 | |
90 | ||
91 | #define CM36651_CS_COLOR_NUM 4 | |
92 | ||
93 | #define CM36651_CLOSE_PROXIMITY 0x32 | |
94 | #define CM36651_FAR_PROXIMITY 0x33 | |
95 | ||
96 | #define CM36651_CS_INT_TIME_AVAIL "80000 160000 320000 640000" | |
97 | #define CM36651_PS_INT_TIME_AVAIL "320 420 520 640" | |
98 | ||
99 | enum cm36651_operation_mode { | |
100 | CM36651_LIGHT_EN, | |
101 | CM36651_PROXIMITY_EN, | |
102 | CM36651_PROXIMITY_EV_EN, | |
103 | }; | |
104 | ||
105 | enum cm36651_light_channel_idx { | |
106 | CM36651_LIGHT_CHANNEL_IDX_RED, | |
107 | CM36651_LIGHT_CHANNEL_IDX_GREEN, | |
108 | CM36651_LIGHT_CHANNEL_IDX_BLUE, | |
109 | CM36651_LIGHT_CHANNEL_IDX_CLEAR, | |
110 | }; | |
111 | ||
112 | enum cm36651_command { | |
113 | CM36651_CMD_READ_RAW_LIGHT, | |
114 | CM36651_CMD_READ_RAW_PROXIMITY, | |
115 | CM36651_CMD_PROX_EV_EN, | |
116 | CM36651_CMD_PROX_EV_DIS, | |
117 | }; | |
118 | ||
119 | static const u8 cm36651_cs_reg[CM36651_CS_CONF_REG_NUM] = { | |
120 | CM36651_CS_CONF1, | |
121 | CM36651_CS_CONF2, | |
122 | }; | |
123 | ||
124 | static const u8 cm36651_ps_reg[CM36651_PS_REG_NUM] = { | |
125 | CM36651_PS_CONF1, | |
126 | CM36651_PS_THD, | |
127 | CM36651_PS_CANC, | |
128 | CM36651_PS_CONF2, | |
129 | }; | |
130 | ||
131 | struct cm36651_data { | |
132 | const struct cm36651_platform_data *pdata; | |
133 | struct i2c_client *client; | |
134 | struct i2c_client *ps_client; | |
135 | struct i2c_client *ara_client; | |
136 | struct mutex lock; | |
137 | struct regulator *vled_reg; | |
138 | unsigned long flags; | |
139 | int cs_int_time[CM36651_CS_COLOR_NUM]; | |
140 | int ps_int_time; | |
141 | u8 cs_ctrl_regs[CM36651_CS_CONF_REG_NUM]; | |
142 | u8 ps_ctrl_regs[CM36651_PS_REG_NUM]; | |
143 | u16 color[CM36651_CS_COLOR_NUM]; | |
144 | }; | |
145 | ||
146 | static int cm36651_setup_reg(struct cm36651_data *cm36651) | |
147 | { | |
148 | struct i2c_client *client = cm36651->client; | |
149 | struct i2c_client *ps_client = cm36651->ps_client; | |
150 | int i, ret; | |
151 | ||
152 | /* CS initialization */ | |
153 | cm36651->cs_ctrl_regs[CM36651_CS_CONF1] = CM36651_ALS_ENABLE | | |
154 | CM36651_ALS_THRES; | |
155 | cm36651->cs_ctrl_regs[CM36651_CS_CONF2] = CM36651_CS_CONF2_DEFAULT_BIT; | |
156 | ||
157 | for (i = 0; i < CM36651_CS_CONF_REG_NUM; i++) { | |
158 | ret = i2c_smbus_write_byte_data(client, cm36651_cs_reg[i], | |
159 | cm36651->cs_ctrl_regs[i]); | |
160 | if (ret < 0) | |
161 | return ret; | |
162 | } | |
163 | ||
164 | /* PS initialization */ | |
165 | cm36651->ps_ctrl_regs[CM36651_PS_CONF1] = CM36651_PS_ENABLE | | |
166 | CM36651_PS_IT2; | |
167 | cm36651->ps_ctrl_regs[CM36651_PS_THD] = CM36651_PS_INITIAL_THD; | |
168 | cm36651->ps_ctrl_regs[CM36651_PS_CANC] = CM36651_PS_CANC_DEFAULT; | |
169 | cm36651->ps_ctrl_regs[CM36651_PS_CONF2] = CM36651_PS_HYS2 | | |
170 | CM36651_PS_DIR_INT | CM36651_PS_SMART_PERS_EN; | |
171 | ||
172 | for (i = 0; i < CM36651_PS_REG_NUM; i++) { | |
173 | ret = i2c_smbus_write_byte_data(ps_client, cm36651_ps_reg[i], | |
174 | cm36651->ps_ctrl_regs[i]); | |
175 | if (ret < 0) | |
176 | return ret; | |
177 | } | |
178 | ||
179 | /* Set shutdown mode */ | |
180 | ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1, | |
181 | CM36651_ALS_DISABLE); | |
182 | if (ret < 0) | |
183 | return ret; | |
184 | ||
185 | ret = i2c_smbus_write_byte_data(cm36651->ps_client, | |
186 | CM36651_PS_CONF1, CM36651_PS_DISABLE); | |
187 | if (ret < 0) | |
188 | return ret; | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | static int cm36651_read_output(struct cm36651_data *cm36651, | |
194 | struct iio_chan_spec const *chan, int *val) | |
195 | { | |
196 | struct i2c_client *client = cm36651->client; | |
197 | int ret = -EINVAL; | |
198 | ||
199 | switch (chan->type) { | |
200 | case IIO_LIGHT: | |
201 | *val = i2c_smbus_read_word_data(client, chan->address); | |
202 | if (*val < 0) | |
203 | return ret; | |
204 | ||
205 | ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1, | |
206 | CM36651_ALS_DISABLE); | |
207 | if (ret < 0) | |
208 | return ret; | |
209 | ||
210 | ret = IIO_VAL_INT; | |
211 | break; | |
212 | case IIO_PROXIMITY: | |
213 | *val = i2c_smbus_read_byte(cm36651->ps_client); | |
214 | if (*val < 0) | |
215 | return ret; | |
216 | ||
217 | if (!test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) { | |
218 | ret = i2c_smbus_write_byte_data(cm36651->ps_client, | |
219 | CM36651_PS_CONF1, CM36651_PS_DISABLE); | |
220 | if (ret < 0) | |
221 | return ret; | |
222 | } | |
223 | ||
224 | ret = IIO_VAL_INT; | |
225 | break; | |
226 | default: | |
227 | break; | |
228 | } | |
229 | ||
230 | return ret; | |
231 | } | |
232 | ||
233 | static irqreturn_t cm36651_irq_handler(int irq, void *data) | |
234 | { | |
235 | struct iio_dev *indio_dev = data; | |
236 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
237 | struct i2c_client *client = cm36651->client; | |
238 | int ev_dir, ret; | |
239 | u64 ev_code; | |
240 | ||
241 | /* | |
242 | * The PS INT pin is an active low signal that PS INT move logic low | |
243 | * when the object is detect. Once the MCU host received the PS INT | |
244 | * "LOW" signal, the Host needs to read the data at Alert Response | |
245 | * Address(ARA) to clear the PS INT signal. After clearing the PS | |
246 | * INT pin, the PS INT signal toggles from low to high. | |
247 | */ | |
248 | ret = i2c_smbus_read_byte(cm36651->ara_client); | |
249 | if (ret < 0) { | |
250 | dev_err(&client->dev, | |
251 | "%s: Data read failed: %d\n", __func__, ret); | |
252 | return IRQ_HANDLED; | |
253 | } | |
254 | switch (ret) { | |
255 | case CM36651_CLOSE_PROXIMITY: | |
256 | ev_dir = IIO_EV_DIR_RISING; | |
257 | break; | |
258 | case CM36651_FAR_PROXIMITY: | |
259 | ev_dir = IIO_EV_DIR_FALLING; | |
260 | break; | |
261 | default: | |
262 | dev_err(&client->dev, | |
263 | "%s: Data read wrong: %d\n", __func__, ret); | |
264 | return IRQ_HANDLED; | |
265 | } | |
266 | ||
267 | ev_code = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, | |
268 | CM36651_CMD_READ_RAW_PROXIMITY, | |
269 | IIO_EV_TYPE_THRESH, ev_dir); | |
270 | ||
271 | iio_push_event(indio_dev, ev_code, iio_get_time_ns()); | |
272 | ||
273 | return IRQ_HANDLED; | |
274 | } | |
275 | ||
276 | static int cm36651_set_operation_mode(struct cm36651_data *cm36651, int cmd) | |
277 | { | |
278 | struct i2c_client *client = cm36651->client; | |
279 | struct i2c_client *ps_client = cm36651->ps_client; | |
280 | int ret = -EINVAL; | |
281 | ||
282 | switch (cmd) { | |
283 | case CM36651_CMD_READ_RAW_LIGHT: | |
284 | ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1, | |
285 | cm36651->cs_ctrl_regs[CM36651_CS_CONF1]); | |
286 | break; | |
287 | case CM36651_CMD_READ_RAW_PROXIMITY: | |
288 | if (test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) | |
289 | return CM36651_PROXIMITY_EV_EN; | |
290 | ||
291 | ret = i2c_smbus_write_byte_data(ps_client, CM36651_PS_CONF1, | |
292 | cm36651->ps_ctrl_regs[CM36651_PS_CONF1]); | |
293 | break; | |
294 | case CM36651_CMD_PROX_EV_EN: | |
295 | if (test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) { | |
296 | dev_err(&client->dev, | |
297 | "Already proximity event enable state\n"); | |
298 | return ret; | |
299 | } | |
300 | set_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags); | |
301 | ||
302 | ret = i2c_smbus_write_byte_data(ps_client, | |
303 | cm36651_ps_reg[CM36651_PS_CONF1], | |
304 | CM36651_PS_INT_EN | CM36651_PS_PERS2 | CM36651_PS_IT2); | |
305 | ||
306 | if (ret < 0) { | |
307 | dev_err(&client->dev, "Proximity enable event failed\n"); | |
308 | return ret; | |
309 | } | |
310 | break; | |
311 | case CM36651_CMD_PROX_EV_DIS: | |
312 | if (!test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) { | |
313 | dev_err(&client->dev, | |
314 | "Already proximity event disable state\n"); | |
315 | return ret; | |
316 | } | |
317 | clear_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags); | |
318 | ret = i2c_smbus_write_byte_data(ps_client, | |
319 | CM36651_PS_CONF1, CM36651_PS_DISABLE); | |
320 | break; | |
321 | } | |
322 | ||
323 | if (ret < 0) | |
324 | dev_err(&client->dev, "Write register failed\n"); | |
325 | ||
326 | return ret; | |
327 | } | |
328 | ||
329 | static int cm36651_read_channel(struct cm36651_data *cm36651, | |
330 | struct iio_chan_spec const *chan, int *val) | |
331 | { | |
332 | struct i2c_client *client = cm36651->client; | |
333 | int cmd, ret; | |
334 | ||
335 | if (chan->type == IIO_LIGHT) | |
336 | cmd = CM36651_CMD_READ_RAW_LIGHT; | |
337 | else if (chan->type == IIO_PROXIMITY) | |
338 | cmd = CM36651_CMD_READ_RAW_PROXIMITY; | |
339 | else | |
340 | return -EINVAL; | |
341 | ||
342 | ret = cm36651_set_operation_mode(cm36651, cmd); | |
343 | if (ret < 0) { | |
344 | dev_err(&client->dev, "CM36651 set operation mode failed\n"); | |
345 | return ret; | |
346 | } | |
347 | /* Delay for work after enable operation */ | |
348 | msleep(50); | |
349 | ret = cm36651_read_output(cm36651, chan, val); | |
350 | if (ret < 0) { | |
351 | dev_err(&client->dev, "CM36651 read output failed\n"); | |
352 | return ret; | |
353 | } | |
354 | ||
355 | return ret; | |
356 | } | |
357 | ||
358 | static int cm36651_read_int_time(struct cm36651_data *cm36651, | |
359 | struct iio_chan_spec const *chan, int *val) | |
360 | { | |
361 | switch (chan->type) { | |
362 | case IIO_LIGHT: | |
363 | if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT1) | |
364 | *val = 80000; | |
365 | else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT2) | |
366 | *val = 160000; | |
367 | else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT3) | |
368 | *val = 320000; | |
369 | else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT4) | |
370 | *val = 640000; | |
371 | else | |
372 | return -EINVAL; | |
373 | break; | |
374 | case IIO_PROXIMITY: | |
375 | if (cm36651->ps_int_time == CM36651_PS_IT1) | |
376 | *val = 320; | |
377 | else if (cm36651->ps_int_time == CM36651_PS_IT2) | |
378 | *val = 420; | |
379 | else if (cm36651->ps_int_time == CM36651_PS_IT3) | |
380 | *val = 520; | |
381 | else if (cm36651->ps_int_time == CM36651_PS_IT4) | |
382 | *val = 640; | |
383 | else | |
384 | return -EINVAL; | |
385 | break; | |
386 | default: | |
387 | return -EINVAL; | |
388 | } | |
389 | ||
390 | return IIO_VAL_INT_PLUS_MICRO; | |
391 | } | |
392 | ||
393 | static int cm36651_write_int_time(struct cm36651_data *cm36651, | |
394 | struct iio_chan_spec const *chan, int val) | |
395 | { | |
396 | struct i2c_client *client = cm36651->client; | |
397 | struct i2c_client *ps_client = cm36651->ps_client; | |
398 | int int_time, ret; | |
399 | ||
400 | switch (chan->type) { | |
401 | case IIO_LIGHT: | |
402 | if (val == 80000) | |
403 | int_time = CM36651_CS_IT1; | |
404 | else if (val == 160000) | |
405 | int_time = CM36651_CS_IT2; | |
406 | else if (val == 320000) | |
407 | int_time = CM36651_CS_IT3; | |
408 | else if (val == 640000) | |
409 | int_time = CM36651_CS_IT4; | |
410 | else | |
411 | return -EINVAL; | |
412 | ||
413 | ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF3, | |
414 | int_time >> 2 * (chan->address)); | |
415 | if (ret < 0) { | |
416 | dev_err(&client->dev, "CS integration time write failed\n"); | |
417 | return ret; | |
418 | } | |
419 | cm36651->cs_int_time[chan->address] = int_time; | |
420 | break; | |
421 | case IIO_PROXIMITY: | |
422 | if (val == 320) | |
423 | int_time = CM36651_PS_IT1; | |
424 | else if (val == 420) | |
425 | int_time = CM36651_PS_IT2; | |
426 | else if (val == 520) | |
427 | int_time = CM36651_PS_IT3; | |
428 | else if (val == 640) | |
429 | int_time = CM36651_PS_IT4; | |
430 | else | |
431 | return -EINVAL; | |
432 | ||
433 | ret = i2c_smbus_write_byte_data(ps_client, | |
434 | CM36651_PS_CONF1, int_time); | |
435 | if (ret < 0) { | |
436 | dev_err(&client->dev, "PS integration time write failed\n"); | |
437 | return ret; | |
438 | } | |
439 | cm36651->ps_int_time = int_time; | |
440 | break; | |
441 | default: | |
442 | return -EINVAL; | |
443 | } | |
444 | ||
445 | return ret; | |
446 | } | |
447 | ||
448 | static int cm36651_read_raw(struct iio_dev *indio_dev, | |
449 | struct iio_chan_spec const *chan, | |
450 | int *val, int *val2, long mask) | |
451 | { | |
452 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
453 | int ret; | |
454 | ||
455 | mutex_lock(&cm36651->lock); | |
456 | ||
457 | switch (mask) { | |
458 | case IIO_CHAN_INFO_RAW: | |
459 | ret = cm36651_read_channel(cm36651, chan, val); | |
460 | break; | |
461 | case IIO_CHAN_INFO_INT_TIME: | |
462 | ret = cm36651_read_int_time(cm36651, chan, val); | |
463 | break; | |
464 | default: | |
465 | ret = -EINVAL; | |
466 | } | |
467 | ||
468 | mutex_unlock(&cm36651->lock); | |
469 | ||
470 | return ret; | |
471 | } | |
472 | ||
473 | static int cm36651_write_raw(struct iio_dev *indio_dev, | |
474 | struct iio_chan_spec const *chan, | |
475 | int val, int val2, long mask) | |
476 | { | |
477 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
478 | struct i2c_client *client = cm36651->client; | |
479 | int ret = -EINVAL; | |
480 | ||
481 | if (mask == IIO_CHAN_INFO_INT_TIME) { | |
482 | ret = cm36651_write_int_time(cm36651, chan, val); | |
483 | if (ret < 0) | |
484 | dev_err(&client->dev, "Integration time write failed\n"); | |
485 | } | |
486 | ||
487 | return ret; | |
488 | } | |
489 | ||
490 | static int cm36651_read_prox_thresh(struct iio_dev *indio_dev, | |
491 | u64 event_code, int *val) | |
492 | { | |
493 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
494 | ||
495 | *val = cm36651->ps_ctrl_regs[CM36651_PS_THD]; | |
496 | ||
497 | return 0; | |
498 | } | |
499 | ||
500 | static int cm36651_write_prox_thresh(struct iio_dev *indio_dev, | |
501 | u64 event_code, int val) | |
502 | { | |
503 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
504 | struct i2c_client *client = cm36651->client; | |
505 | int ret; | |
506 | ||
507 | if (val < 3 || val > 255) | |
508 | return -EINVAL; | |
509 | ||
510 | cm36651->ps_ctrl_regs[CM36651_PS_THD] = val; | |
511 | ret = i2c_smbus_write_byte_data(cm36651->ps_client, CM36651_PS_THD, | |
512 | cm36651->ps_ctrl_regs[CM36651_PS_THD]); | |
513 | ||
514 | if (ret < 0) { | |
515 | dev_err(&client->dev, "PS threshold write failed: %d\n", ret); | |
516 | return ret; | |
517 | } | |
518 | ||
519 | return 0; | |
520 | } | |
521 | ||
522 | static int cm36651_write_prox_event_config(struct iio_dev *indio_dev, | |
523 | u64 event_code, int state) | |
524 | { | |
525 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
526 | int cmd, ret = -EINVAL; | |
527 | ||
528 | mutex_lock(&cm36651->lock); | |
529 | ||
530 | cmd = state ? CM36651_CMD_PROX_EV_EN : CM36651_CMD_PROX_EV_DIS; | |
531 | ret = cm36651_set_operation_mode(cm36651, cmd); | |
532 | ||
533 | mutex_unlock(&cm36651->lock); | |
534 | ||
535 | return ret; | |
536 | } | |
537 | ||
538 | static int cm36651_read_prox_event_config(struct iio_dev *indio_dev, | |
539 | u64 event_code) | |
540 | { | |
541 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
542 | int event_en; | |
543 | ||
544 | mutex_lock(&cm36651->lock); | |
545 | ||
546 | event_en = test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags); | |
547 | ||
548 | mutex_unlock(&cm36651->lock); | |
549 | ||
550 | return event_en; | |
551 | } | |
552 | ||
553 | #define CM36651_LIGHT_CHANNEL(_color, _idx) { \ | |
554 | .type = IIO_LIGHT, \ | |
555 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | |
556 | BIT(IIO_CHAN_INFO_INT_TIME), \ | |
557 | .address = _idx, \ | |
558 | .modified = 1, \ | |
559 | .channel2 = IIO_MOD_LIGHT_##_color, \ | |
560 | } \ | |
561 | ||
562 | static const struct iio_chan_spec cm36651_channels[] = { | |
563 | { | |
564 | .type = IIO_PROXIMITY, | |
565 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
566 | BIT(IIO_CHAN_INFO_INT_TIME), | |
567 | .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER) | |
568 | }, | |
569 | CM36651_LIGHT_CHANNEL(RED, CM36651_LIGHT_CHANNEL_IDX_RED), | |
570 | CM36651_LIGHT_CHANNEL(GREEN, CM36651_LIGHT_CHANNEL_IDX_GREEN), | |
571 | CM36651_LIGHT_CHANNEL(BLUE, CM36651_LIGHT_CHANNEL_IDX_BLUE), | |
572 | CM36651_LIGHT_CHANNEL(CLEAR, CM36651_LIGHT_CHANNEL_IDX_CLEAR), | |
573 | }; | |
574 | ||
575 | static IIO_CONST_ATTR(in_illuminance_integration_time_available, | |
576 | CM36651_CS_INT_TIME_AVAIL); | |
577 | static IIO_CONST_ATTR(in_proximity_integration_time_available, | |
578 | CM36651_PS_INT_TIME_AVAIL); | |
579 | ||
580 | static struct attribute *cm36651_attributes[] = { | |
581 | &iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr, | |
582 | &iio_const_attr_in_proximity_integration_time_available.dev_attr.attr, | |
583 | NULL, | |
584 | }; | |
585 | ||
586 | static const struct attribute_group cm36651_attribute_group = { | |
587 | .attrs = cm36651_attributes | |
588 | }; | |
589 | ||
590 | static const struct iio_info cm36651_info = { | |
591 | .driver_module = THIS_MODULE, | |
592 | .read_raw = &cm36651_read_raw, | |
593 | .write_raw = &cm36651_write_raw, | |
594 | .read_event_value = &cm36651_read_prox_thresh, | |
595 | .write_event_value = &cm36651_write_prox_thresh, | |
596 | .read_event_config = &cm36651_read_prox_event_config, | |
597 | .write_event_config = &cm36651_write_prox_event_config, | |
598 | .attrs = &cm36651_attribute_group, | |
599 | }; | |
600 | ||
601 | static int cm36651_probe(struct i2c_client *client, | |
602 | const struct i2c_device_id *id) | |
603 | { | |
604 | struct cm36651_data *cm36651; | |
605 | struct iio_dev *indio_dev; | |
606 | int ret; | |
607 | ||
608 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm36651)); | |
609 | if (!indio_dev) | |
610 | return -ENOMEM; | |
611 | ||
612 | cm36651 = iio_priv(indio_dev); | |
613 | ||
614 | cm36651->vled_reg = devm_regulator_get(&client->dev, "vled"); | |
615 | if (IS_ERR(cm36651->vled_reg)) { | |
616 | dev_err(&client->dev, "get regulator vled failed\n"); | |
617 | return PTR_ERR(cm36651->vled_reg); | |
618 | } | |
619 | ||
620 | ret = regulator_enable(cm36651->vled_reg); | |
621 | if (ret) { | |
622 | dev_err(&client->dev, "enable regulator vled failed\n"); | |
623 | return ret; | |
624 | } | |
625 | ||
626 | i2c_set_clientdata(client, indio_dev); | |
627 | ||
628 | cm36651->client = client; | |
629 | cm36651->ps_client = i2c_new_dummy(client->adapter, | |
630 | CM36651_I2C_ADDR_PS); | |
631 | cm36651->ara_client = i2c_new_dummy(client->adapter, CM36651_ARA); | |
632 | mutex_init(&cm36651->lock); | |
633 | indio_dev->dev.parent = &client->dev; | |
634 | indio_dev->channels = cm36651_channels; | |
635 | indio_dev->num_channels = ARRAY_SIZE(cm36651_channels); | |
636 | indio_dev->info = &cm36651_info; | |
637 | indio_dev->name = id->name; | |
638 | indio_dev->modes = INDIO_DIRECT_MODE; | |
639 | ||
640 | ret = cm36651_setup_reg(cm36651); | |
641 | if (ret) { | |
642 | dev_err(&client->dev, "%s: register setup failed\n", __func__); | |
643 | goto error_disable_reg; | |
644 | } | |
645 | ||
646 | ret = request_threaded_irq(client->irq, NULL, cm36651_irq_handler, | |
647 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | |
648 | "cm36651", indio_dev); | |
649 | if (ret) { | |
650 | dev_err(&client->dev, "%s: request irq failed\n", __func__); | |
651 | goto error_disable_reg; | |
652 | } | |
653 | ||
654 | ret = iio_device_register(indio_dev); | |
655 | if (ret) { | |
656 | dev_err(&client->dev, "%s: regist device failed\n", __func__); | |
657 | goto error_free_irq; | |
658 | } | |
659 | ||
660 | return 0; | |
661 | ||
662 | error_free_irq: | |
663 | free_irq(client->irq, indio_dev); | |
664 | error_disable_reg: | |
665 | regulator_disable(cm36651->vled_reg); | |
666 | return ret; | |
667 | } | |
668 | ||
669 | static int cm36651_remove(struct i2c_client *client) | |
670 | { | |
671 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
672 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
673 | ||
674 | iio_device_unregister(indio_dev); | |
675 | regulator_disable(cm36651->vled_reg); | |
676 | free_irq(client->irq, indio_dev); | |
677 | ||
678 | return 0; | |
679 | } | |
680 | ||
681 | static const struct i2c_device_id cm36651_id[] = { | |
682 | { "cm36651", 0 }, | |
683 | { } | |
684 | }; | |
685 | ||
686 | MODULE_DEVICE_TABLE(i2c, cm36651_id); | |
687 | ||
688 | static const struct of_device_id cm36651_of_match[] = { | |
689 | { .compatible = "capella,cm36651" }, | |
690 | { } | |
691 | }; | |
692 | ||
693 | static struct i2c_driver cm36651_driver = { | |
694 | .driver = { | |
695 | .name = "cm36651", | |
696 | .of_match_table = of_match_ptr(cm36651_of_match), | |
697 | .owner = THIS_MODULE, | |
698 | }, | |
699 | .probe = cm36651_probe, | |
700 | .remove = cm36651_remove, | |
701 | .id_table = cm36651_id, | |
702 | }; | |
703 | ||
704 | module_i2c_driver(cm36651_driver); | |
705 | ||
706 | MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>"); | |
707 | MODULE_DESCRIPTION("CM36651 proximity/ambient light sensor driver"); | |
708 | MODULE_LICENSE("GPL v2"); |