/* * 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 #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(struct ambapp_bus *abus) { #if defined(DEBUG) printk("leon_register_i2c called..."); #endif int rc; int device_found = 0; struct ambapp_apb_info apbi2cmst; /* Scan AMBA bus for I2CMST core */ device_found = ambapp_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 */ { struct ambapp_apb_info gptimer; struct gptimer_regs *tregs; if (ambapp_find_apbslv(abus, VENDOR_GAISLER, GAISLER_GPTIMER, &gptimer) == 1 ) { tregs = (struct gptimer_regs *)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; }