summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/sparc/shared/i2c
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2007-10-11 12:56:09 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2007-10-11 12:56:09 +0000
commit0743eaed1c83e9eb73986e15e9f7e8dc9c297a92 (patch)
tree4b991c8d5e96e9196d15fa52f05373b3bad45fa0 /c/src/lib/libbsp/sparc/shared/i2c
parent2007-10-11 Daniel Hellstrom <daniel@gaisler.com> (diff)
downloadrtems-0743eaed1c83e9eb73986e15e9f7e8dc9c297a92.tar.bz2
2007-10-11 Daniel Hellstrom <daniel@gaisler.com>
* Makefile.am, shared/can/occan.c, shared/include/ambapp.h: Add initial i2c and update OC-CAN support. * shared/i2c/i2cmst.c, shared/include/i2cmst.h: New files.
Diffstat (limited to 'c/src/lib/libbsp/sparc/shared/i2c')
-rw-r--r--c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c358
1 files changed, 358 insertions, 0 deletions
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;
+}