Index: c/src/lib/libcpu/bfin/ChangeLog
===================================================================
--- c/src/lib/libcpu/bfin/ChangeLog	(revision 04a82e9c497f154499f7f21a10be7e8d4b522108)
+++ c/src/lib/libcpu/bfin/ChangeLog	(revision fe83cef6831e46410c6095033fe53c2750340099)
@@ -1,2 +1,9 @@
+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>
 
Index: c/src/lib/libcpu/bfin/README
===================================================================
--- c/src/lib/libcpu/bfin/README	(revision 04a82e9c497f154499f7f21a10be7e8d4b522108)
+++ c/src/lib/libcpu/bfin/README	(revision fe83cef6831e46410c6095033fe53c2750340099)
@@ -10,6 +10,6 @@
 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.
 
Index: c/src/lib/libcpu/bfin/include/spiRegs.h
===================================================================
--- c/src/lib/libcpu/bfin/include/spiRegs.h	(revision 04a82e9c497f154499f7f21a10be7e8d4b522108)
+++ c/src/lib/libcpu/bfin/include/spiRegs.h	(revision fe83cef6831e46410c6095033fe53c2750340099)
@@ -1,5 +1,5 @@
 /*  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>
  *
@@ -23,5 +23,5 @@
 #define SPI_RDBR_OFFSET                               0x0010
 #define SPI_BAUD_OFFSET                               0x0014
-#define SPI_SHADOW                                    0x0018
+#define SPI_SHADOW_OFFSET                             0x0018
 
 
Index: c/src/lib/libcpu/bfin/serial/spi.c
===================================================================
--- c/src/lib/libcpu/bfin/serial/spi.c	(revision 04a82e9c497f154499f7f21a10be7e8d4b522108)
+++ c/src/lib/libcpu/bfin/serial/spi.c	(revision fe83cef6831e46410c6095033fe53c2750340099)
@@ -1,7 +1,5 @@
-/* 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>
  *
@@ -13,97 +11,232 @@
  */
 
-
 #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'),
+#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 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 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;
+}
+
+
+rtems_status_code bfin_spi_init(rtems_libi2c_bus_t *bus) {
+  rtems_status_code result;
+  bfin_spi_state_t *state;
+
+  state = &((bfin_spi_bus_t *) bus)->p;
+
+  BFIN_REG16(state->base, SPI_CTL_OFFSET) = SPI_CTL_SPE |
+                                            SPI_CTL_MSTR |
+                                            SPI_CTL_CPHA |
+                                            SPI_CTL_TIMOD_RDBR;
+
+  result = rtems_semaphore_create(rtems_build_name('s','p','i','s'),
                                   0,
                                   RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE,
                                   0,
-                                  &softc->irq_sema_id);
-
-  return status;
-}
-
-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 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 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);
-
-  return status;
-}
-
-static int spiReadBytes(rtems_libi2c_bus_t *bus,
-                        unsigned char *buf, int len) {
-  bfin_spi_softc_t *softc;
-
-  softc = &(((bfin_spi_desc_t *)(bus))->softc);
-
-  return 0;
-}
-
-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);
-
-  return 0;
-}
-
-static int spiIoctl(rtems_libi2c_bus_t *bus, int cmd, void *arg) {
-  bfin_spi_softc_t *softc;
-
-  softc = &(((bfin_spi_desc_t *)(bus))->softc);
-
-
-  return 0;
-}
-
-void bfin_spi_isr(int source) {
-}
-
-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
-};
-
+                                  &state->sem);
+  if (result == RTEMS_SUCCESSFUL)
+    bfin_spi = state; /* for isr */
+
+  return result;
+}
+
+rtems_status_code bfin_spi_send_start(rtems_libi2c_bus_t *bus) {
+
+  return RTEMS_SUCCESSFUL;
+}
+
+int bfin_spi_read_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
+
+  return readWrite(bus, buf, NULL, len);
+}
+
+int bfin_spi_write_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
+
+  return readWrite(bus, NULL, buf, len);
+}
+
+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;
+}
+
Index: c/src/lib/libcpu/bfin/serial/spi.h
===================================================================
--- c/src/lib/libcpu/bfin/serial/spi.h	(revision 04a82e9c497f154499f7f21a10be7e8d4b522108)
+++ c/src/lib/libcpu/bfin/serial/spi.h	(revision fe83cef6831e46410c6095033fe53c2750340099)
@@ -1,8 +1,6 @@
-/* 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>
  *
@@ -14,8 +12,6 @@
  */
 
-
-#ifndef _spi_h_
-#define _spi_h_
-
+#ifndef _spi_h
+#define _spi_h
 
 #ifdef __cplusplus
@@ -23,25 +19,32 @@
 #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;
 
 
-extern rtems_libi2c_bus_ops_t bfin_spi_libi2c_bus_ops;
+void bfin_spi_isr(int v);
 
+rtems_status_code bfin_spi_init(rtems_libi2c_bus_t *bus);
 
-void bfin_spi_isr(int source);
+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);
+
+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
@@ -49,4 +52,4 @@
 #endif
 
-#endif /* _spi_h_ */
 
+#endif /* _spi_h */
