mirror of
git://projects.qi-hardware.com/ben-blinkenlights.git
synced 2024-11-27 18:06:16 +02:00
ubb-patgen/: add DMA-based pattern transfer (WIP)
Works for some patterns but seems to have issues at the edge of transfers. Clock selection also needs more work.
This commit is contained in:
parent
8d59f8a206
commit
09bf9c23ab
@ -14,11 +14,18 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include <ubb/ubb.h>
|
#include <ubb/ubb.h>
|
||||||
|
#include <ubb/regs4740.h>
|
||||||
#include <ubb/mmcclk.h>
|
#include <ubb/mmcclk.h>
|
||||||
|
#include <ubb/physmem.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define DMA 5
|
||||||
|
|
||||||
|
|
||||||
/* ----- List available bus clock frequencies ------------------------------ */
|
/* ----- List available bus clock frequencies ------------------------------ */
|
||||||
@ -122,6 +129,142 @@ static int select_freq(struct mmcclk *res, int hz, int rel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----- DMA the pattern --------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static uint32_t old_dmac;
|
||||||
|
|
||||||
|
|
||||||
|
static void dma_stop(void)
|
||||||
|
{
|
||||||
|
DCS(DMA) = (1 << 3) | (1 << 2); /* halt DMA channel */
|
||||||
|
DCS(DMA) = 0; /* reset DMA channel */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void dma_init(void)
|
||||||
|
{
|
||||||
|
old_dmac = DMAC;
|
||||||
|
|
||||||
|
DMAC = 1; /* activate the DMA controller (in case it's off) */
|
||||||
|
dma_stop();
|
||||||
|
|
||||||
|
DCM(DMA) =
|
||||||
|
(1 << 23) | /* source address increment */
|
||||||
|
(4 << 8); /* transfer size is 32 bytes */
|
||||||
|
DRT(DMA) = 26; /* MSC transmit-fifo-empty transfer request */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void dma_cleanup(void)
|
||||||
|
{
|
||||||
|
DMAC = old_dmac;
|
||||||
|
dma_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void dma_setup(unsigned long buf, int nibbles)
|
||||||
|
{
|
||||||
|
assert(!(nibbles & 63));
|
||||||
|
|
||||||
|
DCS(DMA) = 1 << 31; /* no-descriptor transfer */
|
||||||
|
DSA(DMA) = buf; /* source */
|
||||||
|
DTA(DMA) = REG_PADDR(MSC_TXFIFO); /* MUST set this each time */
|
||||||
|
DTC(DMA) = nibbles >> 6; /* 32 bytes per transfer */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void mmc_buffer(const struct mmcclk *clk,
|
||||||
|
unsigned long buf, int nibbles)
|
||||||
|
{
|
||||||
|
MSC_STRPCL = 1 << 3; /* reset the MSC */
|
||||||
|
|
||||||
|
while (MSC_STAT & (1 << 15)); /* wait until reset finishes */
|
||||||
|
|
||||||
|
dma_setup(buf, nibbles);
|
||||||
|
|
||||||
|
MSC_CLKRT = clk->clkrt; /* cleared by MSC reset */
|
||||||
|
MSC_STRPCL = 2; /* start the bus clock */
|
||||||
|
MSC_RESTO = 0xffff; /* maximum response time-out */
|
||||||
|
|
||||||
|
MSC_CMDAT =
|
||||||
|
(2 << 9) | /* 4 bit bus */
|
||||||
|
(1 << 8) | /* DMA */
|
||||||
|
(1 << 4) | /* write */
|
||||||
|
(1 << 3) | /* with data transfer */
|
||||||
|
1; /* R1 response */
|
||||||
|
|
||||||
|
MSC_STRPCL = 4; /* START_OP */
|
||||||
|
|
||||||
|
DCS(DMA) =
|
||||||
|
(1 << 31) | /* no descriptor */
|
||||||
|
1; /* enable transfer */
|
||||||
|
|
||||||
|
usleep(100);
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
printf("MSC_STAT 0x%x\n", MSC_STAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void send_buffer(const struct mmcclk *clk,
|
||||||
|
const uint8_t *buf, int nibbles)
|
||||||
|
{
|
||||||
|
unsigned long phys;
|
||||||
|
|
||||||
|
phys = physmem_xlat((void *) buf);
|
||||||
|
mmc_buffer(clk, phys, nibbles);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int dma_pattern(const struct mmcclk *clk,
|
||||||
|
const char *pattern, uint32_t mask)
|
||||||
|
{
|
||||||
|
int n = strlen(pattern);
|
||||||
|
int rounded = (n+63) & ~63;
|
||||||
|
uint8_t *buf = physmem_malloc(rounded >> 1);
|
||||||
|
uint8_t *tmp = physmem_malloc(32);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
memset(buf, 0, rounded);
|
||||||
|
for (i = 0; i != rounded; i++) {
|
||||||
|
char ch[2] = { pattern[i < n ? i : n-1], 0 };
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
buf[i >> 1] |= strtoul(ch, &end, 16) << 4*(~i & 1);
|
||||||
|
if (*end)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_init();
|
||||||
|
|
||||||
|
PDDATC = ~((buf[0] >> 4) << 10) & mask;
|
||||||
|
PDDATS = (buf[0] >> 4) << 10;
|
||||||
|
PDDIRS = mask;
|
||||||
|
|
||||||
|
memset(tmp, (buf[0] >> 4)*0x11, 64);
|
||||||
|
|
||||||
|
send_buffer(clk, tmp, 64);
|
||||||
|
|
||||||
|
PDFUNS = mask;
|
||||||
|
|
||||||
|
send_buffer(clk, buf, rounded);
|
||||||
|
|
||||||
|
PDDATC = ~((buf[(rounded >> 1)-1] & 0xf) << 10) & mask;
|
||||||
|
PDDATS = (buf[(rounded >> 1)-1] & 0xf) << 10;
|
||||||
|
|
||||||
|
PDFUNC = mask;
|
||||||
|
|
||||||
|
dma_cleanup();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ----- Command-line processing ------------------------------------------- */
|
/* ----- Command-line processing ------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
@ -174,15 +317,18 @@ static void usage(const char *name)
|
|||||||
{
|
{
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"usage: %s\n"
|
"usage: %s\n"
|
||||||
"usage: %s [-b freq_hz] [-c] [-q] active_s\n\n"
|
"usage: %s [-b freq_hz] [-f freq_hz] [-c] [-q] [pattern] active_s\n\n"
|
||||||
" -b freq_hz set bus clock to the specified frequency (default: 1 MHz)\n"
|
" -b freq_hz set bus clock to the specified frequency (default: 1 MHz)\n"
|
||||||
" -c output bus clock on CLK\n"
|
" -c output bus clock on CLK\n"
|
||||||
|
" -f freq_hz set pattern rate (default: same as bus clock)\n"
|
||||||
" -q quiet. Don't report clock differences.\n\n"
|
" -q quiet. Don't report clock differences.\n\n"
|
||||||
" active_s keep running that many seconds after setting the clock\n\n"
|
" active_s keep running that many seconds after setting the clock\n"
|
||||||
|
" pattern send the specified pattern on DAT0 through DAT3\n\n"
|
||||||
"Frequency: the frequency in Hz, optionally followed by \"M\" or \"k\",\n"
|
"Frequency: the frequency in Hz, optionally followed by \"M\" or \"k\",\n"
|
||||||
" optionally followed by \"Hz\", optionally followed by \"+\" or \"-\".\n"
|
" optionally followed by \"Hz\", optionally followed by \"+\" or \"-\".\n"
|
||||||
" \"+\" selects a frequency >= the specified one, \"-\" one <=.\n"
|
" \"+\" selects a frequency >= the specified one, \"-\" one <=.\n"
|
||||||
" Without +/-, the closest available frequency is selected.\n"
|
" Without +/-, the closest available frequency is selected.\n"
|
||||||
|
"Pattern: hex digits corresponding to 1 for DAT0, 2 for DAT1, etc.\n"
|
||||||
, name, name);
|
, name, name);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -191,7 +337,9 @@ static void usage(const char *name)
|
|||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct mmcclk clk;
|
struct mmcclk clk;
|
||||||
int bus_hz = 1000000, clkout = 0, rel = 0;
|
int bus_hz = 0, clkout = 0, bus_rel = 0;
|
||||||
|
int pattern_hz = 0, pattern_rel = 0;
|
||||||
|
const char *pattern = NULL;
|
||||||
int quiet = 0;
|
int quiet = 0;
|
||||||
double active_s;
|
double active_s;
|
||||||
struct timespec active_ns;
|
struct timespec active_ns;
|
||||||
@ -201,7 +349,11 @@ int main(int argc, char **argv)
|
|||||||
while ((c = getopt(argc, argv, "b:cq")) != EOF)
|
while ((c = getopt(argc, argv, "b:cq")) != EOF)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'b':
|
case 'b':
|
||||||
if (!frequency(optarg, &bus_hz, &rel))
|
if (!frequency(optarg, &bus_hz, &bus_rel))
|
||||||
|
usage(*argv);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
if (!frequency(optarg, &pattern_hz, &pattern_rel))
|
||||||
usage(*argv);
|
usage(*argv);
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
@ -221,8 +373,11 @@ int main(int argc, char **argv)
|
|||||||
ubb_open(UBB_ALL);
|
ubb_open(UBB_ALL);
|
||||||
show_frequencies();
|
show_frequencies();
|
||||||
return 1;
|
return 1;
|
||||||
|
case 2:
|
||||||
|
pattern = argv[optind];
|
||||||
|
/* fall through */
|
||||||
case 1:
|
case 1:
|
||||||
active_s = strtod(argv[optind], &end);
|
active_s = strtod(argv[argc-1], &end);
|
||||||
if (*end)
|
if (*end)
|
||||||
usage(*argv);
|
usage(*argv);
|
||||||
active_ns.tv_sec = (int) active_s;
|
active_ns.tv_sec = (int) active_s;
|
||||||
@ -234,7 +389,12 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
ubb_open(UBB_ALL);
|
ubb_open(UBB_ALL);
|
||||||
|
|
||||||
if (!select_freq(&clk, bus_hz, rel)) {
|
PDFUNS = UBB_CMD;
|
||||||
|
|
||||||
|
if (!bus_hz)
|
||||||
|
bus_hz = 1000000;
|
||||||
|
|
||||||
|
if (!select_freq(&clk, bus_hz, bus_rel)) {
|
||||||
fprintf(stderr, "no suitable frequency found\n");
|
fprintf(stderr, "no suitable frequency found\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -254,12 +414,17 @@ int main(int argc, char **argv)
|
|||||||
PDFUNS = UBB_CLK;
|
PDFUNS = UBB_CLK;
|
||||||
mmcclk_start(&clk);
|
mmcclk_start(&clk);
|
||||||
|
|
||||||
|
if (pattern)
|
||||||
|
if (!dma_pattern(&clk, pattern,
|
||||||
|
UBB_DAT0 | UBB_DAT1 | UBB_DAT2 | UBB_DAT3))
|
||||||
|
usage(*argv);
|
||||||
|
|
||||||
if (nanosleep(&active_ns, NULL))
|
if (nanosleep(&active_ns, NULL))
|
||||||
perror("nanosleep");
|
perror("nanosleep");
|
||||||
|
|
||||||
mmcclk_stop();
|
mmcclk_stop();
|
||||||
|
|
||||||
ubb_close(0);
|
ubb_close(UBB_DAT0 | UBB_DAT1 | UBB_DAT2 | UBB_DAT3);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user