From 8e0800e99488179f2f0ebeceab7d7d55b15c22ff Mon Sep 17 00:00:00 2001 From: Korobov Nikita Date: Wed, 12 Jul 2017 23:59:13 +0300 Subject: [PATCH] [MAJOR] First commit for the RPI i2c driver This commit present Makefile for the several platforms. It doesn't work yet. --- distrib/sets/lists/minix-base/md.evbarm | 5 +- minix/drivers/bus/i2c/Makefile | 22 +- minix/drivers/bus/i2c/arch/earm/Makefile.inc | 11 - .../bus/i2c/arch/earm/{ => omap}/i2c.conf | 0 .../bus/i2c/arch/earm/{ => omap}/omap_i2c.c | 0 .../bus/i2c/arch/earm/{ => omap}/omap_i2c.h | 0 .../arch/earm/{ => omap}/omap_i2c_registers.h | 0 minix/drivers/bus/i2c/arch/earm/rpi/i2c.conf | 12 + minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c.c | 876 ++++++++++++++++++ minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c.h | 10 + .../bus/i2c/arch/earm/rpi/rpi_i2c_registers.h | 152 +++ minix/drivers/bus/i2c/arch/i386/Makefile.inc | 7 - minix/drivers/bus/i2c/i2c.c | 1 + 13 files changed, 1069 insertions(+), 27 deletions(-) delete mode 100644 minix/drivers/bus/i2c/arch/earm/Makefile.inc rename minix/drivers/bus/i2c/arch/earm/{ => omap}/i2c.conf (100%) rename minix/drivers/bus/i2c/arch/earm/{ => omap}/omap_i2c.c (100%) rename minix/drivers/bus/i2c/arch/earm/{ => omap}/omap_i2c.h (100%) rename minix/drivers/bus/i2c/arch/earm/{ => omap}/omap_i2c_registers.h (100%) create mode 100644 minix/drivers/bus/i2c/arch/earm/rpi/i2c.conf create mode 100644 minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c.c create mode 100644 minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c.h create mode 100644 minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c_registers.h delete mode 100644 minix/drivers/bus/i2c/arch/i386/Makefile.inc diff --git a/distrib/sets/lists/minix-base/md.evbarm b/distrib/sets/lists/minix-base/md.evbarm index b4a021b39..b01b7608a 100644 --- a/distrib/sets/lists/minix-base/md.evbarm +++ b/distrib/sets/lists/minix-base/md.evbarm @@ -9,9 +9,7 @@ ./etc/rc.capes/BB-BONE-WTHR-01 minix-base ./etc/system.conf.d/bmp085 minix-base ./etc/system.conf.d/gpio minix-base -./etc/system.conf.d/i2c minix-base ./etc/system.conf.d/lan8710a minix-base -./etc/system.conf.d/mailbox minix-base ./etc/system.conf.d/sht21 minix-base ./etc/system.conf.d/tsl2550 minix-base ./etc/system.conf.d/usbd minix-base @@ -19,11 +17,12 @@ ./service/cat24c256 minix-base ./service/fb minix-base ./service/gpio minix-base -./service/i2c minix-base ./service/lan8710a minix-base ./service/mailbox minix-base ./service/omap_emmc minix-base +./service/i2c_omap minix-base ./service/omap_mmc minix-base +./service/i2c_rpi minix-base ./service/rpi_mmc minix-base ./service/random minix-base ./service/sht21 minix-base diff --git a/minix/drivers/bus/i2c/Makefile b/minix/drivers/bus/i2c/Makefile index 4bda6f01b..4f9302479 100644 --- a/minix/drivers/bus/i2c/Makefile +++ b/minix/drivers/bus/i2c/Makefile @@ -1,13 +1,23 @@ # Makefile for I2C support -PROG= i2c -.include "arch/${MACHINE_ARCH}/Makefile.inc" - -SRCS+= i2c.c +.if ${MACHINE_ARCH} == "earm" +.for pl in rpi omap +.PATH: ${.CURDIR}/arch/earm/${pl} +PROGS+= i2c_${pl} +SRCS.i2c_${pl}+= i2c.c ${pl}_i2c.c ${pl}_i2c.h ${pl}_i2c_registers.h +FILES.i2c_${pl}=i2c_${pl}.conf +FILESNAME=i2c_${pl} +.endfor +.else +.PATH: ${.CURDIR}/arch/${MACHINE_ARCH} +SRCS.i2c += pci_i2c.c pci_i2c.h pci_i2c_register.h +.endif +FILESDIR= /etc/system.conf.d +CPPFLAGS+= -I${.CURDIR} -I${.CURDIR}/arch/earm/rpi -I${.CURDIR}/arch/earm/omap DPADD+= ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS} ${LIBCLKCONF} LDADD+= -lchardriver -lsys -ltimers -lclkconf -CPPFLAGS+= -I${.CURDIR} -I${.CURDIR}/arch/${MACHINE_ARCH} +MKMAN:= no -.include +.include \ No newline at end of file diff --git a/minix/drivers/bus/i2c/arch/earm/Makefile.inc b/minix/drivers/bus/i2c/arch/earm/Makefile.inc deleted file mode 100644 index 50a28e4d5..000000000 --- a/minix/drivers/bus/i2c/arch/earm/Makefile.inc +++ /dev/null @@ -1,11 +0,0 @@ -# Makefile for arch-dependent i2c code -.include - -HERE=${.CURDIR}/arch/${MACHINE_ARCH} -.PATH: ${HERE} - -SRCS += omap_i2c.c omap_i2c.h omap_i2c_registers.h - -FILES=${PROG}.conf -FILESNAME=${PROG} -FILESDIR= /etc/system.conf.d diff --git a/minix/drivers/bus/i2c/arch/earm/i2c.conf b/minix/drivers/bus/i2c/arch/earm/omap/i2c.conf similarity index 100% rename from minix/drivers/bus/i2c/arch/earm/i2c.conf rename to minix/drivers/bus/i2c/arch/earm/omap/i2c.conf diff --git a/minix/drivers/bus/i2c/arch/earm/omap_i2c.c b/minix/drivers/bus/i2c/arch/earm/omap/omap_i2c.c similarity index 100% rename from minix/drivers/bus/i2c/arch/earm/omap_i2c.c rename to minix/drivers/bus/i2c/arch/earm/omap/omap_i2c.c diff --git a/minix/drivers/bus/i2c/arch/earm/omap_i2c.h b/minix/drivers/bus/i2c/arch/earm/omap/omap_i2c.h similarity index 100% rename from minix/drivers/bus/i2c/arch/earm/omap_i2c.h rename to minix/drivers/bus/i2c/arch/earm/omap/omap_i2c.h diff --git a/minix/drivers/bus/i2c/arch/earm/omap_i2c_registers.h b/minix/drivers/bus/i2c/arch/earm/omap/omap_i2c_registers.h similarity index 100% rename from minix/drivers/bus/i2c/arch/earm/omap_i2c_registers.h rename to minix/drivers/bus/i2c/arch/earm/omap/omap_i2c_registers.h diff --git a/minix/drivers/bus/i2c/arch/earm/rpi/i2c.conf b/minix/drivers/bus/i2c/arch/earm/rpi/i2c.conf new file mode 100644 index 000000000..441ed4443 --- /dev/null +++ b/minix/drivers/bus/i2c/arch/earm/rpi/i2c.conf @@ -0,0 +1,12 @@ +service rpi_i2c +{ + system + PRIVCTL # 4 + IRQCTL # 19 + PADCONF # 57 + ; + irq + ; + ipc SYSTEM RS DS; +}; + diff --git a/minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c.c b/minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c.c new file mode 100644 index 000000000..a67cabe89 --- /dev/null +++ b/minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c.c @@ -0,0 +1,876 @@ +/* + * This file implements support for i2c on the BeagleBone and BeagleBoard-xM + */ + +/* kernel headers */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* device headers */ +#include + +/* system headers */ +#include +#include +#include + +/* usr headers */ +#include +#include +#include + +/* local headers */ +#include "rpi_i2c.h" + +/* + * defines the set of register + * + * Warning: always use the 16-bit variants of read/write/set from mmio.h + * to access these registers. The DM37XX TRM Section 17.6 warns that 32-bit + * accesses can corrupt the register contents. + */ +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_I2C_BUS, DM37XX_I2C_BUS} 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_I2C_BUS, 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_I2C_BUS, 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_I2C_BUS, 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_I2C_BUS, 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_I2C_BUS, 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_I2C_BUS, 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_bus_is_free(void); +static int omap_i2c_soft_reset(void); +static void omap_i2c_bus_init(void); +static void omap_i2c_padconf(int i2c_bus_id); +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); + +/* + * 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_bus_is_free(); + if (r == 0) { + log_warn(&log, "Bus is busy\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; + } + + 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) read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_DATA); + + /* clear the read ready flag */ + omap_i2c_write_status(1 << RRDY); + + } 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) +{ + spin_t spin; + uint16_t status; + + /* poll for up to 1 s */ + spin_init(&spin, 1000000); + do { + status = omap_i2c_read_status(); + if ((status & mask) != 0) { /* any bits in mask set */ + return status; + } + + } while (spin_check(&spin)); + + return status; /* timeout reached, abort */ +} + +/* + * Poll Bus Busy Flag until the bus becomes free (return 1) or the timeout + * expires (return 0). + */ +static int +omap_i2c_bus_is_free(void) +{ + spin_t spin; + uint16_t status; + + /* wait for up to 1 second for the bus to become free */ + spin_init(&spin, 1000000); + do { + + status = omap_i2c_read_status(); + if ((status & (1 << BB)) == 0) { + return 1; /* bus is free */ + } + + } while (spin_check(&spin)); + + return 0; /* timeout expired */ +} + +static void +omap_i2c_clkconf(int i2c_bus_id) +{ + clkconf_init(); + + if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { + + 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_I2C_BUS) { + + 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(); +} + +static void +omap_i2c_padconf(int i2c_bus_id) +{ + int r; + u32_t pinopts; + + if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { + + /* 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); + + r = sys_padconf(CONTROL_CONF_I2C0_SDA, 0xffffffff, + pinopts); + if (r != OK) { + log_warn(&log, "padconf failed (r=%d)\n", r); + } + + r = sys_padconf(CONTROL_CONF_I2C0_SCL, 0xffffffff, + pinopts); + if (r != OK) { + log_warn(&log, "padconf failed (r=%d)\n", r); + } + + log_debug(&log, "pinopts=0x%x\n", pinopts); + break; + + case 1: + pinopts |= CONTROL_CONF_MUXMODE(2); + + r = sys_padconf(CONTROL_CONF_SPI0_CS0, 0xffffffff, + pinopts); + if (r != OK) { + log_warn(&log, "padconf failed (r=%d)\n", r); + } + + r = sys_padconf(CONTROL_CONF_SPI0_D1, 0xffffffff, + pinopts); + if (r != OK) { + log_warn(&log, "padconf failed (r=%d)\n", r); + } + log_debug(&log, "pinopts=0x%x\n", pinopts); + break; + + case 2: + pinopts |= CONTROL_CONF_MUXMODE(3); + + r = sys_padconf(CONTROL_CONF_UART1_CTSN, 0xffffffff, + pinopts); + if (r != OK) { + log_warn(&log, "padconf failed (r=%d)\n", r); + } + + r = sys_padconf(CONTROL_CONF_UART1_RTSN, + 0xffffffff, pinopts); + if (r != OK) { + log_warn(&log, "padconf failed (r=%d)\n", r); + } + + log_debug(&log, "pinopts=0x%x\n", pinopts); + break; + + default: + log_warn(&log, "Invalid i2c_bus_id\n"); + break; + } + } + + /* nothing to do for the DM37XX */ +} + +static int +omap_i2c_soft_reset(void) +{ + spin_t spin; + + /* Disable to do soft reset */ + write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, 0); + micro_delay(50000); + + /* Do a soft reset */ + write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SYSC, (1 << SRST)); + + /* Have to temporarily enable I2C to read RDONE */ + set16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, (1<mapped_addr + omap_i2c_bus->regs->I2C_SYSS) & (1 << RDONE)) { + return OK; + } + + } while (spin_check(&spin)); + + 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_I2C_BUS) { + write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_IRQENABLE_SET, intmask); + } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { + write16(omap_i2c_bus->mapped_addr + 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 */ + write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, 0); + micro_delay(50000); + + /* Disable autoidle */ + set16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SYSC, (1<mapped_addr + omap_i2c_bus->regs->I2C_PSC, + ((omap_i2c_bus->functional_clock / omap_i2c_bus->module_clock) - + 1)); + + /* Set the bus speed */ + write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SCLL, + ((omap_i2c_bus->module_clock / (2 * omap_i2c_bus->bus_speed)) - + 7)); + write16(omap_i2c_bus->mapped_addr + 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_I2C_BUS) { + write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_OA, I2C_OWN_ADDRESS); + } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { + write16(omap_i2c_bus->mapped_addr + 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 */ + write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_BUF, 0x0000); + + /* Bring the i2c module out of reset */ + set16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, (1<bus_type == AM335X_I2C_BUS) { + /* TRM says to use RAW for polling for events */ + status = read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_IRQSTATUS_RAW); + } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { + status = read16(omap_i2c_bus->mapped_addr + 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_I2C_BUS) { + /* write 1's to IRQSTATUS (not RAW) to clear the bits */ + write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_IRQSTATUS, mask); + } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { + write16(omap_i2c_bus->mapped_addr + 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; + uint16_t pollmask; + uint16_t errmask; + + /* Set address of slave device */ + conopts = 0; + addr &= MAX_I2C_SA_MASK; /* sanitize address (10-bit max) */ + if (addr > 0x7f) { + /* 10-bit extended address in use, need to set XSA */ + conopts |= (1 << XSA); + } + + errmask = 0; + errmask |= (1 << ROVR); + errmask |= (1 << AERR); + errmask |= (1 << NACK); + errmask |= (1 << AL); + + pollmask = 0; + pollmask |= (1 << RRDY); + + /* Set bytes to read and slave address */ + write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CNT, buflen); + write16(omap_i2c_bus->mapped_addr + 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 */ + } + + write16(omap_i2c_bus->mapped_addr + 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] = read16(omap_i2c_bus->mapped_addr + 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; + } + + /* Wait for operation to complete */ + pollmask = (1< 0x7f) { + /* 10-bit extended address in use, need to set XSA */ + conopts |= (1 << XSA); + } + + pollmask = 0; + pollmask |= (1 << XRDY); + + errmask = 0; + errmask |= (1 << ROVR); + errmask |= (1 << AERR); + errmask |= (1 << NACK); + errmask |= (1 << AL); + + write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CNT, buflen); + write16(omap_i2c_bus->mapped_addr + 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); + write16(omap_i2c_bus->mapped_addr + 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; + } + + write16(omap_i2c_bus->mapped_addr + 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) { + log_warn(&log, "NACK\n"); + return EIO; + } + + /* Wait for operation to complete */ + pollmask = (1<= omap_i2c_nbuses) { + return EINVAL; + } + + /* select the bus to operate on */ + omap_i2c_bus = &omap_i2c_buses[i2c_bus_id]; + + /* Configure Pins */ + omap_i2c_padconf(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); + + /* 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_BUS) { + /* I2C_REVLO revision: major (bits 10-8), minor (bits 5-0) */ + i2c_rev = read16(omap_i2c_bus->mapped_addr + 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_BUS) { + /* I2C_REV revision: major (bits 7-4), minor (bits 3-0) */ + i2c_rev = read16(omap_i2c_bus->mapped_addr + 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/minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c.h b/minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c.h new file mode 100644 index 000000000..70e56b432 --- /dev/null +++ b/minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c.h @@ -0,0 +1,10 @@ +#ifndef _RPI_I2C_H +#define _RPI_I2C_H + +#include +#include +#include "rpi_i2c_registers.h" + +int omap_interface_setup(int (**process)(minix_i2c_ioctl_exec_t *ioctl_exec), int i2c_bus_id); + +#endif /* _RPI_I2C_H */ diff --git a/minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c_registers.h b/minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c_registers.h new file mode 100644 index 000000000..fa4be6f16 --- /dev/null +++ b/minix/drivers/bus/i2c/arch/earm/rpi/rpi_i2c_registers.h @@ -0,0 +1,152 @@ +#ifndef _RPI_I2C_REGISTERS_H +#define _RPI_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 MAX_I2C_SA_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 /* _RPI_I2C_REGISTERS_H */ diff --git a/minix/drivers/bus/i2c/arch/i386/Makefile.inc b/minix/drivers/bus/i2c/arch/i386/Makefile.inc deleted file mode 100644 index 9138888db..000000000 --- a/minix/drivers/bus/i2c/arch/i386/Makefile.inc +++ /dev/null @@ -1,7 +0,0 @@ -# Makefile for arch-dependent i2c code -.include - -HERE=${.CURDIR}/arch/${MACHINE_ARCH} -.PATH: ${HERE} - -SRCS += pci_i2c.c pci_i2c.h pci_i2c_register.h diff --git a/minix/drivers/bus/i2c/i2c.c b/minix/drivers/bus/i2c/i2c.c index 3714d8a42..58371c7b1 100644 --- a/minix/drivers/bus/i2c/i2c.c +++ b/minix/drivers/bus/i2c/i2c.c @@ -21,6 +21,7 @@ /* SoC specific headers - 1 for each SoC */ #include "omap_i2c.h" +#include "rpi_i2c.h" /* local definitions */