summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/bfin
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2010-01-11 16:14:47 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2010-01-11 16:14:47 +0000
commitfe83cef6831e46410c6095033fe53c2750340099 (patch)
treee2ef1fc15c9aa918513c07d3171df140a2f65c29 /c/src/lib/libcpu/bfin
parentUpdate. (diff)
downloadrtems-fe83cef6831e46410c6095033fe53c2750340099.tar.bz2
2010-01-11 Allan Hessenflow <allanh@kallisti.com>
* serial/spi.c, serial/spi.h: Fill in skeleton with functional SPI master code. * include/spiRegs.h: Correct spi shadow register declaration.
Diffstat (limited to 'c/src/lib/libcpu/bfin')
-rw-r--r--c/src/lib/libcpu/bfin/ChangeLog7
-rw-r--r--c/src/lib/libcpu/bfin/README6
-rw-r--r--c/src/lib/libcpu/bfin/include/spiRegs.h4
-rw-r--r--c/src/lib/libcpu/bfin/serial/spi.c271
-rw-r--r--c/src/lib/libcpu/bfin/serial/spi.h43
5 files changed, 237 insertions, 94 deletions
diff --git a/c/src/lib/libcpu/bfin/ChangeLog b/c/src/lib/libcpu/bfin/ChangeLog
index 3a3d5f86c0..be5e42e460 100644
--- a/c/src/lib/libcpu/bfin/ChangeLog
+++ b/c/src/lib/libcpu/bfin/ChangeLog
@@ -1,3 +1,10 @@
+2010-01-11 Allan Hessenflow <allanh@kallisti.com>
+
+ * serial/spi.c, serial/spi.h:
+ Fill in skeleton with functional SPI master code.
+ * include/spiRegs.h:
+ Correct spi shadow register declaration.
+
2009-12-11 Ralf Corsépius <ralf.corsepius@rtems.org>
* serial/uart.c:
diff --git a/c/src/lib/libcpu/bfin/README b/c/src/lib/libcpu/bfin/README
index 8a7c592857..d2176a2c94 100644
--- a/c/src/lib/libcpu/bfin/README
+++ b/c/src/lib/libcpu/bfin/README
@@ -9,7 +9,7 @@ It is assumed that bsp.h includes <libcpu/bfxxx.h>, where xxx is
the processor type. This is how the libcpu modules determine which
processor variant they're being built for.
-serial/spi* and serial/sport* are currently just placeholders.
-serial/twi* does not contain enough code to do anything useful;
-it is however a start at an I2C driver.
+serial/sport* is currently just a placeholders. serial/twi* does not
+contain enough code to do anything useful; it is however a start at an
+I2C driver.
diff --git a/c/src/lib/libcpu/bfin/include/spiRegs.h b/c/src/lib/libcpu/bfin/include/spiRegs.h
index 183ee557c7..5cf06af8cf 100644
--- a/c/src/lib/libcpu/bfin/include/spiRegs.h
+++ b/c/src/lib/libcpu/bfin/include/spiRegs.h
@@ -1,6 +1,6 @@
/* Blackfin SPI Registers
*
- * Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA
+ * Copyright (c) 2010 Kallisti Labs, Los Gatos, CA, USA
* written by Allan Hessenflow <allanh@kallisti.com>
*
* The license and distribution terms for this file may be
@@ -22,7 +22,7 @@
#define SPI_TDBR_OFFSET 0x000c
#define SPI_RDBR_OFFSET 0x0010
#define SPI_BAUD_OFFSET 0x0014
-#define SPI_SHADOW 0x0018
+#define SPI_SHADOW_OFFSET 0x0018
/* register fields */
diff --git a/c/src/lib/libcpu/bfin/serial/spi.c b/c/src/lib/libcpu/bfin/serial/spi.c
index 471a607f2f..c78867b296 100644
--- a/c/src/lib/libcpu/bfin/serial/spi.c
+++ b/c/src/lib/libcpu/bfin/serial/spi.c
@@ -1,8 +1,6 @@
-/* placeholder (just a shell) */
-
/* SPI driver for Blackfin
*
- * Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA
+ * Copyright (c) 2010 Kallisti Labs, Los Gatos, CA, USA
* written by Allan Hessenflow <allanh@kallisti.com>
*
* The license and distribution terms for this file may be
@@ -12,98 +10,233 @@
* $Id$
*/
-
#include <stdlib.h>
-#include <rtems.h>
+#include <bsp.h>
+#include <rtems/error.h>
+#include <rtems/bspIo.h>
+#include <errno.h>
#include <rtems/libi2c.h>
-
#include <libcpu/spiRegs.h>
#include "spi.h"
-static rtems_status_code spiInit(rtems_libi2c_bus_t *bus) {
- bfin_spi_softc_t *softc;
- rtems_status_code status;
-
- softc = &(((bfin_spi_desc_t *)(bus))->softc);
-
- status = rtems_semaphore_create(rtems_build_name('s','p','i','s'),
- 0,
- RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE,
- 0,
- &softc->irq_sema_id);
-
- return status;
+#ifndef BFIN_REG16
+#define BFIN_REG16(base, offset) \
+ (*((uint16_t volatile *) ((uint8_t *)(base) + (offset))))
+#endif
+
+
+static bfin_spi_state_t *bfin_spi;
+
+
+void bfin_spi_isr(int v) {
+ bfin_spi_state_t *state;
+ uint16_t r;
+
+ state = bfin_spi;
+ if (state->len > state->bytes_per_word) {
+ if (state->wr_ptr) {
+ if (state->bytes_per_word == 2)
+ r = *(uint16_t *) state->wr_ptr;
+ else
+ r = (uint16_t) *state->wr_ptr;
+ state->wr_ptr += state->bytes_per_word;
+ } else
+ r = state->idle_pattern;
+ BFIN_REG16(state->base, SPI_TDBR_OFFSET) = r;
+ }
+ state->len -= state->bytes_per_word;
+ if (state->len <= 0) {
+ /*
+ The transfers are done, so I don't want to kick off another
+ transfer or get any more interrupts. Reading the last word from
+ SPI_SHADOW instead of SPI_RDBR should prevent it from triggering
+ another transfer, but that doesn't clear the interrupt flag. I
+ could mask the interrupt in the SIC, but that would preclude ever
+ using the DMA channel that shares the interrupt independently (and
+ they might just share it with something more important in some other
+ member of the Blackfin family). And who knows what problems it
+ might cause in this code potentially dealing with that still pended
+ interrupt at the beginning of the next transfer.
+
+ So instead I disable the SPI interface, read the data from RDBR
+ (thus clearing the interrupt but not triggering another transfer
+ since the interface is disabled), then re-eanble the interface.
+ This has the problem that the bf537 tri-states the SPI signals
+ while the interface is disabled. Either adding pull-ups on at
+ least the chip select signals, or using GPIOs for them so they're
+ not controlled by the SPI module, would be correct fixes for that
+ (really pull-ups/downs should be added to the SPI CLK and MOSI
+ signals as well to insure they cannot float into some region that
+ causes input structures to consume excessive power). Or they can
+ all be left alone, assuming that there's enough capacitance on the
+ lines to prevent any problems for the short time they're being left
+ disabled.
+
+ An alternative approach I attempted involved switching TIMOD
+ between RDBR and TDBR when starting and finishing a transfer, but
+ I didn't get anywhere with that. In my limited testing TIMOD TDBR
+ wasn't behaving as I expected it to, but maybe with more
+ experimentation I'd find some solution there. However I'm out
+ of time for this project, at least for now.
+ */
+
+ BFIN_REG16(state->base, SPI_CTL_OFFSET) &= ~SPI_CTL_SPE;
+ r = BFIN_REG16(state->base, SPI_RDBR_OFFSET);
+ BFIN_REG16(state->base, SPI_CTL_OFFSET) |= SPI_CTL_SPE;
+ rtems_semaphore_release(state->sem);
+ } else
+ r = BFIN_REG16(state->base, SPI_RDBR_OFFSET);
+
+ if (state->rd_ptr) {
+ if (state->bytes_per_word == 2)
+ *(uint16_t *) state->rd_ptr = r;
+ else
+ *state->rd_ptr = (uint8_t) r;
+ state->rd_ptr += state->bytes_per_word;
+ }
}
-static rtems_status_code spiSendStart(rtems_libi2c_bus_t *bus) {
- bfin_spi_softc_t *softc;
- rtems_status_code status;
-
- status = RTEMS_SUCCESSFUL;
- softc = &(((bfin_spi_desc_t *)(bus))->softc);
-
- return status;
+static rtems_status_code setTFRMode(rtems_libi2c_bus_t *bus,
+ const rtems_libi2c_tfr_mode_t *tfrMode) {
+ rtems_status_code result;
+ bfin_spi_state_t *state;
+ uint32_t divisor;
+ uint16_t ctrl;
+
+ result = RTEMS_SUCCESSFUL;
+ state = &((bfin_spi_bus_t *) bus)->p;
+
+ if (result == RTEMS_SUCCESSFUL) {
+ if (tfrMode->bits_per_char != 8 &&
+ tfrMode->bits_per_char != 16)
+ result = RTEMS_INVALID_NUMBER;
+ if (tfrMode->baudrate <= 0)
+ result = RTEMS_INVALID_NUMBER;
+ }
+ if (result == RTEMS_SUCCESSFUL) {
+ divisor = (SCLK / 2 + tfrMode->baudrate - 1) /
+ tfrMode->baudrate;
+ if (divisor < 2)
+ divisor = 2;
+ else if (divisor > 65535)
+ result = RTEMS_INVALID_NUMBER;
+ }
+ if (result == RTEMS_SUCCESSFUL) {
+ state->idle_pattern = (uint16_t) tfrMode->idle_char;
+ state->bytes_per_word = (tfrMode->bits_per_char > 8) ? 2 : 1;
+ BFIN_REG16(state->base, SPI_BAUD_OFFSET) = divisor;
+ ctrl = BFIN_REG16(state->base, SPI_CTL_OFFSET);
+ if (tfrMode->lsb_first)
+ ctrl |= SPI_CTL_LSBF;
+ else
+ ctrl &= ~SPI_CTL_LSBF;
+ if (tfrMode->bits_per_char > 8)
+ ctrl |= SPI_CTL_SIZE;
+ else
+ ctrl &= ~SPI_CTL_SIZE;
+ if (tfrMode->clock_inv)
+ ctrl |= SPI_CTL_CPOL;
+ else
+ ctrl &= ~SPI_CTL_CPOL;
+ if (tfrMode->clock_phs)
+ ctrl |= SPI_CTL_CPHA;
+ else
+ ctrl &= ~SPI_CTL_CPHA;
+ BFIN_REG16(state->base, SPI_CTL_OFFSET) = ctrl;
+ }
+
+ return result;
}
-static rtems_status_code spiSendStop(rtems_libi2c_bus_t *bus) {
- bfin_spi_softc_t *softc;
- rtems_status_code status;
-
- status = RTEMS_SUCCESSFUL;
- softc = &(((bfin_spi_desc_t *)(bus))->softc);
-
- return status;
+static int readWrite(rtems_libi2c_bus_t *bus, uint8_t *rdBuf,
+ const uint8_t *wrBuf, int len) {
+ rtems_status_code result;
+ bfin_spi_state_t *state;
+ uint16_t r;
+
+ result = RTEMS_SUCCESSFUL;
+ state = &((bfin_spi_bus_t *) bus)->p;
+
+ if (len) {
+ state->rd_ptr = rdBuf;
+ state->wr_ptr = wrBuf;
+ state->len = len;
+ if (state->wr_ptr) {
+ if (state->bytes_per_word == 2)
+ r = *(uint16_t *) state->wr_ptr;
+ else
+ r = (uint16_t) *state->wr_ptr;
+ state->wr_ptr += state->bytes_per_word;
+ } else
+ r = state->idle_pattern;
+ BFIN_REG16(state->base, SPI_TDBR_OFFSET) = r;
+ BFIN_REG16(state->base, SPI_RDBR_OFFSET); /* trigger */
+ /* wait until done */
+ do {
+ result = rtems_semaphore_obtain(state->sem, RTEMS_WAIT, 100);
+ } while (result == RTEMS_SUCCESSFUL && state->len > 0);
+ }
+
+ return (result == RTEMS_SUCCESSFUL) ? len : -result;
}
-static rtems_status_code spiSendAddr(rtems_libi2c_bus_t *bus,
- uint32_t addr, int rw) {
- bfin_spi_softc_t *softc;
- rtems_status_code status;
- status = RTEMS_SUCCESSFUL;
- softc = &(((bfin_spi_desc_t *)(bus))->softc);
+rtems_status_code bfin_spi_init(rtems_libi2c_bus_t *bus) {
+ rtems_status_code result;
+ bfin_spi_state_t *state;
- return status;
-}
+ state = &((bfin_spi_bus_t *) bus)->p;
-static int spiReadBytes(rtems_libi2c_bus_t *bus,
- unsigned char *buf, int len) {
- bfin_spi_softc_t *softc;
+ BFIN_REG16(state->base, SPI_CTL_OFFSET) = SPI_CTL_SPE |
+ SPI_CTL_MSTR |
+ SPI_CTL_CPHA |
+ SPI_CTL_TIMOD_RDBR;
- softc = &(((bfin_spi_desc_t *)(bus))->softc);
+ result = rtems_semaphore_create(rtems_build_name('s','p','i','s'),
+ 0,
+ RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE,
+ 0,
+ &state->sem);
+ if (result == RTEMS_SUCCESSFUL)
+ bfin_spi = state; /* for isr */
- return 0;
+ return result;
}
-static int spiWriteBytes(rtems_libi2c_bus_t *bus,
- unsigned char *buf, int len) {
- bfin_spi_softc_t *softc;
-
- softc = &(((bfin_spi_desc_t *)(bus))->softc);
+rtems_status_code bfin_spi_send_start(rtems_libi2c_bus_t *bus) {
- return 0;
+ return RTEMS_SUCCESSFUL;
}
-static int spiIoctl(rtems_libi2c_bus_t *bus, int cmd, void *arg) {
- bfin_spi_softc_t *softc;
+int bfin_spi_read_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
- softc = &(((bfin_spi_desc_t *)(bus))->softc);
+ return readWrite(bus, buf, NULL, len);
+}
+int bfin_spi_write_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
- return 0;
+ return readWrite(bus, NULL, buf, len);
}
-void bfin_spi_isr(int source) {
+int bfin_spi_ioctl(rtems_libi2c_bus_t *bus, int cmd, void *arg) {
+ int result;
+
+ result = -RTEMS_NOT_DEFINED;
+ switch(cmd) {
+ case RTEMS_LIBI2C_IOCTL_SET_TFRMODE:
+ result = -setTFRMode(bus, (const rtems_libi2c_tfr_mode_t *) arg);
+ break;
+ case RTEMS_LIBI2C_IOCTL_READ_WRITE:
+ result = readWrite(bus,
+ ((rtems_libi2c_read_write_t *) arg)->rd_buf,
+ ((rtems_libi2c_read_write_t *) arg)->wr_buf,
+ ((rtems_libi2c_read_write_t *) arg)->byte_cnt);
+ break;
+ default:
+ break;
+ }
+
+ return result;
}
-rtems_libi2c_bus_ops_t bfin_spi_libi2c_bus_ops = {
- init: spiInit,
- send_start: spiSendStart,
- send_stop: spiSendStop,
- send_addr: spiSendAddr,
- read_bytes: spiReadBytes,
- write_bytes: spiWriteBytes,
- ioctl: spiIoctl
-};
-
diff --git a/c/src/lib/libcpu/bfin/serial/spi.h b/c/src/lib/libcpu/bfin/serial/spi.h
index a656f44c29..63609a3ccb 100644
--- a/c/src/lib/libcpu/bfin/serial/spi.h
+++ b/c/src/lib/libcpu/bfin/serial/spi.h
@@ -1,9 +1,7 @@
-/* placeholder (just a shell) */
-
/*
* RTEMS driver for Blackfin SPI
*
- * COPYRIGHT (c) 2008 Kallisti Labs, Los Gatos, CA, USA
+ * COPYRIGHT (c) 2010 Kallisti Labs, Los Gatos, CA, USA
* written by Allan Hessenflow <allanh@kallisti.com>
*
* The license and distribution terms for this file may be
@@ -13,40 +11,45 @@
* $Id$
*/
-
-#ifndef _spi_h_
-#define _spi_h_
-
+#ifndef _spi_h
+#define _spi_h
#ifdef __cplusplus
extern "C" {
#endif
-
typedef struct {
- /* parameters provided by bsp */
- uint32_t freq;
- void *base;
- bool fast;
- /* internal use */
- rtems_id irq_sema_id;
-} bfin_spi_softc_t;
+ void *base;
+ /* remaining entries are for internal use */
+ rtems_id sem;
+ int bytes_per_word;
+ uint16_t idle_pattern;
+ uint8_t *rd_ptr;
+ const uint8_t *wr_ptr;
+ int len;
+} bfin_spi_state_t;
typedef struct {
rtems_libi2c_bus_t bus;
- bfin_spi_softc_t softc;
-} bfin_spi_desc_t;
+ bfin_spi_state_t p;
+} bfin_spi_bus_t;
+
+
+void bfin_spi_isr(int v);
+rtems_status_code bfin_spi_init(rtems_libi2c_bus_t *bus);
-extern rtems_libi2c_bus_ops_t bfin_spi_libi2c_bus_ops;
+rtems_status_code bfin_spi_send_start(rtems_libi2c_bus_t *bus);
+int bfin_spi_read_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len);
-void bfin_spi_isr(int source);
+int bfin_spi_write_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len);
+int bfin_spi_ioctl(rtems_libi2c_bus_t *bus, int cmd, void *arg);
#ifdef __cplusplus
}
#endif
-#endif /* _spi_h_ */
+#endif /* _spi_h */