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
sef_cb_lu_state_save(int UNUSED (result),int UNUSED (flags))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
do_reserve(endpoint_t endpt,int slave_addr)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
check_reservation(endpoint_t endpt,int slave_addr)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
update_reservation(endpoint_t endpt,char * key)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
validate_ioctl_exec(minix_i2c_ioctl_exec_t * ioctl_exec)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
do_i2c_ioctl_exec(endpoint_t caller,cp_grant_id_t grant_nr)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
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))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
i2c_other(message * m,int ipc_status)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
ds_event(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
lu_state_restore(void)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
sef_cb_init(int type,sef_init_info_t * UNUSED (info))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
sef_local_startup()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
env_parse_instance(void)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
main(int argc,char * argv[])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