summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2007-12-05 15:49:40 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2007-12-05 15:49:40 +0000
commit1b0c3e6cffc89c0e50977a3e6cbbe6fb404af0e4 (patch)
tree29613bd032f5967bee9f14e4323cedeeb8db0d0f
parent2007-12-05 Daniel Hellstrom <daniel@gaisler.com> (diff)
downloadrtems-1b0c3e6cffc89c0e50977a3e6cbbe6fb404af0e4.tar.bz2
2007-12-05 Daniel Hellstrom <daniel@gaisler.com>
* Makefile.am: i2c should be on branch as well as CVS head. * shared/i2c/i2cmst.c, shared/include/i2cmst.h: New files.
-rw-r--r--c/src/lib/libbsp/sparc/ChangeLog5
-rw-r--r--c/src/lib/libbsp/sparc/Makefile.am4
-rw-r--r--c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c358
-rw-r--r--c/src/lib/libbsp/sparc/shared/include/i2cmst.h77
4 files changed, 444 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/sparc/ChangeLog b/c/src/lib/libbsp/sparc/ChangeLog
index 9d9eefd69e..4ca2766dec 100644
--- a/c/src/lib/libbsp/sparc/ChangeLog
+++ b/c/src/lib/libbsp/sparc/ChangeLog
@@ -1,5 +1,10 @@
2007-12-05 Daniel Hellstrom <daniel@gaisler.com>
+ * Makefile.am: i2c should be on branch as well as CVS head.
+ * shared/i2c/i2cmst.c, shared/include/i2cmst.h: New files.
+
+2007-12-05 Daniel Hellstrom <daniel@gaisler.com>
+
* shared/include/ambapp.h: Sync head and 4.8 branch.
2007-11-30 Daniel Hellstrom <daniel@gaisler.com>
diff --git a/c/src/lib/libbsp/sparc/Makefile.am b/c/src/lib/libbsp/sparc/Makefile.am
index 3f2bfff9a4..8081d61609 100644
--- a/c/src/lib/libbsp/sparc/Makefile.am
+++ b/c/src/lib/libbsp/sparc/Makefile.am
@@ -59,5 +59,9 @@ EXTRA_DIST += shared/include/b1553brm.h
EXTRA_DIST += shared/include/b1553brm_pci.h
EXTRA_DIST += shared/include/b1553brm_rasta.h
+# I2C-master (I2CMST)
+EXTRA_DIST += shared/i2c/i2cmst.c
+EXTRA_DIST += shared/include/i2cmst.h
+
include $(top_srcdir)/../../../automake/subdirs.am
include $(top_srcdir)/../../../automake/local.am
diff --git a/c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c b/c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c
new file mode 100644
index 0000000000..2a59d32879
--- /dev/null
+++ b/c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c
@@ -0,0 +1,358 @@
+/*
+ * Driver for GRLIB port of OpenCores I2C-master
+ *
+ * COPYRIGHT (c) 2007 Gaisler Research
+ * based on the RTEMS MPC83xx I2C driver (c) 2007 Embedded Brains GmbH.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ *
+ * This file contains the driver and initialization code
+ *
+ * 2007-09-27: First version of driver (jan@gaisler.com)
+ */
+
+
+#include <bsp.h>
+#include <i2cmst.h>
+#include <ambapp.h>
+#include <rtems/libi2c.h>
+
+/* Enable debug printks? */
+/* #define DEBUG */
+
+/* Default to 40 MHz system clock? */
+/*
+ #ifndef SYS_FREQ_kHZ
+ #define SYS_FREQ_kHZ 40000
+ #endif
+*/
+
+
+/* Calculates the scaler value for 100 kHz operation */
+static int gr_i2cmst_calc_scaler(int sysfreq)
+{
+ return sysfreq/500 - 1;
+}
+
+/* Wait for the current transfer to end */
+static int gr_i2cmst_wait(gr_i2cmst_prv_t *prv_ptr, uint8_t expected_sts)
+{
+ uint32_t tout = 0;
+ int current_sts;
+#if defined(DEBUG)
+ printk("(gr_i2cmst_wait called...");
+#endif
+
+ do {
+ if (tout++ > 1000000) {
+ return RTEMS_TIMEOUT;
+ }
+ } while (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_TIP);
+
+ current_sts = prv_ptr->reg_ptr->cmdsts & ~GRI2C_STS_IF & ~GRI2C_STS_BUSY;
+
+ if (current_sts != expected_sts) {
+#if defined(DEBUG)
+ if (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_RXACK) {
+ printk("Transfer NAKed..");
+ }
+ if (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_AL) {
+ printk("arbitration lost..");
+ }
+ if (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_TIP) {
+ printk("transfer still in progress, huh?..");
+ }
+ printk("exited with IO error..)");
+#endif
+ return RTEMS_IO_ERROR;
+ }
+
+#if defined(DEBUG)
+ printk("exited...)");
+#endif
+ return RTEMS_SUCCESSFUL;
+}
+
+/* Initialize hardware core */
+static rtems_status_code gr_i2cmst_init(rtems_libi2c_bus_t *bushdl)
+{
+ gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
+#if defined(DEBUG)
+ printk("gr_i2cmst_init called...");
+#endif
+
+ /* Disable core before changing prescale register */
+ prv_ptr->reg_ptr->ctrl = 0;
+
+ /* Calculate and set prescale value */
+ prv_ptr->reg_ptr->prescl = gr_i2cmst_calc_scaler(prv_ptr->sysfreq);
+
+ /* Enable core, interrupts are not enabled */
+ prv_ptr->reg_ptr->ctrl = GRI2C_CTRL_EN;
+
+ /* Clear possible START condition */
+ prv_ptr->sendstart = 0;
+
+#if defined(DEBUG)
+ printk("exited\n");
+#endif
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code gr_i2cmst_send_start(rtems_libi2c_bus_t *bushdl)
+{
+ gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
+#if defined(DEBUG)
+ printk("gr_i2cmst_send_start called...");
+#endif
+
+ /* The OC I2C core does not work with stand alone START events,
+ instead the event is buffered */
+ prv_ptr->sendstart = GRI2C_CMD_STA;
+
+#if defined(DEBUG)
+ printk("exited\n");
+#endif
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code gr_i2cmst_send_stop(rtems_libi2c_bus_t *bushdl)
+{
+ gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
+#if defined(DEBUG)
+ printk("gr_i2cmst_send_stop called...");
+#endif
+
+ prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_STO;
+
+#if defined(DEBUG)
+ printk("exited\n");
+#endif
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code gr_i2cmst_send_addr(rtems_libi2c_bus_t *bushdl,
+ uint32_t addr, int rw)
+{
+ gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
+ uint8_t addr_byte;
+ rtems_status_code rc;
+#if defined(DEBUG)
+ printk("gr_i2cmst_send_addr called, addr = 0x%x, rw = %d...",
+ addr, rw);
+#endif
+
+ /* Check if long address is needed */
+ if (addr > 0x7f) {
+ addr_byte = ((addr >> 7) & 0x06) | (rw ? 1 : 0);
+
+ prv_ptr->reg_ptr->tdrd = addr_byte;
+ prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_WR | prv_ptr->sendstart;
+ prv_ptr->sendstart = 0;
+
+ /* Wait for transfer to complete */
+ rc = gr_i2cmst_wait(prv_ptr, GRI2C_STATUS_IDLE);
+ if (rc != RTEMS_SUCCESSFUL) {
+#if defined(DEBUG)
+ printk("exited with error\n");
+#endif
+ return -rc;
+ }
+ }
+
+ /* For 10-bit adresses the last byte should only be written for a
+ write operation */
+ rc = RTEMS_SUCCESSFUL;
+ if (addr <= 0x7f || rw == 0) {
+ addr_byte = (addr << 1) | (rw ? 1 : 0);
+
+ prv_ptr->reg_ptr->tdrd = addr_byte;
+ prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_WR | prv_ptr->sendstart;
+ prv_ptr->sendstart = 0;
+
+ /* Wait for transfer to complete */
+ rc = gr_i2cmst_wait(prv_ptr, GRI2C_STATUS_IDLE);
+ if (rc != RTEMS_SUCCESSFUL) {
+#if defined(DEBUG)
+ printk("exited with error\n");
+#endif
+ return -rc;
+ }
+ }
+
+#if defined(DEBUG)
+ printk("exited\n");
+#endif
+ return rc;
+}
+
+
+static int gr_i2cmst_read_bytes(rtems_libi2c_bus_t *bushdl,
+ unsigned char *bytes, int nbytes)
+{
+ gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
+ unsigned char *buf = bytes;
+ rtems_status_code rc;
+ unsigned char expected_sts = GRI2C_STATUS_IDLE;
+#if defined(DEBUG)
+ printk("gr_i2cmst_read_bytes called, nbytes = %d...", nbytes);
+#endif
+
+ while (nbytes-- > 0) {
+ if (nbytes == 0) {
+ /* Respond with NAK to end sequential read */
+ prv_ptr->reg_ptr->cmdsts = (GRI2C_CMD_RD | GRI2C_CMD_ACK |
+ prv_ptr->sendstart);
+ expected_sts = GRI2C_STS_RXACK;
+ } else {
+ prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_RD | prv_ptr->sendstart;
+ }
+ prv_ptr->sendstart = 0;
+ /* Wait until end of transfer */
+ rc = gr_i2cmst_wait(prv_ptr, expected_sts);
+ if (rc != RTEMS_SUCCESSFUL) {
+ return -rc;
+#if defined(DEBUG)
+ printk("exited with error\n");
+#endif
+ }
+ *buf++ = prv_ptr->reg_ptr->tdrd;
+ }
+
+#if defined(DEBUG)
+ printk("exited\n");
+#endif
+ return buf - bytes;
+}
+
+static int gr_i2cmst_write_bytes(rtems_libi2c_bus_t *bushdl,
+ unsigned char *bytes, int nbytes)
+{
+ gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
+ unsigned char *buf = bytes;
+ rtems_status_code rc;
+#if defined(DEBUG)
+ printk("gr_i2cmst_write_bytes called, nbytes = %d...", nbytes);
+#endif
+
+ while (nbytes-- > 0) {
+#if defined(DEBUG)
+ printk("writing byte 0x%02X...", *buf);
+#endif
+ prv_ptr->reg_ptr->tdrd = *buf++;
+ prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_WR | prv_ptr->sendstart;
+ prv_ptr->sendstart = 0;
+
+ /* Wait for transfer to complete */
+ rc = gr_i2cmst_wait(prv_ptr, GRI2C_STATUS_IDLE);
+
+ if (rc != RTEMS_SUCCESSFUL) {
+#if defined(DEBUG)
+ printk("exited with error\n");
+#endif
+ return -rc;
+ }
+ }
+
+#if defined(DEBUG)
+ printk("exited\n");
+#endif
+ return buf - bytes;
+}
+
+static rtems_libi2c_bus_ops_t gr_i2cmst_ops = {
+ init: gr_i2cmst_init,
+ send_start: gr_i2cmst_send_start,
+ send_stop: gr_i2cmst_send_stop,
+ send_addr: gr_i2cmst_send_addr,
+ read_bytes: gr_i2cmst_read_bytes,
+ write_bytes: gr_i2cmst_write_bytes,
+};
+
+
+static gr_i2cmst_desc_t gr_i2cmst_desc = {
+ { /* rtems_libi2c_bus_t */
+ ops : &gr_i2cmst_ops,
+ size : sizeof(gr_i2cmst_ops),
+ },
+ { /* gr_i2cmst_prv_t, private data */
+ reg_ptr : NULL,
+ sysfreq : 40000,
+ }
+
+};
+
+/* Scans for I2CMST core and initalizes i2c library */
+rtems_status_code leon_register_i2c(amba_confarea_type *abus)
+{
+#if defined(DEBUG)
+ printk("leon_register_i2c called...");
+#endif
+
+ int rc;
+ int device_found = 0;
+ amba_apb_device apbi2cmst;
+
+ /* Scan AMBA bus for I2CMST core */
+ device_found = amba_find_apbslv(abus, VENDOR_GAISLER, GAISLER_I2CMST,
+ &apbi2cmst);
+
+ if (device_found == 1) {
+
+ /* Initialize i2c library */
+ rc = rtems_libi2c_initialize();
+ if (rc < 0) {
+#if defined(DEBUG)
+ printk("rtems_libi2x_initialize failed, exiting...\n");
+#endif
+ return rc;
+ }
+
+ gr_i2cmst_desc.prv.reg_ptr = (gr_i2cmst_regs_t *)apbi2cmst.start;
+
+ /* Detect system frequency, same as in apbuart_initialize */
+#ifndef SYS_FREQ_kHZ
+#if defined(LEON3)
+ /* LEON3: find timer address via AMBA Plug&Play info */
+ {
+ amba_apb_device gptimer;
+ LEON3_Timer_Regs_Map *tregs;
+
+ if (amba_find_apbslv(abus,VENDOR_GAISLER,
+ GAISLER_GPTIMER,&gptimer) == 1 ) {
+ tregs = (LEON3_Timer_Regs_Map *)gptimer.start;
+ gr_i2cmst_desc.prv.sysfreq = (tregs->scaler_reload+1)*1000;
+ } else {
+ gr_i2cmst_desc.prv.sysfreq = 40000; /* Default to 40MHz */
+ }
+ }
+#elif defined(LEON2)
+ /* LEON2: use hardcoded address to get to timer */
+ {
+ LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000;
+ gr_i2cmst_desc.prv.sysfreq = (regs->Scaler_Reload+1)*1000;
+ }
+#else
+#error CPU not supported for I2CMST driver */
+#endif
+#else
+ /* Use hardcoded frequency */
+ gr_i2cmst_desc.prv.sysfreq = SYS_FREQ_kHZ;
+#endif
+
+ rc = rtems_libi2c_register_bus("/dev/i2c1", &gr_i2cmst_desc.bus_desc);
+ if (rc < 0) {
+#if defined(DEBUG)
+ printk("rtems_libi2c_register_bus failed, exiting..\n");
+#endif
+ return -rc;
+ }
+ }
+
+#if defined(DEBUG)
+ printk("exited\n");
+#endif
+ return 0;
+}
diff --git a/c/src/lib/libbsp/sparc/shared/include/i2cmst.h b/c/src/lib/libbsp/sparc/shared/include/i2cmst.h
new file mode 100644
index 0000000000..63942a45e6
--- /dev/null
+++ b/c/src/lib/libbsp/sparc/shared/include/i2cmst.h
@@ -0,0 +1,77 @@
+/*
+ * Driver for GRLIB port of OpenCores I2C-master
+ *
+ * COPYRIGHT (c) 2007 Gaisler Research
+ * with parts from the RTEMS MPC83xx I2C driver (c) 2007 Embedded Brains GmbH.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ *
+ * This file contains the driver declarations
+ */
+#ifndef _I2CMST_H
+#define _I2CMST_H
+
+#include <rtems/libi2c.h>
+#include <ambapp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* I2C-master operational registers */
+
+typedef struct gr_i2cmst_regs {
+ volatile unsigned int prescl; /* Prescale register */
+ volatile unsigned int ctrl; /* Control register */
+ volatile unsigned int tdrd; /* Transmit and Receive registers */
+ volatile unsigned int cmdsts; /* Command and Status registers */
+} gr_i2cmst_regs_t;
+
+/* Control (CTRL) register */
+#define GRI2C_CTRL_EN 0x00000080 /* Enable core */
+#define GRI2C_CTRL_IEN 0x00000040 /* Interrupt enable */
+
+/* Command (CMD) register */
+#define GRI2C_CMD_STA 0x00000080 /* Generate START condition */
+#define GRI2C_CMD_STO 0x00000040 /* Generate STOP condition */
+#define GRI2C_CMD_RD 0x00000020 /* Read from slave */
+#define GRI2C_CMD_WR 0x00000010 /* Write to slave */
+#define GRI2C_CMD_ACK 0x00000008 /* Acknowledge */
+#define GRI2C_CMD_IACK 0x00000001 /* Interrupt acknowledge */
+
+/* Status (STS) register */
+#define GRI2C_STS_RXACK 0x00000080 /* Receive acknowledge */
+#define GRI2C_STS_BUSY 0x00000040 /* I2C-bus busy */
+#define GRI2C_STS_AL 0x00000020 /* Arbitration lost */
+#define GRI2C_STS_TIP 0x00000002 /* Transfer in progress */
+#define GRI2C_STS_IF 0x00000001 /* Interrupt flag */
+
+#define GRI2C_STATUS_IDLE 0x00000000
+
+/* The OC I2C core will perform a write after a start unless the RD bit
+ in the command register has been set. Since the rtems framework has
+ a send_start function we buffer that command and use it when the first
+ data is written. The START is buffered in the sendstart member below */
+typedef struct gr_i2cmst_prv {
+ gr_i2cmst_regs_t *reg_ptr;
+ unsigned int sysfreq; /* System clock frequency in kHz */
+ unsigned char sendstart; /* START events are buffered here */
+ /* rtems_irq_number irq_number; */
+ /* rtems_id irq_sema_id; */
+} gr_i2cmst_prv_t;
+
+typedef struct gr_i2cmst_desc {
+ rtems_libi2c_bus_t bus_desc;
+ gr_i2cmst_prv_t prv;
+} gr_i2cmst_desc_t;
+
+/* Scans for I2CMST core and initalizes i2c library */
+rtems_status_code leon_register_i2c(amba_confarea_type *abus);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _I2CMST_H */