linux BSPmini2440之DMA

 

s3c2440中DMA的一般操作步骤分七步:

linux <wbr>BSPmini2440之DMA

s3c2410_dma_client 的定义为:
struct s3c2410_dma_client {
        char                *name;
};
void* dev一般设置为NULL。channel是通道号。

linux <wbr>BSPmini2440之DMA
根据xferunit以及dcon设置通道的控制寄存器DCONx
xferunit 为每次传输的数据大小:0:byte 1:half word 2:word
linux <wbr>BSPmini2440之DMA
设置相应的dma通道完成一次dma传输后的回调函数,也即是s3c2410_dma_enqueue完成后会调用的函数
回调函数应具有一下格式:
typedef void (*s3c2410_dma_cbfn_t)(struct s3c2410_dma_chan *,
                                                                      void *buf, int size,
                                                                      enum s3c2410_dma_buffresult result);
buf可以传递一些有用的数据。
linux <wbr>BSPmini2440之DMA
linux <wbr>BSPmini2440之DMA
source: S3C2410_DMASRC_HW(外设),或者S3C2410_DMASRC_MEM(内存)。
hwcfg:        the value for xxxSTCn register,
                      bit 0: 0=increment pointer, 1=leave pointer
                    bit 1: 0=soucre is AHB, 1=soucre is APB
devaddr:source 的物理地址。
linux <wbr>BSPmini2440之DMA
建立一致性DMA映射函数,
该函数实际获得两个地址,
    1、函数的返回值是一个(rc),代表缓冲区的内核虚拟地址
    2、相关的总线地址,保存在dma_handle中
linux <wbr>BSPmini2440之DMA
发起一次dma传输
参数意义:
  * id                the device driver's id information for this buffer
  * data            the physical address of the buffer data
  * size            the size of the buffer in bytes
将dma_alloc_coherent中得到的 dmaphys传递给s3c2410_dma_enqueue. s3c2410_dma_enqueue提交一次dma请求,当dma通道可用的时候通过s3c2410_dma_loadbuffer开始一次传输,传输完成后会产生irq中断。其dma的中断服务函数中会继续启动dma请求队列中的请求,传输剩下的数据。
 
DMA的使用有了相关的认识。那么关于DMA的体系架构是怎么建立起来的。接下了看看:
DMA先有arch_initcall(s3c2440_dma_init);core_initcall(s3c24xx_dma_sysclass_init);然后late_initcall(s3c24xx_dma_sysdev_register);
 
先看看段arch_initcall(s3c2440_dam_init)做了些什么。
它主要是驱动注册
static int __init s3c2440_dma_init(void)
{
  return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_dma_driver);
}
&s3c2440_sysclass,s3c2440_dma_driver定义如下
struct sysdev_class s3c2440_sysclass = {
  .name    = "s3c2440-core",
  .suspend  = s3c244x_suspend,
  .resume    = s3c244x_resume
};
static struct sysdev_driver s3c2440_dma_driver = {
  .add  = s3c2440_dma_add,
};
这里我们只关心.add=s3c2440_dam_add
在函数里s3c2440_dam_add做了什么呢?
static int __init s3c2440_dma_add(struct sys_device *sysdev)
{
  s3c2410_dma_init();
  s3c24xx_dma_order_set(&s3c2440_dma_order);
  return s3c24xx_dma_init_map(&s3c2440_dma_sel);
}
它调用了三个函数s3c2410_dma_init(),s3c24xx_dma_order_set(&s3c2440_dma_order),s3c24xx_dma_init_map(&s3c2440_dma_sel);
s3c2410_dma_init()完成s3c2410_chans[]的初始化,并完成地址映射。s3c2410_chans[]是s3c2410_dma_chan结构体类型数组。s3c2410_dma_chan描述了一个通道的属性,下面是这个结构体得定义:
struct s3c2410_dma_chan {
 
  unsigned char    number;          代表第几个DMA通道
  unsigned char    in_use;          是否使用,0:为被使用1:已被占用
  unsigned char    irq_claimed;
  unsigned char    irq_enabled;
//irq相关标识,用于判断
  unsigned char    xfer_unit;    一次传输大小
 
  enum s3c2410_dma_state  state;
  enum s3c2410_dma_loadst  load_state;
  struct s3c2410_dma_client *client;
 
  enum s3c2410_dmasrc  source;//有两个值,外设和内存。具体宏看源代码
  enum dma_ch    req_ch;
  unsigned long    dev_addr;
  unsigned long    load_timeout;
  unsigned int    flags;   
  struct s3c24xx_dma_map  *map;    通道实映射
 
  void __iomem    *regs;   
  void __iomem    *addr_reg; 
  unsigned int    irq;   
  unsigned long    dcon;    寄存器DCONx值
 
  s3c2410_dma_cbfn_t  callback_fn;  回调函数
  s3c2410_dma_opfn_t  op_fn;   
 
  struct s3c2410_dma_stats *stats;
  struct s3c2410_dma_stats  stats_store;
 
  struct s3c2410_dma_buf  *curr;   
  struct s3c2410_dma_buf  *next;   
  struct s3c2410_dma_buf  *end;   
 
  struct sys_device  dev;
};
下面是s3c2410_dma_init()函数的源代码:
int __init s3c2410_dma_init(void)
{
  return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);
}
int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,
            unsigned int stride)
{
  struct s3c2410_dma_chan *cp;
  int channel;
  int ret;
  printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics\n");
  dma_channels = channels;
  dma_base = ioremap(S3C24XX_PA_DMA, stride * channels);
  if (dma_base == NULL) {
    printk(KERN_ERR "dma failed to remap register block\n");
    return -ENOMEM;
  }
  dma_kmem = kmem_cache_create("dma_desc",
                sizeof(struct s3c2410_dma_buf), 0,
                SLAB_HWCACHE_ALIGN,
                s3c2410_dma_cache_ctor);
  if (dma_kmem == NULL) {
    printk(KERN_ERR "dma failed to make kmem cache\n");
    ret = -ENOMEM;
    goto err;
  }
  for (channel = 0; channel < channels;  channel++) {
    cp = &s3c2410_chans[channel];
    memset(cp, 0, sizeof(struct s3c2410_dma_chan));
   
    cp->number = channel;
    cp->irq      = channel + irq;
    cp->regs    = dma_base + (channel * stride);
   
    cp->stats  = &cp->stats_store;
    cp->stats_store.timeout_shortest = LONG_MAX;
   
    cp->load_timeout = 1<<18;
    printk("DMA channel %d at %p, irq %d\n",
                cp->number, cp->regs, cp->irq);
  }
  return 0;
  err:
  kmem_cache_destroy(dma_kmem);
  iounmap(dma_base);
  dma_base = NULL;
  return ret;
}
有几个全局变量需要注意:

static void __iomem *dma_base;
static struct kmem_cache *dma_kmem;
static int dma_channels;通道数
struct s3c2410_dma_chan s3c2410_chans[S3C_DMA_CHANNELS];
  kmem_cache_create()创建struct s3c2410_dma_buf对象cache。dma_kmen是这个cache的引用。主要是为了提高速度的作用。dma_channels被初始化为4,dam_base ioremap()获得。在for语句中,初始化了全局变量s3c4210_chans[0],s3c4210_chans[1],s3c4210_chans[2],s3c4210_chans[3].
 
接下来倒回到s3c24xx_dma_order_set(&s3c2440_dma_order)预定目标板要用的DMA通道。
&s3c2440_dma_order定义如下:
struct s3c24xx_dma_order {
  struct s3c24xx_dma_order_ch  channels[DMACH_MAX];
};
static struct s3c24xx_dma_order __initdata s3c2440_dma_order = {
  .channels  = {
    [DMACH_SDI]  = {
      .list  = {
        [0]  = 3 | DMA_CH_VALID,
        [1]  = 2 | DMA_CH_VALID,
        [2]  = 1 | DMA_CH_VALID,
        [3]  = 0 | DMA_CH_VALID,
      },
    },
    [DMACH_I2S_IN]  = {
      .list  = {
        [0]  = 1 | DMA_CH_VALID,
        [1]  = 2 | DMA_CH_VALID,
      },
    },
    [DMACH_I2S_OUT]  = {
      .list  = {
        [0]  = 2 | DMA_CH_VALID,
        [1]  = 1 | DMA_CH_VALID,
      },
    },
    [DMACH_PCM_IN] = {
      .list  = {
        [0]  = 2 | DMA_CH_VALID,
        [1]  = 1 | DMA_CH_VALID,
      },
    },
    [DMACH_PCM_OUT] = {
      .list  = {
        [0]  = 1 | DMA_CH_VALID,
        [1]  = 3 | DMA_CH_VALID,
      },
    },
    [DMACH_MIC_IN] = {
      .list  = {
        [0]  = 3 | DMA_CH_VALID,
        [1]  = 2 | DMA_CH_VALID,
      },
    },
  },
};
int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord)
{
  struct s3c24xx_dma_order *nord = dma_order;
  if (nord == NULL)
    nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL);
  if (nord == NULL) {
    printk(KERN_ERR "no memory to store dma channel order\n");
    return -ENOMEM;
  }
  dma_order = nord;
  memcpy(nord, ord, sizeof(struct s3c24xx_dma_order));
  return 0;
}
dam_order是一个全局变量
static struct s3c24xx_dma_order *dma_order;函数中使用kmalloc获得nord,然后memcpy s3c2440_dma_order 给nord。这里就有两个值相同的内存块。而dma_order指针指向nord.完成全局变量赋值。
 
s3c24xx_dma_init_map(&s3c2440_dma_sel);
看这个函数想看下相关的东西
static struct s3c24xx_dma_selection __initdata s3c2440_dma_sel = {
  .select    = s3c2440_dma_select,
  .dcon_mask  = 7 << 24,
  .map    = s3c2440_dma_mappings,
  .map_size  = ARRAY_SIZE(s3c2440_dma_mappings),
};
static struct s3c24xx_dma_map __initdata s3c2440_dma_mappings[] = {
  [DMACH_XD0] = {
    .name    = "xdreq0",
    .channels[0]  = S3C2410_DCON_CH0_XDREQ0 | DMA_CH_VALID,
  },
  [DMACH_XD1] = {
    .name    = "xdreq1",
    .channels[1]  = S3C2410_DCON_CH1_XDREQ1 | DMA_CH_VALID,
  },
  [DMACH_SDI] = {
    .name    = "sdi",
    .channels[0]  = S3C2410_DCON_CH0_SDI | DMA_CH_VALID,
    .channels[1]  = S3C2440_DCON_CH1_SDI | DMA_CH_VALID,
    .channels[2]  = S3C2410_DCON_CH2_SDI | DMA_CH_VALID,
    .channels[3]  = S3C2410_DCON_CH3_SDI | DMA_CH_VALID,
    .hw_addr.to  = S3C2410_PA_IIS + S3C2410_IISFIFO,
    .hw_addr.from  = S3C2410_PA_IIS + S3C2410_IISFIFO,
  },
  [DMACH_SPI0] = {
    .name    = "spi0",
    .channels[1]  = S3C2410_DCON_CH1_SPI | DMA_CH_VALID,
    .hw_addr.to  = S3C2410_PA_SPI + S3C2410_SPTDAT,
    .hw_addr.from  = S3C2410_PA_SPI + S3C2410_SPRDAT,
  },
  [DMACH_SPI1] = {
    .name    = "spi1",
    .channels[3]  = S3C2410_DCON_CH3_SPI | DMA_CH_VALID,
    .hw_addr.to  = S3C2410_PA_SPI + 0x20 + S3C2410_SPTDAT,
    .hw_addr.from  = S3C2410_PA_SPI + 0x20 + S3C2410_SPRDAT,
  },
  [DMACH_UART0] = {
    .name    = "uart0",
    .channels[0]  = S3C2410_DCON_CH0_UART0 | DMA_CH_VALID,
    .hw_addr.to  = S3C2410_PA_UART0 + S3C2410_UTXH,
    .hw_addr.from  = S3C2410_PA_UART0 + S3C2410_URXH,
  },
  [DMACH_UART1] = {
    .name    = "uart1",
    .channels[1]  = S3C2410_DCON_CH1_UART1 | DMA_CH_VALID,
    .hw_addr.to  = S3C2410_PA_UART1 + S3C2410_UTXH,
    .hw_addr.from  = S3C2410_PA_UART1 + S3C2410_URXH,
  },
            [DMACH_UART2] = {
    .name    = "uart2",
    .channels[3]  = S3C2410_DCON_CH3_UART2 | DMA_CH_VALID,
    .hw_addr.to  = S3C2410_PA_UART2 + S3C2410_UTXH,
    .hw_addr.from  = S3C2410_PA_UART2 + S3C2410_URXH,
  },
  [DMACH_TIMER] = {
    .name    = "timer",
    .channels[0]  = S3C2410_DCON_CH0_TIMER | DMA_CH_VALID,
    .channels[2]  = S3C2410_DCON_CH2_TIMER | DMA_CH_VALID,
    .channels[3]  = S3C2410_DCON_CH3_TIMER | DMA_CH_VALID,
  },
  [DMACH_I2S_IN] = {
    .name    = "i2s-sdi",
    .channels[1]  = S3C2410_DCON_CH1_I2SSDI | DMA_CH_VALID,
    .channels[2]  = S3C2410_DCON_CH2_I2SSDI | DMA_CH_VALID,
    .hw_addr.from  = S3C2410_PA_IIS + S3C2410_IISFIFO,
  },
  [DMACH_I2S_OUT] = {
    .name    = "i2s-sdo",
    .channels[0]  = S3C2440_DCON_CH0_I2SSDO | DMA_CH_VALID,
    .channels[2]  = S3C2410_DCON_CH2_I2SSDO | DMA_CH_VALID,
    .hw_addr.to  = S3C2410_PA_IIS + S3C2410_IISFIFO,
  },
  [DMACH_PCM_IN] = {
    .name    = "pcm-in",
    .channels[0]  = S3C2440_DCON_CH0_PCMIN | DMA_CH_VALID,
    .channels[2]  = S3C2440_DCON_CH2_PCMIN | DMA_CH_VALID,
    .hw_addr.from  = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
  },
  [DMACH_PCM_OUT] = {
    .name    = "pcm-out",
    .channels[1]  = S3C2440_DCON_CH1_PCMOUT | DMA_CH_VALID,
    .channels[3]  = S3C2440_DCON_CH3_PCMOUT | DMA_CH_VALID,
    .hw_addr.to  = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
  },
  [DMACH_MIC_IN] = {
    .name    = "mic-in",
    .channels[2]  = S3C2440_DCON_CH2_MICIN | DMA_CH_VALID,
    .channels[3]  = S3C2440_DCON_CH3_MICIN | DMA_CH_VALID,
    .hw_addr.from  = S3C2440_PA_AC97 + S3C_AC97_MIC_DATA,
  },
  [DMACH_USB_EP1] = {
    .name    = "usb-ep1",
    .channels[0]  = S3C2410_DCON_CH0_USBEP1 | DMA_CH_VALID,
  },
  [DMACH_USB_EP2] = {
    .name    = "usb-ep2",
    .channels[1]  = S3C2410_DCON_CH1_USBEP2 | DMA_CH_VALID,
  },
  [DMACH_USB_EP3] = {
    .name    = "usb-ep3",
    .channels[2]  = S3C2410_DCON_CH2_USBEP3 | DMA_CH_VALID,
  },
  [DMACH_USB_EP4] = {
    .name    = "usb-ep4",
    .channels[3]  = S3C2410_DCON_CH3_USBEP4 | DMA_CH_VALID,
  },
};
s3c2440_dma_sel实现对S3C24XX下DMA资源的统一管理。
struct s3c24xx_dma_selection {
  struct s3c24xx_dma_map  *map;记录struct s3c24xx_dma_map的首地址
  unsigned long    map_size;//struct s3c24xx_dma_map数组成员个数
  unsigned long    dcon_mask;//dcon控制器掩码
  void  (*select)(struct s3c2410_dma_chan *chan,
        struct s3c24xx_dma_map *map);//
  void  (*direction)(struct s3c2410_dma_chan *chan,
              struct s3c24xx_dma_map *map,
              enum s3c2410_dmasrc dir);
};
struct s3c24xx_dma_map {
  const char    *name;//虚拟通道名
  struct s3c24xx_dma_addr  hw_addr;
  unsigned long    channels[S3C_DMA_CHANNELS];//实际DAM通道信息
  unsigned long    channels_rx[S3C_DMA_CHANNELS];
};
struct s3c24xx_dma_map提供了DAM虚拟通道与实际的DMA通道的直接关联。
 
int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel)
{
  struct s3c24xx_dma_map *nmap;
  size_t map_sz = sizeof(*nmap) * sel->map_size;
  int ptr;
  nmap = kmalloc(map_sz, GFP_KERNEL);
  if (nmap == NULL)
    return -ENOMEM;
  memcpy(nmap, sel->map, map_sz);
  memcpy(&dma_sel, sel, sizeof(*sel));
  dma_sel.map = nmap;
  for (ptr = 0; ptr < sel->map_size; ptr++)
    s3c24xx_dma_check_entry(nmap+ptr, ptr);
  return 0;
}
函数初始化了全局变量static struct s3c24xx_dma_selection dma_sel;
s3c24xx_dma_check_entry()是空函数。
 
plat-s3c24xx的dma.c中封装了一系列的API。以后慢慢看。

你可能感兴趣的:(c,linux,timer,struct,cache,null)