Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1189943
  • 博文数量: 221
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 2139
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-27 19:53
个人简介

JustForFun

文章分类

全部博文(221)

文章存档

2024年(6)

2023年(8)

2022年(2)

2021年(2)

2020年(29)

2019年(11)

2018年(23)

2017年(41)

2016年(76)

2015年(23)

我的朋友
最近访客

分类: LINUX

2016-08-17 14:49:41

arch\arm\mach-zynq\pl330.c

图片
#define PL330_DBGSTATUS_OFFSET 0xD00 /* Debug Status Register */
#define PL330_DBGCMD_OFFSET 0xD04 /* Debug Command Register */
图片


 module_init(pl330_init);   ->  pl330_init()    ->  pl330_driver_init();

//////////////////////////////////////////////////////////////////////
/**
 * pl330_driver_init - Initialize the dma_struct array and store the pointer
 * to array
 */
static void pl330_driver_init(void)
{
 unsigned int i;
 PDEBUG("inside pl330_driver_init, dma_chan is %x\n",
        (unsigned int)dma_chan);
 driver_data.dma_chan = dma_chan;
 memset(dma_chan, 0, sizeof(dma_chan[MAX_DMA_CHANNELS]));
 for (i = 0; i < MAX_DMA_CHANNELS; i++) {
  dma_chan[i].d_ops = &pl330_ops;
  isa_dma_add(i, dma_chan + i);
 }
}
/////////////////////////////////////////////////////////////////////// 

/*
 * Platform bus binding
 */
static struct dma_ops pl330_ops = {
 .request     = pl330_request_dma,
 .free        = pl330_free_dma,
 .enable      = pl330_enable_dma,
 .disable     = pl330_disable_dma,
 .setspeed    = pl330_setspeed_dma,
 .residue     = pl330_get_residue_dma,
 .type        = "PL330",
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 * pl330_enable_dma - Implementation of enable_dma. It translates the
 * DMA parameters to a DMA program if the DMA program is not provided,
 * then starts the DMA program on a channel thread.
 * @channel: DMA channel number
 * @indexed_dma_chan: Instance of the dma_struct.
 */
static void pl330_enable_dma(unsigned int channel,
        struct dma_struct *indexed_dma_chan)
{
 struct dma_struct *dma = indexed_dma_chan;
 struct pl330_channel_data *channel_data;
 struct pl330_channel_static_data *channel_static_data;
 struct pl330_client_data *client_data;
 struct pl330_device_data *device_data;
 unsigned int dev_chan;
 struct pl330_bus_des *src_bus_des = NULL;
 struct pl330_bus_des *dst_bus_des = NULL;
 struct default_src_bus_des;
 struct default_dst_bus_des;
 unsigned src_inc = 1;
 unsigned dst_inc = 1;
 u32 src_addr;
 u32 dst_addr;
 u32 dma_prog;
 char *dma_prog_buf;
 int dma_prog_bytes;
 u32 inten;
 unsigned long spin_flags;
 struct prog_build_args build_args;
 channel_static_data = driver_data.channel_static_data + channel;
 device_data = driver_data.device_data + channel_static_data->dev_id;
 channel_data = driver_data.channel_data + channel;
 client_data = driver_data.channel_data[channel].client_data;
 if (!client_data) {
  printk(KERN_ERR
         "client data is not set for DMA channel %d\n",
         channel);
  BUG();
  return;
 }
 /*
  * find out which one is source which one is destination
  */
 if (dma->dma_mode == DMA_MODE_READ) {
  PDEBUG("dma_mode is DMA_MODE_READ\n");
  src_bus_des = &client_data->dev_bus_des;
  dst_bus_des = &client_data->mem_bus_des;
  src_addr = (u32)client_data->dev_addr;
  dst_addr = (u32)virt_to_bus(dma->addr);
  src_inc = channel_data->incr_dev_addr;
  dst_inc = 1;
 } else if (dma->dma_mode == DMA_MODE_WRITE) {
  PDEBUG("dma_mode is DMA_MODE_WRITE\n");
  src_bus_des = &client_data->mem_bus_des;
  dst_bus_des = &client_data->dev_bus_des;
  src_addr = (u32)virt_to_bus(dma->addr);
  dst_addr = (u32)client_data->dev_addr;
  src_inc = 1;
  dst_inc = channel_data->incr_dev_addr;
 } else {
  printk(KERN_ERR "Error: mode %x is not supported\n",
         dma->dma_mode);
  return;
 }
 if (dma->count == 0) {
  printk(KERN_ERR "Error: DMA count for channel %d is zero",
         channel);
  return;
 }
 /* print some debugging messages */
 PDEBUG("count is %ld\n", dma->count);
 PDEBUG("dev_addr = %x\n",  (unsigned int)client_data->dev_addr);
 PDEBUG("dev_bus_des = {\n");
 print_pl330_bus_des(&client_data->dev_bus_des);
 PDEBUG("}\n");
 PDEBUG("mem_bus_des = {\n");
 print_pl330_bus_des(&client_data->mem_bus_des);
 PDEBUG("}\n");
 PDEBUG("endian_swap_size = %d\n",
        client_data->endian_swap_size);
 PDEBUG("incr_dev_addr = %d\n",
        channel_data->incr_dev_addr);
 dma_prog = channel_data->dma_program;
 dev_chan = channel_static_data->dev_chan;
 if (dma_prog == 0) {
  /*
   * if the DMA program is not set by a user,
   * construct the dma program
   */
  PDEBUG("constructing DMA program\n");
  if (!channel_data->dma_prog_buf) {
   /* allocate the dma prog buffer */
   channel_data->dma_prog_buf =
    dma_alloc_coherent(device_data->dev,
         0x1000,
         &channel_data->dma_prog_phy,
         GFP_KERNEL);
  }
  PDEBUG("channel %d DMA program: vir %#08x, phy %#08x\n",
         channel,
         (u32)channel_data->dma_prog_buf,
         (u32)channel_data->dma_prog_phy);
  dma_prog_buf = (char *)channel_data->dma_prog_buf;
  /*
   * setup the arguments
   */
  build_args.channel = channel;
  build_args.dma_prog_buf = dma_prog_buf;
  build_args.dev_chan = dev_chan;
  build_args.dma_count = dma->count;
  build_args.src_addr = src_addr;
  build_args.src_bus_des = src_bus_des;
  build_args.src_inc = src_inc;
  build_args.dst_addr = dst_addr;
  build_args.dst_bus_des = dst_bus_des;
  build_args.dst_inc = dst_inc;
  build_args.src_is_mem = dma->dma_mode == DMA_MODE_WRITE;
  build_args.endian_swap_size = client_data->endian_swap_size;
  build_args.cache_length = device_data->i_cache_len;
  dma_prog_bytes = pl330_build_dma_prog(&build_args);
  /*
   * using physical address for DMA prog
   */
  dma_prog = channel_data->dma_prog_phy;
  channel_data->dma_prog_len = dma_prog_bytes;
  PDEBUG("DMA program constructed\n");
 } else {
  PDEBUG("channel %d user defined DMA program %#08x\n",
         channel, (u32)dma_prog);
 }
 PDEBUG("enable_dma: spin_lock_irqsave\n");
 spin_lock_irqsave(&device_data->lock, spin_flags);
 /* enable the interrupt */
 PDEBUG("enable_dma: enabling interrupt\n");
 inten = pl330_readreg(device_data->base, PL330_INTEN_OFFSET);
 inten |= 0x01 << dev_chan; /* set the correpsonding bit */
 pl330_writereg(inten, device_data->base, PL330_INTEN_OFFSET);
 PDEBUG("pl330 interrupt enabled for channel %d\n", channel);
 pl330_exec_dmago(device_data->dev_id,
    device_data->base,
    dev_chan,
    dma_prog);
 spin_unlock_irqrestore(&device_data->lock, spin_flags);
 return;
}

/////////////////////////////////////////////////////////////////////////////////
/**
 * pl33_exec_dmago - Execute the DMAGO to start a channel.
 * @dev_id: PL330 device ID indicating which PL330, the ID starts at 0.
 * @base: PL330 device base address
 * @dev_chan: Channel number for the device
 * @dma_prog: DMA program starting address, this should be DMA address
 *
 * Returns 0 on success, -1 on time out
 */
int pl330_exec_dmago(unsigned int dev_id,
        void __iomem *base,
        unsigned int dev_chan,
        u32 dma_prog)
{
 char dma_go_prog[8];
 u32 dbginst0;
 u32 dbginst1;
 int wait_count;
 PDEBUG("pl330_exec_dmago: entering\n");
 pl330_instr_dmago(dma_go_prog, dev_chan, dma_prog, 0);
 dbginst0 = PL330_DBGINST0(*(dma_go_prog + 1), *dma_go_prog, 0, 0);
 dbginst1 = (u32)dma_prog;
 PDEBUG("inside pl330_exec_dmago: base %x, dev_chan %d, dma_prog %x\n",
        (u32)base, dev_chan, dma_prog);
 /* wait while debug status is busy */
 wait_count = 0;
 while (pl330_readreg(base, PL330_DBGSTATUS_OFFSET)
        & PL330_DBGSTATUS_BUSY
        && wait_count < PL330_MAX_WAIT) {
  PDEBUG("dbgstatus %x\n",
         pl330_readreg(base, PL330_DBGSTATUS_OFFSET));
  wait_count++;
 }
 if (wait_count >= PL330_MAX_WAIT) {
  printk(KERN_ERR
         "PL330 device %d debug status busy time out\n",
         dev_id);
  return -1;
 }
 PDEBUG("dbgstatus idle\n");
 /* write debug instruction 0 */
 pl330_writereg(dbginst0, base, PL330_DBGINST0_OFFSET);
 /* write debug instruction 1 */
 pl330_writereg(dbginst1, base, PL330_DBGINST1_OFFSET);

 /* wait while the DMA Manager is busy */
 wait_count = 0;
 while ((pl330_readreg(base, PL330_DS_OFFSET) & PL330_DS_DMA_STATUS)
        != PL330_DS_DMA_STATUS_STOPPED
        && wait_count <= PL330_MAX_WAIT) {
  PDEBUG("ds %x\n",
         pl330_readreg(base, PL330_DS_OFFSET));
  wait_count++;
 }
 if (wait_count >= PL330_MAX_WAIT) {
  printk(KERN_ERR
         "PL330 device %d debug status busy time out\n",
         dev_id);
  return -1;
 }
 /* run the command in dbginst0 and dbginst1 */
 pl330_writereg(0, base, PL330_DBGCMD_OFFSET);
 PDEBUG("pl330_exec_dmago done\n");
 return 0;
}

////////////////////
#define pl330_writereg(data, base, offset) \
 __raw_writel(data, (void __iomem *)((u32)(base) + (u32)(offset)))
//////////////////////////////////////////////////////

阅读(2142) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~