Commit eb346cf7 authored by Wesley W. Terpstra's avatar Wesley W. Terpstra

Added SPEC card driver

parent 7ac2c7b3
......@@ -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)
......
......@@ -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");
......
#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);
#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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment