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.
This commit is contained in:
Joel Sherrill
2010-01-11 16:14:47 +00:00
parent 04a82e9c49
commit fe83cef683
5 changed files with 239 additions and 96 deletions

View File

@@ -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:

View File

@@ -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.

View File

@@ -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 */

View File

@@ -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;
#ifndef BFIN_REG16
#define BFIN_REG16(base, offset) \
(*((uint16_t volatile *) ((uint8_t *)(base) + (offset))))
#endif
softc = &(((bfin_spi_desc_t *)(bus))->softc);
status = rtems_semaphore_create(rtems_build_name('s','p','i','s'),
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);
&state->sem);
if (result == RTEMS_SUCCESSFUL)
bfin_spi = state; /* for isr */
return status;
return result;
}
static rtems_status_code spiSendStart(rtems_libi2c_bus_t *bus) {
bfin_spi_softc_t *softc;
rtems_status_code status;
rtems_status_code bfin_spi_send_start(rtems_libi2c_bus_t *bus) {
status = RTEMS_SUCCESSFUL;
softc = &(((bfin_spi_desc_t *)(bus))->softc);
return status;
return RTEMS_SUCCESSFUL;
}
static rtems_status_code spiSendStop(rtems_libi2c_bus_t *bus) {
bfin_spi_softc_t *softc;
rtems_status_code status;
int bfin_spi_read_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
status = RTEMS_SUCCESSFUL;
softc = &(((bfin_spi_desc_t *)(bus))->softc);
return status;
return readWrite(bus, buf, NULL, len);
}
static rtems_status_code spiSendAddr(rtems_libi2c_bus_t *bus,
uint32_t addr, int rw) {
bfin_spi_softc_t *softc;
rtems_status_code status;
int bfin_spi_write_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
status = RTEMS_SUCCESSFUL;
softc = &(((bfin_spi_desc_t *)(bus))->softc);
return status;
return readWrite(bus, NULL, buf, len);
}
static int spiReadBytes(rtems_libi2c_bus_t *bus,
unsigned char *buf, int len) {
bfin_spi_softc_t *softc;
int bfin_spi_ioctl(rtems_libi2c_bus_t *bus, int cmd, void *arg) {
int result;
softc = &(((bfin_spi_desc_t *)(bus))->softc);
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 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);
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
};

View File

@@ -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;
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
}
#endif
#endif /* _spi_h_ */
#endif /* _spi_h */