mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-09 00:53:16 +00:00
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:
@@ -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>
|
2009-12-11 Ralf Corsépius <ralf.corsepius@rtems.org>
|
||||||
|
|
||||||
* serial/uart.c:
|
* serial/uart.c:
|
||||||
|
|||||||
@@ -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
|
the processor type. This is how the libcpu modules determine which
|
||||||
processor variant they're being built for.
|
processor variant they're being built for.
|
||||||
|
|
||||||
serial/spi* and serial/sport* are currently just placeholders.
|
serial/sport* is currently just a placeholders. serial/twi* does not
|
||||||
serial/twi* does not contain enough code to do anything useful;
|
contain enough code to do anything useful; it is however a start at an
|
||||||
it is however a start at an I2C driver.
|
I2C driver.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Blackfin SPI Registers
|
/* 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>
|
* written by Allan Hessenflow <allanh@kallisti.com>
|
||||||
*
|
*
|
||||||
* The license and distribution terms for this file may be
|
* The license and distribution terms for this file may be
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
#define SPI_TDBR_OFFSET 0x000c
|
#define SPI_TDBR_OFFSET 0x000c
|
||||||
#define SPI_RDBR_OFFSET 0x0010
|
#define SPI_RDBR_OFFSET 0x0010
|
||||||
#define SPI_BAUD_OFFSET 0x0014
|
#define SPI_BAUD_OFFSET 0x0014
|
||||||
#define SPI_SHADOW 0x0018
|
#define SPI_SHADOW_OFFSET 0x0018
|
||||||
|
|
||||||
|
|
||||||
/* register fields */
|
/* register fields */
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
/* placeholder (just a shell) */
|
|
||||||
|
|
||||||
/* SPI driver for Blackfin
|
/* 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>
|
* written by Allan Hessenflow <allanh@kallisti.com>
|
||||||
*
|
*
|
||||||
* The license and distribution terms for this file may be
|
* The license and distribution terms for this file may be
|
||||||
@@ -12,98 +10,233 @@
|
|||||||
* $Id$
|
* $Id$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#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 <rtems/libi2c.h>
|
||||||
|
|
||||||
#include <libcpu/spiRegs.h>
|
#include <libcpu/spiRegs.h>
|
||||||
#include "spi.h"
|
#include "spi.h"
|
||||||
|
|
||||||
|
|
||||||
static rtems_status_code spiInit(rtems_libi2c_bus_t *bus) {
|
#ifndef BFIN_REG16
|
||||||
bfin_spi_softc_t *softc;
|
#define BFIN_REG16(base, offset) \
|
||||||
rtems_status_code status;
|
(*((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,
|
0,
|
||||||
RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE,
|
RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE,
|
||||||
0,
|
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) {
|
rtems_status_code bfin_spi_send_start(rtems_libi2c_bus_t *bus) {
|
||||||
bfin_spi_softc_t *softc;
|
|
||||||
rtems_status_code status;
|
|
||||||
|
|
||||||
status = RTEMS_SUCCESSFUL;
|
return RTEMS_SUCCESSFUL;
|
||||||
softc = &(((bfin_spi_desc_t *)(bus))->softc);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static rtems_status_code spiSendStop(rtems_libi2c_bus_t *bus) {
|
int bfin_spi_read_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
|
||||||
bfin_spi_softc_t *softc;
|
|
||||||
rtems_status_code status;
|
|
||||||
|
|
||||||
status = RTEMS_SUCCESSFUL;
|
return readWrite(bus, buf, NULL, len);
|
||||||
softc = &(((bfin_spi_desc_t *)(bus))->softc);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static rtems_status_code spiSendAddr(rtems_libi2c_bus_t *bus,
|
int bfin_spi_write_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) {
|
||||||
uint32_t addr, int rw) {
|
|
||||||
bfin_spi_softc_t *softc;
|
|
||||||
rtems_status_code status;
|
|
||||||
|
|
||||||
status = RTEMS_SUCCESSFUL;
|
return readWrite(bus, NULL, buf, len);
|
||||||
softc = &(((bfin_spi_desc_t *)(bus))->softc);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spiReadBytes(rtems_libi2c_bus_t *bus,
|
int bfin_spi_ioctl(rtems_libi2c_bus_t *bus, int cmd, void *arg) {
|
||||||
unsigned char *buf, int len) {
|
int result;
|
||||||
bfin_spi_softc_t *softc;
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
/* placeholder (just a shell) */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* RTEMS driver for Blackfin SPI
|
* 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>
|
* written by Allan Hessenflow <allanh@kallisti.com>
|
||||||
*
|
*
|
||||||
* The license and distribution terms for this file may be
|
* The license and distribution terms for this file may be
|
||||||
@@ -13,40 +11,45 @@
|
|||||||
* $Id$
|
* $Id$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef _spi_h
|
||||||
#ifndef _spi_h_
|
#define _spi_h
|
||||||
#define _spi_h_
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* parameters provided by bsp */
|
|
||||||
uint32_t freq;
|
|
||||||
void *base;
|
void *base;
|
||||||
bool fast;
|
/* remaining entries are for internal use */
|
||||||
/* internal use */
|
rtems_id sem;
|
||||||
rtems_id irq_sema_id;
|
int bytes_per_word;
|
||||||
} bfin_spi_softc_t;
|
uint16_t idle_pattern;
|
||||||
|
uint8_t *rd_ptr;
|
||||||
|
const uint8_t *wr_ptr;
|
||||||
|
int len;
|
||||||
|
} bfin_spi_state_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
rtems_libi2c_bus_t bus;
|
rtems_libi2c_bus_t bus;
|
||||||
bfin_spi_softc_t softc;
|
bfin_spi_state_t p;
|
||||||
} bfin_spi_desc_t;
|
} 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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* _spi_h_ */
|
|
||||||
|
|
||||||
|
#endif /* _spi_h */
|
||||||
|
|||||||
Reference in New Issue
Block a user