linux内存学习笔记(三)——静态映射与DMA

来源:互联网 发布:淘宝服装质检标准 编辑:程序博客网 时间:2024/06/10 15:55
 

一、静态映射概述

在将linux移植到目标电路板时,通常会建立外设I/O内存到虚拟地址的静态映射。

能过在map_desc结构体添加新成员即可完成静态映射任务。

       struct map_desc{

              unsigned long virtual;

              unsigned long pfn;

              unsigned long length;

              unsigned long type;

              };

二、LPC3250相关

       static struct map_desc lpc32xx_io_desc[] __initdata = {

       {

              .virtual    = io_p2v(AHB0_START),

              .pfn        = __phys_to_pfn(AHB0_START),

              .length            = AHB0_SIZE,

              .type              = MT_DEVICE

       },

       {

              .virtual    = io_p2v(AHB1_START),

              .pfn        = __phys_to_pfn(AHB1_START),

              .length            = AHB1_SIZE,

              .type              = MT_DEVICE

       },

       {

              .virtual    = io_p2v(FABAPB_START),

              .pfn        = __phys_to_pfn(FABAPB_START),

              .length            = FABAPB_SIZE,

              .type              = MT_DEVICE

       },

       {

              .virtual    = io_p2v(IRAM_BASE),

              .pfn        = __phys_to_pfn(IRAM_BASE),

              .length            = (SZ_64K * 4),

              .type              = MT_DEVICE

       },

};

 

void __init lpc32xx_map_io(void)

{

       iotable_init (lpc32xx_io_desc, ARRAY_SIZE (lpc32xx_io_desc));

}

 

lpc32xx_io_desc[]数组指明要静态初始化的成员及其范围,实际的页面映射则是由lpc32xx_map_io()调用iotable_init()来完成的。lpc3250开发板静态映射分两步完成的

即有两个数组要进行静态映射,除了刚介绍的,还有就是smartarm3250_io_desc[]。当然映射的原理都是一样的。最后smartarm3250_map_io()函数由MACHIN_START,MACHINE_END宏赋值给map_io()函数。

static void __init smartarm3250_map_io(void)

{

    lpc32xx_map_io();

    iotable_init(smartarm3250_io_desc, ARRAY_SIZE(smartarm3250_io_desc));

}

 

static struct map_desc smartarm3250_io_desc[] __initdata = {

    {   /* nCS2, CAN SJA1000 */

        .virtual  = io_p2v(EMC_CS2_BASE),

        .pfn      = __phys_to_pfn(EMC_CS2_BASE),

        .length   = SZ_1M,

        .type     = MT_DEVICE

    },

    {   /* nCS1, CF Card */

        .virtual  = io_p2v(EMC_CS1_BASE),

        .pfn      = __phys_to_pfn(EMC_CS1_BASE),

        .length   = SZ_1M,

        .type     = MT_DEVICE

    }

 

};

 

MACHINE_START (LPC3XXX, "SmartARM3250 board with the LPC3250 Microcontroller")

       /* Maintainer: Kevin Wells, NXP Semiconductors */

       .phys_io  = UART5_BASE,

       .io_pg_offst    = ((io_p2v (UART5_BASE))>>18) & 0xfffc,

       .boot_params  = 0x80000100,

       .map_io          = smartarm3250_map_io,

       .init_irq   = lpc32xx_init_irq,

       .timer             = &lpc32xx_timer,

       .init_machine  = smartarm3250_board_init,

MACHINE_END

 

三、DMA

n         当DMA的目的地址与Cache所缓存的内存地址访问有重叠时,会发生Cache与内存之间数据“不一致”问题。

n         获得具备DMA能力的内存

       __get_dma_pages(gfp_mask,order)

       它的定义

#define  __get_dma_pages(gfp_mask,order) __get_free_pages((gfp_mask) | GFP_DMA,(order))

n         虚拟地址:linux内核看到的地址

物理地址:CPU看到的地址

总线地址:设备看到的地址

n         int dma_set_mask(struct device *dev, u64 mask);

如果,对于只能在24位地址上执行DMA操作的设备而言,就应该调用

dma_set_mask(dev,0xffffff)

n         一致性DMA缓冲区

一致性DMA缓冲区,首先内存要具有DMA能力,然后内存区域和Cache不会产生数据“不一致”问题。

void *dma_alloc_coherent(struct device *dev,size_t size,dma_addr_t *handle,gfp_t gfp)

dma_addr_t,代表总线地址

该函数返回申请到的DMA缓冲区的虚拟地址。

释放函数

void dma_free_coherent(struct device *dev,size_t size,void *cpu_addr,dma_addr_t handle);

n         流式DMA缓冲区

流式DMA缓冲区即缓冲区不是由驱动申请的,而是由内核上层使用kmalloc(),__get_free_pages()等方法实现。

流式DMA映射操作在本质上多数就是进行Cache的invalidate或flush操作,以解决Cache一致性问题。

 

dma_addr_t dma_map_single(struct device *dev,void *buffer,size_t size,

enum dma_data_direction direction);

 

              dma_data_direction为DMA的方向

                     DMA_TO_DEVICE,DMA_FROM_DEVICE,

                     DMA_BIDIRECTIONAL,DMA_NONE

              反函数

              void dma_unmap_single(struct device *dev,dma_addr_t dma_addr,

                            size_t size,enum dma_data_direction direction);

 

              如果支持SG(分散聚集)模式。

       int dma_map_sg(struct device *dev,struct scatterlist *sg,int nents,

enum dma_data_direction direction);

nents,散列表(scatterlist)的入口数量

返回DMA缓冲区的数量。

 

       dma_addr_t sg_dma_address(struct scatterlist *sg);

       返回scatterlist对应缓冲区的总线地址

       unsigned int sg_dma_len(struct scatterlist *sg)

       返回scatterlist对应缓冲区的长度。

 

       去映射

       void dma_unmap_sg(struct device *dev,struct scatterlist *list,

int nents,enum dma_data_direction direction);

 

 

n         申请和释放DMA通道

int request_dma(unsigned int dmanr,const chat *device_id)

设备结构体指针可作为device_id的最佳参数。

void free_dma(unsigned int dmanr);

 

n         DMA使用流程

request_dma()初始化DMAC

申请DMA缓冲区

进行DMA传输

中断处理

释放DMA缓冲区

free_dma()

原创粉丝点击