From eb346cf7247d14a2908c6cce3b740351ec047c79 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" <w.terpstra@gsi.de> Date: Tue, 17 Jul 2012 13:10:50 +0200 Subject: [PATCH] Added SPEC card driver --- driver/Makefile | 6 +- driver/pcie_wb.c | 6 +- driver/spec_wb.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++ driver/spec_wb.h | 36 ++++++ 4 files changed, 369 insertions(+), 6 deletions(-) create mode 100644 driver/spec_wb.c create mode 100644 driver/spec_wb.h diff --git a/driver/Makefile b/driver/Makefile index 0919a00..927c8b3 100644 --- a/driver/Makefile +++ b/driver/Makefile @@ -14,10 +14,10 @@ #OBJDUMP =$(CROSS_COMPILE)objdump - -KERNELDIR ?= /lib/modules/`uname -r`/build #/usr/src/linux-3.2.14 #/common/usr/embedded/kernel/scu/linux-2.6.33.6/ +KERNELVER ?= `uname -r` +KERNELDIR ?= /lib/modules/$(KERNELVER)/build ifneq ($(KERNELRELEASE),) - obj-m := pcie_wb.o wishbone.o + obj-m := pcie_wb.o wishbone.o spec_wb.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) diff --git a/driver/pcie_wb.c b/driver/pcie_wb.c index ea15cf4..30d3a03 100644 --- a/driver/pcie_wb.c +++ b/driver/pcie_wb.c @@ -152,11 +152,11 @@ static wb_data_t wb_read(struct wishbone* wb, wb_addr_t addr) out = ((wb_data_t)ioread32(window + (addr & WINDOW_LOW))); break; case 2: - if (unlikely(debug)) printk(KERN_ALERT PCIE_WB ": ioread32(0x%x)\n", addr + dev->low_addr); + if (unlikely(debug)) printk(KERN_ALERT PCIE_WB ": ioread16(0x%x)\n", addr + dev->low_addr); out = ((wb_data_t)ioread16(window + (addr & WINDOW_LOW) + dev->low_addr)) << dev->shift; break; case 1: - if (unlikely(debug)) printk(KERN_ALERT PCIE_WB ": ioread32(0x%x)\n", addr + dev->low_addr); + if (unlikely(debug)) printk(KERN_ALERT PCIE_WB ": ioread8(0x%x)\n", addr + dev->low_addr); out = ((wb_data_t)ioread8 (window + (addr & WINDOW_LOW) + dev->low_addr)) << dev->shift; break; default: // technically should be unreachable @@ -354,7 +354,7 @@ static void __exit pcie_wb_exit(void) } MODULE_AUTHOR("Stefan Rauch <s.rauch@gsi.de>"); -MODULE_DESCRIPTION("GSI Etherbone to Wishbone bridge driver"); +MODULE_DESCRIPTION("GSI Altera-Wishbone bridge driver"); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Enable debugging information"); MODULE_LICENSE("GPL"); diff --git a/driver/spec_wb.c b/driver/spec_wb.c new file mode 100644 index 0000000..3cd9a51 --- /dev/null +++ b/driver/spec_wb.c @@ -0,0 +1,327 @@ + +#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 <linux/sched.h> +#include <linux/miscdevice.h> + +#include <asm/io.h> +#include <asm/spinlock.h> +#include <asm/byteorder.h> + +#include "spec_wb.h" +#include "wishbone.h" + +#if defined(__BIG_ENDIAN) +#define endian_addr(width, shift) (sizeof(wb_data_t)-width)-shift +#elif defined(__LITTLE_ENDIAN) +#define endian_addr(width, shift) shift +#else +#error "unknown machine byte order (endian)" +#endif + +static unsigned int debug = 0; + +static void wb_cycle(struct wishbone* wb, int on) +{ +} + +static void wb_byteenable(struct wishbone* wb, unsigned char be) +{ + struct spec_wb_dev* dev; + + dev = container_of(wb, struct spec_wb_dev, wb); + + switch (be) { + case 0x1: + dev->width = 1; + dev->shift = 0; + dev->low_addr = endian_addr(1, 0); + break; + case 0x2: + dev->width = 1; + dev->shift = 8; + dev->low_addr = endian_addr(1, 1); + break; + case 0x4: + dev->width = 1; + dev->shift = 16; + dev->low_addr = endian_addr(1, 2); + break; + case 0x8: + dev->width = 1; + dev->shift = 24; + dev->low_addr = endian_addr(1, 3); + break; + case 0x3: + dev->width = 2; + dev->shift = 0; + dev->low_addr = endian_addr(2, 0); + break; + case 0xC: + dev->width = 2; + dev->shift = 16; + dev->low_addr = endian_addr(2, 2); + break; + case 0xF: + dev->width = 4; + dev->shift = 0; + dev->low_addr = endian_addr(4, 0); + break; + default: + /* noop -- ignore the strange bitmask */ + break; + } +} + +static void wb_write(struct wishbone* wb, wb_addr_t addr, wb_data_t data) +{ + struct spec_wb_dev* dev; + unsigned char* window; + + dev = container_of(wb, struct spec_wb_dev, wb); + window = (unsigned char*)dev->pci_res[WB_BAR].addr + WB_OFFSET; + addr = (addr & WB_LOW) + dev->low_addr; + + switch (dev->width) { + case 4: + if (unlikely(debug)) printk(KERN_ALERT SPEC_WB ": iowrite32(0x%x, 0x%x)\n", data, addr); + writel(data, window + addr); + break; + case 2: + if (unlikely(debug)) printk(KERN_ALERT SPEC_WB ": iowrite16(0x%x, 0x%x)\n", data >> dev->shift, addr); + iowrite16(data >> dev->shift, window + addr); + break; + case 1: + if (unlikely(debug)) printk(KERN_ALERT SPEC_WB ": iowrite8(0x%x, 0x%x)\n", data >> dev->shift, addr); + iowrite8 (data >> dev->shift, window + addr); + break; + } +} + +static wb_data_t wb_read(struct wishbone* wb, wb_addr_t addr) +{ + wb_data_t out; + struct spec_wb_dev* dev; + unsigned char* window; + + dev = container_of(wb, struct spec_wb_dev, wb); + window = (unsigned char*)dev->pci_res[WB_BAR].addr + WB_OFFSET; + addr = (addr & WB_LOW) + dev->low_addr; + + switch (dev->width) { + case 4: + if (unlikely(debug)) printk(KERN_ALERT SPEC_WB ": ioread32(0x%x)\n", addr); + out = ((wb_data_t)readl(window + addr)); + break; + case 2: + if (unlikely(debug)) printk(KERN_ALERT SPEC_WB ": ioread16(0x%x)\n", addr); + out = ((wb_data_t)ioread16(window + addr)) << dev->shift; + break; + case 1: + if (unlikely(debug)) printk(KERN_ALERT SPEC_WB ": ioread8(0x%x)\n", addr); + out = ((wb_data_t)ioread8 (window + addr)) << dev->shift; + break; + default: // technically should be unreachable + out = 0; + break; + } + + mb(); /* ensure serial ordering of non-posted operations for wishbone */ + + return out; +} + +static wb_data_t wb_read_cfg(struct wishbone *wb, wb_addr_t addr) +{ + wb_data_t out; + + switch (addr) { + case 0: out = 0; break; + case 4: out = 0; break; + case 8: out = 0; break; + case 12: out = 0x30000; break; + default: out = 0; break; + } + + mb(); /* ensure serial ordering of non-posted operations for wishbone */ + + return out; +} + +static const struct wishbone_operations wb_ops = { + .cycle = wb_cycle, + .byteenable = wb_byteenable, + .write = wb_write, + .read = wb_read, + .read_cfg = wb_read_cfg, +}; + +#if 0 +static irq_handler_t irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + return (irq_handler_t)IRQ_HANDLED; +} +#endif + +static int setup_bar(struct pci_dev* pdev, struct spec_wb_resource* res, int bar) +{ + /*init of pci_res0 */ + res->start = pci_resource_start(pdev, bar); + res->end = pci_resource_end(pdev, bar); + res->size = res->end - res->start + 1; + + if (debug) + printk(KERN_ALERT SPEC_WB "/BAR%d 0x%lx - 0x%lx\n", bar, res->start, res->end); + + // is_mem = pci_resource_flags(pdev, 0); + // is_mem = is_mem & IORESOURCE_MEM; + + if (!request_mem_region(res->start, res->size, SPEC_WB)) { + printk(KERN_ALERT SPEC_WB "/BAR%d: request_mem_region failed\n", bar); + return -ENOMEM; + } + + res->addr = ioremap_nocache(res->start, res->size); + if (debug) + printk(KERN_ALERT SPEC_WB "/BAR%d: ioremap to %lx\n", bar, (unsigned long)res->addr); + + return 0; +} + +static void destroy_bar(struct spec_wb_resource* res) +{ + if (debug) + printk(KERN_ALERT "released io 0x%lx\n", res->start); + + iounmap(res->addr); + release_mem_region(res->start, res->size); +} + +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 + */ + u8 revision; + struct spec_wb_dev *dev; + + pci_read_config_byte(pdev, PCI_REVISION_ID, &revision); + if (revision != 0x03) { + printk(KERN_ALERT SPEC_WB ": revision ID wrong!\n"); + goto fail_out; + } + + dev = kmalloc(sizeof(struct spec_wb_dev), GFP_KERNEL); + if (!dev) { + printk(KERN_ALERT SPEC_WB ": could not allocate memory for spec_wb_dev structure!\n"); + goto fail_out; + } + + /* Initialize structure */ + dev->pci_dev = pdev; + dev->wb.wops = &wb_ops; + strcpy(dev->wb.name, SPEC_WB "%d"); + dev->wb.parent = &pdev->dev; + mutex_init(&dev->mutex); + dev->window_offset = 0; + dev->low_addr = 0; + dev->width = 4; + dev->shift = 0; + pci_set_drvdata(pdev, dev); + + /* enable message signaled interrupts */ + if (pci_enable_msi(pdev) != 0) { + /* resort to legacy interrupts */ + printk(KERN_ALERT SPEC_WB ": could not enable MSI interrupting\n"); + goto fail_free; + } + + if (setup_bar(pdev, &dev->pci_res[0], 0) < 0) goto fail_msi; + if (setup_bar(pdev, &dev->pci_res[1], 2) < 0) goto fail_bar0; + if (setup_bar(pdev, &dev->pci_res[2], 4) < 0) goto fail_bar1; + + if (wishbone_register(&dev->wb) < 0) { + printk(KERN_ALERT SPEC_WB ": could not register wishbone bus\n"); + goto fail_bar2; + } + + return pci_enable_device(pdev); + + /* cleaning up */ +fail_bar2: + destroy_bar(&dev->pci_res[2]); +fail_bar1: + destroy_bar(&dev->pci_res[1]); +fail_bar0: + destroy_bar(&dev->pci_res[0]); +fail_msi: + pci_disable_msi(pdev); +fail_free: + kfree(dev); +fail_out: + return -EIO; +} + +static void remove(struct pci_dev *pdev) +{ + /* clean up any allocated resources and stuff here. + * like call release_mem_region(); + */ + + struct spec_wb_dev *dev; + + dev = pci_get_drvdata(pdev); + wishbone_unregister(&dev->wb); + + destroy_bar(&dev->pci_res[2]); + destroy_bar(&dev->pci_res[1]); + destroy_bar(&dev->pci_res[0]); + + pci_disable_msi(pdev); + + kfree(dev); +} + +static struct pci_device_id ids[] = { + { PCI_DEVICE(SPEC_WB_VENDOR_ID, SPEC_WB_DEVICE_ID), }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ids); + +static struct pci_driver spec_wb_driver = { + .name = SPEC_WB, + .id_table = ids, + .probe = probe, + .remove = remove, +}; + +static int __init spec_wb_init(void) +{ + return pci_register_driver(&spec_wb_driver); +} + +static void __exit spec_wb_exit(void) +{ + pci_unregister_driver(&spec_wb_driver); +} + +MODULE_AUTHOR("Wesley W. Terpstra <w.tersptra@gsi.de>"); +MODULE_DESCRIPTION("CERN SPEC card bridge"); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable debugging information"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SPEC_WB_VERSION); + +module_init(spec_wb_init); +module_exit(spec_wb_exit); diff --git a/driver/spec_wb.h b/driver/spec_wb.h new file mode 100644 index 0000000..53bfaeb --- /dev/null +++ b/driver/spec_wb.h @@ -0,0 +1,36 @@ +#ifndef SPEC_WB_DRIVER_H +#define SPEC_WB_DRIVER_H + +#include "wishbone.h" + +#define SPEC_WB "spec_wb" +#define SPEC_WB_VERSION "0.1" + +#define SPEC_WB_VENDOR_ID 0x10dc +#define SPEC_WB_DEVICE_ID 0x018d + +#define WB_BAR 0 +#define WB_OFFSET 0x80000 +#define WB_LOW 0x3fffc + +/* One per BAR */ +struct spec_wb_resource { + unsigned long start; /* start addr of BAR */ + unsigned long end; /* end addr of BAR */ + unsigned long size; /* size of BAR */ + void *addr; /* remapped addr */ +}; + +/* One per physical card */ +struct spec_wb_dev { + struct pci_dev* pci_dev; + struct spec_wb_resource pci_res[3]; + int pci_irq[4]; + + struct wishbone wb; + struct mutex mutex; /* only one user can open a cycle at a time */ + unsigned int window_offset; + unsigned int low_addr, width, shift; +}; + +#endif -- GitLab