Compare commits

...

2 commits

Author SHA1 Message Date
Stephan I. Böttcher
560f7b806b ssp rewrite, gpio
Drop ssp compatibility, injection, etc.
Rewrite the isr optimised for small messages.
Allow operation without buffer.

Add gpio documentation and simple gpio primitives.
2024-10-31 19:00:19 +01:00
Stephan I. Böttcher
88f7f36fb4 printf: fix whitespace 2024-10-29 20:03:47 +01:00
11 changed files with 297 additions and 629 deletions

1
.gitignore vendored
View file

@ -8,3 +8,4 @@ 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 \
pressure gpio \
adc rtc \
plugin \
nomalloc strtol uart uart1 base85

View file

@ -13,16 +13,6 @@
#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[] = {
@ -117,15 +107,3 @@ 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,64 +1,6 @@
#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,
@ -103,4 +45,6 @@ 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 Normal file
View file

@ -0,0 +1,24 @@
#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 Normal file
View file

@ -0,0 +1,105 @@
#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,24 +647,8 @@ 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++;
@ -894,7 +878,6 @@ const struct keywords *variable_names[] = {
script_variable_names,
eth_variable_names,
pressure_variable_names,
altera_variable_names,
0
};

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.

584
ssp.c
View file

@ -1,119 +1,54 @@
#include <string.h>
#include <lpc2148/ssp.h>
#include <lpc2148/gpio.h>
#include <lpc2148/pcb.h>
#include <lpc2148/vic.h>
#include "ssp.h"
#include "dma.h"
#include "gpio.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 idle;
int count;
unsigned int cmd_count;
unsigned int read_count;
unsigned int idle_count;
unsigned int buf_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_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_bits;
volatile unsigned int ssp_lastword;
unsigned int ssp_ssel_mask = SSP_SSEL_MASK0 | SSP_SSEL_MASK1;
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 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;
// 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.
unsigned short ssp_scratch[256];
///// 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)
void ssp_set_buffer(void *start, int size)
{
unsigned int iflg = disable_irq(INT_DISABLE);
volatile struct ssp_job *j=&job;
if (!(size && start)) {
j->flags &=~ ssp_do_dma;
start = scratch;
size = sizeof(scratch);
start = ssp_scratch;
size = sizeof(ssp_scratch);
}
j->buf_start = (unsigned short *)start;
ssp_buffer = (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.
@ -121,12 +56,10 @@ static inline int get_new_buffer(void *oldbuf)
void ssp_poll(void)
{
volatile struct ssp_job *j=&job;
if (j->idle || j->count || SSPSR & (SSPSR_RNE|SSPSR_BSY))
if (j->idle_count || j->read_count || j->cmd_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.
@ -135,96 +68,33 @@ 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;
int nread = 0;
unsigned int nread = 0;
unsigned int rc = j->read_count;
while (sr&SSPSR_RNE) {
unsigned int dr = SSPDR;
sr=SSPSR;
nread++;
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
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;
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->buf_count = bc-1;
}
}
}
j->read_count = rc;
j->compress_count = cc;
if (!nread && !(sr&SSPSR_BSY) && !ssp_idling(j) && !j->count) {
if (!nread && !rc && !(sr&SSPSR_BSY) && !j->cmd_count && !j->idle_count) {
// there is nothing left to do
SSPIMSC = 0;
VICVectAddr = 0;
@ -244,26 +114,28 @@ 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->count) {
j->count--;
if (j->cmd_count) {
j->cmd_count--;
SSPDR = *j->command++;
}
else {
if (!ssp_idling(j)) {
if (!(rc || ic)) {
// nothing to send, disable TX irq
SSPIMSC = SSP_INT_RX;
break;
}
SSPDR = j->idle;
if (ic)
ic--;
SSPDR = ssp_idle;
}
nread--;
sr = SSPSR;
}
// we disable IRQs when there is no data, for the main loop to ge a chance
if (cc>0x100 && !j->count)
SSPIMSC = 0;
j->idle_count = ic;
VICVectAddr = 0;
}
@ -279,17 +151,15 @@ 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 idle, unsigned int flags)
int ssp_submit(unsigned short *cmd, int cmd_size, unsigned int ic, unsigned int rc)
{
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->count = cmd_size;
j->idle = idle;
j->flags = flags;
j->cmd_count = cmd_size;
j->idle_count = ic;
j->read_count = rc;
ssp_frame_count = 0;
enable_irq(iflg);
ssp_poll();
return 0;
@ -300,24 +170,31 @@ void ssp_reset(void)
SSPIMSC = 0;
volatile struct ssp_job *j=&job;
unsigned int iflg = disable_irq(INT_DISABLE);
j->idle = 0;
j->count = 0;
j->flags = 0;
j->word_in = 0;
j->compress_count = 0;
j->cmd_count = 0;
j->idle_count = 0;
j->read_count = 0;
ssp_set_buffer(0,0);
ssp_injection_write_ptr = 0;
ssp_injection_read_ptr = 0;
ssp_dma_size = 0;
enable_irq(iflg);
}
void ssp_init(int pclk)
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)
{
ssp_reset();
// 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);
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;
// 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
@ -327,298 +204,69 @@ void ssp_init(int pclk)
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_flush(void)
int ssp_busy(void)
{
volatile struct ssp_job *j=&job;
j->idle = 0; // stop streaming whatever
ssp_poll(); // run the isr to drain the backlog
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;
// j->* is volatile and we don't want races
unsigned int c = j->cmd_count + j->idle_count + j->read_count;
if (c)
return c;
// Hardware is busy
if (SSPSR&(SSPSR_RNE|SSPSR_BSY))
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);
}
return 1;
done:
ssp_reset();
return 0;
}
// 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)
{
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);
}
// 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)
{
int r = ssp_flush();
if (!r)
*res = ssp_lastword;
return r;
}
// Return extended response from scratch
int ssp_wait(int timeout)
{
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;
while (ssp_busy() && timeout-->0);
return !timeout;
}
int ssp_extended_response(unsigned short *res, int size)
const struct ssp_config ssp_conf_adc =
{
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 | SSPCR0_CPHA | SSP_kHz(10000),
.ssel = SSP_ADS8688,
};
const struct ssp_config ssp_conf_adc_daisy =
{
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 | SSPCR0_CPHA | SSP_kHz(1000),
.ssel = SSP_ADS8688 | SSP_SSEL,
};
const struct ssp_config ssp_conf_dac =
{
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(30000),
.ssel = SSP_LTC2656,
};
const struct ssp_config ssp_conf_dac_daisy =
{
volatile struct ssp_job *j=&job;
j->idle = idle;
j->maxidlecount = mic;
ssp_poll();
}
int ssp_idle_wait(unsigned int idle, unsigned int mic)
.mode = SSPCR0_16bits | SSP_kHz(1000),
.ssel = SSP_LTC2656 | SSP_SSEL,
};
const struct ssp_config ssp_conf_bate =
{
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)
.mode = SSPCR0_16bits | SSP_kHz(250),
.ssel = SSP_MS5534C,
};
const struct ssp_config ssp_conf_bate_ext =
{
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;
}
.mode = SSPCR0_16bits | SSP_kHz(120),
.ssel = SSP_EXT | SSP_MCLK,
};

87
ssp.h
View file

@ -1,64 +1,49 @@
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_flush(void);
int ssp_send_command(unsigned int cmd, int read);
int ssp_inject_command(unsigned int cmd, unsigned int csize, unsigned int timeout);
int ssp_busy(void);
int ssp_wait(int timeout);
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 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];
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;
extern struct ssp_config {
unsigned int ssel;
unsigned int mode;
} ssp_config;
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,
enum ssp_config_flags {
ssp_flag_mclk = 0x01,
};
#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);
void ssp_init(struct ssp_config *c);
void ssp_deassert_ssel();
void ssp_assert_ssel(unsigned int ssel);
static inline void disable_ssp_dma() { ssp_dma_size = 0; }
#define SSP_kHz(kHz) SSPCR0_SCR(30000/(kHz))
// DLRENA does not do ssp dma
#ifndef DLRENA_SSP
#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)
#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
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;