分类: Android平台
2015-03-17 13:57:13
一、4412 spec 11章, Direct Memory Access Controller (DMAC)
DMAC 分两部分:DMA_mem 和 DMA_peri 。前者有一个PL330组成,后者由两个PL330组成。
这两部分的特点如下:
1,DMA_peri: You should set all the peripherals as non-secure in TrustZone Protection Controller (TZPC) module since DMA_peri operates only as non-secure. 只在非安全模式下运行。
2,DMA_mem: The attributes that the DMA_mem DMA Controllers have:
· DMA_mem accesses memory through the AXI_IMGX bus and is located in IMG block.
· DMA_mem supports only the secured AXI transaction.
AXI_IMG 模块执行,只在安全模式下执行。(所以,普通模式下kernel没有这个控制器的注册,参考文件Dma.c (arch\arm\mach-exynos) 末尾)
关于PL330的具体细节,可以在在ARM官网下载PL330的datasheet 阅读:DDI0424D_dma330_r1p2_trm
附录:相关杂记:
1,下面博客记录了一个PL330 probe 的时候失败的案例,是因为时钟没打开导致读ID 失败。
http://blog.chinaunix.net/uid-122754-id-3182046.html
二、串口DMA 研究
在/kernel/drivers/tty/serial$ 目录中有Samsung.c ,make menuconfig打开CONFIG_SERIAL_SAMSUNG_DMA 宏, 串口DMA就被启动了。
在DMAC的驱动Pl330.c (drivers\dma) 中的pl330_filter函数中添加dump_stack 即可看到调度流程如下:
[ 3.865000] liucong pl330_filter
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
又在DMAC的驱动Pl330.c (drivers\dma) 中的pl330_alloc_chan_resources函数中添加dump_stack 即可看到调度流程如下:
[ 3.865000] liucong pl330_alloc_chan_resources
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
[ 3.865000] [
其中在文件Samsung.c (drivers\tty\serial) 的 s3c64xx_serial_startup函数中有关于DMA的初始化和申请的一段:
#ifdef CONFIG_SERIAL_SAMSUNG_DMA
if (uart_dma->use_dma) {
/* initialize err_occurred */
ourport->err_occurred = 0;
/* Acquire DMA channels */
while (!acquire_dma(uart_dma))
msleep(20);
/* Alloc buffer for dma */
uart_dma->rx_buff = kmalloc(RX_BUFFER_SIZE, GFP_KERNEL);
uart_dma->rx.req_size = RX_BUFFER_SIZE;
/* uart_rx_dma_request */
uart_rx_dma_request(ourport);
/* UnMask Err interrupt */
uintm = rd_regl(port, S3C64XX_UINTM);
uintm &= ~(S3C64XX_UINTM_ERR_MSK);
wr_regl(port, S3C64XX_UINTM, uintm);
/* enable rx dma mode */
enable_rx_dma(port);
}
#endif
三、MMC DMA研究
static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
{
int ret;
struct mmc_data *data = host->data;
ret = mmci_dma_prep_data(host, host->data, NULL);
if (ret)
return ret;
/* Okay, go for it. */
dev_vdbg(mmc_dev(host->mmc),
"Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n",
data->sg_len, data->blksz, data->blocks, data->flags);
dmaengine_submit(host->dma_desc_current);
dma_async_issue_pending(host->dma_current);
datactrl |= MCI_DPSM_DMAENABLE;
/* Trigger the DMA transfer */
writel(datactrl, host->base + MMCIDATACTRL);
/*
* Let the MMCI say when the data is ended and it's time
* to fire next DMA request. When that happens, MMCI will
* call mmci_data_end()
*/
writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK,
host->base + MMCIMASK0);
return 0;
}
蓝色标记的即是触发DMAC的开关,而USB 并没有这样的开关,那么USB 的DMA 是怎么回事呢?相信我,接下来更精彩!
参考链接:
http://blog.csdn.net/qq405180763/article/details/14524675
四、USB DMA的调查:
Usb dma 使用:
首先看一个USB 结构体:
struct usb_bus {
struct device *controller; /* host/master side hardware */
int busnum; /* Bus number (in order of reg) */
const char *bus_name; /* stable id (PCI slot_name etc) */
u8 uses_dma; /* Does the host controller use DMA? */
u8 uses_pio_for_control; /*
* Does the host controller use PIO
* for control transfers?
*/
u8 otg_port; /* 0, or number of OTG/HNP port */
... ...
标蓝的字就指明了这个USB 设备是否使用DMA 。
然后在函数int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,gfp_t mem_flags)中会判断这个值。
if (hcd->self.uses_dma) {//判断是否DMA 传输
urb->setup_dma = dma_map_single( //返回一个设备可以识别的总线地址
hcd->self.controller,
urb->setup_packet,
sizeof(struct usb_ctrlrequest),//urb 结构类型的请求结构体
DMA_TO_DEVICE);//从内存到设备
========================研究结论=================
USB 应该只是用了DMA 的接口来获取一个总线地址,并没有真正使用DMA 控制器,USB 的ehci 控制器应该自己具有类似DMA 的功能。
在内核里面通过 dma_map_single返回一个地址,这个地址是内存的物理地址,这个地址是IOMMU 能识别的。
所以接下来调查uboot 里面的给ehci的地址也应该是物理地址。
我采取的方法是,把所有变量编译链接的地址改成跟物理内存实际范围一样的状况,物理内存的实际地址范围是4000000-8000000 ,而我们的链接地址是C000000-1000000,所以我们要改链接地址,
使得他们的范围对应,这里涉及到了一点编译链接的知识。
这里涉及到要改链接地址:
要修改两个地方:
1,./board/samsung/smdk4412/config.mk:13:CONFIG_SYS_TEXT_BASE = 0xc3e00000
2,Tiny4412.h (include\configs)
#define CONFIG_ENABLE_MMU
/*
#ifdef CONFIG_ENABLE_MMU
#define CONFIG_SYS_MAPPED_RAM_BASE 0xc0000000
#define virt_to_phys(x) virt_to_phy_s5pv310(x)
#else
#define CONFIG_SYS_MAPPED_RAM_BASE CONFIG_SYS_SDRAM_BASE
#define virt_to_phys(x) (x)
#endif*/
#ifdef CONFIG_ENABLE_MMU
#define CONFIG_SYS_MAPPED_RAM_BASE 0x40000000
#define virt_to_phys(x) (x)
#else
#define CONFIG_SYS_MAPPED_RAM_BASE CONFIG_SYS_SDRAM_BASE
#define virt_to_phys(x) (x)
#endif