| /**************************************************************************** |
| * arch/arm/src/efm32/efm32_dma.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <sys/types.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <assert.h> |
| #include <debug.h> |
| #include <errno.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/arch.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/nuttx.h> |
| #include <nuttx/semaphore.h> |
| |
| #include "arm_internal.h" |
| #include "hardware/efm32_cmu.h" |
| #include "hardware/efm32_dma.h" |
| #include "efm32_dma.h" |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* This structure describes one DMA channel */ |
| |
| struct dma_channel_s |
| { |
| uint8_t chan; /* DMA channel number (0-EFM32_DMA_NCHANNELS) */ |
| bool inuse; /* TRUE: The DMA channel is in use */ |
| dma_config_t config; /* Current configuration */ |
| dma_callback_t callback; /* Callback invoked when the DMA completes */ |
| void *arg; /* Argument passed to callback function */ |
| }; |
| |
| /* This structure describes the state of the DMA controller */ |
| |
| struct dma_controller_s |
| { |
| mutex_t lock; /* Protects channel table */ |
| sem_t chansem; /* Count of free channels */ |
| }; |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* This is the overall state of the DMA controller */ |
| |
| static struct dma_controller_s g_dmac = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| .chansem = SEM_INITIALIZER(EFM32_DMA_NCHANNELS), |
| }; |
| |
| /* This is the array of all DMA channels */ |
| |
| static struct dma_channel_s g_dmach[EFM32_DMA_NCHANNELS]; |
| |
| /* This array describes the available channel control data structures. Each |
| * structure must be aligned to a 256 address boundary. The last 8 or 9 |
| * bits of address are provided by the DMA controller: |
| * |
| * 8-Channels: |
| * Bit 7: Selects the alternate descriptor list |
| * Bits 4-6: The DMA channel (0-7) |
| * Bits 2-3: Selects the descriptor field |
| * Bits 0-1: Always zero |
| * |
| * 12-Channels: |
| * Bit 8: Selects the alternate descriptor list |
| * Bits 4-7: The DMA channel (0-11) |
| * Bits 2-3: Selects the descriptor field |
| * Bits 0-1: Always zero |
| */ |
| |
| #if EFM32_DMA_NCHANNELS <= 8 |
| # define DESC_TABLE_SIZE 8 |
| # define DESC_TABLE_ALIGN 256 /* 2*8*16 */ |
| #elif EFM32_DMA_NCHANNELS <= 16 |
| # define DESC_TABLE_SIZE 16 |
| # define DESC_TABLE_ALIGN 512 /* 2*16*16 */ |
| #else |
| # error Unknown descriptor table size |
| #endif |
| |
| #ifdef CONFIG_EFM32_DMA_ALTDSEC |
| static struct dma_descriptor_s |
| g_descriptors[DESC_TABLE_SIZE + EFM32_DMA_NCHANNELS] |
| aligned_data(DESC_TABLE_ALIGN); |
| #else |
| static struct dma_descriptor_s g_descriptors[EFM32_DMA_NCHANNELS] |
| aligned_data(DESC_TABLE_ALIGN); |
| #endif |
| |
| /**************************************************************************** |
| * Public Data |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: efm32_set_chctrl |
| * |
| * Description: |
| * Set the channel control register |
| * |
| ****************************************************************************/ |
| |
| static void efm32_set_chctrl(struct dma_channel_s *dmach, |
| dma_config_t config) |
| { |
| uintptr_t regaddr; |
| uint32_t decoded; |
| uint32_t regval; |
| |
| decoded = (uint32_t)(config & EFM32_DMA_SIGSEL_MASK) >> |
| EFM32_DMA_SIGSEL_SHIFT; |
| regval = (decoded << _DMA_CH_CTRL_SIGSEL_SHIFT); |
| decoded = (uint32_t)(config & EFM32_DMA_SOURCSEL_MASK) >> |
| EFM32_DMA_SOURCSEL_SHIFT; |
| regval |= (decoded << _DMA_CH_CTRL_SOURCESEL_SHIFT); |
| |
| regaddr = EFM32_DMA_CHN_CTRL(dmach->chan); |
| putreg32(regval, regaddr); |
| } |
| |
| /**************************************************************************** |
| * Name: efm32_align_shift |
| * |
| * Description: |
| * Set the channel control register |
| * |
| ****************************************************************************/ |
| |
| static inline unsigned int efm32_align_shift(dma_config_t config) |
| { |
| unsigned int shift; |
| |
| shift = (config & EFM32_DMA_XFERSIZE_MASK) >> EFM32_DMA_XFERSIZE_SHIFT; |
| DEBUGASSERT(shift != 3); |
| return shift; |
| } |
| |
| /**************************************************************************** |
| * Name: efm32_get_descriptor |
| * |
| * Description: |
| * Get the address of the primary or alternate descriptor assigned to the |
| * DMA channel |
| * |
| ****************************************************************************/ |
| |
| static inline struct dma_descriptor_s * |
| efm32_get_descriptor(struct dma_channel_s *dmach, bool alt) |
| { |
| uintptr_t base = alt ? getreg32(EFM32_DMA_ALTCTRLBASE) : |
| getreg32(EFM32_DMA_CTRLBASE); |
| return ((struct dma_descriptor_s *)base) + dmach->chan; |
| } |
| |
| /**************************************************************************** |
| * Name: efm32_dmac_interrupt |
| * |
| * Description: |
| * DMA interrupt handler |
| * |
| ****************************************************************************/ |
| |
| static int efm32_dmac_interrupt(int irq, void *context, void *arg) |
| { |
| struct dma_channel_s *dmach; |
| unsigned int chndx; |
| uint32_t pending; |
| uint32_t bit; |
| |
| /* Get the set of pending, unmasked global XDMAC interrupts */ |
| |
| pending = getreg32(EFM32_DMA_IF) & getreg32(EFM32_DMA_IEN); |
| putreg32(pending, EFM32_DMA_IFC); |
| |
| /* Check each bit to see which channel(s) have interrupted */ |
| |
| for (chndx = 0; chndx < EFM32_DMA_NCHANNELS && pending != 0; chndx++) |
| { |
| /* Are any interrupts pending for this channel? */ |
| |
| bit = 1 << chndx; |
| if ((pending & bit) != 0) |
| { |
| dmach = &g_dmach[chndx]; |
| |
| /* Put the DMA channel in the stopped state */ |
| |
| efm32_dmastop((DMA_HANDLE)dmach); |
| |
| /* Call the DMA completion callback */ |
| |
| if (dmach->callback) |
| { |
| dmach->callback((DMA_HANDLE)dmach, OK, dmach->arg); |
| dmach->callback = NULL; |
| } |
| |
| dmach->arg = NULL; |
| |
| /* Clear the bit in the sampled set of pending interrupts */ |
| |
| pending &= !bit; |
| } |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: arm_dma_initialize |
| * |
| * Description: |
| * Initialize the DMA subsystem |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| void weak_function arm_dma_initialize(void) |
| { |
| uint32_t regval; |
| int i; |
| |
| dmainfo("Initialize XDMAC0\n"); |
| |
| /* Initialize the channel list */ |
| |
| for (i = 0; i < EFM32_DMA_NCHANNELS; i++) |
| { |
| g_dmach[i].chan = i; |
| } |
| |
| /* Enable clocking to the DMA module. DMA is clocked by HFCORECLK. */ |
| |
| regval = getreg32(EFM32_CMU_HFCORECLKEN0); |
| regval |= CMU_HFCORECLKEN0_DMA; |
| putreg32(regval, EFM32_CMU_HFCORECLKEN0); |
| |
| /* Set the control base addresses. Note: EFM32_DMA_ALTCTRLBASE |
| * is a read-only register that provides the location where hardware |
| * will obtain the alternative descriptors. |
| */ |
| |
| putreg32((uint32_t)g_descriptors, EFM32_DMA_CTRLBASE); |
| |
| /* Attach DMA interrupt vector */ |
| |
| irq_attach(EFM32_IRQ_DMA, efm32_dmac_interrupt, NULL); |
| |
| /* Enable the DMA controller */ |
| |
| putreg32(DMA_CONFIG_EN, EFM32_DMA_CONFIG); |
| |
| /* Enable the IRQ at the AIC (still disabled at the DMA controller) */ |
| |
| up_enable_irq(EFM32_IRQ_DMA); |
| } |
| |
| /**************************************************************************** |
| * Name: efm32_dmachannel |
| * |
| * Description: |
| * Allocate a DMA channel. This function gives the caller mutually |
| * exclusive access to a DMA channel. |
| * |
| * If no DMA channel is available, then efm32_dmachannel() will wait |
| * until the holder of a channel relinquishes the channel by calling |
| * efm32_dmafree(). |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * This function ALWAYS returns a non-NULL, void* DMA channel handle. |
| * |
| * Assumptions: |
| * - The caller can wait for a DMA channel to be freed if it is not |
| * available. |
| * |
| ****************************************************************************/ |
| |
| DMA_HANDLE efm32_dmachannel(void) |
| { |
| struct dma_channel_s *dmach; |
| unsigned int chndx; |
| uint32_t bit; |
| int ret; |
| |
| /* Take a count from the channel counting semaphore. We may block |
| * if there are no free channels. When we get the count, then we can |
| * be assured that a channel is available in the channel list and is |
| * reserved for us. |
| */ |
| |
| ret = nxsem_wait_uninterruptible(&g_dmac.chansem); |
| if (ret < 0) |
| { |
| return NULL; |
| } |
| |
| /* Get exclusive access to the DMA channel list */ |
| |
| ret = nxmutex_lock(&g_dmac.lock); |
| if (ret < 0) |
| { |
| nxsem_post(&g_dmac.chansem); |
| return NULL; |
| } |
| |
| /* Search for an available DMA channel */ |
| |
| for (chndx = 0, dmach = NULL; chndx < EFM32_DMA_NCHANNELS; chndx++) |
| { |
| struct dma_channel_s *candidate = &g_dmach[chndx]; |
| if (!candidate->inuse) |
| { |
| dmach = candidate; |
| dmach->inuse = true; |
| |
| /* Clear any pending channel interrupts */ |
| |
| bit = 1 << chndx; |
| putreg32(bit, EFM32_DMA_IFC); |
| |
| /* Disable the channel */ |
| |
| putreg32(bit, EFM32_DMA_CHENC); |
| break; |
| } |
| } |
| |
| nxmutex_unlock(&g_dmac.lock); |
| |
| /* Since we have reserved a DMA descriptor by taking a count from chansem, |
| * it would be a serious logic failure if we could not find a free channel |
| * for our use. |
| */ |
| |
| DEBUGASSERT(dmach); |
| return (DMA_HANDLE)dmach; |
| } |
| |
| /**************************************************************************** |
| * Name: efm32_dmafree |
| * |
| * Description: |
| * Release a DMA channel. If another thread is waiting for this DMA channel |
| * in a call to efm32_dmachannel, then this function will re-assign the |
| * DMA channel to that thread and wake it up. NOTE: The 'handle' used |
| * in this argument must NEVER be used again until efm32_dmachannel() is |
| * called again to re-gain access to the channel. |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * - The caller holds the DMA channel. |
| * - There is no DMA in progress |
| * |
| ****************************************************************************/ |
| |
| void efm32_dmafree(DMA_HANDLE handle) |
| { |
| struct dma_channel_s *dmach = (struct dma_channel_s *)handle; |
| |
| DEBUGASSERT(dmach != NULL && dmach->inuse); |
| dmainfo("DMA channel %d\n", dmach->chan); |
| |
| /* Disable the channel */ |
| |
| putreg32(1 << dmach->chan, EFM32_DMA_CHENC); |
| |
| /* Mark the channel no longer in use. Clearing the in-use flag is an |
| * atomic operation and so should be safe. |
| */ |
| |
| dmach->inuse = false; |
| |
| /* And increment the count of free channels... possibly waking up a |
| * thread that may be waiting for a channel. |
| */ |
| |
| nxsem_post(&g_dmac.chansem); |
| } |
| |
| /**************************************************************************** |
| * Name: efm32_rxdmasetup |
| * |
| * Description: |
| * Configure an RX (peripheral-to-memory) DMA before starting the transfer. |
| * |
| * Input Parameters: |
| * paddr - Peripheral address (source) |
| * maddr - Memory address (destination) |
| * nbytes - Number of bytes to transfer. Must be an even multiple of the |
| * configured transfer size. |
| * config - Channel configuration selections |
| * |
| ****************************************************************************/ |
| |
| void efm32_rxdmasetup(DMA_HANDLE handle, uintptr_t paddr, uintptr_t maddr, |
| size_t nbytes, dma_config_t config) |
| { |
| struct dma_channel_s *dmach = (struct dma_channel_s *)handle; |
| struct dma_descriptor_s *desc; |
| unsigned int xfersize; |
| unsigned int shift; |
| uint32_t regval; |
| uint32_t incr; |
| uint32_t mask; |
| |
| DEBUGASSERT(dmach != NULL && dmach->inuse); |
| |
| /* Get the properly alignment shift and mask */ |
| |
| shift = efm32_align_shift(config); |
| mask = ALIGN_MASK(shift); |
| |
| /* Make sure that the number of bytes we are asked to transfer is a |
| * multiple of the transfer size. |
| */ |
| |
| xfersize = (1 << shift); |
| nbytes = ALIGN_DOWN_MASK(nbytes, mask); |
| DEBUGASSERT(nbytes > 0); |
| |
| /* Save the configuration (for efm32_dmastart()). */ |
| |
| dmach->config = config; |
| |
| /* Configure for the selected peripheral */ |
| |
| efm32_set_chctrl(dmach, config); |
| |
| /* Configure the primary channel descriptor */ |
| |
| desc = efm32_get_descriptor(dmach, false); |
| desc->srcend = (uint32_t *)paddr; |
| desc->dstend = (uint32_t *)(maddr + nbytes - xfersize); |
| |
| /* No source increment, destination increments according to transfer size. |
| * No privileges. Arbitrate after each transfer. Default priority. |
| */ |
| |
| regval = DMA_CTRL_SRC_INC_NONE | DMA_CTRL_DST_PROT_NON_PRIVILEGED | |
| DMA_CTRL_SRC_PROT_NON_PRIVILEGED | DMA_CTRL_R_POWER_1 | |
| (0 << _DMA_CTRL_NEXT_USEBURST_SHIFT) | _DMA_CTRL_CYCLE_CTRL_BASIC; |
| |
| switch (shift) |
| { |
| default: |
| case 0: /* Byte transfer */ |
| regval |= DMA_CTRL_DST_SIZE_BYTE | DMA_CTRL_SRC_SIZE_BYTE; |
| incr = DMA_CTRL_DST_INC_BYTE; |
| break; |
| |
| case 1: /* Half word transfer */ |
| regval |= DMA_CTRL_DST_SIZE_HALFWORD | DMA_CTRL_SRC_SIZE_HALFWORD; |
| incr = DMA_CTRL_DST_INC_HALFWORD; |
| break; |
| |
| case 2: /* Word transfer */ |
| regval |= DMA_CTRL_DST_SIZE_WORD | DMA_CTRL_SRC_SIZE_WORD; |
| incr = DMA_CTRL_DST_INC_WORD; |
| break; |
| } |
| |
| /* Do we need to increment the memory address? */ |
| |
| if ((config & EFM32_DMA_MEMINCR_MASK) == EFM32_DMA_MEMINCR) |
| { |
| regval |= incr; |
| } |
| |
| /* Set the number of transfers (minus 1) */ |
| |
| DEBUGASSERT((nbytes >> shift) < 1024); |
| regval |= ((nbytes >> shift) - 1) << _DMA_CTRL_N_MINUS_1_SHIFT; |
| desc->ctrl = regval; |
| desc->user = 0; |
| } |
| |
| /**************************************************************************** |
| * Name: efm32_txdmasetup |
| * |
| * Description: |
| * Configure an TX (memory-to-memory) DMA before starting the transfer. |
| * |
| * Input Parameters: |
| * paddr - Peripheral address (destination) |
| * maddr - Memory address (source) |
| * nbytes - Number of bytes to transfer. Must be an even multiple of the |
| * configured transfer size. |
| * config - Channel configuration selections |
| * |
| ****************************************************************************/ |
| |
| void efm32_txdmasetup(DMA_HANDLE handle, uintptr_t paddr, uintptr_t maddr, |
| size_t nbytes, dma_config_t config) |
| { |
| struct dma_channel_s *dmach = (struct dma_channel_s *)handle; |
| struct dma_descriptor_s *desc; |
| unsigned int xfersize; |
| unsigned int shift; |
| uint32_t regval; |
| uint32_t incr; |
| uint32_t mask; |
| |
| DEBUGASSERT(dmach != NULL && dmach->inuse); |
| |
| /* Get the properly alignment shift and mask */ |
| |
| shift = efm32_align_shift(config); |
| mask = ALIGN_MASK(shift); |
| |
| /* Make sure that the number of bytes we are asked to transfer is a |
| * multiple of the transfer size. |
| */ |
| |
| xfersize = (1 << shift); |
| nbytes = ALIGN_DOWN_MASK(nbytes, mask); |
| DEBUGASSERT(nbytes > 0); |
| |
| /* Save the configuration (for efm32_dmastart()). */ |
| |
| dmach->config = config; |
| |
| /* Configure for the selected peripheral */ |
| |
| efm32_set_chctrl(dmach, config); |
| |
| /* Configure the primary channel descriptor */ |
| |
| desc = efm32_get_descriptor(dmach, false); |
| desc->srcend = (uint32_t *)(maddr + nbytes - xfersize); |
| desc->dstend = (uint32_t *)paddr; |
| |
| /* No destination increment, source increments according to transfer size. |
| * No privileges. Arbitrate after each transfer. Default priority. |
| */ |
| |
| regval = DMA_CTRL_DST_INC_NONE | DMA_CTRL_DST_PROT_NON_PRIVILEGED | |
| DMA_CTRL_SRC_PROT_NON_PRIVILEGED | DMA_CTRL_R_POWER_1 | |
| (0 << _DMA_CTRL_NEXT_USEBURST_SHIFT) | _DMA_CTRL_CYCLE_CTRL_BASIC; |
| |
| switch (shift) |
| { |
| default: |
| case 0: /* Byte transfer */ |
| regval |= DMA_CTRL_DST_SIZE_BYTE | DMA_CTRL_SRC_SIZE_BYTE; |
| incr = DMA_CTRL_SRC_INC_BYTE; |
| break; |
| |
| case 1: /* Half word transfer */ |
| regval |= DMA_CTRL_DST_SIZE_HALFWORD | DMA_CTRL_SRC_SIZE_HALFWORD; |
| incr = DMA_CTRL_SRC_INC_HALFWORD; |
| break; |
| |
| case 2: /* Word transfer */ |
| regval |= DMA_CTRL_DST_SIZE_WORD | DMA_CTRL_SRC_SIZE_WORD; |
| incr = DMA_CTRL_SRC_INC_WORD; |
| break; |
| } |
| |
| /* Do we need to increment the memory address? */ |
| |
| if ((config & EFM32_DMA_MEMINCR_MASK) == EFM32_DMA_MEMINCR) |
| { |
| regval |= incr; |
| } |
| |
| /* Set the number of transfers (minus 1) */ |
| |
| DEBUGASSERT((nbytes >> shift) < 1024); |
| regval |= ((nbytes >> shift) - 1) << _DMA_CTRL_N_MINUS_1_SHIFT; |
| desc->ctrl = regval; |
| desc->user = 0; |
| } |
| |
| /**************************************************************************** |
| * Name: efm32_dmastart |
| * |
| * Description: |
| * Start the DMA transfer |
| * |
| * Assumptions: |
| * - DMA handle allocated by efm32_dmachannel() |
| * - No DMA in progress |
| * |
| ****************************************************************************/ |
| |
| void efm32_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg) |
| { |
| struct dma_channel_s *dmach = (struct dma_channel_s *)handle; |
| irqstate_t flags; |
| uint32_t regval; |
| uint32_t bit; |
| |
| DEBUGASSERT(dmach && dmach->inuse && dmach->desc); |
| |
| /* Save the DMA complete callback info */ |
| |
| dmach->callback = callback; |
| dmach->arg = arg; |
| |
| /* Finish configuring the channel */ |
| |
| bit = 1 << dmach->chan; |
| if ((dmach->config & EFM32_DMA_SINGLE_MASK) == EFM32_DMA_BUFFER_FULL) |
| { |
| /* Disable the single requests for the channel (i.e. do not react to |
| * data available, wait for buffer full) |
| */ |
| |
| putreg32(bit, EFM32_DMA_CHUSEBURSTS); |
| |
| /* Enable buffer-full requests for the channel */ |
| |
| putreg32(bit, EFM32_DMA_CHREQMASKC); |
| } |
| else |
| { |
| /* Enable the single requests for the channel */ |
| |
| putreg32(bit, EFM32_DMA_CHUSEBURSTC); |
| |
| /* Disable buffer-full requests for the channel */ |
| |
| putreg32(bit, EFM32_DMA_CHREQMASKS); |
| } |
| |
| /* Use the primary data structure for the channel */ |
| |
| putreg32(bit, EFM32_DMA_CHALTC); |
| |
| /* Enable DMA completion interrupts */ |
| |
| flags = enter_critical_section(); |
| regval = getreg32(EFM32_DMA_IEN); |
| regval |= bit; |
| putreg32(regval, EFM32_DMA_IEN); |
| |
| /* Enable the channel */ |
| |
| putreg32(bit, EFM32_DMA_CHENS); |
| leave_critical_section(flags); |
| } |
| |
| /**************************************************************************** |
| * Name: efm32_dmastop |
| * |
| * Description: |
| * Cancel the DMA. After efm32_dmastop() is called, the DMA channel is |
| * reset and efm32_dmasetup() must be called before efm32_dmastart() can be |
| * called again |
| * |
| * Assumptions: |
| * - DMA handle allocated by efm32_dmachannel() |
| * |
| ****************************************************************************/ |
| |
| void efm32_dmastop(DMA_HANDLE handle) |
| { |
| struct dma_channel_s *dmach = (struct dma_channel_s *)handle; |
| irqstate_t flags; |
| uint32_t regval; |
| uint32_t bit; |
| |
| DEBUGASSERT(dmach); |
| bit = 1 << dmach->chan; |
| |
| /* Disable the channel */ |
| |
| flags = enter_critical_section(); |
| putreg32(bit, EFM32_DMA_CHENC); |
| |
| /* Disable Channel interrupts */ |
| |
| regval = getreg32(EFM32_DMA_IEN); |
| regval |= bit; |
| putreg32(regval, EFM32_DMA_IEN); |
| leave_critical_section(flags); |
| } |
| |
| /**************************************************************************** |
| * Name: efm32_dmasample |
| * |
| * Description: |
| * Sample DMA register contents |
| * |
| * Assumptions: |
| * - DMA handle allocated by efm32_dmachannel() |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_DEBUG_DMA_INFO |
| void efm32_dmasample(DMA_HANDLE handle, struct efm32_dmaregs_s *regs) |
| { |
| struct dma_channel_s *dmach = (struct dma_channel_s *)handle; |
| uintptr_t regaddr; |
| irqstate_t flags; |
| |
| /* Sample DMA registers. */ |
| |
| flags = enter_critical_section(); |
| |
| regs->status = getreg32(EFM32_DMA_STATUS); |
| regs->ctrlbase = getreg32(EFM32_DMA_CTRLBASE); |
| regs->altctrlbase = getreg32(EFM32_DMA_ALTCTRLBASE); |
| regs->chwaitstatus = getreg32(EFM32_DMA_CHWAITSTATUS); |
| regs->chusebursts = getreg32(EFM32_DMA_CHUSEBURSTS); |
| regs->chreqmasks = getreg32(EFM32_DMA_CHREQMASKS); |
| regs->chens = getreg32(EFM32_DMA_CHENS); |
| regs->chalts = getreg32(EFM32_DMA_CHALTS); |
| regs->chpris = getreg32(EFM32_DMA_CHPRIS); |
| regs->errorc = getreg32(EFM32_DMA_ERRORC); |
| regs->chreqstatus = getreg32(EFM32_DMA_CHREQSTATUS); |
| regs->chsreqstatus = getreg32(EFM32_DMA_CHSREQSTATUS); |
| regs->ifr = getreg32(EFM32_DMA_IF); |
| regs->ien = getreg32(EFM32_DMA_IEN); |
| #if defined(CONFIG_EFM32_EFM32GG) |
| regs->ctrl = getreg32(EFM32_DMA_CTRL); |
| regs->rds = getreg32(EFM32_DMA_RDS); |
| regs->loop0 = getreg32(EFM32_DMA_LOOP0); |
| regs->loop1 = getreg32(EFM32_DMA_LOOP1); |
| regs->rect0 = getreg32(EFM32_DMA_RECT0); |
| #endif |
| |
| /* Sample channel control register */ |
| |
| regaddr = EFM32_DMA_CHN_CTRL(dmach->chan) |
| regs->chnctrl = getreg32(regaddr); |
| |
| leave_critical_section(flags); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: efm32_dmadump |
| * |
| * Description: |
| * Dump previously sampled DMA register contents |
| * |
| * Assumptions: |
| * - DMA handle allocated by efm32_dmachannel() |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_DEBUG_DMA_INFO |
| void efm32_dmadump(DMA_HANDLE handle, const struct efm32_dmaregs_s *regs, |
| const char *msg) |
| { |
| struct dma_channel_s *dmach = (struct dma_channel_s *)handle; |
| |
| dmainfo("%s\n", msg); |
| dmainfo(" DMA Registers:\n"); |
| dmainfo(" STATUS: %08x\n", regs->status); |
| dmainfo(" CTRLBASE: %08x\n", regs->ctrlbase); |
| dmainfo(" ALTCTRLBASE: %08x\n", regs->altctrlbase); |
| dmainfo(" CHWAITSTATUS: %08x\n", regs->chwaitstatus); |
| dmainfo(" CHUSEBURSTS: %08x\n", regs->chusebursts); |
| dmainfo(" CHREQMASKS: %08x\n", regs->chreqmasks); |
| dmainfo(" CHENS: %08x\n", regs->chens); |
| dmainfo(" CHALTS: %08x\n", regs->chalts); |
| dmainfo(" CHPRIS: %08x\n", regs->chpris); |
| dmainfo(" ERRORC: %08x\n", regs->errorc); |
| dmainfo(" CHREQSTATUS: %08x\n", regs->chreqstatus); |
| dmainfo(" CHSREQSTATUS: %08x\n", regs->chsreqstatus); |
| dmainfo(" IEN: %08x\n", regs->ien); |
| #if defined(CONFIG_EFM32_EFM32GG) |
| dmainfo(" CTRL: %08x\n", regs->ctrl); |
| dmainfo(" RDS: %08x\n", regs->rds); |
| dmainfo(" LOOP0: %08x\n", regs->loop0); |
| dmainfo(" LOOP1: %08x\n", regs->loop1); |
| dmainfo(" RECT0: %08x\n", regs->rect0); |
| #endif |
| dmainfo(" DMA Channel %d Registers:\n", dmach->chan); |
| dmainfo(" CHCTRL: %08x\n", regs->chnctrl); |
| } |
| #endif |