📄 cfe_dmtest.c
字号:
/* ********************************************************************* * Broadcom Common Firmware Environment (CFE) * * Test commands File: cfe_dmtest.c * * A temporary sandbox for data mover test routines and commands. * ********************************************************************* * * Copyright 2000,2001,2002,2003 * Broadcom Corporation. All rights reserved. * * This software is furnished under license and may be used and * copied only in accordance with the following terms and * conditions. Subject to these conditions, you may download, * copy, install, use, modify and distribute modified or unmodified * copies of this software in source and/or binary form. No title * or ownership is transferred hereby. * * 1) Any source code used, modified or distributed must reproduce * and retain this copyright notice and list of conditions * as they appear in the source file. * * 2) No right is granted to use any trade name, trademark, or * logo of Broadcom Corporation. The "Broadcom Corporation" * name may not be used to endorse or promote products derived * from this software without the prior written permission of * Broadcom Corporation. * * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************* */#include "cfe.h"#include "sbmips.h"#include "sb1250_defs.h"#include "sb1250_regs.h"#include "sb1250_int.h"#include "sb1250_dma.h"/* pass 1 holdover */#define M_DM_DSCRA_THROTTLE _SB_MAKEMASK1(43)#ifdef _SENTOSA_#include "sentosa.h"#endif#include "ui_command.h"#include "cfe_irq.h"#include "lib_hssubr.h"static uint64_tphys_addr(uint64_t va){ uint64_t pa; if (va - K0BASE >= 0 && va - K0BASE < K0SIZE) pa = K0_TO_PHYS(va); else if (va - K1BASE >= 0 && va - K1BASE < K1SIZE) pa = K1_TO_PHYS(va); else pa = XKPHYS_TO_PHYS(va); return pa;}/* to be exported */void dm_bzero(void *dest, int cnt);void dm_bsink(void *src, int cnt);void dm_bcopy(void *src, void *dest, int cnt);void dm_multibcopy(void *src, void *dest, int cnt, int blksize);static uint64_t zb_hz;static uint64_t zb_mhz;static int ui_cmd_zero(ui_cmdline_t *cmd, int argc, char *argv[]);static int ui_cmd_sink(ui_cmdline_t *cmd, int argc, char *argv[]);static int ui_cmd_cpy(ui_cmdline_t *cmd, int argc, char *argv[]);static int ui_cmd_mcpy(ui_cmdline_t *cmd, int argc, char *argv[]);/* demo stuff */static int ui_cmd_testdm(ui_cmdline_t *cmd, int argc, char *argv[]);static int ui_cmd_talkdm(ui_cmdline_t *cmd, int argc, char *argv[]);int ui_init_testcmds2(void);int ui_init_testcmds2(void){ zb_hz = cfe_cpu_speed/2; zb_mhz = (zb_hz + 500000)/1000000; /* demo commands */ cmd_addcmd("test dm", ui_cmd_testdm, NULL, "Loop copying memory using the data mover", "test dm [-v|-p] [-u] [-t] [-off] [dest src len]\n\n" "This command continuously copies from source to destination\n " " using the data mover and displays MB/s (times 10)", "-p;Address is a physical address|" "-v;Address is a (kernel) virtual address|" "-u;Uncachable source and destination|" "-t;Throttle DMA|" "-off;Stop test"); cmd_addcmd("talk", ui_cmd_talkdm, NULL, "Text echo", "talk [-l]\n\n" "This command sends to the text demon using the\n " " data mover and mailbox variables", "-l;Use local addresses"); /* timing/utility commands */ cmd_addcmd("sink", ui_cmd_sink, NULL, "Read memory using the data mover", "sink [-v|-p] [-u|-l2] [addr [len]]\n\n" "This command reads a region of memory using the data\n" "mover and discards the result.", "-p;Address is a physical address|" "-v;Address is a (kernel) virtual address|" "-u;Uncachable source|" "-l2;L2 allocate at source"); cmd_addcmd("zero", ui_cmd_zero, NULL, "Zero memory using the data mover", "zero [-v|-p] [-u|-l2] [addr [len]]\n\n" "This command clears a region of memory to zero using the\n" "data mover.", "-p;Address is a physical address|" "-v;Address is a (kernel) virtual address|" "-u;Uncachable destination|" "-l2;L2 allocate at destination"); cmd_addcmd("mcpy", ui_cmd_mcpy, NULL, "Copy memory using the data mover with multiple descriptors", "cpy [-v|-p] [-u|-l2] dest src [[len] blksize]\n\n" "This command copies from source to destination using the\n" "data mover with blksize bytes per descriptor.", "-p;Address is a physical address|" "-v;Address is a (kernel) virtual address|" "-u;Uncachable source and destination|" "-l2;L2 allocate at destination"); cmd_addcmd("cpy", ui_cmd_cpy, NULL, "Copy memory using the data mover", "cpy [-v|-p] [-u|-l2] dest src [len]\n\n" "This command copies from source to destination using the\n" "data mover.", "-p;Address is a physical address|" "-v;Address is a (kernel) virtual address|" "-u;Uncachable source and destination|" "-l2;L2 allocate at destination"); return 0;}#if 0#define _SBDMA_POLL_#else#define _SBDMA_INTERRUPTS_#endif#define dm_nextbuf(d,f) ((((d)->f+1) == (d)->dm_dscrring_end) ? \ (d)->dm_dscrring : (d)->f+1)/* The following macro won't work with 64-bit virtual addresses. */#define SBDMA_VTOP(x) ((uint64_t) K1_TO_PHYS((uint64_t)((unsigned long)(x))))/* abbreviations for generating descriptors */typedef uint64_t flags_t;#define SRCUNC M_DM_DSCRA_UN_SRC /* Source uncached */#define SRCINC V_DM_DSCRA_DIR_SRC_INCR /* inc src ptr */#define SRCDEC V_DM_DSCRA_DIR_SRC_DECR /* dec src ptr */#define SRCCON V_DM_DSCRA_DIR_SRC_CONST /* src ptr constant */#define DSTUNC M_DM_DSCRA_UN_DEST /* Dest uncached */#define DSTINC V_DM_DSCRA_DIR_DEST_INCR /* inc dest ptr */#define DSTDEC V_DM_DSCRA_DIR_DEST_DECR /* dec dest ptr */#define DSTCON V_DM_DSCRA_DIR_DEST_CONST /* dest ptr constant */#define ZEROMEM M_DM_DSCRA_ZERO_MEM /* just zero dest */#define PREFETCH M_DM_DSCRA_PREFETCH /* don't write dest */#define L2CDEST M_DM_DSCRA_L2C_DEST /* dest l2 cacheable */#define L2CSRC M_DM_DSCRA_L2C_SRC /* src l2 cacheable *//* NB: THROTTLE is defined only for pass 1 parts. */#define THROTTLE M_DM_DSCRA_THROTTLE /* throttle DMA */#ifdef _SBDMA_INTERRUPTS_#define INTER M_DM_DSCRA_INTERRUPT /* int on dscr done */#else#define INTER 0#endiftypedef uint32_t sbport_t;typedef uint64_t sbphysaddr_t;typedef struct sbdmadscr_s { uint64_t dscr_a; uint64_t dscr_b;} sbdmadscr_t;typedef struct sbgendma_s { int ch; /* channel number */ void *dm_dscrmem; /* Memory for descriptor list */ sbport_t dm_dscrbase; /* Descriptor base address */ sbport_t dm_dscrcnt; /* Descriptor count register */ sbport_t dm_curdscr; /* current descriptor address */ sbdmadscr_t *dm_dscrring; /* base of descriptor table */ sbdmadscr_t *dm_dscrring_end; /* end of descriptor table */ sbphysaddr_t dm_dscrring_phys; /* and also the phys addr */ sbdmadscr_t *dm_addptr; /* next dscr for sw to add */ sbdmadscr_t *dm_remptr; /* next dscr for sw to remove */ /* number of descriptors we have not told the hardware about yet. */ int dm_newcnt; int dm_busy;} sbgendma_t;#define DM_READCSR(x) SBREADCSR(x)#define DM_WRITECSR(x,y) SBWRITECSR(x,y)static sbgendma_t sbgendma_ctx[DM_NUM_CHANNELS];/* Channel allocation: 0 utility commands that use the data mover (foreground) 1 data mover test loop (background) 2 talk command (background) 3 data mover multicopy (foreground)*/#define SBDMA_CMD_CH 0#define SBDMA_TEST_CH 1#define SBDMA_TALK_CH 2#define SBDMA_MCMD_CH 3static int sbgendma_initialized = 0;/* For Pass 1, dedicate an SCD peformance counter to use as a counter of ZBbus cycles. */#include "sb1250_scd.h"#define ZCTR_MODULUS 0x10000000000LL/* The counter is a shared resource that must be reset periodically since it doesn't roll over. Furthermore, there is a pass one bug that makes the interrupt unreliable and the final value either all ones or all zeros. We therefore reset the count when it exceeds half the modulus. We also assume that intervals of interest are much less than half the modulus and attempt to adjust for the reset in zclk_elapsed. */static voidzclk_init(uint64_t val){ *((volatile uint64_t *) UNCADDR(A_SCD_PERF_CNT_0)) = val; *((volatile uint64_t *) UNCADDR(A_SCD_PERF_CNT_CFG)) = V_SPC_CFG_SRC0(1) | M_SPC_CFG_ENABLE;}static uint64_tzclk_get(void){ uint64_t ticks; ticks = *((volatile uint64_t *) UNCADDR(A_SCD_PERF_CNT_0)); if (ticks == 0 || ticks == ZCTR_MODULUS-1) { ticks = 0; zclk_init(ticks); } else if (ticks >= ZCTR_MODULUS/2) { ticks -= ZCTR_MODULUS/2; zclk_init(ticks); /* Ignore the fudge and lose a few ticks */ } return ticks;}static uint64_tzclk_elapsed(uint64_t stop, uint64_t start){ return ((stop >= start) ? stop : stop + ZCTR_MODULUS/2) - start;}/* ********************************************************************* * DM_PROCBUFFERS(d) * * Process "completed" buffers on the specified DMA channel. * This is normally called within the interrupt service routine. * Note that this isn't really ideal for priority channels, since * it processes all of the packets on a given channel before * returning. * * Input parameters: * d - DMA channel context * * Return value: * number of packets processed. ********************************************************************* */static intdm_procbuffers(sbgendma_t *d){ int curidx; int hwidx; int count = 0; for (;;) { /* Figure out where we are (as an index) and where the hardware is (also as an index) This could be done faster if (for example) the descriptor table was page-aligned and contiguous in both virtual and physical memory -- you could then just compare the low-order bits of the virtual address (dm_remptr) and the physical address (dm_curdscr CSR) */ curidx = d->dm_remptr - d->dm_dscrring; hwidx = ((DM_READCSR(d->dm_curdscr) & M_DM_CUR_DSCR_DSCR_ADDR) - d->dm_dscrring_phys) / sizeof(sbdmadscr_t); /* If they're the same, we've processed all of the descriptors owned by us. */ if (curidx == hwidx) break; /* Otherwise, issue the upcall, or do whatever we want to do to indicate to the client program that the operation is complete (not used here). */ count++; /* .. and advance to the next buffer. */ d->dm_remptr = dm_nextbuf(d, dm_remptr); } return count;}/* ********************************************************************* * DM_ADDDESCR(d,newdsc,n) * * Add a descriptor to the specified DMA channel. * * Input parameters: * d - DMA channel descriptor * newdsc - pointer to descriptor * n - number of descriptors to add * * Return value: * number of descriptors actually added ********************************************************************* */static intdm_adddescr(sbgendma_t *d, sbdmadscr_t *newdsc, int n){ sbdmadscr_t *dsc; sbdmadscr_t *nextdsc; int cnt = 0; while (n > 0) { dsc = d->dm_addptr; nextdsc = dm_nextbuf(d, dm_addptr); /* Test whether the ring is full (next == remptr) */ if (nextdsc == d->dm_remptr) { break; } dsc->dscr_a = newdsc->dscr_a; dsc->dscr_b = newdsc->dscr_b; d->dm_addptr = nextdsc; cnt++; n--; newdsc++; } d->dm_newcnt += cnt; return cnt;}/* ********************************************************************* * DM_START(d) * * Start DMA on the specified channel, queueing all descriptors * that we have added with dm_adddescr() * * Input parameters: * d - DMA context * * Return value: * start time (zbclks) ********************************************************************* */static uint64_tdm_start(sbgendma_t *d){ uint64_t now; if (d->dm_newcnt) { d->dm_busy = 1; DM_WRITECSR(d->dm_dscrcnt, d->dm_newcnt); } now = zclk_get(); d->dm_newcnt = 0; return now;}#ifdef _SBDMA_INTERRUPTS_/* ********************************************************************* * DM_INTERRUPT(ctx) * * "interrupt" routine for a given channel * * Input parameters: * ctx - DMA channel context * * Return value: * nothing ********************************************************************* */static voiddm_interrupt(void *ctx){ sbgendma_t *d = (sbgendma_t *)ctx; /* Clear the interrupt (better in procbuffers? */ (void) DM_READCSR(d->dm_dscrbase); dm_procbuffers(d); if (G_DM_CUR_DSCR_DSCR_COUNT(DM_READCSR(d->dm_curdscr)) == 0) d->dm_busy = 0;}/* ********************************************************************* * DM_ISDONE(d) * * Returns TRUE if all descriptors on the specified channel * are complete. * * Input parameters: * d - DMA context * * Return value: * 1 if all descriptors are processed (none pending in hw) * 0 if there are still some descriptors in progress ********************************************************************* */static intdm_isdone(sbgendma_t *d){ cfe_irq_poll(NULL); /* avoid full bg poll if timing */ return (d->dm_busy == 0);}/* ********************************************************************* * DM_WAIT(d) * * Returns elapsed zb clks when an interrupt becomes pending on * the channel and also processes the interrupt. * CAUTION: spins forever if misused. * * Input parameters: * none * * Return value: * stop time (zbclks) ********************************************************************* */#define IMR_POINTER(cpu,reg) \ ((volatile uint64_t *)(PHYS_TO_K1(A_IMR_REGISTER(cpu,reg))))extern int32_t _getcause(void); /* return value of CP0 CAUSE */static uint64_tdm_wait(sbgendma_t *d){ uint64_t mask = (1LL << (K_INT_DM_CH_0 + d->ch)); volatile uint64_t *status = IMR_POINTER(0, R_IMR_INTERRUPT_SOURCE_STATUS); uint64_t stop; /* XXX Add sanity timer here */ for (;;) { while ((_getcause() & M_CAUSE_IP2) == 0) continue; if ((*status & mask) != 0) break; cfe_irq_poll(NULL); if (d->dm_busy == 0) break; } stop = zclk_get(); while (!dm_isdone(d)) continue; return stop;}#else /* !_SBDMA_INTERRUPTS_ *//* ********************************************************************* * DM_ISDONE(d) * * Returns TRUE if all descriptors on the specified channel * are complete. * * Input parameters: * d - DMA context * * Return value: * 1 if all descriptors are processed (none pending in hw) * 0 if there are still some descriptors in progress ********************************************************************* */static intdm_isdone(sbgendma_t *d){#ifdef _SBDMA_TEST_PTRS_ int curidx, hwidx;#endif /* If polling, now is a good time to process finished descriptors. */ dm_procbuffers(d); /* Test for DMA complete. There are two ways to do this, to use
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -