Commit 8156f0e1 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

Tom's initial version of the driver.

Done as the evil twin brother of the fine-delay driver. Still a bit incomplete
and lacking documentation. Supports only SPEC carrier so far.
parent d2fe4e12
*.o
*.ko
*~
*.mod.c
/.tmp_versions
.*cmd
modules.order
Module.symvers
\ No newline at end of file
[submodule "zio"]
path = zio
url = git://ohwr.org/misc/zio.git
[submodule "fmc-bus"]
path = fmc-bus
url = git://ohwr.org/fmc-projects/fmc-bus.git
all:
$(MAKE) -C drivers
$(MAKE) -C lib
$(MAKE) -C test
.PHONY: all clean
.PHONY: all clean modules install modules_install
.PHONY: gitmodules prereq prereq_install prereq_install_warn
clean:
$(MAKE) clean -C drivers
$(MAKE) clean -C lib
$(MAKE) clean -C test
DIRS = kernel lib
all clean modules install modules_install: gitmodules
for d in $(DIRS); do $(MAKE) -C $$d $@ || exit 1; done
@if echo $@ | grep -q install; then $(MAKE) prereq_install_warn; fi
all modules: prereq
# a hack, to prevent compiling wr-nic.ko, which won't work on older kernels
CONFIG_WR_NIC=n
export CONFIG_WR_NIC
#### The following targets are used to manage prerequisite repositories
gitmodules:
@test -d fmc-bus/doc || echo "Checking out submodules"
@test -d fmc-bus/doc || git submodule update --init
@git submodule update
# The user can override, using environment variables, all these three:
FMC_BUS ?= fmc-bus
ZIO ?= zio
SUBMOD = $(FMC_BUS) $(ZIO)
prereq:
for d in $(SUBMOD); do $(MAKE) -C $$d || exit 1; done
prereq_install_warn:
@test -f .prereq_installed || \
echo -e "\n\n\tWARNING: Consider \"make prereq_install\"\n"
prereq_install:
for d in $(SUBMOD); do $(MAKE) -C $$d modules_install || exit 1; done
touch .prereq_installed
Introduction
---
WARNING!
In electronic instrumentation and signal processing, a time to digital con-
verter (abbreviated TDC) is a device for recognizing events and providing
a digital representation of the time they occurred. At this case, the FMC
TDC outputs the time of arrival for each incoming pulse.
This is work in progress. It is buggy and can be incomplete!
This is the project to provide support of the FMC TDC board in the
Linux kernel plugged to a SPEC carrier board.
Please check doc/
The project's aim is to provide an loadable module to be used along with this
board for the latest Linux kernel versions. The driver relies on ZIO frame-
work and the FMC bus dependencies, which are other projects hosted in
OHWR.
This version is known to compile and run with kernels 3.3 onwards.
License
---
Unless otherwise indicated, all these files of this project are under GPLv2
only.
If you want a copy of this license, it is provided in the COPYING file or write
to:
Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA.
asking for a written copy of the license.
Documentation
---
For further information, you have a more updated version of the documentation
under the doc/ directory.
Just go there and execute "make". You will need "texlive" and "doxygen" packages
installed on your system in order to generate the documentation files.
Links
---
* FMC TDC HW development website: http://www.ohwr.org/projects/fmc-tdc
* FMC TDC driver support website: http://www.ohwr.org/projects/fmc-tdc-sw
* ZIO framework website: http://www.ohwr.org/projects/zio
* SPEC driver support website: http://www.ohwr.org/projects/spec-sw
* FMC bus driver support website: http://www.ohwr.org/projects/fmc-bus
Contact
---
Mailing list: http://lists.ohwr.org/sympa/info/fmc-tdc-sw
FMC TDC driver support website: http://www.ohwr.org/projects/fmc-tdc-sw
In ./doc/, fmc-tdc.in is the source file, and you can "make" to get
pdf and other formats provided you have the proper tools installed
(mainly texinfo and tex).
all: fmc-tdc.pdf doxygen-doc
fmc-tdc.pdf: fmc-tdc.tex
texi2pdf $<
doxygen-doc:
rm -rf fmc-tdc-doxygen
mkdir -p fmc-tdc-doxygen
./doxy.sh -n"FMCTDC Device Driver" -o "fmc-tdc-doxygen" ../lib/libtdc.h
clean:
rm -f *.pdf *.out *.toc *.aux *.log
rm -rf fmc-tdc-doxygen
all:
This diff is collapsed.
# doxy.sh
# Invokes doxygen modifying a pre-defined doxygen config file.
#
# Known bugs:
# - Exclamation marks (!) in any of the arguments break the script due to
# delimiter collision when invoking sed.
OUTPUT_DIR=doxygen_output
usage()
{
cat << EOF
`basename $0`: generate doxygen documentation
Usage: `basename $0` [-o<OUTPUT_DIRECTORY>] [-n<PROJECT_NAME>] input_files
options:
-h Show this message
-n Name of the project
-o Output directory for the generated files (default: $OUTPUT_DIR)
Example: `basename $0` -o"doxygen_output" -n"MyLib" mylib.c include/mylib.h
EOF
}
while getopts "hn:o:" OPTION
do
case $OPTION in
h)
usage
exit 1
;;
n)
NAME="$OPTARG"
;;
o)
OUTPUT_DIR="$OPTARG"
;;
?)
usage
exit
;;
esac
done
# set subsequent non-option arguments to $1...n
shift $((OPTIND-1))
OPTIND=1
if [[ -z "$*" ]]
then
echo "No input files given"
usage
exit 1
fi
cat $(dirname $0)/default.doxycfg | \
sed "s!__MAGIC_PROJECT_NAME__!$NAME!" | \
sed "s!__MAGIC_OUTPUT_DIRECTORY__!$OUTPUT_DIR!" | \
sed "s!__MAGIC_INPUT__!$*!" | \
doxygen -
\documentclass[a4paper,11pt]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{lmodern}
\usepackage{graphicx}
\usepackage[hidelinks]{hyperref}
\title{User manual}
\author{FMC TDC Linux kernel support}
\date{}
\begin{document}
\maketitle
\tableofcontents
\newpage
\section{Introduction}
%\begin{figure}
\begin{center}
\includegraphics[scale=0.5]{img/fmc-tdc.jpg}
% \label{fig:fmc-tdc}
% \caption{FMC TDC board}
\end{center}
%\end{figure}
$\newline$
In electronic instrumentation and signal processing, a time to digital converter (abbreviated TDC) is a device for recognizing events and providing a digital representation of the time they occurred. At this case, the FMC TDC outputs the time of arrival for each incoming pulse.
This is the project to provide support of the FMC TDC board in the Linux kernel plugged to a SPEC carrier board.
The aim is to provide an loadable module to be used along with this board for
the latest Linux kernel versions. The driver relies on ZIO framework and the FMC
bus dependencies, which are other projects hosted in OHWR.
\section{Installation}
\subsection{Dependencies}
To compile properly this driver, you should have downloaded the following repositories (branch master). For that purpose, you should have installed \textit{git} software.
\begin{itemize}
\item \href{http://www.ohwr.org/projects/zio}{ZIO} framework
\item \href{http://www.ohwr.org/projects/spec-sw}{SPEC} driver
\item \href{http://www.ohwr.org/projects/fmc-bus}{FMC bus} support
\end{itemize}
\subsection{Download the sources}
To download the sources, you should execute the following command:
\indent\indent\texttt{\$ git clone git://ohwr.org/fmc-projects/fmc-tdc/fmc-tdc-sw.git}
\subsection{Compile}
To compile the sources, you should execute the following command:
\indent\indent\texttt{\$ make}
\subsection{Load the drivers}
To load the drivers, we should load the dependencies first:
\indent\indent\texttt{\# insmod <path\_zio>/zio.ko} \\
\indent\indent\texttt{\# insmod <path\_fmc-bus>/kernel/fmc.ko} \\
\indent\indent\texttt{\# insmod <path\_spec-sw>/kernel/spec.ko} \\
Once the dependencies are loaded, we load the drivers:
\indent\indent\texttt{\# insmod <path\_fmc-tdc-sw>/drivers/spec-tdc.ko <parameters>}
\subsubsection{FMC TDC driver parameters}
The spec-tdc.ko driver has several parameters, you can discover them executing: \texttt{modinfo ./spec-tdc.ko}.
\begin{itemize}
\item lun: Logical unit number.
\item bus: PCI bus number where the SPEC+TDC is plugged on.
\item slot: PCI slot where the SPEC+TDC is plugged on.
\end{itemize}
To know which are the bus and slot numbers, one can use the command \textit{lspci}:
\indent\indent\texttt{\$ lspci} \\
\indent\indent\texttt{[...]} \\
\indent\indent\texttt{00:04.0 Non-VGA unclassified device: CERN/ECP/EDU Device 018d (rev 03)} \\
In the previous output, the parameters will be: bus=0 slot=4.
\section{libtdc, an user-space library}
\subsection{Introduction}
To facilitate the task of managing the FMC TDC devices, it is provided an C/C++
user-space library. It is recommended to use it instead of accesing directly to
the driver.
\subsection{API}
\textbf{struct tdc\_board *tdc\_open(int lun);} \\
Open the selected device.\\
\textbf{int tdc\_close(struct tdc\_board *b);} \\
Close the device. \\
\textbf{struct tdc\_time *tdc\_zalloc(unsigned int events);} \\
Allocate a struct tdc\_time buffer with \textit{events} number of events. \\
\textbf{void tdc\_free(struct tdc\_time *buffer);} \\
Free the previously allocated struct tdc\_time buffer. \\
\textbf{int tdc\_start\_acquisition(struct tdc\_board *b);} \\
Start acquisition.\\
\textbf{int tdc\_stop\_acquisition(struct tdc\_board *b);} \\
Stop acquisition.\\
\textbf{int tdc\_set\_host\_utc\_time(struct tdc\_board *b);} \\
Set FMC TDC time reference to local host UTC time. \\
\textbf{int tdc\_set\_utc\_time(struct tdc\_board *b, uint32\_t utc);} \\
Set FMC TDC time reference to a given value.\\
\textbf{int tdc\_get\_utc\_time(struct tdc\_board *b, uint32\_t *utc);} \\
Get FMC TDC time reference value. \\
\textbf{int tdc\_set\_dac\_word(struct tdc\_board *b, uint32\_t dw);} \\
Set FMC TDC DAC with a given value. \\
\textbf{int tdc\_get\_dac\_word(struct tdc\_board *b, uint32\_t *dw);} \\
Get FMC TDC DAC value. \\
\textbf{int tdc\_set\_time\_threshold(struct tdc\_board *b, uint32\_t thres);} \\
Set time threshold (time between IRQ where the event number acquired are less than the timestamp
threshold). In seconds. \\
\textbf{int tdc\_get\_time\_threshold(struct tdc\_board *b, uint32\_t *thres);} \\
Get time threshold.\\
\textbf{int tdc\_set\_timestamp\_threshold(struct tdc\_board *b, uint32\_t thres);} \\
Set timestamp threshold.\\
\textbf{int tdc\_get\_timestamp\_threshold(struct tdc\_board *b, uint32\_t *thres);} \\
Get timestamp threshold.\\
\textbf{int tdc\_set\_channels\_term(struct tdc\_board *b, uint32\_t config);} \\
Set channel termination resistor (50 Ohms). This is not an enable of the channel. \\
\textbf{int tdc\_get\_channels\_term(struct tdc\_board *b, uint32\_t *config);} \\
Get channel termination setup. \\
\textbf{int tdc\_activate\_channels(struct tdc\_board *b);} \\
Activate/enable all the channels to acquire data. This should be done before call tdc\_start\_acquisition(). \\
\textbf{int tdc\_deactivate\_channels(struct tdc\_board *b);} \\
Deactivate/disable all channels. \\
\textbf{int tdc\_get\_circular\_buffer\_pointer(struct tdc\_board *b, uint32\_t *ptr);} \\
Get circular buffer pointer value.\\
\textbf{int tdc\_clear\_dacapo\_flag(struct tdc\_board *b);} \\
Clear Dacapo flag from the circular buffer pointer value.\\
\textbf{int tdc\_read(struct tdc\_board *b, int chan, struct tdc\_time *t,
int n, int flags);} \\
Read \textit{n} events. The allowed flags are 0 (blocking read) or O\_NONBLOCK (non-blocking read).
\section{Test program}
\subsection{Introduction}
The test program is used to check the proper behavior of the board in case of failure or to check if there is a bug in the driver or in the library.
The test program has an CLI interface due to some limitations when accessing remotely to the machine. It is designed to allow the execution of the program under SSH.
\subsection{Dependencies}
\begin{itemize}
\item Python 2.7 or higher.
\end{itemize}
First of all, you should compile the shared object library:
\indent\indent\texttt{\$ cd <path\_fmc-tdc-sw>/lib} \\
\indent\indent\texttt{\$ make libtdc.so}
\subsection{How to use it}
To execute it:
\indent\indent\texttt{\$ cd <path\_fmc-tdc-sw>/test} \\
\indent\indent\texttt{\$ sudo ./test-fmctdc.py} \\
If you want the help, you can execute:\\
\indent\indent\texttt{\$ ./test-fmctdc.py -h} \\
\section{Contact}
\href{http://www.ohwr.org/projects/fmc-tdc-sw}{FMC TDC Linux kernel support website} on \href{http://www.ohwr.org/}{OHWR}:
\indent\indent\texttt{\url{http://www.ohwr.org/projects/fmc-tdc-sw}}\\
\href{http://www.ohwr.org/mailing_list/show?project_id=fmc-tdc-sw}{Mailing list} on \href{http://www.ohwr.org/}{OHWR}:
\indent\indent\texttt{\url{http://www.ohwr.org/mailing_list/show?project_id=fmc-tdc-sw}}
\end{document}
LINUX ?= /lib/modules/$(shell uname -r)/build
ZIO ?= $(HOME)/zio
SPEC_SW ?= $(HOME)/spec-sw
KBUILD_EXTRA_SYMBOLS := $(ZIO)/Module.symvers $(SPEC_SW)/kernel/Module.symvers $(SPEC_SW)/fmc-bus/kernel/Module.symvers
ccflags-y = -I$(ZIO)/include -I$(SPEC_SW)/kernel -I$M -I$(SPEC_SW)/kernel -I$(SPEC_SW)/fmc-bus/kernel/include
#ccflags-y += -DDEBUG
subdirs-ccflags-y = $(ccflags-y)
obj-m := spec-tdc.o
spec-tdc-objs = tdc-core.o tdc-zio.o tdc-fmc.o tdc-acam.o tdc-dma.o
all: modules
modules_install clean modules:
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) $@
/*
* ACAM support for tdc driver
*
* Copyright (C) 2012 CERN (http://www.cern.ch)
* Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
* Author: Miguel Angel Gomez Sexto <magomez@igalia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <asm/io.h>
#include "tdc.h"
#include "hw/tdc_regs.h"
void tdc_acam_reset(struct spec_tdc *tdc)
{
writel(TDC_CTRL_RESET_ACAM, tdc->base + TDC_CTRL_REG);
}
static void __tdc_acam_do_load_config(struct spec_tdc *tdc)
{
writel(TDC_CTRL_LOAD_ACAM_CFG, tdc->base + TDC_CTRL_REG);
}
static void __tdc_acam_do_read_config(struct spec_tdc *tdc)
{
writel(TDC_CTRL_READ_ACAM_CFG, tdc->base + TDC_CTRL_REG);
}
u32 tdc_acam_status(struct spec_tdc *tdc)
{
/* Send the command to read acam status */
writel(TDC_CTRL_READ_ACAM_STAT, tdc->base + TDC_CTRL_REG);
return readl(tdc->base + TDC_ACAM_RDBACK_REG_12);
}
u32 tdc_acam_read_ififo1(struct spec_tdc *tdc)
{
/* Send the command to read acam status */
writel(TDC_CTRL_READ_ACAM_IFIFO1, tdc->base + TDC_CTRL_REG);
return readl(tdc->base + TDC_ACAM_RDBACK_REG_8);
}
u32 tdc_acam_read_ififo2(struct spec_tdc *tdc)
{
/* Send the command to read acam status */
writel(TDC_CTRL_READ_ACAM_IFIFO2, tdc->base + TDC_CTRL_REG);
return readl(tdc->base + TDC_ACAM_RDBACK_REG_9);
}
u32 tdc_acam_read_start01(struct spec_tdc *tdc)
{
/* Send the command to read acam status */
writel(TDC_CTRL_READ_ACAM_START01_R, tdc->base + TDC_CTRL_REG);
return readl(tdc->base + TDC_ACAM_RDBACK_REG_10);
}
int tdc_acam_load_config(struct spec_tdc *tdc, struct tdc_acam_cfg *cfg)
{
/* Write the configuration parameters to the registers */
writel(cfg->edge_config, tdc->base + TDC_ACAM_CFG_REG_0);
writel(cfg->channel_adj, tdc->base + TDC_ACAM_CFG_REG_1);
writel(cfg->mode_enable, tdc->base + TDC_ACAM_CFG_REG_2);
writel(cfg->resolution, tdc->base + TDC_ACAM_CFG_REG_3);
writel(cfg->start_timer_set, tdc->base + TDC_ACAM_CFG_REG_4);
writel(cfg->start_retrigger, tdc->base + TDC_ACAM_CFG_REG_5);
writel(cfg->lf_flags_level, tdc->base + TDC_ACAM_CFG_REG_6);
writel(cfg->pll, tdc->base + TDC_ACAM_CFG_REG_7);
writel(cfg->err_flag_cfg, tdc->base + TDC_ACAM_CFG_REG_11);
writel(cfg->int_flag_cfg, tdc->base + TDC_ACAM_CFG_REG_12);
writel(cfg->ctrl_16_bit_mode, tdc->base + TDC_ACAM_CFG_REG_14);
/* Send the load command to the firmware */
__tdc_acam_do_load_config(tdc);
mdelay(100);
return 0;
}
int tdc_acam_get_config(struct spec_tdc *tdc, struct tdc_acam_cfg *cfg)
{
/* Send read config command to retrieve the data to the registers */
__tdc_acam_do_read_config(tdc);
/* Read the configuration values from the read-back registers */
cfg->edge_config = readl(tdc->base + TDC_ACAM_RDBACK_REG_0);
cfg->channel_adj = readl(tdc->base + TDC_ACAM_RDBACK_REG_1);
cfg->mode_enable = readl(tdc->base + TDC_ACAM_RDBACK_REG_2);
cfg->resolution = readl(tdc->base + TDC_ACAM_RDBACK_REG_3);
cfg->start_timer_set = readl(tdc->base + TDC_ACAM_RDBACK_REG_4);
cfg->start_retrigger = readl(tdc->base + TDC_ACAM_RDBACK_REG_5);
cfg->lf_flags_level = readl(tdc->base + TDC_ACAM_RDBACK_REG_6);
cfg->pll = readl(tdc->base + TDC_ACAM_RDBACK_REG_7);
cfg->err_flag_cfg = readl(tdc->base + TDC_ACAM_RDBACK_REG_11);
cfg->int_flag_cfg = readl(tdc->base + TDC_ACAM_RDBACK_REG_12);
cfg->ctrl_16_bit_mode = readl(tdc->base + TDC_ACAM_RDBACK_REG_14);
return 0;
}
int tdc_acam_set_default_config(struct spec_tdc *tdc)
{
struct tdc_acam_cfg cfg;
/* Default setup as indicated in the datasheet */
cfg.edge_config = 0x01F0FC81;
cfg.channel_adj = 0x0;
cfg.mode_enable = 0xE02;
cfg.resolution = 0x0;
cfg.start_timer_set = 0x0200000F;
cfg.start_retrigger = 0x07D0;
cfg.lf_flags_level = 0x03;
cfg.pll = 0x001FEA;
cfg.err_flag_cfg = 0x00FF0000;
cfg.int_flag_cfg = 0x04000000;
cfg.ctrl_16_bit_mode = 0x0;
return tdc_acam_load_config(tdc, &cfg);
}
/*
* core tdc driver
*
* Copyright (C) 2012 CERN (http://www.cern.ch)
* Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
* Author: Miguel Angel Gomez Sexto <magomez@igalia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/io.h>
#include "spec.h"
#include "tdc.h"
#include "hw/tdc_regs.h"
int lun[MAX_DEVICES];
unsigned int nlun;
module_param_array(lun, int, &nlun, S_IRUGO);
int bus[MAX_DEVICES];
unsigned int nbus;
module_param_array(bus, int, &nbus, S_IRUGO);
int slot[MAX_DEVICES];
unsigned int nslot;
module_param_array(slot, int, &nslot, S_IRUGO);
char *gateware = "eva_tdc_for_v2.bin";
module_param(gateware, charp, S_IRUGO);
void tdc_set_utc_time(struct spec_tdc *tdc, u32 value)
{
writel(value, tdc->base + TDC_START_UTC_R);
writel(TDC_CTRL_LOAD_UTC, tdc->base + TDC_CTRL_REG);
}
void tdc_set_local_utc_time(struct spec_tdc *tdc)
{
struct timeval utc_time;
do_gettimeofday(&utc_time);
tdc_set_utc_time(tdc, utc_time.tv_sec);
}
u32 tdc_get_current_utc_time(struct spec_tdc *tdc)
{
return readl(tdc->base + TDC_CURRENT_UTC_R);
}
void tdc_set_irq_tstamp_thresh(struct spec_tdc *tdc, u32 val)
{
writel(val, tdc->base + TDC_IRQ_TSTAMP_THRESH_R);
}
u32 tdc_get_irq_tstamp_thresh(struct spec_tdc *tdc)
{
return readl(tdc->base + TDC_IRQ_TSTAMP_THRESH_R);
}
void tdc_set_irq_time_thresh(struct spec_tdc *tdc, u32 val)
{
writel(val, tdc->base + TDC_IRQ_TIME_THRESH_R);
}
u32 tdc_get_irq_time_thresh(struct spec_tdc *tdc)
{
return readl(tdc->base + TDC_IRQ_TIME_THRESH_R);
}
void tdc_set_dac_word(struct spec_tdc *tdc, u32 val)
{
writel(val, tdc->base + TDC_DAC_WORD_R);
writel(TDC_CTRL_CONFIG_DAC, tdc->base + TDC_CTRL_REG);
}
u32 tdc_get_dac_word(struct spec_tdc *tdc)
{
return readl(tdc->base + TDC_DAC_WORD_R);
}
void tdc_clear_da_capo_flag(struct spec_tdc *tdc)
{
writel(TDC_CTRL_CLEAR_DACAPO_FLAG, tdc->base + TDC_CTRL_REG);
}
int tdc_activate_acquisition(struct spec_tdc *tdc)
{
u32 acam_status_test;
/* Before activate the adquisition is required to reset the ACAM chip */
tdc_acam_reset(tdc);
acam_status_test = tdc_acam_status(tdc)-0xC4000800;
if (acam_status_test != 0) {
dev_err(&tdc->fmc->dev, "ACAM status: not ready! 0x%x\n", acam_status_test);
return -EBUSY;
}
writel(TDC_CTRL_EN_ACQ, tdc->base + TDC_CTRL_REG);
return 0;
}
void tdc_deactivate_acquisition(struct spec_tdc *tdc)
{
writel(TDC_CTRL_DIS_ACQ, tdc->base + TDC_CTRL_REG);
}
void tdc_set_input_enable(struct spec_tdc *tdc, u32 value)
{
writel(value, tdc->base + TDC_INPUT_ENABLE_R);
}
u32 tdc_get_input_enable(struct spec_tdc *tdc)
{
return readl(tdc->base + TDC_INPUT_ENABLE_R);
}
inline u32 tdc_get_circular_buffer_wr_pointer(struct spec_tdc *tdc)
{
return readl(tdc->base + TDC_CIRCULAR_BUF_PTR_R);
}
static int check_parameters(void)
{
if (nlun < 0 || nlun > MAX_DEVICES) {
pr_err("Invalid number of devices (%d)", nlun);
return -EINVAL;
}
if ((nlun != nbus) || (nlun != nslot)) {
pr_err("Parameter mismatch: %d luns, %d buses and %d slots\n",
nlun, nbus, nslot);
return -EINVAL;
}
if (nlun == 0) {
pr_err("No LUNs provided. The driver won't match any device");
}
return 0;
}
static int tdc_init(void)
{
int err;
err = check_parameters();
if (err < 0)
return err;
err = tdc_zio_init();
if (err < 0)
return err;
err = tdc_fmc_init();
if (err < 0) {
tdc_zio_exit();
return err;
}
return 0;
}
static void tdc_exit(void)
{
tdc_fmc_exit();
tdc_zio_exit();
}
module_init(tdc_init);
module_exit(tdc_exit);
MODULE_LICENSE("GPL");
/*
* DMA support for tdc driver
*
* Copyright (C) 2012 CERN (http://www.cern.ch)
* Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
* Author: Miguel Angel Gomez Sexto <magomez@igalia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation.
*/
#include <asm/io.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include "spec.h"
#include "tdc.h"
#include "hw/tdc_regs.h"
/*
* tdc_dma_setup -- Setup DMA operation
*
* @tdc: pointer to spec_tdc struct of the device
* @src: address to copy the data from (in TDC board)
* @dst: address to copy the data to (in host computer)
* @size: size of the DMA transfer (in bytes)
*
*/
int tdc_dma_setup(struct spec_tdc *tdc, unsigned long src, unsigned long dst, int size)
{
dma_addr_t mapping;
mapping = dma_map_single(&tdc->spec->pdev->dev, (char *)dst, size