1 /* Driver for the TSL2550 Ambient Light Sensor */
4 #include <minix/drivers.h>
6 #include <minix/i2cdriver.h>
7 #include <minix/chardriver.h>
9 #include <minix/type.h>
10 #include <minix/spin.h>
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
22 /* When powered up and communicating, the register should have this value */
23 #define EXPECTED_PWR_UP_TEST_VAL 0x03
25 /* Maximum Lux value in Standard Mode */
26 #define MAX_LUX_STD_MODE 1846
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))
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)
37 /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
38 static struct log log
= {
40 .log_level
= LEVEL_INFO
,
41 .log_func
= default_log
44 /* The slave address is hardwired to 0x39 and cannot be changed. */
45 static i2c_addr_t valid_addrs
[2] = {
49 /* Buffer to store output string returned when reading from device file. */
51 char buffer
[BUFFER_LEN
+ 1];
53 /* the bus that this device is on (counting starting at 1) */
56 /* slave address of the device */
57 static i2c_addr_t address
;
59 /* endpoint for the driver for the bus itself. */
60 static endpoint_t bus_endpoint
;
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
);
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
);
72 /* Entry points to this driver from libchardriver. */
73 static struct chardriver tsl2550_tab
= {
74 .cdr_read
= tsl2550_read
,
75 .cdr_other
= tsl2550_other
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'.
83 * The tables and formulas eliminate the need for floating point math and
84 * functions from libm. It also speeds up the calculations.
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
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,
129 measure_lux(uint32_t * lux
)
132 uint8_t adc0_val
, adc1_val
;
133 uint32_t adc0_cnt
, adc1_cnt
;
136 r
= adc_read(0, &adc0_val
);
141 r
= adc_read(1, &adc1_val
);
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
];
150 /* default scaling factor */
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
);
158 /* ensure ratio isn't outside ratio_lut[] */
164 *lux
= ((adc0_cnt
- adc1_cnt
) * ratio_lut
[ratio
]) / 256;
167 if (*lux
> MAX_LUX_STD_MODE
) {
168 *lux
= MAX_LUX_STD_MODE
;
175 adc_read(int adc
, uint8_t * val
)
180 if (adc
!= 0 && adc
!= 1) {
181 log_warn(&log
, "Invalid ADC number %d, expected 0 or 1.\n",
187 log_warn(&log
, "Read called with a NULL pointer.\n");
191 *val
= (adc
== 0) ? CMD_READ_ADC0
: CMD_READ_ADC1
;
193 /* Select the ADC to read from */
194 r
= i2creg_raw_write8(bus_endpoint
, address
, *val
);
196 log_warn(&log
, "Failed to write ADC read command.\n");
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.
206 spin_init(&spin
, 400000);
208 r
= i2creg_raw_read8(bus_endpoint
, address
, val
);
210 log_warn(&log
, "Failed to read ADC%d value.\n", adc
);
214 if (ADC_VAL_IS_VALID(*val
)) {
217 } while (spin_check(&spin
));
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.
225 r
= i2creg_raw_read8(bus_endpoint
, address
, val
);
227 log_warn(&log
, "Failed to read ADC%d value.\n", adc
);
231 if (ADC_VAL_IS_VALID(*val
)) {
234 log_warn(&log
, "ADC%d never returned a valid result.\n", adc
);
245 /* Power on the device */
246 r
= i2creg_raw_write8(bus_endpoint
, address
, CMD_PWR_UP
);
248 log_warn(&log
, "Power-up command failed.\n");
252 /* Read power on test value */
253 r
= i2creg_raw_read8(bus_endpoint
, address
, &val
);
255 log_warn(&log
, "Failed to read power on test value.\n");
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
);
266 /* Set range to normal */
267 r
= i2creg_raw_write8(bus_endpoint
, address
, CMD_NORM_RANGE
);
269 log_warn(&log
, "Normal range command failed.\n");
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
))
284 r
= measure_lux(&lux
);
289 memset(buffer
, '\0', BUFFER_LEN
+ 1);
290 snprintf(buffer
, BUFFER_LEN
, "%-16s: %d\n", "ILLUMINANCE", lux
);
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
);
297 r
= sys_safecopyto(endpt
, grant
, 0,
298 (vir_bytes
)(buffer
+ (size_t)position
), size
);
300 return (r
!= OK
) ? r
: size
;
304 tsl2550_other(message
* m
, int ipc_status
)
308 if (is_ipc_notify(ipc_status
)) {
309 if (m
->m_source
== DS_PROC_NR
) {
311 "bus driver changed state, update endpoint\n");
312 i2cdriver_handle_bus_update(&bus_endpoint
, bus
,
318 log_warn(&log
, "Invalid message type (0x%x)\n", m
->m_type
);
322 sef_cb_lu_state_save(int UNUSED(result
), int UNUSED(flags
))
324 ds_publish_u32("bus", bus
, DSF_OVERWRITE
);
325 ds_publish_u32("address", address
, DSF_OVERWRITE
);
330 lu_state_restore(void)
332 /* Restore the state. */
335 ds_retrieve_u32("bus", &value
);
336 ds_delete_u32("bus");
339 ds_retrieve_u32("address", &value
);
340 ds_delete_u32("address");
341 address
= (int) value
;
347 sef_cb_init(int type
, sef_init_info_t
* UNUSED(info
))
351 if (type
== SEF_INIT_LU
) {
352 /* Restore the state. */
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");
363 /* claim the device */
364 r
= i2cdriver_reserve_device(bus_endpoint
, address
);
366 log_warn(&log
, "Couldn't reserve device 0x%x (r=%d)\n",
373 log_warn(&log
, "Device Init Failed\n");
377 if (type
!= SEF_INIT_LU
) {
379 /* sign up for updates about the i2c bus going down/up */
380 r
= i2cdriver_subscribe_bus_updates(bus
);
382 log_warn(&log
, "Couldn't subscribe to bus updates\n");
386 i2cdriver_announce(bus
);
387 log_debug(&log
, "announced\n");
394 sef_local_startup(void)
397 * Register init callbacks. Use the same function for all event types
399 sef_setcb_init_fresh(sef_cb_init
);
400 sef_setcb_init_lu(sef_cb_init
);
401 sef_setcb_init_restart(sef_cb_init
);
404 * Register live update callbacks.
406 sef_setcb_lu_state_save(sef_cb_lu_state_save
);
408 /* Let SEF perform startup. */
413 main(int argc
, char *argv
[])
417 env_setargs(argc
, argv
);
419 r
= i2cdriver_env_parse(&bus
, &address
, valid_addrs
);
421 log_warn(&log
, "Expecting -args 'bus=X address=0xYY'\n");
422 log_warn(&log
, "Example -args 'bus=1 address=0x39'\n");
426 "Invalid slave address for device, expecting 0x39\n");
432 chardriver_task(&tsl2550_tab
);