1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * tcs3472.c - Support for TAOS TCS3472 color light-to-digital converter |
4 | * |
5 | * Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net> |
6 | * |
7 | * Color light sensor with 16-bit channels for red, green, blue, clear); |
8 | * 7-bit I2C slave address 0x39 (TCS34721, TCS34723) or 0x29 (TCS34725, |
9 | * TCS34727) |
10 | * |
11 | * Datasheet: http://ams.com/eng/content/download/319364/1117183/file/TCS3472_Datasheet_EN_v2.pdf |
12 | * |
13 | * TODO: wait time |
14 | */ |
15 | |
16 | #include <linux/module.h> |
17 | #include <linux/i2c.h> |
18 | #include <linux/delay.h> |
19 | #include <linux/pm.h> |
20 | |
21 | #include <linux/iio/iio.h> |
22 | #include <linux/iio/sysfs.h> |
23 | #include <linux/iio/events.h> |
24 | #include <linux/iio/trigger_consumer.h> |
25 | #include <linux/iio/buffer.h> |
26 | #include <linux/iio/triggered_buffer.h> |
27 | |
28 | #define TCS3472_DRV_NAME "tcs3472" |
29 | |
30 | #define TCS3472_COMMAND BIT(7) |
31 | #define TCS3472_AUTO_INCR BIT(5) |
32 | #define TCS3472_SPECIAL_FUNC (BIT(5) | BIT(6)) |
33 | |
34 | #define TCS3472_INTR_CLEAR (TCS3472_COMMAND | TCS3472_SPECIAL_FUNC | 0x06) |
35 | |
36 | #define TCS3472_ENABLE (TCS3472_COMMAND | 0x00) |
37 | #define TCS3472_ATIME (TCS3472_COMMAND | 0x01) |
38 | #define TCS3472_WTIME (TCS3472_COMMAND | 0x03) |
39 | #define TCS3472_AILT (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x04) |
40 | #define TCS3472_AIHT (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x06) |
41 | #define TCS3472_PERS (TCS3472_COMMAND | 0x0c) |
42 | #define TCS3472_CONFIG (TCS3472_COMMAND | 0x0d) |
43 | #define TCS3472_CONTROL (TCS3472_COMMAND | 0x0f) |
44 | #define TCS3472_ID (TCS3472_COMMAND | 0x12) |
45 | #define TCS3472_STATUS (TCS3472_COMMAND | 0x13) |
46 | #define TCS3472_CDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x14) |
47 | #define TCS3472_RDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x16) |
48 | #define TCS3472_GDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x18) |
49 | #define TCS3472_BDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x1a) |
50 | |
51 | #define TCS3472_STATUS_AINT BIT(4) |
52 | #define TCS3472_STATUS_AVALID BIT(0) |
53 | #define TCS3472_ENABLE_AIEN BIT(4) |
54 | #define TCS3472_ENABLE_AEN BIT(1) |
55 | #define TCS3472_ENABLE_PON BIT(0) |
56 | #define TCS3472_CONTROL_AGAIN_MASK (BIT(0) | BIT(1)) |
57 | |
58 | struct tcs3472_data { |
59 | struct i2c_client *client; |
60 | struct mutex lock; |
61 | u16 low_thresh; |
62 | u16 high_thresh; |
63 | u8 enable; |
64 | u8 control; |
65 | u8 atime; |
66 | u8 apers; |
67 | /* Ensure timestamp is naturally aligned */ |
68 | struct { |
69 | u16 chans[4]; |
70 | s64 timestamp __aligned(8); |
71 | } scan; |
72 | }; |
73 | |
74 | static const struct iio_event_spec tcs3472_events[] = { |
75 | { |
76 | .type = IIO_EV_TYPE_THRESH, |
77 | .dir = IIO_EV_DIR_RISING, |
78 | .mask_separate = BIT(IIO_EV_INFO_VALUE), |
79 | }, { |
80 | .type = IIO_EV_TYPE_THRESH, |
81 | .dir = IIO_EV_DIR_FALLING, |
82 | .mask_separate = BIT(IIO_EV_INFO_VALUE), |
83 | }, { |
84 | .type = IIO_EV_TYPE_THRESH, |
85 | .dir = IIO_EV_DIR_EITHER, |
86 | .mask_separate = BIT(IIO_EV_INFO_ENABLE) | |
87 | BIT(IIO_EV_INFO_PERIOD), |
88 | }, |
89 | }; |
90 | |
91 | #define TCS3472_CHANNEL(_color, _si, _addr) { \ |
92 | .type = IIO_INTENSITY, \ |
93 | .modified = 1, \ |
94 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
95 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ |
96 | BIT(IIO_CHAN_INFO_INT_TIME), \ |
97 | .channel2 = IIO_MOD_LIGHT_##_color, \ |
98 | .address = _addr, \ |
99 | .scan_index = _si, \ |
100 | .scan_type = { \ |
101 | .sign = 'u', \ |
102 | .realbits = 16, \ |
103 | .storagebits = 16, \ |
104 | .endianness = IIO_CPU, \ |
105 | }, \ |
106 | .event_spec = _si ? NULL : tcs3472_events, \ |
107 | .num_event_specs = _si ? 0 : ARRAY_SIZE(tcs3472_events), \ |
108 | } |
109 | |
110 | static const int tcs3472_agains[] = { 1, 4, 16, 60 }; |
111 | |
112 | static const struct iio_chan_spec tcs3472_channels[] = { |
113 | TCS3472_CHANNEL(CLEAR, 0, TCS3472_CDATA), |
114 | TCS3472_CHANNEL(RED, 1, TCS3472_RDATA), |
115 | TCS3472_CHANNEL(GREEN, 2, TCS3472_GDATA), |
116 | TCS3472_CHANNEL(BLUE, 3, TCS3472_BDATA), |
117 | IIO_CHAN_SOFT_TIMESTAMP(4), |
118 | }; |
119 | |
120 | static int tcs3472_req_data(struct tcs3472_data *data) |
121 | { |
122 | int tries = 50; |
123 | int ret; |
124 | |
125 | while (tries--) { |
126 | ret = i2c_smbus_read_byte_data(client: data->client, TCS3472_STATUS); |
127 | if (ret < 0) |
128 | return ret; |
129 | if (ret & TCS3472_STATUS_AVALID) |
130 | break; |
131 | msleep(msecs: 20); |
132 | } |
133 | |
134 | if (tries < 0) { |
135 | dev_err(&data->client->dev, "data not ready\n" ); |
136 | return -EIO; |
137 | } |
138 | |
139 | return 0; |
140 | } |
141 | |
142 | static int tcs3472_read_raw(struct iio_dev *indio_dev, |
143 | struct iio_chan_spec const *chan, |
144 | int *val, int *val2, long mask) |
145 | { |
146 | struct tcs3472_data *data = iio_priv(indio_dev); |
147 | int ret; |
148 | |
149 | switch (mask) { |
150 | case IIO_CHAN_INFO_RAW: |
151 | ret = iio_device_claim_direct_mode(indio_dev); |
152 | if (ret) |
153 | return ret; |
154 | ret = tcs3472_req_data(data); |
155 | if (ret < 0) { |
156 | iio_device_release_direct_mode(indio_dev); |
157 | return ret; |
158 | } |
159 | ret = i2c_smbus_read_word_data(client: data->client, command: chan->address); |
160 | iio_device_release_direct_mode(indio_dev); |
161 | if (ret < 0) |
162 | return ret; |
163 | *val = ret; |
164 | return IIO_VAL_INT; |
165 | case IIO_CHAN_INFO_CALIBSCALE: |
166 | *val = tcs3472_agains[data->control & |
167 | TCS3472_CONTROL_AGAIN_MASK]; |
168 | return IIO_VAL_INT; |
169 | case IIO_CHAN_INFO_INT_TIME: |
170 | *val = 0; |
171 | *val2 = (256 - data->atime) * 2400; |
172 | return IIO_VAL_INT_PLUS_MICRO; |
173 | } |
174 | return -EINVAL; |
175 | } |
176 | |
177 | static int tcs3472_write_raw(struct iio_dev *indio_dev, |
178 | struct iio_chan_spec const *chan, |
179 | int val, int val2, long mask) |
180 | { |
181 | struct tcs3472_data *data = iio_priv(indio_dev); |
182 | int i; |
183 | |
184 | switch (mask) { |
185 | case IIO_CHAN_INFO_CALIBSCALE: |
186 | if (val2 != 0) |
187 | return -EINVAL; |
188 | for (i = 0; i < ARRAY_SIZE(tcs3472_agains); i++) { |
189 | if (val == tcs3472_agains[i]) { |
190 | data->control &= ~TCS3472_CONTROL_AGAIN_MASK; |
191 | data->control |= i; |
192 | return i2c_smbus_write_byte_data( |
193 | client: data->client, TCS3472_CONTROL, |
194 | value: data->control); |
195 | } |
196 | } |
197 | return -EINVAL; |
198 | case IIO_CHAN_INFO_INT_TIME: |
199 | if (val != 0) |
200 | return -EINVAL; |
201 | for (i = 0; i < 256; i++) { |
202 | if (val2 == (256 - i) * 2400) { |
203 | data->atime = i; |
204 | return i2c_smbus_write_byte_data( |
205 | client: data->client, TCS3472_ATIME, |
206 | value: data->atime); |
207 | } |
208 | |
209 | } |
210 | return -EINVAL; |
211 | } |
212 | return -EINVAL; |
213 | } |
214 | |
215 | /* |
216 | * Translation from APERS field value to the number of consecutive out-of-range |
217 | * clear channel values before an interrupt is generated |
218 | */ |
219 | static const int tcs3472_intr_pers[] = { |
220 | 0, 1, 2, 3, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60 |
221 | }; |
222 | |
223 | static int tcs3472_read_event(struct iio_dev *indio_dev, |
224 | const struct iio_chan_spec *chan, enum iio_event_type type, |
225 | enum iio_event_direction dir, enum iio_event_info info, int *val, |
226 | int *val2) |
227 | { |
228 | struct tcs3472_data *data = iio_priv(indio_dev); |
229 | int ret; |
230 | unsigned int period; |
231 | |
232 | mutex_lock(&data->lock); |
233 | |
234 | switch (info) { |
235 | case IIO_EV_INFO_VALUE: |
236 | *val = (dir == IIO_EV_DIR_RISING) ? |
237 | data->high_thresh : data->low_thresh; |
238 | ret = IIO_VAL_INT; |
239 | break; |
240 | case IIO_EV_INFO_PERIOD: |
241 | period = (256 - data->atime) * 2400 * |
242 | tcs3472_intr_pers[data->apers]; |
243 | *val = period / USEC_PER_SEC; |
244 | *val2 = period % USEC_PER_SEC; |
245 | ret = IIO_VAL_INT_PLUS_MICRO; |
246 | break; |
247 | default: |
248 | ret = -EINVAL; |
249 | break; |
250 | } |
251 | |
252 | mutex_unlock(lock: &data->lock); |
253 | |
254 | return ret; |
255 | } |
256 | |
257 | static int tcs3472_write_event(struct iio_dev *indio_dev, |
258 | const struct iio_chan_spec *chan, enum iio_event_type type, |
259 | enum iio_event_direction dir, enum iio_event_info info, int val, |
260 | int val2) |
261 | { |
262 | struct tcs3472_data *data = iio_priv(indio_dev); |
263 | int ret; |
264 | u8 command; |
265 | int period; |
266 | int i; |
267 | |
268 | mutex_lock(&data->lock); |
269 | switch (info) { |
270 | case IIO_EV_INFO_VALUE: |
271 | switch (dir) { |
272 | case IIO_EV_DIR_RISING: |
273 | command = TCS3472_AIHT; |
274 | break; |
275 | case IIO_EV_DIR_FALLING: |
276 | command = TCS3472_AILT; |
277 | break; |
278 | default: |
279 | ret = -EINVAL; |
280 | goto error; |
281 | } |
282 | ret = i2c_smbus_write_word_data(client: data->client, command, value: val); |
283 | if (ret) |
284 | goto error; |
285 | |
286 | if (dir == IIO_EV_DIR_RISING) |
287 | data->high_thresh = val; |
288 | else |
289 | data->low_thresh = val; |
290 | break; |
291 | case IIO_EV_INFO_PERIOD: |
292 | period = val * USEC_PER_SEC + val2; |
293 | for (i = 1; i < ARRAY_SIZE(tcs3472_intr_pers) - 1; i++) { |
294 | if (period <= (256 - data->atime) * 2400 * |
295 | tcs3472_intr_pers[i]) |
296 | break; |
297 | } |
298 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_PERS, value: i); |
299 | if (ret) |
300 | goto error; |
301 | |
302 | data->apers = i; |
303 | break; |
304 | default: |
305 | ret = -EINVAL; |
306 | break; |
307 | } |
308 | error: |
309 | mutex_unlock(lock: &data->lock); |
310 | |
311 | return ret; |
312 | } |
313 | |
314 | static int tcs3472_read_event_config(struct iio_dev *indio_dev, |
315 | const struct iio_chan_spec *chan, enum iio_event_type type, |
316 | enum iio_event_direction dir) |
317 | { |
318 | struct tcs3472_data *data = iio_priv(indio_dev); |
319 | int ret; |
320 | |
321 | mutex_lock(&data->lock); |
322 | ret = !!(data->enable & TCS3472_ENABLE_AIEN); |
323 | mutex_unlock(lock: &data->lock); |
324 | |
325 | return ret; |
326 | } |
327 | |
328 | static int tcs3472_write_event_config(struct iio_dev *indio_dev, |
329 | const struct iio_chan_spec *chan, enum iio_event_type type, |
330 | enum iio_event_direction dir, int state) |
331 | { |
332 | struct tcs3472_data *data = iio_priv(indio_dev); |
333 | int ret = 0; |
334 | u8 enable_old; |
335 | |
336 | mutex_lock(&data->lock); |
337 | |
338 | enable_old = data->enable; |
339 | |
340 | if (state) |
341 | data->enable |= TCS3472_ENABLE_AIEN; |
342 | else |
343 | data->enable &= ~TCS3472_ENABLE_AIEN; |
344 | |
345 | if (enable_old != data->enable) { |
346 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_ENABLE, |
347 | value: data->enable); |
348 | if (ret) |
349 | data->enable = enable_old; |
350 | } |
351 | mutex_unlock(lock: &data->lock); |
352 | |
353 | return ret; |
354 | } |
355 | |
356 | static irqreturn_t tcs3472_event_handler(int irq, void *priv) |
357 | { |
358 | struct iio_dev *indio_dev = priv; |
359 | struct tcs3472_data *data = iio_priv(indio_dev); |
360 | int ret; |
361 | |
362 | ret = i2c_smbus_read_byte_data(client: data->client, TCS3472_STATUS); |
363 | if (ret >= 0 && (ret & TCS3472_STATUS_AINT)) { |
364 | iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, |
365 | IIO_EV_TYPE_THRESH, |
366 | IIO_EV_DIR_EITHER), |
367 | timestamp: iio_get_time_ns(indio_dev)); |
368 | |
369 | i2c_smbus_read_byte_data(client: data->client, TCS3472_INTR_CLEAR); |
370 | } |
371 | |
372 | return IRQ_HANDLED; |
373 | } |
374 | |
375 | static irqreturn_t tcs3472_trigger_handler(int irq, void *p) |
376 | { |
377 | struct iio_poll_func *pf = p; |
378 | struct iio_dev *indio_dev = pf->indio_dev; |
379 | struct tcs3472_data *data = iio_priv(indio_dev); |
380 | int i, j = 0; |
381 | |
382 | int ret = tcs3472_req_data(data); |
383 | if (ret < 0) |
384 | goto done; |
385 | |
386 | for_each_set_bit(i, indio_dev->active_scan_mask, |
387 | indio_dev->masklength) { |
388 | ret = i2c_smbus_read_word_data(client: data->client, |
389 | TCS3472_CDATA + 2*i); |
390 | if (ret < 0) |
391 | goto done; |
392 | |
393 | data->scan.chans[j++] = ret; |
394 | } |
395 | |
396 | iio_push_to_buffers_with_timestamp(indio_dev, data: &data->scan, |
397 | timestamp: iio_get_time_ns(indio_dev)); |
398 | |
399 | done: |
400 | iio_trigger_notify_done(trig: indio_dev->trig); |
401 | |
402 | return IRQ_HANDLED; |
403 | } |
404 | |
405 | static ssize_t tcs3472_show_int_time_available(struct device *dev, |
406 | struct device_attribute *attr, |
407 | char *buf) |
408 | { |
409 | size_t len = 0; |
410 | int i; |
411 | |
412 | for (i = 1; i <= 256; i++) |
413 | len += scnprintf(buf: buf + len, PAGE_SIZE - len, fmt: "0.%06d " , |
414 | 2400 * i); |
415 | |
416 | /* replace trailing space by newline */ |
417 | buf[len - 1] = '\n'; |
418 | |
419 | return len; |
420 | } |
421 | |
422 | static IIO_CONST_ATTR(calibscale_available, "1 4 16 60" ); |
423 | static IIO_DEV_ATTR_INT_TIME_AVAIL(tcs3472_show_int_time_available); |
424 | |
425 | static struct attribute *tcs3472_attributes[] = { |
426 | &iio_const_attr_calibscale_available.dev_attr.attr, |
427 | &iio_dev_attr_integration_time_available.dev_attr.attr, |
428 | NULL |
429 | }; |
430 | |
431 | static const struct attribute_group tcs3472_attribute_group = { |
432 | .attrs = tcs3472_attributes, |
433 | }; |
434 | |
435 | static const struct iio_info tcs3472_info = { |
436 | .read_raw = tcs3472_read_raw, |
437 | .write_raw = tcs3472_write_raw, |
438 | .read_event_value = tcs3472_read_event, |
439 | .write_event_value = tcs3472_write_event, |
440 | .read_event_config = tcs3472_read_event_config, |
441 | .write_event_config = tcs3472_write_event_config, |
442 | .attrs = &tcs3472_attribute_group, |
443 | }; |
444 | |
445 | static int tcs3472_probe(struct i2c_client *client) |
446 | { |
447 | struct tcs3472_data *data; |
448 | struct iio_dev *indio_dev; |
449 | int ret; |
450 | |
451 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
452 | if (indio_dev == NULL) |
453 | return -ENOMEM; |
454 | |
455 | data = iio_priv(indio_dev); |
456 | i2c_set_clientdata(client, data: indio_dev); |
457 | data->client = client; |
458 | mutex_init(&data->lock); |
459 | |
460 | indio_dev->info = &tcs3472_info; |
461 | indio_dev->name = TCS3472_DRV_NAME; |
462 | indio_dev->channels = tcs3472_channels; |
463 | indio_dev->num_channels = ARRAY_SIZE(tcs3472_channels); |
464 | indio_dev->modes = INDIO_DIRECT_MODE; |
465 | |
466 | ret = i2c_smbus_read_byte_data(client: data->client, TCS3472_ID); |
467 | if (ret < 0) |
468 | return ret; |
469 | |
470 | if (ret == 0x44) |
471 | dev_info(&client->dev, "TCS34721/34725 found\n" ); |
472 | else if (ret == 0x4d) |
473 | dev_info(&client->dev, "TCS34723/34727 found\n" ); |
474 | else |
475 | return -ENODEV; |
476 | |
477 | ret = i2c_smbus_read_byte_data(client: data->client, TCS3472_CONTROL); |
478 | if (ret < 0) |
479 | return ret; |
480 | data->control = ret; |
481 | |
482 | ret = i2c_smbus_read_byte_data(client: data->client, TCS3472_ATIME); |
483 | if (ret < 0) |
484 | return ret; |
485 | data->atime = ret; |
486 | |
487 | ret = i2c_smbus_read_word_data(client: data->client, TCS3472_AILT); |
488 | if (ret < 0) |
489 | return ret; |
490 | data->low_thresh = ret; |
491 | |
492 | ret = i2c_smbus_read_word_data(client: data->client, TCS3472_AIHT); |
493 | if (ret < 0) |
494 | return ret; |
495 | data->high_thresh = ret; |
496 | |
497 | data->apers = 1; |
498 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_PERS, |
499 | value: data->apers); |
500 | if (ret < 0) |
501 | return ret; |
502 | |
503 | ret = i2c_smbus_read_byte_data(client: data->client, TCS3472_ENABLE); |
504 | if (ret < 0) |
505 | return ret; |
506 | |
507 | /* enable device */ |
508 | data->enable = ret | TCS3472_ENABLE_PON | TCS3472_ENABLE_AEN; |
509 | data->enable &= ~TCS3472_ENABLE_AIEN; |
510 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_ENABLE, |
511 | value: data->enable); |
512 | if (ret < 0) |
513 | return ret; |
514 | |
515 | ret = iio_triggered_buffer_setup(indio_dev, NULL, |
516 | tcs3472_trigger_handler, NULL); |
517 | if (ret < 0) |
518 | return ret; |
519 | |
520 | if (client->irq) { |
521 | ret = request_threaded_irq(irq: client->irq, NULL, |
522 | thread_fn: tcs3472_event_handler, |
523 | IRQF_TRIGGER_FALLING | IRQF_SHARED | |
524 | IRQF_ONESHOT, |
525 | name: client->name, dev: indio_dev); |
526 | if (ret) |
527 | goto buffer_cleanup; |
528 | } |
529 | |
530 | ret = iio_device_register(indio_dev); |
531 | if (ret < 0) |
532 | goto free_irq; |
533 | |
534 | return 0; |
535 | |
536 | free_irq: |
537 | if (client->irq) |
538 | free_irq(client->irq, indio_dev); |
539 | buffer_cleanup: |
540 | iio_triggered_buffer_cleanup(indio_dev); |
541 | return ret; |
542 | } |
543 | |
544 | static int tcs3472_powerdown(struct tcs3472_data *data) |
545 | { |
546 | int ret; |
547 | u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; |
548 | |
549 | mutex_lock(&data->lock); |
550 | |
551 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_ENABLE, |
552 | value: data->enable & ~enable_mask); |
553 | if (!ret) |
554 | data->enable &= ~enable_mask; |
555 | |
556 | mutex_unlock(lock: &data->lock); |
557 | |
558 | return ret; |
559 | } |
560 | |
561 | static void tcs3472_remove(struct i2c_client *client) |
562 | { |
563 | struct iio_dev *indio_dev = i2c_get_clientdata(client); |
564 | |
565 | iio_device_unregister(indio_dev); |
566 | if (client->irq) |
567 | free_irq(client->irq, indio_dev); |
568 | iio_triggered_buffer_cleanup(indio_dev); |
569 | tcs3472_powerdown(data: iio_priv(indio_dev)); |
570 | } |
571 | |
572 | static int tcs3472_suspend(struct device *dev) |
573 | { |
574 | struct tcs3472_data *data = iio_priv(indio_dev: i2c_get_clientdata( |
575 | to_i2c_client(dev))); |
576 | return tcs3472_powerdown(data); |
577 | } |
578 | |
579 | static int tcs3472_resume(struct device *dev) |
580 | { |
581 | struct tcs3472_data *data = iio_priv(indio_dev: i2c_get_clientdata( |
582 | to_i2c_client(dev))); |
583 | int ret; |
584 | u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; |
585 | |
586 | mutex_lock(&data->lock); |
587 | |
588 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_ENABLE, |
589 | value: data->enable | enable_mask); |
590 | if (!ret) |
591 | data->enable |= enable_mask; |
592 | |
593 | mutex_unlock(lock: &data->lock); |
594 | |
595 | return ret; |
596 | } |
597 | |
598 | static DEFINE_SIMPLE_DEV_PM_OPS(tcs3472_pm_ops, tcs3472_suspend, |
599 | tcs3472_resume); |
600 | |
601 | static const struct i2c_device_id tcs3472_id[] = { |
602 | { "tcs3472" , 0 }, |
603 | { } |
604 | }; |
605 | MODULE_DEVICE_TABLE(i2c, tcs3472_id); |
606 | |
607 | static struct i2c_driver tcs3472_driver = { |
608 | .driver = { |
609 | .name = TCS3472_DRV_NAME, |
610 | .pm = pm_sleep_ptr(&tcs3472_pm_ops), |
611 | }, |
612 | .probe = tcs3472_probe, |
613 | .remove = tcs3472_remove, |
614 | .id_table = tcs3472_id, |
615 | }; |
616 | module_i2c_driver(tcs3472_driver); |
617 | |
618 | MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>" ); |
619 | MODULE_DESCRIPTION("TCS3472 color light sensors driver" ); |
620 | MODULE_LICENSE("GPL" ); |
621 | |