From 09bf9c23ab8938b31af7bdff7390a7f28e19d53d Mon Sep 17 00:00:00 2001 From: Werner Almesberger Date: Mon, 14 Jan 2013 03:55:51 -0300 Subject: [PATCH] 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. --- ubb-patgen/ubb-patgen.c | 179 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 172 insertions(+), 7 deletions(-) diff --git a/ubb-patgen/ubb-patgen.c b/ubb-patgen/ubb-patgen.c index 6bdd8d2..b60891a 100644 --- a/ubb-patgen/ubb-patgen.c +++ b/ubb-patgen/ubb-patgen.c @@ -14,11 +14,18 @@ #include #include #include +#include #include #include +#include #include +#include #include +#include + + +#define DMA 5 /* ----- 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 ------------------------------------------- */ @@ -174,15 +317,18 @@ static void usage(const char *name) { fprintf(stderr, "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" " -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" -" 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" " optionally followed by \"Hz\", optionally followed by \"+\" or \"-\".\n" " \"+\" selects a frequency >= the specified one, \"-\" one <=.\n" " Without +/-, the closest available frequency is selected.\n" +"Pattern: hex digits corresponding to 1 for DAT0, 2 for DAT1, etc.\n" , name, name); exit(1); } @@ -191,7 +337,9 @@ static void usage(const char *name) int main(int argc, char **argv) { 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; double active_s; struct timespec active_ns; @@ -201,7 +349,11 @@ int main(int argc, char **argv) while ((c = getopt(argc, argv, "b:cq")) != EOF) switch (c) { 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); break; case 'c': @@ -221,8 +373,11 @@ int main(int argc, char **argv) ubb_open(UBB_ALL); show_frequencies(); return 1; + case 2: + pattern = argv[optind]; + /* fall through */ case 1: - active_s = strtod(argv[optind], &end); + active_s = strtod(argv[argc-1], &end); if (*end) usage(*argv); active_ns.tv_sec = (int) active_s; @@ -234,7 +389,12 @@ int main(int argc, char **argv) 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"); exit(1); } @@ -254,12 +414,17 @@ int main(int argc, char **argv) PDFUNS = UBB_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)) perror("nanosleep"); mmcclk_stop(); - ubb_close(0); + ubb_close(UBB_DAT0 | UBB_DAT1 | UBB_DAT2 | UBB_DAT3); return 0; }