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 = {