1 /* Driver for the TSL2550 Ambient Light Sensor */
2
3 #include <minix/ds.h>
4 #include <minix/drivers.h>
5 #include <minix/i2c.h>
6 #include <minix/i2cdriver.h>
7 #include <minix/chardriver.h>
8 #include <minix/log.h>
9 #include <minix/type.h>
10 #include <minix/spin.h>
11
12 /*
13 * Device Commands
14 */
15 #define CMD_PWR_DOWN 0x00
16 #define CMD_PWR_UP 0x03
17 #define CMD_EXT_RANGE 0x1d
18 #define CMD_NORM_RANGE 0x18
19 #define CMD_READ_ADC0 0x43
20 #define CMD_READ_ADC1 0x83
21
22 /* When powered up and communicating, the register should have this value */
23 #define EXPECTED_PWR_UP_TEST_VAL 0x03
24
25 /* Maximum Lux value in Standard Mode */
26 #define MAX_LUX_STD_MODE 1846
27
28 /* Bit Masks for ADC Data */
29 #define ADC_VALID_MASK (1<<7)
30 #define ADC_CHORD_MASK ((1<<6)|(1<<5)|(1<<4))
31 #define ADC_STEP_MASK ((1<<3)|(1<<2)|(1<<1)|(1<<0))
32
33 #define ADC_VAL_IS_VALID(x) ((x & ADC_VALID_MASK) == ADC_VALID_MASK)
34 #define ADC_VAL_TO_CHORD_BITS(x) ((x & ADC_CHORD_MASK) >> 4)
35 #define ADC_VAL_TO_STEP_BITS(x) (x & ADC_STEP_MASK)
36
37 /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
38 static struct log log = {
39 .name = "tsl2550",
40 .log_level = LEVEL_INFO,
41 .log_func = default_log
42 };
43
44 /* The slave address is hardwired to 0x39 and cannot be changed. */
45 static i2c_addr_t valid_addrs[2] = {
46 0x39, 0x00
47 };
48
49 /* Buffer to store output string returned when reading from device file. */
50 #define BUFFER_LEN 32
51 char buffer[BUFFER_LEN + 1];
52
53 /* the bus that this device is on (counting starting at 1) */
54 static uint32_t bus;
55
56 /* slave address of the device */
57 static i2c_addr_t address;
58
59 /* endpoint for the driver for the bus itself. */
60 static endpoint_t bus_endpoint;
61
62 /* main driver functions */
63 static int tsl2550_init(void);
64 static int adc_read(int adc, uint8_t * val);
65 static int measure_lux(uint32_t * lux);
66
67 /* libchardriver callbacks */
68 static ssize_t tsl2550_read(devminor_t minor, u64_t position, endpoint_t endpt,
69 cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
70 static void tsl2550_other(message * m, int ipc_status);
71
72 /* Entry points to this driver from libchardriver. */
73 static struct chardriver tsl2550_tab = {
74 .cdr_read = tsl2550_read,
75 .cdr_other = tsl2550_other
76 };
77
78 /*
79 * These two lookup tables and the formulas used in measure_lux() are from
80 * 'TAOS INTELLIGENT OPTO SENSOR DESIGNER'S NOTEBOOK' Number 9
81 * 'Simplified TSL2550 Lux Calculation for Embedded and Micro Controllers'.
82 *
83 * The tables and formulas eliminate the need for floating point math and
84 * functions from libm. It also speeds up the calculations.
85 */
86
87 /* Look up table for converting ADC values to ADC counts */
88 static const uint32_t adc_counts_lut[128] = {
89 0, 1, 2, 3, 4, 5, 6, 7,
90 8, 9, 10, 11, 12, 13, 14, 15,
91 16, 18, 20, 22, 24, 26, 28, 30,
92 32, 34, 36, 38, 40, 42, 44, 46,
93 49, 53, 57, 61, 65, 69, 73, 77,
94 81, 85, 89, 93, 97, 101, 105, 109,
95 115, 123, 131, 139, 147, 155, 163, 171,
96 179, 187, 195, 203, 211, 219, 227, 235,
97 247, 263, 279, 295, 311, 327, 343, 359,
98 375, 391, 407, 423, 439, 455, 471, 487,
99 511, 543, 575, 607, 639, 671, 703, 735,
100 767, 799, 831, 863, 895, 927, 959, 991,
101 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
102 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
103 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
104 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015
105 };
106
107 /* Look up table of scaling factors */
108 static const uint32_t ratio_lut[129] = {
109 100, 100, 100, 100, 100, 100, 100, 100,
110 100, 100, 100, 100, 100, 100, 99, 99,
111 99, 99, 99, 99, 99, 99, 99, 99,
112 99, 99, 99, 98, 98, 98, 98, 98,
113 98, 98, 97, 97, 97, 97, 97, 96,
114 96, 96, 96, 95, 95, 95, 94, 94,
115 93, 93, 93, 92, 92, 91, 91, 90,
116 89, 89, 88, 87, 87, 86, 85, 84,
117 83, 82, 81, 80, 79, 78, 77, 75,
118 74, 73, 71, 69, 68, 66, 64, 62,
119 60, 58, 56, 54, 52, 49, 47, 44,
120 42, 41, 40, 40, 39, 39, 38, 38,
121 37, 37, 37, 36, 36, 36, 35, 35,
122 35, 35, 34, 34, 34, 34, 33, 33,
123 33, 33, 32, 32, 32, 32, 32, 31,
124 31, 31, 31, 31, 30, 30, 30, 30,
125 30
126 };
127
128 static int
measure_lux(uint32_t * lux)129 measure_lux(uint32_t * lux)
130 {
131 int r;
132 uint8_t adc0_val, adc1_val;
133 uint32_t adc0_cnt, adc1_cnt;
134 uint32_t ratio;
135
136 r = adc_read(0, &adc0_val);
137 if (r != OK) {
138 return -1;
139 }
140
141 r = adc_read(1, &adc1_val);
142 if (r != OK) {
143 return -1;
144 }
145
146 /* Look up the adc count, drop the MSB to put in range 0-127. */
147 adc0_cnt = adc_counts_lut[adc0_val & ~ADC_VALID_MASK];
148 adc1_cnt = adc_counts_lut[adc1_val & ~ADC_VALID_MASK];
149
150 /* default scaling factor */
151 ratio = 128;
152
153 /* calculate ratio - avoid div by 0, ensure cnt1 <= cnt0 */
154 if ((adc0_cnt != 0) && (adc1_cnt <= adc0_cnt)) {
155 ratio = (adc1_cnt * 128 / adc0_cnt);
156 }
157
158 /* ensure ratio isn't outside ratio_lut[] */
159 if (ratio > 128) {
160 ratio = 128;
161 }
162
163 /* calculate lux */
164 *lux = ((adc0_cnt - adc1_cnt) * ratio_lut[ratio]) / 256;
165
166 /* range check */
167 if (*lux > MAX_LUX_STD_MODE) {
168 *lux = MAX_LUX_STD_MODE;
169 }
170
171 return OK;
172 }
173
174 static int
adc_read(int adc,uint8_t * val)175 adc_read(int adc, uint8_t * val)
176 {
177 int r;
178 spin_t spin;
179
180 if (adc != 0 && adc != 1) {
181 log_warn(&log, "Invalid ADC number %d, expected 0 or 1.\n",
182 adc);
183 return EINVAL;
184 }
185
186 if (val == NULL) {
187 log_warn(&log, "Read called with a NULL pointer.\n");
188 return EINVAL;
189 }
190
191 *val = (adc == 0) ? CMD_READ_ADC0 : CMD_READ_ADC1;
192
193 /* Select the ADC to read from */
194 r = i2creg_raw_write8(bus_endpoint, address, *val);
195 if (r != OK) {
196 log_warn(&log, "Failed to write ADC read command.\n");
197 return -1;
198 }
199
200 *val = 0;
201
202 /* Repeatedly read until the value is valid (i.e. the conversion
203 * finishes). Depending on the timing, the data sheet says this
204 * could take up to 400ms.
205 */
206 spin_init(&spin, 400000);
207 do {
208 r = i2creg_raw_read8(bus_endpoint, address, val);
209 if (r != OK) {
210 log_warn(&log, "Failed to read ADC%d value.\n", adc);
211 return -1;
212 }
213
214 if (ADC_VAL_IS_VALID(*val)) {
215 return OK;
216 }
217 } while (spin_check(&spin));
218
219 /* Final read attempt. If the bus was really busy with other requests
220 * and the timing of things happened in the worst possible case,
221 * there is a chance that the loop above only did 1 read (slightly
222 * before 400 ms) and left the loop. To ensure there is a final read
223 * at or after the 400 ms mark, we try one last time here.
224 */
225 r = i2creg_raw_read8(bus_endpoint, address, val);
226 if (r != OK) {
227 log_warn(&log, "Failed to read ADC%d value.\n", adc);
228 return -1;
229 }
230
231 if (ADC_VAL_IS_VALID(*val)) {
232 return OK;
233 } else {
234 log_warn(&log, "ADC%d never returned a valid result.\n", adc);
235 return EIO;
236 }
237 }
238
239 static int
tsl2550_init(void)240 tsl2550_init(void)
241 {
242 int r;
243 uint8_t val;
244
245 /* Power on the device */
246 r = i2creg_raw_write8(bus_endpoint, address, CMD_PWR_UP);
247 if (r != OK) {
248 log_warn(&log, "Power-up command failed.\n");
249 return -1;
250 }
251
252 /* Read power on test value */
253 r = i2creg_raw_read8(bus_endpoint, address, &val);
254 if (r != OK) {
255 log_warn(&log, "Failed to read power on test value.\n");
256 return -1;
257 }
258
259 /* Check power on test value */
260 if (val != EXPECTED_PWR_UP_TEST_VAL) {
261 log_warn(&log, "Bad test value. Got 0x%x, expected 0x%x\n",
262 val, EXPECTED_PWR_UP_TEST_VAL);
263 return -1;
264 }
265
266 /* Set range to normal */
267 r = i2creg_raw_write8(bus_endpoint, address, CMD_NORM_RANGE);
268 if (r != OK) {
269 log_warn(&log, "Normal range command failed.\n");
270 return -1;
271 }
272
273 return OK;
274 }
275
276 static ssize_t
tsl2550_read(devminor_t UNUSED (minor),u64_t position,endpoint_t endpt,cp_grant_id_t grant,size_t size,int UNUSED (flags),cdev_id_t UNUSED (id))277 tsl2550_read(devminor_t UNUSED(minor), u64_t position, endpoint_t endpt,
278 cp_grant_id_t grant, size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
279 {
280 u64_t dev_size;
281 int bytes, r;
282 uint32_t lux;
283
284 r = measure_lux(&lux);
285 if (r != OK) {
286 return EIO;
287 }
288
289 memset(buffer, '\0', BUFFER_LEN + 1);
290 snprintf(buffer, BUFFER_LEN, "%-16s: %d\n", "ILLUMINANCE", lux);
291
292 dev_size = (u64_t)strlen(buffer);
293 if (position >= dev_size) return 0;
294 if (position + size > dev_size)
295 size = (size_t)(dev_size - position);
296
297 r = sys_safecopyto(endpt, grant, 0,
298 (vir_bytes)(buffer + (size_t)position), size);
299
300 return (r != OK) ? r : size;
301 }
302
303 static void
tsl2550_other(message * m,int ipc_status)304 tsl2550_other(message * m, int ipc_status)
305 {
306 int r;
307
308 if (is_ipc_notify(ipc_status)) {
309 if (m->m_source == DS_PROC_NR) {
310 log_debug(&log,
311 "bus driver changed state, update endpoint\n");
312 i2cdriver_handle_bus_update(&bus_endpoint, bus,
313 address);
314 }
315 return;
316 }
317
318 log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
319 }
320
321 static int
sef_cb_lu_state_save(int UNUSED (result),int UNUSED (flags))322 sef_cb_lu_state_save(int UNUSED(result), int UNUSED(flags))
323 {
324 ds_publish_u32("bus", bus, DSF_OVERWRITE);
325 ds_publish_u32("address", address, DSF_OVERWRITE);
326 return OK;
327 }
328
329 static int
lu_state_restore(void)330 lu_state_restore(void)
331 {
332 /* Restore the state. */
333 u32_t value;
334
335 ds_retrieve_u32("bus", &value);
336 ds_delete_u32("bus");
337 bus = (int) value;
338
339 ds_retrieve_u32("address", &value);
340 ds_delete_u32("address");
341 address = (int) value;
342
343 return OK;
344 }
345
346 static int
sef_cb_init(int type,sef_init_info_t * UNUSED (info))347 sef_cb_init(int type, sef_init_info_t * UNUSED(info))
348 {
349 int r;
350
351 if (type == SEF_INIT_LU) {
352 /* Restore the state. */
353 lu_state_restore();
354 }
355
356 /* look-up the endpoint for the bus driver */
357 bus_endpoint = i2cdriver_bus_endpoint(bus);
358 if (bus_endpoint == 0) {
359 log_warn(&log, "Couldn't find bus driver.\n");
360 return EXIT_FAILURE;
361 }
362
363 /* claim the device */
364 r = i2cdriver_reserve_device(bus_endpoint, address);
365 if (r != OK) {
366 log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
367 address, r);
368 return EXIT_FAILURE;
369 }
370
371 r = tsl2550_init();
372 if (r != OK) {
373 log_warn(&log, "Device Init Failed\n");
374 return EXIT_FAILURE;
375 }
376
377 if (type != SEF_INIT_LU) {
378
379 /* sign up for updates about the i2c bus going down/up */
380 r = i2cdriver_subscribe_bus_updates(bus);
381 if (r != OK) {
382 log_warn(&log, "Couldn't subscribe to bus updates\n");
383 return EXIT_FAILURE;
384 }
385
386 i2cdriver_announce(bus);
387 log_debug(&log, "announced\n");
388 }
389
390 return OK;
391 }
392
393 static void
sef_local_startup(void)394 sef_local_startup(void)
395 {
396 /*
397 * Register init callbacks. Use the same function for all event types
398 */
399 sef_setcb_init_fresh(sef_cb_init);
400 sef_setcb_init_lu(sef_cb_init);
401 sef_setcb_init_restart(sef_cb_init);
402
403 /*
404 * Register live update callbacks.
405 */
406 sef_setcb_lu_state_save(sef_cb_lu_state_save);
407
408 /* Let SEF perform startup. */
409 sef_startup();
410 }
411
412 int
main(int argc,char * argv[])413 main(int argc, char *argv[])
414 {
415 int r;
416
417 env_setargs(argc, argv);
418
419 r = i2cdriver_env_parse(&bus, &address, valid_addrs);
420 if (r < 0) {
421 log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n");
422 log_warn(&log, "Example -args 'bus=1 address=0x39'\n");
423 return EXIT_FAILURE;
424 } else if (r > 0) {
425 log_warn(&log,
426 "Invalid slave address for device, expecting 0x39\n");
427 return EXIT_FAILURE;
428 }
429
430 sef_local_startup();
431
432 chardriver_task(&tsl2550_tab);
433
434 return 0;
435 }
436