gnurabbit: added new version

parent 80d44a5f
This diff is collapsed.
DIRS = kernel user bench# doc
all:
@for d in $(DIRS); do $(MAKE) -C $$d $@ || exit 1; done
clean:
@for d in $(DIRS); do $(MAKE) -C $$d $@ || exit 1; done
This work is part of the White Rabbit project at CERN (cern.ch).
The package includes the raw I/O driver and the GN4124 driver. The kernel
part lives in the kernel/ subdir of the package, while user-space utilities
are under user/
Documentation is under doc/ and is written in Texinfo, which though
old is simple enough to require little efforts on my side. Output is
pdf, text and info.
To compile, just "make". If you miss TeX or texinfo you won't have the
docs. If you miss the compiler or gmake you won't have anything.
To make clean just "make clean".
ioctl
irq878
rdwr
\ No newline at end of file
CFLAGS = -Wall -ggdb -I../kernel
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
ALL = ioctl irq878 rdwr
all: $(ALL)
clean:
rm -f $(ALL) *.o *~
\ No newline at end of file
/*
* Trivial performance test for ioctl I/O
*
* 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.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include "rawrabbit.h"
#define DEVNAME "/dev/rawrabbit"
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
struct rr_iocmd iocmd = {
.address = __RR_SET_BAR(4) | 0xa08,
.datasize = 4,
};
int main(int argc, char **argv)
{
int fd, count, count0, usec;
struct timeval tv1, tv2;
if (argc != 2) {
fprintf(stderr, "%s: use \"%s <count>\"\n", argv[0], argv[0]);
exit(1);
}
count0 = count = atoi(argv[1]);
if (!count) {
fprintf(stderr, "%s: not a number \"%s\"\n", argv[0], argv[1]);
exit(1);
}
fd = open(DEVNAME, O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s: %s: %s\n", argv[0], DEVNAME,
strerror(errno));
exit(1);
}
gettimeofday(&tv1, NULL);
while (count--) {
static int values[] = {
/* These make a pwm signal on the leds */
0xf000, 0xf000, 0xf000, 0xf000,
0xe000, 0xc000, 0x8000, 0x0000
};
iocmd.data32 = values[ count % ARRAY_SIZE(values) ];
if (ioctl(fd, RR_WRITE, &iocmd) < 0) {
fprintf(stderr, "%s: %s: ioctl: %s\n", argv[0], DEVNAME,
strerror(errno));
exit(1);
}
}
gettimeofday(&tv2, NULL);
usec = (tv2.tv_sec - tv1.tv_sec) * 1000 * 1000
+ tv2.tv_usec - tv1.tv_usec;
printf("%i ioctls in %i usecs\n", count0, usec);
printf("%i ioctls per second\n",
(int)(count0 * 1000LL * 1000LL / usec));
exit(0);
}
/*
* Trivial performance test for irq management
*
* 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.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include "rawrabbit.h"
#define DEVNAME "/dev/rawrabbit"
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
struct rr_devsel devsel = {
.vendor = 0x109e,
.device = 0x036e,
.subvendor = RR_DEVSEL_UNUSED,
.bus = RR_DEVSEL_UNUSED,
};
#define ENA_VAL 0x02
#define ENA_REG (__RR_SET_BAR(0) | 0x104)
#define ACK_REG (__RR_SET_BAR(0) | 0x100)
struct rr_iocmd iocmd = {
.datasize = 4,
};
int main(int argc, char **argv)
{
int fd, count, count0, nsec;
unsigned long long total = 0LL;
if (argc != 2) {
fprintf(stderr, "%s: use \"%s <count>\"\n", argv[0], argv[0]);
exit(1);
}
count0 = count = atoi(argv[1]);
if (!count) {
fprintf(stderr, "%s: not a number \"%s\"\n", argv[0], argv[1]);
exit(1);
}
fd = open(DEVNAME, O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s: %s: %s\n", argv[0], DEVNAME,
strerror(errno));
exit(1);
}
/* choose the 878 device */
if (ioctl(fd, RR_DEVSEL, &devsel) < 0) {
fprintf(stderr, "%s: %s: ioctl: %s\n", argv[0], DEVNAME,
strerror(errno));
exit(1);
}
/* enable */
iocmd.address = ENA_REG;
iocmd.data32 = ENA_VAL;
if (ioctl(fd, RR_WRITE, &iocmd) < 0) {
fprintf(stderr, "%s: %s: ioctl: %s\n", argv[0], DEVNAME,
strerror(errno));
exit(1);
}
iocmd.address = ACK_REG;
while (count) {
nsec = ioctl(fd, RR_IRQWAIT);
if (nsec < 0) {
if (errno == EAGAIN) {
ioctl(fd, RR_IRQENA);
continue;
}
fprintf(stderr, "%s: %s: ioctl: %s\n", argv[0], DEVNAME,
strerror(errno));
exit(1); /* Argh! */
}
count--;
/* ack: this must work */
ioctl(fd, RR_WRITE, &iocmd);
nsec = ioctl(fd, RR_IRQENA, &iocmd);
if (nsec < 0) {
fprintf(stderr, "%s: %s: ioctl: %s\n", argv[0], DEVNAME,
strerror(errno));
/* Hmm... */
} else {
total += nsec;
}
}
/* now disable and then acknowledge */
iocmd.address = ENA_REG;
iocmd.data32 = 0;
ioctl(fd, RR_WRITE, &iocmd);
iocmd.address = ACK_REG;
iocmd.data32 = ~0;
ioctl(fd, RR_WRITE, &iocmd);
printf("got %i interrupts, average delay %lins\n", count0,
(long)(total / count0));
exit(0);
}
/*
* Trivial performance test for read(2)/write(2) I/O
*
* 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.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "rawrabbit.h"
#define DEVNAME "/dev/rawrabbit"
int main(int argc, char **argv)
{
int fd, count, count0, usec;
struct timeval tv1, tv2;
if (argc != 2) {
fprintf(stderr, "%s: use \"%s <count>\"\n", argv[0], argv[0]);
exit(1);
}
count0 = count = atoi(argv[1]);
if (!count) {
fprintf(stderr, "%s: not a number \"%s\"\n", argv[0], argv[1]);
exit(1);
}
fd = open(DEVNAME, O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s: %s: %s\n", argv[0], DEVNAME,
strerror(errno));
exit(1);
}
/* write */
lseek(fd, __RR_SET_BAR(4) | 0xa08, SEEK_SET);
gettimeofday(&tv1, NULL);
while (count--) {
static uint32_t values[] = {0x0000, 0xf000};
write(fd, values + (count & 1), sizeof(values[0]));
lseek(fd, -sizeof(values[0]), SEEK_CUR);
}
gettimeofday(&tv2, NULL);
usec = (tv2.tv_sec - tv1.tv_sec) * 1000 * 1000
+ tv2.tv_usec - tv1.tv_usec;
printf("%i writes in %i usecs\n", count0, usec);
printf("%i writes per second\n",
(int)(count0 * 1000LL * 1000LL / usec));
/* read: we cut and paste the code, oh so lazy */
count = count0;
lseek(fd, __RR_SET_BAR(4) | 0xa08, SEEK_SET);
gettimeofday(&tv1, NULL);
while (count--) {
static uint32_t value;
read(fd, &value, sizeof(value));
lseek(fd, -sizeof(value), SEEK_CUR);
}
gettimeofday(&tv2, NULL);
usec = (tv2.tv_sec - tv1.tv_sec) * 1000 * 1000
+ tv2.tv_usec - tv1.tv_usec;
printf("%i reads in %i usecs\n", count0, usec);
printf("%i reads per second\n",
(int)(count0 * 1000LL * 1000LL / usec));
exit(0);
}
# temporaries
gnurabbit.texi
gnurabbit.aux
gnurabbit.cp
gnurabbit.fn
gnurabbit.ky
gnurabbit.log
gnurabbit.pg
gnurabbit.toc
gnurabbit.tp
gnurabbit.vr
# outputs
gnurabbit.html
gnurabbit.info
gnurabbit.pdf
gnurabbit.txt
#
# Makefile for the documentation directory
#
# Copyright 1994,2000,2010 Alessandro Rubini <rubini@linux.it>
#
#################
#
# BE CAREFUL in editing:
# due to the large number of index files, and my use of a non standard
# info input file, any file $(TARGET).* is removed by "make clean"
#
# I chose to use a prefix for the input file ("doc.$(TARGET)"), to ease
# makeing clean and applying my own rules.
#
###################################################################
TARGET = gnurabbit
# Assume makeinfo can do images and --html.
# In any case, MAKEINFO can be specified on the commandline
MAKEINFO = makeinfo
##############################################
INPUT = $(wildcard *.in)
TEXI = $(INPUT:.in=.texi)
.SUFFIXES: .in .texi .info .html .txt
.in.texi:
@rm -f $@ 2> /dev/null
sed -f ./infofilter $< > $@
chmod -w $@
# unfortuantely implicit rules are not concatenated, so force a make run
%.pdf: %.texi $(TEXI)
$(MAKE) $(TEXI)
texi2pdf --batch $<
%.info: %.texi $(TEXI)
$(MAKE) $(TEXI)
$(MAKEINFO) $< -o $@
%.html: %.texi $(TEXI)
$(MAKE) $(TEXI)
$(MAKEINFO) --html --no-split -o $@ $<
%.txt: %.texi $(TEXI)
$(MAKE) $(TEXI)
$(MAKEINFO) --no-headers $< > $@
##############################################
ALL = $(TARGET).info $(TARGET).txt $(TARGET).html $(TARGET).pdf
all: images $(TEXI) $(ALL)
images::
if [ -d images ]; then $(MAKE) -C images || exit 1; fi
info: $(TARGET).info
check: _err.ps
gs -sDEVICE=linux -r320x200x16 $<
terse:
for n in cp fn ky pg toc tp vr; do \
rm -f $(TARGET).$$n; \
done
rm -f *~
clean: terse
rm -f $(ALL) $(TEXI)
install:
This diff is collapsed.
#! /usr/bin/sed -f
# allow "%" as a comment char, but only at the beginning of the line
s/^%/@c /
#s/[^\\]%.*$//
s/^\\%/%/
# turn accents into tex encoding (for Italian language)
s/a`//g
s/e`//g
s/E`//g
s/i`//g
s/o`//g
s/u`//g
s/erch/erch/g
s/oich/oich/g
s/n/n/g
s//@`a/g
s//@`e/g
s//@'e/g
s//@`i/g
s//@`o/g
s//@`E/g
s//@`u/g
#preserve blanks, braces and @ in @example and @smallexample blocks
/@example/,/@end example/ s/{/@{/g
/@example/,/@end example/ s/}/@}/g
/@example/,/@end example/ s/@/@@/g
s/^@@example/@example/
s/^@@end/@end/
/@example/,/@end example/ p
/@example/,/@end example/ d
/@smallexample/,/@end smallexample/ s/{/@{/g
/@smallexample/,/@end smallexample/ s/}/@}/g
/@smallexample/,/@end smallexample/ s/@/@@/g
s/^@@smallexample/@smallexample/
s/^@@end/@end/
/@smallexample/,/@end smallexample/ p
/@smallexample/,/@end smallexample/ d
# remove leading blanks
s/^[ ]*//
# fix include to include texi not in
s/^\(.include.*\).in$/\1.texi/
.*cmd
.tmp_versions
Module.symvers
Module.markers
modules.order
*.mod.c
*.ko
\ No newline at end of file
LINUX ?= /lib/modules/$(shell uname -r)/build
obj-m = rawrabbit.o
rawrabbit-objs = rawrabbit-core.o loader.o loader-ll.o
all modules:
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) modules
clean:
rm -rf *.o *~ .*.cmd *.ko *.mod.c .tmp_versions Module.symvers \
Module.markers modules.order
#include <linux/version.h>
/* Simple compatibility macros */
#ifdef CONFIG_X86
/* Readq for IA32 introduced in 2.6.28-rc7 */
#ifndef readq
static inline __u64 readq(const volatile void __iomem *addr)
{
const volatile u32 __iomem *p = addr;
u32 low, high;
low = readl(p);
high = readl(p + 1);
return low + ((u64)high << 32);
}
static inline void writeq(__u64 val, volatile void __iomem *addr)
{
writel(val, addr);
writel(val >> 32, addr+4);
}
#endif
#endif /* X86 */
/*
* request_firmware_nowait adds a gfp_t argument at some point:
* patch 9ebfbd45f9d4ee9cd72529cf99e5f300eb398e67 == v2.6.32-5357-g9ebfbd4
*/
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)
#define __RR_GFP_FOR_RFNW(x) x,
#else
#define __RR_GFP_FOR_RFNW(x) /* nothing */
#endif
/* Hack... something I sometimes need */
static inline void dumpstruct(char *name, void *ptr, int size)
{
int i;
unsigned char *p = ptr;
printk("dump %s at %p (size 0x%x)\n", name, ptr, size);
for (i = 0; i < size; ) {
printk("%02x", p[i]);
i++;
printk(i & 3 ? " " : i & 0xf ? " " : "\n");
}
if (i & 0xf)
printk("\n");
}
/*
* This is the low-level engine of firmware loading. It is meant
* to be compiled both as kernel code and user code, using the associated
* header to differentiate
*/
#define __LOADER_LL_C__ /* Callers won't define this symbol */
#include "rawrabbit.h"
#include "loader-ll.h"
static inline uint8_t reverse_bits8(uint8_t x)
{
x = ((x >> 1) & 0x55) | ((x & 0x55) << 1);
x = ((x >> 2) & 0x33) | ((x & 0x33) << 2);
x = ((x >> 4) & 0x0f) | ((x & 0x0f) << 4);
return x;
}
static uint32_t unaligned_bitswap_le32(const uint32_t *ptr32)
{
static uint32_t tmp32;
static uint8_t *tmp8 = (uint8_t *) &tmp32;
static uint8_t *ptr8;
ptr8 = (uint8_t *) ptr32;
*(tmp8 + 0) = reverse_bits8(*(ptr8 + 0));
*(tmp8 + 1) = reverse_bits8(*(ptr8 + 1));
*(tmp8 + 2) = reverse_bits8(*(ptr8 + 2));
*(tmp8 + 3) = reverse_bits8(*(ptr8 + 3));
return tmp32;
}
/*
* Unfortunately, most of the following is from fcl_gn4124.cpp, for which
* the license terms are at best ambiguous.
*/
int loader_low_level(int fd, void __iomem *bar4, const void *data, int size8)
{
int size32 = (size8 + 3) >> 2;
const uint32_t *data32 = data;
int ctrl = 0, i, done = 0, wrote = 0;
lll_write(fd, bar4, 0x00, FCL_CLK_DIV);
lll_write(fd, bar4, 0x40, FCL_CTRL); /* Reset */
i = lll_read(fd, bar4, FCL_CTRL);
if (i != 0x40) {
printk(KERN_ERR "%s: %i: error\n", __func__, __LINE__);
return -EIO;
}
lll_write(fd, bar4, 0x00, FCL_CTRL);
lll_write(fd, bar4, 0x00, FCL_IRQ); /* clear pending irq */
switch(size8 & 3) {
case 3: ctrl = 0x116; break;
case 2: ctrl = 0x126; break;
case 1: ctrl = 0x136; break;
case 0: ctrl = 0x106; break;
}
lll_write(fd, bar4, ctrl, FCL_CTRL);
lll_write(fd, bar4, 0x00, FCL_CLK_DIV); /* again? maybe 1 or 2? */
lll_write(fd, bar4, 0x00, FCL_TIMER_CTRL); /* "disable FCL timr fun" */
lll_write(fd, bar4, 0x10, FCL_TIMER_0); /* "pulse width" */
lll_write(fd, bar4, 0x00, FCL_TIMER_1);
/*
* Set delay before data and clock is applied by FCL
* after SPRI_STATUS is detected being assert.
*/
lll_write(fd, bar4, 0x08, FCL_TIMER2_0); /* "delay before data/clk" */
lll_write(fd, bar4, 0x00, FCL_TIMER2_1);
lll_write(fd, bar4, 0x17, FCL_EN); /* "output enable" */
ctrl |= 0x01; /* "start FSM configuration" */
lll_write(fd, bar4, ctrl, FCL_CTRL);
while(size32 > 0)
{
/* Check to see if FPGA configuation has error */
i = lll_read(fd, bar4, FCL_IRQ);
if ( (i & 8) && wrote) {
done = 1;
printk("%s: %i: done after %i\n", __func__, __LINE__,
wrote);
} else if ( (i & 0x4) && !done) {
printk("%s: %i: error after %i\n", __func__, __LINE__,
wrote);
return -EIO;
}
/* Wait until at least 1/2 of the fifo is empty */
while (lll_read(fd, bar4, FCL_IRQ) & (1<<5))
;
/* Write a few dwords into FIFO at a time. */
for (i = 0; size32 && i < 32; i++) {
lll_write(fd, bar4, unaligned_bitswap_le32(data32),
FCL_FIFO);
data32++; size32--; wrote++;
}
}
lll_write(fd, bar4, 0x186, FCL_CTRL); /* "last data written" */
/* Checking for the "interrupt" condition is left to the caller */
return wrote;
}
/*
* This header differentiates between kernel-mode and user-mode compilation,
* as loader-ll.c is meant to be used in both contexts.
*/
#ifndef __iomem
#define __iomem /* nothing, for user space */
#endif
extern int loader_low_level(
int fd, /* This is ignored in kernel space */
void __iomem *bar4, /* This is ignored in user space */
const void *data,
int size8);
/* The following part implements a different access rule for user and kernel */
#ifdef __LOADER_LL_C__
#ifdef __KERNEL__
#include <asm/io.h>
//#include <linux/kernel.h> /* for printk */
static inline void lll_write(int fd, void __iomem *bar4, u32 val, int reg)
{
writel(val, bar4 + reg);
}
static inline u32 lll_read(int fd, void __iomem *bar4, int reg)
{
return readl(bar4 + reg);
}
#else /* ! __KERNEL__ */
#include <stdio.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <errno.h>
static inline void lll_write(int fd, void __iomem *bar4, uint32_t val, int reg)
{
struct rr_iocmd iocmd = {
.datasize = 4,
.address = reg | __RR_SET_BAR(4),
};
iocmd.data32 = val;
if (ioctl(fd, RR_WRITE, &iocmd) < 0) perror("ioctl");
return;
}
static inline uint32_t lll_read(int fd, void __iomem *bar4, int reg)
{
struct rr_iocmd iocmd = {
.datasize = 4,
.address = reg | __RR_SET_BAR(4),
};
if (ioctl(fd, RR_READ, &iocmd) < 0) perror("ioctl");
return iocmd.data32;
}
#define KERN_ERR /* nothing */
#define printk(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
#endif
#endif /* __LOADER_LL_C__ */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <linux/hardirq.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>
#include <linux/jiffies.h>
#include <linux/ctype.h>
#include <linux/mutex.h>
#include "rawrabbit.h"
#include "compat.h"
#include "loader-ll.h"
#ifndef CONFIG_FW_LOADER
#warning "CONFIG_FW_LOADER is need in kernel configuration."
#warning "Compiling anyways, but won't be able to load firmware"
#endif
/* a few prototypes, to avoid many diff lines from previous versions */
static int __rr_gennum_load(struct rr_dev *dev, const void *data, int size);
static int rr_expand_name(struct rr_dev *dev, char *outname);
/* I need to be notified when I get written, so "charp" won't work */
static int param_set_par_fwname(const char *val, const struct kernel_param *kp)
{
const char *prev = *(const char **)kp->arg;
int ret = param_set_charp(val, kp);
extern struct rr_dev rr_dev; /* global: no good */
struct rr_dev *dev = &rr_dev;
static char fwname[RR_MAX_FWNAME_SIZE];
if (ret)
return ret;
ret = rr_expand_name(dev, fwname);
if (ret) {
/*
* bad it went: refuse this string and restore prev one
* Looks like there's a problem here: the previous string
* was not freed, or it's me who didn't see it?
*/
kfree(*(const char **)kp->arg);
*(const char **)kp->arg = prev;
return ret;
}
/*
* Use the new firmware immediately at this time. We are in user
* context, so take the mutex to avoid contention with ioctl.
* Note that if we are not bound (like at insmod time) we don't
* want to do the actual loading
*/
if (dev->pdev) {
mutex_lock(&dev->mutex);
rr_ask_firmware(dev);
mutex_unlock(&dev->mutex);
}
return 0;
}
static int param_get_par_fwname(char *buffer, const struct kernel_param *kp)
{
int ret = param_get_charp(buffer, kp);
return ret;
}
static struct kernel_param_ops param_ops_par_fwname = {
.set = param_set_par_fwname,
.get = param_get_par_fwname,
};
#define param_check_par_fwname(name, p) __param_check(name, p, char *)
static char *rr_fwname = RR_DEFAULT_FWNAME;
module_param_named(fwname, rr_fwname, par_fwname, 0644);
static int rr_expand_name(struct rr_dev *dev, char *outname)
{
struct rr_devsel *devsel = dev->devsel;
char *si, *so = outname;
for (si = rr_fwname; *si ; si++) {
if (so - outname >= RR_MAX_FWNAME_SIZE)
return -ENOSPC;
if (*si != '%') {
*so++ = *si;
continue;
}
si++;
if (so - outname + 9 >= RR_MAX_FWNAME_SIZE)
return -ENOSPC;
switch(*si) {
case 'P': /* PCI vendor:device */;
so += sprintf(so, "%04x:%04x",
devsel->vendor, devsel->device);
break;
case 'p': /* PCI subvendor:subdevice */;
so += sprintf(so, "%04x:%04x",
devsel->subvendor, devsel->subdevice);
break;
case 'b': /* BUS id */
so += sprintf(so, "%04x:%04x",
devsel->bus, devsel->devfn);
break;
case '%':
*so++ = '%';
default:
return -EINVAL;
}
}
/* terminate and remove trailing spaces (includes newlines) */
*so = '\0';
while (isspace(*--so))
*so = '\0';
return 0;
}
/* This stupid function is used to report what is the calling environment */
static void __rr_report_env(const char *func)
{
/* Choose 0 or 1 in the if below, to turn on/off without warnings */
if (1) {
printk("%s: called with preempt_cont == 0x%08x\n", func,
preempt_count());
printk("%s: current = %i: %s\n", func,
current->pid, current->comm);
}
}
/*
* The callback when loading is over, either with or without data.
* This a context that can sleep, so we can program it taking as
* much time as we want.
*/
static void rr_loader_complete(const struct firmware *fw, void *context)
{
struct rr_dev *dev = context;
int ret;
dev->fw = fw;
__rr_report_env(__func__); /* Here, preempt count is 0: good! */
if (fw) {
pr_info("%s: %p: size %i (0x%x), data %p\n", __func__, fw,
fw ? fw->size : 0, fw ? fw->size : 0,
fw ? fw->data : 0);
} else {
pr_warning("%s: no firmware\n", __func__);
return;
}
/*
* At this point, use fw->data as a linear array of fw->size bytes.
* If we are handling the gennum device, load this firmware binary;
* otherwise, just complain to the user -- I'd better use a much
* fancier method of defining one programmer per vendor/device...
*/
if (dev->devsel->vendor == RR_DEFAULT_VENDOR
&& dev->devsel->device == RR_DEFAULT_DEVICE) {
ret = __rr_gennum_load(dev, fw->data, fw->size);
} else {
pr_err("%s: not loading firmware: this is not a GN4124\n",
__func__);
ret = -ENODEV;
}
/* At this point, we can releae the firmware we got */
release_firmware(dev->fw);
if (ret)
pr_err("%s: loading returned error %i\n", __func__, ret);
dev->fw = NULL;
}
/*
* We want to run the actual loading from a work queue, to have a known
* loading environment, especially one that can sleep. The function
* pointer is already in the work structure, set at compile time from
* rawrabbit-core.c .
*/
void rr_load_firmware(struct work_struct *work)
{
struct rr_dev *dev = container_of(work, struct rr_dev, work);
struct pci_dev *pdev = dev->pdev;
static char fwname[RR_MAX_FWNAME_SIZE];
int err;
if (rr_expand_name(dev, fwname)) {
dev_err(&pdev->dev, "Wrong fwname: \"%s\"\n", rr_fwname);
return;
}
if (1)
printk("%s: %s\n", __func__, fwname);
__rr_report_env(__func__);
printk(KERN_INFO "firmware name: %s\n", fwname);
err = request_firmware_nowait(THIS_MODULE, 1, fwname, &pdev->dev,
__RR_GFP_FOR_RFNW(GFP_KERNEL)
dev, rr_loader_complete);
printk("request firmware returned %i\n", err);
}
/* This function is called by the PCI probe function. */
void rr_ask_firmware(struct rr_dev *dev)
{
/* Here, preempt_cont is 1 for insmode/rrcmd. What for hotplug? */
__rr_report_env(__func__);
if (dev->fw) {
pr_err("%s: firmware asked, but firmware already present\n",
__func__);
return;
}
/* Activate the work queue, so we are sure we are in user context */
schedule_work(&dev->work);
}
/*
* Finally, this is the most important thing, the loader for a Gennum
* 4124 evaluation board. We know we are mounting a Xilinx Spartan.
*
* This function doesn't actually do the actual work: another level is
* there to allow the same code to be run both in kernel and user space.
* Thus, actual access to registers has been split to loader_low_level(),
* in loader-ll.c (aided by loader-ll.h).
*/
static int __rr_gennum_load(struct rr_dev *dev, const void *data, int size8)
{
int i, done = 0, wrote = 0;
unsigned long j;
void __iomem *bar4 = dev->remap[2]; /* remap == bar0, bar2, bar4 */
printk("programming with bar4 @ %x, vaddr %p\n",
dev->area[2]->start, bar4);
/* Ok, now call register access, which lived elsewhere */
wrote = loader_low_level( 0 /* unused fd */, bar4, data, size8);
if (wrote < 0)
return wrote;
j = jiffies + 2 * HZ;
/* Wait for DONE interrupt */
while(!done) {
i = readl(bar4 + FCL_IRQ);
if (i & 0x8) {
printk("%s: %i: done after %i\n", __func__, __LINE__,
wrote);
done = 1;
} else if( (i & 0x4) && !done) {
printk("%s: %i: error after %i\n", __func__, __LINE__,
wrote);
return -ETIMEDOUT;
}
if (time_after(jiffies, j)) {
printk("%s: %i: tout after %i\n", __func__, __LINE__,
wrote);
return -ETIMEDOUT;
}
}
return 0;
}
This diff is collapsed.
/*
* 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>
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;
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;
int usecount;
};
#define RR_FLAG_REGISTERED 0x00000001
#define RR_FLAG_IRQDISABLE 0x00000002
#define RR_FLAG_IRQREQUEST 0x00000002
#define RR_PROBE_TIMEOUT (HZ/10) /* 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);
#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 {
GPIO_BASE = 0xA00,
GPIO_INT_STATUS = GPIO_BASE + 0x20,
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__ */
CFLAGS = -Wall -ggdb -I../kernel
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
ALL = rrcmd flip
all: $(ALL)
clean:
rm -f $(ALL) *.o *~
\ No newline at end of file
/* Trivial.... */
#include <stdio.h>
int main(int argc, char **argv)
{
unsigned int c;
while ( (c = getchar()) != EOF)
putchar( 0
| ((c & 0x80) >> 7)
| ((c & 0x40) >> 5)
| ((c & 0x20) >> 3)
| ((c & 0x10) >> 1)
| ((c & 0x08) << 1)
| ((c & 0x04) << 3)
| ((c & 0x02) << 5)
| ((c & 0x01) << 7));
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include "rr_io.h"
int main(int argc, char *argv[])
{
if(argc != 2){
printf( "usage: %s filename \n", argv[0]);
}
else{
rr_init();
gennum_writel(0xE001F07C, 0x808); // set local bus freq
//gennum_writel(0x00002000, 0xA04); // GPIO direction
//gennum_writel(0x0000D000, 0xA08); // GPIO output enable
//gennum_writel(0x00008000, 0xA0C); // GPIO output value
printf("Firmware to be loaded : %s \n", argv[1]);
rr_load_bitstream_from_file(argv[1]);
printf("Firmware loaded successfully ! \n");
}
}
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include "rr_io.h"
#include "gpio.h"
int main(int argc, char *argv[])
{
uint32_t i=0;
if(argc != 2){
printf( "usage: %s filename \n", argv[0]);
}
else{
rr_init();
gpio_init();
gpio_bootselect(GENNUM_FPGA);
//gpio_bootselect(FPGA_FLASH);
printf("CLK_CSR : %.8X\n", gennum_readl(0x808));
/*
while(1){
i++;
//printf("%5d Flash status : %.2X\n", i, flash_read_status());
printf("%5d Flash status : %.8X\n", i, flash_read_id());
sleep(1);
}
*/
printf("Firmware to be loaded : %s \n", argv[1]);
rr_load_bitstream_from_file(argv[1]);
printf("Firmware loaded successfully ! \n");
}
}
#include <stdio.h>
#include <sys/time.h>
#include "rr_io.h"
#include "gpio.h"
void gpio_set1(uint32_t addr, uint8_t bit)
{
uint32_t reg;
reg = gennum_readl(addr);
//printf("register:%.8X ", reg);
reg |= (1 << bit);
//printf("SET :%.8X(%.2d):%.8X\n", addr, bit, reg);
gennum_writel(reg, addr);
}
void gpio_set0(uint32_t addr, uint8_t bit)
{
uint32_t reg;
reg = gennum_readl(addr);
//printf("register:%.8X ", reg);
reg &= ~(1 << bit);
//printf("CLEAR:%.8X(%.2d):%.8X\n", addr, bit, reg);
gennum_writel(reg, addr);
}
uint8_t gpio_get(uint32_t addr, uint8_t bit)
{
return (gennum_readl(addr) & (1 << bit)) ? 1 : 0;
}
void gpio_init(void)
{
gennum_writel(0x00000000, FCL_CTRL);// FCL mode
gennum_writel(0x00000017, FCL_EN);// FCL output enable
gennum_writel(0x00000000, FCL_IODATA_OUT);// FCL outputs to 0
gennum_writel(0x00002000, GPIO_DIRECTION_MODE); // GPIO direction (1=input)
gennum_writel(0x0000D000, GPIO_OUTPUT_ENABLE); // GPIO output enable
gennum_writel(0x00000000, GPIO_OUTPUT_VALUE); // GPIO output to 0
gpio_set1(GPIO_OUTPUT_VALUE, GPIO_FLASH_CS);
}
void gpio_bootselect(uint8_t select)
{
switch(select){
case GENNUM_FLASH:
gpio_set0(GPIO_OUTPUT_VALUE, GPIO_BOOTSEL0);
gpio_set1(GPIO_OUTPUT_VALUE, GPIO_BOOTSEL1);
break;
case GENNUM_FPGA:
gpio_set1(GPIO_OUTPUT_VALUE, GPIO_BOOTSEL0);
gpio_set0(GPIO_OUTPUT_VALUE, GPIO_BOOTSEL1);
break;
case FPGA_FLASH:
gennum_writel(0x00000000, FCL_EN);// FCL output all disabled
gpio_set1(GPIO_OUTPUT_VALUE, GPIO_BOOTSEL0);
gpio_set1(GPIO_OUTPUT_VALUE, GPIO_BOOTSEL1);
break;
default:
break;
}
}
static uint8_t spi_read8(void)
{
uint8_t rx;
int i;
gpio_set0(FCL_IODATA_OUT, SPRI_CLKOUT);
for(i = 0; i < 8;i++){
usleep(SPI_DELAY);
rx <<= 1;
if (gpio_get(GPIO_INPUT_VALUE, GPIO_SPRI_DIN))
rx |= 1;
//usleep(SPI_DELAY);
gpio_set1(FCL_IODATA_OUT, SPRI_CLKOUT);
usleep(SPI_DELAY);
gpio_set0(FCL_IODATA_OUT, SPRI_CLKOUT);
}
usleep(SPI_DELAY);
return rx;
}
static void spi_write8(uint8_t tx)
{
int i;
gpio_set0(FCL_IODATA_OUT, SPRI_CLKOUT);
for(i = 0; i < 8;i++){
//usleep(SPI_DELAY);
if(tx & 0x80)
gpio_set1(FCL_IODATA_OUT, SPRI_DATAOUT);
else
gpio_set0(FCL_IODATA_OUT, SPRI_DATAOUT);
tx<<=1;
usleep(SPI_DELAY);
gpio_set1(FCL_IODATA_OUT, SPRI_CLKOUT);
usleep(SPI_DELAY);
gpio_set0(FCL_IODATA_OUT, SPRI_CLKOUT);
}
usleep(SPI_DELAY);
}
uint8_t flash_read_status(void)
{
uint8_t val;
gpio_set0(GPIO_OUTPUT_VALUE, GPIO_FLASH_CS);
usleep(SPI_DELAY);
spi_write8(FLASH_RDSR);
val = spi_read8();
gpio_set1(GPIO_OUTPUT_VALUE, GPIO_FLASH_CS);
usleep(SPI_DELAY);
return val;
}
uint32_t flash_read_id(void)
{
uint32_t val=0;
gpio_set0(GPIO_OUTPUT_VALUE, GPIO_FLASH_CS);
usleep(SPI_DELAY);
spi_write8(FLASH_RDID);
val = (spi_read8() << 16);
val += (spi_read8() << 8);
val += spi_read8();
gpio_set1(GPIO_OUTPUT_VALUE, GPIO_FLASH_CS);
usleep(SPI_DELAY);
return val;
}
#define FCL_CTRL 0xB00
#define FCL_STATUS 0xB04
#define FCL_IODATA_IN 0xB08
#define FCL_IODATA_OUT 0xB0C
#define FCL_EN 0xB10
#define GPIO_DIRECTION_MODE 0xA04
#define GPIO_OUTPUT_ENABLE 0xA08
#define GPIO_OUTPUT_VALUE 0xA0C
#define GPIO_INPUT_VALUE 0xA10
#define SPRI_CLKOUT 0
#define SPRI_DATAOUT 1
#define SPRI_CONFIG 2
#define SPRI_DONE 3
#define SPRI_XI_SWAP 4
#define SPRI_STATUS 5
#define GPIO_SPRI_DIN 13
#define GPIO_FLASH_CS 12
#define GPIO_BOOTSEL0 15
#define GPIO_BOOTSEL1 14
#define SPI_DELAY 50
#define FLASH_WREN 0x06
#define FLASH_WRDI 0x04
#define FLASH_RDID 0x9F
#define FLASH_RDSR 0x05
#define FLASH_WRSR 0x01
#define FLASH_READ 0x03
#define FLASH_FAST_READ 0x0B
#define FLASH_PP 0x02
#define FLASH_SE 0xD8
#define FLASH_BE 0xC7
#define GENNUM_FLASH 1
#define GENNUM_FPGA 2
#define FPGA_FLASH 3
void gpio_init(void);
void gpio_bootselect(uint8_t select);
uint8_t flash_read_status(void);
uint32_t flash_read_id(void);
#!/bin/bash
cat pfc_top.bin | ./gnurabbit/user/flip > /lib/firmware/pfc_top_flipped.bin
#rmmod rawrabbit
insmod gnurabbit/kernel/rawrabbit.ko
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include "../kernel/rawrabbit.h"
#include "rr_io.h"
#define DEVNAME "/dev/rawrabbit"
static int fd;
int rr_init()
{
struct rr_devsel devsel;
int ret = -EINVAL;
fd = open(DEVNAME, O_RDWR);
if (fd < 0) {
return -1;
}
return 0;
}
int rr_writel(uint32_t data, uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(0);
iocmd.data32 = data;
ioctl(fd, RR_WRITE, &iocmd);
}
uint32_t rr_readl(uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(0);
ioctl(fd, RR_READ, &iocmd);
return iocmd.data32;
}
void gennum_writel(uint32_t data, uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(4);
iocmd.data32 = data;
ioctl(fd, RR_WRITE, &iocmd);
}
uint32_t gennum_readl(uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(4);
ioctl(fd, RR_READ, &iocmd);
return iocmd.data32;
}
static inline int64_t get_tics()
{
struct timezone tz= {0,0};
struct timeval tv;
gettimeofday(&tv, &tz);
return (int64_t)tv.tv_sec * 1000000LL + (int64_t) tv.tv_usec;
}
int rr_load_bitstream(const void *data, int size8)
{
int i, ctrl, done = 0, wrote = 0;
unsigned long j;
uint8_t val8;
const uint8_t *data8 = data;
const uint32_t *data32 = data;
int size32 = (size8 + 3) >> 2;
// fprintf(stderr,"Loading %d bytes...\n", size8);
if (1) {
/*
* Hmmm.... revers bits for xilinx images?
* We can't do in kernel space anyways, as the pages are RO
*/
uint8_t *d8 = (uint8_t *)data8; /* Horrible: kill const */
for (i = 0; i < size8; i++) {
val8 = d8[i];
d8[i] = 0
| ((val8 & 0x80) >> 7)
| ((val8 & 0x40) >> 5)
| ((val8 & 0x20) >> 3)
| ((val8 & 0x10) >> 1)
| ((val8 & 0x08) << 1)
| ((val8 & 0x04) << 3)
| ((val8 & 0x02) << 5)
| ((val8 & 0x01) << 7);
}
}
/* Do real stuff */
gennum_writel(0x00, FCL_CLK_DIV);
gennum_writel(0x40, FCL_CTRL); /* Reset */
i = gennum_readl( FCL_CTRL);
if (i != 0x40) {
fprintf(stderr, "%s: %i: error\n", __func__, __LINE__);
return;
}
gennum_writel(0x00, FCL_CTRL);
gennum_writel(0x00, FCL_IRQ); /* clear pending irq */
switch(size8 & 3) {
case 3: ctrl = 0x116; break;
case 2: ctrl = 0x126; break;
case 1: ctrl = 0x136; break;
case 0: ctrl = 0x106; break;
}
gennum_writel(ctrl, FCL_CTRL);
gennum_writel(0x00, FCL_CLK_DIV); /* again? maybe 1 or 2? */
gennum_writel(0x00, FCL_TIMER_CTRL); /* "disable FCL timer func" */
gennum_writel(0x10, FCL_TIMER_0); /* "pulse width" */
gennum_writel(0x00, FCL_TIMER_1);
/* Set delay before data and clock is applied by FCL after SPRI_STATUS is
detected being assert.
*/
gennum_writel(0x08, FCL_TIMER2_0); /* "delay before data/clock..." */
gennum_writel(0x00, FCL_TIMER2_1);
gennum_writel(0x17, FCL_EN); /* "output enable" */
ctrl |= 0x01; /* "start FSM configuration" */
gennum_writel(ctrl, FCL_CTRL);
while(size32 > 0)
{
/* Check to see if FPGA configuation has error */
i = gennum_readl( FCL_IRQ);
if ( (i & 8) && wrote) {
done = 1;
// fprintf(stderr,"%s: %idone after %i\n", __func__, __LINE__, wrote);
} else if ( (i & 0x4) && !done) {
fprintf(stderr,"Error after %i\n", wrote);
return -1;
}
// fprintf(stderr,".");
while(gennum_readl(FCL_IRQ) & (1<<5)); // wait until at least 1/2 of the FIFO is empty
/* Write 64 dwords into FIFO at a time. */
for (i = 0; size32 && i < 32; i++) {
gennum_writel(*data32, FCL_FIFO);
data32++; size32--; wrote++;
// udelay(20);
}
}
gennum_writel(0x186, FCL_CTRL); /* "last data written" */
int64_t tstart = get_tics();
//j = jiffies + 2 * HZ;
/* Wait for DONE interrupt */
while(!done) {
// fprintf(stderr,stderr, "Wait!");
i = gennum_readl( FCL_IRQ);
if (i & 0x8) {
// fprintf(stderr,"done after %i\n", wrote);
done = 1;
} else if( (i & 0x4) && !done) {
fprintf(stderr,"Error after %i\n", wrote);
return -1;
}
usleep(10000);
if(get_tics() - tstart > 1000000LL)
{
fprintf(stderr,"Loader: DONE timeout. Did you choose proper bitgen options?\n");
return;
}
/*if (time_after(jiffies, j)) {
printk("%s: %i: tout after %i\n", __func__, __LINE__,
wrote);
return;
} */
}
return done?0:-1;
}
int rr_load_bitstream_from_file(const char *file_name)
{
uint8_t *buf;
FILE *f;
uint32_t size;
f=fopen(file_name,"rb");
if(!f) return -1;
fseek(f, 0, SEEK_END);
size = ftell(f);
buf = malloc(size);
if(!buf)
{
fclose(f);
return -1;
}
fseek(f, 0, SEEK_SET);
fread(buf, 1, size, f);
fclose(f);
int rval = rr_load_bitstream(buf, size);
free(buf);
return rval;
}
#ifndef __RR_IO_H
#define __RR_IO_H
#include <stdint.h>
int rr_init();
int rr_writel(uint32_t data, uint32_t addr);
uint32_t rr_readl(uint32_t addr);
void gennum_writel(uint32_t data, uint32_t addr);
uint32_t gennum_readl(uint32_t addr);
int rr_load_bitstream(const void *data, int size8);
int rr_load_bitstream_from_file(const char *file_name);
#endif
/*
* User space frontend (command line) for the raw I/O interface
*
* 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.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include "rawrabbit.h"
#define DEVNAME "/dev/rawrabbit"
char *prgname;
void help(void)
{
fprintf(stderr, "%s: use like this (all numbers are hex):\n"
" %s [<vendor:device>[/<subvendor>:<subdev>]"
"[@<bus>:<devfn>]] <cmd>\n", prgname, prgname);
fprintf(stderr, " <cmd> = info\n");
fprintf(stderr, " <cmd> = irqwait\n");
fprintf(stderr, " <cmd> = irqena\n");
fprintf(stderr, " <cmd> = getdmasize\n");
fprintf(stderr, " <cmd> = getplist\n");
fprintf(stderr, " <cmd> = r[<sz>] <bar>:<addr>\n");
fprintf(stderr, " <cmd> = w[<sz>] <bar>:<addr> <val>\n");
fprintf(stderr, " <sz> = 1, 2, 4, 8 (default = 4)\n");
fprintf(stderr, " <bar> = 0, 2, 4, c (c == dma buffer)\n");
exit(1);
}
int parse_devsel(int fd, char *arg)
{
struct rr_devsel devsel;
int n;
devsel.subvendor = RR_DEVSEL_UNUSED;
devsel.bus = RR_DEVSEL_UNUSED;
if (strlen(arg) > 32) /* to prevent overflow with strtol */
return -EINVAL;
n = sscanf(arg, "%hx:%hx/%hx:%hx@%hx:%hx",
&devsel.vendor, &devsel.device,
&devsel.subvendor, &devsel.subdevice,
&devsel.bus, &devsel.devfn);
switch(n) {
case 6: /* all info */
case 4: /* id/subid but no busdev */
break;
case 2:
/* check if bus/dev is there */
n = sscanf(arg, "%hx:%hx@%hx:%hx",
&devsel.vendor, &devsel.device,
&devsel.bus, &devsel.devfn);
if (n == 4 || n == 2)
break;
/* fall through */
default:
printf("%s: can't parse \"%s\"\n", prgname, arg);
return -EINVAL;
}
/* Now try to do the change */
if (ioctl(fd, RR_DEVSEL, &devsel) < 0) {
fprintf(stderr, "%s: %s: ioctl(DEVSEL): %s\n", prgname, DEVNAME,
strerror(errno));
return -EIO;
}
return 0;
}
int do_iocmd(int fd, char *cmdname, char *addr, char *datum)
{
char rest[32];
struct rr_iocmd iocmd;
int i, ret;
unsigned bar;
__u64 d;
char cmd;
if (strlen(cmdname) >= sizeof(rest))
return -EINVAL;
if (strlen(addr) >= sizeof(rest))
return -EINVAL;
if (datum && strlen(addr) >= sizeof(rest))
return -EINVAL;
/* parse command and size */
i = sscanf(cmdname, "%c%i%s\n", &cmd, &iocmd.datasize, rest);
if (cmd != 'r' && cmd != 'w')
return -EINVAL;
if (i == 3)
return -EINVAL;
if (i == 1)
iocmd.datasize = 4;
/* parse address */
i = sscanf(addr, "%x:%x%s", &bar, &iocmd.address, rest);
if (i != 2)
return -EINVAL;
iocmd.address |= __RR_SET_BAR(bar);
if (!rr_is_valid_bar(iocmd.address))
return -EINVAL;
/* parse datum */
if (datum) {
i = sscanf(datum, "%llx%s", &d, rest);
if (i == 2)
return -EINVAL;
switch(iocmd.datasize) {
case 1:
iocmd.data8 = d;
break;
case 2:
iocmd.data16 = d;
break;
case 4:
iocmd.data32 = d;
break;
case 8:
iocmd.data64 = d;
break;
default:
return -EINVAL;
}
}
if (datum)
ret = ioctl(fd, RR_WRITE, &iocmd);
else
ret = ioctl(fd, RR_READ, &iocmd);
if (ret < 0)
return -errno;
if (!datum && !ret) {
switch(iocmd.datasize) {
case 1:
printf("0x%02x\n", (unsigned int)iocmd.data8);
break;
case 2:
printf("0x%04x\n", (unsigned int)iocmd.data16);
break;
case 4:
printf("0x%08lx\n", (unsigned long)iocmd.data32);
break;
case 8:
printf("0x%016llx\n", (unsigned long long)iocmd.data64);
break;
default:
return -EINVAL;
}
}
return ret;
}
int do_getplist(int fd)
{
uintptr_t plist[RR_PLIST_LEN];
int i, size;
size = ioctl(fd, RR_GETDMASIZE);
if (size < 0)
return -errno;
i = ioctl(fd, RR_GETPLIST, plist);
if (i < 0)
return -errno;
for (i = 0; i < size/RR_PLIST_SIZE; i++)
printf("buf 0x%08x: pfn 0x%08x, addr 0x%012llx\n",
i * RR_PLIST_SIZE, plist[i],
(unsigned long long)plist[i] << 12);
return 0;
}
int main(int argc, char **argv)
{
struct rr_devsel devsel;
int fd, ret = -EINVAL;
prgname = argv[0];
fd = open(DEVNAME, O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s: %s: %s\n", prgname, DEVNAME,
strerror(errno));
exit(1);
}
/* parse argv[1] for devsel */
if (argc > 1 && strchr(argv[1], ':')) {
ret = parse_devsel(fd, argv[1]);
if (!ret) {
argc--, argv++;
}
}
if (argc > 1 && !strcmp(argv[1], "info")) {
if (ioctl(fd, RR_DEVGET, &devsel) < 0) {
if (errno == ENODEV) {
printf("%s: not bound\n", DEVNAME);
exit(0);
}
fprintf(stderr, "%s: %s: ioctl(DEVGET): %s\n", prgname,
DEVNAME, strerror(errno));
exit(1);
}
printf("%s: bound to %04x:%04x/%04x:%04x@%04x:%04x\n", DEVNAME,
devsel.vendor, devsel.device,
devsel.subvendor, devsel.subdevice,
devsel.bus, devsel.devfn);
ret = 0;
} else if (argc > 1 && !strcmp(argv[1], "irqwait")) {
ret = ioctl(fd, RR_IRQWAIT);
if (ret < 0)
fprintf(stderr, "%s: ioctl(IRQWAIT): %s\n", argv[0],
strerror(errno));
} else if (argc > 1 && !strcmp(argv[1], "irqena")) {
ret = ioctl(fd, RR_IRQENA);
if (ret < 0) {
fprintf(stderr, "%s: ioctl(IRQENA): %s\n", argv[0],
strerror(errno));
} else {
printf("delay: %i ns\n", ret);
ret = 0;
}
} else if (argc > 1 && !strcmp(argv[1], "getdmasize")) {
ret = ioctl(fd, RR_GETDMASIZE);
printf("dmasize: %i (0x%x -- %g MB)\n", ret, ret,
ret / (double)(1024*1024));
ret = 0;
} else if (argc > 1 && !strcmp(argv[1], "getplist")) {
ret = do_getplist(fd);
} else if (argc == 3 || argc == 4) {
ret = do_iocmd(fd, argv[1], argv[2], argv[3] /* may be NULL */);
} else if (argc > 4) {
ret = -EINVAL;
}
/* subroutines return "invalid argument" to ask for help */
if (ret == -EINVAL)
help();
if (ret) {
fprintf(stderr, "%s: command returned \"%s\"\n", prgname,
strerror(errno));
exit(1);
}
return 0;
}
CFLAGS = -Wall -ggdb -I../kernel
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
ALL = rrcmd flip loadfile
all: $(ALL)
loadfile: load-main.o loader-ll.o
$(CC) $^ $(LDFLAGS) -o $@
loader-ll.o: ../kernel/loader-ll.c
$(CC) $(CFLAGS) $^ -c -o $@
clean:
rm -f $(ALL) *.o *~
/* Trivial.... */
#include <stdio.h>
int main(int argc, char **argv)
{
unsigned int c;
while ( (c = getchar()) != EOF)
putchar( 0
| ((c & 0x80) >> 7)
| ((c & 0x40) >> 5)
| ((c & 0x20) >> 3)
| ((c & 0x10) >> 1)
| ((c & 0x08) << 1)
| ((c & 0x04) << 3)
| ((c & 0x02) << 5)
| ((c & 0x01) << 7));
return 0;
}
/* Trivial frontend for the gennum loader (../kernel/loader-ll.c) */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include "rawrabbit.h"
#include "loader-ll.h"
#define DEVNAME "/dev/rawrabbit"
static char buf[64*1024*1024]; /* 64 MB binary? */
int main(int argc, char **argv)
{
int fd;
FILE *f;
int nbytes, rval;
if (argc != 2) {
fprintf(stderr, "%s: Use \"%s <firmware-file>\n",
argv[0], argv[0]);
exit(1);
}
f = fopen(argv[1], "r");
if (!f) {
fprintf(stderr, "%s: %s: %s\n",
argv[0], argv[1], strerror(errno));
exit(1);
}
fd = open(DEVNAME, O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s: %s: %s\n",
argv[0], DEVNAME, strerror(errno));
exit(1);
}
nbytes = fread(buf, 1, sizeof(buf), f);
fclose(f);
if (nbytes < 0) {
fprintf(stderr, "%s: %s: %s\n",
argv[0], argv[1], strerror(errno));
exit(1);
}
printf("Programming %i bytes of binary gateware\n", nbytes);
rval = loader_low_level(fd, NULL, buf, nbytes);
if (rval < 0) {
fprintf(stderr, "%s: load_firmware: %s\n",
argv[0], strerror(-rval));
exit(1);
}
/* We must now wait for the "done" interrupt bit */
{
unsigned long t = time(NULL) + 3;
int i, done = 0;
struct rr_iocmd iocmd = {
.datasize = 4,
.address = FCL_IRQ | __RR_SET_BAR(4),
};
if (ioctl(fd, RR_READ, &iocmd) < 0) perror("ioctl");
while (time(NULL) < t) {
if (ioctl(fd, RR_READ, &iocmd) < 0) perror("ioctl");
i = iocmd.data32;
if (i & 0x8) {
done = 1;
break;
}
if (i & 0x4) {
fprintf(stderr,"Error after %i words\n", rval);
exit(1);
}
usleep(100*1000);
}
}
return 0;
}
/*
* User space frontend (command line) for the raw I/O interface
*
* 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.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include "rawrabbit.h"
#define DEVNAME "/dev/rawrabbit"
char *prgname;
void help(void)
{
fprintf(stderr, "%s: use like this (all numbers are hex):\n"
" %s [<vendor:device>[/<subvendor>:<subdev>]"
"[@<bus>:<devfn>]] <cmd>\n", prgname, prgname);
fprintf(stderr, " <cmd> = info\n");
fprintf(stderr, " <cmd> = irqwait\n");
fprintf(stderr, " <cmd> = irqena\n");
fprintf(stderr, " <cmd> = getdmasize\n");
fprintf(stderr, " <cmd> = getplist\n");
fprintf(stderr, " <cmd> = r[<sz>] <bar>:<addr>\n");
fprintf(stderr, " <cmd> = w[<sz>] <bar>:<addr> <val>\n");
fprintf(stderr, " <sz> = 1, 2, 4, 8 (default = 4)\n");
fprintf(stderr, " <bar> = 0, 2, 4, c (c == dma buffer)\n");
exit(1);
}
int parse_devsel(int fd, char *arg)
{
struct rr_devsel devsel;
int n;
devsel.subvendor = RR_DEVSEL_UNUSED;
devsel.bus = RR_DEVSEL_UNUSED;
if (strlen(arg) > 32) /* to prevent overflow with strtol */
return -EINVAL;
n = sscanf(arg, "%hx:%hx/%hx:%hx@%hx:%hx",
&devsel.vendor, &devsel.device,
&devsel.subvendor, &devsel.subdevice,
&devsel.bus, &devsel.devfn);
switch(n) {
case 6: /* all info */
case 4: /* id/subid but no busdev */
break;
case 2:
/* check if bus/dev is there */
n = sscanf(arg, "%hx:%hx@%hx:%hx",
&devsel.vendor, &devsel.device,
&devsel.bus, &devsel.devfn);
if (n == 4 || n == 2)
break;
/* fall through */
default:
printf("%s: can't parse \"%s\"\n", prgname, arg);
return -EINVAL;
}
/* Now try to do the change */
if (ioctl(fd, RR_DEVSEL, &devsel) < 0) {
fprintf(stderr, "%s: %s: ioctl(DEVSEL): %s\n", prgname, DEVNAME,
strerror(errno));
return -EIO;
}
return 0;
}
int do_iocmd(int fd, char *cmdname, char *addr, char *datum)
{
char rest[32];
struct rr_iocmd iocmd;
int i, ret;
unsigned bar;
__u64 d;
char cmd;
if (strlen(cmdname) >= sizeof(rest))
return -EINVAL;
if (strlen(addr) >= sizeof(rest))
return -EINVAL;
if (datum && strlen(addr) >= sizeof(rest))
return -EINVAL;
/* parse command and size */
i = sscanf(cmdname, "%c%i%s\n", &cmd, &iocmd.datasize, rest);
if (cmd != 'r' && cmd != 'w')
return -EINVAL;
if (i == 3)
return -EINVAL;
if (i == 1)
iocmd.datasize = 4;
/* parse address */
i = sscanf(addr, "%x:%x%s", &bar, &iocmd.address, rest);
if (i != 2)
return -EINVAL;
iocmd.address |= __RR_SET_BAR(bar);
if (!rr_is_valid_bar(iocmd.address))
return -EINVAL;
/* parse datum */
if (datum) {
i = sscanf(datum, "%llx%s", &d, rest);
if (i == 2)
return -EINVAL;
switch(iocmd.datasize) {
case 1:
iocmd.data8 = d;
break;
case 2:
iocmd.data16 = d;
break;
case 4:
iocmd.data32 = d;
break;
case 8:
iocmd.data64 = d;
break;
default:
return -EINVAL;
}
}
if (datum)
ret = ioctl(fd, RR_WRITE, &iocmd);
else
ret = ioctl(fd, RR_READ, &iocmd);
if (ret < 0)
return -errno;
if (!datum && !ret) {
switch(iocmd.datasize) {
case 1:
printf("0x%02x\n", (unsigned int)iocmd.data8);
break;
case 2:
printf("0x%04x\n", (unsigned int)iocmd.data16);
break;
case 4:
printf("0x%08lx\n", (unsigned long)iocmd.data32);
break;
case 8:
printf("0x%016llx\n", (unsigned long long)iocmd.data64);
break;
default:
return -EINVAL;
}
}
return ret;
}
int do_getplist(int fd)
{
uintptr_t plist[RR_PLIST_LEN];
int i, size;
size = ioctl(fd, RR_GETDMASIZE);
if (size < 0)
return -errno;
i = ioctl(fd, RR_GETPLIST, plist);
if (i < 0)
return -errno;
for (i = 0; i < size/RR_PLIST_SIZE; i++)
printf("buf 0x%08x: pfn 0x%08x, addr 0x%012llx\n",
i * RR_PLIST_SIZE, plist[i],
(unsigned long long)plist[i] << 12);
return 0;
}
int main(int argc, char **argv)
{
struct rr_devsel devsel;
int fd, ret = -EINVAL;
prgname = argv[0];
fd = open(DEVNAME, O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s: %s: %s\n", prgname, DEVNAME,
strerror(errno));
exit(1);
}
/* parse argv[1] for devsel */
if (argc > 1 && strchr(argv[1], ':')) {
ret = parse_devsel(fd, argv[1]);
if (!ret) {
argc--, argv++;
}
}
if (argc > 1 && !strcmp(argv[1], "info")) {
if (ioctl(fd, RR_DEVGET, &devsel) < 0) {
if (errno == ENODEV) {
printf("%s: not bound\n", DEVNAME);
exit(0);
}
fprintf(stderr, "%s: %s: ioctl(DEVGET): %s\n", prgname,
DEVNAME, strerror(errno));
exit(1);
}
printf("%s: bound to %04x:%04x/%04x:%04x@%04x:%04x\n", DEVNAME,
devsel.vendor, devsel.device,
devsel.subvendor, devsel.subdevice,
devsel.bus, devsel.devfn);
ret = 0;
} else if (argc > 1 && !strcmp(argv[1], "irqwait")) {
ret = ioctl(fd, RR_IRQWAIT);
if (ret < 0)
fprintf(stderr, "%s: ioctl(IRQWAIT): %s\n", argv[0],
strerror(errno));
} else if (argc > 1 && !strcmp(argv[1], "irqena")) {
ret = ioctl(fd, RR_IRQENA);
if (ret < 0) {
fprintf(stderr, "%s: ioctl(IRQENA): %s\n", argv[0],
strerror(errno));
} else {
printf("delay: %i ns\n", ret);
ret = 0;
}
} else if (argc > 1 && !strcmp(argv[1], "getdmasize")) {
ret = ioctl(fd, RR_GETDMASIZE);
printf("dmasize: %i (0x%x -- %g MB)\n", ret, ret,
ret / (double)(1024*1024));
ret = 0;
} else if (argc > 1 && !strcmp(argv[1], "getplist")) {
ret = do_getplist(fd);
} else if (argc == 3 || argc == 4) {
ret = do_iocmd(fd, argv[1], argv[2], argv[3] /* may be NULL */);
} else if (argc > 4) {
ret = -EINVAL;
}
/* subroutines return "invalid argument" to ask for help */
if (ret == -EINVAL)
help();
if (ret) {
fprintf(stderr, "%s: command returned \"%s\"\n", prgname,
strerror(errno));
exit(1);
}
return 0;
}
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