/* * PCI Host Interface Board device driver * for GRAPE-4/5/6TB and PROGRAPE-1 * * DMA strategy: * + init_module() allocate DMA buf in kernel space * + GET_DMA_INFO ioctl returns phys addr of the buf * + specify mmap behavior by SET_MAP_MODE ioctl * + mmap maps PHIB local reg if mapmode==MAP_PHIBREG * DMA buf if mapmode==MAP_DMABUF */ #ifdef __alpha #define USE_48_BIT_KSEG #endif /* __alpha */ #include #include #if defined(CONFIG_SMP) && !defined(__SMP__) #define __SMP__ #endif #include #include #include #include #include #include #include #include #include "pcimem.h" #if 1 #define Cprintk(fmt, args...) printk(fmt, ## args) #else #define Cprintk(fmt, args...) #endif #define PAGE_REQ_ORDER (0) /* request 2^order pages for DMA buffer (max 2^5) */ /* macros to hide kernel version dependency */ #if LINUX_VERSION_CODE < 0x020400 #define pci_resource_start(dev, region) (dev->base_address[region]) #ifndef VMA_OFFSET #define VMA_OFFSET(vma) (vma->vm_offset) #endif #ifndef VM_RESERVED #define VM_RESERVED (0) #endif #define Page unsigned long int pci_enable_device(dev) { return (0); } static Page virt_to_page(unsigned long virtual) { return virtual; } static void get_page(Page pageptr) { atomic_inc(&mem_map[MAP_NR(pageptr)].count); } #else /* kernel 2.4 or higher */ #ifndef VMA_OFFSET #define VMA_OFFSET(vma) ((vma->vm_pgoff)<= 0; ii--) { free_pages((unsigned long)dmabuf[ii], PAGE_REQ_ORDER); } return (-ENOMEM); } } printk(KERN_ALERT "pcimem installed\n"); return (0); } void cleanup_module(void) { int i; for (i = 0; i < npcimem; i++) { free_pages((unsigned long)dmabuf[i], PAGE_REQ_ORDER); } unregister_chrdev(major, DEVNAME); printk(KERN_ALERT "pcimem uninstalled\n"); } static int pcimem_open(struct inode *ip, struct file *fp) { Cprintk(KERN_ALERT "pcimem_open\n"); /* MOD_INC_USE_COUNT; */ return (0); } static int pcimem_close(struct inode *ip, struct file *fp) { Cprintk(KERN_ALERT "pcimem_close\n"); /* MOD_DEC_USE_COUNT; */ return (0); } static int pcimem_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg) { int minor = MINOR(ip->i_rdev); Cprintk(KERN_ALERT "pcimem_ioctl cmd: %x\n", (int) cmd); /* exec cmd with argument arg */ switch (cmd) { case WRITE_CFG: { struct long_access *myarg = (struct long_access *)arg; pci_write_config_dword(pcimem[minor], myarg->addr, myarg->data); break; } case READ_CFG: { struct long_access *myarg = (struct long_access *)arg; pci_read_config_dword(pcimem[minor], myarg->addr, &myarg->data); break; } case MAP_BAR2_DENSE_ADDR: { Cprintk(KERN_ALERT "MAP_BAR2_DENSE_ADDR: do nothing. exist only for compatibility with driver on Tru64\n"); break; } case DMA_MAP_LOAD: { Cprintk(KERN_ALERT "DMA_MAP_LOAD: do nothing. exist only for compatibility with driver on Tru64\n"); Cprintk(KERN_ALERT "dmabuf[0][0]: 0x%08x\n", dmabuf[0][0]); break; } case GET_DMA_INFO: { unsigned long *ba = (unsigned long *)arg; *ba = virt_to_bus(dmabuf[minor]); Cprintk(KERN_ALERT "GET_DMA_INFO: DMA buf bus addr: %016lx\n", *ba); break; } case SET_MAP_MODE: { mapmode[minor] = (unsigned int)arg; Cprintk(KERN_ALERT "SET_MAP_MODE: mapmode: %d\n", mapmode[minor]); break; } default: return (-EINVAL); } return (0); } static Page pcimem_vma_nopage(struct vm_area_struct *vp, unsigned long address, int write_access) { int minor = MINOR(vp->vm_file->f_dentry->d_inode->i_rdev); Page pageptr; unsigned long offset = address - vp->vm_start + VMA_OFFSET(vp); Cprintk(KERN_ALERT "pcimem_vma_nopage minor: %d\n", minor); pageptr = virt_to_page(dmabuf[minor] + offset); get_page(pageptr); return pageptr; } static void pcimem_vma_open(struct vm_area_struct *vp) { Cprintk(KERN_ALERT "pcimem_vma_open\n"); MOD_INC_USE_COUNT; } static void pcimem_vma_close(struct vm_area_struct *vp) { Cprintk(KERN_ALERT "pcimem_vma_close\n"); MOD_DEC_USE_COUNT; } static int pcimem_mmap(struct file *fp, struct vm_area_struct *vp) { unsigned long off = VMA_OFFSET(vp); unsigned long virtual = vp->vm_start; unsigned long physical; unsigned long vsize = vp->vm_end - vp->vm_start; int ret = 0; int minor = MINOR(fp->f_dentry->d_inode->i_rdev); switch (mapmode[minor]) { case MAP_PHIBREG: /* map PHIB local reg */ physical = pci_resource_start(pcimem[minor], 2) & PCI_BASE_ADDRESS_MEM_MASK; physical += off; #ifdef __alpha #define BASE2PHYS(a) __pa(dense_mem(a)+((a)&0xffffffffUL)) physical = BASE2PHYS(physical); #endif /* __alpha */ vp->vm_flags |= VM_RESERVED; ret = remap_page_range(vp,virtual, physical, vsize, vp->vm_page_prot); if (ret) return (-EAGAIN); Cprintk(KERN_ALERT "MAP_PHIBREG done\n"); break; case MAP_DMABUF: /* map DMA buf */ vp->vm_ops = &vmops; vp->vm_flags |= VM_RESERVED; pcimem_vma_open(vp); Cprintk(KERN_ALERT "MAP_DMABUF done\n"); break; default: printk(KERN_ALERT "unknown map mode %d\n", mapmode[minor]); ret = 1; break; } if (ret) { return (-EAGAIN); } return (ret); } static int n_pcimem_exist(int vendorid, int deviceid) { int i; struct pci_dev *pcimem0 = NULL; if (!pci_present()) { printk(KERN_ALERT "pci is not supported on this machine\n"); return (-ENODEV); } for (i = 0; ; i++) { pcimem[i] = pci_find_device(vendorid, deviceid, pcimem0); pcimem0 = pcimem[i]; if (!pcimem[i]) { break; } else if (pci_enable_device(pcimem[i])) { break; } } if (i == 0) { printk(KERN_ALERT "no pcimem found\n"); return (-ENODEV); } else { printk(KERN_ALERT "%d pcimem(s) found\n", i); return (i); } }