rt-thread-official/bsp/maxim/libraries/MAX32660PeriphDriver/Source/dma.c

375 lines
11 KiB
C

/* *****************************************************************************
* 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);
}