diff --git a/pcie-wb/Makefile b/pcie-wb/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a8273b1d47bf8ddac18efe95e1aaa7aba36216bb --- /dev/null +++ b/pcie-wb/Makefile @@ -0,0 +1,32 @@ +#export PATH=$PATH:/share/eldk/bin:/share/eldk/usr/bin +#export CROSS_COMPILE=ppc_4xxFP- +#export ARCH=powerpc + +# This is useful if cross-compiling. Taken from kernel Makefile (CC changed) +#AS =$(CROSS_COMPILE)as +#LD =$(CROSS_COMPILE)ld +#CC =$(CROSS_COMPILE)gcc +#CPP =$(CC) -E +#AR =$(CROSS_COMPILE)ar +#NM =$(CROSS_COMPILE)nm +#STRIP =$(CROSS_COMPILE)strip +#OBJCOPY =$(CROSS_COMPILE)objcopy +#OBJDUMP =$(CROSS_COMPILE)objdump + + + +KERNELDIR ?= /common/usr/embedded/kernel/scu/linux-2.6.33.6/ +ifneq ($(KERNELRELEASE),) + obj-m := pci_char.o sleepy.o +else + KERNELDIR ?= /lib/modules/$(shell uname -r)/build + PWD := $(shell pwd) + +all: + $(MAKE) -C $(KERNELDIR) M=$(PWD) + +endif + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + diff --git a/pcie-wb/irq_response.c b/pcie-wb/irq_response.c new file mode 100644 index 0000000000000000000000000000000000000000..44eecbf5bd1e484dddaf3cc878a6440e0bc9bd2e --- /dev/null +++ b/pcie-wb/irq_response.c @@ -0,0 +1,114 @@ +/* + * syscalls.c + * Program to illustrate common system calls. Doesn't actually + * perform any useful function, but will later be expanded into + * a program which does. + */ + +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +#include "pci_char.h" + +#define FALSE 0 +#define TRUE 1 +#define BUFCOUNT 100 +#define TICK 8 /* 8 ns */ + + +int main() +{ + int fd_irq; /* device file descriptor for blocking read */ + int fd_rd; /* device file descriptor for non-blocking read */ + unsigned int cnt_value_irq; /* buffer to hold data */ + unsigned int cnt_value_rd; /* buffer to hold data */ + unsigned int diff_buffer[BUFCOUNT]; + int status; /* return status of system calls */ + int i; + int arg; + + /* open irq device */ + status = fd_irq = open("/dev/pci_char0", O_RDWR); + if (status == -1) { + perror("error opening /dev/pci_char0"); + return(1); + } + + /* open rd device after irq device */ + status = fd_rd = open("/dev/pci_char1", O_RDWR); + if (status == -1) { + perror("error opening /dev/pci_char1"); + return(1); + } + + /* one tick is 8ns, so 1250000 ticks give 10µs */ + arg = 1250000; // 0.01s / 8*E-9s + //status = ioctl(fd_irq, SET_PERIOD, arg); + // if (status == -1) { + // perror("error from SET_PERIOD ioctl"); + // return(1); + // } + + //status = ioctl(fd_irq, GET_PERIOD, arg); + // if (status == -1) { + // perror("error from GET_PERIOD ioctl"); + // return(1); + //} else + // printf("Period value is %d\n", status); + + + for (i = 0; i < BUFCOUNT; i++) { + // blocking read + status = read(fd_irq, &cnt_value_irq, sizeof(int)); + if (status == -1) { + perror("error reading from fd_irq"); + return(1); + } + + // non-blocking read + status = read(fd_rd, &cnt_value_rd, sizeof(int)); + if (status == -1) { + perror("error reading from fd_rd"); + return(1); + } + + diff_buffer[i] = abs(cnt_value_irq-cnt_value_rd); + + } + + for (i = 0; i < BUFCOUNT; i++) { + printf("%d\n", diff_buffer[i]); + } + + close(fd_irq); + close(fd_rd); + + return(0); + + /* write some data */ +// *buf = 1; +// while(1) { +// status = write(fd, buf, 1); +// if (status == -1) { +// perror("error writing to /dev/pci_char0"); +// exit(1); +// } + + + + //usleep(125000); /* wait for 250ms */ + //} + + /* close the device */ +// status = close(fd_irq); +// if (status == -1) { +// perror("error closing /dev/pci_char0"); +// return(1); +// } + + /* and exit */ + +} diff --git a/pcie-wb/ledloop.c b/pcie-wb/ledloop.c new file mode 100644 index 0000000000000000000000000000000000000000..0eb8c6f0884ed8822f065d25f89b0d3ad8c93a1d --- /dev/null +++ b/pcie-wb/ledloop.c @@ -0,0 +1,80 @@ +/* + * syscalls.c + * Program to illustrate common system calls. Doesn't actually + * perform any useful function, but will later be expanded into + * a program which does. + */ + +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +#define FALSE 0 +#define TRUE 1 + + +int main() +{ + int fd; /* device file descriptor */ + //int arg; /* argument for ioctl call */ + unsigned char buf[1]; /* buffer to hold data */ + int status; /* return status of system calls */ + char up = TRUE; + + /* open device */ + status = fd = open("/dev/pci_char0", O_RDWR); + if (status == -1) { + perror("error opening /dev/pci_char0"); + exit(1); + } + + /* set a parameter using ioctl call */ + + //arg = 8000; /* sampling rate */ + /*status = ioctl(fd, SOUND_PCM_WRITE_RATE, &arg); + if (status == -1) { + perror("error from SOUND_PCM_WRITE_RATE ioctl"); + exit(1); + }*/ + + /* read some data */ + //status = read(fd, buf, sizeof(buf)); + //if (status == -1) { + // perror("error reading from /dev/dsp"); + // exit(1); + //} + + /* write some data */ + *buf = 1; + while(1) { + status = write(fd, buf, 1); + if (status == -1) { + perror("error writing to /dev/pci_char0"); + exit(1); + } + + if (buf[0] == 0x80) /* turn if highest bit is set */ + up = FALSE; + else if (buf[0] == 0x01) + up = TRUE; + + if (up == TRUE) + buf[0] <<= 1; + else + buf[0] >>= 1; + + usleep(125000); /* wait for 250ms */ + } + + /* close the device */ + status = close(fd); + if (status == -1) { + perror("error closing /dev/pci_char0"); + exit(1); + } + + /* and exit */ + return(0); +} diff --git a/pcie-wb/mmap.c b/pcie-wb/mmap.c new file mode 100644 index 0000000000000000000000000000000000000000..6bcb27c0cda4197f63f2e79958c08e6f75585741 --- /dev/null +++ b/pcie-wb/mmap.c @@ -0,0 +1,80 @@ +/* + * Accessing a PCI memory region using mmap() on /dev/mem + * + * Compile with 'gcc -Wall -O file.c -lpci -o peeker' (-O *is* mandatory) + * + * Run with './peeker x y z' arguments can be derived from 'lspci -tvv' + * + * Make sure (with lspci -vvs x:y.z) that "Region 0" is memory and that + * "Region 1" is I/O. + */ + +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <pci/pci.h> +#include <sys/io.h> +#include <sys/mman.h> +#include <string.h> + +#include "pci_char.h" + +#define PCI_MEM_LEN (1<<18) + +#define COUNT 100000 + +int main( int argc, char *argv[]){ + + + unsigned char* ptr_to_pci_mem; + unsigned int pci_mem_addr; + int buffer[COUNT]; + int i; + FILE* fd; + + + /* Check IO permissions to be able to open /dev/mem + */ + if(iopl(3)){ + printf("Cannot get I/O permissions (being root helps)\n"); + return -1; + } + + pci_mem_addr = 0xfeac0000; + //pci_mem_addr += SCU_MASTER; + + fd = fopen ( "/dev/mem", "r+w"); + + /* ...and map the PCI memory area (man 2 mmap for more info) + */ + ptr_to_pci_mem = mmap( NULL, + PCI_MEM_LEN, + PROT_READ|PROT_WRITE, + MAP_SHARED, + fileno(fd), pci_mem_addr); + + + + /* Voila! Two bytes of PCI memory out from the board + * You can also use the memcpy() family of functions + */ + printf("Memory pointer: %p\n", ptr_to_pci_mem); + printf("PCI memory @%#x\n", pci_mem_addr); + for (i = 0; i< 30; i++) { + printf("addr %p : %#x\n",ptr_to_pci_mem + PCIE_BASE + 0x800+ i*4, *(ptr_to_pci_mem + PCIE_BASE + 0x800 + i*4) ); + } + //printf("The 2nd byte on PCI memory is : %#x\n", *(ptr_to_pci_mem+2) ); + + //memcpy(buffer, ptr_to_pci_mem, COUNT); + +//for (i=0; i < COUNT; i+=1) { + // buffer[i] = *(ptr_to_pci_mem++); + // //*(ptr_to_pci_mem+1) = i; + //} + + + munmap(ptr_to_pci_mem, PCI_MEM_LEN); + fclose(fd); + return 0; +} diff --git a/pcie-wb/pci_char.c b/pcie-wb/pci_char.c new file mode 100644 index 0000000000000000000000000000000000000000..cf5f728b1db80d4e526bccffcc4587952fb43f79 --- /dev/null +++ b/pcie-wb/pci_char.c @@ -0,0 +1,507 @@ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/kdev_t.h> +#include <linux/poll.h> +#include <linux/interrupt.h> +#include <linux/cdev.h> +#include <linux/aer.h> + +#include <asm/io.h> /*outb iowrite8 */ +#include <asm/spinlock.h> + +#include "pci_char.h" + +#define FALSE 0 +#define TRUE 1 + +int use_mem = FALSE; /* mem or io port */ +int use_msi = FALSE; /* Message Signaled Interrupts enabled */ + +static int major_num = 0; /* major number for char device */ +static int minor_num = 0; /* minor number for char device */ +static struct cdev *cdev_irq; +static struct cdev *cdev_rd; + +struct pci_resource *pci_res0 = NULL; /* ptrs to resource objects */ +struct pci_resource *pci_res1 = NULL; + +char name_str[] = "pci_char"; +char myirq = -1; /* IRQ from PCI config reg */ +int instance_count = 0; /* counting connected processes for housekeeping */ + +static DECLARE_WAIT_QUEUE_HEAD(event_wq); +static DEFINE_SPINLOCK(snapshot_lock); +static DEFINE_SPINLOCK(period_lock); + +unsigned long short_buffer = 0; +unsigned long volatile short_head; +volatile unsigned long short_tail; + + +static inline void short_incr_bp(volatile unsigned long *index, int delta) +{ + unsigned long new = *index + delta; + barrier(); /* Don't optimize these two together */ + *index = (new >= (short_buffer + PAGE_SIZE)) ? short_buffer : new; +} + +struct pci_resource { + int bar; /* BAR number from 0 to 5 */ + unsigned long start; /* start addr of BAR */ + unsigned long end; /* end addr of BAR */ + unsigned int size; + void *m_addr; /* remapped addr */ + int is_mem; +}; + +irq_handler_t my_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + + int ret; + int snapl_value; + int snaph_value; + int snap_value; + unsigned char *bptr = pci_res0->m_addr; + //int written; + + mb(); + /* check if our device has triggered */ + ret = ioread32(bptr + TMR_0_BASE);mb(); + if (!(ret & 0x1)) + return IRQ_NONE; + + + /* copy snapshot value from timer */ + /* should be atomic */ + spin_lock(&snapshot_lock); + /* trigger the latch of the counter value */ + iowrite16(0x0, bptr + TMR_0_SNAPL); + rmb(); // has to be executed before reading + snapl_value = ioread16(bptr + TMR_0_SNAPL);rmb(); + snaph_value = ioread16(bptr + TMR_0_SNAPH);rmb(); + spin_unlock(&snapshot_lock); + snap_value = (int)(snaph_value << 16) | snapl_value; + //printk(KERN_ALERT "%s: snap_value = 0x%x\n", name_str, snap_value); + //snap_value = snap_value | (snaph_value << 32); + //written = sprintf((char *)short_head,"%08x\n", snap_value); + *(u32 *)short_head = snap_value; + + short_incr_bp(&short_head, sizeof(u32)); + + /* wake up the reading process */ + wake_up_interruptible(&event_wq); + + /* clear the interrupting bit in the pio */ + //iowrite32(0x0, bptr + PIO_BASE + EDGE_CAPTURE); + /* clear pending bit in edge capture register */ + mb(); + //iowrite32(0x0, bptr + TMR_EDGE_CAPTURE); + iowrite16(0x0, bptr + TMR_0_BASE);mb(); + + + + //printk(KERN_ALERT "%s: irq handler called\n", name_str); + return (irq_handler_t)IRQ_HANDLED; +} + + +int pci_skel_open (struct inode *inode, struct file *filp) +{ + + int result; + unsigned char *bptr = pci_res0->m_addr; + int arg = IRQ_RATE; + + instance_count++; + + iowrite16(arg, bptr + TMR_0_PERIODL); + wmb(); + iowrite16(arg >> 16, bptr + TMR_0_PERIODH); + + /* we have a irq number and the first process is connecting */ + if (myirq >= 0 && instance_count == 1) { + result = request_irq( myirq, + (irq_handler_t)my_irq_handler, + IRQF_SHARED, + name_str, + pci_res0 /* used as dev_id */ + ); + if (result) { + printk(KERN_ALERT "%s: can't get IRQ %d\n", name_str, myirq); + myirq = -1; + } else { + /* enable irq */ + /* enable MSI in MSI Control Register of PCI Endpoint */ + result = ioread32(bptr + PCIE_BASE + MSI); + iowrite32(result |= (1UL << 16), bptr + PCIE_BASE + MSI); + + /* enable AVL_IRQ in MSI Control Register of PCI Endpoint */ + result = ioread32(bptr + PCIE_BASE + MSI); + iowrite32(result |= (1UL << 7), bptr + PCIE_BASE + MSI); + + mb(); + + iowrite16(0x7, bptr + TMR_0_CTRL); + + ioread32(bptr + PCIE_BASE); /* dummy read */ + mb(); + + } + } + + return 0; +} + +int pci_rd_open (struct inode *inode, struct file *filp) +{ + return 0; +} + +int pci_irq_release (struct inode *inode, struct file *filp) +{ + unsigned char *bptr = pci_res0->m_addr; + int result; + + instance_count--; + + /* we have a irq number and the last connected process is to be released*/ + if (myirq && instance_count == 0) + { + free_irq(myirq, pci_res0 /* dev_id */); + + mb(); + /* disable timer irq */ + iowrite16(0x0, bptr + TMR_0_CTRL); + mb(); + + /* disable AVL_IRQ in MSI Control Register of PCI Endpoint */ + result = ioread32(bptr + PCIE_BASE + MSI); + iowrite32(result |= (0UL << 7), bptr + PCIE_BASE + MSI); + /* disable MSI in MSI Control Register of PCI Endpoint */ + result = ioread32(bptr + PCIE_BASE + MSI); + iowrite32(result |= (0UL << 16), bptr + PCIE_BASE + MSI); + + ioread32(bptr + PCIE_BASE); /* dummy read */ + mb(); + + } + return 0; +} + +int pci_rd_release (struct inode *inode, struct file *filp) +{ + + return 0; +} + +ssize_t pci_blocking_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + int count0; + + + /* there is no data so we are going to wait. */ + /* but only if there is no wakeup call pending */ + wait_event_interruptible(event_wq, short_head != short_tail); + + /* now we should have at least one event in the buffer */ + + /* count0 is the number of readable data bytes */ + count0 = short_head - short_tail; + if (count0 < 0) /* wrapped */ + count0 = short_buffer + PAGE_SIZE - short_tail; + //printk(KERN_ALERT "%s: read count0 = %d\n", name_str, count0); + if (count0 < count) count = count0; + if (copy_to_user(buf, (char *)short_tail, count)) + return -EFAULT; + short_incr_bp (&short_tail, count); + return count; +} + +ssize_t pci_nonblocking_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + int snapl_value; + int snaph_value; + int snap_value; + unsigned char *bptr = pci_res0->m_addr; + + /* copy snapshot value from timer */ + /* should be atomic */ + spin_lock(&snapshot_lock); + /* trigger the latch of the counter value */ + iowrite16(0x0, bptr + TMR_0_SNAPL); + rmb(); // has to be executed before reading + snapl_value = ioread16(bptr + TMR_0_SNAPL);rmb(); + snaph_value = ioread16(bptr + TMR_0_SNAPH);rmb(); + spin_unlock(&snapshot_lock); + snap_value = (int)(snaph_value << 16) | snapl_value; + + if (copy_to_user(buf, &snap_value, 4)) + return -EFAULT; + + return 4; +} + +ssize_t pci_skel_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + return 0; +} + +int pci_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg) { + + int retval = 0; + unsigned char *bptr = pci_res0->m_addr; + + if(_IOC_TYPE(cmd) != PCI_CHAR_MAGIC) return -ENOTTY; + if(_IOC_NR(cmd) > PCI_CHAR_MAXNR) return -ENOTTY; + + switch(cmd) { + /*case SET_PERIOD: + //printk(KERN_ALERT "%s: periodl 0x%x\n", name_str, arg); + should be atomic + spin_lock(&period_lock); + iowrite16(arg, bptr + TMR_0_PERIODL); + wmb(); + iowrite16(arg >> 16, bptr + TMR_0_PERIODH); + spin_unlock(&period_lock); + break; + case GET_PERIOD: + spin_lock(&period_lock); + retval = ioread16(bptr + TMR_0_PERIODL); + rmb(); + retval = ioread16(bptr + TMR_0_PERIODH) << 16 | retval; + spin_unlock(&period_lock); + printk(KERN_ALERT "%s: period value is 0x%x\n", name_str, retval); + return retval; + break;*/ + + default: + return -ENOTTY; + } + return retval; +} + + +unsigned int pci_skel_poll(struct file *filp, poll_table *wait) +{ + return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; +} + +struct file_operations pci_irq_fops = { + .owner = THIS_MODULE, + .read = pci_blocking_read, + .write = pci_skel_write, + .ioctl = pci_ioctl, + .poll = pci_skel_poll, + .open = pci_skel_open, + .release = pci_irq_release, +}; + +struct file_operations pci_rd_fops = { + .owner = THIS_MODULE, + .read = pci_nonblocking_read, + .poll = pci_skel_poll, + .open = pci_rd_open, + .release = pci_rd_release, +}; + +static struct pci_device_id ids[] = { + { PCI_DEVICE(ALTERA_ID, DEVICE_ID), }, + { PCI_DEVICE(0x1002, 0x4750), }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ids); + +static unsigned char skel_get_revision(struct pci_dev *dev) +{ + u8 revision; + + pci_read_config_byte(dev, PCI_REVISION_ID, &revision); + return revision; +} + +static int probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + /* Do probing type stuff here. + * Like calling request_region(); + * reading BARs + * reading IRQ + * register char dev + */ + int result; + int is_mem; + dev_t dev_irq = MKDEV(major_num, minor_num); + dev_t dev_rd = MKDEV(major_num, minor_num); + + if (skel_get_revision(pdev) != 0x01) { + printk(KERN_ALERT "%s: revision ID wrong!\n", name_str); + return -ENODEV; + } + + /* enable pcie error reporting */ + result = pci_enable_pcie_error_reporting(pdev); + if (result) { + printk(KERN_ALERT "Could not enable pcie error reporting.\n"); + } + + /* enable message signaled interrupts */ + result = pci_enable_msi(pdev); + /* could not use MSI? */ + if (result) { + /* resort to legacy interrupts */ + printk(KERN_ALERT "Could not enable MSI interrupting, staying with legacy.\n"); + /* MSI enabled, remember for cleanup */ + } else { + use_msi = TRUE; + printk(KERN_ALERT "Enabled MSI interrupting.\n"); + } + + pci_res0 = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); /* mem for object */ + if (!pci_res0) { + printk(KERN_ALERT "%s: could not alloc mem for pci_res0!\n", name_str); + goto err_pci_res0; + } + + /*init of pci_res0 */ + pci_res0->bar = 0; + pci_res0->start = pci_resource_start(pdev, 0); + pci_res0->end = pci_resource_end(pdev, 0); + pci_res0->size = pci_res0->end - pci_res0->start + 1; + + is_mem = pci_resource_flags(pdev, 0); + is_mem = is_mem & IORESOURCE_MEM; + pci_res0->is_mem = is_mem; + + printk(KERN_ALERT "%s: BAR0 0x%lx - 0x%lx\n", name_str, pci_res0->start, pci_res0->end); + + if (!request_mem_region(pci_res0->start, pci_res0->size, name_str)) { + printk(KERN_ALERT "%s: request_mem_region failed\n", name_str); + goto err_mem_region; + } + + pci_res0->m_addr = ioremap_nocache(pci_res0->start, pci_res0->size); + printk(KERN_ALERT "%s: ioremap to %lx\n", name_str, (unsigned long)pci_res0->m_addr); + + /* get irq number from pci */ + myirq = pdev->irq; + printk(KERN_ALERT "%s: using IRQ number %d\n", name_str, myirq); + + /* alloc DEV_COUNT device number */ + result = alloc_chrdev_region (&dev_irq, FIRSTMINOR, DEV_COUNT, name_str); + if(result < 0) { + printk(KERN_ALERT "%s: ERROR unable to get major number\n", name_str); + goto err_alloc_chrdev; + } else { + major_num = MAJOR(dev_irq); + printk(KERN_ALERT "%s: My major nummber is: %d\n", name_str, major_num); + }; + + // assign dev_rd the next minor number + dev_rd = MKDEV(major_num, MINOR(dev_irq) + 1); + + // allocate cdev structures + cdev_irq = cdev_alloc(); + cdev_rd = cdev_alloc(); + + if((cdev_irq == NULL) || (cdev_rd == NULL)){ + printk("%s: ERROR allocate cdev structur failure\n", name_str); + goto err_cdev_alloc; + } + + cdev_irq->owner = THIS_MODULE; + cdev_irq->ops = &pci_irq_fops; + + cdev_rd->owner = THIS_MODULE; + cdev_rd->ops = &pci_rd_fops; + + result = cdev_add(cdev_irq, dev_irq, 1); + if(result < 0){ + printk("%s: cdev_add dev_irq failed\n", name_str); + goto err_alloc_chrdev; + } else + printk("%s: added cdev_irq\n", name_str); + + result = cdev_add(cdev_rd, dev_rd, 1); + if(result < 0){ + printk("%s: cdev_add dev_rd failed\\n", name_str); + goto err_cdev_add; + } else + printk("%s: added cdev_rd\n", name_str); + + + short_buffer = __get_free_pages(GFP_KERNEL,0); //never fails + short_head = short_tail = short_buffer; + + return pci_enable_device(pdev); + + /* cleaning up */ + err_cdev_add: + cdev_del(cdev_irq); + err_cdev_alloc: + unregister_chrdev_region (MKDEV(major_num, minor_num), DEV_COUNT); + err_alloc_chrdev: + iounmap(pci_res0->m_addr); + release_mem_region(pci_res0->start, pci_res0->size); + err_mem_region: + kfree(pci_res0); + err_pci_res0: + return -EIO; +} + +static void remove(struct pci_dev *pdev) +{ + + /* clean up any allocated resources and stuff here. + * like call release_mem_region(); + */ + + printk(KERN_ALERT "%s: releasing resources\n", name_str); + if (cdev_rd) + cdev_del(cdev_rd); + if (cdev_irq) + cdev_del(cdev_irq); + if (major_num) { + unregister_chrdev_region (MKDEV(major_num, minor_num), DEV_COUNT); + printk(KERN_ALERT "%s: released major number %i\n", name_str, major_num); + } + if (use_msi) + pci_disable_msi(pdev); + + iounmap(pci_res0->m_addr); + release_mem_region(pci_res0->start, pci_res0->size); + printk(KERN_ALERT "%s: released io 0x%lx\n", name_str, pci_res0->start); + if (pci_res0 != NULL) + kfree(pci_res0); +} + +static int err_handler(struct pci_dev *pdev) { + printk(KERN_ALERT "%s: err handler called!\n", name_str); + return 0; +} + +static struct pci_driver pci_driver = { + .name = name_str, + .id_table = ids, + .probe = probe, + .remove = remove, + .err_handler = err_handler, +}; + +static int __init pci_skel_init(void) +{ + return pci_register_driver(&pci_driver); +} + +static void __exit pci_skel_exit(void) +{ + pci_unregister_driver(&pci_driver); +} + +MODULE_LICENSE("GPL"); + +module_init(pci_skel_init); +module_exit(pci_skel_exit); diff --git a/pcie-wb/pci_char.h b/pcie-wb/pci_char.h new file mode 100644 index 0000000000000000000000000000000000000000..375f95e293db85906b7b73007dcf97c82f276ec9 --- /dev/null +++ b/pcie-wb/pci_char.h @@ -0,0 +1,58 @@ +#ifndef PCI_CHAR_DRIVER_H_ +#define PCI_CHAR_DRIVER_H_ + +#endif /*PCI_CHAR_DRIVER_H_*/ + +#include <linux/ioctl.h> + +#define PCI_CHAR_MAGIC 'S' + +#define SET_PERIOD _IO(PCI_CHAR_MAGIC, 0) +#define GET_PERIOD _IO(PCI_CHAR_MAGIC, 1) + +#define PCI_CHAR_MAXNR 3 + +#define ALTERA_ID 0x1172 +#define DEVICE_ID 0x0004 +#define FIRSTMINOR 0 +#define DEV_COUNT 2 + +#define ONCHIP_RAM_OFFSET 0x4000 +#define SCU_ADDR 0x4030 +#define SCU_TIMING 0x4040 +#define SCU_WR_DATA 0x4020 +#define SCU_CTRL 0x4050 +#define SCU_RD_DATA 0x4060 +#define SCU_STATUS 0x4070 +#define SCU_MASTER 0x1000000 + +#define SCU_RD 'R' +#define SCU_WR 'W' +#define SCU_TM 'T' + + +#define PCIE_BASE 0x20000 +#define MSI 0x050 +#define TMR_BASE 0x24000 +#define TMR_DIR (TMR_BASE + 0x8) +#define TMR_EDGE_CAPTURE (TMR_BASE + 0x18) +#define TMR_IRQ_MASK (TMR_BASE + 0x10) +#define TMR_SNAP 0x24020 +#define TMR_0_BASE 0x24080 +#define TMR_0_CTRL (TMR_0_BASE + 0x8) +#define TMR_0_PERIODL (TMR_0_BASE + 0x10) +#define TMR_0_PERIODH (TMR_0_BASE + 0x18) +#define TMR_0_SNAPL (TMR_0_BASE + 0x20) +#define TMR_0_SNAPH (TMR_0_BASE + 0x28) + + +#define EVENT_BUFFER_SIZE 100 +#define IRQ_RATE 1250000 + + + +struct scu_cycle_data { + unsigned int data; /* 16 bit data */ + unsigned int addr; /* 20 bit addr */ + char type; /* SCU_RD, SCU_WR, SCU_TM */ +}; diff --git a/pcie-wb/pci_char.mod.c b/pcie-wb/pci_char.mod.c new file mode 100644 index 0000000000000000000000000000000000000000..6e658253eb98b50e1502675e89be6df49cb6815b --- /dev/null +++ b/pcie-wb/pci_char.mod.c @@ -0,0 +1,22 @@ +#include <linux/module.h> +#include <linux/vermagic.h> +#include <linux/compiler.h> + +MODULE_INFO(vermagic, VERMAGIC_STRING); + +struct module __this_module +__attribute__((section(".gnu.linkonce.this_module"))) = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +static const char __module_depends[] +__used +__attribute__((section(".modinfo"))) = +"depends="; + +MODULE_ALIAS("pci:v00001172d00000004sv*sd*bc*sc*i*"); diff --git a/pcie-wb/pci_char_load b/pcie-wb/pci_char_load new file mode 100755 index 0000000000000000000000000000000000000000..75167ab11e88f741953595ab32756ae90d621f46 --- /dev/null +++ b/pcie-wb/pci_char_load @@ -0,0 +1,16 @@ +#!/bin/sh +module="pci_char" +device="pci_char" +mode="664" + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod ./$module.ko $* || exit 1 + +#remove stale nodes +rm -f /dev/${device}[0-3] + +major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices) + +mknod /dev/${device}0 c $major 0 +mknod /dev/${device}1 c $major 1 diff --git a/pcie-wb/scu_test.c b/pcie-wb/scu_test.c new file mode 100644 index 0000000000000000000000000000000000000000..f7b7a1f81afec6c8652ad8bcb31ce30fb32d864e --- /dev/null +++ b/pcie-wb/scu_test.c @@ -0,0 +1,81 @@ +/* + * syscalls.c + * Program to illustrate common system calls. Doesn't actually + * perform any useful function, but will later be expanded into + * a program which does. + */ + +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <stdlib.h> + +#include "pci_char_driver.h" + +#define FALSE 0 +#define TRUE 1 + + +#define BUFSIZE 1 + + +int main() +{ + int fd; /* device file descriptor */ + int i = 0, j = 0; + unsigned int wbuf[BUFSIZE]; /* buffer to hold data */ + unsigned int rbuf[BUFSIZE]; + int status; /* return status of system calls */ + char up = TRUE; + struct scu_cycle_data *scu_buf = malloc(sizeof(struct scu_cycle_data)); + + /* open device */ + status = fd = open("/dev/pci", O_RDWR); + if (status == -1) { + perror("error opening /dev/pci"); + exit(1); + } + + + + + + /* reset of the SCU Macro */ + status = ioctl(fd, PCI_CHAR_RESET); + if (status == -1) { + perror("error from PCI_CHAR_RESET"); + exit(1); + } + + scu_buf->addr = 0x00002; + scu_buf->addr <<= 2; + scu_buf->data = 0xAFFE; + scu_buf->type = SCU_WR; + status = write(fd, scu_buf, sizeof(struct scu_cycle_data)); + if (status == -1) { + perror("error writing to SCU bus!"); + exit(1); + } + + //for (j=0; j<1000; j++) { + status = read(fd, scu_buf, sizeof(struct scu_cycle_data)); + if (status == -1) { + perror("error reading from SCU bus!"); + exit(1); + } + + //} + + + /* close the device */ + status = close(fd); + if (status == -1) { + perror("error closing /dev/pci"); + exit(1); + } + + /* and exit */ + return(0); +} diff --git a/pcie-wb/sleepy.c b/pcie-wb/sleepy.c new file mode 100644 index 0000000000000000000000000000000000000000..7a96e2fd433dbb766c38cfbd94f2cebb05404c4e --- /dev/null +++ b/pcie-wb/sleepy.c @@ -0,0 +1,85 @@ +/* + * sleepy.c -- the writers awake the readers + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: sleepy.c,v 1.7 2004/09/26 07:02:43 gregkh Exp $ + */ + +#include <linux/module.h> +#include <linux/init.h> + +#include <linux/sched.h> /* current and everything */ +#include <linux/kernel.h> /* printk() */ +#include <linux/fs.h> /* everything... */ +#include <linux/types.h> /* size_t */ +#include <linux/wait.h> + +MODULE_LICENSE("GPL"); + +static int sleepy_major = 0; + +static DECLARE_WAIT_QUEUE_HEAD(wq); +static int flag = 0; + +ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos) +{ + printk(KERN_ALERT "process %i (%s) going to sleep\n", + current->pid, current->comm); + wait_event_interruptible(wq, flag != 0); + flag = 0; + printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm); + return 0; /* EOF */ +} + +ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count, + loff_t *pos) +{ + printk(KERN_ALERT "process %i (%s) awakening the readers...\n", + current->pid, current->comm); + flag = 1; + wake_up_interruptible(&wq); + return count; /* succeed, to avoid retrial */ +} + + +struct file_operations sleepy_fops = { + .owner = THIS_MODULE, + .read = sleepy_read, + .write = sleepy_write, +}; + + +int sleepy_init(void) +{ + int result; + + /* + * Register your major, and accept a dynamic number + */ + result = register_chrdev(sleepy_major, "sleepy", &sleepy_fops); + if (result < 0) + return result; + if (sleepy_major == 0) + sleepy_major = result; /* dynamic */ + return 0; +} + +void sleepy_cleanup(void) +{ + unregister_chrdev(sleepy_major, "sleepy"); +} + +module_init(sleepy_init); +module_exit(sleepy_cleanup); + + diff --git a/pcie-wb/sleepy.mod.c b/pcie-wb/sleepy.mod.c new file mode 100644 index 0000000000000000000000000000000000000000..147b9f606a20424745b223749c95caab818e50d1 --- /dev/null +++ b/pcie-wb/sleepy.mod.c @@ -0,0 +1,21 @@ +#include <linux/module.h> +#include <linux/vermagic.h> +#include <linux/compiler.h> + +MODULE_INFO(vermagic, VERMAGIC_STRING); + +struct module __this_module +__attribute__((section(".gnu.linkonce.this_module"))) = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +static const char __module_depends[] +__used +__attribute__((section(".modinfo"))) = +"depends="; +