From 0743eaed1c83e9eb73986e15e9f7e8dc9c297a92 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Thu, 11 Oct 2007 12:56:09 +0000 Subject: 2007-10-11 Daniel Hellstrom * 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. --- c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c | 358 +++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c (limited to 'c/src/lib/libbsp/sparc/shared/i2c') 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; +} -- cgit v1.2.3