<sys/ioccom.h>, <sys/ioctl.h>
[minix3.git] / drivers / i2c / i2c.c
blob26e7b869f01ea49c2dbfe23b2205dd4987b89e6b
1 /*
2 * i2c - generic driver for Inter-Integrated Circuit bus (I2C).
3 */
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>
14 /* system headers */
15 #include <sys/mman.h>
17 /* usr headers */
18 #include <string.h>
19 #include <stdio.h>
20 #include <stdlib.h>
22 /* SoC specific headers - 1 for each SoC */
23 #include "omap_i2c.h"
25 /* local definitions */
27 /* i2c slave addresses can be up to 10 bits */
28 #define NR_I2CDEV (0x3ff)
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);
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);
39 static int env_parse_instance(void);
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);
46 /* SEF callbacks and driver state management */
47 static int sef_cb_lu_state_save(int);
48 static int lu_state_restore(void);
49 static int sef_cb_init(int type, sef_init_info_t * info);
50 static void sef_local_startup(void);
52 /* Globals */
54 /* the bus that this instance of the driver is responsible for */
55 uint32_t i2c_bus_id;
57 /* Table of i2c device reservations. */
58 static struct i2cdev
60 uint8_t inuse;
61 endpoint_t endpt;
62 char key[DS_MAX_KEYLEN];
63 } i2cdev[NR_I2CDEV];
65 /* Process a request for an i2c operation.
66 * This is the interface that all hardware specific code must implement.
68 int (*process) (minix_i2c_ioctl_exec_t * ioctl_exec);
70 /* logging - use with log_warn(), log_info(), log_debug(), log_trace() */
71 static struct log log = {
72 .name = "i2c",
73 .log_level = LEVEL_INFO,
74 .log_func = default_log
77 /* Entry points to the i2c driver from libchardriver.
78 * Only i2c_ioctl() and i2c_other() are implemented. The rest are no-op.
80 static struct chardriver i2c_tab = {
81 .cdr_ioctl = i2c_ioctl,
82 .cdr_other = i2c_other
86 * Claim an unclaimed device for exclusive use by endpt. This function can
87 * also be used to update the endpt if the endpt's label matches the label
88 * already associated with the slave address. This is useful if a driver
89 * shuts down unexpectedly and starts up with a new endpt and wants to reserve
90 * the same device it reserved before.
92 static int
93 do_reserve(endpoint_t endpt, int slave_addr)
95 int r;
96 char key[DS_MAX_KEYLEN];
97 char label[DS_MAX_KEYLEN];
99 /* find the label for the endpoint */
100 r = ds_retrieve_label_name(label, endpt);
101 if (r != OK) {
102 log_warn(&log, "Couldn't find label for endpt='0x%x'\n",
103 endpt);
104 return r;
107 /* construct the key i2cdriver_announce published (saves an IPC call) */
108 snprintf(key, DS_MAX_KEYLEN, "drv.i2c.%d.%s", i2c_bus_id + 1, label);
110 if (slave_addr < 0 || slave_addr >= NR_I2CDEV) {
111 log_debug(&log,
112 "slave address must be positive & no more than 10 bits\n");
113 return EINVAL;
116 /* check if device is in use by another driver */
117 if (i2cdev[slave_addr].inuse != 0
118 && strncmp(i2cdev[slave_addr].key, key, DS_MAX_KEYLEN) != 0) {
119 log_debug(&log, "address in use by '%s'/0x%x\n",
120 i2cdev[slave_addr].key, i2cdev[slave_addr].endpt);
121 return EBUSY;
124 /* device is free or already owned by us, claim it */
125 i2cdev[slave_addr].inuse = 1;
126 i2cdev[slave_addr].endpt = endpt;
127 memcpy(i2cdev[slave_addr].key, key, DS_MAX_KEYLEN);
129 sef_cb_lu_state_save(0); /* save reservations */
131 log_debug(&log, "Device 0x%x claimed by 0x%x key='%s'\n",
132 slave_addr, endpt, key);
134 return OK;
138 * All drivers must reserve their device(s) before doing operations on them
139 * (read/write, etc). ioctl()'s from VFS (i.e. user programs) can only use
140 * devices that haven't been reserved. A driver isn't allowed to access a
141 * device that another driver has reserved (not even other instances of the
142 * same driver).
144 static int
145 check_reservation(endpoint_t endpt, int slave_addr)
147 if (slave_addr < 0 || slave_addr >= NR_I2CDEV) {
148 log_debug(&log,
149 "slave address must be positive & no more than 10 bits\n");
150 return EINVAL;
153 if (endpt == VFS_PROC_NR && i2cdev[slave_addr].inuse == 0) {
154 log_debug(&log,
155 "allowing ioctl() from VFS to access unclaimed device\n");
156 return OK;
159 if (i2cdev[slave_addr].inuse && i2cdev[slave_addr].endpt != endpt) {
160 log_debug(&log, "device reserved by another endpoint\n");
161 return EBUSY;
162 } else if (i2cdev[slave_addr].inuse == 0) {
163 log_debug(&log,
164 "all drivers sending messages directly to this driver must reserve\n");
165 return EPERM;
166 } else {
167 log_debug(&log, "allowing access to registered device\n");
168 return OK;
173 * i2c listens to updates from ds about i2c device drivers starting up.
174 * When a driver comes back up with the same label, the endpt associated
175 * with the reservation needs to be updated. This function does the updating.
177 static void
178 update_reservation(endpoint_t endpt, char *key)
180 int i;
182 log_debug(&log, "Updating reservation for '%s' endpt=0x%x\n", key,
183 endpt);
185 for (i = 0; i < NR_I2CDEV; i++) {
187 /* find devices in use that the driver owns */
188 if (i2cdev[i].inuse != 0
189 && strncmp(i2cdev[i].key, key, DS_MAX_KEYLEN) == 0) {
190 /* update reservation with new endpoint */
191 do_reserve(endpt, i);
192 log_debug(&log, "Found device to update 0x%x\n", i);
198 * Checks a minix_i2c_ioctl_exec_t to see if the fields make sense.
200 static int
201 validate_ioctl_exec(minix_i2c_ioctl_exec_t * ioctl_exec)
203 i2c_op_t op;
204 i2c_addr_t addr;
205 size_t len;
207 op = ioctl_exec->iie_op;
208 if (op != I2C_OP_READ &&
209 op != I2C_OP_READ_WITH_STOP &&
210 op != I2C_OP_WRITE &&
211 op != I2C_OP_WRITE_WITH_STOP &&
212 op != I2C_OP_READ_BLOCK && op != I2C_OP_WRITE_BLOCK) {
213 log_warn(&log, "iie_op value not valid\n");
214 return EINVAL;
217 addr = ioctl_exec->iie_addr;
218 if (addr < 0 || addr >= NR_I2CDEV) {
219 log_warn(&log, "iie_addr out of range 0x0-0x%x\n", NR_I2CDEV);
220 return EINVAL;
223 len = ioctl_exec->iie_cmdlen;
224 if (len > I2C_EXEC_MAX_CMDLEN) {
225 log_warn(&log,
226 "iie_cmdlen out of range 0-I2C_EXEC_MAX_CMDLEN\n");
227 return EINVAL;
230 len = ioctl_exec->iie_buflen;
231 if (len > I2C_EXEC_MAX_BUFLEN) {
232 log_warn(&log,
233 "iie_buflen out of range 0-I2C_EXEC_MAX_BUFLEN\n");
234 return EINVAL;
237 return OK;
241 * Performs the action in minix_i2c_ioctl_exec_t.
243 static int
244 do_i2c_ioctl_exec(endpoint_t caller, cp_grant_id_t grant_nr)
246 int r;
247 minix_i2c_ioctl_exec_t ioctl_exec;
249 /* Copy the requested exection into the driver */
250 r = sys_safecopyfrom(caller, grant_nr, (vir_bytes) 0,
251 (vir_bytes) & ioctl_exec, sizeof(ioctl_exec));
252 if (r != OK) {
253 log_warn(&log, "sys_safecopyfrom() failed\n");
254 return r;
257 /* input validation */
258 r = validate_ioctl_exec(&ioctl_exec);
259 if (r != OK) {
260 log_debug(&log, "Message validation failed\n");
261 return r;
264 /* permission check */
265 r = check_reservation(caller, ioctl_exec.iie_addr);
266 if (r != OK) {
267 log_debug(&log, "check_reservation() denied the request\n");
268 return r;
271 /* Call the device specific code to execute the action */
272 r = process(&ioctl_exec);
273 if (r != OK) {
274 log_debug(&log, "process() failed\n");
275 return r;
278 /* Copy the results of the execution back to the calling process */
279 r = sys_safecopyto(caller, grant_nr, (vir_bytes) 0,
280 (vir_bytes) & ioctl_exec, sizeof(ioctl_exec));
281 if (r != OK) {
282 log_warn(&log, "sys_safecopyto() failed\n");
283 return r;
286 return OK;
289 static int
290 i2c_ioctl(devminor_t UNUSED(minor), unsigned long request, endpoint_t endpt,
291 cp_grant_id_t grant, int UNUSED(flags), endpoint_t UNUSED(user_endpt),
292 cdev_id_t UNUSED(id))
294 int r;
296 switch (request) {
297 case MINIX_I2C_IOCTL_EXEC:
298 r = do_i2c_ioctl_exec(endpt, grant);
299 break;
300 default:
301 log_warn(&log, "Invalid ioctl() 0x%x\n", request);
302 r = ENOTTY;
303 break;
306 return r;
309 static void
310 i2c_other(message * m, int ipc_status)
312 message m_reply;
313 int r;
315 if (is_ipc_notify(ipc_status)) {
316 /* handle notifications about drivers changing state */
317 if (m->m_source == DS_PROC_NR) {
318 ds_event();
320 return;
323 switch (m->m_type) {
324 case BUSC_I2C_RESERVE:
325 /* reserve a device on the bus for exclusive access */
326 r = do_reserve(m->m_source, m->BUSC_I2C_ADDR);
327 break;
328 case BUSC_I2C_EXEC:
329 /* handle request from another driver */
330 r = do_i2c_ioctl_exec(m->m_source, m->BUSC_I2C_GRANT);
331 break;
332 default:
333 log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
334 r = EINVAL;
335 break;
338 log_trace(&log, "i2c_other() returning r=%d\n", r);
340 /* Send a reply. */
341 memset(&m_reply, 0, sizeof(m_reply));
342 m_reply.m_type = r;
344 if ((r = ipc_send(m->m_source, &m_reply)) != OK)
345 log_warn(&log, "ipc_send() to %d failed: %d\n", m->m_source, r);
349 * The bus drivers are subscribed to DS events about device drivers on their
350 * bus. When the device drivers restart, DS sends a notification and this
351 * function updates the reservation table with the device driver's new
352 * endpoint.
354 static void
355 ds_event(void)
357 char key[DS_MAX_KEYLEN];
358 u32_t value;
359 int type;
360 endpoint_t owner_endpoint;
361 int r;
363 /* check for pending events */
364 while ((r = ds_check(key, &type, &owner_endpoint)) == OK) {
366 r = ds_retrieve_u32(key, &value);
367 if (r != OK) {
368 log_warn(&log, "ds_retrieve_u32() failed r=%d\n", r);
369 return;
372 log_debug(&log, "key='%s' owner_endpoint=0x%x\n", key,
373 owner_endpoint);
375 if (value == DS_DRIVER_UP) {
376 /* clean up any old reservations the driver had */
377 log_debug(&log, "DS_DRIVER_UP\n");
378 update_reservation(owner_endpoint, key);
383 static int
384 sef_cb_lu_state_save(int UNUSED(state))
386 int r;
387 char key[DS_MAX_KEYLEN];
389 memset(key, '\0', DS_MAX_KEYLEN);
390 snprintf(key, DS_MAX_KEYLEN, "i2c.%d.i2cdev", i2c_bus_id + 1);
391 r = ds_publish_mem(key, i2cdev, sizeof(i2cdev), DSF_OVERWRITE);
392 if (r != OK) {
393 log_warn(&log, "ds_publish_mem(%s) failed (r=%d)\n", key, r);
394 return r;
397 log_debug(&log, "State Saved\n");
399 return OK;
402 static int
403 lu_state_restore(void)
405 int r;
406 char key[DS_MAX_KEYLEN];
407 size_t size;
409 env_parse_instance();
411 size = sizeof(i2cdev);
413 memset(key, '\0', DS_MAX_KEYLEN);
414 snprintf(key, DS_MAX_KEYLEN, "i2c.%d.i2cdev", i2c_bus_id + 1);
416 r = ds_retrieve_mem(key, (char *) i2cdev, &size);
417 if (r != OK) {
418 log_warn(&log, "ds_retrieve_mem(%s) failed (r=%d)\n", key, r);
419 return r;
422 log_debug(&log, "State Restored\n");
424 return OK;
427 static int
428 sef_cb_init(int type, sef_init_info_t * UNUSED(info))
430 int r;
431 char regex[DS_MAX_KEYLEN];
432 struct machine machine;
433 sys_getmachine(&machine);
435 if (type != SEF_INIT_FRESH) {
436 /* Restore a prior state. */
437 lu_state_restore();
440 if (BOARD_IS_BBXM(machine.board_id) || BOARD_IS_BB(machine.board_id)){
441 /* Set callback and initialize the bus */
442 r = omap_interface_setup(&process, i2c_bus_id);
443 if (r != OK) {
444 return r;
446 } else {
447 return ENODEV;
450 /* Announce we are up when necessary. */
451 if (type != SEF_INIT_LU) {
453 /* only capture events for this particular bus */
454 snprintf(regex, DS_MAX_KEYLEN, "drv\\.i2c\\.%d\\..*",
455 i2c_bus_id + 1);
457 /* Subscribe to driver events for i2c drivers */
458 r = ds_subscribe(regex, DSF_INITIAL | DSF_OVERWRITE);
459 if (r != OK) {
460 log_warn(&log, "ds_subscribe() failed\n");
461 return r;
464 chardriver_announce();
467 /* Save state */
468 sef_cb_lu_state_save(0);
470 /* Initialization completed successfully. */
471 return OK;
474 static void
475 sef_local_startup()
477 /* Register init callbacks. */
478 sef_setcb_init_fresh(sef_cb_init);
479 sef_setcb_init_lu(sef_cb_init);
480 sef_setcb_init_restart(sef_cb_init);
482 /* Register live update callbacks */
483 /* Agree to update immediately when LU is requested in a valid state */
484 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
485 /* - Support live update starting from any standard state */
486 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
487 /* - Register a custom routine to save the state. */
488 sef_setcb_lu_state_save(sef_cb_lu_state_save);
490 /* Let SEF perform startup. */
491 sef_startup();
494 static int
495 env_parse_instance(void)
497 int r;
498 long instance;
500 /* Parse the instance number passed to service */
501 instance = 0;
502 r = env_parse("instance", "d", 0, &instance, 1, 3);
503 if (r == -1) {
504 log_warn(&log,
505 "Expecting '-arg instance=N' argument (N=1..3)\n");
506 return EXIT_FAILURE;
509 /* Device files count from 1, hardware starts counting from 0 */
510 i2c_bus_id = instance - 1;
512 return OK;
516 main(int argc, char *argv[])
518 int r;
520 env_setargs(argc, argv);
522 r = env_parse_instance();
523 if (r != OK) {
524 return r;
527 memset(i2cdev, '\0', sizeof(i2cdev));
528 sef_local_startup();
529 chardriver_task(&i2c_tab);
531 return OK;