diff options
author | Joel Sherrill <joel.sherrill@OARcorp.com> | 2010-01-11 16:14:47 +0000 |
---|---|---|
committer | Joel Sherrill <joel.sherrill@OARcorp.com> | 2010-01-11 16:14:47 +0000 |
commit | fe83cef6831e46410c6095033fe53c2750340099 (patch) | |
tree | e2ef1fc15c9aa918513c07d3171df140a2f65c29 /c/src/lib/libcpu/bfin | |
parent | Update. (diff) | |
download | rtems-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/ChangeLog | 7 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/README | 6 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/include/spiRegs.h | 4 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/serial/spi.c | 271 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/serial/spi.h | 43 |
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 */ |