/* this is not much more than a shell; it does not do anything useful yet */ /* TWI (I2C) driver for Blackfin * * Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA * written by Allan Hessenflow * * 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. * * $Id$ */ #include #include #include #include "twi.h" #ifndef N_BFIN_TWI #define N_BFIN_TWI 1 #endif #define BFIN_REG16(base, offset) \ (*((uint16_t volatile *) ((char *)(base) + (offset)))) static struct { void *base; rtems_id irqSem; rtems_id mutex; bfin_twi_callback_t callback; void *callbackArg; bfin_twi_request_t volatile *req; uint8_t volatile *dataPtr; int volatile count; boolean volatile masterActive; rtems_status_code volatile masterResult; boolean volatile slaveActive; } twi[N_BFIN_TWI]; rtems_status_code bfin_twi_init(int channel, bfin_twi_config_t *config) { rtems_status_code result; void *base; if (channel < 0 || channel >= N_BFIN_TWI) return RTEMS_INVALID_NUMBER; base = config->base; twi[channel].base = base; result = rtems_semaphore_create(rtems_build_name('t','w','i','s'), 0, RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0, &twi[channel].irqSem); result = rtems_semaphore_create(rtems_build_name('t','w','i','m'), 1, RTEMS_PRIORITY | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0, &twi[channel].mutex); BFIN_REG16(base, TWI_CONTROL_OFFSET) = (uint16_t) (((config->sclk +9999999) / 10000000) << TWI_CONTROL_PRESCALE_SHIFT) | TWI_CONTROL_TWI_ENA; BFIN_REG16(base, TWI_CLKDIV_OFFSET) = config->fast ? ((8 << TWI_CLKDIV_CLKHI_SHIFT) | (17 << TWI_CLKDIV_CLKLOW_SHIFT)) : ((33 << TWI_CLKDIV_CLKHI_SHIFT) | (67 << TWI_CLKDIV_CLKLOW_SHIFT)); BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET) = 0; BFIN_REG16(base, TWI_MASTER_CTL_OFFSET) = config->fast ? TWI_MASTER_CTL_FAST : 0; BFIN_REG16(base, TWI_SLAVE_ADDR_OFFSET) = (uint16_t) config->slave_address << TWI_SLAVE_ADDR_SADDR_SHIFT; BFIN_REG16(base, TWI_MASTER_STAT_OFFSET) = TWI_MASTER_STAT_BUFWRERR | TWI_MASTER_STAT_BUFRDERR | TWI_MASTER_STAT_DNAK | TWI_MASTER_STAT_ANAK | TWI_MASTER_STAT_LOSTARB; BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = TWI_FIFO_CTL_XMTFLUSH | TWI_FIFO_CTL_RCVFLUSH; BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = 0; BFIN_REG16(base, TWI_INT_STAT_OFFSET) = TWI_INT_STAT_RCVSERV | TWI_INT_STAT_XMTSERV | TWI_INT_STAT_MERR | TWI_INT_STAT_MCOMP | TWI_INT_STAT_SOVF | TWI_INT_STAT_SERR | TWI_INT_STAT_SCOMP | TWI_INT_STAT_SINIT; BFIN_REG16(base, TWI_INT_MASK_OFFSET) = TWI_INT_MASK_RCVSERVM | TWI_INT_MASK_XMTSERVM; return result; } rtems_status_code bfin_twi_register_callback(int channel, bfin_twi_callback_t callback, void *arg) { void *base; int level; if (channel < 0 || channel >= N_BFIN_TWI) return RTEMS_INVALID_NUMBER; base = twi[channel].base; if (callback == NULL) BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET) = 0; rtems_interrupt_disable(level); twi[channel].callback = callback; twi[channel].callbackArg = arg; rtems_interrupt_enable(level); if (callback != NULL) BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET) = TWI_SLAVE_CTL_GEN | TWI_SLAVE_CTL_SEN; return RTEMS_SUCCESSFUL; } void bfin_twi_isr(int source) { void *base; int i; uint16_t r; uint16_t stat; for (i = 0; i < N_BFIN_TWI; i++) { base = twi[i].base; if (base) { stat = BFIN_REG16(base, TWI_INT_STAT_OFFSET); if (stat) { BFIN_REG16(base, TWI_INT_STAT_OFFSET) = stat; if ((stat & TWI_INT_STAT_SINIT) && !twi[i].slaveActive) { twi[i].slaveActive = TRUE; r = BFIN_REG16(base, TWI_FIFO_CTL_OFFSET); BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = r | TWI_FIFO_CTL_XMTFLUSH; BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = r; r = BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET); BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET) = r | TWI_SLAVE_CTL_STDVAL; } if (twi[i].slaveActive) { if (stat & (TWI_INT_STAT_SCOMP | TWI_INT_STAT_SERR)) { r = BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET); BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET) = r & ~TWI_SLAVE_CTL_STDVAL; twi[i].slaveActive = FALSE; } } if (twi[i].masterActive && !twi[i].slaveActive) { if (stat & (TWI_INT_STAT_MCOMP | TWI_INT_STAT_MERR)) { if (!(stat & TWI_INT_STAT_MERR)) { rtems_semaphore_release(twi[i].irqSem); } else rtems_semaphore_release(twi[i].irqSem); } } } } } } rtems_status_code bfin_twi_request(int channel, uint8_t address, bfin_twi_request_t *request, rtems_interval timeout) { rtems_status_code result; void *base; rtems_interrupt_level level; uint16_t r; uint16_t masterMode; if (channel < 0 || channel >= N_BFIN_TWI) return RTEMS_INVALID_NUMBER; result = rtems_semaphore_obtain(twi[channel].mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (result == RTEMS_SUCCESSFUL) { base = twi[channel].base; twi[channel].req = request; if (request->write) { twi[channel].dataPtr = request->data; twi[channel].count = request->count; } else twi[channel].count = 0; BFIN_REG16(base, TWI_MASTER_ADDR_OFFSET) = (uint16_t) address << TWI_MASTER_ADDR_MADDR_SHIFT; masterMode = BFIN_REG16(base, TWI_MASTER_CTL_OFFSET); masterMode |= (request->count << TWI_MASTER_CTL_DCNT_SHIFT); if (request->next) masterMode |= TWI_MASTER_CTL_RSTART; if (!request->write) masterMode |= TWI_MASTER_CTL_MDIR; masterMode |= TWI_MASTER_CTL_MEN; rtems_interrupt_disable(level); if (!twi[channel].slaveActive) { r = BFIN_REG16(base, TWI_FIFO_CTL_OFFSET); BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = r | TWI_FIFO_CTL_XMTFLUSH; BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = r; if (request->write) { while (twi[channel].count && (BFIN_REG16(base, TWI_FIFO_STAT_OFFSET) & TWI_FIFO_STAT_XMTSTAT_MASK) != TWI_FIFO_STAT_XMTSTAT_FULL) { BFIN_REG16(base, TWI_XMT_DATA8_OFFSET) = (uint16_t) *twi[channel].dataPtr++; twi[channel].count--; } } twi[channel].masterActive = TRUE; BFIN_REG16(base, TWI_MASTER_CTL_OFFSET) = masterMode; } else { twi[channel].masterActive = FALSE; twi[channel].masterResult = -1; /* BISON (code should be equiv to lost arbitration) */ } rtems_interrupt_enable(level); while (result == RTEMS_SUCCESSFUL && twi[channel].masterActive) result = rtems_semaphore_obtain(twi[channel].irqSem, RTEMS_WAIT, timeout); if (result == RTEMS_SUCCESSFUL) result = twi[channel].masterResult; else { /* BISON abort */ } rtems_semaphore_release(twi[channel].mutex); } return result; }