/* Anthony Pringle Geoffrey Wall pci_avnet.c -- AvNet FPGA Driver June 20 2005 Linux Kernel Device Drivers Dr. Ted Baker */ #include "pci_avnet.h" /* File Ops */ static struct file_operations av_sram_fops = { .open = av_open, .mmap = mmap_sram, .read = read_sram, .write = write_sram, }; static struct file_operations av_flash_fops = { .open = av_open, .mmap = mmap_flash, .read = read_flash, }; static struct file_operations av_ctrlreg_fops = { .open = av_open, .mmap = mmap_ctrlreg, .read = read_ctrlreg, .write = write_ctrlreg, }; static struct file_operations av_led_fops = { .open = av_open, .mmap = mmap_led, .read = read_led, .write = write_led, }; static int av_open(struct inode *inode, struct file *filp) { int i = 0, minorm4; int minor = iminor(inode); av_dev *dev = NULL; minorm4 = minor % 4; for (i = 0; i < numboards; i++) { if ((&av_devs[i])) { if ((av_devs[i].sram_minor == minor) ||(av_devs[i].flash_minor == minor) ||(av_devs[i].led_minor == minor) ||(av_devs[i].ctrlreg_minor == minor)) { dev = &av_devs[i]; filp->private_data = dev; dev->filp = filp; switch (minorm4) { case 0: filp->f_op = &av_sram_fops; break; case 1: filp->f_op = &av_flash_fops; break; case 2: filp->f_op = &av_ctrlreg_fops; break; case 3: filp->f_op = &av_led_fops; break; default: printk(KERN_ALERT "AV_OPEN: BAD MINOR NUMBER - NO FOPS ASSIGNED TO FILP\n"); break; } } } } if (!dev) return -ENODEV; else return 0; } static int mmap_sram(struct file * file, struct vm_area_struct * vma) { av_dev *dev = file->private_data; /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ unsigned long region_origin = vma->vm_pgoff * PAGE_SIZE; unsigned long region_length = vma->vm_end - vma->vm_start; unsigned long physical_addr = dev->sram_base + region_origin; unsigned long user_virtaddr = vma->vm_start; // sanity check: mapped region cannot expend past end of vram if ( region_origin + region_length > dev->sram_size ) return -EINVAL; // let the kernel know not to try swapping out this region vma->vm_flags = VM_RESERVED; // invoke kernel function that sets up the page-table entries if ( remap_pfn_range( vma, user_virtaddr, physical_addr >> PAGE_SHIFT, region_length, vma->vm_page_prot ) ) return -EAGAIN; return 0; } ssize_t read_sram(struct file *file, char __user *buf, size_t count, loff_t *f_pos) { int i = 0, sram_offset; u8 sram_val; av_dev *dev = file->private_data; // read from flash for (i = 0, sram_offset = 0; i < count; i++, sram_offset += 1) { sram_val = ioread8((dev->SRAM)+sram_offset); // acquire value at current offset if (copy_to_user(buf,&sram_val,1)) { return -EFAULT; } buf++; // increment to next buffer position } return count; } ssize_t write_sram(struct file *file, const char __user *buf, size_t count, loff_t *f_pos) { int i = 0, sram_offset; av_dev *dev = file->private_data; for (i = 0, sram_offset = 0; i < count; i++, sram_offset += 1) { iowrite8( (u8) *buf,((dev->SRAM)+sram_offset)); buf++; } return count; } /* Flash File Operations*/ static int mmap_flash(struct file * file, struct vm_area_struct * vma) { av_dev *dev = file->private_data; /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ unsigned long region_origin = vma->vm_pgoff * PAGE_SIZE; unsigned long region_length = vma->vm_end - vma->vm_start; unsigned long physical_addr = dev->flash_base + region_origin; unsigned long user_virtaddr = vma->vm_start; // sanity check: mapped region cannot expend past end of vram if ( region_origin + region_length > dev->flash_size ) return -EINVAL; // let the kernel know not to try swapping out this region vma->vm_flags = VM_RESERVED; // invoke kernel function that sets up the page-table entries if ( remap_pfn_range( vma, user_virtaddr, physical_addr >> PAGE_SHIFT, region_length, vma->vm_page_prot ) ) return -EAGAIN; return 0; } ssize_t read_flash(struct file *file, char __user *buf, size_t count, loff_t *f_pos) { int i = 0, flash_offset; u8 flash_val; av_dev *dev = file->private_data; // read from flash for (i = 0, flash_offset = 0; i < count; i++, flash_offset += 1) { flash_val = ioread8((dev->FLASH)+flash_offset); // acquire value at current offset if (copy_to_user(buf,&flash_val,1)) { return -EFAULT; } buf++; // increment to next buffer position } return count; } /* Control Register File Operations*/ static int mmap_ctrlreg(struct file * file, struct vm_area_struct * vma) { av_dev *dev = file->private_data; /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ unsigned long region_origin = vma->vm_pgoff * PAGE_SIZE; unsigned long region_length = vma->vm_end - vma->vm_start; unsigned long physical_addr = dev->ctrlreg_base + region_origin; unsigned long user_virtaddr = vma->vm_start; // sanity check: mapped region cannot expend past end of vram if ( region_origin + region_length > dev->ctrlreg_size ) return -EINVAL; // let the kernel know not to try swapping out this region vma->vm_flags = VM_RESERVED; // invoke kernel function that sets up the page-table entries if ( remap_pfn_range( vma, user_virtaddr, physical_addr >> PAGE_SHIFT, region_length, vma->vm_page_prot ) ) return -EAGAIN; return 0; } ssize_t read_ctrlreg(struct file *file, char __user *buf, size_t count, loff_t *f_pos) { u16 ctrlreg_val; av_dev *dev = file->private_data; // access Control Register contents ctrlreg_val = ioread16(dev->ctrlreg); // copy 16-bit value to user if (copy_to_user(buf,&ctrlreg_val, 2)) { return -EFAULT; } return 2; // read 2 bytes (16-bit control register, 11 bits used) } ssize_t write_ctrlreg(struct file *file, const char __user *buf, size_t count, loff_t *f_pos) { /* Note: the contents of the buffer may not be accurately written * to the Control Register. This is because the Virtex II FPGA may * may have ownership over specific parameters in the register. */ av_dev *dev = file->private_data; // write just 2 bytes (16 bits) of buffer's contents to the Control Register iowrite16( (u16) *(buf), dev->ctrlreg); return 2; // wrote 2 bytes (16-bit control register, 11 bits used) } /* LED File Operations*/ static int mmap_led(struct file * file, struct vm_area_struct * vma) { av_dev *dev = file->private_data; /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ unsigned long region_origin = vma->vm_pgoff * PAGE_SIZE; unsigned long region_length = vma->vm_end - vma->vm_start; unsigned long physical_addr = dev->led_base + region_origin; unsigned long user_virtaddr = vma->vm_start; // sanity check: mapped region cannot expend past end of vram if ( region_origin + region_length > dev->led_size ) return -EINVAL; // let the kernel know not to try swapping out this region vma->vm_flags = VM_RESERVED; // invoke kernel function that sets up the page-table entries if ( remap_pfn_range( vma, user_virtaddr, physical_addr >> PAGE_SHIFT, region_length, vma->vm_page_prot ) ) return -EAGAIN; return 0; } ssize_t read_led(struct file *file, char __user *buf, size_t count, loff_t *f_pos) { av_dev *dev = file->private_data; u8 led_val; // access LED contents led_val = ioread8((dev->ctrlreg) + led_offset); // copy 8-bit value to user if (copy_to_user(buf,&led_val, 1)) { return -EFAULT; } return 1; // returned 1 byte (8-bit LED) } ssize_t write_led(struct file *file, const char __user *buf, size_t count, loff_t *f_pos) { av_dev *dev = file->private_data; // write just 1 byte of whatever was written to the buffer iowrite8( (u8) *(buf), (dev->ctrlreg)+led_offset); return 1; // wrote 1 byte (8-bits) } static void av_setup_cdev(struct av_dev *dev, int index) { // int whichdev; int err = 0; dev_t devno; //whichdev = index / 4; devno = MKDEV(major, index); printk(KERN_ALERT "in av_setup_cdev, index = %d\n", index); if (dev == 0) { printk(KERN_ALERT "DEV IS NULL\n"); return; } printk(KERN_ALERT "devno = %d\n", devno); switch (index%4) //4 device types 0~3 { case 0: cdev_init(dev->sram_cdev, &av_sram_fops); dev->sram_cdev->ops = &av_sram_fops; dev->sram_minor = index; dev->sram_cdev->owner = THIS_MODULE; err = cdev_add(dev->sram_cdev, devno, 1); if (err) printk(KERN_ALERT "Error %d adding SRAM with index %d\n" ,err,index); break; case 1: cdev_init(dev->flash_cdev, &av_flash_fops); dev->flash_cdev->ops = &av_flash_fops; dev->flash_minor = index; dev->flash_cdev->owner = THIS_MODULE; err = cdev_add(dev->flash_cdev, devno, 1); if (err) printk(KERN_ALERT "Error %d adding FLASH with index %d\n" ,err,index); break; case 2: cdev_init(dev->ctrlreg_cdev, &av_ctrlreg_fops); dev->ctrlreg_cdev->ops = &av_ctrlreg_fops; dev->ctrlreg_minor = index; dev->ctrlreg_cdev->owner = THIS_MODULE; err = cdev_add(dev->ctrlreg_cdev, devno, 1); if (err) printk(KERN_ALERT "Error %d adding CTRLREG with index %d\n" ,err,index); break; case 3: cdev_init(dev->led_cdev, &av_led_fops); dev->led_cdev->ops = &av_led_fops; dev->led_minor = index; dev->led_cdev->owner = THIS_MODULE; err = cdev_add(dev->led_cdev, devno, 1); if (err) printk(KERN_ALERT "Error %d adding LED with index %d\n" ,err,index); break; } /* Fail gracefully if need be */ if (err) printk(KERN_NOTICE "Error %d adding charddev v2p %d", err, index); } static int probe(struct pci_dev *dev, const struct pci_device_id *id) { int result, sxtn_MB, two_MB; printk(KERN_ALERT "in probe\n"); /* I/O Remap */ // request the control register memory region if (!request_mem_region(pci_resource_start(dev,BAR2), pci_resource_len(dev,BAR2),"avnet_ctrlreg")) { printk(KERN_ALERT "avnet: can't request iomem (0x%lx).\n", pci_resource_start(dev,BAR2)); return -EBUSY; } printk(KERN_ALERT "avnet: got requested iomem(0x%lx).\n", pci_resource_start(dev,BAR2)); av_devs[numboards].ctrlreg = (unsigned int *) ioremap(pci_resource_start(dev,BAR2), 12); if (!av_devs[numboards].ctrlreg) { printk(KERN_ALERT "avnet: ioremap() failed\n"); result = -EIO; goto fail1; } printk(KERN_ALERT "avnet: control_reg ioremap() succeeded -- addr is %p\n", av_devs[numboards].ctrlreg); // request the FLASH memory region if (!request_mem_region(pci_resource_start(dev,BAR1), pci_resource_len(dev,BAR1),"avnet_flash")) { printk(KERN_ALERT "avnet: can't request iomem (0x%lx).\n", pci_resource_start(dev,BAR1)); return -EBUSY; } printk(KERN_ALERT "avnet: got requested iomem(0x%lx).\n", pci_resource_start(dev,BAR1)); sxtn_MB = 1 << 24; av_devs[numboards].FLASH = (unsigned char *) ioremap(pci_resource_start(dev,BAR1), sxtn_MB); //remap with size 16MB if (!av_devs[numboards].FLASH) { printk(KERN_ALERT "avnet: ioremap() failed\n"); result = -EIO; goto fail2; } printk(KERN_ALERT "avnet: FLASH ioremap() succeeded -- addr is %p\n", av_devs[numboards].FLASH); // request the SRAM memory region if (!request_mem_region(pci_resource_start(dev,BAR0), pci_resource_len(dev,BAR0),"avnet_sram")) { printk(KERN_ALERT "avnet: can't request iomem (0x%lx).\n", pci_resource_start(dev,BAR0)); return -EBUSY; } printk(KERN_ALERT "avnet: got requested iomem(0x%lx).\n", pci_resource_start(dev,BAR0)); two_MB = 1 << 21; av_devs[numboards].SRAM = (unsigned char *) ioremap(pci_resource_start(dev,BAR0), two_MB); //remap with size 2MB if (!av_devs[numboards].SRAM) { printk(KERN_ALERT "avnet: ioremap() failed\n"); result = -EIO; goto fail3; } printk(KERN_ALERT "avnet: SRAM ioremap() succeeded -- addr is %p\n", av_devs[numboards].SRAM); /* Assign V2P_ID here */ /* so on the 0th call to probe numboards = 0 */ /* v2P_ID = 0... 1st call numbaords = 1... */ av_devs[numboards].V2P_NUM = numboards; av_devs[numboards].pcidev = dev; /* dev->dev is a device struct */ /* we store a ptr to this board (@av_devs[numboards]) */ /* inside of driver_data */ dev->dev.driver_data = (void *)&av_devs[numboards]; pci_enable_device(dev); // enable device /* acquire base addresses and sizes */ av_devs[numboards].led_base = pci_resource_start(dev,BAR2) + 8; av_devs[numboards].led_size = pci_resource_len(dev,BAR2); printk(KERN_ALERT "led_base = 0x%x\n", (unsigned int)av_devs[numboards].led_base); av_devs[numboards].sram_base = pci_resource_start(dev,BAR0); av_devs[numboards].sram_size = pci_resource_len(dev,BAR0); printk(KERN_ALERT "sram_base = 0x%x\n", (unsigned int)av_devs[numboards].sram_base); av_devs[numboards].flash_base = pci_resource_start(dev,BAR1); av_devs[numboards].flash_size = pci_resource_len(dev,BAR1); printk(KERN_ALERT "flash_base = 0x%x\n", (unsigned int)av_devs[numboards].flash_base); av_devs[numboards].ctrlreg_base = pci_resource_start(dev,BAR2); av_devs[numboards].ctrlreg_size = pci_resource_len(dev,BAR2); printk(KERN_ALERT "ctrlreg_base = 0x%x\n", (unsigned int)av_devs[numboards].ctrlreg_base); numboards++; //counts the number of times this function is called (# of boards in system) printk(KERN_ALERT "numboards = %d\n", numboards); return 0; fail1: if (av_devs[numboards].ctrlreg) { iounmap(av_devs[numboards].ctrlreg); release_mem_region(pci_resource_start(dev,BAR2), pci_resource_len(dev,BAR2)); } fail2: if (av_devs[numboards].FLASH) { iounmap(av_devs[numboards].FLASH); release_mem_region(pci_resource_start(dev,BAR1), pci_resource_len(dev,BAR1)); } fail3: if (av_devs[numboards].SRAM) { iounmap(av_devs[numboards].SRAM); release_mem_region(pci_resource_start(dev,BAR0), pci_resource_len(dev,BAR0)); } return result; } static void remove(struct pci_dev *dev) { //int i; av_dev *avdev = NULL; avdev =(av_dev*)dev->dev.driver_data; /* this is called multiple times as each pci_dev is removed */ /* remove/unmap anything that was mapped */ printk(KERN_ALERT "IN remove\n"); iounmap(avdev->ctrlreg); release_mem_region(pci_resource_start(avdev->pcidev,BAR2) , pci_resource_len(avdev->pcidev,BAR2)); iounmap(avdev->FLASH); release_mem_region(pci_resource_start(avdev->pcidev,BAR1) , pci_resource_len(avdev->pcidev,BAR1)); iounmap(avdev->SRAM); release_mem_region(pci_resource_start(avdev->pcidev,BAR0) , pci_resource_len(avdev->pcidev,BAR0)); } static struct pci_driver av_pci_driver = { .name = "pci_avnet", .id_table = avnet_ids, .probe = probe, .remove = remove, }; static int __init avnet_init(void) { int i; int result = 0; /* CHANGE ALL OF THIS IN THE FUTURE */ dev_t dev = MKDEV(major, 0); printk(KERN_ALERT "in avnet_init\n"); pci_register_driver(&av_pci_driver);//this calls probe result = alloc_chrdev_region(&dev, 0, 4*numboards, //nr devs "pci_avnet"); major = MAJOR(dev); printk(KERN_ALERT "INIT: result = %d\n", result); for (i = 0; i < numboards; i++) { if (result < 0) { printk(KERN_WARNING "av_pci: can't get major %d\n", major); return result; } init_MUTEX(&av_devs[i].sem); //i = 0 1 2 av_devs[i].sram_cdev = (struct cdev *) kmalloc(sizeof(struct cdev), GFP_KERNEL); av_setup_cdev(&av_devs[i], 4*i+0);//SRAM 0 4 8 av_devs[i].flash_cdev = (struct cdev *) kmalloc(sizeof(struct cdev), GFP_KERNEL); av_setup_cdev(&av_devs[i], 4*i+1);//FLASH 1 5 9 av_devs[i].ctrlreg_cdev = (struct cdev *) kmalloc(sizeof(struct cdev), GFP_KERNEL); av_setup_cdev(&av_devs[i], 4*i+2);//CTRLREG 2 6 10 av_devs[i].led_cdev = (struct cdev *) kmalloc(sizeof(struct cdev), GFP_KERNEL); av_setup_cdev(&av_devs[i], 4*i+3);//LED 3 7 11 printk("av_pci: device %d acquired\n", i); } return result; } static void __exit avnet_exit(void) { int i; dev_t devno; pci_unregister_driver(&av_pci_driver); /* Get rid of our char dev entries */ if (av_devs) { /* have 4 chrdevs per board */ for (i = 0; i < numboards; i++) { printk(KERN_ALERT "IN avnet_exit\n"); cdev_del(av_devs[i].sram_cdev); cdev_del(av_devs[i].flash_cdev); cdev_del(av_devs[i].led_cdev); cdev_del(av_devs[i].ctrlreg_cdev); kfree(av_devs[i].sram_cdev); kfree(av_devs[i].flash_cdev); kfree(av_devs[i].ctrlreg_cdev); kfree(av_devs[i].led_cdev); } devno = MKDEV(major, 0); unregister_chrdev_region(devno, 4*numboards); } } MODULE_LICENSE("GPL"); module_init(avnet_init); module_exit(avnet_exit);