summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/bfin/serial/twi.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libcpu/bfin/serial/twi.c')
-rw-r--r--c/src/lib/libcpu/bfin/serial/twi.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/bfin/serial/twi.c b/c/src/lib/libcpu/bfin/serial/twi.c
new file mode 100644
index 0000000000..5bdb9ef742
--- /dev/null
+++ b/c/src/lib/libcpu/bfin/serial/twi.c
@@ -0,0 +1,256 @@
+/* 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 <allanh@kallisti.com>
+ *
+ * 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 <stdlib.h>
+#include <rtems.h>
+
+#include <libcpu/twiRegs.h>
+#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;
+ uint8_t data;
+
+ 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;
+}
+