123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- /* *****************************************************************************
- * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
- * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Except as contained in this notice, the name of Maxim Integrated
- * Products, Inc. shall not be used except as stated in the Maxim Integrated
- * Products, Inc. Branding Policy.
- *
- * The mere transfer of this software does not imply any licenses
- * of trade secrets, proprietary technology, copyrights, patents,
- * trademarks, maskwork rights, or any other form of intellectual
- * property whatsoever. Maxim Integrated Products, Inc. retains all
- * ownership rights.
- *
- * $Date: 2019-07-01 11:06:19 -0500 (Mon, 01 Jul 2019) $
- * $Revision: 44383 $
- *
- **************************************************************************** */
- #include <stddef.h>
- #include <stdint.h>
- #include "mxc_config.h"
- #include "mxc_assert.h"
- #include "mxc_lock.h"
- #include "mxc_sys.h"
- #include "dma.h"
- /*
- * Structure type
- */
- typedef struct {
- unsigned int valid; /* Flag to invalidate this resource */
- unsigned int instance; /* Hardware instance of this DMA controller */
- unsigned int id; /* Channel ID, which matches the index into the underlying hardware */
- mxc_dma_ch_regs_t *regs; /* Pointer to the registers for this channel */
- void (*cb)(int, int); /* Pointer to a callback function type */
- } dma_channel_t;
- #define CHECK_HANDLE(x) ((x >= 0) && (x < MXC_DMA_CHANNELS) && (dma_resource[x].valid))
- /* DMA driver must be initialized once before use, and may not be initialized again without shutdown, as it is a shared resource */
- static unsigned int dma_initialized = 0;
- static dma_channel_t dma_resource[MXC_DMA_CHANNELS];
- static uint32_t dma_lock;
- /* Initialize DMA to known state */
- int DMA_Init(void)
- {
- int i;
-
- if (dma_initialized) {
- return E_BAD_STATE;
- }
-
- /* Initialize any system-level DMA settings */
- SYS_DMA_Init();
-
- /* Initialize mutex */
- mxc_free_lock(&dma_lock);
- if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
- return E_BUSY;
- }
-
- /* Ensure all channels are disabled at start, clear flags, init handles */
- MXC_DMA->cn = 0;
- for (i = 0; i < MXC_DMA_CHANNELS; i++) {
- dma_resource[i].valid = 0;
- dma_resource[i].instance = 0;
- dma_resource[i].id = i;
- dma_resource[i].regs = (mxc_dma_ch_regs_t *)&MXC_DMA->ch[i];
- dma_resource[i].regs->cfg = 0;
- dma_resource[i].regs->st = dma_resource[i].regs->st;
-
- dma_resource[i].cb = NULL;
- }
- dma_initialized++;
- mxc_free_lock(&dma_lock);
-
- return E_NO_ERROR;
- }
- /* Shut down DMA in an orderly manner, informing clients that their requests did not complete */
- int DMA_Shutdown(void)
- {
- int i;
-
- if (!dma_initialized) {
- /* Never initialized, so shutdown is not appropriate */
- return E_BUSY;
- }
-
- if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
- return E_BUSY;
- }
-
- /* Prevent any new resource allocation by this API */
- dma_initialized = 0;
- /* Disable interrupts, preventing future callbacks */
- MXC_DMA->cn = 0;
-
- /* For each channel:
- * - invalidate the handles held by clients
- * - stop any transfer in progress
- */
- for (i = 0; i < MXC_DMA_CHANNELS; i++) {
- dma_resource[i].regs->cfg = 0;
- if (dma_resource[i].valid) {
- dma_resource[i].valid = 0;
- if (dma_resource[i].cb != NULL) {
- dma_resource[i].cb(i, E_SHUTDOWN);
- }
- }
- }
-
- /* Disable any system-level DMA settings */
- SYS_DMA_Shutdown();
-
- mxc_free_lock(&dma_lock);
-
- return E_NO_ERROR;
- }
- /* Request DMA channel */
- /* Once "owned", this channel may be used directly via the DMA_GetCHRegs(ch) pointer, or */
- /* configured via the API functions */
- int DMA_AcquireChannel(void)
- {
- int i, channel;
-
- /* Check for initialization */
- if (!dma_initialized) {
- return E_BAD_STATE;
- }
-
- /* If DMA is locked return busy */
- if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
- return E_BUSY;
- }
-
- /* Default is no channel available */
- channel = E_NONE_AVAIL;
- if (dma_initialized) {
- for (i = 0; i < MXC_DMA_CHANNELS; i++) {
- if (!dma_resource[i].valid) {
- /* Found one */
- channel = i;
- dma_resource[i].valid = 1;
- dma_resource[i].regs->cfg = 0;
- dma_resource[i].regs->cnt_rld = 0; /* Used by DMA_Start() to conditionally set RLDEN */
- break;
- }
- }
- }
- mxc_free_lock(&dma_lock);
-
- return channel;
- }
- /* Release DMA channel */
- /* Callbacks will not be called */
- int DMA_ReleaseChannel(int ch)
- {
- if (CHECK_HANDLE(ch)) {
- if (mxc_get_lock(&dma_lock, 1) != E_NO_ERROR) {
- return E_BUSY;
- }
- dma_resource[ch].valid = 0;
- dma_resource[ch].regs->cfg = 0;
- dma_resource[ch].regs->st = dma_resource[ch].regs->st;
- mxc_free_lock(&dma_lock);
- } else {
- return E_BAD_PARAM;
- }
-
- return E_NO_ERROR;
- }
- /* Channel configuration */
- int DMA_ConfigChannel(int ch,
- dma_priority_t prio,
- dma_reqsel_t reqsel, unsigned int reqwait_en,
- dma_timeout_t tosel, dma_prescale_t pssel,
- dma_width_t srcwd, unsigned int srcinc_en,
- dma_width_t dstwd, unsigned int dstinc_en,
- unsigned int burst_size, unsigned int chdis_inten,
- unsigned int ctz_inten)
- {
- if (CHECK_HANDLE(ch) && (burst_size > 0)) {
- /* Designed to be safe, not speedy. Should not be called often */
- dma_resource[ch].regs->cfg =
- ((reqwait_en ? MXC_F_DMA_CFG_REQWAIT : 0) |
- (srcinc_en ? MXC_F_DMA_CFG_SRCINC : 0) |
- (dstinc_en ? MXC_F_DMA_CFG_DSTINC : 0) |
- (chdis_inten ? MXC_F_DMA_CFG_CHDIEN : 0) |
- (ctz_inten ? MXC_F_DMA_CFG_CTZIEN : 0) |
- prio |reqsel | tosel | pssel |
- (srcwd << MXC_F_DMA_CFG_SRCWD_POS) |
- (dstwd << MXC_F_DMA_CFG_DSTWD_POS) |
- (((burst_size - 1) << MXC_F_DMA_CFG_BRST_POS) & MXC_F_DMA_CFG_BRST));
- } else {
- return E_BAD_PARAM;
- }
-
- return E_NO_ERROR;
- }
- /*
- * DMA request selects for peripherals will override either src_addr or dst_addr.
- * In these cases, the overridden address is a don't care and may be 0.
- */
- int DMA_SetSrcDstCnt(int ch,
- void *src_addr,
- void *dst_addr,
- unsigned int count)
- {
- if (CHECK_HANDLE(ch)) {
- dma_resource[ch].regs->src = (unsigned int)src_addr;
- dma_resource[ch].regs->dst = (unsigned int)dst_addr;
- dma_resource[ch].regs->cnt = count;
- } else {
- return E_BAD_PARAM;
- }
-
- return E_NO_ERROR;
- }
- /* Must set en_reload == 1 to have any effect */
- int DMA_SetReload(int ch,
- void *src_addr_reload,
- void *dst_addr_reload,
- unsigned int count_reload)
- {
- if (CHECK_HANDLE(ch)) {
- dma_resource[ch].regs->src_rld = (unsigned int)src_addr_reload;
- dma_resource[ch].regs->dst_rld = (unsigned int)dst_addr_reload;
- if (dma_resource[ch].regs->cfg & MXC_F_DMA_CFG_CHEN) {
- /* If channel is already running, set RLDEN to enable next reload */
- dma_resource[ch].regs->cnt_rld = MXC_F_DMA_CNT_RLD_RLDEN | count_reload;
- } else {
- /* Otherwise, this is the initial setup, so DMA_Start() will handle setting that bit */
- dma_resource[ch].regs->cnt_rld = count_reload;
- }
- } else {
- return E_BAD_PARAM;
- }
-
- return E_NO_ERROR;
- }
- int DMA_SetCallback(int ch, void (*callback)(int, int))
- {
- if (CHECK_HANDLE(ch)) {
- /* Callback for interrupt handler, no checking is done, as NULL is valid for (none) */
- dma_resource[ch].cb = callback;
- } else {
- return E_BAD_PARAM;
- }
-
- return E_NO_ERROR;
- }
- /* Interrupt enable/disable */
- int DMA_EnableInterrupt(int ch)
- {
- if (CHECK_HANDLE(ch)) {
- MXC_DMA->cn |= (1 << ch);
- } else {
- return E_BAD_PARAM;
- }
-
- return E_NO_ERROR;
- }
- int DMA_DisableInterrupt(int ch)
- {
- if (CHECK_HANDLE(ch)) {
- MXC_DMA->cn &= ~(1 << ch);
- } else {
- return E_BAD_PARAM;
- }
-
- return E_NO_ERROR;
- }
- /* Channel interrupt flags */
- int DMA_GetFlags(int ch, unsigned int *fl)
- {
- if (CHECK_HANDLE(ch) && fl) {
- *fl = dma_resource[ch].regs->st;
- } else {
- return E_BAD_PARAM;
- }
-
- return E_NO_ERROR;
- }
- int DMA_ClearFlags(int ch)
- {
- if (CHECK_HANDLE(ch)) {
- dma_resource[ch].regs->st = dma_resource[ch].regs->st;
- } else {
- return E_BAD_PARAM;
- }
-
- return E_NO_ERROR;
- }
- /* Start channel */
- int DMA_Start(int ch)
- {
- if (CHECK_HANDLE(ch)) {
- DMA_ClearFlags(ch);
- if (dma_resource[ch].regs->cnt_rld) {
- dma_resource[ch].regs->cfg |= (MXC_F_DMA_CFG_CHEN | MXC_F_DMA_CFG_RLDEN);
- } else {
- dma_resource[ch].regs->cfg |= MXC_F_DMA_CFG_CHEN;
- }
- } else {
- return E_BAD_PARAM;
- }
-
- return E_NO_ERROR;
- }
- /* Stop channel */
- int DMA_Stop(int ch)
- {
- if (CHECK_HANDLE(ch)) {
- dma_resource[ch].regs->cfg &= ~MXC_F_DMA_CFG_CHEN;
- } else {
- return E_BAD_PARAM;
- }
-
- return E_NO_ERROR;
- }
- /* Get pointer to registers, for advanced users */
- mxc_dma_ch_regs_t *DMA_GetCHRegs(int ch)
- {
- if (CHECK_HANDLE(ch)) {
- return dma_resource[ch].regs;
- } else {
- return NULL;
- }
- }
- /* */
- void DMA_Handler(int ch)
- {
- /* Do callback, if enabled */
- if (dma_resource[ch].cb != NULL) {
- dma_resource[ch].cb(ch, E_NO_ERROR);
- }
- DMA_ClearFlags(ch);
- }
|