From dc32678da1e390fce3be3351dfe733a6c944b235 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 6 Jun 2014 11:20:28 -0600 Subject: [PATCH] LPC17: Fix a critical error in GPDMA reported by Lizhuoyi --- arch/arm/src/lpc17xx/lpc17_gpdma.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/arch/arm/src/lpc17xx/lpc17_gpdma.c b/arch/arm/src/lpc17xx/lpc17_gpdma.c index ee12b1b229..585061697b 100644 --- a/arch/arm/src/lpc17xx/lpc17_gpdma.c +++ b/arch/arm/src/lpc17xx/lpc17_gpdma.c @@ -1,7 +1,7 @@ /**************************************************************************** * arch/arm/src/lpc17xx/lpc17_gpdma.c * - * Copyright (C) 2010 Gregory Nutt. All rights reserved. + * Copyright (C) 2010, 2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -73,6 +73,7 @@ struct lpc17_dmach_s uint8_t chn; /* The DMA channel number */ bool inuse; /* True: The channel is in use */ bool inprogress; /* True: DMA is in progress on this channel */ + uint16_t nxfrs; /* Number of transfers */ dma_callback_t callback; /* DMA completion callback function */ void *arg; /* Argument to pass to the callback function */ }; @@ -464,7 +465,7 @@ int lpc17_dmasetup(DMA_HANDLE handle, uint32_t control, uint32_t config, uint32_t regval; uint32_t base; - DEBUGASSERT(dmach && dmach->inuse); + DEBUGASSERT(dmach && dmach->inuse && nxfrs < 4096); chbit = DMACH((uint32_t)dmach->chn); base = LPC17_DMACH_BASE((uint32_t)dmach->chn); @@ -522,6 +523,10 @@ int lpc17_dmasetup(DMA_HANDLE handle, uint32_t control, uint32_t config, regval |= ((uint32_t)nxfrs << DMACH_CONTROL_XFRSIZE_SHIFT); putreg32(regval, base + LPC17_DMACH_CONTROL_OFFSET); + /* Save the number of transfer to perform for lpc17_dmastart */ + + dmach->nxfrs = (uint16_t)nxfrs; + /* 7. "Write the channel configuration information into the DMACCxConfig * register. If the enable bit is set then the DMA channel is * automatically enabled." @@ -573,11 +578,15 @@ int lpc17_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg) putreg32(chbit, LPC17_DMA_INTTCCLR); putreg32(chbit, LPC17_DMA_INTERRCLR); - /* Enable terminal count interrupt */ + /* Enable terminal count interrupt. Note that we need to restore the + * number transfers. That is because the value has a different meaning + * when it is read. + */ base = LPC17_DMACH_BASE((uint32_t)dmach->chn); regval = getreg32(base + LPC17_DMACH_CONTROL_OFFSET); - regval |= DMACH_CONTROL_I; + regval &= ~DMACH_CONTROL_XFRSIZE_MASK; + regval |= (DMACH_CONTROL_I | ((uint32_t)dmach->nxfrs << DMACH_CONTROL_XFRSIZE_SHIFT)); putreg32(regval, base + LPC17_DMACH_CONTROL_OFFSET); /* Enable the channel and unmask terminal count and error interrupts.