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
);
73 static int sef_cb_lu_state_save(int);
74 static int lu_state_restore(void);
75 static int sef_cb_init(int type
, sef_init_info_t
* info
);
76 static void sef_local_startup(void);
78 /* Entry points to this driver from libchardriver. */
79 static struct chardriver tsl2550_tab
= {
80 .cdr_read
= tsl2550_read
,
81 .cdr_other
= tsl2550_other
85 * These two lookup tables and the formulas used in measure_lux() are from
86 * 'TAOS INTELLIGENT OPTO SENSOR DESIGNER'S NOTEBOOK' Number 9
87 * 'Simplified TSL2550 Lux Calculation for Embedded and Micro Controllers'.
89 * The tables and formulas eliminate the need for floating point math and
90 * functions from libm. It also speeds up the calculations.
93 /* Look up table for converting ADC values to ADC counts */
94 static const uint32_t adc_counts_lut
[128] = {
95 0, 1, 2, 3, 4, 5, 6, 7,
96 8, 9, 10, 11, 12, 13, 14, 15,
97 16, 18, 20, 22, 24, 26, 28, 30,
98 32, 34, 36, 38, 40, 42, 44, 46,
99 49, 53, 57, 61, 65, 69, 73, 77,
100 81, 85, 89, 93, 97, 101, 105, 109,
101 115, 123, 131, 139, 147, 155, 163, 171,
102 179, 187, 195, 203, 211, 219, 227, 235,
103 247, 263, 279, 295, 311, 327, 343, 359,
104 375, 391, 407, 423, 439, 455, 471, 487,
105 511, 543, 575, 607, 639, 671, 703, 735,
106 767, 799, 831, 863, 895, 927, 959, 991,
107 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
108 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
109 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
110 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015
113 /* Look up table of scaling factors */
114 static const uint32_t ratio_lut
[129] = {
115 100, 100, 100, 100, 100, 100, 100, 100,
116 100, 100, 100, 100, 100, 100, 99, 99,
117 99, 99, 99, 99, 99, 99, 99, 99,
118 99, 99, 99, 98, 98, 98, 98, 98,
119 98, 98, 97, 97, 97, 97, 97, 96,
120 96, 96, 96, 95, 95, 95, 94, 94,
121 93, 93, 93, 92, 92, 91, 91, 90,
122 89, 89, 88, 87, 87, 86, 85, 84,
123 83, 82, 81, 80, 79, 78, 77, 75,
124 74, 73, 71, 69, 68, 66, 64, 62,
125 60, 58, 56, 54, 52, 49, 47, 44,
126 42, 41, 40, 40, 39, 39, 38, 38,
127 37, 37, 37, 36, 36, 36, 35, 35,
128 35, 35, 34, 34, 34, 34, 33, 33,
129 33, 33, 32, 32, 32, 32, 32, 31,
130 31, 31, 31, 31, 30, 30, 30, 30,
135 measure_lux(uint32_t * lux
)
138 uint8_t adc0_val
, adc1_val
;
139 uint32_t adc0_cnt
, adc1_cnt
;
142 r
= adc_read(0, &adc0_val
);
147 r
= adc_read(1, &adc1_val
);
152 /* Look up the adc count, drop the MSB to put in range 0-127. */
153 adc0_cnt
= adc_counts_lut
[adc0_val
& ~ADC_VALID_MASK
];
154 adc1_cnt
= adc_counts_lut
[adc1_val
& ~ADC_VALID_MASK
];
156 /* default scaling factor */
159 /* calculate ratio - avoid div by 0, ensure cnt1 <= cnt0 */
160 if ((adc0_cnt
!= 0) && (adc1_cnt
<= adc0_cnt
)) {
161 ratio
= (adc1_cnt
* 128 / adc0_cnt
);
164 /* ensure ratio isn't outside ratio_lut[] */
170 *lux
= ((adc0_cnt
- adc1_cnt
) * ratio_lut
[ratio
]) / 256;
173 if (*lux
> MAX_LUX_STD_MODE
) {
174 *lux
= MAX_LUX_STD_MODE
;
181 adc_read(int adc
, uint8_t * val
)
186 if (adc
!= 0 && adc
!= 1) {
187 log_warn(&log
, "Invalid ADC number %d, expected 0 or 1.\n",
193 log_warn(&log
, "Read called with a NULL pointer.\n");
197 *val
= (adc
== 0) ? CMD_READ_ADC0
: CMD_READ_ADC1
;
199 /* Select the ADC to read from */
200 r
= i2creg_raw_write8(bus_endpoint
, address
, *val
);
202 log_warn(&log
, "Failed to write ADC read command.\n");
208 /* Repeatedly read until the value is valid (i.e. the conversion
209 * finishes). Depending on the timing, the data sheet says this
210 * could take up to 400ms.
212 spin_init(&spin
, 400000);
214 r
= i2creg_raw_read8(bus_endpoint
, address
, val
);
216 log_warn(&log
, "Failed to read ADC%d value.\n", adc
);
220 if (ADC_VAL_IS_VALID(*val
)) {
223 } while (spin_check(&spin
));
225 /* Final read attempt. If the bus was really busy with other requests
226 * and the timing of things happened in the worst possible case,
227 * there is a chance that the loop above only did 1 read (slightly
228 * before 400 ms) and left the loop. To ensure there is a final read
229 * at or after the 400 ms mark, we try one last time here.
231 r
= i2creg_raw_read8(bus_endpoint
, address
, val
);
233 log_warn(&log
, "Failed to read ADC%d value.\n", adc
);
237 if (ADC_VAL_IS_VALID(*val
)) {
240 log_warn(&log
, "ADC%d never returned a valid result.\n", adc
);
251 /* Power on the device */
252 r
= i2creg_raw_write8(bus_endpoint
, address
, CMD_PWR_UP
);
254 log_warn(&log
, "Power-up command failed.\n");
258 /* Read power on test value */
259 r
= i2creg_raw_read8(bus_endpoint
, address
, &val
);
261 log_warn(&log
, "Failed to read power on test value.\n");
265 /* Check power on test value */
266 if (val
!= EXPECTED_PWR_UP_TEST_VAL
) {
267 log_warn(&log
, "Bad test value. Got 0x%x, expected 0x%x\n",
268 val
, EXPECTED_PWR_UP_TEST_VAL
);
272 /* Set range to normal */
273 r
= i2creg_raw_write8(bus_endpoint
, address
, CMD_NORM_RANGE
);
275 log_warn(&log
, "Normal range command failed.\n");
283 tsl2550_read(devminor_t
UNUSED(minor
), u64_t position
, endpoint_t endpt
,
284 cp_grant_id_t grant
, size_t size
, int UNUSED(flags
), cdev_id_t
UNUSED(id
))
290 r
= measure_lux(&lux
);
295 memset(buffer
, '\0', BUFFER_LEN
+ 1);
296 snprintf(buffer
, BUFFER_LEN
, "%-16s: %d\n", "ILLUMINANCE", lux
);
298 dev_size
= (u64_t
)strlen(buffer
);
299 if (position
>= dev_size
) return 0;
300 if (position
+ size
> dev_size
)
301 size
= (size_t)(dev_size
- position
);
303 r
= sys_safecopyto(endpt
, grant
, 0,
304 (vir_bytes
)(buffer
+ (size_t)position
), size
);
306 return (r
!= OK
) ? r
: size
;
310 tsl2550_other(message
* m
, int ipc_status
)
314 if (is_ipc_notify(ipc_status
)) {
315 if (m
->m_source
== DS_PROC_NR
) {
317 "bus driver changed state, update endpoint\n");
318 i2cdriver_handle_bus_update(&bus_endpoint
, bus
,
324 log_warn(&log
, "Invalid message type (0x%x)\n", m
->m_type
);
328 sef_cb_lu_state_save(int UNUSED(state
))
330 ds_publish_u32("bus", bus
, DSF_OVERWRITE
);
331 ds_publish_u32("address", address
, DSF_OVERWRITE
);
336 lu_state_restore(void)
338 /* Restore the state. */
341 ds_retrieve_u32("bus", &value
);
342 ds_delete_u32("bus");
345 ds_retrieve_u32("address", &value
);
346 ds_delete_u32("address");
347 address
= (int) value
;
353 sef_cb_init(int type
, sef_init_info_t
* UNUSED(info
))
357 if (type
== SEF_INIT_LU
) {
358 /* Restore the state. */
362 /* look-up the endpoint for the bus driver */
363 bus_endpoint
= i2cdriver_bus_endpoint(bus
);
364 if (bus_endpoint
== 0) {
365 log_warn(&log
, "Couldn't find bus driver.\n");
369 /* claim the device */
370 r
= i2cdriver_reserve_device(bus_endpoint
, address
);
372 log_warn(&log
, "Couldn't reserve device 0x%x (r=%d)\n",
379 log_warn(&log
, "Device Init Failed\n");
383 if (type
!= SEF_INIT_LU
) {
385 /* sign up for updates about the i2c bus going down/up */
386 r
= i2cdriver_subscribe_bus_updates(bus
);
388 log_warn(&log
, "Couldn't subscribe to bus updates\n");
392 i2cdriver_announce(bus
);
393 log_debug(&log
, "announced\n");
400 sef_local_startup(void)
403 * Register init callbacks. Use the same function for all event types
405 sef_setcb_init_fresh(sef_cb_init
);
406 sef_setcb_init_lu(sef_cb_init
);
407 sef_setcb_init_restart(sef_cb_init
);
410 * Register live update callbacks.
412 /* Agree to update immediately when LU is requested in a valid state. */
413 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready
);
414 /* Support live update starting from any standard state. */
415 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard
);
416 /* Register a custom routine to save the state. */
417 sef_setcb_lu_state_save(sef_cb_lu_state_save
);
419 /* Let SEF perform startup. */
424 main(int argc
, char *argv
[])
428 env_setargs(argc
, argv
);
430 r
= i2cdriver_env_parse(&bus
, &address
, valid_addrs
);
432 log_warn(&log
, "Expecting -args 'bus=X address=0xYY'\n");
433 log_warn(&log
, "Example -args 'bus=1 address=0x39'\n");
437 "Invalid slave address for device, expecting 0x39\n");
443 chardriver_task(&tsl2550_tab
);