Skip to content
Snippets Groups Projects
Commit 9f1965ab authored by Wesley W. Terpstra's avatar Wesley W. Terpstra
Browse files

Merge branch 'vme-wb-130904'

Added Cesar's driver for VME.
parents 9f313e9b 6c4712b7
No related merge requests found
# 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
KERNELVER ?= `uname -r`
KERNELDIR ?= /lib/modules/$(KERNELVER)/build
REPO_VME = git://ohwr.org/hdl-core-lib/vme64x-core/legacy-vme64x-core.git
DRV_DIR = $(shell pwd)/../../legacy-vme64x-core
ifneq ($(KERNELRELEASE),)
obj-m := vme_wb.o
ccflags-y := -I$(M)/../../legacy-vme64x-core/drv/driver
ccflags-y += -I$(M)/../pcie-wb
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
#KBUILD_EXTMOD := $(PWD)/../pcie-wb/Module.symvers
#KBUILD_EXTRA_SYMBOLS += $(M)/Module.symvers.vme
all: gitmodules wishbone modules
gitmodules:
@test -d $(DRV_DIR) && \
echo "Driver source code found" || \
git clone $(REPO_VME) $(DRV_DIR)
wishbone:
$(MAKE) -C ../pcie-wb
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD)
install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
endif
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
0xd5d87388 vme_bus_error_check ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0xfdd837c3 vme_create_window ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x404e46ad vme_release_mapping ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x8c7c5159 wishbone_slave_ready ../pcie-wb/wishbone EXPORT_SYMBOL
0x3cc3d254 vme_unregister_driver ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x27d77c1f vme_do_dma_kernel ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0xd1694422 find_controller ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x97a23c39 vme_register_berr_handler ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x38a64230 wishbone_register ../pcie-wb/wishbone EXPORT_SYMBOL
0xd72f079c vme_request_irq ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0xa63fcdf2 wishbone_unregister ../pcie-wb/wishbone EXPORT_SYMBOL
0x8ecccb22 vme_free_irq ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x17f13619 find_vme_mapping_from_addr ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0xae07c6e9 vme_find_mapping ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x74ee0fbc vme_bus_error_check_clear ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x3a330d53 vme_register_driver ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x7832e4d0 vme_generate_interrupt ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x2fe70581 vme_destroy_window ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0xbea0c878 vme_do_dma ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0xbccb7cc9 return_controller ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x0106863e vme_unregister_berr_handler ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x824cc545 vme_intclr ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0xe35baa39 vme_intset ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
0x20314cbf vme_get_window_attr ../../vetar_drv/vmebridge/driver/vmebus EXPORT_SYMBOL_GPL
* Author: Cesar Prados <c.prados@gsi.de>
*
* Released according to the GNU GPL, version 2 or any later version
*
* VME-WB bridge for VME
This kernle module provides methods for using Etherbone over VME. It depens on
the wishbone and vmebridge modules. The first steers the Ethernet communication and
the later provides the needed methods for managing the vme bus (open, create
windows, destroy windows, write and read) wraping up the Ethernet protocol in
VME transactions.
The module create 3 VME windows:
1) CS/CSR[AM=VME_CR_CSR, DW=VME_D32, size=0x80000, base=0x80000 * Slot]
This window exposes the configuration area of the Legacy vme64x-core:
[WB Base Addres]
[WB Ctrl Base Addres]
[IRQ Leve and Vector]
[WB bus 32 o 64 bits]
[Enable/Disable the WB Bus]
2) WB Bus[AM=VME_A32_USER_MBLT, DW=VME_D32, size=0x1000000, base=0x1000000 * Slot]
This widows exposes the internal WB bus in the device that the VME bus in connected to.
3) WB Ctrl[AM=VME_A24_USER_MBLT, DW=VME_D32, size=0xA0, base=0x400 * Slot]
This windows exposes a sort of registers that contain Etherbone config parameters, control the WB bus,
and the MSI WB bus
This kernel module handles MSI interrupts using Etherbone interfaces.
For loading the module:
#insmod vme_wb.ko slot= vmebase= vector= lun=
<slot> this parameter means in a vme64, the position of the card in the rack, and in legacy
vme, the position of the base address switch.
<vmebase> set the vmebase for the CS/CSR
<vector> of the IRQ
<lun> index value for VME card
#!/bin/bash
insmod vme_wb.ko slot=1 vmebase=0x0 vector=1 lun=1
#!/bin/bash
rmmod vme_wb.ko
/*
* Copyright (C) 2012-2013 GSI (www.gsi.de)
* Author: Cesar Prados <c.prados@gsi.de>
*
* Released according to the GNU GPL, version 2 or any later version
*
* VME-WB bridge for VME
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/io.h>
#include "vme_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
/* Module parameters */
static int slot[VME_MAX_DEVICES];
static unsigned int slot_num;
static unsigned int vmebase[VME_MAX_DEVICES];
static unsigned int vmebase_num;
static int vector[VME_MAX_DEVICES];
static unsigned int vector_num;
static int lun[VME_MAX_DEVICES] = VME_DEFAULT_IDX;
static unsigned int lun_num;
static unsigned int debug = 0;
static unsigned int swapbe32(unsigned int val)
{
return (((val & 0xff000000) >> 24) | ((val & 0xff0000) >> 8) |
((val & 0xff00) << 8) | ((val & 0xff) << 24));
}
static void wb_cycle(struct wishbone *wb, int on)
{
unsigned char *ctrl_win;
struct vme_wb_dev *dev;
dev = container_of(wb, struct vme_wb_dev, wb);
ctrl_win = dev->vme_res.map[MAP_CTRL]->kernel_va;
if (on)
mutex_lock(&dev->mutex);
if (unlikely(debug))
printk(KERN_ALERT ": Cycle(%d)\n", on);
iowrite32(swapbe32((on ? 0x80000000UL : 0) + 0x40000000UL),
ctrl_win + CTRL);
if (!on)
mutex_unlock(&dev->mutex);
}
static wb_data_t wb_read_cfg(struct wishbone *wb, wb_addr_t addr)
{
wb_data_t out;
struct vme_wb_dev *dev;
unsigned char *ctrl_win;
dev = container_of(wb, struct vme_wb_dev, wb);
ctrl_win = dev->vme_res.map[MAP_CTRL]->kernel_va;
if (unlikely(debug))
printk(KERN_ALERT VME_WB ": READ CFG addr %d \n", addr);
switch (addr) {
case 0:
out = 0;
break;
case 4:
out = be32_to_cpu(ioread32(ctrl_win + ERROR_FLAG));
break;
case 8:
out = 0;
break;
case 12:
out = be32_to_cpu(ioread32(ctrl_win + SDWB_ADDRESS));
break;
default:
out = 0;
break;
}
mb(); /* ensure serial ordering of non-posted operations for wishbone */
return out;
}
static void wb_write(struct wishbone *wb, wb_addr_t addr, wb_data_t data)
{
struct vme_wb_dev *dev;
unsigned char *reg_win;
dev = container_of(wb, struct vme_wb_dev, wb);
reg_win = dev->vme_res.map[MAP_REG]->kernel_va;
addr = addr << 2;
switch (dev->width) {
case 4:
if (unlikely(debug))
printk(KERN_ALERT VME_WB ": iowrite32(0x%x, 0x%x)\n",
data, addr);
iowrite32(data, reg_win + addr);
break;
case 2:
if (unlikely(debug))
printk(KERN_ALERT VME_WB ": iowrite16(0x%x, 0x%x)\n",
data >> dev->shift, addr);
iowrite16(data >> dev->shift, reg_win + addr);
break;
case 1:
if (unlikely(debug))
printk(KERN_ALERT VME_WB ": iowrite8(0x%x, 0x%x)\n",
data >> dev->shift, addr);
iowrite8(data >> dev->shift, reg_win + addr);
break;
}
if (unlikely(debug))
printk(KERN_ALERT VME_WB ": WRITE \n");
}
static wb_data_t wb_read(struct wishbone *wb, wb_addr_t addr)
{
wb_data_t out;
struct vme_wb_dev *dev;
unsigned char *reg_win;
dev = container_of(wb, struct vme_wb_dev, wb);
reg_win = dev->vme_res.map[MAP_REG]->kernel_va;
addr = addr << 2; /* convertion of the map from VME to WB32 */
out = be32_to_cpu(ioread32(reg_win + (addr)));
if (unlikely(debug))
printk(KERN_ALERT VME_WB ": READ (%x) = %x \n", (addr), out);
mb();
return out;
}
static int wb_request(struct wishbone *wb, struct wishbone_request *req)
{
struct vme_wb_dev *dev;
unsigned char *ctrl_win;
uint32_t ctrl;
dev = container_of(wb, struct vme_wb_dev, wb);
ctrl_win = dev->vme_res.map[MAP_CTRL]->kernel_va;
ctrl = be32_to_cpu(ioread32(ctrl_win + MASTER_CTRL));
req->addr = be32_to_cpu(ioread32(ctrl_win + MASTER_ADD));
req->data = be32_to_cpu(ioread32(ctrl_win + MASTER_DATA));
req->mask = ctrl & 0xf;
req->write = (ctrl & 0x40000000) != 0;
iowrite32(swapbe32(1), ctrl_win + MASTER_CTRL); /* dequeue operation */
if (unlikely(debug))
printk(KERN_ALERT
"WB REQUEST:Request ctrl %x addr %x data %x mask %x return %x \n",
ctrl, req->addr, req->data, req->mask,
(ctrl & 0x80000000) != 0);
return (ctrl & 0x80000000) != 0;
}
static void wb_reply(struct wishbone *wb, int err, wb_data_t data)
{
struct vme_wb_dev *dev;
unsigned char *ctrl_win;
dev = container_of(wb, struct vme_wb_dev, wb);
ctrl_win = dev->vme_res.map[MAP_CTRL]->kernel_va;
iowrite32(swapbe32(data), ctrl_win + MASTER_DATA);
iowrite32(swapbe32(err + 2), ctrl_win + MASTER_CTRL);
if (unlikely(debug))
printk(KERN_ALERT "WB REPLY: pushing data %x reply %x\n", data,
err + 2);
}
static void wb_byteenable(struct wishbone *wb, unsigned char be)
{
struct vme_wb_dev *dev;
dev = container_of(wb, struct vme_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 const struct wishbone_operations wb_ops = {
.cycle = wb_cycle,
.byteenable = wb_byteenable,
.write = wb_write,
.read = wb_read,
.read_cfg = wb_read_cfg,
.request = wb_request,
.reply = wb_reply,
};
int irq_handler(void *dev_id)
{
struct vme_wb_dev *dev = dev_id;
printk(KERN_ALERT "posting MSI!!\n");
wishbone_slave_ready(&dev->wb);
return IRQ_HANDLED;
}
int vme_map_window(struct vme_wb_dev *vme_dev, enum vme_map_win map_type)
{
struct device *dev = vme_dev->vme_dev;
enum vme_address_modifier am = 0;
enum vme_data_width dw = 0;
unsigned long base = 0;
unsigned int size = 0;
int rval;
uint8_t *map_type_c = "";
if (vme_dev->vme_res.map[map_type] != NULL) {
dev_err(dev, "Window %d already mapped\n", (int)map_type);
return -EPERM;
}
if (map_type == MAP_REG) {
am = VME_A32_USER_MBLT; /* or VME_A32_USER_DATA_SCT */
dw = VME_D32;
//base = vme_dev->vme_res.vmebase;
base = vme_dev->vme_res.slot * 0x1000000;
size = 0x1000000;
map_type_c = "WB MAP REG";
} else if (map_type == MAP_CTRL) {
am = VME_A24_USER_MBLT;
dw = VME_D32;
//base = vme_dev->vme_res.vmebase;
base = vme_dev->vme_res.slot * 0x400;
size = 0xA0;
map_type_c = "WB MAP CTRL";
} else if (map_type == MAP_CR_CSR) {
am = VME_CR_CSR;
dw = VME_D32;
base = vme_dev->vme_res.slot * 0x80000;
size = 0x80000;
map_type_c = "WB MAP CS/CSR";
}
vme_dev->vme_res.map[map_type] =
kzalloc(sizeof(struct vme_mapping), GFP_KERNEL);
if (!vme_dev->vme_res.map[map_type]) {
dev_err(dev, "Cannot allocate memory for vme_mapping struct\n");
return -ENOMEM;
}
/* Window mapping */
vme_dev->vme_res.map[map_type]->am = am;
vme_dev->vme_res.map[map_type]->data_width = dw;
vme_dev->vme_res.map[map_type]->vme_addru = 0;
vme_dev->vme_res.map[map_type]->vme_addrl = base;
vme_dev->vme_res.map[map_type]->sizeu = 0;
vme_dev->vme_res.map[map_type]->sizel = size;
if ((rval = vme_find_mapping(vme_dev->vme_res.map[map_type], 1)) != 0) {
dev_err(dev, "Failed to map window %d: (%d)\n",
(int)map_type, rval);
kfree(vme_dev->vme_res.map[map_type]);
vme_dev->vme_res.map[map_type] = NULL;
return -EINVAL;
}
dev_info(dev, "%s mapping successful at 0x%p\n",
map_type_c, vme_dev->vme_res.map[map_type]->kernel_va);
return 0;
}
int vme_unmap_window(struct vme_wb_dev *vme_dev, enum vme_map_win map_type)
{
struct device *dev = vme_dev->vme_dev;
if (vme_dev->vme_res.map[map_type] == NULL) {
dev_err(dev, "Window %d not mapped. Cannot unmap\n",
(int)map_type);
return -EINVAL;
}
if (vme_release_mapping(vme_dev->vme_res.map[map_type], 1)) {
dev_err(dev, "Unmap for window %d failed\n", (int)map_type);
return -EINVAL;
}
dev_info(dev, "Window %d unmaped\n", (int)map_type);
kfree(vme_dev->vme_res.map[map_type]);
vme_dev->vme_res.map[map_type] = NULL;
return 0;
}
static void vme_csr_write(u8 value, void *base, u32 offset)
{
offset -= offset % 4;
iowrite32be(value, base + offset);
}
void vme_setup_csr_fa0(void *base, u32 wb_vme, unsigned vector, unsigned level)
{
u8 fa[4]; /* FUN0 ADER contents */
u32 wb_add = wb_vme << 28;
u32 wb_ctrl_add = wb_vme << 10;
/* reset the core */
vme_csr_write(RESET_CORE, base, BIT_SET_REG);
msleep(10);
/* disable the core */
vme_csr_write(ENABLE_CORE, base, BIT_CLR_REG);
/* default to 32bit WB interface */
vme_csr_write(WB32, base, WB_32_64);
/* irq vector */
vme_csr_write(vector, base, IRQ_VECTOR);
/* irq level */
vme_csr_write(level, base, IRQ_LEVEL);
/*do address relocation for FUN0, WB data mapping */
// fa[0] = (wb_vme >> 24) & 0xFF;
// fa[1] = (wb_vme >> 16) & 0xFF;
// fa[2] = (wb_vme >> 8 ) & 0xFF;
// fa[3] = (VME_A32_USER_MBLT & 0x3F) << 2; /* or VME_A32_USER_DATA_SCT */
fa[0] = (wb_add >> 24) & 0xFF;
fa[1] = (wb_add >> 16) & 0xFF;
fa[2] = (wb_add >> 8) & 0xFF;
fa[3] = (VME_A32_USER_MBLT & 0x3F) << 2; /* or VME_A32_USER_DATA_SCT */
vme_csr_write(fa[0], base, FUN0ADER);
vme_csr_write(fa[1], base, FUN0ADER + 4);
vme_csr_write(fa[2], base, FUN0ADER + 8);
vme_csr_write(fa[3], base, FUN0ADER + 12);
/*do address relocation for FUN1, WB control mapping */
// fa[0] = 0x00;
// fa[1] = 0x00;
// fa[2] = (wb_vme >> 24 ) & 0xFF;
// fa[3] = (VME_A24_USER_MBLT & 0x3F) << 2;
fa[0] = (wb_ctrl_add >> 24) & 0xFF;
fa[1] = (wb_ctrl_add >> 16) & 0xFF;
fa[2] = (wb_ctrl_add >> 8) & 0xFF;
fa[3] = (VME_A24_USER_MBLT & 0x3F) << 2; /* or VME_A24_USER_DATA_SCT */
vme_csr_write(fa[0], base, FUN1ADER);
vme_csr_write(fa[1], base, FUN1ADER + 4);
vme_csr_write(fa[2], base, FUN1ADER + 8);
vme_csr_write(fa[3], base, FUN1ADER + 12);
/* enable module, hence make FUN0 and FUN1 available */
vme_csr_write(ENABLE_CORE, base, BIT_SET_REG);
}
static int vme_remove(struct device *pdev, unsigned int ndev)
{
struct vme_wb_dev *dev = dev_get_drvdata(pdev);
vme_unmap_window(dev, MAP_CR_CSR);
vme_unmap_window(dev, MAP_REG);
vme_unmap_window(dev, MAP_CTRL);
wishbone_unregister(&dev->wb);
vme_free_irq(vector_num);
kfree(dev);
dev_info(pdev, "removed\n");
return 0;
}
int vme_is_present(struct vme_wb_dev *vme_dev)
{
struct device *dev = vme_dev->vme_dev;
uint32_t idc;
void *addr;
addr =
vme_dev->vme_res.map[MAP_CR_CSR]->kernel_va + VME_VENDOR_ID_OFFSET;
idc = be32_to_cpu(ioread32(addr)) << 16;
idc += be32_to_cpu(ioread32(addr + 4)) << 8;
idc += be32_to_cpu(ioread32(addr + 8));
if (idc == VME_VENDOR_ID) {
dev_info(dev, "vendor ID is 0x%08x\n", idc);
return 1;
}
dev_err(dev, "wrong vendor ID. 0x%08x found, 0x%08x expected\n",
idc, VME_VENDOR_ID);
dev_err(dev, "VME not present at slot %d\n", vme_dev->vme_res.slot);
return 0;
}
static int vme_probe(struct device *pdev, unsigned int ndev)
{
struct vme_wb_dev *dev;
const char *name;
int error = 0;
if (lun[ndev] >= VME_MAX_DEVICES) {
dev_err(pdev, "Card lun %d out of range [0..%d]\n",
lun[ndev], VME_MAX_DEVICES - 1);
return -EINVAL;
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(pdev, "Cannot allocate memory for vme card struct\n");
return -ENOMEM;
}
/* Initialize struct fields */
dev->vme_res.lun = lun[ndev];
dev->vme_res.slot = slot[ndev];
dev->vme_res.vmebase = vmebase[ndev];
dev->vme_res.vector = vector[ndev];
dev->vme_res.level = VME_IRQ_LEVEL; /* Default value */
dev->vme_dev = pdev;
mutex_init(&dev->mutex);
dev->wb.wops = &wb_ops;
dev->wb.parent = pdev;
/* Map CR/CSR space */
error = vme_map_window(dev, MAP_CR_CSR);
if (error)
goto failed;
if (!vme_is_present(dev)) {
error = -EINVAL;
goto failed_unmap_crcsr;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
name = pdev->bus_id;
#else
name = dev_name(pdev);
#endif
strlcpy(dev->vme_res.driver, KBUILD_MODNAME,
sizeof(dev->vme_res.driver));
snprintf(dev->vme_res.description, sizeof(dev->vme_res.description),
"VME at VME-A32 slot %d 0x%08x - 0x%08x irqv %d irql %d",
dev->vme_res.slot, dev->vme_res.slot << 19,
dev->vme_res.vmebase, vector[ndev], dev->vme_res.level);
dev_info(pdev, "%s\n", dev->vme_res.description);
dev_set_drvdata(dev->vme_dev, dev);
/* configure and activate function 0 */
//vme_setup_csr_fa0(dev->vme_res.map[MAP_CR_CSR]->kernel_va, vmebase[ndev],
// vector[ndev], dev->vme_res.level);
vme_setup_csr_fa0(dev->vme_res.map[MAP_CR_CSR]->kernel_va,
dev->vme_res.slot, vector[ndev], dev->vme_res.level);
/* Map WB A32 space */
error = vme_map_window(dev, MAP_REG);
if (error)
goto failed_unmap_wb;
/* Map WB control A24 space */
error = vme_map_window(dev, MAP_CTRL);
if (error)
goto failed_unmap_wb;
/* wishbone resitation */
if (wishbone_register(&dev->wb) < 0) {
dev_err(pdev, "Could not register wishbone bus\n");
goto failed_unmap_wb;
}
/* register interrupt handler */
if (vme_request_irq(vector_num, irq_handler, dev, "wb_irq") != 0) {
printk(KERN_ALERT VME_WB
": could not register interrupt handler\n");
goto fail_irq;
}
return 0;
fail_irq:
vme_free_irq(vector_num);
failed_unmap_wb:
vme_unmap_window(dev, MAP_REG);
failed_unmap_crcsr:
vme_unmap_window(dev, MAP_CR_CSR);
failed:
kfree(dev);
return error;
}
static struct vme_driver vme_driver = {
.probe = vme_probe,
.remove = vme_remove,
.driver = {
.name = KBUILD_MODNAME,
},
};
static int __init vme_init(void)
{
int error = 0;
/* Check that all insmod argument vectors are the same length */
if (lun_num != slot_num || lun_num != vmebase_num ||
lun_num != vector_num) {
pr_err("%s: The number of parameters doesn't match\n",
__func__);
return -EINVAL;
}
error = vme_register_driver(&vme_driver, lun_num);
if (error) {
pr_err("%s: Cannot register vme driver - lun [%d]\n", __func__,
lun_num);
}
return error;
}
static void __exit vme_exit(void)
{
vme_unregister_driver(&vme_driver);
}
module_init(vme_init);
module_exit(vme_exit);
module_param_array(slot, int, &slot_num, S_IRUGO);
MODULE_PARM_DESC(slot, "Slot where VME card is installed");
module_param_array(vmebase, uint, &vmebase_num, S_IRUGO);
MODULE_PARM_DESC(vmebase, "VME Base address of the VME card registers");
module_param_array(vector, int, &vector_num, S_IRUGO);
MODULE_PARM_DESC(vector, "IRQ vector");
module_param_array(lun, int, &lun_num, S_IRUGO);
MODULE_PARM_DESC(lun, "Index value for VME card");
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Enable debugging information");
MODULE_AUTHOR("Cesar Prados Boda");
MODULE_LICENSE("GPL");
/* MODULE_VERSION(GIT_VERSION); */
MODULE_VERSION("v0.1");
MODULE_DESCRIPTION("vme wb bridge driver");
/*
* Copyright (C) 2012-2013 GSI (www.gsi.de)
* Author: Cesar Prados <c.prados@gsi.de>
*
* Released according to the GNU GPL, version 2 or any later version
*
* Driver for VME VME board.
*/
#ifndef __VME_H__
#define __VME_H__
#include <linux/firmware.h>
#include <vmebus.h>
#include <wishbone.h>
#define VME_WB "vme_wb"
#define VME_MAX_DEVICES 32
#define VME_DEFAULT_IDX { [0 ... (VME_MAX_DEVICES-1)] = -1 }
/* VME CSR offsets */
#define FUN0ADER 0x7FF63
#define FUN1ADER 0x7FF73
#define WB_32_64 0x7ff33
#define BIT_SET_REG 0x7FFFB
#define BIT_CLR_REG 0x7FFF7
#define IRQ_VECTOR 0x7FF5F
#define IRQ_LEVEL 0x7FF5B
#define VME_VENDOR_ID_OFFSET 0x24
/* VME CSR VALUES */
#define WB32 1
#define WB64 0
#define RESET_CORE 0x80
#define ENABLE_CORE 0x10
#define VME_IRQ_LEVEL 0x6
#define VME_VENDOR_ID 0x80031
/* VME WB Interdace*/
#define ERROR_FLAG 0
#define SDWB_ADDRESS 8
#define CTRL 16
#define MASTER_CTRL 24
#define MASTER_ADD 32
#define MASTER_DATA 40
enum vme_map_win {
MAP_CR_CSR = 0, /* CR/CSR */
MAP_REG, /* A32 wb space */
MAP_CTRL /* A24 wb ctrl space */
};
/* Our device structure */
struct vme_dev {
int lun;
int slot;
uint32_t vmebase;
int vector;
int level;
char driver[16];
char description[80];
struct vme_mapping *map[3];
/* struct work_struct work; */
unsigned long irq;
};
struct vme_wb_dev {
struct device *vme_dev;
struct wishbone wb;
struct vme_dev vme_res;
struct mutex mutex;
unsigned int window_offset;
unsigned int low_addr, width, shift;
};
/* Functions and data in vme_wb.c */
extern void vme_setup_csr_fa0(void *base, u32 vme, unsigned vector,
unsigned level);
extern int vme_unmap_window(struct vme_wb_dev *vetar,
enum vme_map_win map_type);
extern int vme_map_window(struct vme_wb_dev *vetar, enum vme_map_win map_type);
#endif /* __VME_H__ */
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