2 * i2c - generic driver for Inter-Integrated Circuit bus (I2C).
6 #include <minix/chardriver.h>
7 #include <minix/drivers.h>
10 #include <minix/log.h>
11 #include <minix/type.h>
12 #include <minix/board.h>
22 /* SoC specific headers - 1 for each SoC */
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);
54 /* the bus that this instance of the driver is responsible for */
57 /* Table of i2c device reservations. */
62 char key
[DS_MAX_KEYLEN
];
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
= {
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.
93 do_reserve(endpoint_t endpt
, int slave_addr
)
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
);
102 log_warn(&log
, "Couldn't find label for endpt='0x%x'\n",
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
) {
112 "slave address must be positive & no more than 10 bits\n");
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
);
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
);
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
145 check_reservation(endpoint_t endpt
, int slave_addr
)
147 if (slave_addr
< 0 || slave_addr
>= NR_I2CDEV
) {
149 "slave address must be positive & no more than 10 bits\n");
153 if (endpt
== VFS_PROC_NR
&& i2cdev
[slave_addr
].inuse
== 0) {
155 "allowing ioctl() from VFS to access unclaimed device\n");
159 if (i2cdev
[slave_addr
].inuse
&& i2cdev
[slave_addr
].endpt
!= endpt
) {
160 log_debug(&log
, "device reserved by another endpoint\n");
162 } else if (i2cdev
[slave_addr
].inuse
== 0) {
164 "all drivers sending messages directly to this driver must reserve\n");
167 log_debug(&log
, "allowing access to registered device\n");
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.
178 update_reservation(endpoint_t endpt
, char *key
)
182 log_debug(&log
, "Updating reservation for '%s' endpt=0x%x\n", key
,
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.
201 validate_ioctl_exec(minix_i2c_ioctl_exec_t
* ioctl_exec
)
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");
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
);
223 len
= ioctl_exec
->iie_cmdlen
;
224 if (len
> I2C_EXEC_MAX_CMDLEN
) {
226 "iie_cmdlen out of range 0-I2C_EXEC_MAX_CMDLEN\n");
230 len
= ioctl_exec
->iie_buflen
;
231 if (len
> I2C_EXEC_MAX_BUFLEN
) {
233 "iie_buflen out of range 0-I2C_EXEC_MAX_BUFLEN\n");
241 * Performs the action in minix_i2c_ioctl_exec_t.
244 do_i2c_ioctl_exec(endpoint_t caller
, cp_grant_id_t grant_nr
)
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
));
253 log_warn(&log
, "sys_safecopyfrom() failed\n");
257 /* input validation */
258 r
= validate_ioctl_exec(&ioctl_exec
);
260 log_debug(&log
, "Message validation failed\n");
264 /* permission check */
265 r
= check_reservation(caller
, ioctl_exec
.iie_addr
);
267 log_debug(&log
, "check_reservation() denied the request\n");
271 /* Call the device specific code to execute the action */
272 r
= process(&ioctl_exec
);
274 log_debug(&log
, "process() failed\n");
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
));
282 log_warn(&log
, "sys_safecopyto() failed\n");
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
))
297 case MINIX_I2C_IOCTL_EXEC
:
298 r
= do_i2c_ioctl_exec(endpt
, grant
);
301 log_warn(&log
, "Invalid ioctl() 0x%x\n", request
);
310 i2c_other(message
* m
, int ipc_status
)
315 if (is_ipc_notify(ipc_status
)) {
316 /* handle notifications about drivers changing state */
317 if (m
->m_source
== DS_PROC_NR
) {
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
);
329 /* handle request from another driver */
330 r
= do_i2c_ioctl_exec(m
->m_source
, m
->BUSC_I2C_GRANT
);
333 log_warn(&log
, "Invalid message type (0x%x)\n", m
->m_type
);
338 log_trace(&log
, "i2c_other() returning r=%d\n", r
);
341 memset(&m_reply
, 0, sizeof(m_reply
));
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
357 char key
[DS_MAX_KEYLEN
];
360 endpoint_t owner_endpoint
;
363 /* check for pending events */
364 while ((r
= ds_check(key
, &type
, &owner_endpoint
)) == OK
) {
366 r
= ds_retrieve_u32(key
, &value
);
368 log_warn(&log
, "ds_retrieve_u32() failed r=%d\n", r
);
372 log_debug(&log
, "key='%s' owner_endpoint=0x%x\n", key
,
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
);
384 sef_cb_lu_state_save(int UNUSED(state
))
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
);
393 log_warn(&log
, "ds_publish_mem(%s) failed (r=%d)\n", key
, r
);
397 log_debug(&log
, "State Saved\n");
403 lu_state_restore(void)
406 char key
[DS_MAX_KEYLEN
];
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
);
418 log_warn(&log
, "ds_retrieve_mem(%s) failed (r=%d)\n", key
, r
);
422 log_debug(&log
, "State Restored\n");
428 sef_cb_init(int type
, sef_init_info_t
* UNUSED(info
))
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. */
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
);
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\\..*",
457 /* Subscribe to driver events for i2c drivers */
458 r
= ds_subscribe(regex
, DSF_INITIAL
| DSF_OVERWRITE
);
460 log_warn(&log
, "ds_subscribe() failed\n");
464 chardriver_announce();
468 sef_cb_lu_state_save(0);
470 /* Initialization completed successfully. */
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. */
495 env_parse_instance(void)
500 /* Parse the instance number passed to service */
502 r
= env_parse("instance", "d", 0, &instance
, 1, 3);
505 "Expecting '-arg instance=N' argument (N=1..3)\n");
509 /* Device files count from 1, hardware starts counting from 0 */
510 i2c_bus_id
= instance
- 1;
516 main(int argc
, char *argv
[])
520 env_setargs(argc
, argv
);
522 r
= env_parse_instance();
527 memset(i2cdev
, '\0', sizeof(i2cdev
));
529 chardriver_task(&i2c_tab
);