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__ __pycache__
revision.h+ revision.h+
*~ *~
*.s

View file

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

View file

@ -13,16 +13,6 @@
#include "script.h" #include "script.h"
#include <string.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; unsigned int stream_flags;
static const struct keywords stream_enable_kw[] = { 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; 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 #ifndef _altera_h
#define _altera_h #define _altera_h
#include "parser.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 { enum {
stk_dma_ch = 3, stk_dma_ch = 3,
@ -103,4 +45,6 @@ extern unsigned int stream_flags;
error_msg_t stream_destination(unsigned int where); error_msg_t stream_destination(unsigned int where);
extern const struct command_par err_invalid_source; extern const struct command_par err_invalid_source;
static inline void disable_ssp_dma() {}
#endif #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. // qtime() is the time of day in units of 1/2¹⁵ seconds.
if (qtime_limit_test(&spi_qtime_limit)) if (qtime_limit_test(&spi_qtime_limit))
buf = dma_get_spi_block(); buf = dma_get_spi_block();
if (buf) { if (buf)
qtime_limit_set(&spi_qtime_limit); 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 (buf && uart_size)
if (uart_send_dma_block_base64(buf)) if (uart_send_dma_block_base64(buf))
uart_size--, uart_count++; uart_size--, uart_count++;
@ -894,7 +878,6 @@ const struct keywords *variable_names[] = {
script_variable_names, script_variable_names,
eth_variable_names, eth_variable_names,
pressure_variable_names, pressure_variable_names,
altera_variable_names,
0 0
}; };

View file

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

6
spi.c
View file

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

584
ssp.c
View file

@ -1,119 +1,54 @@
#include <string.h> #include <string.h>
#include <lpc2148/ssp.h> #include <lpc2148/ssp.h>
#include <lpc2148/gpio.h>
#include <lpc2148/pcb.h>
#include <lpc2148/vic.h> #include <lpc2148/vic.h>
#include "ssp.h" #include "ssp.h"
#include "dma.h" #include "gpio.h"
#include "isr.h" #include "isr.h"
// The SSP keeps its state in these private static variables. // The SSP keeps its state in these private static variables.
// The interface is through public functions. // The interface is through public functions.
static volatile struct ssp_job { static volatile struct ssp_job {
unsigned int idle; unsigned int cmd_count;
int count; unsigned int read_count;
unsigned int idle_count;
unsigned int buf_count;
unsigned short *command; unsigned short *command;
unsigned int flags;
int compress_count;
unsigned short *buf_start;
unsigned short *buf_ptr; unsigned short *buf_ptr;
int buf_count;
unsigned int word_in;
unsigned int maxidlecount;
} job; } job;
unsigned int ssp_idleword; unsigned int ssp_bits;
unsigned int ssp_lastword; volatile unsigned int ssp_lastword;
unsigned int ssp_dma_size; unsigned int ssp_ssel_mask = SSP_SSEL_MASK0 | SSP_SSEL_MASK1;
unsigned int ssp_min_size = 1;
unsigned int ssp_block_idle = 256;
unsigned int ssp_block_size = 3;
unsigned int ssp_get_status(void) unsigned int ssp_read_size;
{ unsigned int ssp_idle;
unsigned int f = job.flags; unsigned int ssp_match_mask;
if (job.count) unsigned int ssp_match_value;
f |= ssp_do_cmd; unsigned int ssp_match_count;
if (job.idle) unsigned short *ssp_buffer;
f |= ssp_do_idle; volatile unsigned int ssp_frame_count;
if (job.buf_count)
f |= ssp_have_buffer;
return f;
}
// This is called when the SSP Rx buffer is full. It shall return unsigned short ssp_scratch[256];
// 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.
///// extern void *dma_poll(void *start); void ssp_set_buffer(void *start, int size)
// 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); unsigned int iflg = disable_irq(INT_DISABLE);
volatile struct ssp_job *j=&job; volatile struct ssp_job *j=&job;
if (!(size && start)) { if (!(size && start)) {
j->flags &=~ ssp_do_dma; start = ssp_scratch;
start = scratch; size = sizeof(ssp_scratch);
size = sizeof(scratch);
} }
j->buf_start = (unsigned short *)start; ssp_buffer = (unsigned short *)start;
j->buf_ptr = (unsigned short *)start; j->buf_ptr = (unsigned short *)start;
j->buf_count = size/2; j->buf_count = size/2;
enable_irq(iflg); 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. // 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, // Other places may call this when they think there should be free traffic,
// or when they wait for traffic. // or when they wait for traffic.
@ -121,12 +56,10 @@ static inline int get_new_buffer(void *oldbuf)
void ssp_poll(void) void ssp_poll(void)
{ {
volatile struct ssp_job *j=&job; 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; 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 // we may want to put this on a rather low priority IRQ line
// because this one will saturate the CPU. // because this one will saturate the CPU.
@ -135,96 +68,33 @@ static void ssp_isr(void)
{ {
volatile struct ssp_job *j=&job; 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 sr = SSPSR;
int nread = 0; unsigned int nread = 0;
unsigned int rc = j->read_count;
while (sr&SSPSR_RNE) { while (sr&SSPSR_RNE) {
unsigned int dr = SSPDR; unsigned int dr = SSPDR;
sr=SSPSR; sr=SSPSR;
nread++; nread++;
if (cc) { ssp_lastword = ssp_lastword << ssp_bits | dr;
// we were counting idlewords ... if (!rc
if (dr==ssp_idleword) { && ssp_frame_count++ >= ssp_match_count
// we continue counting idle words && (dr & ssp_match_mask) == ssp_match_value) {
if (++cc >= 0x10000) rc = ssp_read_size;
cc = 0xffff; j->idle_count = 0;
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 (rc) {
if (flags & ssp_skipidle) { rc--;
// give the mainloop a chance to run unsigned int bc = j->buf_count;
SSPIMSC = 0; if (bc) {
continue; *j->buf_ptr++ = dr;
j->buf_count = bc-1;
} }
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;
j->compress_count = cc; if (!nread && !rc && !(sr&SSPSR_BSY) && !j->cmd_count && !j->idle_count) {
if (!nread && !(sr&SSPSR_BSY) && !ssp_idling(j) && !j->count) {
// there is nothing left to do // there is nothing left to do
SSPIMSC = 0; SSPIMSC = 0;
VICVectAddr = 0; VICVectAddr = 0;
@ -244,27 +114,29 @@ static void ssp_isr(void)
if (!(sr & (SSPSR_BSY|SSPSR_RNE))) if (!(sr & (SSPSR_BSY|SSPSR_RNE)))
nread = 8; nread = 8;
} }
if (rc && nread > rc)
nread = rc;
unsigned int ic = j->idle_count;
while (nread && (sr&SSPSR_TNF)) { while (nread && (sr&SSPSR_TNF)) {
if (j->count) { if (j->cmd_count) {
j->count--; j->cmd_count--;
SSPDR = *j->command++; SSPDR = *j->command++;
} }
else { else {
if (!ssp_idling(j)) { if (!(rc || ic)) {
// nothing to send, disable TX irq // nothing to send, disable TX irq
SSPIMSC = SSP_INT_RX; SSPIMSC = SSP_INT_RX;
break; break;
} }
SSPDR = j->idle; if (ic)
ic--;
SSPDR = ssp_idle;
} }
nread--; nread--;
sr = SSPSR; 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; VICVectAddr = 0;
} }
@ -279,17 +151,15 @@ static void ssp_isr(void)
// idle=0x8000 RFIFO, send what the acquisition fifos provide. // idle=0x8000 RFIFO, send what the acquisition fifos provide.
// idle=0x8001 NOOPP, just drain the FPGA buffer. // 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; volatile struct ssp_job *j=&job;
unsigned int c = j->count;
if (c)
return c;
unsigned int iflg = disable_irq(INT_DISABLE); unsigned int iflg = disable_irq(INT_DISABLE);
j->command = cmd; j->command = cmd;
j->count = cmd_size; j->cmd_count = cmd_size;
j->idle = idle; j->idle_count = ic;
j->flags = flags; j->read_count = rc;
ssp_frame_count = 0;
enable_irq(iflg); enable_irq(iflg);
ssp_poll(); ssp_poll();
return 0; return 0;
@ -300,24 +170,31 @@ void ssp_reset(void)
SSPIMSC = 0; SSPIMSC = 0;
volatile struct ssp_job *j=&job; volatile struct ssp_job *j=&job;
unsigned int iflg = disable_irq(INT_DISABLE); unsigned int iflg = disable_irq(INT_DISABLE);
j->idle = 0; j->cmd_count = 0;
j->count = 0; j->idle_count = 0;
j->flags = 0; j->read_count = 0;
j->word_in = 0;
j->compress_count = 0;
ssp_set_buffer(0,0); ssp_set_buffer(0,0);
ssp_injection_write_ptr = 0;
ssp_injection_read_ptr = 0;
ssp_dma_size = 0;
enable_irq(iflg); 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(); ssp_reset();
// pclk is ignored, we run as fast as we can (pclk/2). ssp_ssel_mask |= c->ssel;
SSPCPSR= SSPCPSR_MIN; // Prescaler div by 2, 60 --> 30 MHz ssp_deassert_ssel();
SSPCR0 = SSPCR0_16bits | SSPCR0_SPI | SSPCR0_CPHA | SSPCR0_SCR(1); 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). // 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. SSPICR = SSP_INT_ROR|SSP_INT_RT; // clear any pending irqs.
// Route the SSP IRQ through the VIC // Route the SSP IRQ through the VIC
@ -327,298 +204,69 @@ void ssp_init(int pclk)
SSPCR1 = SSPCR1_SSE; // enable 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 // 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; volatile struct ssp_job *j=&job;
j->idle = 0; // stop streaming whatever
ssp_poll(); // run the isr to drain the backlog ssp_poll(); // run the isr to drain the backlog
while (!j->buf_count) { // j->* is volatile and we don't want races
// dma is busy, abort whatever we do unsigned int c = j->cmd_count + j->idle_count + j->read_count;
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) if (c)
return c; return c;
// Hardware is busy // Hardware is busy
if (SSPSR&(SSPSR_RNE|SSPSR_BSY)) if (SSPSR&(SSPSR_RNE|SSPSR_BSY))
return 90; return 1;
// Submit the last dma buffer done:
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(); ssp_reset();
return 0; 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) int ssp_wait(int timeout)
{ {
volatile struct ssp_job *j=&job; while (ssp_busy() && timeout-->0);
if (j->idle) return !timeout;
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) const struct ssp_config ssp_conf_adc =
{ {
int r = ssp_wait(1000); .mode = SSPCR0_16bits | SSPCR0_CPHA | SSP_kHz(10000),
if (r) .ssel = SSP_ADS8688,
return -r; };
volatile struct ssp_job *j=&job; const struct ssp_config ssp_conf_adc_daisy =
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)
{ {
int r = ssp_flush(); .mode = SSPCR0_16bits | SSPCR0_CPHA | SSP_kHz(1000),
if (r) .ssel = SSP_ADS8688 | SSP_SSEL,
return r; };
const struct ssp_config ssp_conf_dac =
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)
{ {
int e = ssp_flush(); .mode = SSPCR0_16bits | SSP_kHz(30000),
if (e) .ssel = SSP_LTC2656,
return e; };
ssp_set_buffer(0,0); const struct ssp_config ssp_conf_dac_daisy =
ssp_submit(r, 1, 0, 0);
return 0;
}
void ssp_idle(unsigned int idle, unsigned int mic)
{ {
volatile struct ssp_job *j=&job; .mode = SSPCR0_16bits | SSP_kHz(1000),
j->idle = idle; .ssel = SSP_LTC2656 | SSP_SSEL,
j->maxidlecount = mic; };
ssp_poll(); const struct ssp_config ssp_conf_bate =
}
int ssp_idle_wait(unsigned int idle, unsigned int mic)
{ {
volatile struct ssp_job *j=&job; .mode = SSPCR0_16bits | SSP_kHz(250),
ssp_idle(idle, mic); .ssel = SSP_MS5534C,
while (mic & j->idle) { };
mic--; const struct ssp_config ssp_conf_bate_ext =
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; .mode = SSPCR0_16bits | SSP_kHz(120),
if (ssp_injection_read_ptr) .ssel = SSP_EXT | SSP_MCLK,
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,64 +1,49 @@
void ssp_init(int pclk);
void ssp_reset(void); void ssp_reset(void);
void ssp_set_buffer(void *start, int size);
void ssp_poll(void); void ssp_poll(void);
int ssp_submit(unsigned short *cmd, int cmd_size, unsigned int idle, unsigned int compress); int ssp_submit(unsigned short *cmd, int cmd_size, unsigned int idle, unsigned int compress);
int ssp_flush(void); int ssp_busy(void);
int ssp_send_command(unsigned int cmd, int read); int ssp_wait(int timeout);
int ssp_inject_command(unsigned int cmd, unsigned int csize, unsigned int timeout);
int ssp_read_response(unsigned short *res); extern unsigned int ssp_bits;
int ssp_extended_response(unsigned short *res, int size); extern volatile unsigned int ssp_lastword;
int ssp_enable_dma(unsigned int idle, unsigned int flags, int usbspi, unsigned int size); extern unsigned int ssp_ssel_mask;
int ssp_sync(unsigned short *r); extern unsigned int ssp_ssel;
void ssp_idle(unsigned int idle, unsigned int mic); extern unsigned int ssp_read_size;
int ssp_idle_wait(unsigned int idle, unsigned int mic); extern unsigned int ssp_idle;
unsigned int ssp_get_status(void); 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 struct ssp_config {
extern unsigned int ssp_lastword; unsigned int ssel;
extern unsigned int ssp_dma_size; unsigned int mode;
extern unsigned int ssp_block_idle; } ssp_config;
extern unsigned int ssp_block_size;
enum ssp_flags { enum ssp_config_flags {
ssp_compress = 1, ssp_flag_mclk = 0x01,
ssp_do_dma = 2,
ssp_skipidle = 4,
ssp_nonblock = 8,
ssp_have_buffer = 0x100,
ssp_do_idle = 0x200,
ssp_do_cmd = 0x400,
}; };
#define SSP_INJ_SIZE 2048 void ssp_init(struct ssp_config *c);
extern unsigned short ssp_injection_buffer[SSP_INJ_SIZE]; void ssp_deassert_ssel();
extern unsigned int ssp_injection_write_ptr; void ssp_assert_ssel(unsigned int ssel);
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);
static inline void disable_ssp_dma() { ssp_dma_size = 0; } #define SSP_kHz(kHz) SSPCR0_SCR(30000/(kHz))
// DLRENA does not do ssp dma #define SSP_MCLK ssp_flag_mclk
#ifndef DLRENA_SSP #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 extern const struct ssp_config ssp_conf_adc;
#define ssp_compress(j) j->compress_count extern const struct ssp_config ssp_conf_adc_daisy;
extern const struct ssp_config ssp_conf_dac;
#else extern const struct ssp_config ssp_conf_dac_daisy;
extern const struct ssp_config ssp_conf_bate;
#define ssp_do_dma 0 extern const struct ssp_config ssp_conf_bate_ext;
#define ssp_do_idle 0
#define ssp_compress 0
#define ssp_skipidle 0
#define ssp_idling(j) 0
#define ssp_compressing(j) 0
#endif