Commit b7f3c485 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

software/tests/gs_logger: crude VME version (needs cleanup & some fixes)

parent eae0d98c
/*
* Public header for the raw I/O interface for PCI or PCI express interfaces
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __RAWRABBIT_H__
#define __RAWRABBIT_H__
#include <linux/types.h>
#include <linux/ioctl.h>
#ifdef __KERNEL__ /* The initial part of the file is driver-internal stuff */
#include <linux/pci.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>
#include <linux/wait.h>
#include <linux/completion.h>
struct rr_devsel;
struct rr_dev {
struct rr_devsel *devsel;
struct pci_driver *pci_driver;
struct pci_device_id *id_table;
struct pci_dev *pdev; /* non-null after pciprobe */
struct mutex mutex;
wait_queue_head_t q;
void *dmabuf;
char *fwname;
struct timespec irqtime;
unsigned long irqcount;
struct completion complete;
struct resource *area[3]; /* bar 0, 2, 4 */
void *remap[3]; /* ioremap of bar 0, 2, 4 */
unsigned long flags;
struct work_struct work;
const struct firmware *fw;
struct completion fw_load;
void (*load_program)(struct rr_dev *); /* lm32 */
int usecount;
#ifdef IS_SPEC_DEMO
struct miscdevice misc;
char miscname[32]; /* "spec-demo-<bus>-<slot> */
struct list_head list;
#endif
};
extern char *rr_fwname; /* module parameter. If "" then defaults apply */
#define RR_FLAG_REGISTERED 0x00000001
#define RR_FLAG_IRQDISABLE 0x00000002
#define RR_FLAG_IRQREQUEST 0x00000002
#define RR_PROBE_TIMEOUT (HZ) /* for pci_register_drv */
/* These two live in ./loader.c */
extern void rr_ask_firmware(struct rr_dev *dev);
extern void rr_load_firmware(struct work_struct *work);
/* And, for the spec only, this is in ./spec-loader.c */
extern void spec_ask_program(struct rr_dev *dev);
#endif /* __KERNEL__ */
/* By default, the driver registers for this vendor/devid */
#define RR_DEFAULT_VENDOR 0x1a39
#define RR_DEFAULT_DEVICE 0x0004
#define RR_DEFAULT_FWNAME "rrabbit-%P-%p@%b"
#define RR_MAX_FWNAME_SIZE 64
#define RR_DEFAULT_BUFSIZE (1<<20) /* 1MB */
#define RR_PLIST_SIZE 4096 /* no PAGE_SIZE in user space */
#define RR_PLIST_LEN (RR_PLIST_SIZE / sizeof(void *))
#define RR_MAX_BUFSIZE (RR_PLIST_SIZE * RR_PLIST_LEN)
/* This structure is used to select the device to be accessed, via ioctl */
struct rr_devsel {
__u16 vendor;
__u16 device;
__u16 subvendor; /* RR_DEVSEL_UNUSED to ignore subvendor/dev */
__u16 subdevice;
__u16 bus; /* RR_DEVSEL_UNUSED to ignore bus and devfn */
__u16 devfn;
};
#define RR_DEVSEL_UNUSED 0xffff
/* Offsets for BAR areas in llseek() and/or ioctl */
#define RR_BAR_0 0x00000000
#define RR_BAR_2 0x20000000
#define RR_BAR_4 0x40000000
#define RR_BAR_BUF 0xc0000000 /* The DMA buffer */
#define RR_IS_DMABUF(addr) ((addr) >= RR_BAR_BUF)
#define __RR_GET_BAR(x) ((x) >> 28)
#define __RR_SET_BAR(x) ((x) << 28)
#define __RR_GET_OFF(x) ((x) & 0x0fffffff)
static inline int rr_is_valid_bar(unsigned long address)
{
int bar = __RR_GET_BAR(address);
return bar == 0 || bar == 2 || bar == 4 || bar == 0x0c;
}
static inline int rr_is_dmabuf_bar(unsigned long address)
{
int bar = __RR_GET_BAR(address);
return bar == 0x0c;
}
struct rr_iocmd {
__u32 address; /* bar and offset */
__u32 datasize; /* 1 or 2 or 4 or 8 */
union {
__u8 data8;
__u16 data16;
__u32 data32;
__u64 data64;
};
};
/* ioctl commands */
#define __RR_IOC_MAGIC '4' /* random or so */
#define RR_DEVSEL _IOW(__RR_IOC_MAGIC, 0, struct rr_devsel)
#define RR_DEVGET _IOR(__RR_IOC_MAGIC, 1, struct rr_devsel)
#define RR_READ _IOWR(__RR_IOC_MAGIC, 2, struct rr_iocmd)
#define RR_WRITE _IOW(__RR_IOC_MAGIC, 3, struct rr_iocmd)
#define RR_IRQWAIT _IO(__RR_IOC_MAGIC, 4)
#define RR_IRQENA _IO(__RR_IOC_MAGIC, 5)
#define RR_GETDMASIZE _IO(__RR_IOC_MAGIC, 6)
/* #define RR_SETDMASIZE _IO(__RR_IOC_MAGIC, 7, unsigned long) */
#define RR_GETPLIST _IO(__RR_IOC_MAGIC, 8) /* returns a whole page */
#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2])
/* Registers from the gennum header files */
enum {
GNGPIO_BASE = 0xA00,
GNGPIO_DIRECTION_MODE = GNGPIO_BASE + 0x4,
GNGPIO_OUTPUT_ENABLE = GNGPIO_BASE + 0x8,
GNGPIO_OUTPUT_VALUE = GNGPIO_BASE + 0xC,
GNGPIO_INPUT_VALUE = GNGPIO_BASE + 0x10,
FCL_BASE = 0xB00,
FCL_CTRL = FCL_BASE,
FCL_STATUS = FCL_BASE + 0x4,
FCL_IODATA_IN = FCL_BASE + 0x8,
FCL_IODATA_OUT = FCL_BASE + 0xC,
FCL_EN = FCL_BASE + 0x10,
FCL_TIMER_0 = FCL_BASE + 0x14,
FCL_TIMER_1 = FCL_BASE + 0x18,
FCL_CLK_DIV = FCL_BASE + 0x1C,
FCL_IRQ = FCL_BASE + 0x20,
FCL_TIMER_CTRL = FCL_BASE + 0x24,
FCL_IM = FCL_BASE + 0x28,
FCL_TIMER2_0 = FCL_BASE + 0x2C,
FCL_TIMER2_1 = FCL_BASE + 0x30,
FCL_DBG_STS = FCL_BASE + 0x34,
FCL_FIFO = 0xE00,
PCI_SYS_CFG_SYSTEM = 0x800
};
#endif /* __RAWRABBIT_H__ */
#ifndef __RR_IO_H
#define __RR_IO_H
#include <stdint.h>
#include <rawrabbit.h>
int rr_bind(int a_fd);
int rr_init(int bus, int devfn);
int rr_writel(uint32_t data, uint32_t addr);
uint32_t rr_readl(uint32_t addr);
int rr_load_bitstream(const void *data, int size8);
int rr_load_bitstream_from_file(const char *file_name);
#endif
SPEC_SW ?= $(shell readlink -f ~/wr-repos/spec-sw)
ETHERBONE ?= $(shell readlink -f ~/wr-repos/etherbone-core/api)
OBJS = fdelay_lib.o i2c_master.o onewire.o fdelay_bus.o spec/tools/speclib.o spec/kernel/loader-ll.o fdelay_dmtd_calibration.o simple-eb.o
OBJS = fdelay_lib.o i2c_master.o onewire.o fdelay_vme.o fdelay_dmtd_calibration.o sveclib/sveclib.o sveclib/libvmebus.o
CFLAGS = -I../include -g -Imini_bone -Ispec/tools -Ietherbone
CFLAGS = -I../include -g -Imini_bone -Ispec/tools -Isveclib
ifeq ($(SPEC_SW),)
throw_error:
@echo "SPEC software package location environment variable is not set. Can't compile :("
endif
#ifeq ($(SPEC_SW),)
#throw_error:
# @echo "SPEC software package location environment variable is not set. Can't compile :("
#endif
all: spec lib
all: lib
spec:
ln -s $(SPEC_SW) spec
ln -s $(ETHERBONE) etherbone
#spec:
# ln -s $(SPEC_SW) spec
# ln -s $(ETHERBONE) etherbone
lib: $(OBJS)
gcc -shared -o libfinedelay.so $(OBJS) -letherbone
gcc -shared -o libfinedelay.so $(OBJS)
ar rc libfinedelay.a $(OBJS)
clean:
......
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <getopt.h>
#include "fdelay_lib.h"
#include "sveclib/sveclib.h"
#include "fdelay_lib.h"
void printk() {};
static void fd_svec_writel(void *priv, uint32_t data, uint32_t addr)
{
svec_writel(priv, data, addr);
}
static uint32_t fd_svec_readl(void *priv, uint32_t addr)
{
return svec_readl(priv, addr);
}
#if 0
static int spec_fdelay_create(fdelay_device_t *dev, int bus, int dev_fn)
{
uint32_t base;
dev->priv_io = spec_open(bus, dev_fn);
if(!dev->priv_io)
{
fprintf(stderr,"Can't map the SPEC @ %x:%x\n", bus, dev_fn);
return -1;
}
dev->writel = fd_spec_writel;
dev->readl = fd_spec_readl;
dev->base_addr = base;
//spec_vuart_init(dev->priv_io, 0xe0500); /* for communication with WRCore during DMTD calibration */
return 0;
}
#endif
#define VENDOR_CERN 0xce42
#define DEVICE_FD_CORE 0xf19ede1a
#define DEVICE_VUART 0xe2d13d04
int fdelay_probe(fdelay_device_t *dev, const char *location)
{
int bus = -1, dev_fn = -1;
char ip_addr[128];
int use_eb = 0;
uint32_t base_core;
void *card;
if(!strncmp(location, "eb:", 3))
{
snprintf(ip_addr, sizeof(ip_addr), "udp/%s", location+3);
use_eb = 1;
} else if (!strncmp(location, "svec:"), 5) {
sscanf(location+5, "%d,%d", &bus, &dev_fn);
}
card = svec_open(8);
if(!card)
{
fprintf(stderr,"SVEC probe failed.\n");
return -1;
}
dev->priv_io = card;
dev->writel = fd_svec_writel;
dev->readl = fd_svec_readl;
dev->base_addr = 0x10000;
dbg("Found FD core @ 0x%x\n", base_core);
}
fdelay_device_t *fdelay_create()
{
return (fdelay_device_t *) malloc(sizeof(fdelay_device_t));
}
*.o
svec-cl
svec-fwloader
svec-vuart
OBJS=sveclib.o libvmebus.o
CFLAGS=-I. -Wall -DDEBUG -g
CC=gcc
all: cl fwloader vuart svecmem
cl: svec-cl.o $(OBJS)
gcc -o svec-cl $(OBJS) svec-cl.o
fwloader: svec-fwloader.o $(OBJS)
gcc -o svec-fwloader $(OBJS) svec-fwloader.o
vuart: svec-vuart.o $(OBJS)
gcc -o svec-vuart $(OBJS) svec-vuart.o
svecmem: svecmem.o $(OBJS)
gcc -o svecmem $(OBJS) svecmem.o
clean:
rm -f *.o svec-cl svec-test svec-fwloader svec-vuart
\ No newline at end of file
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!! WARNING !!!!!!!!
!!!!!!!! DEVELOPER USE ONLY !!!!!!!!
!!!!!!!! DO NOT DEPLOY IN PRODUCTION ENVIRONMENTS !!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sveclib - a tiny userspace library (and a set of tools) allowing developers to play with the svec.
The library provides:
- raw I/O to a spec in particular slot (uses geographical addressing). Currently, there's
a fixed A32/D32/SINGLE mapping available and the memory can be accesed by calling
svec_writel()/svec_readl()
- application FPGA firmware loader (svec-fwloader)
- LM32 CPU firmware loader (svec-cl)
- WR Core virtual UART console (svec-vuart)
All these tools require the slot location to be specified with -b command line switch.
Requirements: Requires vmebus driver to be installed (tested on MEN A20).
Compilation: just run make.
\ No newline at end of file
/**
* \file libvmebus.c
* \brief VME Bus access user library
* \author Sbastien Dugu
* \date 04/02/2009
*
* This library gives userspace applications access to the VME bus
*
* Copyright (c) 2009 \em Sbastien \em Dugu
*
* \par License:
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <vmebus.h>
/** \brief VME address space mapping device */
#define VME_MWINDOW_DEV "/dev/vme_mwindow"
/** \brief VME DMA device */
#define VME_DMA_DEV "/dev/vme_dma"
/**
* \brief Check for VME a bus error
* \param desc VME mapping descriptor
*
* \return 0 if no bus error occured, 1 if bus error occured or -1 on
* any other error (with errno set appropriately).
*
* Note: This function is DEPRECATED. Use vme_bus_error_check_clear instead.
*/
int vme_bus_error_check(struct vme_mapping *desc)
{
int bus_err;
if (ioctl(desc->fd, VME_IOCTL_GET_BUS_ERROR, &bus_err) < 0) {
#ifdef DEBUG
printf("libvmebus: Failed to get bus error status: %s\n",
strerror(errno));
#endif
return -1;
}
return bus_err;
}
/**
* \brief Check and clear a VME bus error from a given address and mapping
* \param desc VME mapping descriptor
* \param address VME address to be checked
*
* \return 0 if no bus error occured, 1 if bus error occured or -1 on
* any other error (with errno set appropriately).
*
* Note that the VME bus error is cleared _only_ if it matches the
* given address/am pair.
*/
int vme_bus_error_check_clear(struct vme_mapping *vme_desc, __u64 address)
{
struct vme_bus_error_desc desc;
desc.valid = 0;
desc.error.address = address;
desc.error.am = vme_desc->am;
if (ioctl(vme_desc->fd, VME_IOCTL_CHECK_CLEAR_BUS_ERROR, &desc) < 0) {
#ifdef DEBUG
printf("libvmebus: Failed to check bus error status: %s\n",
strerror(errno));
#endif
return -1;
}
return desc.valid;
}
static off_t __page_addr(off_t address)
{
long pagemask = sysconf(_SC_PAGESIZE) - 1;
return address & ~pagemask;
}
static size_t __page_aligned_size(struct vme_mapping *desc)
{
return desc->pci_addrl + desc->sizel - __page_addr(desc->pci_addrl);
}
static size_t __page_aligned_size_incr(struct vme_mapping *desc)
{
return desc->pci_addrl - __page_addr(desc->pci_addrl);
}
/*
* Note that the caller might want to map a non-page-aligned region.
* In order to comply with the alignment requirements of mmap, we map a larger,
* page-aligned region and then store the mapped address of the requested
* non-page-aligned region.
*/
static int vme_mmap(struct vme_mapping *desc)
{
void *addr;
size_t size;
off_t page_addr;
page_addr = __page_addr(desc->pci_addrl);
size = __page_aligned_size(desc);
addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, desc->fd, page_addr);
if (addr == MAP_FAILED)
return -1;
desc->user_va = addr + __page_aligned_size_incr(desc);
return 0;
}
static int vme_munmap(struct vme_mapping *desc)
{
void *addr = desc->user_va - __page_aligned_size_incr(desc);
return munmap(addr, __page_aligned_size(desc));
}
/**
* \brief Map a VME address space into the user address space
* \param vmeaddr VME physical start address of the mapping
* \param len Window size (must be a multiple of 64k)
* \param am VME address modifier
* \param offset Offset in the mapping for read access test (Not used)
* \param size Data width
* \param param VME mapping parameters
*
* \return the virtual address of the mapping on succes, or 0 if an error
* occurs (in that case errno is set appropriately).
*
* \deprecated This function is an emulation of the CES functionality on LynxOS,
* new applications should use the vme_map() function instead.
*
* The CES function interface does not give all the needed VME parameters, so
* the following choices were made and may have to be tweaked:
*
* \li if read prefetch is enabled then the prefetch size is set to 2 cache
* lines
* \li the VME address and size are automatically aligned on 64k if needed
* \li the VME address is limited to 32 bits
*
* \note The descriptor allocated by this function is stored into the
* pdparam_master->sgmin field and the file descriptor in
* pdparam_master->dum[1].
*
* \note We voluntarily do not close the file descriptor here, because doing
* so would automatically remove the mapping made using it. The
* mapping will be removed at return_controller() time.
*/
unsigned long find_controller(unsigned long vmeaddr, unsigned long len,
unsigned long am, unsigned long offset,
unsigned long size, struct pdparam_master *param)
{
struct vme_mapping *desc;
int org_force_create;
int force_create = 1;
int fd;
/* Allocate our mapping descriptor */
if ((desc = calloc(1, sizeof(struct vme_mapping))) == NULL)
return 0;
/* Now fill it with the parameters we got */
desc->window_num = 0;
desc->am = am;
if (param->rdpref) {
desc->read_prefetch_enabled = 1;
desc->read_prefetch_size = VME_PREFETCH_2;
}
switch (size) {
case 2:
desc->data_width = VME_D16;
break;
case 4:
desc->data_width = VME_D32;
break;
default:
printf("libvmebus: %s - "
"Unsupported data width %ld\n", __func__, size);
goto out_free;
break;
}
desc->bcast_select = 0;
if (len & 0xffff) {
printf("libvmebus: %s - "
"Mapping size %lx is not 64k aligned, "
"aligning it to %lx.\n",
__func__, len, (len + 0x10000) & ~0xffff);
len &= ~0xffff;
}
desc->sizel = len;
desc->sizeu = 0;
if (vmeaddr & 0xffff) {
printf("libvmebus: %s - "
"VME address %lx is not 64k aligned, "
"aligning it to %lx.\n",
__func__, vmeaddr, vmeaddr & ~0xffff);
vmeaddr &= ~0xffff;
}
desc->vme_addrl = vmeaddr;
desc->vme_addru = 0;
/*
* Now we're all set up, let's start the real stuff
*/
if ((fd = open(VME_MWINDOW_DEV, O_RDWR)) < 0)
goto out_free;
desc->fd = fd;
/* Save the force create flag */
if (ioctl(fd, VME_IOCTL_GET_CREATE_ON_FIND_FAIL, &org_force_create) < 0)
goto out_free;
/* Set the force create flag */
if (ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL, &force_create) < 0)
goto out_free;
/* Create a new mapping for the area */
if (ioctl(fd, VME_IOCTL_FIND_MAPPING, desc) < 0)
goto out_restore_flag;
/* Now mmap the area */
if (vme_mmap(desc))
goto out_restore_flag;
/*
* Save the descriptor address into the struct pdparam_master sgmin
* field, it will be freed with the call to return_controller().
*/
param->sgmin = (unsigned long)desc;
/* Restore the force create flag */
ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL, &org_force_create);
#ifdef DEBUG
printf("Created mapping\n\tVME addr: 0x%08x size: 0x%08x "
"AM: 0x%02x data width: %d\n\n",
desc->vme_addrl, desc->sizel, desc->am, desc->data_width);
#endif
return (unsigned long)desc->user_va;
out_restore_flag:
ioctl(fd, VME_IOCTL_SET_CREATE_ON_FIND_FAIL, &org_force_create);
out_free:
free(desc);
return 0;
}
/**
* \brief Release a VME mapping
* \param desc VME mapping descriptor
*
* \return 0 on success, or -1 if an error occurs (in that case errno is set
* appropriately).
*
* \deprecated This function is an emulation of the CES functionality on LynxOS,
* new applications should use the vme_unmap() function instead.
*
* \warning The function interface has been changed to a single parameter,
* a struct vme_mapping descriptor as the CES original parameters
* are not enough to match a VME mapping.
*/
unsigned long return_controller(struct vme_mapping *desc)
{
int ret = 0;
int org_force_destroy;
int force_destroy = 1;
if (!desc) {
errno = -EFAULT;
return -1;
}
if (vme_munmap(desc)) {
printf("libvmebus: %s - failed to munmap\n", __func__);
ret = -1;
}
/* Save the force destroy flag */
if (ioctl(desc->fd, VME_IOCTL_GET_DESTROY_ON_REMOVE,
&org_force_destroy) < 0) {
printf("libvmebus: %s - "
"Failed to get the force destroy flag\n", __func__);
}
/* Set the force destroy flag */
if (ioctl(desc->fd, VME_IOCTL_SET_DESTROY_ON_REMOVE,
&force_destroy) < 0) {
printf("libvmebus: %s - "
"Failed to set the force destroy flag\n", __func__);
}
if (ioctl(desc->fd, VME_IOCTL_RELEASE_MAPPING, desc) < 0) {
printf("libvmebus: %s - "
"failed to release mapping\n", __func__);
ret = -1;
}
/* Restore the force destroy flag */
if (ioctl(desc->fd, VME_IOCTL_SET_DESTROY_ON_REMOVE,
&org_force_destroy) < 0)
printf("libvmebus: %s - "
"Failed to restore force destroy flag\n", __func__);
close(desc->fd);
free(desc);
return ret;
}
/**
* \brief Map a VME address space into the user address space
*
* \param desc -- a struct vme_mapping descriptor describing the mapping
* \param force -- flag indicating whether a new window should be created if
* needed.
*
* \return a userspace virtual address for the mapping or NULL on error
* (in that case errno is set appropriately).
*/
void *vme_map(struct vme_mapping *desc, int force)
{
int fd;
int org_force_create;
fprintf(stderr,"Open\n");
if ((fd = open(VME_MWINDOW_DEV, O_RDWR)) < 0)
return NULL;
desc->fd = fd;
fprintf(stderr,"i1\n");
/* Save the force create flag */
if (ioctl(fd, VME_IOCTL_GET_CREATE_ON_FIND_FAIL, &org_force_create) < 0)
goto out_close;