rt-thread-official/bsp/x1000/drivers/dma.c

358 lines
11 KiB
C
Raw Normal View History

2017-11-11 13:51:56 +08:00
/*
* 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
*/
/*********************************************************************************************************
** ͷ<EFBFBD>ļ<EFBFBD>
*********************************************************************************************************/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include "dma.h"
/*********************************************************************************************************
** ȫ<EFBFBD>ֱ<EFBFBD><EFBFBD><EFBFBD>
*********************************************************************************************************/
/*********************************************************************************************************
** <EFBFBD><EFBFBD><EFBFBD>
*********************************************************************************************************/
#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)
/*********************************************************************************************************
** ȫ<EFBFBD>ֱ<EFBFBD><EFBFBD><EFBFBD>
*********************************************************************************************************/
struct rt_dma_channel _g_dma_chan_head;
static rt_bool_t rt_dma_init_flag = RT_FALSE;
/*********************************************************************************************************
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: _dma_init
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: <EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD> DMA
** <EFBFBD> <EFBFBD><EFBFBD>: void
** <EFBFBD><EFBFBD> <EFBFBD><EFBFBD>: void
** <EFBFBD><EFBFBD> ע: NONE
*********************************************************************************************************/
void _dma_init (void)
{
_g_dma_chan_head.ch = -1;
rt_list_init(&(_g_dma_chan_head.list));
} /* _dma_init */
/*********************************************************************************************************
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: rt_dma_drv_install
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: DMA ͨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>װ
** <EFBFBD> <EFBFBD><EFBFBD>: rt_uint32_t channel,RT_DMA_FUNCS* funcs,rt_size_t maxBurstBytes
** <EFBFBD><EFBFBD> <EFBFBD><EFBFBD>: rt_err_t
** <EFBFBD><EFBFBD> ע: NONE
*********************************************************************************************************/
rt_err_t rt_dma_drv_install(struct rt_dma_channel *dmac, struct dma_ops *ops,struct dma_config *config,void* user_data)
{
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
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;
}
/* <20><><EFBFBD>ص<EFBFBD>ͨ<EFBFBD><CDA8><EFBFBD>б<EFBFBD> */
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;
}
//
///*********************************************************************************************************
//** <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: rt_dma_flush
//** <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: ɾ<><C9BE><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><D0B1>ӳٴ<D3B3><D9B4><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD>ûص<C3BB><D8B5><EFBFBD><EFBFBD><EFBFBD>)
//** <20><20><>: rt_uint32_t channel
//** <20><> <20><>: rt_err_t
//** <20><> ע: 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;
// /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
// 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)
//// {
//// /* <20><><EFBFBD><EFBFBD> DMA<4D><41>Ϣ */
////// if(message->release_cb != RT_NULL)
////// message->release_cb(dmac,message);
//// }
//
// __DMA_CHANNEL_RESET(dmac);
//
//// dmac->tmsg_actived = RT_FALSE;
// return RT_EOK;
//}
/*********************************************************************************************************
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: rt_dma_trans_message
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: <EFBFBD><EFBFBD><EFBFBD><EFBFBD> һ<EFBFBD><EFBFBD>DMA<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
** <EFBFBD> <EFBFBD><EFBFBD>: rt_uint32_t channel DMA_MSG *pMsg
** <EFBFBD><EFBFBD> <EFBFBD><EFBFBD>: rt_err_t
** <EFBFBD><EFBFBD> ע: 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;
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
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;
}
//<2F>жϴ<D0B6><CFB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD>
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;
//<2F><><EFBFBD><EFBFBD>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); /* <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ԫ */
(void)ret;
} while (0);
}
else
{
rt_hw_interrupt_enable(level);
}
return RT_EOK;
}
/*********************************************************************************************************
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: rt_dma_configture
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: DMA ͨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
** <EFBFBD> <EFBFBD><EFBFBD>: struct rt_dma_channel *dmac,struct dma_config *config
** <EFBFBD><EFBFBD> <EFBFBD><EFBFBD>: rt_err_t
** <EFBFBD><EFBFBD> ע: NONE
*********************************************************************************************************/
rt_err_t rt_dma_configture (struct rt_dma_channel *dmac,struct dma_config *config)
{
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
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 */
/*********************************************************************************************************
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: rt_dma_get_current_message
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: DMA <EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
** <EFBFBD> <EFBFBD><EFBFBD>: struct rt_dma_channel *dmac
** <EFBFBD><EFBFBD> <EFBFBD><EFBFBD>: struct dma_message *
** <EFBFBD><EFBFBD> ע: 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 */
/*********************************************************************************************************
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: rt_dma_contex_service
** <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: DMA <EFBFBD>жϷ<EFBFBD><EFBFBD><EFBFBD>
** <EFBFBD> <EFBFBD><EFBFBD>: rt_uint32_t channel
** <EFBFBD><EFBFBD> <EFBFBD><EFBFBD>: rt_err_t
** <EFBFBD><EFBFBD> ע: 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;
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
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();
/* <20><><EFBFBD>ȷ<EFBFBD><C8B7><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>е<EFBFBD><D0B5><EFBFBD>Ϣ */
last_message = &(dmac->msg_list[dmac->get_index]);
dmac->get_index = next_index;
if(dmac->get_index != dmac->put_index)
{
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣδ<CFA2><CEB4><EFBFBD>ͣ<EFBFBD><CDA3><EFBFBD><EFBFBD>ȴ<EFBFBD><C8B4><EFBFBD> */
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); /* <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ԫ */
(void)ret;
} while (0);
}
else
{
rt_hw_interrupt_enable(level);
}
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>Ϣ<EFBFBD>Ļص<C4BB><D8B5><EFBFBD><EFBFBD><EFBFBD> */
if (dmac->complete != RT_NULL)
{
dmac->complete(dmac, last_message);
}
break;
default:
break;
}
return RT_EOK;
}