xref: /minix/minix/drivers/bus/i2c/i2c.c (revision 0a6a1f1d)
1 /*
2  * i2c - generic driver for Inter-Integrated Circuit bus (I2C).
3  */
4 
5 /* kernel headers */
6 #include <minix/chardriver.h>
7 #include <minix/drivers.h>
8 #include <minix/ds.h>
9 #include <minix/i2c.h>
10 #include <minix/log.h>
11 #include <minix/type.h>
12 #include <minix/board.h>
13 
14 /* system headers */
15 #include <sys/mman.h>
16 
17 /* usr headers */
18 #include <string.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 
22 /* SoC specific headers - 1 for each SoC */
23 #include "omap_i2c.h"
24 
25 /* local definitions */
26 
27 /* i2c slave addresses can be up to 10 bits */
28 #define NR_I2CDEV (0x3ff)
29 
30 /* local function prototypes */
31 static int do_reserve(endpoint_t endpt, int slave_addr);
32 static int check_reservation(endpoint_t endpt, int slave_addr);
33 static void update_reservation(endpoint_t endpt, char *key);
34 static void ds_event(void);
35 
36 static int validate_ioctl_exec(minix_i2c_ioctl_exec_t * ioctl_exec);
37 static int do_i2c_ioctl_exec(endpoint_t caller, cp_grant_id_t grant_nr);
38 
39 static int env_parse_instance(void);
40 
41 /* libchardriver callbacks */
42 static int i2c_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
43 	cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id);
44 static void i2c_other(message * m, int ipc_status);
45 
46 /* Globals  */
47 
48 /* the bus that this instance of the driver is responsible for */
49 uint32_t i2c_bus_id;
50 
51 /* Table of i2c device reservations. */
52 static struct i2cdev
53 {
54 	uint8_t inuse;
55 	endpoint_t endpt;
56 	char key[DS_MAX_KEYLEN];
57 } i2cdev[NR_I2CDEV];
58 
59 /* Process a request for an i2c operation.
60  * This is the interface that all hardware specific code must implement.
61  */
62 int (*process) (minix_i2c_ioctl_exec_t * ioctl_exec);
63 
64 /* logging - use with log_warn(), log_info(), log_debug(), log_trace() */
65 static struct log log = {
66 	.name = "i2c",
67 	.log_level = LEVEL_INFO,
68 	.log_func = default_log
69 };
70 
71 /* Entry points to the i2c driver from libchardriver.
72  * Only i2c_ioctl() and i2c_other() are implemented. The rest are no-op.
73  */
74 static struct chardriver i2c_tab = {
75 	.cdr_ioctl	= i2c_ioctl,
76 	.cdr_other	= i2c_other
77 };
78 
79 static int
80 sef_cb_lu_state_save(int UNUSED(result), int UNUSED(flags))
81 {
82 	int r;
83 	char key[DS_MAX_KEYLEN];
84 
85 	memset(key, '\0', DS_MAX_KEYLEN);
86 	snprintf(key, DS_MAX_KEYLEN, "i2c.%d.i2cdev", i2c_bus_id + 1);
87 	r = ds_publish_mem(key, i2cdev, sizeof(i2cdev), DSF_OVERWRITE);
88 	if (r != OK) {
89 		log_warn(&log, "ds_publish_mem(%s) failed (r=%d)\n", key, r);
90 		return r;
91 	}
92 
93 	log_debug(&log, "State Saved\n");
94 
95 	return OK;
96 }
97 
98 /*
99  * Claim an unclaimed device for exclusive use by endpt. This function can
100  * also be used to update the endpt if the endpt's label matches the label
101  * already associated with the slave address. This is useful if a driver
102  * shuts down unexpectedly and starts up with a new endpt and wants to reserve
103  * the same device it reserved before.
104  */
105 static int
106 do_reserve(endpoint_t endpt, int slave_addr)
107 {
108 	int r;
109 	char key[DS_MAX_KEYLEN];
110 	char label[DS_MAX_KEYLEN];
111 
112 	/* find the label for the endpoint */
113 	r = ds_retrieve_label_name(label, endpt);
114 	if (r != OK) {
115 		log_warn(&log, "Couldn't find label for endpt='0x%x'\n",
116 		    endpt);
117 		return r;
118 	}
119 
120 	/* construct the key i2cdriver_announce published (saves an IPC call) */
121 	snprintf(key, DS_MAX_KEYLEN, "drv.i2c.%d.%s", i2c_bus_id + 1, label);
122 
123 	if (slave_addr < 0 || slave_addr >= NR_I2CDEV) {
124 		log_debug(&log,
125 		    "slave address must be positive & no more than 10 bits\n");
126 		return EINVAL;
127 	}
128 
129 	/* check if device is in use by another driver */
130 	if (i2cdev[slave_addr].inuse != 0
131 	    && strncmp(i2cdev[slave_addr].key, key, DS_MAX_KEYLEN) != 0) {
132 		log_debug(&log, "address in use by '%s'/0x%x\n",
133 		    i2cdev[slave_addr].key, i2cdev[slave_addr].endpt);
134 		return EBUSY;
135 	}
136 
137 	/* device is free or already owned by us, claim it */
138 	i2cdev[slave_addr].inuse = 1;
139 	i2cdev[slave_addr].endpt = endpt;
140 	memcpy(i2cdev[slave_addr].key, key, DS_MAX_KEYLEN);
141 
142 	sef_cb_lu_state_save(0, 0);	/* save reservations */
143 
144 	log_debug(&log, "Device 0x%x claimed by 0x%x key='%s'\n",
145 	    slave_addr, endpt, key);
146 
147 	return OK;
148 }
149 
150 /*
151  * All drivers must reserve their device(s) before doing operations on them
152  * (read/write, etc). ioctl()'s from VFS (i.e. user programs) can only use
153  * devices that haven't been reserved. A driver isn't allowed to access a
154  * device that another driver has reserved (not even other instances of the
155  * same driver).
156  */
157 static int
158 check_reservation(endpoint_t endpt, int slave_addr)
159 {
160 	if (slave_addr < 0 || slave_addr >= NR_I2CDEV) {
161 		log_debug(&log,
162 		    "slave address must be positive & no more than 10 bits\n");
163 		return EINVAL;
164 	}
165 
166 	if (endpt == VFS_PROC_NR && i2cdev[slave_addr].inuse == 0) {
167 		log_debug(&log,
168 		    "allowing ioctl() from VFS to access unclaimed device\n");
169 		return OK;
170 	}
171 
172 	if (i2cdev[slave_addr].inuse && i2cdev[slave_addr].endpt != endpt) {
173 		log_debug(&log, "device reserved by another endpoint\n");
174 		return EBUSY;
175 	} else if (i2cdev[slave_addr].inuse == 0) {
176 		log_debug(&log,
177 		    "all drivers sending messages directly to this driver must reserve\n");
178 		return EPERM;
179 	} else {
180 		log_debug(&log, "allowing access to registered device\n");
181 		return OK;
182 	}
183 }
184 
185 /*
186  * i2c listens to updates from ds about i2c device drivers starting up.
187  * When a driver comes back up with the same label, the endpt associated
188  * with the reservation needs to be updated. This function does the updating.
189  */
190 static void
191 update_reservation(endpoint_t endpt, char *key)
192 {
193 	int i;
194 
195 	log_debug(&log, "Updating reservation for '%s' endpt=0x%x\n", key,
196 	    endpt);
197 
198 	for (i = 0; i < NR_I2CDEV; i++) {
199 
200 		/* find devices in use that the driver owns */
201 		if (i2cdev[i].inuse != 0
202 		    && strncmp(i2cdev[i].key, key, DS_MAX_KEYLEN) == 0) {
203 			/* update reservation with new endpoint */
204 			do_reserve(endpt, i);
205 			log_debug(&log, "Found device to update 0x%x\n", i);
206 		}
207 	}
208 }
209 
210 /*
211  * Checks a minix_i2c_ioctl_exec_t to see if the fields make sense.
212  */
213 static int
214 validate_ioctl_exec(minix_i2c_ioctl_exec_t * ioctl_exec)
215 {
216 	i2c_op_t op;
217 	i2c_addr_t addr;
218 	size_t len;
219 
220 	op = ioctl_exec->iie_op;
221 	if (op != I2C_OP_READ &&
222 	    op != I2C_OP_READ_WITH_STOP &&
223 	    op != I2C_OP_WRITE &&
224 	    op != I2C_OP_WRITE_WITH_STOP &&
225 	    op != I2C_OP_READ_BLOCK && op != I2C_OP_WRITE_BLOCK) {
226 		log_warn(&log, "iie_op value not valid\n");
227 		return EINVAL;
228 	}
229 
230 	addr = ioctl_exec->iie_addr;
231 	if (addr < 0 || addr >= NR_I2CDEV) {
232 		log_warn(&log, "iie_addr out of range 0x0-0x%x\n", NR_I2CDEV);
233 		return EINVAL;
234 	}
235 
236 	len = ioctl_exec->iie_cmdlen;
237 	if (len > I2C_EXEC_MAX_CMDLEN) {
238 		log_warn(&log,
239 		    "iie_cmdlen out of range 0-I2C_EXEC_MAX_CMDLEN\n");
240 		return EINVAL;
241 	}
242 
243 	len = ioctl_exec->iie_buflen;
244 	if (len > I2C_EXEC_MAX_BUFLEN) {
245 		log_warn(&log,
246 		    "iie_buflen out of range 0-I2C_EXEC_MAX_BUFLEN\n");
247 		return EINVAL;
248 	}
249 
250 	return OK;
251 }
252 
253 /*
254  * Performs the action in minix_i2c_ioctl_exec_t.
255  */
256 static int
257 do_i2c_ioctl_exec(endpoint_t caller, cp_grant_id_t grant_nr)
258 {
259 	int r;
260 	minix_i2c_ioctl_exec_t ioctl_exec;
261 
262 	/* Copy the requested exection into the driver */
263 	r = sys_safecopyfrom(caller, grant_nr, (vir_bytes) 0,
264 	    (vir_bytes) & ioctl_exec, sizeof(ioctl_exec));
265 	if (r != OK) {
266 		log_warn(&log, "sys_safecopyfrom() failed\n");
267 		return r;
268 	}
269 
270 	/* input validation */
271 	r = validate_ioctl_exec(&ioctl_exec);
272 	if (r != OK) {
273 		log_debug(&log, "Message validation failed\n");
274 		return r;
275 	}
276 
277 	/* permission check */
278 	r = check_reservation(caller, ioctl_exec.iie_addr);
279 	if (r != OK) {
280 		log_debug(&log, "check_reservation() denied the request\n");
281 		return r;
282 	}
283 
284 	/* Call the device specific code to execute the action */
285 	r = process(&ioctl_exec);
286 	if (r != OK) {
287 		log_debug(&log, "process() failed\n");
288 		return r;
289 	}
290 
291 	/* Copy the results of the execution back to the calling process */
292 	r = sys_safecopyto(caller, grant_nr, (vir_bytes) 0,
293 	    (vir_bytes) & ioctl_exec, sizeof(ioctl_exec));
294 	if (r != OK) {
295 		log_warn(&log, "sys_safecopyto() failed\n");
296 		return r;
297 	}
298 
299 	return OK;
300 }
301 
302 static int
303 i2c_ioctl(devminor_t UNUSED(minor), unsigned long request, endpoint_t endpt,
304 	cp_grant_id_t grant, int UNUSED(flags), endpoint_t UNUSED(user_endpt),
305 	cdev_id_t UNUSED(id))
306 {
307 	int r;
308 
309 	switch (request) {
310 	case MINIX_I2C_IOCTL_EXEC:
311 		r = do_i2c_ioctl_exec(endpt, grant);
312 		break;
313 	default:
314 		log_warn(&log, "Invalid ioctl() 0x%x\n", request);
315 		r = ENOTTY;
316 		break;
317 	}
318 
319 	return r;
320 }
321 
322 static void
323 i2c_other(message * m, int ipc_status)
324 {
325 	message m_reply;
326 	int r;
327 
328 	if (is_ipc_notify(ipc_status)) {
329 		/* handle notifications about drivers changing state */
330 		if (m->m_source == DS_PROC_NR) {
331 			ds_event();
332 		}
333 		return;
334 	}
335 
336 	switch (m->m_type) {
337 	case BUSC_I2C_RESERVE:
338 		/* reserve a device on the bus for exclusive access */
339 		r = do_reserve(m->m_source, m->m_li2cdriver_i2c_busc_i2c_reserve.addr);
340 		break;
341 	case BUSC_I2C_EXEC:
342 		/* handle request from another driver */
343 		r = do_i2c_ioctl_exec(m->m_source, m->m_li2cdriver_i2c_busc_i2c_exec.grant);
344 		break;
345 	default:
346 		log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
347 		r = EINVAL;
348 		break;
349 	}
350 
351 	log_trace(&log, "i2c_other() returning r=%d\n", r);
352 
353 	/* Send a reply. */
354 	memset(&m_reply, 0, sizeof(m_reply));
355 	m_reply.m_type = r;
356 
357 	if ((r = ipc_send(m->m_source, &m_reply)) != OK)
358 		log_warn(&log, "ipc_send() to %d failed: %d\n", m->m_source, r);
359 }
360 
361 /*
362  * The bus drivers are subscribed to DS events about device drivers on their
363  * bus. When the device drivers restart, DS sends a notification and this
364  * function updates the reservation table with the device driver's new
365  * endpoint.
366  */
367 static void
368 ds_event(void)
369 {
370 	char key[DS_MAX_KEYLEN];
371 	u32_t value;
372 	int type;
373 	endpoint_t owner_endpoint;
374 	int r;
375 
376 	/* check for pending events */
377 	while ((r = ds_check(key, &type, &owner_endpoint)) == OK) {
378 
379 		r = ds_retrieve_u32(key, &value);
380 		if (r != OK) {
381 			log_warn(&log, "ds_retrieve_u32() failed r=%d\n", r);
382 			return;
383 		}
384 
385 		log_debug(&log, "key='%s' owner_endpoint=0x%x\n", key,
386 		    owner_endpoint);
387 
388 		if (value == DS_DRIVER_UP) {
389 			/* clean up any old reservations the driver had */
390 			log_debug(&log, "DS_DRIVER_UP\n");
391 			update_reservation(owner_endpoint, key);
392 		}
393 	}
394 }
395 
396 static int
397 lu_state_restore(void)
398 {
399 	int r;
400 	char key[DS_MAX_KEYLEN];
401 	size_t size;
402 
403 	env_parse_instance();
404 
405 	size = sizeof(i2cdev);
406 
407 	memset(key, '\0', DS_MAX_KEYLEN);
408 	snprintf(key, DS_MAX_KEYLEN, "i2c.%d.i2cdev", i2c_bus_id + 1);
409 
410 	r = ds_retrieve_mem(key, (char *) i2cdev, &size);
411 	if (r != OK) {
412 		log_warn(&log, "ds_retrieve_mem(%s) failed (r=%d)\n", key, r);
413 		return r;
414 	}
415 
416 	log_debug(&log, "State Restored\n");
417 
418 	return OK;
419 }
420 
421 static int
422 sef_cb_init(int type, sef_init_info_t * UNUSED(info))
423 {
424 	int r;
425 	char regex[DS_MAX_KEYLEN];
426 	struct machine machine;
427 	sys_getmachine(&machine);
428 
429 	if (type != SEF_INIT_FRESH) {
430 		/* Restore a prior state. */
431 		lu_state_restore();
432 	}
433 
434 	if (BOARD_IS_BBXM(machine.board_id) || BOARD_IS_BB(machine.board_id)){
435 		/* Set callback and initialize the bus */
436 		r = omap_interface_setup(&process, i2c_bus_id);
437 		if (r != OK) {
438 			return r;
439 		}
440 	} else {
441 		return ENODEV;
442 	}
443 
444 	/* Announce we are up when necessary. */
445 	if (type != SEF_INIT_LU) {
446 
447 		/* only capture events for this particular bus */
448 		snprintf(regex, DS_MAX_KEYLEN, "drv\\.i2c\\.%d\\..*",
449 		    i2c_bus_id + 1);
450 
451 		/* Subscribe to driver events for i2c drivers */
452 		r = ds_subscribe(regex, DSF_INITIAL | DSF_OVERWRITE);
453 		if (r != OK) {
454 			log_warn(&log, "ds_subscribe() failed\n");
455 			return r;
456 		}
457 
458 		chardriver_announce();
459 	}
460 
461 	/* Save state */
462 	sef_cb_lu_state_save(0, 0);
463 
464 	/* Initialization completed successfully. */
465 	return OK;
466 }
467 
468 static void
469 sef_local_startup()
470 {
471 	/* Register init callbacks. */
472 	sef_setcb_init_fresh(sef_cb_init);
473 	sef_setcb_init_lu(sef_cb_init);
474 	sef_setcb_init_restart(sef_cb_init);
475 
476 	/* Register live update callbacks */
477 	sef_setcb_lu_state_save(sef_cb_lu_state_save);
478 
479 	/* Let SEF perform startup. */
480 	sef_startup();
481 }
482 
483 static int
484 env_parse_instance(void)
485 {
486 	int r;
487 	long instance;
488 
489 	/* Parse the instance number passed to service */
490 	instance = 0;
491 	r = env_parse("instance", "d", 0, &instance, 1, 3);
492 	if (r == -1) {
493 		log_warn(&log,
494 		    "Expecting '-arg instance=N' argument (N=1..3)\n");
495 		return EXIT_FAILURE;
496 	}
497 
498 	/* Device files count from 1, hardware starts counting from 0 */
499 	i2c_bus_id = instance - 1;
500 
501 	return OK;
502 }
503 
504 int
505 main(int argc, char *argv[])
506 {
507 	int r;
508 
509 	env_setargs(argc, argv);
510 
511 	r = env_parse_instance();
512 	if (r != OK) {
513 		return r;
514 	}
515 
516 	memset(i2cdev, '\0', sizeof(i2cdev));
517 	sef_local_startup();
518 	chardriver_task(&i2c_tab);
519 
520 	return OK;
521 }
522