/* aa_fifo.c */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aq_axi_fifo_common.h" #include "aq_axi_fifo.h" /* Standard module information*/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Hidemi Ishihara - hidemi@sweetcafe.jp"); MODULE_DESCRIPTION("aa_fifo - aa_fifo driver"); #define DRIVER_NAME "aa_fifo" int aa_fifo_major = 0; // major number (dynamically allocated in probe) int aa_fifo_minor = 0; // minor number (zero fixed) int aa_fifo_nr_devs = 1; // only one device node is supported. /******************************************************************************* For Debug *******************************************************************************/ #define _DEBUG_MODE_ #ifdef _DEBUG_MODE_ #define DEBUG_PRINT(args...) printk(args) #else #define DEBUG_PRINT(args...) #endif /******************************************************************************* Global Paramaters *******************************************************************************/ //static unsigned int major = DEFAULT_MAJOR_NUM; //static unsigned int minor = DEFAULT_MINOR_NUM; //#define AA_FIFO_BASE (0x40000000) // DMA #define WR_START (0x00) #define WR_ADRS (0x04) #define WR_LEN (0x08) #define RD_START (0x0C) #define RD_ADRS (0x10) #define RD_LEN (0x14) #define STATUS (0x18) #define TEST (0x1C) #define TESTRST (0x20) #define DEBUG (0x24) #define FSTATUS0 (0x28) #define FSTATUS1 (0x2C) #define FSTATUS2 (0x30) #define FSTATUS3 (0x34) /******************************************************************************* axi_fifo DEVICE MAPPING *******************************************************************************/ #define ALLOC_BLOCK (3) #define ALLOC_SIZE (0x800000) //char *aa_fifo_mem[i].dmabuf[ALLOC_BLOCK]; //dma_addr_t aa_fifo_mem[i].dmaphys[ALLOC_BLOCK]; //int aa_fifo_mem[i].dmasize[ALLOC_BLOCK]; struct st_aa_fifo_mem{ char *dmabuf; dma_addr_t dmaphys; int dmasize; }; struct st_aa_fifo_mem aa_fifo_mem[ALLOC_BLOCK]; ////////////////////////////////////////////////////////// file operation override ssize_t aa_fifo_read(struct file * filp, char __user * buf, size_t count, loff_t * f_pos); ssize_t aa_fifo_write(struct file * filp, const char __user * buf, size_t count, loff_t * f_pos); long aa_fifo_ioctl(struct file * filp, unsigned int cmd, unsigned long arg); loff_t aa_fifo_llseek(struct file * filp, loff_t off, int whence); int aa_fifo_open(struct inode * inode, struct file * filp); int aa_fifo_release(struct inode * inode , struct file * filp); // file operation object struct file_operations aa_fifo_fops = { .owner = THIS_MODULE, .unlocked_ioctl = aa_fifo_ioctl, .read = aa_fifo_read, .write = aa_fifo_write, .llseek = aa_fifo_llseek, .open = aa_fifo_open, .release = aa_fifo_release, }; //module_param( aa_fifo_param , int , S_IRUGO ); struct aa_fifo_local { int irq; unsigned long mem_start; unsigned long mem_end; void __iomem *base_addr; struct cdev cdev; struct device *dev; }; /* * @brief file opration */ ssize_t aa_fifo_read(struct file * filp, char __user * buf, size_t count, loff_t * f_pos) { ssize_t retval = 0; return retval; } ssize_t aa_fifo_write(struct file * filp, const char __user * buf, size_t count, loff_t * f_pos) { ssize_t retval = -ENOMEM; return retval; } long aa_fifo_ioctl(struct file * filp, unsigned int cmd, unsigned long arg) { int err = 0; int retval = 0; unsigned int val; struct aa_fifo_local * lp = filp->private_data; #if 0 if (_IOC_TYPE(cmd) != SAMPLE_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > SAMPLE_IOC_MAXNR) return -ENOTTY; if(_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void __user*) arg, _IOC_SIZE(cmd)); else if(_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void __user*)arg, _IOC_SIZE(cmd)); if(err) return -EFAULT; switch(cmd){ case SAMPLE_IOCRESET: iowrite32(tbuf_mask, lp->base_addr); break; case SAMPLE_IOCSET: retval = __get_user(val, (unsigned int __user *) arg); iowrite32(val, lp->base_addr); break; case SAMPLE_IOCGET: val = ioread32(lp->base_addr); retval = __put_user(val, (unsigned int __user*) arg); break; } #endif dma_addr_t dma_addr; DEBUG_PRINT("axi_fifo: device control(0x%08x).\n",cmd); // Check User Permission if( !capable(CAP_SYS_ADMIN) ){ DEBUG_PRINT("aq_axi_fifo: capable error\n"); return -EPERM; } // Check axi_fifo ioctl command if( _IOC_TYPE(cmd) != AA_FIFO_CMD_MAGIC ){ DEBUG_PRINT("aq_axi_fifo: cmd magic error(%x)\n", _IOC_TYPE(cmd)); return -ENOTTY; } if( _IOC_NR(cmd) > AA_FIFO_CMD_NR_MAX ){ DEBUG_PRINT("aq_axi_fifo: cmd nr error\n"); return -ENOTTY; } // Check User Area Access if( _IOC_DIR(cmd) & _IOC_READ ){ if( !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)) ){ DEBUG_PRINT("aq_axi_fifo: access_ok error for write\n"); return -EFAULT; } } if( _IOC_DIR(cmd) & _IOC_WRITE ){ if( !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)) ){ DEBUG_PRINT("aq_axi_fifo: access_ok error for read\n"); return -EFAULT; } } // Call axi_fifo Command switch (cmd) { case AA_FIFO_DMABUF_ALLOC: /* * Memory Allocation */ { int i; printk("aq_axi_fifo: allocate memory - ioctl\n"); for(i=0;idev, (int)ALLOC_SIZE, &aa_fifo_mem[i].dmaphys, GFP_KERNEL|GFP_DMA); aa_fifo_mem[i].dmabuf = dma_alloc_coherent(lp->dev, PAGE_ALIGN(arg), &aa_fifo_mem[i].dmaphys, GFP_KERNEL); aa_fifo_mem[i].dmasize = arg; if(aa_fifo_mem[i].dmabuf == NULL){ printk("aq_axi_fifo: can' allocate memory - no space with CMA(%d)\n", i); aa_fifo_mem[i].dmasize = 0; return -1; } printk("aq_axi_fifo: allocate memory - Phy Address = %08X\n", aa_fifo_mem[i].dmaphys); return aa_fifo_mem[i].dmaphys; } break; case AA_FIFO_DMABUF_FREE: /* * Mmeory Free */ { int i; printk("aq_axi_fifo: free memory - ioctl\n"); for(i=0;idev, (int)ALLOC_SIZE, aa_fifo_mem[i].dmabuf, aa_fifo_mem[i].dmaphys); aa_fifo_mem[i].dmabuf = NULL; aa_fifo_mem[i].dmaphys = NULL; aa_fifo_mem[i].dmasize = 0; return 0; } break; case AA_FIFO_DMA_TXSTART: /* * Transmit DMA Start */ { int i; printk("aq_axi_fifo: tx dma start\n", i); for(i=0;ibase_addr[RD_ADRS>>2] = arg; // DA lp->base_addr[RD_LEN>>2] = aa_fifo_mem[i].dmasize; // Leng lp->base_addr[RD_START>>2] = 0xFFFFFFFF; // Tx Run */ iowrite32(arg, lp->base_addr+RD_ADRS); iowrite32(aa_fifo_mem[i].dmasize, lp->base_addr+RD_LEN); iowrite32(0xFFFFFFFF, lp->base_addr+RD_START); printk("RD: %08x,%08x\n",arg,ioread32(lp->base_addr+RD_ADRS)); printk("RD: %08x,%08x\n",aa_fifo_mem[i].dmasize,ioread32(lp->base_addr+RD_LEN)); printk("RD: %08x,%08x\n",arg,ioread32(lp->base_addr+4)); printk("RD: %08x,%08x\n",aa_fifo_mem[i].dmasize,ioread32(lp->base_addr+8)); return 0; } break; case AA_FIFO_DMA_RXSTART: /* * Receive DMA Start */ { int i; printk("aq_axi_fifo: rx dma start\n", i); for(i=0;ibase_addr[WR_ADRS>>2] = arg; // DA lp->base_addr[WR_LEN>>2] = aa_fifo_mem[i].dmasize; // Leng lp->base_addr[WR_START>>2] = 0xFFFFFFFF; // Rd Run */ iowrite32(arg, lp->base_addr+WR_ADRS); iowrite32(aa_fifo_mem[i].dmasize, lp->base_addr+WR_LEN); iowrite32(0xFFFFFFFF, lp->base_addr+WR_START); return 0; } break; case AA_FIFO_DMA_TXSTATUS: // ステータスの応答 { int i; unsigned int buf; buf = ioread32(lp->base_addr+RD_START); printk("aq_axi_fifo: tx dma status(%08X)\n", buf); for(i=0;idev, aa_fifo_mem[i].dmabuf, aa_fifo_mem[i].dmasize, DMA_TO_DEVICE); dma_sync_single_for_device(lp->dev, dma_addr, aa_fifo_mem[i].dmasize, DMA_TO_DEVICE); dma_unmap_single(lp->dev, aa_fifo_mem[i].dmabuf, aa_fifo_mem[i].dmasize, DMA_TO_DEVICE); break; } } return buf; } break; case AA_FIFO_DMA_RXSTATUS: // ステータスの応答 { int i; unsigned int buf; buf = ioread32(lp->base_addr+WR_START); printk("aq_axi_fifo: rx dma status(%08X)\n", buf); for(i=0;idev, aa_fifo_mem[i].dmabuf, aa_fifo_mem[i].dmasize, DMA_FROM_DEVICE); dma_sync_single_for_cpu(lp->dev, dma_addr, aa_fifo_mem[i].dmasize, DMA_FROM_DEVICE); dma_unmap_single(lp->dev, dma_addr, aa_fifo_mem[i].dmasize, DMA_FROM_DEVICE); break; } } return buf; } break; case AA_FIFO_DMA_RESET: /* * Transmit DMA Start */ { iowrite32(0x0, lp->base_addr+TESTRST); // usleep(1); iowrite32(0x1, lp->base_addr+TESTRST); // usleep(1); iowrite32(0x0, lp->base_addr+TESTRST); return 0; } break; default: DEBUG_PRINT("aq_axi_fifo: ioctl cmd?\n"); return -EINVAL; } return retval; } loff_t aa_fifo_llseek(struct file * filp, loff_t off, int whence) { loff_t newpos = off; return newpos; } int aa_fifo_open(struct inode * inode, struct file * filp) { struct aa_fifo_local * lp; printk("aq_axi_fifo: open device.\n"); lp = container_of(inode->i_cdev, struct aa_fifo_local, cdev); filp->private_data = lp; /* ToDo */ // lp->base_addr = ioremap_nocache(AA_FIFO_BASE, 0x1000); DEBUG_PRINT("aq_axi_fifo: lp->base_addr = %08x\n", lp->base_addr); return 0; } int aa_fifo_release(struct inode * inode , struct file * filp) { struct aa_fifo_local * lp; lp = container_of(inode->i_cdev, struct aa_fifo_local, cdev); /* ToDo */ printk("axi_fifo: release device.\n"); { int i; for(i = 0; i < ALLOC_BLOCK; i++){ if(aa_fifo_mem[i].dmabuf != NULL){ printk("aq_axi_mem: free memory[%d]\n", i); dma_free_coherent(lp->dev, (int)ALLOC_SIZE, aa_fifo_mem[i].dmabuf, aa_fifo_mem[i].dmaphys); aa_fifo_mem[i].dmabuf = NULL; aa_fifo_mem[i].dmaphys = NULL; aa_fifo_mem[i].dmasize = 0; } } } return 0; } /* * @brief platform driver function */ static irqreturn_t aa_fifo_irq(int irq, void *lp) { /* printk(KERN_INFO "aa_fifo interrupt\n"); iowrite32(irq_st_mask, ((struct aa_fifo_local *)lp)->base_addr + irq_st_addr); */ return IRQ_HANDLED; } static int aa_fifo_probe(struct platform_device *pdev) { struct resource *r_irq; /* Interrupt resources */ struct resource *r_mem; /* IO mem resources */ struct device *dev = &pdev->dev; struct aa_fifo_local *lp = NULL; dev_t devno = 0; int rc = 0; printk("axi_fifo: probe start.\n"); dev_info(dev, "Device Tree Probing\n"); /* Get iospace for the device */ r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r_mem) { dev_err(dev, "invalid address\n"); return -ENODEV; } printk("axi_fifo: probe 1.\n"); lp = (struct aa_fifo_local *) kmalloc(sizeof(struct aa_fifo_local), GFP_KERNEL); if (!lp) { dev_err(dev, "Cound not allocate aa_fifo device\n"); return -ENOMEM; } printk("axi_fifo: probe 2.\n"); dev_set_drvdata(dev, lp); lp->dev = dev; lp->mem_start = r_mem->start; lp->mem_end = r_mem->end; if (!request_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1, DRIVER_NAME)) { dev_err(dev, "Couldn't lock memory region at %p\n", (void *)lp->mem_start); rc = -EBUSY; goto error1; } printk("axi_fifo: probe 3.\n"); lp->base_addr = ioremap(lp->mem_start, lp->mem_end - lp->mem_start + 1); if (!lp->base_addr) { dev_err(dev, "aa_fifo: Could not allocate iomem\n"); rc = -EIO; goto error2; } printk("axi_fifo: probe 4.\n"); /* Get IRQ for the device */ /* r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!r_irq) { dev_info(dev, "no IRQ found\n"); dev_info(dev, "aa_fifo at 0x%08x mapped to 0x%08x\n", (unsigned int __force)lp->mem_start, (unsigned int __force)lp->base_addr); return 0; } lp->irq = r_irq->start; printk("axi_fifo: probe 5.\n"); rc = request_irq(lp->irq, &aa_fifo_irq, 0, DRIVER_NAME, lp); if (rc) { dev_err(dev, "testmodule: Could not allocate interrupt %d.\n", lp->irq); goto error3; } dev_info(dev,"aa_fifo at 0x%08x mapped to 0x%08x, irq=%d\n", (unsigned int __force)lp->mem_start, (unsigned int __force)lp->base_addr, lp->irq); */ // initialization for char device if(aa_fifo_major){ devno = MKDEV(aa_fifo_major, aa_fifo_minor); rc = register_chrdev_region(devno, aa_fifo_nr_devs, DRIVER_NAME); }else{ rc = alloc_chrdev_region(&devno, aa_fifo_minor, aa_fifo_nr_devs, DRIVER_NAME); aa_fifo_major = MAJOR(devno); } dev_info(dev, "aa_fifo allocate cdev %d %d", aa_fifo_major, aa_fifo_minor); if(rc < 0){ printk(KERN_WARNING "%s: can't get major %d\n", DRIVER_NAME, aa_fifo_major); return rc; } printk("axi_fifo: probe 6.\n"); /* ToDo - Initialize for driver */ /* * DMA Buffer Addressの初期化 */ { int i; for(i = 0; i < ALLOC_BLOCK; i++){ aa_fifo_mem[i].dmaphys = NULL; } } printk("axi_fifo: init module complete.\n"); cdev_init(&lp->cdev, &aa_fifo_fops); lp->cdev.owner = THIS_MODULE; rc = cdev_add(&lp->cdev, devno, 1); if(rc) printk(KERN_NOTICE "Error %d adding %s%d", rc, DRIVER_NAME, 0); return 0; error3: free_irq(lp->irq, lp); error2: release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1); error1: kfree(lp); dev_set_drvdata(dev, NULL); printk("axi_fifo: probe failed.\n"); return rc; } static int aa_fifo_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct aa_fifo_local *lp = dev_get_drvdata(dev); dev_t devno = MKDEV(aa_fifo_major, aa_fifo_minor); cdev_del(&lp->cdev); unregister_chrdev_region(devno, aa_fifo_nr_devs); free_irq(lp->irq, lp); release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1); kfree(lp); dev_set_drvdata(dev, NULL); return 0; } static struct of_device_id aa_fifo_of_match[] = { { .compatible = "aquaxis,aa_fifo-1.00.a", }, /* { .compatible = "maker,hoge2-aa_fifo-1.00.b", }, */ { /* end of list */ }, }; MODULE_DEVICE_TABLE(of, aa_fifo_of_match); static struct platform_driver aa_fifo_driver = { .driver = { .name = "aa_fifo", .owner = THIS_MODULE, .of_match_table = aa_fifo_of_match, }, .probe = aa_fifo_probe, .remove = aa_fifo_remove, }; static int __init aa_fifo_init(void) { printk(KERN_INFO "start aa_fifo.\n"); return platform_driver_register(&aa_fifo_driver); } static void __exit aa_fifo_exit(void) { platform_driver_unregister(&aa_fifo_driver); printk(KERN_INFO "end aa_fifo.\n"); } module_init(aa_fifo_init); module_exit(aa_fifo_exit);