xref: /minix3/minix/drivers/sensors/tsl2550/tsl2550.c (revision 3f82ac6a4e188419336747098d0d6616cd2f3d3d)
1433d6423SLionel Sambuc /* Driver for the TSL2550 Ambient Light Sensor */
2433d6423SLionel Sambuc 
3433d6423SLionel Sambuc #include <minix/ds.h>
4433d6423SLionel Sambuc #include <minix/drivers.h>
5433d6423SLionel Sambuc #include <minix/i2c.h>
6433d6423SLionel Sambuc #include <minix/i2cdriver.h>
7433d6423SLionel Sambuc #include <minix/chardriver.h>
8433d6423SLionel Sambuc #include <minix/log.h>
9433d6423SLionel Sambuc #include <minix/type.h>
10433d6423SLionel Sambuc #include <minix/spin.h>
11433d6423SLionel Sambuc 
12433d6423SLionel Sambuc /*
13433d6423SLionel Sambuc  * Device Commands
14433d6423SLionel Sambuc  */
15433d6423SLionel Sambuc #define CMD_PWR_DOWN 0x00
16433d6423SLionel Sambuc #define CMD_PWR_UP 0x03
17433d6423SLionel Sambuc #define CMD_EXT_RANGE 0x1d
18433d6423SLionel Sambuc #define CMD_NORM_RANGE 0x18
19433d6423SLionel Sambuc #define CMD_READ_ADC0 0x43
20433d6423SLionel Sambuc #define CMD_READ_ADC1 0x83
21433d6423SLionel Sambuc 
22433d6423SLionel Sambuc /* When powered up and communicating, the register should have this value */
23433d6423SLionel Sambuc #define EXPECTED_PWR_UP_TEST_VAL 0x03
24433d6423SLionel Sambuc 
25433d6423SLionel Sambuc /* Maximum Lux value in Standard Mode */
26433d6423SLionel Sambuc #define MAX_LUX_STD_MODE 1846
27433d6423SLionel Sambuc 
28433d6423SLionel Sambuc /* Bit Masks for ADC Data */
29433d6423SLionel Sambuc #define ADC_VALID_MASK (1<<7)
30433d6423SLionel Sambuc #define ADC_CHORD_MASK ((1<<6)|(1<<5)|(1<<4))
31433d6423SLionel Sambuc #define ADC_STEP_MASK ((1<<3)|(1<<2)|(1<<1)|(1<<0))
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc #define ADC_VAL_IS_VALID(x) ((x & ADC_VALID_MASK) == ADC_VALID_MASK)
34433d6423SLionel Sambuc #define ADC_VAL_TO_CHORD_BITS(x) ((x & ADC_CHORD_MASK) >> 4)
35433d6423SLionel Sambuc #define ADC_VAL_TO_STEP_BITS(x) (x & ADC_STEP_MASK)
36433d6423SLionel Sambuc 
37433d6423SLionel Sambuc /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
38433d6423SLionel Sambuc static struct log log = {
39433d6423SLionel Sambuc 	.name = "tsl2550",
40433d6423SLionel Sambuc 	.log_level = LEVEL_INFO,
41433d6423SLionel Sambuc 	.log_func = default_log
42433d6423SLionel Sambuc };
43433d6423SLionel Sambuc 
44433d6423SLionel Sambuc /* The slave address is hardwired to 0x39 and cannot be changed. */
45433d6423SLionel Sambuc static i2c_addr_t valid_addrs[2] = {
46433d6423SLionel Sambuc 	0x39, 0x00
47433d6423SLionel Sambuc };
48433d6423SLionel Sambuc 
49433d6423SLionel Sambuc /* Buffer to store output string returned when reading from device file. */
50433d6423SLionel Sambuc #define BUFFER_LEN 32
51433d6423SLionel Sambuc char buffer[BUFFER_LEN + 1];
52433d6423SLionel Sambuc 
53433d6423SLionel Sambuc /* the bus that this device is on (counting starting at 1) */
54433d6423SLionel Sambuc static uint32_t bus;
55433d6423SLionel Sambuc 
56433d6423SLionel Sambuc /* slave address of the device */
57433d6423SLionel Sambuc static i2c_addr_t address;
58433d6423SLionel Sambuc 
59433d6423SLionel Sambuc /* endpoint for the driver for the bus itself. */
60433d6423SLionel Sambuc static endpoint_t bus_endpoint;
61433d6423SLionel Sambuc 
62433d6423SLionel Sambuc /* main driver functions */
63433d6423SLionel Sambuc static int tsl2550_init(void);
64433d6423SLionel Sambuc static int adc_read(int adc, uint8_t * val);
65433d6423SLionel Sambuc static int measure_lux(uint32_t * lux);
66433d6423SLionel Sambuc 
67433d6423SLionel Sambuc /* libchardriver callbacks */
68433d6423SLionel Sambuc static ssize_t tsl2550_read(devminor_t minor, u64_t position, endpoint_t endpt,
69433d6423SLionel Sambuc     cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
70433d6423SLionel Sambuc static void tsl2550_other(message * m, int ipc_status);
71433d6423SLionel Sambuc 
72433d6423SLionel Sambuc /* Entry points to this driver from libchardriver. */
73433d6423SLionel Sambuc static struct chardriver tsl2550_tab = {
74433d6423SLionel Sambuc 	.cdr_read	= tsl2550_read,
75433d6423SLionel Sambuc 	.cdr_other	= tsl2550_other
76433d6423SLionel Sambuc };
77433d6423SLionel Sambuc 
78433d6423SLionel Sambuc /*
79433d6423SLionel Sambuc  * These two lookup tables and the formulas used in measure_lux() are from
80433d6423SLionel Sambuc  * 'TAOS INTELLIGENT OPTO SENSOR DESIGNER'S NOTEBOOK' Number 9
81433d6423SLionel Sambuc  * 'Simplified TSL2550 Lux Calculation for Embedded and Micro Controllers'.
82433d6423SLionel Sambuc  *
83433d6423SLionel Sambuc  * The tables and formulas eliminate the need for floating point math and
84433d6423SLionel Sambuc  * functions from libm. It also speeds up the calculations.
85433d6423SLionel Sambuc  */
86433d6423SLionel Sambuc 
87433d6423SLionel Sambuc /* Look up table for converting ADC values to ADC counts */
88433d6423SLionel Sambuc static const uint32_t adc_counts_lut[128] = {
89433d6423SLionel Sambuc 	0, 1, 2, 3, 4, 5, 6, 7,
90433d6423SLionel Sambuc 	8, 9, 10, 11, 12, 13, 14, 15,
91433d6423SLionel Sambuc 	16, 18, 20, 22, 24, 26, 28, 30,
92433d6423SLionel Sambuc 	32, 34, 36, 38, 40, 42, 44, 46,
93433d6423SLionel Sambuc 	49, 53, 57, 61, 65, 69, 73, 77,
94433d6423SLionel Sambuc 	81, 85, 89, 93, 97, 101, 105, 109,
95433d6423SLionel Sambuc 	115, 123, 131, 139, 147, 155, 163, 171,
96433d6423SLionel Sambuc 	179, 187, 195, 203, 211, 219, 227, 235,
97433d6423SLionel Sambuc 	247, 263, 279, 295, 311, 327, 343, 359,
98433d6423SLionel Sambuc 	375, 391, 407, 423, 439, 455, 471, 487,
99433d6423SLionel Sambuc 	511, 543, 575, 607, 639, 671, 703, 735,
100433d6423SLionel Sambuc 	767, 799, 831, 863, 895, 927, 959, 991,
101433d6423SLionel Sambuc 	1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
102433d6423SLionel Sambuc 	1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
103433d6423SLionel Sambuc 	2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
104433d6423SLionel Sambuc 	3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015
105433d6423SLionel Sambuc };
106433d6423SLionel Sambuc 
107433d6423SLionel Sambuc /* Look up table of scaling factors */
108433d6423SLionel Sambuc static const uint32_t ratio_lut[129] = {
109433d6423SLionel Sambuc 	100, 100, 100, 100, 100, 100, 100, 100,
110433d6423SLionel Sambuc 	100, 100, 100, 100, 100, 100, 99, 99,
111433d6423SLionel Sambuc 	99, 99, 99, 99, 99, 99, 99, 99,
112433d6423SLionel Sambuc 	99, 99, 99, 98, 98, 98, 98, 98,
113433d6423SLionel Sambuc 	98, 98, 97, 97, 97, 97, 97, 96,
114433d6423SLionel Sambuc 	96, 96, 96, 95, 95, 95, 94, 94,
115433d6423SLionel Sambuc 	93, 93, 93, 92, 92, 91, 91, 90,
116433d6423SLionel Sambuc 	89, 89, 88, 87, 87, 86, 85, 84,
117433d6423SLionel Sambuc 	83, 82, 81, 80, 79, 78, 77, 75,
118433d6423SLionel Sambuc 	74, 73, 71, 69, 68, 66, 64, 62,
119433d6423SLionel Sambuc 	60, 58, 56, 54, 52, 49, 47, 44,
120433d6423SLionel Sambuc 	42, 41, 40, 40, 39, 39, 38, 38,
121433d6423SLionel Sambuc 	37, 37, 37, 36, 36, 36, 35, 35,
122433d6423SLionel Sambuc 	35, 35, 34, 34, 34, 34, 33, 33,
123433d6423SLionel Sambuc 	33, 33, 32, 32, 32, 32, 32, 31,
124433d6423SLionel Sambuc 	31, 31, 31, 31, 30, 30, 30, 30,
125433d6423SLionel Sambuc 	30
126433d6423SLionel Sambuc };
127433d6423SLionel Sambuc 
128433d6423SLionel Sambuc static int
measure_lux(uint32_t * lux)129433d6423SLionel Sambuc measure_lux(uint32_t * lux)
130433d6423SLionel Sambuc {
131433d6423SLionel Sambuc 	int r;
132433d6423SLionel Sambuc 	uint8_t adc0_val, adc1_val;
133433d6423SLionel Sambuc 	uint32_t adc0_cnt, adc1_cnt;
134433d6423SLionel Sambuc 	uint32_t ratio;
135433d6423SLionel Sambuc 
136433d6423SLionel Sambuc 	r = adc_read(0, &adc0_val);
137433d6423SLionel Sambuc 	if (r != OK) {
138433d6423SLionel Sambuc 		return -1;
139433d6423SLionel Sambuc 	}
140433d6423SLionel Sambuc 
141433d6423SLionel Sambuc 	r = adc_read(1, &adc1_val);
142433d6423SLionel Sambuc 	if (r != OK) {
143433d6423SLionel Sambuc 		return -1;
144433d6423SLionel Sambuc 	}
145433d6423SLionel Sambuc 
146433d6423SLionel Sambuc 	/* Look up the adc count, drop the MSB to put in range 0-127. */
147433d6423SLionel Sambuc 	adc0_cnt = adc_counts_lut[adc0_val & ~ADC_VALID_MASK];
148433d6423SLionel Sambuc 	adc1_cnt = adc_counts_lut[adc1_val & ~ADC_VALID_MASK];
149433d6423SLionel Sambuc 
150433d6423SLionel Sambuc 	/* default scaling factor */
151433d6423SLionel Sambuc 	ratio = 128;
152433d6423SLionel Sambuc 
153433d6423SLionel Sambuc 	/* calculate ratio - avoid div by 0, ensure cnt1 <= cnt0 */
154433d6423SLionel Sambuc 	if ((adc0_cnt != 0) && (adc1_cnt <= adc0_cnt)) {
155433d6423SLionel Sambuc 		ratio = (adc1_cnt * 128 / adc0_cnt);
156433d6423SLionel Sambuc 	}
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc 	/* ensure ratio isn't outside ratio_lut[] */
159433d6423SLionel Sambuc 	if (ratio > 128) {
160433d6423SLionel Sambuc 		ratio = 128;
161433d6423SLionel Sambuc 	}
162433d6423SLionel Sambuc 
163433d6423SLionel Sambuc 	/* calculate lux */
164433d6423SLionel Sambuc 	*lux = ((adc0_cnt - adc1_cnt) * ratio_lut[ratio]) / 256;
165433d6423SLionel Sambuc 
166433d6423SLionel Sambuc 	/* range check */
167433d6423SLionel Sambuc 	if (*lux > MAX_LUX_STD_MODE) {
168433d6423SLionel Sambuc 		*lux = MAX_LUX_STD_MODE;
169433d6423SLionel Sambuc 	}
170433d6423SLionel Sambuc 
171433d6423SLionel Sambuc 	return OK;
172433d6423SLionel Sambuc }
173433d6423SLionel Sambuc 
174433d6423SLionel Sambuc static int
adc_read(int adc,uint8_t * val)175433d6423SLionel Sambuc adc_read(int adc, uint8_t * val)
176433d6423SLionel Sambuc {
177433d6423SLionel Sambuc 	int r;
178433d6423SLionel Sambuc 	spin_t spin;
179433d6423SLionel Sambuc 
180433d6423SLionel Sambuc 	if (adc != 0 && adc != 1) {
181433d6423SLionel Sambuc 		log_warn(&log, "Invalid ADC number %d, expected 0 or 1.\n",
182433d6423SLionel Sambuc 		    adc);
183433d6423SLionel Sambuc 		return EINVAL;
184433d6423SLionel Sambuc 	}
185433d6423SLionel Sambuc 
186433d6423SLionel Sambuc 	if (val == NULL) {
187433d6423SLionel Sambuc 		log_warn(&log, "Read called with a NULL pointer.\n");
188433d6423SLionel Sambuc 		return EINVAL;
189433d6423SLionel Sambuc 	}
190433d6423SLionel Sambuc 
191433d6423SLionel Sambuc 	*val = (adc == 0) ? CMD_READ_ADC0 : CMD_READ_ADC1;
192433d6423SLionel Sambuc 
193433d6423SLionel Sambuc 	/* Select the ADC to read from */
194433d6423SLionel Sambuc 	r = i2creg_raw_write8(bus_endpoint, address, *val);
195433d6423SLionel Sambuc 	if (r != OK) {
196433d6423SLionel Sambuc 		log_warn(&log, "Failed to write ADC read command.\n");
197433d6423SLionel Sambuc 		return -1;
198433d6423SLionel Sambuc 	}
199433d6423SLionel Sambuc 
200433d6423SLionel Sambuc 	*val = 0;
201433d6423SLionel Sambuc 
202433d6423SLionel Sambuc 	/* Repeatedly read until the value is valid (i.e. the conversion
203433d6423SLionel Sambuc 	 * finishes). Depending on the timing, the data sheet says this
204433d6423SLionel Sambuc 	 * could take up to 400ms.
205433d6423SLionel Sambuc 	 */
206433d6423SLionel Sambuc 	spin_init(&spin, 400000);
207433d6423SLionel Sambuc 	do {
208433d6423SLionel Sambuc 		r = i2creg_raw_read8(bus_endpoint, address, val);
209433d6423SLionel Sambuc 		if (r != OK) {
210433d6423SLionel Sambuc 			log_warn(&log, "Failed to read ADC%d value.\n", adc);
211433d6423SLionel Sambuc 			return -1;
212433d6423SLionel Sambuc 		}
213433d6423SLionel Sambuc 
214433d6423SLionel Sambuc 		if (ADC_VAL_IS_VALID(*val)) {
215433d6423SLionel Sambuc 			return OK;
216433d6423SLionel Sambuc 		}
217433d6423SLionel Sambuc 	} while (spin_check(&spin));
218433d6423SLionel Sambuc 
219433d6423SLionel Sambuc 	/* Final read attempt. If the bus was really busy with other requests
220433d6423SLionel Sambuc 	 * and the timing of things happened in the worst possible case,
221433d6423SLionel Sambuc 	 * there is a chance that the loop above only did 1 read (slightly
222433d6423SLionel Sambuc 	 * before 400 ms) and left the loop. To ensure there is a final read
223433d6423SLionel Sambuc 	 * at or after the 400 ms mark, we try one last time here.
224433d6423SLionel Sambuc 	 */
225433d6423SLionel Sambuc 	r = i2creg_raw_read8(bus_endpoint, address, val);
226433d6423SLionel Sambuc 	if (r != OK) {
227433d6423SLionel Sambuc 		log_warn(&log, "Failed to read ADC%d value.\n", adc);
228433d6423SLionel Sambuc 		return -1;
229433d6423SLionel Sambuc 	}
230433d6423SLionel Sambuc 
231433d6423SLionel Sambuc 	if (ADC_VAL_IS_VALID(*val)) {
232433d6423SLionel Sambuc 		return OK;
233433d6423SLionel Sambuc 	} else {
234433d6423SLionel Sambuc 		log_warn(&log, "ADC%d never returned a valid result.\n", adc);
235433d6423SLionel Sambuc 		return EIO;
236433d6423SLionel Sambuc 	}
237433d6423SLionel Sambuc }
238433d6423SLionel Sambuc 
239433d6423SLionel Sambuc static int
tsl2550_init(void)240433d6423SLionel Sambuc tsl2550_init(void)
241433d6423SLionel Sambuc {
242433d6423SLionel Sambuc 	int r;
243433d6423SLionel Sambuc 	uint8_t val;
244433d6423SLionel Sambuc 
245433d6423SLionel Sambuc 	/* Power on the device */
246433d6423SLionel Sambuc 	r = i2creg_raw_write8(bus_endpoint, address, CMD_PWR_UP);
247433d6423SLionel Sambuc 	if (r != OK) {
248433d6423SLionel Sambuc 		log_warn(&log, "Power-up command failed.\n");
249433d6423SLionel Sambuc 		return -1;
250433d6423SLionel Sambuc 	}
251433d6423SLionel Sambuc 
252433d6423SLionel Sambuc 	/* Read power on test value */
253433d6423SLionel Sambuc 	r = i2creg_raw_read8(bus_endpoint, address, &val);
254433d6423SLionel Sambuc 	if (r != OK) {
255433d6423SLionel Sambuc 		log_warn(&log, "Failed to read power on test value.\n");
256433d6423SLionel Sambuc 		return -1;
257433d6423SLionel Sambuc 	}
258433d6423SLionel Sambuc 
259433d6423SLionel Sambuc 	/* Check power on test value */
260433d6423SLionel Sambuc 	if (val != EXPECTED_PWR_UP_TEST_VAL) {
261433d6423SLionel Sambuc 		log_warn(&log, "Bad test value. Got 0x%x, expected 0x%x\n",
262433d6423SLionel Sambuc 		    val, EXPECTED_PWR_UP_TEST_VAL);
263433d6423SLionel Sambuc 		return -1;
264433d6423SLionel Sambuc 	}
265433d6423SLionel Sambuc 
266433d6423SLionel Sambuc 	/* Set range to normal */
267433d6423SLionel Sambuc 	r = i2creg_raw_write8(bus_endpoint, address, CMD_NORM_RANGE);
268433d6423SLionel Sambuc 	if (r != OK) {
269433d6423SLionel Sambuc 		log_warn(&log, "Normal range command failed.\n");
270433d6423SLionel Sambuc 		return -1;
271433d6423SLionel Sambuc 	}
272433d6423SLionel Sambuc 
273433d6423SLionel Sambuc 	return OK;
274433d6423SLionel Sambuc }
275433d6423SLionel Sambuc 
276433d6423SLionel Sambuc 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))277433d6423SLionel Sambuc tsl2550_read(devminor_t UNUSED(minor), u64_t position, endpoint_t endpt,
278433d6423SLionel Sambuc     cp_grant_id_t grant, size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
279433d6423SLionel Sambuc {
280433d6423SLionel Sambuc 	u64_t dev_size;
281433d6423SLionel Sambuc 	int bytes, r;
282433d6423SLionel Sambuc 	uint32_t lux;
283433d6423SLionel Sambuc 
284433d6423SLionel Sambuc 	r = measure_lux(&lux);
285433d6423SLionel Sambuc 	if (r != OK) {
286433d6423SLionel Sambuc 		return EIO;
287433d6423SLionel Sambuc 	}
288433d6423SLionel Sambuc 
289433d6423SLionel Sambuc 	memset(buffer, '\0', BUFFER_LEN + 1);
290433d6423SLionel Sambuc 	snprintf(buffer, BUFFER_LEN, "%-16s: %d\n", "ILLUMINANCE", lux);
291433d6423SLionel Sambuc 
292433d6423SLionel Sambuc 	dev_size = (u64_t)strlen(buffer);
293433d6423SLionel Sambuc 	if (position >= dev_size) return 0;
294433d6423SLionel Sambuc 	if (position + size > dev_size)
295433d6423SLionel Sambuc 		size = (size_t)(dev_size - position);
296433d6423SLionel Sambuc 
297433d6423SLionel Sambuc 	r = sys_safecopyto(endpt, grant, 0,
298433d6423SLionel Sambuc 	    (vir_bytes)(buffer + (size_t)position), size);
299433d6423SLionel Sambuc 
300433d6423SLionel Sambuc 	return (r != OK) ? r : size;
301433d6423SLionel Sambuc }
302433d6423SLionel Sambuc 
303433d6423SLionel Sambuc static void
tsl2550_other(message * m,int ipc_status)304433d6423SLionel Sambuc tsl2550_other(message * m, int ipc_status)
305433d6423SLionel Sambuc {
306433d6423SLionel Sambuc 	int r;
307433d6423SLionel Sambuc 
308433d6423SLionel Sambuc 	if (is_ipc_notify(ipc_status)) {
309433d6423SLionel Sambuc 		if (m->m_source == DS_PROC_NR) {
310433d6423SLionel Sambuc 			log_debug(&log,
311433d6423SLionel Sambuc 			    "bus driver changed state, update endpoint\n");
312433d6423SLionel Sambuc 			i2cdriver_handle_bus_update(&bus_endpoint, bus,
313433d6423SLionel Sambuc 			    address);
314433d6423SLionel Sambuc 		}
315433d6423SLionel Sambuc 		return;
316433d6423SLionel Sambuc 	}
317433d6423SLionel Sambuc 
318433d6423SLionel Sambuc 	log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
319433d6423SLionel Sambuc }
320433d6423SLionel Sambuc 
321433d6423SLionel Sambuc static int
sef_cb_lu_state_save(int UNUSED (result),int UNUSED (flags))322*e1f889d2SCristiano Giuffrida sef_cb_lu_state_save(int UNUSED(result), int UNUSED(flags))
323433d6423SLionel Sambuc {
324433d6423SLionel Sambuc 	ds_publish_u32("bus", bus, DSF_OVERWRITE);
325433d6423SLionel Sambuc 	ds_publish_u32("address", address, DSF_OVERWRITE);
326433d6423SLionel Sambuc 	return OK;
327433d6423SLionel Sambuc }
328433d6423SLionel Sambuc 
329433d6423SLionel Sambuc static int
lu_state_restore(void)330433d6423SLionel Sambuc lu_state_restore(void)
331433d6423SLionel Sambuc {
332433d6423SLionel Sambuc 	/* Restore the state. */
333433d6423SLionel Sambuc 	u32_t value;
334433d6423SLionel Sambuc 
335433d6423SLionel Sambuc 	ds_retrieve_u32("bus", &value);
336433d6423SLionel Sambuc 	ds_delete_u32("bus");
337433d6423SLionel Sambuc 	bus = (int) value;
338433d6423SLionel Sambuc 
339433d6423SLionel Sambuc 	ds_retrieve_u32("address", &value);
340433d6423SLionel Sambuc 	ds_delete_u32("address");
341433d6423SLionel Sambuc 	address = (int) value;
342433d6423SLionel Sambuc 
343433d6423SLionel Sambuc 	return OK;
344433d6423SLionel Sambuc }
345433d6423SLionel Sambuc 
346433d6423SLionel Sambuc static int
sef_cb_init(int type,sef_init_info_t * UNUSED (info))347433d6423SLionel Sambuc sef_cb_init(int type, sef_init_info_t * UNUSED(info))
348433d6423SLionel Sambuc {
349433d6423SLionel Sambuc 	int r;
350433d6423SLionel Sambuc 
351433d6423SLionel Sambuc 	if (type == SEF_INIT_LU) {
352433d6423SLionel Sambuc 		/* Restore the state. */
353433d6423SLionel Sambuc 		lu_state_restore();
354433d6423SLionel Sambuc 	}
355433d6423SLionel Sambuc 
356433d6423SLionel Sambuc 	/* look-up the endpoint for the bus driver */
357433d6423SLionel Sambuc 	bus_endpoint = i2cdriver_bus_endpoint(bus);
358433d6423SLionel Sambuc 	if (bus_endpoint == 0) {
359433d6423SLionel Sambuc 		log_warn(&log, "Couldn't find bus driver.\n");
360433d6423SLionel Sambuc 		return EXIT_FAILURE;
361433d6423SLionel Sambuc 	}
362433d6423SLionel Sambuc 
363433d6423SLionel Sambuc 	/* claim the device */
364433d6423SLionel Sambuc 	r = i2cdriver_reserve_device(bus_endpoint, address);
365433d6423SLionel Sambuc 	if (r != OK) {
366433d6423SLionel Sambuc 		log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
367433d6423SLionel Sambuc 		    address, r);
368433d6423SLionel Sambuc 		return EXIT_FAILURE;
369433d6423SLionel Sambuc 	}
370433d6423SLionel Sambuc 
371433d6423SLionel Sambuc 	r = tsl2550_init();
372433d6423SLionel Sambuc 	if (r != OK) {
373433d6423SLionel Sambuc 		log_warn(&log, "Device Init Failed\n");
374433d6423SLionel Sambuc 		return EXIT_FAILURE;
375433d6423SLionel Sambuc 	}
376433d6423SLionel Sambuc 
377433d6423SLionel Sambuc 	if (type != SEF_INIT_LU) {
378433d6423SLionel Sambuc 
379433d6423SLionel Sambuc 		/* sign up for updates about the i2c bus going down/up */
380433d6423SLionel Sambuc 		r = i2cdriver_subscribe_bus_updates(bus);
381433d6423SLionel Sambuc 		if (r != OK) {
382433d6423SLionel Sambuc 			log_warn(&log, "Couldn't subscribe to bus updates\n");
383433d6423SLionel Sambuc 			return EXIT_FAILURE;
384433d6423SLionel Sambuc 		}
385433d6423SLionel Sambuc 
386433d6423SLionel Sambuc 		i2cdriver_announce(bus);
387433d6423SLionel Sambuc 		log_debug(&log, "announced\n");
388433d6423SLionel Sambuc 	}
389433d6423SLionel Sambuc 
390433d6423SLionel Sambuc 	return OK;
391433d6423SLionel Sambuc }
392433d6423SLionel Sambuc 
393433d6423SLionel Sambuc static void
sef_local_startup(void)394433d6423SLionel Sambuc sef_local_startup(void)
395433d6423SLionel Sambuc {
396433d6423SLionel Sambuc 	/*
397433d6423SLionel Sambuc 	 * Register init callbacks. Use the same function for all event types
398433d6423SLionel Sambuc 	 */
399433d6423SLionel Sambuc 	sef_setcb_init_fresh(sef_cb_init);
400433d6423SLionel Sambuc 	sef_setcb_init_lu(sef_cb_init);
401433d6423SLionel Sambuc 	sef_setcb_init_restart(sef_cb_init);
402433d6423SLionel Sambuc 
403433d6423SLionel Sambuc 	/*
404433d6423SLionel Sambuc 	 * Register live update callbacks.
405433d6423SLionel Sambuc 	 */
406433d6423SLionel Sambuc 	sef_setcb_lu_state_save(sef_cb_lu_state_save);
407433d6423SLionel Sambuc 
408433d6423SLionel Sambuc 	/* Let SEF perform startup. */
409433d6423SLionel Sambuc 	sef_startup();
410433d6423SLionel Sambuc }
411433d6423SLionel Sambuc 
412433d6423SLionel Sambuc int
main(int argc,char * argv[])413433d6423SLionel Sambuc main(int argc, char *argv[])
414433d6423SLionel Sambuc {
415433d6423SLionel Sambuc 	int r;
416433d6423SLionel Sambuc 
417433d6423SLionel Sambuc 	env_setargs(argc, argv);
418433d6423SLionel Sambuc 
419433d6423SLionel Sambuc 	r = i2cdriver_env_parse(&bus, &address, valid_addrs);
420433d6423SLionel Sambuc 	if (r < 0) {
421433d6423SLionel Sambuc 		log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n");
422433d6423SLionel Sambuc 		log_warn(&log, "Example -args 'bus=1 address=0x39'\n");
423433d6423SLionel Sambuc 		return EXIT_FAILURE;
424433d6423SLionel Sambuc 	} else if (r > 0) {
425433d6423SLionel Sambuc 		log_warn(&log,
426433d6423SLionel Sambuc 		    "Invalid slave address for device, expecting 0x39\n");
427433d6423SLionel Sambuc 		return EXIT_FAILURE;
428433d6423SLionel Sambuc 	}
429433d6423SLionel Sambuc 
430433d6423SLionel Sambuc 	sef_local_startup();
431433d6423SLionel Sambuc 
432433d6423SLionel Sambuc 	chardriver_task(&tsl2550_tab);
433433d6423SLionel Sambuc 
434433d6423SLionel Sambuc 	return 0;
435433d6423SLionel Sambuc }
436