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 */ | |
26c17a1c BS |
53 | #define CM36651_CS_IT1 0x00 /* Integration time 80 msec */ |
54 | #define CM36651_CS_IT2 0x40 /* Integration time 160 msec */ | |
55 | #define CM36651_CS_IT3 0x80 /* Integration time 320 msec */ | |
56 | #define CM36651_CS_IT4 0xC0 /* Integration time 640 msec */ | |
e590d451 BS |
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 */ | |
26c17a1c BS |
67 | #define CM36651_PS_IT1 0x00 /* Integration time 0.32 msec */ |
68 | #define CM36651_PS_IT2 0x10 /* Integration time 0.42 msec */ | |
69 | #define CM36651_PS_IT3 0x20 /* Integration time 0.52 msec */ | |
70 | #define CM36651_PS_IT4 0x30 /* Integration time 0.64 msec */ | |
e590d451 BS |
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 | ||
26c17a1c BS |
96 | #define CM36651_CS_INT_TIME_AVAIL "0.08 0.16 0.32 0.64" |
97 | #define CM36651_PS_INT_TIME_AVAIL "0.000320 0.000420 0.000520 0.000640" | |
e590d451 BS |
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, | |
26c17a1c | 359 | struct iio_chan_spec const *chan, int *val2) |
e590d451 BS |
360 | { |
361 | switch (chan->type) { | |
362 | case IIO_LIGHT: | |
363 | if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT1) | |
26c17a1c | 364 | *val2 = 80000; |
e590d451 | 365 | else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT2) |
26c17a1c | 366 | *val2 = 160000; |
e590d451 | 367 | else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT3) |
26c17a1c | 368 | *val2 = 320000; |
e590d451 | 369 | else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT4) |
26c17a1c | 370 | *val2 = 640000; |
e590d451 BS |
371 | else |
372 | return -EINVAL; | |
373 | break; | |
374 | case IIO_PROXIMITY: | |
375 | if (cm36651->ps_int_time == CM36651_PS_IT1) | |
26c17a1c | 376 | *val2 = 320; |
e590d451 | 377 | else if (cm36651->ps_int_time == CM36651_PS_IT2) |
26c17a1c | 378 | *val2 = 420; |
e590d451 | 379 | else if (cm36651->ps_int_time == CM36651_PS_IT3) |
26c17a1c | 380 | *val2 = 520; |
e590d451 | 381 | else if (cm36651->ps_int_time == CM36651_PS_IT4) |
26c17a1c | 382 | *val2 = 640; |
e590d451 BS |
383 | else |
384 | return -EINVAL; | |
385 | break; | |
386 | default: | |
387 | return -EINVAL; | |
388 | } | |
389 | ||
26c17a1c | 390 | return IIO_VAL_INT_PLUS_MICRO; |
e590d451 BS |
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: | |
26c17a1c BS |
462 | *val = 0; |
463 | ret = cm36651_read_int_time(cm36651, chan, val2); | |
e590d451 BS |
464 | break; |
465 | default: | |
466 | ret = -EINVAL; | |
467 | } | |
468 | ||
469 | mutex_unlock(&cm36651->lock); | |
470 | ||
471 | return ret; | |
472 | } | |
473 | ||
474 | static int cm36651_write_raw(struct iio_dev *indio_dev, | |
475 | struct iio_chan_spec const *chan, | |
476 | int val, int val2, long mask) | |
477 | { | |
478 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
479 | struct i2c_client *client = cm36651->client; | |
480 | int ret = -EINVAL; | |
481 | ||
482 | if (mask == IIO_CHAN_INFO_INT_TIME) { | |
26c17a1c | 483 | ret = cm36651_write_int_time(cm36651, chan, val2); |
e590d451 BS |
484 | if (ret < 0) |
485 | dev_err(&client->dev, "Integration time write failed\n"); | |
486 | } | |
487 | ||
488 | return ret; | |
489 | } | |
490 | ||
491 | static int cm36651_read_prox_thresh(struct iio_dev *indio_dev, | |
bb7f9d90 LPC |
492 | const struct iio_chan_spec *chan, |
493 | enum iio_event_type type, | |
494 | enum iio_event_direction dir, | |
495 | enum iio_event_info info, | |
496 | int *val, int *val2) | |
e590d451 BS |
497 | { |
498 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
499 | ||
500 | *val = cm36651->ps_ctrl_regs[CM36651_PS_THD]; | |
501 | ||
502 | return 0; | |
503 | } | |
504 | ||
505 | static int cm36651_write_prox_thresh(struct iio_dev *indio_dev, | |
bb7f9d90 LPC |
506 | const struct iio_chan_spec *chan, |
507 | enum iio_event_type type, | |
508 | enum iio_event_direction dir, | |
509 | enum iio_event_info info, | |
510 | int val, int val2) | |
e590d451 BS |
511 | { |
512 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
513 | struct i2c_client *client = cm36651->client; | |
514 | int ret; | |
515 | ||
516 | if (val < 3 || val > 255) | |
517 | return -EINVAL; | |
518 | ||
519 | cm36651->ps_ctrl_regs[CM36651_PS_THD] = val; | |
520 | ret = i2c_smbus_write_byte_data(cm36651->ps_client, CM36651_PS_THD, | |
521 | cm36651->ps_ctrl_regs[CM36651_PS_THD]); | |
522 | ||
523 | if (ret < 0) { | |
524 | dev_err(&client->dev, "PS threshold write failed: %d\n", ret); | |
525 | return ret; | |
526 | } | |
527 | ||
528 | return 0; | |
529 | } | |
530 | ||
531 | static int cm36651_write_prox_event_config(struct iio_dev *indio_dev, | |
bb7f9d90 LPC |
532 | const struct iio_chan_spec *chan, |
533 | enum iio_event_type type, | |
534 | enum iio_event_direction dir, | |
535 | int state) | |
e590d451 BS |
536 | { |
537 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
538 | int cmd, ret = -EINVAL; | |
539 | ||
540 | mutex_lock(&cm36651->lock); | |
541 | ||
542 | cmd = state ? CM36651_CMD_PROX_EV_EN : CM36651_CMD_PROX_EV_DIS; | |
543 | ret = cm36651_set_operation_mode(cm36651, cmd); | |
544 | ||
545 | mutex_unlock(&cm36651->lock); | |
546 | ||
547 | return ret; | |
548 | } | |
549 | ||
550 | static int cm36651_read_prox_event_config(struct iio_dev *indio_dev, | |
bb7f9d90 LPC |
551 | const struct iio_chan_spec *chan, |
552 | enum iio_event_type type, | |
553 | enum iio_event_direction dir) | |
e590d451 BS |
554 | { |
555 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
556 | int event_en; | |
557 | ||
558 | mutex_lock(&cm36651->lock); | |
559 | ||
560 | event_en = test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags); | |
561 | ||
562 | mutex_unlock(&cm36651->lock); | |
563 | ||
564 | return event_en; | |
565 | } | |
566 | ||
567 | #define CM36651_LIGHT_CHANNEL(_color, _idx) { \ | |
568 | .type = IIO_LIGHT, \ | |
569 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | |
570 | BIT(IIO_CHAN_INFO_INT_TIME), \ | |
571 | .address = _idx, \ | |
572 | .modified = 1, \ | |
573 | .channel2 = IIO_MOD_LIGHT_##_color, \ | |
574 | } \ | |
575 | ||
bb7f9d90 LPC |
576 | static const struct iio_event_spec cm36651_event_spec[] = { |
577 | { | |
578 | .type = IIO_EV_TYPE_THRESH, | |
579 | .dir = IIO_EV_DIR_EITHER, | |
580 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | | |
581 | BIT(IIO_EV_INFO_ENABLE), | |
582 | } | |
583 | }; | |
584 | ||
e590d451 BS |
585 | static const struct iio_chan_spec cm36651_channels[] = { |
586 | { | |
587 | .type = IIO_PROXIMITY, | |
588 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
589 | BIT(IIO_CHAN_INFO_INT_TIME), | |
bb7f9d90 LPC |
590 | .event_spec = cm36651_event_spec, |
591 | .num_event_specs = ARRAY_SIZE(cm36651_event_spec), | |
e590d451 BS |
592 | }, |
593 | CM36651_LIGHT_CHANNEL(RED, CM36651_LIGHT_CHANNEL_IDX_RED), | |
594 | CM36651_LIGHT_CHANNEL(GREEN, CM36651_LIGHT_CHANNEL_IDX_GREEN), | |
595 | CM36651_LIGHT_CHANNEL(BLUE, CM36651_LIGHT_CHANNEL_IDX_BLUE), | |
596 | CM36651_LIGHT_CHANNEL(CLEAR, CM36651_LIGHT_CHANNEL_IDX_CLEAR), | |
597 | }; | |
598 | ||
599 | static IIO_CONST_ATTR(in_illuminance_integration_time_available, | |
600 | CM36651_CS_INT_TIME_AVAIL); | |
601 | static IIO_CONST_ATTR(in_proximity_integration_time_available, | |
602 | CM36651_PS_INT_TIME_AVAIL); | |
603 | ||
604 | static struct attribute *cm36651_attributes[] = { | |
605 | &iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr, | |
606 | &iio_const_attr_in_proximity_integration_time_available.dev_attr.attr, | |
607 | NULL, | |
608 | }; | |
609 | ||
610 | static const struct attribute_group cm36651_attribute_group = { | |
611 | .attrs = cm36651_attributes | |
612 | }; | |
613 | ||
614 | static const struct iio_info cm36651_info = { | |
615 | .driver_module = THIS_MODULE, | |
616 | .read_raw = &cm36651_read_raw, | |
617 | .write_raw = &cm36651_write_raw, | |
618 | .read_event_value = &cm36651_read_prox_thresh, | |
619 | .write_event_value = &cm36651_write_prox_thresh, | |
620 | .read_event_config = &cm36651_read_prox_event_config, | |
621 | .write_event_config = &cm36651_write_prox_event_config, | |
622 | .attrs = &cm36651_attribute_group, | |
623 | }; | |
624 | ||
625 | static int cm36651_probe(struct i2c_client *client, | |
626 | const struct i2c_device_id *id) | |
627 | { | |
628 | struct cm36651_data *cm36651; | |
629 | struct iio_dev *indio_dev; | |
630 | int ret; | |
631 | ||
632 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm36651)); | |
633 | if (!indio_dev) | |
634 | return -ENOMEM; | |
635 | ||
636 | cm36651 = iio_priv(indio_dev); | |
637 | ||
638 | cm36651->vled_reg = devm_regulator_get(&client->dev, "vled"); | |
639 | if (IS_ERR(cm36651->vled_reg)) { | |
640 | dev_err(&client->dev, "get regulator vled failed\n"); | |
641 | return PTR_ERR(cm36651->vled_reg); | |
642 | } | |
643 | ||
644 | ret = regulator_enable(cm36651->vled_reg); | |
645 | if (ret) { | |
646 | dev_err(&client->dev, "enable regulator vled failed\n"); | |
647 | return ret; | |
648 | } | |
649 | ||
650 | i2c_set_clientdata(client, indio_dev); | |
651 | ||
652 | cm36651->client = client; | |
653 | cm36651->ps_client = i2c_new_dummy(client->adapter, | |
654 | CM36651_I2C_ADDR_PS); | |
655 | cm36651->ara_client = i2c_new_dummy(client->adapter, CM36651_ARA); | |
656 | mutex_init(&cm36651->lock); | |
657 | indio_dev->dev.parent = &client->dev; | |
658 | indio_dev->channels = cm36651_channels; | |
659 | indio_dev->num_channels = ARRAY_SIZE(cm36651_channels); | |
660 | indio_dev->info = &cm36651_info; | |
661 | indio_dev->name = id->name; | |
662 | indio_dev->modes = INDIO_DIRECT_MODE; | |
663 | ||
664 | ret = cm36651_setup_reg(cm36651); | |
665 | if (ret) { | |
666 | dev_err(&client->dev, "%s: register setup failed\n", __func__); | |
667 | goto error_disable_reg; | |
668 | } | |
669 | ||
670 | ret = request_threaded_irq(client->irq, NULL, cm36651_irq_handler, | |
671 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | |
672 | "cm36651", indio_dev); | |
673 | if (ret) { | |
674 | dev_err(&client->dev, "%s: request irq failed\n", __func__); | |
675 | goto error_disable_reg; | |
676 | } | |
677 | ||
678 | ret = iio_device_register(indio_dev); | |
679 | if (ret) { | |
680 | dev_err(&client->dev, "%s: regist device failed\n", __func__); | |
681 | goto error_free_irq; | |
682 | } | |
683 | ||
684 | return 0; | |
685 | ||
686 | error_free_irq: | |
687 | free_irq(client->irq, indio_dev); | |
688 | error_disable_reg: | |
689 | regulator_disable(cm36651->vled_reg); | |
690 | return ret; | |
691 | } | |
692 | ||
693 | static int cm36651_remove(struct i2c_client *client) | |
694 | { | |
695 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
696 | struct cm36651_data *cm36651 = iio_priv(indio_dev); | |
697 | ||
698 | iio_device_unregister(indio_dev); | |
699 | regulator_disable(cm36651->vled_reg); | |
700 | free_irq(client->irq, indio_dev); | |
701 | ||
702 | return 0; | |
703 | } | |
704 | ||
705 | static const struct i2c_device_id cm36651_id[] = { | |
706 | { "cm36651", 0 }, | |
707 | { } | |
708 | }; | |
709 | ||
710 | MODULE_DEVICE_TABLE(i2c, cm36651_id); | |
711 | ||
712 | static const struct of_device_id cm36651_of_match[] = { | |
713 | { .compatible = "capella,cm36651" }, | |
714 | { } | |
715 | }; | |
716 | ||
717 | static struct i2c_driver cm36651_driver = { | |
718 | .driver = { | |
719 | .name = "cm36651", | |
a451521d | 720 | .of_match_table = cm36651_of_match, |
e590d451 BS |
721 | .owner = THIS_MODULE, |
722 | }, | |
723 | .probe = cm36651_probe, | |
724 | .remove = cm36651_remove, | |
725 | .id_table = cm36651_id, | |
726 | }; | |
727 | ||
728 | module_i2c_driver(cm36651_driver); | |
729 | ||
730 | MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>"); | |
731 | MODULE_DESCRIPTION("CM36651 proximity/ambient light sensor driver"); | |
732 | MODULE_LICENSE("GPL v2"); |