Support functions forthe Intel 80310 DMA channels ================================================== Dave Jiang Last updated: 09/18/2001 The Intel 80310 XScale chipset provides 3 DMA channels via the 80312 I/O companion chip. Two of them resides on the primary PCI bus and one on the secondary PCI bus. The DMA API provided is not compatible with the generic interface in the ARM tree unfortunately due to how the 80312 DMACs work. Hopefully some time in the near future a software interface can be done to bridge the differences. The DMA API has been modeled after Nicholas Pitre's SA11x0 DMA API therefore they will look somewhat similar. 80310 DMA API ------------- int dma_request(dmach_t channel, const char *device_id); This function will attempt to allocate the channel depending on what the user requests: IOP310_DMA_P0: PCI Primary 1 IOP310_DMA_P1: PCI Primary 2 IOP310_DMA_S0: PCI Secondary 1 /*EOF*/ Once the user allocates the DMA channel it is owned until released. Although other users can also use the same DMA channel, but no new resources will be allocated. The function will return the allocated channel number if successful. int dma_queue_buffer(dmach_t channel, dma_sghead_t *listhead); The user will construct a SGL in the form of below: /* * Scattered Gather DMA List for user */ typedef struct _dma_desc { u32 NDAR; /* next descriptor adress [READONLY] */ u32 PDAR; /* PCI address */ u32 PUADR; /* upper PCI address */ u32 LADR; /* local address */ u32 BC; /* byte count */ u32 DC; /* descriptor control */ } dma_desc_t; typedef struct _dma_sgl { dma_desc_t dma_desc; /* DMA descriptor */ u32 status; /* descriptor status [READONLY] */ u32 data; /* user defined data */ struct _dma_sgl *next; /* next descriptor [READONLY] */ } dma_sgl_t; /* dma sgl head */ typedef struct _dma_head { u32 total; /* total elements in SGL */ u32 status; /* status of sgl */ u32 mode; /* read or write mode */ dma_sgl_t *list; /* pointer to list */ dma_callback_t callback; /* callback function */ } dma_head_t; The user shall allocate user SGL elements by calling the function: dma_get_buffer(). This function will give the user an SGL element. The user is responsible for creating the SGL head however. The user is also responsible for allocating the memory for DMA data. The following code segment shows how a DMA operation can be performed: #include void dma_test(void) { char dev_id[] = "Primary 0"; dma_head_t *sgl_head = NULL; dma_sgl_t *sgl = NULL; int err = 0; int channel = -1; u32 *test_ptr = 0; DECLARE_WAIT_QUEUE_HEAD(wait_q); *(IOP310_ATUCR) = (IOP310_ATUCR_PRIM_OUT_ENAB | IOP310_ATUCR_DIR_ADDR_ENAB); channel = dma_request(IOP310_DMA_P0, dev_id); sgl_head = (dma_head_t *)kmalloc(sizeof(dma_head_t), GFP_KERNEL); sgl_head->callback = NULL; /* no callback created */ sgl_head->total = 2; /* allocating 2 DMA descriptors */ sgl_head->mode = (DMA_MOD_WRITE); sgl_head->status = 0; /* now we get the two descriptors */ sgl = dma_get_buffer(channel, 2); /* we set the header to point to the list we allocated */ sgl_head->list = sgl; /* allocate 1k of DMA data */ sgl->data = (u32)kmalloc(1024, GFP_KERNEL); /* Local address is physical */ sgl->dma_desc.LADR = (u32)virt_to_phys(sgl->data); /* write to arbitrary location over the PCI bus */ sgl->dma_desc.PDAR = 0x00600000; sgl->dma_desc.PUADR = 0; sgl->dma_desc.BC = 1024; /* set write & invalidate PCI command */ sgl->dma_desc.DC = DMA_DCR_PCI_MWI; sgl->status = 0; /* set a pattern */ memset(sgl->data, 0xFF, 1024); /* User's responsibility to keep buffers cached coherent */ cpu_dcache_clean(sgl->data, sgl->data + 1024); sgl = sgl->next; sgl->data = (u32)kmalloc(1024, GFP_KERNEL); sgl->dma_desc.LADR = (u32)virt_to_phys(sgl->data); sgl->dma_desc.PDAR = 0x00610000; sgl->dma_desc.PUADR = 0; sgl->dma_desc.BC = 1024; /* second descriptor has interrupt flag enabled */ sgl->dma_desc.DC = (DMA_DCR_PCI_MWI | DMA_DCR_IE); /* must set end of chain flag */ sgl->status = DMA_END_CHAIN; /* DO NOT FORGET THIS!!!! */ memset(sgl->data, 0x0f, 1024); /* User's responsibility to keep buffers cached coherent */ cpu_dcache_clean(sgl->data, sgl->data + 1024); /* queing the buffer, this function will sleep since no callback */ err = dma_queue_buffer(channel, sgl_head); /* now we are woken from DMA complete */ /* do data operations here */ /* free DMA data if necessary */ /* return the descriptors */ dma_return_buffer(channel, sgl_head->list); /* free the DMA */ dma_free(channel); kfree((void *)sgl_head); } dma_sgl_t * dma_get_buffer(dmach_t channel, int buf_num); This call allocates DMA descriptors for the user. void dma_return_buffer(dmach_t channel, dma_sgl_t *list); This call returns the allocated descriptors back to the API. int dma_suspend(dmach_t channel); This call suspends any DMA transfer on the given channel. int dma_resume(dmach_t channel); This call resumes a DMA transfer which would have been stopped through dma_suspend(). int dma_flush_all(dmach_t channel); This completely flushes all queued buffers and on-going DMA transfers on a given channel. This is called when DMA channel errors have occured. void dma_free(dmach_t channel); This clears all activities on a given DMA channel and releases it for future requests. Buffer Allocation ----------------- It is the user's responsibility to allocate, free, and keep track of the allocated DMA data memory. Upon calling dma_queue_buffer() the user must relinquish the control of the buffers to the kernel and not change the state of the buffers that it has passed to the kernel. The user will regain the control of the buffers when it has been woken up by the bottom half of the DMA interrupt handler. The user can allocate cached buffers or non-cached via pci_alloc_consistent(). It is the user's responsibility to ensure that the data is cache coherent. *Reminder* The user is responsble to ensure the ATU is setup properly for DMA transfers. All Disclaimers apply. Use this at your own discretion. Neither Intel nor I will be responsible ifanything goes wrong.