#include <linux/time.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/module.h> #include <linux/device.h> #include <linux/pci.h> #include <linux/cdev.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/sched.h> #include <linux/atomic.h> #include <linux/random.h> #include "altera_dma_cmd.h" #include <linux/unistd.h> #define P40_FMT "P40DMA:%s(): " #define PCIE40_DMA_CLASS "pcie40_dma" #include "altera_dma.h" #define TIMEOUT 0x2000000 static long altera_dma_ioctl (struct file *filp, unsigned int cmd, unsigned long arg) { int rc=-2; struct pcie40_dma_state *bk_ptr = filp->private_data; switch (cmd) { case ALTERA_IOCX_START: rc= dma_test(bk_ptr, bk_ptr->pci_dev); wait_event_interruptible(bk_ptr->wait_q, !atomic_read(&bk_ptr->status)); break; case ALTERA_CMD_WAIT_DMA: // wait_event_interruptible(bk_ptr->wait_q, !atomic_read(&bk_ptr->status)); break; } return rc; } static int altera_dma_mmap(struct file *file,struct vm_area_struct *vma ) { unsigned long mem_addr; unsigned int size; struct pcie40_dma_state *bk_ptr; #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,6,999) vma->vm_flags |= VM_RESERVED ; #else vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP); #endif size = vma->vm_end - vma->vm_start; bk_ptr = file->private_data; mem_addr = virt_to_phys(bk_ptr->rp_wr_buffer_virt_addr); printk(" map vma %p pfn %lX vm_start %lX vm_end %lX size %X\n", vma, (mem_addr >> PAGE_SHIFT), vma->vm_start, vma->vm_end, size); if (remap_pfn_range(vma, vma->vm_start, (mem_addr >> PAGE_SHIFT), size, vma->vm_page_prot)){ printk(P40_ERR "Error remap_pfn_range \n",P40_PARM); return -EAGAIN; } return 0; } int altera_dma_open(struct inode *inode, struct file *file) { struct pcie40_dma_state *bk_ptr = 0; bk_ptr = container_of(inode->i_cdev, struct pcie40_dma_state, cdev); file->private_data = bk_ptr; bk_ptr->user_pid = current->pid; return 0; } int altera_dma_release(struct inode *inode, struct file *file) { return 0; } /*static irqreturn_t dma_isr(int irq, void *dev_id) { struct pcie40_dma_state *bk_ptr = (struct pcie40_dma_state* )dev_id; if (!bk_ptr){ printk(KERN_ALERT "bk_ptr lost \n"); return IRQ_NONE; } atomic_set(&bk_ptr->status, 0); bk_ptr->irq_count++; if (bk_ptr->irq_count%1000) printk(KERN_ALERT "nombre IRq %d\n",bk_ptr->irq_count++); wake_up(&bk_ptr->wait_q); return IRQ_HANDLED; } */ struct file_operations altera_dma_fops = { .owner = THIS_MODULE, .mmap = altera_dma_mmap, .open = altera_dma_open, .release = altera_dma_release, .unlocked_ioctl = altera_dma_ioctl, }; static struct class *pcie40_dma_class = NULL; static int __init init_chrdev (struct pcie40_dma_state *bk_ptr) { int dev_minor = 0; int dev_major = 0; int devno = 0; int rc =-1; rc = alloc_chrdev_region(&bk_ptr->cdevno, dev_minor, 1, ALTERA_DMA_DEVFILE); if (rc < 0) { printk(P40_ERR "alloc_chrdev_region()\n", P40_PARM); goto err_alloc_chrdev_region; } dev_major = MAJOR(bk_ptr->cdevno); if (dev_major < 0) { printk(P40_ERR "cannot get major ID %d",P40_PARM, dev_major); } devno = MKDEV(dev_major, dev_minor); rc = pcie40_setup_cdev(pcie40_dma_class, &(bk_ptr->cdev), devno, dev_minor, 0, ALTERA_DMA_DEVFILE, bk_ptr->common->dev_id, &altera_dma_fops); if (rc < 0) { goto err_bar0_dev; } return 0; err_bar0_dev: unregister_chrdev_region(bk_ptr->cdevno, 1); err_alloc_chrdev_region: return rc; } static int set_write_desc(struct dma_descriptor *wr_desc, u64 source, dma_addr_t dest, u32 ctl_dma_len, u32 id) { wr_desc->src_addr_ldw = cpu_to_le32(source & 0xffffffffUL); wr_desc->src_addr_udw = cpu_to_le32((source >> 32)); wr_desc->dest_addr_ldw = cpu_to_le32(dest & 0xffffffffUL); wr_desc->dest_addr_udw = cpu_to_le32((dest >> 32)); wr_desc->ctl_dma_len = cpu_to_le32(ctl_dma_len | (id << 18)); wr_desc->reserved[0] = cpu_to_le32(0x0); wr_desc->reserved[1] = cpu_to_le32(0x0); wr_desc->reserved[2] = cpu_to_le32(0x0); return 0; } static int init_ep_mem(struct pcie40_dma_state *bk_ptr, u32 mem_base, u32 num_dwords, u32 init_value, u32 cpt) { u32 i = 0; iowrite32 (cpu_to_le32(cpt), (u32 *)(bk_ptr->bar[4]+mem_base)); for (i = 1; i < num_dwords; i++) { iowrite32 (cpu_to_le32(i), (u32 *)(bk_ptr->bar[4]+mem_base)+i); wmb(); } return 0; } static int ep_read(u8 *virt_addr, struct pcie40_dma_state *bk_ptr, u32 mem_base, u32 num_dwords) { u32 i = 0; u32 ep_data = 0; //printk(KERN_DEBUG "RP EP"); for (i = 0; i < num_dwords; i+=128) { ep_data = ioread32((u32 *)(bk_ptr->bar[4]+mem_base)+i); rmb(); printk(KERN_DEBUG "%p: 0x%08x ", (u64 *)((u32*)virt_addr+i), ep_data); } return 0; } static int set_lite_table_header(struct lite_dma_header *header) { int i; for (i = 0; i < 128; i++) header->flags[i] = cpu_to_le32(0x0); return 0; } static int dma_test(struct pcie40_dma_state *bk_ptr, struct pci_dev *dev) { u8 *rp_wr_buffer_virt_addr = bk_ptr->rp_wr_buffer_virt_addr; u32 last_id, write_127; u32 timeout; void * __iomem base_bar0; int Cpt=0; int i; base_bar0 =bk_ptr->common->bar0_regs; atomic_set(&bk_ptr->status, 1); /* printk(KERN_DEBUG " before read at 0x50120 %x \n" ,pcie40_read32_bar2 (bk_ptr->common,0x00)); pcie40_write32_bar2 (bk_ptr->common,0x00,0x1FF); wmb(); if ( pcie40_wait_data(bk_ptr->common) == 0 ) { atomic_set(&bk_ptr->status, 0); return -2; // no data in fifo } */ // iowrite32 (0, base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_CONTROL);// for MSI timeout = TIMEOUT; write_127 = 0; last_id = ioread32((u32 *)(base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_LAST_PTR)); set_lite_table_header((struct lite_dma_header *)bk_ptr->lite_table_wr_cpu_virt_addr); if(last_id == 0xFF) last_id = bk_ptr->dma_status.altera_dma_descriptor_num - 1; last_id = last_id + bk_ptr->dma_status.altera_dma_descriptor_num; if(last_id > (bk_ptr->dma_status.altera_dma_descriptor_num -1) ){ last_id = last_id - bk_ptr->dma_status.altera_dma_descriptor_num; if((bk_ptr->dma_status.altera_dma_descriptor_num > 1) && (last_id != ( bk_ptr->dma_status.altera_dma_descriptor_num-1))) write_127 = 1; } if(write_127) iowrite32 (( bk_ptr->dma_status.altera_dma_descriptor_num - 1), base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_LAST_PTR); // Start DMA iowrite32 (last_id, base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_LAST_PTR); while (1) { if (bk_ptr->lite_table_wr_cpu_virt_addr->header.flags[last_id]) { break; } if(timeout == 0){ memset(rp_wr_buffer_virt_addr, 0, bk_ptr->dma_status.altera_dma_num_dwords*4); bk_ptr->dma_status.write_eplast_timeout = 1; printk(KERN_DEBUG "Write DMA times out\n"); printk(KERN_DEBUG "DWORD = %08x\n", bk_ptr->dma_status.altera_dma_num_dwords); printk(KERN_DEBUG "Desc = %08x\n", bk_ptr->dma_status.altera_dma_descriptor_num); atomic_set(&bk_ptr->status, 0); return -1; } timeout--; //cpu_relax(); } // pcie40_reset_fifo(bk_ptr->common); atomic_set(&bk_ptr->status, 0); return 1; } static int __init map_bars(struct pcie40_dma_state *bk_ptr, struct pci_dev *dev) { int i; struct pcie40_state *common; common = bk_ptr->common; if ( common->bar_start[BAR0] && common->bar_size[BAR0]){ bk_ptr->bar[BAR0] = pci_iomap(dev,BAR0, common->bar_size[BAR0]); if (!bk_ptr->bar[BAR0]) { dev_err(&dev->dev, "could not map BAR[%d]",BAR0); return -1; } else dev_info(&dev->dev, "BAR[%d] mapped to 0x%p, length %lu", 2, bk_ptr->bar[BAR0], (long unsigned int)common->bar_size[BAR0]); } if ( common->bar_start[BAR4] && common->bar_size[BAR4]){ bk_ptr->bar[BAR4] = pci_iomap(dev,BAR4, common->bar_size[BAR4]); if (!bk_ptr->bar[BAR4]) { dev_err(&dev->dev, "could not map BAR[%d]", BAR4); return -1; } else dev_info(&dev->dev, "BAR[%d] mapped to 0x%p, length %lu", 4, bk_ptr->bar[BAR4], (long unsigned int)common->bar_size[BAR4]); } return 0; } static void unmap_bars(struct pcie40_dma_state *bk_ptr, struct pci_dev *dev) { int i; for (i = 0; i < P40_MAX_BAR; i++) { if (bk_ptr->bar[i]) { pci_iounmap(dev, bk_ptr->bar[i]); bk_ptr->bar[i] = NULL; } } } static void altera_pcie40_set_drvdata(struct pci_dev *pdev, struct pcie40_dma_state *state) { struct pcie40_state *common = pci_get_drvdata(pdev); common->dma_state = state; common->dma_state->common = common; } static struct pcie40_dma_state *altera_pcie40_get_drvdata(struct pci_dev *pdev) { struct pcie40_state *common = pci_get_drvdata(pdev); return common->dma_state; } int altera_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { int rc = 0; int i; struct pcie40_dma_state *bk_ptr = NULL; struct pcie40_state *common; void * __iomem base_bar0; bk_ptr = kzalloc(sizeof(struct pcie40_dma_state), GFP_KERNEL); if(!bk_ptr) goto err_bk_alloc; bk_ptr->pci_dev = dev; altera_pcie40_set_drvdata(dev, bk_ptr); common = pci_get_drvdata(dev); bk_ptr->common = common; base_bar0 =common->bar0_regs; rc = init_chrdev(bk_ptr); if (rc) { dev_err(&dev->dev, "init_chrdev() failed\n"); goto err_initchrdev; } rc = pci_enable_device(dev); if (rc) { dev_err(&dev->dev, "pci_enable_device() failed\n"); goto err_enable; } else { dev_info(&dev->dev, "pci_enable_device() successful"); } //rc = pci_request_regions(dev, P40_DRV_NAME); rc = pci_request_selected_regions_exclusive(dev, DMA_BARS, P40_DRV_NAME); if (rc) { dev_err(&dev->dev, "pci_request_regions() failed\n"); goto err_regions; } printk(P40_DIAG " pci request ok\n", P40_PARM); pci_set_master(dev); rc = pci_enable_msi(dev); if (rc) { dev_info(&dev->dev, "pci_enable_msi() failed\n"); bk_ptr->msi_enabled = 0; } else { dev_info(&dev->dev, "pci_enable_msi() successful\n"); bk_ptr->msi_enabled = 1; } pci_read_config_byte(dev, PCI_REVISION_ID, &bk_ptr->revision); pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &bk_ptr->irq_pin); pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &bk_ptr->irq_line); if (!pci_set_dma_mask(dev, DMA_BIT_MASK(64))) { pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(64)); dev_info(&dev->dev, "using a 64-bit irq mask\n"); } else { dev_info(&dev->dev, "unable to use 64-bit irq mask\n"); goto err_dma_mask; } dev_info(&dev->dev, "irq pin: %d\n", bk_ptr->irq_pin); dev_info(&dev->dev, "irq line: %d\n", bk_ptr->irq_line); dev_info(&dev->dev, "irq: %d\n", dev->irq); bk_ptr->irq_count=0; // scan_bars(bk_ptr, dev); //map_bars(bk_ptr, dev); // waitqueue for user process init_waitqueue_head(&bk_ptr->wait_q); // set default settings to run bk_ptr->dma_status.altera_dma_num_dwords = ALTERA_DMA_NUM_DWORDS; bk_ptr->dma_status.altera_dma_descriptor_num = ALTERA_DMA_DESCRIPTOR_NUM; bk_ptr->dma_status.run_write = 1; bk_ptr->dma_status.run_read = 1; bk_ptr->dma_status.run_simul = 1; bk_ptr->dma_status.offset = 0; bk_ptr->dma_status.onchip = 1; bk_ptr->dma_status.rand = 0; bk_ptr->lite_table_rd_cpu_virt_addr = ((struct lite_dma_desc_table *)pci_alloc_consistent(dev, sizeof(struct lite_dma_desc_table), &bk_ptr->lite_table_rd_bus_addr)); if ( !bk_ptr->lite_table_rd_cpu_virt_addr) { rc = -ENOMEM; goto err_rd_table; } bk_ptr->lite_table_wr_cpu_virt_addr = ((struct lite_dma_desc_table *)pci_alloc_consistent(dev, sizeof(struct lite_dma_desc_table), &bk_ptr->lite_table_wr_bus_addr)); if (!bk_ptr->lite_table_wr_cpu_virt_addr) { rc = -ENOMEM; goto err_wr_table; } bk_ptr->numpages = (PAGE_SIZE >= MAX_NUM_DWORDS*4) ? 1 : (int)((MAX_NUM_DWORDS*4)/PAGE_SIZE); bk_ptr->rp_rd_buffer_virt_addr = pci_alloc_consistent(dev, PAGE_SIZE*bk_ptr->numpages, &bk_ptr->rp_rd_buffer_bus_addr); if (!bk_ptr->rp_rd_buffer_virt_addr) { rc = -ENOMEM; goto err_rd_buffer; } bk_ptr->rp_wr_buffer_virt_addr = pci_alloc_consistent(dev, PAGE_SIZE*bk_ptr->numpages, &bk_ptr->rp_wr_buffer_bus_addr); printk(P40_DIAG " pci alloc size %x \n", P40_PARM,PAGE_SIZE*bk_ptr->numpages ); if (!bk_ptr->rp_wr_buffer_virt_addr) { printk(P40_DIAG " ERROR ENOMEM virt_addr ", P40_PARM,ENOMEM); rc = -ENOMEM; goto err_wr_buffer; } for ( i=0; i< ALTERA_DMA_DESCRIPTOR_NUM; i++) { if (i==0) set_write_desc(&bk_ptr->lite_table_wr_cpu_virt_addr->descriptors[i],ALTERA_MEM_MAIN_BUF0_START, (dma_addr_t)(bk_ptr->rp_wr_buffer_bus_addr)+(i* 0x8000),DMASIZE, i); if (i==1) set_write_desc(&bk_ptr->lite_table_wr_cpu_virt_addr->descriptors[i],ALTERA_MEM_MAIN_BUF1_START, (dma_addr_t)(bk_ptr->rp_wr_buffer_bus_addr)+(i* 0x8000),DMASIZE, i); if (i==2) set_write_desc(&bk_ptr->lite_table_wr_cpu_virt_addr->descriptors[i],ALTERA_MEM_MAIN_BUF2_START, (dma_addr_t)(bk_ptr->rp_wr_buffer_bus_addr)+(i* 0x8000),DMASIZE, i); if (i==3) set_write_desc(&bk_ptr->lite_table_wr_cpu_virt_addr->descriptors[i],ALTERA_MEM_MAIN_BUF3_START, (dma_addr_t)(bk_ptr->rp_wr_buffer_bus_addr)+(i* 0x8000),DMASIZE, i); } wmb(); iowrite32 (((dma_addr_t)bk_ptr->lite_table_wr_bus_addr)>>32, base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_RC_HIGH_SRC_ADDR); iowrite32 ((dma_addr_t)bk_ptr->lite_table_wr_bus_addr, base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_RC_LOW_SRC_ADDR); iowrite32 (WR_CTRL_BUF_BASE_HI, base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_CTRL_HIGH_DEST_ADDR); iowrite32 (WR_CTRL_BUF_BASE_LOW, base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_CTLR_LOW_DEST_ADDR); iowrite32 (0, base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_CONTROL);// no to begin MSI wmb(); bk_ptr->dma_status.altera_dma_num_dwords =DMASIZE; iowrite32 (bk_ptr->dma_status.altera_dma_descriptor_num -1, base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_TABLE_SIZE);// valeur par defaut 127 iowrite32(0xFF,(base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_LAST_PTR)); dev_info(&dev->dev, "config of dma done\n"); /* rc= request_irq(bk_ptr->irq_line, dma_isr, IRQF_SHARED, P40_DRV_NAME, (void *)bk_ptr); if (rc) { dev_info(&dev->dev, "Could not request IRQ #%d", bk_ptr->irq_line); bk_ptr->irq_line = -1; goto err_irq; } else { dev_info(&dev->dev, "request irq: %d", bk_ptr->irq_line); } wmb(); iowrite32 (1, base_bar0+DESC_CTRLLER_BASE+ALTERA_LITE_DMA_WR_CONTROL);// for MSI IRQ not working MTQ */ return 0; // error clean up err_wr_buffer: dev_err(&dev->dev, "goto err_wr_buffer"); pci_free_consistent(dev, PAGE_SIZE*bk_ptr->numpages, bk_ptr->rp_rd_buffer_virt_addr, bk_ptr->rp_rd_buffer_bus_addr); err_rd_buffer: dev_err(&dev->dev, "goto err_rd_buffer"); err_wr_table: dev_err(&dev->dev, "goto err_wr_table"); err_rd_table: dev_err(&dev->dev, "goto err_rd_table"); // free_irq(bk_ptr->irq_line, (void *)bk_ptr); //err_irq: //unmap_bars(bk_ptr, dev); //dev_err(&dev->dev, "goto err_regions"); err_dma_mask: dev_err(&dev->dev, "goto err_dma_mask"); pci_release_regions(dev); err_regions: dev_err(&dev->dev, "goto err_irq"); pci_disable_device(dev); err_enable: dev_err(&dev->dev, "goto err_enable"); unregister_chrdev_region (bk_ptr->cdevno, 1); err_initchrdev: dev_err(&dev->dev, "goto err_initchrdev"); kfree(bk_ptr); err_bk_alloc: dev_err(&dev->dev, "goto err_bk_alloc"); return rc; } void altera_pci_remove(struct pci_dev *dev) { struct pcie40_dma_state *bk_ptr = NULL; bk_ptr = altera_pcie40_get_drvdata(dev); device_destroy(pcie40_dma_class, MKDEV(MAJOR(bk_ptr->cdevno), MINOR(bk_ptr->cdevno))); cdev_del(&bk_ptr->cdev); unregister_chrdev_region(bk_ptr->cdevno, 1); pci_disable_device(dev); if(bk_ptr) { if(bk_ptr->msi_enabled) { pci_disable_msi(dev); bk_ptr->msi_enabled = 0; } } //unmap_bars(bk_ptr, dev); pci_release_regions(dev); /* if (bk_ptr->irq_line >= 0) { printk(KERN_DEBUG "Freeing IRQ #%d", bk_ptr->irq_line); free_irq(bk_ptr->irq_line, (void *)bk_ptr); } */ pci_free_consistent(dev, sizeof(struct lite_dma_desc_table), bk_ptr->lite_table_rd_cpu_virt_addr, bk_ptr->lite_table_rd_bus_addr); pci_free_consistent(dev, sizeof(struct lite_dma_desc_table), bk_ptr->lite_table_wr_cpu_virt_addr, bk_ptr->lite_table_wr_bus_addr); pci_free_consistent(dev, PAGE_SIZE*bk_ptr->numpages, bk_ptr->rp_rd_buffer_virt_addr, bk_ptr->rp_rd_buffer_bus_addr); pci_free_consistent(dev, PAGE_SIZE*bk_ptr->numpages, bk_ptr->rp_wr_buffer_virt_addr, bk_ptr->rp_wr_buffer_bus_addr); kfree(bk_ptr); printk(P40_INFO ": " "altera_dma_remove()," " " __DATE__ " " __TIME__ " " "\n"); } int altera_dma_init(void) { int rc = 0; printk(KERN_DEBUG P40_DRV_NAME ": " "altera_dma_init()," " " __DATE__ " " __TIME__ " " "\n"); pcie40_dma_class = class_create(THIS_MODULE, PCIE40_DMA_CLASS); if (IS_ERR(pcie40_dma_class)) { rc = PTR_ERR(pcie40_dma_class); printk(P40_WARN "failed to register class, %d\n", P40_PARM, rc); goto err_class_create; } //pcie40_ecs_class->dev_uevent = pcie40_dev_uevent; pcie40_dma_class->devnode = pcie40_devnode; return rc; err_class_create: return rc; } void altera_dma_exit(void) { class_destroy(pcie40_dma_class); }