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 | aligned_s64 timestamp; |
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 | if (!iio_device_claim_direct(indio_dev)) |
152 | return -EBUSY; |
153 | ret = tcs3472_req_data(data); |
154 | if (ret < 0) { |
155 | iio_device_release_direct(indio_dev); |
156 | return ret; |
157 | } |
158 | ret = i2c_smbus_read_word_data(client: data->client, command: chan->address); |
159 | iio_device_release_direct(indio_dev); |
160 | if (ret < 0) |
161 | return ret; |
162 | *val = ret; |
163 | return IIO_VAL_INT; |
164 | case IIO_CHAN_INFO_CALIBSCALE: |
165 | *val = tcs3472_agains[data->control & |
166 | TCS3472_CONTROL_AGAIN_MASK]; |
167 | return IIO_VAL_INT; |
168 | case IIO_CHAN_INFO_INT_TIME: |
169 | *val = 0; |
170 | *val2 = (256 - data->atime) * 2400; |
171 | return IIO_VAL_INT_PLUS_MICRO; |
172 | } |
173 | return -EINVAL; |
174 | } |
175 | |
176 | static int tcs3472_write_raw(struct iio_dev *indio_dev, |
177 | struct iio_chan_spec const *chan, |
178 | int val, int val2, long mask) |
179 | { |
180 | struct tcs3472_data *data = iio_priv(indio_dev); |
181 | int i; |
182 | |
183 | switch (mask) { |
184 | case IIO_CHAN_INFO_CALIBSCALE: |
185 | if (val2 != 0) |
186 | return -EINVAL; |
187 | for (i = 0; i < ARRAY_SIZE(tcs3472_agains); i++) { |
188 | if (val == tcs3472_agains[i]) { |
189 | data->control &= ~TCS3472_CONTROL_AGAIN_MASK; |
190 | data->control |= i; |
191 | return i2c_smbus_write_byte_data( |
192 | client: data->client, TCS3472_CONTROL, |
193 | value: data->control); |
194 | } |
195 | } |
196 | return -EINVAL; |
197 | case IIO_CHAN_INFO_INT_TIME: |
198 | if (val != 0) |
199 | return -EINVAL; |
200 | for (i = 0; i < 256; i++) { |
201 | if (val2 == (256 - i) * 2400) { |
202 | data->atime = i; |
203 | return i2c_smbus_write_byte_data( |
204 | client: data->client, TCS3472_ATIME, |
205 | value: data->atime); |
206 | } |
207 | |
208 | } |
209 | return -EINVAL; |
210 | } |
211 | return -EINVAL; |
212 | } |
213 | |
214 | /* |
215 | * Translation from APERS field value to the number of consecutive out-of-range |
216 | * clear channel values before an interrupt is generated |
217 | */ |
218 | static const int tcs3472_intr_pers[] = { |
219 | 0, 1, 2, 3, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60 |
220 | }; |
221 | |
222 | static int tcs3472_read_event(struct iio_dev *indio_dev, |
223 | const struct iio_chan_spec *chan, enum iio_event_type type, |
224 | enum iio_event_direction dir, enum iio_event_info info, int *val, |
225 | int *val2) |
226 | { |
227 | struct tcs3472_data *data = iio_priv(indio_dev); |
228 | int ret; |
229 | unsigned int period; |
230 | |
231 | mutex_lock(&data->lock); |
232 | |
233 | switch (info) { |
234 | case IIO_EV_INFO_VALUE: |
235 | *val = (dir == IIO_EV_DIR_RISING) ? |
236 | data->high_thresh : data->low_thresh; |
237 | ret = IIO_VAL_INT; |
238 | break; |
239 | case IIO_EV_INFO_PERIOD: |
240 | period = (256 - data->atime) * 2400 * |
241 | tcs3472_intr_pers[data->apers]; |
242 | *val = period / USEC_PER_SEC; |
243 | *val2 = period % USEC_PER_SEC; |
244 | ret = IIO_VAL_INT_PLUS_MICRO; |
245 | break; |
246 | default: |
247 | ret = -EINVAL; |
248 | break; |
249 | } |
250 | |
251 | mutex_unlock(lock: &data->lock); |
252 | |
253 | return ret; |
254 | } |
255 | |
256 | static int tcs3472_write_event(struct iio_dev *indio_dev, |
257 | const struct iio_chan_spec *chan, enum iio_event_type type, |
258 | enum iio_event_direction dir, enum iio_event_info info, int val, |
259 | int val2) |
260 | { |
261 | struct tcs3472_data *data = iio_priv(indio_dev); |
262 | int ret; |
263 | u8 command; |
264 | int period; |
265 | int i; |
266 | |
267 | mutex_lock(&data->lock); |
268 | switch (info) { |
269 | case IIO_EV_INFO_VALUE: |
270 | switch (dir) { |
271 | case IIO_EV_DIR_RISING: |
272 | command = TCS3472_AIHT; |
273 | break; |
274 | case IIO_EV_DIR_FALLING: |
275 | command = TCS3472_AILT; |
276 | break; |
277 | default: |
278 | ret = -EINVAL; |
279 | goto error; |
280 | } |
281 | ret = i2c_smbus_write_word_data(client: data->client, command, value: val); |
282 | if (ret) |
283 | goto error; |
284 | |
285 | if (dir == IIO_EV_DIR_RISING) |
286 | data->high_thresh = val; |
287 | else |
288 | data->low_thresh = val; |
289 | break; |
290 | case IIO_EV_INFO_PERIOD: |
291 | period = val * USEC_PER_SEC + val2; |
292 | for (i = 1; i < ARRAY_SIZE(tcs3472_intr_pers) - 1; i++) { |
293 | if (period <= (256 - data->atime) * 2400 * |
294 | tcs3472_intr_pers[i]) |
295 | break; |
296 | } |
297 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_PERS, value: i); |
298 | if (ret) |
299 | goto error; |
300 | |
301 | data->apers = i; |
302 | break; |
303 | default: |
304 | ret = -EINVAL; |
305 | break; |
306 | } |
307 | error: |
308 | mutex_unlock(lock: &data->lock); |
309 | |
310 | return ret; |
311 | } |
312 | |
313 | static int tcs3472_read_event_config(struct iio_dev *indio_dev, |
314 | const struct iio_chan_spec *chan, enum iio_event_type type, |
315 | enum iio_event_direction dir) |
316 | { |
317 | struct tcs3472_data *data = iio_priv(indio_dev); |
318 | int ret; |
319 | |
320 | mutex_lock(&data->lock); |
321 | ret = !!(data->enable & TCS3472_ENABLE_AIEN); |
322 | mutex_unlock(lock: &data->lock); |
323 | |
324 | return ret; |
325 | } |
326 | |
327 | static int tcs3472_write_event_config(struct iio_dev *indio_dev, |
328 | const struct iio_chan_spec *chan, enum iio_event_type type, |
329 | enum iio_event_direction dir, bool state) |
330 | { |
331 | struct tcs3472_data *data = iio_priv(indio_dev); |
332 | int ret = 0; |
333 | u8 enable_old; |
334 | |
335 | mutex_lock(&data->lock); |
336 | |
337 | enable_old = data->enable; |
338 | |
339 | if (state) |
340 | data->enable |= TCS3472_ENABLE_AIEN; |
341 | else |
342 | data->enable &= ~TCS3472_ENABLE_AIEN; |
343 | |
344 | if (enable_old != data->enable) { |
345 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_ENABLE, |
346 | value: data->enable); |
347 | if (ret) |
348 | data->enable = enable_old; |
349 | } |
350 | mutex_unlock(lock: &data->lock); |
351 | |
352 | return ret; |
353 | } |
354 | |
355 | static irqreturn_t tcs3472_event_handler(int irq, void *priv) |
356 | { |
357 | struct iio_dev *indio_dev = priv; |
358 | struct tcs3472_data *data = iio_priv(indio_dev); |
359 | int ret; |
360 | |
361 | ret = i2c_smbus_read_byte_data(client: data->client, TCS3472_STATUS); |
362 | if (ret >= 0 && (ret & TCS3472_STATUS_AINT)) { |
363 | iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, |
364 | IIO_EV_TYPE_THRESH, |
365 | IIO_EV_DIR_EITHER), |
366 | timestamp: iio_get_time_ns(indio_dev)); |
367 | |
368 | i2c_smbus_read_byte_data(client: data->client, TCS3472_INTR_CLEAR); |
369 | } |
370 | |
371 | return IRQ_HANDLED; |
372 | } |
373 | |
374 | static irqreturn_t tcs3472_trigger_handler(int irq, void *p) |
375 | { |
376 | struct iio_poll_func *pf = p; |
377 | struct iio_dev *indio_dev = pf->indio_dev; |
378 | struct tcs3472_data *data = iio_priv(indio_dev); |
379 | int i, j = 0; |
380 | |
381 | int ret = tcs3472_req_data(data); |
382 | if (ret < 0) |
383 | goto done; |
384 | |
385 | iio_for_each_active_channel(indio_dev, i) { |
386 | ret = i2c_smbus_read_word_data(client: data->client, |
387 | TCS3472_CDATA + 2*i); |
388 | if (ret < 0) |
389 | goto done; |
390 | |
391 | data->scan.chans[j++] = ret; |
392 | } |
393 | |
394 | iio_push_to_buffers_with_timestamp(indio_dev, data: &data->scan, |
395 | timestamp: iio_get_time_ns(indio_dev)); |
396 | |
397 | done: |
398 | iio_trigger_notify_done(trig: indio_dev->trig); |
399 | |
400 | return IRQ_HANDLED; |
401 | } |
402 | |
403 | static ssize_t tcs3472_show_int_time_available(struct device *dev, |
404 | struct device_attribute *attr, |
405 | char *buf) |
406 | { |
407 | size_t len = 0; |
408 | int i; |
409 | |
410 | for (i = 1; i <= 256; i++) |
411 | len += scnprintf(buf: buf + len, PAGE_SIZE - len, fmt: "0.%06d " , |
412 | 2400 * i); |
413 | |
414 | /* replace trailing space by newline */ |
415 | buf[len - 1] = '\n'; |
416 | |
417 | return len; |
418 | } |
419 | |
420 | static IIO_CONST_ATTR(calibscale_available, "1 4 16 60" ); |
421 | static IIO_DEV_ATTR_INT_TIME_AVAIL(tcs3472_show_int_time_available); |
422 | |
423 | static struct attribute *tcs3472_attributes[] = { |
424 | &iio_const_attr_calibscale_available.dev_attr.attr, |
425 | &iio_dev_attr_integration_time_available.dev_attr.attr, |
426 | NULL |
427 | }; |
428 | |
429 | static const struct attribute_group tcs3472_attribute_group = { |
430 | .attrs = tcs3472_attributes, |
431 | }; |
432 | |
433 | static const struct iio_info tcs3472_info = { |
434 | .read_raw = tcs3472_read_raw, |
435 | .write_raw = tcs3472_write_raw, |
436 | .read_event_value = tcs3472_read_event, |
437 | .write_event_value = tcs3472_write_event, |
438 | .read_event_config = tcs3472_read_event_config, |
439 | .write_event_config = tcs3472_write_event_config, |
440 | .attrs = &tcs3472_attribute_group, |
441 | }; |
442 | |
443 | static int tcs3472_probe(struct i2c_client *client) |
444 | { |
445 | struct tcs3472_data *data; |
446 | struct iio_dev *indio_dev; |
447 | int ret; |
448 | |
449 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
450 | if (indio_dev == NULL) |
451 | return -ENOMEM; |
452 | |
453 | data = iio_priv(indio_dev); |
454 | i2c_set_clientdata(client, data: indio_dev); |
455 | data->client = client; |
456 | mutex_init(&data->lock); |
457 | |
458 | indio_dev->info = &tcs3472_info; |
459 | indio_dev->name = TCS3472_DRV_NAME; |
460 | indio_dev->channels = tcs3472_channels; |
461 | indio_dev->num_channels = ARRAY_SIZE(tcs3472_channels); |
462 | indio_dev->modes = INDIO_DIRECT_MODE; |
463 | |
464 | ret = i2c_smbus_read_byte_data(client: data->client, TCS3472_ID); |
465 | if (ret < 0) |
466 | return ret; |
467 | |
468 | if (ret == 0x44) |
469 | dev_info(&client->dev, "TCS34721/34725 found\n" ); |
470 | else if (ret == 0x4d) |
471 | dev_info(&client->dev, "TCS34723/34727 found\n" ); |
472 | else |
473 | return -ENODEV; |
474 | |
475 | ret = i2c_smbus_read_byte_data(client: data->client, TCS3472_CONTROL); |
476 | if (ret < 0) |
477 | return ret; |
478 | data->control = ret; |
479 | |
480 | ret = i2c_smbus_read_byte_data(client: data->client, TCS3472_ATIME); |
481 | if (ret < 0) |
482 | return ret; |
483 | data->atime = ret; |
484 | |
485 | ret = i2c_smbus_read_word_data(client: data->client, TCS3472_AILT); |
486 | if (ret < 0) |
487 | return ret; |
488 | data->low_thresh = ret; |
489 | |
490 | ret = i2c_smbus_read_word_data(client: data->client, TCS3472_AIHT); |
491 | if (ret < 0) |
492 | return ret; |
493 | data->high_thresh = ret; |
494 | |
495 | data->apers = 1; |
496 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_PERS, |
497 | value: data->apers); |
498 | if (ret < 0) |
499 | return ret; |
500 | |
501 | ret = i2c_smbus_read_byte_data(client: data->client, TCS3472_ENABLE); |
502 | if (ret < 0) |
503 | return ret; |
504 | |
505 | /* enable device */ |
506 | data->enable = ret | TCS3472_ENABLE_PON | TCS3472_ENABLE_AEN; |
507 | data->enable &= ~TCS3472_ENABLE_AIEN; |
508 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_ENABLE, |
509 | value: data->enable); |
510 | if (ret < 0) |
511 | return ret; |
512 | |
513 | ret = iio_triggered_buffer_setup(indio_dev, NULL, |
514 | tcs3472_trigger_handler, NULL); |
515 | if (ret < 0) |
516 | return ret; |
517 | |
518 | if (client->irq) { |
519 | ret = request_threaded_irq(irq: client->irq, NULL, |
520 | thread_fn: tcs3472_event_handler, |
521 | IRQF_TRIGGER_FALLING | IRQF_SHARED | |
522 | IRQF_ONESHOT, |
523 | name: client->name, dev: indio_dev); |
524 | if (ret) |
525 | goto buffer_cleanup; |
526 | } |
527 | |
528 | ret = iio_device_register(indio_dev); |
529 | if (ret < 0) |
530 | goto free_irq; |
531 | |
532 | return 0; |
533 | |
534 | free_irq: |
535 | if (client->irq) |
536 | free_irq(client->irq, indio_dev); |
537 | buffer_cleanup: |
538 | iio_triggered_buffer_cleanup(indio_dev); |
539 | return ret; |
540 | } |
541 | |
542 | static int tcs3472_powerdown(struct tcs3472_data *data) |
543 | { |
544 | int ret; |
545 | u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; |
546 | |
547 | mutex_lock(&data->lock); |
548 | |
549 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_ENABLE, |
550 | value: data->enable & ~enable_mask); |
551 | if (!ret) |
552 | data->enable &= ~enable_mask; |
553 | |
554 | mutex_unlock(lock: &data->lock); |
555 | |
556 | return ret; |
557 | } |
558 | |
559 | static void tcs3472_remove(struct i2c_client *client) |
560 | { |
561 | struct iio_dev *indio_dev = i2c_get_clientdata(client); |
562 | |
563 | iio_device_unregister(indio_dev); |
564 | if (client->irq) |
565 | free_irq(client->irq, indio_dev); |
566 | iio_triggered_buffer_cleanup(indio_dev); |
567 | tcs3472_powerdown(data: iio_priv(indio_dev)); |
568 | } |
569 | |
570 | static int tcs3472_suspend(struct device *dev) |
571 | { |
572 | struct tcs3472_data *data = iio_priv(indio_dev: i2c_get_clientdata( |
573 | to_i2c_client(dev))); |
574 | return tcs3472_powerdown(data); |
575 | } |
576 | |
577 | static int tcs3472_resume(struct device *dev) |
578 | { |
579 | struct tcs3472_data *data = iio_priv(indio_dev: i2c_get_clientdata( |
580 | to_i2c_client(dev))); |
581 | int ret; |
582 | u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; |
583 | |
584 | mutex_lock(&data->lock); |
585 | |
586 | ret = i2c_smbus_write_byte_data(client: data->client, TCS3472_ENABLE, |
587 | value: data->enable | enable_mask); |
588 | if (!ret) |
589 | data->enable |= enable_mask; |
590 | |
591 | mutex_unlock(lock: &data->lock); |
592 | |
593 | return ret; |
594 | } |
595 | |
596 | static DEFINE_SIMPLE_DEV_PM_OPS(tcs3472_pm_ops, tcs3472_suspend, |
597 | tcs3472_resume); |
598 | |
599 | static const struct i2c_device_id tcs3472_id[] = { |
600 | { "tcs3472" }, |
601 | { } |
602 | }; |
603 | MODULE_DEVICE_TABLE(i2c, tcs3472_id); |
604 | |
605 | static struct i2c_driver tcs3472_driver = { |
606 | .driver = { |
607 | .name = TCS3472_DRV_NAME, |
608 | .pm = pm_sleep_ptr(&tcs3472_pm_ops), |
609 | }, |
610 | .probe = tcs3472_probe, |
611 | .remove = tcs3472_remove, |
612 | .id_table = tcs3472_id, |
613 | }; |
614 | module_i2c_driver(tcs3472_driver); |
615 | |
616 | MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>" ); |
617 | MODULE_DESCRIPTION("TCS3472 color light sensors driver" ); |
618 | MODULE_LICENSE("GPL" ); |
619 | |