Date: | 2013-01-14 07:55:51 (11 years 2 months ago) |
---|---|
Author: | Werner Almesberger |
Commit: | 09bf9c23ab8938b31af7bdff7390a7f28e19d53d |
Message: | 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. |
Files: |
ubb-patgen/ubb-patgen.c (8 diffs) |
Change Details
ubb-patgen/ubb-patgen.c | ||
---|---|---|
14 | 14 | #include <stdlib.h> |
15 | 15 | #include <stdio.h> |
16 | 16 | #include <unistd.h> |
17 | #include <string.h> | |
17 | 18 | #include <math.h> |
18 | 19 | #include <time.h> |
20 | #include <assert.h> | |
19 | 21 | |
20 | 22 | #include <ubb/ubb.h> |
23 | #include <ubb/regs4740.h> | |
21 | 24 | #include <ubb/mmcclk.h> |
25 | #include <ubb/physmem.h> | |
26 | ||
27 | ||
28 | #define DMA 5 | |
22 | 29 | |
23 | 30 | |
24 | 31 | /* ----- List available bus clock frequencies ------------------------------ */ |
... | ... | |
122 | 129 | } |
123 | 130 | |
124 | 131 | |
132 | /* ----- DMA the pattern --------------------------------------------------- */ | |
133 | ||
134 | ||
135 | static uint32_t old_dmac; | |
136 | ||
137 | ||
138 | static void dma_stop(void) | |
139 | { | |
140 | DCS(DMA) = (1 << 3) | (1 << 2); /* halt DMA channel */ | |
141 | DCS(DMA) = 0; /* reset DMA channel */ | |
142 | } | |
143 | ||
144 | ||
145 | static void dma_init(void) | |
146 | { | |
147 | old_dmac = DMAC; | |
148 | ||
149 | DMAC = 1; /* activate the DMA controller (in case it's off) */ | |
150 | dma_stop(); | |
151 | ||
152 | DCM(DMA) = | |
153 | (1 << 23) | /* source address increment */ | |
154 | (4 << 8); /* transfer size is 32 bytes */ | |
155 | DRT(DMA) = 26; /* MSC transmit-fifo-empty transfer request */ | |
156 | } | |
157 | ||
158 | ||
159 | static void dma_cleanup(void) | |
160 | { | |
161 | DMAC = old_dmac; | |
162 | dma_stop(); | |
163 | } | |
164 | ||
165 | ||
166 | static void dma_setup(unsigned long buf, int nibbles) | |
167 | { | |
168 | assert(!(nibbles & 63)); | |
169 | ||
170 | DCS(DMA) = 1 << 31; /* no-descriptor transfer */ | |
171 | DSA(DMA) = buf; /* source */ | |
172 | DTA(DMA) = REG_PADDR(MSC_TXFIFO); /* MUST set this each time */ | |
173 | DTC(DMA) = nibbles >> 6; /* 32 bytes per transfer */ | |
174 | } | |
175 | ||
176 | ||
177 | static void mmc_buffer(const struct mmcclk *clk, | |
178 | unsigned long buf, int nibbles) | |
179 | { | |
180 | MSC_STRPCL = 1 << 3; /* reset the MSC */ | |
181 | ||
182 | while (MSC_STAT & (1 << 15)); /* wait until reset finishes */ | |
183 | ||
184 | dma_setup(buf, nibbles); | |
185 | ||
186 | MSC_CLKRT = clk->clkrt; /* cleared by MSC reset */ | |
187 | MSC_STRPCL = 2; /* start the bus clock */ | |
188 | MSC_RESTO = 0xffff; /* maximum response time-out */ | |
189 | ||
190 | MSC_CMDAT = | |
191 | (2 << 9) | /* 4 bit bus */ | |
192 | (1 << 8) | /* DMA */ | |
193 | (1 << 4) | /* write */ | |
194 | (1 << 3) | /* with data transfer */ | |
195 | 1; /* R1 response */ | |
196 | ||
197 | MSC_STRPCL = 4; /* START_OP */ | |
198 | ||
199 | DCS(DMA) = | |
200 | (1 << 31) | /* no descriptor */ | |
201 | 1; /* enable transfer */ | |
202 | ||
203 | usleep(100); | |
204 | ||
205 | sleep(1); | |
206 | ||
207 | printf("MSC_STAT 0x%x\n", MSC_STAT); | |
208 | } | |
209 | ||
210 | ||
211 | static void send_buffer(const struct mmcclk *clk, | |
212 | const uint8_t *buf, int nibbles) | |
213 | { | |
214 | unsigned long phys; | |
215 | ||
216 | phys = physmem_xlat((void *) buf); | |
217 | mmc_buffer(clk, phys, nibbles); | |
218 | } | |
219 | ||
220 | ||
221 | static int dma_pattern(const struct mmcclk *clk, | |
222 | const char *pattern, uint32_t mask) | |
223 | { | |
224 | int n = strlen(pattern); | |
225 | int rounded = (n+63) & ~63; | |
226 | uint8_t *buf = physmem_malloc(rounded >> 1); | |
227 | uint8_t *tmp = physmem_malloc(32); | |
228 | int i; | |
229 | ||
230 | if (!n) | |
231 | return 1; | |
232 | ||
233 | memset(buf, 0, rounded); | |
234 | for (i = 0; i != rounded; i++) { | |
235 | char ch[2] = { pattern[i < n ? i : n-1], 0 }; | |
236 | char *end; | |
237 | ||
238 | buf[i >> 1] |= strtoul(ch, &end, 16) << 4*(~i & 1); | |
239 | if (*end) | |
240 | return 0; | |
241 | } | |
242 | ||
243 | dma_init(); | |
244 | ||
245 | PDDATC = ~((buf[0] >> 4) << 10) & mask; | |
246 | PDDATS = (buf[0] >> 4) << 10; | |
247 | PDDIRS = mask; | |
248 | ||
249 | memset(tmp, (buf[0] >> 4)*0x11, 64); | |
250 | ||
251 | send_buffer(clk, tmp, 64); | |
252 | ||
253 | PDFUNS = mask; | |
254 | ||
255 | send_buffer(clk, buf, rounded); | |
256 | ||
257 | PDDATC = ~((buf[(rounded >> 1)-1] & 0xf) << 10) & mask; | |
258 | PDDATS = (buf[(rounded >> 1)-1] & 0xf) << 10; | |
259 | ||
260 | PDFUNC = mask; | |
261 | ||
262 | dma_cleanup(); | |
263 | ||
264 | return 1; | |
265 | } | |
266 | ||
267 | ||
125 | 268 | /* ----- Command-line processing ------------------------------------------- */ |
126 | 269 | |
127 | 270 | |
... | ... | |
174 | 317 | { |
175 | 318 | fprintf(stderr, |
176 | 319 | "usage: %s\n" |
177 | "usage: %s [-b freq_hz] [-c] [-q] active_s\n\n" | |
320 | "usage: %s [-b freq_hz] [-f freq_hz] [-c] [-q] [pattern] active_s\n\n" | |
178 | 321 | " -b freq_hz set bus clock to the specified frequency (default: 1 MHz)\n" |
179 | 322 | " -c output bus clock on CLK\n" |
323 | " -f freq_hz set pattern rate (default: same as bus clock)\n" | |
180 | 324 | " -q quiet. Don't report clock differences.\n\n" |
181 | " active_s keep running that many seconds after setting the clock\n\n" | |
325 | " active_s keep running that many seconds after setting the clock\n" | |
326 | " pattern send the specified pattern on DAT0 through DAT3\n\n" | |
182 | 327 | "Frequency: the frequency in Hz, optionally followed by \"M\" or \"k\",\n" |
183 | 328 | " optionally followed by \"Hz\", optionally followed by \"+\" or \"-\".\n" |
184 | 329 | " \"+\" selects a frequency >= the specified one, \"-\" one <=.\n" |
185 | 330 | " Without +/-, the closest available frequency is selected.\n" |
331 | "Pattern: hex digits corresponding to 1 for DAT0, 2 for DAT1, etc.\n" | |
186 | 332 | , name, name); |
187 | 333 | exit(1); |
188 | 334 | } |
... | ... | |
191 | 337 | int main(int argc, char **argv) |
192 | 338 | { |
193 | 339 | struct mmcclk clk; |
194 | int bus_hz = 1000000, clkout = 0, rel = 0; | |
340 | int bus_hz = 0, clkout = 0, bus_rel = 0; | |
341 | int pattern_hz = 0, pattern_rel = 0; | |
342 | const char *pattern = NULL; | |
195 | 343 | int quiet = 0; |
196 | 344 | double active_s; |
197 | 345 | struct timespec active_ns; |
... | ... | |
201 | 349 | while ((c = getopt(argc, argv, "b:cq")) != EOF) |
202 | 350 | switch (c) { |
203 | 351 | case 'b': |
204 | if (!frequency(optarg, &bus_hz, &rel)) | |
352 | if (!frequency(optarg, &bus_hz, &bus_rel)) | |
353 | usage(*argv); | |
354 | break; | |
355 | case 'f': | |
356 | if (!frequency(optarg, &pattern_hz, &pattern_rel)) | |
205 | 357 | usage(*argv); |
206 | 358 | break; |
207 | 359 | case 'c': |
... | ... | |
221 | 373 | ubb_open(UBB_ALL); |
222 | 374 | show_frequencies(); |
223 | 375 | return 1; |
376 | case 2: | |
377 | pattern = argv[optind]; | |
378 | /* fall through */ | |
224 | 379 | case 1: |
225 | active_s = strtod(argv[optind], &end); | |
380 | active_s = strtod(argv[argc-1], &end); | |
226 | 381 | if (*end) |
227 | 382 | usage(*argv); |
228 | 383 | active_ns.tv_sec = (int) active_s; |
... | ... | |
234 | 389 | |
235 | 390 | ubb_open(UBB_ALL); |
236 | 391 | |
237 | if (!select_freq(&clk, bus_hz, rel)) { | |
392 | PDFUNS = UBB_CMD; | |
393 | ||
394 | if (!bus_hz) | |
395 | bus_hz = 1000000; | |
396 | ||
397 | if (!select_freq(&clk, bus_hz, bus_rel)) { | |
238 | 398 | fprintf(stderr, "no suitable frequency found\n"); |
239 | 399 | exit(1); |
240 | 400 | } |
... | ... | |
254 | 414 | PDFUNS = UBB_CLK; |
255 | 415 | mmcclk_start(&clk); |
256 | 416 | |
417 | if (pattern) | |
418 | if (!dma_pattern(&clk, pattern, | |
419 | UBB_DAT0 | UBB_DAT1 | UBB_DAT2 | UBB_DAT3)) | |
420 | usage(*argv); | |
421 | ||
257 | 422 | if (nanosleep(&active_ns, NULL)) |
258 | 423 | perror("nanosleep"); |
259 | 424 | |
260 | 425 | mmcclk_stop(); |
261 | 426 | |
262 | ubb_close(0); | |
427 | ubb_close(UBB_DAT0 | UBB_DAT1 | UBB_DAT2 | UBB_DAT3); | |
263 | 428 | |
264 | 429 | return 0; |
265 | 430 | } |
Branches:
master