Compare commits

..

No commits in common. "560f7b806beff732d083ff94e954ac3d4e4c7ed1" and "b23ad870c8b16ef3091c593d9b663abc4a37c48a" have entirely different histories.

11 changed files with 629 additions and 297 deletions

1
.gitignore vendored
View file

@ -8,4 +8,3 @@ revision.h
__pycache__
revision.h+
*~
*.s

View file

@ -147,7 +147,7 @@ sources = irena mainloop parser message isr crc \
usb controlpipe usbconfig dma stream \
enc28j60 net udp \
expression variables display \
pressure gpio \
pressure \
adc rtc \
plugin \
nomalloc strtol uart uart1 base85

View file

@ -13,6 +13,16 @@
#include "script.h"
#include <string.h>
error_msg_t ssp_error(const char *m, int c)
{
if (!c)
return 0;
return error_printf(800+c, "SSP error: %s: %d\n", m, c);
}
static unsigned int ssp_response;
static unsigned int ssp_sync_response;
unsigned int stream_flags;
static const struct keywords stream_enable_kw[] = {
@ -107,3 +117,15 @@ error_msg_t parse_stream_flags(struct command *cmd, unsigned int defaults)
return 0;
}
const struct keywords altera_variable_names[] = {
{"ssp_response", {.par=&ssp_response}},
{"ssp_sync_response", {.par=&ssp_sync_response}},
{"ssp_lastword", {.par=&ssp_lastword}},
{"ssp_buffer", {.par=&ssp_injection_buffer}},
{"ssp_block_idle", {.par=&ssp_block_idle}},
{"ssp_block_size", {.par=&ssp_block_size}},
{"ssp_write_ptr", {.par=&ssp_injection_write_ptr}},
{"ssp_read_ptr", {.par=&ssp_injection_read_ptr}},
{"stream_flags", {.par=&stream_flags}},
{"", {0}}};

View file

@ -1,6 +1,64 @@
#ifndef _altera_h
#define _altera_h
#include "parser.h"
void altera_init(void);
error_msg_t altera_reset(void);
error_msg_t altera_config(unsigned char *buf, unsigned int bsize);
error_msg_t altera_config_file(int fd, const char *fn, int secondary);
error_msg_t altera_send_command(unsigned int c, unsigned int l, unsigned short *r);
error_msg_t altera_sync(unsigned short c, unsigned short *r);
error_msg_t inject(unsigned int c);
error_msg_t inject_delay(unsigned int c, unsigned int flags, unsigned int delay);
error_msg_t altera_stream_enable(unsigned int where, unsigned int what, unsigned int size);
extern const struct command_par altera_command;
extern const struct command_par altera2_command;
extern const struct keywords altera_variable_names[];
error_msg_t altera_read_packet(unsigned int fifo, unsigned int packetsize, unsigned short *buf);
error_msg_t altera_read_timer(unsigned int addr, unsigned int *r);
error_msg_t altera_set_timer(unsigned int addr, unsigned int *r);
error_msg_t altera_wait_status(unsigned int c, unsigned short mask, unsigned short val, unsigned int timeout);
extern unsigned int wait_status;
#define Altera_CRC_Error 0x00010000
#define Altera_nSTATUS 0x00020000
#define Altera_DCLK 0x00040000
#define Altera_DATA0 0x00080000
#define Altera_nCONFIG 0x00100000
#define Altera_CONF_DONE 0x00200000
#define GPIO1_Altera_Mask (~0x003f0000)
#define GPIO1_Altera_Dir 0x001c0000
error_msg_t ssp_error(const char *m, int c);
int altera_inject_command(struct command *cmd, const struct command_par *par);
int altera_set_register(struct command *cmd, const struct command_par *par,
const struct keywords *regs,
unsigned int how, unsigned int cc,
const char *name);
int altera_cmd(struct command *cmd, const struct command_par *par);
enum {
alt_cmd_inj = 0x010000,
alt_cmd_sync = 0x020000,
alt_cmd_send = 0x040000,
alt_cmd_scan = 0x080000,
alt_cmd_trans = 0x0f0000,
alt_cmd_double= 0x0800000,
alt_cmd_clock = 0x1000000,
alt_cmd_read = 0x2000000,
alt_cmd_delay = 0x4000000,
alt_cmd_save = 0x8000000,
alt_cmd_force = 0x10000000,
alt_cmd_aflag = 0x20000000,
alt_cmd_nkeyw = 0x80000000,
alt_cmd_write = 0x4000,
alt_cmd_addr = 0x8000,
};
#define ALT_CMD_KW(p) (&(p)->name)
#define ALT_CMD_AD(a) ((const void*)((a)|alt_cmd_nkeyw|alt_cmd_addr))
#define ALT_CMD_AF(a) ((const void*)((a)|alt_cmd_nkeyw|alt_cmd_aflag))
#define ALT_CMD_FG(f) ((const void*)((f)|alt_cmd_nkeyw))
enum {
stk_dma_ch = 3,
@ -45,6 +103,4 @@ extern unsigned int stream_flags;
error_msg_t stream_destination(unsigned int where);
extern const struct command_par err_invalid_source;
static inline void disable_ssp_dma() {}
#endif

24
gpio.c
View file

@ -1,24 +0,0 @@
#include "gpio.h"
#include <lpc2148/gpio.h>
unsigned int gpio1_mask = 0x00ff0000;
void gpio_set(unsigned int pins)
{
pins &= gpio1_mask;
GPIO_FIO1SET = pins;
GPIO_FIO1DIR |= pins;
}
void gpio_clr(unsigned int pins)
{
pins &= gpio1_mask;
GPIO_FIO1CLR = pins;
GPIO_FIO1DIR |= pins;
}
unsigned int gpio_read(unsigned int pins)
{
pins &= gpio1_mask;
GPIO_FIO1DIR &=~ pins;
return GPIO_FIO1PIN & pins;
}

105
gpio.h
View file

@ -1,105 +0,0 @@
#ifndef _gpio_h
#define _gpio_h
/*
* port: (pin) function (options) (remarks)
*
* 0.0: (19) TxD0
* 0.1: (21) RxD0
* 0.2: (22) SPI SSEL flash (open drain)
* 0.3: (26) SPI SSEL sdcard (open drain)
* 0.4: (27) SPI SCK
* 0.5: (29) SPI MISO
* 0.6: (30) SPI MOSI
* 0.7: (31) SPI SSEL (PWM, EINT) (power board)
* 0.8: (33) TxD1 (AD1.1, PWM)
* 0.9: (34) RxD1 (PWM, EINT)
* 0.10: (35) AD1.2 (power board)
* 0.11: (37) SSP SSEL DAC (open drain)
* 0.12: (38) AD1.3 (MAT) (power board)
* 0.13: (39) AD1.4 Ipos (MAT)
* 0.14: (41) EINT1 ISP
* 0.15: (45) AD1.5 Ineg (EINT)
* 0.16: (46) SSP SSEL/MCLK MAT0.2
* 0.17: (47) SSP SCK
* 0.18: (53) SSP MISO
* 0.19: (54) SSP MOSI
* 0.20: (55) SSL SSEL ADC
* 0.21: (1) AD1.6 Vpos (PWM)
* 0.22: (2) AD1.7 Vneg (MAT)
* 0.23: (58) USB VBUS
* 0.24:
* 0.25: (9) AOUT (AD0.4)
* 0.26:
* 0.27:
* 0.28: (13) AD0.1 (MAT)
* 0.29: (14) AD0.2 (MAT)
* 0.30: (15) AD0.3 (MAT, EINT)
* 0.31: (17) USB UP_LED
*
* 1.16: (16) GPIO OUT (internal pull-up)
* 1.17: (12) GPIO OUT (internal pull-up)
* 1.18: (8) GPIO OUT (internal pull-up)
* 1.19: (4) GPIO IN (internal pull-up)
* 1.20: (48) GPIO (internal pull-up)
* 1.21: (44) GPIO (internal pull-up)
* 1.22: (40) GPIO RS232 Tx (internal pull-up)
* 1.23: (36) GPIO RS232 Rx (internal pull-up)
* 1.24: (32) SSP SSEL_P2 (internal pull-up)
* 1.25: (28) SSP SSEL_P1 MS5534C (internal pull-up)
* 1.26: (24) JTAG
* 1.27: (64) JTAG
* 1.28: (60) JTAG
* 1.29: (56) JTAG
* 1.30: (52) JTAG
* 1.31: (20) JTAG
*
* UART1:
* 3.3V CMOS/RS232
* TxD, RxD, IN, OUT
* RxD can be used as EINT interrupt input
* RxD and TxD can be used as 2 phase PWM outputs
* IN OUT are part of the 8-bit GPIO1 port
*
* GPIO1:
* eight bits [32:16], 3.3V CMOS with pull-up
* one RS232 output [22]
* one RS232 input [23]
* three 5V driver outputs [18:16]
* one 5V driver input [17]
*
* AD0, DAC:
* four AD inputs, AD0.1 AD0.4.
* AD0.4 is the DAC output
* Use as GPIO
* AD0.13 three MATch outputs for clock generation
* AD0.3 can be used as EINT interrupt.
*
* SSP:
* five SSEL pins
* P0.11: DAC LTC2656
* P0.16: External SPI/pressure sensor MCLK
* P0.20: ADC ADS8688
* P1.24: External SPI driver enable
* P1.25: internal pressure sensor MS5534C driver enable
*
* To readout the pressure sensor(s), P0.16 must be driven by counter 0
* to provide a 32768kHz MCLK (MAT0.2).
* The external MISO input can be jumpered to the ADS8688 DAISY chain,
* to readout external ADS8688 in a chain with the internal one.
*/
static inline void init_gpio(void) {}
#define SSP_SSEL_MASK0 0x00110800 // gpio0 pins to set high on deassert ssel
#define SSP_SSEL_MASK1 0x03000000 // gpio1 pins to set high on deassert ssel
#define SSP_SSEL_CONF0 0x00113f00 // ssel bits associated with gpio0 pins
#define SSP_SSEL_CONF1 0x03ee0000 // ssel bits associated with gpio1 pins
extern unsigned int gpio1_mask;
void gpio_set(unsigned int pins);
void gpio_clr(unsigned int pins);
unsigned int gpio_read(unsigned int pins);
#endif

View file

@ -647,8 +647,24 @@ void sleep(int count)
// qtime() is the time of day in units of 1/2¹⁵ seconds.
if (qtime_limit_test(&spi_qtime_limit))
buf = dma_get_spi_block();
if (buf)
if (buf) {
qtime_limit_set(&spi_qtime_limit);
// test if the data is all the same words
unsigned short *b = buf;
int i=255;
while (i && b[0]==b[i])
i--;
if (!i) {
spy_return = 0xb0ff0000 | b[0];
// if its 0xffff, the FPGA tripped.
if (b[0]==0xffff) {
ssp_dma_size = 0;
message(MSG_PRIO_ERROR, "got block of 0xffff, ssp_dma stopped\n");
}
else
message(MSG_PRIO_WARN, "got block of 0x%04x\n", b[0]);
}
}
if (buf && uart_size)
if (uart_send_dma_block_base64(buf))
uart_size--, uart_count++;
@ -878,6 +894,7 @@ const struct keywords *variable_names[] = {
script_variable_names,
eth_variable_names,
pressure_variable_names,
altera_variable_names,
0
};

View file

@ -147,6 +147,6 @@ int printf(const char *fmt, ...)
return -1;
submit:
poll_dma_buffer_size += n;
poll_dma_submit(0,0);
return n;
poll_dma_submit(0,0);
return n;
}

6
spi.c
View file

@ -68,9 +68,9 @@ void spi_init()
if (ssel)
ssel |= SPI_Ssel_Ext;
unsigned int mask = SPI_Ssel_Flash|SPI_Ssel_SD|ssel;
GPIO_FIO0MASK &= ~mask;
GPIO_FIO0PIN |= mask;
GPIO_FIO0DIR |= mask;
GPIO_FIO0MASK = ~mask;
GPIO_FIO0PIN = mask;
GPIO_FIO0DIR = mask;
ssel &= ~SPI_Ssel_Ext;
// make any selected extra SSEL a GPIO
// This reaches up to pin 15.

588
ssp.c
View file

@ -1,54 +1,119 @@
#include <string.h>
#include <lpc2148/ssp.h>
#include <lpc2148/gpio.h>
#include <lpc2148/pcb.h>
#include <lpc2148/vic.h>
#include "ssp.h"
#include "gpio.h"
#include "dma.h"
#include "isr.h"
// The SSP keeps its state in these private static variables.
// The interface is through public functions.
static volatile struct ssp_job {
unsigned int cmd_count;
unsigned int read_count;
unsigned int idle_count;
unsigned int buf_count;
unsigned int idle;
int count;
unsigned short *command;
unsigned int flags;
int compress_count;
unsigned short *buf_start;
unsigned short *buf_ptr;
int buf_count;
unsigned int word_in;
unsigned int maxidlecount;
} job;
unsigned int ssp_bits;
volatile unsigned int ssp_lastword;
unsigned int ssp_ssel_mask = SSP_SSEL_MASK0 | SSP_SSEL_MASK1;
unsigned int ssp_idleword;
unsigned int ssp_lastword;
unsigned int ssp_dma_size;
unsigned int ssp_min_size = 1;
unsigned int ssp_block_idle = 256;
unsigned int ssp_block_size = 3;
unsigned int ssp_read_size;
unsigned int ssp_idle;
unsigned int ssp_match_mask;
unsigned int ssp_match_value;
unsigned int ssp_match_count;
unsigned short *ssp_buffer;
volatile unsigned int ssp_frame_count;
unsigned int ssp_get_status(void)
{
unsigned int f = job.flags;
if (job.count)
f |= ssp_do_cmd;
if (job.idle)
f |= ssp_do_idle;
if (job.buf_count)
f |= ssp_have_buffer;
return f;
}
unsigned short ssp_scratch[256];
// This is called when the SSP Rx buffer is full. It shall return
// zero when a new buffer was made available
// When the buffer could not be taken care of, return non-zero, we
// will call again soon.
// The SSP is stopped when no buffer is made available, i.e., we do
// not send any further frames.
void ssp_set_buffer(void *start, int size)
///// extern void *dma_poll(void *start);
// Provide a new buffer with size bytes, size must be a multiple of 2.
// Normally, this should be 512 bytes. This should be called by
// by the DMA enable with the first buffer..
static unsigned short scratch[16];
static inline void ssp_set_buffer(void *start, int size)
{
unsigned int iflg = disable_irq(INT_DISABLE);
volatile struct ssp_job *j=&job;
if (!(size && start)) {
start = ssp_scratch;
size = sizeof(ssp_scratch);
j->flags &=~ ssp_do_dma;
start = scratch;
size = sizeof(scratch);
}
ssp_buffer = (unsigned short *)start;
j->buf_start = (unsigned short *)start;
j->buf_ptr = (unsigned short *)start;
j->buf_count = size/2;
enable_irq(iflg);
}
static inline int get_new_buffer(void *oldbuf)
{
// We disable IRQs for the main loop to ge a chance to run
SSPIMSC = 0;
volatile struct ssp_job *j=&job;
if (j->flags & ssp_do_dma) {
// When we run out of ssp_dma_size, just wait for somebody
// to increase it again ...
if (!ssp_dma_size)
return 1;
void *newbuf = dma_poll(oldbuf);
if (newbuf) {
j->buf_start = newbuf;
j->buf_ptr = newbuf;
j->buf_count = 512/2;
ssp_dma_size--;
return 0;
}
return 91;
}
j->buf_ptr = scratch;
j->buf_count = sizeof(scratch)/2;
return 0;
}
/*
THE FOLLOWING COMMENT IS OBSOLETE, we now use 16-bit frames
All incomming data goes into 512 byte buffers. Well, we require
the size of the buffer to be a multiple of 2 16-bit words.
j->buf_ptr points to the next free 16-bit word, j->buf_count is the
number of free 16-bit words. j->buf_start is the start of the buffer,
for the dma_poll to check if we are in sync.
The FPGA shall send ssp_idleword when no data is available. When requested
(e.g., for FIFO streaming), sequences of those will be run length
compressed. every word with value ssp_idleword will be followed by the
number of occurances, or 0xffff there were more.
*/
// ssp_poll shall be called by the main loop once, to reenable the IRQ.
// Other places may call this when they think there should be free traffic,
// or when they wait for traffic.
@ -56,10 +121,12 @@ void ssp_set_buffer(void *start, int size)
void ssp_poll(void)
{
volatile struct ssp_job *j=&job;
if (j->idle_count || j->read_count || j->cmd_count || SSPSR & (SSPSR_RNE|SSPSR_BSY))
if (j->idle || j->count || SSPSR & (SSPSR_RNE|SSPSR_BSY))
SSPIMSC = SSP_INT_RX | SSP_INT_TX;
}
unsigned int ssp_stalled_count;
// we may want to put this on a rather low priority IRQ line
// because this one will saturate the CPU.
@ -68,33 +135,96 @@ static void ssp_isr(void)
{
volatile struct ssp_job *j=&job;
// Somehow we could not get rid of the data last time.
// Try again.
if (!j->buf_count) {
if (get_new_buffer(j->buf_start)) {
VICVectAddr = 0;
return;
}
if (j->word_in) {
// We have a word in the cache
// to go into the buffer
*j->buf_ptr++ = j->word_in;
--j->buf_count;
j->word_in = 0;
}
}
unsigned int cc = ssp_compressing(j);
unsigned int flags = j->flags;
unsigned int sr = SSPSR;
unsigned int nread = 0;
unsigned int rc = j->read_count;
int nread = 0;
while (sr&SSPSR_RNE) {
unsigned int dr = SSPDR;
sr=SSPSR;
nread++;
ssp_lastword = ssp_lastword << ssp_bits | dr;
if (!rc
&& ssp_frame_count++ >= ssp_match_count
&& (dr & ssp_match_mask) == ssp_match_value) {
rc = ssp_read_size;
j->idle_count = 0;
}
if (rc) {
rc--;
unsigned int bc = j->buf_count;
if (bc) {
*j->buf_ptr++ = dr;
j->buf_count = bc-1;
if (cc) {
// we were counting idlewords ...
if (dr==ssp_idleword) {
// we continue counting idle words
if (++cc >= 0x10000)
cc = 0xffff;
unsigned int mic = j->maxidlecount;
if (mic && cc>=mic)
j->idle = 0;
if (flags & ssp_nonblock && cc >= ssp_block_idle) {
// To send a partial buffer in
// nonblock mode, the buffer
// must have two words
// content, i.e., more than an
// idleword.
unsigned int c = j->buf_count;
if (c + ssp_block_size < 256) {
unsigned short *p = j->buf_ptr;
*p++ = cc;
while (--c)
*p++ = 0;
if (get_new_buffer(j->buf_start))
SSPIMSC = 0;
else
cc = 0;
}
}
continue;
}
*j->buf_ptr++ = cc;
cc = 0;
if (!--j->buf_count && get_new_buffer(j->buf_start)) {
j->word_in = dr|0x10000;
goto stall;
}
// Fall thru, put the word into the buffer
}
else if (dr==ssp_idleword) {
if (flags & ssp_skipidle) {
// give the mainloop a chance to run
SSPIMSC = 0;
continue;
}
if (flags & ssp_compress)
// This is the start of an idle sequence,
cc = 1;
// Fall thru, put the word into the buffer
}
*j->buf_ptr++ = dr;
ssp_lastword = dr;
if (!--j->buf_count && get_new_buffer(j->buf_start)) {
// 512 bytes received, do something with it.
// If we cannot get rid of the data just now,
// do not clock the ssp further.
stall:
j->compress_count = cc;
ssp_stalled_count++;
VICVectAddr = 0;
return;
}
}
j->read_count = rc;
if (!nread && !rc && !(sr&SSPSR_BSY) && !j->cmd_count && !j->idle_count) {
j->compress_count = cc;
if (!nread && !(sr&SSPSR_BSY) && !ssp_idling(j) && !j->count) {
// there is nothing left to do
SSPIMSC = 0;
VICVectAddr = 0;
@ -114,29 +244,27 @@ static void ssp_isr(void)
if (!(sr & (SSPSR_BSY|SSPSR_RNE)))
nread = 8;
}
if (rc && nread > rc)
nread = rc;
unsigned int ic = j->idle_count;
while (nread && (sr&SSPSR_TNF)) {
if (j->cmd_count) {
j->cmd_count--;
if (j->count) {
j->count--;
SSPDR = *j->command++;
}
else {
if (!(rc || ic)) {
if (!ssp_idling(j)) {
// nothing to send, disable TX irq
SSPIMSC = SSP_INT_RX;
break;
}
if (ic)
ic--;
SSPDR = ssp_idle;
SSPDR = j->idle;
}
nread--;
sr = SSPSR;
}
j->idle_count = ic;
// we disable IRQs when there is no data, for the main loop to ge a chance
if (cc>0x100 && !j->count)
SSPIMSC = 0;
VICVectAddr = 0;
}
@ -151,15 +279,17 @@ static void ssp_isr(void)
// idle=0x8000 RFIFO, send what the acquisition fifos provide.
// idle=0x8001 NOOPP, just drain the FPGA buffer.
int ssp_submit(unsigned short *cmd, int cmd_size, unsigned int ic, unsigned int rc)
int ssp_submit(unsigned short *cmd, int cmd_size, unsigned int idle, unsigned int flags)
{
volatile struct ssp_job *j=&job;
unsigned int c = j->count;
if (c)
return c;
unsigned int iflg = disable_irq(INT_DISABLE);
j->command = cmd;
j->cmd_count = cmd_size;
j->idle_count = ic;
j->read_count = rc;
ssp_frame_count = 0;
j->count = cmd_size;
j->idle = idle;
j->flags = flags;
enable_irq(iflg);
ssp_poll();
return 0;
@ -170,31 +300,24 @@ void ssp_reset(void)
SSPIMSC = 0;
volatile struct ssp_job *j=&job;
unsigned int iflg = disable_irq(INT_DISABLE);
j->cmd_count = 0;
j->idle_count = 0;
j->read_count = 0;
j->idle = 0;
j->count = 0;
j->flags = 0;
j->word_in = 0;
j->compress_count = 0;
ssp_set_buffer(0,0);
ssp_injection_write_ptr = 0;
ssp_injection_read_ptr = 0;
ssp_dma_size = 0;
enable_irq(iflg);
}
struct ssp_config ssp_config;
void ssp_deassert_ssel()
{
GPIO_FIO1SET = ssp_ssel_mask & SSP_SSEL_CONF1;
GPIO_FIO0SET = ssp_ssel_mask & SSP_SSEL_CONF0;
}
void ssp_init(struct ssp_config *c)
void ssp_init(int pclk)
{
ssp_reset();
ssp_ssel_mask |= c->ssel;
ssp_deassert_ssel();
ssp_config = *c;
ssp_bits = (c->mode & 15) + 1;
SSPCR1 = 0;
SSPCPSR = SSPCPSR_MIN; // 30 MHz
SSPCR0 = c->mode;
// pclk is ignored, we run as fast as we can (pclk/2).
SSPCPSR= SSPCPSR_MIN; // Prescaler div by 2, 60 --> 30 MHz
SSPCR0 = SSPCR0_16bits | SSPCR0_SPI | SSPCR0_CPHA | SSPCR0_SCR(1);
// 16 bit frames, SPI mode, CPOL=0, CPHA=1, clock div by 1 (30 MHz).
SSPICR = SSP_INT_ROR|SSP_INT_RT; // clear any pending irqs.
// Route the SSP IRQ through the VIC
@ -204,69 +327,298 @@ void ssp_init(struct ssp_config *c)
SSPCR1 = SSPCR1_SSE; // enable
}
void ssp_assert_ssel(unsigned int ssel)
{
if (ssel & ssp_flag_mclk)
PCB_PINSEL1 |= PCB_PINSEL1_P016_Match0_2;
else
PCB_PINSEL1 &=~ PCB_PINSEL1_P016_Match0_2;
GPIO_FIO1SET = ssp_ssel_mask & SSP_SSEL_CONF1 & ~ssel;
GPIO_FIO0SET = ssp_ssel_mask & SSP_SSEL_CONF0 & ~ssel;
GPIO_FIO1DIR |= SSP_SSEL_CONF1 & ssel;
GPIO_FIO0DIR |= SSP_SSEL_CONF0 & ssel;
}
// When this command returns zero, the SSP is idle and reset
int ssp_busy(void)
int ssp_flush(void)
{
volatile struct ssp_job *j=&job;
j->idle = 0; // stop streaming whatever
ssp_poll(); // run the isr to drain the backlog
// j->* is volatile and we don't want races
unsigned int c = j->cmd_count + j->idle_count + j->read_count;
while (!j->buf_count) {
// dma is busy, abort whatever we do
unsigned int iflg = disable_irq(INT_DISABLE);
if (!j->buf_count) {
ssp_reset();
// drain the ssp hardware
while (SSPSR&(SSPSR_RNE|SSPSR_BSY))
if (SSPSR&SSPSR_RNE) {
unsigned int x = SSPDR;
}
enable_irq(iflg);
return 0;
}
enable_irq(iflg);
}
// j->count is volatile and we don't want races
unsigned int c = j->count;
if (c)
return c;
// Hardware is busy
if (SSPSR&(SSPSR_RNE|SSPSR_BSY))
return 1;
done:
return 90;
// Submit the last dma buffer
if (j->flags & ssp_do_dma) {
unsigned int iflg = disable_irq(INT_DISABLE);
if (j->flags & ssp_do_dma) {
unsigned short *b=j->buf_ptr;
unsigned short *s=j->buf_start;
unsigned int n = j->buf_count;
ssp_reset();
enable_irq(iflg);
if (n && b>s) {
memset(b, 0, 2*n);
dma_poll(s);
}
return 0;
}
enable_irq(iflg);
}
ssp_reset();
return 0;
}
int ssp_wait(int timeout)
// Send a 16 or 32 bit command to the FPGA.
// 'read' tells how many NOOPPs need to be send, 0..3.
int ssp_send_command(unsigned int cmd, int read)
{
while (ssp_busy() && timeout-->0);
return !timeout;
int r = ssp_flush();
if (r)
return r;
ssp_injection_write(cmd|0x8000, cmd&0x4000 ? 2 : 1);
while (read>=4) {
ssp_injection_write(0,2);
read -= 2;
}
switch (read) {
case 3: ssp_injection_write(0,1);
case 2: ssp_injection_write(0x8001,2);
break;
case 1: ssp_injection_write(0,1);
}
return ssp_injection_submit(1);
}
const struct ssp_config ssp_conf_adc =
// Read the command response, i.e., the last 16 bits received.
// Return 0 when done, non-zero when the SSP is still busy.
int ssp_read_response(unsigned short *res)
{
.mode = SSPCR0_16bits | SSPCR0_CPHA | SSP_kHz(10000),
.ssel = SSP_ADS8688,
};
const struct ssp_config ssp_conf_adc_daisy =
int r = ssp_flush();
if (!r)
*res = ssp_lastword;
return r;
}
// Return extended response from scratch
int ssp_wait(int timeout)
{
.mode = SSPCR0_16bits | SSPCR0_CPHA | SSP_kHz(1000),
.ssel = SSP_ADS8688 | SSP_SSEL,
};
const struct ssp_config ssp_conf_dac =
volatile struct ssp_job *j=&job;
if (j->idle)
return 92;
while ((j->count || SSPSR&(SSPSR_RNE|SSPSR_BSY)) && timeout-->0)
ssp_poll();
if (j->count)
return 93;
return 0;
}
int ssp_extended_response(unsigned short *res, int size)
{
.mode = SSPCR0_16bits | SSP_kHz(30000),
.ssel = SSP_LTC2656,
};
const struct ssp_config ssp_conf_dac_daisy =
int r = ssp_wait(1000);
if (r)
return -r;
volatile struct ssp_job *j=&job;
if (j->buf_start != scratch)
return -94;
int n=0;
unsigned short *p = j->buf_start;
if (j->buf_ptr - j->buf_start > size)
p = j->buf_ptr - size;
while (p < j->buf_ptr && n++<size)
*res++ = *p++;
ssp_flush();
return n;
}
#ifndef DLRENA_SSP
// Turn on DMA streaming.
// Turn off with ssp_flush().
int ssp_enable_dma(unsigned int idle, unsigned int flags, int usbspi, unsigned int size)
{
.mode = SSPCR0_16bits | SSP_kHz(1000),
.ssel = SSP_LTC2656 | SSP_SSEL,
};
const struct ssp_config ssp_conf_bate =
int r = ssp_flush();
if (r)
return r;
void *buf = dma_reset(usbspi&1, usbspi&2);
if (!buf)
return 95;
ssp_set_buffer(buf, 512);
ssp_dma_size = size;
volatile struct ssp_job *j=&job;
j->maxidlecount = 0;
j->flags = flags;
j->idle = idle;
ssp_poll();
return 0;
}
int ssp_sync(unsigned short *r)
{
.mode = SSPCR0_16bits | SSP_kHz(250),
.ssel = SSP_MS5534C,
};
const struct ssp_config ssp_conf_bate_ext =
int e = ssp_flush();
if (e)
return e;
ssp_set_buffer(0,0);
ssp_submit(r, 1, 0, 0);
return 0;
}
void ssp_idle(unsigned int idle, unsigned int mic)
{
.mode = SSPCR0_16bits | SSP_kHz(120),
.ssel = SSP_EXT | SSP_MCLK,
};
volatile struct ssp_job *j=&job;
j->idle = idle;
j->maxidlecount = mic;
ssp_poll();
}
int ssp_idle_wait(unsigned int idle, unsigned int mic)
{
volatile struct ssp_job *j=&job;
ssp_idle(idle, mic);
while (mic & j->idle) {
mic--;
ssp_poll();
}
if (!j->buf_count)
return ssp_flush();
return j->idle;
}
#endif
// API for injecting a stream of commands while we block during DMA
unsigned short ssp_injection_buffer[SSP_INJ_SIZE];
unsigned int ssp_injection_write_ptr = 0;
unsigned int ssp_injection_read_ptr = 0;
int ssp_injection_reset(unsigned int timeout)
{
volatile struct ssp_job *j=&job;
if (ssp_injection_read_ptr)
while (j->count) {
if (!--timeout) {
ssp_injection_write_ptr = ssp_injection_read_ptr;
return 80;
}
ssp_poll();
}
ssp_injection_write_ptr = 0;
ssp_injection_read_ptr = 0;
return 0;
}
int ssp_injection_abort(unsigned int timeout)
{
volatile struct ssp_job *j=&job;
if (ssp_injection_reset(timeout)) {
unsigned int iflg = disable_irq(INT_DISABLE);
j->count = 0;
enable_irq(iflg);
ssp_injection_write_ptr = 0;
ssp_injection_read_ptr = 0;
}
return 0;
}
int ssp_injection_write_buf(unsigned short *cmd, unsigned int csize)
{
if (ssp_injection_read_ptr && ssp_injection_write_ptr==ssp_injection_read_ptr)
ssp_injection_reset(2);
if (ssp_injection_write_ptr+csize>SSP_INJ_SIZE)
return 81;
while (csize--)
ssp_injection_buffer[ssp_injection_write_ptr++] = *cmd++;
return 0;
}
int ssp_injection_write(unsigned int cmd, unsigned int csize)
{
// return ssp_injection_write_buf((unsigned short *)&cmd, csize);
if (ssp_injection_read_ptr && ssp_injection_write_ptr==ssp_injection_read_ptr)
ssp_injection_reset(2);
if (ssp_injection_write_ptr+csize>SSP_INJ_SIZE)
return 82;
if (csize>=1)
ssp_injection_buffer[ssp_injection_write_ptr++] = cmd&0xffff;
if (csize>=2)
ssp_injection_buffer[ssp_injection_write_ptr++] = cmd>>16;
return 0;
}
int ssp_injection_write_submit(unsigned int cmd, unsigned int csize)
{
int e = ssp_injection_write(cmd, csize);
if (e==82) {
e = ssp_injection_submit(100);
if (!e)
e = ssp_injection_write(cmd, csize);
}
return e;
}
int ssp_injection_submit(unsigned int timeout)
{
if (ssp_injection_write_ptr <= ssp_injection_read_ptr)
return 83;
unsigned int csize = ssp_injection_write_ptr - ssp_injection_read_ptr;
unsigned short *command = ssp_injection_buffer + ssp_injection_read_ptr;
volatile struct ssp_job *j=&job;
unsigned int iflg = disable_irq(INT_DISABLE);
if (j->count && j->command + j->count == command) {
j->count += csize;
ssp_injection_read_ptr = ssp_injection_write_ptr;
enable_irq(iflg);
return 0;
}
enable_irq(iflg);
while (j->count) {
if (!--timeout)
return 84;
ssp_poll();
}
if (ssp_injection_read_ptr) {
for (unsigned int i=0; i<csize; i++)
ssp_injection_buffer[i] = ssp_injection_buffer[ssp_injection_read_ptr+i];
ssp_injection_read_ptr = 0;
ssp_injection_write_ptr = csize;
command = ssp_injection_buffer;
}
iflg = disable_irq(INT_DISABLE);
j->command = command;
j->count = csize;
ssp_injection_read_ptr = ssp_injection_write_ptr;
enable_irq(iflg);
ssp_poll();
return 0;
}
int ssp_inject_command(unsigned int cmd, unsigned int csize, unsigned int timeout)
{
ssp_injection_reset(timeout);
int r = ssp_injection_write(cmd, csize);
if (!r)
r = ssp_injection_submit(timeout);
return r;
}

87
ssp.h
View file

@ -1,49 +1,64 @@
void ssp_init(int pclk);
void ssp_reset(void);
void ssp_set_buffer(void *start, int size);
void ssp_poll(void);
int ssp_submit(unsigned short *cmd, int cmd_size, unsigned int idle, unsigned int compress);
int ssp_busy(void);
int ssp_wait(int timeout);
int ssp_flush(void);
int ssp_send_command(unsigned int cmd, int read);
int ssp_inject_command(unsigned int cmd, unsigned int csize, unsigned int timeout);
extern unsigned int ssp_bits;
extern volatile unsigned int ssp_lastword;
extern unsigned int ssp_ssel_mask;
extern unsigned int ssp_ssel;
extern unsigned int ssp_read_size;
extern unsigned int ssp_idle;
extern unsigned int ssp_match_mask;
extern unsigned int ssp_match_value;
extern unsigned int ssp_match_count;
extern volatile unsigned int ssp_frame_count;
extern unsigned short ssp_scratch[256];
int ssp_read_response(unsigned short *res);
int ssp_extended_response(unsigned short *res, int size);
int ssp_enable_dma(unsigned int idle, unsigned int flags, int usbspi, unsigned int size);
int ssp_sync(unsigned short *r);
void ssp_idle(unsigned int idle, unsigned int mic);
int ssp_idle_wait(unsigned int idle, unsigned int mic);
unsigned int ssp_get_status(void);
extern struct ssp_config {
unsigned int ssel;
unsigned int mode;
} ssp_config;
extern unsigned int ssp_idleword;
extern unsigned int ssp_lastword;
extern unsigned int ssp_dma_size;
extern unsigned int ssp_block_idle;
extern unsigned int ssp_block_size;
enum ssp_config_flags {
ssp_flag_mclk = 0x01,
enum ssp_flags {
ssp_compress = 1,
ssp_do_dma = 2,
ssp_skipidle = 4,
ssp_nonblock = 8,
ssp_have_buffer = 0x100,
ssp_do_idle = 0x200,
ssp_do_cmd = 0x400,
};
void ssp_init(struct ssp_config *c);
void ssp_deassert_ssel();
void ssp_assert_ssel(unsigned int ssel);
#define SSP_INJ_SIZE 2048
extern unsigned short ssp_injection_buffer[SSP_INJ_SIZE];
extern unsigned int ssp_injection_write_ptr;
extern unsigned int ssp_injection_read_ptr;
int ssp_injection_reset(unsigned int timeout);
int ssp_injection_abort(unsigned int timeout);
int ssp_injection_write_buf(unsigned short *cmd, unsigned int csize);
int ssp_injection_write(unsigned int cmd, unsigned int csize);
int ssp_injection_write_submit(unsigned int cmd, unsigned int csize);
int ssp_injection_submit(unsigned int timeout);
#define SSP_kHz(kHz) SSPCR0_SCR(30000/(kHz))
static inline void disable_ssp_dma() { ssp_dma_size = 0; }
#define SSP_MCLK ssp_flag_mclk
#define SSP_LTC2656 (1<<11)
#define SSP_ADS8688 (1<<20)
#define SSP_EXT (1<<24)
#define SSP_SSEL ((1<<16)|SSP_EXT)
#define SSP_MS5534C ((1<<25)|SSP_MCLK)
// DLRENA does not do ssp dma
#ifndef DLRENA_SSP
extern const struct ssp_config ssp_conf_adc;
extern const struct ssp_config ssp_conf_adc_daisy;
extern const struct ssp_config ssp_conf_dac;
extern const struct ssp_config ssp_conf_dac_daisy;
extern const struct ssp_config ssp_conf_bate;
extern const struct ssp_config ssp_conf_bate_ext;
#define ssp_idle(j) j->idle
#define ssp_compress(j) j->compress_count
#else
#define ssp_do_dma 0
#define ssp_do_idle 0
#define ssp_compress 0
#define ssp_skipidle 0
#define ssp_idling(j) 0
#define ssp_compressing(j) 0
#endif