358 lines
11 KiB
C
358 lines
11 KiB
C
/*
|
||
* File : dma.c
|
||
* This file is part of RT-Thread RTOS
|
||
* COPYRIGHT (C) 2008 - 2012, RT-Thread Development Team
|
||
*
|
||
* 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 Free Software Foundation; either version 2 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License along
|
||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
*
|
||
* Change Logs:
|
||
* Date Author Notes
|
||
* 2015-11-19 Urey the first version
|
||
*/
|
||
|
||
|
||
/*********************************************************************************************************
|
||
** Í·Îļþ
|
||
*********************************************************************************************************/
|
||
#include <rthw.h>
|
||
#include <rtthread.h>
|
||
#include <rtdevice.h>
|
||
|
||
#include "dma.h"
|
||
|
||
|
||
/*********************************************************************************************************
|
||
** È«¾Ö±äÁ¿
|
||
*********************************************************************************************************/
|
||
|
||
|
||
/*********************************************************************************************************
|
||
** ºê¶¨Òå
|
||
*********************************************************************************************************/
|
||
#define DMA_DEBUG 0
|
||
#if DMA_DEBUG
|
||
#include <stdio.h>
|
||
#define DMA_DBG(...) rt_kprintf("[DMA]"),rt_kprintf(__VA_ARGS__)
|
||
#else
|
||
#define DMA_DBG(...)
|
||
#endif
|
||
|
||
|
||
|
||
#define __DMA_CHANNEL_RESET(dmac) do { \
|
||
if (dmac->ops && dmac->ops->reset) {\
|
||
dmac->ops->reset(dmac); \
|
||
} \
|
||
} while (0)
|
||
|
||
#define __DMA_CHANNEL_TRANS(dmac, message, ret) do { \
|
||
if (dmac->ops && dmac->ops->trans) { \
|
||
ret = dmac->ops->trans(dmac, message); \
|
||
} \
|
||
} while (0)
|
||
|
||
#define __DMA_CHANNEL_STATUS(ch, ret) do { \
|
||
if (dmac->ops && dmac->ops->status) {\
|
||
ret = dmac->ops->status(dmac); \
|
||
} \
|
||
} while (0)
|
||
|
||
|
||
/*********************************************************************************************************
|
||
** È«¾Ö±äÁ¿
|
||
*********************************************************************************************************/
|
||
struct rt_dma_channel _g_dma_chan_head;
|
||
static rt_bool_t rt_dma_init_flag = RT_FALSE;
|
||
|
||
/*********************************************************************************************************
|
||
** º¯ÊýÃû³Æ: _dma_init
|
||
** ¹¦ÄÜÃèÊö: ³õʼ»¯ DMA
|
||
** Êä¡¡ Èë: void
|
||
** ·µ »Ø: void
|
||
** ±¸ ×¢: NONE
|
||
*********************************************************************************************************/
|
||
void _dma_init (void)
|
||
{
|
||
_g_dma_chan_head.ch = -1;
|
||
rt_list_init(&(_g_dma_chan_head.list));
|
||
} /* _dma_init */
|
||
|
||
|
||
/*********************************************************************************************************
|
||
** º¯ÊýÃû³Æ: rt_dma_drv_install
|
||
** ¹¦ÄÜÃèÊö: DMA ͨÓÃÇý¶¯³ÌÐò°²×°
|
||
** Êä¡¡ Èë: rt_uint32_t channel,RT_DMA_FUNCS* funcs,rt_size_t maxBurstBytes
|
||
** ·µ »Ø: rt_err_t
|
||
** ±¸ ×¢: NONE
|
||
*********************************************************************************************************/
|
||
rt_err_t rt_dma_drv_install(struct rt_dma_channel *dmac, struct dma_ops *ops,struct dma_config *config,void* user_data)
|
||
{
|
||
/* ²ÎÊý¼ì²é */
|
||
RT_ASSERT(dmac != RT_NULL);
|
||
|
||
if(rt_dma_init_flag == RT_FALSE)
|
||
{
|
||
rt_dma_init_flag = RT_TRUE;
|
||
|
||
_dma_init();
|
||
}
|
||
|
||
if(ops == RT_NULL)
|
||
{
|
||
DMA_DBG("dma param invalid.\r\n");
|
||
|
||
return -RT_EIO;
|
||
}
|
||
/* ¹ÒÔص½Í¨µÀÁбí */
|
||
rt_list_insert_after(&(_g_dma_chan_head.list),&(dmac->list));
|
||
|
||
dmac->ops = ops;
|
||
if(config != RT_NULL)
|
||
{
|
||
dmac->config.direction = config->direction;
|
||
dmac->config.src_addr_width = config->src_addr_width;
|
||
dmac->config.src_maxburst = config->src_maxburst;
|
||
dmac->config.dst_addr_width = config->dst_addr_width;
|
||
dmac->config.dst_maxburst = config->dst_maxburst;
|
||
}
|
||
|
||
dmac->user_data = user_data;
|
||
rt_memset(dmac->msg_list,0,RT_DMA_MAX_NODES * sizeof(struct dma_message));
|
||
__DMA_CHANNEL_RESET(dmac);
|
||
return RT_EOK;
|
||
}
|
||
|
||
struct rt_dma_channel *rt_dma_get_channel(int id)
|
||
{
|
||
struct rt_dma_channel *dmac;
|
||
struct rt_list_node *node;
|
||
|
||
for (node = _g_dma_chan_head.list.next; node != &(_g_dma_chan_head.list); node = node->next)
|
||
{
|
||
dmac = rt_list_entry(node, struct rt_dma_channel, list);
|
||
|
||
if(dmac->ch == id)
|
||
return dmac;
|
||
}
|
||
|
||
return RT_NULL;
|
||
}
|
||
//
|
||
///*********************************************************************************************************
|
||
//** º¯ÊýÃû³Æ: rt_dma_flush
|
||
//** ¹¦ÄÜÃèÊö: ɾ³ýËùÓб»ÑÓ³Ù´¦ÀíµÄ´«ÊäÇëÇó (²»µ÷Óûص÷º¯Êý)
|
||
//** Êä¡¡ Èë: rt_uint32_t channel
|
||
//** ·µ »Ø: rt_err_t
|
||
//** ±¸ ×¢: NONE
|
||
//*********************************************************************************************************/
|
||
//rt_err_t rt_dma_flush (struct rt_dma_channel *dmac)
|
||
//{
|
||
// rt_size_t data_size;
|
||
// struct dma_message *last_message,*message;
|
||
// rt_uint16_t next_index;
|
||
// /* ²ÎÊý¼ì²é */
|
||
// RT_ASSERT(dmac != RT_NULL);
|
||
//
|
||
//
|
||
// next_index = dmac->get_index + 1;
|
||
// if(next_index >= RT_DMA_MAX_NODES)
|
||
// next_index = 0;
|
||
//
|
||
//
|
||
//// while (rt_data_queue_pop(&(dmac->tmsg_queue),(const void **)&message, &data_size, 0) == RT_EOK)
|
||
//// {
|
||
//// /* Çå³ý DMAÏûÏ¢ */
|
||
////// if(message->release_cb != RT_NULL)
|
||
////// message->release_cb(dmac,message);
|
||
//// }
|
||
//
|
||
// __DMA_CHANNEL_RESET(dmac);
|
||
//
|
||
//// dmac->tmsg_actived = RT_FALSE;
|
||
// return RT_EOK;
|
||
//}
|
||
|
||
/*********************************************************************************************************
|
||
** º¯ÊýÃû³Æ: rt_dma_trans_message
|
||
** ¹¦ÄÜÃèÊö: Ìí¼Ó Ò»¸öDMAÇëÇó
|
||
** Êä¡¡ Èë: rt_uint32_t channel DMA_MSG *pMsg
|
||
** ·µ »Ø: rt_err_t
|
||
** ±¸ ×¢: NONE
|
||
*********************************************************************************************************/
|
||
rt_err_t rt_dma_trans_message (struct rt_dma_channel *dmac,struct dma_message* message)
|
||
{
|
||
rt_base_t level;
|
||
rt_err_t result;
|
||
rt_uint16_t next_index;
|
||
struct dma_message *msg_node;
|
||
/* ²ÎÊý¼ì²é */
|
||
RT_ASSERT(dmac != RT_NULL);
|
||
RT_ASSERT(message != RT_NULL);
|
||
RT_ASSERT(message->t_size <= (64 * 1024));
|
||
|
||
if(message->t_size == 0)
|
||
{
|
||
if (dmac->complete != RT_NULL)
|
||
{
|
||
dmac->complete(dmac, message);
|
||
}
|
||
return RT_EOK;
|
||
}
|
||
|
||
//Åжϴ«Êä¶ÓÁÐÊÇ·ñÂú
|
||
next_index = dmac->put_index + 1;
|
||
if(next_index >= RT_DMA_MAX_NODES)
|
||
next_index = 0;
|
||
if(next_index == dmac->get_index)
|
||
return -RT_ENOMEM;
|
||
|
||
level = rt_hw_interrupt_disable();
|
||
|
||
msg_node = &(dmac->msg_list[dmac->put_index]);
|
||
dmac->put_index = next_index;
|
||
|
||
//±£´æmessage
|
||
rt_memcpy(msg_node,message,sizeof(struct dma_message));
|
||
|
||
next_index = dmac->get_index + 1;
|
||
if(next_index >= RT_DMA_MAX_NODES)
|
||
next_index = 0;
|
||
/* check message list whether is empty */
|
||
if(next_index == dmac->put_index)
|
||
{
|
||
rt_hw_interrupt_enable(level);
|
||
/* Make a DMA transfer */
|
||
if(dmac->start != RT_NULL)
|
||
dmac->start(dmac,message);
|
||
|
||
do{
|
||
int ret;
|
||
__DMA_CHANNEL_TRANS(dmac, message, ret); /* ³õʼ»¯´«ÊäÖîÔª */
|
||
(void)ret;
|
||
} while (0);
|
||
}
|
||
else
|
||
{
|
||
rt_hw_interrupt_enable(level);
|
||
}
|
||
|
||
return RT_EOK;
|
||
}
|
||
|
||
/*********************************************************************************************************
|
||
** º¯ÊýÃû³Æ: rt_dma_configture
|
||
** ¹¦ÄÜÃèÊö: DMA ͨµÀÅäÖÃ
|
||
** Êä¡¡ Èë: struct rt_dma_channel *dmac,struct dma_config *config
|
||
** ·µ »Ø: rt_err_t
|
||
** ±¸ ×¢: NONE
|
||
*********************************************************************************************************/
|
||
rt_err_t rt_dma_configture (struct rt_dma_channel *dmac,struct dma_config *config)
|
||
{
|
||
/* ²ÎÊý¼ì²é */
|
||
RT_ASSERT(dmac != RT_NULL);
|
||
RT_ASSERT(config != RT_NULL);
|
||
|
||
dmac->config.direction = config->direction;
|
||
dmac->config.src_addr_width = config->src_addr_width;
|
||
dmac->config.src_maxburst = config->src_maxburst;
|
||
dmac->config.dst_addr_width = config->dst_addr_width;
|
||
dmac->config.dst_maxburst = config->dst_maxburst;
|
||
|
||
__DMA_CHANNEL_RESET(dmac);
|
||
|
||
return RT_EOK;
|
||
} /* rt_dma_configture */
|
||
|
||
/*********************************************************************************************************
|
||
** º¯ÊýÃû³Æ: rt_dma_get_current_message
|
||
** ¹¦ÄÜÃèÊö: DMA »ñÈ¡µ±Ç°´«ÊäµÄÏûÏ¢¾ä±ú
|
||
** Êä¡¡ Èë: struct rt_dma_channel *dmac
|
||
** ·µ »Ø: struct dma_message *
|
||
** ±¸ ×¢: NONE
|
||
*********************************************************************************************************/
|
||
struct dma_message * rt_dma_get_current_message (struct rt_dma_channel *dmac)
|
||
{
|
||
rt_base_t level;
|
||
struct dma_message *message;
|
||
|
||
level = rt_hw_interrupt_disable();
|
||
|
||
message = &(dmac->msg_list[dmac->get_index]);
|
||
|
||
rt_hw_interrupt_enable(level);
|
||
return message;
|
||
} /* rt_dma_get_current_message */
|
||
|
||
/*********************************************************************************************************
|
||
** º¯ÊýÃû³Æ: rt_dma_contex_service
|
||
** ¹¦ÄÜÃèÊö: DMA ÖжϷþÎñ
|
||
** Êä¡¡ Èë: rt_uint32_t channel
|
||
** ·µ »Ø: rt_err_t
|
||
** ±¸ ×¢: global
|
||
*********************************************************************************************************/
|
||
rt_err_t rt_dma_contex_service (struct rt_dma_channel *dmac,rt_uint32_t event)
|
||
{
|
||
rt_base_t level;
|
||
rt_size_t data_size;
|
||
struct dma_message *last_message,*message;
|
||
rt_uint16_t next_index;
|
||
|
||
/* ²ÎÊý¼ì²é */
|
||
RT_ASSERT(dmac != RT_NULL);
|
||
switch (event)
|
||
{
|
||
case RT_DMA_EVENT_COMPLETE:
|
||
next_index = dmac->get_index + 1;
|
||
if(next_index >= RT_DMA_MAX_NODES)
|
||
next_index = 0;
|
||
|
||
level = rt_hw_interrupt_disable();
|
||
/* ÓÅÏÈ·¢ËÍ »º³åÇøÖеÄÏûÏ¢ */
|
||
last_message = &(dmac->msg_list[dmac->get_index]);
|
||
dmac->get_index = next_index;
|
||
if(dmac->get_index != dmac->put_index)
|
||
{
|
||
/* ¶ÓÁÐÖÐÓÐÏûϢδ·¢ËÍ£¬ÓÅÏÈ´¦Àí */
|
||
message = &(dmac->msg_list[dmac->get_index]);
|
||
|
||
rt_hw_interrupt_enable(level);
|
||
/* Make a DMA transfer */
|
||
if(dmac->start != RT_NULL)
|
||
dmac->start(dmac,message);
|
||
|
||
do{
|
||
int ret;
|
||
__DMA_CHANNEL_TRANS(dmac, message, ret); /* ³õʼ»¯´«ÊäÖîÔª */
|
||
(void)ret;
|
||
} while (0);
|
||
}
|
||
else
|
||
{
|
||
rt_hw_interrupt_enable(level);
|
||
}
|
||
|
||
/* ´¦ÀíÉÏÒ»¸öÏûÏ¢µÄ»Øµ÷º¯Êý */
|
||
if (dmac->complete != RT_NULL)
|
||
{
|
||
dmac->complete(dmac, last_message);
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return RT_EOK;
|
||
}
|