i2c: initial bus drivers for am335x and dm37xx
Change-Id: I478704fbf30dbf6d3382bcbfb11e75b512c032a1
This commit is contained in:
parent
2dab6a5384
commit
550fdfb443
22 changed files with 1968 additions and 7 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
20
drivers/i2c/Makefile
Normal file
20
drivers/i2c/Makefile
Normal file
|
@ -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>
|
||||
|
64
drivers/i2c/README.txt
Normal file
64
drivers/i2c/README.txt
Normal file
|
@ -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.
|
7
drivers/i2c/arch/earm/Makefile.inc
Normal file
7
drivers/i2c/arch/earm/Makefile.inc
Normal file
|
@ -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
|
855
drivers/i2c/arch/earm/omap_i2c.c
Normal file
855
drivers/i2c/arch/earm/omap_i2c.c
Normal file
|
@ -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;
|
||||
}
|
10
drivers/i2c/arch/earm/omap_i2c.h
Normal file
10
drivers/i2c/arch/earm/omap_i2c.h
Normal file
|
@ -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 */
|
152
drivers/i2c/arch/earm/omap_i2c_registers.h
Normal file
152
drivers/i2c/arch/earm/omap_i2c_registers.h
Normal file
|
@ -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 */
|
555
drivers/i2c/i2c.c
Normal file
555
drivers/i2c/i2c.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
13
etc/usr/rc
13
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 ]
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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. */
|
||||
|
|
26
include/minix/i2c.h
Normal file
26
include/minix/i2c.h
Normal file
|
@ -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 */
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue