From 550fdfb44349ffa54822d9aec87820baf4136763 Mon Sep 17 00:00:00 2001 From: Thomas Cort Date: Mon, 15 Jul 2013 10:29:09 -0400 Subject: [PATCH] i2c: initial bus drivers for am335x and dm37xx Change-Id: I478704fbf30dbf6d3382bcbfb11e75b512c032a1 --- commands/MAKEDEV/MAKEDEV.sh | 11 +- distrib/sets/lists/minix/md.evbarm | 1 + distrib/sets/lists/minix/mi | 1 + drivers/Makefile | 2 +- drivers/i2c/Makefile | 20 + drivers/i2c/README.txt | 64 ++ drivers/i2c/arch/earm/Makefile.inc | 7 + drivers/i2c/arch/earm/omap_i2c.c | 855 +++++++++++++++++++++ drivers/i2c/arch/earm/omap_i2c.h | 10 + drivers/i2c/arch/earm/omap_i2c_registers.h | 152 ++++ drivers/i2c/i2c.c | 555 +++++++++++++ etc/system.conf | 19 + etc/usr/rc | 13 + include/minix/Makefile | 2 +- include/minix/clkconf.h | 11 + include/minix/com.h | 2 + include/minix/dmap.h | 3 + include/minix/i2c.h | 26 + include/minix/padconf.h | 139 +++- lib/libc/sys-minix/ioctl.c | 75 +- lib/libclkconf/clkconf.c | 5 + lib/libpadconf/padconf.c | 2 +- 22 files changed, 1968 insertions(+), 7 deletions(-) create mode 100644 drivers/i2c/Makefile create mode 100644 drivers/i2c/README.txt create mode 100644 drivers/i2c/arch/earm/Makefile.inc create mode 100644 drivers/i2c/arch/earm/omap_i2c.c create mode 100644 drivers/i2c/arch/earm/omap_i2c.h create mode 100644 drivers/i2c/arch/earm/omap_i2c_registers.h create mode 100644 drivers/i2c/i2c.c create mode 100644 include/minix/i2c.h diff --git a/commands/MAKEDEV/MAKEDEV.sh b/commands/MAKEDEV/MAKEDEV.sh index d41ba53f2..9ca26f8c2 100644 --- a/commands/MAKEDEV/MAKEDEV.sh +++ b/commands/MAKEDEV/MAKEDEV.sh @@ -23,7 +23,8 @@ case $#:$1 in ttypa ttypb ttypc ttypd ttype ttypf \ ttyq0 ttyq1 ttyq2 ttyq3 ttyq4 ttyq5 ttyq6 ttyq7 ttyq8 ttyq9 \ ttyqa ttyqb ttyqc ttyqd ttyqe ttyqf \ - eth klog random uds filter fbd hello fb0 + eth klog random uds filter fbd hello fb0 \ + i2c-1 i2c-2 i2c-3 ;; 0:|1:-\?) cat >&2 <&2 ex=1 diff --git a/distrib/sets/lists/minix/md.evbarm b/distrib/sets/lists/minix/md.evbarm index 6cb871d91..7a63dee46 100644 --- a/distrib/sets/lists/minix/md.evbarm +++ b/distrib/sets/lists/minix/md.evbarm @@ -102,6 +102,7 @@ ./usr/mdec minix-sys ./usr/sbin/fb minix-sys ./usr/sbin/gpio minix-sys +./usr/sbin/i2c minix-sys ./usr/sbin/random minix-sys ./usr/tests/minix-posix/mod minix-sys ./usr/tests/minix-posix/test63 minix-sys diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index bd30d1d18..e4a0ce613 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -1072,6 +1072,7 @@ ./usr/include/minix/gpio.h minix-sys ./usr/include/minix/hash.h minix-sys ./usr/include/minix/hgfs.h minix-sys +./usr/include/minix/i2c.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/drivers/Makefile b/drivers/Makefile index a5306585f..d0e2405a2 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -23,7 +23,7 @@ SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \ .endif .if ${MACHINE_ARCH} == "earm" -SUBDIR= fb gpio mmc log tty random +SUBDIR= fb gpio i2c mmc log tty random .endif .endif # ${MKIMAGEONLY} != "yes" diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile new file mode 100644 index 000000000..03df14375 --- /dev/null +++ b/drivers/i2c/Makefile @@ -0,0 +1,20 @@ +# Makefile for I2C support + +PROG= i2c + +.include "arch/${MACHINE_ARCH}/Makefile.inc" + +SRCS+= i2c.c + +DPADD+= ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS} ${CLKCONF} ${PADCONF} +LDADD+= -lchardriver -lsys -ltimers -lclkconf -lpadconf + +MAN= + +BINDIR?= /usr/sbin + +CPPFLAGS+= -I${.CURDIR} -I${.CURDIR}/arch/${MACHINE_ARCH} + +.include +.include + diff --git a/drivers/i2c/README.txt b/drivers/i2c/README.txt new file mode 100644 index 000000000..e9a69de30 --- /dev/null +++ b/drivers/i2c/README.txt @@ -0,0 +1,64 @@ +Minix i2c Driver +================ + +TODO: this probably belongs on the wiki + +Overview +-------- + +This is the driver for the i2c bus. It provides the same /dev interface as +NetBSD and OpenBSD (see dev/i2c/i2c_io.h). It also provides an interface for +other drivers to access the I2C bus using Minix IPC. + +Organization and Layout +----------------------- + +i2c.c generic i2c bus driver +arch/ arch specific code + earm/ earm specific code + omap_i2c.c AM335X/DM37XX i2c bus driver + omap_i2c.h AM335X/DM37XX function prototypes + omap_i2c_registers.h AM335X/DM37XX register offsets, etc. + +Testing the Code +---------------- + +Below are the steps needed to start up the i2c driver instances. Though, +now they get started at boot in /usr/etc/rc, it's still useful to know if +you take down the service and need to start it again. + +Creating the device files (this is already done automatically, but if not): + +cd /dev && MAKEDEV i2c-1 && MAKEDEV i2c-2 && MAKEDEV i2c-3 + +Starting up the instances: + +/bin/service up /usr/sbin/i2c -dev /dev/i2c-1 -label i2c.1 -args instance=1 +/bin/service up /usr/sbin/i2c -dev /dev/i2c-2 -label i2c.2 -args instance=2 +/bin/service up /usr/sbin/i2c -dev /dev/i2c-3 -label i2c.3 -args instance=3 + +There is an i2cscan program from NetBSD which can detect devices on the bus: + +i2cscan -r /dev/i2c-1 +i2cscan -r /dev/i2c-2 +i2cscan -r /dev/i2c-3 + +Limitations +----------- + +The i2c controllers used in the am335x and the dm37xx do not support zero +byte transfers. Writing 0 bytes is a common method used to probe the bus +for devices. Most of the address ranges i2cscan scans are done by this +method. Therefore, only a subset of devices on the bus will be detected by +i2cscan (i.e. the devices it detects using the 1 byte read method). See +the register description for I2C_CNT in the technical reference manuals +for details about why 0 byte transfers are not allowed. + +Developing I2C Device Drivers +----------------------------- + +The driver for the EEPROM (a.k.a. drivers/cat24c256) is the hello world of +Minix i2c device drivers. It shows how to use the i2cdriver library and +how to use the bus for reads and writes. commands/eepromread is another +place to look if you're interested in accessing devices through the /dev +interface. diff --git a/drivers/i2c/arch/earm/Makefile.inc b/drivers/i2c/arch/earm/Makefile.inc new file mode 100644 index 000000000..497fd24c3 --- /dev/null +++ b/drivers/i2c/arch/earm/Makefile.inc @@ -0,0 +1,7 @@ +# Makefile for arch-dependent i2c code +.include + +HERE=${.CURDIR}/arch/${MACHINE_ARCH} +.PATH: ${HERE} + +SRCS += omap_i2c.c omap_i2c.h omap_i2c_registers.h diff --git a/drivers/i2c/arch/earm/omap_i2c.c b/drivers/i2c/arch/earm/omap_i2c.c new file mode 100644 index 000000000..664a841bb --- /dev/null +++ b/drivers/i2c/arch/earm/omap_i2c.c @@ -0,0 +1,855 @@ +/* + * This file implements support for i2c on the BeagleBone and BeagleBoard-xM + */ + +/* kernel headers */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* device headers */ +#include + +/* system headers */ +#include +#include +#include + +/* usr headers */ +#include +#include +#include + +/* local headers */ +#include "omap_i2c.h" + +/* defines the set of register */ + +typedef struct omap_i2c_registers +{ + vir_bytes I2C_REVNB_LO; /* AM335X Only */ + vir_bytes I2C_REVNB_HI; /* AM335X Only */ + vir_bytes I2C_REV; /* DM37XX Only */ + vir_bytes I2C_IE; /* DM37XX Only */ + vir_bytes I2C_STAT; /* DM37XX Only */ + vir_bytes I2C_SYSC; + vir_bytes I2C_IRQSTATUS_RAW; /* AM335X Only */ + vir_bytes I2C_IRQSTATUS; /* AM335X Only */ + vir_bytes I2C_IRQENABLE_SET; /* AM335X Only */ + vir_bytes I2C_IRQENABLE_CLR; /* AM335X Only */ + vir_bytes I2C_WE; + vir_bytes I2C_DMARXENABLE_SET; /* AM335X Only */ + vir_bytes I2C_DMATXENABLE_SET; /* AM335X Only */ + vir_bytes I2C_DMARXENABLE_CLR; /* AM335X Only */ + vir_bytes I2C_DMATXENABLE_CLR; /* AM335X Only */ + vir_bytes I2C_DMARXWAKE_EN; /* AM335X Only */ + vir_bytes I2C_DMATXWAKE_EN; /* AM335X Only */ + vir_bytes I2C_SYSS; + vir_bytes I2C_BUF; + vir_bytes I2C_CNT; + vir_bytes I2C_DATA; + vir_bytes I2C_CON; + vir_bytes I2C_OA; /* AM335X Only */ + vir_bytes I2C_OA0; /* DM37XX Only */ + vir_bytes I2C_SA; + vir_bytes I2C_PSC; + vir_bytes I2C_SCLL; + vir_bytes I2C_SCLH; + vir_bytes I2C_SYSTEST; + vir_bytes I2C_BUFSTAT; + vir_bytes I2C_OA1; + vir_bytes I2C_OA2; + vir_bytes I2C_OA3; + vir_bytes I2C_ACTOA; + vir_bytes I2C_SBLOCK; +} omap_i2c_regs_t; + +/* generic definition an i2c bus */ + +typedef struct omap_i2c_bus +{ + enum bus_types + { am335x, dm37xx } bus_type; + phys_bytes mr_base; + phys_bytes mr_size; + vir_bytes mapped_addr; + omap_i2c_regs_t *regs; + uint32_t functional_clock; + uint32_t module_clock; + uint32_t bus_speed; + uint16_t major; + uint16_t minor; + int irq; + int irq_hook_id; + int irq_hook_kernel_id; +} omap_i2c_bus_t; + +/* Define the registers for each chip */ + +static omap_i2c_regs_t am335x_i2c_regs = { + .I2C_REVNB_LO = AM335X_I2C_REVNB_LO, + .I2C_REVNB_HI = AM335X_I2C_REVNB_HI, + .I2C_SYSC = AM335X_I2C_SYSC, + .I2C_IRQSTATUS_RAW = AM335X_I2C_IRQSTATUS_RAW, + .I2C_IRQSTATUS = AM335X_I2C_IRQSTATUS, + .I2C_IRQENABLE_SET = AM335X_I2C_IRQENABLE_SET, + .I2C_IRQENABLE_CLR = AM335X_I2C_IRQENABLE_CLR, + .I2C_WE = AM335X_I2C_WE, + .I2C_DMARXENABLE_SET = AM335X_I2C_DMARXENABLE_SET, + .I2C_DMATXENABLE_SET = AM335X_I2C_DMATXENABLE_SET, + .I2C_DMARXENABLE_CLR = AM335X_I2C_DMARXENABLE_CLR, + .I2C_DMATXENABLE_CLR = AM335X_I2C_DMATXENABLE_CLR, + .I2C_DMARXWAKE_EN = AM335X_I2C_DMARXWAKE_EN, + .I2C_DMATXWAKE_EN = AM335X_I2C_DMATXWAKE_EN, + .I2C_SYSS = AM335X_I2C_SYSS, + .I2C_BUF = AM335X_I2C_BUF, + .I2C_CNT = AM335X_I2C_CNT, + .I2C_DATA = AM335X_I2C_DATA, + .I2C_CON = AM335X_I2C_CON, + .I2C_OA = AM335X_I2C_OA, + .I2C_SA = AM335X_I2C_SA, + .I2C_PSC = AM335X_I2C_PSC, + .I2C_SCLL = AM335X_I2C_SCLL, + .I2C_SCLH = AM335X_I2C_SCLH, + .I2C_SYSTEST = AM335X_I2C_SYSTEST, + .I2C_BUFSTAT = AM335X_I2C_BUFSTAT, + .I2C_OA1 = AM335X_I2C_OA1, + .I2C_OA2 = AM335X_I2C_OA2, + .I2C_OA3 = AM335X_I2C_OA3, + .I2C_ACTOA = AM335X_I2C_ACTOA, + .I2C_SBLOCK = AM335X_I2C_SBLOCK +}; + +static omap_i2c_regs_t dm37xx_i2c_regs = { + .I2C_REV = DM37XX_I2C_REV, + .I2C_IE = DM37XX_I2C_IE, + .I2C_STAT = DM37XX_I2C_STAT, + .I2C_WE = DM37XX_I2C_WE, + .I2C_SYSS = DM37XX_I2C_SYSS, + .I2C_BUF = DM37XX_I2C_BUF, + .I2C_CNT = DM37XX_I2C_CNT, + .I2C_DATA = DM37XX_I2C_DATA, + .I2C_SYSC = DM37XX_I2C_SYSC, + .I2C_CON = DM37XX_I2C_CON, + .I2C_OA0 = DM37XX_I2C_OA0, + .I2C_SA = DM37XX_I2C_SA, + .I2C_PSC = DM37XX_I2C_PSC, + .I2C_SCLL = DM37XX_I2C_SCLL, + .I2C_SCLH = DM37XX_I2C_SCLH, + .I2C_SYSTEST = DM37XX_I2C_SYSTEST, + .I2C_BUFSTAT = DM37XX_I2C_BUFSTAT, + .I2C_OA1 = DM37XX_I2C_OA1, + .I2C_OA2 = DM37XX_I2C_OA2, + .I2C_OA3 = DM37XX_I2C_OA3, + .I2C_ACTOA = DM37XX_I2C_ACTOA, + .I2C_SBLOCK = DM37XX_I2C_SBLOCK +}; + +/* Define the buses available on each chip */ + +static omap_i2c_bus_t am335x_i2c_buses[] = { + {am335x, AM335X_I2C0_BASE, AM335X_I2C0_SIZE, 0, &am335x_i2c_regs, + AM335X_FUNCTIONAL_CLOCK, AM335X_MODULE_CLOCK, + BUS_SPEED_400KHz, AM335X_REV_MAJOR, AM335X_REV_MINOR, + AM335X_I2C0_IRQ, 1, 1}, + {am335x, AM335X_I2C1_BASE, AM335X_I2C1_SIZE, 0, &am335x_i2c_regs, + AM335X_FUNCTIONAL_CLOCK, AM335X_MODULE_CLOCK, + BUS_SPEED_100KHz, AM335X_REV_MAJOR, AM335X_REV_MINOR, + AM335X_I2C1_IRQ, 2, 3}, + {am335x, AM335X_I2C2_BASE, AM335X_I2C2_SIZE, 0, &am335x_i2c_regs, + AM335X_FUNCTIONAL_CLOCK, AM335X_MODULE_CLOCK, + BUS_SPEED_100KHz, AM335X_REV_MAJOR, AM335X_REV_MINOR, + AM335X_I2C2_IRQ, 3, 3} +}; + +#define AM335X_OMAP_NBUSES (sizeof(am335x_i2c_buses) / sizeof(omap_i2c_bus_t)) + +static omap_i2c_bus_t dm37xx_i2c_buses[] = { + {dm37xx, DM37XX_I2C0_BASE, DM37XX_I2C0_SIZE, 0, &dm37xx_i2c_regs, + DM37XX_FUNCTIONAL_CLOCK, DM37XX_MODULE_CLOCK, + BUS_SPEED_100KHz, DM37XX_REV_MAJOR, DM37XX_REV_MINOR, + DM37XX_I2C0_IRQ, 1, 1}, + {dm37xx, DM37XX_I2C1_BASE, DM37XX_I2C1_SIZE, 0, &dm37xx_i2c_regs, + DM37XX_FUNCTIONAL_CLOCK, DM37XX_MODULE_CLOCK, + BUS_SPEED_100KHz, DM37XX_REV_MAJOR, DM37XX_REV_MINOR, + DM37XX_I2C1_IRQ, 2, 2}, + {dm37xx, DM37XX_I2C2_BASE, DM37XX_I2C2_SIZE, 0, &dm37xx_i2c_regs, + DM37XX_FUNCTIONAL_CLOCK, DM37XX_MODULE_CLOCK, + BUS_SPEED_100KHz, DM37XX_REV_MAJOR, DM37XX_REV_MINOR, + DM37XX_I2C2_IRQ, 3, 3} +}; + +#define DM37XX_OMAP_NBUSES (sizeof(dm37xx_i2c_buses) / sizeof(omap_i2c_bus_t)) + +/* Globals */ + +static omap_i2c_bus_t *omap_i2c_buses; /* all available buses for this SoC */ +static omap_i2c_bus_t *omap_i2c_bus; /* the bus selected at start-up */ +static int omap_i2c_nbuses; /* number of buses supported by SoC */ + +/* logging - use with log_warn(), log_info(), log_debug(), log_trace() */ +static struct log log = { + .name = "i2c", + .log_level = LEVEL_INFO, + .log_func = default_log +}; + +/* Local Function Prototypes */ + +/* Implementation of Generic I2C Interface using Bus Specific Code */ +static int omap_i2c_process(minix_i2c_ioctl_exec_t * m); + +/* Bus Specific Code */ +static void omap_i2c_flush(void); +static uint16_t omap_i2c_poll(uint16_t mask); +static int omap_i2c_claim_bus(void); +static int omap_i2c_release_bus(void); +static int omap_i2c_soft_reset(void); +static void omap_i2c_bus_init(void); +#if 0 +static void omap_i2c_padconf(int i2c_bus_id); +#endif /* disable until libpadconf is fixed */ +static void omap_i2c_clkconf(int i2c_bus_id); +static void omap_i2c_intr_enable(void); +static uint16_t omap_i2c_read_status(void); +static void omap_i2c_write_status(uint16_t mask); +static int omap_i2c_read(i2c_addr_t addr, uint8_t * buf, size_t buflen, + int dostop); +static int omap_i2c_write(i2c_addr_t addr, const uint8_t * buf, size_t buflen, + int dostop); + +/* Helpers for register access */ + +#define reg_read(a) (*(volatile uint16_t *)(omap_i2c_bus->mapped_addr + a)) +#define reg_write(a,v) (*(volatile uint16_t *)(omap_i2c_bus->mapped_addr + a) = (v)) +#define reg_set_bit(a,v) reg_write((a), reg_read((a)) | (1<iie_buflen == 0) { + return EINVAL; + } + + omap_i2c_flush(); /* clear any garbage in the fifo */ + + /* Check bus busy flag before using the bus */ + r = omap_i2c_claim_bus(); + if (r == -1) { + log_warn(&log, "Can't Claim Bus\n"); + return EBUSY; + } + + if (ioctl_exec->iie_cmdlen > 0) { + r = omap_i2c_write(ioctl_exec->iie_addr, ioctl_exec->iie_cmd, + ioctl_exec->iie_cmdlen, + !(I2C_OP_READ_P(ioctl_exec->iie_op))); + if (r != OK) { + omap_i2c_soft_reset(); + omap_i2c_bus_init(); + return r; + } + } + + if (I2C_OP_READ_P(ioctl_exec->iie_op)) { + r = omap_i2c_read(ioctl_exec->iie_addr, ioctl_exec->iie_buf, + ioctl_exec->iie_buflen, I2C_OP_STOP_P(ioctl_exec->iie_op)); + } else { + r = omap_i2c_write(ioctl_exec->iie_addr, ioctl_exec->iie_buf, + ioctl_exec->iie_buflen, I2C_OP_STOP_P(ioctl_exec->iie_op)); + } + + if (r != OK) { + omap_i2c_soft_reset(); + omap_i2c_bus_init(); + return r; + } + + omap_i2c_release_bus(); + + return OK; +} + +/* + * Drain the incoming FIFO. + * + * Usually called to clear any garbage that may be in the buffer before + * doing a read. + */ +static void +omap_i2c_flush(void) +{ + int tries; + int status; + + for (tries = 0; tries < 1000; tries++) { + status = omap_i2c_poll(1 << RRDY); + if ((status & (1 << RRDY)) != 0) { /* bytes available for reading */ + + /* consume the byte and throw it away */ + (void) reg_read(omap_i2c_bus->regs->I2C_DATA); + + /* clear the read ready flag */ + omap_i2c_write_status(1 << RRDY); + + micro_delay(250); + + } else { + break; /* buffer drained */ + } + } +} + +/* + * Poll the status register checking the bits set in 'mask'. + * Returns the status if any bits set or 0x0000 when the timeout is reached. + */ +static uint16_t +omap_i2c_poll(uint16_t mask) +{ + int tries; + uint16_t status; + + for (tries = 0; tries < 1000; tries++) { + + status = omap_i2c_read_status(); + if ((status & mask) != 0) { /* any bits in mask set */ + return status; + } + + micro_delay(250); /* else wait and try again */ + } + + return status; /* timeout reached, abort */ +} + +/* + * Poll Bus Busy Flag until the bus becomes free (return 0) or the timeout + * expires (return -1). + */ +static int +omap_i2c_claim_bus(void) +{ + int tries; + uint16_t status; + + for (tries = 0; tries < 1000; tries++) { + + status = omap_i2c_read_status(); + if ((status & (1 << BB)) == 0) { + return 0; /* bus is free */ + } + + micro_delay(1000); + } + + return -1; /* timeout expired */ +} + +/* + * Clean up after a transfer + */ +static int +omap_i2c_release_bus(void) +{ + omap_i2c_write_status(0x7fff); + micro_delay(50000); + + return 0; +} + +static void +omap_i2c_clkconf(int i2c_bus_id) +{ + clkconf_init(); + + if (omap_i2c_bus->bus_type == dm37xx) { + + clkconf_set(CM_ICLKEN1_CORE, BIT((15 + i2c_bus_id)), + 0xffffffff); + clkconf_set(CM_FCLKEN1_CORE, BIT((15 + i2c_bus_id)), + 0xffffffff); + + } else if (omap_i2c_bus->bus_type == am335x) { + + switch (i2c_bus_id) { + case 0: + clkconf_set(CM_WKUP_I2C0_CLKCTRL, BIT(1), 0xffffffff); + break; + case 1: + clkconf_set(CM_PER_I2C1_CLKCTRL, BIT(1), 0xffffffff); + break; + case 2: + clkconf_set(CM_PER_I2C2_CLKCTRL, BIT(1), 0xffffffff); + break; + default: + log_warn(&log, "Invalid i2c_bus_id\n"); + break; + } + } + + clkconf_release(); +} + +#if 0 +/* re-enable this code when libpadconf is working */ +static void +omap_i2c_padconf(int i2c_bus_id) +{ + u32_t pinopts; + + padconf_init(); + + if (omap_i2c_bus->bus_type == am335x) { + + /* use the options suggested in starterware driver */ + pinopts = + CONTROL_CONF_SLEWCTRL | CONTROL_CONF_RXACTIVE | + CONTROL_CONF_PUTYPESEL; + + switch (i2c_bus_id) { + case 0: + pinopts |= CONTROL_CONF_MUXMODE(0); + padconf_set(CONTROL_CONF_I2C0_SDA, 0xffffffff, + pinopts); + padconf_set(CONTROL_CONF_I2C0_SCL, 0xffffffff, + pinopts); + log_info(&log, "pinopts=%x\n", pinopts); + break; + + case 1: + pinopts |= CONTROL_CONF_MUXMODE(2); + padconf_set(CONTROL_CONF_SPI0_CS0, 0xffffffff, + pinopts); + padconf_set(CONTROL_CONF_SPI0_D1, 0xffffffff, pinopts); + log_info(&log, "pinopts=%x\n", pinopts); + break; + + case 2: + pinopts |= CONTROL_CONF_MUXMODE(3); + padconf_set(CONTROL_CONF_UART1_CTSN, + 0xffffffff, pinopts); + padconf_set(CONTROL_CONF_UART1_RTSN, + 0xffffffff, pinopts); + log_info(&log, "pinopts=%x\n", pinopts); + break; + + default: + log_warn(&log, "Invalid i2c_bus_id\n"); + break; + } + } + + /* nothing to do for the DM37XX */ + + padconf_release(); +} +#endif + +static int +omap_i2c_soft_reset(void) +{ + int tries; + + /* Disable to do soft reset */ + reg_write(omap_i2c_bus->regs->I2C_CON, 0); + micro_delay(50000); + + /* Do a soft reset */ + reg_write(omap_i2c_bus->regs->I2C_SYSC, (1 << SRST)); + + /* Have to temporarily enable I2C to read RDONE */ + reg_set_bit(omap_i2c_bus->regs->I2C_CON, I2C_EN); + micro_delay(50000); + + /* wait for reset to complete */ + for (tries = 0; tries < 10000; tries++) { + + if (reg_read(omap_i2c_bus->regs->I2C_SYSS) & (1 << RDONE)) { + return OK; + } + micro_delay(1000); + } + + log_warn(&log, "Tried soft reset, but bus never came back.\n"); + return EIO; +} + +static void +omap_i2c_intr_enable(void) +{ + int r; + uint16_t intmask; + static int policy_set = 0; + static int enabled = 0; + + if (!policy_set) { + r = sys_irqsetpolicy(omap_i2c_bus->irq, 0, + &omap_i2c_bus->irq_hook_kernel_id); + if (r == OK) { + policy_set = 1; + } else { + log_warn(&log, "Couldn't set irq policy\n"); + } + } + + if (policy_set && !enabled) { + r = sys_irqenable(&omap_i2c_bus->irq_hook_kernel_id); + if (r == OK) { + enabled = 1; + } else { + log_warn(&log, "Couldn't enable irq %d (hooked)\n", + omap_i2c_bus->irq); + } + } + + /* According to NetBSD driver and u-boot, these are needed even + * if just using polling (i.e. non-interrupt driver programming). + */ + intmask = 0; + intmask |= (1 << ROVR); + intmask |= (1 << AERR); + intmask |= (1 << XRDY); + intmask |= (1 << RRDY); + intmask |= (1 << ARDY); + intmask |= (1 << NACK); + intmask |= (1 << AL); + + if (omap_i2c_bus->bus_type == am335x) { + reg_write(omap_i2c_bus->regs->I2C_IRQENABLE_SET, intmask); + } else if (omap_i2c_bus->bus_type == dm37xx) { + reg_write(omap_i2c_bus->regs->I2C_IE, intmask); + } else { + log_warn(&log, "Don't know how to enable interrupts.\n"); + } +} + +static void +omap_i2c_bus_init(void) +{ + + /* Ensure i2c module is disabled before setting prescalar & bus speed */ + reg_write(omap_i2c_bus->regs->I2C_CON, 0); + micro_delay(50000); + + /* Disable autoidle */ + reg_clear_bit(omap_i2c_bus->regs->I2C_SYSC, AUTOIDLE); + + /* Set prescalar to obtain 12 MHz i2c module clock */ + reg_write(omap_i2c_bus->regs->I2C_PSC, + ((omap_i2c_bus->functional_clock / omap_i2c_bus->module_clock) - + 1)); + + /* Set the bus speed */ + reg_write(omap_i2c_bus->regs->I2C_SCLL, + ((omap_i2c_bus->module_clock / (2 * omap_i2c_bus->bus_speed)) - + 7)); + reg_write(omap_i2c_bus->regs->I2C_SCLH, + ((omap_i2c_bus->module_clock / (2 * omap_i2c_bus->bus_speed)) - + 5)); + + /* Set own I2C address */ + if (omap_i2c_bus->bus_type == am335x) { + reg_write(omap_i2c_bus->regs->I2C_OA, I2C_OWN_ADDRESS); + } else if (omap_i2c_bus->bus_type == dm37xx) { + reg_write(omap_i2c_bus->regs->I2C_OA0, I2C_OWN_ADDRESS); + } else { + log_warn(&log, "Don't know how to set own address.\n"); + } + + /* Set TX/RX Threshold to 1 and disable I2C DMA */ + reg_write(omap_i2c_bus->regs->I2C_BUF, 0x0000); + + /* Bring the i2c module out of reset */ + reg_set_bit(omap_i2c_bus->regs->I2C_CON, I2C_EN); + micro_delay(50000); + + /* + * Enable interrupts + */ + omap_i2c_intr_enable(); +} + +static uint16_t +omap_i2c_read_status(void) +{ + uint16_t status = 0x0000; + + if (omap_i2c_bus->bus_type == am335x) { + /* TRM says to use RAW for polling for events */ + status = reg_read(omap_i2c_bus->regs->I2C_IRQSTATUS_RAW); + } else if (omap_i2c_bus->bus_type == dm37xx) { + status = reg_read(omap_i2c_bus->regs->I2C_STAT); + } else { + log_warn(&log, "Don't know how to read i2c bus status.\n"); + } + + return status; +} + +static void +omap_i2c_write_status(uint16_t mask) +{ + if (omap_i2c_bus->bus_type == am335x) { + /* write 1's to IRQSTATUS (not RAW) to clear the bits */ + reg_write(omap_i2c_bus->regs->I2C_IRQSTATUS, mask); + } else if (omap_i2c_bus->bus_type == dm37xx) { + reg_write(omap_i2c_bus->regs->I2C_STAT, mask); + } else { + log_warn(&log, "Don't know how to clear i2c bus status.\n"); + } +} + +static int +omap_i2c_read(i2c_addr_t addr, uint8_t * buf, size_t buflen, int dostop) +{ + int r, i; + uint16_t conopts = 0; + uint16_t pollmask = 0; + uint16_t errmask = 0; + + /* Set address of slave device */ + addr &= ADDRESS_MASK; /* sanitize address (10-bit max) */ + if (addr > 0x7f) { + /* 10-bit extended address in use, need to set XSA */ + conopts |= (1 << XSA); + } + + errmask |= (1 << ROVR); + errmask |= (1 << AERR); + errmask |= (1 << NACK); + errmask |= (1 << AL); + + pollmask |= (1 << RRDY); + + /* Set bytes to read and slave address */ + reg_write(omap_i2c_bus->regs->I2C_CNT, buflen); + reg_write(omap_i2c_bus->regs->I2C_SA, addr); + + /* Set control register */ + conopts |= (1 << I2C_EN); /* enabled */ + conopts |= (1 << MST); /* master mode */ + conopts |= (1 << STT); /* start condition */ + + if (dostop != 0) { + conopts |= (1 << STP); /* stop condition */ + } + + reg_write(omap_i2c_bus->regs->I2C_CON, conopts); + + for (i = 0; i < buflen; i++) { + /* Data to read? */ + r = omap_i2c_poll(pollmask | errmask); + if ((r & errmask) != 0) { + /* only debug log level because i2cscan trigers this */ + log_debug(&log, "Read Error! Status=%x\n", r); + return EIO; + } else if ((r & pollmask) == 0) { + log_warn(&log, "No RRDY Interrupt. Status=%x\n", r); + log_warn(&log, + "Likely cause: bad pinmux or no devices on bus\n"); + return EBUSY; + } + + /* read a byte */ + buf[i] = reg_read(omap_i2c_bus->regs->I2C_DATA) & 0xff; + + /* clear the read ready flag */ + omap_i2c_write_status(pollmask); + } + + r = omap_i2c_read_status(); + if ((r & (1 << NACK)) != 0) { + log_warn(&log, "NACK\n"); + return EIO; + } + + omap_i2c_write_status(0x7fff); + micro_delay(50000); + + return 0; +} + +static int +omap_i2c_write(i2c_addr_t addr, const uint8_t * buf, size_t buflen, int dostop) +{ + int r, i; + uint16_t conopts = 0; + uint16_t pollmask = 0; + uint16_t errmask = 0; + + /* Set address of slave device */ + addr &= ADDRESS_MASK; /* sanitize address (10-bit max) */ + if (addr > 0x7f) { + /* 10-bit extended address in use, need to set XSA */ + conopts |= (1 << XSA); + } + + pollmask |= (1 << XRDY); + + errmask |= (1 << ROVR); + errmask |= (1 << AERR); + errmask |= (1 << NACK); + errmask |= (1 << AL); + + reg_write(omap_i2c_bus->regs->I2C_CNT, buflen); + reg_write(omap_i2c_bus->regs->I2C_SA, addr); + + /* Set control register */ + conopts |= (1 << I2C_EN); /* enabled */ + conopts |= (1 << MST); /* master mode */ + conopts |= (1 << TRX); /* TRX mode */ + conopts |= (1 << STT); /* start condition */ + + if (dostop != 0) { + conopts |= (1 << STP); /* stop condition */ + } + + omap_i2c_write_status(0x7fff); + reg_write(omap_i2c_bus->regs->I2C_CON, conopts); + + for (i = 0; i < buflen; i++) { + + /* Ready to write? */ + r = omap_i2c_poll(pollmask | errmask); + if ((r & errmask) != 0) { + log_warn(&log, "Write Error! Status=%x\n", r); + return EIO; + } else if ((r & pollmask) == 0) { + log_warn(&log, "Not ready for write? Status=%x\n", r); + return EBUSY; + } + + reg_write(omap_i2c_bus->regs->I2C_DATA, buf[i]); + + /* clear the write ready flag */ + omap_i2c_write_status(pollmask); + } + + r = omap_i2c_read_status(); + if ((r & (1 << NACK)) != 0) { + return EIO; + } + + omap_i2c_write_status(0x7fff); + micro_delay(50000); + + return 0; +} + +int +omap_interface_setup(int (**process) (minix_i2c_ioctl_exec_t * ioctl_exec), + int i2c_bus_id) +{ + int r; + int i2c_rev, major, minor; + struct minix_mem_range mr; + + /* Fill in the function pointer */ + + *process = omap_i2c_process; + + /* Select the correct i2c definition for this SoC */ + +#if defined(AM335X) + omap_i2c_buses = am335x_i2c_buses; + omap_i2c_nbuses = AM335X_OMAP_NBUSES; +#elif defined(DM37XX) + omap_i2c_buses = dm37xx_i2c_buses; + omap_i2c_nbuses = DM37XX_OMAP_NBUSES; +#else +#error /* Unsupported SoC */ +#endif + + if (i2c_bus_id < 0 || i2c_bus_id >= omap_i2c_nbuses) { + return EINVAL; + } + + /* select the bus to operate on */ + omap_i2c_bus = &omap_i2c_buses[i2c_bus_id]; + + /* + * Map I2C Registers + */ + + /* Configure memory access */ + mr.mr_base = omap_i2c_bus->mr_base; /* start addr */ + mr.mr_limit = mr.mr_base + omap_i2c_bus->mr_size; /* end addr */ + + /* ask for privileges to access the I2C memory range */ + if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != OK) { + panic("Unable to obtain i2c memory range privileges"); + } + + /* map the memory into this process */ + omap_i2c_bus->mapped_addr = (vir_bytes) vm_map_phys(SELF, + (void *) omap_i2c_bus->mr_base, omap_i2c_bus->mr_size); + + if (omap_i2c_bus->mapped_addr == (vir_bytes) MAP_FAILED) { + panic("Unable to map i2c registers"); + } + + /* Enable Clocks */ + omap_i2c_clkconf(i2c_bus_id); + +#if 0 +/* disable until libpadconf is fixed */ + /* Configure Pins */ + omap_i2c_padconf(i2c_bus_id); +#endif + + /* Perform a soft reset of the I2C module to ensure a fresh start */ + r = omap_i2c_soft_reset(); + if (r != OK) { + /* module didn't come back up :( */ + return r; + } + + /* Bring up I2C module */ + omap_i2c_bus_init(); + + /* Get I2C Revision */ + if (omap_i2c_bus->bus_type == am335x) { + /* I2C_REVLO revision: major (bits 10-8), minor (bits 5-0) */ + i2c_rev = reg_read(omap_i2c_bus->regs->I2C_REVNB_LO); + major = (i2c_rev >> 8) & 0x07; + minor = i2c_rev & 0x3f; + + } else if (omap_i2c_bus->bus_type == dm37xx) { + /* I2C_REV revision: major (bits 7-4), minor (bits 3-0) */ + i2c_rev = reg_read(omap_i2c_bus->regs->I2C_REV); + major = (i2c_rev >> 4) & 0x0f; + minor = i2c_rev & 0x0f; + } else { + panic("Don't know how to read i2c revision."); + } + + if (major != omap_i2c_bus->major || minor != omap_i2c_bus->minor) { + log_warn(&log, "Unrecognized value in I2C_REV register.\n"); + log_warn(&log, "Read: 0x%x.0x%x | Expected: 0x%x.0x%x\n", + major, minor, omap_i2c_bus->major, omap_i2c_bus->minor); + } + + /* display i2c revision information for debugging purposes */ + log_debug(&log, "i2c_%d: I2C rev 0x%x.0x%x\n", (i2c_bus_id + 1), + major, minor); + + return OK; +} diff --git a/drivers/i2c/arch/earm/omap_i2c.h b/drivers/i2c/arch/earm/omap_i2c.h new file mode 100644 index 000000000..014df2c83 --- /dev/null +++ b/drivers/i2c/arch/earm/omap_i2c.h @@ -0,0 +1,10 @@ +#ifndef _OMAP_I2C_H +#define _OMAP_I2C_H + +#include +#include +#include "omap_i2c_registers.h" + +int omap_interface_setup(int (**process)(minix_i2c_ioctl_exec_t *ioctl_exec), int i2c_bus_id); + +#endif /* _OMAP_I2C_H */ diff --git a/drivers/i2c/arch/earm/omap_i2c_registers.h b/drivers/i2c/arch/earm/omap_i2c_registers.h new file mode 100644 index 000000000..ce6932ca8 --- /dev/null +++ b/drivers/i2c/arch/earm/omap_i2c_registers.h @@ -0,0 +1,152 @@ +#ifndef _OMAP_I2C_REGISTERS_H +#define _OMAP_I2C_REGISTERS_H + +/* I2C Addresses for am335x (BeagleBone White / BeagleBone Black) */ + +/* IRQ Numbers */ +#define AM335X_I2C0_IRQ 70 +#define AM335X_I2C1_IRQ 71 +#define AM335X_I2C2_IRQ 30 + +/* Base Addresses */ +#define AM335X_I2C0_BASE 0x44e0b000 +#define AM335X_I2C1_BASE 0x4802a000 +#define AM335X_I2C2_BASE 0x4819c000 + +/* Size of I2C Register Address Range */ +#define AM335X_I2C0_SIZE 0x1000 +#define AM335X_I2C1_SIZE 0x1000 +#define AM335X_I2C2_SIZE 0x1000 + +/* Register Offsets */ +#define AM335X_I2C_REVNB_LO 0x00 +#define AM335X_I2C_REVNB_HI 0x04 +#define AM335X_I2C_SYSC 0x10 +#define AM335X_I2C_IRQSTATUS_RAW 0x24 +#define AM335X_I2C_IRQSTATUS 0x28 +#define AM335X_I2C_IRQENABLE_SET 0x2c +#define AM335X_I2C_IRQENABLE_CLR 0x30 +#define AM335X_I2C_WE 0x34 +#define AM335X_I2C_DMARXENABLE_SET 0x38 +#define AM335X_I2C_DMATXENABLE_SET 0x3c +#define AM335X_I2C_DMARXENABLE_CLR 0x40 +#define AM335X_I2C_DMATXENABLE_CLR 0x44 +#define AM335X_I2C_DMARXWAKE_EN 0x48 +#define AM335X_I2C_DMATXWAKE_EN 0x4c +#define AM335X_I2C_SYSS 0x90 +#define AM335X_I2C_BUF 0x94 +#define AM335X_I2C_CNT 0x98 +#define AM335X_I2C_DATA 0x9c +#define AM335X_I2C_CON 0xa4 +#define AM335X_I2C_OA 0xa8 +#define AM335X_I2C_SA 0xac +#define AM335X_I2C_PSC 0xb0 +#define AM335X_I2C_SCLL 0xb4 +#define AM335X_I2C_SCLH 0xb8 +#define AM335X_I2C_SYSTEST 0xbc +#define AM335X_I2C_BUFSTAT 0xc0 +#define AM335X_I2C_OA1 0xc4 +#define AM335X_I2C_OA2 0xc8 +#define AM335X_I2C_OA3 0xcc +#define AM335X_I2C_ACTOA 0xd0 +#define AM335X_I2C_SBLOCK 0xd4 + +/* Constants */ +#define AM335X_FUNCTIONAL_CLOCK 96000000 /* 96 MHz */ +#define AM335X_MODULE_CLOCK 12000000 /* 12 MHz */ + +/* I2C_REV value found on the BeagleBone / BeagleBone Black */ +#define AM335X_REV_MAJOR 0x00 +#define AM335X_REV_MINOR 0x0b + +/* I2C Addresses for dm37xx (BeagleBoard-xM) */ + +/* IRQ Numbers */ +#define DM37XX_I2C0_IRQ 56 +#define DM37XX_I2C1_IRQ 57 +#define DM37XX_I2C2_IRQ 61 + +/* Base Addresses */ +#define DM37XX_I2C0_BASE 0x48070000 +#define DM37XX_I2C1_BASE 0x48072000 +#define DM37XX_I2C2_BASE 0x48060000 + +/* Size of I2C Register Address Range */ +#define DM37XX_I2C0_SIZE 0x1000 +#define DM37XX_I2C1_SIZE 0x1000 +#define DM37XX_I2C2_SIZE 0x1000 + +/* Register Offsets */ +#define DM37XX_I2C_REV 0x00 +#define DM37XX_I2C_IE 0x04 +#define DM37XX_I2C_STAT 0x08 +#define DM37XX_I2C_WE 0x0C +#define DM37XX_I2C_SYSS 0x10 +#define DM37XX_I2C_BUF 0x14 +#define DM37XX_I2C_CNT 0x18 +#define DM37XX_I2C_DATA 0x1c +#define DM37XX_I2C_SYSC 0x20 +#define DM37XX_I2C_CON 0x24 +#define DM37XX_I2C_OA0 0x28 +#define DM37XX_I2C_SA 0x2c +#define DM37XX_I2C_PSC 0x30 +#define DM37XX_I2C_SCLL 0x34 +#define DM37XX_I2C_SCLH 0x38 +#define DM37XX_I2C_SYSTEST 0x3c +#define DM37XX_I2C_BUFSTAT 0x40 +#define DM37XX_I2C_OA1 0x44 +#define DM37XX_I2C_OA2 0x48 +#define DM37XX_I2C_OA3 0x4c +#define DM37XX_I2C_ACTOA 0x50 +#define DM37XX_I2C_SBLOCK 0x54 + +/* Constants */ +#define DM37XX_FUNCTIONAL_CLOCK 96000000 /* 96 MHz */ +#define DM37XX_MODULE_CLOCK 19200000 /* 19.2 MHz */ + +#define DM37XX_REV_MAJOR 0x04 +#define DM37XX_REV_MINOR 0x00 + +/* Shared Values */ + +#define BUS_SPEED_100KHz 100000 /* 100 KHz */ +#define BUS_SPEED_400KHz 400000 /* 400 KHz */ +#define I2C_OWN_ADDRESS 0x01 + +/* Masks */ + +#define ADDRESS_MASK (0x3ff) /* Highest 10 bit address -- 9..0 */ + +/* Bit Offsets within Registers (only those used are listed) */ + +/* Same offsets for both dm37xx and am335x */ + +#define I2C_EN 15 /* I2C_CON */ +#define MST 10 /* I2C_CON */ +#define TRX 9 /* I2C_CON */ +#define XSA 8 /* I2C_CON */ +#define STP 1 /* I2C_CON */ +#define STT 0 /* I2C_CON */ + +#define CLKACTIVITY_S 9 /* I2C_SYSC */ +#define CLKACTIVITY_I 8 /* I2C_SYSC */ +#define SMART_WAKE_UP 4 /* I2C_SYSC */ +#define NO_IDLE_MODE 3 /* I2C_SYSC */ +#define SRST 1 /* I2C_SYSC */ +#define AUTOIDLE 0 /* I2C_SYSC */ + +#define RDONE 0 /* I2C_SYSS */ + +#define RXFIFO_CLR 14 /* I2C_BUF */ +#define TXFIFO_CLR 6 /* I2C_BUF */ + +#define BB 12 /* I2C_IRQSTATUS / I2C_STAT / I2C_IRQENABLE_SET / I2C_IE */ +#define ROVR 11 /* I2C_IRQSTATUS / I2C_STAT / I2C_IRQENABLE_SET / I2C_IE */ +#define AERR 7 /* I2C_IRQSTATUS / I2C_STAT / I2C_IRQENABLE_SET / I2C_IE */ +#define XRDY 4 /* I2C_IRQSTATUS / I2C_STAT / I2C_IRQENABLE_SET / I2C_IE */ +#define RRDY 3 /* I2C_IRQSTATUS / I2C_STAT / I2C_IRQENABLE_SET / I2C_IE */ +#define ARDY 2 /* I2C_IRQSTATUS / I2C_STAT / I2C_IRQENABLE_SET / I2C_IE */ +#define NACK 1 /* I2C_IRQSTATUS / I2C_STAT / I2C_IRQENABLE_SET / I2C_IE */ +#define AL 0 /* I2C_IRQSTATUS / I2C_STAT / I2C_IRQENABLE_SET / I2C_IE */ + +#endif /* _OMAP_I2C_REGISTERS_H */ diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c new file mode 100644 index 000000000..36d8af324 --- /dev/null +++ b/drivers/i2c/i2c.c @@ -0,0 +1,555 @@ +/* + * i2c - generic driver for Inter-Integrated Circuit bus (I2C). + */ + +/* kernel headers */ +#include +#include +#include +#include +#include +#include + +/* system headers */ +#include + +/* usr headers */ +#include +#include +#include + +/* SoC specific headers - 1 for each SoC */ +#include "omap_i2c.h" + +/* local definitions */ + +/* i2c slave addresses can be up to 10 bits */ +#define NR_I2CDEV (0x3ff) + +/* local function prototypes */ +static int do_reserve(endpoint_t endpt, int slave_addr); +static int check_reservation(endpoint_t endpt, int slave_addr); +static void update_reservation(endpoint_t endpt, char *key); +static void ds_event(void); + +static int validate_ioctl_exec(minix_i2c_ioctl_exec_t * ioctl_exec); +static int do_i2c_ioctl_exec(message * m); + +static int env_parse_instance(void); + +/* libchardriver callbacks */ +int i2c_ioctl(message * m); +struct device *i2c_prepare(dev_t dev); +int i2c_transfer(endpoint_t endpt, int opcode, u64_t position, + iovec_t * iov, unsigned nr_req, endpoint_t user_endpt, unsigned int flags); +int i2c_other(message * m); + +/* SEF callbacks and driver state management */ +static int sef_cb_lu_state_save(int); +static int lu_state_restore(void); +static int sef_cb_init(int type, sef_init_info_t * info); +static void sef_local_startup(void); + +/* Globals */ + +/* the bus that this instance of the driver is responsible for */ +uint32_t i2c_bus_id; + +/* Table of i2c device reservations. */ +static struct i2cdev +{ + uint8_t inuse; + endpoint_t endpt; + char key[DS_MAX_KEYLEN]; +} i2cdev[NR_I2CDEV]; + +/* Process a request for an i2c operation. + * This is the interface that all hardware specific code must implement. + */ +int (*process) (minix_i2c_ioctl_exec_t * ioctl_exec); + +struct device i2c_device; + +/* logging - use with log_warn(), log_info(), log_debug(), log_trace() */ +static struct log log = { + .name = "i2c", + .log_level = LEVEL_INFO, + .log_func = default_log +}; + +/* Entry points to the i2c driver from libchardriver. + * Only i2c_ioctl() and i2c_other() are implemented. The rest are no-op. + */ +static struct chardriver i2c_tab = { + .cdr_open = do_nop, + .cdr_close = do_nop, + .cdr_ioctl = i2c_ioctl, + .cdr_prepare = i2c_prepare, + .cdr_transfer = i2c_transfer, + .cdr_cleanup = nop_cleanup, + .cdr_alarm = nop_alarm, + .cdr_cancel = nop_cancel, + .cdr_select = nop_select, + .cdr_other = i2c_other +}; + +/* + * Claim an unclaimed device for exclusive use by endpt. This function can + * also be used to update the endpt if the endpt's label matches the label + * already associated with the slave address. This is useful if a driver + * shuts down unexpectedly and starts up with a new endpt and wants to reserve + * the same device it reserved before. + */ +static int +do_reserve(endpoint_t endpt, int slave_addr) +{ + int r; + char key[DS_MAX_KEYLEN]; + char label[DS_MAX_KEYLEN]; + + /* find the label for the endpoint */ + r = ds_retrieve_label_name(label, endpt); + if (r != OK) { + log_warn(&log, "Couldn't find label for endpt='0x%x'\n", + endpt); + return r; + } + + /* construct the key i2cdriver_announce published (saves an IPC call) */ + snprintf(key, DS_MAX_KEYLEN, "drv.i2c.%d.%s", i2c_bus_id + 1, label); + + if (slave_addr < 0 || slave_addr >= NR_I2CDEV) { + log_debug(&log, + "slave address must be positive & no more than 10 bits\n"); + return EINVAL; + } + + /* check if device is in use by another driver */ + if (i2cdev[slave_addr].inuse != 0 + && strncmp(i2cdev[slave_addr].key, key, DS_MAX_KEYLEN) != 0) { + log_debug(&log, "address in use by '%s'/0x%x\n", + i2cdev[slave_addr].key, i2cdev[slave_addr].endpt); + return EBUSY; + } + + /* device is free or already owned by us, claim it */ + i2cdev[slave_addr].inuse = 1; + i2cdev[slave_addr].endpt = endpt; + memcpy(i2cdev[slave_addr].key, key, DS_MAX_KEYLEN); + + sef_cb_lu_state_save(0); /* save reservations */ + + log_debug(&log, "Device 0x%x claimed by 0x%x key='%s'\n", + slave_addr, endpt, key); + + return OK; +} + +/* + * All drivers must reserve their device(s) before doing operations on them + * (read/write, etc). ioctl()'s from VFS (i.e. user programs) can only use + * devices that haven't been reserved. A driver isn't allowed to access a + * device that another driver has reserved (not even other instances of the + * same driver). + */ +static int +check_reservation(endpoint_t endpt, int slave_addr) +{ + if (slave_addr < 0 || slave_addr >= NR_I2CDEV) { + log_debug(&log, + "slave address must be positive & no more than 10 bits\n"); + return EINVAL; + } + + if (endpt == VFS_PROC_NR && i2cdev[slave_addr].inuse == 0) { + log_debug(&log, + "allowing ioctl() from VFS to access unclaimed device\n"); + return OK; + } + + if (i2cdev[slave_addr].inuse && i2cdev[slave_addr].endpt != endpt) { + log_debug(&log, "device reserved by another endpoint\n"); + return EBUSY; + } else if (i2cdev[slave_addr].inuse == 0) { + log_debug(&log, + "all drivers sending messages directly to this driver must reserve\n"); + return EPERM; + } else { + log_debug(&log, "allowing access to registered device\n"); + return OK; + } +} + +/* + * i2c listens to updates from ds about i2c device drivers starting up. + * When a driver comes back up with the same label, the endpt associated + * with the reservation needs to be updated. This function does the updating. + */ +static void +update_reservation(endpoint_t endpt, char *key) +{ + int i; + + log_debug(&log, "Updating reservation for '%s' endpt=0x%x\n", key, + endpt); + + for (i = 0; i < NR_I2CDEV; i++) { + + /* find devices in use that the driver owns */ + if (i2cdev[i].inuse != 0 + && strncmp(i2cdev[i].key, key, DS_MAX_KEYLEN) == 0) { + /* update reservation with new endpoint */ + do_reserve(endpt, i); + log_debug(&log, "Found device to update 0x%x\n", i); + } + } +} + +/* + * Checks a minix_i2c_ioctl_exec_t to see if the fields make sense. + */ +static int +validate_ioctl_exec(minix_i2c_ioctl_exec_t * ioctl_exec) +{ + i2c_op_t op; + i2c_addr_t addr; + size_t len; + + op = ioctl_exec->iie_op; + if (op != I2C_OP_READ && + op != I2C_OP_READ_WITH_STOP && + op != I2C_OP_WRITE && + op != I2C_OP_WRITE_WITH_STOP && + op != I2C_OP_READ_BLOCK && op != I2C_OP_WRITE_BLOCK) { + log_warn(&log, "iie_op value not valid\n"); + return EINVAL; + } + + addr = ioctl_exec->iie_addr; + if (addr < 0 || addr >= NR_I2CDEV) { + log_warn(&log, "iie_addr out of range 0x0-0x%x\n", NR_I2CDEV); + return EINVAL; + } + + len = ioctl_exec->iie_cmdlen; + if (len < 0 || len > I2C_EXEC_MAX_CMDLEN) { + log_warn(&log, + "iie_cmdlen out of range 0-I2C_EXEC_MAX_CMDLEN\n"); + return EINVAL; + } + + len = ioctl_exec->iie_buflen; + if (len < 0 || len > I2C_EXEC_MAX_BUFLEN) { + log_warn(&log, + "iie_buflen out of range 0-I2C_EXEC_MAX_BUFLEN\n"); + return EINVAL; + } + + return OK; +} + +/* + * Performs the action in minix_i2c_ioctl_exec_t. + */ +static int +do_i2c_ioctl_exec(message * m) +{ + int r; + endpoint_t caller; + cp_grant_id_t grant_nr; + minix_i2c_ioctl_exec_t ioctl_exec; + + caller = (endpoint_t) m->m_source; + grant_nr = (cp_grant_id_t) m->IO_GRANT; + + /* Copy the requested exection into the driver */ + r = sys_safecopyfrom(caller, grant_nr, (vir_bytes) 0, + (vir_bytes) & ioctl_exec, sizeof(ioctl_exec)); + if (r != OK) { + log_warn(&log, "sys_safecopyfrom() failed\n"); + return r; + } + + /* input validation */ + r = validate_ioctl_exec(&ioctl_exec); + if (r != OK) { + log_debug(&log, "Message validation failed\n"); + return r; + } + + /* permission check */ + r = check_reservation(caller, ioctl_exec.iie_addr); + if (r != OK) { + log_debug(&log, "check_reservation() denied the request\n"); + return r; + } + + /* Call the device specific code to execute the action */ + r = process(&ioctl_exec); + if (r != OK) { + log_debug(&log, "process() failed\n"); + return r; + } + + /* Copy the results of the execution back to the calling process */ + r = sys_safecopyto(caller, grant_nr, (vir_bytes) 0, + (vir_bytes) & ioctl_exec, sizeof(ioctl_exec)); + if (r != OK) { + log_warn(&log, "sys_safecopyto() failed\n"); + return r; + } + + return OK; +} + +int +i2c_ioctl(message * m) +{ + int r; + + switch (m->COUNT) { + case MINIX_I2C_IOCTL_EXEC: + r = do_i2c_ioctl_exec(m); + break; + default: + log_warn(&log, "Invalid ioctl() 0x%x\n", m->COUNT); + r = EINVAL; + break; + } + + return r; +} + +int +i2c_other(message * m) +{ + int r; + + switch (m->m_type) { + case BUSC_I2C_RESERVE: + /* reserve a device on the bus for exclusive access */ + r = do_reserve((endpoint_t) m->m_source, m->DEVICE); + break; + case BUSC_I2C_EXEC: + /* handle request from another driver */ + m->COUNT = MINIX_I2C_IOCTL_EXEC; + r = do_i2c_ioctl_exec(m); + break; + case NOTIFY_MESSAGE: + /* handle notifications about drivers changing state */ + if (m->m_source == DS_PROC_NR) { + ds_event(); + } + r = OK; + break; + default: + log_warn(&log, "Invalid message type (0x%x)\n", m->m_type); + r = EINVAL; + break; + } + + log_trace(&log, "i2c_other() returning r=%d\n", r); + + return r; +} + +struct device * +i2c_prepare(dev_t dev) +{ + /* NOP */ + i2c_device.dv_base = make64(0, 0); + i2c_device.dv_size = make64(0, 0); + + return &i2c_device; +} + +int +i2c_transfer(endpoint_t endpt, int opcode, u64_t position, + iovec_t * iov, unsigned nr_req, endpoint_t user_endpt, unsigned int flags) +{ + /* NOP */ + return OK; +} + +/* + * The bus drivers are subscribed to DS events about device drivers on their + * bus. When the device drivers restart, DS sends a notification and this + * function updates the reservation table with the device driver's new + * endpoint. + */ +static void +ds_event(void) +{ + char key[DS_MAX_KEYLEN]; + u32_t value; + int type; + endpoint_t owner_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) { + log_warn(&log, "ds_retrieve_u32() failed r=%d\n", r); + return; + } + + log_debug(&log, "key='%s' owner_endpoint=0x%x\n", key, + owner_endpoint); + + if (value == DS_DRIVER_UP) { + /* clean up any old reservations the driver had */ + log_debug(&log, "DS_DRIVER_UP\n"); + update_reservation(owner_endpoint, key); + } + } +} + +static int +sef_cb_lu_state_save(int UNUSED(state)) +{ + int r; + char key[DS_MAX_KEYLEN]; + + memset(key, '\0', DS_MAX_KEYLEN); + snprintf(key, DS_MAX_KEYLEN, "i2c.%d.i2cdev", i2c_bus_id + 1); + r = ds_publish_mem(key, i2cdev, sizeof(i2cdev), DSF_OVERWRITE); + if (r != OK) { + log_warn(&log, "ds_publish_mem(%s) failed (r=%d)\n", key, r); + return r; + } + + log_debug(&log, "State Saved\n"); + + return OK; +} + +static int +lu_state_restore(void) +{ + int r; + char key[DS_MAX_KEYLEN]; + size_t size; + + env_parse_instance(); + + size = sizeof(i2cdev); + + memset(key, '\0', DS_MAX_KEYLEN); + snprintf(key, DS_MAX_KEYLEN, "i2c.%d.i2cdev", i2c_bus_id + 1); + + r = ds_retrieve_mem(key, (char *) i2cdev, &size); + if (r != OK) { + log_warn(&log, "ds_retrieve_mem(%s) failed (r=%d)\n", key, r); + return r; + } + + log_debug(&log, "State Restored\n"); + + return OK; +} + +static int +sef_cb_init(int type, sef_init_info_t * UNUSED(info)) +{ + int r; + char regex[DS_MAX_KEYLEN]; + + if (type != SEF_INIT_FRESH) { + /* Restore a prior state. */ + lu_state_restore(); + } +#if defined(AM335X) || defined(DM37XX) + /* Set callback and initialize the bus */ + r = omap_interface_setup(&process, i2c_bus_id); + if (r != OK) { + return r; + } +#else +#error /* Unknown SoC or bad configuration */ +#endif + + /* Announce we are up when necessary. */ + if (type != SEF_INIT_LU) { + + /* only capture events for this particular bus */ + snprintf(regex, DS_MAX_KEYLEN, "drv\\.i2c\\.%d\\..*", + i2c_bus_id + 1); + + /* Subscribe to driver events for i2c drivers */ + r = ds_subscribe(regex, DSF_INITIAL | DSF_OVERWRITE); + if (r != OK) { + log_warn(&log, "ds_subscribe() failed\n"); + return r; + } + + chardriver_announce(); + } + + /* Save state */ + sef_cb_lu_state_save(0); + + /* Initialization completed successfully. */ + return OK; +} + +static void +sef_local_startup() +{ + /* Register init callbacks. */ + sef_setcb_init_fresh(sef_cb_init); + sef_setcb_init_lu(sef_cb_init); + sef_setcb_init_restart(sef_cb_init); + + /* Register live update callbacks */ + /* Agree to update immediately when LU is requested in a valid state */ + sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready); + /* - Support live update starting from any standard state */ + sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard); + /* - Register a custom routine to save the state. */ + sef_setcb_lu_state_save(sef_cb_lu_state_save); + + /* Let SEF perform startup. */ + sef_startup(); +} + +static int +env_parse_instance(void) +{ + int r; + long instance; + + /* Parse the instance number passed to service */ + instance = 0; + r = env_parse("instance", "d", 0, &instance, 1, 3); + if (r == -1) { + log_warn(&log, + "Expecting '-arg instance=N' argument (N=1..3)\n"); + return EXIT_FAILURE; + } + + /* Device files count from 1, hardware starts counting from 0 */ + i2c_bus_id = instance - 1; + + return OK; +} + +int +main(int argc, char *argv[]) +{ + int r; + + env_setargs(argc, argv); + + r = env_parse_instance(); + if (r != OK) { + return r; + } + + memset(i2cdev, '\0', sizeof(i2cdev)); + sef_local_startup(); + chardriver_task(&i2c_tab, CHARDRIVER_SYNC); + + return OK; +} diff --git a/etc/system.conf b/etc/system.conf index 59a2178cd..6e111b368 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -589,6 +589,25 @@ service gpio }; +service i2c +{ + system + PRIVCTL # 4 + IRQCTL # 19 + ; + irq + # DM37XX (BeagleBoard-xM) + 56 # I2C module 1 + 57 # I2C module 2 + 61 # I2C module 3 + # AM335X (BeagleBone) + 70 # I2C module 1 + 71 # I2C module 2 + 30 # I2C module 3 + ; + ipc SYSTEM RS DS; +}; + service vbox { system diff --git a/etc/usr/rc b/etc/usr/rc index 6c14e3e5d..21c2cd99c 100644 --- a/etc/usr/rc +++ b/etc/usr/rc @@ -183,6 +183,19 @@ start) daemonize syslogd echo . + # i2c only supported on ARM at the moment + if [ $ARCH = earm ] + then + echo -n "Starting i2c subsystem: " + for bus in 1 2 3 + do + test -e /dev/i2c-${bus} || (cd /dev && MAKEDEV i2c-${bus}) + up i2c -dev /dev/i2c-${bus} -label i2c.${bus} \ + -args instance=${bus} + done + echo . + fi + if [ "$net" ] then if [ -f /etc/rc.net ] diff --git a/include/minix/Makefile b/include/minix/Makefile index 071d38770..e1b65cc4f 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 ioctl.h input.h ipc.h ipcconst.h \ + hgfs.h i2c.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/clkconf.h b/include/minix/clkconf.h index deba8a2c2..dc66f4aac 100644 --- a/include/minix/clkconf.h +++ b/include/minix/clkconf.h @@ -1,7 +1,18 @@ +#ifndef _CLKCONF_H +#define _CLKCONF_H + /* Clock configuration */ +#define CM_FCLKEN1_CORE 0xA00 +#define CM_ICLKEN1_CORE 0xA10 #define CM_FCLKEN_WKUP 0xC00 #define CM_ICLKEN_WKUP 0xC10 +#define CM_PER_I2C2_CLKCTRL 0x044 +#define CM_PER_I2C1_CLKCTRL 0x048 +#define CM_WKUP_I2C0_CLKCTRL 0x4B8 + int clkconf_init(); int clkconf_set(u32_t clk, u32_t mask, u32_t value); int clkconf_release(); + +#endif diff --git a/include/minix/com.h b/include/minix/com.h index 59bab8112..4cabf8162 100644 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -180,6 +180,8 @@ * a segment of memory */ +#define BUSC_I2C_RESERVE (BUSC_RQ_BASE + 64) /* reserve i2c device */ +#define BUSC_I2C_EXEC (BUSC_RQ_BASE + 65) /* perform i2c action */ /*===========================================================================* * Messages for CHARACTER device drivers * diff --git a/include/minix/dmap.h b/include/minix/dmap.h index 6aac5804b..71b30db53 100644 --- a/include/minix/dmap.h +++ b/include/minix/dmap.h @@ -39,6 +39,9 @@ enum dev_style { STYLE_NDEV, STYLE_DEV, STYLE_DEVA, STYLE_TTY, STYLE_CTTY, #define HELLO_MAJOR 17 /* 17 = /dev/hello (hello driver) */ #define UDS_MAJOR 18 /* 18 = /dev/uds (pfs) */ #define FB_MAJOR 19 /* 18 = /dev/fb0 (fb driver) */ +#define I2C0_MAJOR 20 /* 20 = /dev/i2c-1 (i2c-dev) */ +#define I2C1_MAJOR 21 /* 21 = /dev/i2c-2 (i2c-dev) */ +#define I2C2_MAJOR 22 /* 22 = /dev/i2c-3 (i2c-dev) */ /* Minor device numbers for memory driver. */ diff --git a/include/minix/i2c.h b/include/minix/i2c.h new file mode 100644 index 000000000..4aebb0231 --- /dev/null +++ b/include/minix/i2c.h @@ -0,0 +1,26 @@ +#ifndef __MINIX_I2C_H +#define __MINIX_I2C_H + +/* + * Minix I2C /dev Interface. + * + * Same as NetBSD/OpenBSD interface but with a flat struct (i.e. no pointers). + * The NetBSD/OpenBSD interface can still be used on i2c device files. The + * ioctl(2) function will translate to/from the Minix version of the struct. + */ + +#include +#include + +typedef struct minix_i2c_ioctl_exec { + i2c_op_t iie_op; /* operation to perform */ + i2c_addr_t iie_addr; /* address of device */ + uint8_t iie_cmd[I2C_EXEC_MAX_CMDLEN]; /* pointer to command */ + size_t iie_cmdlen; /* length of command */ + uint8_t iie_buf[I2C_EXEC_MAX_BUFLEN]; /* pointer to data buffer */ + size_t iie_buflen; /* length of data buffer */ +} minix_i2c_ioctl_exec_t; + +#define MINIX_I2C_IOCTL_EXEC _IORW('I', 1, minix_i2c_ioctl_exec_t) + +#endif /* __MINIX_I2C_H */ diff --git a/include/minix/padconf.h b/include/minix/padconf.h index 8d8f7cc63..486577b7f 100644 --- a/include/minix/padconf.h +++ b/include/minix/padconf.h @@ -1,13 +1,17 @@ #ifndef __PADCONF_H__ #define __PADCONF_H__ +#ifdef AM335X +#define PADCONF_REGISTERS_BASE 0x44E10000 +#elif DM37XX #define PADCONF_REGISTERS_BASE 0x48002030 +#endif #define PADCONF_MUXMODE(X) (X & 0x7) /* mode 1 til 7 [2:0] */ #define PADCONF_PULL_MODE(X) ((X & 0x3) << 3) /* 2 bits[4:3] */ #define PADCONF_PULL_MODE_PD_DIS PADCONF_PULL_MODE(0) /* pull down disabled */ #define PADCONF_PULL_MODE_PD_EN PADCONF_PULL_MODE(1) /* pull down enabled */ -#define PADCONF_PULL_MODE_PU_DIS PADCONF_PULL_MODE(2) /* pull up enabled */ +#define PADCONF_PULL_MODE_PU_DIS PADCONF_PULL_MODE(2) /* pull up disabled */ #define PADCONF_PULL_MODE_PU_EN PADCONF_PULL_MODE(3) /* pull up enabled */ #define PADCONF_INPUT_ENABLE(X) ((X & 0x1) << 8) /* 1 bits[8] */ #define PADCONF_OFF_MODE(X) ((X & 0xFE) << 9) /* 5 bits[13:9] */ @@ -177,6 +181,139 @@ #define CONTROL_PADCONF_ETK_D12 (0x000005C4) #define CONTROL_PADCONF_ETK_D14 (0x000005C8) +/* conf pin descriptions (am335x) */ +#define CONTROL_CONF_GPMC_AD0 (0x00000800) +#define CONTROL_CONF_GPMC_AD1 (0x00000804) +#define CONTROL_CONF_GPMC_AD2 (0x00000808) +#define CONTROL_CONF_GPMC_AD3 (0x0000080C) +#define CONTROL_CONF_GPMC_AD4 (0x00000810) +#define CONTROL_CONF_GPMC_AD5 (0x00000814) +#define CONTROL_CONF_GPMC_AD6 (0x00000818) +#define CONTROL_CONF_GPMC_AD7 (0x0000081C) +#define CONTROL_CONF_GPMC_AD8 (0x00000820) +#define CONTROL_CONF_GPMC_AD9 (0x00000824) +#define CONTROL_CONF_GPMC_AD10 (0x00000828) +#define CONTROL_CONF_GPMC_AD11 (0x0000082C) +#define CONTROL_CONF_GPMC_AD12 (0x00000830) +#define CONTROL_CONF_GPMC_AD13 (0x00000834) +#define CONTROL_CONF_GPMC_AD14 (0x00000838) +#define CONTROL_CONF_GPMC_AD15 (0x0000083C) +#define CONTROL_CONF_GPMC_A0 (0x00000840) +#define CONTROL_CONF_GPMC_A1 (0x00000844) +#define CONTROL_CONF_GPMC_A2 (0x00000848) +#define CONTROL_CONF_GPMC_A3 (0x0000084C) +#define CONTROL_CONF_GPMC_A4 (0x00000850) +#define CONTROL_CONF_GPMC_A5 (0x00000854) +#define CONTROL_CONF_GPMC_A6 (0x00000858) +#define CONTROL_CONF_GPMC_A7 (0x0000085C) +#define CONTROL_CONF_GPMC_A8 (0x00000860) +#define CONTROL_CONF_GPMC_A9 (0x00000864) +#define CONTROL_CONF_GPMC_A10 (0x00000868) +#define CONTROL_CONF_GPMC_A11 (0x0000086C) +#define CONTROL_CONF_GPMC_WAIT0 (0x00000870) +#define CONTROL_CONF_GPMC_WPN (0x00000874) +#define CONTROL_CONF_GPMC_BEN1 (0x00000878) +#define CONTROL_CONF_GPMC_CSN0 (0x0000087C) +#define CONTROL_CONF_GPMC_CSN1 (0x00000880) +#define CONTROL_CONF_GPMC_CSN2 (0x00000884) +#define CONTROL_CONF_GPMC_CSN3 (0x00000888) +#define CONTROL_CONF_GPMC_CLK (0x0000088C) +#define CONTROL_CONF_GPMC_ADVN_ALE (0x00000890) +#define CONTROL_CONF_GPMC_OEN_REN (0x00000894) +#define CONTROL_CONF_GPMC_WEN (0x00000898) +#define CONTROL_CONF_GPMC_BEN0_CLE (0x0000089C) +#define CONTROL_CONF_LCD_DATA0 (0x000008A0) +#define CONTROL_CONF_LCD_DATA1 (0x000008A4) +#define CONTROL_CONF_LCD_DATA2 (0x000008A8) +#define CONTROL_CONF_LCD_DATA3 (0x000008AC) +#define CONTROL_CONF_LCD_DATA4 (0x000008B0) +#define CONTROL_CONF_LCD_DATA5 (0x000008B4) +#define CONTROL_CONF_LCD_DATA6 (0x000008B8) +#define CONTROL_CONF_LCD_DATA7 (0x000008BC) +#define CONTROL_CONF_LCD_DATA8 (0x000008C0) +#define CONTROL_CONF_LCD_DATA9 (0x000008C4) +#define CONTROL_CONF_LCD_DATA10 (0x000008C8) +#define CONTROL_CONF_LCD_DATA11 (0x000008CC) +#define CONTROL_CONF_LCD_DATA12 (0x000008D0) +#define CONTROL_CONF_LCD_DATA13 (0x000008D4) +#define CONTROL_CONF_LCD_DATA14 (0x000008D8) +#define CONTROL_CONF_LCD_DATA15 (0x000008DC) +#define CONTROL_CONF_LCD_VSYNC (0x000008E0) +#define CONTROL_CONF_LCD_HSYNC (0x000008E4) +#define CONTROL_CONF_LCD_PCLK (0x000008E8) +#define CONTROL_CONF_LCD_AC_BIAS_EN (0x000008EC) +#define CONTROL_CONF_MMC0_DAT3 (0x000008F0) +#define CONTROL_CONF_MMC0_DAT2 (0x000008F4) +#define CONTROL_CONF_MMC0_DAT1 (0x000008F8) +#define CONTROL_CONF_MMC0_DAT0 (0x000008FC) +#define CONTROL_CONF_MMC0_CLK (0x00000900) +#define CONTROL_CONF_MMC0_CMD (0x00000904) +#define CONTROL_CONF_MII1_COL (0x00000908) +#define CONTROL_CONF_MII1_CRS (0x0000090C) +#define CONTROL_CONF_MII1_RX_ER (0x00000910) +#define CONTROL_CONF_MII1_TX_EN (0x00000914) +#define CONTROL_CONF_MII1_RX_DV (0x00000918) +#define CONTROL_CONF_MII1_TXD3 (0x0000091C) +#define CONTROL_CONF_MII1_TXD2 (0x00000920) +#define CONTROL_CONF_MII1_TXD1 (0x00000924) +#define CONTROL_CONF_MII1_TXD0 (0x00000928) +#define CONTROL_CONF_MII1_TX_CLK (0x0000092C) +#define CONTROL_CONF_MII1_RX_CLK (0x00000930) +#define CONTROL_CONF_MII1_RXD3 (0x00000934) +#define CONTROL_CONF_MII1_RXD2 (0x00000938) +#define CONTROL_CONF_MII1_RXD1 (0x0000093C) +#define CONTROL_CONF_MII1_RXD0 (0x00000940) +#define CONTROL_CONF_RMII1_REF_CLK (0x00000944) +#define CONTROL_CONF_MDIO (0x00000948) +#define CONTROL_CONF_MDC (0x0000094C) +#define CONTROL_CONF_SPI0_SCLK (0x00000950) +#define CONTROL_CONF_SPI0_D0 (0x00000954) +#define CONTROL_CONF_SPI0_D1 (0x00000958) +#define CONTROL_CONF_SPI0_CS0 (0x0000095C) +#define CONTROL_CONF_SPI0_CS1 (0x00000960) +#define CONTROL_CONF_ECAP0_IN_PWM0_OUT (0x00000964) +#define CONTROL_CONF_UART0_CTSN (0x00000968) +#define CONTROL_CONF_UART0_RTSN (0x0000096C) +#define CONTROL_CONF_UART0_RXD (0x00000970) +#define CONTROL_CONF_UART0_TXD (0x00000974) +#define CONTROL_CONF_UART1_CTSN (0x00000978) +#define CONTROL_CONF_UART1_RTSN (0x0000097C) +#define CONTROL_CONF_UART1_RXD (0x00000980) +#define CONTROL_CONF_UART1_TXD (0x00000984) +#define CONTROL_CONF_I2C0_SDA (0x00000988) +#define CONTROL_CONF_I2C0_SCL (0x0000098C) +#define CONTROL_CONF_MCASP0_ACLKX (0x00000990) +#define CONTROL_CONF_MCASP0_FSX (0x00000994) +#define CONTROL_CONF_MCASP0_AXR0 (0x00000998) +#define CONTROL_CONF_MCASP0_AHCLKR (0x0000099C) +#define CONTROL_CONF_MCASP0_ACLKR (0x000009A0) +#define CONTROL_CONF_MCASP0_FSR (0x000009A4) +#define CONTROL_CONF_MCASP0_AXR1 (0x000009A8) +#define CONTROL_CONF_MCASP0_AHCLKX (0x000009AC) +#define CONTROL_CONF_XDMA_EVENT_INTR0 (0x000009B0) +#define CONTROL_CONF_XDMA_EVENT_INTR1 (0x000009B4) +#define CONTROL_CONF_WARMRSTN (0x000009B8) +#define CONTROL_CONF_NNMI (0x000009C0) +#define CONTROL_CONF_TMS (0x000009D0) +#define CONTROL_CONF_TDI (0x000009D4) +#define CONTROL_CONF_TDO (0x000009D8) +#define CONTROL_CONF_TCK (0x000009DC) +#define CONTROL_CONF_TRSTN (0x000009E0) +#define CONTROL_CONF_EMU0 (0x000009E4) +#define CONTROL_CONF_EMU1 (0x000009E8) +#define CONTROL_CONF_RTC_PWRONRSTN (0x000009F8) +#define CONTROL_CONF_PMIC_POWER_EN (0x000009FC) +#define CONTROL_CONF_EXT_WAKEUP (0x00000A00) +#define CONTROL_CONF_RTC_KALDO_ENN (0x00000A04) +#define CONTROL_CONF_USB0_DRVVBUS (0x00000A1C) +#define CONTROL_CONF_USB1_DRVVBUS (0x00000A34) + +#define CONTROL_CONF_SLEWCTRL (1<<6) +#define CONTROL_CONF_RXACTIVE (1<<5) +#define CONTROL_CONF_PUTYPESEL (1<<4) +#define CONTROL_CONF_PUDEN (1<<3) +#define CONTROL_CONF_MUXMODE(X) (X&0x7) + int padconf_init(); int padconf_set(u32_t padconf, u32_t mask, u32_t value); int padconf_release(); diff --git a/lib/libc/sys-minix/ioctl.c b/lib/libc/sys-minix/ioctl.c index 0c1610a34..c61478473 100644 --- a/lib/libc/sys-minix/ioctl.c +++ b/lib/libc/sys-minix/ioctl.c @@ -3,20 +3,91 @@ #include #include +#include #ifdef __weak_alias __weak_alias(ioctl, _ioctl) #endif +static void rewrite_i2c_netbsd_to_minix(minix_i2c_ioctl_exec_t *out, + i2c_ioctl_exec_t *in); +static void rewrite_i2c_minix_to_netbsd(i2c_ioctl_exec_t *out, + minix_i2c_ioctl_exec_t *in); + +static void rewrite_i2c_netbsd_to_minix(minix_i2c_ioctl_exec_t *out, + i2c_ioctl_exec_t *in) +{ + memset(out, '\0', sizeof(minix_i2c_ioctl_exec_t)); + + out->iie_op = in->iie_op; + out->iie_addr = in->iie_addr; + out->iie_cmdlen = I2C_EXEC_MAX_CMDLEN < in->iie_cmdlen ? + I2C_EXEC_MAX_CMDLEN : in->iie_cmdlen; + out->iie_buflen = I2C_EXEC_MAX_BUFLEN < in->iie_buflen ? + I2C_EXEC_MAX_BUFLEN : in->iie_buflen; + + if (in->iie_cmdlen > 0 && in->iie_cmd != NULL) { + memcpy(out->iie_cmd, in->iie_cmd, in->iie_cmdlen); + } + + if (in->iie_buflen > 0 && in->iie_buf != NULL) { + memcpy(out->iie_buf, in->iie_buf, in->iie_buflen); + } +} + +static void rewrite_i2c_minix_to_netbsd(i2c_ioctl_exec_t *out, + minix_i2c_ioctl_exec_t *in) +{ + /* the only field that changes is iie_buf, everything else is the same */ + if (in->iie_buflen > 0 && in->iie_buf != NULL) { + memcpy(out->iie_buf, in->iie_buf, in->iie_buflen); + } +} + int ioctl(fd, request, data) int fd; int request; void *data; { + int r, request_save; message m; + void *addr; + + /* + * To support compatibility with interfaces on other systems, certain + * requests are re-written to flat structures (i.e. without pointers). + */ + minix_i2c_ioctl_exec_t i2c; + + request_save = request; + + switch (request) { + case I2C_IOCTL_EXEC: + rewrite_i2c_netbsd_to_minix(&i2c, data); + addr = (void *) &i2c; + request = MINIX_I2C_IOCTL_EXEC; + break; + default: + /* Keep original as-is */ + addr = (void *) data; + break; + } m.TTY_LINE = fd; m.TTY_REQUEST = request; - m.ADDRESS = (char *) data; - return(_syscall(VFS_PROC_NR, IOCTL, &m)); + m.ADDRESS = (char *) addr; + + r = _syscall(VFS_PROC_NR, IOCTL, &m); + + /* Translate back to original form */ + switch (request_save) { + case I2C_IOCTL_EXEC: + rewrite_i2c_minix_to_netbsd(data, &i2c); + break; + default: + /* Nothing to do */ + break; + } + + return r; } diff --git a/lib/libclkconf/clkconf.c b/lib/libclkconf/clkconf.c index e830c07f2..53f89f44d 100644 --- a/lib/libclkconf/clkconf.c +++ b/lib/libclkconf/clkconf.c @@ -24,7 +24,12 @@ static struct log log = { .log_func = default_log }; +#ifdef DM37XX #define CM_BASE 0x48004000 +#elif AM335X +#define CM_BASE 0x44E00000 +#endif + static u32_t base = 0; static u32_t use_count = 0; diff --git a/lib/libpadconf/padconf.c b/lib/libpadconf/padconf.c index e25de226a..ab013125d 100644 --- a/lib/libpadconf/padconf.c +++ b/lib/libpadconf/padconf.c @@ -65,7 +65,7 @@ padconf_init() int padconf_set(u32_t padconf, u32_t mask, u32_t value) { - assert(padconf <= CONTROL_PADCONF_ETK_D14); + assert(padconf <= CONTROL_CONF_USB1_DRVVBUS); set32(base + padconf, mask, value); return OK; }