From 1b0c3e6cffc89c0e50977a3e6cbbe6fb404af0e4 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Wed, 5 Dec 2007 15:49:40 +0000 Subject: 2007-12-05 Daniel Hellstrom * Makefile.am: i2c should be on branch as well as CVS head. * shared/i2c/i2cmst.c, shared/include/i2cmst.h: New files. --- c/src/lib/libbsp/sparc/ChangeLog | 5 + c/src/lib/libbsp/sparc/Makefile.am | 4 + c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c | 358 +++++++++++++++++++++++++ c/src/lib/libbsp/sparc/shared/include/i2cmst.h | 77 ++++++ 4 files changed, 444 insertions(+) create mode 100644 c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c create mode 100644 c/src/lib/libbsp/sparc/shared/include/i2cmst.h 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,3 +1,8 @@ +2007-12-05 Daniel Hellstrom + + * 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 * shared/include/ambapp.h: Sync head and 4.8 branch. 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 +#include +#include +#include + +/* 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 +#include + +#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 */ -- cgit v1.2.3