* [PATCH] ppc4xx_sgdma.c
@ 2005-02-28 1:22 Roger Larsson
2005-03-02 16:37 ` Matt Porter
0 siblings, 1 reply; 2+ messages in thread
From: Roger Larsson @ 2005-02-28 1:22 UTC (permalink / raw)
To: linuxppc-embedded; +Cc: roger.larsson
[-- Attachment #1.1: Type: text/plain, Size: 216 bytes --]
* Dynamic list length
1. short lists will not waste a whole page
2. no limit in list length
* End of Transfer termination
* Residue corrected
Working with hardware (some tests remaining)
/RogerL
[-- Attachment #1.2: Type: text/html, Size: 644 bytes --]
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: ppc4xx_sgdma.c.patch --]
[-- Type: text/x-diff; name="ppc4xx_sgdma.c.patch", Size: 10625 bytes --]
--- linux-2.4.25-eldk31/arch/ppc/kernel/ppc4xx_sgdma.c 2003-10-30 01:32:15.000000000 +0100
+++ linux/arch/ppc/kernel/ppc4xx_sgdma.c 2005-02-28 01:51:00.569706952 +0100
@@ -4,11 +4,17 @@
* IBM PPC4xx DMA engine scatter/gather library
*
* Copyright 2002-2003 MontaVista Software Inc.
+ * Copyright 2005 Optronic dp AB
*
* Cleaned by Matt Porter <mporter@mvista.com>
*
* Original code by Armin Kuster <akuster@mvista.com>
* and Pete Popov <ppopov@mvista.com>
+ *
+ * Use of kmalloc, good for short and very long lists
+ * End of Transfer termination and residue
+ * Roger Larsson <roger.larsson@optronic.se> and
+ * Ronnie Hedlund, DataDuctus AB
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -19,7 +25,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-
+
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/mm.h>
@@ -125,18 +131,19 @@
}
#endif
- if ((unsigned) (psgl->ptail + 1) > ((unsigned) psgl + SGL_LIST_SIZE)) {
- printk("sgl handle out of memory \n");
- return DMA_STATUS_OUT_OF_MEMORY;
- }
-
- if (!psgl->ptail) {
- psgl->phead = (ppc_sgl_t *)
- ((unsigned) psgl + sizeof (sgl_list_info_t));
- psgl->ptail = psgl->phead;
- } else {
- psgl->ptail->next = iopa((unsigned long)(psgl->ptail + 1));
- psgl->ptail++;
+ /* dynamic alloc each list element */
+ {
+ ppc_sgl_t *sgl_el = kmalloc(sizeof(ppc_sgl_t), GFP_KERNEL|GFP_DMA);
+ if (!sgl_el)
+ return DMA_STATUS_OUT_OF_MEMORY;
+
+ if (!psgl->phead) { /* list was empty */
+ psgl->phead = sgl_el;
+ } else { /* not empty, tail exists */
+ psgl->ptail->next = virt_to_phys(sgl_el);
+ dma_cache_wback((unsigned long)psgl->ptail, sizeof(ppc_sgl_t));
+ }
+ psgl->ptail = sgl_el;
}
psgl->ptail->control = psgl->control;
@@ -144,7 +151,8 @@
psgl->ptail->dst_addr = dst_addr;
psgl->ptail->control_count = (count >> p_dma_ch->shift) |
psgl->sgl_control;
- psgl->ptail->next = (uint32_t) NULL;
+ psgl->ptail->next = virt_to_phys(NULL);
+ dma_cache_wback((unsigned long)psgl->ptail, sizeof(ppc_sgl_t)); /* handled later, skip this one? */
return DMA_STATUS_GOOD;
}
@@ -152,6 +160,7 @@
/*
* Enable (start) the DMA described by the sgl handle.
*/
+
void
ppc4xx_enable_dma_sgl(sgl_handle_t handle)
{
@@ -173,9 +182,18 @@
p_dma_ch = &dma_channels[psgl->dmanr];
psgl->ptail->control_count &= ~SG_LINK; /* make this the last dscrptr */
+ if (p_dma_ch->int_enable)
+ {
+ /* Require Terminal Count interrupt on last */
+ psgl->ptail->control_count |= SG_TCI_ENABLE;
+ }
+
+ /* No more changes to tail object allowed */
+ dma_cache_wback((unsigned long)psgl->ptail, sizeof(ppc_sgl_t));
+
sg_command = mfdcr(DCRN_ASGC);
- ppc4xx_set_sg_addr(psgl->dmanr, iopa((unsigned long)psgl->phead));
+ ppc4xx_set_sg_addr(psgl->dmanr, virt_to_phys(psgl->phead));
switch (psgl->dmanr) {
case 0:
@@ -193,7 +211,7 @@
default:
printk("ppc4xx_enable_dma_sgl: bad channel: %d\n", psgl->dmanr);
}
-
+
mtdcr(DCRN_ASGC, sg_command); /* start transfer */
}
@@ -242,6 +260,8 @@
* the sgl descriptor where the DMA stopped.
*
* An sgl transfer must NOT be active when this function is called.
+ * Note: Make sure ppc4xx_disable_dma_sgl was called before returning from
+ * interrupt handler (TSn, CSn will not disable the sgl)!
*/
int
ppc4xx_get_dma_sgl_residue(sgl_handle_t handle, phys_addr_t * src_addr,
@@ -263,19 +283,19 @@
switch (psgl->dmanr) {
case 0:
- sgl_addr = (ppc_sgl_t *) bus_to_virt(mfdcr(DCRN_ASG0));
+ sgl_addr = (ppc_sgl_t *) phys_to_virt(mfdcr(DCRN_ASG0));
count_left = mfdcr(DCRN_DMACT0);
break;
case 1:
- sgl_addr = (ppc_sgl_t *) bus_to_virt(mfdcr(DCRN_ASG1));
+ sgl_addr = (ppc_sgl_t *) phys_to_virt(mfdcr(DCRN_ASG1));
count_left = mfdcr(DCRN_DMACT1);
break;
case 2:
- sgl_addr = (ppc_sgl_t *) bus_to_virt(mfdcr(DCRN_ASG2));
+ sgl_addr = (ppc_sgl_t *) phys_to_virt(mfdcr(DCRN_ASG2));
count_left = mfdcr(DCRN_DMACT2);
break;
case 3:
- sgl_addr = (ppc_sgl_t *) bus_to_virt(mfdcr(DCRN_ASG3));
+ sgl_addr = (ppc_sgl_t *) phys_to_virt(mfdcr(DCRN_ASG3));
count_left = mfdcr(DCRN_DMACT3);
break;
default:
@@ -284,54 +304,34 @@
}
if (!sgl_addr) {
- printk("ppc4xx_get_dma_sgl_residue: sgl addr register is null\n");
- goto error;
+ /* Last in list */
+ return count_left;
}
- pnext = psgl->phead;
- while (pnext &&
- ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE) &&
- (pnext != sgl_addr))
- ) {
- pnext++;
- }
-
- if (pnext == sgl_addr) { /* found the sgl descriptor */
-
- *src_addr = pnext->src_addr;
- *dst_addr = pnext->dst_addr;
-
- /*
- * Now search the remaining descriptors and add their count.
- * We already have the remaining count from this descriptor in
- * count_left.
- */
- pnext++;
-
- while ((pnext != psgl->ptail) &&
- ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE))
- ) {
- count_left += pnext->control_count & SG_COUNT_MASK;
- }
+ pnext = sgl_addr; /* sgl_addr is next to be loaded */
- if (pnext != psgl->ptail) { /* should never happen */
- printk
- ("ppc4xx_get_dma_sgl_residue error (1) psgl->ptail 0x%x handle 0x%x\n",
- (unsigned int) psgl->ptail, (unsigned int) handle);
- goto error;
- }
+ /*
+ * Why this strange interface? return nothing or sgl_addr instead...
+ * (please check for null pointers)
+ */
+ *src_addr = pnext->src_addr;
+ *dst_addr = pnext->dst_addr;
- /* success */
- p_dma_ch = &dma_channels[psgl->dmanr];
- return (count_left << p_dma_ch->shift); /* count in bytes */
+ /*
+ * Now search the remaining descriptors and add their count.
+ * We already have the remaining count from this descriptor in
+ * count_left.
+ */
+
+ while (pnext) {
+ count_left += pnext->control_count & SG_COUNT_MASK;
+ pnext = phys_to_virt(pnext->next);
+ }
- } else {
- /* this shouldn't happen */
- printk
- ("get_dma_sgl_residue, unable to match current address 0x%x, handle 0x%x\n",
- (unsigned int) sgl_addr, (unsigned int) handle);
- }
+ /* success */
+ p_dma_ch = &dma_channels[psgl->dmanr];
+ return (count_left << p_dma_ch->shift); /* count in bytes */
error:
*src_addr = (phys_addr_t) NULL;
@@ -362,7 +362,7 @@
}
if (!psgl->phead) {
- printk("ppc4xx_delete_sgl_element: sgl list empty\n");
+ /* printk("ppc4xx_delete_sgl_element: sgl list empty\n"); - not an error */
*src_dma_addr = (phys_addr_t) NULL;
*dst_dma_addr = (phys_addr_t) NULL;
return DMA_STATUS_SGL_LIST_EMPTY;
@@ -373,10 +373,13 @@
if (psgl->phead == psgl->ptail) {
/* last descriptor on the list */
+ kfree(psgl->phead);
psgl->phead = NULL;
psgl->ptail = NULL;
} else {
- psgl->phead++;
+ ppc_sgl_t *next = phys_to_virt(psgl->phead->next);
+ kfree(psgl->phead);
+ psgl->phead = next;
}
return DMA_STATUS_GOOD;
@@ -388,12 +391,7 @@
* describes a scatter/gather list.
*
* A handle is returned in "handle" which the driver should save in order to
- * be able to access this list later. A chunk of memory will be allocated
- * to be used by the API for internal management purposes, including managing
- * the sg list and allocating memory for the sgl descriptors. One page should
- * be more than enough for that purpose. Perhaps it's a bit wasteful to use
- * a whole page for a single sg list, but most likely there will be only one
- * sg list per channel.
+ * be able to access this list later.
*
* Interrupt notes:
* Each sgl descriptor has a copy of the DMA control word which the DMA engine
@@ -410,15 +408,15 @@
* however, only the last descriptor will be setup to interrupt. Thus, an
* interrupt will occur (if interrupts are enabled) only after the complete
* sgl transfer is done.
+ * End of Transfer Interrupt needs to be enabled in all descriptors, since it
+ * is impossible to know which one will be the last...
*/
int
ppc4xx_alloc_dma_handle(sgl_handle_t * phandle, unsigned int mode, unsigned int dmanr)
{
- sgl_list_info_t *psgl;
- dma_addr_t dma_addr;
+ sgl_list_info_t *psgl = NULL;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
uint32_t sg_command;
- void *ret;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_alloc_dma_handle: invalid channel 0x%x\n", dmanr);
@@ -430,19 +428,15 @@
return DMA_STATUS_NULL_POINTER;
}
- /* Get a page of memory, which is zeroed out by consistent_alloc() */
- ret = consistent_alloc(GFP_KERNEL, DMA_PPC4xx_SIZE, &dma_addr);
- if (ret != NULL) {
- memset(ret, 0, DMA_PPC4xx_SIZE);
- psgl = (sgl_list_info_t *) ret;
- }
-
+ /* Get memory for the listinfo struct */
+ psgl = kmalloc(sizeof(sgl_list_info_t), GFP_KERNEL);
if (psgl == NULL) {
*phandle = (sgl_handle_t) NULL;
return DMA_STATUS_OUT_OF_MEMORY;
}
-
- psgl->dma_addr = dma_addr;
+ memset(psgl, 0, sizeof(sgl_list_info_t));
+
+ /* dma_addr is unused now */
psgl->dmanr = dmanr;
/*
@@ -456,7 +450,9 @@
psgl->control &= ~(DMA_TM_MASK | DMA_TD);
/* Save control word and mode */
psgl->control |= (mode | DMA_CE_ENABLE);
-
+ /* PPC Errata? DMA else ignore count on first in list */
+ psgl->control |= SET_DMA_TCE(1);
+
/* In MM mode, we must set ETD/TCE */
if (mode == DMA_MODE_MM)
psgl->control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
@@ -491,13 +487,15 @@
/* Enable SGL control access */
mtdcr(DCRN_ASGC, sg_command);
- psgl->sgl_control = SG_ERI_ENABLE | SG_LINK;
+ psgl->sgl_control = SG_LINK;
if (p_dma_ch->int_enable) {
if (p_dma_ch->tce_enable)
+ {
+ /* reuse as Terminal Count Interrupt Enable on all descr. */
psgl->sgl_control |= SG_TCI_ENABLE;
- else
- psgl->sgl_control |= SG_ETI_ENABLE;
+ }
+ psgl->sgl_control |= SG_ERI_ENABLE | SG_ETI_ENABLE;
}
*phandle = (sgl_handle_t) psgl;
@@ -516,15 +514,14 @@
if (!handle) {
printk("ppc4xx_free_dma_handle: got NULL\n");
return;
- } else if (psgl->phead) {
- printk("ppc4xx_free_dma_handle: list not empty\n");
- return;
- } else if (!psgl->dma_addr) { /* should never happen */
- printk("ppc4xx_free_dma_handle: no dma address\n");
- return;
+ } else if (psgl->phead) { /* free list here, why do it externaly? */
+ phys_addr_t dummy;
+ while (ppc4xx_delete_dma_sgl_element(handle, &dummy, &dummy) == DMA_STATUS_GOOD)
+ /* NOOP */;
+ /* printk("ppc4xx_free_dma_handle: list not empty\n"); */
}
- consistent_free((void *) psgl);
+ kfree((void *) psgl);
}
EXPORT_SYMBOL(ppc4xx_alloc_dma_handle);
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] ppc4xx_sgdma.c
2005-02-28 1:22 [PATCH] ppc4xx_sgdma.c Roger Larsson
@ 2005-03-02 16:37 ` Matt Porter
0 siblings, 0 replies; 2+ messages in thread
From: Matt Porter @ 2005-03-02 16:37 UTC (permalink / raw)
To: Roger Larsson; +Cc: linuxppc-embedded
On Mon, Feb 28, 2005 at 02:22:46AM +0100, Roger Larsson wrote:
> * Dynamic list length
> 1. short lists will not waste a whole page
> 2. no limit in list length
> * End of Transfer termination
> * Residue corrected
>
> Working with hardware (some tests remaining)
You might want to copy/forward to Wolfgang since this patch is
against the Denx 2.4 tree in ELDK. Since it doesn't mention the
tree (most patches here are for linuxppc-2.4 or 2.6), he might
have missed this thread.
-Matt
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2005-03-02 16:38 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-02-28 1:22 [PATCH] ppc4xx_sgdma.c Roger Larsson
2005-03-02 16:37 ` Matt Porter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).