diff --git a/cmt/Makefile b/cmt/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..950cb692304af59c0152aeafa7811e590d5ae3e9 --- /dev/null +++ b/cmt/Makefile @@ -0,0 +1,27 @@ +# +KERNEL_VERSION := $(shell uname -r) +KERNEL_MODLIB := /lib/modules/$(KERNEL_VERSION) +KERNEL_SOURCES := $(shell test -d $(KERNEL_MODLIB)/source && echo $(KERNEL_MODLIB)/source || echo $(KERNEL_MODLIB)/build) +CPPFLAGS += -I/afs/cern.ch/atlas/project/tdaq/cmt/nightly/installed/include -DDRIVER_ERROR -DDRIVER_DEBUG -DCVSTAG=\"tagtag\" -DRELEASE_NAME=\"${CMTRELEASE}\" + +ifneq ($(KERNELRELEASE),) +obj-m := quest.o solar.o cmem_rcc.o io_rcc.o vme_rcc.o filar.o robin.o +#obj-m := quest.o solar.o cmem_rcc.o io_rcc.o vme_rcc.o filar.o filar_dma.o robin.o +else +KDIR := $(KERNEL_SOURCES) +PWD := $(shell pwd) +default: + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules + @if [ ! -d ../$(CMTCONFIG) ]; then \ + echo Making directory ; \ + mkdir ../$(CMTCONFIG) ; \ + fi + cp quest.ko ../$(CMTCONFIG)/quest-${KERNEL_VERSION}.ko + cp solar.ko ../$(CMTCONFIG)/solar-${KERNEL_VERSION}.ko + cp io_rcc.ko ../$(CMTCONFIG)/io_rcc-${KERNEL_VERSION}.ko + cp cmem_rcc.ko ../$(CMTCONFIG)/cmem_rcc-${KERNEL_VERSION}.ko + cp vme_rcc.ko ../$(CMTCONFIG)/vme_rcc-${KERNEL_VERSION}.ko + cp filar.ko ../$(CMTCONFIG)/filar-${KERNEL_VERSION}.ko +# cp filar_dma.ko ../$(CMTCONFIG)/filar_dma-${KERNEL_VERSION}.ko + cp robin.ko ../$(CMTCONFIG)/robin-${KERNEL_VERSION}.ko +endif diff --git a/cmt/cmem_rcc.c b/cmt/cmem_rcc.c new file mode 100644 index 0000000000000000000000000000000000000000..cd59c1b2f2e23c01402048ebf708029bf4629ef8 --- /dev/null +++ b/cmt/cmem_rcc.c @@ -0,0 +1,889 @@ +/************************************************************************/ +/* */ +/* This is the CMEM_RCC driver */ +/* Its purpose is to provide user applications with contiguous data */ +/* buffers for DMA operations. */ +/* */ +/* 12. Dec. 01 MAJO created */ +/* */ +/*******C 2005 - The software with that certain something****************/ + +/************************************************************************/ +/*NOTES: */ +/*- This driver should work on kernels from 2.6.9 onwards */ +/************************************************************************/ + + +#include <linux/init.h> //MJ: for 2.6, p30 +#include <linux/module.h> +#include <linux/moduleparam.h> //MJ: for 2.6, p30 +#include <linux/kernel.h> +#include <linux/stat.h> //MJ: for 2.6, e.g. for module_param +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/cdev.h> //e.g. for cdev_alloc +#include <linux/proc_fs.h> +#include <linux/bigphysarea.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include "cmem_rcc/cmem_rcc_drv.h" + + +// Globals +static int debug = 0; +static char *proc_read_text; +buffer_t *buffer_table; +static u_long* sram_p_addr; +static dev_t major_minor; +static struct cdev *cmem_rcc_cdev; + +// Prototypes +static void cmem_rcc_vmaClose(struct vm_area_struct *vma); +static void cmem_rcc_vmaOpen(struct vm_area_struct *vma); +static int cmem_rcc_open(struct inode *inode, struct file *file); +static int cmem_rcc_release(struct inode *inode, struct file *file); +static int cmem_rcc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg); +static int cmem_rcc_mmap(struct file *file, struct vm_area_struct *vma); +static int cmem_rcc_proc_write(struct file *file, const char *buffer, u_long count, void *data); +static int cmem_rcc_proc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data); + + +/***************************************************************/ +/* Use /sbin/modinfo <module name> to extract this information */ +/***************************************************************/ +module_param (debug, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(debug, "1 = enable debugging 0 = disable debugging"); +MODULE_DESCRIPTION("Allocation of contiguous memory"); +MODULE_AUTHOR("Markus Joos, CERN/EP"); +MODULE_LICENSE("Private: Contact markus.joos@cern.ch"); +MODULE_VERSION("2.0"); + + +// The ordinary device operations +static struct file_operations fops = +{ + .owner = THIS_MODULE, + .ioctl = cmem_rcc_ioctl, + .open = cmem_rcc_open, + .mmap = cmem_rcc_mmap, + .release = cmem_rcc_release, +}; + +// memory handler functions. MJ: Not actually required. Just for kdebug +static struct vm_operations_struct cmem_rcc_vm_ops = +{ + .close = cmem_rcc_vmaClose, + .open = cmem_rcc_vmaOpen, //MJ: Note the comma at the end of the list! +}; + + +/*****************************/ +/* Standard driver functions */ +/*****************************/ + +/****************************/ +static int cmem_rcc_init(void) +/****************************/ +{ + int loop, ecode; + static u_long* sram_k_addr; + struct cmem_proc_data_t cmem_proc_data; + static struct proc_dir_entry *cmem_rcc_file; + + ecode = alloc_chrdev_region (&major_minor, 0, 1, "cmem_rcc"); //MJ: for 2.6 p45 + if (ecode) + { + kdebug(("cmem_rcc(cmem_rcc_init): failed to obtain device numbers\n")); + goto fail1; + } + + proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL); + if (proc_read_text == NULL) + { + ecode = -ENOMEM; + kdebug(("cmem_rcc(cmem_rcc_init): error from kmalloc\n")); + goto fail2; + } + + // Install /proc entry + cmem_rcc_file = create_proc_entry("cmem_rcc", 0644, NULL); + if (cmem_rcc_file == NULL) + { + kdebug(("cmem_rcc(cmem_rcc_init): error from call to create_proc_entry\n")); + ecode = -EFAULT; + goto fail3; + } + + strcpy(cmem_proc_data.name, "cmem_rcc"); + strcpy(cmem_proc_data.value, "cmem_rcc"); + cmem_rcc_file->data = &cmem_proc_data; + cmem_rcc_file->read_proc = cmem_rcc_proc_read; + cmem_rcc_file->write_proc = cmem_rcc_proc_write; + cmem_rcc_file->owner = THIS_MODULE; + + // Allocate memory for the buffer table + kdebug(("cmem_rcc(cmem_rcc_init): MAX_BUFFS = %d\n", MAX_BUFFS)); + kdebug(("cmem_rcc(cmem_rcc_init): sizeof(buffer_t) = %d\n", sizeof(buffer_t))); + kdebug(("cmem_rcc(cmem_rcc_init): need %d bytes\n", MAX_BUFFS * sizeof(buffer_t))); + buffer_table = (buffer_t *)kmalloc(MAX_BUFFS * sizeof(buffer_t), GFP_KERNEL); + if (buffer_table == NULL) + { + printk("cmem_rcc(cmem_rcc_init): unable to allocate memory for buffer table\n"); + ecode = -EFAULT; + goto fail4; + } + + // Clear the buffer table + for(loop = 0; loop < MAX_BUFFS; loop++) + { + buffer_table[loop].paddr = 0; + buffer_table[loop].size = 0; + buffer_table[loop].used = 0; + buffer_table[loop].locked = 0; + buffer_table[loop].type = 0; + buffer_table[loop].pid = 0; + } + + // Temporarily allocate the first page to get the PCI base address for the auto-map feature in vmeconfig + sram_k_addr = (u_long *) bigphysarea_alloc_pages(1, 0, GFP_KERNEL); + if (sram_k_addr) + { + //MJ: Rubini does not like virt_to_bus but for the simple thing we want to do here it is probably OK + sram_p_addr = (u_long *) virt_to_bus(sram_k_addr); + kdebug(("cmem_rcc(cmem_rcc_init): sram_k_addr = 0x%08lx\n", (u_long)sram_k_addr)); + kdebug(("cmem_rcc(cmem_rcc_init): sram_p_addr = 0x%08lx\n", (u_long)sram_p_addr)); + bigphysarea_free_pages((void *)sram_k_addr); + } + else + { + kdebug(("cmem_rcc(cmem_rcc_init): Failed to determine the BPA base address\n")); + kdebug(("cmem_rcc(cmem_rcc_init): Check if BPA memory has been reserved at boot time\n")); + sram_p_addr = 0; + } + + cmem_rcc_cdev = (struct cdev *)cdev_alloc(); //MJ: for 2.6 p55 + cmem_rcc_cdev->ops = &fops; + ecode = cdev_add(cmem_rcc_cdev, major_minor, 1); //MJ: for 2.6 p56 + if (ecode) + { + kdebug(("cmem_rcc(cmem_rcc_init): error from call to cdev_add.\n")); + goto fail5; + } + + kdebug(("cmem_rcc(cmem_rcc_init): driver loaded; major device number = %d\n", MAJOR(major_minor))); + return(0); + + fail5: + kfree(buffer_table); + + fail4: + remove_proc_entry("cmem_rcc", NULL); + + fail3: + kfree(proc_read_text); + + fail2: + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + + fail1: + return(ecode); +} + + +/********************************/ +static void cmem_rcc_cleanup(void) +/********************************/ +{ + int loop, loop2; + struct page *page_ptr; + + // Release orphaned buffers + for(loop = 0; loop < MAX_BUFFS; loop++) + { + if (buffer_table[loop].used) + { + if (buffer_table[loop].locked) + { + kdebug(("cmem_rcc(cmem_rcc_cleanup): releasing locked buffer: type=%d paddr=0x%08lx size=0x%08x name=%s\n", + buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name)); + } + else + { + kdebug(("cmem_rcc(cmem_rcc_cleanup): Releasing orphaned buffer: type=%d paddr=0x%08lx size=0x%08x name=%s\n", + buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name)); + } + + if (buffer_table[loop].type == TYPE_GFP) + { + // unreserve all pages + page_ptr = virt_to_page(buffer_table[loop].kaddr); + + for (loop2 = (1 << buffer_table[loop].order); loop2 > 0; loop2--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + + // free the area + free_pages(buffer_table[loop].kaddr, buffer_table[loop].order); + } + else + bigphysarea_free_pages((void *)buffer_table[loop].kaddr); + } + } + + cdev_del(cmem_rcc_cdev); //MJ: for 2.6 p56 + + // Remove /proc entry + remove_proc_entry("cmem_rcc", NULL); + kfree(proc_read_text); + + // Return the buffer table + kfree(buffer_table); + + // Unregister the device + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 +} + + +module_init(cmem_rcc_init); //MJ: for 2.6 p16 +module_exit(cmem_rcc_cleanup); //MJ: for 2.6 p16 + + +/**************************************************************/ +static int cmem_rcc_open(struct inode *inode, struct file *file) +/**************************************************************/ +{ + int loop; + private_stuff *pptr; + + kdebug(("cmem_rcc(cmem_rcc_open): function called for file at 0x%016lx\n", (u_long)file)) + //reserve space to store information about the memory buffers managed by this "file" + pptr = (private_stuff *)kmalloc(sizeof(private_stuff), GFP_KERNEL); + if (pptr == NULL) + { + kdebug(("cmem_rcc(cmem_rcc_open): error from kmalloc\n")); + return(-EFAULT); + } + + //Initialize the space + for (loop = 0; loop < MAX_BUFFS; loop++) + pptr->buffer[loop] = 0; + + file->private_data = pptr; + kdebug(("cmem_rcc(cmem_rcc_open): private_data = 0x%08lx\n", (u_long)file->private_data)); + + return(0); +} + + +/*****************************************************************/ +static int cmem_rcc_release(struct inode *inode, struct file *file) +/*****************************************************************/ +{ + int loop, loop2; + struct page *page_ptr; + private_stuff *pptr; + + kdebug(("cmem_rcc(cmem_rcc_release): function called from process %d for file at 0x%016lx\n", current->pid, (u_long)file)); + pptr = (private_stuff *) file->private_data; + + // Release orphaned buffers of the current process +//MJ-SMP: protect this fragment (preferrably with a spinlock) + for(loop = 0; loop < MAX_BUFFS; loop++) + { + if ((pptr->buffer[loop] == 1) && (!buffer_table[loop].locked == 1)) + { + if (buffer_table[loop].type == TYPE_GFP) + { + // unreserve all pages + page_ptr = virt_to_page(buffer_table[loop].kaddr); + + for (loop2 = (1 << buffer_table[loop].order); loop2 > 0; loop2--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + + // free the area + free_pages(buffer_table[loop].kaddr, buffer_table[loop].order); + } + else + bigphysarea_free_pages((void *)buffer_table[loop].kaddr); + + kdebug(("cmem_rcc(cmem_rcc_release): Releasing orphaned buffer of process %d: type=%d paddr=0x%08lx size=0x%08x name=%s\n", + buffer_table[loop].pid, buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name)); + + // clear the entry in the buffer table + buffer_table[loop].paddr = 0; + buffer_table[loop].kaddr = 0; + buffer_table[loop].size = 0; + buffer_table[loop].type = 0; + buffer_table[loop].pid = 0; + buffer_table[loop].order = 0; + buffer_table[loop].used = 0; + pptr->buffer[loop] = 0; + } + } +//MJ-SMP: end of protected zone + + kfree(pptr); + return(0); +} + + +/**************************************************************************************/ +static int cmem_rcc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) +/**************************************************************************************/ +{ + private_stuff *pptr; + + pptr = (private_stuff *) file->private_data; + + switch (cmd) + { + case CMEM_RCC_GET: + { + u_int loop, tnum, ok, pagecount; + cmem_rcc_t uio_desc; + struct page *page_ptr; + + if (copy_from_user(&uio_desc, (void *)arg, sizeof(cmem_rcc_t)) !=0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_GET): error in from copy_from_user\n"); + return(-CMEM_RCC_CFU); + } + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.size = 0x%08x\n", uio_desc.size)); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.type = 0x%08x\n", uio_desc.type)); + +//MJ-SMP: protect this fragment (preferrably with a spinlock) + // Look for a free slot in the buffer table + ok = 0; + for(tnum = 0; tnum < MAX_BUFFS; tnum++) + { + if (buffer_table[tnum].used == 0) + { + buffer_table[tnum].used = 1; //This is to reserve the entry + pptr->buffer[tnum] = 1; //Remember which file this buffer will belong to + uio_desc.handle = tnum; + ok = 1; + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): tnum = %d\n", tnum)); + break; + } + } +//MJ-SMP: end of protected zone + + if (!ok) + { + printk("cmem_rcc(ioctl,CMEM_RCC_GET): all buffers are in use\n"); + return(-CMEM_RCC_OVERFLOW); + } + + if(uio_desc.type == TYPE_GFP) + { + uio_desc.kaddr = 0; + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): calling __get_free_pages\n")); + uio_desc.kaddr = __get_free_pages(GFP_ATOMIC, uio_desc.order); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): __get_free_pages returns address 0x%08lx\n", (u_long)uio_desc.kaddr)); + if (!uio_desc.kaddr) + { + printk("cmem_rcc(ioctl,CMEM_RCC_GET): error from __get_free_pages for order=%d\n", uio_desc.order); +//MJ-SMP: protect this fragment (preferrably with a spinlock) + buffer_table[tnum].used = 0; // No longer required + pptr->buffer[tnum] = 0; +//MJ-SMP: end of protected zone + return(-CMEM_RCC_GFP); + } + + // Reserve all pages to make them remapable + page_ptr = virt_to_page(uio_desc.kaddr); + + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): reserving pages\n")); + for (loop = (1 << uio_desc.order); loop > 0; loop--, page_ptr++) + set_bit(PG_reserved, &page_ptr->flags); //MJ: have a look at ther kernel book + + uio_desc.size = PAGE_SIZE * (1 << uio_desc.order); + } + else + { + pagecount = (int)((uio_desc.size - 1) / PAGE_SIZE + 1); // pages + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): requested number of BPA pages = %d\n", pagecount)); + + uio_desc.kaddr = (u_long)bigphysarea_alloc_pages(pagecount, 0, GFP_KERNEL); + uio_desc.size = PAGE_SIZE * pagecount; + if (uio_desc.kaddr == 0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_GET): error on bigphysarea_alloc_pages\n"); +//MJ-SMP: protect this fragment (preferrably with a spinlock) + buffer_table[tnum].used = 0; //Not required any more + pptr->buffer[tnum] = 0; +//MJ-SMP: end of protected zone + return(-CMEM_RCC_BPA); + } + } + + uio_desc.paddr = virt_to_bus((void *) uio_desc.kaddr); + + // Complete the entry in the buffer table + buffer_table[tnum].size = uio_desc.size; + buffer_table[tnum].paddr = uio_desc.paddr; + buffer_table[tnum].kaddr = uio_desc.kaddr; + buffer_table[tnum].pid = current->pid; + buffer_table[tnum].order = uio_desc.order; + buffer_table[tnum].type = uio_desc.type; + buffer_table[tnum].locked = 0; + strcpy(buffer_table[tnum].name, uio_desc.name); + + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): PAGE_SIZE = 0x%08x\n", (u_int)PAGE_SIZE)); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.kaddr = 0x%016lx\n", (u_long)uio_desc.kaddr)); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.paddr = 0x%016lx\n", (u_long)uio_desc.paddr)); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.size = 0x%08x\n", (u_int)uio_desc.size)); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.name = %s\n", uio_desc.name)); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.order = 0x%08x\n", (u_int)uio_desc.order)); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.type = 0x%08x\n", (u_int)uio_desc.type)); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): uio_desc.handle = 0x%08x\n", (u_int)uio_desc.handle)); + + if (copy_to_user((void *)arg, &uio_desc, sizeof(cmem_rcc_t)) != 0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_GET): error in from copy_to_user\n"); + return(-CMEM_RCC_CTU); + } + + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GET): done\n")); + break; + } + + case CMEM_RCC_FREE: + { + u_int handle, loop; + struct page *page_ptr; + + if (copy_from_user(&handle, (void *)arg, sizeof(int)) !=0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_FREE): error in from copy_from_user\n"); + return(-CMEM_RCC_CFU); + } + + kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): handle = 0x%08x\n", handle)); + +//MJ-SMP: protect this fragment (preferrably with a spinlock) + // Check if the handle makes sense + if (buffer_table[handle].used == 0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_FREE): Invalid handle\n"); + return(-CMEM_RCC_ILLHAND); + } + buffer_table[handle].used = 0; +//MJ-SMP: end of protected zone + + if (buffer_table[handle].type == TYPE_GFP) + { + // unreserve all pages + kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): unreserving GFP pages\n")); + page_ptr = virt_to_page(buffer_table[handle].kaddr); + + for (loop = (1 << buffer_table[handle].order); loop > 0; loop--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + + // free the area + free_pages(buffer_table[handle].kaddr, buffer_table[handle].order); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): pages freed\n")); + } + else + { + bigphysarea_free_pages((void *)buffer_table[handle].kaddr); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): BPA memory freed @ address 0x%8lx\n", buffer_table[handle].kaddr)); + } + + // Delete the entry in the buffer table + buffer_table[handle].paddr = 0; + buffer_table[handle].locked = 0; + buffer_table[handle].pid = 0; + buffer_table[handle].kaddr = 0; + buffer_table[handle].type = 0; + buffer_table[handle].order = 0; + buffer_table[handle].size = 0; //This enables the entry to be re-used + pptr->buffer[handle] = 0; + + kdebug(("cmem_rcc(ioctl,CMEM_RCC_FREE): done\n")); + break; + } + + case CMEM_RCC_LOCK: + { + u_int handle; + + if (copy_from_user(&handle, (void *)arg, sizeof(int)) !=0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_LOCK): error in from copy_from_user\n"); + return(-CMEM_RCC_CFU); + } + + kdebug(("cmem_rcc(ioctl,CMEM_RCC_LOCK): handle = 0x%08x\n", handle)); + + // Check if the handle makes sense + if (buffer_table[handle].used == 0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_LOCK): Invalid handle\n"); + return(-CMEM_RCC_ILLHAND); + } + + buffer_table[handle].locked = 1; + + kdebug(("cmem_rcc(ioctl,CMEM_RCC_LOCK): done\n")); + break; + } + + case CMEM_RCC_UNLOCK: + { + u_int handle; + + if (copy_from_user(&handle, (void *)arg, sizeof(int)) !=0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_UNLOCK): error in from copy_from_user\n"); + return(-CMEM_RCC_CFU); + } + + kdebug(("cmem_rcc(ioctl,CMEM_RCC_UNLOCK): handle = 0x%08x\n", handle)); + + // Check if the handle makes sense + if (buffer_table[handle].used == 0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_UNLOCK): Invalid handle\n"); + return(-CMEM_RCC_ILLHAND); + } + + buffer_table[handle].locked = 0; + + kdebug(("cmem_rcc(ioctl,CMEM_RCC_UNLOCK): done\n")); + break; + } + + case CMEM_RCC_GETPARAMS: + { + cmem_rcc_t uio_desc; + + if (copy_from_user(&uio_desc, (void *)arg, sizeof(cmem_rcc_t)) !=0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): error in from copy_from_user\n"); + return(-CMEM_RCC_CFU); + } + + // Check if the handle makes sense + if (buffer_table[uio_desc.handle].used == 0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): Invalid handle\n"); + return(-CMEM_RCC_ILLHAND); + } + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): called for handle %d\n", uio_desc.handle)); + uio_desc.paddr = buffer_table[uio_desc.handle].paddr; + uio_desc.uaddr = buffer_table[uio_desc.handle].uaddr; + uio_desc.kaddr = buffer_table[uio_desc.handle].kaddr; + uio_desc.size = buffer_table[uio_desc.handle].size; + uio_desc.order = buffer_table[uio_desc.handle].order; + uio_desc.locked = buffer_table[uio_desc.handle].locked; + uio_desc.type = buffer_table[uio_desc.handle].type; + strcpy(uio_desc.name, buffer_table[uio_desc.handle].name); + + if (copy_to_user((void *)arg, &uio_desc, sizeof(cmem_rcc_t)) != 0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): error in from copy_to_user\n"); + return(-CMEM_RCC_CTU); + } + kdebug(("cmem_rcc(ioctl,CMEM_RCC_GETPARAMS): done\n")); + break; + } + + case CMEM_RCC_SETUADDR: + { + cmem_rcc_t uio_desc; + + if (copy_from_user(&uio_desc, (void *)arg, sizeof(cmem_rcc_t)) !=0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): error in from copy_from_user\n"); + return(-CMEM_RCC_CFU); + } + + // Check if the handle makes sense + if (buffer_table[uio_desc.handle].used == 0) + { + printk("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): Invalid handle\n"); + return(-CMEM_RCC_ILLHAND); + } + kdebug(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): called for handle %d\n", uio_desc.handle)); + kdebug(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): uaddr = 0x%08lx\n", uio_desc.uaddr)); + buffer_table[uio_desc.handle].uaddr = uio_desc.uaddr; + + kdebug(("cmem_rcc(ioctl,CMEM_RCC_SETUADDR): done\n")); + break; + } + + case CMEM_RCC_DUMP: + { + char *buf; + int len, loop; + + kdebug(("cmem_rcc(ioctl,CMEM_RCC_DUMP): called\n")); + + buf = (char *)kmalloc(TEXT_SIZE, GFP_KERNEL); + if (buf == NULL) + { + kdebug(("cmem_rcc(ioctl,CMEM_RCC_DUMP): error from kmalloc\n")); + return(-CMEM_RCC_KMALLOC); + } + + len = 0; + len += sprintf(buf + len, "Memory allocated by __get_free_pages\n"); + len += sprintf(buf + len, " PID | Phys. address | Size | Order | Locked | Name\n"); +//MJ-SMP: protect this fragment (preferrably with a spinlock) + for(loop = 0; loop < MAX_BUFFS; loop++) + { + if (buffer_table[loop].used && buffer_table[loop].type == TYPE_GFP) + { + len += sprintf(buf + len, "%5d |", buffer_table[loop].pid); + len += sprintf(buf + len, " 0x%08lx |", buffer_table[loop].paddr); + len += sprintf(buf + len, " 0x%08x |", buffer_table[loop].size); + len += sprintf(buf + len, " %d |", buffer_table[loop].order); + len += sprintf(buf + len, " %s |", buffer_table[loop].locked ? "yes" : " no"); + len += sprintf(buf + len, " %s\n", buffer_table[loop].name); + } + } + + len += sprintf(buf + len, "Memory allocated by BigPhysArea\n"); + len += sprintf(buf + len, " PID | Phys. address | Size | Locked | Name\n"); + for(loop = 0; loop < MAX_BUFFS; loop++) + { + if (buffer_table[loop].used && buffer_table[loop].type == TYPE_BPA) + { + len += sprintf(buf + len, "%5d |", buffer_table[loop].pid); + len += sprintf(buf + len, " 0x%08lx |", buffer_table[loop].paddr); + len += sprintf(buf + len, " 0x%08x |", buffer_table[loop].size); + len += sprintf(buf + len, " %s |", buffer_table[loop].locked ? "yes" : " no"); + len += sprintf(buf + len, " %s\n", buffer_table[loop].name); + } + } +//MJ-SMP: end of protected zone + + if (copy_to_user((void *)arg, buf, TEXT_SIZE * sizeof(char)) != 0) + { + kdebug(("cmem_rcc(ioctl,CMEM_RCC_DUMP): error from copy_to_user\n")); + return(-CMEM_RCC_CTU); + } + + kfree(buf); + break; + } + } + return(0); +} + + +/******************************************************/ +static void cmem_rcc_vmaOpen(struct vm_area_struct *vma) +/******************************************************/ +{ + kdebug(("cmem_rcc_vmaOpen: Called\n")); +} + + +/*******************************************************/ +static void cmem_rcc_vmaClose(struct vm_area_struct *vma) +/*******************************************************/ +{ + kdebug(("cmem_rcc(cmem_rcc_vmaClose): Virtual address = 0x%08lx\n", (u_long)vma->vm_start)); + kdebug(("cmem_rcc(cmem_rcc_vmaClose): mmap released\n")); +} + + +/*********************************************************************/ +static int cmem_rcc_mmap(struct file *file, struct vm_area_struct *vma) +/*********************************************************************/ +{ + u_long offset, size; + + kdebug(("cmem_rcc(cmem_rcc_mmap): cmem_rcc_mmap called\n")); + vma->vm_flags |= VM_RESERVED; + vma->vm_flags |= VM_LOCKED; + kdebug(("cmem_rcc(cmem_rcc_mmap): vma->vm_end = 0x%016lx\n", (u_long)vma->vm_end)); + kdebug(("cmem_rcc(cmem_rcc_mmap): vma->vm_start = 0x%016lx\n", (u_long)vma->vm_start)); + kdebug(("cmem_rcc(cmem_rcc_mmap): vma->vm_offset = 0x%016lx\n", (u_long)vma->vm_pgoff << PAGE_SHIFT)); + kdebug(("cmem_rcc(cmem_rcc_mmap): vma->vm_flags = 0x%08x\n", (u_int)vma->vm_flags)); + + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + + // we only support shared mappings. "Copy on write" mappings are + // rejected here. A shared mapping that is writeable must have the + // shared flag set. +//MJ: do we need this? if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) +//MJ: do we need this? { +//MJ: do we need this? printk("cmem_rcc(cmem_rcc_mmap): writeable mappings must be shared, rejecting\n"); +//MJ: do we need this? return(-EINVAL); +//MJ: do we need this? } + + if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot)) + { + kdebug(("cmem_rcc(cmem_rcc_mmap): function remap_page_range failed \n")); + return(-CMEM_RCC_MMAP); + } + kdebug(("cmem_rcc(cmem_rcc_mmap): vma->vm_start(2) = 0x%016lx\n", (u_long)vma->vm_start)); + + vma->vm_ops = &cmem_rcc_vm_ops; + kdebug(("cmem_rcc(cmem_rcc_mmap): cmem_rcc_mmap done\n")); + return(0); +} + + +/*********************************************************************************************/ +static int cmem_rcc_proc_write(struct file *file, const char *buffer, u_long count, void *data) +/*********************************************************************************************/ +{ + int len, loop, loop2; + struct cmem_proc_data_t *fb_data = (struct cmem_proc_data_t *)data; + struct page *page_ptr; + + kdebug(("cmem_rcc(cmem_rcc_proc_write): cmem_rcc_proc_write called\n")); + + if(count > 99) + len = 99; + else + len = count; + + if (copy_from_user(fb_data->value, buffer, len)) + { + kdebug(("cmem_rcc(cmem_rcc_proc_write): error from copy_from_user\n")); + return(-EFAULT); + } + + kdebug(("cmem_rcc(cmem_rcc_proc_write): len = %d\n", len)); + fb_data->value[len - 1] = '\0'; + kdebug(("cmem_rcc(cmem_rcc_proc_write): text passed = %s\n", fb_data->value)); + + if (!strcmp(fb_data->value, "debug")) + { + debug = 1; + kdebug(("cmem_rcc(cmem_rcc_proc_write): debugging enabled\n")); + } + + if (!strcmp(fb_data->value, "nodebug")) + { + kdebug(("cmem_rcc(cmem_rcc_proc_write): debugging disabled\n")); + debug = 0; + } + + if (!strcmp(fb_data->value, "freelock")) + { + kdebug(("cmem_rcc(cmem_rcc_proc_write): releasing all locked segments\n")); +//MJ-SMP: protect this fragment (preferrably with a spinlock) + for(loop = 0; loop < MAX_BUFFS; loop++) + { + if (buffer_table[loop].used && buffer_table[loop].locked) + { + kdebug(("cmem_rcc(cmem_rcc_proc_write): releasing locked buffer: type=%d paddr=0x%08lx size=0x%08x name=%s\n", buffer_table[loop].type, buffer_table[loop].paddr, buffer_table[loop].size, buffer_table[loop].name)); + if (buffer_table[loop].type == TYPE_GFP) + { + page_ptr = virt_to_page(buffer_table[loop].kaddr); // unreserve all pages + for (loop2 = (1 << buffer_table[loop].order); loop2 > 0; loop2--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + free_pages(buffer_table[loop].kaddr, buffer_table[loop].order); // free the area + } + else + bigphysarea_free_pages((void *)buffer_table[loop].kaddr); + + // clear the entry in the buffer table + buffer_table[loop].paddr = 0; + buffer_table[loop].kaddr = 0; + buffer_table[loop].size = 0; + buffer_table[loop].type = 0; + buffer_table[loop].pid = 0; + buffer_table[loop].order = 0; + buffer_table[loop].used = 0; + } + } +//MJ-SMP: end of protected zone + } + + return len; +} + + +/***************************************************************************************************/ +static int cmem_rcc_proc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data) +/***************************************************************************************************/ +{ + int loop, nchars = 0; + static int len = 0; + + kdebug(("cmem_rcc(cmem_rcc_proc_read): Called with buf = 0x%08lx\n", (u_long)buf)); + kdebug(("cmem_rcc(cmem_rcc_proc_read): Called with *start = 0x%08lx\n", (u_long)*start)); + kdebug(("cmem_rcc(cmem_rcc_proc_read): Called with offset = %ld\n", (u_long)offset)); + kdebug(("cmem_rcc(cmem_rcc_proc_read): Called with count = %d\n", count)); + + if (offset == 0) + { + kdebug(("cmem_rcc(cmem_rcc_proc_read): Creating text....\n")); + len = 0; + + len += sprintf(proc_read_text, "\n"); + len += sprintf(proc_read_text + len, "0x%016lx\n", (u_long)sram_p_addr); + len += sprintf(proc_read_text + len, "CMEM RCC driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG); + + len += sprintf(proc_read_text + len, "__get_free_pages\n"); + len += sprintf(proc_read_text + len, " PID | Handle | Phys. address | Size | Locked | Order| Name\n"); + for(loop = 0; loop < MAX_BUFFS; loop++) + { + if (buffer_table[loop].used && buffer_table[loop].type == TYPE_GFP) + { + len += sprintf(proc_read_text + len, "%5d |", buffer_table[loop].pid); + len += sprintf(proc_read_text + len, "%7d |", loop); + len += sprintf(proc_read_text + len, " 0x%08lx |", buffer_table[loop].paddr); + len += sprintf(proc_read_text + len, " 0x%08x |", buffer_table[loop].size); + len += sprintf(proc_read_text + len, " %s |", buffer_table[loop].locked ? "yes" : " no"); + len += sprintf(proc_read_text + len, " %d |", buffer_table[loop].order); + len += sprintf(proc_read_text + len, " %s\n", buffer_table[loop].name); + } + } + + len += sprintf(proc_read_text + len, "BigPhysArea\n"); + len += sprintf(proc_read_text + len, " PID | Handle | Phys. address | Size | Locked | Name\n"); + for(loop = 0; loop < MAX_BUFFS; loop++) + { + if (buffer_table[loop].used && buffer_table[loop].type == TYPE_BPA) + { + len += sprintf(proc_read_text + len, "%5d |", buffer_table[loop].pid); + len += sprintf(proc_read_text + len, "%7d |", loop); + len += sprintf(proc_read_text + len, " 0x%08lx |", buffer_table[loop].paddr); + len += sprintf(proc_read_text + len, " 0x%08x |", buffer_table[loop].size); + len += sprintf(proc_read_text + len, " %s |", buffer_table[loop].locked ? "yes" : " no"); + len += sprintf(proc_read_text + len, " %s\n", buffer_table[loop].name); + } + } + + len += sprintf(proc_read_text + len, " \n"); + len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/cmem_rcc', executed as root,\n"); + len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n"); + len += sprintf(proc_read_text + len, "debug -> enable debugging\n"); + len += sprintf(proc_read_text + len, "nodebug -> disable debugging\n"); + len += sprintf(proc_read_text + len, "freelock -> release all locked segments\n"); + } + kdebug(("cmem_rcc(cmem_rcc_proc_read): number of characters in text buffer = %d\n", len)); + + if (count < (len - offset)) + nchars = count; + else + nchars = len - offset; + kdebug(("cmem_rcc(cmem_rcc_proc_read): min nchars = %d\n", nchars)); + + if (nchars > 0) + { + for (loop = 0; loop < nchars; loop++) + buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop]; + *start = buf + (offset & (PAGE_SIZE - 1)); + } + else + { + nchars = 0; + *eof = 1; + } + + kdebug(("cmem_rcc(cmem_rcc_proc_read): returning *start = 0x%08lx\n", (u_long)*start)); + kdebug(("cmem_rcc(cmem_rcc_proc_read): returning nchars = %d\n", nchars)); + return(nchars); +} diff --git a/cmt/filar.c b/cmt/filar.c new file mode 100644 index 0000000000000000000000000000000000000000..677c6259f6e6651b90cc0758656adb04d3d6753b --- /dev/null +++ b/cmt/filar.c @@ -0,0 +1,1521 @@ +/************************************************************************/ +/* */ +/* File: filar_driver.c */ +/* */ +/* driver for the FILAR S-Link interface */ +/* */ +/* 25. Jun. 02 MAJO created */ +/* */ +/************ C 2005 - The software with that certain something *********/ + +/************************************************************************/ +/*NOTES: */ +/*- This driver should work on kernels from 2.6.9 onwards */ +/************************************************************************/ + +/************************************************************************/ +/*Open issues: */ +/*- The ISR can have errors from the FIFOS (unlikely). It is not clear */ +/* how such errors would be reported. For now they are just logged */ +/*- The start and end control word patters passed via the INIT ioctl */ +/* are not yet used in the ISR for consistency checking. */ +/*- The ioctl fuctions copy large arrays into the driver and back to */ +/* the user application. This is so because the amount of data in the */ +/* arrays is not known. If possible one should only copy the valid */ +/* data. I don't know yet how to do this */ +/************************************************************************/ + + +#include <linux/init.h> //MJ: for 2.6, p30 +#include <linux/module.h> +#include <linux/moduleparam.h> //MJ: for 2.6, p30 +#include <linux/kernel.h> +#include <linux/stat.h> //MJ: for 2.6, e.g. for module_param +#include <linux/pci.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/mm.h> +#include <linux/cdev.h> //e.g. for cdev_alloc +#include <linux/slab.h> //e.g. for kmalloc +#include <linux/delay.h> //e.g. for udelay +#include <linux/interrupt.h> //e.g. for request_irq +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/system.h> //e.g. for local_irq_save +#include "ROSfilar/filar_driver.h" + + +/*********/ +/*Globals*/ +/*********/ +static int debug = 0, errorlog = 1; +static char *proc_read_text; +static u_int fuse = 1, scw, ecw; +static u_int maxcard = 0, channels[MAXCARDS][MAXCHANNELS], served[MAXCARDS][MAXCHANNELS]; +static u_int irqlist[MAXIRQ], cmask[MAXCARDS], hwfifo[MAXROLS][MAXHWFIFO]; +static u_int opid, hwfifow[MAXROLS], hwfifor[MAXROLS], hwfifon[MAXROLS]; +static u_int infifor[MAXROLS], outfifow[MAXROLS]; +static u_int ccw, order, swfifobasephys, outfifodatasize, infifodatasize, fifoptrsize, tsize = 0; +static u_int ackdata[MAXROLS][MAXOUTFIFO][4], acknum[MAXROLS]; +static u_int *outfifo, *outfifon, *infifo, *infifon, swfifobase; +struct filar_proc_data_t filar_proc_data; +static struct proc_dir_entry *filar_file; +static struct cdev *filar_cdev; +static T_filar_card filarcard[MAXCARDS]; +static dev_t major_minor; + +/************/ +/*Prototypes*/ +/************/ +static int hw_fifo_level(int ro, int *nfree); +static int __devinit filarProbe(struct pci_dev *dev, const struct pci_device_id *id); +static int filar_init(void); +static void disable_input(void); +static void enable_input(void); +static void filar_cleanup(void); +static void filar_vmaOpen(struct vm_area_struct *vma); +static void filar_vmaClose(struct vm_area_struct *vma); +static void filarRemove(struct pci_dev *dev); +static irqreturn_t filar_irq_handler (int irq, void *dev_id, struct pt_regs *regs); + +MODULE_DESCRIPTION("S-Link FILAR"); +MODULE_AUTHOR("Markus Joos, CERN/PH"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("Private: Contact markus.joos@cern.ch"); +module_param (debug, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(debug, "1 = enable debugging 0 = disable debugging"); +module_param (errorlog, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(errorlog, "1 = enable debugging 0 = disable debugging"); + + +// memory handler functions +static struct vm_operations_struct filar_vm_ops = +{ + .close = filar_vmaClose, //mmap-close + .open = filar_vmaOpen, //MJ: Note the comma at the end of the list! +}; + +// Standard file operations +static struct file_operations fops = +{ + .owner = THIS_MODULE, + .ioctl = filar_ioctl, + .open = filar_open, + .mmap = filar_mmap, + .release = filar_release, +}; + +// PCI driver structures. See p311 +static struct pci_device_id filarIds[] = +{ + {PCI_DEVICE(PCI_VENDOR_ID_CERN, 0x14)}, + {0,}, +}; + +//PCI operations +static struct pci_driver filarPciDriver = +{ + .name = "filar", + .id_table = filarIds, + .probe = filarProbe, + .remove = filarRemove, +}; + + +/*****************************/ +/* Standard driver functions */ +/*****************************/ +/*************************/ +static int filar_init(void) +/*************************/ +{ + int rol, tfsize, result; + u_int loop; + struct page *page_ptr; + + kerror(("FILAR (SC) driver version 3.0\n")); + + if (alloc_chrdev_region(&major_minor, 0, 1, "filar")) //MJ: for 2.6 p45 + { + kdebug(("filar(filar_init): failed to obtain device numbers\n")); + result = -FILAR_EIO; + goto fail1; + } + + proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL); + if (proc_read_text == NULL) + { + kdebug(("filar(filar_init): error from kmalloc\n")); + result = -EFAULT; + goto fail2; + } + + filar_file = create_proc_entry("filar", 0644, NULL); + if (filar_file == NULL) + { + kerror(("filar(filar_init): error from call to create_proc_entry\n")); + result = -EFAULT; + goto fail3; + } + + strcpy(filar_proc_data.name, "filar"); + strcpy(filar_proc_data.value, "filar"); + filar_file->data = &filar_proc_data; + filar_file->read_proc = filar_read_procmem; + filar_file->write_proc = filar_write_procmem; + filar_file->owner = THIS_MODULE; + + //Clear the list of used interupts + for(loop = 0; loop < MAXIRQ; loop++) + irqlist[loop] = 0; + + kdebug(("filar(filar_init): registering PCIDriver\n")); + result = pci_register_driver(&filarPciDriver); //MJ: See P312 + if (result == 0) + { + kerror(("filar(filar_init): ERROR! no FILAR cards found!\n")); + result = -EIO; + goto fail4; + } + else + { + kerror(("filar(filar_init): Found %d FILAR cards!\n", result)); + } + + // Allocate contiguous memory for the FIFOs and store the base addresses in a global structure + outfifodatasize = MAXROLS * MAXOUTFIFO * OUTFIFO_ELEMENTS * sizeof(u_int); //outfifo + infifodatasize = MAXROLS * MAXINFIFO * INFIFO_ELEMENTS * sizeof(u_int); //infifo + fifoptrsize = MAXROLS * sizeof(u_int); //outfifo(w/r/n) + infifo(w/r/n) + kdebug(("filar(filar_init): 0x%08x bytes needed for the OUT FIFO\n", outfifodatasize)); + kdebug(("filar(filar_init): 0x%08x bytes needed for the IN FIFO\n", infifodatasize)); + kdebug(("filar(filar_init): 0x%08x bytes needed for the FIFO pointers\n", fifoptrsize)); + + tsize = outfifodatasize + infifodatasize + 2 * fifoptrsize; + kdebug(("filar(filar_init): 0x%08x bytes needed for all FIFOs\n", tsize)); + + tfsize = (tsize + 4095) & 0xfffff000; // round up to next 4K boundary + tfsize = tfsize >> 12; // compute number of pages + kdebug(("filar(filar_init): %d pages needed for the S/W FIFOs\n", tfsize)); + + order = 0; + while(tfsize) + { + order++; + tfsize = tfsize >> 1; // compute order + } + kdebug(("filar(filar_init): order = %d\n", order)); + + swfifobase = __get_free_pages(GFP_ATOMIC, order); + if (!swfifobase) + { + kerror(("filar(filar_init): error from __get_free_pages\n")); + result = -FILAR_EFAULT; + goto fail5; + } + kdebug(("filar(filar_init): swfifobase = 0x%08x\n", swfifobase)); + + // Reserve all pages to make them remapable + page_ptr = virt_to_page(swfifobase); + + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + set_bit(PG_reserved, &page_ptr->flags); //MJ: have a look at the kernel book + + swfifobasephys = virt_to_bus((void *)swfifobase); + kdebug(("filar(filar_init): swfifobasephys = 0x%08x\n", swfifobasephys)); + + // Assign base addresses to the FIFO arrays + outfifo = (u_int *)swfifobase; + infifo = (u_int *)(swfifobase + outfifodatasize); + outfifon = (u_int *)(swfifobase + outfifodatasize + infifodatasize); + infifon = (u_int *)(swfifobase + outfifodatasize + infifodatasize + fifoptrsize); + + kdebug(("filar(filar_init): outfifo is at 0x%016lx\n", (u_long)outfifo)); + kdebug(("filar(filar_init): infifo is at 0x%016lx\n", (u_long)infifo)); + kdebug(("filar(filar_init): outfifon is at 0x%016lx\n", (u_long)outfifon)); + kdebug(("filar(filar_init): infifon is at 0x%016lx\n", (u_long)infifon)); + + //Initialize the FIFOs + for(rol = 0; rol < MAXROLS; rol++) + { + infifor[rol] = 0; + infifon[rol] = 0; + hwfifow[rol] = 0; + hwfifor[rol] = 0; + hwfifon[rol] = 0; + outfifow[rol] = 0; + outfifon[rol] = 0; + } + + opid = 0; + + filar_cdev = (struct cdev *)cdev_alloc(); //MJ: for 2.6 p55 + filar_cdev->ops = &fops; + result = cdev_add(filar_cdev, major_minor, 1); //MJ: for 2.6 p56 + if (result) + { + kdebug(("filar(filar_init): error from call to cdev_add.\n")); + goto fail6; + } + + + kdebug(("filar(filar_init): driver loaded; major device number = %d\n", MAJOR(major_minor))); + return(0); + + fail6: + page_ptr = virt_to_page(swfifobase); + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + free_pages(swfifobase, order); + + fail5: + pci_unregister_driver(&filarPciDriver); + + fail4: + remove_proc_entry("io_rcc", NULL); + + fail3: + kfree(proc_read_text); + + fail2: + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + + fail1: + return(result); +} + + +/*****************************/ +static void filar_cleanup(void) +/*****************************/ +{ + u_int loop; + struct page *page_ptr; + + cdev_del(filar_cdev); //MJ: for 2.6 p56 + pci_unregister_driver(&filarPciDriver); + page_ptr = virt_to_page(swfifobase); // unreserve all pages used for the S/W FIFOs + + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + + free_pages(swfifobase, order); // free the area + remove_proc_entry("filar", NULL); + kfree(proc_read_text); + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + + kdebug(("filar(filar_cleanup): driver removed\n")); +} + + +module_init(filar_init); //MJ: for 2.6 p16 +module_exit(filar_cleanup); //MJ: for 2.6 p16 + + +/**********************************************************/ +static int filar_open(struct inode *ino, struct file *filep) +/**********************************************************/ +{ + kdebug(("filar(filar_open): nothing to be done\n")); + return(0); +} + + +/*************************************************************/ +static int filar_release(struct inode *ino, struct file *filep) +/*************************************************************/ +{ + kdebug(("filar(filar_release): (alomst) nothing to be done\n")); + opid = 0; + return(0); +} + + +/***********************************************************************************/ +static int filar_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) +/***********************************************************************************/ +{ + switch (cmd) + { + case OPEN: + { + FILAR_open_t params; + + kdebug(("filar(ioctl,OPEN): called from process %d\n", current->pid)) + if (!opid) + opid = current->pid; + else + { + kerror(("filar(ioctl, OPEN): The FILAR is already used by process %d\n", opid)); + return(-FILAR_USED); + } + + params.size = tsize; + params.physbase = swfifobasephys; + params.outsize = outfifodatasize; + params.insize = infifodatasize; + params.ptrsize = fifoptrsize; + kdebug(("filar(ioctl, OPEN): tsize = 0x%08x\n", tsize)); + kdebug(("filar(ioctl, OPEN): swfifobasephys = 0x%08x\n", swfifobasephys)); + kdebug(("filar(ioctl, OPEN): outfifodatasize = 0x%08x\n", outfifodatasize)); + kdebug(("filar(ioctl, OPEN): infifodatasize = 0x%08x\n", infifodatasize)); + kdebug(("filar(ioctl, OPEN): fifoptrsize = 0x%08x\n", fifoptrsize)); + + if (copy_to_user((void *)arg, ¶ms, sizeof(FILAR_open_t)) != 0) + { + kerror(("filar(ioctl, OPEN): error from copy_to_user\n")); + return(-FILAR_EFAULT); + } + return(0); + break; + } + + case CLOSE: + { + kdebug(("filar(ioctl, CLOSE): called from process %d\n", current->pid)) + opid = 0; + + return(0); + break; + } + + case CONFIG: + { + FILAR_config_t params; + u_int card, channel, data, rol; + + if (copy_from_user(¶ms, (void *)arg, sizeof(FILAR_config_t)) !=0) + { + kerror(("filar(ioctl, CONFIG): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + + scw = params.scw; + ecw = params.ecw; + ccw = params.ccw; + kdebug(("filar(ioctl, CONFIG): params.bswap = %d\n", params.bswap)); + kdebug(("filar(ioctl, CONFIG): params.wswap = %d\n", params.wswap)); + kdebug(("filar(ioctl, CONFIG): params.psize = %d\n", params.psize)); + kdebug(("filar(ioctl, CONFIG): params.ccw = %d\n", params.ccw)); + + for(card = 0; card < maxcard; card++) + { + data = 0; + for(channel = 0; channel < MAXCHANNELS; channel++) + { + served[card][channel] = 0; + rol = (card << 2) + channel; //works only for 4 channels per card + kdebug(("filar(ioctl, CONFIG): params.enable[%d] = %d\n", rol, params.enable[rol])); + if (params.enable[rol]) + channels[card][channel] = 1; + else + { + data += (1 << (6 * channel + 9)); + channels[card][channel] = 0; + } + kdebug(("filar(ioctl, CONFIG): channels[%d][%d] = %d\n", card, channel, channels[card][channel])); + kdebug(("filar(ioctl, CONFIG): data = 0x%08x\n", data)); + } + data += (params.bswap << 1) + (params.wswap << 2) + (params.psize << 3); + kdebug(("filar(ioctl, CONFIG): Writing 0x%08x to the OCR\n", data)); + filarcard[card].regs->ocr = data; + cmask[card] = data; //remember which channels were enabled + + if (params.interrupt) + filarcard[card].regs->imask |= 0x2; //Enable the ACK FIFO almost full interrupt + } + break; + } + + case RESET: + { + u_int channel, data, card, rol; + + if (copy_from_user(&card, (void *)arg, sizeof(int)) !=0) + { + kerror(("filar(ioctl, RESET): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + + for(channel = 0; channel < MAXCHANNELS; channel++) + served[card][channel] = 0; + + if (card <= maxcard) + { + kdebug(("filar(ioctl, RESET): Resetting card %d\n", card)); + data = filarcard[card].regs->ocr; + data |= 0x1; + filarcard[card].regs->ocr = data; + udelay(2); // Delay for at least one us + data &= 0xfffffffe; + filarcard[card].regs->ocr = data; + + //reset the FIFOs + for(channel = 0; channel < MAXCHANNELS; channel++) + { + rol = (card << 2) + channel; + infifor[rol] = 0; + infifon[rol] = 0; + hwfifow[rol] = 0; + hwfifor[rol] = 0; + hwfifon[rol] = 0; + outfifow[rol] = 0; + outfifon[rol] = 0; + } + } + else + { + kerror(("filar(ioctl,RESET): This card is unknown\n")); + return(-FILAR_NOCARD); + } + + return(0); + break; + } + + case LINKRESET: + { + u_int waited, rol, data, card, channel; + + if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0) + { + kerror(("filar(ioctl,RESET): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + + card = rol >> 2; + if (card <= maxcard) + { + channel = rol & 0x3; + kdebug(("filar(ioctl,LINKRESET): Resetting card %d, channel %d\n", card, channel)); + served[card][channel] = 0; + + data = 0x1 << (8 + channel * 6); // Set the URESET bit + filarcard[card].regs->ocr |= data; + mdelay(30); // Delay for at least 30 ms + data = 0x1 << (16 + channel * 4); // Now wait for LDOWN to come up again + waited = 0; + while(filarcard[card].regs->osr & data) + { + waited++; + if (waited > 10000) + { + kerror(("filar(ioctl,LINKRESET): channel %d of card %d does not come up again\n", channel, card)); + data = ~(0x1 << (8 + channel * 6)); // Reset the URESET bit + filarcard[card].regs->ocr &= data; + return(-FILAR_STUCK); + } + } + data = ~(0x1 << (8 + channel * 6)); // Reset the URESET bit + filarcard[card].regs->ocr &= data; + } + else + { + kerror(("filar(ioctl,LINKRESET): This card is unknown\n")); + return(-FILAR_NOCARD); + } + + kdebug(("filar(ioctl,LINKRESET): Done\n")); + return(0); + break; + } + + case LINKSTATUS: + { + u_int isup, data, card, rol, channel; + + if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0) + { + kerror(("filar(ioctl, RESET): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + + card = rol >> 2; + if (card <= maxcard) + { + channel = rol & 0x3; + data = 0x1 << (16 + channel * 4); + if (filarcard[card].regs->osr & data) + isup = 0; + else + isup = 1; + + if (copy_to_user((void *)arg, &isup, sizeof(int)) != 0) + { + kerror(("filar(ioctl, LINKSTATUS): error from copy_to_user\n")); + return(-FILAR_EFAULT); + } + } + else + { + kerror(("filar(ioctl, LINKSTATUS): This card is unknown\n")); + return(-FILAR_NOCARD); + } + return(0); + break; + } + + case INFO: + { + FILAR_info_t info; + u_int rol, card, data; + + //Initialise the array to 0 + for(rol = 0; rol < MAXROLS; rol++) + { + info.channels[rol] = 0; + info.enabled[rol] = 0; + } + info.nchannels = 0; + + for(card = 0; card < maxcard; card++) + { + data = filarcard[card].regs->osr; + kdebug(("filar(ioctl, INFO): 0x%08x read from OSR\n", data)); + if (data & 0x00080000) info.channels[(card << 2)] = 0; + else {info.channels[(card << 2)] = 1; info.nchannels++;} + + if (data & 0x00800000) info.channels[(card << 2) + 1] = 0; + else {info.channels[(card << 2) + 1] = 1; info.nchannels++;} + + if (data & 0x08000000) info.channels[(card << 2) + 2] = 0; + else {info.channels[(card << 2) + 2] = 1; info.nchannels++;} + + if (data & 0x80000000) info.channels[(card << 2) + 3] = 0; + else {info.channels[(card << 2) + 3] = 1; info.nchannels++;} + + data = filarcard[card].regs->ocr; + kdebug(("filar(ioctl, INFO): 0x%08x read from OCR\n", data)); + + if (data & 0x00000200) info.enabled[(card << 2)] = 0; else info.enabled[(card << 2)] = 1; + if (data & 0x00008000) info.enabled[(card << 2) + 1] = 0; else info.enabled[(card << 2) + 1] = 1; + if (data & 0x00200000) info.enabled[(card << 2) + 2] = 0; else info.enabled[(card << 2) + 2] = 1; + if (data & 0x08000000) info.enabled[(card << 2) + 3] = 0; else info.enabled[(card << 2) + 3] = 1; + } + + if (copy_to_user((void *)arg, &info, sizeof(FILAR_info_t)) != 0) + { + kerror(("filar(ioctl, INFO): error from copy_to_user\n")); + return(-FILAR_EFAULT); + } + return(0); + break; + } + + case FIFOIN: + { + u_int card, channel, rol, loop, nfree, navail, min, data; + u_long flags; + + if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0) + { + kerror(("filar(ioctl,FIFOIN): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + card = rol >> 2; + channel = rol & 0x3; + kdebug(("filar(ioctl,FIFOIN): channel = %d\n", channel)); + kdebug(("filar(ioctl,FIFOIN): card = %d\n", card)); + + //We have to make sure that this code does not get interrupted. Otherwhise there will be + //overflows in the IN FIFO which lead to incorrect PCI addresses being written to the FILAR + local_irq_save(flags); //MJ: for 2.6 see p274 & p275 + + //Is there space in the FILAR + hw_fifo_level(rol, &nfree); + kdebug(("filar(ioctl,FIFOIN): There are %d slots available in the HW FIFO of ROL %d\n", nfree, rol)); + + //How many entries are in the IN FIFO + navail = infifon[rol]; + kdebug(("filar(ioctl,FIFOIN): There are %d entries in the IN FIFO\n", navail)); + + if (nfree < navail) + min = nfree; + else + min = navail; + + kdebug(("filar(ioctl,FIFOIN): min = %d\n", min)); + + for(loop = 0; loop < min; loop++) + { + // We have checked how many entries are free / available in the FIFOS + // Therefore we can waive the return values + in_fifo_pop(rol, &data); + hw_fifo_push(rol, data); + + // I don't check if there is space as this should be guaranteed by + // the coupling between the FILAR FIFO and the H/W FIFO which has the same depth + if (channel == 0) filarcard[card].regs->req1 = data; + else if (channel == 1) filarcard[card].regs->req2 = data; + else if (channel == 2) filarcard[card].regs->req3 = data; + else filarcard[card].regs->req4 = data; + + kdebug(("filar(ioctl,FIFOIN): PCI addr. 0x%08x written to card %d, channel %d\n", data, card, channel)); + } + + local_irq_restore(flags); + return(0); + break; + } + + case FLUSH: + { + flush_cards(); + break; + } + } + return(0); +} + + +/***************************************************/ +static void filar_vmaOpen(struct vm_area_struct *vma) +/***************************************************/ +{ + kdebug(("filar(filar_vmaOpen): Called\n")); +} + + +/****************************************************/ +static void filar_vmaClose(struct vm_area_struct *vma) +/****************************************************/ +{ + kdebug(("filar(filar_vmaClose): Virtual address = 0x%016lx\n",(u_long)vma->vm_start)); + kdebug(("filar(filar_vmaClose): mmap released\n")); +} + + +/******************************************************************/ +static int filar_mmap(struct file *file, struct vm_area_struct *vma) +/******************************************************************/ +{ + u_long offset, size; + + kdebug(("filar(filar_mmap): function called\n")); + vma->vm_flags |= VM_RESERVED; + vma->vm_flags |= VM_LOCKED; + + kdebug(("filar(filar_mmap): vma->vm_end = 0x%016lx\n", (u_long)vma->vm_end)); + kdebug(("filar(filar_mmap): vma->vm_start = 0x%016lx\n", (u_long)vma->vm_start)); + kdebug(("filar(filar_mmap): vma->vm_offset = 0x%016lx\n", (u_long)vma->vm_pgoff << PAGE_SHIFT)); + kdebug(("filar(filar_mmap): vma->vm_flags = 0x%08x\n", (u_int)vma->vm_flags)); + + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + kdebug(("filar(filar_mmap): offset = 0x%08x\n",(u_int)offset)); + kdebug(("filar(filar_mmap): size = 0x%08x\n",(u_int)size)); + +//MJ: required? if (offset & ~PAGE_MASK) +//MJ: required? { +//MJ: required? kerror(("filar(filar_mmap): offset not aligned: %ld\n", offset)); +//MJ: required? return -ENXIO; +//MJ: required? } +//MJ: required? if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) +//MJ: required? { +//MJ: required? kerror(("filar(filar_mmap): writeable mappings must be shared, rejecting\n")); +//MJ: required? return(-EINVAL); +//MJ: required? } + + if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot)) + { + kerror(("filar(filar_mmap): remap page range failed\n")); + return -ENXIO; + } + + vma->vm_ops = &filar_vm_ops; + kdebug(("filar(filar_mmap): function done\n")); + return(0); +} + + +/*********************************************************************************************/ +static int filar_write_procmem(struct file *file, const char *buffer, u_long count, void *data) +/*********************************************************************************************/ +{ + int len; + u_int card; + struct filar_proc_data_t *fb_data = (struct filar_proc_data_t *)data; + + kdebug(("filar(filar_write_procmem): filar_write_procmem called\n")); + + if(count > 99) + len = 99; + else + len = count; + + if (copy_from_user(fb_data->value, buffer, len)) + { + kerror(("filar(filar_write_procmem): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + + kdebug(("filar(filar_write_procmem): len = %d\n", len)); + fb_data->value[len - 1] = '\0'; + kdebug(("filar(filar_write_procmem): text passed = %s\n", fb_data->value)); + + if (!strcmp(fb_data->value, "debug")) + { + debug = 1; + kdebug(("filar(filar_write_procmem): debugging enabled\n")); + } + + if (!strcmp(fb_data->value, "nodebug")) + { + kdebug(("filar(filar_write_procmem): debugging disabled\n")); + debug = 0; + } + + if (!strcmp(fb_data->value, "elog")) + { + kdebug(("filar(filar_write_procmem): Error logging enabled\n")) + errorlog = 1; + } + + if (!strcmp(fb_data->value, "noelog")) + { + kdebug(("filar(filar_write_procmem): Error logging disabled\n")) + errorlog = 0; + } + + if (!strcmp(fb_data->value, "disable")) + { + for(card = 0; card < maxcard; card++) + filarcard[card].regs->ocr |= 0x08208200; + kdebug(("filar(filar_write_procmem): All channels disabled\n")); + } + + if (!strcmp(fb_data->value, "reset")) + { + reset_cards(1); + kdebug(("filar(filar_write_procmem): All cards reset\n")); + } + + if (!strcmp(fb_data->value, "flush")) + { + flush_cards(); + kdebug(("filar(filar_write_procmem): All cards flushed\n")); + } + + return len; +} + + +/***************************************************************************************************/ +static int filar_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data) +/***************************************************************************************************/ +{ + u_int rol, ret, card, ocr[MAXROLS], osr[MAXROLS], osr2[MAXROLS], imask[MAXROLS], fifostat[MAXROLS], value, value2, channel; + int loop, nchars = 0; + static int len = 0; + + kdebug(("filar(filar_read_procmem): Called with buf = 0x%016lx\n", (u_long)buf)); + kdebug(("filar(filar_read_procmem): Called with *start = 0x%016lx\n", (u_long)*start)); + kdebug(("filar(filar_read_procmem): Called with offset = %d\n", (u_int)offset)); + kdebug(("filar(filar_read_procmem): Called with count = %d\n", count)); + + if (offset == 0) + { + kdebug(("exp(exp_read_procmem): Creating text....\n")); + len = 0; + len += sprintf(proc_read_text + len, "\n\n\nFILAR driver (single cycle protocol) for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG); + + if (opid) + len += sprintf(proc_read_text + len, "The drivers is currently used by PID %d\n", opid); + len += sprintf(proc_read_text + len, "=========================================================================\n"); + len += sprintf(proc_read_text + len, "Card|IRQ line| ACK IRQ|revision|PCI MEM addr.| page size|wswap|bswap|temperature\n"); + len += sprintf(proc_read_text + len, "----|--------|--------|--------|-------------|------------------|-----|-----|-----------\n"); + + //Read the registers + for(card = 0; card < maxcard; card++) + { + ocr[card] = filarcard[card].regs->ocr; + osr[card] = filarcard[card].regs->osr; + osr2[card] = filarcard[card].regs->osr; + imask[card] = filarcard[card].regs->imask; + fifostat[card] = filarcard[card].regs->fifostat; + } + + for(card = 0; card < maxcard; card++) + { + len += sprintf(proc_read_text + len, " %d|", card); + len += sprintf(proc_read_text + len, " %2d|", filarcard[card].irq_line); + len += sprintf(proc_read_text + len, "%s|", (imask[card] & 0x2)?" enabled":"disabled"); + len += sprintf(proc_read_text + len, " 0x%02x|", filarcard[card].pci_revision); + len += sprintf(proc_read_text + len, " 0x%08x|", filarcard[card].pci_memaddr); + value = (ocr[card] >> 3) & 0x7; + if (value == 0) len += sprintf(proc_read_text + len, " 256 bytes|"); + if (value == 1) len += sprintf(proc_read_text + len, " 1 Kbyte|"); + if (value == 2) len += sprintf(proc_read_text + len, " 2 Kbytes|"); + if (value == 3) len += sprintf(proc_read_text + len, " 4 Kbytes|"); + if (value == 4) len += sprintf(proc_read_text + len, " 16 Kbytes|"); + if (value == 5) len += sprintf(proc_read_text + len, " 64 Kbytes|"); + if (value == 6) len += sprintf(proc_read_text + len, " 256 Kbytes|"); + if (value == 7) len += sprintf(proc_read_text + len, "4 MBytes - 8 Bytes|"); + len += sprintf(proc_read_text + len, " %s|", (ocr[card] & 0x4)?"yes":" no"); + len += sprintf(proc_read_text + len, " %s|", (ocr[card] & 0x2)?"yes":" no"); + len += sprintf(proc_read_text + len, " %3d deg. C\n", (osr[card] >>8) & 0xff); + } + + for(card = 0; card < maxcard; card++) + { + len += sprintf(proc_read_text + len, "\nCard %d:\n", card); + len += sprintf(proc_read_text + len, "=======\n"); + len += sprintf(proc_read_text + len, " | | | |free | | | | |free |\n"); + len += sprintf(proc_read_text + len, " | | |entries in|entries in| | | |entries in|entries in|fragments\n"); + len += sprintf(proc_read_text + len, "Channel|present|enabled|ACK FIFO |REQ FIFO |LDOWN|Overflow| XOFF|OUT FIFO |IN FIFO |served\n"); + len += sprintf(proc_read_text + len, "-------|-------|-------|----------|----------|-----|--------|-------|----------|----------|---------\n"); + + for(channel = 0; channel < MAXCHANNELS; channel++) + { + len += sprintf(proc_read_text + len, " %d|", channel); + + value = osr[card] & (1 << (19 + channel * 4)); + len += sprintf(proc_read_text + len, " %s|", value?" no":"yes"); + + value = ocr[card] & (1 << (9 + channel * 6)); + len += sprintf(proc_read_text + len, " %s|", value?" no":"yes"); + + value = (fifostat[card] >> (channel * 8)) & 0xf; + if (value < 15) + len += sprintf(proc_read_text + len, " %2d|", value); + else + len += sprintf(proc_read_text + len, " >14|"); + + value = (fifostat[card] >> ( 4 + channel * 8)) & 0xf; + if (value < 15) + len += sprintf(proc_read_text + len, " %2d|", value); + else + len += sprintf(proc_read_text + len, " >14|"); + + value = osr[card] & (1 << (16 + channel * 4)); + len += sprintf(proc_read_text + len, " %s|", value?"yes":" no"); + + value = osr[card] & (1 << (17 + channel * 4)); + len += sprintf(proc_read_text + len, " %s|", value?"yes":" no"); + + value = osr[card] & (1 << (18 + channel * 4)); + value2 = osr2[card] & (1 << (18 + channel * 4)); + if (!value && !value2) + len += sprintf(proc_read_text + len, " is off|"); + else if (value && value2) + len += sprintf(proc_read_text + len, " is on|"); + else if (value && !value2) + len += sprintf(proc_read_text + len, " was on|"); + else + len += sprintf(proc_read_text + len, "went on|"); + + rol = (card << 2) + channel; + ret = out_fifo_level(rol, &value); + len += sprintf(proc_read_text + len, " %4d|", MAXOUTFIFO - value); + + ret = in_fifo_level(rol, &value); + len += sprintf(proc_read_text + len, " %4d|", value); + + len += sprintf(proc_read_text + len, " %8d\n", served[card][channel]); + } + } + + len += sprintf(proc_read_text + len, " \n"); + len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/filar', executed as root,\n"); + len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n"); + len += sprintf(proc_read_text + len, "debug -> enable debugging\n"); + len += sprintf(proc_read_text + len, "nodebug -> disable debugging\n"); + len += sprintf(proc_read_text + len, "elog -> Log errors to /var/log/message\n"); + len += sprintf(proc_read_text + len, "noelog -> Do not log errors to /var/log/message\n"); + len += sprintf(proc_read_text + len, "disable -> disable all FILAR channels\n"); + len += sprintf(proc_read_text + len, "reset -> Reset all FILAR channels\n"); + len += sprintf(proc_read_text + len, "flush -> Flush all FILAR channels\n"); + } + kdebug(("filar(filar_read_procmem): number of characters in text buffer = %d\n", len)); + + if (count < (len - offset)) + nchars = count; + else + nchars = len - offset; + kdebug(("filar(filar_read_procmem): min nchars = %d\n", nchars)); + + if (nchars > 0) + { + for(loop = 0; loop < nchars; loop++) + buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop]; + *start = buf + (offset & (PAGE_SIZE - 1)); + } + else + { + nchars = 0; + *eof = 1; + } + + kdebug(("filar(filar_read_procmem): returning *start = 0x%016lx\n", (u_long)*start)); + kdebug(("filar(filar_read_procmem): returning nchars = %d\n", nchars)); + return(nchars); +} + + +/********************************************************************************/ +static irqreturn_t filar_irq_handler (int irq, void *dev_id, struct pt_regs *regs) //MJ: for 2.6 see p272 & p273 +/********************************************************************************/ +{ + u_int card; + + // Note: the FILAR card(s) may have to share an interrupt line with other PCI devices + // It is therefore important to exit quickly if we are not concerned with an interrupt + // For now this is done by looking at the FIFOSTAT register. There may be a dedicated + // interrupt status bit in the future. + + for(card = 0; card < maxcard; card++) + { + if ((filarcard[card].irq_line == (u_int)irq) && (filarcard[card].regs->osr & 0x2)) + { + if (fuse) + read_ack_and_write_out(card); + else + { + filarcard[card].regs->imask = 0; //Disable all interrupts + kerror(("filar(filar_irq_handler): CATASTROPHY: Interrupt received from card %d with fuse=0\n", card)); + } + return(IRQ_HANDLED); //MJ: for 2.6 see p273 + } + else + { + kerror(("filar(filar_irq_handler): This was not a FILAR interrupt\n")); + return(IRQ_NONE); + } + } + return(IRQ_NONE); +} + + + +/*****************************/ +/* PCI driver functions */ +/*****************************/ + +/**********************************************************************************/ +static int __devinit filarProbe(struct pci_dev *dev, const struct pci_device_id *id) //MJ: for _devinit see p31 +/**********************************************************************************/ +{ + int i, card; + u_long flags; + u_int size; + + kdebug(("filar(filarProbe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device)); + kdebug(("filar(filarProbe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn)); + + /* Find a new "slot" for the card */ + card = -1; + for (i = 0; i < MAXCARDS; i++) + { + if (filarcard[i].pciDevice == NULL) + { + card = i; + filarcard[card].pciDevice = dev; + break; + } + } + + if (card == -1) + { + kerror(("filar(filarProbe): Could not find space in the filarcard array for this card.")); + return(-FILAR_TOOMANYCARDS); + } + + flags = pci_resource_flags(dev, BAR0); //MJ: See p317 + if ((flags & IORESOURCE_MEM) != 0) + { + filarcard[card].pci_memaddr = pci_resource_start(dev, BAR0) & PCI_BASE_ADDRESS_MEM_MASK; //MJ: See p317 + size = pci_resource_end(dev, BAR0) - pci_resource_start(dev, BAR0); //MJ: See p317 + kdebug(("filar(filarProbe): Mapping %d bytes at PCI address 0x%08x\n", size, filarcard[card].pci_memaddr)); + + filarcard[card].regs = (T_filar_regs *)ioremap(filarcard[card].pci_memaddr, size); + if (filarcard[card].regs == NULL) + { + kerror(("filar(filarProbe): error from ioremap for filarcard[%d].regs\n", card)); + filarcard[card].pciDevice = NULL; + return(-FILAR_IOREMAP); + } + kdebug(("filar(filarProbe): filarcard[%d].regs is at 0x%016lx\n", card, (u_long)filarcard[card].regs)); + } + else + { + kerror(("filar(filarProbe): Bar 0 is not a memory resource")); + return(-FILAR_EIO); + } + + // get revision directly from the Filar + pci_read_config_byte(filarcard[card].pciDevice, PCI_REVISION_ID, &filarcard[card].pci_revision); + kdebug(("filar(filarProbe): revision = %x \n", filarcard[card].pci_revision)); + if (filarcard[card].pci_revision < MINREV) + { + kerror(("filar(filarProbe): Illegal Filar Revision\n")); + return(-FILAR_ILLREV); + } + + pci_read_config_byte(filarcard[card].pciDevice, PCI_INTERRUPT_LINE, &filarcard[card].irq_line); + kdebug(("filar(filarProbe): interrupt line = %d \n", filarcard[card].irq_line)); + + //Several FILAR cards may use the same interrupt but we want to install the driver only once + if (irqlist[filarcard[card].irq_line] == 0) + { + if(request_irq(filarcard[card].irq_line, filar_irq_handler, SA_SHIRQ, "filar", dev)) + { + kdebug(("filar(filarProbe): request_irq failed on IRQ = %d\n", filarcard[card].irq_line)); + return(-FILAR_REQIRQ); + } + irqlist[filarcard[card].irq_line]++; + kdebug(("filar(filarProbe): Interrupt %d registered\n", filarcard[card].irq_line)); + } + + maxcard++; + return(0); +} + + +/******************************************/ +static void filarRemove(struct pci_dev *dev) +/******************************************/ +{ + int i, card; + + /* Find the "slot" of the card */ + card = -1; + for (i = 0; i < MAXCARDS; i++) + { + if (filarcard[i].pciDevice == dev) + { + card = i; + filarcard[i].pciDevice = 0; + break; + } + } + + if (card == -1) + { + kerror(("filar(filarRemove): Could not find device to remove.")); + return; + } + + if (--irqlist[filarcard[card].irq_line] == 0) + { + kerror(("filar(filarRemove): removing handler for interrupt %d", filarcard[card].irq_line)); + free_irq(filarcard[card].irq_line, dev); + } + + iounmap((void *)filarcard[card].regs); + kerror(("filar(filarRemove): Card %d removed", card)); + maxcard--; +} + + + +/*********************/ +/* Service functions */ +/*********************/ + +/****************************************/ +static int hw_fifo_push(int rol, int data) +/****************************************/ +{ + if (hwfifon[rol] == MAXHWFIFO) + { + kerror(("filar(hw_fifo_push): The HW FIFO is full\n")); + blow_fuse(); + return(-1); + } + hwfifo[rol][hwfifow[rol]] = data; + hwfifow[rol]++; + if (hwfifow[rol] == MAXHWFIFO) + hwfifow[rol] = 0; + + hwfifon[rol]++; + return(0); +} + + +/****************************************/ +static int hw_fifo_pop(int rol, int *data) +/****************************************/ +{ + if (hwfifon[rol] == 0) + { + kerror(("filar(hw_fifo_pop): The HW FIFO is empty\n")); + blow_fuse(); + return(-1); + } + *data = hwfifo[rol][hwfifor[rol]]; + hwfifor[rol]++; + if (hwfifor[rol] == MAXHWFIFO) + hwfifor[rol] = 0; + + hwfifon[rol]--; + return(0); +} + + +/*******************************************/ +static int hw_fifo_level(int rol, int *nfree) +/*******************************************/ +{ + *nfree = MAXHWFIFO - hwfifon[rol]; + return(0); +} + + +/****************************************/ +static int in_fifo_pop(int rol, int *data) +/****************************************/ +{ + if (infifon[rol] == 0) + { + kdebug(("filar(in_fifo_pop): The IN FIFO is empty\n")); + return(-1); + } + *data = infifo[IN_INDEX(rol, infifor[rol])]; + + infifor[rol]++; + if (infifor[rol] == MAXINFIFO) + infifor[rol] = 0; + + infifon[rol]--; + + return(0); +} + + +/*******************************************/ +static int in_fifo_level(int rol, int *nfree) +/*******************************************/ +{ + *nfree = MAXINFIFO - infifon[rol]; + return(0); +} + + +/******************************************************************************************/ +static int out_fifo_push(int rol, int pciaddr, int fragsize, int fragstat, int scw, int ecw) +/******************************************************************************************/ +{ + int oo; + + oo = OUT_INDEX(rol, outfifow[rol]); + kdebug(("filar(out_fifo_push): oo = %d\n", oo)); + + if (outfifon[rol] == MAXOUTFIFO) + { + kerror(("filar(out_fifo_push): The OUT FIFO is full\n")); + return(-1); + } + + outfifo[oo] = pciaddr; + outfifo[oo + 1] = fragsize; + outfifo[oo + 2] = fragstat; + outfifo[oo + 3] = scw; + outfifo[oo + 4] = ecw; + + outfifow[rol]++; + if (outfifow[rol] == MAXOUTFIFO) + outfifow[rol] = 0; + outfifon[rol]++; + return(0); +} + + +/********************************************/ +static int out_fifo_level(int rol, int *nfree) +/********************************************/ +{ + *nfree = MAXOUTFIFO - outfifon[rol]; + return(0); +} + + +/*****************************/ +static void disable_input(void) +/*****************************/ +{ + u_int card, data; + + for(card = 0; card < maxcard; card++) + { + data = filarcard[card].regs->ocr; + data |= 0x08208200; + filarcard[card].regs->ocr = data; //disable all channels + } +} + + +/****************************/ +static void enable_input(void) +/****************************/ +{ + u_int card; + + for(card = 0; card < maxcard; card++) + filarcard[card].regs->ocr = cmask[card]; //reset cannels to initial state +} + + +/******************************************/ +static void read_ack_and_write_out(int card) +/******************************************/ +{ + u_int data2, fragstat, nfree, pciaddr, ret, dindex, rol, loop, nacks, channel; + volatile u_int data, *ackptr, *reqptr; + + //---> Part 1: read / write FILAR registers + disable_input(); + for(channel = 0; channel < MAXCHANNELS; channel++) + { + kdebug(("filar(read_ack_and_write_out): channels[%d][%d] = %d\n", card, channel, channels[card][channel])); + if (channels[card][channel]) + { + dindex = 0; + rol = (card << 2) + channel; + reqptr = (u_int *) (&filarcard[card].regs->req1 + 4 * channel); + ackptr = (u_int *) reqptr + 1; + out_fifo_level(rol, &nfree); + + while(1) + { + nacks = (filarcard[card].regs->fifostat >> (channel * 8)) & 0xf; + kdebug(("filar(read_ack_and_write_out): card=%d channel=%d rol=%d nacks=%d\n", card, channel, rol, nacks)); + if (!nacks) + break; + + for(loop = 0; loop < nacks; loop++) + { + ret = hw_fifo_pop(rol, &pciaddr); + if (ret) + { + kerror(("filar(read_ack_and_write_out): error from hw_fifo_pop\n")); + blow_fuse(); + return; + } + kdebug(("filar(read_ack_and_write_out): Read PCI address 0x%08x from HW FIFO\n", pciaddr)); + + data = *ackptr; + kdebug(("filar(read_ack_and_write_out): Read 0x%08x from ACK FIFO\n", data)); + ackdata[rol][dindex][0] = data; + if ((data & 0x40000000) || ccw) + ackdata[rol][dindex][1] = filarcard[card].regs->scw; + if ((data & 0x10000000) || ccw) + ackdata[rol][dindex][2] = filarcard[card].regs->ecw; + + ackdata[rol][dindex][3] = pciaddr; + dindex++; + nfree--; + if (!nfree) + { + kerror(("filar(read_ack_and_write_out): nfree=0 , dindex=%d-> blowing fuse\n", dindex)); + blow_fuse(); + return; + } + } + } + + acknum[rol] = dindex; + kdebug(("filar(read_ack_and_write_out): Received %d fragments from card=%d, channel=%d\n", dindex, card, channel)); + + for(loop = 0; loop < dindex; loop++) + { + ret = in_fifo_pop(rol, &pciaddr); + if (!ret) + { + ret = hw_fifo_push(rol, pciaddr); + if (ret) + { + kerror(("filar(read_ack_and_write_out): error from hw_fifo_push\n")); + blow_fuse(); + return; + } + *reqptr = pciaddr; + kdebug(("filar(read_ack_and_write_out): Wrote PCI address 0x%08x to REQ FIFO\n", pciaddr)); + } + } + } + } + enable_input(); + + //---> Part 2: process data + for(channel = 0; channel < MAXCHANNELS; channel++) + { + if (channels[card][channel]) + { + rol = (card << 2) + channel; + for(loop = 0; loop < acknum[rol]; loop++) + { + fragstat = 0; + data = ackdata[rol][loop][0]; + kdebug(("filar(read_ack_and_write_out): Analyzing card=%d channel=%d fragment=%d ack=0x%08x\n", card, channel, loop, data)); + if (data & 0x80000000) + { + fragstat |= NO_SCW; + kerror(("filar(read_ack_and_write_out): NO_SCW")); + } + else + { + if (data & 0x40000000) + { + fragstat |= BAD_SCW; + kerror(("filar(read_ack_and_write_out): BAD_SCW")); + + data2 = ackdata[rol][loop][1]; + kerror(("filar(read_ack_and_write_out): Start Control Word = 0x%08x\n", data2)); + + if (data2 & 0x1) + { + kerror(("filar(read_ack_and_write_out): Data transmission error in SCW\n")); + fragstat |= SCW_DTE; + } + + if (data2 & 0x2) + { + kerror(("filar(read_ack_and_write_out): Control word transmission error in SCW\n")); + fragstat |= SCW_CTE; + } + } + } + + if (data & 0x20000000) + { + fragstat |= NO_ECW; + kerror(("filar(read_ack_and_write_out): NO_ECW")); + } + else + { + if (data & 0x10000000) + { + fragstat |= BAD_ECW; + kerror(("filar(read_ack_and_write_out): BAD_ECW")); + + data2 = ackdata[rol][loop][2]; + kerror(("filar(read_ack_and_write_out): End Control Word = 0x%08x\n", data2)); + + if (data2 & 0x1) + { + kerror(("filar(read_ack_and_write_out): Data transmission error in ECW\n")); + fragstat |= ECW_DTE; + } + + if (data2 & 0x2) + { + kerror(("filar(read_ack_and_write_out): Control word transmission error in ECW\n")); + fragstat |= ECW_CTE; + } + } + } + + if (ccw) + ret = out_fifo_push(rol, ackdata[rol][loop][3], data & 0x000fffff, fragstat, ackdata[rol][loop][1], ackdata[rol][loop][2]); + else + ret = out_fifo_push(rol, ackdata[rol][loop][3], data & 0x000fffff, fragstat, 0 ,0); + + if (ret) + { + kerror(("filar(read_ack_and_write_out): error from out_fifo_push\n")); + blow_fuse(); + return; + } + } + served[card][channel] += acknum[rol]; + } + } +} + + +/*************************/ +static void blow_fuse(void) +/*************************/ +{ + // If this fuse blows one has to re-configure the FILAR (to re-enable the interrupt) + u_int data, card, channel, rol, nacks, nfree; + + fuse = 0; + kerror(("filar(blow_fuse): There was an internal error in the driver\n")); + kerror(("filar(blow_fuse): All interrupt have been disabled\n")); + kerror(("filar(blow_fuse): You have to reload the driver\n")); + + for(card = 0; card < MAXCARDS; card++) + { + for(channel = 0; channel < MAXCHANNELS; channel++) + { + if (channels[card][channel]) + { + rol = (card << 2) + channel; + nacks = (filarcard[card].regs->fifostat >> (channel * 8)) & 0xf; + out_fifo_level(rol, &nfree); + kerror(("filar(blow_fuse): rol=%d nacks=%d nfree=%d\n", rol, nacks, nfree)); + } + } + } + + for(card = 0; card < maxcard; card++) + { + filarcard[card].regs->imask = 0; //Disable all interrupts + + data = filarcard[card].regs->ocr; + data |= 0x1; + filarcard[card].regs->ocr = data; + udelay(2); // Delay for at least one us + data &= 0xfffffffe; + filarcard[card].regs->ocr = data; + } +} + + +/*******************************/ +static void reset_cards(int mode) +/*******************************/ +{ + u_int card, rol, channel, data; + + kdebug(("filar(reset_cards): maxcard = %d mode = %d\n", maxcard, mode)); + for(card = 0; card < maxcard; card++) + { + data = filarcard[card].regs->ocr; + data |= 0x1; + filarcard[card].regs->ocr = data; + // Delay for at least one us + udelay(10); + data &= 0xfffffffe; + filarcard[card].regs->ocr = data; + //reset the FIFOs + if (mode) + { + for(channel = 0; channel < MAXCHANNELS; channel++) + { + served[card][channel] = 0; + rol = (card << 2) + channel; + infifor[rol] = 0; + infifon[rol] = 0; + outfifow[rol] = 0; + outfifon[rol] = 0; + hwfifow[rol] = 0; + hwfifor[rol] = 0; + hwfifon[rol] = 0; + } + } + } + kdebug(("filar(reset_cards): Done\n")); +} + + +/***************************/ +static void flush_cards(void) +/***************************/ +{ + u_long flags; + u_int card; + + //We have to make sure that this code does not get interrupted. + //Otherwhise there can be a race condition. + local_irq_save(flags); + for(card = 0; card < maxcard; card++) + { + kdebug(("filar(flush_cards): Calling read_ack_and_write_out for card %d\n", card)); + read_ack_and_write_out(card); + } + + local_irq_restore(flags); +} + diff --git a/cmt/filar_dma.c b/cmt/filar_dma.c new file mode 100644 index 0000000000000000000000000000000000000000..c82858f7df63cbc5a83ba61fcb4b83ff8f52a878 --- /dev/null +++ b/cmt/filar_dma.c @@ -0,0 +1,1511 @@ +/************************************************************************/ +/* */ +/* File: filar_driver.c */ +/* */ +/* driver for the FILAR S-Link interface */ +/* */ +/* 25. Jun. 02 MAJO created */ +/* */ +/************ C 2003 - The software with that certain something *********/ + +/************************************************************************/ +/* NOTES: */ +/* - This driver should work on kernels from 2.4 onwards */ +/************************************************************************/ + +/************************************************************************/ +/*Open issues: */ +/*- so far the errors in the ISR only printk to var/log/message. One */ +/* could use a global variable to remember the error and report it */ +/* via the next ioctl() call. */ +/*- The start and end control word patters passed via the INIT ioctl */ +/* are not yet used in the ISR for consistency checking. */ +/*- The FILAR does not return the actual control words via the ACK block*/ +/*- The ioctl fuctions copy large arrays into the driver and back to */ +/* the user application. This is so because the amount of data in the */ +/* arrays is not known. If possible one should only copy the valid */ +/* data. I don't know yet how to do this */ +/************************************************************************/ + +#include <linux/init.h> //MJ: for 2.6, p30 +#include <linux/module.h> +#include <linux/moduleparam.h> //MJ: for 2.6, p30 +#include <linux/kernel.h> +#include <linux/stat.h> //MJ: for 2.6, e.g. for module_param +#include <linux/pci.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/mm.h> +#include <linux/cdev.h> //e.g. for cdev_alloc +#include <linux/slab.h> //e.g. for kmalloc +#include <linux/delay.h> //e.g. for udelay +#include <linux/interrupt.h> //e.g. for request_irq +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/system.h> //e.g. for local_irq_save +#include "ROSfilar/filar_driver.h" + + +/*********/ +/*Globals*/ +/*********/ +static int debug = 0, errorlog = 1; +static u_int opid, scw, ecw, filar_irq_line[MAXCARDS]; +static u_int maxcards, channels[MAXCARDS][MAXCHANNELS], served[MAXCARDS][MAXCHANNELS], pci_memaddr[MAXCARDS]; +static u_char pci_revision[MAXCARDS]; +static u_int irq_in[MAXCARDS], hwfifo[MAXROLS][MAXHWFIFO]; +static u_int hwfifow[MAXROLS], hwfifor[MAXROLS], hwfifon[MAXROLS]; +static u_int *outfifo, *outfifon; +static u_int *infifo, *infifon; +static u_int irqlist[MAXIRQ], infifor[MAXROLS], outfifow[MAXROLS]; +static u_int order, ackadd[MAXCARDS], reqadd[MAXCARDS], *ackbuf[MAXCARDS], *reqbuf[MAXCARDS], swfifobasephys, swfifobase, outfifodatasize, infifodatasize, fifoptrsize, tsize = 0; +static struct proc_dir_entry *filar_file; +struct filar_proc_data_t filar_proc_data; +static char *proc_read_text; +static dev_t major_minor; +static struct cdev *filar_cdev; +static T_filar_card filarcard[MAXCARDS]; + +/************/ +/*Prototypes*/ +/************/ +static int req_block(int card, int ackenable); +static int __devinit filarProbe(struct pci_dev *dev, const struct pci_device_id *id); +static int filar_init(void); +static void filar_cleanup(void); +static void filar_vmaOpen(struct vm_area_struct *vma); +static void filar_vmaClose(struct vm_area_struct *vma); +static void filarRemove(struct pci_dev *dev); +static irqreturn_t filar_irq_handler (int irq, void *dev_id, struct pt_regs *regs); + +MODULE_DESCRIPTION("S-Link FILAR (DMA F/W)"); +MODULE_AUTHOR("Markus Joos, CERN/PH"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("Private: Contact markus.joos@cern.ch"); +module_param (debug, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(debug, "1 = enable debugging 0 = disable debugging"); +module_param (errorlog, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(errorlog, "1 = enable debugging 0 = disable debugging"); + +// memory handler functions +static struct vm_operations_struct filar_vm_ops = +{ + .close = filar_vmaClose, //mmap-close + .open = filar_vmaOpen, //MJ: Note the comma at the end of the list! +}; + +// Standard file operations +static struct file_operations fops = +{ + .owner = THIS_MODULE, + .ioctl = filar_ioctl, + .open = filar_open, + .mmap = filar_mmap, + .release = filar_release, +}; + +// PCI driver structures. See p311 +static struct pci_device_id filarIds[] = +{ + {PCI_DEVICE(PCI_VENDOR_ID_CERN, 0x14)}, + {0,}, +}; + +//PCI operations +static struct pci_driver filarPciDriver = +{ + .name = "filar_dma", + .id_table = filarIds, + .probe = filarProbe, + .remove = filarRemove, +}; + + +/*****************************/ +/* Standard driver functions */ +/*****************************/ + +/**************************/ +static int filar_init(void) +/**************************/ +{ + int rol, tfsize, result; + u_int card, loop; + struct page *page_ptr; + + kerror(("FILAR (SC) driver version 3.0\n")); + + if (alloc_chrdev_region(&major_minor, 0, 1, "filar")) //MJ: for 2.6 p45 + { + kdebug(("filar(filar_init): failed to obtain device numbers\n")); + result = -FILAR_EIO; + goto fail1; + } + + proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL); + if (proc_read_text == NULL) + { + kdebug(("filar(filar_init): error from kmalloc\n")); + result = -EFAULT; + goto fail2; + } + + filar_file = create_proc_entry("filar", 0644, NULL); + if (filar_file == NULL) + { + kerror(("filar(filar_init): error from call to create_proc_entry\n")); + result = -EFAULT; + goto fail3; + } + + strcpy(filar_proc_data.name, "filar"); + strcpy(filar_proc_data.value, "filar"); + filar_file->data = &filar_proc_data; + filar_file->read_proc = filar_read_procmem; + filar_file->write_proc = filar_write_procmem; + filar_file->owner = THIS_MODULE; + + //Clear the list of used interupts + for(loop = 0; loop < MAXIRQ; loop++) + irqlist[loop] = 0; + + kdebug(("filar(filar_init): registering PCIDriver\n")); + result = pci_register_driver(&filarPciDriver); //MJ: See P312 + if (result == 0) + { + kerror(("filar(filar_init): ERROR! no FILAR cards found!\n")); + result = -EIO; + goto fail4; + } + else + { + kerror(("filar(filar_init): Found %d FILAR cards!\n", result)); + } + + // Allocate contiguous memory for the FIFOs and store the base addresses in a global structure + outfifodatasize = MAXROLS * MAXOUTFIFO * OUTFIFO_ELEMENTS * sizeof(u_int); //outfifo + infifodatasize = MAXROLS * MAXINFIFO * INFIFO_ELEMENTS * sizeof(u_int); //infifo + fifoptrsize = MAXROLS * sizeof(u_int); //outfifo(w/r/n) + infifo(w/r/n) + kdebug(("filar(filar_init): 0x%08x bytes needed for the OUT FIFO\n", outfifodatasize)); + kdebug(("filar(filar_init): 0x%08x bytes needed for the IN FIFO\n", infifodatasize)); + kdebug(("filar(filar_init): 0x%08x bytes needed for the FIFO pointers\n", fifoptrsize)); + + tsize = outfifodatasize + infifodatasize + 2 * fifoptrsize; + kdebug(("filar(filar_init): 0x%08x bytes needed for all FIFOs\n", tsize)); + + tfsize = (tsize + 4095) & 0xfffff000; // round up to next 4K boundary + tfsize = tfsize >> 12; // compute number of pages + kdebug(("filar(filar_init): %d pages needed for the S/W FIFOs\n", tfsize)); + + order = 0; + while(tfsize) + { + order++; + tfsize = tfsize >> 1; // compute order + } + kdebug(("filar(filar_init): order = %d\n", order)); + + swfifobase = __get_free_pages(GFP_ATOMIC, order); + if (!swfifobase) + { + kerror(("filar(filar_init): error from __get_free_pages\n")); + result = -FILAR_EFAULT; + goto fail5; + } + kdebug(("filar(filar_init): swfifobase = 0x%08x\n", swfifobase)); + + // Reserve all pages to make them remapable + page_ptr = virt_to_page(swfifobase); + + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + set_bit(PG_reserved, &page_ptr->flags); //MJ: have a look at the kernel book + + swfifobasephys = virt_to_bus((void *)swfifobase); + kdebug(("filar(filar_init): swfifobasephys = 0x%08x\n", swfifobasephys)); + + // Assign base addresses to the FIFO arrays + outfifo = (u_int *)swfifobase; + infifo = (u_int *)(swfifobase + outfifodatasize); + outfifon = (u_int *)(swfifobase + outfifodatasize + infifodatasize); + infifon = (u_int *)(swfifobase + outfifodatasize + infifodatasize + fifoptrsize); + + kdebug(("filar(filar_init): outfifo is at 0x%016lx\n", (u_long)outfifo)); + kdebug(("filar(filar_init): infifo is at 0x%016lx\n", (u_long)infifo)); + kdebug(("filar(filar_init): outfifon is at 0x%016lx\n", (u_long)outfifon)); + kdebug(("filar(filar_init): infifon is at 0x%016lx\n", (u_long)infifon)); + + //Initialize the FIFOs + for(rol = 0; rol < MAXROLS; rol++) + { + infifor[rol] = 0; + infifon[rol] = 0; + hwfifow[rol] = 0; + hwfifor[rol] = 0; + hwfifon[rol] = 0; + outfifow[rol] = 0; + outfifon[rol] = 0; + } + + //Allocate the buffers for the REQ and ACK blocks + //The max size of such a block is: 4 channels * (1 size word + 31 data words) = 128 words = 512 bytes + for(card = 0; card < maxcards; card++) + { + reqbuf[card] = (u_int *)__get_free_page(GFP_ATOMIC); + if (!reqbuf[card]) + { + kerror(("filar(init_module): error from __get_free_pages for reqbuf\n")); + return(-FILAR_EFAULT); + } + reqadd[card] = virt_to_bus((void *) reqbuf[card]); + kdebug(("filar(init_module): reqbuf[%d] = 0x%08x (PCI = 0x%08x)\n", card, (u_int)reqbuf[card], reqadd[card])); + + ackbuf[card] = (u_int *)__get_free_page(GFP_ATOMIC); + if (!ackbuf[card]) + { + kerror(("filar(init_module): error from __get_free_pages for ackbuf\n")); + return(-FILAR_EFAULT); + } + ackadd[card] = virt_to_bus((void *) ackbuf[card]); + kdebug(("filar(init_module): ackbuf[%d] = 0x%08x (PCI = 0x%08x)\n", card, (u_int)ackbuf[card], ackadd[card])); + } + + opid = 0; + + filar_cdev = (struct cdev *)cdev_alloc(); //MJ: for 2.6 p55 + filar_cdev->ops = &fops; + result = cdev_add(filar_cdev, major_minor, 1); //MJ: for 2.6 p56 + if (result) + { + kdebug(("filar(filar_init): error from call to cdev_add.\n")); + goto fail6; + } + + + kdebug(("filar(filar_init): driver loaded; major device number = %d\n", MAJOR(major_minor))); + return(0); + + fail6: + page_ptr = virt_to_page(swfifobase); + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + free_pages(swfifobase, order); + + fail5: + pci_unregister_driver(&filarPciDriver); + + fail4: + remove_proc_entry("io_rcc", NULL); + + fail3: + kfree(proc_read_text); + + fail2: + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + + fail1: + return(result); +} + + +/******************************/ +static void filar_cleanup(void) +/******************************/ +{ + u_int card, loop; + struct page *page_ptr; + + cdev_del(filar_cdev); //MJ: for 2.6 p56 + pci_unregister_driver(&filarPciDriver); + page_ptr = virt_to_page(swfifobase); // unreserve all pages used for the S/W FIFOs + + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + + free_pages(swfifobase, order); // free the area + + //Return the buffers for the REQ and ACK blocks + for(card = 0; card < maxcards; card++) + { + free_page((int)reqbuf[card]); + free_page((int)ackbuf[card]); + } + + remove_proc_entry("filar", NULL); + kfree(proc_read_text); + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + + kdebug(("filar(filar_cleanup): driver removed\n")); +} + + +module_init(filar_init); //MJ: for 2.6 p16 +module_exit(filar_cleanup); //MJ: for 2.6 p16 + + +/**********************************************************/ +static int filar_open(struct inode *ino, struct file *filep) +/**********************************************************/ +{ + return(0); +} + + +/*************************************************************/ +static int filar_release(struct inode *ino, struct file *filep) +/*************************************************************/ +{ + opid = 0; + return(0); +} + + +/*************************************************************************************************/ +static int filar_ioctl(struct inode *inode, struct file *file, u_int cmd, unsigned long arg) +/*************************************************************************************************/ +{ + switch (cmd) + { + case OPEN: + { + FILAR_open_t params; + + kdebug(("filar(ioctl,OPEN): called from process %d\n", current->pid)) + if (!opid) + opid = current->pid; + else + { + kerror(("filar(ioctl, OPEN): The FILAR is already used by process %d\n", opid)); + return(-FILAR_USED); + } + + params.size = tsize; + params.physbase = swfifobasephys; + params.outsize = outfifodatasize; + params.insize = infifodatasize; + params.ptrsize = fifoptrsize; + + if (copy_to_user((void *)arg, ¶ms, sizeof(FILAR_open_t)) != 0) + { + kerror(("filar(ioctl, OPEN): error from copy_to_user\n")); + return(-FILAR_EFAULT); + } + return(0); + break; + } + + case CLOSE: + { + kdebug(("filar(ioctl,CLOSE): called from process %d\n", current->pid)) + opid = 0; + + return(0); + break; + } + case CONFIG: + { + FILAR_config_t params; + u_int card, channel, data, rol; + + kdebug(("filar(ioctl,CONFIG): \n")); + if (copy_from_user(¶ms, (void *)arg, sizeof(FILAR_config_t)) !=0 ) + { + kerror(("filar(ioctl,CONFIG): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + + scw = params.scw; + ecw = params.ecw; + + kdebug(("filar(ioctl,CONFIG): params.bswap = %d\n", params.bswap)); + kdebug(("filar(ioctl,CONFIG): params.wswap = %d\n", params.wswap)); + kdebug(("filar(ioctl,CONFIG): params.psize = %d\n", params.psize)); + + for(card = 0; card < maxcards; card++) + { + irq_in[card] = 0; + data = 0; + for(channel = 0; channel < MAXCHANNELS; channel++) + { + served[card][channel] = 0; + rol = (card << 2) + channel; //works only for 4 channels per card + kdebug(("filar(ioctl,CONFIG): params.enable[%d] = %d\n", rol, params.enable[rol])); + if (!params.enable[rol]) + { + data += (1 << (6 * channel + 9)); + channels[card][channel] = 0; + kdebug(("filar(ioctl,CONFIG): channels[%d][%d] = %d\n", card, channel, channels[card][channel])); + } + else + { + channels[card][channel] = 1; + kdebug(("filar(ioctl,CONFIG): channels[%d][%d] = %d\n", card, channel, channels[card][channel])); + } + kdebug(("filar(ioctl,CONFIG): data = 0x%08x\n", data)); + } + data += (params.bswap << 1) + (params.wswap << 2) + (params.psize << 3); + kdebug(("filar(ioctl,CONFIG): Writing 0x%08x to the OCR\n", data)); + filarcard[card].regs->ocr = (data | 0x80); + filarcard[card].regs->ackadd = ackadd[card]; + if (params.interrupt) + filarcard[card].regs->imask |= 0x8; //Enable the ACKBLK_DONE interrupt + } + break; + } + + case RESET: + { + u_int channel, data, card, rol; + + if (copy_from_user(&card, (void *)arg, sizeof(int)) !=0 ) + { + kerror(("filar(ioctl,RESET): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + + irq_in[card] = 0; + for(channel = 0; channel < MAXCHANNELS; channel++) + served[card][channel] = 0; + + if (card <= maxcards) + { + kdebug(("filar(ioctl,RESET): Resetting card %d\n", card)); + data = filarcard[card].regs->ocr; + data |= 0x1; + filarcard[card].regs->ocr = data; + // Delay for at least one us + udelay(10); + data &= 0xfffffffe; + filarcard[card].regs->ocr = data; + + //reset the FIFOs + for(channel = 0; channel < MAXCHANNELS; channel++) + { + rol = (card << 2) + channel; + infifor[rol] = 0; + infifon[rol] = 0; + outfifow[rol] = 0; + outfifon[rol] = 0; + hwfifow[rol] = 0; + hwfifor[rol] = 0; + hwfifon[rol] = 0; + } + } + else + { + kerror(("filar(ioctl,RESET): This card is unknown\n")); + return(-FILAR_NOCARD); + } + + return(0); + break; + } + + case LINKRESET: + { + u_int waited, rol, data, card, channel; + + if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0 ) + { + kerror(("filar(ioctl,LINKRESET): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + + card = rol >> 2; + if (card <= maxcards) + { + channel = rol & 0x3; + kdebug(("filar(ioctl,LINKRESET): Resetting card %d, channel %d\n", card, channel)); + served[card][channel] = 0; + + // Set the URESET bit + data = 0x1 << (8 + channel * 6); + filarcard[card].regs->ocr |= data; + + // Delay for at least 30 ms + mdelay(30); + + // Now wait for LDOWN to come up again + data = 0x1 << (16 + channel * 4); + waited = 0; + while(filarcard[card].regs->osr & data) + { + waited++; + if (waited > 10000) + { + kerror(("filar(ioctl,LINKRESET): channel %d of card %d does not come up again\n", channel, card)); + // Reset the URESET bit + data = ~(0x1 << (8 + channel * 6)); + filarcard[card].regs->ocr &= data; + return(-FILAR_STUCK); + } + } + + // Reset the URESET bit + data = ~(0x1 << (8 + channel * 6)); + filarcard[card].regs->ocr &= data; + } + else + { + kerror(("filar(ioctl,LINKRESET): This card is unknown\n")); + return(-FILAR_NOCARD); + } + + return(0); + break; + } + + case LINKSTATUS: + { + u_int isup, data, card, rol, channel; + + if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0 ) + { + kerror(("filar(ioctl, RESET): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + + card = rol >> 2; + if (card <= maxcards) + { + channel = rol & 0x3; + data = 0x1 << (16 + channel * 4); + if (filarcard[card].regs->osr & data) + isup = 0; + else + isup = 1; + + if (copy_to_user((void *)arg, &isup, sizeof(int)) != 0) + { + kerror(("filar(ioctl, LINKSTATUS): error from copy_to_user\n")); + return(-FILAR_EFAULT); + } + } + else + { + kerror(("filar(ioctl, LINKSTATUS): This card is unknown\n")); + return(-FILAR_NOCARD); + } + + return(0); + break; + } + + case INFO: + { + FILAR_info_t info; + u_int rol, card, data; + + //Initialise the array to 0 + for(rol = 0; rol < MAXROLS; rol++) + { + info.channels[rol] = 0; + info.enabled[rol] = 0; + } + info.nchannels = 0; + + for(card = 0; card < maxcards; card++) + { + data = filarcard[card].regs->osr; + kdebug(("filar(ioctl, INFO): 0x%08x read from OSR\n", data)); + if (data & 0x00080000) info.channels[(card << 2)] = 0; + else {info.channels[(card << 2)] = 1; info.nchannels++;} + + if (data & 0x00800000) info.channels[(card << 2) + 1] = 0; + else {info.channels[(card << 2) + 1] = 1; info.nchannels++;} + + if (data & 0x08000000) info.channels[(card << 2) + 2] = 0; + else {info.channels[(card << 2) + 2] = 1; info.nchannels++;} + + if (data & 0x80000000) info.channels[(card << 2) + 3] = 0; + else {info.channels[(card << 2) + 3] = 1; info.nchannels++;} + + data = filarcard[card].regs->ocr; + kdebug(("filar(ioctl, INFO): 0x%08x read from OCR\n", data)); + + if (data & 0x00000200) info.enabled[(card << 2)] = 0; else info.enabled[(card << 2)] = 1; + if (data & 0x00008000) info.enabled[(card << 2) + 1] = 0; else info.enabled[(card << 2) + 1] = 1; + if (data & 0x00200000) info.enabled[(card << 2) + 2] = 0; else info.enabled[(card << 2) + 2] = 1; + if (data & 0x08000000) info.enabled[(card << 2) + 3] = 0; else info.enabled[(card << 2) + 3] = 1; + } + + if (copy_to_user((void *)arg, &info, sizeof(FILAR_info_t)) != 0) + { + kerror(("filar(ioctl, INFO): error from copy_to_user\n")); + return(-FILAR_EFAULT); + } + return(0); + break; + } + + case FIFOIN: + { + u_int ret, card, channel, rol; + unsigned long flags; + + if (copy_from_user(&rol, (void *)arg, sizeof(int)) !=0) + { + kerror(("filar(ioctl,FIFOIN): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + card = rol >> 2; + channel = rol & 0x3; + kdebug(("filar(ioctl,FIFOIN): channel = %d card = %d\n", channel, card)); + + //We have to make sure that this code does not get interrupted. Otherwhise there will be + //inconsistencies in the REQ block which lead to incorrect PCI addresses being written to the FILAR + save_flags(flags); + cli(); + + ret = req_block(card, 0); + if (ret) + { + kerror(("filar(ioctl,FIFOIN): error from req_block\n")); + return(-FILAR_EREQ); + } + + restore_flags(flags); + return(0); + break; + } + + case FLUSH: + { + flush_cards(); + break; + } + } + return(0); +} + + +/****************************************************************************************************/ +static int filar_write_procmem(struct file *file, const char *buffer, unsigned long count, void *data) +/****************************************************************************************************/ +{ + struct filar_proc_data_t *fb_data = (struct filar_proc_data_t *)data; + int len; + u_int card; + + kdebug(("filar(filar_write_procmem): filar_write_procmem called\n")); + + if(count > 99) + len = 99; + else + len = count; + + if (copy_from_user(fb_data->value, buffer, len)) + { + kerror(("filar(filar_write_procmem): error from copy_from_user\n")); + return(-FILAR_EFAULT); + } + + kdebug(("filar(filar_write_procmem): len = %d\n", len)); + fb_data->value[len - 1] = '\0'; + kdebug(("filar(filar_write_procmem): text passed = %s\n", fb_data->value)); + + if (!strcmp(fb_data->value, "debug")) + { + debug = 1; + kdebug(("filar(filar_write_procmem): debugging enabled\n")); + } + + if (!strcmp(fb_data->value, "nodebug")) + { + kdebug(("filar(filar_write_procmem): debugging disabled\n")); + debug = 0; + } + + if (!strcmp(fb_data->value, "disable")) + { + for(card = 0; card < maxcards; card++) + filarcard[card].regs->ocr |= 0x08208200; + kdebug(("filar(filar_write_procmem): All channels disabled\n")); + } + + if (!strcmp(fb_data->value, "reset")) + { + reset_cards(1); + kdebug(("filar(filar_write_procmem): All cards reset\n")); + } + + if (!strcmp(fb_data->value, "elog")) + { + kdebug(("filar(filar_write_procmem): Error logging enabled\n")) + errorlog = 1; + } + + if (!strcmp(fb_data->value, "noelog")) + { + kdebug(("filar(filar_write_procmem): Error logging disabled\n")) + errorlog = 0; + } + + if (!strcmp(fb_data->value, "flush")) + { + flush_cards(); + kdebug(("filar(filar_write_procmem): All cards flushed\n")); + } + + if (!strcmp(fb_data->value, "dec")) + { + kdebug(("filar(filar_write_procmem): Use count decremented\n")); + } + + if (!strcmp(fb_data->value, "inc")) + { + kdebug(("filar(filar_write_procmem): Use count incremented\n")); + } + + return len; +} + + +/***************************************************************************************************/ +static int filar_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data) +/***************************************************************************************************/ +{ + u_int fdata, rol, ret, card, ocr[MAXROLS], osr[MAXROLS], osr2[MAXROLS], imask[MAXROLS], efstatr[MAXROLS], efstata[MAXROLS], value, value2, channel; + int loop, nchars = 0; + static int len = 0; + + kdebug(("filar(filar_read_procmem): Called with buf = 0x%08x\n", (u_int)buf)); + kdebug(("filar(filar_read_procmem): Called with *start = 0x%08x\n", (u_int)*start)); + kdebug(("filar(filar_read_procmem): Called with offset = %d\n", (u_int)offset)); + kdebug(("filar(filar_read_procmem): Called with count = %d\n", count)); + + if (offset == 0) + { + kdebug(("exp(exp_read_procmem): Creating text....\n")); + len = 0; + len += sprintf(proc_read_text + len, "\n\n\nFILAR driver (DMA protocol) for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG); + + if (opid) + len += sprintf(proc_read_text + len, "The drivers is currently used by PID %d\n", opid); + len += sprintf(proc_read_text + len, "=================================================================\n"); + len += sprintf(proc_read_text + len, "Card|IRQ| ACK IRQ|revision|PCI MEM addr.| page size|wswap|bswap|DMA|temperature|ACK address|REQ address|IRQs received\n"); + len += sprintf(proc_read_text + len, "----|---|--------|--------|-------------|----------|-----|-----|---|-----------|-----------|-----------|-------------\n"); + + //Read the registers + for(card = 0; card < maxcards; card++) + { + ocr[card] = filarcard[card].regs->ocr; + osr[card] = filarcard[card].regs->osr; + osr2[card] = filarcard[card].regs->osr; + imask[card] = filarcard[card].regs->imask; + efstatr[card] = filarcard[card].regs->efstatr; + efstata[card] = filarcard[card].regs->efstata; + } + + for(card = 0; card < maxcards; card++) + { + len += sprintf(proc_read_text + len, " %d|", card); + len += sprintf(proc_read_text + len, "%3d|", filar_irq_line[card]); + len += sprintf(proc_read_text + len, "%s|", (imask[card] & 0x8)?" enabled":"disabled"); + len += sprintf(proc_read_text + len, " 0x%02x|", pci_revision[card]); + len += sprintf(proc_read_text + len, " 0x%08x|", pci_memaddr[card]); + value = (ocr[card] >> 3) & 0x7; + if (value == 0) len += sprintf(proc_read_text + len, " 256 bytes|"); + if (value == 1) len += sprintf(proc_read_text + len, " 1 Kbyte|"); + if (value == 2) len += sprintf(proc_read_text + len, " 2 Kbytes|"); + if (value == 3) len += sprintf(proc_read_text + len, " 4 Kbytes|"); + if (value == 4) len += sprintf(proc_read_text + len, " 16 Kbytes|"); + if (value == 5) len += sprintf(proc_read_text + len, " 64 Kbytes|"); + if (value == 6) len += sprintf(proc_read_text + len, "256 Kbytes|"); + if (value == 7) len += sprintf(proc_read_text + len, "4 MB - 8 B|"); + len += sprintf(proc_read_text + len, " %s|", (ocr[card] & 0x4)?"yes":" no"); + len += sprintf(proc_read_text + len, " %s|", (ocr[card] & 0x2)?"yes":" no"); + len += sprintf(proc_read_text + len, "%s|", (ocr[card] & 0x80)?"yes":" no"); + len += sprintf(proc_read_text + len, " %3d deg. C|", (osr[card] >>8) & 0xff); + fdata = filarcard[card].regs->ackadd; + len += sprintf(proc_read_text + len, " 0x%08x|", fdata); + fdata = filarcard[card].regs->reqadd; + len += sprintf(proc_read_text + len, " 0x%08x|", fdata); + fdata = filarcard[card].regs->blkctl & 0xff; + len += sprintf(proc_read_text + len, "%13d\n", irq_in[card]); + } + + for(card = 0; card < maxcards; card++) + { + len += sprintf(proc_read_text + len, "\nCard %d:\n", card); + len += sprintf(proc_read_text + len, "=======\n"); + len += sprintf(proc_read_text + len, " | | | |free | | | | |free | |fragments\n"); + len += sprintf(proc_read_text + len, " | | |entries in|entries in| | | |entries in|entries in|fragments|per \n"); + len += sprintf(proc_read_text + len, "Channel|present|enabled|ACK FIFO |REQ FIFO |LDOWN|Overflow| XOFF|OUT FIFO |IN FIFO |served |interrupt\n"); + len += sprintf(proc_read_text + len, "-------|-------|-------|----------|----------|-----|--------|-------|----------|----------|---------|---------\n"); + + for(channel = 0; channel < MAXCHANNELS; channel++) + { + len += sprintf(proc_read_text + len, " %d|", channel); + + value = osr[card] & (1 << (19 + channel * 4)); + len += sprintf(proc_read_text + len, " %s|", value?" no":"yes"); + + value = ocr[card] & (1 << (9 + channel * 6)); + len += sprintf(proc_read_text + len, " %s|", value?" no":"yes"); + + value = (efstata[card] >> (channel * 8)) & 0xff; + len += sprintf(proc_read_text + len, " %2d|", value); + + value = (efstatr[card] >> (channel * 8)) & 0xff; + len += sprintf(proc_read_text + len, " %2d|", value); + + value = osr[card] & (1 << (16 + channel * 4)); + len += sprintf(proc_read_text + len, " %s|", value?"yes":" no"); + + value = osr[card] & (1 << (17 + channel * 4)); + len += sprintf(proc_read_text + len, " %s|", value?"yes":" no"); + + value = osr[card] & (1 << (18 + channel * 4)); + value2 = osr2[card] & (1 << (18 + channel * 4)); + if (!value && !value2) + len += sprintf(proc_read_text + len, " is off|"); + else if (value && value2) + len += sprintf(proc_read_text + len, " is on|"); + else if (value && !value2) + len += sprintf(proc_read_text + len, " was on|"); + else + len += sprintf(proc_read_text + len, "went on|"); + + rol = (card << 2) + channel; + ret = out_fifo_level(rol, &value); + len += sprintf(proc_read_text + len, " %4d|", MAXOUTFIFO - value); + + ret = in_fifo_level(rol, &value); + len += sprintf(proc_read_text + len, " %4d|", value); + + len += sprintf(proc_read_text + len, " %8d|", served[card][channel]); + + if (irq_in[card]) + len += sprintf(proc_read_text + len, "%f\n", (float)served[card][channel] / (float)irq_in[card]); + else + len += sprintf(proc_read_text + len, "undefined\n"); + } + } + + len += sprintf(proc_read_text + len, " \n"); + len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/filar', executed as root,\n"); + len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n"); + len += sprintf(proc_read_text + len, "debug -> Enable debugging\n"); + len += sprintf(proc_read_text + len, "nodebug -> Disable debugging\n"); + len += sprintf(proc_read_text + len, "elog -> Log errors to /var/log/message\n"); + len += sprintf(proc_read_text + len, "noelog -> Do not log errors to /var/log/message\n"); + len += sprintf(proc_read_text + len, "disable -> Disable all FILAR channels\n"); + len += sprintf(proc_read_text + len, "reset -> Reset all FILAR channels\n"); + len += sprintf(proc_read_text + len, "flush -> Flush all FILAR channels\n"); + len += sprintf(proc_read_text + len, "dec -> Decrement use count\n"); + len += sprintf(proc_read_text + len, "inc -> Increment use count\n"); + } + kdebug(("filar(filar_read_procmem): number of characters in text buffer = %d\n", len)); + + if (count < (len - offset)) + nchars = count; + else + nchars = len - offset; + kdebug(("filar(filar_read_procmem): min nchars = %d\n", nchars)); + + if (nchars > 0) + { + for (loop = 0; loop < nchars; loop++) + buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop]; + *start = buf + (offset & (PAGE_SIZE - 1)); + } + else + { + nchars = 0; + *eof = 1; + } + + kdebug(("filar(filar_read_procmem): returning *start = 0x%08x\n", (u_int)*start)); + kdebug(("filar(filar_read_procmem): returning nchars = %d\n", nchars)); + return(nchars); +} + + + + +/*************************************************************************/ +static irqreturn_t filar_irq_handler (int irq, void *dev_id, struct pt_regs *regs) +/*************************************************************************/ +{ + u_int card; + + // Note: the FILAR card(s) may have to share an interrupt line with other PCI devices + // It is therefore important to exit quickly if we are not concerned with an interrupt + + for(card = 0; card < maxcards; card++) + { + if (filar_irq_line[card] == (u_int)irq) + { + if (filarcard[card].regs->osr & 0x8) + { + irq_in[card]++; + read_ack_and_write_out(card); + return(IRQ_HANDLED); //MJ: for 2.6 see p273 + } + } + } + return(IRQ_NONE); +} + +/**********************************************************************************/ +static int __devinit filarProbe(struct pci_dev *dev, const struct pci_device_id *id) //MJ: for _devinit see p31 +/**********************************************************************************/ +{ + int i, card; + u_long flags; + u_int size; + + kdebug(("filar(filarProbe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device)); + kdebug(("filar(filarProbe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn)); + + /* Find a new "slot" for the card */ + card = -1; + for (i = 0; i < MAXCARDS; i++) + { + if (filarcard[i].pciDevice == NULL) + { + card = i; + filarcard[card].pciDevice = dev; + break; + } + } + + if (card == -1) + { + kerror(("filar(filarProbe): Could not find space in the filarcard array for this card.")); + return(-FILAR_TOOMANYCARDS); + } + + flags = pci_resource_flags(dev, BAR0); //MJ: See p317 + if ((flags & IORESOURCE_MEM) != 0) + { + filarcard[card].pci_memaddr = pci_resource_start(dev, BAR0) & PCI_BASE_ADDRESS_MEM_MASK; //MJ: See p317 + size = pci_resource_end(dev, BAR0) - pci_resource_start(dev, BAR0); //MJ: See p317 + kdebug(("filar(filarProbe): Mapping %d bytes at PCI address 0x%08x\n", size, filarcard[card].pci_memaddr)); + + filarcard[card].regs = (T_filar_regs *)ioremap(filarcard[card].pci_memaddr, size); + if (filarcard[card].regs == NULL) + { + kerror(("filar(filarProbe): error from ioremap for filarcard[%d].regs\n", card)); + filarcard[card].pciDevice = NULL; + return(-FILAR_IOREMAP); + } + kdebug(("filar(filarProbe): filarcard[%d].regs is at 0x%016lx\n", card, (u_long)filarcard[card].regs)); + } + else + { + kerror(("filar(filarProbe): Bar 0 is not a memory resource")); + return(-FILAR_EIO); + } + + // get revision directly from the Filar + pci_read_config_byte(filarcard[card].pciDevice, PCI_REVISION_ID, &filarcard[card].pci_revision); + kdebug(("filar(filarProbe): revision = %x \n", filarcard[card].pci_revision)); + if (filarcard[card].pci_revision < MINREV) + { + kerror(("filar(filarProbe): Illegal Filar Revision\n")); + return(-FILAR_ILLREV); + } + + pci_read_config_byte(filarcard[card].pciDevice, PCI_INTERRUPT_LINE, &filarcard[card].irq_line); + kdebug(("filar(filarProbe): interrupt line = %d \n", filarcard[card].irq_line)); + + //Several FILAR cards may use the same interrupt but we want to install the driver only once + if (irqlist[filarcard[card].irq_line] == 0) + { + if(request_irq(filarcard[card].irq_line, filar_irq_handler, SA_SHIRQ, "filar", dev)) + { + kdebug(("filar(filarProbe): request_irq failed on IRQ = %d\n", filarcard[card].irq_line)); + return(-FILAR_REQIRQ); + } + irqlist[filarcard[card].irq_line]++; + kdebug(("filar(filarProbe): Interrupt %d registered\n", filarcard[card].irq_line)); + } + + maxcards++; + return(0); +} + + +/******************************************/ +static void filarRemove(struct pci_dev *dev) +/******************************************/ +{ + int i, card; + + /* Find the "slot" of the card */ + card = -1; + for (i = 0; i < MAXCARDS; i++) + { + if (filarcard[i].pciDevice == dev) + { + card = i; + filarcard[i].pciDevice = 0; + break; + } + } + + if (card == -1) + { + kerror(("filar(filarRemove): Could not find device to remove.")); + return; + } + + if (--irqlist[filarcard[card].irq_line] == 0) + { + kerror(("filar(filarRemove): removing handler for interrupt %d", filarcard[card].irq_line)); + free_irq(filarcard[card].irq_line, dev); + } + + iounmap((void *)filarcard[card].regs); + kerror(("filar(filarRemove): Card %d removed", card)); + maxcards--; +} +//------------------ +// Service functions +//------------------ + + +/****************************************/ +static int hw_fifo_push(int rol, int data) +/****************************************/ +{ + if (hwfifon[rol] == MAXHWFIFO) + { + kerror(("filar(hw_fifo_push): The HW FIFO is full\n")); + return(-1); + } + hwfifo[rol][hwfifow[rol]] = data; + hwfifow[rol]++; + if (hwfifow[rol] == MAXHWFIFO) + hwfifow[rol] = 0; + + hwfifon[rol]++; + return(0); +} + + +/****************************************/ +static int hw_fifo_pop(int rol, int *data) +/****************************************/ +{ + if (hwfifon[rol] == 0) + { + kerror(("filar(hw_fifo_pop): The HW FIFO is empty\n")); + return(-1); + } + *data = hwfifo[rol][hwfifor[rol]]; + hwfifor[rol]++; + if (hwfifor[rol] == MAXHWFIFO) + hwfifor[rol] = 0; + + hwfifon[rol]--; + return(0); +} + + +/****************************************/ +static int in_fifo_pop(int rol, int *data) +/****************************************/ +{ + if (infifon[rol] == 0) + { + kdebug(("filar(in_fifo_pop): The IN FIFO is empty\n")); + return(-1); + } + *data = infifo[IN_INDEX(rol, infifor[rol])]; + + infifor[rol]++; + + if (infifor[rol] > (MAXINFIFO - 1)) + infifor[rol] = 0; + + infifon[rol]--; + + return(0); +} + + +/*******************************************/ +static int in_fifo_level(int rol, int *nfree) +/*******************************************/ +{ + *nfree = MAXINFIFO - infifon[rol]; + return(0); +} + + +/******************************************************************************************/ +static int out_fifo_push(int rol, int pciaddr, int fragsize, int fragstat, int scw, int ecw) +/******************************************************************************************/ +{ + int oo; + + oo = OUT_INDEX(rol, outfifow[rol]); + kdebug(("filar(out_fifo_push): oo = %d\n", oo)); + + if (outfifon[rol] == MAXOUTFIFO) + { + kerror(("filar(out_fifo_push): The OUT FIFO is full\n")); + return(-1); + } + + outfifo[oo] = pciaddr; + outfifo[oo + 1] = fragsize; + outfifo[oo + 2] = fragstat; + outfifo[oo + 3] = scw; + outfifo[oo + 4] = ecw; + + outfifow[rol]++; + if (outfifow[rol] == MAXOUTFIFO) + outfifow[rol] = 0; + + outfifon[rol]++; + return(0); +} + + +/********************************************/ +static int out_fifo_level(int rol, int *nfree) +/********************************************/ +{ + *nfree = MAXOUTFIFO - outfifon[rol]; + return(0); +} + + +/*******************************************/ +static int req_block(int card, int ackenable) +/*******************************************/ +{ + u_int rol, ret, reqlen, reqfifo, loop, nreqs, navail, nmin, channel, pciaddr, *reqptr; + + reqlen = 0; + reqptr = reqbuf[card]; + reqfifo = filarcard[card].regs->efstatr; + for(channel = 0; channel < MAXCHANNELS; channel++) + { + rol = (card << 2) + channel; + nreqs = (reqfifo >> (8 * channel)) & 0xff; + navail = infifon[rol]; + if (nreqs < navail) + nmin = nreqs; + else + nmin = navail; + + kdebug(("filar(req_block): nreqs=%d navail=%d\n", nreqs, navail)); + *reqptr++ = nmin; + reqlen++; + kdebug(("filar(req_block): Wrote nmin=%d to REQ block for channel=%d\n", nmin, channel)); + for(loop = 0; loop < nmin; loop++) + { + ret = in_fifo_pop(rol, &pciaddr); + if (ret) + { + kerror(("filar(req_block): error from in_fifo_pop\n")); + return(-1); + } + + ret = hw_fifo_push(rol, pciaddr); + if (ret) + { + kerror(("filar(req_block): error from hw_fifo_push\n")); + return(-1); + } + *reqptr++ = pciaddr; + reqlen++; + kdebug(("filar(req_block): Wrote PCI address 0x%08x to REQ block\n", pciaddr)); + } + } + + if (ackenable) + filarcard[card].regs->blkctl = 0x60000000 | reqlen; + else + filarcard[card].regs->blkctl = 0x20000000 | reqlen; + + kdebug(("filar(req_block): Wrote reqlen=%d to FILAR\n", reqlen)); + filarcard[card].regs->reqadd = reqadd[card]; + kdebug(("filar(req_block): Wrote reqadd=0x%08x to FILAR card=%d\n", reqadd[card], card)); + + //Wait until the card has read the REQ block + loop = 0; + while(1) + { + if (++loop == 1000) + { + kerror(("filar(req_block) FILAR refuses to read REQ block\n")); + return(-1); + } + if (filarcard[card].regs->osr & 0x4) break; + } + + return(0); +} + + +/***************************************************/ +static void filar_vmaOpen(struct vm_area_struct *vma) +/***************************************************/ +{ + kdebug(("filar(filar_vmaOpen): Called\n")); +} + + +/****************************************************/ +static void filar_vmaClose(struct vm_area_struct *vma) +/****************************************************/ +{ + kdebug(("filar(filar_vmaClose): Virtual address = 0x%016lx\n",(u_long)vma->vm_start)); + kdebug(("filar(filar_vmaClose): mmap released\n")); +} + + +/******************************************************************/ +static int filar_mmap(struct file *file, struct vm_area_struct *vma) +/******************************************************************/ +{ + unsigned long offset, size; + + kdebug(("filar(filar_mmap): function called\n")); + + offset = vma->vm_pgoff << PAGE_SHIFT; + + size = vma->vm_end - vma->vm_start; + + kdebug(("filar(filar_mmap): offset = 0x%08x\n",(u_int)offset)); + kdebug(("filar(filar_mmap): size = 0x%08x\n",(u_int)size)); + + if (offset & ~PAGE_MASK) + { + kerror(("filar(filar_mmap): offset not aligned: %ld\n", offset)); + return -ENXIO; + } + + // we only support shared mappings. "Copy on write" mappings are + // rejected here. A shared mapping that is writeable must have the + // shared flag set. + if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) + { + kerror(("filar(filar_mmap): writeable mappings must be shared, rejecting\n")); + return(-EINVAL); + } + + vma->vm_flags |= VM_RESERVED; + + // we do not want to have this area swapped out, lock it + vma->vm_flags |= VM_LOCKED; + + // we create a mapping between the physical pages and the virtual + // addresses of the application with remap_page_range. + // enter pages into mapping of application + kdebug(("filar(filar_mmap): Parameters of remap_page_range()\n")); + kdebug(("filar(filar_mmap): Virtual address = 0x%08x\n",(u_int)vma->vm_start)); + kdebug(("filar(filar_mmap): Physical address = 0x%08x\n",(u_int)offset)); + kdebug(("filar(filar_mmap): Size = 0x%08x\n",(u_int)size)); + if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot)) + { + kerror(("filar(filar_mmap): remap page range failed\n")); + return -ENXIO; + } + + vma->vm_ops = &filar_vm_ops; + + kdebug(("filar(filar_mmap): function done\n")); + return(0); +} + + +/******************************************/ +static void read_ack_and_write_out(int card) +/******************************************/ +{ + u_int rol, ret, data, scw = 0, ecw = 0, loop, nacks, channel, fragstat, pciaddr, *ackptr; + + //---> Part 1: process the ACK blocks + ackptr = ackbuf[card]; + for(channel = 0; channel < MAXCHANNELS; channel++) + { + rol = (card << 2) + channel; + nacks = *ackptr++; + served[card][channel] += nacks; + kdebug(("filar(read_ack_and_write_out): card=%d channel=%d nacks=%d\n", card, channel, nacks)); + for(loop = 0; loop < nacks; loop++) + { + data = *ackptr++; + kdebug(("filar(read_ack_and_write_out): Analyzing 0x%08x\n", data)); + fragstat = 0; + + if (data & 0x50000000) + { + scw = *ackptr++; + ecw = *ackptr++; + } + + if (data & 0x80000000) + { + fragstat |= NO_SCW; + kerror(("filar(read_ack_and_write_out): NO_SCW")); + } + else + { + if (data & 0x40000000) + { + fragstat |= BAD_SCW; + kerror(("filar(read_ack_and_write_out): BAD_SCW")); + kerror(("filar(read_ack_and_write_out): Start Control Word = 0x%08x\n", scw)); + if (scw & 0x1) + { + kerror(("filar(read_ack_and_write_out): Data transmission error in SCW\n")); + fragstat |= SCW_DTE; + } + if (scw & 0x2) + { + kerror(("filar(read_ack_and_write_out): Control word transmission error in SCW\n")); + fragstat |= SCW_CTE; + } + } + } + + if (data & 0x20000000) + { + fragstat |= NO_ECW; + kerror(("filar(read_ack_and_write_out): NO_ECW")); + } + else + { + if (data & 0x10000000) + { + fragstat |= BAD_ECW; + kerror(("filar(read_ack_and_write_out): BAD_ECW")); + kerror(("filar(read_ack_and_write_out): End Control Word = 0x%08x\n", ecw)); + + if (ecw & 0x1) + { + kerror(("filar(read_ack_and_write_out): Data transmission error in ECW\n")); + fragstat |= ECW_DTE; + } + + if (ecw & 0x2) + { + kerror(("filar(read_ack_and_write_out): Control word transmission error in ECW\n")); + fragstat |= ECW_CTE; + } + } + } + + ret = hw_fifo_pop(rol, &pciaddr); + if (ret) + { + kerror(("filar(read_ack_and_write_out): error from hw_fifo_pop\n")); + blow_fuse(); + return; + } + kdebug(("filar(read_ack_and_write_out): Read PCI address 0x%08x from HW FIFO\n", pciaddr)); + + ret = out_fifo_push(rol, pciaddr, data & 0x000fffff, fragstat, 0 ,0); + if (ret) + { + kerror(("filar(read_ack_and_write_out): error from out_fifo_push\n")); + blow_fuse(); + return; + } + } + } + + //---> Part 2: write REQ blocks + ret = req_block(card, 1); + if (ret) + kerror(("filar(read_ack_and_write_out): error from req_block\n")); + + kdebug(("filar(read_ack_and_write_out): End of ISR\n")); +} + + +/*************************/ +static void blow_fuse(void) +/*************************/ +{ + u_int card; + + for(card = 0; card < maxcards; card++) + filarcard[card].regs->imask = 0; + kerror(("filar(blow_fuse): fuse blown\n")); +} + + +/*******************************/ +static void reset_cards(int mode) +/*******************************/ +{ + u_int card, rol, channel, data; + + kdebug(("filar(reset_cards): maxcards = %d mode = %d\n", maxcards, mode)); + for(card = 0; card < maxcards; card++) + { + data = filarcard[card].regs->ocr; + data |= 0x1; + filarcard[card].regs->ocr = data; + // Delay for at least one us + udelay(10); + data &= 0xfffffffe; + filarcard[card].regs->ocr = data; + irq_in[card] = 0; + //reset the FIFOs + if (mode) + { + for(channel = 0; channel < MAXCHANNELS; channel++) + { + served[card][channel] = 0; + rol = (card << 2) + channel; + infifor[rol] = 0; + infifon[rol] = 0; + outfifow[rol] = 0; + outfifon[rol] = 0; + hwfifow[rol] = 0; + hwfifor[rol] = 0; + hwfifon[rol] = 0; + } + } + } + kdebug(("filar(reset_cards): Done\n")); +} + + +/***************************/ +static void flush_cards(void) +/***************************/ +{ + u_int loop, card, channel; + unsigned long flags; + + //We have to make sure that this code does not get interrupted. + //Otherwhise there can be a race condition. + save_flags(flags); + cli(); + + for(card = 0; card < maxcards; card++) + { + filarcard[card].regs->blkctl = 0x80000000; + loop = 0; + //MJ: should I sleep a bit to wait for ACKBLK_DONE = 0 ? + while(1) + { + if (++loop == 1000) + { + kerror(("filar(flush_cards): FILAR refuses to send ACK block\n")); + return; + } + if (filarcard[card].regs->osr & 0x8) break; + } + read_ack_and_write_out(card); + } + + for(channel = 0; channel < MAXCHANNELS; channel++) + { + kdebug(("filar(flush_cards): There are %d events in the OUT FIFO of channel %d\n", outfifon[channel], channel)); + } + + restore_flags(flags); +} + diff --git a/cmt/io_rcc.c b/cmt/io_rcc.c new file mode 100644 index 0000000000000000000000000000000000000000..8775409b24014d1f4995fb82910edc3c3f8f3774 --- /dev/null +++ b/cmt/io_rcc.c @@ -0,0 +1,785 @@ +/************************************************************************/ +/* */ +/* File: io_rcc.c */ +/* */ +/* IO RCC driver */ +/* */ +/* 7. Jun. 02 MAJO created */ +/* */ +/************ C 2005 - The software with that certain something *********/ + +/************************************************************************/ +/*NOTES: */ +/*- This driver should work on kernels from 2.6.9 onwards */ +/************************************************************************/ + +//MJ: for 2.6//#include <linux/config.h> +//MJ: for 2.6//#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS) +//MJ: for 2.6// #define MODVERSIONS +//MJ: for 2.6//#endif +//MJ-SMP:#ifdef CONFIG_SMP +//MJ-SMP: #define __SMP__ +//MJ-SMP:#endif + +//MJ: for 2.6//#if defined(MODVERSIONS) +//MJ: for 2.6// #include <linux/modversions.h> +//MJ: for 2.6//#endif +#include <linux/init.h> //MJ: for 2.6, p30 +#include <linux/module.h> //p30 +#include <linux/moduleparam.h> //MJ: for 2.6, p30 +#include <linux/kernel.h> //e.g. for printk +#include <linux/stat.h> //MJ: for 2.6, e.g. for module_param +#include <linux/pci.h> //e.g. for pci_find_device +#include <linux/errno.h> //e.g. for EFAULT +#include <linux/fs.h> //e.g. for register_chrdev +#include <linux/proc_fs.h> //e.g. for create_proc_entry +#include <linux/mm.h> //e.g. for vm_operations_struct +//MJ: for 2.6//#include <linux/string.h> +//MJ: obsolete?//#include <linux/vmalloc.h> +//MJ: for 2.6//#include <linux/mman.h> +//MJ: for 2.6//#include <linux/wrapper.h> +#include <linux/slab.h> //e.g. for kmalloc +#include <linux/cdev.h> //e.g. for cdev_alloc +#include <asm/uaccess.h> //e.g. for copy_from_user +#include <asm/io.h> //e.g. for inl +#include <asm/system.h> //e.g. for wmb +#include "io_rcc/io_rcc_driver.h" + + +/*********************************** Notes **************************************/ +/* */ +/* In 2.6 driver parameters (such a "debug") can be accessed via /sys/modlue/ */ +/* Therefore it may be possible to remove them from the writable /proc file */ +/* */ +/* Rubini (p83) recommends to replace /proc by /sysfs */ +/* The seq_file service could be used for proc_read */ +/* and /sysfs for proc_write. The details need to be understood */ +/* (and can be tricky) */ +/* */ +/* It is not clear if SLC4 will provide support for the devfs. If though the */ +/* creation of nodes could be simplified. Seems devfs was replaced by udev. */ +/* */ +/* The book of Rubini is based on 2.6.10. SLC4 is 2.6.9 */ +/********************************************************************************/ + + +/*********/ +/*Globals*/ +/*********/ +static int debug = 0; +static u_int board_type; +static char *proc_read_text; +static struct proc_dir_entry *io_rcc_file; +static pci_devices_t pcidev[IO_MAX_PCI]; +struct io_proc_data_t io_proc_data; +static dev_t major_minor; +static struct cdev *io_rcc_cdev; + + +/***************************************************************/ +/* Use /sbin/modinfo <module name> to extract this information */ +/***************************************************************/ +MODULE_DESCRIPTION("PCI IO driver"); +MODULE_AUTHOR("Markus Joos, CERN/PH"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("Private: Contact markus.joos@cern.ch"); +module_param (debug, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(debug, "1 = enable debugging 0 = disable debugging"); + + +//MJ: Not actually required. Just for kdebug +struct vm_operations_struct io_rcc_vm_ops = +{ + .close = io_rcc_vmaClose, + .open = io_rcc_vmaOpen, //MJ: Note the comma at the end of the list! +}; + + +static struct file_operations fops = +{ + .owner = THIS_MODULE, + .ioctl = io_rcc_ioctl, + .open = io_rcc_open, + .mmap = io_rcc_mmap, + .release = io_rcc_release, +}; + + +/*****************************/ +/* Standard driver functions */ +/*****************************/ + +/*************************/ +static int io_rcc_init(void) //MJ: Declared "static" because we do not want to export it +/*************************/ +{ + int loop, ecode = 0; + u_char reg; + + //MJ: for 2.6//SET_MODULE_OWNER(&fops); + + // Read the board identification (works only on VP110/31x). MJ: Do we still need this? + outb(BID1, CMOSA); + reg = inb(CMOSD); + kdebug(("io_rcc(io_rcc_init): BID1 = 0x%02x\n", reg)); + + if (!(reg & 0x80)) + { + kdebug(("io_rcc(io_rcc_init): Unable to determine board type\n")); + board_type = VP_UNKNOWN; + } + else + { + outb(BID2, CMOSA); + reg = inb(CMOSD); + kdebug(("io_rcc(io_rcc_init): BID2 = 0x%02x\n", reg)); + + reg &= 0x1f; // Mask board ID bits + board_type = VP_UNKNOWN; + if (reg == 2) board_type = VP_PSE; // VP-PSE + else if (reg == 3) board_type = VP_PSE; // VP-PSE + else if (reg == 4) board_type = VP_PSE; // VP-PSE + else if (reg == 5) board_type = VP_PMC; // VP-PMC + else if (reg == 6) board_type = VP_100; // VP-100 + else if (reg == 7) board_type = VP_CP1; // VP-CP1 + else board_type = 0; + if (board_type == VP_PSE) + {kdebug(("io_rcc(io_rcc_init): Board type = VP-PSE\n"));} + if (board_type == VP_PMC) + {kdebug(("io_rcc(io_rcc_init): Board type = VP-PMC\n"));} + if (board_type == VP_100) + {kdebug(("io_rcc(io_rcc_init): Board type = VP-100\n"));} + if (board_type == VP_CP1) + {kdebug(("io_rcc(io_rcc_init): Board type = VP-CP1\n"));} + if (!board_type) + {kdebug(("io_rcc(io_rcc_init): Unable to determine board type(2)\n"));} + } + + if(alloc_chrdev_region (&major_minor, 0, 1, "io_rcc")); //MJ: for 2.6 p45 + { + kdebug(("io_rcc(io_rcc_init): failed to obtain device numbers\n")); + goto fail1; + } + + proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL); + if (proc_read_text == NULL) + { + ecode = -ENOMEM; + kdebug(("io_rcc(io_rcc_init): error from kmalloc\n")); + goto fail2; + } + + io_rcc_file = create_proc_entry("io_rcc", 0644, NULL); + if (io_rcc_file == NULL) + { + kdebug(("io_rcc(io_rcc_init): error from call to create_proc_entry\n")); + ecode = -EFAULT; + goto fail3; + } + + + //MJ: Rubini does not mention the block below in the 2.6 boot but he is in + //general not very verbose about /proc. Maybe we just keep the commands as they + //worked in 2.4 + strcpy(io_proc_data.name, "io_rcc"); + strcpy(io_proc_data.value, "io_rcc"); + io_rcc_file->data = &io_proc_data; + io_rcc_file->read_proc = io_rcc_read_procmem; + io_rcc_file->write_proc = io_rcc_write_procmem; + io_rcc_file->owner = THIS_MODULE; + + for(loop = 0; loop < IO_MAX_PCI; loop++) + pcidev[loop].pid = 0; + + io_rcc_cdev = (struct cdev *)cdev_alloc(); //MJ: for 2.6 p55 + io_rcc_cdev->ops = &fops; + ecode = cdev_add(io_rcc_cdev, major_minor, 1); //MJ: for 2.6 p56 + if (ecode) + { + kdebug(("io_rcc(io_rcc_init): error from call to cdev_add.\n")); + goto fail4; + } + + kdebug(("io_rcc(io_rcc_init): driver loaded; major device number = %d\n", MAJOR(major_minor))); + return(0); + + fail4: + remove_proc_entry("io_rcc", NULL); + + fail3: + kfree(proc_read_text); + + fail2: + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + + fail1: + return(ecode); +} + + +/******************************/ +static void io_rcc_cleanup(void) +/******************************/ +{ + cdev_del(io_rcc_cdev); //MJ: for 2.6 p56 + remove_proc_entry("io_rcc", NULL); + kfree(proc_read_text); + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + kdebug(("io_rcc(io_rcc_cleanup): driver removed\n")); +} + + +module_init(io_rcc_init); //MJ: for 2.6 p16 +module_exit(io_rcc_cleanup); //MJ: for 2.6 p16 + + +/***********************************************************/ +static int io_rcc_open(struct inode *ino, struct file *filep) +/***********************************************************/ +{ + int loop; + private_stuff *pptr; + + //reserve space to store information about the devices managed by this "file" + pptr = (private_stuff *)kmalloc(sizeof (private_stuff), GFP_KERNEL); + if (pptr == NULL) + { + kdebug(("io_rcc_drv(io_rcc_open): error from kmalloc\n")); + return(-EFAULT); + } + + //Initialize the space + for (loop = 0; loop < IO_MAX_PCI; loop++) + pptr->linked[loop] = 0; + + filep->private_data = pptr; + kdebug(("io_rcc_drv(io_rcc_open): private_data = 0x%08lx\n", (u_long)filep->private_data)); + + return(0); +} + + +/**************************************************************/ +static int io_rcc_release(struct inode *ino, struct file *filep) +/**************************************************************/ +{ + int loop; + private_stuff *pptr; + + kdebug(("io_rcc(release): pid = %d\n", current->pid)); + pptr = (private_stuff *) filep->private_data; + + // Release orphaned links to PCI devices + for(loop = 0; loop < IO_MAX_PCI; loop++) + { + if (pptr->linked[loop] == 1) + { + kdebug(("io_rcc(release): Orphaned PCI device unlinked (pid=%d: vid=0x%08x did=0x%08x)\n", pcidev[loop].pid, pcidev[loop].vid, pcidev[loop].did)); + pcidev[loop].pid = 0; + pptr->linked[loop] = 0; + } + } + + kfree(pptr); + return(0); +} + + +/************************************************************************************/ +static int io_rcc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) +/************************************************************************************/ +{ + private_stuff *pptr; + + pptr = (private_stuff *) file->private_data; + + switch (cmd) + { + case IOPEEK: + { + IO_RCC_IO_t params; + int ret; + + ret = copy_from_user(¶ms, (void *)arg, sizeof(IO_RCC_IO_t)); + if (ret) + { + kdebug(("io_rcc(ioctl,IOPEEK): error %d from copy_from_user\n",ret)); + return(-EFAULT); + } + + if (params.size == 4) + { + params.data = inl(params.offset); + kdebug(("io_rcc(ioctl,IOPEEK): 0x%08x (int) read from 0x%08x\n", params.data, params.offset)); + } + if (params.size == 2) + { + params.data = inw(params.offset); + kdebug(("io_rcc(ioctl,IOPEEK): 0x%08x (word) read from 0x%08x\n", params.data, params.offset)); + } + if (params.size == 1) + { + params.data = inb(params.offset); + kdebug(("io_rcc(ioctl,IOPEEK): 0x%08x (byte) read from 0x%08x\n", params.data, params.offset)); + } + + if (copy_to_user((void *)arg, ¶ms, sizeof(IO_RCC_IO_t)) != 0) + { + kdebug(("io_rcc(ioctl,IOPEEK): error from copy_to_user\n")); + return(-EFAULT); + } + break; + } + + case IOPOKE: + { + IO_RCC_IO_t params; + int ret; + + ret = copy_from_user(¶ms, (void *)arg, sizeof(IO_RCC_IO_t)); + if (ret) + { + kdebug(("io_rcc(ioctl,IOPOKE): error %d from copy_from_user\n",ret)); + return(-EFAULT); + } + + if (params.size == 4) + { + kdebug(("io_rcc(ioctl,IOPOKE): writing 0x%08x (int) to 0x%08x\n", params.data, params.offset)); + outl(params.data, params.offset); + wmb(); // Recommended by Rubini on p247 + } + if (params.size == 2) + { + kdebug(("io_rcc(ioctl,IOPOKE): writing 0x%08x (word) to 0x%08x\n", params.data, params.offset)); + outw(params.data&0xffff, params.offset); + wmb(); + } + if (params.size == 1) + { + kdebug(("io_rcc(ioctl,IOPOKE): writing 0x%08x (byte) to 0x%08x\n", params.data, params.offset)); + outb(params.data&0xff, params.offset); + wmb(); + } + + if (copy_to_user((void *)arg, ¶ms, sizeof(IO_RCC_IO_t)) != 0) + { + kdebug(("io_rcc(ioctl,IOPOKE): error from copy_to_user\n")); + return(-EFAULT); + } + break; + } + + case IOGETID: + { + if (copy_to_user((void *)arg, &board_type, sizeof(int)) != 0) + { + kdebug(("io_rcc(ioctl,IOGETID): error from copy_to_user\n")); + return(-EFAULT); + } + break; + } + + case IOPCILINK: + { + int ret, dnum; + u_int loop; + IO_PCI_FIND_t find; + struct pci_dev *dev = NULL; + + ret = copy_from_user(&find, (void *)arg, sizeof(IO_PCI_FIND_t)); + if (ret) + { + kdebug(("io_rcc(ioctl,IOPCILINK): error %d from copy_from_user\n",ret)); + return(-EFAULT); + } + + //Find a free slot in the pcidev array + //MJ: for now I am not checking if the same device has already been opened by an other call + dnum = -1; +//MJ-SMP: to be protected (spinlock) + for(loop = 0; loop < IO_MAX_PCI; loop++) + { + if (pcidev[loop].pid == 0) + { + dnum = loop; + pcidev[dnum].pid = current->pid; //Reserve this array element + pptr->linked[dnum] = 1; //Remember to which "file" this mapping belongs to + break; + } + } +//MJ-SMP: end of protected zone + + if (dnum == -1) + { + kdebug(("io_rcc(ioctl,IOPCILINK): Device table is full\n")); + return(-IO_PCI_TABLEFULL); + } + + for(loop = 0; loop < find.occ; loop++) + { + dev = pci_get_device(find.vid, find.did, dev); //Find N-th device //MJ: for 2.6 p313 + if (dev == NULL) + { + kdebug(("io_rcc(ioctl,IOPCILINK): No device found\n")); + pcidev[dnum].pid = 0; //array element no longer required + pptr->linked[dnum] = 0; + return(-IO_PCI_NOT_FOUND); + } + } + + pci_dev_put(dev); //MJ: for 2.6 p314 + pci_enable_device(dev); //MJ: for 2.6 p314 + + kdebug(("io_rcc(ioctl,IOPCILINK):Device found\n")); + kdebug(("io_rcc(ioctl,IOPCILINK):devfn =0x%08x\n", dev->devfn)); + kdebug(("io_rcc(ioctl,IOPCILINK):irq =0x%08x\n", dev->irq)); + + pcidev[dnum].vid = find.vid; + pcidev[dnum].did = find.did; + pcidev[dnum].occ = find.occ; + pcidev[dnum].dev_ptr = dev; + + find.handle = dnum; + + ret = copy_to_user((void *)arg, &find, sizeof(IO_PCI_FIND_t)); + if (ret) + { + kdebug(("io_rcc(ioctl,IOPCILINK): error from copy_to_user\n")); + return(-EFAULT); + } + break; + } + + case IOPCIINFO: + { + int ret, loop; + pci_info_t info; + + ret = copy_from_user(&info, (void *)arg, sizeof(pci_info_t)); + if (ret) + { + kdebug(("io_rcc(ioctl,IOPCIINFO): error %d from copy_from_user\n", ret)); + return(-EFAULT); + } + + kdebug(("io_rcc(ioctl,IOPCIINFO): Info requested for handle %d\n", info.handle)); + + for (loop = 0; loop < 6; loop++) + { + info.bar[loop].base = pci_resource_start(pcidev[info.handle].dev_ptr, loop); + info.bar[loop].size = pci_resource_len(pcidev[info.handle].dev_ptr, loop); + info.bar[loop].flags = pci_resource_flags(pcidev[info.handle].dev_ptr, loop); + kdebug(("io_rcc(ioctl,IOPCIINFO): BAR %d:\n", loop)); + kdebug(("io_rcc(ioctl,IOPCIINFO): base = 0x%08x:\n", info.bar[loop].base)); + kdebug(("io_rcc(ioctl,IOPCIINFO): end = 0x%08x:\n", info.bar[loop].size)); + kdebug(("io_rcc(ioctl,IOPCIINFO): flags = 0x%08x:\n", info.bar[loop].flags)); + } + + ret = copy_to_user((void *)arg, &info, sizeof(pci_info_t)); + if (ret) + { + kdebug(("io_rcc(ioctl,IOPCIINFO): error %d from copy_to_user\n", ret)); + return(-EFAULT); + } + + break; + } + + case IOPCIUNLINK: + { + u_int dnum; + int ret; + + ret = copy_from_user(&dnum, (void *)arg, sizeof(int)); + if (ret) + { + kdebug(("io_rcc(ioctl,IOPCIUNLINK): error %d from copy_from_user\n",ret)); + return(-EFAULT); + } + + if (pcidev[dnum].pid == 0) + { + kdebug(("io_rcc(ioctl,IOPCICONFR): Illegal handle\n")); + return(-IO_PCI_ILL_HANDLE); + } + + pcidev[dnum].pid = 0; + pptr->linked[dnum] = 0; + + break; + } + + case IOPCICONFR: + { + int ret; + IO_PCI_CONF_t params; + u_int idata; + u_short sdata; + u_char cdata; + + ret = copy_from_user(¶ms, (void *)arg, sizeof(IO_PCI_CONF_t)); + if (ret) + { + kdebug(("io_rcc(ioctl,IOPCICONFR): error %d from copy_from_user\n",ret)); + return(-EFAULT); + } + + if (pcidev[params.handle].pid == 0) + { + kdebug(("io_rcc(ioctl,IOPCICONFR): Illegal handle\n")); + return(-IO_PCI_ILL_HANDLE); + } + + if (params.size == 4) + { + ret = pci_read_config_dword(pcidev[params.handle].dev_ptr, params.offs, &idata); + params.data = idata; + } + if (params.size == 2) + { + ret = pci_read_config_word(pcidev[params.handle].dev_ptr, params.offs, &sdata); + params.data = sdata; + } + if (params.size == 1) + { + ret = pci_read_config_byte(pcidev[params.handle].dev_ptr, params.offs, &cdata); + params.data = cdata; + } + + if (ret) + { + kdebug(("io_rcc(ioctl,IOPCICONFR): ERROR from pci_read_config_xxx\n")); + return(-IO_PCI_CONFIGRW); + } + + ret = copy_to_user((void *)arg, ¶ms, sizeof(IO_PCI_CONF_t)); + if (ret) + { + kdebug(("io_rcc(ioctl,IOPCICONFR): error from copy_to_user\n")); + return(-EFAULT); + } + + break; + } + + case IOPCICONFW: + { + int ret; + IO_PCI_CONF_t params; + + ret = copy_from_user(¶ms, (void *)arg, sizeof(IO_PCI_CONF_t)); + if (ret) + { + kdebug(("io_rcc(ioctl,IOPCICONFW): error %d from copy_from_user\n",ret)); + return(-EFAULT); + } + + if (pcidev[params.handle].pid == 0) + { + kdebug(("io_rcc(ioctl,IOPCICONFW): Illegal handle\n")); + return(-IO_PCI_ILL_HANDLE); + } + + if (params.size == 4) + ret = pci_write_config_dword(pcidev[params.handle].dev_ptr, params.offs, params.data); + if (params.size == 2) + ret = pci_write_config_dword(pcidev[params.handle].dev_ptr, params.offs, params.data & 0xffff); + if (params.size == 1) + ret = pci_write_config_dword(pcidev[params.handle].dev_ptr, params.offs, params.data & 0xff); + + if (ret) + { + kdebug(("io_rcc(ioctl,IOPCICONFW): ERROR from pci_write_config_xxxx\n")); + return(-IO_PCI_CONFIGRW); + } + + break; + } + } + return(0); +} + + +/****************************************************/ +static void io_rcc_vmaOpen(struct vm_area_struct *vma) +/****************************************************/ +{ + kdebug(("io_rcc_vmaOpen: Called\n")); +} + + +/*****************************************************/ +static void io_rcc_vmaClose(struct vm_area_struct *vma) +/*****************************************************/ +{ + kdebug(("io_rcc_vmaClose: mmap released\n")); +} + + +/*******************************************************************/ +static int io_rcc_mmap(struct file *file, struct vm_area_struct *vma) +/*******************************************************************/ +{ +//MJ FIXME: remap_pfn_range seems not to exist in /proc/kallsyms. Therefore: +/* + int result; + + vma->vm_flags |= VM_RESERVED; // This memory must not be swapped out + vma->vm_flags |= VM_LOCKED; // We also want to lock it //MJ: for 2.6: this flag still exists but Rubini does not mention it + + kdebug(("io_rcc_mmap: vma->vm_end = 0x%016lx\n", (u_long)vma->vm_end)); + kdebug(("io_rcc_mmap: vma->vm_start = 0x%016lx\n", (u_long)vma->vm_start)); + kdebug(("io_rcc_mmap: vma->vm_pgoff = 0x%016lx\n", (u_long)vma->vm_pgoff)); + kdebug(("io_rcc_mmap: vma->vm_flags = 0x%08x\n", (u_int)vma->vm_flags)); + + //MJ: is this OK or should we use the example from p429? + result = remap_page_range(vma, vma->vm_start, vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot); //MJ: for 2.6 p424 + if (result) + { + kdebug(("io_rcc_mmap: function remap_page_range failed \n")); + return(-IO_PCI_REMAP); + } + kdebug(("io_rcc_mmap: vma->vm_start(2) = 0x%016lx\n", (u_long)vma->vm_start)); + + vma->vm_ops = &io_rcc_vm_ops; + return(0); +*/ + + u_long size, offset; + + vma->vm_flags |= VM_RESERVED; // This memory must not be swapped out + vma->vm_flags |= VM_LOCKED; // We also want to lock it //MJ: for 2.6: this flag still exists but Rubini does not mention it + + kdebug(("io_rcc_mmap: vma->vm_end = 0x%016lx\n", (u_long)vma->vm_end)); + kdebug(("io_rcc_mmap: vma->vm_start = 0x%016lx\n", (u_long)vma->vm_start)); + kdebug(("io_rcc_mmap: vma->vm_offset = 0x%016lx\n", (u_long)vma->vm_pgoff << PAGE_SHIFT)); + kdebug(("io_rcc_mmap: vma->vm_flags = 0x%08x\n", (u_int)vma->vm_flags)); + + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + + if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot)) + { + kdebug(("io_rcc_mmap: function remap_page_range failed \n")); + return(-IO_PCI_REMAP); + } + kdebug(("io_rcc_mmap: vma->vm_start(2) = 0x%016lx\n", (u_long)vma->vm_start)); + + vma->vm_ops = &io_rcc_vm_ops; + return(0); +} + + +/**********************************************************************************************/ +static int io_rcc_write_procmem(struct file *file, const char *buffer, u_long count, void *data) +/**********************************************************************************************/ +{ + int len; + struct io_proc_data_t *fb_data = (struct io_proc_data_t *)data; + + kdebug(("io_rcc_mmap(io_rcc_write_procmem): io_rcc_write_procmem called\n")); + + if(count > 99) + len = 99; + else + len = count; + + if (copy_from_user(fb_data->value, buffer, len)) + { + kdebug(("io_rcc_mmap(io_rcc_write_procmem): error from copy_from_user\n")); + return(-EFAULT); + } + + kdebug(("io_rcc_mmap(io_rcc_write_procmem): len = %d\n", len)); + fb_data->value[len - 1] = '\0'; + kdebug(("io_rcc_mmap(io_rcc_write_procmem): text passed = %s\n", fb_data->value)); + + if (!strcmp(fb_data->value, "debug")) + { + debug = 1; + kdebug(("io_rcc_mmap(io_rcc_write_procmem): debugging enabled\n")); + } + + if (!strcmp(fb_data->value, "nodebug")) + { + kdebug(("io_rcc_mmap(io_rcc_write_procmem): debugging disabled\n")); + debug = 0; + } + + return len; +} + + +/****************************************************************************************************/ +static int io_rcc_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data) +/****************************************************************************************************/ +{ + int loop, nchars = 0; + static int len = 0; + + kdebug(("io_rcc_read_procmem: Called with buf = 0x%08lx\n", (u_long)buf)); + kdebug(("io_rcc_read_procmem: Called with *start = 0x%08lx\n", (u_long)*start)); + kdebug(("io_rcc_read_procmem: Called with offset = %ld\n", (u_long)offset)); + kdebug(("io_rcc_read_procmem: Called with count = %d\n", count)); + + if (offset == 0) + { + kdebug(("io_rcc_read_procmem: Creating text....\n")); + len = 0; + len += sprintf(proc_read_text + len, "IO RCC driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG); + len += sprintf(proc_read_text + len, "Dumping table of linked devices\n"); + len += sprintf(proc_read_text + len, "Handle | Vendor ID | Device ID | Occurrence | Process ID\n"); + for(loop = 0; loop < IO_MAX_PCI; loop++) + { + if (pcidev[loop].pid != 0) + { + len += sprintf(proc_read_text + len, " %2d |", loop); + len += sprintf(proc_read_text + len, "0x%08x |", pcidev[loop].vid); + len += sprintf(proc_read_text + len, "0x%08x |", pcidev[loop].did); + len += sprintf(proc_read_text + len, " 0x%08x |", pcidev[loop].occ); + len += sprintf(proc_read_text + len, " 0x%08x\n", pcidev[loop].pid); + } + } + + len += sprintf(proc_read_text + len, " \n"); + len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/io_rcc', executed as root,\n"); + len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n"); + len += sprintf(proc_read_text + len, "debug -> enable debugging\n"); + len += sprintf(proc_read_text + len, "nodebug -> disable debugging\n"); + + len += sprintf(proc_read_text + len, "Current values of the parameter(s)\n"); + len += sprintf(proc_read_text + len, "debug = %d\n", debug); + } + kdebug(("io_rcc_read_procmem: number of characters in text buffer = %d\n", len)); + + if (count < (len - offset)) + nchars = count; + else + nchars = len - offset; + kdebug(("io_rcc_read_procmem: min nchars = %d\n", nchars)); + + if (nchars > 0) + { + for (loop = 0; loop < nchars; loop++) + buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop]; + *start = buf + (offset & (PAGE_SIZE - 1)); + } + else + { + nchars = 0; + *eof = 1; + } + + kdebug(("io_rcc_read_procmem: returning *start = 0x%08lx\n", (u_long)*start)); + kdebug(("io_rcc_read_procmem: returning nchars = %d\n", nchars)); + return(nchars); +} + + + + + + + + diff --git a/cmt/quest.c b/cmt/quest.c new file mode 100644 index 0000000000000000000000000000000000000000..b4e5fdd6ee4ecb7977950a1968b1c60731b84006 --- /dev/null +++ b/cmt/quest.c @@ -0,0 +1,986 @@ +/************************************************************************/ +/* */ +/* File: quest_driver.c */ +/* */ +/* driver for the QUEST S-Link interface */ +/* */ +/* 9. Dec. 03 MAJO created */ +/* */ +/************ C 2005 - The software with that certain something *********/ + +/************************************************************************/ +/*NOTES: */ +/*- This driver should work on kernels from 2.6.9 onwards */ +/************************************************************************/ + +#include <linux/init.h> //MJ: for 2.6, p30 +#include <linux/module.h> //p30 +#include <linux/moduleparam.h> //MJ: for 2.6, p30 +#include <linux/kernel.h> //e.g. for printk +#include <linux/stat.h> //MJ: for 2.6, e.g. for module_param +#include <linux/pci.h> //e.g. for pci_find_device +#include <linux/errno.h> //e.g. for EFAULT +#include <linux/fs.h> //e.g. for register_chrdev +#include <linux/proc_fs.h> //e.g. for create_proc_entry +#include <linux/mm.h> //e.g. for vm_operations_struct +#include <linux/slab.h> //e.g. for kmalloc +#include <linux/cdev.h> //e.g. for cdev_alloc +#include <linux/delay.h> +#include <linux/interrupt.h> //e.g. for request_irq +#include <asm/uaccess.h> //e.g. for copy_from_user +#include <asm/io.h> //e.g. for inl +#include <asm/system.h> //e.g. for wmb +#include "ROSsolar/quest_driver.h" + +/*********/ +/*Globals*/ +/*********/ +static int debug = 0; +static u_int maxcards = 0; +static u_int opid, served[MAXQUESTROLS], irqlist[MAXIRQ]; +static u_int *infifo, *infifon; +static u_int reqadd[MAXQUESTCARDS], reqbuf[MAXQUESTCARDS], infifor[MAXQUESTROLS], req_dma_active[MAXQUESTCARDS]; +static u_int order, swfifobasephys, infifodatasize, fifoptrsize, tsize = 0; +static u_long swfifobase; +static struct proc_dir_entry *quest_file; +struct quest_proc_data_t quest_proc_data; +static char *proc_read_text; +static dev_t major_minor; +static struct cdev *quest_cdev; +static T_quest_card questcard[MAXQUESTCARDS]; + +/************/ +/*Prototypes*/ +/************/ +static void quest_cleanup(void); +static void quest_vmaOpen(struct vm_area_struct *vma); +static void quest_vmaClose(struct vm_area_struct *vma); +static void questRemove(struct pci_dev *dev); +static int __devinit questProbe(struct pci_dev *dev, const struct pci_device_id *id); +static int quest_init(void); +static irqreturn_t quest_irq_handler (int irq, void *dev_id, struct pt_regs *regs); + + +/***************************************************************/ +/* Use /sbin/modinfo <module name> to extract this information */ +/***************************************************************/ +MODULE_DESCRIPTION("S-Link QUEST"); +MODULE_AUTHOR("Markus Joos, CERN/PH"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("Private: Contact markus.joos@cern.ch"); +module_param (debug, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(debug, "1 = enable debugging 0 = disable debugging"); + + +//MJ: Not actually required. Just for kdebug +struct vm_operations_struct quest_vm_ops = +{ + .close = quest_vmaClose, + .open = quest_vmaOpen, //MJ: Note the comma at the end of the list! +}; + +static struct file_operations fops = +{ + .owner = THIS_MODULE, + .ioctl = quest_ioctl, + .open = quest_open, + .mmap = quest_mmap, + .release = quest_release, +}; + +// PCI driver structures. See p311 +static struct pci_device_id questIds[] = +{ + {PCI_DEVICE(PCI_VENDOR_ID_CERN, QUEST_PCI_DEVICE_ID)}, + {0,}, +}; + +//PCI operations +static struct pci_driver questPciDriver = +{ + .name = "quest", + .id_table = questIds, + .probe = questProbe, + .remove = questRemove, +}; + +/*****************************/ +/* Standard driver functions */ +/*****************************/ + +/*************************/ +static int quest_init(void) +/*************************/ +{ + int tfsize, result; + u_int channel, card, loop; + struct page *page_ptr; + + if (alloc_chrdev_region(&major_minor, 0, 1, "quest")) //MJ: for 2.6 p45 + { + kdebug(("quest(quest_init): failed to obtain device numbers\n")); + result = -QUEST_EIO; + goto fail1; + } + + proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL); + if (proc_read_text == NULL) + { + kdebug(("quest(quest_init): error from kmalloc\n")); + result = -EFAULT; + goto fail2; + } + + quest_file = create_proc_entry("quest", 0644, NULL); + if (quest_file == NULL) + { + kdebug(("quest(quest_init): error from call to create_proc_entry\n")); + result = -EFAULT; + goto fail3; + } + + strcpy(quest_proc_data.name, "quest"); + strcpy(quest_proc_data.value, "quest"); + quest_file->data = &quest_proc_data; + quest_file->read_proc = quest_read_procmem; + quest_file->write_proc = quest_write_procmem; + quest_file->owner = THIS_MODULE; + + //Clear the list of used interupts + for(loop = 0; loop < MAXIRQ; loop++) + irqlist[loop] = 0; + + kdebug(("quest(quest_init): registering PCIDriver\n")); + result = pci_register_driver(&questPciDriver); //MJ: See P312 + if (result == 0) + { + kdebug(("quest(quest_init): ERROR! no QUEST cards found!\n")); + result = -EIO; + goto fail4; + } + else + { + kdebug(("quest(quest_init): Found %d QUEST cards!\n", result)); + } + + // Allocate contiguous memory for the FIFOs and store the base addresses in a global structure + infifodatasize = MAXQUESTROLS * MAXQUESTINFIFO * sizeof(u_int) * 2; //infifo + fifoptrsize = MAXQUESTROLS * sizeof(u_int); //infifo counter + kdebug(("quest(quest_init): 0x%08x bytes needed for the IN FIFOs\n", infifodatasize)); + kdebug(("quest(quest_init): 0x%08x bytes needed for the FIFO counters\n", fifoptrsize)); + + tsize = infifodatasize + fifoptrsize; + kdebug(("quest(quest_init): 0x%08x bytes needed for all FIFOs\n", tsize)); + + tfsize = (tsize + 4095) & 0xfffff000; // round up to next 4K boundary + tfsize = tfsize >> 12; // compute number of pages + kdebug(("quest(quest_init): %d pages needed for the S/W FIFOs\n", tfsize)); + + order = 0; + while(tfsize) + { + order++; + tfsize = tfsize >> 1; // compute order + } + kdebug(("quest(quest_init): order = %d\n", order)); + + swfifobase = __get_free_pages(GFP_ATOMIC, order); + if (!swfifobase) + { + kdebug(("rquest(quest_init): error from __get_free_pages\n")); + result = -QUEST_EFAULT; + goto fail5; + } + kdebug(("quest(quest_init): swfifobase = 0x%016lx\n", swfifobase)); + + // Reserve all pages to make them remapable + page_ptr = virt_to_page(swfifobase); + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + set_bit(PG_reserved, &page_ptr->flags); + + swfifobasephys = virt_to_bus((void *) swfifobase); + kdebug(("quest(quest_init): swfifobasephys = 0x%08x\n", swfifobasephys)); + + // Assign base addresses to the FIFO arrays + infifo = (u_int *)swfifobase; + infifon = (u_int *)(swfifobase + infifodatasize); + kdebug(("quest(quest_init): infifo is at 0x%016lx\n", (u_long)infifo)); + kdebug(("quest(quest_init): infifon is at 0x%016lx\n", (u_long)infifon)); + + //Initialize the FIFOs + for(channel = 0; channel < MAXQUESTROLS; channel++) + { + infifor[channel] = 0; + infifon[channel] = 0; + } + + //Allocate the buffers for the REQ blocks + //The max size of such a block is: 4 channels * (1 size word + 126 data words) = 504 words = 2032 bytes + //It is therefore safe if we get one page (4k) per buffer + for(card = 0; card < maxcards; card++) + { + req_dma_active[card] = 0; + reqbuf[card] = __get_free_page(GFP_ATOMIC); + if (!reqbuf[card]) + { + kdebug(("quest(quest_init): error from __get_free_pages for reqbuf\n")); + result = -QUEST_EFAULT; + goto fail6; + } + reqadd[card] = virt_to_bus((void *) reqbuf[card]); + kdebug(("quest(quest_init): reqbuf[%d] = 0x%08x (PCI = 0x%08x)\n", card, reqbuf[card], reqadd[card])); + } + + opid = 0; + quest_cdev = (struct cdev *)cdev_alloc(); //MJ: for 2.6 p55 + quest_cdev->ops = &fops; + result = cdev_add(quest_cdev, major_minor, 1); //MJ: for 2.6 p56 + if (result) + { + kdebug(("quest(quest_init): error from call to cdev_add.\n")); + goto fail7; + } + + kdebug(("quest(quest_init): driver loaded; major device number = %d\n", MAJOR(major_minor))); + return(0); + + fail7: + for(card = 0; card < maxcards; card++) + free_page((u_long)reqbuf[card]); + + fail6: + page_ptr = virt_to_page(swfifobase); + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + free_pages(swfifobase, order); + + fail5: + pci_unregister_driver(&questPciDriver); + + fail4: + remove_proc_entry("io_rcc", NULL); + + fail3: + kfree(proc_read_text); + + fail2: + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + + fail1: + return(result); +} + + +/*****************************/ +static void quest_cleanup(void) +/*****************************/ +{ + u_int card, loop; + struct page *page_ptr; + + cdev_del(quest_cdev); //MJ: for 2.6 p56 + pci_unregister_driver(&questPciDriver); + + // unreserve all pages used for the S/W FIFOs + page_ptr = virt_to_page(swfifobase); + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + + // free the area + free_pages(swfifobase, order); + + // Return the buffers for the REQ blocks + for(card = 0; card < maxcards; card++) + free_page(reqbuf[card]); + + remove_proc_entry("quest", NULL); + kfree(proc_read_text); + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + + kdebug(("quest(quest_cleanup): driver removed\n")); +} + + +module_init(quest_init); //MJ: for 2.6 p16 +module_exit(quest_cleanup); //MJ: for 2.6 p16 + + +/**********************************************************/ +static int quest_open(struct inode *ino, struct file *filep) +/**********************************************************/ +{ + kdebug(("quest(quest_open): nothing to be done\n")); + return(0); +} + + +/*************************************************************/ +static int quest_release(struct inode *ino, struct file *filep) +/*************************************************************/ +{ + kdebug(("quest(quest_release): (almost) nothing to be done\n")); + opid = 0; + return(0); +} + + +/***********************************************************************************/ +static int quest_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) +/***********************************************************************************/ +{ + switch (cmd) + { + case OPEN: + { + QUEST_open_t params; + + kdebug(("quest(ioctl,OPEN): called from process %d\n", current->pid)) + if (!opid) + opid = current->pid; + else + { + kdebug(("quest(ioctl,OPEN): The QUEST cards are already used by process %d\n", opid)) + return(-QUEST_USED); + } + + params.size = tsize; + params.insize = infifodatasize; + params.physbase = swfifobasephys; + kdebug(("quest(ioctl,OPEN): params.size = 0x%08x\n", params.size)); + kdebug(("quest(ioctl,OPEN): params.physbase = 0x%08x\n", params.physbase)); + kdebug(("quest(ioctl,OPEN): params.insize = 0x%08x\n", params.insize)); + + if (copy_to_user((void *)arg, ¶ms, sizeof(QUEST_open_t))) + { + kdebug(("quest(ioctl, OPEN): error from copy_to_user\n")); + return(-QUEST_EFAULT); + } + return(0); + break; + } + + case CLOSE: + { + kdebug(("quest(ioctl,CLOSE): called from process %d\n", current->pid)) + opid = 0; + + return(0); + break; + } + + case CONFIG: + { + QUEST_config_t params; + u_int channel, card, data; + + if (copy_from_user(¶ms, (void *)arg, sizeof(QUEST_config_t))) + { + kdebug(("quest(ioctl,CONFIG): error from copy_from_user\n")); + return(-QUEST_EFAULT); + } + kdebug(("quest(ioctl,CONFIG): \n")); + kdebug(("quest(ioctl,CONFIG): params.bswap = %d\n", params.bswap)); + kdebug(("quest(ioctl,CONFIG): params.wswap = %d\n", params.wswap)); + kdebug(("quest(ioctl,CONFIG): params.scw = 0x%08x\n", params.scw)); + kdebug(("quest(ioctl,CONFIG): params.ecw = 0x%08x\n", params.ecw)); + + for(card = 0; card < maxcards; card++) + { + data = 0; + questcard[card].regs->opctl = (params.wswap << 2) + (params.bswap << 1); + questcard[card].regs->sctl = params.scw & 0xfffffffc; + questcard[card].regs->ectl = params.ecw & 0xfffffffc; + + //The LFF and LDOWN interrupts are not yet supported + questcard[card].regs->intctl = 0xFBEFBE05; // threshold fixed at 62 for all channels, clear any outstanding interrupts + questcard[card].regs->intmask = 0x1; + } + + for(channel = 0; channel < MAXQUESTROLS; channel++) + served[channel] = 0; + + break; + } + + case RESET: + { + u_int rol, data, card, channel; + + if (copy_from_user(&card, (void *)arg, sizeof(int))) + { + kdebug(("quest(ioctl,RESET): error from copy_from_user\n")); + return(-QUEST_EFAULT); + } + + kdebug(("quest(ioctl,RESET): Resetting card %d\n", card)); + data = questcard[card].regs->opctl; + data |= 0x1; + questcard[card].regs->opctl = data; + udelay(2); // Delay for at least one us + data &= 0xfffffffe; + questcard[card].regs->opctl = data; + req_dma_active[card] = 0; + + for (channel = 0; channel < MAXQUESTCHANNELS; channel++) //reset the FIFOs + { + rol = (card << 2) + channel; + infifor[rol] = 0; + infifon[rol] = 0; + } + + return(0); + break; + } + + case LINKRESET: + { + u_int data1, data2, waited = 0, card, channel; + + if (copy_from_user(&channel, (void *)arg, sizeof(int))) + { + kdebug(("quest(ioctl,LINKRESET): error from copy_from_user\n")); + return(-QUEST_EFAULT); + } + + card = channel >> 2; + channel &= 0x3; + kdebug(("quest(ioctl,LINKRESET): Resetting channel %d of card %d\n", channel, card)); + + data1 = 1 << (16 + channel * 4); + data2 = 1 << (4 + channel * 7); + questcard[card].regs->opctl |= data1; // Set the URESET bit + udelay(2000); // Delay for at least two ms + + while(questcard[card].regs->opstat & data2) // Now wait for LDOWN to come up again + { + waited++; + if (waited > 10000) + { + kdebug(("quest(ioctl,LINKRESET): card %d does not come up again\n", card)); + questcard[card].regs->opctl &= ~data1; // Reset the URESET bit + return(-QUEST_STUCK); + } + } + + questcard[card].regs->opctl &= ~data1; // Reset the URESET bit + served[4* card + channel] = 0; + return(0); + break; + } + + case LINKSTATUS: + { + u_long flags; + u_int data, card; + QUEST_status_t params; + + if (copy_from_user(¶ms, (void *)arg, sizeof(QUEST_status_t))) + { + kdebug(("quest(ioctl, LINKSTATUS): error from copy_from_user\n")); + return(-QUEST_EFAULT); + } + + card = params.channel >> 2; + data = 1 << (4 + (params.channel & 0x3) * 7); + if (questcard[card].regs->opstat & data) + params.ldown = 1; + else + params.ldown = 0; + + //We have to make sure that this code does not get interrupted. + local_irq_save(flags); //MJ: for 2.6 see p274 & p275 + params.infifo = infifon[params.channel]; + params.reqfifo = REQFIFODEPTH - ((questcard[card].regs->fstat >> (8 * (params.channel & 0x3))) & 0xff); + kdebug(("quest(ioctl, LINKSTATUS): params.ldown = %d\n", params.ldown)); + kdebug(("quest(ioctl, LINKSTATUS): params.infifo = %d\n", params.infifo)); + kdebug(("quest(ioctl, LINKSTATUS): params.reqfifo = %d\n", params.reqfifo)); + local_irq_restore(flags); //MJ: for 2.6 see p274 & p275 + + if (copy_to_user((void *)arg, ¶ms, sizeof(QUEST_status_t))) + { + kdebug(("quest(ioctl, LINKSTATUS): error from copy_to_user\n")); + return(-QUEST_EFAULT); + } + + return(0); + break; + } + + case INFO: + { + QUEST_info_t info; + u_int data, card, channel; + + info.ncards = maxcards; + kdebug(("quest(ioctl, INFO): maxcards = %d\n", maxcards)); + + for(card = 0; card < MAXQUESTCARDS; card++) + { + if (card < maxcards) + info.cards[card] = 1; + else + info.cards[card] = 0; + kdebug(("quest(ioctl, INFO): info.cards[%d] = %d\n", card, info.cards[card])); + } + + for(channel = 0; channel < MAXQUESTCHANNELS; channel++) + { + card = channel >> 2; + if (info.cards[card] == 1) + { + data = questcard[card].regs->opstat & (1 << (10 + 7 * (channel & 0x3))); + if (data) + info.channel[channel] = 0; + else + info.channel[channel] = 1; + } + else + info.channel[channel] = 0; + kdebug(("quest(ioctl, INFO): info.channel[%d] = %d\n", channel, info.channel[channel])); + } + + if (copy_to_user((void *)arg, &info, sizeof(QUEST_info_t))) + { + kdebug(("quest(ioctl, INFO): error from copy_to_user\n")); + return(-QUEST_EFAULT); + } + return(0); + break; + } + + case FIFOIN: + { + u_int channel; + + if (copy_from_user(&channel, (void *)arg, sizeof(int)) !=0) + { + kdebug(("quest(ioctl,FIFOIN): error from copy_from_user\n")); + return(-QUEST_EFAULT); + } + kdebug(("quest(ioctl,FIFOIN): channel = %d\n", channel)); + + //Try to post a new request block on the card of this channel + refill_req_fifo_dma(channel >> 2); + + return(0); + break; + } + } + return(0); +} + + +/***************************************************/ +static void quest_vmaOpen(struct vm_area_struct *vma) +/***************************************************/ +{ + kdebug(("quest(quest_vmaOpen): Called\n")); +} + + +/****************************************************/ +static void quest_vmaClose(struct vm_area_struct *vma) +/****************************************************/ +{ + kdebug(("quest(quest_vmaClose): Virtual address = 0x%016lx\n",(u_long)vma->vm_start)); + kdebug(("quest(quest_vmaClose): mmap released\n")); +} + + +/******************************************************************/ +static int quest_mmap(struct file *file, struct vm_area_struct *vma) +/******************************************************************/ +{ + u_long offset, size; + + kdebug(("quest(quest_mmap): function called\n")); + + vma->vm_flags |= VM_RESERVED; + vma->vm_flags |= VM_LOCKED; + + kdebug(("quest(quest_mmap): vma->vm_end = 0x%016lx\n", (u_long)vma->vm_end)); + kdebug(("quest(quest_mmap): vma->vm_start = 0x%016lx\n", (u_long)vma->vm_start)); + kdebug(("quest(quest_mmap): vma->vm_offset = 0x%016lx\n", (u_long)vma->vm_pgoff << PAGE_SHIFT)); + kdebug(("quest(quest_mmap): vma->vm_flags = 0x%08x\n", (u_int)vma->vm_flags)); + + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + + kdebug(("quest(quest_mmap): offset = 0x%08x\n",(u_int)offset)); + kdebug(("quest(quest_mmap): size = 0x%08x\n",(u_int)size)); + + if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot)) + { + kdebug(("quest(quest_mmap): remap page range failed\n")); + return -ENXIO; + } + + vma->vm_ops = &quest_vm_ops; + kdebug(("quest(quest_mmap): function done\n")); + return(0); +} + + +/*********************************************************************************************/ +static int quest_write_procmem(struct file *file, const char *buffer, u_long count, void *data) +/*********************************************************************************************/ +{ + int len; + struct quest_proc_data_t *fb_data = (struct quest_proc_data_t *)data; + + kdebug(("quest(quest_write_procmem): quest_write_procmem called\n")); + + if(count > 99) + len = 99; + else + len = count; + + if (copy_from_user(fb_data->value, buffer, len)) + { + kdebug(("quest(quest_write_procmem): error from copy_from_user\n")); + return(-QUEST_EFAULT); + } + + kdebug(("quest(quest_write_procmem): len = %d\n", len)); + fb_data->value[len - 1] = '\0'; + kdebug(("quest(quest_write_procmem): text passed = %s\n", fb_data->value)); + + if (!strcmp(fb_data->value, "debug")) + { + debug = 1; + kdebug(("quest(quest_write_procmem): debugging enabled\n")); + } + + if (!strcmp(fb_data->value, "nodebug")) + { + kdebug(("quest(quest_write_procmem): debugging disabled\n")); + debug = 0; + } + + return len; +} + + +/***************************************************************************************************/ +static int quest_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data) +/***************************************************************************************************/ +{ + u_int card, channel, fstat, ocr, osr, imask, estat, value; + int loop, nchars = 0; + static int len = 0; + + kdebug(("quest(quest_read_procmem): Called with buf = 0x%016lx\n", (u_long)buf)); + kdebug(("quest(quest_read_procmem): Called with *start = 0x%016lx\n", (u_long)*start)); + kdebug(("quest(quest_read_procmem): Called with offset = %d\n", (u_int)offset)); + kdebug(("quest(quest_read_procmem): Called with count = %d\n", count)); + + if (offset == 0) + { + kdebug(("exp(exp_read_procmem): Creating text....\n")); + len = 0; + len += sprintf(proc_read_text + len, "\n\n\nQUEST driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG); + + len += sprintf(proc_read_text + len, "==================================================\n"); + len += sprintf(proc_read_text + len, "Card|IRQ line| REQ IRQ| DONE IRQ|revision|PCI MEM addr.|wswap|bswap|Temperature\n"); + len += sprintf(proc_read_text + len, "----|--------|---------|---------|--------|-------------|-----|-----|-----------\n"); + for(card = 0; card < maxcards; card++) + { + ocr = questcard[card].regs->opctl; + osr = questcard[card].regs->opstat; + imask = questcard[card].regs->intmask; + estat = questcard[card].regs->estat; + len += sprintf(proc_read_text + len, " %d|", card); + len += sprintf(proc_read_text + len, " %2d|", questcard[card].irq_line); + len += sprintf(proc_read_text + len, " %s|", (imask & 0x4)?"enabled":"disabled"); + len += sprintf(proc_read_text + len, " %s|", (imask & 0x1)?"enabled":"disabled"); + len += sprintf(proc_read_text + len, " 0x%02x|", questcard[card].pci_revision); + len += sprintf(proc_read_text + len, " 0x%08x|", questcard[card].pci_memaddr); + len += sprintf(proc_read_text + len, " %s|", (ocr & 0x4)?"yes":" no"); + len += sprintf(proc_read_text + len, " %s", (ocr & 0x2)?"yes":" no"); + len += sprintf(proc_read_text + len, "%9d C", estat & 0xff); + } + + for(card = 0; card < maxcards; card++) + { + len += sprintf(proc_read_text + len, "\nCard %d:\n", card); + len += sprintf(proc_read_text + len, "=======\n"); + len += sprintf(proc_read_text + len, "Channel|present|LDOWN|#REQ slots avail.|#fragments sent\n"); + len += sprintf(proc_read_text + len, "-------|-------|-----|-----------------|---------------\n"); + + osr = questcard[card].regs->opstat; + fstat = questcard[card].regs->fstat; + for(channel = 0; channel < MAXQUESTCHANNELS; channel++) + { + len += sprintf(proc_read_text + len, " %1d|", channel); + value = osr & (1 << (10 + channel * 7)); + len += sprintf(proc_read_text + len, " %s|", value?"Yes":" No"); + value = osr & (1 << (4 + channel * 7)); + len += sprintf(proc_read_text + len, " %s|", value?"Yes":" No"); + value = (fstat >> (8 * channel)) & 0xff; + len += sprintf(proc_read_text + len, "%17d|", value); + len += sprintf(proc_read_text + len, "%15d\n", served[(card << 2) + channel]); + } + } + + len += sprintf(proc_read_text + len, " \n"); + len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/quest', executed as root,\n"); + len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n"); + len += sprintf(proc_read_text + len, "debug -> enable debugging\n"); + len += sprintf(proc_read_text + len, "nodebug -> disable debugging\n"); + } + kdebug(("quest(quest_read_procmem): number of characters in text buffer = %d\n", len)); + + if (count < (len - offset)) + nchars = count; + else + nchars = len - offset; + kdebug(("quest(quest_read_procmem): min nchars = %d\n", nchars)); + + if (nchars > 0) + { + for (loop = 0; loop < nchars; loop++) + buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop]; + *start = buf + (offset & (PAGE_SIZE - 1)); + } + else + { + nchars = 0; + *eof = 1; + } + + kdebug(("quest(quest_read_procmem): returning *start = 0x%016lx\n", (u_long)*start)); + kdebug(("quest(quest_read_procmem): returning nchars = %d\n", nchars)); + return(nchars); +} + + +/********************************************************************************/ +static irqreturn_t quest_irq_handler (int irq, void *dev_id, struct pt_regs *regs) +/********************************************************************************/ +{ + u_int card; + + // Note: the QUEST card(s) may have to share an interrupt line with other PCI devices. + // It is therefore important to exit quickly if we are not concerned with an interrupt. + // This is done by looking at REQBLK_DONE bit in the OPSTAT register. + + for(card = 0; card < maxcards; card++) + { + if ((questcard[card].irq_line == (u_int)irq) && questcard[card].regs->opstat & 0x1) + { + kdebug(("quest(quest_irq_handler): Interrupt received\n")); + questcard[card].regs->intctl |= 0x5; //Acknowledge (and clear) the inetrrupt + req_dma_active[card] = 0; + refill_req_fifo_dma(card); + return(IRQ_HANDLED); //MJ: for 2.6 see p273 + } + else + { + kdebug(("quest(quest_irq_handler): This was not a quest interrupt\n")); + return(IRQ_NONE); + } + } + return(IRQ_HANDLED); //MJ: for 2.6 see p273 +} + + +/*****************************/ +/* PCI driver functions */ +/*****************************/ + +/**********************************************************************************/ +static int __devinit questProbe(struct pci_dev *dev, const struct pci_device_id *id) //MJ: for _devinit see p31 +/**********************************************************************************/ +{ + int i, card; + u_long flags; + u_int size; + + kdebug(("quest(questProbe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device)); + kdebug(("quest(questProbe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn)); + + /* Find a new "slot" for the card */ + card = -1; + for (i = 0; i < MAXQUESTCARDS; i++) + { + if (questcard[i].pciDevice == NULL) + { + card = i; + questcard[card].pciDevice = dev; + break; + } + } + + if (card == -1) + { + kdebug(("quest(questProbe): Could not find space in the questcard array for this card.")); + return(-QUEST_TOOMANYCARDS); + } + + flags = pci_resource_flags(dev, BAR0); //MJ: See p317 + if ((flags & IORESOURCE_MEM) != 0) + { + questcard[card].pci_memaddr = pci_resource_start(dev, BAR0) & PCI_BASE_ADDRESS_MEM_MASK; //MJ: See p317 + size = pci_resource_end(dev, BAR0) - pci_resource_start(dev, BAR0); //MJ: See p317 + kdebug(("quest(questProbe): Mapping %d bytes at PCI address 0x%08x\n", size, questcard[card].pci_memaddr)); + + questcard[card].regs = (T_quest_regs *)ioremap(questcard[card].pci_memaddr, size); + if (questcard[card].regs == NULL) + { + kdebug(("quest(questProbe): error from ioremap for questcard[%d].regs\n", card)); + questcard[card].pciDevice = NULL; + return(-QUEST_IOREMAP); + } + kdebug(("quest(questProbe): questcard[%d].regs is at 0x%016lx\n", card, (u_long)questcard[card].regs)); + } + else + { + kdebug(("quest(questProbe): Bar 0 is not a memory resource")); + return(-QUEST_EIO); + } + + // get revision directly from the QUEST + pci_read_config_byte(questcard[card].pciDevice, PCI_REVISION_ID, &questcard[card].pci_revision); + kdebug(("quest(questProbe): revision = %x \n", questcard[card].pci_revision)); + if (questcard[card].pci_revision < MINREV) + { + kdebug(("quest(questProbe): Illegal quest Revision\n")); + return(-QUEST_ILLREV); + } + + pci_read_config_byte(questcard[card].pciDevice, PCI_INTERRUPT_LINE, &questcard[card].irq_line); + kdebug(("quest(questProbe): interrupt line = %d \n", questcard[card].irq_line)); + + //Several quest cards may use the same interrupt but we want to install the driver only once + if (irqlist[questcard[card].irq_line] == 0) + { + if(request_irq(questcard[card].irq_line, quest_irq_handler, SA_SHIRQ, "quest", dev)) + { + kdebug(("quest(questProbe): request_irq failed on IRQ = %d\n", questcard[card].irq_line)); + return(-QUEST_REQIRQ); + } + irqlist[questcard[card].irq_line]++; + kdebug(("quest(questProbe): Interrupt %d registered\n", questcard[card].irq_line)); + } + + maxcards++; + return(0); +} + + +/******************************************/ +static void questRemove(struct pci_dev *dev) +/******************************************/ +{ + int i, card; + + /* Find the "slot" of the card */ + card = -1; + for (i = 0; i < MAXQUESTCARDS; i++) + { + if (questcard[i].pciDevice == dev) + { + card = i; + questcard[i].pciDevice = 0; + break; + } + } + + if (card == -1) + { + kdebug(("quest(questRemove): Could not find device to remove.")); + return; + } + + questcard[card].regs->intctl |= 0x5; // clear any outstanding interrupts + questcard[card].regs->intmask = 0; // mask the interrupts + + if (--irqlist[questcard[card].irq_line] == 0) + { + kdebug(("quest(questRemove): removing handler for interrupt %d", questcard[card].irq_line)); + free_irq(questcard[card].irq_line, dev); + } + + iounmap((void *)questcard[card].regs); + kdebug(("quest(questRemove): Card %d removed", card)); + maxcards--; +} + + + + +/*********************/ +/* Service functions */ +/*********************/ + +/*************************************************************/ +static int in_fifo_pop(int channel, u_int *data1, u_int *data2) +/*************************************************************/ +{ + int index; + + if (infifon[channel] == 0) + { + kdebug(("quest(in_fifo_pop): The IN FIFO is empty\n")); + return(-1); + } + + index = QUEST_IN_INDEX(channel, infifor[channel]); + *data1 = infifo[index]; + *data2 = infifo[index + 1]; + kdebug(("quest(in_fifo_pop): index=%d data1=0x%08x data2=0x%08x\n", index, *data1, *data2)); + + infifor[channel]++; + + if (infifor[channel] > (MAXQUESTINFIFO - 1)) + infifor[channel] = 0; + + infifon[channel]--; + + return(0); +} + + +/***************************************/ +static void refill_req_fifo_dma(int card) +/***************************************/ +{ + int channel; + int reqlen = 2; // 4 32-bit words for the entry count -> 2 64-bit words + int rol = 4 * card; + u_int *reqptr = (u_int *) reqbuf[card]; + u_int fstat = questcard[card].regs->fstat; + + if (req_dma_active[card]) return; + for (channel = 0; channel < MAXQUESTCHANNELS; channel++) + { + int loop; + int numfree = fstat & 0xff; + int numreq = infifon[rol]; + int numcopy = (numfree < numreq) ? numfree : numreq; + + *reqptr++ = numcopy << 1; // number of requests x 2 + + for (loop = 0; loop < numcopy; loop++) + { + u_int addr, size; + in_fifo_pop(rol, &addr, &size); + served[rol]++; + *reqptr++ = addr; // address of the data block + *reqptr++ = size; // block size and control bits + } + reqlen += numcopy; + fstat >>= 8; + rol++; + } + + // Post the request + if (reqlen > 2) // at least one request + { + req_dma_active[card]++; + questcard[card].regs->blklen = reqlen; // reqlen is in 64 bit words + questcard[card].regs->reqadd = reqadd[card]; + } +} + + diff --git a/cmt/requirements b/cmt/requirements new file mode 100644 index 0000000000000000000000000000000000000000..78f07a48e078cf4b18a5624c285db6fd2c72a636 --- /dev/null +++ b/cmt/requirements @@ -0,0 +1,19 @@ +package ROSRCDdrivers + +author markus.joos@cern.ch +manager markus.joos@cern.ch + +#========================================================== +public +#========================================================== +use TDAQPolicy +use cmem_rcc +use io_rcc +use vme_rcc +use ROSsolar +use ROSfilar +use ROSRobin + +#action driver "cd ../$CMTCONFIG; gmake -f ../driver2.6/Makefile" +action driver "gmake -f Makefile" + diff --git a/cmt/robin.c b/cmt/robin.c new file mode 100644 index 0000000000000000000000000000000000000000..cb72771d3188b7362af37d5c708569d51791ae17 --- /dev/null +++ b/cmt/robin.c @@ -0,0 +1,1182 @@ +/****************************************************************/ +/* file: robindriver.c */ +/* author: Markus Joos, CERN-PH/ESS */ +/****** C 2005 - The software with that certain something *******/ + + +// Description: +// Driver for the ATLAS ROBIN PCI card +// NOTES: +// - This driver has been developed with SLC3 + +// Strings like P311 refer to pages (e.g.311) in the third edition of the +// Linux Device Drivers book + +#include <linux/init.h> //MJ: for 2.6, p30 +#include <linux/module.h> +#include <linux/moduleparam.h> //MJ: for 2.6, p30 +#include <linux/kernel.h> +#include <linux/stat.h> //MJ: for 2.6, e.g. for module_param +#include <linux/pci.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/mm.h> +#include <linux/cdev.h> //e.g. for cdev_alloc +#include <linux/slab.h> //e.g. for kmalloc +#include <linux/delay.h> //e.g. for udelay +#include <linux/interrupt.h> //e.g. for request_irq +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/system.h> //e.g. for local_irq_save +#include "ROSRobin/robindriver_common.h" +#include "ROSRobin/robindriver.h" + +#ifndef EXPORT_SYMTAB //MJ: For 2.6: symbol seems not to be required + #define EXPORT_SYMTAB +#endif + + +/*********/ +/*Globals*/ +/*********/ +static int debug = 0, errorlog = 1, msg_fifo_avail[MAXCARDS]; +static int robin_major = 0; // use dynamic allocation +static u_int maxcard; +static T_card robincard[MAXCARDS]; +static struct proc_dir_entry *robin_file; +struct robin_proc_data_t robin_proc_data; +static T_blocklet blocklets[MAXCARDS][BLOCK_TYPES][MAX_BLOCKLETS]; +char *proc_read_text; + +/*****************/ +/*Some prototypes*/ +/*****************/ +static int robin_write_procmem(struct file *file, const char *buffer, u_long count, void *data); +static int robin_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data); +static int robinProbe(struct pci_dev *dev, const struct pci_device_id *id); +static void robinRemove(struct pci_dev *dev); +module_init(robindriver_init); //MJ: See P39 +module_exit(robindriver_exit); + +MODULE_DESCRIPTION("ATLAS ROBIN"); +MODULE_AUTHOR("Markus Joos, CERN/PH"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("Private: Contact markus.joos@cern.ch"); +module_param (debug, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(debug, "1 = enable debugging 0 = disable debugging"); +module_param (errorlog, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(errorlog, "1 = enable debugging 0 = disable debugging"); + +static struct file_operations fops = +{ + owner: THIS_MODULE, + mmap: robin_mmap, + ioctl: robin_ioctl, + open: robin_open, + release: robin_release +}; + + +// memory handler functions +static struct vm_operations_struct robin_vm_ops = +{ + close: robin_vclose, // mmap-close +}; + + +/* PCI driver structures */ + +/* ID tables */ //MJ: See P311 +static struct pci_device_id robinIds[] = +{ + { PCI_DEVICE(PCI_VENDOR_ID_ROBIN3, PCI_DEVICE_ID_ROBIN3) }, //ROBIN2, correct VID + { PCI_DEVICE(PCI_VENDOR_ID_ROBIN3, PCI_DEVICE_ID_ROBIN3_bad) }, //ROBIN2, wrong VID + { PCI_DEVICE(PCI_VENDOR_ID_ROBIN2, PCI_DEVICE_ID_ROBIN2) }, //ROBIN prototype, correct VID + { 0, }, +}; + + +/* The driver struct */ //MJ: See P311 +static struct pci_driver robinPciDriver = +{ + .name = "robindriver", + .id_table = robinIds, + .probe = robinProbe, + .remove = robinRemove, +}; + + +/*******************************/ +static int robindriver_init(void) +/*******************************/ +{ + int result, i; + + kerror(("robin driver version 2.1\n")); + + // Initialise the robin card map. This is used to assign the slot parameter to a ROBIN board when calling ioctl(link) + + for (i = 0; i < MAXCARDS; i++) + { + robincard[i].pciDevice = NULL; + robincard[i].pci_memaddr_plx = 0; + robincard[i].pci_memaddr_bus = 0; + robincard[i].pci_memsize_plx = 0; + robincard[i].pci_memsize_bus = 0; + robincard[i].mem_base = 0; + robincard[i].mem_size = 0; + robincard[i].plx_regs = NULL; + robincard[i].fpga_regs = NULL; + robincard[i].nrols = 0; + robincard[i].bar2 = 0; + robincard[i].fpgaConfigured = 0; //Will be set in INIT_DMA + } + + maxcard = 0; + + kdebug(("robin(robindriver_init): registering PCIDriver\n")); + result = pci_register_driver(&robinPciDriver); //MJ: See P312 + if (result == 0) + { + kerror(("robin(robindriver_init): Warning! no PCI devices found!\n")); + return(-RD_EIO); + } + else + { + kerror(("robin(robindriver_init): Found %d devices!\n", result)); + } + + result = register_chrdev(robin_major, "robin", &fops); //MJ: This is old fashioned. Update for 2.6. See P57 + if (result < 1) + { + kerror(("robin(robindriver_init): registering robin driver failed.\n")); + return(-RD_EIO2); + } + robin_major = result; + + proc_read_text = (char *)kmalloc(PROC_MAX_CHARS, GFP_KERNEL); + if (proc_read_text == NULL) + { + kdebug(("robin(robindriver_init): error from kmalloc\n")); + return(-RD_KMALLOC); + } + + robin_file = create_proc_entry("robin", 0644, NULL); //MJ: See P87 (seq_file) for a better 2.6 implementation + if (robin_file == NULL) + { + kerror(("robin(robindriver_init): error from call to create_proc_entry\n")); + return(-RD_PROC); + } + + strcpy(robin_proc_data.name, "robin"); + strcpy(robin_proc_data.value, "robin"); + robin_file->data = &robin_proc_data; + robin_file->read_proc = robin_read_procmem; + robin_file->write_proc = robin_write_procmem; + robin_file->owner = THIS_MODULE; + + kdebug(("robin(robindriver_init): driver loaded; major device number = %d\n", robin_major)); + return(0); +} + + +/********************************/ +static void robindriver_exit(void) +/********************************/ +{ + if (unregister_chrdev(robin_major, "robin") != 0) + { + kerror(("robin(cleanup_module): cleanup_module failed\n")); + } + + kfree(proc_read_text); + remove_proc_entry("robin", NULL); + pci_unregister_driver(&robinPciDriver); + kdebug(("robin(cleanup_module): driver removed\n")); +} + + +/*****************************/ +/* PCI driver functions */ +/*****************************/ + +/**********************************************************************************/ +static int __devinit robinProbe(struct pci_dev *dev, const struct pci_device_id *id) //MJ: for _devinit see P31 +/**********************************************************************************/ +{ + int i; + u_long flags; + u_int baddr, size, card, bar2Available; + + // Check which type of ROBIN is installed + kdebug(("robin(robinProbe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device)); + kdebug(("robin(robinProbe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn)); + + //MJ: Should we check if the FPGA is loaded and exit if not? + + /* Find a new "slot" for the card */ + for (i = 0, card = -1; (i < MAXCARDS) && (card == -1); i++) + { + if (robincard[i].pciDevice == NULL) + { + card = i; + robincard[card].pciDevice = dev; + } + } + + if (card == -1) + { + kerror(("robin(robinProbe): Could not find space in the robincard array for this card.")); + return(-RD_TOOMANYCARDS); + } + + flags = pci_resource_flags(dev, PLX_BAR); //MJ: See P317 + if ((flags & IORESOURCE_MEM) != 0) + { + baddr = pci_resource_start(dev, PLX_BAR) & PCI_BASE_ADDRESS_MEM_MASK; //MJ: See P317 + size = pci_resource_end(dev, PLX_BAR) - pci_resource_start(dev, PLX_BAR); //MJ: See P317 + kdebug(("robin(robinProbe): PLX: Mapping %d bytes at PCI address 0x%08x\n", size, baddr)); + robincard[card].plx_regs = (u_int *)ioremap(baddr, size); + if (robincard[card].plx_regs == NULL) + { + kerror(("robin(robinProbe): error from ioremap for robincard[%d].plx_regs\n", card)); + robincard[card].pciDevice = NULL; + return(-RD_REMAP); + } + kdebug(("robin(robinProbe): robincard[%d].plx_regs is at 0x%08x\n", card, (u_int)robincard[card].plx_regs)); + + robincard[card].pci_memaddr_plx = baddr; + robincard[card].pci_memsize_plx = size; + } + else + { + kerror(("robin(robinProbe): Bar 0 is not a memory resource")); + return(-RD_BAR); + } + + flags = pci_resource_flags(dev, FPGA_BAR); + bar2Available = 1; + if ((flags & IORESOURCE_MEM) != 0) + { + baddr = pci_resource_start(dev, FPGA_BAR) & PCI_BASE_ADDRESS_MEM_MASK; + size = pci_resource_end(dev, FPGA_BAR) - pci_resource_start(dev, FPGA_BAR); + kdebug(("robin(robinProbe): FPGA: Mapping %d bytes at PCI address 0x%08x\n", size, baddr)); + robincard[card].fpga_regs = (u_int *)ioremap(baddr, size); + robincard[card].bar2 = baddr; + if (robincard[card].fpga_regs == NULL) + { + kerror(("robin(robinProbe): error from ioremap for robincard[%d].fpga_regs\n", card)); + return(-RD_REMAP); + } + kdebug(("robin(robinProbe): robincard[%d].fpga_regs is at 0x%08x\n", card, (u_int)robincard[card].fpga_regs)); + + robincard[card].pci_memaddr_bus = baddr; + robincard[card].pci_memsize_bus = size; + } + else + { + kerror(("robin(robinProbe): Warning! Bar 2 is not available")); + bar2Available = 0; + } + + //MJ: bar2Available is there for the rare case that a Robin is used un-initialized for production tests + if ((dev->device == 0x144) || (dev->device == 0x324)) + { + /* This is a ROBIN */ + robincard[card].nrols = 3; + } + else if ((dev->device == 0x9656) && (bar2Available = 1)) + { + /* This is a ROBIN Prototype */ + robincard[card].nrols = 2; + } + + maxcard += 1; + return(0); +} + + +/******************************************/ +static void robinRemove(struct pci_dev *dev) +/******************************************/ +{ + int i; + u_int card; + + /* Find the "slot" of the card */ + for (i = 0, card = -1; (i < MAXCARDS) && (card == -1); i++) + { + if (robincard[i].pciDevice == dev) + card = i; + } + + if (card == -1) + { + kerror(("robin(robinRemove): Could not find device to remove.")); + return; + } + + iounmap((void *)robincard[card].plx_regs); + iounmap((void *)robincard[card].fpga_regs); + + robincard[card].pciDevice = NULL; + robincard[card].pci_memaddr_plx = 0; + robincard[card].pci_memaddr_bus = 0; + robincard[card].pci_memsize_plx = 0; + robincard[card].pci_memsize_bus = 0; + robincard[card].mem_base = 0; + robincard[card].mem_size = 0; + robincard[card].plx_regs = NULL; + robincard[card].fpga_regs = NULL; + robincard[card].nrols = 0; + robincard[card].bar2 = 0; + robincard[i].fpgaConfigured = 0; + + maxcard -= 1; +} + + + +/*****************************/ +/* Standard driver functions */ +/*****************************/ + + +/*********************************************************/ +static int robin_open(struct inode *ino, struct file *file) +/*********************************************************/ +{ + u_int loop; + T_private_data *pdata; + + kdebug(("robin(robin_open): called from process %d\n", current->pid)) + pdata = (T_private_data *)kmalloc(sizeof(T_private_data), GFP_KERNEL); + if (pdata == NULL) + { + kerror(("robin(robin_open): error from kmalloc\n")) + return(-RD_KMALLOC); + } + + //I guess we have to protect code from being executed by parallel threads on a SMP machine. Therefore: + sema_init(&pdata->thread_sema, 1); + + for(loop = 0; loop < MAXCARDS; loop++) + { + pdata->dpm_handle[loop] = -1; + pdata->mem_handle[loop] = -1; + pdata->fifo_handle[loop] = 0; + } + + pdata->slot = -1; + + file->private_data = (char *)pdata; + return(0); +} + + +/************************************************************/ +static int robin_release(struct inode *ino, struct file *file) +/************************************************************/ +{ + T_private_data *pdata; + u_int ret; + + kdebug(("robin(robin_release): called from process %d\n", current->pid)) + + pdata = (T_private_data *)file->private_data; + kdebug(("robin(robin_release): Releasing orphaned resources for slot %d\n", pdata->slot)) + + if (pdata->slot != -1) + { + if (pdata->fifo_handle[pdata->slot] != 0) + { + kdebug(("robin(robin_release): Releasing %d FIFO slots for card %d\n", pdata->fifo_handle[pdata->slot], pdata->slot)) + msg_fifo_avail[pdata->slot] += pdata->fifo_handle[pdata->slot]; + } + + if (pdata->dpm_handle[pdata->slot] != -1) + { + kdebug(("robin(robin_release): Freeing handle %d of the DPM\n", pdata->dpm_handle[pdata->slot])) + ret = retmem(pdata->slot, 0, pdata->dpm_handle[pdata->slot]); + if (ret) + kerror(("robin(robin_release): retmem for DPM returned %d\n", ret)); + } + + if (pdata->mem_handle[pdata->slot] != -1) + { + kdebug(("robin(robin_release): Freeing handle %d of the PC MEM\n", pdata->mem_handle[pdata->slot])) + ret = retmem(pdata->slot, 1, pdata->mem_handle[pdata->slot]); + if (ret) + kerror(("robin(robin_release): retmem for PC MEM returned %d\n", ret)); + } + } + + kfree(file->private_data); + kdebug(("robin(robin_release): kfreed file private data @ %x\n", (u_int)file->private_data)) + file->private_data = NULL; // Required? + return(0); +} + + +/***********************************************************************************/ +static int robin_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) +/***********************************************************************************/ +{ + T_private_data *pdata; + int ret, slot; + + pdata = (T_private_data *)file->private_data; + kdebug(("robin(ioctl): file at 0x%08x, file private data at 0x%08x\n", (u_int)file, (u_int)pdata)) + + switch (cmd) + { + case LINK: + { + kdebug(("robin(ioctl,LINK): called from process %d\n", current->pid)) + + //As pdata->link_params has to be shared by all threads that are derived from the same + //open(/dev/robin) call we have to avoid that two threads use it at the same time. Therefore: + down(&pdata->thread_sema); + + if (copy_from_user(&pdata->link_params, (void *)arg, sizeof(T_link_params)) !=0) + { + kerror(("robin(ioctl,LINK): error from copy_from_user\n")); + up(&pdata->thread_sema); + return(-RD_CFU); + } + + if (robincard[pdata->link_params.slot].pciDevice == NULL) + { + up(&pdata->thread_sema); + return(-RD_NOCARD); + } + + kdebug(("robin(ioctl,LINK): params.slot = %d\n", pdata->link_params.slot)) + pdata->link_params.plx_base = robincard[pdata->link_params.slot].pci_memaddr_plx; + pdata->link_params.plx_size = robincard[pdata->link_params.slot].pci_memsize_plx; + pdata->link_params.fpga_base = robincard[pdata->link_params.slot].pci_memaddr_bus; + pdata->link_params.fpga_size = robincard[pdata->link_params.slot].pci_memsize_bus; + pdata->link_params.nrols = robincard[pdata->link_params.slot].nrols; + kdebug(("robin(ioctl,LINK): params.nrols = 0x%08x\n", robincard[pdata->link_params.slot].nrols)) + kdebug(("robin(ioctl,LINK): params.plx_base = 0x%08x\n", pdata->link_params.plx_base)) + kdebug(("robin(ioctl,LINK): params.plx_size = 0x%08x\n", pdata->link_params.plx_size)) + kdebug(("robin(ioctl,LINK): params.fpga_base = 0x%08x\n", pdata->link_params.fpga_base)) + kdebug(("robin(ioctl,LINK): params.fpga_size = 0x%08x\n", pdata->link_params.fpga_size)) + + if (copy_to_user((void *)arg, &pdata->link_params, sizeof(T_link_params)) != 0) + { + kerror(("robin(ioctl,LINK): error from copy_to_user\n")); + up(&pdata->thread_sema); + return(-RD_CTU); + } + + up(&pdata->thread_sema); + kdebug(("robin(ioctl,LINK): OK\n")) + break; + } + + case INIT_DMA: + { + kdebug(("robin(ioctl,INIT_DMA): called from process %d\n", current->pid)) + //This ioctl will only be called one from "robinconfigure". Therefore SMP and race conditions are not an issue + + if (copy_from_user(&pdata->init_dma_params, (void *)arg, sizeof(T_init_dma_params)) !=0) + { + kerror(("robin(ioctl,INIT_DMA): error from copy_from_user\n")); + return(-RD_CFU); + } + + //MJ: This is a bit of a trick. Before calling INIT_DMA "robinconfigure" checks via Jtag if the FPGA is loaded. + //Therefore proc_read and the TTY driver will only work once "robinconfigure" has been run + robincard[pdata->init_dma_params.slot].fpgaConfigured = 1; + + kdebug(("robin(ioctl,INIT_DMA): link_params.slot = %d\n", pdata->init_dma_params.slot)) + kdebug(("robin(ioctl,INIT_DMA): link_params.mem_base = 0x%08x\n", pdata->init_dma_params.mem_base)) + kdebug(("robin(ioctl,INIT_DMA): link_params.mem_size = 0x%08x\n", pdata->init_dma_params.mem_size)) + robincard[pdata->init_dma_params.slot].mem_base = pdata->init_dma_params.mem_base; + robincard[pdata->init_dma_params.slot].mem_size = pdata->init_dma_params.mem_size; + + //Initialize the memory manager + initmem(pdata->init_dma_params.slot, pdata->init_dma_params.mem_size, pdata->init_dma_params.mem_base); + + break; + } + + case MSG_REG: + { + kdebug(("robin(ioctl,MSG_REG): called from process %d\n", current->pid)); + + //As pdata->msg_req_params has to be shared by all threads that are derived from the same + //open(/dev/robin) call we have to avoid that two threads use it at the same time. Therefore: + down(&pdata->thread_sema); + + if (copy_from_user(&pdata->msg_req_params, (void *)arg, sizeof(T_msg_req_params)) !=0) + { + kerror(("robin(ioctl,LINK): error from copy_from_user\n")); + up(&pdata->thread_sema); + return(-RD_CFU); + } + + kdebug(("robin(ioctl,MSG_REG): msg_req_params.slot = %d\n", pdata->msg_req_params.slot)) + kdebug(("robin(ioctl,MSG_REG): msg_req_params.n_fifo = %d\n", pdata->msg_req_params.n_fifo)) + kdebug(("robin(ioctl,MSG_REG): msg_req_params.dpm_size = %d\n", pdata->msg_req_params.dpm_size)) + kdebug(("robin(ioctl,MSG_REG): msg_req_params.mem_size = %d\n", pdata->msg_req_params.mem_size)) + + //MJ: If pdata->slot already has a value assigned we should return an error + pdata->slot = pdata->msg_req_params.slot; + + if ((pdata->dpm_handle[pdata->msg_req_params.slot] != -1) || (pdata->mem_handle[pdata->msg_req_params.slot] != -1)) + { + kerror(("robin(ioctl,MSG_REG): This process has already requested message resources for card %d\n", pdata->msg_req_params.slot)); + kerror(("robin(ioctl,MSG_REG): pdata->dpm_handle[%d] = %d\n", pdata->msg_req_params.slot, pdata->dpm_handle[pdata->msg_req_params.slot])); + kerror(("robin(ioctl,MSG_REG): pdata->mem_handle[%d] = %d\n", pdata->msg_req_params.slot, pdata->mem_handle[pdata->msg_req_params.slot])); + up(&pdata->thread_sema); + return(-RD_ALREADYDONE); + } + + //Get a buffer in the DPM + ret = getmem(pdata->msg_req_params.slot, 0, pdata->msg_req_params.dpm_size, &pdata->msg_req_params.dpm_base, &pdata->dpm_handle[pdata->msg_req_params.slot]); + if (ret) + { + kerror(("robin(ioctl,MSG_REG): getmem for DPM returned %d\n", ret)); + up(&pdata->thread_sema); + return(ret); + } + + //Add the PCI base address of the DPM + pdata->msg_req_params.dpm_offset = pdata->msg_req_params.dpm_base; + pdata->msg_req_params.dpm_base += (robincard[pdata->msg_req_params.slot].pci_memaddr_bus + MESSAGE_RAM_OFFSET); + kdebug(("robin(ioctl,MSG_REG): getmem returns for DPM: base=0x%08x handle=%d\n", pdata->msg_req_params.dpm_base, pdata->dpm_handle[pdata->msg_req_params.slot])) + kdebug(("robin(ioctl,MSG_REG): pdata->msg_req_params.dpm_offset = 0x%08x\n", pdata->msg_req_params.dpm_offset)) + + //Get a buffer in the PC MEM + ret = getmem(pdata->msg_req_params.slot, 1, pdata->msg_req_params.mem_size, &pdata->msg_req_params.mem_base, &pdata->mem_handle[pdata->msg_req_params.slot]); + if (ret) + { + kerror(("robin(ioctl,MSG_REG): getmem for PC MEM returned %d\n", ret)); + up(&pdata->thread_sema); + return(ret); + } + kdebug(("robin(ioctl,MSG_REG): getmem returns for MEM: base=0x%08x handle=%d\n", pdata->msg_req_params.mem_base, pdata->mem_handle[pdata->msg_req_params.slot])) + + pdata->msg_req_params.mem_offset = pdata->msg_req_params.mem_base - robincard[pdata->msg_req_params.slot].mem_base; + kdebug(("robin(ioctl,MSG_REG): pdata->msg_req_params.mem_base = 0x%08x\n", pdata->msg_req_params.mem_base)) + kdebug(("robin(ioctl,MSG_REG): robincard[%d].mem_base = 0x%08x\n", pdata->msg_req_params.slot, robincard[pdata->msg_req_params.slot].mem_base)) + kdebug(("robin(ioctl,MSG_REG): pdata->msg_req_params.mem_offset = 0x%08x\n", pdata->msg_req_params.mem_offset)) + + //Get FIFO slots + kdebug(("robin(ioctl,MSG_REG): card=%d FIFO entries requested=%d FIFO entries available=%d\n", pdata->msg_req_params.slot, msg_fifo_avail[pdata->msg_req_params.slot], pdata->msg_req_params.n_fifo)); + if (msg_fifo_avail[pdata->msg_req_params.slot] < pdata->msg_req_params.n_fifo) + { + kerror(("robin(ioctl,MSG_REG): card=%d FIFO entries requested=%d FIFO entries available=%d\n", pdata->msg_req_params.slot, msg_fifo_avail[pdata->msg_req_params.slot], pdata->msg_req_params.n_fifo)); + up(&pdata->thread_sema); + return(-RD_FIFOEMPTY); + } + else + { + pdata->fifo_handle[pdata->msg_req_params.slot] = pdata->msg_req_params.n_fifo; + msg_fifo_avail[pdata->msg_req_params.slot] -= pdata->msg_req_params.n_fifo; + kdebug(("robin(ioctl,MSG_REG): %d entries in MSG FIFO allocated on card %d\n", pdata->msg_req_params.n_fifo, pdata->msg_req_params.slot)) + } + + if (copy_to_user((void *)arg, &pdata->msg_req_params, sizeof(T_msg_req_params)) != 0) + { + kerror(("robin(ioctl,MSG_REG): error from copy_to_user\n")); + up(&pdata->thread_sema); + return(-RD_CTU); + } + + up(&pdata->thread_sema); + break; + } + + case MSG_FREE: + { + kdebug(("robin(ioctl,MSG_FREE): called from process %d\n", current->pid)) + + if (copy_from_user(&slot, (void *)arg, sizeof(int)) !=0) + { + kerror(("robin(ioctl,MSG_FREE): error from copy_from_user\n")); + return(-RD_CFU); + } + + kdebug(("robin(ioctl,MSG_FREE): card = %d\n", slot)) + kdebug(("robin(ioctl,MSG_FREE): Freeing %d entries in the FIFO\n", pdata->fifo_handle[slot])) + msg_fifo_avail[slot] += pdata->fifo_handle[slot]; + pdata->fifo_handle[slot] = 0; + + kdebug(("robin(ioctl,MSG_FREE): Freeing handle %d of the DPM\n", pdata->dpm_handle[slot])) + ret = retmem(slot, 0, pdata->dpm_handle[slot]); + if (ret) + { + kerror(("robin(ioctl,MSG_FREE): retmem for DPM returned %d\n", ret)); + return(ret); + } + + kdebug(("robin(ioctl,MSG_FREE): Freeing handle %d of the PC MEM\n", pdata->mem_handle[slot])) + ret = retmem(slot, 1, pdata->mem_handle[slot]); + if (ret) + { + kerror(("robin(ioctl,MSG_FREE): retmem for PC MEM returned %d\n", ret)); + return(ret); + } + + pdata->dpm_handle[slot] = -1; + pdata->mem_handle[slot] = -1; + kdebug(("robin(ioctl,MSG_FREE): pdata->dpm_handle[%d] = %d\n", slot, pdata->dpm_handle[slot])) + kdebug(("robin(ioctl,MSG_FREE): pdata->mem_handle[%d] = %d\n", slot, pdata->mem_handle[slot])) + + break; + } + + case LINK_IRQ: + { + kdebug(("robin(ioctl,LINK_IRQ): called from process %d\n", current->pid)) + break; + } + } + return(0); +} + + +/*********************************************************************************************/ +static int robin_write_procmem(struct file *file, const char *buffer, u_long count, void *data) +/*********************************************************************************************/ +{ + int len; + struct robin_proc_data_t *fb_data = (struct robin_proc_data_t *)data; + + kdebug(("robin(robin_write_procmem): robin_write_procmem called\n")); + + if(count > 99) + len = 99; + else + len = count; + + if (copy_from_user(fb_data->value, buffer, len)) + { + kerror(("robin(robin_write_procmem): error from copy_from_user\n")); + return(-RD_CFU); + } + + kdebug(("robin(robin_write_procmem): len = %d\n", len)); + fb_data->value[len - 1] = '\0'; + kdebug(("robin(robin_write_procmem): text passed = %s\n", fb_data->value)); + + if (!strcmp(fb_data->value, "debug")) + { + debug = 1; + kdebug(("robin(robin_write_procmem): debugging enabled\n")); + } + + if (!strcmp(fb_data->value, "nodebug")) + { + kdebug(("robin(robin_write_procmem): debugging disabled\n")); + debug = 0; + } + + if (!strcmp(fb_data->value, "elog")) + { + kdebug(("robin(robin_write_procmem): Error logging enabled\n")) + errorlog = 1; + } + + if (!strcmp(fb_data->value, "noelog")) + { + kdebug(("robin(robin_write_procmem): Error logging disabled\n")) + errorlog = 0; + } + + return(len); +} + + +/***************************************************************************************************/ +static int robin_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data) +/***************************************************************************************************/ +{ + u_int card, value, v1, v2, v3, v4, v5, fpgadesign; + int ret, nchars = 0, loop1, loop2; + static int len = 0; + + kdebug(("robin(robin_read_procmem): Called with buf = 0x%08x\n", (unsigned int)buf)); + kdebug(("robin(robin_read_procmem): Called with *start = 0x%08x\n", (unsigned int)*start)); + kdebug(("robin(robin_read_procmem): Called with offset = %d\n", (unsigned int)offset)); + kdebug(("robin(robin_read_procmem): Called with count = %d\n", count)); + + if (offset == 0) + { + kdebug(("robin(robin_read_procmem): Creating text....\n")); + len = 0; + len += sprintf(proc_read_text + len, "\n\n\nROBIN driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG); + len += sprintf(proc_read_text + len, "Number of cards detected = %d\n", maxcard); + len += sprintf(proc_read_text + len, "\nSome PLX registers\n"); + len += sprintf(proc_read_text + len, "card | LAS0RR | LAS0BA | DMRR | DMLBAM | DMPBAM | CNTRL | INTCSR |\n"); + for (card = 0; card < maxcard; card++) + { + len += sprintf(proc_read_text + len, "%4d |", card); + len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_LAS0RR]); + len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_LAS0BA]); + len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_DMRR]); + len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_DMLBAM]); + len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_DMPBAM]); + len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].plx_regs[PLX_CNTRL]); + len += sprintf(proc_read_text + len, " 0x%08x |\n", robincard[card].plx_regs[PLX_INTCSR]); + } + + len += sprintf(proc_read_text + len, "\nSome other registers\n"); + len += sprintf(proc_read_text + len, "card | FPGA design ID | BAR2 |\n"); + for (card = 0; card < maxcard; card++) + { + len += sprintf(proc_read_text + len, "%4d |", card); + ret = fpgaLoaded(card, &fpgadesign); + if(ret == 0) + { + len += sprintf(proc_read_text + len, " 0x%08x |", robincard[card].fpga_regs[0]); + len += sprintf(proc_read_text + len, " 0x%08x |\n", robincard[card].bar2); + } + else if (ret == -1) + len += sprintf(proc_read_text + len, "robinconfig has not yet been run on this Robin\n"); + else + len += sprintf(proc_read_text + len, "The FPGA of this card (0x%08x) is out of date\n", fpgadesign); + } + + len += sprintf(proc_read_text + len, "\nS-Link status bits\n"); + len += sprintf(proc_read_text + len, "Card | ROL | Link err. | Link up | Xoff | Link active | ROL Emulation |\n"); + for (card = 0; card < maxcard; card++) + { + ret = fpgaLoaded(card, &fpgadesign); + if(ret == 0) + { + value = robincard[card].fpga_regs[SLINK_REG_OFFSET >> 2]; + + v1 = (value >> 0) & 0x1; + v2 = (value >> 1) & 0x1; + v3 = (value >> 2) & 0x1; + v4 = (value >> 3) & 0x1; + v5 = (value >> 12) & 0x1; + len += sprintf(proc_read_text + len, " %d | 0 | %s | %s | %s | %s | %s |\n", card, v1?" No":"Yes", v2?" No":"Yes", v3?" No":"Yes", v4?" No":"Yes", v5?"Yes":" No"); + + v1 = (value >> 4) & 0x1; + v2 = (value >> 5) & 0x1; + v3 = (value >> 6) & 0x1; + v4 = (value >> 7) & 0x1; + v5 = (value >> 13) & 0x1; + len += sprintf(proc_read_text + len, " %d | 1 | %s | %s | %s | %s | %s |\n", card, v1?" No":"Yes", v2?" No":"Yes", v3?" No":"Yes", v4?" No":"Yes", v5?"Yes":" No"); + + v1 = (value >> 8) & 0x1; + v2 = (value >> 9) & 0x1; + v3 = (value >> 10) & 0x1; + v4 = (value >> 11) & 0x1; + v5 = (value >> 14) & 0x1; + len += sprintf(proc_read_text + len, " %d | 2 | %s | %s | %s | %s | %s |\n", card, v1?" No":"Yes", v2?" No":"Yes", v3?" No":"Yes", v4?" No":"Yes", v5?"Yes":" No"); + } + else if (ret == -1) + len += sprintf(proc_read_text + len, "robinconfig has not yet been run on this Robin\n"); + else + len += sprintf(proc_read_text + len, "The FPGA of this card (0x%08x) is out of date\n", fpgadesign); + } + + len += sprintf(proc_read_text + len, "\nDumping status of Message FIFOs\n"); + for(loop1 = 0; loop1 < maxcard; loop1++) + len += sprintf(proc_read_text + len,"There are %d FIFO slots available on card %d\n", msg_fifo_avail[loop1], loop1); + + len += sprintf(proc_read_text + len, "\nDumping status of Dual Ported Memory\n"); + len += sprintf(proc_read_text + len,"Index | Status | Start | Size\n"); + for(loop1 = 0; loop1 < maxcard; loop1++) + { + len += sprintf(proc_read_text + len, "Card %d\n", loop1); + for(loop2 = 0; loop2 < MAX_BLOCKLETS; loop2++) + { + len += sprintf(proc_read_text + len,"%5d |", loop2); + len += sprintf(proc_read_text + len,"%7d |", blocklets[loop1][0][loop2].used); + len += sprintf(proc_read_text + len," 0x%08x |", blocklets[loop1][0][loop2].start); + len += sprintf(proc_read_text + len," 0x%08x\n", blocklets[loop1][0][loop2].size); + } + } + + len += sprintf(proc_read_text + len,"\nDumping status of PC direct-DMA Memory\n"); + len += sprintf(proc_read_text + len,"Index | Status | Start | Size\n"); + for(loop1 = 0; loop1 < maxcard; loop1++) + { + len += sprintf(proc_read_text + len, "Card %d\n", loop1); + for(loop2 = 0; loop2 < MAX_BLOCKLETS; loop2++) + { + len += sprintf(proc_read_text + len,"%5d |", loop2); + len += sprintf(proc_read_text + len,"%7d |", blocklets[loop1][1][loop2].used); + len += sprintf(proc_read_text + len," 0x%08x |", blocklets[loop1][1][loop2].start); + len += sprintf(proc_read_text + len," 0x%08x\n", blocklets[loop1][1][loop2].size); + } + } + + len += sprintf(proc_read_text + len, " \n"); + len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/robin', executed as root,\n"); + len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n"); + len += sprintf(proc_read_text + len, "debug -> enable debugging\n"); + len += sprintf(proc_read_text + len, "nodebug -> disable debugging\n"); + len += sprintf(proc_read_text + len, "elog -> Log errors to /var/log/message\n"); + len += sprintf(proc_read_text + len, "noelog -> Do not log errors to /var/log/message\n"); + kdebug(("robin(robin_read_procmem): Number of characters created = %d\n", len)); + } + + + if (count < (len - offset)) + nchars = count; + else + nchars = len - offset; + kdebug(("robin(robin_read_procmem): min nchars = %d\n", nchars)); + kdebug(("robin(robin_read_procmem): position = %ld\n", (offset & (PAGE_SIZE - 1)))); + + if (nchars > 0) + { + for (loop1 = 0; loop1 < nchars; loop1++) + buf[loop1 + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop1]; + *start = buf + (offset & (PAGE_SIZE - 1)); + } + else + { + nchars = 0; + *eof = 1; + } + + kdebug(("robin(robin_read_procmem): returning *start = 0x%08x\n", (unsigned int)*start)); + kdebug(("robin(robin_read_procmem): returning nchars = %d\n", nchars)); + return(nchars); +} + + +//------------------ +// Service functions +//------------------ + + +/******************************************************************/ +static int robin_mmap(struct file *file, struct vm_area_struct *vma) +/******************************************************************/ +{ + u_long offset, size; + + kdebug(("robin(robin_mmap): function called\n")); + + offset = vma->vm_pgoff << PAGE_SHIFT; + size = vma->vm_end - vma->vm_start; + kdebug(("robin(robin_mmap): offset = 0x%08x\n",(u_int)offset)); + kdebug(("robin(robin_mmap): size = 0x%08x\n",(u_int)size)); + + if (offset & ~PAGE_MASK) + { + kerror(("robin(robin_mmap): offset not aligned: %ld\n", offset)); + return(-RD_MMAP); + } + + // we only support shared mappings. "Copy on write" mappings are + // rejected here. A shared mapping that is writeable must have the + // shared flag set. + if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) + { + kerror(("robin(robin_mmap): writeable mappings must be shared, rejecting\n")); + return(-RD_NOSHARE); + } + + vma->vm_flags |= VM_RESERVED; + + // we do not want to have this area swapped out, lock it + vma->vm_flags |= VM_LOCKED; + + // we create a mapping between the physical pages and the virtual + // addresses of the application with remap_page_range. + // enter pages into mapping of application + kdebug(("robin(robin_mmap): Parameters of remap_page_range()\n")); + kdebug(("robin(robin_mmap): Virtual address = 0x%08x\n",(u_int)vma->vm_start)); + kdebug(("robin(robin_mmap): Physical address = 0x%08x\n",(u_int)offset)); + kdebug(("robin(robin_mmap): Size = 0x%08x\n",(u_int)size)); + if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot)) + { + kerror(("robin(robin_mmap): remap page range failed\n")); + return(-RD_RPR); + } + + vma->vm_ops = &robin_vm_ops; + + kdebug(("robin(robin_mmap): function done\n")); + return(0); +} + + +/**************************************************/ +static void robin_vclose(struct vm_area_struct *vma) +/**************************************************/ +{ + kdebug(("robin(robin_vclose): Virtual address = 0x%08x\n",(u_int)vma->vm_start)); + kdebug(("robin(robin_vclose): mmap released.\n")); +} + + +/****************************************************/ +static int initmem(u_int card, u_int size, u_int base) +/****************************************************/ +{ + int loop1, loop2; + + kdebug(("robin(initmem): Initializing memory management structures for card %d\n", card)); + + for(loop1 = 0; loop1 < BLOCK_TYPES; loop1++) + { + for(loop2 = 0; loop2 < MAX_BLOCKLETS; loop2++) + { + blocklets[card][loop1][loop2].used = BLOCKLET_UNUSED; + blocklets[card][loop1][loop2].start = 0; + blocklets[card][loop1][loop2].size = 0; + } + } + + //Initialise the DPM buffer + blocklets[card][0][0].used = BLOCKLET_FREE; + blocklets[card][0][0].start = 0; + blocklets[card][0][0].size = MESSAGE_RAM_SIZE; + + //Initialise the PC MEM buffer + blocklets[card][1][0].used = BLOCKLET_FREE; + blocklets[card][1][0].start = base; + blocklets[card][1][0].size = size; + + msg_fifo_avail[card] = MESSAGE_FIFO_DEPTH; + kdebug(("robin(initmem): msg_fifo_avail has been initialised to %d\n", MESSAGE_FIFO_DEPTH)); + + return(0); +} + + +/************************************************************************************/ +static int getmem(u_int card, u_int type, u_int size, u_int *start_add, u_int *handle) +/************************************************************************************/ +{ + int loop, b1 = -1 , b2 = -1; + + kdebug(("robin(getmem): called with card=%d type=%d size=%d\n", card, type, size)); + + kdebug(("robin(getmem): looking for space...\n")); + //look for a large enough free blocklet + for(loop = 0; loop < MAX_BLOCKLETS; loop++) + { + kdebug(("robin(getmem): blocklets[%d][%d][%d].used = %d\n", card, type, loop, blocklets[card][type][loop].used)); + kdebug(("robin(getmem): blocklets[%d][%d][%d].size = %d\n", card, type, loop, blocklets[card][type][loop].size)); + if (blocklets[card][type][loop].used == BLOCKLET_FREE && blocklets[card][type][loop].size >= size) + { + b1 = loop; + break; + } + } + + kdebug(("robin(getmem): looking for free slot...\n")); + //look for an empty slot in the array to hold the new blocklet + for(loop = 0; loop < MAX_BLOCKLETS; loop++) + { + kdebug(("robin(getmem): blocklets[%d][%d][%d].used = %d\n", card, type, loop, blocklets[card][type][loop].used)); + if (blocklets[card][type][loop].used == BLOCKLET_UNUSED) + { + b2 = loop; + break; + } + } + + if (b1 == -1) + { + kerror(("robin(getmem): failed to find free memory\n")) + return(-RD_NOMEMORY); + } + + if (b2 == -1) + { + kerror(("robin(getmem): failed to find an enpty slot\n")) + return(-RD_NOSLOT); + } + + blocklets[card][type][b2].used = BLOCKLET_USED; + blocklets[card][type][b2].size = size; + blocklets[card][type][b2].start = blocklets[card][type][b1].start; + + blocklets[card][type][b1].size -= size; + if (blocklets[card][type][b1].size == 0) + { + blocklets[card][type][b1].used = BLOCKLET_UNUSED; + blocklets[card][type][b1].start = 0; + } + else + blocklets[card][type][b1].start += size; + + *start_add = blocklets[card][type][b2].start; + *handle = b2; + + return(0); +} + + +/*****************************************************/ +static int retmem(u_int card, u_int type, u_int handle) +/*****************************************************/ +{ + int loop, bprev = -1, bnext = -1; + + kdebug(("robin(retmem): called with card=%d type=%d handle=%d\n", card, type, handle)); + if (blocklets[card][type][handle].used != BLOCKLET_USED) + return(-RD_ILLHANDLE); + + blocklets[card][type][handle].used = BLOCKLET_FREE; + + //Check if the blocklet can be merged with a predecessor + for(loop = 0; loop < MAX_BLOCKLETS; loop++) + { + if (blocklets[card][type][loop].used == BLOCKLET_FREE && (blocklets[card][type][loop].start + blocklets[card][type][loop].size) == blocklets[card][type][handle].start) + { + bprev = loop; + break; + } + } + + //Check if the blocklet can be merged with a sucessor + for(loop = 0; loop < MAX_BLOCKLETS; loop++) + { + if (blocklets[card][type][loop].used == BLOCKLET_FREE && (blocklets[card][type][handle].start + blocklets[card][type][handle].size) == blocklets[card][type][loop].start) + { + bnext = loop; + break; + } + } + + if (bprev != -1) + { + kdebug(("robin(retmem): merging %d with predecessor %d\n", handle, bprev)); + blocklets[card][type][handle].start = blocklets[card][type][bprev].start; + blocklets[card][type][handle].size += blocklets[card][type][bprev].size; + blocklets[card][type][bprev].used = BLOCKLET_UNUSED; + blocklets[card][type][bprev].start = 0; + blocklets[card][type][bprev].size = 0; + } + + if (bnext != -1) + { + kdebug(("robin(retmem): merging %d with sucessor %d\n", handle, bnext)); + blocklets[card][type][handle].size += blocklets[card][type][bnext].size; + blocklets[card][type][bnext].used = BLOCKLET_UNUSED; + blocklets[card][type][bnext].start = 0; + blocklets[card][type][bnext].size = 0; + } + return(0); +} + + + +/**************************************************/ +static int readFpga(u_int card, u_int addressOffset) +/**************************************************/ +{ + u_int fpgadesign; + int value = 0; + void *address; + + kdebug(("robin(readFpga): function called\n")) + + if ((robincard[card].fpga_regs != NULL) && (addressOffset < robincard[card].pci_memsize_bus)) + { + address = (void *)(robincard[card].fpga_regs + addressOffset); + if (fpgaLoaded(card, &fpgadesign) == 0) + { + value = ioread32(address); + kdebug(("robin(readFpga): 0x%08x read from FPGA of card %d at offset 0x%08x\n", value, card, addressOffset)) + } + return value; + } + else + return 0; +} + + +/**************************************************************/ +static void writeFpga(u_int card, u_int addressOffset, int data) +/**************************************************************/ +{ + u_int fpgadesign; + void *address; + + kdebug(("robin(writeFpga): function called\n")) + + if ((robincard[card].fpga_regs != NULL) && (addressOffset < robincard[card].pci_memsize_bus)) + { + address = (void *)(robincard[card].fpga_regs + addressOffset); + if (fpgaLoaded(card, &fpgadesign) == 0) + iowrite32(data, robincard[card].fpga_regs + addressOffset); + } +} + + +/**************************************************/ +static int fpgaLoaded(u_int card, u_int *fpgadesign) +/**************************************************/ +{ + int value, version, majorRevision, minorRevision; + int eversion, emajorRevision, eminorRevision; + + kdebug(("robin(fpgaLoaded): function called\n")) + + eversion = DESIGN_ID >> 24; + emajorRevision = (DESIGN_ID >> 16) & 0xff; + eminorRevision = DESIGN_ID & 0x0fff; + + if ((card < maxcard) && (robincard[card].fpgaConfigured == 1)) + { + value = ioread32((void *) (robincard[card].fpga_regs)); + *fpgadesign = value; + + version = value >> 24; + majorRevision = (value >> 16) & 0xff; + minorRevision = value & 0x0fff; + + kdebug(("robin(fpgaLoaded): Expected = 0x%08x\n", DESIGN_ID)); + kdebug(("robin(fpgaLoaded): eversion: 0x%x\n", eversion)); + kdebug(("robin(fpgaLoaded): emajorRevision: 0x%x\n", emajorRevision)); + kdebug(("robin(fpgaLoaded): eminorRevision: 0x%x\n", eminorRevision)); + kdebug(("robin(fpgaLoaded): Detected = 0x%08x\n", value)); + kdebug(("robin(fpgaLoaded): version: 0x%x\n", version)); + kdebug(("robin(fpgaLoaded): majorRevision: 0x%x\n", majorRevision)); + kdebug(("robin(fpgaLoaded): minorRevision: 0x%x\n", minorRevision)); + + if ((eversion != version) || (emajorRevision != majorRevision)) + { + kerror(("robin(fpgaLoaded): Incompatible FPGA version. Can't continue!\n")); + return -2; + } + else + { + if (eminorRevision > minorRevision) + { + kerror(("robin(fpgaLoaded): Incompatible FPGA revision (too small). Can't continue!\n")); + return -2; + } + if (eminorRevision < minorRevision) + kdebug(("robin(fpgaLoaded): Actual FPGA revision too high. Not all FPGA features might be exploited\n")); + } + return 0; + } + else + { + kdebug(("robin(fpgaLoaded): robinconfig has not yet been called on this Robin\n")); + *fpgadesign = 0; + return -1; + } +} + + +/*************************/ +static int getCardNum(void) +/*************************/ +{ + return(maxcard); +} + + +EXPORT_SYMBOL(readFpga); +EXPORT_SYMBOL(writeFpga); +EXPORT_SYMBOL(fpgaLoaded); +EXPORT_SYMBOL(getCardNum); diff --git a/cmt/solar.c b/cmt/solar.c new file mode 100644 index 0000000000000000000000000000000000000000..d34045550d1a116d69af262bfb3252867189ad86 --- /dev/null +++ b/cmt/solar.c @@ -0,0 +1,893 @@ +/************************************************************************/ +/* */ +/* File: solar_driver.c */ +/* */ +/* driver for the SOLAR S-Link interface */ +/* */ +/* 2. Apr. 03 MAJO created */ +/* */ +/************ C 2005 - The software with that certain something *********/ + +/************************************************************************/ +/*NOTES: */ +/*- This driver should work on kernels from 2.6.9 onwards */ +/************************************************************************/ + + +#include <linux/init.h> //MJ: for 2.6, p30 +#include <linux/module.h> //p30 +#include <linux/moduleparam.h> //MJ: for 2.6, p30 +#include <linux/kernel.h> //e.g. for printk +#include <linux/stat.h> //MJ: for 2.6, e.g. for module_param +#include <linux/pci.h> //e.g. for pci_find_device +#include <linux/errno.h> //e.g. for EFAULT +#include <linux/fs.h> //e.g. for register_chrdev +#include <linux/proc_fs.h> //e.g. for create_proc_entry +#include <linux/mm.h> //e.g. for vm_operations_struct +#include <linux/slab.h> //e.g. for kmalloc +#include <linux/cdev.h> //e.g. for cdev_alloc +#include <linux/delay.h> +#include <linux/interrupt.h> //e.g. for request_irq +#include <asm/uaccess.h> //e.g. for copy_from_user +#include <asm/io.h> //e.g. for inl +#include <asm/system.h> //e.g. for wmb +#include "ROSsolar/solar_driver.h" + + +/*********/ +/*Globals*/ +/*********/ +static int debug = 0; +static char *proc_read_text; +static u_int irqlist[MAXIRQ], infifor[MAXCARDS], served[MAXCARDS]; +static u_int maxcards, *infifo, *infifon, order, swfifobasephys, infifodatasize, fifoptrsize, tsize = 0; +static u_long swfifobase; +static struct proc_dir_entry *solar_file; +static struct cdev *solar_cdev; +struct solar_proc_data_t solar_proc_data; +static dev_t major_minor; +static T_solar_card solarcard[MAXCARDS]; + +/************/ +/*Prototypes*/ +/************/ +static int __devinit solarProbe(struct pci_dev *dev, const struct pci_device_id *id); +static void solar_cleanup(void); +static void solar_vmaOpen(struct vm_area_struct *vma); +static void solar_vmaClose(struct vm_area_struct *vma); +static void solarRemove(struct pci_dev *dev); +static int solar_init(void); +static irqreturn_t solar_irq_handler (int irq, void *dev_id, struct pt_regs *regs); + +/***************************************************************/ +/* Use /sbin/modinfo <module name> to extract this information */ +/***************************************************************/ +MODULE_DESCRIPTION("S-Link SOLAR"); +MODULE_AUTHOR("Markus Joos, CERN/PH"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("Private: Contact markus.joos@cern.ch"); +module_param (debug, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(debug, "1 = enable debugging 0 = disable debugging"); + + +//MJ: Not actually required. Just for kdebug +struct vm_operations_struct solar_vm_ops = +{ + .close = solar_vmaClose, + .open = solar_vmaOpen, //MJ: Note the comma at the end of the list! +}; + + +static struct file_operations fops = +{ + .owner = THIS_MODULE, + .ioctl = solar_ioctl, + .open = solar_open, + .mmap = solar_mmap, + .release = solar_release, +}; + +// PCI driver structures. See p311 +static struct pci_device_id solarIds[] = +{ + {PCI_DEVICE(PCI_VENDOR_ID_CERN, SOLAR_PCI_DEVICE_ID)}, + {0,}, +}; + +//PCI operations +static struct pci_driver solarPciDriver = +{ + .name = "solar", + .id_table = solarIds, + .probe = solarProbe, + .remove = solarRemove, +}; + + + +/*****************************/ +/* Standard driver functions */ +/*****************************/ + +/*************************/ +static int solar_init(void) +/*************************/ +{ + int card, tfsize, result; + u_int loop; + struct page *page_ptr; + + if (alloc_chrdev_region(&major_minor, 0, 1, "solar")) //MJ: for 2.6 p45 + { + kdebug(("solar(solar_init): failed to obtain device numbers\n")); + result = -EIO; + goto fail1; + } + + proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL); + if (proc_read_text == NULL) + { + kdebug(("solar(solar_init): error from kmalloc\n")); + result = -EFAULT; + goto fail2; + } + + solar_file = create_proc_entry("solar", 0644, NULL); + if (solar_file == NULL) + { + kdebug(("solar(solar_init): error from call to create_proc_entry\n")); + result = -EFAULT; + goto fail3; + } + + strcpy(solar_proc_data.name, "solar"); + strcpy(solar_proc_data.value, "solar"); + solar_file->data = &solar_proc_data; + solar_file->read_proc = solar_read_procmem; + solar_file->write_proc = solar_write_procmem; + solar_file->owner = THIS_MODULE; + + kdebug(("solar(solar_init): registering PCIDriver\n")); + result = pci_register_driver(&solarPciDriver); //MJ: See P312 + if (result == 0) + { + kdebug(("solar(solar_init): ERROR! no SOLAR cards found!\n")); + result = -EIO; + goto fail4; + } + else + { + kdebug(("solar(solar_init): Found %d SOLAR cards!\n", result)); + } + + // Allocate contiguous memory for the FIFOs and store the base addresses in a global structure + infifodatasize = MAXCARDS * MAXINFIFO * sizeof(u_int) * 2; //infifo + fifoptrsize = MAXCARDS * sizeof(u_int); //infifo(w/r/n) + kdebug(("solar(solar_init): 0x%08x bytes needed for the IN FIFO\n", infifodatasize)); + kdebug(("solar(solar_init): 0x%08x bytes needed for the FIFO pointers\n", fifoptrsize)); + + tsize = infifodatasize + fifoptrsize; + kdebug(("solar(solar_init): 0x%08x bytes needed for all FIFOs\n", tsize)); + + tfsize = (tsize + 4095) & 0xfffff000; // round up to next 4K boundary + tfsize = tfsize >> 12; // compute number of pages + kdebug(("solar(solar_init): %d pages needed for the S/W FIFOs\n", tfsize)); + + order = 0; + while(tfsize) + { + order++; + tfsize = tfsize >> 1; // compute order + } + kdebug(("solar(solar_init): order = %d\n", order)); + + swfifobase = __get_free_pages(GFP_ATOMIC, order); + if (!swfifobase) + { + kdebug(("rsolar(solar_init): error from __get_free_pages\n")); + result = -EFAULT; + goto fail5; + } + kdebug(("solar(solar_init): swfifobase = 0x%016lx\n", swfifobase)); + + // Reserve all pages to make them remapable + page_ptr = virt_to_page(swfifobase); + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + set_bit(PG_reserved, &page_ptr->flags); + + swfifobasephys = virt_to_bus((void *) swfifobase); + kdebug(("solar(solar_init): swfifobasephys = 0x%08x\n", swfifobasephys)); + + // Assign base addresses to the FIFO arrays + infifo = (u_int *)swfifobase; + infifon = (u_int *)(swfifobase + infifodatasize); + kdebug(("solar(solar_init): infifo is at 0x%016lx\n", (u_long)infifo)); + kdebug(("solar(solar_init): infifon is at 0x%016lx\n", (u_long)infifon)); + + //Initialize the FIFOs + for(card = 0; card < MAXCARDS; card++) + { + infifor[card] = 0; + infifon[card] = 0; + } + + solar_cdev = (struct cdev *)cdev_alloc(); //MJ: for 2.6 p55 + solar_cdev->ops = &fops; + result = cdev_add(solar_cdev, major_minor, 1); //MJ: for 2.6 p56 + if (result) + { + kdebug(("solar(solar_init): error from call to cdev_add.\n")); + goto fail6; + } + + + kdebug(("solar(solar_init): driver loaded; major device number = %d\n", MAJOR(major_minor))); + return(0); + + fail6: + page_ptr = virt_to_page(swfifobase); + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + free_pages(swfifobase, order); + + fail5: + pci_unregister_driver(&solarPciDriver); + + fail4: + remove_proc_entry("io_rcc", NULL); + + fail3: + kfree(proc_read_text); + + fail2: + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + + fail1: + return(result); +} + + +/*****************************/ +static void solar_cleanup(void) +/*****************************/ +{ + u_int loop; + struct page *page_ptr; + + cdev_del(solar_cdev); //MJ: for 2.6 p56 + pci_unregister_driver(&solarPciDriver); + + // unreserve all pages used for the S/W FIFOs + page_ptr = virt_to_page(swfifobase); + for (loop = (1 << order); loop > 0; loop--, page_ptr++) + clear_bit(PG_reserved, &page_ptr->flags); + + // free the area + free_pages(swfifobase, order); + remove_proc_entry("solar", NULL); + kfree(proc_read_text); + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + + kdebug(("solar(solar_cleanup): driver removed\n")); +} + + +module_init(solar_init); //MJ: for 2.6 p16 +module_exit(solar_cleanup); //MJ: for 2.6 p16 + + +/**********************************************************/ +static int solar_open(struct inode *ino, struct file *filep) +/**********************************************************/ +{ + kdebug(("solar(solar_open): nothing to be done\n")); + return(0); +} + + +/*************************************************************/ +static int solar_release(struct inode *ino, struct file *filep) +/*************************************************************/ +{ + kdebug(("solar(solar_release): nothing to be done\n")); + return(0); +} + +/***********************************************************************************/ +static int solar_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) +/***********************************************************************************/ +{ + switch (cmd) + { + case OPEN: + { + SOLAR_open_t params; + + params.size = tsize; + params.physbase = swfifobasephys; + params.insize = infifodatasize; + kdebug(("solar(ioctl,OPEN): params.size = 0x%08x\n", params.size)); + kdebug(("solar(ioctl,OPEN): params.physbase = 0x%08x\n", params.physbase)); + kdebug(("solar(ioctl,OPEN): params.insize = 0x%08x\n", params.insize)); + + if (copy_to_user((void *)arg, ¶ms, sizeof(SOLAR_open_t))) + { + kdebug(("solar(ioctl, OPEN): error from copy_to_user\n")); + return(-SOLAR_EFAULT); + } + return(0); + break; + } + + case CONFIG: + { + SOLAR_config_t params; + u_int card, data; + + if (copy_from_user(¶ms, (void *)arg, sizeof(SOLAR_config_t))) + { + kdebug(("solar(ioctl,CONFIG): error from copy_from_user\n")); + return(-SOLAR_EFAULT); + } + kdebug(("solar(ioctl,CONFIG): \n")); + kdebug(("solar(ioctl,CONFIG): params.bswap = %d\n", params.bswap)); + kdebug(("solar(ioctl,CONFIG): params.wswap = %d\n", params.wswap)); + kdebug(("solar(ioctl,CONFIG): params.scw = 0x%08x\n", params.scw)); + kdebug(("solar(ioctl,CONFIG): params.ecw = 0x%08x\n", params.ecw)); + kdebug(("solar(ioctl,CONFIG): params.udw = %d\n", params.udw)); + kdebug(("solar(ioctl,CONFIG): params.clksel = %d\n", params.clksel)); + kdebug(("solar(ioctl,CONFIG): params.clkdiv = %d\n", params.clkdiv)); + + for(card = 0; card < maxcards; card++) + { + data = 0; + solarcard[card].regs->opctrl = (params.udw << 18) + (params.wswap << 2) + (params.bswap << 1); + solarcard[card].regs->bctrlw = params.scw & 0xfffffffc; + solarcard[card].regs->ectrlw = params.ecw & 0xfffffffc; + solarcard[card].regs->opfeat = (params.clksel << 31) + (params.clkdiv << 29); + + //The LFF and LDOWN interrupts are not yet supported + //If enabled the REQ interrupt has a fixed threshold of 32 + //This is to reduce the IRQ frequency + solarcard[card].regs->intmask = INTERRUPT_THRESHOLD; + served[card] = 0; + } + break; + } + + case RESET: + { + u_int data, card; + + if (copy_from_user(&card, (void *)arg, sizeof(int))) + { + kdebug(("solar(ioctl,RESET): error from copy_from_user\n")); + return(-SOLAR_EFAULT); + } + + kdebug(("solar(ioctl,RESET): Resetting card %d\n", card)); + data = solarcard[card].regs->opctrl; + data |= 0x1; + solarcard[card].regs->opctrl = data; + udelay(2); // Delay for at least one us + data &= 0xfffffffe; + solarcard[card].regs->opctrl = data; + + //reset the FIFOs + infifor[card] = 0; + infifon[card] = 0; + + return(0); + break; + } + + case LINKRESET: + { + u_int waited = 0, card; + + if (copy_from_user(&card, (void *)arg, sizeof(int))) + { + kdebug(("solar(ioctl,LINKRESET): error from copy_from_user\n")); + return(-SOLAR_EFAULT); + } + + kdebug(("solar(ioctl,LINKRESET): Resetting link of card %d\n", card)); + + solarcard[card].regs->opctrl |= 0x00020000; // Set the URESET bit + udelay(2000); // Delay for at least two ms + while(solarcard[card].regs->opstat & 0x00040000) // Now wait for LDOWN to come up again + { + waited++; + if (waited > 10000) + { + kdebug(("solar(ioctl,LINKRESET): card %d does not come up again\n", card)); + solarcard[card].regs->opctrl &= 0xfffdffff; // Reset the URESET bit + return(-SOLAR_STUCK); + } + } + + // Reset the URESET bit + solarcard[card].regs->opctrl &= 0xfffdffff; + served[card] = 0; + return(0); + break; + } + + case LINKSTATUS: + { + u_long flags; + SOLAR_status_t params; + + if (copy_from_user(¶ms, (void *)arg, sizeof(SOLAR_status_t))) + { + kdebug(("solar(ioctl, LINKSTATUS): error from copy_from_user\n")); + return(-SOLAR_EFAULT); + } + + if (solarcard[params.card].regs->opstat & 0x00040000) + params.ldown = 1; + else + params.ldown = 0; + + //We have to make sure that this code does not get interrupted. + local_irq_save(flags); //MJ: for 2.6 see p274 & p275 + params.infifo = infifon[params.card]; + params.reqfifo = REQFIFODEPTH - ((solarcard[params.card].regs->opstat) & 0x3f); + kdebug(("solar(ioctl, LINKSTATUS): params.ldown = %d\n", params.ldown)); + kdebug(("solar(ioctl, LINKSTATUS): params.infifo = %d\n", params.infifo)); + kdebug(("solar(ioctl, LINKSTATUS): params.reqfifo = %d\n", params.reqfifo)); + local_irq_restore(flags); //MJ: for 2.6 see p274 & p275 + + if (copy_to_user((void *)arg, ¶ms, sizeof(SOLAR_status_t))) + { + kdebug(("solar(ioctl, LINKSTATUS): error from copy_to_user\n")); + return(-SOLAR_EFAULT); + } + + return(0); + break; + } + + case INFO: + { + SOLAR_info_t info; + u_int card; + + //Initialise the array to 1 (it is not possible to disable a SOLAR card via a register) + for(card = 0; card < MAXCARDS; card++) + { + info.cards[card] = 0; + info.enabled[card] = 0; + } + + for(card = 0; card < maxcards; card++) + { + info.cards[card] = 1; + info.enabled[card] = 1; + } + + info.ncards = maxcards; + kdebug(("solar(ioctl, INFO): maxcards = %d\n", maxcards)); + + if (copy_to_user((void *)arg, &info, sizeof(SOLAR_info_t))) + { + kdebug(("solar(ioctl, INFO): error from copy_to_user\n")); + return(-SOLAR_EFAULT); + } + return(0); + break; + } + + case FIFOIN: + { + u_int card; + + if (copy_from_user(&card, (void *)arg, sizeof(int)) !=0) + { + kdebug(("solar(ioctl,FIFOIN): error from copy_from_user\n")); + return(-SOLAR_EFAULT); + } + kdebug(("solar(ioctl,FIFOIN): card = %d\n", card)); + + // (re)enable the interupt. Once the REQ FIFO is empty the ISR will fill it with the new requests + solarcard[card].regs->intmask = INTERRUPT_THRESHOLD; + + return(0); + break; + } + } + return(0); +} + + +/***************************************************/ +static void solar_vmaOpen(struct vm_area_struct *vma) +/***************************************************/ +{ + kdebug(("solar(solar_vmaOpen): Called\n")); +} + + +/****************************************************/ +static void solar_vmaClose(struct vm_area_struct *vma) +/****************************************************/ +{ + kdebug(("solar(solar_vmaClose): Virtual address = 0x%016lx\n",(u_long)vma->vm_start)); + kdebug(("solar(solar_vmaClose): mmap released\n")); +} + +/******************************************************************/ +static int solar_mmap(struct file *file, struct vm_area_struct *vma) +/******************************************************************/ +{ + u_long offset, size; + + kdebug(("solar(solar_mmap): function called\n")); + vma->vm_flags |= VM_RESERVED; + vma->vm_flags |= VM_LOCKED; + + kdebug(("solar(solar_mmap): vma->vm_end = 0x%016lx\n", (u_long)vma->vm_end)); + kdebug(("solar(solar_mmap): vma->vm_start = 0x%016lx\n", (u_long)vma->vm_start)); + kdebug(("solar(solar_mmap): vma->vm_offset = 0x%016lx\n", (u_long)vma->vm_pgoff << PAGE_SHIFT)); + kdebug(("solar(solar_mmap): vma->vm_flags = 0x%08x\n", (u_int)vma->vm_flags)); + + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + + kdebug(("solar(solar_mmap): offset = 0x%08x\n",(u_int)offset)); + kdebug(("solar(solar_mmap): size = 0x%08x\n",(u_int)size)); + + if (remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot)) + { + kdebug(("solar(solar_mmap): remap page range failed\n")); + return -ENXIO; + } + + vma->vm_ops = &solar_vm_ops; + kdebug(("solar(solar_mmap): function done\n")); + return(0); +} + + +/*********************************************************************************************/ +static int solar_write_procmem(struct file *file, const char *buffer, u_long count, void *data) +/*********************************************************************************************/ +{ + int len; + struct solar_proc_data_t *fb_data = (struct solar_proc_data_t *)data; + + kdebug(("solar(solar_write_procmem): solar_write_procmem called\n")); + + if(count > 99) + len = 99; + else + len = count; + + if (copy_from_user(fb_data->value, buffer, len)) + { + kdebug(("solar(solar_write_procmem): error from copy_from_user\n")); + return(-SOLAR_EFAULT); + } + + kdebug(("solar(solar_write_procmem): len = %d\n", len)); + fb_data->value[len - 1] = '\0'; + kdebug(("solar(solar_write_procmem): text passed = %s\n", fb_data->value)); + + if (!strcmp(fb_data->value, "debug")) + { + debug = 1; + kdebug(("solar(solar_write_procmem): debugging enabled\n")); + } + + if (!strcmp(fb_data->value, "nodebug")) + { + kdebug(("solar(solar_write_procmem): debugging disabled\n")); + debug = 0; + } + + return len; +} + + +/***************************************************************************************************/ +static int solar_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data) +/***************************************************************************************************/ +{ + u_int card, ocr, osr, imask; + int loop, nchars = 0; + static int len = 0; + + kdebug(("solar(solar_read_procmem): Called with buf = 0x%016lx\n", (u_long)buf)); + kdebug(("solar(solar_read_procmem): Called with *start = 0x%016lx\n", (u_long)*start)); + kdebug(("solar(solar_read_procmem): Called with offset = %d\n", (u_int)offset)); + kdebug(("solar(solar_read_procmem): Called with count = %d\n", count)); + + if (offset == 0) + { + kdebug(("exp(exp_read_procmem): Creating text....\n")); + len = 0; + len += sprintf(proc_read_text + len, "\n\n\nSOLAR driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG); + + len += sprintf(proc_read_text + len, "==================================================\n"); + len += sprintf(proc_read_text + len, "Card|IRQ line| REQ IRQ|revision|PCI MEM addr.|wswap|bswap|LDOWN|REQ available|REQs posted|\n"); + len += sprintf(proc_read_text + len, "----|--------|--------|--------|-------------|-----|-----|-----|-------------|-----------|\n"); + for(card = 0; card < maxcards; card++) + { + ocr = solarcard[card].regs->opctrl; + osr = solarcard[card].regs->opstat; + imask = solarcard[card].regs->intmask; + len += sprintf(proc_read_text + len, " %d|", card); + len += sprintf(proc_read_text + len, " %2d|", solarcard[card].irq_line); + len += sprintf(proc_read_text + len, " %2d|", (imask & 0xf)); + len += sprintf(proc_read_text + len, " 0x%02x|", solarcard[card].pci_revision); + len += sprintf(proc_read_text + len, " 0x%08x|", solarcard[card].pci_memaddr); + len += sprintf(proc_read_text + len, " %s|", (ocr & 0x4)?"yes":" no"); + len += sprintf(proc_read_text + len, " %s|", (ocr & 0x2)?"yes":" no"); + len += sprintf(proc_read_text + len, " %s|", (osr & 0x00040000)?"yes":" no"); + len += sprintf(proc_read_text + len, " %2d|", (osr & 0x3f)); + len += sprintf(proc_read_text + len, "%11d|\n", served[card]); + } + + len += sprintf(proc_read_text + len, " \n"); + len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/solar', executed as root,\n"); + len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n"); + len += sprintf(proc_read_text + len, "debug -> enable debugging\n"); + len += sprintf(proc_read_text + len, "nodebug -> disable debugging\n"); + } + kdebug(("solar(solar_read_procmem): number of characters in text buffer = %d\n", len)); + + if (count < (len - offset)) + nchars = count; + else + nchars = len - offset; + kdebug(("solar(solar_read_procmem): min nchars = %d\n", nchars)); + + if (nchars > 0) + { + for (loop = 0; loop < nchars; loop++) + buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop]; + *start = buf + (offset & (PAGE_SIZE - 1)); + } + else + { + nchars = 0; + *eof = 1; + } + + kdebug(("solar(solar_read_procmem): returning *start = 0x%016lx\n", (u_long)*start)); + kdebug(("solar(solar_read_procmem): returning nchars = %d\n", nchars)); + return(nchars); +} + + +/********************************************************************************/ +static irqreturn_t solar_irq_handler (int irq, void *dev_id, struct pt_regs *regs) +/********************************************************************************/ +{ + u_int card, for_us; + + // Note: the SOLAR card(s) may have to share an interrupt line with other PCI devices. + // It is therefore important to exit quickly if we are not concerned with an interrupt. + // For now this is done by looking at the OPSTAT register. There may be a dedicated + // interrupt status bit in the future. + + for_us = 0; + for(card = 0; card < maxcards; card++) + for_us += solarcard[card].regs->opstat & 0x3f; + + if (for_us) + { + kdebug(("solar(solar_irq_handler): Interrupt received\n")); + refill_req_fifo(); + return(IRQ_HANDLED); //MJ: for 2.6 see p273 + } + else + { + kdebug(("solar(solar_irq_handler): This was not a SOLAR interrupt\n")); + return(IRQ_NONE); + } +} + + + +/*****************************/ +/* PCI driver functions */ +/*****************************/ + +/**********************************************************************************/ +static int __devinit solarProbe(struct pci_dev *dev, const struct pci_device_id *id) //MJ: for _devinit see p31 +/**********************************************************************************/ +{ + int i, card; + u_long flags; + u_int size; + + kdebug(("solar(solarProbe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device)); + kdebug(("solar(solarProbe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn)); + + /* Find a new "slot" for the card */ + card = -1; + for (i = 0; i < MAXCARDS; i++) + { + if (solarcard[i].pciDevice == NULL) + { + card = i; + solarcard[card].pciDevice = dev; + break; + } + } + + if (card == -1) + { + kdebug(("solar(solarProbe): Could not find space in the solarcard array for this card.")); + return(-EIO); + } + + flags = pci_resource_flags(dev, BAR0); //MJ: See p317 + if ((flags & IORESOURCE_MEM) != 0) + { + solarcard[card].pci_memaddr = pci_resource_start(dev, BAR0) & PCI_BASE_ADDRESS_MEM_MASK; //MJ: See p317 + size = pci_resource_end(dev, BAR0) - pci_resource_start(dev, BAR0); //MJ: See p317 + kdebug(("solar(solarProbe): Mapping %d bytes at PCI address 0x%08x\n", size, solarcard[card].pci_memaddr)); + + solarcard[card].regs = (T_solar_regs *)ioremap(solarcard[card].pci_memaddr, size); + if (solarcard[card].regs == NULL) + { + kdebug(("solar(solarProbe): error from ioremap for solarcard[%d].regs\n", card)); + solarcard[card].pciDevice = NULL; + return(-SOLAR_IOREMAP); + } + kdebug(("solar(solarProbe): solarcard[%d].regs is at 0x%016lx\n", card, (u_long)solarcard[card].regs)); + } + else + { + kdebug(("solar(solarProbe): Bar 0 is not a memory resource")); + return(-SOLAR_EIO); + } + + // get revision directly from the SOLAR + pci_read_config_byte(solarcard[card].pciDevice, PCI_REVISION_ID, &solarcard[card].pci_revision); + kdebug(("solar(solarProbe): revision = %x \n", solarcard[card].pci_revision)); + if (solarcard[card].pci_revision < MINREV) + { + kdebug(("solar(solarProbe): Illegal SOLAR Revision\n")); + return(-SOLAR_ILLREV); + } + + pci_read_config_byte(solarcard[card].pciDevice, PCI_INTERRUPT_LINE, &solarcard[card].irq_line); + kdebug(("solar(solarProbe): interrupt line = %d \n", solarcard[card].irq_line)); + + //Several solar cards may use the same interrupt but we want to install the driver only once + if (irqlist[solarcard[card].irq_line] == 0) + { + if(request_irq(solarcard[card].irq_line, solar_irq_handler, SA_SHIRQ, "solar", dev)) + { + kdebug(("solar(solarProbe): request_irq failed on IRQ = %d\n", solarcard[card].irq_line)); + return(-SOLAR_REQIRQ); + } + irqlist[solarcard[card].irq_line]++; + kdebug(("solar(solarProbe): Interrupt %d registered\n", solarcard[card].irq_line)); + } + + maxcards++; + return(0); +} + + +/******************************************/ +static void solarRemove(struct pci_dev *dev) +/******************************************/ +{ + int i, card; + + /* Find the "slot" of the card */ + card = -1; + for (i = 0; i < MAXCARDS; i++) + { + if (solarcard[i].pciDevice == dev) + { + card = i; + solarcard[i].pciDevice = 0; + break; + } + } + + if (card == -1) + { + kdebug(("solar(solarRemove): Could not find device to remove.")); + return; + } + + if (--irqlist[solarcard[card].irq_line] == 0) + { + kdebug(("solar(solarRemove): removing handler for interrupt %d", solarcard[card].irq_line)); + free_irq(solarcard[card].irq_line, dev); + } + + iounmap((void *)solarcard[card].regs); + kdebug(("solar(solarRemove): Card %d removed", card)); + maxcards--; +} + + + + +/*********************/ +/* Service functions */ +/*********************/ + +/**********************************************************/ +static int in_fifo_pop(int card, u_int *data1, u_int *data2) +/**********************************************************/ +{ + int index; + + if (infifon[card] == 0) + { + kdebug(("solar(in_fifo_pop): The IN FIFO is empty\n")); + return(-1); + } + + index = IN_INDEX(card, infifor[card]); + *data1 = infifo[index]; + *data2 = infifo[index + 1]; + kdebug(("solar(in_fifo_pop): index=%d data1=0x%08x data2=0x%08x\n", index, *data1, *data2)); + + infifor[card]++; + + if (infifor[card] > (MAXINFIFO - 1)) + infifor[card] = 0; + + infifon[card]--; + + return(0); +} + + +/*******************************/ +static void refill_req_fifo(void) +/*******************************/ +{ + u_int data1, data2, loop, card, numfree, numreq, numcopy; + + // We got here because one of the cards generated an interrupt. + // As other cards may also have space in ther REQ FIFOs we process all of them + // to reduce the interrupt frequency. + + for(card = 0; card < maxcards; card++) + { + // Is there space in the REQ FIFO? + numfree = (solarcard[card].regs->opstat) & 0x3f; + + // Have we got new requests? + numreq = infifon[card]; + kdebug(("solar(refill_req_fifo): numfree=%d numreq=%d\n", numfree, numreq)); + + if (numfree && !numreq) + { + //We have to disable the interrupt as we can not clear it by filling the REQ FIFO + kdebug(("solar(refill_req_fifo): Disabling interrupt\n")); + solarcard[card].regs->intmask = 0; + } + else + { + if (numfree < numreq) + numcopy = numfree; + else + numcopy = numreq; + kdebug(("solar(refill_req_fifo): numcopy=%d \n", numcopy)); + + for (loop = 0; loop < numcopy; loop++) + { + // As we have checked the number of entries in the IN FIFO we do not have to worry about errors + in_fifo_pop(card, &data1, &data2); + solarcard[card].regs->reqfifo1 = data1; + solarcard[card].regs->reqfifo2 = data2; + served[card]++; + kdebug(("solar(refill_req_fifo): data1=0x%08x data2=0x%08x\n", data1, data2)); + } + } + } +} + + diff --git a/cmt/vme_rcc.c b/cmt/vme_rcc.c new file mode 100644 index 0000000000000000000000000000000000000000..3dbe3920c6679de03f767a012d49ccd37db37c63 --- /dev/null +++ b/cmt/vme_rcc.c @@ -0,0 +1,2874 @@ +// $Id$ +/************************************************************************/ +/* */ +/* File: vme_rcc_driver.c */ +/* */ +/* RCC VMEbus driver */ +/* */ +/* 25. Oct. 01 MAJO created */ +/* 01. Nov. 01 JOP prepare for interrupts */ +/* 26. Nov. 01 JOP bus errors */ +/* 15. Jan. 02 JOP clean link & berr structures in release */ +/* 26. Feb. 02 JOP update to Linux 2.4 */ +/* 17. Apr. 02 JOP RORA interrupts */ +/* 27. May. 02 JOP modify API for IRs */ +/* */ +/************ C 2005 - The software with that certain something *********/ + + +//MJ - FIXME - We have to decide what to do with the pid + + +/************************************************************************/ +/*NOTES: */ +/*- This driver should work on kernels from 2.6.9 onwards */ +/*- This driver requires a UniverseII. Universe I is not supported. */ +/*- The proper way of accessing the Universe would be through */ +/* writel/readl functions. For a first implementation I have chosen to */ +/* use pointer based I/O as this simplifies the re-use of old code */ +/************************************************************************/ + +#include <linux/init.h> //MJ: for 2.6, p30 +#include <linux/module.h> +#include <linux/moduleparam.h> //MJ: for 2.6, p30 +#include <linux/kernel.h> +#include <linux/stat.h> //MJ: for 2.6, e.g. for module_param +#include <linux/pci.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/cdev.h> //e.g. for cdev_alloc +#include <linux/interrupt.h> //e.g. for request_irq +//MJ: obsolete? #include <linux/sched.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include "vme_rcc/universe.h" +#include "vme_rcc/cct_berr.h" +#include "vme_rcc/vme_rcc_driver.h" + + +/*********/ +/*Globals*/ +/*********/ +static int berr_interlock = 0, vmetab_ok = 0, debug = 0, errorlog = 1; +static int dma_sem_busy = 0, sem_positive[2] = {0,0}; +static char *proc_read_text; +static u_char vme_irq_line; +static u_int maxcard = 0; +static u_int board_type, dma_to; +static u_int irq_level_table[7]; +static u_int irq_sysfail; +static u_int berr_int_count = 0; +static u_int txfifo_wait_1; // # waits on TXFIFO empty +static u_int txfifo_wait_2; +static all_mstslvmap_t stmap; +static VME_DMAhandle_t dma_donelist[VME_DMADONESIZE]; +static master_map_t master_map_table; +static link_dsc_table_t link_table; // link descriptors +static berr_proc_table_t berr_proc_table; // processes with BERR handling +static sysfail_link_dsc_t sysfail_link; // processes with SYSFAIL handling +static berr_dsc_t berr_dsc; // last BERR +#if defined (VMEDEBUG) || defined (INTDEBUG) + static u_int *marker; +#endif +static InterruptCounters_t Interrupt_counters = {0,0,0,0,0,0,0, {0,0,0,0,0,0,0}, 0}; +static dev_t major_minor; +static universe_regs_t *uni; +static volatile VME_DMAhandle_t current_dmahandle; +static struct vme_proc_data_t vme_proc_data; +static struct proc_dir_entry *vme_rcc_file; +static struct timer_list dma_timer; +static struct semaphore dma_semaphore; +static struct cdev *vme_rcc_cdev; +static struct pci_dev *universedev; + +/************/ +/*Prototypes*/ +/************/ +static irqreturn_t universe_irq_handler (int irq, void *dev_id, struct pt_regs *regs); +static void vme_rcc_Remove(struct pci_dev *dev); +static void vme_rcc_vmaOpen(struct vm_area_struct *vma); +static void vme_rcc_cleanup(void); +static int __devinit vme_rcc_Probe(struct pci_dev *dev, const struct pci_device_id *id); +static int vme_rcc_init(void); + +/***************************************************************/ +/* Use /sbin/modinfo <module name> to extract this information */ +/***************************************************************/ +MODULE_DESCRIPTION("VMEbus driver for the Tundra Universe on SBCs from CCT"); +MODULE_AUTHOR("Markus Joos & Jorgen Petersen, CERN/PH"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("Private: Contact markus.joos@cern.ch"); +module_param (debug, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(debug, "1 = enable debugging 0 = disable debugging"); +module_param (errorlog, int, S_IRUGO | S_IWUSR); //MJ: for 2.6 p37 +MODULE_PARM_DESC(errorlog, "1 = enable error logging 0 = disable error logging"); + + +//MJ: Not actually required. Just for kdebug +struct vm_operations_struct vme_rcc_vm_ops = +{ + .close = vme_rcc_vmaClose, + .open = vme_rcc_vmaOpen, //MJ: Note the comma at the end of the list! +}; + +static struct file_operations fops = +{ + .owner = THIS_MODULE, + .ioctl = vme_rcc_ioctl, + .open = vme_rcc_open, + .mmap = vme_rcc_mmap, + .release = vme_rcc_release, +}; + +// PCI driver structures. See p311 +static struct pci_device_id vme_rcc_Ids[] = +{ + {PCI_DEVICE(PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_CA91C042)}, + {0,}, +}; + +//PCI operations +static struct pci_driver vme_rcc_PciDriver = +{ + .name = "vme_rcc", + .id_table = vme_rcc_Ids, + .probe = vme_rcc_Probe, + .remove = vme_rcc_Remove, +}; + + +/****************************/ +/* Standard driver function */ +/****************************/ + +/***************************/ +static int vme_rcc_init(void) +/***************************/ +{ + u_int i; + int result, loop; + u_char reg; + + kerror(("vme_rcc(vme_rcc_init): Error debug is active\n")) + + if(alloc_chrdev_region (&major_minor, 0, 1, "vme_rcc")) //MJ: for 2.6 p45 + { + kdebug(("vme_rcc(vme_rcc_init): failed to obtain device numbers\n")); + result = -EIO; + goto fail1; + } + + proc_read_text = (char *)kmalloc(MAX_PROC_TEXT_SIZE, GFP_KERNEL); + if (proc_read_text == NULL) + { + kdebug(("vme_rcc(vme_rcc_init): error from kmalloc\n")); + result = -ENOMEM; + goto fail2; + } + + vme_rcc_file = create_proc_entry("vme_rcc", 0644, NULL); + if (vme_rcc_file == NULL) + { + kdebug(("vme_rcc(vme_rcc_init): error from call to create_proc_entry\n")); + result = -EFAULT; + goto fail3; + } + + strcpy(vme_proc_data.name, "vme_rcc"); + strcpy(vme_proc_data.value, "vme_rcc"); + vme_rcc_file->data = &vme_proc_data; + vme_rcc_file->read_proc = vme_rcc_read_procmem; + vme_rcc_file->write_proc = vme_rcc_write_procmem; + vme_rcc_file->owner = THIS_MODULE; + + kdebug(("vme_rcc(vme_rcc_init): registering PCIDriver\n")); + result = pci_register_driver(&vme_rcc_PciDriver); //MJ: See P312 + if (result == 0) + { + kerror(("vme_rcc(vme_rcc_init): ERROR! no UNIVERSE chip found!\n")); + result = -EIO; + goto fail3; + } + else + { + kerror(("vme_rcc(vme_rcc_init): Found %d UNIVERSE chips!\n", result)); + } + + // Read the board identification + outb(BID1, CMOSA); + reg = inb(CMOSD); + kdebug(("vme_rcc(vme_rcc_init): BID1 = 0x%02x\n", reg)) + + if (!(reg & 0x80)) + { + kerror(("vme_rcc(vme_rcc_init): Unable to determine board type\n")) + result = -VME_UNKNOWN_BOARD; + goto fail3; + } + + outb(BID2, CMOSA); + reg = inb(CMOSD); + kdebug(("vme_rcc(vme_rcc_init): BID2 = 0x%02x\n", reg)) + + reg &= 0x1f; // Mask board ID bits + board_type = VP_UNKNOWN; + if (reg == 2) {board_type = VP_PSE; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-PSE\n"));} + else if (reg == 3) {board_type = VP_PSE; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-PSE\n"));} + else if (reg == 4) {board_type = VP_PSE; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-PSE\n"));} + else if (reg == 5) {board_type = VP_PMC; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-PMC\n"));} + else if (reg == 6) {board_type = VP_CP1; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-CP1\n"));} + else if (reg == 7) {board_type = VP_100; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-100\n"));} + else if (reg == 8) {board_type = VP_110; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-110\n"));} + else if (reg == 11) {board_type = VP_315; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-315\n"));} + else if (reg == 13) {board_type = VP_317; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-317\n"));} + else if (reg == 14) {board_type = VP_325; kdebug(("vme_rcc(vme_rcc_init): Board type = VP-325\n"));} + + if (!board_type) + { + kerror(("vme_rcc(vme_rcc_init): Unable to determine board type(2). Board seems to be of type %d\n", reg)) + return(-VME_UNKNOWN_BOARD); + } + + if (board_type == VP_100 || board_type == VP_110 || board_type == VP_315 || board_type == VP_317 || board_type == VP_325) + outb(0x1, BERR_VP100); // Enable VMEbus BERR capturing + + init_cct_berr(); // Enable CCT bus errors + sema_init(&dma_semaphore, 1); // Initialize the DMA semaphore + sema_init(&link_table.link_dsc_sem, 1); // Initialize the Link Descriptor protection semaphore + init_timer(&dma_timer); // Initialise the watchdog timer for DMA transactions + + for (loop = 0; loop < VME_DMADONESIZE; loop++) // Initialize the list of completed DMA transaction + { + dma_donelist[loop].handle = 0xffffffff; + dma_donelist[loop].pid = 0; + } + + for (loop = 0; loop < VME_MAX_MASTERMAP; loop++) //Initialize the list of active master mappings + { + master_map_table.map[loop].pid = 0; + master_map_table.map[loop].vaddr = 0; + } + sema_init(&master_map_table.sem, 1); + + for (i = 0; i < VME_VCTSIZE; i++) // Reset link and BERR tables + { + link_table.link_dsc[i].pid = 0; + link_table.link_dsc[i].vct = 0; + link_table.link_dsc[i].lvl = 0; + link_table.link_dsc[i].pending = 0; + link_table.link_dsc[i].total_count = 0; + link_table.link_dsc[i].group_ptr = 0; + sema_init(&link_table.link_dsc[i].sem, 0); + link_table.link_dsc[i].sig = 0; + } + + for (i = 0; i < VME_MAX_BERR_PROCS; i++) + { + berr_proc_table.berr_link_dsc[i].pid = 0; + berr_proc_table.berr_link_dsc[i].sig = 0; + sema_init(&berr_proc_table.proc_sem, 1); + } + + berr_dsc.vmeadd = 0; + berr_dsc.am = 0; + berr_dsc.iack = 0; + berr_dsc.lword = 0; + berr_dsc.ds0 = 0; + berr_dsc.ds1 = 0; + berr_dsc.wr = 0; + berr_dsc.multiple = 0; + berr_dsc.flag = 0; + + sysfail_link.pid = 0; + sysfail_link.sig = 0; + sema_init(&sysfail_link.proc_sem, 1); + + vme_rcc_cdev = (struct cdev *)cdev_alloc(); //MJ: for 2.6 p55 + vme_rcc_cdev->ops = &fops; + result = cdev_add(vme_rcc_cdev, major_minor, 1); //MJ: for 2.6 p56 + if (result) + { + kdebug(("vme_rcc(vme_rcc_init): error from call to cdev_add.\n")); + goto fail4; + } + + kdebug(("vme_rcc(vme_rcc_init): driver loaded; major device number = %d\n", MAJOR(major_minor))); + return(0); + + fail4: + remove_proc_entry("vme_rcc", NULL); + + fail3: + kfree(proc_read_text); + + fail2: + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + + fail1: + return(result); +} + + +/********************************/ +static void vme_rcc_cleanup(void) +/********************************/ +{ + pci_unregister_driver(&vme_rcc_PciDriver); + + cdev_del(vme_rcc_cdev); //MJ: for 2.6 p56 + remove_proc_entry("vme_rcc", NULL); + kfree(proc_read_text); + unregister_chrdev_region(major_minor, 1); //MJ: for 2.6 p45 + kdebug(("vme_rcc(vme_rcc_cleanup): driver removed\n")) +} + + +module_init(vme_rcc_init); //MJ: for 2.6 p16 +module_exit(vme_rcc_cleanup); //MJ: for 2.6 p16 + + +/************************************************************/ +static int vme_rcc_open(struct inode *ino, struct file *filep) +/************************************************************/ +{ + int vct, berr, loop; + private_data_t *pdata; + + pdata = (private_data_t *)kmalloc(sizeof(private_data_t), GFP_KERNEL); + if (pdata == NULL) + { + kerror(("vme_rcc(open): error from kmalloc\n")) + return(-VME_EFAULT); + } + + kdebug(("vme_rcc(open): pointer to link flags in file descriptor = %x\n", (u_int)&pdata->link_dsc_flag[0])) + for (vct = 0; vct < VME_VCTSIZE; vct++) + pdata->link_dsc_flag[vct] = 0; + + kdebug(("vme_rcc(open): pointer to berr flags in file descriptor = %x\n", (u_int)&pdata->berr_dsc_flag[0])) + for (berr = 0; berr < VME_MAX_BERR_PROCS; berr++) + pdata->berr_dsc_flag[berr] = 0; + + pdata->sysfail_dsc_flag = 0; + + for (loop = 0; loop < VME_DMADONESIZE; loop++) + pdata->dma[loop] = 0; + + for (loop = 0; loop < VME_MAX_MASTERMAP; loop++) + pdata->mastermap[loop] = 0; + + filep->private_data = pdata; + return(0); +} + + +/***************************************************************/ +static int vme_rcc_release(struct inode *ino, struct file *filep) +/***************************************************************/ +{ + int vct, no_freed, isave, free_flag, loop; + u_int i; + VME_IntHandle_t *group_ptr; + VME_IntHandle_t *group_ptr_save[VME_VCTSIZE]; + private_data_t *pdata; + + pdata = (private_data_t *)filep->private_data; + + down(&link_table.link_dsc_sem); + + no_freed = 0; + group_ptr_save[0] = (VME_IntHandle_t*)0xffffffff; + + kdebug(("vme_rcc(release): pid = %d\n", current->pid)) + + for (vct = 0; vct < VME_VCTSIZE; vct++) + { + if (pdata->link_dsc_flag[vct] == 1) + { + if (link_table.link_dsc[vct].group_ptr) // grouped vector + { + group_ptr = link_table.link_dsc[vct].group_ptr; + kdebug(("vme_rcc(release): vector = %d, group_ptr = 0x%p\n", vct, group_ptr)) + free_flag = 1; + for (isave = 0; isave <= no_freed; isave++) + { + if (group_ptr == group_ptr_save[isave]) + { + free_flag = 0; // already freed + break; + } + } + + kdebug(("vme_rcc(release): free_flag = %d\n", free_flag)) + if (free_flag) + { + kdebug(("vme_rcc(release): group_ptr = 0x%p is now kfree'd\n", group_ptr)) + kfree(group_ptr); //MJ: where is the corresponding kmalloc? + group_ptr_save[no_freed] = group_ptr; + no_freed++; + kdebug(("vme_rcc(release): no_freed = %d \n", no_freed)) + } + } + + link_table.link_dsc[vct].pid = 0; + link_table.link_dsc[vct].vct = 0; + link_table.link_dsc[vct].lvl = 0; + link_table.link_dsc[vct].pending = 0; + link_table.link_dsc[vct].total_count = 0; + link_table.link_dsc[vct].group_ptr = 0; + sema_init(&link_table.link_dsc[vct].sem,0); + link_table.link_dsc[vct].sig = 0; + + pdata->link_dsc_flag[vct] = 0; + } + } + up(&link_table.link_dsc_sem); + + // and now the BERR links + down(&berr_proc_table.proc_sem); + for (i = 0; i < VME_MAX_BERR_PROCS; i++) + { + if (pdata->berr_dsc_flag[i] == 1) + { + kdebug(("vme_rcc(release): clearing BERR entry %d \n", i)) + berr_proc_table.berr_link_dsc[i].pid = 0; + berr_proc_table.berr_link_dsc[i].sig = 0; + pdata->link_dsc_flag[i] = 0; + } + } + up(&berr_proc_table.proc_sem); + + down(&sysfail_link.proc_sem); + if (pdata->sysfail_dsc_flag) + { + kdebug(("vme_rcc(release): clearing SYSFAIL entry that has been left open by process %d\n", sysfail_link.pid)) + sysfail_link.pid = 0; + sysfail_link.sig = 0; + pdata->sysfail_dsc_flag = 0; + } + up(&sysfail_link.proc_sem); + + // Release orphaned master mappings + down(&master_map_table.sem); + for (loop = 0; loop < VME_MAX_MASTERMAP; loop++) + { + if (pdata->mastermap[loop]) + { + kdebug(("vme_rcc(release): Unmapping orphaned virtual address 0x%08x for process %d\n", master_map_table.map[loop].vaddr, master_map_table.map[loop].pid)) + iounmap((void *)master_map_table.map[loop].vaddr); + master_map_table.map[loop].vaddr = 0; + master_map_table.map[loop].pid = 0; + pdata->mastermap[loop] = 0; + } + } + up(&master_map_table.sem); + + // and finally the DMA done list + for (loop = 0; loop < VME_DMADONESIZE; loop++) + { + if (pdata->dma[loop]) + { + kdebug(("vme_rcc(release): entry %d in dma_done list released (was owned by process %d)\n", loop, dma_donelist[loop].pid)) + dma_donelist[loop].pid = 0; + dma_donelist[loop].handle = 0xffffffff; + pdata->dma[loop] = 0; + } + } + + kfree(pdata); + kdebug(("vme_rcc(release): kfreed file private data @ %x\n", (u_int)pdata)) + return(0); +} + + +/**************************************************************************************/ +static int vme_rcc_ioctl(struct inode *inode, struct file *filep, u_int cmd, u_long arg) +/**************************************************************************************/ +{ + private_data_t *pdata; + + kdebug(("vme_rcc(ioctl): filep at 0x%08x\n", (u_int)filep)) + pdata = (private_data_t *)filep->private_data; + + switch (cmd) + { + case VMEMASTERMAP: + { + int ok2, ok = 0, loop, loop2; + u_int vaddr; + + if (copy_from_user(&pdata->mm, (void *)arg, sizeof(VME_MasterMapInt_t))) + { + kerror(("vme_rcc(ioctl,VMEMASTERMAP): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): mm.in.vmebus_address = 0x%08x\n", pdata->mm.in.vmebus_address)) + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): mm.in.window_size = 0x%08x\n", pdata->mm.in.window_size)) + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): mm.in.address_modifier = 0x%08x\n", pdata->mm.in.address_modifier)) + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): mm.in.options = 0x%08x\n", pdata->mm.in.options)) + + for (loop = 0; loop < 8; loop++) + { + if (pdata->mm.in.vmebus_address >= stmap.master[loop].vbase && + (pdata->mm.in.vmebus_address + pdata->mm.in.window_size) <= stmap.master[loop].vtop && + stmap.master[loop].enab == 1 && + stmap.master[loop].am == pdata->mm.in.address_modifier && + stmap.master[loop].options == pdata->mm.in.options) + { + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): Decoder %d matches parameters\n", loop)) + pdata->mm.pci_address = stmap.master[loop].pbase + (pdata->mm.in.vmebus_address-stmap.master[loop].vbase); + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): PCI address = 0x%08x\n", pdata->mm.pci_address)) + + //find a free slot in the master map table + down(&master_map_table.sem); + ok2 = 0; + for (loop2 = 0; loop2 < VME_MAX_MASTERMAP; loop2++) + { + if (master_map_table.map[loop2].pid == 0) + { + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): Will use entry %d in master_map_table\n", loop2)) + ok2 = 1; + break; + } + } + if (!ok2) + { + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): master_map_table is full\n")) + return(-VME_NOSTATMAP2); + } + + vaddr = (u_int)ioremap(pdata->mm.pci_address, pdata->mm.in.window_size); + if (!vaddr) + { + kerror(("vme_rcc(ioctl,VMEMASTERMAP): error from ioremap\n")) + up(&master_map_table.sem); + return(-VME_EFAULT); + } + master_map_table.map[loop2].vaddr = vaddr; + master_map_table.map[loop2].pid = current->pid; + pdata->mastermap[loop2] = 1; + up(&master_map_table.sem); + + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): kernel virtual address = 0x%08x pid = %d\n", vaddr, current->pid)) + pdata->mm.kvirt_address = vaddr; + ok = 1; + break; + } + else + { + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): stmap.master[%d].vbase = 0x%08x\n", loop, stmap.master[loop].vbase)) + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): stmap.master[%d].vtop = 0x%08x\n", loop, stmap.master[loop].vtop)) + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): stmap.master[%d].enab = %d\n", loop, stmap.master[loop].enab)) + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): stmap.master[%d].am = %d\n", loop, stmap.master[loop].am)) + kdebug(("vme_rcc(ioctl,VMEMASTERMAP): stmap.master[%d].options = 0x%08x\n", loop, stmap.master[loop].options)) + } + } + if (!ok) + return(-VME_NOSTATMAP); + + if (copy_to_user((void *)arg, &pdata->mm, sizeof(VME_MasterMapInt_t))) + { + kerror(("vme_rcc(ioctl,VMEMASTERMAP): error from copy_to_user\n")) + return(-VME_EFAULT); + } + break; + } + + case VMEMASTERUNMAP: + { + u_int ok, mm, loop; + + if (copy_from_user(&mm, (void *)arg, sizeof(u_int))) + { + kerror(("vme_rcc(ioctl,VMEMASTERUNMAP): error from copy_from_user\n")) + return(-VME_EFAULT); + } + + down(&master_map_table.sem); + ok = 0; + for (loop = 0; loop < VME_MAX_MASTERMAP; loop++) + { + kdebug(("vme_rcc(ioctl,VMEMASTERUNMAP): master_map_table.map[%d].vaddr = 0x%08x <-> mm = 0x%08x\n", loop, master_map_table.map[loop].vaddr, mm)); + kdebug(("vme_rcc(ioctl,VMEMASTERUNMAP): master_map_table.map[%d].pid = %d <-> current->pid = %d\n", loop, master_map_table.map[loop].pid, current->pid)); + + if (master_map_table.map[loop].vaddr == mm) + { + kdebug(("vme_rcc(ioctl,VMEMASTERUNMAP): Entry %d in master_map_table matches\n", loop)) + ok = 1; + iounmap((void *)mm); + kdebug(("vme_rcc(ioctl,VMEMASTERUNMAP): unmapping address 0x%08x\n", mm)) + master_map_table.map[loop].vaddr = 0; + master_map_table.map[loop].pid = 0; + pdata->mastermap[loop] = 0; + break; + } + } + up(&master_map_table.sem); + + if (!ok) + { + kdebug(("vme_rcc(ioctl,VMEMASTERUNMAP): No entry in master_map_table matched for address 0x%08x\n", mm)) + return(-VME_IOUNMAP); + } + break; + } + + case VMEMASTERMAPDUMP: + { + char *buf; + int len, loop; + + buf = (char *)kmalloc(TEXT_SIZE1, GFP_KERNEL); + if (buf == NULL) + { + kerror(("vme_rcc(ioctl,VMEMASTERMAPDUMP): error from kmalloc\n")) + return(-VME_KMALLOC); + } + + len = 0; + len += sprintf(buf + len, "Master mapping:\n"); + len += sprintf(buf + len, "LSI VME address range PCI address range EN WP AM\n"); + for (loop = 0; loop < 8; loop++) + len += sprintf(buf + len, "%d %08x-%08x %08x-%08x %d %d %d\n" + ,loop + ,stmap.master[loop].vbase + ,stmap.master[loop].vtop + ,stmap.master[loop].pbase + ,stmap.master[loop].ptop + ,stmap.master[loop].enab + ,stmap.master[loop].wp + ,stmap.master[loop].am); + if (copy_to_user((void *)arg, buf, TEXT_SIZE1 * sizeof(char))) + { + kerror(("vme_rcc(ioctl,VMEMASTERMAPDUMP): error from copy_to_user\n")) + return(-VME_EFAULT); + } + + kfree(buf); + break; + } + + case VMESYSFAILLINK: + { + down(&sysfail_link.proc_sem); + + if (sysfail_link.pid) // Already linked by any other process? + { + up(&sysfail_link.proc_sem); + return(-VME_SYSFAILTBLFULL); + } + + sysfail_link.pid = current->pid; + pdata->sysfail_dsc_flag = 1; + sema_init(&sysfail_link.sem, 0); // initialise semaphore count + up(&sysfail_link.proc_sem); + break; + } + + case VMESYSFAILUNLINK: + { + down(&sysfail_link.proc_sem); + + if (pdata->sysfail_dsc_flag == 0) //Check if the current process (group) has linked it + { + up(&sysfail_link.proc_sem); + return(-VME_SYSFAILTBLNOTLINKED); + } + + sysfail_link.pid = 0; + sysfail_link.sig = 0; + pdata->sysfail_dsc_flag = 0; + up(&sysfail_link.proc_sem); + + break; + } + + case VMESYSFAILREGISTERSIGNAL: + { + if (copy_from_user(&pdata->VME_SysfailRegSig, (void *)arg, sizeof(VME_RegSig_t))) + { + kerror(("vme_rcc(ioctl,VMESYSFAILREGISTERSIGNAL): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMESYSFAILREGISTERSIGNAL): signal # = %d\n", pdata->VME_SysfailRegSig.signum)) + down(&sysfail_link.proc_sem); + + if (sysfail_link.pid != current->pid) + { + up(&sysfail_link.proc_sem); + return(-VME_SYSFAILNOTLINKED); + } + if (pdata->VME_SysfailRegSig.signum) // register + sysfail_link.sig = pdata->VME_SysfailRegSig.signum; + else // unregister + sysfail_link.sig = 0; + up(&sysfail_link.proc_sem); + break; + } + + case VMESYSFAILWAIT: + { + struct timer_list int_timer; + int use_timer = 0, result = 0; + + if (copy_from_user(&pdata->VME_WaitInt, (void *)arg, sizeof(VME_WaitInt_t))) + { + kerror(("vme_rcc(ioctl,VMESYSFAILWAIT): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMESYSFAILWAIT): timeout = %d\n", pdata->VME_WaitInt.timeout)) + + if (pdata->VME_WaitInt.timeout > 0) + { + init_timer(&int_timer); + int_timer.function = vme_intSysfailTimeout; + int_timer.data = (u_long)&pdata->VME_WaitInt; + int_timer.expires = jiffies + pdata->VME_WaitInt.timeout; + add_timer(&int_timer); + use_timer = 1; + } + + if (pdata->VME_WaitInt.timeout) // Wait : with timeout or not + { + kdebug(("vme_rcc(ioctl,VMESYSFAILWAIT): Before Down, semaphore = %d\n", sysfail_link.sem.count.counter)) + down_interruptible(&sysfail_link.sem); // wait .. + if (signal_pending(current)) + { // interrupted system call + kdebug(("vme_rcc(ioctl,VMESYSFAILWAIT): Interrupted by signal\n")) + result = -VME_INTBYSIGNAL; // or ERESTARTSYS ? + } + else if ((use_timer == 1) && (pdata->VME_WaitInt.timeout == 0)) + result = -VME_TIMEOUT; + } + + if (use_timer == 1) + del_timer(&int_timer); // OK also if timer ran out + + if (copy_to_user((void *)arg, &pdata->VME_WaitInt, sizeof(VME_WaitInt_t))) + { + kerror(("vme_rcc(ioctl,VMESYSFAILWAIT): error from copy_to_user\n")) + return(-VME_EFAULT); + } + + return (result); + break; // dummy .. + } + + // SYSFAIL is latched in lint_stat (I believe). Therefore, even if SYSFAIL is removed from VMEbus + // the interrupt status bit has to be cleared in LINT_STAT + // this will only work if SYSFAIL is not on the bus + case VMESYSFAILREENABLE: + { + u_int lint_stat, lint_en; + + lint_stat = readl((char*)uni + LINT_STAT); // read the interrupt bits + lint_stat |= 0x4000; + kdebug(("vme_rcc(ioctl,VMESYSFAILREENABLE): lint_stat = %x\n", lint_stat)) + writel(lint_stat, (char*)uni + LINT_STAT); // only success if SYSFAIL is not asserted on VMEbus + lint_en = readl((char*)uni + LINT_EN); // read enabled irq bits + kdebug(("vme_rcc(ioctl,VMESYSFAILREENABLE): lint_en = 0x%x\n", lint_en)) + lint_en |= 0x4000; + kdebug(("vme_rcc(ioctl,VMESYSFAILREENABLE): lint_en after masking = 0x%x\n", lint_en)) + writel(lint_en, (char*)uni + LINT_EN); //reenable SYSFAIL + break; + } + + // reset the SYSFAIL interrupt bit + // if it comes back on, the SYSFAIL is on VMEbus + case VMESYSFAILPOLL: + { + int flag; + u_int lint_stat; + + lint_stat = readl((char*)uni + LINT_STAT); // read the interrupt bits + lint_stat |= 0x4000; + kdebug(("vme_rcc(ioctl,VMESYSFAILPOLL): lint_stat = %x\n", lint_stat)) + writel(lint_stat, (char*)uni + LINT_STAT); // only success if SYSFAIL is not asserted on VMEbus + lint_stat = readl((char*)uni + LINT_STAT); // read the interrupt bits + kdebug(("vme_rcc(ioctl,VMESYSFAILPOLL): lint_stat = %x\n", lint_stat)) + if (lint_stat & 0x4000) // the SYSFAIL bit + flag = 1; + else + flag = 0; + + if (copy_to_user((void *)arg, &flag, sizeof(int))) + { + kerror(("vme_rcc(ioctl,VMESYSFAILPOLL): error from copy_to_user\n")) + return(-VME_EFAULT); + } + break; + } + + case VMESYSFAILSET: + { + u_int vcsr_set; + + vcsr_set = readl((char*)uni + VCSR_SET); + vcsr_set |= 0x40000000; // bit SYSFAIL + kdebug(("vme_rcc(ioctl,VMESYSFAILSET): vcsr_set = %x\n", vcsr_set)) + writel(vcsr_set, (char*)uni + VCSR_SET); + break; + } + + + case VMESYSFAILRESET: + { + u_int vcsr_clr; + + vcsr_clr = readl((char*)uni + VCSR_CLR); + vcsr_clr |= 0x40000000; // bit SYSFAIL + kdebug(("vme_rcc(ioctl,VMESYSFAILRESET): vcsr_clr = %x\n", vcsr_clr)) + writel(vcsr_clr, (char*)uni + VCSR_CLR); + break; + } + + case VMEBERRREGISTERSIGNAL: + { + u_int i; + int index; + + if (copy_from_user(&pdata->VME_BerrRegSig, (void *)arg, sizeof(VME_RegSig_t))) + { + kerror(("vme_rcc(ioctl,VMEBERRREGISTERSIGNAL): error from copy_from_user\n")) + return(-VME_EFAULT); + } + + kdebug(("vme_rcc(ioctl,VMEBERRREGISTERSIGNAL): signal # = %d\n", pdata->VME_BerrRegSig.signum)) + down(&berr_proc_table.proc_sem); + + if (pdata->VME_BerrRegSig.signum) + { + // find a free slot + index = -1; + for (i = 0; i < VME_MAX_BERR_PROCS; i++) + { + if (berr_proc_table.berr_link_dsc[i].pid == 0) + { + index = i; + break; + } + } + if (index == -1) + { + up(&berr_proc_table.proc_sem); + return(-VME_BERRTBLFULL); + } + + berr_proc_table.berr_link_dsc[index].pid = current->pid; + berr_proc_table.berr_link_dsc[index].sig = pdata->VME_BerrRegSig.signum; + berr_dsc.flag = 0; // enable BERR latching via signal + // race: could occur while a(nother) bus error is latched and not yet serviced .. + pdata->berr_dsc_flag[index] = 1; // remember which file berr belongs to + } + else // unregister + { + index = -1; + for (i = 0; i < VME_MAX_BERR_PROCS; i++) + { + if (pdata->berr_dsc_flag[i] == 1) + { + index = i; + break; + } + } + if (index == -1) + { + up(&berr_proc_table.proc_sem); + return(-VME_BERRNOTFOUND); + } + + berr_proc_table.berr_link_dsc[index].pid = 0; + berr_proc_table.berr_link_dsc[index].sig = 0; + pdata->berr_dsc_flag[index] = 0; + } + + up(&berr_proc_table.proc_sem); + break; + } + + case VMEBERRINFO: + { + u_long flags; + + if (berr_dsc.flag == 0) // slight risk of race with ISR .. + return(-VME_NOBUSERROR); + + local_irq_save(flags); //MJ: for 2.6 see p274 & p275 + + pdata->VME_BerrInfo.vmebus_address = berr_dsc.vmeadd; + pdata->VME_BerrInfo.address_modifier = berr_dsc.am; + pdata->VME_BerrInfo.multiple = berr_dsc.flag; + pdata->VME_BerrInfo.lword = berr_dsc.lword; + pdata->VME_BerrInfo.iack = berr_dsc.iack; + pdata->VME_BerrInfo.ds0 = berr_dsc.ds0; + pdata->VME_BerrInfo.ds1 = berr_dsc.ds1; + pdata->VME_BerrInfo.wr = berr_dsc.wr; + + berr_dsc.vmeadd = 0; + berr_dsc.am = 0; + berr_dsc.multiple = 0; + berr_dsc.iack = 0; + berr_dsc.flag = 0; + berr_dsc.lword = 0; + berr_dsc.ds0 = 0; + berr_dsc.ds1 = 0; + berr_dsc.wr = 0; + + local_irq_restore(flags); + + if (copy_to_user((void *)arg, &pdata->VME_BerrInfo, sizeof(VME_BusErrorInfo_t))) + { + kerror(("vme_rcc(ioctl,VMEBERRINFO): error from copy_to_user\n")) + return(-VME_EFAULT); + } + break; + } + + case VMESLAVEMAP: + { + int loop, ok = 0; + + if (copy_from_user(&pdata->sm, (void *)arg, sizeof(VME_SlaveMapInt_t))) + { + kerror(("vme_rcc(ioctl,VMESLAVEMAP): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMESLAVEMAP): mm.in.system_iobus_address = 0x%08x\n", pdata->sm.in.system_iobus_address)) + kdebug(("vme_rcc(ioctl,VMESLAVEMAP): mm.in.window_size = 0x%08x\n", pdata->sm.in.window_size)) + kdebug(("vme_rcc(ioctl,VMESLAVEMAP): mm.in.address_width = 0x%08x\n", pdata->sm.in.address_width)) + kdebug(("vme_rcc(ioctl,VMESLAVEMAP): mm.in.options = 0x%08x\n", pdata->sm.in.options)) + + for(loop = 0; loop < 8; loop++) + { + kdebug(("vme_rcc(ioctl,VMESLAVEMAP): stmap.slave[%d].pbase = 0x%08x\n", loop, stmap.slave[loop].pbase)) + kdebug(("vme_rcc(ioctl,VMESLAVEMAP): stmap.slave[%d].ptop = 0x%08x\n", loop, stmap.slave[loop].ptop)) + kdebug(("vme_rcc(ioctl,VMESLAVEMAP): stmap.slave[%d].enab = 0x%08x\n", loop, stmap.slave[loop].enab)) + kdebug(("vme_rcc(ioctl,VMESLAVEMAP): stmap.slave[%d].space = 0x%08x\n", loop, stmap.slave[loop].space)) + if (pdata->sm.in.system_iobus_address >= stmap.slave[loop].pbase && + (pdata->sm.in.system_iobus_address + pdata->sm.in.window_size) <= stmap.slave[loop].ptop && + stmap.slave[loop].enab == 1 && + stmap.slave[loop].am == pdata->sm.in.address_width) + { + kdebug(("vme_rcc(ioctl,VMESLAVEMAP): Decoder %d matches parameters\n", loop)) + pdata->sm.vme_address = stmap.slave[loop].vbase + (pdata->sm.in.system_iobus_address - stmap.slave[loop].pbase); + kdebug(("vme_rcc(ioctl,VMESLAVEMAP): VME address = 0x%08x\n", pdata->sm.vme_address)) + ok = 1; + break; + } + } + if (!ok) + { + kerror(("vme_rcc(ioctl,VMESLAVEMAP): No matching mapping found\n")) + return(-VME_NOSTATMAP); + } + if (copy_to_user((void *)arg, &pdata->sm, sizeof(VME_SlaveMapInt_t))) + { + kerror(("vme_rcc(ioctl,VMESLAVEMAP): error from copy_to_user\n")) + return(-VME_EFAULT); + } + break; + } + + case VMESLAVEMAPDUMP: + { + char *buf; + int len, loop; + + buf = (char *)kmalloc(TEXT_SIZE1, GFP_KERNEL); + if (buf == NULL) + { + kerror(("vme_rcc(ioctl,VMESLAVEMAPDUMP): error from kmalloc\n")) + return(-VME_KMALLOC); + } + len = 0; + len += sprintf(buf + len, "Slave mapping:\n"); + len += sprintf(buf + len, "VSI VME address range PCI address range EN WP RP AM PCI Space\n"); + for (loop = 0 ;loop < 8; loop++) + len += sprintf(buf + len, "%d %08x-%08x %08x-%08x %d %d %d %d %d\n" + ,loop + ,stmap.slave[loop].vbase + ,stmap.slave[loop].vtop + ,stmap.slave[loop].pbase + ,stmap.slave[loop].ptop + ,stmap.slave[loop].enab + ,stmap.slave[loop].wp + ,stmap.slave[loop].rp + ,stmap.slave[loop].am + ,stmap.slave[loop].space); + if (copy_to_user((void *)arg, buf, TEXT_SIZE1*sizeof(char))) + { + kerror(("vme_rcc(ioctl,VMESLAVEMAPDUMP): error from copy_to_user\n")) + return(-VME_EFAULT); + } + kfree(buf); + break; + } + + case VMESCSAFE: + { + u_int addr, multi, am, *lptr, ldata; + u_short *sptr, sdata; + u_char *bptr, bdata; + int ret; + + if (copy_from_user(&pdata->sc, (void *)arg, sizeof(VME_SingleCycle_t))) + { + kerror(("vme_rcc(ioctl,VMESCSAFE): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMESCSAFE): sc.kvirt_address = 0x%08x\n", pdata->sc.kvirt_address)) + kdebug(("vme_rcc(ioctl,VMESCSAFE): sc.offset = 0x%08x\n", pdata->sc.offset)) + kdebug(("vme_rcc(ioctl,VMESCSAFE): sc.nbytes = %d\n", pdata->sc.nbytes)) + kdebug(("vme_rcc(ioctl,VMESCSAFE): sc.rw = %d\n", pdata->sc.rw)) + + //Wait until the Universe has emptied the write posting buffer + while (!(uni->misc_stat & 0x40000)) + { + txfifo_wait_1++; + kdebug(("vme_rcc(ioctl,VMESCSAFE): TXFIFO still not empty\n")) + } + + //Disable the CCT BERR logic for the duration of the safe single cycle + mask_cct_berr(); + + if (pdata->sc.rw == 1) //read + { + if (pdata->sc.nbytes == 4) //D32 + { + lptr = (u_int *)(pdata->sc.kvirt_address + pdata->sc.offset); + ldata = *lptr; + pdata->sc.data = (u_int)ldata; + } + else if (pdata->sc.nbytes == 2) //D16 + { + sptr = (u_short *)(pdata->sc.kvirt_address + pdata->sc.offset); + sdata = *sptr; + pdata->sc.data = (u_int)sdata; + } + else //D8 + { + bptr = (u_char *)(pdata->sc.kvirt_address + pdata->sc.offset); + bdata = *bptr; + pdata->sc.data = (u_int)bdata; + } + } + else //write + { + if (pdata->sc.nbytes == 4) //D32 + { + lptr = (u_int *)(pdata->sc.kvirt_address + pdata->sc.offset); + ldata = pdata->sc.data; + *lptr = ldata; + } + else if (pdata->sc.nbytes == 2) //D16 + { + sptr = (u_short *)(pdata->sc.kvirt_address + pdata->sc.offset); + sdata = (u_short)pdata->sc.data; + *sptr = sdata; + } + else //D8 + { + bptr = (u_char *)(pdata->sc.kvirt_address + pdata->sc.offset); + bdata = (u_char)pdata->sc.data; + *bptr = bdata; + } + + //Wait again until the Universe has emptied the write posting buffer + while (!(uni->misc_stat & 0x40000)) + { + txfifo_wait_2++; + kdebug(("vme_rcc(ioctl,VMESCSAFE): TXFIFO still not empty (2)\n")) + } + } + + //MJ: Do we have to wait a bit for bus errors to arrive? + //MJ: What to do with addr, multi and am in case of BERR? + ret = berr_check(&addr, &multi, &am); + if (ret) + { + kerror(("vme_rcc(ioctl,VMESCSAFE): Bus error received\n")) + init_cct_berr(); + return(-VME_BUSERROR); + } + + //Reenable the CCT BERR logic and clear the BERR flag in the CCT logic + init_cct_berr(); + + if (copy_to_user((void *)arg, &pdata->sc, sizeof(VME_SingleCycle_t))) + { + kerror(("vme_rcc(ioctl,VMESCSAFE): error from copy_to_user\n")) + return(-VME_EFAULT); + } + break; + } + + case VMEDMASTART: + { + int ret, loop, ok; + u_long flags; + + //Check if there is space in the done list + ok = 0; + for (loop = 0; loop < VME_DMADONESIZE; loop++) + if (dma_donelist[loop].handle == 0xffffffff && dma_donelist[loop].pid == 0) + { + ok = 1; + break; + } + if (!ok) + { + kerror(("vme_rcc(ioctl,VMEDMASTART): no memory left in done list\n")) + return(-VME_NODOMEMEM); + } + else + kdebug(("vme_rcc(ioctl,VMEDMASTART): using entry %d in done list\n", loop)) + + current_dmahandle.index = loop; + pdata->dma[loop] = 1; + + //Get the DMA semaphore + //MJ: The API document says that the function should return VME_BUSY if the DMA controller + // is in use (semaphore=0). There seems to be a function (down_trylock) but it is so badly + // documented (i.e. not at all) that I don't dare to use it. Therefore I do it the forbidden way. + + #ifdef VMEDEBUG + *marker=((current->pid) << 8) | 2; + #endif + + local_irq_save(flags); //MJ: for 2.6 see p274 & p275 + if (dma_semaphore.count.counter < 1) + { + local_irq_restore(flags); + dma_sem_busy++; + kerror(("vme_rcc(ioctl,VMEDMASTART): failed to get semaphore\n")) + return(-VME_DMABUSY); + } + + ret = down_interruptible(&dma_semaphore); //we should now have the garantee to get the semaphore + local_irq_restore(flags); + if (ret) + return(-VME_ERESTARTSYS); + + #ifdef VMEDEBUG + *marker=((current->pid) << 8) | 3; + #endif + + if (copy_from_user(&pdata->dma_params, (void *)arg, sizeof(VME_DMAstart_t))) + { + kerror(("vme_rcc(ioctl,VMEDMASTART): error from copy_from_user\n")) + return(-VME_EFAULT); + } + + kdebug(("vme_rcc(ioctl,VMEDMASTART): PCI address of chain = 0x%08x\n", pdata->dma_params.paddr)) + kdebug(("vme_rcc(ioctl,VMEDMASTART): Handle chain = 0x%08x\n", pdata->dma_params.handle)) + + current_dmahandle.handle = pdata->dma_params.handle; + current_dmahandle.pid = current->pid; + kdebug(("vme_rcc(ioctl,VMEDMASTART): current_dmahandle.handle = %d\n", current_dmahandle.handle)) + kdebug(("vme_rcc(ioctl,VMEDMASTART): current_dmahandle.pid = %d\n", current_dmahandle.pid)) + + //start the chained DMA + uni->dgcs |= 0x00006f00; //clear all status and error flags + uni->dcpp = pdata->dma_params.paddr; + uni->dgcs = 0x88000008; //start chaind DMA, enable EOT interrupt + kdebug(("vme_rcc(ioctl,VMEDMASTART): Transfer started\n")) + + #ifdef VMEDEBUG + *marker=((current->pid) << 8) | 4; + #endif + + break; + } + + case VMEDMAPOLL: + { + u_int ok, loop = 0; + + if (copy_from_user(&pdata->dma_poll_params, (void *)arg, sizeof(VME_DMAhandle_t))) + { + kerror(("vme_rcc(ioctl,VMEDMAPOLL): error from copy_from_user\n")) + return(-VME_EFAULT); + } + + #ifdef VMEDEBUG + *marker=((current->pid) << 8) | 5; + #endif + + current_dmahandle.timeout = 1; + + if (pdata->dma_poll_params.timeoutval > 0) + { + dma_timer.function = vme_dmaTimeout; + dma_timer.data = (u_long)&dma_to; + dma_timer.expires = jiffies + pdata->dma_poll_params.timeoutval; + add_timer(&dma_timer); + } + + ok = 0; + + #ifdef VMEDEBUG + *marker=((current->pid) << 8) | 7; + #endif + + while (!ok && current_dmahandle.timeout == 1) + { + for (loop = 0; loop < VME_DMADONESIZE; loop++) + { + kdebug(("vme_rcc(ioctl,VMEDMAPOLL): dma_donelist[%d].handle = %d params.handle = %d\n", loop, dma_donelist[loop].handle, pdata->dma_poll_params.handle)) + kdebug(("vme_rcc(ioctl,VMEDMAPOLL): dma_donelist[%d].pid = %d pid = %d\n", loop, dma_donelist[loop].pid, current->pid)) + + if (dma_donelist[loop].handle == pdata->dma_poll_params.handle && dma_donelist[loop].pid == (u_int) current->pid) + { + ok = 1; + kdebug(("vme_rcc(ioctl,VMEDMAPOLL): DMA confirmation found under index %d\n", loop)) + #ifdef VMEDEBUG + *marker=((current->pid) << 8) | 0xa; + *marker=pdata->dma_poll_params.handle; + *marker=loop; + #endif + break; + } + } + if (pdata->dma_poll_params.timeoutval == 0) + break; //Don't poll, just probe + } + + #ifdef VMEDEBUG + *marker=((current->pid) << 8) | 0x60 | current_dmahandle.timeout; + #endif + + if (!ok) //This DMA has not yet completed + { + #ifdef VMEDEBUG + *marker=0x4444; + #endif + + kdebug(("vme_rcc(ioctl,VMEDMAPOLL): Still busy\n")) + pdata->dma_poll_params.ctrl = 0; + pdata->dma_poll_params.counter = 0x80000000; // Just to avoid 0 + pdata->dma_poll_params.timeout = current_dmahandle.timeout; + } + else + { + #ifdef VMEDEBUG + *marker=((current->pid) << 8) | 9; + *marker=loop; + #endif + + kdebug(("vme_rcc(ioctl,VMEDMAPOLL): loop = %d\n", loop)) + pdata->dma_poll_params.ctrl = dma_donelist[loop].ctrl; + pdata->dma_poll_params.counter = dma_donelist[loop].counter; + pdata->dma_poll_params.timeout = current_dmahandle.timeout; + dma_donelist[loop].handle = 0xffffffff; + dma_donelist[loop].pid = 0; + pdata->dma[loop] = 0; + kdebug(("vme_rcc(ioctl,VMEDMAPOLL): params.ctrl = 0x%08x\n", pdata->dma_poll_params.ctrl)) + kdebug(("vme_rcc(ioctl,VMEDMAPOLL): params.counter = 0x%08x\n", pdata->dma_poll_params.counter)) + kdebug(("vme_rcc(ioctl,VMEDMAPOLL): params.timeout = %d\n", pdata->dma_poll_params.timeout)) + } + + //Stop the timer + del_timer(&dma_timer); // OK also if timer ran out + #ifdef VMEDEBUG + *marker=((current->pid) << 8) | 8; + #endif + + if (copy_to_user((void *)arg, &pdata->dma_poll_params, sizeof(VME_DMAhandle_t))) + { + kerror(("vme_rcc(ioctl,VMEDMAPOLL): error from copy_to_user\n")) + return(-VME_EFAULT); + } + break; + } + + case VMEDMADUMP: + { + char *buf; + int loop,len; + u_int data, value; + + buf = (char *)kmalloc(TEXT_SIZE2, GFP_KERNEL); + if (buf == NULL) + { + kerror(("vme_rcc(ioctl,VMESLAVEMAPDUMP): error from kmalloc\n")) + return(-VME_KMALLOC); + } + len = 0; + len += sprintf(buf + len, "PCI_CSR register = 0x%08x\n", uni->pci_csr); + len += sprintf(buf + len, "DMA registers:\n"); + len += sprintf(buf + len, "PCI address (DLA) = 0x%08x\n", uni->dla); + len += sprintf(buf + len, "VME address (DVA) = 0x%08x\n", uni->dva); + len += sprintf(buf + len, "Byte count (DTBC) = 0x%08x\n", uni->dtbc); + len += sprintf(buf + len, "Chain pointer (DCPP) = 0x%08x\n", uni->dcpp); + data = uni->dctl; + len += sprintf(buf + len, "Transfer control reg. (DCTL) = 0x%08x\n", data); + len += sprintf(buf + len, "64 bit PCI is %s\n", data & 0x80 ? "Enabled" : "Disabled"); + len += sprintf(buf + len, "VMEbus cycle type: %s\n", data & 0x100 ? "Block cycles" : "Single cycles"); + len += sprintf(buf + len, "AM code tpye: %s\n", data & 0x1000 ? "Supervisor" : "User"); + len += sprintf(buf + len, "AM code tpye: %s\n", data & 0x4000 ? "Programm" : "Data"); + len += sprintf(buf + len, "VMEbus address space: "); + value = (data >> 16) & 0x7; + if (value == 0) len += sprintf(buf + len, "A16\n"); + if (value == 1) len += sprintf(buf + len, "A24\n"); + if (value == 2) len += sprintf(buf + len, "A32\n"); + if (value == 3) len += sprintf(buf + len, "Reserved\n"); + if (value == 4) len += sprintf(buf + len, "Reserved\n"); + if (value == 5) len += sprintf(buf + len, "Reserved\n"); + if (value == 6) len += sprintf(buf + len, "User 1\n"); + if (value == 7) len += sprintf(buf + len, "User 2\n"); + len += sprintf(buf + len, "VMEbus max data width: "); + value = (data >> 22) & 0x3; + if (value == 0) len += sprintf(buf + len, "D8\n"); + if (value == 1) len += sprintf(buf + len, "D16\n"); + if (value == 2) len += sprintf(buf + len, "D32\n"); + if (value == 3) len += sprintf(buf + len, "D64\n"); + len += sprintf(buf + len, "Transfer direction: VMEbus %s\n", data & 0x80000000 ? "Write": "Read"); + data = uni->dgcs; + len += sprintf(buf + len, "General control reg. (DGCS) = 0x%08x\n", data); + len += sprintf(buf + len, "Interrupt on protocol error: %s\n", data & 0x1 ? "Enabled" : "Disabled"); + len += sprintf(buf + len, "Interrupt on VME error: %s\n", data & 0x2 ? "Enabled" : "Disabled"); + len += sprintf(buf + len, "Interrupt on PCI error: %s\n", data & 0x4 ? "Enabled" : "Disabled"); + len += sprintf(buf + len, "Interrupt when done: %s\n", data & 0x8 ? "Enabled" : "Disabled"); + len += sprintf(buf + len, "Interrupt when halted: %s\n", data & 0x20 ? "Enabled" : "Disabled"); + len += sprintf(buf + len, "Interrupt when stopped: %s\n", data & 0x40 ? "Enabled" : "Disabled"); + len += sprintf(buf + len, "Protocol error: %s\n", data & 0x100 ? "Yes" : "No"); + len += sprintf(buf + len, "VMEbus error: %s\n", data & 0x200 ? "Yes" : "No"); + len += sprintf(buf + len, "PCI Bus error: %s\n", data & 0x400 ? "Yes" : "No"); + len += sprintf(buf + len, "Transfer complete: %s\n", data & 0x800 ? "Yes" : "No"); + len += sprintf(buf + len, "DMA halted: %s\n",data&0x2000?"Yes":"No"); + len += sprintf(buf + len, "DMA stopped: %s\n",data&0x4000?"Yes":"No"); + len += sprintf(buf + len, "DMA active: %s\n",data&0x8000?"Yes":"No"); + value = (data >> 16) & 0xf; + len += sprintf(buf + len, "Minimum inter tenure gap: "); + if (value == 0) len += sprintf(buf + len, "0 us\n"); + else if (value == 1) len += sprintf(buf + len, "16 us\n"); + else if (value == 2) len += sprintf(buf + len, "32 us\n"); + else if (value == 3) len += sprintf(buf + len, "64 us\n"); + else if (value == 4) len += sprintf(buf + len, "128 us\n"); + else if (value == 5) len += sprintf(buf + len, "256 us\n"); + else if (value == 6) len += sprintf(buf + len, "512 us\n"); + else if (value == 7) len += sprintf(buf + len, "1024 us\n"); + else len += sprintf(buf + len, "Out of range\n"); + len += sprintf(buf + len, "Bytes transfered per bus tenure: "); + value = (data >> 20) & 0xf; + if (value == 0) len += sprintf(buf + len, "Until done\n"); + else if (value == 1) len += sprintf(buf + len, "256 bytes\n"); + else if (value == 2) len += sprintf(buf + len, "512 bytes\n"); + else if (value == 3) len += sprintf(buf + len, "1 Kbye\n"); + else if (value == 4) len += sprintf(buf + len, "2 Kbye\n"); + else if (value == 5) len += sprintf(buf + len, "4 Kbye\n"); + else if (value == 6) len += sprintf(buf + len, "8 Kbye\n"); + else if (value == 7) len += sprintf(buf + len, "16 Kbye\n"); + else len += sprintf(buf + len, "Out of range\n"); + len += sprintf(buf + len, "DMA chaining: %s\n",data & 0x8000000 ? "Yes" : "No"); + len += sprintf(buf + len, "DMA halt request: %s\n",data & 0x20000000 ? "Halt DMA after current package" : "No"); + len += sprintf(buf + len, "DMA stop request: %s\n",data & 0x40000000 ? "process buffered data and stop" : "No"); + len += sprintf(buf + len, "DMA go: %s\n",data & 0x80000000 ? "Yes" : "No"); + + len += sprintf(buf + len, "\nDumping active entries in dma_donelist:\n"); + for (loop = 0; loop < VME_DMADONESIZE; loop++) + { + if (dma_donelist[loop].handle != 0xffffffff && dma_donelist[loop].pid) + { + len += sprintf(buf + len, "dma_donelist[%d].handle = %d\n", loop, dma_donelist[loop].handle); + len += sprintf(buf + len, "dma_donelist[%d].pid = %d\n", loop, dma_donelist[loop].pid); + len += sprintf(buf + len, "dma_donelist[%d].ctrl = 0x%08x\n", loop, dma_donelist[loop].ctrl); + len += sprintf(buf + len, "dma_donelist[%d].counter = 0x%08x\n", loop, dma_donelist[loop].counter); + len += sprintf(buf + len, "dma_donelist[%d].timeout = %d\n\n", loop, dma_donelist[loop].timeout); + } + } + + if (copy_to_user((void *)arg, buf, TEXT_SIZE2*sizeof(char))) + { + kerror(("vme_rcc(ioctl,VMEDMADUMP): error from copy_to_user\n")) + return(-VME_EFAULT); + } + kfree(buf); + break; + } + + case VMELINK: + { + VME_IntHandle_t *group_ptr; + u_int i; + int vct; + + if (copy_from_user(&pdata->VME_int_handle, (void *)arg, sizeof(VME_IntHandle_t))) + { + kerror(("vme_rcc(ioctl,VMELINK): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMELINK): # vectors = %d\n", pdata->VME_int_handle.nvectors)) + for (i = 0; i < pdata->VME_int_handle.nvectors; i++) + kdebug(("vme_rcc(ioctl,VMELINK): vector # %d = 0x%8x\n", i, pdata->VME_int_handle.vector[i])) + + if (irq_level_table[pdata->VME_int_handle.level - 1] == 0) // is this level enabled? + { + kdebug(("vme_rcc(ioctl,VMELINK): level %d is disabled\n", pdata->VME_int_handle.level)) + return(-VME_INTDISABLED); + } + + kdebug(("vme_rcc(ioctl,VMELINK): level = %d, type = %d\n", pdata->VME_int_handle.level, pdata->VME_int_handle.type)) + + if (pdata->VME_int_handle.type != irq_level_table[pdata->VME_int_handle.level - 1]) // check type + { + kdebug(("vme_rcc(ioctl,VMELINK): level %d, user mode = %d, VMEconfig mode = %d\n", + pdata->VME_int_handle.level, pdata->VME_int_handle.type, irq_level_table[pdata->VME_int_handle.level - 1])) + return(-VME_INTCONF); + } + + down(&link_table.link_dsc_sem); + + for (i = 0; i < pdata->VME_int_handle.nvectors; i++) + { + vct = pdata->VME_int_handle.vector[i]; + if (link_table.link_dsc[vct].pid) + { + kdebug(("vme_rcc(ioctl,VMELINK): vector busy, pid = %d\n", link_table.link_dsc[vct].pid)) + up(&link_table.link_dsc_sem); + return(-VME_INTUSED); + } + } + + // allocate aux. structure for multi vector handles and fill it + group_ptr = (VME_IntHandle_t*) NULL; + if (pdata->VME_int_handle.nvectors > 1) + { + group_ptr = kmalloc(sizeof(VME_IntHandle_t), GFP_KERNEL); + if (!group_ptr) + { + up(&link_table.link_dsc_sem); + return(-VME_ENOMEM); + } + group_ptr->nvectors = pdata->VME_int_handle.nvectors; + for (i = 0; i < pdata->VME_int_handle.nvectors; i++) + group_ptr->vector[i] = pdata->VME_int_handle.vector[i]; + } + + for (i = 0; i < pdata->VME_int_handle.nvectors; i++) + { + vct = pdata->VME_int_handle.vector[i]; + + link_table.link_dsc[vct].pid = current->pid; // current process + link_table.link_dsc[vct].vct = vct; // vector + link_table.link_dsc[vct].lvl = pdata->VME_int_handle.level; // level + link_table.link_dsc[vct].type = pdata->VME_int_handle.type; // type + link_table.link_dsc[vct].pending = 0; // no interrupt pending + link_table.link_dsc[vct].total_count = 0; // total # interrupts + link_table.link_dsc[vct].group_ptr = group_ptr; // the group vectors + sema_init(&link_table.link_dsc[vct].sem,0); // initialise semaphore count + link_table.link_dsc[vct].sig = 0; // no signal + + pdata->link_dsc_flag[vct] = 1; // remember which file it belongs to + } + + up(&link_table.link_dsc_sem); + break; + } + + case VMESETLEVEL: //MJ: It seems that this entry point is obsolete + { + if (copy_from_user(&pdata->VME_IntEnable, (void *)arg, sizeof(VME_IntEnable_t))) + { + kerror(("vme_rcc(ioctl,VMESETLEVEL): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMESETLEVEL): level = %d, type = %d\n", pdata->VME_IntEnable.level, pdata->VME_IntEnable.type)) + + if (irq_level_table[pdata->VME_IntEnable.level-1] == VME_LEVELISDISABLED) + return(-VME_LVLDISABLED); + + irq_level_table[pdata->VME_IntEnable.level-1] = pdata->VME_IntEnable.type; + break; + } + + case VMEINTENABLE: + { + u_int lint_en; + u_long flags; + + if (copy_from_user(&pdata->VME_IntEnable2, (void *)arg, sizeof(VME_IntEnable_t))) + { + kerror(("vme_rcc(ioctl,VMEINTENABLE): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMEINTENABLE): level = %d\n", pdata->VME_IntEnable2.level)) + + // ONLY allowed if RORA level + if (irq_level_table[pdata->VME_IntEnable2.level-1] != VME_INT_RORA) + return(-VME_LVLISNOTRORA); + + local_irq_save(flags); //MJ: for 2.6 see p274 & p275 + + lint_en = readl((char*)uni + LINT_EN); // read enabled irq bits + lint_en |= (1 << pdata->VME_IntEnable2.level); // BM_LINT_VIRQxxx + kdebug(("vme_rcc(ioctl,VMEINTENABLE): lint_en =0x %x\n", lint_en)) + writel(lint_en, (char*)uni + LINT_EN); // write it back + + local_irq_restore(flags); + break; + } + + case VMEINTDISABLE: + { + u_int lint_en; + u_long flags; + + if (copy_from_user(&pdata->VME_IntEnable3, (void *)arg, sizeof(VME_IntEnable_t))) + { + kerror(("vme_rcc(ioctl,VMEINTDISABLE): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMEINTDISABLE): level = %d\n", pdata->VME_IntEnable3.level)) + + local_irq_save(flags); //MJ: for 2.6 see p274 & p275 + + lint_en = readl((char*)uni + LINT_EN); // read enabled irq bits + lint_en &= ~(1 << pdata->VME_IntEnable3.level); // BM_LINT_VIRQxxx + kdebug(("vme_rcc(ioctl,VMEINTDISABLE): lint_en =0x %x\n", lint_en)) + writel(lint_en, (char*)uni + LINT_EN); // write it back + + local_irq_restore(flags); + break; + } + + case VMEWAIT: + { + int first_vct, cur_vct = 0, use_timer = 0, result = 0; + u_int i; + u_long flags; + struct timer_list int_timer; + VME_IntHandle_t *group_ptr; + + if (copy_from_user(&pdata->VME_WaitInt2, (void *)arg, sizeof(VME_WaitInt_t))) + { + kerror(("vme_rcc(ioctl,VMEWAIT): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMEWAIT): # vectors = %d, timeout = %d\n", pdata->VME_WaitInt2.int_handle.nvectors, pdata->VME_WaitInt2.timeout)) + for (i = 0; i < pdata->VME_WaitInt2.int_handle.nvectors; i++) + kdebug(("vme_rcc(ioctl,VMEWAIT): vector # %d = 0x%8x\n", i, pdata->VME_WaitInt2.int_handle.vector[i])) + + first_vct = pdata->VME_WaitInt2.int_handle.vector[0]; + + if (pdata->VME_WaitInt2.timeout > 0) + { + init_timer(&int_timer); + int_timer.function = vme_intTimeout; + int_timer.data = (u_long)&pdata->VME_WaitInt2; + int_timer.expires = jiffies + pdata->VME_WaitInt2.timeout; + add_timer(&int_timer); + use_timer = 1; + } + + if (pdata->VME_WaitInt2.timeout) // Wait : with timeout or not + { + kdebug(("vme_rcc(ioctl,VMEWAIT): Before Down, semaphore = %d\n", link_table.link_dsc[first_vct].sem.count.counter)) + + // IR handled before we wait. Not quite RACE safe but only for statistics .. + if (link_table.link_dsc[first_vct].sem.count.counter > 0) + { + if (link_table.link_dsc[first_vct].sem.count.counter == 1) + sem_positive[0]++; + else + sem_positive[1]++; + } + + #ifdef INTDEBUG + *marker=((current->pid) << 8) | 0x3; + #endif + + down_interruptible(&link_table.link_dsc[first_vct].sem); // wait .. + + #ifdef INTDEBUG + *marker=((current->pid) << 8) | 0x4; + *marker=first_vct; + *marker=link_table.link_dsc[first_vct].sem.count.counter; + #endif + + if (signal_pending(current)) + { // interrupted system call + kdebug(("vme_rcc(ioctl,VMEWAIT): Interrupted by signal\n")) + result = -VME_INTBYSIGNAL; // or ERESTARTSYS ? + } + + else if ((use_timer == 1) && (pdata->VME_WaitInt2.timeout == 0)) + result = -VME_TIMEOUT; + } + + pdata->VME_WaitInt2.vector = 0; // return 0 if NO pending + pdata->VME_WaitInt2.level = 0; + pdata->VME_WaitInt2.type = 0; + pdata->VME_WaitInt2.multiple = 0; + + if (result == 0) // for all values of timeout & no early returns: get IR info + { + if (link_table.link_dsc[first_vct].group_ptr == NULL) // single vector + { + if (link_table.link_dsc[first_vct].pending > 0) + { + pdata->VME_WaitInt2.level = link_table.link_dsc[first_vct].lvl; + pdata->VME_WaitInt2.type = link_table.link_dsc[first_vct].type; + pdata->VME_WaitInt2.vector = first_vct; + pdata->VME_WaitInt2.multiple = link_table.link_dsc[first_vct].pending; + link_table.link_dsc[first_vct].pending = 0; // done + } + } + else // scan vectors in group and return FIRST one pending + // the scan starts from the first: an issue ? + { + local_irq_save(flags); // may not really be needed //MJ: for 2.6 see p274 & p275 + + group_ptr = link_table.link_dsc[first_vct].group_ptr; + kdebug(("vme_rcc(ioctl,VMEWAIT): group pointer = 0x%p\n",group_ptr)) + for (i = 0; i < group_ptr->nvectors; i++) + { + cur_vct = group_ptr->vector[i]; + kdebug(("vme_rcc(ioctl,VMEWAIT): current vector = %d\n",cur_vct)) + if (link_table.link_dsc[cur_vct].pending > 0) + { + pdata->VME_WaitInt2.level = link_table.link_dsc[cur_vct].lvl; + pdata->VME_WaitInt2.type = link_table.link_dsc[cur_vct].type; + pdata->VME_WaitInt2.vector = cur_vct; + pdata->VME_WaitInt2.multiple = link_table.link_dsc[cur_vct].pending; + link_table.link_dsc[cur_vct].pending = 0; // done + break; + } + } + local_irq_restore(flags); + } + } + + if (use_timer == 1) + del_timer(&int_timer); // OK also if timer ran out + + if (copy_to_user((void *)arg, &pdata->VME_WaitInt2, sizeof(VME_WaitInt_t))) + { + kerror(("vme_rcc(ioctl,VMEWAIT): error from copy_to_user\n")) + return(-VME_EFAULT); + } + + return (result); + break; // dummy .. + } + + case VMEREGISTERSIGNAL: + { + int vct; + u_int i; + + if (copy_from_user(&pdata->VME_RegSig, (void *)arg, sizeof(VME_RegSig_t))) + { + kerror(("vme_rcc(ioctl,VMEREGISTERSIGNAL): error from copy_from_user\n")) + return(-VME_EFAULT); + } + + pdata->VME_int_handle2 = pdata->VME_RegSig.int_handle; + + kdebug(("vme_rcc(ioctl,VMEREGISTERSIGNAL): # vectors = %d, signal # = %d\n", pdata->VME_int_handle2.nvectors, pdata->VME_RegSig.signum)) + for (i = 0; i < pdata->VME_int_handle2.nvectors; i++) + kdebug(("vme_rcc(ioctl,VMEREGISTERSIGNAL): vector # %d = 0x%8x\n", i, pdata->VME_int_handle2.vector[i])); + + down(&link_table.link_dsc_sem); + + for (i = 0; i < pdata->VME_int_handle2.nvectors; i++) + { + vct = pdata->VME_int_handle2.vector[i]; + link_table.link_dsc[vct].sig = pdata->VME_RegSig.signum; + } + + up(&link_table.link_dsc_sem); + break; + } + + case VMEINTERRUPTINFOGET: + { + int first_vct, cur_vct = 0; + u_int i; + u_long flags; + VME_IntHandle_t *group_ptr; + + if (copy_from_user(&pdata->VME_WaitInt3, (void *)arg, sizeof(VME_WaitInt_t))) + { + kdebug(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): # vectors = %d \n", pdata->VME_WaitInt3.int_handle.nvectors)) + for (i = 0; i < pdata->VME_WaitInt3.int_handle.nvectors; i++) + kdebug(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): vector # %d = 0x%8x\n", i, pdata->VME_WaitInt3.int_handle.vector[i])) + + first_vct = pdata->VME_WaitInt3.int_handle.vector[0]; + + pdata->VME_WaitInt3.vector = 0; // return 0 if NO pending + pdata->VME_WaitInt3.level = 0; + pdata->VME_WaitInt3.type = 0; + pdata->VME_WaitInt3.multiple = 0; + + if (link_table.link_dsc[first_vct].group_ptr == NULL) + { + if (link_table.link_dsc[first_vct].pending > 0) + { + pdata->VME_WaitInt3.level = link_table.link_dsc[first_vct].lvl; + pdata->VME_WaitInt3.type = link_table.link_dsc[first_vct].type; + pdata->VME_WaitInt3.vector = first_vct; + pdata->VME_WaitInt3.multiple = link_table.link_dsc[first_vct].pending; // TBD + link_table.link_dsc[first_vct].pending = 0; // done + } + } + else // scan vectors in group and return FIRST one pending + { + local_irq_save(flags); // may not really be needed //MJ: for 2.6 see p274 & p275 + + group_ptr = link_table.link_dsc[first_vct].group_ptr; + kdebug(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): group pointer = 0x%p\n",group_ptr)) + for (i = 0; i < group_ptr->nvectors; i++) + { + cur_vct = group_ptr->vector[i]; + kdebug(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): current vector = %d\n",cur_vct)) + if (link_table.link_dsc[cur_vct].pending > 0) + { + pdata->VME_WaitInt3.level = link_table.link_dsc[cur_vct].lvl; + pdata->VME_WaitInt3.type = link_table.link_dsc[cur_vct].type; + pdata->VME_WaitInt3.vector = cur_vct; + pdata->VME_WaitInt3.multiple = link_table.link_dsc[cur_vct].pending; + link_table.link_dsc[cur_vct].pending = 0; // done + break; + } + } + local_irq_restore(flags); + } + + if (copy_to_user((void *)arg, &pdata->VME_WaitInt3, sizeof(VME_WaitInt_t))) + { + kerror(("vme_rcc(ioctl,VMEINTERRUPTINFOGET): error from copy_to_user\n")) + return(-VME_EFAULT); + } + break; + } + + case VMEUNLINK: + { + u_int i; + int vct, first_vector; + + if (copy_from_user(&pdata->VME_int_handle3, (void *)arg, sizeof(VME_IntHandle_t))) + { + kerror(("vme_rcc(ioctl,VMEUNLINK): error from copy_from_user\n")) + return(-VME_EFAULT); + } + kdebug(("vme_rcc(ioctl,VMEUNLINK): # vectors = %d\n", pdata->VME_int_handle3.nvectors)) + for (i = 0; i < pdata->VME_int_handle3.nvectors; i++) + kdebug(("vme_rcc(ioctl,VMEUNLINK): vector # %d = 0x%x\n", i, pdata->VME_int_handle3.vector[i])); + + down(&link_table.link_dsc_sem); + + if (pdata->VME_int_handle3.nvectors > 1) + { + first_vector = pdata->VME_int_handle3.vector[0]; + kfree(link_table.link_dsc[first_vector].group_ptr); + kdebug(("vme_rcc(ioctl,VMEUNLINK): first vector = %d\n", first_vector)) + kdebug(("vme_rcc(ioctl,VMEUNLINK): group_ptr = 0x%p\n", link_table.link_dsc[first_vector].group_ptr)) + } + + for (i = 0; i < pdata->VME_int_handle3.nvectors; i++) + { + vct = pdata->VME_int_handle3.vector[i]; + + link_table.link_dsc[vct].pid = 0; + link_table.link_dsc[vct].vct = 0; + link_table.link_dsc[vct].lvl = 0; + link_table.link_dsc[vct].pending = 0; + link_table.link_dsc[vct].total_count = 0; + link_table.link_dsc[vct].group_ptr = 0; + sema_init(&link_table.link_dsc[vct].sem,0); + link_table.link_dsc[vct].sig = 0; + + pdata->link_dsc_flag[vct] = 0; + } + up(&link_table.link_dsc_sem); + break; + } + + // called from vmeconfig when Universe registers are updated + case VMEUPDATE: + { + int ret, i; + + kdebug(("vme_rcc(ioctl,VMEUPDATE): Function called\n")) + + ret = copy_from_user(&pdata->update_data, (void *)arg, sizeof(VME_Update_t)); + if (ret) + { + kerror(("vme_rcc(ioctl,VMEUPDATE): error %d from copy_from_user\n",ret)) + return(-VME_EFAULT); + } + + fill_mstmap(uni->lsi0_ctl, uni->lsi0_bs, uni->lsi0_bd, uni->lsi0_to, &stmap.master[0]); + fill_mstmap(uni->lsi1_ctl, uni->lsi1_bs, uni->lsi1_bd, uni->lsi1_to, &stmap.master[1]); + fill_mstmap(uni->lsi2_ctl, uni->lsi2_bs, uni->lsi2_bd, uni->lsi2_to, &stmap.master[2]); + fill_mstmap(uni->lsi3_ctl, uni->lsi3_bs, uni->lsi3_bd, uni->lsi3_to, &stmap.master[3]); + fill_mstmap(uni->lsi4_ctl, uni->lsi4_bs, uni->lsi4_bd, uni->lsi4_to, &stmap.master[4]); + fill_mstmap(uni->lsi5_ctl, uni->lsi5_bs, uni->lsi5_bd, uni->lsi5_to, &stmap.master[5]); + fill_mstmap(uni->lsi6_ctl, uni->lsi6_bs, uni->lsi6_bd, uni->lsi6_to, &stmap.master[6]); + fill_mstmap(uni->lsi7_ctl, uni->lsi7_bs, uni->lsi7_bd, uni->lsi7_to, &stmap.master[7]); + + fill_slvmap(uni->vsi0_ctl, uni->vsi0_bs, uni->vsi0_bd, uni->vsi0_to, &stmap.slave[0]); + fill_slvmap(uni->vsi1_ctl, uni->vsi1_bs, uni->vsi1_bd, uni->vsi1_to, &stmap.slave[1]); + fill_slvmap(uni->vsi2_ctl, uni->vsi2_bs, uni->vsi2_bd, uni->vsi2_to, &stmap.slave[2]); + fill_slvmap(uni->vsi3_ctl, uni->vsi3_bs, uni->vsi3_bd, uni->vsi3_to, &stmap.slave[3]); + fill_slvmap(uni->vsi4_ctl, uni->vsi4_bs, uni->vsi4_bd, uni->vsi4_to, &stmap.slave[4]); + fill_slvmap(uni->vsi5_ctl, uni->vsi5_bs, uni->vsi5_bd, uni->vsi5_to, &stmap.slave[5]); + fill_slvmap(uni->vsi6_ctl, uni->vsi6_bs, uni->vsi6_bd, uni->vsi6_to, &stmap.slave[6]); + fill_slvmap(uni->vsi7_ctl, uni->vsi7_bs, uni->vsi7_bd, uni->vsi7_to, &stmap.slave[7]); + + // update level table + if (pdata->update_data.irq_mode[0]) + { + kdebug(("vme_rcc(ioctl,VMEUPDATE): IRQ mode parameters are valid\n")) + for (i = 0; i < 7; i++) + { + irq_level_table[i] = pdata->update_data.irq_mode[i + 1]; + kdebug(("vme_rcc(ioctl,VMEUPDATE): IRQ mode for level %d = %d\n", i, pdata->update_data.irq_mode[i + 1])) + } + irq_sysfail = pdata->update_data.irq_mode[8]; + kdebug(("vme_rcc(ioctl,VMEUPDATE): IRQ mode for SYSFAIL = %d\n", pdata->update_data.irq_mode[8])) + } + else + kdebug(("vme_rcc(ioctl,VMEUPDATE): IRQ mode parameters are not valid\n")) + vmetab_ok = 1; + break; + } + + case VMETEST: + { + int ret, count; + + ret = copy_from_user(&count, (void *)arg, sizeof(int)); + if (ret) + { + kerror(("vme_rcc(ioctl,VMETEST): error %d from copy_from_user\n",ret)) + return(-VME_EFAULT); + } + + count++; + + if (copy_to_user((void *)arg, &count, sizeof(int))) + { + kerror(("vme_rcc(ioctl,VMETEST): error from copy_to_user\n")) + return(-VME_EFAULT); + } + + kdebug(("vme_rcc(ioctl,VMETEST): VME_ENOSYS=0x%08x\n",VME_ENOSYS)) + return(-VME_ENOSYS); + break; + } + } + return(0); +} + + +/*****************************************************/ +static void vme_rcc_vmaOpen(struct vm_area_struct *vma) +/*****************************************************/ +{ + kdebug(("vme_rcc_vmaOpen: Called\n")); +} + + +/******************************************************/ +static void vme_rcc_vmaClose(struct vm_area_struct *vma) +/******************************************************/ +{ + kdebug(("vme_rcc_vmaClose: mmap released\n")); +} + + +/********************************************************************/ +static int vme_rcc_mmap(struct file *file, struct vm_area_struct *vma) +/********************************************************************/ +{ + u_int size, offset; + + vma->vm_flags |= VM_RESERVED; + vma->vm_flags |= VM_LOCKED; + + kdebug(("vme_rcc(mmap): vma->vm_end = 0x%08x\n", (u_int)vma->vm_end)) + kdebug(("vme_rcc(mmap): vma->vm_start = 0x%08x\n", (u_int)vma->vm_start)) + kdebug(("vme_rcc(mmap): vma->vm_offset = 0x%08x\n", (u_int)vma->vm_pgoff << PAGE_SHIFT)) + kdebug(("vme_rcc(mmap): vma->vm_flags = 0x%08x\n", (u_int)vma->vm_flags)) + + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + + if(remap_page_range(vma, vma->vm_start, offset, size, vma->vm_page_prot)) + { + kerror(("vme_rcc(mmap): function remap_page_range failed \n")) + return(-VME_REMAP); + } + kdebug(("vme_rcc(mmap): vma->vm_start(2) = 0x%08x\n", (u_int)vma->vm_start)) + + vma->vm_ops = &vme_rcc_vm_ops; + return(0); +} + + +/***********************************************************************************************/ +static int vme_rcc_write_procmem(struct file *file, const char *buffer, u_long count, void *data) +/***********************************************************************************************/ +{ + int len; + struct vme_proc_data_t *fb_data = (struct vme_proc_data_t *)data; + + kdebug(("vme_rcc(write_procmem): vme_rcc_write_procmem called\n")) + + if(count > 99) + len = 99; + else + len = count; + + if (copy_from_user(fb_data->value, buffer, len)) + { + kdebug(("vme_rcc(write_procmem): error from copy_from_user\n")) + return(-VME_EFAULT); + } + + kdebug(("vme_rcc(write_procmem): len = %d\n", len)) + fb_data->value[len - 1] = '\0'; + kdebug(("vme_rcc(write_procmem): text passed = %s\n", fb_data->value)) + + if (!strcmp(fb_data->value, "debug")) + { + debug = 1; + kdebug(("vme_rcc(write_procmem): debugging enabled\n")) + } + + if (!strcmp(fb_data->value, "nodebug")) + { + kdebug(("vme_rcc(write_procmem): debugging disabled\n")) + debug = 0; + } + + if (!strcmp(fb_data->value, "elog")) + { + kdebug(("vme_rcc(write_procmem): error logging enabled\n")) + errorlog = 1; + } + + if (!strcmp(fb_data->value, "noelog")) + { + kdebug(("vme_rcc(write_procmem): error logging disabled\n")) + errorlog = 0; + } + return len; +} + + +/*****************************************************************************************************/ +static int vme_rcc_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data) +/*****************************************************************************************************/ +{ + int nchars = 0, cnt, loop; + u_int lint_stat, lint_en, lint_mask; + static int len = 0; + + kdebug(("vme_rcc(vme_rcc_read_procmem): Called with buf = 0x%08x\n", (u_int)buf)); + kdebug(("vme_rcc(vme_rcc_read_procmem): Called with *start = 0x%08x\n", (u_int)*start)); + kdebug(("vme_rcc(vme_rcc_read_procmem): Called with offset = %d\n", (u_int)offset)); + kdebug(("vme_rcc(vme_rcc_read_procmem): Called with count = %d\n", count)); + + if (offset == 0) + { + kdebug(("exp(exp_read_procmem): Creating text....\n")); + len = 0; + len += sprintf(proc_read_text + len, "RCC VMEbus driver for release %s (based on CVS tag %s)\n", RELEASE_NAME, CVSTAG); + + if (board_type == VP_PSE) len += sprintf(proc_read_text + len, "Board type: VP_PSE\n"); + if (board_type == VP_PMC) len += sprintf(proc_read_text + len, "Board type: VP_PMC\n"); + if (board_type == VP_100) len += sprintf(proc_read_text + len, "Board type: VP_100\n"); + if (board_type == VP_CP1) len += sprintf(proc_read_text + len, "Board type: VP_CP1\n"); + if (board_type == VP_110) len += sprintf(proc_read_text + len, "Board type: VP_110\n"); + if (board_type == VP_315) len += sprintf(proc_read_text + len, "Board type: VP_315\n"); + if (board_type == VP_317) len += sprintf(proc_read_text + len, "Board type: VP_317\n"); + if (board_type == VP_325) len += sprintf(proc_read_text + len, "Board type: VP_325\n"); + + len += sprintf(proc_read_text + len, "\nThe Universe chip %s been initialized with vmeconfig\n", vmetab_ok ? "has" : "has NOT"); + len += sprintf(proc_read_text + len, "\n SAFE function statistics\n"); + len += sprintf(proc_read_text + len, " TXFIFO not empty before safe cycle : %d times\n", txfifo_wait_1); + len += sprintf(proc_read_text + len, " TXFIFO not empty after safe cycle : %d times\n", txfifo_wait_2); + len += sprintf(proc_read_text + len, "\n VMEbus interrupts\n"); + len += sprintf(proc_read_text + len, "\n level state enabled pending # interrupts\n"); + + lint_stat = readl((char*)uni + LINT_STAT); // read the interrupt bits + lint_en = readl((char*)uni + LINT_EN); // read enabled irq bitsa + + for (cnt = 0; cnt < 7; cnt++) + { + lint_mask = 1<<(cnt+1); // BM_Lxxx_VIRQxxx + + if (irq_level_table[cnt] == VME_LEVELISDISABLED) + len += sprintf(proc_read_text + len, " %d %s %s %s\n", cnt+1,"disabled", + " ", (lint_stat & lint_mask) ? "yes":" no") ; + else if (irq_level_table[cnt] == VME_INT_ROAK) + len += sprintf(proc_read_text + len, " %d %s %11s %7s %8d\n", cnt+1,"ROAK", + (lint_en & lint_mask) ? "yes":" no", (lint_stat & lint_mask) ? "yes":" no", + Interrupt_counters.virq[cnt]); + else if (irq_level_table[cnt] == VME_INT_RORA) + len += sprintf(proc_read_text + len, " %d %s %11s %7s %8d\n", cnt+1,"RORA", + (lint_en & lint_mask) ? "yes":" no", (lint_stat & lint_mask) ? "yes":" no", + Interrupt_counters.virq[cnt]); + } + + if (irq_sysfail == VME_LEVELISDISABLED) + len += sprintf(proc_read_text + len, " %d %s %s %s\n", cnt+1, "disabled", " ", (lint_stat & lint_mask) ? "yes":" no") ; + + else + len += sprintf(proc_read_text + len, "SYSFAIL %s %11s %7s %8d\n", "RORA", (lint_en & 0x4000) ? "yes":" no", (lint_stat & 0x4000) ? "yes":" no", Interrupt_counters.sysfail); + + len += sprintf(proc_read_text + len, " Bus Error %8d\n", berr_int_count); + len += sprintf(proc_read_text + len, " DMA %8d\n", Interrupt_counters.dma); + len += sprintf(proc_read_text + len, "\n Link Descriptors: \n"); + + for (cnt = 0; cnt< VME_VCTSIZE; cnt++) + { + if (link_table.link_dsc[cnt].pid) + { + len += sprintf(proc_read_text + len, "\n Array index = %02d\n", cnt); + len += sprintf(proc_read_text + len, " Pid = %02d\n", link_table.link_dsc[cnt].pid); + len += sprintf(proc_read_text + len, " Vector = %02d\n", link_table.link_dsc[cnt].vct); + len += sprintf(proc_read_text + len, " Level = %02d\n", link_table.link_dsc[cnt].lvl); + len += sprintf(proc_read_text + len, " Type = %02d\n", link_table.link_dsc[cnt].type); + len += sprintf(proc_read_text + len, " Pending = %02d\n", link_table.link_dsc[cnt].pending); + len += sprintf(proc_read_text + len, " # interrupts = %02d\n", link_table.link_dsc[cnt].total_count); + len += sprintf(proc_read_text + len, " group pointer = 0x%p\n", link_table.link_dsc[cnt].group_ptr); + len += sprintf(proc_read_text + len, " Semaphore count = 0x%x\n", link_table.link_dsc[cnt].sem.count.counter); + len += sprintf(proc_read_text + len, " Signal # = %d\n", link_table.link_dsc[cnt].sig); + } + } + + len += sprintf(proc_read_text + len, " semaphore statistics\n"); + len += sprintf(proc_read_text + len, " semaphore count = 1 : %d times\n", sem_positive[0]); + len += sprintf(proc_read_text + len, " semaphore count > 1 : %d times\n", sem_positive[1]); + + len += sprintf(proc_read_text + len, "\n BERR Links: \n"); + for (cnt = 0; cnt< VME_MAX_BERR_PROCS; cnt++) + { + if (berr_proc_table.berr_link_dsc[cnt].pid) + { + len += sprintf(proc_read_text + len, "\n Array index = %02d\n", cnt); + len += sprintf(proc_read_text + len, " Pid = %02d\n", berr_proc_table.berr_link_dsc[cnt].pid); + len += sprintf(proc_read_text + len, " Signal # = %d\n", berr_proc_table.berr_link_dsc[cnt].sig); + } + } + + len += sprintf(proc_read_text + len, "\n SYSFAIL Link: \n"); + if (sysfail_link.pid) + { + len += sprintf(proc_read_text + len, " Pid = %02d\n", sysfail_link.pid); + len += sprintf(proc_read_text + len, " Signal # = %d\n", sysfail_link.sig); + } + + len += sprintf(proc_read_text + len, "\n BERR Descriptor: \n"); + len += sprintf(proc_read_text + len, " VMEbus address = 0x%8x\n", berr_dsc.vmeadd); + len += sprintf(proc_read_text + len, " address modifier = 0x%8x\n", berr_dsc.am); + len += sprintf(proc_read_text + len, " IACK = %d\n", berr_dsc.iack); + len += sprintf(proc_read_text + len, " LWORD* = %d\n", berr_dsc.lword); + len += sprintf(proc_read_text + len, " DS0* = %d\n", berr_dsc.ds0); + len += sprintf(proc_read_text + len, " DS1* = %d\n", berr_dsc.ds1); + len += sprintf(proc_read_text + len, " WRITE* = %d\n", berr_dsc.wr); + len += sprintf(proc_read_text + len, " multiple bit = 0x%8x\n", berr_dsc.multiple); + len += sprintf(proc_read_text + len, " bus error flag = 0x%8x\n", berr_dsc.flag); + + len += sprintf(proc_read_text + len, "\n Block transfer status\n"); + len += sprintf(proc_read_text + len, " # of unsuccessful semaphore requests: %d\n",dma_sem_busy); + len += sprintf(proc_read_text + len, " List of completed DMA transactions:\n"); + len += sprintf(proc_read_text + len, " process ID | handle | DMA ctrl | DMA counter\n"); + + for (loop = 0; loop < VME_DMADONESIZE; loop++) + { + if (dma_donelist[loop].pid) + { + len += sprintf(proc_read_text + len, " 0x%08x |", dma_donelist[loop].pid); + len += sprintf(proc_read_text + len, " 0x%08x |", dma_donelist[loop].handle); + len += sprintf(proc_read_text + len, " 0x%08x |", dma_donelist[loop].ctrl); + len += sprintf(proc_read_text + len, " 0x%08x\n", dma_donelist[loop].counter); + } + } + + len += sprintf(proc_read_text + len, " \n"); + len += sprintf(proc_read_text + len, "The command 'echo <action> > /proc/vme_rcc', executed as root,\n"); + len += sprintf(proc_read_text + len, "allows you to interact with the driver. Possible actions are:\n"); + len += sprintf(proc_read_text + len, "debug -> Enable debugging\n"); + len += sprintf(proc_read_text + len, "nodebug -> Disable debugging\n"); + len += sprintf(proc_read_text + len, "elog -> Log errors to /var/log/messages\n"); + len += sprintf(proc_read_text + len, "noelog -> Do not log errors to /var/log/messages\n"); + } + kdebug(("vme_rcc(vme_rcc_read_procmem): number of characters in text buffer = %d\n", len)); + + if (count < (len - offset)) + nchars = count; + else + nchars = len - offset; + kdebug(("vme_rcc(vme_rcc_read_procmem): min nchars = %d\n", nchars)); + + if (nchars > 0) + { + for (loop = 0; loop < nchars; loop++) + buf[loop + (offset & (PAGE_SIZE - 1))] = proc_read_text[offset + loop]; + *start = buf + (offset & (PAGE_SIZE - 1)); + } + else + { + nchars = 0; + *eof = 1; + } + + kdebug(("vme_rcc(vme_rcc_read_procmem): returning *start = 0x%08x\n", (u_int)*start)); + kdebug(("vme_rcc(vme_rcc_read_procmem): returning nchars = %d\n", nchars)); + return(nchars); +} + + +/*****************************/ +/* PCI driver functions */ +/*****************************/ + +/*************************************************************************************/ +static int __devinit vme_rcc_Probe(struct pci_dev *dev, const struct pci_device_id *id) //MJ: for _devinit see p31 +/*************************************************************************************/ +{ + u_char pci_revision; + u_int size, pci_ioaddr = 0; + u_long flags; + + kdebug(("vme_rcc(vme_rcc_Probe): called for device: 0x%04x / 0x%04x\n", dev->vendor, dev->device)); + kdebug(("vme_rcc(vme_rcc_Probe): Bus: 0x%04x / Devfn 0x%04x\n", dev->bus->number, dev->devfn)); + + if (maxcard == 0) + { + kdebug(("vme_rcc(vme_rcc_Probe): First (and hopefully only) Universe found\n")); + universedev = dev; + maxcard = 1; + } + else + { + kdebug(("vme_rcc(vme_rcc_Probe): Another Universe found (Parallel Universes are not supported)\n")); + return(-VME_EIO); + } + + // get revision directly from the Tundra + pci_read_config_byte(universedev, PCI_REVISION_ID, &pci_revision); + kdebug(("vme_rcc(vme_rcc_Probe): revision = %x\n", pci_revision)) + if (pci_revision < 1) + { + kerror(("vme_rcc(vme_rcc_Probe): Illegal Universe Revision %d\n", pci_revision)) + return(-VME_ILLREV); + } + + flags = pci_resource_flags(dev, BAR0); //MJ: See p317 + if ((flags & IORESOURCE_MEM) != 0) + { + pci_ioaddr = pci_resource_start(dev, BAR0) & PCI_BASE_ADDRESS_MEM_MASK; //MJ: See p317 + size = pci_resource_end(dev, BAR0) - pci_resource_start(dev, BAR0); //MJ: See p317 + kdebug(("vme_rcc(vme_rcc_Probe): Mapping %d bytes at PCI address 0x%08x\n", size, pci_ioaddr)); + uni = (universe_regs_t *)ioremap(pci_ioaddr, size); + if (uni == NULL) + { + kerror(("vme_rcc(vme_rcc_Probe): error from ioremap\n")); + return(-VME_EFAULT); + } + kdebug(("vme_rcc(vme_rcc_Probe): uni is at 0x%08x\n", (u_int)uni)); + } + else + { + kerror(("vme_rcc(vme_rcc_Probe): Bar 0 is not a memory resource")); + return(-EIO); + } + + pci_read_config_byte(universedev, PCI_INTERRUPT_LINE, &vme_irq_line); + kdebug(("vme_rcc(vme_rcc_Probe): interrupt line = %d \n", vme_irq_line)) + + if (request_irq(vme_irq_line, universe_irq_handler, SA_SHIRQ, "vme_rcc", universe_irq_handler)) + { + kerror(("vme_rcc(vme_rcc_Probe): request_irq failed on IRQ = %d\n", vme_irq_line)) + return(-VME_REQIRQ); + } + kdebug(("vme_rcc(vme_rcc_Probe): request_irq OK\n")) + + // Clear and enable the DMA interrupt + uni->lint_stat |= 0x100; + uni->lint_en |= 0x100; + + // Read the static master and slave mappings once + fill_mstmap(uni->lsi0_ctl, uni->lsi0_bs, uni->lsi0_bd, uni->lsi0_to, &stmap.master[0]); + fill_mstmap(uni->lsi1_ctl, uni->lsi1_bs, uni->lsi1_bd, uni->lsi1_to, &stmap.master[1]); + fill_mstmap(uni->lsi2_ctl, uni->lsi2_bs, uni->lsi2_bd, uni->lsi2_to, &stmap.master[2]); + fill_mstmap(uni->lsi3_ctl, uni->lsi3_bs, uni->lsi3_bd, uni->lsi3_to, &stmap.master[3]); + fill_mstmap(uni->lsi4_ctl, uni->lsi4_bs, uni->lsi4_bd, uni->lsi4_to, &stmap.master[4]); + fill_mstmap(uni->lsi5_ctl, uni->lsi5_bs, uni->lsi5_bd, uni->lsi5_to, &stmap.master[5]); + fill_mstmap(uni->lsi6_ctl, uni->lsi6_bs, uni->lsi6_bd, uni->lsi6_to, &stmap.master[6]); + fill_mstmap(uni->lsi7_ctl, uni->lsi7_bs, uni->lsi7_bd, uni->lsi7_to, &stmap.master[7]); + + fill_slvmap(uni->vsi0_ctl, uni->vsi0_bs, uni->vsi0_bd, uni->vsi0_to, &stmap.slave[0]); + fill_slvmap(uni->vsi1_ctl, uni->vsi1_bs, uni->vsi1_bd, uni->vsi1_to, &stmap.slave[1]); + fill_slvmap(uni->vsi2_ctl, uni->vsi2_bs, uni->vsi2_bd, uni->vsi2_to, &stmap.slave[2]); + fill_slvmap(uni->vsi3_ctl, uni->vsi3_bs, uni->vsi3_bd, uni->vsi3_to, &stmap.slave[3]); + fill_slvmap(uni->vsi4_ctl, uni->vsi4_bs, uni->vsi4_bd, uni->vsi4_to, &stmap.slave[4]); + fill_slvmap(uni->vsi5_ctl, uni->vsi5_bs, uni->vsi5_bd, uni->vsi5_to, &stmap.slave[5]); + fill_slvmap(uni->vsi6_ctl, uni->vsi6_bs, uni->vsi6_bd, uni->vsi6_to, &stmap.slave[6]); + fill_slvmap(uni->vsi7_ctl, uni->vsi7_bs, uni->vsi7_bd, uni->vsi7_to, &stmap.slave[7]); + +#if defined (VMEDEBUG) || defined (INTDEBUG) + marker = (u_int *)ioremap(0x80008000, 1024); + *marker=1; +#endif + + return(0); +} + + +/*********************************************/ +static void vme_rcc_Remove(struct pci_dev *dev) +/*********************************************/ +{ + u_int i, reg; + + for (i = 0; i < 7; i++) // restore initial enable bits from vmetab + { + kdebug(("vme_rcc(vme_rcc_Remove): irq_level_table[%d] =0x%x\n", i, irq_level_table[i])) + if (irq_level_table[i] != VME_LEVELISDISABLED) + { + reg = readl((char*)uni + LINT_EN); // read enabled irq bits + kdebug(("vme_rcc(vme_rcc_Remove): reg =0x%x\n", reg)) + reg |= (1<<(i+1)); // BM_LINT_VIRQxxx + kdebug(("vme_rcc(vme_rcc_Remove): reg =0x%x\n", reg)) + writel(reg, (char*)uni + LINT_EN); + } + } + + kdebug(("vme_rcc(vme_rcc_Remove): irqsysfail = 0x%x\n", irq_sysfail)) + if (irq_sysfail != VME_LEVELISDISABLED) + { + reg = readl((char*)uni + LINT_EN); // read enabled irq bits + kdebug(("vme_rcc(vme_rcc_Remove): reg =0x%x\n", reg)) + reg |= 0x4000; // SYSFAIL + kdebug(("vme_rcc(vme_rcc_Remove): reg =0x%x\n", reg)) + writel(reg, (char*)uni + LINT_EN); + } + + free_irq(vme_irq_line, universe_irq_handler); + kdebug(("vme_rcc(vme_rcc_Remove): interrupt line %d released\n", vme_irq_line)) + + iounmap((void *)uni); + kerror(("vme_rcc(vme_rcc_Remove): Universe removed\n")); + maxcard = 0; +} + + +/*********************/ +/* Service functions */ +/*********************/ + +// Interrupt timeout function. Called when an interrupt fails to occur following +// a call to VME_InterruptWait() +/************************************/ +static void vme_intTimeout(u_long arg) +/************************************/ +{ + VME_WaitInt_t *iPtr; + int first_vct; + + iPtr = (VME_WaitInt_t *) arg; + kdebug(("vme_rcc(vme_intTimeout): timeout = %d\n",iPtr->timeout)) + iPtr->timeout = 0; // zero to indicate timeout has occurred + first_vct = iPtr->int_handle.vector[0]; + kdebug(("vme_rcc(vme_intTimeout): first vector = 0x%x\n",first_vct)) + up(&link_table.link_dsc[first_vct].sem); // wake up WAIT +} + + +// Interrupt timeout function. Called when an interrupt fails to occur following +// a call to VME_SysfailInterruptWait() +/*******************************************/ +static void vme_intSysfailTimeout(u_long arg) +/*******************************************/ +{ + VME_WaitInt_t *iPtr; + + iPtr = (VME_WaitInt_t *) arg; + kdebug(("vme_rcc(vme_intSysfailTimeout): timeout = %d\n",iPtr->timeout)) + iPtr->timeout = 0; // zero to indicate timeout has occurred + up(&sysfail_link.sem); // wake up WAIT +} + + +/************************************/ +static void vme_dmaTimeout(u_long arg) +/************************************/ +{ + //MJ: It is very unclear if there will ever be a time-out + // This function should be removed unless we find a good reason for keeping it + + current_dmahandle.ctrl = uni->dgcs & 0xf00; + current_dmahandle.counter = uni->dtbc; + current_dmahandle.timeout = 0; + + kdebug(("vme_rcc(vme_dmaTimeout): current_dmahandle.ctrl = 0x%08x\n", current_dmahandle.ctrl)) + kdebug(("vme_rcc(vme_dmaTimeout): current_dmahandle.counter = 0x%08x\n", current_dmahandle.counter)) + + //time-out: reset the DMA controller + uni->dctl = 0; + uni->dtbc = 0; + uni->dla = 0; + uni->dva = 0; + uni->dcpp = 0; + uni->dgcs = 0x6f00; + + if (dma_donelist[current_dmahandle.index].handle != 0xffffffff && dma_donelist[current_dmahandle.index].pid) + kerror(("vme_rcc(vme_dmaTimeout): ERROR: Entry in dma_donelist is not empty\n")) + + kdebug(("vme_rcc(vme_dmaTimeout): entering data into array at index %d\n", current_dmahandle.index)) + dma_donelist[current_dmahandle.index].handle = current_dmahandle.handle; + dma_donelist[current_dmahandle.index].pid = current_dmahandle.pid; + dma_donelist[current_dmahandle.index].ctrl = current_dmahandle.ctrl; + dma_donelist[current_dmahandle.index].counter = current_dmahandle.counter; + dma_donelist[current_dmahandle.index].timeout = 0; //time out + + up(&dma_semaphore); //DMA reset, we can release the lock +} + + +/*******************************/ +static void vme_dma_handler(void) +/*******************************/ +{ + current_dmahandle.ctrl = uni->dgcs & 0xf00; + current_dmahandle.counter = uni->dtbc; + + kdebug(("vme_rcc(vme_dma_handler): current_dmahandle.ctrl = 0x%08x\n", current_dmahandle.ctrl)) + kdebug(("vme_rcc(vme_dma_handler): current_dmahandle.counter = 0x%08x\n", current_dmahandle.counter)) + + if (dma_donelist[current_dmahandle.index].handle != 0xffffffff && dma_donelist[current_dmahandle.index].pid) + kerror(("vme_rcc(vme_dma_handler): ERROR: Entry in dma_donelist is not empty\n")) + + kdebug(("vme_rcc(vme_dma_handler): entering data into array at index %d\n", current_dmahandle.index)) + kdebug(("vme_rcc(vme_dma_handler): current_dmahandle.handle = %d\n", current_dmahandle.handle)) + kdebug(("vme_rcc(vme_dma_handler): current_dmahandle.pid = %d\n", current_dmahandle.pid)) + dma_donelist[current_dmahandle.index].handle = current_dmahandle.handle; + dma_donelist[current_dmahandle.index].pid = current_dmahandle.pid; + dma_donelist[current_dmahandle.index].ctrl = current_dmahandle.ctrl; + dma_donelist[current_dmahandle.index].counter = current_dmahandle.counter; + dma_donelist[current_dmahandle.index].timeout = 1; //normal termination, no time out + + up(&dma_semaphore); //EOT, we can release the lock +} + + +/*****************************/ +static void init_cct_berr(void) +/*****************************/ +{ + u_char reg; + + reg = inb(BERR_INT_PORT); + kdebug(("vme_rcc(init_cct_berr): CCT BERR register = 0x%x\n", reg)) + + reg &= ~BERR_INT_MASK; //Clear the error flag + outb(reg, BERR_INT_PORT); + reg |= BERR_INT_ENABLE; // Enable the BERR interrupt + outb(reg, BERR_INT_PORT); + + berr_interlock = 0; // For cards with shared interrupts such as the VP325 + kdebug(("vme_rcc(init_cct_berr): CCT BERR register = 0x%x\n", reg)) +} + + +/*****************************/ +static void mask_cct_berr(void) +/*****************************/ +{ + u_char reg; + + berr_interlock = 1; // For cards with shared interrupts such as the VP325 + reg = inb(BERR_INT_PORT); + reg &= ~BERR_INT_ENABLE; + outb(reg, BERR_INT_PORT); + + kdebug(("vme_rcc(mask_cct_berr): CCT BERR register = 0x%x\n", reg)) +} + + +/* VME bus error interrupt function for boards which have the hardware support. +* e.g VP PMC/C1x and VP CPI/Pxx boards +* +* Do nothing if the BERR was caused by a DMA operation +* +* If there is a BERR interrupt: +* If the BERR descriptor is empty, fill it and set the count = 1, else just count++ +* re-enable the latching of the BERR registers in the Universe for evry interrupt.*/ +/**************************/ +static int cct_berrInt(void) +/**************************/ +{ + int result = 0; + u_char reg; + u_int data; + + reg = inb(BERR_INT_PORT); + kdebug(("vme_rcc(cct_berrInt): CCT BERR register = 0x%x\n", reg)) + + if ((uni->dgcs & 0x700)) // was there a DMA BERR + { + kerror(("vme_rcc(cct_berrInt): DMA BERR detected\n")) + reg &= ~BERR_INT_MASK; // clear VME bus error + outb(reg, BERR_INT_PORT); + + // fill the dma_done list + current_dmahandle.ctrl = uni->dgcs & 0xf00; + current_dmahandle.counter = uni->dtbc; + current_dmahandle.timeout = 0; + + kdebug(("vme_rcc(cct_berrInt): current_dmahandle.ctrl = 0x%08x\n", current_dmahandle.ctrl)) + kdebug(("vme_rcc(cct_berrInt): current_dmahandle.counter = 0x%08x\n", current_dmahandle.counter)) + + //error: reset the DMA controller + uni->dctl=0; + uni->dtbc=0; + uni->dla =0; + uni->dva =0; + uni->dcpp=0; + uni->dgcs=0x6f00; + + if (berr_dsc.flag == 0) // copy info from Universe into BERR descriptor + { + if (board_type == VP_100 || board_type == VP_110 || board_type == VP_315 || board_type == VP_317 || board_type == VP_325) + read_berr_capture(); + else + { + berr_dsc.vmeadd = 0xffffffff; + berr_dsc.am = 0xffffffff; + berr_dsc.iack = 0xffffffff; + berr_dsc.multiple = 0xffffffff; + berr_dsc.lword = 0xffffffff; + berr_dsc.ds0 = 0xffffffff; + berr_dsc.ds1 = 0xffffffff; + berr_dsc.wr = 0xffffffff; + } + // log the error + kerror(("vme_rcc(cct_berrInt): bus error on VMEbus block transfer, current pid = %d\n", current->pid)) + } + + if (dma_donelist[current_dmahandle.index].handle != 0xffffffff && dma_donelist[current_dmahandle.index].pid) + kerror(("vme_rcc(cct_berrInt): ERROR: Entry in dma_donelist is not empty\n")) + + kdebug(("vme_rcc(cct_berrInt): entering data into array at index %d\n", current_dmahandle.index)) + dma_donelist[current_dmahandle.index].handle = current_dmahandle.handle; + dma_donelist[current_dmahandle.index].pid = current_dmahandle.pid; + dma_donelist[current_dmahandle.index].ctrl = current_dmahandle.ctrl; + dma_donelist[current_dmahandle.index].counter = current_dmahandle.counter; + dma_donelist[current_dmahandle.index].timeout = 1; + + up(&dma_semaphore); //Error handled, DMA reset, we can release the lock + + return(result); + } + + if ((reg & BERR_INT_MASK)) // if VME bus error has occurred + { + data = readl((char*)uni + PCI_CSR); // error with coupled cycle ? + kdebug(("vme_rcc(cct_berrInt): PCI_CSR = 0x%x\n", data)) + if (data & 0x08000000) // check S_TA bit + { + if (berr_dsc.flag == 0) // copy info from Universe into BERR descriptor + { + if (board_type == VP_100 || board_type == VP_110 || board_type == VP_315 || board_type == VP_317 || board_type == VP_325) + read_berr_capture(); + else + { + berr_dsc.vmeadd = 0xffffffff; + berr_dsc.am = 0xffffffff; + berr_dsc.iack = 0xffffffff; + berr_dsc.multiple = 0xffffffff; + berr_dsc.lword = 0xffffffff; + berr_dsc.ds0 = 0xffffffff; + berr_dsc.ds1 = 0xffffffff; + berr_dsc.wr = 0xffffffff; + } + // log the error + kerror(("vme_rcc(cct_berrInt): bus error on coupled VMEbus cycle, current pid = %d\n", current->pid)) + // send signals, if anybody registered + send_berr_signals(); + berr_dsc.flag = 1; // reset by BusErrorInfoGet or RegisterSignal + } + writel(data | 0x08000000, (char*)uni + PCI_CSR); // unlock error in Universe + } + + data = readl((char*)uni + V_AMERR); // error with posted cycle ? + kdebug(("vme_rcc(cct_berrInt): V_AMERR = 0x%x\n", data)) + if (data & 0x00800000) // V_STAT + { + if (berr_dsc.flag == 0) // copy info from Universe into BERR descriptor + { + if (board_type == VP_100 || board_type == VP_110 || board_type == VP_315 || board_type == VP_317 || board_type == VP_325) + read_berr_capture(); + else + { + berr_dsc.am = (data >> 26) & 0x3f; + berr_dsc.multiple = (data >> 24) & 0x1; + berr_dsc.vmeadd = readl((char*)uni + VAERR); + berr_dsc.iack = (data >> 25) & 0x1; + berr_dsc.lword = 0xffffffff; + berr_dsc.ds0 = 0xffffffff; + berr_dsc.ds1 = 0xffffffff; + berr_dsc.wr = 0xffffffff; + } + kdebug(("vme_rcc(cct_berrInt): VAERR = 0x%x\n", berr_dsc.vmeadd)) + + // log the error + kerror(("vme_rcc(cct_berrInt): VMEbus error: address = 0x%8x, AM code = 0x%8x\n", berr_dsc.vmeadd, berr_dsc.am)) + // send signals, if anybody registered + send_berr_signals(); + berr_dsc.flag = 1; // reset by BusErrorInfoGet or RegisterSignal + } + writel(0x00800000, (char*)uni + V_AMERR); // unlock error + } + + reg &= ~BERR_INT_MASK; // clear VME bus error + outb(reg, BERR_INT_PORT); + result = 1; // BERR + berr_int_count++; + } + return(result); +} + + +/***********************************/ +static void sysfail_irq_handler(void) +/***********************************/ +{ + u_int lint_en; + + // The SYSFAIL interupt is active as long as the SYSFAIL signal is asserted on the bus + // In order to prevent a deadlock we disable the interrupt here and provide an ioctl() to re-enable it + lint_en = readl((char*)uni + LINT_EN); // read enabled irq bits + kdebug(("vme_rcc(sysfail_irq_handler): lint_en = 0x%x\n", lint_en)) + lint_en &= 0xffffbfff; + kdebug(("vme_rcc(sysfail_irq_handler): lint_en after masking = 0x%x\n", lint_en)) + writel(lint_en, (char*)uni + LINT_EN); //disable SYSFAIL + + if (sysfail_link.pid == 0) // not linked: "spurious" interrupt + { + kerror(("vme_rcc(sysfail_irq_handler): SYSFAIL interrupt not linked\n")) + kerror(("vme_rcc(sysfail_irq_handler): SYSFAIL interrupt disabled\n")) + } + else + { + if (sysfail_link.sig > 0) + { + kdebug(("vme_rcc(sysfail_irq_handler): pid = %d, signal = %d\n", sysfail_link.pid, sysfail_link.sig)) + kill_proc (sysfail_link.pid, sysfail_link.sig, 1); + } + else + up(&sysfail_link.sem); + } +} + + +/************************************/ +static void vme_irq_handler(int level) +/************************************/ +{ + int statid, vector, first_vector; + u_int lint_en; + VME_IntHandle_t* group_ptr; + + #ifdef VMEDEBUG + *marker=((current->pid) << 8) | 0x40; + #endif + + Interrupt_counters.virq[level-1]++; + statid = readl((char*)uni + STATID + 4 * level); + + if (statid & 0x00000100) // bus error during IACK cycle + kerror(("vme_rcc(vme_irq_handler): bus error during IACK cycle on level %d\n", level)) + else + { + vector = statid & 0x000000FF; + kdebug(("vme_rcc(vme_irq_handler): vector = 0x%x\n",vector)) + + if (link_table.link_dsc[vector].pid == 0) // not linked: "spurious" interrupt + kerror(("vme_rcc(vme_irq_handler): interrupt with vector %d not linked\n", vector)) + else // handle it + { + if (irq_level_table[level-1] == VME_INT_RORA) + { + lint_en = readl((char*)uni + LINT_EN); // read enabled irq bits + kdebug(("vme_rcc(vme_irq_handler): level = %d, lint_en = 0x%x\n", level, lint_en)) + lint_en &= ~(1<<level); // BM_LINT_VIRQxxx + kdebug(("vme_rcc(vme_irq_handler): lint_en after masking = 0x%x\n", lint_en)) + writel(lint_en, (char*)uni + LINT_EN); //disable SHOULD BE MARKED?? + } + + link_table.link_dsc[vector].lvl = level; // store in link descriptor + link_table.link_dsc[vector].pending = 1; + link_table.link_dsc[vector].total_count++; + + // signal FIRST(common) semaphore in a group + // for signals: use signal in the link descriptor for "vector" + group_ptr = link_table.link_dsc[vector].group_ptr; + if (group_ptr == NULL) + first_vector = vector; + else + first_vector = group_ptr->vector[0]; + kdebug(("vme_rcc(vme_irq_handler): first vector = 0x%x\n",first_vector)) + + if (link_table.link_dsc[vector].sig > 0) + { + kdebug(("vme_rcc(vme_irq_handler): pid = %d, signal = %d\n", link_table.link_dsc[vector].pid, link_table.link_dsc[vector].sig)) + kill_proc (link_table.link_dsc[vector].pid, link_table.link_dsc[vector].sig,1); + } + else + { + up(&link_table.link_dsc[first_vector].sem); + #ifdef INTDEBUG + *marker=((current->pid) << 8) | 0x2; + *marker=first_vector; + #endif + } + } + } + #ifdef VMEDEBUG + *marker=((current->pid) << 8) | 0x4f; + #endif +} + + +// This function handles Universe Interrupts & CCT direct BUS error interrupts. +// The latter are handled first and if found, the function returns. +/***********************************************************************************/ +static irqreturn_t universe_irq_handler (int irq, void *dev_id, struct pt_regs *regs) +/***********************************************************************************/ +{ + u_int lint_stat, lint_estat, lint_en; + + #ifdef VMEDEBUG + *marker=0x10000000 | (current->pid); + #endif + + lint_stat = readl((char*)uni + LINT_STAT); // read the interrupt bits + lint_en = readl((char*)uni + LINT_EN); // read enabled irq bits + lint_estat = lint_stat & lint_en; // and look only at enabled irqs + kdebug(("vme_rcc(universe_irq_handler): lint_estat = 0x%x\n", lint_estat)) + + if (!berr_interlock && cct_berrInt()) // first check and handle CCT VMEbus errors + return(IRQ_HANDLED); //MJ: to be checked + + //handle all the interrupts sequentially + + if (lint_estat & BM_LINT_VIRQ7) vme_irq_handler(7); + if (lint_estat & BM_LINT_VIRQ6) vme_irq_handler(6); + if (lint_estat & BM_LINT_VIRQ5) vme_irq_handler(5); + if (lint_estat & BM_LINT_VIRQ4) vme_irq_handler(4); + if (lint_estat & BM_LINT_VIRQ3) vme_irq_handler(3); + if (lint_estat & BM_LINT_VIRQ2) vme_irq_handler(2); + if (lint_estat & BM_LINT_VIRQ1) vme_irq_handler(1); + + if (lint_estat & BM_LINT_SYSFAIL) + { + Interrupt_counters.sysfail++; + sysfail_irq_handler(); + } + + // ERROR CHECKING + if (lint_estat & BM_LINT_VOWN) Interrupt_counters.vown++; + if (lint_estat & BM_LINT_ACFAIL) Interrupt_counters.acfail++; + if (lint_estat & BM_LINT_SW_INT) Interrupt_counters.sw_int++; + if (lint_estat & BM_LINT_SW_IACK) Interrupt_counters.sw_iack++; + if (lint_estat & BM_LINT_VERR) Interrupt_counters.verr++; + if (lint_estat & BM_LINT_LERR) Interrupt_counters.lerr++; + + if (lint_estat & BM_LINT_DMA) + { + #ifdef VMEDEBUG + *marker=0x30000000 | (current->pid); + #endif + Interrupt_counters.dma++; + vme_dma_handler(); + } + + #ifdef VMEDEBUG + *marker=0x40000000 | (current->pid); + #endif + + writel(lint_estat, (char*)uni + LINT_STAT); // clear only the enabled bits + return(IRQ_HANDLED); //MJ: foe 2.6 see p 273 //MJ: we also have to treat IRQ_NONE +} + + +/*************************************************************************************/ +static void fill_mstmap(u_int d1, u_int d2, u_int d3, u_int d4, mstslvmap_t *mstslvmap) +/*************************************************************************************/ +{ + if (d3 == 0) + d3 = 0xffffffff; + d2 = d2 & 0xfffff000; + d4 = d4 & 0xfffff000; + mstslvmap->vbase = d2 + d4; + mstslvmap->vtop = d3 + d4; + mstslvmap->pbase = d2; + mstslvmap->ptop = d3; + mstslvmap->am = (d1 >> 16) & 0x7; //0 = A16, 1 = A24, 2 = A32 + mstslvmap->options = d1 & 0x0000f000; + mstslvmap->enab = (d1 >> 31) & 0x1; + mstslvmap->wp = (d1 >> 30) & 0x1; + mstslvmap->rp = 0; + kdebug(("vme_rcc(fill_mstmap): mstslvmap->vbase = 0x%08x @ 0x%08x\n", mstslvmap->vbase, (u_int) &mstslvmap->vbase)) + kdebug(("vme_rcc(fill_mstmap): mstslvmap->vtop = 0x%08x @ 0x%08x\n", mstslvmap->vtop, (u_int) &mstslvmap->vtop)) + kdebug(("vme_rcc(fill_mstmap): mstslvmap->am = %d @ 0x%08x\n", mstslvmap->am, (u_int) &mstslvmap->am)) + kdebug(("vme_rcc(fill_mstmap): mstslvmap->options = %d @ 0x%08x\n", mstslvmap->options, (u_int) &mstslvmap->options)) + kdebug(("vme_rcc(fill_mstmap): mstslvmap->enab = %d @ 0x%08x\n", mstslvmap->enab, (u_int) &mstslvmap->enab)) +} + + +/*************************************************************************************/ +static void fill_slvmap(u_int d1, u_int d2, u_int d3, u_int d4, mstslvmap_t *mstslvmap) +/*************************************************************************************/ +{ + d2 = d2 & 0xfffff000; + d4 = d4 & 0xfffff000; + mstslvmap->vbase = d2; + mstslvmap->vtop = d3; + mstslvmap->pbase = d2 + d4; + mstslvmap->ptop = d3 + d4; + mstslvmap->space = d1 & 0x3; + mstslvmap->am = (d1 >> 16) & 0x7; //0 = A16, 1 = A24, 2 = A32 + mstslvmap->enab = (d1 >> 31) & 0x1; + mstslvmap->wp = (d1 >> 30) & 0x1; + mstslvmap->rp = (d1 >> 29) & 0x1; +} + + +/*********************************/ +static void send_berr_signals(void) +/*********************************/ +{ + u_int i; + + for (i = 0; i < VME_MAX_BERR_PROCS; i++) + { + if (berr_proc_table.berr_link_dsc[i].pid) + { + kdebug(("vme_rcc(send_berr_signals): index = %d, pid (current)= %d, signal = %d\n", i, berr_proc_table.berr_link_dsc[i].pid, berr_proc_table.berr_link_dsc[i].sig)) + kill_proc(berr_proc_table.berr_link_dsc[i].pid, berr_proc_table.berr_link_dsc[i].sig,1); + } + } +} + + +/*********************************************************/ +static int berr_check(u_int *addr, u_int *multi, u_int *am) +/*********************************************************/ +{ + //This function is to look for single cycle related bus errors + //It does not handle BERRs of DMAs or IACK cycles + u_int data; + + kdebug(("vme_rcc(berr_check): called\n")) + + //lets see if it there was an error with a coupled cycle + data = uni->pci_csr; //read the target abort status bit + if (data & 0x08000000) //check S_TA bit + { + uni->pci_csr = data | 0x08000000; //clear the bit + *addr = 0xffffffff; + *multi = 0xffffffff; + *am = 0xffffffff; + kdebug(("vme_rcc(berr_check): BERR on coupled cycle detected\n")) + return(VME_BUSERROR); //bus error + } + + //lets see if it there was a VMEbus error with a posted cycle + data = uni->v_amerr; + if (data & 0x00800000) + { + *am = (data >> 26) & 0x3f; + *multi = (data >> 24) & 0x1; + *addr = uni->vaerr; + uni->v_amerr = 0x00800000; + kdebug(("vme_rcc(berr_check): BERR on coupled posted detected\n")) + return(VME_BUSERROR); + } + else + { + kdebug(("vme_rcc(berr_check): No BERR detected\n")) + return(0); //no error + } +} + + +/*********************************/ +static void read_berr_capture(void) +/*********************************/ +{ + u_char reg, berr_info[16]; + u_int data, loop; + + reg = inb(BERR_VP100); + if (reg & 0x80) + { + + berr_dsc.vmeadd = 0xffffffff; + berr_dsc.am = 0xffffffff; + berr_dsc.iack = 0xffffffff; + berr_dsc.multiple = 0xffffffff; + berr_dsc.lword = 0xffffffff; + berr_dsc.ds0 = 0xffffffff; + berr_dsc.ds1 = 0xffffffff; + berr_dsc.wr = 0xffffffff; + kerror(("vme_rcc(read_berr_capture): The BERR capture logic was busy. Information could not be read\n")) + return; + } + + outb(0x2, BERR_VP100); // Reset the read sequence + for(loop = 0; loop < 16; loop++) + berr_info[loop] = inb(BERR_VP100) & 0xf; // Read the raw data + + // Assemble the values + berr_dsc.vmeadd = (berr_info[0] << 28) + + (berr_info[1] << 24) + + (berr_info[2] << 20) + + (berr_info[3] << 16) + + (berr_info[4] << 12) + + (berr_info[5] << 8) + + (berr_info[6] << 4) + + (berr_info[7] & 0xe); + berr_dsc.am = ((berr_info[8] & 0x3) << 4) + berr_info[9]; + berr_dsc.multiple = 0xffffffff; + berr_dsc.lword = berr_info[7] & 0x1; + berr_dsc.ds0 = (berr_info[8] & 0x4) >> 2; + berr_dsc.ds1 = (berr_info[8] & 0x8) >> 3; + berr_dsc.wr = (berr_info[10] & 0x8) >> 3; + + data = readl((char*)uni + V_AMERR); // error with IACK cycle ? + kdebug(("vme_rcc(read_berr_capture): V_AMERR = 0x%x\n", data)) + if (data & 0x00800000) // V_STAT + berr_dsc.iack = (data >> 25) & 0x1; + else + berr_dsc.iack = 0xffffffff; + + kdebug(("vme_rcc(read_berr_capture): berr_dsc.vmeadd = 0x%08x\n", berr_dsc.vmeadd)) + kdebug(("vme_rcc(read_berr_capture): berr_dsc.am = 0x%02x\n", berr_dsc.am)) + kdebug(("vme_rcc(read_berr_capture): berr_dsc.iack = 0x%02x\n", berr_dsc.iack)) + kdebug(("vme_rcc(read_berr_capture): berr_dsc.lword = %d\n", berr_dsc.lword)) + kdebug(("vme_rcc(read_berr_capture): berr_dsc.ds0 = %d\n", berr_dsc.ds0)) + kdebug(("vme_rcc(read_berr_capture): berr_dsc.ds1 = %d\n", berr_dsc.ds1)) + kdebug(("vme_rcc(read_berr_capture): berr_dsc.write = %d\n", berr_dsc.wr)) + + // Reset the capture buffer + outb(0x4, BERR_VP100); + + //Reenable the capture + outb(0x1, BERR_VP100); +}