diff --git a/distrib/sets/lists/minix/md.evbarm b/distrib/sets/lists/minix/md.evbarm index 7a63dee46..841f92b7a 100644 --- a/distrib/sets/lists/minix/md.evbarm +++ b/distrib/sets/lists/minix/md.evbarm @@ -97,6 +97,8 @@ ./usr/lib/libclkconf_pic.a minix-sys ./usr/lib/libgpio.a minix-sys ./usr/lib/libgpio_pic.a minix-sys +./usr/lib/libi2cdriver.a minix-sys +./usr/lib/libi2cdriver_pic.a minix-sys ./usr/lib/libpadconf.a minix-sys ./usr/lib/libpadconf_pic.a minix-sys ./usr/mdec minix-sys diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index fd849bda4..ecbe78608 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -1073,6 +1073,7 @@ ./usr/include/minix/hash.h minix-sys ./usr/include/minix/hgfs.h minix-sys ./usr/include/minix/i2c.h minix-sys +./usr/include/minix/i2cdriver.h minix-sys ./usr/include/minix/input.h minix-sys ./usr/include/minix/ioctl.h minix-sys ./usr/include/minix/ipcconst.h minix-sys diff --git a/include/minix/Makefile b/include/minix/Makefile index e1b65cc4f..c2de2a71c 100644 --- a/include/minix/Makefile +++ b/include/minix/Makefile @@ -11,7 +11,7 @@ INCS+= acpi.h audio_fw.h bitmap.h \ debug.h devio.h devman.h dmap.h \ driver.h drivers.h drvlib.h ds.h \ endpoint.h fb.h fslib.h gpio.h gcov.h hash.h \ - hgfs.h i2c.h ioctl.h input.h ipc.h ipcconst.h \ + hgfs.h i2c.h i2cdriver.h ioctl.h input.h ipc.h ipcconst.h \ keymap.h log.h mmio.h mount.h mthread.h minlib.h \ netdriver.h optset.h padconf.h partition.h portio.h \ priv.h procfs.h profile.h queryparam.h \ diff --git a/include/minix/i2cdriver.h b/include/minix/i2cdriver.h new file mode 100644 index 000000000..3dd902cd7 --- /dev/null +++ b/include/minix/i2cdriver.h @@ -0,0 +1,21 @@ +/* Prototypes and definitions for i2c drivers. */ + +#ifndef _MINIX_I2CDRIVER_H +#define _MINIX_I2CDRIVER_H + +#include +#include +#include + +/* Functions defined by i2cdriver.c: */ +int i2cdriver_env_parse(uint32_t * bus, i2c_addr_t * address, + i2c_addr_t * valid_addrs); +void i2cdriver_announce(uint32_t bus); +endpoint_t i2cdriver_bus_endpoint(uint32_t bus); +int i2cdriver_subscribe_bus_updates(uint32_t bus); +void i2cdriver_handle_bus_update(endpoint_t * bus_endpoint, uint32_t bus, + i2c_addr_t address); +int i2cdriver_reserve_device(endpoint_t bus_endpoint, i2c_addr_t address); +int i2cdriver_exec(endpoint_t bus_endpoint, minix_i2c_ioctl_exec_t *ioctl_exec); + +#endif /* _MINIX_I2CDRIVER_H */ diff --git a/lib/Makefile b/lib/Makefile index eeccc41c6..ad747ec15 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -54,7 +54,7 @@ SUBDIR += libvassert libhgfs libvboxfs libvirtio .endif .if (${MACHINE_ARCH} == "earm") -SUBDIR += libclkconf libgpio libpadconf +SUBDIR += libclkconf libgpio libi2cdriver libpadconf .endif .if (${MKRUMP} != "no") diff --git a/lib/libi2cdriver/Makefile b/lib/libi2cdriver/Makefile new file mode 100644 index 000000000..c4abd7237 --- /dev/null +++ b/lib/libi2cdriver/Makefile @@ -0,0 +1,7 @@ +# Makefile for libi2cdriver + +LIB= i2cdriver + +SRCS= i2cdriver.c + +.include diff --git a/lib/libi2cdriver/i2cdriver.c b/lib/libi2cdriver/i2cdriver.c new file mode 100644 index 000000000..755bd8a1f --- /dev/null +++ b/lib/libi2cdriver/i2cdriver.c @@ -0,0 +1,195 @@ +/* This file contains device independent i2c device driver helpers. */ + +#include +#include +#include +#include +#include +#include + +void +i2cdriver_announce(uint32_t bus) +{ + /* Announce we are up after a fresh start or restart. */ + int r; + char key[DS_MAX_KEYLEN]; + char label[DS_MAX_KEYLEN]; + char *driver_prefix = "drv.i2c."; + + /* Callers are allowed to use sendrec to communicate with drivers. + * For this reason, there may blocked callers when a driver restarts. + * Ask the kernel to unblock them (if any). + */ +#if USE_STATECTL + if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS)) != OK) { + panic("chardriver_init: sys_statectl failed: %d", r); + } +#endif + + /* Publish a driver up event. */ + r = ds_retrieve_label_name(label, getprocnr()); + if (r != OK) { + panic("unable to get own label: %d\n", r); + } + /* example key: drv.i2c.1.cat24c245.0x50 */ + snprintf(key, DS_MAX_KEYLEN, "%s%d.%s", driver_prefix, bus, label); + r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE); + if (r != OK) { + panic("unable to publish driver up event: %d\n", r); + } +} + +int +i2cdriver_env_parse(uint32_t * bus, i2c_addr_t * address, + i2c_addr_t * valid_addrs) +{ + /* fill in bus and address with the values passed on the command line */ + int r; + int found; + long int busl; + long int addressl; + + r = env_parse("bus", "d", 0, &busl, 1, 3); + if (r != EP_SET) { + return -1; + } + *bus = (uint32_t) busl; + + r = env_parse("address", "x", 0, &addressl, 0x0000, 0x03ff); + if (r != EP_SET) { + return -1; + } + *address = addressl; + + found = 0; + while (*valid_addrs != 0x0000) { + + if (*address == *valid_addrs) { + found = 1; + break; + } + + valid_addrs++; + } + + if (!found) { + return 1; + } + + return 0; +} + +endpoint_t +i2cdriver_bus_endpoint(uint32_t bus) +{ + /* locate the driver for the i2c bus itself */ + int r; + char *label_prefix = "i2c."; + char label[DS_MAX_KEYLEN]; + endpoint_t bus_endpoint; + + snprintf(label, DS_MAX_KEYLEN, "%s%d", label_prefix, bus); + + r = ds_retrieve_label_endpt(label, &bus_endpoint); + if (r != OK) { + return 0; + } + + return bus_endpoint; +} + +int +i2cdriver_subscribe_bus_updates(uint32_t bus) +{ + int r; + char regex[DS_MAX_KEYLEN]; + + /* only capture events for the specified bus */ + snprintf(regex, DS_MAX_KEYLEN, "drv\\.chr\\.i2c\\.%d", bus); + + /* Subscribe to driver events from the i2c bus */ + r = ds_subscribe(regex, DSF_INITIAL | DSF_OVERWRITE); + if (r != OK) { + return r; + } + + return OK; +} + +void +i2cdriver_handle_bus_update(endpoint_t * bus_endpoint, uint32_t bus, + i2c_addr_t address) +{ + char key[DS_MAX_KEYLEN]; + u32_t value; + int type; + endpoint_t owner_endpoint, old_endpoint; + int r; + + /* check for pending events */ + while ((r = ds_check(key, &type, &owner_endpoint)) == OK) { + + r = ds_retrieve_u32(key, &value); + if (r != OK) { + return; + } + + if (value == DS_DRIVER_UP) { + old_endpoint = *bus_endpoint; + + /* look up the bus's (potentially new) endpoint */ + *bus_endpoint = i2cdriver_bus_endpoint(bus); + + /* was updated endpoint? */ + if (old_endpoint != *bus_endpoint) { + /* re-reserve device to allow the driver to + * continue working, even through a manual + * down/up. + */ + i2cdriver_reserve_device(*bus_endpoint, + address); + } + } + } +} + +int +i2cdriver_reserve_device(endpoint_t bus_endpoint, i2c_addr_t address) +{ + int r; + message m; + + m.m_type = BUSC_I2C_RESERVE; + m.DEVICE = address; + + r = sendrec(bus_endpoint, &m); + if (r != OK) { + return EIO; + } + + return m.REP_STATUS; /* return reply code OK, EBUSY, EINVAL, etc. */ +} + +int +i2cdriver_exec(endpoint_t bus_endpoint, minix_i2c_ioctl_exec_t * ioctl_exec) +{ + int r; + message m; + cp_grant_id_t grant_nr; + + grant_nr = cpf_grant_direct(bus_endpoint, (vir_bytes) ioctl_exec, + sizeof(minix_i2c_ioctl_exec_t), CPF_READ | CPF_WRITE); + + memset(&m, '\0', sizeof(message)); + + m.m_type = BUSC_I2C_EXEC; + m.IO_GRANT = (char *) grant_nr; + + r = sendrec(bus_endpoint, &m); + cpf_revoke(grant_nr); + if (r != OK) { + return EIO; + } + + return m.REP_STATUS; +}