]> Zhao Yanbai Git Server - minix.git/commitdiff
i2c: initial bus drivers for am335x and dm37xx 76/676/1
authorThomas Cort <tcort@minix3.org>
Mon, 15 Jul 2013 14:29:09 +0000 (10:29 -0400)
committerThomas Cort <tcort@minix3.org>
Mon, 15 Jul 2013 15:11:13 +0000 (11:11 -0400)
Change-Id: I478704fbf30dbf6d3382bcbfb11e75b512c032a1

22 files changed:
commands/MAKEDEV/MAKEDEV.sh
distrib/sets/lists/minix/md.evbarm
distrib/sets/lists/minix/mi
drivers/Makefile
drivers/i2c/Makefile [new file with mode: 0644]
drivers/i2c/README.txt [new file with mode: 0644]
drivers/i2c/arch/earm/Makefile.inc [new file with mode: 0644]
drivers/i2c/arch/earm/omap_i2c.c [new file with mode: 0644]
drivers/i2c/arch/earm/omap_i2c.h [new file with mode: 0644]
drivers/i2c/arch/earm/omap_i2c_registers.h [new file with mode: 0644]
drivers/i2c/i2c.c [new file with mode: 0644]
etc/system.conf
etc/usr/rc
include/minix/Makefile
include/minix/clkconf.h
include/minix/com.h
include/minix/dmap.h
include/minix/i2c.h [new file with mode: 0644]
include/minix/padconf.h
lib/libc/sys-minix/ioctl.c
lib/libclkconf/clkconf.c
lib/libpadconf/padconf.c

index d41ba53f25f247babf1c691606f4347ad8283f64..9ca26f8c29fb1709b45d1df05e46910e68762bc6 100644 (file)
@@ -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 <<EOF
@@ -31,6 +32,7 @@ Usage:        $0 [-n] key ...
 Where key is one of the following:
   ram mem kmem null boot zero    # One of these makes all these memory devices
   fb0                    # Make /dev/fb0
+  i2c-1 i2c-2 i2c-3       # Make /dev/i2c-[1-3]
   fd0 fd1 ...            # Floppy devices for drive 0, 1, ...
   fd0p0 fd1p0 ...        # Make floppy partitions fd0p[0-3], fd1p[0-3], ...
   c0d0 c0d1 ...                  # Make disks c0d0, c0d1, ...
@@ -284,6 +286,13 @@ do
        $e mknod fb0 c 19 0
        $e chmod 644 fb0
        ;;
+    i2c-[1-3])
+       # i2c driver
+       b=`expr $dev : '....\\(.*\\)'` # bus number
+       m=`expr $dev : '....\\(.*\\)' - 1` # least significant digit of major
+       $e mknod i2c-${b} c 2${m} 0
+       $e chmod 600 i2c-${b}
+       ;;
     *)
        echo "$0: don't know about $dev" >&2
        ex=1
index 6cb871d91184e6aa95f1f22abe633e3cb93632dd..7a63dee4624bdd528afecded940286d4c6334127 100644 (file)
 ./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
index bd30d1d18bcdd67fe8de36b74b442d45c8a4b11c..e4a0ce613af99f3500131d833a53e5d0d7780b13 100644 (file)
 ./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
index a5306585f37fb0ffaa04a243cf6ee757bf4a52e7..d0e2405a2791b5f47fbd882cea519617ea540735 100644 (file)
@@ -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 (file)
index 0000000..03df143
--- /dev/null
@@ -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 <minix.service.mk>
+.include <bsd.subdir.mk>
+
diff --git a/drivers/i2c/README.txt b/drivers/i2c/README.txt
new file mode 100644 (file)
index 0000000..e9a69de
--- /dev/null
@@ -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 (file)
index 0000000..497fd24
--- /dev/null
@@ -0,0 +1,7 @@
+# Makefile for arch-dependent i2c code
+.include <bsd.own.mk>
+
+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 (file)
index 0000000..664a841
--- /dev/null
@@ -0,0 +1,855 @@
+/*
+ * This file implements support for i2c on the BeagleBone and BeagleBoard-xM
+ */
+
+/* kernel headers */
+#include <minix/chardriver.h>
+#include <minix/clkconf.h>
+#include <minix/drivers.h>
+#include <minix/ds.h>
+#include <minix/log.h>
+#include <minix/mmio.h>
+#include <minix/padconf.h>
+#include <minix/sysutil.h>
+#include <minix/type.h>
+
+/* device headers */
+#include <minix/i2c.h>
+
+/* system headers */
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+/* usr headers */
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* 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<<v))
+#define reg_clear_bit(a,v) reg_write((a), reg_read((a)) & ~(1<<v))
+
+/*
+ * Performs the action in minix_i2c_ioctl_exec_t.
+ */
+static int
+omap_i2c_process(minix_i2c_ioctl_exec_t * ioctl_exec)
+{
+       int r;
+
+       /*
+        * Zero data bytes transfers are not allowed. The controller treats
+        * I2C_CNT register value of 0x0 as 65536. This is true for both the
+        * am335x and dm37xx. Full details in the TRM on the I2C_CNT page.
+        */
+       if (ioctl_exec->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 (file)
index 0000000..014df2c
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _OMAP_I2C_H
+#define _OMAP_I2C_H
+
+#include <minix/chardriver.h>
+#include <minix/i2c.h>
+#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 (file)
index 0000000..ce6932c
--- /dev/null
@@ -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 (file)
index 0000000..36d8af3
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ * i2c - generic driver for Inter-Integrated Circuit bus (I2C).
+ */
+
+/* kernel headers */
+#include <minix/chardriver.h>
+#include <minix/drivers.h>
+#include <minix/ds.h>
+#include <minix/i2c.h>
+#include <minix/log.h>
+#include <minix/type.h>
+
+/* system headers */
+#include <sys/mman.h>
+
+/* usr headers */
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* 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;
+}
index 59a2178cda22c29df8470822d3f6212dfaa9467a..6e111b368973b741224da7bbc83ecfe312739850 100644 (file)
@@ -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
index 6c14e3e5de503ac913ff34b53ca63d6b25f2b84a..21c2cd99c0108cb12e762fde9c24a23674b56a25 100644 (file)
@@ -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 ]
index 071d38770b7481b26fc18ff784cb219272e0dbb3..e1b65cc4f1664ef147a65cabdceece6d65fab9e6 100644 (file)
@@ -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 \
index deba8a2c2c1cf9e94f38731a3817af9adfaa2e54..dc66f4aac273467fdf18495a5c6117fb14828cb0 100644 (file)
@@ -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
index 59bab8112bae2f1cf66c4f79dcbfaee84ddb8819..4cabf816214818004a7900fdf76fbd1b8cd87c9f 100644 (file)
                                                         * 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                             *
index 6aac5804b7774d450d0f8e663855ea62cc4f6d04..71b30db53fba48e7eb7770e18d06a8a103691f58 100644 (file)
@@ -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 (file)
index 0000000..4aebb02
--- /dev/null
@@ -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 <minix/ioctl.h>
+#include <dev/i2c/i2c_io.h>
+
+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 */
index 8d8f7cc63287a215a20b64b2e6c2de00150f7242..486577b7f9d716c549590120a413e637cfe9fca6 100644 (file)
@@ -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] */
 #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();
index 0c1610a34cb08ae11e6c2d861879a5d93a20867a..c6147847320d204dfac68e8d57238bd11f8cb126 100644 (file)
@@ -3,20 +3,91 @@
 #include <lib.h>
 
 #include <sys/ioctl.h>
+#include <minix/i2c.h>
 
 #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;
 }
index e830c07f2d89bb9872ecabbcfe7787e92a625354..53f89f44df71697f75e0991996b0ed564e493283 100644 (file)
@@ -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;
 
index e25de226a0385d1049e505ac67b8ef29a3040f58..ab013125d2c8019b1b42be57568cfa66889552fd 100644 (file)
@@ -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;
 }