xref: /minix3/minix/drivers/bus/i2c/i2c.c (revision 3f82ac6a4e188419336747098d0d6616cd2f3d3d)
1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc  * i2c - generic driver for Inter-Integrated Circuit bus (I2C).
3433d6423SLionel Sambuc  */
4433d6423SLionel Sambuc 
5433d6423SLionel Sambuc /* kernel headers */
6433d6423SLionel Sambuc #include <minix/chardriver.h>
7433d6423SLionel Sambuc #include <minix/drivers.h>
8433d6423SLionel Sambuc #include <minix/ds.h>
9433d6423SLionel Sambuc #include <minix/i2c.h>
10433d6423SLionel Sambuc #include <minix/log.h>
11433d6423SLionel Sambuc #include <minix/type.h>
12433d6423SLionel Sambuc #include <minix/board.h>
13433d6423SLionel Sambuc 
14433d6423SLionel Sambuc /* system headers */
15433d6423SLionel Sambuc #include <sys/mman.h>
16433d6423SLionel Sambuc 
17433d6423SLionel Sambuc /* usr headers */
18433d6423SLionel Sambuc #include <string.h>
19433d6423SLionel Sambuc #include <stdio.h>
20433d6423SLionel Sambuc #include <stdlib.h>
21433d6423SLionel Sambuc 
22433d6423SLionel Sambuc /* SoC specific headers - 1 for each SoC */
23433d6423SLionel Sambuc #include "omap_i2c.h"
24433d6423SLionel Sambuc 
25433d6423SLionel Sambuc /* local definitions */
26433d6423SLionel Sambuc 
27433d6423SLionel Sambuc /* i2c slave addresses can be up to 10 bits */
28433d6423SLionel Sambuc #define NR_I2CDEV (0x3ff)
29433d6423SLionel Sambuc 
30433d6423SLionel Sambuc /* local function prototypes */
31433d6423SLionel Sambuc static int do_reserve(endpoint_t endpt, int slave_addr);
32433d6423SLionel Sambuc static int check_reservation(endpoint_t endpt, int slave_addr);
33433d6423SLionel Sambuc static void update_reservation(endpoint_t endpt, char *key);
34433d6423SLionel Sambuc static void ds_event(void);
35433d6423SLionel Sambuc 
36433d6423SLionel Sambuc static int validate_ioctl_exec(minix_i2c_ioctl_exec_t * ioctl_exec);
37433d6423SLionel Sambuc static int do_i2c_ioctl_exec(endpoint_t caller, cp_grant_id_t grant_nr);
38433d6423SLionel Sambuc 
39433d6423SLionel Sambuc static int env_parse_instance(void);
40433d6423SLionel Sambuc 
41433d6423SLionel Sambuc /* libchardriver callbacks */
42433d6423SLionel Sambuc static int i2c_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
43433d6423SLionel Sambuc 	cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id);
44433d6423SLionel Sambuc static void i2c_other(message * m, int ipc_status);
45433d6423SLionel Sambuc 
46433d6423SLionel Sambuc /* Globals  */
47433d6423SLionel Sambuc 
48433d6423SLionel Sambuc /* the bus that this instance of the driver is responsible for */
49433d6423SLionel Sambuc uint32_t i2c_bus_id;
50433d6423SLionel Sambuc 
51433d6423SLionel Sambuc /* Table of i2c device reservations. */
52433d6423SLionel Sambuc static struct i2cdev
53433d6423SLionel Sambuc {
54433d6423SLionel Sambuc 	uint8_t inuse;
55433d6423SLionel Sambuc 	endpoint_t endpt;
56433d6423SLionel Sambuc 	char key[DS_MAX_KEYLEN];
57433d6423SLionel Sambuc } i2cdev[NR_I2CDEV];
58433d6423SLionel Sambuc 
59433d6423SLionel Sambuc /* Process a request for an i2c operation.
60433d6423SLionel Sambuc  * This is the interface that all hardware specific code must implement.
61433d6423SLionel Sambuc  */
62433d6423SLionel Sambuc int (*process) (minix_i2c_ioctl_exec_t * ioctl_exec);
63433d6423SLionel Sambuc 
64433d6423SLionel Sambuc /* logging - use with log_warn(), log_info(), log_debug(), log_trace() */
65433d6423SLionel Sambuc static struct log log = {
66433d6423SLionel Sambuc 	.name = "i2c",
67433d6423SLionel Sambuc 	.log_level = LEVEL_INFO,
68433d6423SLionel Sambuc 	.log_func = default_log
69433d6423SLionel Sambuc };
70433d6423SLionel Sambuc 
71433d6423SLionel Sambuc /* Entry points to the i2c driver from libchardriver.
72433d6423SLionel Sambuc  * Only i2c_ioctl() and i2c_other() are implemented. The rest are no-op.
73433d6423SLionel Sambuc  */
74433d6423SLionel Sambuc static struct chardriver i2c_tab = {
75433d6423SLionel Sambuc 	.cdr_ioctl	= i2c_ioctl,
76433d6423SLionel Sambuc 	.cdr_other	= i2c_other
77433d6423SLionel Sambuc };
78433d6423SLionel Sambuc 
79*e1f889d2SCristiano Giuffrida static int
sef_cb_lu_state_save(int UNUSED (result),int UNUSED (flags))80*e1f889d2SCristiano Giuffrida sef_cb_lu_state_save(int UNUSED(result), int UNUSED(flags))
81*e1f889d2SCristiano Giuffrida {
82*e1f889d2SCristiano Giuffrida 	int r;
83*e1f889d2SCristiano Giuffrida 	char key[DS_MAX_KEYLEN];
84*e1f889d2SCristiano Giuffrida 
85*e1f889d2SCristiano Giuffrida 	memset(key, '\0', DS_MAX_KEYLEN);
86*e1f889d2SCristiano Giuffrida 	snprintf(key, DS_MAX_KEYLEN, "i2c.%d.i2cdev", i2c_bus_id + 1);
87*e1f889d2SCristiano Giuffrida 	r = ds_publish_mem(key, i2cdev, sizeof(i2cdev), DSF_OVERWRITE);
88*e1f889d2SCristiano Giuffrida 	if (r != OK) {
89*e1f889d2SCristiano Giuffrida 		log_warn(&log, "ds_publish_mem(%s) failed (r=%d)\n", key, r);
90*e1f889d2SCristiano Giuffrida 		return r;
91*e1f889d2SCristiano Giuffrida 	}
92*e1f889d2SCristiano Giuffrida 
93*e1f889d2SCristiano Giuffrida 	log_debug(&log, "State Saved\n");
94*e1f889d2SCristiano Giuffrida 
95*e1f889d2SCristiano Giuffrida 	return OK;
96*e1f889d2SCristiano Giuffrida }
97*e1f889d2SCristiano Giuffrida 
98433d6423SLionel Sambuc /*
99433d6423SLionel Sambuc  * Claim an unclaimed device for exclusive use by endpt. This function can
100433d6423SLionel Sambuc  * also be used to update the endpt if the endpt's label matches the label
101433d6423SLionel Sambuc  * already associated with the slave address. This is useful if a driver
102433d6423SLionel Sambuc  * shuts down unexpectedly and starts up with a new endpt and wants to reserve
103433d6423SLionel Sambuc  * the same device it reserved before.
104433d6423SLionel Sambuc  */
105433d6423SLionel Sambuc static int
do_reserve(endpoint_t endpt,int slave_addr)106433d6423SLionel Sambuc do_reserve(endpoint_t endpt, int slave_addr)
107433d6423SLionel Sambuc {
108433d6423SLionel Sambuc 	int r;
109433d6423SLionel Sambuc 	char key[DS_MAX_KEYLEN];
110433d6423SLionel Sambuc 	char label[DS_MAX_KEYLEN];
111433d6423SLionel Sambuc 
112433d6423SLionel Sambuc 	/* find the label for the endpoint */
113433d6423SLionel Sambuc 	r = ds_retrieve_label_name(label, endpt);
114433d6423SLionel Sambuc 	if (r != OK) {
115433d6423SLionel Sambuc 		log_warn(&log, "Couldn't find label for endpt='0x%x'\n",
116433d6423SLionel Sambuc 		    endpt);
117433d6423SLionel Sambuc 		return r;
118433d6423SLionel Sambuc 	}
119433d6423SLionel Sambuc 
120433d6423SLionel Sambuc 	/* construct the key i2cdriver_announce published (saves an IPC call) */
121433d6423SLionel Sambuc 	snprintf(key, DS_MAX_KEYLEN, "drv.i2c.%d.%s", i2c_bus_id + 1, label);
122433d6423SLionel Sambuc 
123433d6423SLionel Sambuc 	if (slave_addr < 0 || slave_addr >= NR_I2CDEV) {
124433d6423SLionel Sambuc 		log_debug(&log,
125433d6423SLionel Sambuc 		    "slave address must be positive & no more than 10 bits\n");
126433d6423SLionel Sambuc 		return EINVAL;
127433d6423SLionel Sambuc 	}
128433d6423SLionel Sambuc 
129433d6423SLionel Sambuc 	/* check if device is in use by another driver */
130433d6423SLionel Sambuc 	if (i2cdev[slave_addr].inuse != 0
131433d6423SLionel Sambuc 	    && strncmp(i2cdev[slave_addr].key, key, DS_MAX_KEYLEN) != 0) {
132433d6423SLionel Sambuc 		log_debug(&log, "address in use by '%s'/0x%x\n",
133433d6423SLionel Sambuc 		    i2cdev[slave_addr].key, i2cdev[slave_addr].endpt);
134433d6423SLionel Sambuc 		return EBUSY;
135433d6423SLionel Sambuc 	}
136433d6423SLionel Sambuc 
137433d6423SLionel Sambuc 	/* device is free or already owned by us, claim it */
138433d6423SLionel Sambuc 	i2cdev[slave_addr].inuse = 1;
139433d6423SLionel Sambuc 	i2cdev[slave_addr].endpt = endpt;
140433d6423SLionel Sambuc 	memcpy(i2cdev[slave_addr].key, key, DS_MAX_KEYLEN);
141433d6423SLionel Sambuc 
142*e1f889d2SCristiano Giuffrida 	sef_cb_lu_state_save(0, 0);	/* save reservations */
143433d6423SLionel Sambuc 
144433d6423SLionel Sambuc 	log_debug(&log, "Device 0x%x claimed by 0x%x key='%s'\n",
145433d6423SLionel Sambuc 	    slave_addr, endpt, key);
146433d6423SLionel Sambuc 
147433d6423SLionel Sambuc 	return OK;
148433d6423SLionel Sambuc }
149433d6423SLionel Sambuc 
150433d6423SLionel Sambuc /*
151433d6423SLionel Sambuc  * All drivers must reserve their device(s) before doing operations on them
152433d6423SLionel Sambuc  * (read/write, etc). ioctl()'s from VFS (i.e. user programs) can only use
153433d6423SLionel Sambuc  * devices that haven't been reserved. A driver isn't allowed to access a
154433d6423SLionel Sambuc  * device that another driver has reserved (not even other instances of the
155433d6423SLionel Sambuc  * same driver).
156433d6423SLionel Sambuc  */
157433d6423SLionel Sambuc static int
check_reservation(endpoint_t endpt,int slave_addr)158433d6423SLionel Sambuc check_reservation(endpoint_t endpt, int slave_addr)
159433d6423SLionel Sambuc {
160433d6423SLionel Sambuc 	if (slave_addr < 0 || slave_addr >= NR_I2CDEV) {
161433d6423SLionel Sambuc 		log_debug(&log,
162433d6423SLionel Sambuc 		    "slave address must be positive & no more than 10 bits\n");
163433d6423SLionel Sambuc 		return EINVAL;
164433d6423SLionel Sambuc 	}
165433d6423SLionel Sambuc 
166433d6423SLionel Sambuc 	if (endpt == VFS_PROC_NR && i2cdev[slave_addr].inuse == 0) {
167433d6423SLionel Sambuc 		log_debug(&log,
168433d6423SLionel Sambuc 		    "allowing ioctl() from VFS to access unclaimed device\n");
169433d6423SLionel Sambuc 		return OK;
170433d6423SLionel Sambuc 	}
171433d6423SLionel Sambuc 
172433d6423SLionel Sambuc 	if (i2cdev[slave_addr].inuse && i2cdev[slave_addr].endpt != endpt) {
173433d6423SLionel Sambuc 		log_debug(&log, "device reserved by another endpoint\n");
174433d6423SLionel Sambuc 		return EBUSY;
175433d6423SLionel Sambuc 	} else if (i2cdev[slave_addr].inuse == 0) {
176433d6423SLionel Sambuc 		log_debug(&log,
177433d6423SLionel Sambuc 		    "all drivers sending messages directly to this driver must reserve\n");
178433d6423SLionel Sambuc 		return EPERM;
179433d6423SLionel Sambuc 	} else {
180433d6423SLionel Sambuc 		log_debug(&log, "allowing access to registered device\n");
181433d6423SLionel Sambuc 		return OK;
182433d6423SLionel Sambuc 	}
183433d6423SLionel Sambuc }
184433d6423SLionel Sambuc 
185433d6423SLionel Sambuc /*
186433d6423SLionel Sambuc  * i2c listens to updates from ds about i2c device drivers starting up.
187433d6423SLionel Sambuc  * When a driver comes back up with the same label, the endpt associated
188433d6423SLionel Sambuc  * with the reservation needs to be updated. This function does the updating.
189433d6423SLionel Sambuc  */
190433d6423SLionel Sambuc static void
update_reservation(endpoint_t endpt,char * key)191433d6423SLionel Sambuc update_reservation(endpoint_t endpt, char *key)
192433d6423SLionel Sambuc {
193433d6423SLionel Sambuc 	int i;
194433d6423SLionel Sambuc 
195433d6423SLionel Sambuc 	log_debug(&log, "Updating reservation for '%s' endpt=0x%x\n", key,
196433d6423SLionel Sambuc 	    endpt);
197433d6423SLionel Sambuc 
198433d6423SLionel Sambuc 	for (i = 0; i < NR_I2CDEV; i++) {
199433d6423SLionel Sambuc 
200433d6423SLionel Sambuc 		/* find devices in use that the driver owns */
201433d6423SLionel Sambuc 		if (i2cdev[i].inuse != 0
202433d6423SLionel Sambuc 		    && strncmp(i2cdev[i].key, key, DS_MAX_KEYLEN) == 0) {
203433d6423SLionel Sambuc 			/* update reservation with new endpoint */
204433d6423SLionel Sambuc 			do_reserve(endpt, i);
205433d6423SLionel Sambuc 			log_debug(&log, "Found device to update 0x%x\n", i);
206433d6423SLionel Sambuc 		}
207433d6423SLionel Sambuc 	}
208433d6423SLionel Sambuc }
209433d6423SLionel Sambuc 
210433d6423SLionel Sambuc /*
211433d6423SLionel Sambuc  * Checks a minix_i2c_ioctl_exec_t to see if the fields make sense.
212433d6423SLionel Sambuc  */
213433d6423SLionel Sambuc static int
validate_ioctl_exec(minix_i2c_ioctl_exec_t * ioctl_exec)214433d6423SLionel Sambuc validate_ioctl_exec(minix_i2c_ioctl_exec_t * ioctl_exec)
215433d6423SLionel Sambuc {
216433d6423SLionel Sambuc 	i2c_op_t op;
217433d6423SLionel Sambuc 	i2c_addr_t addr;
218433d6423SLionel Sambuc 	size_t len;
219433d6423SLionel Sambuc 
220433d6423SLionel Sambuc 	op = ioctl_exec->iie_op;
221433d6423SLionel Sambuc 	if (op != I2C_OP_READ &&
222433d6423SLionel Sambuc 	    op != I2C_OP_READ_WITH_STOP &&
223433d6423SLionel Sambuc 	    op != I2C_OP_WRITE &&
224433d6423SLionel Sambuc 	    op != I2C_OP_WRITE_WITH_STOP &&
225433d6423SLionel Sambuc 	    op != I2C_OP_READ_BLOCK && op != I2C_OP_WRITE_BLOCK) {
226433d6423SLionel Sambuc 		log_warn(&log, "iie_op value not valid\n");
227433d6423SLionel Sambuc 		return EINVAL;
228433d6423SLionel Sambuc 	}
229433d6423SLionel Sambuc 
230433d6423SLionel Sambuc 	addr = ioctl_exec->iie_addr;
231433d6423SLionel Sambuc 	if (addr < 0 || addr >= NR_I2CDEV) {
232433d6423SLionel Sambuc 		log_warn(&log, "iie_addr out of range 0x0-0x%x\n", NR_I2CDEV);
233433d6423SLionel Sambuc 		return EINVAL;
234433d6423SLionel Sambuc 	}
235433d6423SLionel Sambuc 
236433d6423SLionel Sambuc 	len = ioctl_exec->iie_cmdlen;
237433d6423SLionel Sambuc 	if (len > I2C_EXEC_MAX_CMDLEN) {
238433d6423SLionel Sambuc 		log_warn(&log,
239433d6423SLionel Sambuc 		    "iie_cmdlen out of range 0-I2C_EXEC_MAX_CMDLEN\n");
240433d6423SLionel Sambuc 		return EINVAL;
241433d6423SLionel Sambuc 	}
242433d6423SLionel Sambuc 
243433d6423SLionel Sambuc 	len = ioctl_exec->iie_buflen;
244433d6423SLionel Sambuc 	if (len > I2C_EXEC_MAX_BUFLEN) {
245433d6423SLionel Sambuc 		log_warn(&log,
246433d6423SLionel Sambuc 		    "iie_buflen out of range 0-I2C_EXEC_MAX_BUFLEN\n");
247433d6423SLionel Sambuc 		return EINVAL;
248433d6423SLionel Sambuc 	}
249433d6423SLionel Sambuc 
250433d6423SLionel Sambuc 	return OK;
251433d6423SLionel Sambuc }
252433d6423SLionel Sambuc 
253433d6423SLionel Sambuc /*
254433d6423SLionel Sambuc  * Performs the action in minix_i2c_ioctl_exec_t.
255433d6423SLionel Sambuc  */
256433d6423SLionel Sambuc static int
do_i2c_ioctl_exec(endpoint_t caller,cp_grant_id_t grant_nr)257433d6423SLionel Sambuc do_i2c_ioctl_exec(endpoint_t caller, cp_grant_id_t grant_nr)
258433d6423SLionel Sambuc {
259433d6423SLionel Sambuc 	int r;
260433d6423SLionel Sambuc 	minix_i2c_ioctl_exec_t ioctl_exec;
261433d6423SLionel Sambuc 
262433d6423SLionel Sambuc 	/* Copy the requested exection into the driver */
263433d6423SLionel Sambuc 	r = sys_safecopyfrom(caller, grant_nr, (vir_bytes) 0,
264433d6423SLionel Sambuc 	    (vir_bytes) & ioctl_exec, sizeof(ioctl_exec));
265433d6423SLionel Sambuc 	if (r != OK) {
266433d6423SLionel Sambuc 		log_warn(&log, "sys_safecopyfrom() failed\n");
267433d6423SLionel Sambuc 		return r;
268433d6423SLionel Sambuc 	}
269433d6423SLionel Sambuc 
270433d6423SLionel Sambuc 	/* input validation */
271433d6423SLionel Sambuc 	r = validate_ioctl_exec(&ioctl_exec);
272433d6423SLionel Sambuc 	if (r != OK) {
273433d6423SLionel Sambuc 		log_debug(&log, "Message validation failed\n");
274433d6423SLionel Sambuc 		return r;
275433d6423SLionel Sambuc 	}
276433d6423SLionel Sambuc 
277433d6423SLionel Sambuc 	/* permission check */
278433d6423SLionel Sambuc 	r = check_reservation(caller, ioctl_exec.iie_addr);
279433d6423SLionel Sambuc 	if (r != OK) {
280433d6423SLionel Sambuc 		log_debug(&log, "check_reservation() denied the request\n");
281433d6423SLionel Sambuc 		return r;
282433d6423SLionel Sambuc 	}
283433d6423SLionel Sambuc 
284433d6423SLionel Sambuc 	/* Call the device specific code to execute the action */
285433d6423SLionel Sambuc 	r = process(&ioctl_exec);
286433d6423SLionel Sambuc 	if (r != OK) {
287433d6423SLionel Sambuc 		log_debug(&log, "process() failed\n");
288433d6423SLionel Sambuc 		return r;
289433d6423SLionel Sambuc 	}
290433d6423SLionel Sambuc 
291433d6423SLionel Sambuc 	/* Copy the results of the execution back to the calling process */
292433d6423SLionel Sambuc 	r = sys_safecopyto(caller, grant_nr, (vir_bytes) 0,
293433d6423SLionel Sambuc 	    (vir_bytes) & ioctl_exec, sizeof(ioctl_exec));
294433d6423SLionel Sambuc 	if (r != OK) {
295433d6423SLionel Sambuc 		log_warn(&log, "sys_safecopyto() failed\n");
296433d6423SLionel Sambuc 		return r;
297433d6423SLionel Sambuc 	}
298433d6423SLionel Sambuc 
299433d6423SLionel Sambuc 	return OK;
300433d6423SLionel Sambuc }
301433d6423SLionel Sambuc 
302433d6423SLionel Sambuc static int
i2c_ioctl(devminor_t UNUSED (minor),unsigned long request,endpoint_t endpt,cp_grant_id_t grant,int UNUSED (flags),endpoint_t UNUSED (user_endpt),cdev_id_t UNUSED (id))303433d6423SLionel Sambuc i2c_ioctl(devminor_t UNUSED(minor), unsigned long request, endpoint_t endpt,
304433d6423SLionel Sambuc 	cp_grant_id_t grant, int UNUSED(flags), endpoint_t UNUSED(user_endpt),
305433d6423SLionel Sambuc 	cdev_id_t UNUSED(id))
306433d6423SLionel Sambuc {
307433d6423SLionel Sambuc 	int r;
308433d6423SLionel Sambuc 
309433d6423SLionel Sambuc 	switch (request) {
310433d6423SLionel Sambuc 	case MINIX_I2C_IOCTL_EXEC:
311433d6423SLionel Sambuc 		r = do_i2c_ioctl_exec(endpt, grant);
312433d6423SLionel Sambuc 		break;
313433d6423SLionel Sambuc 	default:
314433d6423SLionel Sambuc 		log_warn(&log, "Invalid ioctl() 0x%x\n", request);
315433d6423SLionel Sambuc 		r = ENOTTY;
316433d6423SLionel Sambuc 		break;
317433d6423SLionel Sambuc 	}
318433d6423SLionel Sambuc 
319433d6423SLionel Sambuc 	return r;
320433d6423SLionel Sambuc }
321433d6423SLionel Sambuc 
322433d6423SLionel Sambuc static void
i2c_other(message * m,int ipc_status)323433d6423SLionel Sambuc i2c_other(message * m, int ipc_status)
324433d6423SLionel Sambuc {
325433d6423SLionel Sambuc 	message m_reply;
326433d6423SLionel Sambuc 	int r;
327433d6423SLionel Sambuc 
328433d6423SLionel Sambuc 	if (is_ipc_notify(ipc_status)) {
329433d6423SLionel Sambuc 		/* handle notifications about drivers changing state */
330433d6423SLionel Sambuc 		if (m->m_source == DS_PROC_NR) {
331433d6423SLionel Sambuc 			ds_event();
332433d6423SLionel Sambuc 		}
333433d6423SLionel Sambuc 		return;
334433d6423SLionel Sambuc 	}
335433d6423SLionel Sambuc 
336433d6423SLionel Sambuc 	switch (m->m_type) {
337433d6423SLionel Sambuc 	case BUSC_I2C_RESERVE:
338433d6423SLionel Sambuc 		/* reserve a device on the bus for exclusive access */
339433d6423SLionel Sambuc 		r = do_reserve(m->m_source, m->m_li2cdriver_i2c_busc_i2c_reserve.addr);
340433d6423SLionel Sambuc 		break;
341433d6423SLionel Sambuc 	case BUSC_I2C_EXEC:
342433d6423SLionel Sambuc 		/* handle request from another driver */
343433d6423SLionel Sambuc 		r = do_i2c_ioctl_exec(m->m_source, m->m_li2cdriver_i2c_busc_i2c_exec.grant);
344433d6423SLionel Sambuc 		break;
345433d6423SLionel Sambuc 	default:
346433d6423SLionel Sambuc 		log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
347433d6423SLionel Sambuc 		r = EINVAL;
348433d6423SLionel Sambuc 		break;
349433d6423SLionel Sambuc 	}
350433d6423SLionel Sambuc 
351433d6423SLionel Sambuc 	log_trace(&log, "i2c_other() returning r=%d\n", r);
352433d6423SLionel Sambuc 
353433d6423SLionel Sambuc 	/* Send a reply. */
354433d6423SLionel Sambuc 	memset(&m_reply, 0, sizeof(m_reply));
355433d6423SLionel Sambuc 	m_reply.m_type = r;
356433d6423SLionel Sambuc 
357433d6423SLionel Sambuc 	if ((r = ipc_send(m->m_source, &m_reply)) != OK)
358433d6423SLionel Sambuc 		log_warn(&log, "ipc_send() to %d failed: %d\n", m->m_source, r);
359433d6423SLionel Sambuc }
360433d6423SLionel Sambuc 
361433d6423SLionel Sambuc /*
362433d6423SLionel Sambuc  * The bus drivers are subscribed to DS events about device drivers on their
363433d6423SLionel Sambuc  * bus. When the device drivers restart, DS sends a notification and this
364433d6423SLionel Sambuc  * function updates the reservation table with the device driver's new
365433d6423SLionel Sambuc  * endpoint.
366433d6423SLionel Sambuc  */
367433d6423SLionel Sambuc static void
ds_event(void)368433d6423SLionel Sambuc ds_event(void)
369433d6423SLionel Sambuc {
370433d6423SLionel Sambuc 	char key[DS_MAX_KEYLEN];
371433d6423SLionel Sambuc 	u32_t value;
372433d6423SLionel Sambuc 	int type;
373433d6423SLionel Sambuc 	endpoint_t owner_endpoint;
374433d6423SLionel Sambuc 	int r;
375433d6423SLionel Sambuc 
376433d6423SLionel Sambuc 	/* check for pending events */
377433d6423SLionel Sambuc 	while ((r = ds_check(key, &type, &owner_endpoint)) == OK) {
378433d6423SLionel Sambuc 
379433d6423SLionel Sambuc 		r = ds_retrieve_u32(key, &value);
380433d6423SLionel Sambuc 		if (r != OK) {
381433d6423SLionel Sambuc 			log_warn(&log, "ds_retrieve_u32() failed r=%d\n", r);
382433d6423SLionel Sambuc 			return;
383433d6423SLionel Sambuc 		}
384433d6423SLionel Sambuc 
385433d6423SLionel Sambuc 		log_debug(&log, "key='%s' owner_endpoint=0x%x\n", key,
386433d6423SLionel Sambuc 		    owner_endpoint);
387433d6423SLionel Sambuc 
388433d6423SLionel Sambuc 		if (value == DS_DRIVER_UP) {
389433d6423SLionel Sambuc 			/* clean up any old reservations the driver had */
390433d6423SLionel Sambuc 			log_debug(&log, "DS_DRIVER_UP\n");
391433d6423SLionel Sambuc 			update_reservation(owner_endpoint, key);
392433d6423SLionel Sambuc 		}
393433d6423SLionel Sambuc 	}
394433d6423SLionel Sambuc }
395433d6423SLionel Sambuc 
396433d6423SLionel Sambuc static int
lu_state_restore(void)397433d6423SLionel Sambuc lu_state_restore(void)
398433d6423SLionel Sambuc {
399433d6423SLionel Sambuc 	int r;
400433d6423SLionel Sambuc 	char key[DS_MAX_KEYLEN];
401433d6423SLionel Sambuc 	size_t size;
402433d6423SLionel Sambuc 
403433d6423SLionel Sambuc 	env_parse_instance();
404433d6423SLionel Sambuc 
405433d6423SLionel Sambuc 	size = sizeof(i2cdev);
406433d6423SLionel Sambuc 
407433d6423SLionel Sambuc 	memset(key, '\0', DS_MAX_KEYLEN);
408433d6423SLionel Sambuc 	snprintf(key, DS_MAX_KEYLEN, "i2c.%d.i2cdev", i2c_bus_id + 1);
409433d6423SLionel Sambuc 
410433d6423SLionel Sambuc 	r = ds_retrieve_mem(key, (char *) i2cdev, &size);
411433d6423SLionel Sambuc 	if (r != OK) {
412433d6423SLionel Sambuc 		log_warn(&log, "ds_retrieve_mem(%s) failed (r=%d)\n", key, r);
413433d6423SLionel Sambuc 		return r;
414433d6423SLionel Sambuc 	}
415433d6423SLionel Sambuc 
416433d6423SLionel Sambuc 	log_debug(&log, "State Restored\n");
417433d6423SLionel Sambuc 
418433d6423SLionel Sambuc 	return OK;
419433d6423SLionel Sambuc }
420433d6423SLionel Sambuc 
421433d6423SLionel Sambuc static int
sef_cb_init(int type,sef_init_info_t * UNUSED (info))422433d6423SLionel Sambuc sef_cb_init(int type, sef_init_info_t * UNUSED(info))
423433d6423SLionel Sambuc {
424433d6423SLionel Sambuc 	int r;
425433d6423SLionel Sambuc 	char regex[DS_MAX_KEYLEN];
426433d6423SLionel Sambuc 	struct machine machine;
427433d6423SLionel Sambuc 	sys_getmachine(&machine);
428433d6423SLionel Sambuc 
429433d6423SLionel Sambuc 	if (type != SEF_INIT_FRESH) {
430433d6423SLionel Sambuc 		/* Restore a prior state. */
431433d6423SLionel Sambuc 		lu_state_restore();
432433d6423SLionel Sambuc 	}
433433d6423SLionel Sambuc 
434433d6423SLionel Sambuc 	if (BOARD_IS_BBXM(machine.board_id) || BOARD_IS_BB(machine.board_id)){
435433d6423SLionel Sambuc 		/* Set callback and initialize the bus */
436433d6423SLionel Sambuc 		r = omap_interface_setup(&process, i2c_bus_id);
437433d6423SLionel Sambuc 		if (r != OK) {
438433d6423SLionel Sambuc 			return r;
439433d6423SLionel Sambuc 		}
440433d6423SLionel Sambuc 	} else {
441433d6423SLionel Sambuc 		return ENODEV;
442433d6423SLionel Sambuc 	}
443433d6423SLionel Sambuc 
444433d6423SLionel Sambuc 	/* Announce we are up when necessary. */
445433d6423SLionel Sambuc 	if (type != SEF_INIT_LU) {
446433d6423SLionel Sambuc 
447433d6423SLionel Sambuc 		/* only capture events for this particular bus */
448433d6423SLionel Sambuc 		snprintf(regex, DS_MAX_KEYLEN, "drv\\.i2c\\.%d\\..*",
449433d6423SLionel Sambuc 		    i2c_bus_id + 1);
450433d6423SLionel Sambuc 
451433d6423SLionel Sambuc 		/* Subscribe to driver events for i2c drivers */
452433d6423SLionel Sambuc 		r = ds_subscribe(regex, DSF_INITIAL | DSF_OVERWRITE);
453433d6423SLionel Sambuc 		if (r != OK) {
454433d6423SLionel Sambuc 			log_warn(&log, "ds_subscribe() failed\n");
455433d6423SLionel Sambuc 			return r;
456433d6423SLionel Sambuc 		}
457433d6423SLionel Sambuc 
458433d6423SLionel Sambuc 		chardriver_announce();
459433d6423SLionel Sambuc 	}
460433d6423SLionel Sambuc 
461433d6423SLionel Sambuc 	/* Save state */
462*e1f889d2SCristiano Giuffrida 	sef_cb_lu_state_save(0, 0);
463433d6423SLionel Sambuc 
464433d6423SLionel Sambuc 	/* Initialization completed successfully. */
465433d6423SLionel Sambuc 	return OK;
466433d6423SLionel Sambuc }
467433d6423SLionel Sambuc 
468433d6423SLionel Sambuc static void
sef_local_startup()469433d6423SLionel Sambuc sef_local_startup()
470433d6423SLionel Sambuc {
471433d6423SLionel Sambuc 	/* Register init callbacks. */
472433d6423SLionel Sambuc 	sef_setcb_init_fresh(sef_cb_init);
473433d6423SLionel Sambuc 	sef_setcb_init_lu(sef_cb_init);
474433d6423SLionel Sambuc 	sef_setcb_init_restart(sef_cb_init);
475433d6423SLionel Sambuc 
476433d6423SLionel Sambuc 	/* Register live update callbacks */
477433d6423SLionel Sambuc 	sef_setcb_lu_state_save(sef_cb_lu_state_save);
478433d6423SLionel Sambuc 
479433d6423SLionel Sambuc 	/* Let SEF perform startup. */
480433d6423SLionel Sambuc 	sef_startup();
481433d6423SLionel Sambuc }
482433d6423SLionel Sambuc 
483433d6423SLionel Sambuc static int
env_parse_instance(void)484433d6423SLionel Sambuc env_parse_instance(void)
485433d6423SLionel Sambuc {
486433d6423SLionel Sambuc 	int r;
487433d6423SLionel Sambuc 	long instance;
488433d6423SLionel Sambuc 
489433d6423SLionel Sambuc 	/* Parse the instance number passed to service */
490433d6423SLionel Sambuc 	instance = 0;
491433d6423SLionel Sambuc 	r = env_parse("instance", "d", 0, &instance, 1, 3);
492433d6423SLionel Sambuc 	if (r == -1) {
493433d6423SLionel Sambuc 		log_warn(&log,
494433d6423SLionel Sambuc 		    "Expecting '-arg instance=N' argument (N=1..3)\n");
495433d6423SLionel Sambuc 		return EXIT_FAILURE;
496433d6423SLionel Sambuc 	}
497433d6423SLionel Sambuc 
498433d6423SLionel Sambuc 	/* Device files count from 1, hardware starts counting from 0 */
499433d6423SLionel Sambuc 	i2c_bus_id = instance - 1;
500433d6423SLionel Sambuc 
501433d6423SLionel Sambuc 	return OK;
502433d6423SLionel Sambuc }
503433d6423SLionel Sambuc 
504433d6423SLionel Sambuc int
main(int argc,char * argv[])505433d6423SLionel Sambuc main(int argc, char *argv[])
506433d6423SLionel Sambuc {
507433d6423SLionel Sambuc 	int r;
508433d6423SLionel Sambuc 
509433d6423SLionel Sambuc 	env_setargs(argc, argv);
510433d6423SLionel Sambuc 
511433d6423SLionel Sambuc 	r = env_parse_instance();
512433d6423SLionel Sambuc 	if (r != OK) {
513433d6423SLionel Sambuc 		return r;
514433d6423SLionel Sambuc 	}
515433d6423SLionel Sambuc 
516433d6423SLionel Sambuc 	memset(i2cdev, '\0', sizeof(i2cdev));
517433d6423SLionel Sambuc 	sef_local_startup();
518433d6423SLionel Sambuc 	chardriver_task(&i2c_tab);
519433d6423SLionel Sambuc 
520433d6423SLionel Sambuc 	return OK;
521433d6423SLionel Sambuc }
522