Commit ac355f2a authored by Federico Vaga's avatar Federico Vaga

Merge branch 'release/v5.0.0.rc5' into master

parents ec1583c6 b70f2816
......@@ -6,6 +6,7 @@
*.order
*.symvers
*.mod.c
*.mod
*.o.d
*.tmp
*.lof
......
[submodule "zio"]
path = zio
url = https://ohwr.org/project/zio.git
[submodule "fmc-bus"]
path = fmc-bus
url = https://ohwr.org/project/fmc-bus.git
# include parent_common.mk for buildsystem's defines
# use absolute path for REPO_PARENT
CURDIR:=$(shell /bin/pwd)
REPO_PARENT ?= $(CURDIR)/..
-include $(REPO_PARENT)/parent_common.mk
all: kernel tools
FMC_BUS ?= fmc-bus
ZIO ?= zio
SVEC_SW ?= svec-sw
VMEBUS ?= $(REPO_PARENT)/vmebridge
# Use the absolute path so it can be used by submodule
# FMC_BUS_ABS and ZIO_ABS has to be absolut path,
# due to beeing passed to the Kbuild
FMC_BUS_ABS ?= $(abspath $(FMC_BUS) )
ZIO_ABS ?= $(abspath $(ZIO) )
SVEC_SW_ABS ?= $(abspath $(SVEC_SW) )
VMEBUS_ABS ?= $(abspath $(VMEBUS) )
export FMC_BUS_ABS
export ZIO_ABS
export SVEC_SW_ABS
export VMEBUS_ABS
DIRS = $(FMC_BUS_ABS) $(ZIO_ABS) kernel tools
kernel: $(FMC_BUS_ABS) $(ZIO_ABS)
DIRS =kernel tools
.PHONY: all clean modules install modules_install $(DIRS)
.PHONY: gitmodules prereq_install prereq_install_warn
install modules_install: prereq_install_warn
install modules_install:
all clean modules install modules_install: $(DIRS)
......@@ -43,25 +20,3 @@ modules_install: TARGET = modules_install
$(DIRS):
$(MAKE) -C $@ $(TARGET)
SUBMOD = $(FMC_BUS_ABS) $(ZIO_ABS)
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
$(FMC_BUS_ABS): fmc-bus-init_repo
$(ZIO_ABS): zio-init_repo
# init submodule if missing
fmc-bus-init_repo:
@test -d $(FMC_BUS_ABS)/doc || ( echo "Checking out submodule $(FMC_BUS_ABS)" && git submodule update --init $(FMC_BUS_ABS) )
# init submodule if missing
zio-init_repo:
@test -d $(ZIO_ABS)/doc || ( echo "Checking out submodule $(ZIO_ABS)" && git submodule update --init $(ZIO_ABS) )
build/
\ No newline at end of file
TOP_DIR ?= ..
DRIVER_NAME := fmc-adc-100m14b4ch
VERSION := $(shell git describe --abbrev=0)
DIR_NAME := $(DRIVER_NAME)-$(VERSION)
KEEP_TEMP ?= n
BUILD ?= $(abspath build)
BUILD_DKMS := $(BUILD)/dkms
BUILD_DKMSSOURCE := $(BUILD_DKMS)/source
BUILD_DKMSTREE := $(BUILD_DKMS)/tree
DKMS_OPT := --dkmstree $(BUILD_DKMSTREE) -m $(DRIVER_NAME)/$(VERSION)
all: kernel
kernel: dkms-tar dkms-rpm
dkms-tree:
@mkdir -p $(BUILD_DKMSSOURCE)
@mkdir -p $(BUILD_DKMSTREE)
dkms-src: dkms-tree
$(eval $@_dir := $(BUILD_DKMSSOURCE)/$(DRIVER_NAME)-$(VERSION))
$(eval $@_src := $(shell git ls-tree -r --name-only HEAD $(TOP_DIR)/kernel/))
@mkdir -p $($@_dir)
@cp $(TOP_DIR)/distribution/dkms.conf $($@_dir)
@cp $(TOP_DIR)/distribution/fmc-adc-100m14b4ch-dkms-mkrpm.spec $($@_dir)
@cp $($@_src) $($@_dir)
@cp $(TOP_DIR)/LICENSES/GPL-2.0-or-later.txt $($@_dir)/LICENSE
@sed -r -i -e "s/^GIT_VERSION\s=\s.*/GIT_VERSION = $(VERSION)/" $($@_dir)/Makefile
@sed -r -i -e "s/@PKGVER@/$(VERSION)/g" -e "s/@PKGNAME@/$(DRIVER_NAME)/g" $($@_dir)/dkms.conf
dkms-add: dkms-src
@dkms add $(DKMS_OPT) --sourcetree $(BUILD_DKMSSOURCE)
dkms-tar: dkms-add
@dkms mktarball $(DKMS_OPT) --source-only
dkms-rpm: dkms-add
@dkms mkrpm $(DKMS_OPT) --source-only
clean:
@rm -rf $(BUILD)
.PHONY: dkmstree dkms-add kernel-dkms-tar
PACKAGE_NAME="@PKGNAME@"
PACKAGE_VERSION="@PKGVER@"
CLEAN="make KVERSION=$kernelver DKMSTREE=$dkms_tree DKMS=1 clean"
MAKE[0]="make KVERSION=$kernelver DKMSTREE=$dkms_tree DKMS=1"
BUILT_MODULE_NAME[0]="@PKGNAME@"
DEST_MODULE_LOCATION[0]="/updates"
AUTOINSTALL="yes"
BUILD_DEPENDS[0]="zio"
\ No newline at end of file
# Mainline copied from the template, added requirements
%{?!module_name: %{error: You did not specify a module name (%%module_name)}}
%{?!version: %{error: You did not specify a module version (%%version)}}
%{?!kernel_versions: %{error: You did not specify kernel versions (%%kernel_version)}}
%{?!packager: %define packager DKMS <dkms-devel@lists.us.dell.com>}
%{?!license: %define license Unknown}
%{?!_dkmsdir: %define _dkmsdir /var/lib/dkms}
%{?!_srcdir: %define _srcdir %_prefix/src}
%{?!_datarootdir: %define _datarootdir %{_datadir}}
Summary: %{module_name} %{version} dkms package
Name: %{module_name}
Version: %{version}
License: %license
Release: 1dkms
BuildArch: noarch
Group: System/Kernel
Requires: dkms >= 1.95, zio >= 1.4
BuildRequires: dkms
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root/
%description
Kernel modules for %{module_name} %{version} in a DKMS wrapper.
%prep
if [ "%mktarball_line" != "none" ]; then
/usr/sbin/dkms mktarball -m %module_name -v %version %mktarball_line --archive `basename %{module_name}-%{version}.dkms.tar.gz`
cp -af %{_dkmsdir}/%{module_name}/%{version}/tarball/`basename %{module_name}-%{version}.dkms.tar.gz` %{module_name}-%{version}.dkms.tar.gz
fi
%install
if [ "$RPM_BUILD_ROOT" != "/" ]; then
rm -rf $RPM_BUILD_ROOT
fi
mkdir -p $RPM_BUILD_ROOT/%{_srcdir}
mkdir -p $RPM_BUILD_ROOT/%{_datarootdir}/%{module_name}
if [ -d %{_sourcedir}/%{module_name}-%{version} ]; then
cp -Lpr %{_sourcedir}/%{module_name}-%{version} $RPM_BUILD_ROOT/%{_srcdir}
fi
if [ -f %{module_name}-%{version}.dkms.tar.gz ]; then
install -m 644 %{module_name}-%{version}.dkms.tar.gz $RPM_BUILD_ROOT/%{_datarootdir}/%{module_name}
fi
if [ -f %{_sourcedir}/common.postinst ]; then
install -m 755 %{_sourcedir}/common.postinst $RPM_BUILD_ROOT/%{_datarootdir}/%{module_name}/postinst
fi
%clean
if [ "$RPM_BUILD_ROOT" != "/" ]; then
rm -rf $RPM_BUILD_ROOT
fi
%post
for POSTINST in %{_prefix}/lib/dkms/common.postinst %{_datarootdir}/%{module_name}/postinst; do
if [ -f $POSTINST ]; then
$POSTINST %{module_name} %{version} %{_datarootdir}/%{module_name}
exit $?
fi
echo "WARNING: $POSTINST does not exist."
done
echo -e "ERROR: DKMS version is too old and %{module_name} was not"
echo -e "built with legacy DKMS support."
echo -e "You must either rebuild %{module_name} with legacy postinst"
echo -e "support or upgrade DKMS to a more current version."
exit 1
%preun
echo -e
echo -e "Uninstall of %{module_name} module (version %{version}) beginning:"
dkms remove -m %{module_name} -v %{version} --all --rpm_safe_upgrade
exit 0
%files
%defattr(-,root,root)
%{_srcdir}
%{_datarootdir}/%{module_name}/
%changelog
* %(date "+%a %b %d %Y") %packager %{version}-%{release}
- Automatic build by DKMS
Subproject commit eb86efcf4e19a31a25471c4ddf3fd9fef8df02ec
CONFIG_FMC_ADC_SVEC ?= CONFIG_VME
VMEBUS_EXTRA_SYMBOLS-$(CONFIG_FMC_ADC_SVEC) := $(VMEBUS_ABS)/driver/Module.symvers
KBUILD_EXTRA_SYMBOLS := \
$(ZIO_ABS)/Module.symvers \
$(FMC_BUS_ABS)/kernel/Module.symvers \
$(VMEBUS_EXTRA_SYMBOLS-y)
ZIO_VERSION = $(shell cd $(ZIO_ABS); git describe --always --dirty --long --tags)
VERSION = $(shell cd $(src); git describe --always --dirty --long --tags)
KBUILD_EXTRA_SYMBOLS += $(ZIO_EXTRA_SYMBOLS-y)
KBUILD_EXTRA_SYMBOLS += $(FMC_EXTRA_SYMBOLS-y)
KBUILD_EXTRA_SYMBOLS += $(VMEBUS_EXTRA_SYMBOLS-y)
ccflags-y = -DVERSION=\"$(VERSION)\"
ccflags-y += -I$(src)
ccflags-y += -I$(ZIO_ABS)/include
ccflags-y += -I$(FMC_ABS)/include
ccflags-y += -I$(VMEBUS_ABS)/driver
ccflags-$(CONFIG_FMC_ADC_SVEC) += -I$(VMEBUS_ABS)/include
ccflags-$(CONFIG_FMC_ADC_DEBUG) += -DDEBUG
# Extract ZIO minimum compatible version
ccflags-y += -D__ZIO_MIN_MAJOR_VERSION=$(shell echo $(ZIO_VERSION) | cut -d '-' -f 1 | cut -d '.' -f 1 | tr -d 'v'; )
ccflags-y += -D__ZIO_MIN_MINOR_VERSION=$(shell echo $(ZIO_VERSION) | cut -d '-' -f 1 | cut -d '.' -f 2; )
# add versions of supermodule. It is useful when fine-delay-sw is included as sub-module
# of a bigger project that we want to track
......@@ -14,45 +30,26 @@ SUBMODULE_VERSIONS-y += MODULE_INFO(version_$(CONFIG_SUPER_REPO),\"$(CONFIG_SUPE
endif
endif
# add versions of used submodules
SUBMODULE_VERSIONS-y += MODULE_INFO(version_fmc_bus,\"$(FMC_BUS_VERSION)\");
SUBMODULE_VERSIONS-y += MODULE_INFO(version_zio,\"$(ZIO_VERSION)\");
ccflags-y += -DADDITIONAL_VERSIONS="$(SUBMODULE_VERSIONS-y)"
# The library includes <sdb.h>, so point -I directtly there
# include our header before to avoid conflicts with the kernel
LINUXINCLUDE := -I$(FMC_BUS_ABS)/kernel/include $(LINUXINCLUDE)
LINUXINCLUDE := -I$(FMC_ABS)/include $(LINUXINCLUDE)
ccflags-y += -DGIT_VERSION=\"$(GIT_VERSION)\" \
-I$(ZIO_ABS)/include \
-I$(src)
ccflags-y += -DADDITIONAL_VERSIONS="$(SUBMODULE_VERSIONS-y)"
ccflags-$(CONFIG_FMC_ADC_SVEC) += -I$(SVEC_SW_ABS)/kernel
ccflags-$(CONFIG_FMC_ADC_SVEC) += -I$(VMEBUS_ABS)/include
ccflags-$(CONFIG_FMC_ADC_DEBUG) += -DDEBUG
ccflags-$(CONFIG_FMC_ADC_SVEC) += -DCONFIG_FMC_ADC_SVEC
subdirs-ccflags-y = $(ccflags-y)
# Extract ZIO minimum compatible version
ccflags-y += -D__ZIO_MIN_MAJOR_VERSION=$(shell echo $(ZIO_VERSION) | cut -d '-' -f 2 | cut -d '.' -f 1; )
ccflags-y += -D__ZIO_MIN_MINOR_VERSION=$(shell echo $(ZIO_VERSION) | cut -d '-' -f 2 | cut -d '.' -f 2; )
obj-m := fmc-adc-100m14b4ch.o
obj-m += fmc-adc-100m14b4ch-spec.o
subdirs-ccflags-y = $(ccflags-y)
fmc-adc-100m14b4ch-y = fa-core.o
fmc-adc-100m14b4ch-y += fa-zio-drv.o
fmc-adc-100m14b4ch-y += fa-calibration.o
fmc-adc-100m14b4ch-y += fa-regtable.o
fmc-adc-100m14b4ch-y += fa-zio-trg.o
fmc-adc-100m14b4ch-y += fa-irq.o
fmc-adc-100m14b4ch-y += fa-debug.o
fmc-adc-100m14b4ch-y += fa-dma.o
fmc-adc-100m14b4ch-y += spi.o
obj-m := fmc-adc-100m14b.o
fmc-adc-100m14b-y = fa-core.o
fmc-adc-100m14b-y += fa-zio-drv.o
fmc-adc-100m14b-y += fa-calibration.o
fmc-adc-100m14b-y += fa-regtable.o
fmc-adc-100m14b-y += fa-zio-trg.o
fmc-adc-100m14b-y += fa-irq.o
fmc-adc-100m14b-y += fa-debug.o
fmc-adc-100m14b-y += onewire.o
fmc-adc-100m14b-y += spi.o
fmc-adc-100m14b-y += fmc-util.o
fmc-adc-100m14b-y += fa-spec-core.o
fmc-adc-100m14b-y += fa-spec-regtable.o
fmc-adc-100m14b-y += fa-spec-dma.o
fmc-adc-100m14b-y += fa-spec-irq.o
fmc-adc-100m14b-$(CONFIG_FMC_ADC_SVEC) += fa-svec-core.o
fmc-adc-100m14b-$(CONFIG_FMC_ADC_SVEC) += fa-svec-regtable.o
fmc-adc-100m14b-$(CONFIG_FMC_ADC_SVEC) += fa-svec-dma.o
fmc-adc-100m14b4ch-spec-objs := fmc-adc-100m14b4ch-spec-core.o
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2019 CERN
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
# use absolute path for REPO_PARENT
CURDIR:=$(shell /bin/pwd)
REPO_PARENT ?= $(CURDIR)/../..
-include $(REPO_PARENT)/parent_common.mk
CPPCHECK ?= cppcheck
DKMS ?= 0
CURDIR := $(shell /bin/pwd)
KVERSION ?= $(shell uname -r)
LINUX ?= /lib/modules/$(KVERSION)/build
FMC_BUS ?= ../fmc-bus
ZIO ?= ../zio
SVEC_SW ?= ../svec-sw
VMEBUS ?= $(REPO_PARENT)/../vmebridge
# FMC_BUS_ABS and ZIO_ABS has to be absolut path,
# due to beeing passed to the Kbuild
FMC_BUS_ABS ?= $(abspath $(FMC_BUS) )
ZIO_ABS ?= $(abspath $(ZIO) )
SVEC_SW_ABS ?= $(abspath $(SVEC_SW) )
VMEBUS_ABS ?= $(abspath $(VMEBUS) )
GIT_VERSION = $(shell git describe --always --dirty --long --tags)
export GIT_VERSION
ifdef REPO_PARENT
ZIO ?= $(REPO_PARENT)/fmc/zio
FMC ?= $(REPO_PARENT)/fmc-sw
VMEBUS ?= $(REPO_PARENT)/vmebridge-ng
endif
ifeq ($(DKMS), 1)
# Take last installed version (if installed using RPM it should be OK)
ZIO_VERSION ?= $(shell basename $(shell ls -d $(DKMSTREE)/zio/* | grep -E "\/[0-9]+\.[0-9]+\.[0-9]+" | sort -V | tail -n 1))
ZIO_ABS ?= $(DKMSTREE)/zio/$(ZIO_VERSION)/source
ZIO_EXTRA_SYMBOLS-y = $(DKMSTREE)/zio/kernel-$(KVERSION)-$(shell uname -p)/module/Module.symvers
else
FMC_BUS_VERSION ?= $(shell cd $(FMC_BUS_ABS); git describe --always --dirty --long --tags)
ZIO_VERSION ?= $(shell cd $(ZIO_ABS); git describe --always --dirty --long --tags)
ifndef ZIO
$(error "Missing ZIO environment variable")
endif
ifndef FMC
$(error "Missing FMC environment variable")
endif
ifndef VMEBUS
$(error "Missing VMEBUS environment variable")
endif
export FMC_BUS_VERSION
export ZIO_VERSION
ZIO_ABS ?= $(abspath $(ZIO))
ZIO_EXTRA_SYMBOLS-y = $(ZIO_ABS)/drivers/zio/Module.symvers
ZIO_VERSION ?= $(shell cd $(ZIO_ABS); git describe --always --dirty --long --tags)
FMC_ABS ?= $(abspath $(FMC))
FMC_EXTRA_SYMBOLS-y = $(FMC_ABS)/drivers/fmc/Module.symvers
endif
VMEBUS_ABS ?= $(abspath $(VMEBUS) )
GIT_VERSION = $(shell git describe --always --dirty --long --tags)
all modules:
$(MAKE) -C $(LINUX) M=$(CURDIR) FMC_BUS_ABS=$(FMC_BUS_ABS) \
ZIO_ABS=$(ZIO_ABS) SVEC_SW_ABS=$(SVEC_SW_ABS) \
$(MAKE) -C $(LINUX) M=$(CURDIR) ZIO_ABS=$(ZIO_ABS) FMC_ABS=$(FMC_ABS) \
ZIO_EXTRA_SYMBOLS-y=$(ZIO_EXTRA_SYMBOLS-y) \
FMC_EXTRA_SYMBOLS-y=$(FMC_EXTRA_SYMBOLS-y) \
ZIO_VERSION=$(ZIO_VERSION) \
GIT_VERSION=$(GIT_VERSION) \
VMEBUS_ABS=$(VMEBUS_ABS) modules
install modules_install: modules
......@@ -40,3 +63,6 @@ install modules_install: modules
clean:
rm -rf *.o *~ .*.cmd *.ko *.mod.c .tmp_versions Module.symvers \
Module.markers modules.order
cppcheck:
$(CPPCHECK) -q -I. -I$(ZIO_ABS)/include -I$(FMC_BUS_ABS)/ --enable=all *.c *.h
......@@ -3,71 +3,337 @@
/*
* EEPROM calibration block retreival code for fa-dev
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/byteorder/generic.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/zio.h>
#include <linux/moduleparam.h>
#include <linux/jiffies.h>
#include <fmc-adc-100m14b4cha.h>
#include "fmc-adc-100m14b4cha.h"
static int fa_calib_period_s = 60;
module_param_named(calib_s, fa_calib_period_s, int, 0444);
/* This identity calibration is used as default */
static struct fa_calib_stanza fa_identity_calib = {
static const struct fa_calib_stanza fa_identity_calib = {
.offset = { 0, },
.gain = {0x8000, 0x8000, 0x8000, 0x8000},
.temperature = 50 * 100, /* 50 celsius degrees */
};
/* Max difference from identity thing */
#define FA_CALIB_MAX_DELTA_OFFSET 0x1000
#define FA_CALIB_MAX_DELTA_GAIN 0x1000
#define FA_CALIB_MAX_DELTA_TEMP (40 * 100) /* 10-90 celsius */
static bool fa_calib_is_busy(struct fa_dev *fa)
{
return !!fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_STA_CALIB_BUSY]);
}
static int fa_calib_apply(struct fa_dev *fa)
{
if (fa_calib_is_busy(fa))
return -EBUSY;
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_CALIB_APPLY], 1);
ndelay(100);
if (fa_calib_is_busy(fa))
return -EBUSY;
return 0;
}
static void fa_calib_gain_set(struct fa_dev *fa, unsigned int chan, int val)
{
int attr_idx;
attr_idx = zfad_get_chx_index(ZFA_CHx_GAIN, chan);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[attr_idx], val);
}
static void fa_calib_offset_set(struct fa_dev *fa, unsigned int chan, int val)
{
int attr_idx;
attr_idx = zfad_get_chx_index(ZFA_CHx_OFFSET, chan);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[attr_idx],
val & 0xFFFF /* prevent warning */);
}
/*
* Empirical values for the gain error slope
* 10V 0.0012500
* 1V -0.0000233
* 100mV -0.0000163
*
* Multiply by 0x8000 to have the same range as the calibration data.
* To do integer math we also multiply by 0x2000.
*/
static const int64_t gain_adc_error_slope_fix[] = {
[FA100M14B4C_RANGE_10V] = 335544,
[FA100M14B4C_RANGE_1V] = -6255,
[FA100M14B4C_RANGE_100mV] = -4375,
};
/**
* Compute the correct gain
* @range: voltage range
* @gain_c: calibration value
* @delta_temp: temperature difference: (current temp. - calibration temp.)
* the unit must be milli-degree
*/
static int fa_calib_adc_gain_fix(int range, int32_t gain_c,
int32_t delta_temp)
{
int64_t error;
error = gain_adc_error_slope_fix[range] * delta_temp;
error /= 0x2000; /* see comment above for gain_adc_error_slope_fix */
error /= 1000; /* convert to degree */
return gain_c - error;
}
/*
* Empirical values for the gain error slope
* 10V 0.00017100
* 1V -0.00000349
* 100mV 0.00001540
* Multiply by 0x8000 to have the same range as the calibration data.
* To do integer math we also multiply by 0x2000.
*/
static const int64_t gain_dac_error_slope_fix[] = {
[FA100M14B4C_RANGE_10V] = 459025,
[FA100M14B4C_RANGE_1V] = -937,
[FA100M14B4C_RANGE_100mV] = 4134,
};
/**
* Compute the correct gain
* @range: voltage range
* @gain_c: calibration value
* @delta_temp: temperature difference: (current temp. - calibration temp.)
* the unit must be milli-degree
*/
static int fa_calib_dac_gain_fix(int range, uint32_t gain_c,
int32_t delta_temp)
{
int64_t error;
error = gain_dac_error_slope_fix[range] * delta_temp;
error /= 0x2000; /* see comment above for gain_dac_error_slope_fix */
error /= 1000; /* convert to degree */
return gain_c - error;
}
/**
* Calibrate a ADC channel
* @fa ADC instance
* @chan channel number
* @temperature temperature
*
* You must hold &fa->zdev->cset->lock while calling this function
*/
void fa_calib_adc_config_chan(struct fa_dev *fa, unsigned int chan,
int32_t temperature)
{
int range = fa->range[chan];
struct fa_calib_stanza *cal = &fa->calib.dac[range];
int gain;
int err;
if (temperature == 0xFFFFFFFF)
temperature = fa_temperature_read(fa);
if (unlikely((fa->flags & FA_DEV_F_PATTERN_DATA)))
gain = cal->gain[chan];
else
gain = fa_calib_adc_gain_fix(range, cal->gain[chan],
temperature - cal->temperature);
dev_dbg(&fa->pdev->dev,
"%s: {calib-temperatur: %d, temperature: %d, chan: %d, range: %d, gain: 0x%x, offset: 0x%x}\n",
__func__, cal->temperature, temperature,
chan, range, gain, cal->offset[chan]);
fa_calib_gain_set(fa, chan, gain);
fa_calib_offset_set(fa, chan, cal->offset[chan]);
err = fa_calib_apply(fa);
if (err)
dev_err(&fa->pdev->dev, "Can't apply calibration values\n");
}
/**
* It sets the DAC voltage to apply an offset on the input channel
* @fa ADC device
* @chan channel number
* @val DAC values (-5V: 0x0000, 0V: 0x8000, +5V: 0x7FFF)
*
* Return: 0 on success, otherwise a negative error number
*/
static int fa_dac_offset_set(struct fa_dev *fa, unsigned int chan,
uint32_t val)
{
return fa_spi_xfer(fa, FA_SPI_SS_DAC(chan), 16, val, NULL);
}
static int64_t fa_dac_offset_raw_get(int32_t offset)
{
int64_t hwval;
hwval = offset * 0x8000LL / 5000000;
if (hwval == 0x8000)
hwval = 0x7fff; /* -32768 .. 32767 */
return hwval;
}
static int64_t fa_dac_offset_raw_calibrate(int32_t raw_offset,
int gain, int offset)
{
int64_t hwval;
hwval = ((raw_offset + offset) * gain) >> 15; /* signed */
hwval += 0x8000; /* offset binary */
if (hwval < 0)
hwval = 0;
if (hwval > 0xffff)
hwval = 0xffff;
return hwval;
}
static int fa_dac_offset_get(struct fa_dev *fa, unsigned int chan)
{
int32_t off_uv = fa->user_offset[chan] + fa->zero_offset[chan];
if (WARN(off_uv < DAC_SAT_LOW,
"DAC lower saturation %d < %d\n",
off_uv, DAC_SAT_LOW)) {
off_uv = DAC_SAT_LOW;
}
if (WARN(off_uv > DAC_SAT_UP,
"DAC upper saturation %d > %d\n",
off_uv, DAC_SAT_UP)) {
off_uv = DAC_SAT_UP;
}
return off_uv;
}
/**
* Calibrate a DAC channel
* @fa ADC instance
* @chan channel number
* @temperature temperature
*
* You must hold &fa->zdev->cset->lock while calling this function
*/
int fa_calib_dac_config_chan(struct fa_dev *fa, unsigned int chan,
int32_t temperature)
{
int32_t off_uv = fa_dac_offset_get(fa, chan);
int32_t off_uv_raw = fa_dac_offset_raw_get(off_uv);
int range = fa->range[chan];
struct fa_calib_stanza *cal = &fa->calib.dac[range];
int gain;
int hwval;
if (temperature == 0xFFFFFFFF)
temperature = fa_temperature_read(fa);
if (unlikely((fa->flags & FA_DEV_F_PATTERN_DATA)))
gain = cal->gain[chan];
else
gain = fa_calib_dac_gain_fix(range, cal->gain[chan],
temperature - cal->temperature);
dev_dbg(&fa->pdev->dev,
"%s: {calib-temperatur: %d, temperature: %d, chan: %d, range: %d, gain: 0x%x, offset: 0x%x}\n",
__func__, cal->temperature, temperature,
chan, range, gain, cal->offset[chan]);
hwval = fa_dac_offset_raw_calibrate(off_uv_raw, gain,
cal->offset[chan]);
return fa_dac_offset_set(fa, chan, hwval);
}
void fa_calib_config(struct fa_dev *fa)
{
int32_t temperature;
int i;
temperature = fa_temperature_read(fa);
spin_lock(&fa->zdev->cset->lock);
for (i = 0; i < FA100M14B4C_NCHAN; ++i) {
fa_calib_adc_config_chan(fa, i, temperature);
fa_calib_dac_config_chan(fa, i, temperature);
}
spin_unlock(&fa->zdev->cset->lock);
}
/**
* Periodically update gain calibration values
* @fa: FMC ADC device
*
* In the ADC we have a calibration value that is good for a small
* temperature range. We proved empirically that the gain error has a
* linear behavior with respect to the temperature.
*
*/
static void fa_calib_gain_update(unsigned long arg)
{
struct fa_dev *fa = (void *)arg;
fa_calib_config(fa);
mod_timer(&fa->calib_timer, jiffies + HZ * fa_calib_period_s);
}
/* Actual verification code */
static int fa_verify_calib_stanza(struct device *msgdev, char *name, int r,
struct fa_calib_stanza *cal,
struct fa_calib_stanza *iden)
struct fa_calib_stanza *cal)
{
int i, err = 0;
const struct fa_calib_stanza *iden = &fa_identity_calib;
int i;
for (i = 0; i < ARRAY_SIZE(cal->offset); i++) {
if (abs(cal->offset[i] - iden->offset[i])
> FA_CALIB_MAX_DELTA_OFFSET) {
dev_dbg(msgdev, "wrong offset 0x%x\n", cal->offset[i]);
err++;
dev_err(msgdev, "wrong offset (%i) 0x%x\n",
i, cal->offset[i]);
return -EINVAL;
}
if (abs((s16)(cal->gain[i] - iden->gain[i]))
> FA_CALIB_MAX_DELTA_GAIN) {
dev_dbg(msgdev, "wrong gain 0x%x\n", cal->gain[i]);
err++;
dev_err(msgdev, "invalid gain (%i) 0x%x\n",
i, cal->gain[i]);
return -EINVAL;
}
}
if (abs((s16)(cal->temperature - iden->temperature))
> FA_CALIB_MAX_DELTA_TEMP) {
dev_dbg(msgdev, "wrong temper 0x%x\n", cal->temperature);
err++;
dev_err(msgdev, "invalid temper 0x%x\n", cal->temperature);
return -EINVAL;
}
if (err)
dev_dbg(msgdev, "%i errors in %s calibration, range %i\n",
err, name, r);
return err;
return 0;
}
static void fa_verify_calib(struct device *msgdev,
struct fa_calib *calib,
struct fa_calib_stanza *identity)
static int fa_verify_calib(struct device *msgdev, struct fa_calib *calib)
{
int i, err = 0;
for (i = 0; i < ARRAY_SIZE(calib->adc); i++) {
err += fa_verify_calib_stanza(msgdev, "adc", i,
calib->adc + i, identity);
err += fa_verify_calib_stanza(msgdev, "dac", i,
calib->dac + i, identity);
err = fa_verify_calib_stanza(msgdev, "adc", i, calib->adc + i);
if (err)
return err;
err = fa_verify_calib_stanza(msgdev, "dac", i, calib->dac + i);
if (err)
return err;
}
if (!err)
return;
dev_info(msgdev, "Invalid calibration in EEPROM (%i errors)\n", err);
dev_info(msgdev, "Using identity calibration\n");
for (i = 0; i < ARRAY_SIZE(calib->adc); i++) {
calib->adc[i] = *identity;
calib->dac[i] = *identity;
}
return 0;
}
/**
......@@ -98,26 +364,33 @@ static void fa_calib_cpu_to_le16s(struct fa_calib *calib)
cpu_to_le16s(p + i); /* s == in situ */
}
void fa_read_eeprom_calib(struct fa_dev *fa)
static void fa_identity_calib_set(struct fa_calib *calib)
{
int i;
/* Retrieve calibration data from the eeprom, then verify it */
memcpy(&fa->calib, fa->fmc->eeprom + FA_CAL_OFFSET, sizeof(fa->calib));
fa_calib_le16_to_cpus(&fa->calib);
fa_verify_calib(&fa->fmc->dev, &fa->calib, &fa_identity_calib);
for (i = 0; i < FA_CALIB_STANZA_N; ++i) {
memcpy(&calib->adc[i], &fa_identity_calib,
sizeof(calib->adc[i]));
memcpy(&calib->dac[i], &fa_identity_calib,
sizeof(calib->dac[i]));
}
fa_calib_le16_to_cpus(calib);
}
/**
* Calculate calibrated values for offset and range using current values
* @fa: FMC ADC device
* @chan: channel
*/
static void fa_apply_calib(struct fa_dev *fa, struct zio_channel *chan)
static void fa_calib_write(struct fa_dev *fa, struct fa_calib *calib)
{
int reg = zfad_get_chx_index(ZFA_CHx_CTL_RANGE, chan);
int range = fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[reg]);
int err;
zfad_set_range(fa, chan, range);
zfad_apply_offset(chan);
fa_calib_le16_to_cpus(calib);
err = fa_verify_calib(fa->msgdev, calib);
if (err) {
dev_info(fa->msgdev, "Apply Calibration Identity\n");
fa_identity_calib_set(&fa->calib);
} else {
memcpy(&fa->calib, calib, sizeof(*calib));
}
fa_calib_config(fa);
}
static ssize_t fa_write_eeprom(struct file *file, struct kobject *kobj,
......@@ -127,21 +400,11 @@ static ssize_t fa_write_eeprom(struct file *file, struct kobject *kobj,
struct device *dev = container_of(kobj, struct device, kobj);
struct fa_dev *fa = get_zfadc(dev);
struct fa_calib *calib = (struct fa_calib *) buf;
int i;
if (off != 0 || count != sizeof(*calib))
return -EINVAL;
fa_calib_le16_to_cpus(calib);
fa_verify_calib(dev, calib, &fa_identity_calib);
/*
* The user should be careful enough to not change calibration
* values while running an acquisition
*/
memcpy(&fa->calib, calib, sizeof(*calib));
for (i = 0; i < FA100M14B4C_NCHAN; ++i)
fa_apply_calib(fa, &fa->zdev->cset->chan[i]);
fa_calib_write(fa, calib);
return count;
}
......@@ -173,3 +436,38 @@ struct bin_attribute dev_attr_calibration = {
.write = fa_write_eeprom,
.read = fa_read_eeprom,
};
int fa_calib_init(struct fa_dev *fa)
{
struct fa_calib calib;
int ret;
ret = fmc_slot_eeprom_read(fa->slot, &calib,
FA_CAL_OFFSET, sizeof(calib));
if (ret < 0) {
dev_warn(fa->msgdev,
"Failed to read calibration from EEPROM: using identity calibration %d\n",
ret);
fa_identity_calib_set(&calib);
goto out;
}
fa_calib_write(fa, &calib);
/* Prepare the timely recalibration */
fa_calib_config(fa);
setup_timer(&fa->calib_timer, fa_calib_gain_update, (unsigned long)fa);
if (fa_calib_period_s)
mod_timer(&fa->calib_timer, jiffies + HZ * fa_calib_period_s);
out:
return 0;
}
void fa_calib_exit(struct fa_dev *fa)
{
del_timer_sync(&fa->calib_timer);
fa_identity_calib_set(&fa->calib);
fa_calib_config(fa);
}
......@@ -8,18 +8,19 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/dmaengine.h>
#include <linux/mod_devicetable.h>
#include <linux/ipmi-fru.h>
#include <linux/fmc.h>
#include "fmc-adc-100m14b4cha.h"
/* Module parameters */
static struct fmc_driver fa_dev_drv;
FMC_PARAM_BUSID(fa_dev_drv);
FMC_PARAM_GATEWARE(fa_dev_drv);
#include <platform_data/fmc-adc-100m14b4cha.h>
static int fa_enable_test_data_fpga;
module_param_named(enable_test_data_fpga, fa_enable_test_data_fpga, int, 0444);
int fa_enable_test_data_adc = 0;
module_param_named(enable_test_data_adc, fa_enable_test_data_adc, int, 0444);
#define FA_EEPROM_TYPE "at24c64"
static const int zfad_hw_range[] = {
[FA100M14B4C_RANGE_10V_CAL] = 0x44,
......@@ -34,139 +35,123 @@ static const int zfad_hw_range[] = {
/* fmc-adc specific workqueue */
struct workqueue_struct *fa_workqueue;
/*
* zfad_convert_hw_range
* @usr_val: range value
/**
* Read FMC mezzanine temperature
* @fa: the adc descriptor
*
* return the enum associated to the range value
* DS18B20 returns units of 1/16 degree. We return units
* of 1/1000 of a degree instead.
*/
int zfad_convert_hw_range(uint32_t bitmask)
int32_t fa_temperature_read(struct fa_dev *fa)
{
int i;
uint32_t raw_temp;
for (i = 0; i < ARRAY_SIZE(zfad_hw_range); i++)
if (zfad_hw_range[i] == bitmask)
return i;
return -EINVAL;
}
/* Calculate correct index in fa_regfield array for channel from CHx indexes */
int zfad_get_chx_index(unsigned long addr, struct zio_channel *chan)
{
int offset;
offset = ZFA_CHx_MULT * (FA100M14B4C_NCHAN - chan->index);
raw_temp = fa_readl(fa, fa->fa_ow_base, &zfad_regs[ZFA_DS18B20_TEMP]);
return addr - offset;
return (raw_temp * 1000 + 8) / 16;
}
/**
* It enables or disables the pattern data on the ADC
* @fa The ADC device instance
* @pattern the pattern data to get from the ADC
* @enable 0 to disable, 1 to enable
* Do a software trigger
* @fa: the adc descriptor
*
* Return: 0 on success, otherwise a negative error number
*/
int zfad_pattern_data_enable(struct fa_dev *fa, uint16_t pattern,
unsigned int enable)
int fa_trigger_software(struct fa_dev *fa)
{
uint32_t frame_tx;
struct zio_ti *ti = fa->zdev->cset->ti;
struct zio_attribute *ti_zattr = ti->zattr_set.std_zattr;
unsigned int timeout;
int err;
frame_tx = 0x0000; /* write mode */
frame_tx |= 0x0400; /* A4 pattern */
frame_tx |= pattern & 0xFF; /* LSB pattern */
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, frame_tx, NULL);
if (err)
return err;
/* Fire if software trigger is enabled (index 5) */
if (!(ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value &
FA100M14B4C_TRG_SRC_SW)) {
dev_info(&fa->pdev->dev, "sw trigger is not enabled\n");
return -EPERM;
}
frame_tx = 0x0000; /* write mode */
frame_tx |= 0x0300; /* A3 pattern + enable */
frame_tx |= (pattern & 0xFF00) >> 8; /* MSB pattern */
frame_tx |= (enable ? 0x80 : 0x00); /* Enable the pattern data */
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, frame_tx, NULL);
/* Fire if nsamples!=0 */
if (!ti->nsamples) {
dev_info(&fa->pdev->dev, "pre + post = 0: cannot acquire\n");
return -EINVAL;
}
/*
* We can do a software trigger if the FSM is not in
* the WAIT trigger status. Wait for it.
* Remember that: timeout is in us, a sample takes 10ns
*/
timeout = ti_zattr[ZIO_ATTR_TRIG_PRE_SAMP].value / 10;
err = fa_fsm_wait_state(fa, FA100M14B4C_STATE_WAIT, timeout);
if (err)
return err;
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SW], 1);
return 0;
}
/**
* It sets the DAC voltage to apply an offset on the input channel
* @chan
* @val DAC values (-5V: 0x0000, 0V: 0x8000, +5V: 0x7FFF)
*
* Return: 0 on success, otherwise a negative error number
* Description:
* The version from the Linux kernel automatically squash contiguous pages.
* Sometimes we do not want to squash (e.g. SVEC)
*/
static int zfad_dac_set(struct zio_channel *chan, uint32_t val)
static int sg_alloc_table_from_pages_no_squash(struct sg_table *sgt,
struct page **pages,
unsigned int n_pages,
unsigned int offset,
unsigned long size,
unsigned int max_segment,
gfp_t gfp_mask)
{
struct fa_dev *fa = get_zfadc(&chan->cset->zdev->head.dev);
struct scatterlist *sg;
int err, i;
return fa_spi_xfer(fa, FA_SPI_SS_DAC(chan->index), 16, val, NULL);
}
static int zfad_offset_to_dac(struct zio_channel *chan,
int32_t uval,
enum fa100m14b4c_input_range range)
{
struct fa_dev *fa = get_zfadc(&chan->cset->zdev->head.dev);
int offset, gain;
int64_t hwval;
hwval = uval * 0x8000LL / 5000000;
if (hwval == 0x8000)
hwval = 0x7fff; /* -32768 .. 32767 */
err = sg_alloc_table(sgt, n_pages, GFP_KERNEL);
if (unlikely(err))
return err;
offset = fa->calib.dac[range].offset[chan->index];
gain = fa->calib.dac[range].gain[chan->index];
for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
unsigned long chunk_size;
hwval = ((hwval + offset) * gain) >> 15; /* signed */
hwval += 0x8000; /* offset binary */
if (hwval < 0)
hwval = 0;
if (hwval > 0xffff)
hwval = 0xffff;
chunk_size = PAGE_SIZE - offset;
sg_set_page(sg, pages[i], min(size, chunk_size), offset);
offset = 0;
size -= chunk_size;
}
return hwval;
return 0;
}
/*
* zfad_apply_user_offset
* @chan: the channel where apply offset
*
* Apply user offset to the channel input. Before apply the user offset it must
* be corrected with offset and gain calibration value.
* zfad_convert_hw_range
* @usr_val: range value
*
* Offset values are taken from `struct fa_dev`, so they must be there before
* calling this function
* return the enum associated to the range value
*/
int zfad_apply_offset(struct zio_channel *chan)
int zfad_convert_hw_range(uint32_t bitmask)
{
struct fa_dev *fa = get_zfadc(&chan->cset->zdev->head.dev);
uint32_t range_reg;
int32_t off_uv;
int hwval, i, range;
off_uv = fa->user_offset[chan->index] + fa->zero_offset[chan->index];
if (off_uv < -5000000 || off_uv > 5000000)
return -EINVAL;
int i;
i = zfad_get_chx_index(ZFA_CHx_CTL_RANGE, chan);
range_reg = fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[i]);
for (i = 0; i < ARRAY_SIZE(zfad_hw_range); i++)
if (zfad_hw_range[i] == bitmask)
return i;
return -EINVAL;
}
range = zfad_convert_hw_range(range_reg);
if (range < 0)
return range;
/* Calculate correct index in fa_regfield array for channel from CHx indexes */
int zfad_get_chx_index(unsigned long addr, unsigned int chan)
{
int offset;
if (range == FA100M14B4C_RANGE_OPEN || fa_enable_test_data_adc)
range = FA100M14B4C_RANGE_1V;
else if (range >= FA100M14B4C_RANGE_10V_CAL)
range -= FA100M14B4C_RANGE_10V_CAL;
offset = ZFA_CHx_MULT * (FA100M14B4C_NCHAN - chan);
hwval = zfad_offset_to_dac(chan, off_uv, range);
return zfad_dac_set(chan, hwval);
return addr - offset;
}
/*
* zfad_reset_offset
* @fa: the fmc-adc descriptor
......@@ -177,11 +162,13 @@ void zfad_reset_offset(struct fa_dev *fa)
{
int i;
spin_lock(&fa->zdev->cset->lock);
for (i = 0; i < FA100M14B4C_NCHAN; ++i) {
fa->user_offset[i] = 0;
fa->zero_offset[i] = 0;
zfad_apply_offset(&fa->zdev->cset->chan[i]);
fa_calib_dac_config_chan(fa, i, ~0);
}
spin_unlock(&fa->zdev->cset->lock);
}
/*
......@@ -199,7 +186,7 @@ void zfad_init_saturation(struct fa_dev *fa)
}
/*
* zfad_set_range
* fa_adc_range_set
* @fa: the fmc-adc descriptor
* @chan: the channel to calibrate
* @usr_val: the volt range to set and calibrate
......@@ -208,16 +195,15 @@ void zfad_init_saturation(struct fa_dev *fa)
* Gain ad offsets must be corrected with offset and gain calibration value.
* An open input and test data do not need any correction.
*/
int zfad_set_range(struct fa_dev *fa, struct zio_channel *chan,
int range)
int fa_adc_range_set(struct fa_dev *fa, struct zio_channel *chan, int range)
{
int i, offset, gain;
int i;
/* Actually set the range */
i = zfad_get_chx_index(ZFA_CHx_CTL_RANGE, chan);
i = zfad_get_chx_index(ZFA_CHx_CTL_RANGE, chan->index);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[i], zfad_hw_range[range]);
if (range == FA100M14B4C_RANGE_OPEN || fa_enable_test_data_adc)
if (range == FA100M14B4C_RANGE_OPEN)
range = FA100M14B4C_RANGE_1V;
else if (range >= FA100M14B4C_RANGE_10V_CAL)
range -= FA100M14B4C_RANGE_10V_CAL;
......@@ -228,17 +214,111 @@ int zfad_set_range(struct fa_dev *fa, struct zio_channel *chan,
return -EINVAL;
}
offset = fa->calib.adc[range].offset[chan->index];
gain = fa->calib.adc[range].gain[chan->index];
spin_lock(&fa->zdev->cset->lock);
fa->range[chan->index] = range;
spin_unlock(&fa->zdev->cset->lock);
return 0;
}
static enum fa100m14b4c_fsm_state fa_fsm_get_state(struct fa_dev *fa)
{
return fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_STA_FSM]);
}
static bool fa_fsm_is_state(struct fa_dev *fa,
enum fa100m14b4c_fsm_state state)
{
return fa_fsm_get_state(fa) == state;
}
int fa_fsm_wait_state(struct fa_dev *fa,
enum fa100m14b4c_fsm_state state,
unsigned int timeout_us)
{
unsigned long timeout;
timeout = jiffies + usecs_to_jiffies(timeout_us);
while (!fa_fsm_is_state(fa, state)) {
cpu_relax();
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
}
return 0;
}
static void fa_fpga_data_pattern_set(struct fa_dev *fa, unsigned int enable)
{
fa_writel(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_CTL_TEST_DATA_EN], enable);
}
/**
* It enables or disables the pattern data on the ADC
* @fa The ADC device instance
* @pattern the pattern data to get from the ADC
* @enable 0 to disable, 1 to enable
*/
int fa_adc_data_pattern_set(struct fa_dev *fa, uint16_t pattern,
unsigned int enable)
{
uint32_t frame_tx;
int err;
dev_dbg(&fa->pdev->dev, "%s {patter: 0x%04x, enable: %d}\n", __func__, pattern, enable);
i = zfad_get_chx_index(ZFA_CHx_OFFSET, chan);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[i],
offset & 0xffff /* prevent warning */);
i = zfad_get_chx_index(ZFA_CHx_GAIN, chan);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[i], gain);
spin_lock(&fa->zdev->cset->lock);
frame_tx = 0x0000; /* write mode */
frame_tx |= 0x0400; /* A4 pattern */
frame_tx |= pattern & 0xFF; /* LSB pattern */
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, frame_tx, NULL);
if (err)
goto err;
frame_tx = 0x0000; /* write mode */
frame_tx |= 0x0300; /* A3 pattern + enable */
frame_tx |= (pattern & 0xFF00) >> 8; /* MSB pattern */
frame_tx |= (enable ? 0x80 : 0x00); /* Enable the pattern data */
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, frame_tx, NULL);
if (err)
goto err;
if (enable)
fa->flags |= FA_DEV_F_PATTERN_DATA;
else
fa->flags &= ~FA_DEV_F_PATTERN_DATA;
err:
spin_unlock(&fa->zdev->cset->lock);
return err;
}
/**
* Get current status for data pattern
* @fa The ADC device instance
* @pattern the pattern data to get from the ADC
* @enable 0 to disable, 1 to enable
*/
int fa_adc_data_pattern_get(struct fa_dev *fa, uint16_t *pattern,
unsigned int *enable)
{
uint32_t tx, rx;
int err;
tx = 0x8000 | (3 << 8);
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, tx, &rx);
if (err)
return err;
*enable = !!(rx & 0x80);
*pattern = ((rx & 0x3F) << 8);
/* recalculate user offset for the new range */
zfad_apply_offset(chan);
tx = 0x8000 | (4 << 8);
err = fa_spi_xfer(fa, FA_SPI_SS_ADC, 16, tx, &rx);
if (err)
return err;
*pattern |= (rx & 0xFF);
return 0;
}
......@@ -317,13 +397,13 @@ int zfad_fsm_command(struct fa_dev *fa, uint32_t command)
return -EIO;
}
dev_dbg(fa->msgdev, "FSM START Command, Enable interrupts\n");
dev_dbg(fa->msgdev, "FSM START Command\n");
fa_enable_irqs(fa);
fa_writel(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_CTL_RST_TRG_STA], 1);
} else {
dev_dbg(fa->msgdev, "FSM STOP Command, Disable interrupts\n");
dev_dbg(fa->msgdev, "FSM STOP Command\n");
fa->enable_auto_start = 0;
fa_disable_irqs(fa);
}
......@@ -333,44 +413,15 @@ int zfad_fsm_command(struct fa_dev *fa, uint32_t command)
return 0;
}
/* Extract from SDB the base address of the core components */
/* which are not carrier specific */
static int __fa_sdb_get_device(struct fa_dev *fa)
static void fa_init_timetag(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
int ret;
ret = fmc_scan_sdb_tree(fmc, 0);
if (ret == -EBUSY) {
/* Not a problem, it's already there. We assume that
it's the correct one */
ret = 0;
}
if (ret < 0) {
dev_err(fa->msgdev,
"%s: no SDB in the bitstream."
"Are you sure you've provided the correct one?\n",
KBUILD_MODNAME);
return ret;
}
unsigned long seconds;
/* Now use SDB to find the base addresses */
fa->fa_irq_vic_base = fmc_find_sdb_device(fmc->sdb, 0xce42,
0x13, NULL);
fa->fa_adc_csr_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x608,
fmc->slot_id, NULL);
fa->fa_irq_adc_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x26ec6086,
fmc->slot_id, NULL);
fa->fa_utc_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x604, fmc->slot_id, NULL);
fa->fa_spi_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42, 0xe503947e,
fmc->slot_id, NULL);
fa->fa_ow_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42, 0x779c5443,
fmc->slot_id, NULL);
return ret;
seconds = get_seconds();
fa_writel(fa, fa->fa_utc_base, &zfad_regs[ZFA_UTC_SECONDS_U],
(seconds >> 32) & 0xFFFFFFFF);
fa_writel(fa, fa->fa_utc_base, &zfad_regs[ZFA_UTC_SECONDS_L],
(seconds >> 00) & 0xFFFFFFFF);
}
/*
......@@ -378,21 +429,10 @@ static int __fa_sdb_get_device(struct fa_dev *fa)
*/
static int __fa_init(struct fa_dev *fa)
{
struct device *hwdev = fa->fmc->hwdev;
struct zio_device *zdev = fa->zdev;
int i, addr;
/* Check if hardware supports 64-bit DMA */
if (dma_set_mask(hwdev, DMA_BIT_MASK(64))) {
/* Check if hardware supports 32-bit DMA */
if (dma_set_mask(hwdev, DMA_BIT_MASK(32))) {
dev_err(fa->msgdev, "32-bit DMA addressing not available\n");
return -EINVAL;
}
}
/* Use identity calibration */
fa_read_eeprom_calib(fa);
fa->mshot_max_samples = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_MULT_MAX_SAMP]);
......@@ -402,11 +442,13 @@ static int __fa_init(struct fa_dev *fa)
/* Initialize channels to use 1V range */
for (i = 0; i < 4; ++i) {
addr = zfad_get_chx_index(ZFA_CHx_CTL_RANGE,
&zdev->cset->chan[i]);
zdev->cset->chan[i].index);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[addr],
FA100M14B4C_RANGE_1V);
zfad_set_range(fa, &zdev->cset->chan[i], FA100M14B4C_RANGE_1V);
fa_adc_range_set(fa, &zdev->cset->chan[i],
FA100M14B4C_RANGE_1V);
}
fa_calib_config(fa);
zfad_reset_offset(fa);
/* Enable mezzanine clock */
......@@ -414,15 +456,14 @@ static int __fa_init(struct fa_dev *fa)
/* Set decimation to minimum */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SR_UNDER], 1);
/* Set test data register */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_TEST_DATA_EN],
fa_enable_test_data_fpga);
fa_fpga_data_pattern_set(fa, fa_enable_test_data_fpga);
/* disable test pattern data in the ADC */
fa_adc_data_pattern_set(fa, 0, 0);
/* Set to single shot mode by default */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SHOTS_NB], 1);
/* Enable the software trigger by default: there is no arm in this */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC],
FA100M14B4C_TRG_SRC_SW);
zfat_trigger_source_reset(fa);
/* Zero offsets and release the DAC clear */
zfad_reset_offset(fa);
......@@ -431,9 +472,7 @@ static int __fa_init(struct fa_dev *fa)
/* Initialize channel saturation values */
zfad_init_saturation(fa);
/* Set UTC seconds from the kernel seconds */
fa_writel(fa, fa->fa_utc_base, &zfad_regs[ZFA_UTC_SECONDS],
get_seconds());
fa_init_timetag(fa);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_EXT_DLY], 0);
......@@ -451,91 +490,166 @@ struct fa_modlist {
static struct fa_modlist mods[] = {
{"spi", fa_spi_init, fa_spi_exit},
{"onewire", fa_onewire_init, fa_onewire_exit},
{"zio", fa_zio_init, fa_zio_exit},
{"debug", fa_debug_init, fa_debug_exit},
{"calibration", fa_calib_init, fa_calib_exit},
};
static int fa_resource_validation(struct platform_device *pdev)
{
struct resource *r;
r = platform_get_resource(pdev, IORESOURCE_IRQ, ADC_IRQ_TRG);
if (!r) {
dev_err(&pdev->dev,
"The ADC needs an interrupt number for the IRQ\n");
return -ENXIO;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, ADC_MEM_BASE);
if (!r) {
dev_err(&pdev->dev,
"The ADC needs base address\n");
return -ENXIO;
}
return 0;
}
#define FA_FMC_NAME "FmcAdc100m14b4cha"
static bool fa_fmc_slot_is_valid(struct fa_dev *fa)
{
int ret;
void *fru = NULL;
char *fmc_name = NULL;
if (!fmc_slot_fru_valid(fa->slot)) {
dev_err(fa->msgdev, "Can't identify FMC card: invalid FRU\n");
return -EINVAL;
}
fru = kmalloc(FRU_SIZE_MAX, GFP_KERNEL);
if (!fru)
return -ENOMEM;
ret = fmc_slot_eeprom_read(fa->slot, fru, 0x0, FRU_SIZE_MAX);
if (ret != FRU_SIZE_MAX) {
dev_err(fa->msgdev, "Failed to read FRU header\n");
goto err;
}
fmc_name = fru_get_product_name(fru);
ret = strcmp(fmc_name, FA_FMC_NAME);
if (ret) {
dev_err(fa->msgdev,
"Invalid FMC card: expectd '%s', found '%s'\n",
FA_FMC_NAME, fmc_name);
goto err;
}
kfree(fmc_name);
kfree(fru);
return true;
err:
kfree(fmc_name);
kfree(fru);
return false;
}
static void fa_memops_detect(struct fa_dev *fa)
{
if (fa_is_flag_set(fa, FMC_ADC_BIG_ENDIAN)) {
fa->memops.read = ioread32be;
fa->memops.write = iowrite32be;
} else {
fa->memops.read = ioread32;
fa->memops.write = iowrite32;
}
}
static void fa_sg_alloc_table_init(struct fa_dev *fa)
{
if (fa_is_flag_set(fa, FMC_ADC_NOSQUASH_SCATTERLIST))
fa->sg_alloc_table_from_pages = sg_alloc_table_from_pages_no_squash;
else
fa->sg_alloc_table_from_pages = __sg_alloc_table_from_pages;
}
static struct fmc_adc_platform_data fmc_adc_pdata_default = {
.flags = 0,
.calib_trig_time = 0,
.calib_trig_threshold = 0,
.calib_trig_internal = 0,
};
/* probe and remove are called by fa-spec.c */
int fa_probe(struct fmc_device *fmc)
int fa_probe(struct platform_device *pdev)
{
struct fa_modlist *m = NULL;
struct fa_dev *fa;
int err, i = 0;
char *fwname;
/* Validate the new FMC device */
i = fmc_validate(fmc, &fa_dev_drv);
if (i < 0) {
dev_info(&fmc->dev, "not using \"%s\" according to "
"modparam\n", KBUILD_MODNAME);
return -ENODEV;
}
struct resource *r;
int err, i = 0, slot_nr;
err = fa_resource_validation(pdev);
if (err)
return err;
/* Driver data */
fa = devm_kzalloc(&fmc->dev, sizeof(struct fa_dev), GFP_KERNEL);
fa = devm_kzalloc(&pdev->dev, sizeof(struct fa_dev), GFP_KERNEL);
if (!fa)
return -ENOMEM;
fmc_set_drvdata(fmc, fa);
fa->fmc = fmc;
fa->msgdev = &fa->fmc->dev;
/* apply carrier-specific hacks and workarounds */
fa->carrier_op = NULL;
if (!strcmp(fmc->carrier_name, "SPEC")) {
fa->carrier_op = &fa_spec_op;
} else if (!strcmp(fmc->carrier_name, "SVEC")) {
#ifdef CONFIG_FMC_ADC_SVEC
fa->carrier_op = &fa_svec_op;
#endif
platform_set_drvdata(pdev, fa);
fa->pdev = pdev;
fa->msgdev = &fa->pdev->dev;
if (!pdev->dev.platform_data) {
dev_err(fa->msgdev, "Missing platform data, use default\n");
pdev->dev.platform_data = &fmc_adc_pdata_default;
}
/*
* Check if carrier operations exists. Otherwise it means that the
* driver was compiled without enable any carrier, so it cannot work
*/
if (!fa->carrier_op) {
dev_err(fa->msgdev,
"This binary doesn't support the '%s' carrier\n",
fmc->carrier_name);
return -ENODEV;
fa_memops_detect(fa);
fa_sg_alloc_table_init(fa);
r = platform_get_resource(pdev, IORESOURCE_MEM, ADC_MEM_BASE);
fa->fa_top_level = ioremap(r->start, resource_size(r));
fa->fa_adc_csr_base = fa->fa_top_level + ADC_CSR_OFF;
fa->fa_irq_adc_base = fa->fa_top_level + ADC_EIC_OFF;
fa->fa_ow_base = fa->fa_top_level + ADC_OW_OFF;
fa->fa_spi_base = fa->fa_top_level + ADC_SPI_OFF;
fa->fa_utc_base = fa->fa_top_level + ADC_UTC_OFF;
slot_nr = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_STA_FMC_NR]) + 1;
fa->slot = fmc_slot_get(pdev->dev.parent->parent, slot_nr);
if (IS_ERR(fa->slot)) {
dev_err(fa->msgdev, "Can't find FMC slot %d err: %ld\n",
slot_nr, PTR_ERR(fa->slot));
goto out_fmc;
}
/*
* If the carrier is still using the golden bitstream or the user is
* asking for a particular one, then program our bistream, otherwise
* we already have our bitstream
*/
if (fmc->flags & FMC_DEVICE_HAS_GOLDEN || fa_dev_drv.gw_n) {
if (fa_dev_drv.gw_n)
fwname = ""; /* reprogram will pick from module parameter */
else
fwname = fa->carrier_op->get_gwname();
/* We first write a new binary (and lm32) within the carrier */
err = fmc_reprogram(fmc, &fa_dev_drv, fwname, 0x0);
if (!fmc_slot_present(fa->slot)) {
dev_err(fa->msgdev, "Can't identify FMC card: missing card\n");
goto out_fmc_pre;
}
if (strcmp(fmc_slot_eeprom_type_get(fa->slot), FA_EEPROM_TYPE)) {
dev_warn(fa->msgdev, "use non standard EERPOM type \"%s\"\n",
FA_EEPROM_TYPE);
err = fmc_slot_eeprom_type_set(fa->slot, FA_EEPROM_TYPE);
if (err) {
dev_err(fa->msgdev, "write firmware \"%s\": error %i\n",
fwname, err);
goto out;
dev_err(fa->msgdev,
"Failed to change EEPROM type to \"%s\"",
FA_EEPROM_TYPE);
goto out_fmc_eeprom;
}
} else {
dev_info(fa->msgdev,
"Gateware already there. Set the \"gateware\" parameter to overwrite the current gateware\n");
}
/* Extract whisbone core base address fron SDB */
err = __fa_sdb_get_device(fa);
if (err < 0)
goto out;
err = fa->carrier_op->init(fa);
if (err < 0)
goto out;
err = fa->carrier_op->reset_core(fa);
if (err < 0)
goto out;
if(!fa_fmc_slot_is_valid(fa))
goto out_fmc_err;
/* init all subsystems */
for (i = 0, m = mods; i < ARRAY_SIZE(mods); i++, m++) {
......@@ -556,28 +670,33 @@ int fa_probe(struct fmc_device *fmc)
if (err < 0)
goto out_irq;
/* Pin the carrier */
if (!try_module_get(fmc->owner))
goto out_mod;
return 0;
out_mod:
fa_free_irqs(fa);
out_irq:
out:
while (--m, --i >= 0)
if (m->exit)
m->exit(fa);
iounmap(fa->fa_top_level);
out_fmc_err:
out_fmc_eeprom:
out_fmc_pre:
fmc_slot_put(fa->slot);
out_fmc:
devm_kfree(&pdev->dev, fa);
platform_set_drvdata(pdev, NULL);
return err;
}
int fa_remove(struct fmc_device *fmc)
int fa_remove(struct platform_device *pdev)
{
struct fa_dev *fa = fmc_get_drvdata(fmc);
struct fa_dev *fa = platform_get_drvdata(pdev);
struct fa_modlist *m;
int i = ARRAY_SIZE(mods);
if (WARN(!fa, "asked to remove fmc-adc-100m device but it does not exists\n"))
return 0;
fa_free_irqs(fa);
flush_workqueue(fa_workqueue);
......@@ -586,30 +705,29 @@ int fa_remove(struct fmc_device *fmc)
if (m->exit)
m->exit(fa);
}
iounmap(fa->fa_top_level);
fa->carrier_op->exit(fa);
/* Release the carrier */
module_put(fmc->owner);
fmc_slot_put(fa->slot);
return 0;
}
static struct fmc_fru_id fa_fru_id[] = {
static const struct platform_device_id fa_id[] = {
{
.product_name = "FmcAdc100m14b4cha",
},
.name = "fmc-adc-100m",
.driver_data = ADC_VER,
}
/* TODO we should support different version */
};
static struct fmc_driver fa_dev_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
static struct platform_driver fa_dev_drv = {
.driver = {
.name = KBUILD_MODNAME,
},
.probe = fa_probe,
.remove = fa_remove,
.id_table = {
.fru_id = fa_fru_id,
.fru_id_nr = ARRAY_SIZE(fa_fru_id),
},
.id_table = fa_id,
};
static int fa_init(void)
......@@ -637,7 +755,7 @@ static int fa_init(void)
goto out2;
/* Finally the fmc driver, whose probe instantiates zio devices */
ret = fmc_driver_register(&fa_dev_drv);
ret = platform_driver_register(&fa_dev_drv);
if (ret)
goto out3;
......@@ -655,7 +773,7 @@ out1:
static void fa_exit(void)
{
fmc_driver_unregister(&fa_dev_drv);
platform_driver_unregister(&fa_dev_drv);
fa_zio_unregister();
fa_trig_exit();
if (fa_workqueue != NULL)
......@@ -668,6 +786,6 @@ module_exit(fa_exit);
MODULE_AUTHOR("Federico Vaga");
MODULE_DESCRIPTION("FMC-ADC-100MS-14b Linux Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(GIT_VERSION);
MODULE_VERSION(VERSION);
ADDITIONAL_VERSIONS;
......@@ -6,6 +6,116 @@
#include "fmc-adc-100m14b4cha.h"
#define FA_DBG_REG32_CH(_n) \
{.name = "ADC-CSR:ch"#_n"_ctl", .offset = ADC_CSR_OFF + 0x080 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_sta", .offset = ADC_CSR_OFF + 0x084 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_cal_nb", .offset = ADC_CSR_OFF + 0x088 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_sat", .offset = ADC_CSR_OFF + 0x08C + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_trig_thres", .offset = ADC_CSR_OFF + 0x090 + ((_n - 1) * 0x40)}, \
{.name = "ADC-CSR:ch"#_n"_trig_dly", .offset = ADC_CSR_OFF + 0x094 + ((_n - 1) * 0x40)}
#define FA_DBG_REG32_TIM(_name, _off) \
{ \
.name = "TIME-TAG:"#_name"_seconds_upper", \
.offset = ADC_UTC_OFF + _off \
}, { \
.name = "TIME-TAG:"#_name"_seconds_lower", \
.offset = ADC_UTC_OFF + _off + 0x4, \
}, { \
.name = "TIME-TAG:"#_name"_coarse", \
.offset = ADC_UTC_OFF + _off + 0x8, \
}
static const struct debugfs_reg32 fa_debugfs_reg32[] = {
{
.name = "ADC-CSR:ctl",
.offset = ADC_CSR_OFF + 0x000,
},
{
.name = "ADC-CSR:sta",
.offset = ADC_CSR_OFF + 0x004,
},
{
.name = "ADC-CSR:trig_stat",
.offset = ADC_CSR_OFF + 0x008,
},
{
.name = "ADC-CSR:trig_en",
.offset = ADC_CSR_OFF + 0x00C,
},
{
.name = "ADC-CSR:trig_pol",
.offset = ADC_CSR_OFF + 0x010,
},
{
.name = "ADC-CSR:ext_trig_dly",
.offset = ADC_CSR_OFF + 0x014,
},
{
.name = "ADC-CSR:sw_trig",
.offset = ADC_CSR_OFF + 0x018,
},
{
.name = "ADC-CSR:shots",
.offset = ADC_CSR_OFF + 0x01C,
},
{
.name = "ADC-CSR:multi_depth",
.offset = ADC_CSR_OFF + 0x020,
},
{
.name = "ADC-CSR:trig_pos",
.offset = ADC_CSR_OFF + 0x024,
},
{
.name = "ADC-CSR:fs_freq",
.offset = ADC_CSR_OFF + 0x028,
},
{
.name = "ADC-CSR:downsample",
.offset = ADC_CSR_OFF + 0x02C,
},
{
.name = "ADC-CSR:pre_samples",
.offset = ADC_CSR_OFF + 0x030,
},
{
.name = "ADC-CSR:post_samples",
.offset = ADC_CSR_OFF + 0x034,
},
{
.name = "ADC-CSR:samples_cnt",
.offset = ADC_CSR_OFF + 0x038,
},
FA_DBG_REG32_CH(1),
FA_DBG_REG32_CH(2),
FA_DBG_REG32_CH(3),
FA_DBG_REG32_CH(4),
{
.name = "ADC-EIC:disable_mask",
.offset = ADC_EIC_OFF + 0x0,
},
{
.name = "ADC-EIC:enable_mask",
.offset = ADC_EIC_OFF + 0x4,
},
{
.name = "ADC-EIC:status_mask",
.offset = ADC_EIC_OFF + 0x8,
},
{
.name = "ADC-EIC:source",
.offset = ADC_EIC_OFF + 0xC,
},
FA_DBG_REG32_TIM(base_time, 0x00),
FA_DBG_REG32_TIM(time_trig, 0x0C),
FA_DBG_REG32_TIM(trig_tag, 0x18),
FA_DBG_REG32_TIM(acq_start_tag, 0x24),
FA_DBG_REG32_TIM(acq_stop_tag, 0x30),
FA_DBG_REG32_TIM(acq_end_tag, 0x3C),
};
static void fa_regdump_seq_read_spi(struct fa_dev *fa, struct seq_file *s)
{
......@@ -53,20 +163,155 @@ static const struct file_operations fa_regdump_ops = {
};
static ssize_t fa_trg_sw_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct fa_dev *fa = file->private_data;
int err;
err = fa_trigger_software(fa);
return err ? err : count;
}
static const struct file_operations fa_trg_sw_ops = {
.owner = THIS_MODULE,
.open = simple_open,
.write = fa_trg_sw_write,
};
#define FA_ADC_DATA_PATTERN_CMD_SIZE 16UL
static int fa_data_pattern_adc_write(struct fa_dev *fa, const char __user *buf,
size_t count)
{
char buf_l[FA_ADC_DATA_PATTERN_CMD_SIZE];
int err;
memset(buf_l, 0, FA_ADC_DATA_PATTERN_CMD_SIZE);
err = copy_from_user(buf_l, buf, min(count,
FA_ADC_DATA_PATTERN_CMD_SIZE));
if (err)
return -EFAULT;
if ((count == 1 || count == 2)&& buf_l[0] == '0') {
err = fa_adc_data_pattern_set(fa, 0, 0);
fa_calib_init(fa);
return err;
} else if (count > 2 && buf_l[0] == '1' && buf_l[1] == ' ') {
uint16_t pattern = 0;
err = kstrtou16(buf_l + 2, 0, &pattern);
if (err)
return err;
err = fa_adc_data_pattern_set(fa, pattern & 0x3FFF, 1);
fa_calib_exit(fa);
return err;
} else {
return -EINVAL;
}
}
static ssize_t fa_data_pattern_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct fa_dev *fa = file->private_data;
if (strncmp(buf, "adc ", 4) == 0) {
int err;
err = fa_data_pattern_adc_write(fa, buf + 4, count - 4);
return err ? err : count;
} else {
dev_err(&fa->pdev->dev, "Unknown command \"%s\"\n", buf);
return -EINVAL;
}
}
static ssize_t fa_data_pattern_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct fa_dev *fa = file->private_data;
char buf_l[FA_ADC_DATA_PATTERN_CMD_SIZE];
uint16_t pattern;
unsigned int enable;
int err;
if (*ppos > 0)
return 0;
err = fa_adc_data_pattern_get(fa, &pattern, &enable);
if (err)
return err;
snprintf(buf_l, FA_ADC_DATA_PATTERN_CMD_SIZE, "adc %d 0x%02x\n",
enable, pattern);
count = min(count, strlen(buf_l));
err = copy_to_user(buf, buf_l, count);
*ppos += count;
return err ? err : count;
}
static const struct file_operations fa_data_pattern_ops = {
.owner = THIS_MODULE,
.open = simple_open,
.write = fa_data_pattern_write,
.read = fa_data_pattern_read,
};
int fa_debug_init(struct fa_dev *fa)
{
fa->reg_dump = debugfs_create_file(dev_name(&fa->zdev->head.dev), 0444,
NULL, fa, &fa_regdump_ops);
if (IS_ERR_OR_NULL(fa->reg_dump)) {
dev_err(fa->msgdev,
int err;
fa->dbg_dir = debugfs_create_dir(dev_name(&fa->zdev->head.dev), NULL);
if (IS_ERR_OR_NULL(fa->dbg_dir)) {
err = PTR_ERR(fa->dbg_dir);
dev_err(&fa->zdev->head.dev,
"Cannot create debugfs directory \"%s\" (%d)\n",
dev_name(&fa->zdev->head.dev), err);
return err;
}
fa->dbg_reg32.regs = fa_debugfs_reg32;
fa->dbg_reg32.nregs = ARRAY_SIZE(fa_debugfs_reg32);
fa->dbg_reg32.base = fa->fa_top_level;
fa->dbg_reg = debugfs_create_regset32("regs", 0200, fa->dbg_dir,
&fa->dbg_reg32);
if (IS_ERR_OR_NULL(fa->dbg_reg)) {
err = PTR_ERR(fa->dbg_reg);
dev_warn(fa->msgdev,
"Cannot create debugfs file \"regs\" (%d)\n",
err);
}
fa->dbg_reg_spi = debugfs_create_file("spi-regs", 0444,
fa->dbg_dir, fa,
&fa_regdump_ops);
if (IS_ERR_OR_NULL(fa->dbg_reg_spi)) {
dev_warn(fa->msgdev,
"Cannot create regdump debugfs file\n");
}
fa->dbg_trg_sw = debugfs_create_file("trigger_software", 0200,
fa->dbg_dir, fa,
&fa_trg_sw_ops);
if (IS_ERR_OR_NULL(fa->dbg_trg_sw)) {
dev_warn(&fa->pdev->dev,
"Cannot create software trigger file\n");
}
fa->dbg_data_pattern = debugfs_create_file("data_pattern", 0644,
fa->dbg_dir, fa,
&fa_data_pattern_ops);
if (IS_ERR_OR_NULL(fa->dbg_data_pattern)) {
dev_warn(&fa->pdev->dev,
"Cannot create ADC data pattern file\n");
}
return 0;
}
void fa_debug_exit(struct fa_dev *fa)
{
debugfs_remove_recursive(fa->reg_dump);
debugfs_remove_recursive(fa->dbg_dir);
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/errno.h>
#ifdef CONFIG_FMC_ADC_SVEC
#include "vmebus.h"
#endif
#include "fmc-adc-100m14b4cha.h"
/* Endianess */
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 0
#endif
#ifndef BIG_ENDIAN
#define BIG_ENDIAN 1
#endif
static void zfad_dma_done(struct zio_cset *cset);
static int __get_endian(void)
{
int i = 1;
char *p = (char *)&i;
if (p[0] == 1)
return LITTLE_ENDIAN;
else
return BIG_ENDIAN;
}
/**
* Fix endianess from big to host endianess (32bit)
*/
static void __endianness(unsigned int byte_length, void *buffer)
{
int i, size;
uint32_t *ptr;
/* CPU may be little endian, VME is big endian */
if (__get_endian() == LITTLE_ENDIAN) {
ptr = buffer;
/* swap samples and trig timetag all seen as 32bits words */
size = byte_length/4;
for (i = 0; i < size; ++i, ++ptr)
*ptr = __be32_to_cpu(*ptr);
}
}
struct zfad_timetag {
uint32_t sec_low;
uint32_t sec_high;
uint32_t ticks;
uint32_t status;
};
static uint32_t zfad_dev_mem_offset(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
uint32_t dev_mem_off, trg_pos, pre_samp;
int nchan = FA100M14B4C_NCHAN;
struct zio_control *ctrl = cset->chan[nchan].current_ctrl;
/* get pre-samples from the current control (interleave chan) */
pre_samp = ctrl->attr_trigger.std_val[ZIO_ATTR_TRIG_PRE_SAMP];
/* Get trigger position in DDR */
trg_pos = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_POS]);
/*
* compute mem offset (in bytes): pre-samp is converted to
* bytes
*/
dev_mem_off = trg_pos - (pre_samp * cset->ssize * nchan);
dev_dbg(fa->msgdev,
"Trigger @ 0x%08x, pre_samp %i, offset 0x%08x\n",
trg_pos, pre_samp, dev_mem_off);
return dev_mem_off;
}
static unsigned int zfad_block_n_pages(struct zio_block *block)
{
unsigned int nr_pages;
long kaddr = (long)block->data;
nr_pages = ((kaddr & ~PAGE_MASK) + block->datalen + ~PAGE_MASK);
nr_pages >>= PAGE_SHIFT;
return nr_pages;
}
#ifdef CONFIG_FMC_ADC_SVEC
#define VME_NO_ADDR_INCREMENT 1
/* FIXME: move to include again */
#ifndef lower_32_bits
#define lower_32_bits(n) ((u32)(n))
#endif /* lower_32_bits */
static void build_dma_desc(struct vme_dma *desc, unsigned long vme_addr,
void *addr_dest, ssize_t len)
{
struct vme_dma_attr *vme;
struct vme_dma_attr *pci;
memset(desc, 0, sizeof(struct vme_dma));
vme = &desc->src;
pci = &desc->dst;
desc->dir = VME_DMA_FROM_DEVICE;
desc->length = len;
desc->novmeinc = VME_NO_ADDR_INCREMENT;
desc->ctrl.pci_block_size = VME_DMA_BSIZE_4096;
desc->ctrl.pci_backoff_time = VME_DMA_BACKOFF_0;
desc->ctrl.vme_block_size = VME_DMA_BSIZE_4096;
desc->ctrl.vme_backoff_time = VME_DMA_BACKOFF_0;
vme->data_width = VME_D32;
vme->am = VME_A24_USER_DATA_SCT;
/*vme->am = VME_A24_USER_MBLT;*/
vme->addru = upper_32_bits(vme_addr);
vme->addrl = lower_32_bits(vme_addr);
pci->addru = upper_32_bits((unsigned long)addr_dest);
pci->addrl = lower_32_bits((unsigned long)addr_dest);
}
#endif
static int zfad_dma_block_to_pages(struct page **pages, unsigned int nr_pages,
struct zio_block *block)
{
int i;
void *data = (void *) block->data;
if (is_vmalloc_addr(data)) {
for (i = 0; i < nr_pages; ++i)
pages[i] = vmalloc_to_page(data + PAGE_SIZE * i);
} else {
for (i = 0; i < nr_pages; ++i)
pages[i] = virt_to_page(data + PAGE_SIZE * i);
}
return 0;
}
static void zfad_dma_context_exit(struct zio_cset *cset,
struct zfad_block *zfad_block)
{
struct fa_dev *fa = cset->zdev->priv_d;
if (fa_is_flag_set(fa, FMC_ADC_SVEC)) {
__endianness(zfad_block->block->datalen,
zfad_block->block->data);
kfree(zfad_block->dma_ctx);
}
}
/**
* It initialize the DMA context for the given block transfer
*/
static int zfad_dma_context_init(struct zio_cset *cset,
struct zfad_block *zfad_block)
{
#ifdef CONFIG_FMC_ADC_SVEC
struct fa_dev *fa = cset->zdev->priv_d;
if (fa_is_flag_set(fa, FMC_ADC_SVEC))) {
struct fa_svec_data *svec_data = fa->carrier_data;
unsigned long vme_addr;
struct vme_dma *desc;
desc = kmalloc(sizeof(struct vme_dma), GFP_ATOMIC);
if (!desc)
return -ENOMEM;
if (zfad_block == cset->interleave->priv_d) {
/*
* Only for the first block:
* write the data address in the ddr_addr register: this
* address has been computed after ACQ_END by looking to the
* trigger position see fa-irq.c::irq_acq_end.
* Be careful: the SVEC HW version expects an address of 32bits word
* therefore mem-offset in byte is translated into 32bit word
*/
fa_writel(fa, svec_data->fa_dma_ddr_addr,
&fa_svec_regfield[FA_DMA_DDR_ADDR],
zfad_block->dev_mem_off/4);
}
zfad_block->dma_ctx = desc;
vme_addr = svec_data->vme_base + svec_data->fa_dma_ddr_data;
build_dma_desc(desc, vme_addr,
zfad_block->block->data,
zfad_block->block->datalen);
}
#endif
return 0;
}
/**
* The proper function from the DMA engine does not allow us to set
* the context, but we need it (e.g. VME bus)
*/
static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg_ctx(
struct dma_chan *chan,
struct scatterlist *sgl, unsigned int sg_len,
enum dma_transfer_direction dir,
unsigned long flags, void *ctx)
{
if (!(chan->device && chan->device->device_prep_slave_sg))
return NULL;
return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
DMA_DEV_TO_MEM, 0, ctx);
}
/**
* zfad_dma_complete
* @arg: data block instance
*
* It handles the data transfer completion of a block
*/
static void zfad_dma_complete(void *arg)
{
struct zfad_block *zfad_block = arg;
struct zio_cset *cset = zfad_block->cset;
struct fa_dev *fa = cset->zdev->priv_d;
/* Release DMA resources */
dma_unmap_sg(fa->msgdev,
zfad_block->sgt.sgl,
zfad_block->sgt.nents,
DMA_DEV_TO_MEM);
sg_free_table(&zfad_block->sgt);
/* Clean/fix the context */
zfad_dma_context_exit(cset, zfad_block);
/* Complete the full acquisition when the last transfer is over */
--fa->transfers_left;
if (!fa->transfers_left) {
dma_release_channel(zfad_block->tx->chan);
zfad_dma_done(cset);
}
}
/**
* zfad_dma_prep_slave_sg
* @dchan: DMA channel to use
* @cset: ZIO channel set that owns the data
* @zfad_block: data block instance to transfer
*
* It prepare the scatterlist for the block transfer and it submits it
* to the dma engine.
*/
static int zfad_dma_prep_slave_sg(struct dma_chan *dchan,
struct zio_cset *cset,
struct zfad_block *zfad_block)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct dma_async_tx_descriptor *tx;
struct page **pages;
unsigned int nr_pages, sg_mapped;
size_t max_segment_size;
int err;
/* prepare the context for the block transfer */
zfad_dma_context_init(cset, zfad_block);
/* Convert buffer to pages */
nr_pages = zfad_block_n_pages(zfad_block->block);
pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL);
if (!pages) {
err = -ENOMEM;
goto err_alloc_pages;
}
err = zfad_dma_block_to_pages(pages, nr_pages, zfad_block->block);
if (err)
goto err_to_pages;
max_segment_size = dma_get_max_seg_size(dchan->device->dev);
max_segment_size &= PAGE_MASK; /* to make alloc_table happy */
err = fa->sg_alloc_table_from_pages(&zfad_block->sgt, pages, nr_pages,
offset_in_page(zfad_block->block->data),
zfad_block->block->datalen,
max_segment_size, GFP_KERNEL);
if (unlikely(err))
goto err_sgt;
sg_mapped = dma_map_sg(&fa->pdev->dev,
zfad_block->sgt.sgl,
zfad_block->sgt.nents,
DMA_DEV_TO_MEM);
if (sg_mapped <= 0) {
err = sg_mapped;
goto err_map;
}
/* Prepare the DMA transmisison */
tx = dmaengine_prep_slave_sg_ctx(dchan, zfad_block->sgt.sgl, sg_mapped,
DMA_DEV_TO_MEM, 0, zfad_block->dma_ctx);
if (!tx) {
dev_err(&cset->head.dev,
"Failed to prepare dmaengine transfer descriptor\n");
return -EBUSY;
}
tx->callback = zfad_dma_complete;
tx->callback_param = (void *)zfad_block;
zfad_block->tx = tx;
/* Submit the DMA transmission to the DMA engine */
zfad_block->cookie = dmaengine_submit(tx);
if (zfad_block->cookie < 0) {
err = zfad_block->cookie;
goto err_submit;
}
/* we do not need the pages anymore */
kfree(pages);
return 0;
err_submit:
dma_unmap_sg(&fa->pdev->dev,
zfad_block->sgt.sgl,
zfad_block->sgt.nents,
DMA_DEV_TO_MEM);
err_map:
sg_free_table(&zfad_block->sgt);
err_sgt:
err_to_pages:
kfree(pages);
err_alloc_pages:
return err;
}
/**
* It matches a valid DMA channel
*/
static bool fa_dmaengine_filter(struct dma_chan *dchan, void *arg)
{
struct dma_device *ddev = dchan->device;
int dev_id = (*((int *)arg) >> 16) & 0xFFFF;
int chan_id = *((int *)arg) & 0xFFFF;
return ddev->dev_id == dev_id && dchan->chan_id == chan_id;
}
/**
* It maps the ZIO blocks with an sg table, then it starts the DMA transfer
* from the ADC to the host memory.
*
* @param cset
*/
static int zfad_dma_start(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct zfad_block *zfad_block = cset->interleave->priv_d;
struct dma_chan *dchan;
struct dma_slave_config sconfig;
dma_cap_mask_t dma_mask;
unsigned int data_offset;
int err, i, dma_dev_id;
struct resource *r;
r = platform_get_resource(fa->pdev, IORESOURCE_DMA, ADC_DMA);
if (!r) {
dev_err(&fa->pdev->dev, "Can't set find DMA channel\n");
return -ENODEV;
}
dma_dev_id = r->start;
err = fa_fsm_wait_state(fa, FA100M14B4C_STATE_IDLE, 10);
if (err) {
dev_warn(fa->msgdev,
"Can't start DMA on the last acquisition, "
"State Machine is not IDLE\n");
return err;
}
/*
* Disable all triggers to prevent fires between
* different DMA transfers required for multi-shots
*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC], 0);
dev_dbg(fa->msgdev, "Start DMA transfer\n");
dma_cap_zero(dma_mask);
dma_cap_set(DMA_SLAVE, dma_mask);
dma_cap_set(DMA_PRIVATE, dma_mask);
dchan = dma_request_channel(dma_mask, fa_dmaengine_filter, &dma_dev_id);
if (!dchan) {
err = -ENODEV;
goto err;
}
memset(&sconfig, 0, sizeof(sconfig));
sconfig.direction = DMA_DEV_TO_MEM;
sconfig.src_addr_width = 8; /* 2 bytes for each channel (4) */
data_offset = (cset->interleave->current_ctrl->ssize * cset->ti->nsamples) + FA_TRIG_TIMETAG_BYTES;
for (i = 0; i < fa->n_shots; ++i) {
/*
* TODO
* Let's see what to do with SVEC. SVEC need to set
* the DMA_DDR_ADDR in hardware before starting the DMA
* (it configures the DMA window).
* In single shot is not a big deal, in multishot we may have
* to issue_pending many time since we can't update the
* DMA_DDR_ADDR for each block submitted to the dma engine.
* But sice the blocks are contigous, perhaps there is no need
* because the address of shot 2 is exactly after shot 1
*/
if (!fa_is_flag_set(fa, FMC_ADC_SVEC) && fa->n_shots == 1)
sconfig.src_addr = zfad_dev_mem_offset(cset);
else
sconfig.src_addr = i * data_offset;
err = dmaengine_slave_config(dchan, &sconfig);
if (err)
goto err_config;
err = zfad_dma_prep_slave_sg(dchan, cset, &zfad_block[i]);
if (err)
goto err_prep;
}
fa->transfers_left = fa->n_shots;
dma_async_issue_pending(dchan);
return 0;
err_prep:
err_config:
dmaengine_terminate_all(dchan);
dma_release_channel(dchan);
err:
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC],
cset->ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value);
dev_err(fa->msgdev, "Failed to run a DMA transfer (%d)\n", err);
return err;
}
static void zfad_tstamp_start_get(struct fa_dev *fa,
struct zio_timestamp *ztstamp)
{
ztstamp->secs = fa_readl(fa, fa->fa_utc_base,
&zfad_regs[ZFA_UTC_ACQ_START_SECONDS_U]);
ztstamp->secs <<= 32;
ztstamp->secs |= fa_readl(fa, fa->fa_utc_base,
&zfad_regs[ZFA_UTC_ACQ_START_SECONDS_L]);
ztstamp->ticks = fa_readl(fa, fa->fa_utc_base,
&zfad_regs[ZFA_UTC_ACQ_START_COARSE]);
ztstamp->bins = 0;
}
static int zfad_block_timetag_extract(struct zio_block *block,
struct zfad_timetag *timetag)
{
struct zfad_timetag *tg;
tg = block->data + block->datalen - FA_TRIG_TIMETAG_BYTES;
if (unlikely((tg->sec_high >> 8) != 0xACCE55))
return -EINVAL;
/* resize the datalen, by removing the trigger tstamp */
block->datalen = block->datalen - FA_TRIG_TIMETAG_BYTES;
memcpy(timetag, tg, sizeof(*timetag));
return 0;
}
static void zfad_block_ctrl_tstamp_update(struct zio_block *block,
struct zfad_timetag *timetag)
{
struct zio_control *ctrl = zio_get_ctrl(block);
struct zio_timestamp *ztstamp = &ctrl->tstamp;
ztstamp->secs = ((uint64_t)timetag->sec_high & 0xFF) << 32;
ztstamp->secs |= timetag->sec_low;
ztstamp->ticks = timetag->ticks;
ztstamp->bins = 0;
}
static void zfad_block_ctrl_attr_update(struct zio_block *block,
struct zfad_timetag *timetag,
unsigned int seq_num)
{
struct zio_control *ctrl = zio_get_ctrl(block);
uint32_t *ext_val = ctrl->attr_channel.ext_val;
ext_val[FA100M14B4C_TATTR_STA]= timetag->status;
ctrl->seq_num = seq_num;
}
static void zfad_block_ctrl_tstamp_start_update(struct zio_block *block,
struct zio_timestamp *ztstamp)
{
struct zio_control *ctrl = zio_get_ctrl(block);
uint32_t *ext_val;
ext_val = ctrl->attr_channel.ext_val;
ext_val[FA100M14B4C_DATTR_ACQ_START_S] = ztstamp->secs;
ext_val[FA100M14B4C_DATTR_ACQ_START_C] = ztstamp->ticks;
ext_val[FA100M14B4C_DATTR_ACQ_START_F] = ztstamp->bins;
}
static void zfad_curr_ctrl_sync(struct zio_cset *cset,
struct zio_block *block)
{
struct zio_channel *interleave = cset->interleave;
struct zio_control *ctrl;
if (WARN(!block, "Missing block\n"))
return;
ctrl = zio_get_ctrl(block);
/* Sync the channel current control with the last ctrl block*/
memcpy(&interleave->current_ctrl->tstamp,
&ctrl->tstamp, sizeof(struct zio_timestamp));
interleave->current_ctrl->seq_num = ctrl->seq_num;
}
/**
* It completes a DMA transfer.
* It tells to the ZIO framework that all blocks are done. Then, it re-enable
* the trigger for the next acquisition. If the device is configured for
* continuous acquisition, the function automatically start the next
* acquisition
*
* @param cset
*/
static void zfad_dma_done(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct zfad_block *zfad_block = cset->interleave->priv_d;
struct zio_ti *ti = cset->ti;
struct zio_block *block = NULL;
struct zio_timestamp ztstamp;
int i;
/*
* Lower CSET_HW_BUSY
*/
spin_lock(&cset->lock);
cset->flags &= ~ZIO_CSET_HW_BUSY;
spin_unlock(&cset->lock);
/* for each shot, set the timetag of each ctrl block by reading the
* trig-timetag appended after the samples. Set also the acquisition
* start timetag on every blocks
*/
zfad_tstamp_start_get(fa, &ztstamp);
for (i = 0; i < fa->n_shots; ++i) {
struct zfad_timetag timetag;
int err;
block = zfad_block[i].block;
err = zfad_block_timetag_extract(block, &timetag);
if (err)
memset(&timetag, 0, sizeof(timetag));
zfad_block_ctrl_tstamp_start_update(block, &ztstamp);
zfad_block_ctrl_tstamp_update(block, &timetag);
zfad_block_ctrl_attr_update(block, &timetag, i);
}
zfad_curr_ctrl_sync(cset, block);
/*
* All DMA transfers done! Inform the trigger about this, so
* it can store blocks into the buffer
*/
dev_dbg(fa->msgdev, "%i blocks transfered\n", fa->n_shots);
zio_trigger_data_done(cset);
/* we can safely re-enable triggers */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC],
ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value);
if (fa->enable_auto_start) {
/* Automatic start next acquisition */
dev_dbg(fa->msgdev, "Automatic start\n");
zfad_fsm_command(fa, FA100M14B4C_CMD_START);
}
}
/**
* It handles the error condition of a DMA transfer.
* The function turn off the state machine by sending the STOP command
*
* @param cset
*/
static void zfad_dma_error(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
/*
* Lower CSET_HW_BUSY
*/
spin_lock(&cset->lock);
cset->flags &= ~ZIO_CSET_HW_BUSY;
spin_unlock(&cset->lock);
zfad_fsm_command(fa, FA100M14B4C_CMD_STOP);
fa->n_dma_err++;
/* FIXME stop pending */
//dmaengine_terminate_all();
if (fa->n_fires == 0)
dev_err(fa->msgdev,
"DMA error occurs but no block was acquired\n");
}
/*
* job executed within a work thread
* Depending of the carrier the job slightly differs:
* SVEC: dma_start() blocks till the the DMA ends
* (fully managed by the vmebus driver)
* Therefore the DMA outcome can be processed immediately
* SPEC: dma_start() launch the job an returns immediately.
* An interrupt DMA_DONE or ERROR is expecting to signal the end
* of the DMA transaction
* (See fa-spec-irq.c::fa-spec_irq_handler)
*/
void fa_irq_work(struct work_struct *work)
{
struct fa_dev *fa = container_of(work, struct fa_dev, irq_work);
struct zio_cset *cset = fa->zdev->cset;
int res;
/*
* This check is not crucial because the HW implements
* a solid state machine and acq-end can happens only after
* the execution of the n requested shots.
*/
fa->n_fires = fa->n_shots - fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_SHOTS_REM]);
if (fa->n_fires != fa->n_shots) {
dev_err(fa->msgdev,
"Expected %i trigger fires, but %i occurs\n",
fa->n_shots, fa->n_fires);
}
res = zfad_dma_start(cset);
if (res) {
/* Stop acquisition on error */
zfad_dma_error(cset);
}
}
......@@ -11,270 +11,9 @@
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include "fmc-adc-100m14b4cha.h"
#include "fa-spec.h"
/**
* It maps the ZIO blocks with an sg table, then it starts the DMA transfer
* from the ADC to the host memory.
*
* @param cset
*/
int zfad_dma_start(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct zfad_block *zfad_block = cset->interleave->priv_d;
uint32_t dev_mem_off, trg_pos, pre_samp;
uint32_t val = 0;
int try = 5, err;
/*
* All programmed triggers fire, so the acquisition is ended.
* If the state machine is _idle_ we can start the DMA transfer.
* If the state machine it is not idle, try again 5 times
*/
while (try-- && val != FA100M14B4C_STATE_IDLE) {
/* udelay(2); */
val = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_STA_FSM]);
}
if (val != FA100M14B4C_STATE_IDLE) {
/* we can't DMA if the state machine is not idle */
dev_warn(fa->msgdev,
"Can't start DMA on the last acquisition, "
"State Machine is not IDLE (status:%d)\n", val);
return -EBUSY;
}
/*
* Disable all triggers to prevent fires between
* different DMA transfers required for multi-shots
*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC], 0);
/* Fix dev_mem_addr in single-shot mode */
if (fa->n_shots == 1) {
int nchan = FA100M14B4C_NCHAN;
struct zio_control *ctrl = cset->chan[nchan].current_ctrl;
/* get pre-samples from the current control (interleave chan) */
pre_samp = ctrl->attr_trigger.std_val[ZIO_ATTR_TRIG_PRE_SAMP];
/* Get trigger position in DDR */
trg_pos = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_POS]);
/*
* compute mem offset (in bytes): pre-samp is converted to
* bytes
*/
dev_mem_off = trg_pos - (pre_samp * cset->ssize * nchan);
dev_dbg(fa->msgdev,
"Trigger @ 0x%08x, pre_samp %i, offset 0x%08x\n",
trg_pos, pre_samp, dev_mem_off);
zfad_block[0].dev_mem_off = dev_mem_off;
}
dev_dbg(fa->msgdev, "Start DMA transfer\n");
err = fa->carrier_op->dma_start(cset);
if (err)
return err;
return 0;
}
/**
* It completes a DMA transfer.
* It tells to the ZIO framework that all blocks are done. Then, it re-enable
* the trigger for the next acquisition. If the device is configured for
* continuous acquisition, the function automatically start the next
* acquisition
*
* @param cset
*/
void zfad_dma_done(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct zio_channel *interleave = cset->interleave;
struct zfad_block *zfad_block = interleave->priv_d;
struct zio_control *ctrl = NULL;
struct zio_ti *ti = cset->ti;
struct zio_block *block;
struct zio_timestamp ztstamp;
int i;
uint32_t *trig_timetag;
fa->carrier_op->dma_done(cset);
/* for each shot, set the timetag of each ctrl block by reading the
* trig-timetag appended after the samples. Set also the acquisition
* start timetag on every blocks
*/
ztstamp.secs = fa_readl(fa, fa->fa_utc_base,
&zfad_regs[ZFA_UTC_ACQ_START_SECONDS]);
ztstamp.ticks = fa_readl(fa, fa->fa_utc_base,
&zfad_regs[ZFA_UTC_ACQ_START_COARSE]);
ztstamp.bins = fa_readl(fa, fa->fa_utc_base,
&zfad_regs[ZFA_UTC_ACQ_START_FINE]);
for (i = 0; i < fa->n_shots; ++i) {
block = zfad_block[i].block;
ctrl = zio_get_ctrl(block);
trig_timetag = (uint32_t *)(block->data + block->datalen
- FA_TRIG_TIMETAG_BYTES);
if (unlikely((*(trig_timetag + 1) >> 8) != 0xACCE55))
dev_err(fa->msgdev,
"Wrong acquisition TAG, expected 0xACCE55 but got 0x%X (0x%X)\n",
(*(trig_timetag + 1) >> 8), *trig_timetag);
ctrl->tstamp.secs = ((uint64_t)*(trig_timetag + 1) & 0xFF) << 32;
ctrl->tstamp.secs |= *(trig_timetag);
ctrl->tstamp.ticks = *(trig_timetag + 2);
ctrl->tstamp.bins = 0;
ctrl->attr_trigger.ext_val[FA100M14B4C_TATTR_STA]= *(trig_timetag + 3);
/* Acquisition start Timetag */
ctrl->attr_channel.ext_val[FA100M14B4C_DATTR_ACQ_START_S] =
ztstamp.secs;
ctrl->attr_channel.ext_val[FA100M14B4C_DATTR_ACQ_START_C] =
ztstamp.ticks;
ctrl->attr_channel.ext_val[FA100M14B4C_DATTR_ACQ_START_F] =
ztstamp.bins;
/* resize the datalen, by removing the trigger tstamp */
block->datalen = block->datalen - FA_TRIG_TIMETAG_BYTES;
/* update seq num */
ctrl->seq_num = i;
}
/* Sync the channel current control with the last ctrl block*/
memcpy(&interleave->current_ctrl->tstamp,
&ctrl->tstamp, sizeof(struct zio_timestamp));
/* Update sequence number */
interleave->current_ctrl->seq_num = ctrl->seq_num;
/*
* All DMA transfers done! Inform the trigger about this, so
* it can store blocks into the buffer
*/
dev_dbg(fa->msgdev, "%i blocks transfered\n", fa->n_shots);
zio_trigger_data_done(cset);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC],
ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value);
}
/**
* It handles the error condition of a DMA transfer.
* The function turn off the state machine by sending the STOP command
*
* @param cset
*/
void zfad_dma_error(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
fa->carrier_op->dma_error(cset);
zfad_fsm_command(fa, FA100M14B4C_CMD_STOP);
fa->n_dma_err++;
if (fa->n_fires == 0)
dev_err(fa->msgdev,
"DMA error occurs but no block was acquired\n");
}
/*
* zfat_irq_acq_end
* @fa: fmc-adc descriptor
*
* The ADC end the acquisition, so, if the state machine is idle, we can
* retrieve data from the ADC DDR memory.
*/
void zfat_irq_acq_end(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
dev_dbg(fa->msgdev, "Acquisition done\n");
/*
* because the driver doesn't listen anymore trig-event
* we agreed that the HW will provide a dedicated register
* to check the real number of shots in order to compare it
* with the requested one.
* This ultimate check is not crucial because the HW implements
* a solid state machine and acq-end can happens only after
* the execution of the n requested shots.
*/
fa->n_fires = fa->n_shots - fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_SHOTS_REM]);
if (fa->n_fires != fa->n_shots) {
dev_err(fa->msgdev,
"Expected %i trigger fires, but %i occurs\n",
fa->n_shots, fa->n_fires);
}
}
/*
* job executed within a work thread
* Depending of the carrier the job slightly differs:
* SVEC: dma_start() blocks till the the DMA ends
* (fully managed by the vmebus driver)
* Therefore the DMA outcome can be processed immediately
* SPEC: dma_start() launch the job an returns immediately.
* An interrupt DMA_DONE or ERROR is expecting to signal the end
* of the DMA transaction
* (See fa-spec-irq.c::fa-spec_irq_handler)
*/
static void fa_irq_work(struct work_struct *work)
{
struct fa_dev *fa = container_of(work, struct fa_dev, irq_work);
struct zio_cset *cset = fa->zdev->cset;
int res;
zfat_irq_acq_end(cset);
res = zfad_dma_start(cset);
if (!res) {
/*
* No error.
* If there is an IRQ DMA src to notify the ends of the DMA,
* leave the workqueue.
* dma_done will be proceed on DMA_END reception.
* Otherwhise call dma_done in sequence
*/
if (fa->irq_src & FA_IRQ_SRC_DMA)
/*
* waiting for END_OF_DMA IRQ
* with the CSET_BUSY flag Raised
* The flag will be lowered by the irq_handler
* handling END_DMA
*/
goto end;
zfad_dma_done(cset);
}
/*
* Lower CSET_HW_BUSY
*/
spin_lock(&cset->lock);
cset->flags &= ~ZIO_CSET_HW_BUSY;
spin_unlock(&cset->lock);
end:
if (res) {
/* Stop acquisition on error */
zfad_dma_error(cset);
} else if (fa->enable_auto_start) {
/* Automatic start next acquisition */
dev_dbg(fa->msgdev, "Automatic start\n");
zfad_fsm_command(fa, FA100M14B4C_CMD_START);
}
/* ack the irq */
fmc_irq_ack(fa->fmc);
}
/*
* fat_get_irq_status
......@@ -284,25 +23,23 @@ end:
* Get irq and clear the register. To clear an interrupt we have to write 1
* on the handled interrupt. We handle all interrupt so we clear all interrupts
*/
static void fa_get_irq_status(struct fa_dev *fa, int irq_core_base,
uint32_t *irq_status)
static void fa_get_irq_status(struct fa_dev *fa, uint32_t *irq_status)
{
/* Get current interrupts status */
*irq_status = fa_readl(fa, irq_core_base, &zfad_regs[ZFA_IRQ_ADC_SRC]);
*irq_status = fa_readl(fa, fa->fa_irq_adc_base, &zfad_regs[ZFA_IRQ_ADC_SRC]);
dev_dbg(fa->msgdev,
"IRQ 0x%x fired an interrupt. IRQ status register: 0x%x\n",
irq_core_base, *irq_status);
"IRQ fired an interrupt. IRQ status register: 0x%x\n",
*irq_status);
if (*irq_status)
/* Clear current interrupts status */
fa_writel(fa, irq_core_base, &zfad_regs[ZFA_IRQ_ADC_SRC],
fa_writel(fa, fa->fa_irq_adc_base, &zfad_regs[ZFA_IRQ_ADC_SRC],
*irq_status);
}
/*
* fa_irq_handler
* @irq:
* @ptr: pointer to fmc_device
* @arg: pointer to fa_dev
*
* The ADC svec firmware fires interrupt from a single wishbone core
* and throught the VIC ACQ_END and TRIG events. Note about "TRIG"
......@@ -314,24 +51,22 @@ static void fa_get_irq_status(struct fa_dev *fa, int irq_core_base,
* of small number of samples and makes the retry loop in the hanlder
* obsolete.
*/
irqreturn_t fa_irq_handler(int irq_core_base, void *dev_id)
irqreturn_t fa_irq_handler(int irq, void *arg)
{
struct fmc_device *fmc = dev_id;
struct fa_dev *fa = fmc_get_drvdata(fmc);
struct fa_dev *fa = arg;
struct zio_cset *cset = fa->zdev->cset;
uint32_t status;
unsigned long flags;
struct zfad_block *zfad_block;
/* irq to handle */
fa_get_irq_status(fa, irq_core_base, &status);
fa_get_irq_status(fa, &status);
if (!status)
return IRQ_NONE; /* No interrupt fired by this mezzanine */
dev_dbg(fa->msgdev, "Handle ADC interrupts fmc slot: %d\n",
fmc->slot_id);
if (status & FA_IRQ_ADC_ACQ_END) {
dev_dbg(fa->msgdev, "Handle ADC interrupts\n");
/*
* Acquiring samples is a critical section
* protected against any concurrent abbort trigger.
......@@ -352,14 +87,12 @@ irqreturn_t fa_irq_handler(int irq_core_base, void *dev_id)
queue_work(fa_workqueue, &fa->irq_work);
/* register the core firing the IRQ in order to */
/* check right IRQ seq.: ACQ_END followed by DMA_END */
fa->last_irq_core_src = irq_core_base;
} else /* current Acquiistion has been stopped */
fmc_irq_ack(fmc);
fa->last_irq_core_src = irq;
}
} else { /* unexpected interrupt we have to ack anyway */
dev_err(fa->msgdev,
"%s unexpected interrupt 0x%x\n",
__func__, status);
fmc_irq_ack(fmc);
}
return IRQ_HANDLED;
......@@ -368,49 +101,28 @@ irqreturn_t fa_irq_handler(int irq_core_base, void *dev_id)
int fa_setup_irqs(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
struct resource *r;
int err;
/* Request IRQ */
dev_dbg(fa->msgdev, "%s request irq fmc slot: %d\n",
__func__, fa->fmc->slot_id);
/* VIC svec setup */
fa_writel(fa, fa->fa_irq_vic_base,
&zfad_regs[ZFA_IRQ_VIC_CTRL],
0x3);
fa_writel(fa, fa->fa_irq_vic_base,
&zfad_regs[ZFA_IRQ_VIC_ENABLE_MASK],
0x3);
/* trick : vic needs the base address of teh core firing the irq
* It cannot provided throught irq_request() call therefore the trick
* is to set it by means of the field irq provided by the fmc device
*/
fmc->irq = fa->fa_irq_adc_base;
err = fmc_irq_request(fmc, fa_irq_handler,
"fmc-adc-100m14b",
0 /*VIC is used */);
if (err) {
dev_err(fa->msgdev, "can't request irq %i (error %i)\n",
fa->fmc->irq, err);
dev_dbg(fa->msgdev, "Request irq\n");
r = platform_get_resource(fa->pdev, IORESOURCE_IRQ, ADC_IRQ_TRG);
err = request_any_context_irq(r->start, fa_irq_handler, 0,
r->name, fa);
if (err < 0) {
dev_err(fa->msgdev, "can't request irq %lli (error %i)\n",
r->start, err);
return err;
}
/* workqueue is required to execute DMA transaction */
INIT_WORK(&fa->irq_work, fa_irq_work);
/* set IRQ sources to listen */
fa->irq_src = FA_IRQ_SRC_ACQ;
if (fa->carrier_op->setup_irqs)
err = fa->carrier_op->setup_irqs(fa);
return err;
}
int fa_free_irqs(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
/*
* When we unload the driver the FPGA is still running so it may
* rises interrupts. Disable IRQs in order to prevent spurious
......@@ -418,42 +130,26 @@ int fa_free_irqs(struct fa_dev *fa)
*/
fa_disable_irqs(fa);
/* Release carrier IRQs (if any) */
if (fa->carrier_op->free_irqs)
fa->carrier_op->free_irqs(fa);
/* Release ADC IRQs */
fmc->irq = fa->fa_irq_adc_base;
fmc_irq_free(fmc);
free_irq(platform_get_irq(fa->pdev, ADC_IRQ_TRG), fa);
return 0;
}
int fa_enable_irqs(struct fa_dev *fa)
{
dev_dbg(fa->msgdev, "%s Enable interrupts fmc slot:%d\n",
__func__, fa->fmc->slot_id);
fa_writel(fa, fa->fa_irq_adc_base,
&zfad_regs[ZFA_IRQ_ADC_ENABLE_MASK],
FA_IRQ_ADC_ACQ_END);
if (fa->carrier_op->enable_irqs)
fa->carrier_op->enable_irqs(fa);
return 0;
}
int fa_disable_irqs(struct fa_dev *fa)
{
dev_dbg(fa->msgdev, "%s Disable interrupts fmc slot:%d\n",
__func__, fa->fmc->slot_id);
fa_writel(fa, fa->fa_irq_adc_base,
&zfad_regs[ZFA_IRQ_ADC_DISABLE_MASK],
FA_IRQ_ADC_ACQ_END);
if (fa->carrier_op->disable_irqs)
fa->carrier_op->disable_irqs(fa);
return 0;
}
......
......@@ -18,10 +18,13 @@ const struct zfa_field_desc zfad_regs[] = {
[ZFA_CTL_TRIG_LED] = {0x00, 0x00000040, 1},
[ZFA_CTL_ACQ_LED] = {0x00, 0x00000080, 1},
[ZFA_CTL_RST_TRG_STA] = {0x00, 0x00000100, 1},
[ZFA_CTL_CALIB_APPLY] = {0x00, 0x00008000, 1},
/* Status registers */
[ZFA_STA_FSM] = {0x04, 0x00000007, 1},
[ZFA_STA_SERDES_PLL] = {0x04, 0x00000008, 1},
[ZFA_STA_SERDES_SYNCED] = {0x04, 0x00000010, 1},
[ZFA_STA_FMC_NR] = {0x04, 0x000000c0, 1},
[ZFA_STA_CALIB_BUSY] = {0x04, 0x00008000, 1},
/* Trigger */
/* Config register */
[ZFAT_CFG_STA] = {0x08, 0xFFFFFFFF, 0},
......@@ -32,67 +35,67 @@ const struct zfa_field_desc zfad_regs[] = {
/* Software */
[ZFAT_SW] = {0x18, 0xFFFFFFFF, 0},
/* Number of shots */
[ZFAT_SHOTS_NB] = {0x1C, 0x0000FFFF, 0},
[ZFAT_SHOTS_NB] = {0x1C, 0x0000FFFF, 1},
/* Remaining shots counter */
[ZFAT_SHOTS_REM] = {0x1C, 0xFFFF0000, 1},
/* Multishot max samples*/
[ZFA_MULT_MAX_SAMP] = {0x20, 0xFFFFFFFF, 0},
/* Remaining shots counter */
[ZFAT_SHOTS_REM] = {0x24, 0x0000FFFF, 0},
/* Position address */
[ZFAT_POS] = {0x28, 0xFFFFFFFF, 0},
[ZFAT_POS] = {0x24, 0xFFFFFFFF, 0},
/* Sampling clock frequency */
[ZFAT_SAMPLING_HZ] = {0x2C, 0xFFFFFFFF, 0},
[ZFAT_SAMPLING_HZ] = {0x28, 0xFFFFFFFF, 0},
/* Sample rate */
[ZFAT_SR_UNDER] = {0x30, 0xFFFFFFFF, 0},
[ZFAT_SR_UNDER] = {0x2C, 0xFFFFFFFF, 0},
/* Pre-sample */
[ZFAT_PRE] = {0x34, 0xFFFFFFFF, 0},
[ZFAT_PRE] = {0x30, 0xFFFFFFFF, 0},
/* Post-sample */
[ZFAT_POST] = {0x38, 0xFFFFFFFF, 0},
[ZFAT_POST] = {0x34, 0xFFFFFFFF, 0},
/* Sample counter */
[ZFAT_CNT] = {0x3C, 0xFFFFFFFF, 0},
[ZFAT_CNT] = {0x38, 0xFFFFFFFF, 0},
/* Channel 1 */
[ZFA_CH1_CTL_RANGE] = {0x80, 0x00000077, 1},
[ZFA_CH1_CTL_TERM] = {0x80, 0x00000008, 1},
[ZFA_CH1_STA] = {0x84, 0x0000FFFF, 0},
[ZFA_CH1_GAIN] = {0x88, 0x0000FFFF, 0},
[ZFA_CH1_OFFSET] = {0x8C, 0x0000FFFF, 0},
[ZFA_CH1_SAT] = {0x90, 0x00007FFF, 0},
[ZFA_CH1_HYST] = {0x94, 0xFFFF0000, 1},
[ZFA_CH1_THRES] = {0x94, 0x0000FFFF, 1},
[ZFA_CH1_DLY] = {0x98, 0xFFFFFFFF, 0},
[ZFA_CH1_GAIN] = {0x88, 0x0000FFFF, 1},
[ZFA_CH1_OFFSET] = {0x88, 0xFFFF0000, 1},
[ZFA_CH1_SAT] = {0x8C, 0x00007FFF, 0},
[ZFA_CH1_HYST] = {0x90, 0xFFFF0000, 1},
[ZFA_CH1_THRES] = {0x90, 0x0000FFFF, 1},
[ZFA_CH1_DLY] = {0x94, 0xFFFFFFFF, 0},
/* Channel 2 */
[ZFA_CH2_CTL_RANGE] = {0x100, 0x00000077, 1},
[ZFA_CH2_CTL_TERM] = {0x100, 0x00000008, 1},
[ZFA_CH2_STA] = {0x104, 0x0000FFFF, 0},
[ZFA_CH2_GAIN] = {0x108, 0x0000FFFF, 0},
[ZFA_CH2_OFFSET] = {0x10C, 0x0000FFFF, 0},
[ZFA_CH2_SAT] = {0x110, 0x00007FFF, 0},
[ZFA_CH2_HYST] = {0x114, 0xFFFF0000, 1},
[ZFA_CH2_THRES] = {0x114, 0x0000FFFF, 1},
[ZFA_CH2_DLY] = {0x118, 0xFFFFFFFF, 0},
[ZFA_CH2_CTL_RANGE] = {0xC0, 0x00000077, 1},
[ZFA_CH2_CTL_TERM] = {0xC0, 0x00000008, 1},
[ZFA_CH2_STA] = {0xC4, 0x0000FFFF, 0},
[ZFA_CH2_GAIN] = {0xC8, 0x0000FFFF, 1},
[ZFA_CH2_OFFSET] = {0xC8, 0xFFFF0000, 1},
[ZFA_CH2_SAT] = {0xCC, 0x00007FFF, 0},
[ZFA_CH2_HYST] = {0xD0, 0xFFFF0000, 1},
[ZFA_CH2_THRES] = {0xD0, 0x0000FFFF, 1},
[ZFA_CH2_DLY] = {0xD4, 0xFFFFFFFF, 0},
/* Channel 3 */
[ZFA_CH3_CTL_RANGE] = {0x180, 0x00000077, 1},
[ZFA_CH3_CTL_TERM] = {0x180, 0x00000008, 1},
[ZFA_CH3_STA] = {0x184, 0x0000FFFF, 0},
[ZFA_CH3_GAIN] = {0x188, 0x0000FFFF, 0},
[ZFA_CH3_OFFSET] = {0x18C, 0x0000FFFF, 0},
[ZFA_CH3_SAT] = {0x190, 0x00007FFF, 0},
[ZFA_CH3_HYST] = {0x194, 0xFFFF0000, 1},
[ZFA_CH3_THRES] = {0x194, 0x0000FFFF, 1},
[ZFA_CH3_DLY] = {0x198, 0xFFFFFFFF, 0},
[ZFA_CH3_CTL_RANGE] = {0x100, 0x00000077, 1},
[ZFA_CH3_CTL_TERM] = {0x100, 0x00000008, 1},
[ZFA_CH3_STA] = {0x104, 0x0000FFFF, 0},
[ZFA_CH3_GAIN] = {0x108, 0x0000FFFF, 1},
[ZFA_CH3_OFFSET] = {0x108, 0xFFFF0000, 1},
[ZFA_CH3_SAT] = {0x10C, 0x00007FFF, 0},
[ZFA_CH3_HYST] = {0x110, 0xFFFF0000, 1},
[ZFA_CH3_THRES] = {0x110, 0x0000FFFF, 1},
[ZFA_CH3_DLY] = {0x114, 0xFFFFFFFF, 0},
/* Channel 4 */
[ZFA_CH4_CTL_RANGE] = {0x200, 0x00000077, 1},
[ZFA_CH4_CTL_TERM] = {0x200, 0x00000008, 1},
[ZFA_CH4_STA] = {0x204, 0x0000FFFF, 0},
[ZFA_CH4_GAIN] = {0x208, 0x0000FFFF, 0},
[ZFA_CH4_OFFSET] = {0x20C, 0x0000FFFF, 0},
[ZFA_CH4_SAT] = {0x210, 0x00007FFF, 0},
[ZFA_CH4_HYST] = {0x214, 0xFFFF0000, 1},
[ZFA_CH4_THRES] = {0x214, 0x0000FFFF, 1},
[ZFA_CH4_DLY] = {0x218, 0xFFFFFFFF, 0},
[ZFA_CH4_CTL_RANGE] = {0x140, 0x00000077, 1},
[ZFA_CH4_CTL_TERM] = {0x140, 0x00000008, 1},
[ZFA_CH4_STA] = {0x144, 0x0000FFFF, 0},
[ZFA_CH4_GAIN] = {0x148, 0x0000FFFF, 1},
[ZFA_CH4_OFFSET] = {0x148, 0xFFFF0000, 1},
[ZFA_CH4_SAT] = {0x14C, 0x00007FFF, 0},
[ZFA_CH4_HYST] = {0x150, 0xFFFF0000, 1},
[ZFA_CH4_THRES] = {0x150, 0x0000FFFF, 1},
[ZFA_CH4_DLY] = {0x154, 0xFFFFFFFF, 0},
/* IRQ */
[ZFA_IRQ_ADC_DISABLE_MASK] = {0x00, 0x00000003, 0},
......@@ -104,23 +107,29 @@ const struct zfa_field_desc zfad_regs[] = {
[ZFA_IRQ_VIC_DISABLE_MASK] = {0x0C, 0x00000003, 0},
[ZFA_IRQ_VIC_MASK_STATUS] = {0x10, 0x00000003, 0},
/* DS18B20 UID/Temperature */
[ZFA_DS18B20_ID_U] = {0x00, 0xFFFFFFFF, 0},
[ZFA_DS18B20_ID_L] = {0x04, 0xFFFFFFFF, 0},
[ZFA_DS18B20_TEMP] = {0x08, 0x0000FFFF, 0},
[ZFA_DS18B20_STAT] = {0x0C, 0x00000003, 0},
/* UTC */
[ZFA_UTC_SECONDS] = {0x00, ~0x0, 0},
[ZFA_UTC_COARSE] = {0x04, ~0x0, 0},
[ZFA_UTC_TRIG_META] = {0x08, ~0x0, 0},
[ZFA_UTC_TRIG_SECONDS] = {0x0C, ~0x0, 0},
[ZFA_UTC_TRIG_COARSE] = {0x10, ~0x0, 0},
[ZFA_UTC_TRIG_FINE] = {0x14, ~0x0, 0},
[ZFA_UTC_ACQ_START_META] = {0x18, ~0x0, 0},
[ZFA_UTC_ACQ_START_SECONDS] = {0x1C, ~0x0, 0},
[ZFA_UTC_ACQ_START_COARSE] = {0x20, ~0x0, 0},
[ZFA_UTC_ACQ_START_FINE] = {0x24, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_META] = {0x28, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_SECONDS] = {0x2C, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_COARSE] = {0x30, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_FINE] = {0x34, ~0x0, 0},
[ZFA_UTC_ACQ_END_META] = {0x38, ~0x0, 0},
[ZFA_UTC_ACQ_END_SECONDS] = {0x3C, ~0x0, 0},
[ZFA_UTC_ACQ_END_COARSE] = {0x40, ~0x0, 0},
[ZFA_UTC_ACQ_END_FINE] = {0x44, ~0x0, 0},
[ZFA_UTC_SECONDS_U] = {0x00, ~0x0, 0},
[ZFA_UTC_SECONDS_L] = {0x04, ~0x0, 0},
[ZFA_UTC_COARSE] = {0x08, ~0x0, 0},
[ZFA_UTC_TRIG_TIME_SECONDS_U] = {0x0C, ~0x0, 0},
[ZFA_UTC_TRIG_TIME_SECONDS_L] = {0x10, ~0x0, 0},
[ZFA_UTC_TRIG_TIME_COARSE] = {0x14, ~0x0, 0},
[ZFA_UTC_TRIG_SECONDS_U] = {0x18, ~0x0, 0},
[ZFA_UTC_TRIG_SECONDS_L] = {0x1C, ~0x0, 0},
[ZFA_UTC_TRIG_COARSE] = {0x20, ~0x0, 0},
[ZFA_UTC_ACQ_START_SECONDS_U] = {0x24, ~0x0, 0},
[ZFA_UTC_ACQ_START_SECONDS_L] = {0x28, ~0x0, 0},
[ZFA_UTC_ACQ_START_COARSE] = {0x2C, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_SECONDS_U] = {0x30, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_SECONDS_L] = {0x34, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_COARSE] = {0x38, ~0x0, 0},
[ZFA_UTC_ACQ_END_SECONDS_U] = {0x3C, ~0x0, 0},
[ZFA_UTC_ACQ_END_SECONDS_L] = {0x40, ~0x0, 0},
[ZFA_UTC_ACQ_END_COARSE] = {0x44, ~0x0, 0},
};
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2012-2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include "fmc-adc-100m14b4cha.h"
#include "fa-spec.h"
static char *fa_spec_get_gwname(void)
{
return FA_GATEWARE_SPEC;
}
static int fa_spec_init(struct fa_dev *fa)
{
struct fa_spec_data *cdata;
uint32_t val;
fa->fa_carrier_csr_base = fmc_find_sdb_device(fa->fmc->sdb, 0xce42,
0x603, NULL);
cdata = kzalloc(sizeof(struct fa_spec_data), GFP_KERNEL);
if (!cdata)
return -ENOMEM;
/* SDB carrier specific */
cdata->fa_dma_base =
fmc_find_sdb_device(fa->fmc->sdb, 0xce42, 0x601, NULL);
cdata->fa_irq_dma_base =
fmc_find_sdb_device(fa->fmc->sdb, 0xce42, 0xd5735ab4, NULL);
dev_info(fa->msgdev,
"Spec Base addrs: irq_dmma:0x%x, dma_ctrl:0x%x, csr:0x%x\n",
cdata->fa_irq_dma_base, cdata->fa_dma_base,
fa->fa_carrier_csr_base);
/* Reset the FMC slot */
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_spec_regs[ZFA_CAR_FMC_RES], 1);
mdelay(50);
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_spec_regs[ZFA_CAR_FMC_RES], 0);
mdelay(50);
/* Verify that the FMC is plugged (0 is plugged) */
val = fa_readl(fa, fa->fa_carrier_csr_base,
&fa_spec_regs[ZFA_CAR_FMC_PRES]);
if (val) {
dev_err(fa->msgdev, "No FCM ADC plugged\n");
return -ENODEV;
}
/* Verify that system PLL is locked (1 is calibrated) */
val = fa_readl(fa, fa->fa_carrier_csr_base,
&fa_spec_regs[ZFA_CAR_SYS_PLL]);
if (!val) {
dev_err(fa->msgdev, "System PLL not locked\n");
return -ENODEV;
}
/* Verify that DDR3 calibration is done (1 is calibrated) */
val = fa_readl(fa, fa->fa_carrier_csr_base,
&fa_spec_regs[ZFA_CAR_DDR_CAL]);
if (!val) {
dev_err(fa->msgdev, "DDR3 Calibration not done\n");
return -ENODEV;
}
/* Set DMA to transfer data from device to host */
fa_writel(fa, cdata->fa_dma_base,
&fa_spec_regs[ZFA_DMA_BR_DIR], 0);
/* register carrier data */
fa->carrier_data = cdata;
dev_info(fa->msgdev, "spec::%s successfully executed\n", __func__);
return 0;
}
static int fa_spec_reset(struct fa_dev *fa)
{
/*struct spec_dev *spec = fa->fmc->carrier_data;*/
dev_info(fa->msgdev, "%s: resetting ADC core through Gennum.\n",
__func__);
return 0;
}
static void fa_spec_exit(struct fa_dev *fa)
{
kfree(fa->carrier_data);
}
/* Unfortunately, on the spec this is GPIO9, i.e. IRQ(1) */
static struct fmc_gpio fa_gpio_on[] = {
{
.gpio = FMC_GPIO_IRQ(0),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}
};
static struct fmc_gpio fa_gpio_off[] = {
{
.gpio = FMC_GPIO_IRQ(0),
.mode = GPIOF_DIR_IN,
.irqmode = 0,
}
};
static int fa_spec_setup_irqs(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
struct fa_spec_data *spec_data = fa->carrier_data;
int err;
/* Request IRQ
* trick : vic needs the base address of teh core firing the irq
* It cannot provided throught irq_request() call therefore the trick
* is to set it by means of the field irq provided by the fmc device
*/
fmc->irq = spec_data->fa_irq_dma_base;
err = fmc_irq_request(fmc, fa_spec_irq_handler,
"fmc-adc-100m14b", 0);
if (err) {
dev_err(fa->msgdev, "can't request irq 0x%x (error %i)\n",
fmc->irq, err);
return err;
}
fmc_gpio_config(fmc, fa_gpio_on, ARRAY_SIZE(fa_gpio_on));
dev_info(fa->msgdev, "spec::%s successfully executed\n", __func__);
/* Add SPEC specific IRQ sources to listen */
fa->irq_src |= FA_IRQ_SRC_DMA;
return 0;
}
static int fa_spec_free_irqs(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
struct fa_spec_data *spec_data = fa->carrier_data;
/* Release DMA IRQs */
fmc->irq = spec_data->fa_irq_dma_base;
fmc_irq_free(fmc);
fmc_gpio_config(fmc, fa_gpio_off, ARRAY_SIZE(fa_gpio_off));
return 0;
}
static int fa_spec_enable_irqs(struct fa_dev *fa)
{
struct fa_spec_data *spec_data = fa->carrier_data;
fa_writel(fa, spec_data->fa_irq_dma_base,
&fa_spec_regs[ZFA_IRQ_DMA_ENABLE_MASK],
FA_SPEC_IRQ_DMA_ALL);
return 0;
}
static int fa_spec_disable_irqs(struct fa_dev *fa)
{
struct fa_spec_data *spec_data = fa->carrier_data;
fa_writel(fa, spec_data->fa_irq_dma_base,
&fa_spec_regs[ZFA_IRQ_DMA_DISABLE_MASK],
FA_SPEC_IRQ_DMA_NONE);
return 0;
}
static int fa_spec_ack_irq(struct fa_dev *fa, int irq_id)
{
return 0;
}
struct fa_carrier_op fa_spec_op = {
.get_gwname = fa_spec_get_gwname,
.init = fa_spec_init,
.reset_core = fa_spec_reset,
.exit = fa_spec_exit,
.setup_irqs = fa_spec_setup_irqs,
.free_irqs = fa_spec_free_irqs,
.enable_irqs = fa_spec_enable_irqs,
.disable_irqs = fa_spec_disable_irqs,
.ack_irq = fa_spec_ack_irq,
.dma_start = fa_spec_dma_start,
.dma_done = fa_spec_dma_done,
.dma_error = fa_spec_dma_error,
};
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/mm.h>
#include "fmc-adc-100m14b4cha.h"
#include "fa-spec.h"
static int gncore_dma_fill(struct zio_dma_sg *zsg)
{
struct gncore_dma_item *item = (struct gncore_dma_item *)zsg->page_desc;
struct scatterlist *sg = zsg->sg;
struct zio_channel *chan = zsg->zsgt->chan;
struct fa_dev *fa = chan->cset->zdev->priv_d;
struct fa_spec_data *spec_data = fa->carrier_data;
dma_addr_t tmp;
/* Prepare DMA item */
item->start_addr = zsg->dev_mem_off;
item->dma_addr_l = sg_dma_address(sg) & 0xFFFFFFFF;
item->dma_addr_h = (uint64_t)sg_dma_address(sg) >> 32;
item->dma_len = sg_dma_len(sg);
if (!sg_is_last(sg)) {/* more transfers */
/* uint64_t so it works on 32 and 64 bit */
tmp = zsg->zsgt->dma_page_desc_pool;
tmp += (zsg->zsgt->page_desc_size * (zsg->page_idx + 1));
item->next_addr_l = ((uint64_t)tmp) & 0xFFFFFFFF;
item->next_addr_h = ((uint64_t)tmp) >> 32;
item->attribute = 0x1; /* more items */
} else {
item->attribute = 0x0; /* last item */
}
/* The first item is written on the device */
if (zsg->page_idx == 0) {
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_ADDR], item->start_addr);
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_ADDR_L], item->dma_addr_l);
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_ADDR_H], item->dma_addr_h);
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_LEN], item->dma_len);
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_NEXT_L], item->next_addr_l);
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_NEXT_H], item->next_addr_h);
/* Set that there is a next item */
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_BR_LAST], item->attribute);
}
dev_dbg(fa->msgdev, "DMA item %d (block %d)\n"
" addr 0x%x\n"
" addr_l 0x%x\n"
" addr_h 0x%x\n"
" length %d\n"
" next_l 0x%x\n"
" next_h 0x%x\n"
" last 0x%x\n",
zsg->page_idx, zsg->block_idx,
item->start_addr, item->dma_addr_l, item->dma_addr_h,
item->dma_len, item->next_addr_l, item->next_addr_h,
item->attribute);
return 0;
}
int fa_spec_dma_start(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct fa_spec_data *spec_data = fa->carrier_data;
struct zio_channel *interleave = cset->interleave;
struct zfad_block *zfad_block = interleave->priv_d;
struct zio_block *blocks[fa->n_shots];
int i, err;
/*
* FIXME very inefficient because arm trigger already prepare
* something like zio_block_sg. In the future ZIO can alloc more
* than 1 block at time
*/
for (i = 0; i < fa->n_shots; ++i)
blocks[i] = zfad_block[i].block;
fa->zdma = zio_dma_alloc_sg(interleave, fa->fmc->hwdev, blocks,
fa->n_shots, GFP_ATOMIC);
if (IS_ERR(fa->zdma))
return PTR_ERR(fa->zdma);
/* Fix block memory offset
* FIXME when official ZIO has multishot and DMA
*/
for (i = 0; i < fa->zdma->n_blocks; ++i)
fa->zdma->sg_blocks[i].dev_mem_off = zfad_block[i].dev_mem_off;
err = zio_dma_map_sg(fa->zdma, sizeof(struct gncore_dma_item),
gncore_dma_fill);
if (err)
goto out_map_sg;
/* Start DMA transfer */
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_CTL_START], 1);
return 0;
out_map_sg:
zio_dma_free_sg(fa->zdma);
return err;
}
void fa_spec_dma_done(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
zio_dma_unmap_sg(fa->zdma);
zio_dma_free_sg(fa->zdma);
}
void fa_spec_dma_error(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct fa_spec_data *spec_data = fa->carrier_data;
uint32_t val;
fa_spec_dma_done(cset);
val = fa_readl(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_STA]);
if (val)
dev_err(fa->msgdev,
"DMA error (status 0x%x). All acquisition lost\n", val);
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include "fmc-adc-100m14b4cha.h"
#include "fa-spec.h"
/*
* fa_get_irq_status
* @fa: adc descriptor
* @irq_status: destination of irq status
* @irq_multi: destination of irq multi
*
* Get irq and clear the register. To clear an interrupt we have to write 1
* on the handled interrupt. We handle all interrupt so we clear all interrupts
*/
static void fa_get_irq_status(struct fa_dev *fa, int irq_core_base,
uint32_t *irq_status)
{
/* Get current interrupts status */
*irq_status = fa_readl(fa, irq_core_base,
&fa_spec_regs[ZFA_IRQ_DMA_SRC]);
dev_dbg(fa->msgdev,
"core DMA: 0x%x fired an interrupt. IRQ status register: 0x%x\n",
irq_core_base, *irq_status);
if (*irq_status)
/* Clear current interrupts status */
fa_writel(fa, irq_core_base,
&fa_spec_regs[ZFA_IRQ_DMA_SRC], *irq_status);
}
/*
* zfad_irq
* @irq:
* @ptr: pointer to fmc_device
*
* The ADC svec firmware fires interrupt from a single wishbone core
* and throught the VIC ACQ_END and TRIG events. Note about "TRIG"
* event: the main reason to listen this interrupt was to read the
* intermediate time stamps in case of multishots.
* With the new firmware (>=3.0) the stamps come with the data,
* therefore the driver doesn't have to listen "TRIG" event. This
* enhancement remove completely the risk of loosing interrupt in case
* of small number of samples and makes the retry loop in the hanlder
* obsolete.
*/
irqreturn_t fa_spec_irq_handler(int irq_core_base, void *ptr)
{
struct fmc_device *fmc = ptr;
struct fa_dev *fa = fmc_get_drvdata(fmc);
struct zio_cset *cset = fa->zdev->cset;
uint32_t status;
/* irq to handle */
fa_get_irq_status(fa, irq_core_base, &status);
if (!status)
return IRQ_NONE;
if (unlikely(!fa->n_shots || !cset->interleave->priv_d)) {
/*
* Mainly this may happen when you are playing with DMA with
* an user-space program or another driver. 99% of the time
* is for debugging purpose. So, if you are seriusly working
* with DMA with two different programs/drivers ... well *you*
* have a problem and this driver may crash badly.
*/
dev_err(fa->msgdev,
"No programmed shot, implies no DMA to perform\n");
goto out;
}
if (unlikely(fa->last_irq_core_src == irq_core_base)) {
WARN(1, "Cannot handle two consecutives %s interrupt."
"The ADC doesn't behave properly\n",
(irq_core_base == fa->fa_irq_adc_base) ? "ACQ" : "DMA");
/* Stop Acquisition, ADC it is not working properly */
zfad_fsm_command(fa, FA100M14B4C_CMD_STOP);
fa->last_irq_core_src = FA_SPEC_IRQ_SRC_NONE;
goto out;
}
dev_dbg(fa->msgdev, "Handle ADC interrupts\n");
if (status & FA_SPEC_IRQ_DMA_DONE)
zfad_dma_done(cset);
else if (unlikely(status & FA_SPEC_IRQ_DMA_ERR))
zfad_dma_error(cset);
/* register the core which just fired the IRQ */
/* check proper sequence of IRQ in case of multi IRQ (ACQ + DMA)*/
fa->last_irq_core_src = irq_core_base;
out:
/*
* DMA transaction is finished
* we can safely lower CSET_BUSY
*/
spin_lock(&cset->lock);
cset->flags &= ~ZIO_CSET_HW_BUSY;
spin_unlock(&cset->lock);
/* ack the irq */
fmc_irq_ack(fa->fmc);
return IRQ_HANDLED;
}
/*
* Copyright CERN 2012
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* Table of register masks, used by driver functions
*/
#include "fa-spec.h"
/* Definition of the fa spec registers field: offset - mask - isbitfield */
const struct zfa_field_desc fa_spec_regs[] = {
/* Carrier CSR */
[ZFA_CAR_FMC_PRES] = {0x04, 0x1, 1},
[ZFA_CAR_P2L_PLL] = {0x04, 0x2, 1},
[ZFA_CAR_SYS_PLL] = {0x04, 0x4, 1},
[ZFA_CAR_DDR_CAL] = {0x04, 0x8, 1},
[ZFA_CAR_FMC_RES] = {0x0c, 0x1, 1},
/* IRQ */
[ZFA_IRQ_DMA_DISABLE_MASK] = {0x00, 0x00000003, 0},
[ZFA_IRQ_DMA_ENABLE_MASK] = {0x04, 0x00000003, 0},
[ZFA_IRQ_DMA_MASK_STATUS] = {0x08, 0x00000003, 0},
[ZFA_IRQ_DMA_SRC] = {0x0C, 0x00000003, 0},
/* DMA */
[ZFA_DMA_CTL_SWP] = {0x00, 0x0000000C, 1},
[ZFA_DMA_CTL_ABORT] = {0x00, 0x00000002, 1},
[ZFA_DMA_CTL_START] = {0x00, 0x00000001, 1},
[ZFA_DMA_STA] = {0x04, 0x00000007, 0},
[ZFA_DMA_ADDR] = {0x08, 0xFFFFFFFF, 0},
[ZFA_DMA_ADDR_L] = {0x0C, 0xFFFFFFFF, 0},
[ZFA_DMA_ADDR_H] = {0x10, 0xFFFFFFFF, 0},
[ZFA_DMA_LEN] = {0x14, 0xFFFFFFFF, 0},
[ZFA_DMA_NEXT_L] = {0x18, 0xFFFFFFFF, 0},
[ZFA_DMA_NEXT_H] = {0x1C, 0xFFFFFFFF, 0},
[ZFA_DMA_BR_DIR] = {0x20, 0x00000002, 1},
[ZFA_DMA_BR_LAST] = {0x20, 0x00000001, 1},
};
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
*/
#ifndef __FA_SPEC_CORE_H__
#define __FA_SPEC_CORE_H__
#include <linux/scatterlist.h>
#include <linux/irqreturn.h>
#include "fmc-adc-100m14b4cha.h"
#include "field-desc.h"
/* default spec gateware */
#define FA_GATEWARE_SPEC "fmc/spec-fmc-adc-100m14b.bin"
/* Should be replaced by an sdb query */
#define SPEC_FA_DMA_MEM_OFF 0x01000
/*
* fa_dma_item: The information about a DMA transfer
* @start_addr: pointer where start to retrieve data from device memory
* @dma_addr_l: low 32bit of the dma address on host memory
* @dma_addr_h: high 32bit of the dma address on host memory
* @dma_len: number of bytes to transfer from device to host
* @next_addr_l: low 32bit of the address of the next memory area to use
* @next_addr_h: high 32bit of the address of the next memory area to use
* @attribute: dma information about data transferm. At the moment it is used
* only to provide the "last item" bit, direction is fixed to
* device->host
*/
struct gncore_dma_item {
uint32_t start_addr; /* 0x00 */
uint32_t dma_addr_l; /* 0x04 */
uint32_t dma_addr_h; /* 0x08 */
uint32_t dma_len; /* 0x0C */
uint32_t next_addr_l; /* 0x10 */
uint32_t next_addr_h; /* 0x14 */
uint32_t attribute; /* 0x18 */
uint32_t reserved; /* ouch */
};
/* SPEC CSR */
enum fa_spec_regs_id {
/* CSR */
ZFA_CAR_FMC_PRES,
ZFA_CAR_P2L_PLL,
ZFA_CAR_SYS_PLL,
ZFA_CAR_DDR_CAL,
ZFA_CAR_FMC_RES,
/* IRQ DMA: DMA spec specific irq controller */
ZFA_IRQ_DMA_DISABLE_MASK,
ZFA_IRQ_DMA_ENABLE_MASK,
ZFA_IRQ_DMA_MASK_STATUS,
ZFA_IRQ_DMA_SRC,
/* DMA */
ZFA_DMA_CTL_SWP,
ZFA_DMA_CTL_ABORT,
ZFA_DMA_CTL_START,
ZFA_DMA_STA,
ZFA_DMA_ADDR,
ZFA_DMA_ADDR_L,
ZFA_DMA_ADDR_H,
ZFA_DMA_LEN,
ZFA_DMA_NEXT_L,
ZFA_DMA_NEXT_H,
ZFA_DMA_BR_DIR,
ZFA_DMA_BR_LAST,
};
/* SPEC ADC have to listen two IRQ sources managed by two different cores */
#define FA_SPEC_IRQ_SRC_NONE 0
#define FA_SPEC_IRQ_SRC_ACQ 1
#define FA_SPEC_IRQ_SRC_DMA 2
/* DMA spec specific IRQ values */
enum fa_spec_irq {
FA_SPEC_IRQ_DMA_NONE = 0x0,
FA_SPEC_IRQ_DMA_DONE = 0x1,
FA_SPEC_IRQ_DMA_ERR = 0x2,
FA_SPEC_IRQ_DMA_ALL = 0x3,
};
/* specific carrier data */
struct fa_spec_data {
/* DMA attributes */
unsigned int fa_dma_base;
unsigned int fa_irq_dma_base;
struct fa_dma_item *items;
dma_addr_t dma_list_item;
unsigned int n_dma_err; /* statistics */
};
/* spec specific hardware registers */
extern const struct zfa_field_desc fa_spec_regs[];
/* spec irq handler */
extern irqreturn_t fa_spec_irq_handler(int irq, void *dev_id);
/* functions exported by fa-dma.c */
extern int fa_spec_dma_start(struct zio_cset *cset);
extern void fa_spec_dma_done(struct zio_cset *cset);
extern void fa_spec_dma_error(struct zio_cset *cset);
#endif /* __FA_SPEC_CORE_H__*/
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2012-2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <svec.h>
#include "fmc-adc-100m14b4cha.h"
#include "fa-svec.h"
static char *fa_svec_get_gwname(void)
{
return FA_GATEWARE_SVEC;
}
static int fa_svec_init(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
struct fa_svec_data *cdata;
struct svec_dev *svec = fmc->carrier_data;
cdata = kzalloc(sizeof(struct fa_svec_data), GFP_KERNEL);
if (!cdata)
return -ENOMEM;
cdata->vme_base = svec->cfg_cur.vme_base;
fa->fa_carrier_csr_base = fmc_find_sdb_device(fmc->sdb, 0xce42,
0x6603, NULL);
cdata->fa_dma_ddr_addr = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x10006611,
fmc->slot_id, NULL);
cdata->fa_dma_ddr_data = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x10006610,
fmc->slot_id, NULL);
/* Reset the FMC slot*/
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_svec_regfield[FA_CAR_FMC0_RES + fmc->slot_id], 1);
mdelay(50);
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_svec_regfield[FA_CAR_FMC0_RES + fmc->slot_id], 0);
mdelay(50);
/* register carrier data */
fa->carrier_data = cdata;
return 0;
}
static int fa_svec_reset(struct fa_dev *fa)
{
return 0;
}
static void fa_svec_exit(struct fa_dev *fa)
{
kfree(fa->carrier_data);
}
struct fa_carrier_op fa_svec_op = {
.get_gwname = fa_svec_get_gwname,
.init = fa_svec_init,
.reset_core = fa_svec_reset,
.exit = fa_svec_exit,
.dma_start = fa_svec_dma_start,
.dma_done = fa_svec_dma_done,
.dma_error = fa_svec_dma_error,
};
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2012-2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/byteorder.h>
#include "fmc-adc-100m14b4cha.h"
#include "fa-svec.h"
#include "vmebus.h"
#define VME_NO_ADDR_INCREMENT 1
/* FIXME: move to include again */
#ifndef lower_32_bits
#define lower_32_bits(n) ((u32)(n))
#endif /* lower_32_bits */
static void build_dma_desc(struct vme_dma *desc, unsigned long vme_addr,
void *addr_dest, ssize_t len)
{
struct vme_dma_attr *vme;
struct vme_dma_attr *pci;
memset(desc, 0, sizeof(struct vme_dma));
vme = &desc->src;
pci = &desc->dst;
desc->dir = VME_DMA_FROM_DEVICE;
desc->length = len;
desc->novmeinc = VME_NO_ADDR_INCREMENT;
desc->ctrl.pci_block_size = VME_DMA_BSIZE_4096;
desc->ctrl.pci_backoff_time = VME_DMA_BACKOFF_0;
desc->ctrl.vme_block_size = VME_DMA_BSIZE_4096;
desc->ctrl.vme_backoff_time = VME_DMA_BACKOFF_0;
vme->data_width = VME_D32;
vme->am = VME_A24_USER_DATA_SCT;
/*vme->am = VME_A24_USER_MBLT;*/
vme->addru = upper_32_bits(vme_addr);
vme->addrl = lower_32_bits(vme_addr);
pci->addru = upper_32_bits((unsigned long)addr_dest);
pci->addrl = lower_32_bits((unsigned long)addr_dest);
}
/* Endianess */
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 0
#endif
#ifndef BIG_ENDIAN
#define BIG_ENDIAN 1
#endif
static int __get_endian(void)
{
int i = 1;
char *p = (char *)&i;
if (p[0] == 1)
return LITTLE_ENDIAN;
else
return BIG_ENDIAN;
}
static void __endianness(unsigned int byte_length, void *buffer)
{
int i, size;
uint32_t *ptr;
/* CPU may be little endian, VME is big endian */
if (__get_endian() == LITTLE_ENDIAN) {
ptr = buffer;
/* swap samples and trig timetag all seen as 32bits words */
size = byte_length/4;
for (i = 0; i < size; ++i, ++ptr)
*ptr = __be32_to_cpu(*ptr);
}
}
int fa_svec_dma_start(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct fa_svec_data *svec_data = fa->carrier_data;
struct zio_channel *interleave = cset->interleave;
struct zfad_block *fa_dma_block = interleave->priv_d;
int i;
struct vme_dma desc; /* Vme driver DMA structure */
unsigned long vme_addr;
vme_addr = svec_data->vme_base + svec_data->fa_dma_ddr_data;
/*
* write the data address in the ddr_addr register: this
* address has been computed after ACQ_END by looking to the
* trigger position see fa-irq.c::irq_acq_end.
* Be careful: the SVEC HW version expects an address of 32bits word
* therefore mem-offset in byte is translated into 32bit word
**/
fa_writel(fa, svec_data->fa_dma_ddr_addr,
&fa_svec_regfield[FA_DMA_DDR_ADDR],
fa_dma_block[0].dev_mem_off/4);
/* Execute DMA shot by shot */
for (i = 0; i < fa->n_shots; ++i) {
dev_dbg(fa->msgdev,
"configure DMA descriptor shot %d "
"vme addr: 0x%llx destination address: 0x%p len: %d\n",
i, (long long)vme_addr, fa_dma_block[i].block->data,
(int)fa_dma_block[i].block->datalen);
build_dma_desc(&desc, vme_addr,
fa_dma_block[i].block->data,
fa_dma_block[i].block->datalen);
if (vme_do_dma_kernel(&desc))
return -1;
__endianness(fa_dma_block[i].block->datalen,
fa_dma_block[i].block->data);
}
return 0;
}
void fa_svec_dma_done(struct zio_cset *cset)
{
/* nothing special to do */
}
void fa_svec_dma_error(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
dev_err(fa->msgdev,
"DMA error. All acquisition lost\n");
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#include "fa-svec.h"
/* fa svec specific registers field: offset - mask - isbitfield */
const struct zfa_field_desc fa_svec_regfield[] = {
[FA_DMA_DDR_ADDR] = {0x0000, 0x00FFFFFF, 0},
[FA_DMA_DDR_DATA] = {0x0000, 0x00FFFFFF, 0},
/* CSR */
[FA_CAR_FMC0_PRES] = {0x0004, 0x00000001, 1},
[FA_CAR_FMC1_PRES] = {0x0004, 0x00000002, 1},
[FA_CAR_SYS_PLL] = {0x0004, 0x00000004, 1},
[FA_CAR_DDR0_CAL] = {0x0004, 0x00000008, 1},
[FA_CAR_DDR1_CAL] = {0x0004, 0x00000010, 1},
[FA_CAR_FMC0_RES] = {0x000C, 0x00000001, 1},
[FA_CAR_FMC1_RES] = {0x000C, 0x00000002, 1},
};
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright CERN 2012-2019
*/
#ifndef __FA_SVEC_CORE_H__
#define __FA_SVEC_CORE_H__
#include <linux/irqreturn.h>
#include "fmc-adc-100m14b4cha.h"
#include "field-desc.h"
/* default spec gateware */
#define FA_GATEWARE_SVEC "fmc/svec-fmc-adc-100m14b.bin"
/* SPEC CSR */
enum fa_spec_regs_id {
/* DMA */
FA_DMA_DDR_ADDR = 0,
FA_DMA_DDR_DATA,
/* CSR */
FA_CAR_FMC0_PRES,
FA_CAR_FMC1_PRES,
FA_CAR_SYS_PLL,
FA_CAR_DDR0_CAL,
FA_CAR_DDR1_CAL,
FA_CAR_FMC0_RES,
FA_CAR_FMC1_RES,
};
/* specific carrier data */
struct fa_svec_data {
/* DMA attributes */
unsigned long vme_base;
unsigned int fa_dma_ddr_data; /* offset */
unsigned int fa_dma_ddr_addr; /* offset */
unsigned int n_dma_err; /* statistics */
};
/* svec specific hardware registers */
extern const struct zfa_field_desc fa_svec_regfield[];
/* svec irq handler */
extern irqreturn_t fa_svec_irq_handler(int irq, void *dev_id);
/* functions exported by fa-svec-dma.c */
extern int fa_svec_dma_start(struct zio_cset *cset);
extern void fa_svec_dma_done(struct zio_cset *cset);
extern void fa_svec_dma_error(struct zio_cset *cset);
#endif /* __FA_SVEC_CORE_H__*/
......@@ -67,16 +67,16 @@ static struct zio_attribute zfad_cset_ext_zattr[] = {
ZIO_ATTR_EXT("ch3-50ohm-term", ZIO_RW_PERM, ZFA_CH4_CTL_TERM, 0),
/* last acquisition start time stamp */
ZIO_ATTR_EXT("tstamp-acq-str-s", ZIO_RO_PERM,
ZFA_UTC_ACQ_START_SECONDS, 0),
ZIO_ATTR_EXT("tstamp-acq-str-su", ZIO_RO_PERM,
ZFA_UTC_ACQ_START_SECONDS_U, 0),
ZIO_ATTR_EXT("tstamp-acq-str-sl", ZIO_RO_PERM,
ZFA_UTC_ACQ_START_SECONDS_L, 0),
ZIO_ATTR_EXT("tstamp-acq-str-t", ZIO_RO_PERM,
ZFA_UTC_ACQ_START_COARSE, 0),
ZIO_ATTR_EXT("tstamp-acq-str-b", ZIO_RO_PERM,
ZFA_UTC_ACQ_START_FINE, 0),
/* Timing base */
ZIO_ATTR_EXT("tstamp-base-s", ZIO_RW_PERM, ZFA_UTC_SECONDS, 0),
ZIO_ATTR_EXT("tstamp-base-su", ZIO_RW_PERM, ZFA_UTC_SECONDS_U, 0),
ZIO_ATTR_EXT("tstamp-base-sl", ZIO_RW_PERM, ZFA_UTC_SECONDS_L, 0),
ZIO_ATTR_EXT("tstamp-base-t", ZIO_RW_PERM, ZFA_UTC_COARSE, 0),
/* Parameters (not attributes) follow */
......@@ -104,26 +104,25 @@ static struct zio_attribute zfad_cset_ext_zattr[] = {
* */
ZIO_PARAM_EXT("fsm-state", ZIO_RO_PERM, ZFA_STA_FSM, 0),
/* last acquisition end time stamp */
ZIO_PARAM_EXT("tstamp-acq-end-s", ZIO_RO_PERM,
ZFA_UTC_ACQ_END_SECONDS, 0),
ZIO_PARAM_EXT("tstamp-acq-end-su", ZIO_RO_PERM,
ZFA_UTC_ACQ_END_SECONDS_U, 0),
ZIO_PARAM_EXT("tstamp-acq-end-sl", ZIO_RO_PERM,
ZFA_UTC_ACQ_END_SECONDS_L, 0),
ZIO_PARAM_EXT("tstamp-acq-end-t", ZIO_RO_PERM,
ZFA_UTC_ACQ_END_COARSE, 0),
ZIO_PARAM_EXT("tstamp-acq-end-b", ZIO_RO_PERM,
ZFA_UTC_ACQ_END_FINE, 0),
/* last acquisition stop time stamp */
ZIO_PARAM_EXT("tstamp-acq-stp-s", ZIO_RO_PERM,
ZFA_UTC_ACQ_STOP_SECONDS, 0),
ZIO_PARAM_EXT("tstamp-acq-stp-su", ZIO_RO_PERM,
ZFA_UTC_ACQ_STOP_SECONDS_U, 0),
ZIO_PARAM_EXT("tstamp-acq-stp-sl", ZIO_RO_PERM,
ZFA_UTC_ACQ_STOP_SECONDS_L, 0),
ZIO_PARAM_EXT("tstamp-acq-stp-t", ZIO_RO_PERM,
ZFA_UTC_ACQ_STOP_COARSE, 0),
ZIO_PARAM_EXT("tstamp-acq-stp-b", ZIO_RO_PERM,
ZFA_UTC_ACQ_STOP_FINE, 0),
/* Reset all channel offset */
ZIO_PARAM_EXT("rst-ch-offset", ZIO_WO_PERM, ZFA_CTL_DAC_CLR_N, 1),
ZIO_PARAM_EXT("sample-frequency", ZIO_RO_PERM, ZFAT_SAMPLING_HZ, 0),
ZIO_PARAM_EXT("max-sample-mshot", ZIO_RO_PERM, ZFA_MULT_MAX_SAMP, 0),
ZIO_PARAM_EXT("sample-counter", ZIO_RO_PERM, ZFAT_CNT, 0),
ZIO_PARAM_EXT("test-data-pattern", ZIO_RW_PERM, ZFAT_ADC_TST_PATTERN, 0),
};
#if 0 /* FIXME Unused until TLV control will be available */
......@@ -150,16 +149,22 @@ static struct zio_attribute zfad_chan_ext_zattr[] = {
};
static struct zio_attribute zfad_dev_ext_zattr[] = {
/* Get Mezzanine temperature from onewire */
/* Get Mezzanine temperature from the DS18B20 chip */
ZIO_PARAM_EXT("temperature", ZIO_RO_PERM, ZFA_SW_R_NOADDRES_TEMP, 0),
};
/* Temporarily, user values are the same as hardware values */
static int zfad_convert_user_range(uint32_t user_val)
int zfad_convert_user_range(uint32_t user_val)
{
return zfad_convert_hw_range(user_val);
}
static bool fa_is_dac_offset_valid(int32_t user, int32_t zero)
{
int32_t offset = user + zero;
return (offset >= DAC_SAT_LOW && offset <= DAC_SAT_UP);
}
/*
* zfad_conf_set
*
......@@ -169,14 +174,15 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
{
struct fa_dev *fa = get_zfadc(dev);
unsigned int baseoff = fa->fa_adc_csr_base;
void *baseoff = fa->fa_adc_csr_base;
struct zio_channel *chan;
int i, range, err = 0, reg_index;
int i, range, reg_index, err;
reg_index = zattr->id;
i = FA100M14B4C_NCHAN;
if (zattr->id >= ZFA_UTC_SECONDS && zattr->id <= ZFA_UTC_ACQ_END_FINE)
if (zattr->id >= ZFA_UTC_SECONDS_U &&
zattr->id <= ZFA_UTC_ACQ_END_COARSE)
baseoff = fa->fa_utc_base;
switch (reg_index) {
......@@ -192,19 +198,24 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
return 0;
case ZFA_SW_CH1_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH2_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH3_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH4_OFFSET_ZERO:
i--;
chan = to_zio_cset(dev)->chan + i;
if (!fa_is_dac_offset_valid(fa->user_offset[chan->index],
usr_val))
return -EINVAL;
spin_lock(&fa->zdev->cset->lock);
fa->zero_offset[i] = usr_val;
err = zfad_apply_offset(chan);
if (err == -EIO)
fa->zero_offset[chan->index] = 0;
return err;
fa_calib_dac_config_chan(fa, i, ~0);
spin_unlock(&fa->zdev->cset->lock);
return 0;
case ZFA_CHx_SAT:
/* TODO when TLV */
break;
......@@ -222,25 +233,30 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
/* FIXME temporary until TLV control */
case ZFA_CH1_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH2_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH3_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH4_OFFSET:
i--;
chan = to_zio_cset(dev)->chan + i;
chan = to_zio_cset(dev)->chan + i;
if (!fa_is_dac_offset_valid(usr_val,
fa->zero_offset[chan->index]))
return -EINVAL;
spin_lock(&fa->zdev->cset->lock);
fa->user_offset[chan->index] = usr_val;
err = zfad_apply_offset(chan);
if (err == -EIO)
fa->user_offset[chan->index] = 0;
err = fa_calib_dac_config_chan(fa, i, ~0);
spin_unlock(&fa->zdev->cset->lock);
return err;
case ZFA_CHx_OFFSET:
chan = to_zio_chan(dev),
chan = to_zio_chan(dev);
spin_lock(&fa->zdev->cset->lock);
fa->user_offset[chan->index] = usr_val;
err = zfad_apply_offset(chan);
if (err == -EIO)
fa->user_offset[chan->index] = 0;
err = fa_calib_dac_config_chan(fa, chan->index, ~0);
spin_unlock(&fa->zdev->cset->lock);
return err;
case ZFA_CTL_DAC_CLR_N:
zfad_reset_offset(fa);
......@@ -251,9 +267,13 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
break;
/* FIXME temporary until TLV control */
case ZFA_CH1_CTL_TERM:
/*fallthrough*/
case ZFA_CH2_CTL_TERM:
/*fallthrough*/
case ZFA_CH3_CTL_TERM:
/*fallthrough*/
case ZFA_CH4_CTL_TERM:
/*fallthrough*/
case ZFA_CHx_CTL_TERM:
if (usr_val > 1)
usr_val = 1;
......@@ -262,22 +282,39 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
/* FIXME temporary until TLV control */
case ZFA_CH1_CTL_RANGE:
i--;
/*fallthrough*/
case ZFA_CH2_CTL_RANGE:
i--;
/*fallthrough*/
case ZFA_CH3_CTL_RANGE:
i--;
/*fallthrough*/
case ZFA_CH4_CTL_RANGE:
i--;
range = zfad_convert_user_range(usr_val);
if (range < 0)
return range;
return zfad_set_range(fa, &to_zio_cset(dev)->chan[i], range);
err = fa_adc_range_set(fa, &to_zio_cset(dev)->chan[i], range);
if (err)
return err;
spin_lock(&fa->zdev->cset->lock);
fa_calib_adc_config_chan(fa, i, ~0);
fa_calib_dac_config_chan(fa, i, ~0);
spin_unlock(&fa->zdev->cset->lock);
return 0;
case ZFA_CHx_CTL_RANGE:
range = zfad_convert_user_range(usr_val);
if (range < 0)
return range;
return zfad_set_range(fa, to_zio_chan(dev), range);
err = fa_adc_range_set(fa, &to_zio_cset(dev)->chan[i], range);
if (err)
return err;
spin_lock(&fa->zdev->cset->lock);
fa_calib_adc_config_chan(fa, i, ~0);
fa_calib_dac_config_chan(fa, i, ~0);
spin_unlock(&fa->zdev->cset->lock);
return 0;
case ZFA_UTC_COARSE:
if (usr_val >= FA100M14B4C_UTC_CLOCK_FREQ) {
......@@ -289,24 +326,6 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
break;
case ZFA_CTL_FMS_CMD:
return zfad_fsm_command(fa, usr_val);
case ZFAT_ADC_TST_PATTERN:
if (unlikely(fa_enable_test_data_adc)) {
usr_val &= 0xFFF;
err = zfad_pattern_data_enable(fa, usr_val,
fa_enable_test_data_adc);
if (err)
dev_warn(fa->msgdev,
"Failed to set the ADC test data. Continue without\n");
else if (fa_enable_test_data_adc)
dev_info(fa->msgdev,
"the ADC test data (0x%x) is enabled on all channels\n",
usr_val);
return err;
} else {
dev_err(fa->msgdev,
"Cannot set the ADC test data. The driver is not in test mode\n");
return -EPERM;
}
}
fa_writel(fa, baseoff, &zfad_regs[reg_index], usr_val);
......@@ -322,22 +341,26 @@ static int zfad_info_get(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
struct fa_dev *fa = get_zfadc(dev);
unsigned int baseoff = fa->fa_adc_csr_base;
void *baseoff = fa->fa_adc_csr_base;
int i, reg_index;
i = FA100M14B4C_NCHAN;
if (zattr->id >= ZFA_UTC_SECONDS && zattr->id <= ZFA_UTC_ACQ_END_FINE)
if (zattr->id >= ZFA_UTC_SECONDS_U &&
zattr->id <= ZFA_UTC_ACQ_END_COARSE)
baseoff = fa->fa_utc_base;
switch (zattr->id) {
/* FIXME temporary until TLV control */
case ZFA_CH1_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH2_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH3_OFFSET:
i--;
/*fallthrough*/
case ZFA_CH4_OFFSET:
i--;
*usr_val = fa->user_offset[i];
......@@ -346,36 +369,36 @@ static int zfad_info_get(struct device *dev, struct zio_attribute *zattr,
case ZFA_CHx_OFFSET:
*usr_val = fa->user_offset[to_zio_chan(dev)->index];
return 0;
case ZFAT_ADC_TST_PATTERN:
case ZFA_SW_R_NOADDRES_NBIT:
/*fallthrough*/
case ZFA_SW_R_NOADDERS_AUTO:
/* ZIO automatically return the attribute value */
return 0;
case ZFA_SW_R_NOADDRES_TEMP:
/*
* Onewire returns units of 1/16 degree. We return units
* of 1/1000 of a degree instead.
*/
*usr_val = fa_read_temp(fa, 0);
*usr_val = (*usr_val * 1000 + 8) / 16;
*usr_val = fa_temperature_read(fa);
return 0;
case ZFA_SW_CH1_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH2_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH3_OFFSET_ZERO:
i--;
/*fallthrough*/
case ZFA_SW_CH4_OFFSET_ZERO:
i--;
*usr_val = fa->zero_offset[i];
return 0;
case ZFA_CHx_SAT:
/*fallthrough*/
case ZFA_CHx_CTL_TERM:
/*fallthrough*/
case ZFA_CHx_CTL_RANGE:
reg_index = zfad_get_chx_index(zattr->id, to_zio_chan(dev));
reg_index = zfad_get_chx_index(zattr->id, to_zio_chan(dev)->index);
break;
case ZFA_CHx_STA:
reg_index = zfad_get_chx_index(zattr->id, to_zio_chan(dev));
reg_index = zfad_get_chx_index(zattr->id, to_zio_chan(dev)->index);
*usr_val = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[reg_index]);
i = (int16_t)(*usr_val); /* now signed integer */
......@@ -444,13 +467,13 @@ static inline int zfat_overflow_detection(struct zio_ti *ti)
static int zfad_input_cset_software(struct fa_dev *fa, struct zio_cset *cset)
{
struct zfad_block *tmp;
int err;
tmp = kzalloc(sizeof(struct zfad_block), GFP_ATOMIC);
if (!tmp)
return -ENOMEM;
tmp->block = cset->interleave->active_block;
cset->interleave->priv_d = tmp;
tmp->dev_mem_off = 0; /* Always the first block */
/* Configure post samples */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_POST],
......@@ -460,7 +483,9 @@ static int zfad_input_cset_software(struct fa_dev *fa, struct zio_cset *cset)
fa->n_shots = 1;
/* Fire software trigger */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SW], 1);
err = fa_trigger_software(fa);
if (err)
return err;
return -EAGAIN;
}
......@@ -539,10 +564,6 @@ static int zfad_zio_probe(struct zio_device *zdev)
/* Save also the pointer to the real zio_device */
fa->zdev = zdev;
err = zfad_pattern_data_enable(fa, 0, fa_enable_test_data_adc);
if (err)
return err;
err = device_create_bin_file(&zdev->head.dev, &dev_attr_calibration);
if (err)
return err;
......@@ -677,7 +698,7 @@ int fa_zio_init(struct fa_dev *fa)
/* Register the hardware zio_device */
err = zio_register_device(fa->hwzdev, "adc-100m14b",
fa->fmc->device_id);
fa->pdev->id);
if (err) {
dev_err(fa->msgdev, "Cannot register ZIO device fmc-adc-100m14b\n");
zio_free_device(fa->hwzdev);
......
......@@ -77,6 +77,19 @@ static struct zio_attribute zfat_ext_zattr[] = {
[FA100M14B4C_TATTR_CH4_DLY] = ZIO_ATTR_EXT("ch3-delay",
ZIO_RW_PERM,
ZFA_CH4_DLY, 0),
/* Time Trigger */
[FA100M14B4C_TATTR_TRG_TIM_SU] = ZIO_ATTR_EXT("trg-time-su",
ZIO_RW_PERM,
ZFA_UTC_TRIG_TIME_SECONDS_U,
0),
[FA100M14B4C_TATTR_TRG_TIM_SL] = ZIO_ATTR_EXT("trg-time-sl",
ZIO_RW_PERM,
ZFA_UTC_TRIG_TIME_SECONDS_L,
0),
[FA100M14B4C_TATTR_TRG_TIM_C] = ZIO_ATTR_EXT("trg-time-t",
ZIO_RW_PERM,
ZFA_UTC_TRIG_TIME_COARSE,
0),
/*
* Delay to apply on the trigger in sampling clock period. The default
......@@ -85,19 +98,27 @@ static struct zio_attribute zfat_ext_zattr[] = {
[FA100M14B4C_TATTR_EXT_DLY] = ZIO_ATTR_EXT("ext-delay", ZIO_RW_PERM,
ZFAT_EXT_DLY, 0),
/* Software Trigger */
[FA100M14B4C_TATTR_SW_FIRE] = ZIO_PARAM_EXT("sw-trg-fire", ZIO_WO_PERM,
ZFAT_SW, 0),
/* last trigger time stamp */
[FA100M14B4C_TATTR_TRG_S] = ZIO_PARAM_EXT("tstamp-trg-lst-s",
ZIO_RO_PERM, ZFA_UTC_TRIG_SECONDS, 0),
[FA100M14B4C_TATTR_TRG_SU] = ZIO_PARAM_EXT("tstamp-trg-lst-su",
ZIO_RO_PERM,
ZFA_UTC_TRIG_SECONDS_U, 0),
[FA100M14B4C_TATTR_TRG_SL] = ZIO_PARAM_EXT("tstamp-trg-lst-sl",
ZIO_RO_PERM,
ZFA_UTC_TRIG_SECONDS_L, 0),
[FA100M14B4C_TATTR_TRG_C] = ZIO_PARAM_EXT("tstamp-trg-lst-t",
ZIO_RO_PERM, ZFA_UTC_TRIG_COARSE, 0),
[FA100M14B4C_TATTR_TRG_F] = ZIO_PARAM_EXT("tstamp-trg-lst-b",
ZIO_RO_PERM, ZFA_UTC_TRIG_FINE, 0),
ZIO_RO_PERM,
ZFA_UTC_TRIG_COARSE, 0),
};
/*
* Reset to default value
*/
void zfat_trigger_source_reset(struct fa_dev *fa)
{
fa_writel(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_CFG_SRC],
FA100M14B4C_TRG_SRC_SW | FA100M14B4C_TRG_SRC_ALT);
}
/*
* zfat_conf_set
......@@ -108,9 +129,13 @@ static int zfat_conf_set(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
{
struct fa_dev *fa = get_zfadc(dev);
struct zio_ti *ti = to_zio_ti(dev);
void *baseoff = fa->fa_adc_csr_base;
uint32_t tmp_val = usr_val;
if (zattr->id >= ZFA_UTC_SECONDS_U &&
zattr->id <= ZFA_UTC_ACQ_END_COARSE)
baseoff = fa->fa_utc_base;
switch (zattr->id) {
case ZFAT_SHOTS_NB:
if (!tmp_val) {
......@@ -125,24 +150,6 @@ static int zfat_conf_set(struct device *dev, struct zio_attribute *zattr,
}
tmp_val--; /* Remove one sample for the trigger */
break;
case ZFAT_SW:
/* Fire if software trigger is enabled (index 5) */
if (!(ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value &
FA100M14B4C_TRG_SRC_SW)) {
dev_info(fa->msgdev, "sw trigger is not enabled\n");
return -EPERM;
}
/* Fire if nsamples!=0 */
if (!ti->nsamples) {
dev_info(fa->msgdev, "pre + post = 0: cannot acquire\n");
return -EINVAL;
}
/*
* The software trigger will be fired to force
* acquisition, so we don't care about current
* acquisition or other problems:
*/
break;
case ZFAT_CFG_SRC:
/*
* Do not copy to hardware when globally disabled
......@@ -155,7 +162,7 @@ static int zfat_conf_set(struct device *dev, struct zio_attribute *zattr,
return 0;
}
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[zattr->id], tmp_val);
fa_writel(fa, baseoff, &zfad_regs[zattr->id], tmp_val);
return 0;
}
......@@ -168,6 +175,11 @@ static int zfat_info_get(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
struct fa_dev *fa = get_zfadc(dev);
void *baseoff = fa->fa_adc_csr_base;
if (zattr->id >= ZFA_UTC_SECONDS_U &&
zattr->id <= ZFA_UTC_ACQ_END_COARSE)
baseoff = fa->fa_utc_base;
switch (zattr->id) {
case ZFAT_CFG_SRC:
......@@ -178,7 +190,7 @@ static int zfat_info_get(struct device *dev, struct zio_attribute *zattr,
return 0;
}
*usr_val = fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[zattr->id]);
*usr_val = fa_readl(fa, baseoff, &zfad_regs[zattr->id]);
switch (zattr->id) {
case ZFAT_POST:
(*usr_val)++; /* add the trigger sample */
......@@ -296,6 +308,12 @@ static int zfat_data_done(struct zio_cset *cset)
kfree(zfad_block);
cset->interleave->priv_d = NULL;
/*
* Reset trigger source to avoid clean up the register for the next
* acquisition
*/
zfat_trigger_source_reset(fa);
return 0;
}
......@@ -313,7 +331,7 @@ static int zfat_arm_trigger(struct zio_ti *ti)
struct zio_block *block;
struct zfad_block *zfad_block;
unsigned int size;
uint32_t dev_mem_off, trg_src;
uint32_t dev_mem_off;
int i, err = 0;
dev_dbg(fa->msgdev, "Arming trigger\n");
......@@ -378,20 +396,13 @@ static int zfat_arm_trigger(struct zio_ti *ti)
zio_control_size(interleave));
/* Add to the vector of prepared blocks */
zfad_block[i].block = block;
zfad_block[i].dev_mem_off = dev_mem_off;
dev_mem_off += size;
dev_dbg(fa->msgdev, "next dev_mem_off 0x%x (+%d)\n",
dev_mem_off, size);
zfad_block[i].cset = ti->cset;
}
err = ti->cset->raw_io(ti->cset);
if (err != -EAGAIN && err != 0)
goto out_allocate;
/* Everything looks fine for the time being, enable the trigger sources */
trg_src = ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value;
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC], trg_src);
return err;
out_allocate:
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2020 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "platform_data/fmc-adc-100m14b4cha.h"
enum fa_spec_dev_offsets {
FA_SPEC_ADC_MEM_START = 0x000002000,
FA_SPEC_ADC_MEM_END = 0x000003FFF,
};
static const struct fmc_adc_platform_data fmc_adc_pdata = {
.flags = 0,
.calib_trig_time = 0,
.calib_trig_threshold = 0,
.calib_trig_internal = 0,
};
static int fa_spec_probe(struct platform_device *pdev) {
static struct resource fa_spec_fdt_res[] = {
{
.name = "fmc-adc-mem",
.flags = IORESOURCE_MEM,
},
{
.name = "fmc-adc-dma",
.flags = IORESOURCE_DMA,
},
{
.name = "fmc-adc-irq",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
}};
struct platform_device_info pdevinfo = {
.parent = &pdev->dev,
.name = "fmc-adc-100m",
.id = PLATFORM_DEVID_AUTO,
.res = fa_spec_fdt_res,
.num_res = ARRAY_SIZE(fa_spec_fdt_res),
.data = &fmc_adc_pdata,
.size_data = sizeof(fmc_adc_pdata),
.dma_mask = DMA_BIT_MASK(32),
};
struct platform_device *pdev_child;
struct resource *rmem;
struct resource *r;
int irq;
int dma_dev_chan;
rmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!rmem) {
dev_err(&pdev->dev, "Missing memory resource\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Missing IRQ number\n");
return -EINVAL;
}
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!r) {
dev_err(&pdev->dev, "Missing DMA engine\n");
return -EINVAL;
}
dma_dev_chan = r->start;
fa_spec_fdt_res[0].parent = rmem;
fa_spec_fdt_res[0].start = rmem->start + FA_SPEC_ADC_MEM_START;
fa_spec_fdt_res[0].end = rmem->start + FA_SPEC_ADC_MEM_END;
fa_spec_fdt_res[1].start = dma_dev_chan;
fa_spec_fdt_res[2].start = irq;
pdev_child = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev_child))
return PTR_ERR(pdev_child);
platform_set_drvdata(pdev, pdev_child);
return 0;
}
static int fa_spec_remove(struct platform_device *pdev) {
struct platform_device *pdev_child = platform_get_drvdata(pdev);
platform_device_unregister(pdev_child);
return 0;
}
/**
* List of supported platform
*/
enum fa_spec_version {
FA_SPEC_VER = 0,
};
static const struct platform_device_id fa_spec_id_table[] = {
{
.name = "fmc-adc-100m-spec",
.driver_data = FA_SPEC_VER,
},
{
.name = "id:000010DC41444301",
.driver_data = FA_SPEC_VER,
},
{
.name = "id:000010dc41444301",
.driver_data = FA_SPEC_VER,
},
{},
};
static struct platform_driver fa_spec_driver = {
.driver =
{
.name = "fmc-adc-spec",
.owner = THIS_MODULE,
},
.id_table = fa_spec_id_table,
.probe = fa_spec_probe,
.remove = fa_spec_remove,
};
module_platform_driver(fa_spec_driver);
MODULE_AUTHOR("Federico Vaga <federico.vaga@cern.ch>");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);
MODULE_DESCRIPTION("Driver for the SPEC ADC 100 MSamples 14 bits 4 Channels");
MODULE_DEVICE_TABLE(platform, fa_spec_id_table);
MODULE_SOFTDEP("pre: spec_fmc_carrier fmc-adc-100m14b4ch");
......@@ -11,11 +11,10 @@
#include <linux/types.h>
#else
#include <stdint.h>
#endif
#ifndef BIT
#define BIT(nr) (1UL << (nr))
#endif
#endif
/* Trigger sources */
#define FA100M14B4C_TRG_SRC_EXT BIT(0)
......@@ -36,6 +35,9 @@
#define FA100M14B4C_TRG_POL_CH4 FA100M14B4C_TRG_SRC_CH4
#define FA100M14B4C_TRG_POL_CHx(_x) (FA100M14B4C_TRG_POL_CH1 << ((_x) - 1))
enum fa_versions {
ADC_VER = 0,
};
/*
* Trigger Extended Attribute Enumeration
......@@ -65,12 +67,14 @@ enum fa100m14b4c_trg_ext_attr {
FA100M14B4C_TATTR_CH2_DLY,
FA100M14B4C_TATTR_CH3_DLY,
FA100M14B4C_TATTR_CH4_DLY,
FA100M14B4C_TATTR_TRG_TIM_SU,
FA100M14B4C_TATTR_TRG_TIM_SL,
FA100M14B4C_TATTR_TRG_TIM_C,
#ifdef __KERNEL__
FA100M14B4C_TATTR_SW_FIRE,
FA100M14B4C_TATTR_TRG_S,
FA100M14B4C_TATTR_TRG_SU,
FA100M14B4C_TATTR_TRG_SL,
FA100M14B4C_TATTR_TRG_C,
FA100M14B4C_TATTR_TRG_F,
#endif
};
......@@ -128,8 +132,8 @@ enum fa100m14b4c_fsm_cmd {
enum fa100m14b4c_fsm_state {
FA100M14B4C_STATE_IDLE = 0x1,
FA100M14B4C_STATE_PRE,
FA100M14B4C_STATE_POST,
FA100M14B4C_STATE_WAIT,
FA100M14B4C_STATE_POST,
FA100M14B4C_STATE_DECR,
};
......@@ -140,19 +144,21 @@ struct fa_calib_stanza {
uint16_t temperature;
};
#define FA_CALIB_STANZA_N 3
struct fa_calib {
struct fa_calib_stanza adc[3]; /* For input, one per range */
struct fa_calib_stanza dac[3]; /* For user offset, one per range */
struct fa_calib_stanza adc[FA_CALIB_STANZA_N]; /* For input, one per range */
struct fa_calib_stanza dac[FA_CALIB_STANZA_N]; /* For user offset, one per range */
};
#ifdef __KERNEL__ /* All the rest is only of kernel users */
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include <linux/zio.h>
#include <linux/zio-dma.h>
#include <linux/zio-sysfs.h>
......@@ -160,8 +166,35 @@ struct fa_calib {
#include <linux/zio-trigger.h>
#include "field-desc.h"
#include <platform_data/fmc-adc-100m14b4cha.h>
#define ADC_CSR_OFF 0x1000
#define ADC_EIC_OFF 0x1500
#define ADC_OW_OFF 0x1700
#define ADC_SPI_OFF 0x1800
#define ADC_UTC_OFF 0x1900
#define DAC_SAT_LOW -5000000
#define DAC_SAT_UP 5000000
extern int fa_enable_test_data_adc;
#define ADC_DMA 0
enum fa_irq_resource {
ADC_IRQ_TRG = 0,
};
enum fa_mem_resource {
ADC_MEM_BASE = 0,
};
enum fa_bus_resource {
ADC_CARR_VME_ADDR,
};
struct fa_memory_ops {
u32 (*read)(void *addr);
void (*write)(u32 value, void *addr);
};
/*
* ZFA_CHx_MULT : the trick which requires channel regs id grouped and ordered
......@@ -181,10 +214,13 @@ enum zfadc_dregs_enum {
ZFA_CTL_TRIG_LED,
ZFA_CTL_ACQ_LED,
ZFA_CTL_RST_TRG_STA,
ZFA_CTL_CALIB_APPLY,
/* Status registers */
ZFA_STA_FSM,
ZFA_STA_SERDES_PLL,
ZFA_STA_SERDES_SYNCED,
ZFA_STA_FMC_NR,
ZFA_STA_CALIB_BUSY,
/* Configuration register */
ZFAT_CFG_STA,
ZFAT_CFG_SRC,
......@@ -209,8 +245,6 @@ enum zfadc_dregs_enum {
ZFAT_POST,
/* Sample counter */
ZFAT_CNT,
/* Pattern data for the ADC chip */
ZFAT_ADC_TST_PATTERN,
/* start:declaration block requiring some order */
/* Channel 1 */
ZFA_CH1_CTL_RANGE,
......@@ -284,25 +318,30 @@ enum zfadc_dregs_enum {
ZFA_IRQ_VIC_DISABLE_MASK,
ZFA_IRQ_VIC_ENABLE_MASK,
ZFA_IRQ_VIC_MASK_STATUS,
/* DS18B20 UID/Temperature */
ZFA_DS18B20_ID_U,
ZFA_DS18B20_ID_L,
ZFA_DS18B20_TEMP,
ZFA_DS18B20_STAT,
/* UTC core */
ZFA_UTC_SECONDS,
ZFA_UTC_SECONDS_U,
ZFA_UTC_SECONDS_L,
ZFA_UTC_COARSE,
ZFA_UTC_TRIG_META,
ZFA_UTC_TRIG_SECONDS,
ZFA_UTC_TRIG_TIME_SECONDS_U,
ZFA_UTC_TRIG_TIME_SECONDS_L,
ZFA_UTC_TRIG_TIME_COARSE,
ZFA_UTC_TRIG_SECONDS_U,
ZFA_UTC_TRIG_SECONDS_L,
ZFA_UTC_TRIG_COARSE,
ZFA_UTC_TRIG_FINE,
ZFA_UTC_ACQ_START_META,
ZFA_UTC_ACQ_START_SECONDS,
ZFA_UTC_ACQ_START_SECONDS_U,
ZFA_UTC_ACQ_START_SECONDS_L,
ZFA_UTC_ACQ_START_COARSE,
ZFA_UTC_ACQ_START_FINE,
ZFA_UTC_ACQ_STOP_META,
ZFA_UTC_ACQ_STOP_SECONDS,
ZFA_UTC_ACQ_STOP_SECONDS_U,
ZFA_UTC_ACQ_STOP_SECONDS_L,
ZFA_UTC_ACQ_STOP_COARSE,
ZFA_UTC_ACQ_STOP_FINE,
ZFA_UTC_ACQ_END_META,
ZFA_UTC_ACQ_END_SECONDS,
ZFA_UTC_ACQ_END_SECONDS_U,
ZFA_UTC_ACQ_END_SECONDS_L,
ZFA_UTC_ACQ_END_COARSE,
ZFA_UTC_ACQ_END_FINE,
ZFA_HW_PARAM_COMMON_LAST,
};
......@@ -330,16 +369,6 @@ enum fa_sw_param_id {
ZFA_SW_PARAM_COMMON_LAST,
};
/*
* Bit pattern used in order to factorize code between SVEC and SPEC
* Depending of the carrier, ADC may have to listen vaious IRQ sources
* SVEC: only ACQ irq source (end DMA irq is manged by vmebus driver)
* SPEC: ACQ and DMA irq source
*/
enum fa_irq_src {
FA_IRQ_SRC_ACQ = 0x1,
FA_IRQ_SRC_DMA = 0x2,
};
/* adc IRQ values */
enum fa_irq_adc {
......@@ -351,25 +380,16 @@ enum fa_irq_adc {
carrier specific stuff, such as DMA or resets, from
mezzanine-specific operations). */
struct fa_dev; /* forward declaration */
struct fa_carrier_op {
char* (*get_gwname)(void);
int (*init) (struct fa_dev *);
int (*reset_core) (struct fa_dev *);
void (*exit) (struct fa_dev *);
int (*setup_irqs) (struct fa_dev *);
int (*free_irqs) (struct fa_dev *);
int (*enable_irqs) (struct fa_dev *);
int (*disable_irqs) (struct fa_dev *);
int (*ack_irq) (struct fa_dev *, int irq_id);
int (*dma_start)(struct zio_cset *cset);
void (*dma_done)(struct zio_cset *cset);
void (*dma_error)(struct zio_cset *cset);
};
/*
* Flag bit set when the ADC uses pattern data
*/
#define FA_DEV_F_PATTERN_DATA BIT(0)
/*
* fa_dev: is the descriptor of the FMC ADC mezzanine
*
* @fmc: the pointer to the fmc_device generic structure
* @pdev: the pointer to the fmc_device generic structure
* @zdev: is the pointer to the real zio_device in use
* @hwzdev: is the pointer to the fake zio_device, used to initialize and
* to remove a zio_device
......@@ -382,31 +402,31 @@ struct fa_carrier_op {
* @zero_offset: necessary offset to push the channel to zero (micro-Volts)
*/
struct fa_dev {
unsigned long flags;
struct device *msgdev; /**< device used to print messages */
/* the pointer to the fmc_device generic structure */
struct fmc_device *fmc;
/* the pointer to the platform_device generic structure */
struct platform_device *pdev;
/* the pointer to the real zio_device in use */
struct zio_device *zdev;
/* the pointer to the fake zio_device, used for init/remove */
struct zio_device *hwzdev;
/* carrier common base offset addresses obtained from SDB */
unsigned int fa_adc_csr_base;
unsigned int fa_spi_base;
unsigned int fa_ow_base;
unsigned int fa_carrier_csr_base;
unsigned int fa_irq_vic_base;
unsigned int fa_irq_adc_base;
unsigned int fa_utc_base;
struct fmc_slot *slot;
struct fa_memory_ops memops;
/* carrier common base offset addresses */
void *fa_adc_csr_base;
void *fa_spi_base;
void *fa_ow_base;
void *fa_top_level;
void *fa_irq_vic_base;
void *fa_irq_adc_base;
void *fa_utc_base;
/* DMA description */
struct zio_dma_sgt *zdma;
struct sg_table sgt;
/* carrier specific functions (init/exit/reset/readout/irq handling) */
struct fa_carrier_op *carrier_op;
/* carrier private data */
void *carrier_data;
int irq_src; /* list of irq sources to listen */
struct work_struct irq_work;
/*
* keep last core having fired an IRQ
......@@ -417,6 +437,7 @@ struct fa_dev {
/* Acquisition */
unsigned int n_shots;
unsigned int n_fires;
unsigned int transfers_left;
unsigned int mshot_max_samples;
/* Statistic informations */
......@@ -432,24 +453,45 @@ struct fa_dev {
/* Calibration Data */
struct fa_calib calib;
int32_t range[FA100M14B4C_NCHAN];
struct timer_list calib_timer;
/* flag */
int enable_auto_start;
struct dentry *reg_dump;
struct dentry *dbg_dir;
struct debugfs_regset32 dbg_reg32;
struct dentry *dbg_reg;
struct dentry *dbg_reg_spi;
struct dentry *dbg_trg_sw;
struct dentry *dbg_data_pattern;
/* Operations */
int (*sg_alloc_table_from_pages)(struct sg_table *sgt,
struct page **pages,
unsigned int n_pages,
unsigned int offset,
unsigned long size,
unsigned int max_segment,
gfp_t gfp_mask);
};
/*
* zfad_block
* @block is zio_block which contains data and metadata from a single shot
* @dev_mem_off is the offset in ADC internal memory. It points to the first
* sample of the stored shot
* @first_nent is the index of the first nent used for this block
* @cset: channel set source for the block
* @tx: DMA transfer descriptor
* @cookie: transfer token
*/
struct zfad_block {
struct zio_block *block;
uint32_t dev_mem_off;
unsigned int first_nent;
struct zio_cset *cset;
struct dma_async_tx_descriptor *tx;
dma_cookie_t cookie;
struct sg_table sgt;
void *dma_ctx;
};
/*
......@@ -493,23 +535,23 @@ static inline struct fa_dev *get_zfadc(struct device *dev)
return NULL;
}
static inline u32 fa_ioread(struct fa_dev *fa, unsigned long addr)
static inline u32 fa_ioread(struct fa_dev *fa, void *addr)
{
return fmc_readl(fa->fmc, addr);
return fa->memops.read(addr);
}
static inline void fa_iowrite(struct fa_dev *fa, u32 value, unsigned long addr)
static inline void fa_iowrite(struct fa_dev *fa, u32 value, void *addr)
{
fmc_writel(fa->fmc, value, addr);
fa->memops.write(value, addr);
}
static inline uint32_t fa_readl(struct fa_dev *fa,
unsigned int base_off,
void *base_off,
const struct zfa_field_desc *field)
{
uint32_t cur;
cur = fa_ioread(fa, base_off+field->offset);
cur = fa_ioread(fa, base_off + field->offset);
if (field->is_bitfield) {
/* apply mask and shift right accordlying to the mask */
cur &= field->mask;
......@@ -517,11 +559,12 @@ static inline uint32_t fa_readl(struct fa_dev *fa,
} else {
cur &= field->mask; /* bitwise and with the mask */
}
return cur;
}
static inline void fa_writel(struct fa_dev *fa,
unsigned int base_off,
void *base_off,
const struct zfa_field_desc *field,
uint32_t usr_val)
{
......@@ -536,12 +579,21 @@ static inline void fa_writel(struct fa_dev *fa,
val = usr_val * (field->mask & -(field->mask));
if (val & ~field->mask)
dev_warn(fa->msgdev,
"addr 0x%lx: value 0x%x doesn't fit mask 0x%x\n",
"addr %p: value 0x%x doesn't fit mask 0x%x\n",
base_off+field->offset, val, field->mask);
val &= field->mask;
val |= cur;
}
fa_iowrite(fa, val, base_off+field->offset);
fa_iowrite(fa, val, base_off + field->offset);
}
static inline int fa_is_flag_set(struct fa_dev *fa, unsigned long flag)
{
const struct fmc_adc_platform_data *fmc_adc_pdata;
fmc_adc_pdata = fa->pdev->dev.platform_data;
return !!(fmc_adc_pdata->flags & flag);
}
extern struct bin_attribute dev_attr_calibration;
......@@ -549,25 +601,31 @@ extern struct bin_attribute dev_attr_calibration;
/* Global variable exported by fa-core.c */
extern struct workqueue_struct *fa_workqueue;
/* Global variable exported by fa-spec.c */
extern struct fa_carrier_op fa_spec_op;
/* Global variable exported by fa-svec.c */
extern struct fa_carrier_op fa_svec_op;
/* Global variable exported by fa-regfield.c */
/* Global variable exported by fa-regtable.c */
extern const struct zfa_field_desc zfad_regs[];
/* Functions exported by fa-core.c */
extern int zfad_fsm_command(struct fa_dev *fa, uint32_t command);
extern int zfad_apply_offset(struct zio_channel *chan);
extern void zfad_reset_offset(struct fa_dev *fa);
extern int zfad_convert_hw_range(uint32_t bitmask);
extern int zfad_set_range(struct fa_dev *fa, struct zio_channel *chan,
int range);
extern int zfad_get_chx_index(unsigned long addr, struct zio_channel *chan);
extern int zfad_pattern_data_enable(struct fa_dev *fa, uint16_t pattern,
unsigned int enable);
extern int32_t fa_temperature_read(struct fa_dev *fa);
extern int fa_trigger_software(struct fa_dev *fa);
extern int fa_fsm_wait_state(struct fa_dev *fa,
enum fa100m14b4c_fsm_state state,
unsigned int timeout_us);
extern int fa_adc_data_pattern_set(struct fa_dev *fa, uint16_t pattern,
unsigned int enable);
extern int fa_adc_data_pattern_get(struct fa_dev *fa, uint16_t *pattern,
unsigned int *enable);
/* Temporarily, user values are the same as hardware values */
extern int zfad_convert_user_range(uint32_t user_val);
extern int fa_adc_range_set(struct fa_dev *fa, struct zio_channel *chan,
int range);
extern int zfad_get_chx_index(unsigned long addr, unsigned int chan);
/* Function exported by fa-dma.c */
extern void fa_irq_work(struct work_struct *work);
/* Functions exported by fa-zio-drv.c */
extern int fa_zio_register(void);
......@@ -576,43 +634,35 @@ extern int fa_zio_init(struct fa_dev *fa);
extern void fa_zio_exit(struct fa_dev *fa);
/* Functions exported by fa-zio-trg.c */
extern void zfat_trigger_source_reset(struct fa_dev *fa);
extern int fa_trig_init(void);
extern void fa_trig_exit(void);
/* Functions exported by fa-irq.c */
extern int zfad_dma_start(struct zio_cset *cset);
extern void zfad_dma_done(struct zio_cset *cset);
extern void zfad_dma_error(struct zio_cset *cset);
extern void zfat_irq_trg_fire(struct zio_cset *cset);
extern void zfat_irq_acq_end(struct zio_cset *cset);
extern int fa_setup_irqs(struct fa_dev *fa);
extern int fa_free_irqs(struct fa_dev *fa);
extern int fa_enable_irqs(struct fa_dev *fa);
extern int fa_disable_irqs(struct fa_dev *fa);
/* Functions exported by onewire.c */
extern int fa_onewire_init(struct fa_dev *fa);
extern void fa_onewire_exit(struct fa_dev *fa);
extern int fa_read_temp(struct fa_dev *fa, int verbose);
/* functions exported by spi.c */
extern int fa_spi_xfer(struct fa_dev *fa, int cs, int num_bits,
uint32_t tx, uint32_t *rx);
extern int fa_spi_init(struct fa_dev *fd);
extern void fa_spi_exit(struct fa_dev *fd);
/* fmc extended function */
signed long fmc_find_sdb_device_ext(struct sdb_array *tree,
uint64_t vid, uint32_t did, int index,
unsigned long *sz);
/* function exporetd by fa-calibration.c */
extern void fa_read_eeprom_calib(struct fa_dev *fa);
extern int fa_calib_init(struct fa_dev *fa);
extern void fa_calib_exit(struct fa_dev *fa);
extern void fa_calib_config(struct fa_dev *fa);
extern void fa_calib_adc_config_chan(struct fa_dev *fa, unsigned int chan,
int32_t temperature);
extern int fa_calib_dac_config_chan(struct fa_dev *fa, unsigned int chan,
int32_t temperature);
/* functions exported by fa-debug.c */
extern int fa_debug_init(struct fa_dev *fa);
extern void fa_debug_exit(struct fa_dev *fa);
#endif /* __KERNEL__ */
#endif /* FMC_ADC_H_ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Some utility functions not supported in the current version of fmc-bus.
*
* Copyright (C) 2012-2019 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*/
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include <linux/err.h>
#include <asm/byteorder.h>
/* Finds index-th SDB device that matches (vid/did) pair. */
signed long fmc_find_sdb_device_ext(struct sdb_array *tree,
uint64_t vid, uint32_t did, int index,
unsigned long *sz)
{
signed long res = -ENODEV;
union sdb_record *r;
struct sdb_product *p;
struct sdb_component *c;
int i, n = tree->len;
uint64_t last, first;
int ci = 0;
/* FIXME: what if the first interconnect is not at zero? */
for (i = 0; i < n; i++) {
r = &tree->record[i];
c = &r->dev.sdb_component;
p = &c->product;
if (!IS_ERR(tree->subtree[i])) {
/* FIXME: this index SHOULD be recursive, too */
res = fmc_find_sdb_device(tree->subtree[i],
vid, did, sz);
if (res >= 0 && ci++ == index)
return res + tree->baseaddr;
}
if (r->empty.record_type != sdb_type_device)
continue;
if (__be64_to_cpu(p->vendor_id) != vid)
continue;
if (__be32_to_cpu(p->device_id) != did)
continue;
/* found */
last = __be64_to_cpu(c->addr_last);
first = __be64_to_cpu(c->addr_first);
if (sz)
*sz = (typeof(*sz)) (last + 1 - first);
if (ci++ == index)
return first + tree->baseaddr;
}
return res;
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Access to 1w thermometer
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Copied from the fine-delay driver and updated with fmc-adc variable
*/
#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/delay.h>
#include "fmc-adc-100m14b4cha.h"
#define R_CSR 0x0
#define R_CDR 0x4
#define CSR_DAT_MSK (1<<0)
#define CSR_RST_MSK (1<<1)
#define CSR_OVD_MSK (1<<2)
#define CSR_CYC_MSK (1<<3)
#define CSR_PWR_MSK (1<<4)
#define CSR_IRQ_MSK (1<<6)
#define CSR_IEN_MSK (1<<7)
#define CSR_SEL_OFS 8
#define CSR_SEL_MSK (0xF<<8)
#define CSR_POWER_OFS 16
#define CSR_POWER_MSK (0xFFFF<<16)
#define CDR_NOR_MSK (0xFFFF<<0)
#define CDR_OVD_OFS 16
#define CDR_OVD_MSK (0xFFFF<<16)
#define CLK_DIV_NOR (624)
#define CLK_DIV_OVD (124)
#define CMD_ROM_SEARCH 0xF0
#define CMD_ROM_READ 0x33
#define CMD_ROM_MATCH 0x55
#define CMD_ROM_SKIP 0xCC
#define CMD_ROM_ALARM_SEARCH 0xEC
#define CMD_CONVERT_TEMP 0x44
#define CMD_WRITE_SCRATCHPAD 0x4E
#define CMD_READ_SCRATCHPAD 0xBE
#define CMD_COPY_SCRATCHPAD 0x48
#define CMD_RECALL_EEPROM 0xB8
#define CMD_READ_POWER_SUPPLY 0xB4
#define FA_OW_PORT 0 /* what is this slow? */
static void ow_writel(struct fa_dev *fa, uint32_t val, unsigned long reg)
{
fa_iowrite(fa, val, fa->fa_ow_base + reg);
}
static uint32_t ow_readl(struct fa_dev *fa, unsigned long reg)
{
return fa_ioread(fa, fa->fa_ow_base + reg);
}
static int ow_reset(struct fa_dev *fa, int port)
{
uint32_t reg, data;
data = ((port << CSR_SEL_OFS) & CSR_SEL_MSK)
| CSR_CYC_MSK | CSR_RST_MSK;
ow_writel(fa, data, R_CSR);
while (ow_readl(fa, R_CSR) & CSR_CYC_MSK)
/* FIXME: timeout */;
reg = ow_readl(fa, R_CSR);
return ~reg & CSR_DAT_MSK;
}
static int slot(struct fa_dev *fa, int port, int bit)
{
uint32_t reg, data;
data = ((port<<CSR_SEL_OFS) & CSR_SEL_MSK)
| CSR_CYC_MSK | (bit & CSR_DAT_MSK);
ow_writel(fa, data, R_CSR);
while (ow_readl(fa, R_CSR) & CSR_CYC_MSK)
/* FIXME: timeout */;
reg = ow_readl(fa, R_CSR);
return reg & CSR_DAT_MSK;
}
static int read_bit(struct fa_dev *fa, int port)
{
return slot(fa, port, 0x1);
}
static int write_bit(struct fa_dev *fa, int port, int bit)
{
return slot(fa, port, bit);
}
static int ow_read_byte(struct fa_dev *fa, int port)
{
int byte = 0, i;
for (i = 0; i < 8; i++)
byte |= (read_bit(fa, port) << i);
return byte;
}
static int ow_write_byte(struct fa_dev *fa, int port, int byte)
{
int data = 0;
int i;
for (i = 0; i < 8; i++) {
data |= write_bit(fa, port, (byte & 0x1)) << i;
byte >>= 1;
}
return 0; /* success */
}
static int ow_write_block(struct fa_dev *fa, int port, uint8_t *block, int len)
{
int i;
for (i = 0; i < len; i++)
ow_write_byte(fa, port, block[i]);
return 0;
}
static int ow_read_block(struct fa_dev *fa, int port, uint8_t *block, int len)
{
int i;
for (i = 0; i < len; i++)
block[i] = ow_read_byte(fa, port);
return 0;
}
static int ds18x_read_serial(struct fa_dev *fa)
{
if (!ow_reset(fa, 0)) {
pr_err("%s: Failure in resetting one-wire channel\n",
KBUILD_MODNAME);
return -EIO;
}
ow_write_byte(fa, FA_OW_PORT, CMD_ROM_READ);
return ow_read_block(fa, FA_OW_PORT, fa->ds18_id, 8);
}
static int ds18x_access(struct fa_dev *fa)
{
if (!ow_reset(fa, 0))
goto out;
if (0) {
/* select the rom among several of them */
if (ow_write_byte(fa, FA_OW_PORT, CMD_ROM_MATCH) < 0)
goto out;
return ow_write_block(fa, FA_OW_PORT, fa->ds18_id, 8);
} else {
/* we have one only, so skip rom */
return ow_write_byte(fa, FA_OW_PORT, CMD_ROM_SKIP);
}
out:
pr_err("%s: Failure in one-wire communication\n", KBUILD_MODNAME);
return -EIO;
}
static void __temp_command_and_next_t(struct fa_dev *fa, int cfg_reg)
{
int ms;
ds18x_access(fa);
ow_write_byte(fa, FA_OW_PORT, CMD_CONVERT_TEMP);
/* The conversion takes some time, so mark when will it be ready */
ms = 94 * ( 1 << (cfg_reg >> 5));
fa->next_t = jiffies + msecs_to_jiffies(ms);
}
int fa_read_temp(struct fa_dev *fa, int verbose)
{
int i, temp;
unsigned long j;
uint8_t data[9];
/* If first conversion, ask for it first */
if (fa->next_t == 0)
__temp_command_and_next_t(fa, 0x7f /* we ignore: max time */);
/* Wait for it to be ready: (FIXME: we need a time policy here) */
j = jiffies;
if (time_before(j, fa->next_t)) {
/* If we cannot sleep, return the previous value */
if (in_atomic())
return fa->temp;
msleep(jiffies_to_msecs(fa->next_t - j));
}
ds18x_access(fa);
ow_write_byte(fa, FA_OW_PORT, CMD_READ_SCRATCHPAD);
ow_read_block(fa, FA_OW_PORT, data, 9);
if (verbose > 1) {
pr_info("%s: Scratchpad: ", __func__);
for (i = 0; i < 9; i++)
printk(KERN_CONT "%02x%c", data[i],
i == 8 ? '\n' : ':');
}
temp = ((int)data[1] << 8) | ((int)data[0]);
if (temp & 0x1000)
temp = -0x10000 + temp;
fa->temp = temp;
if (verbose) {
pr_info("%s: Temperature 0x%x (%i bits: %i.%03i)\n", __func__,
temp, 9 + (data[4] >> 5),
temp / 16, (temp & 0xf) * 1000 / 16);
}
__temp_command_and_next_t(fa, data[4]); /* start next conversion */
return temp;
}
int fa_onewire_init(struct fa_dev *fa)
{
ow_writel(fa, ((CLK_DIV_NOR & CDR_NOR_MSK)
| (( CLK_DIV_OVD << CDR_OVD_OFS) & CDR_OVD_MSK)),
R_CDR);
if (ds18x_read_serial(fa) < 0)
return -EIO;
return 0;
}
void fa_onewire_exit(struct fa_dev *fa)
{
/* Nothing to do */
}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2020 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#ifndef __FMC_ADC_PDATA_H__
#define __FMC_ADC_PDATA_H__
#define FMC_ADC_BIG_ENDIAN BIT(0)
#define FMC_ADC_NOSQUASH_SCATTERLIST BIT(1)
/*
* In principle this should not be necessary. The two variants should
* be as close as possible to each other. But this is not the case, the DMA
* interface is different and we need to distinguish between SPEC and SVEC.
* NOTE any other carrier is not supported!
*/
#define FMC_ADC_SVEC BIT(3)
struct fmc_adc_platform_data {
unsigned long flags;
uint8_t calib_trig_time;
uint8_t calib_trig_threshold;
uint8_t calib_trig_internal;
};
#endif
......@@ -50,7 +50,8 @@ int fa_spi_xfer(struct fa_dev *fa, int cs, int num_bits,
while (fa_ioread(fa, fa->fa_spi_base + FA_SPI_CTRL)
& FA_SPI_CTRL_BUSY) {
if (jiffies > j) {
dev_err(fa->msgdev, "SPI transfer error\n");
dev_err(fa->msgdev, "SPI transfer error cs:%d, ctrl: 0x%x\n",
cs, fa_ioread(fa, fa->fa_spi_base + FA_SPI_CTRL));
err = -EIO;
goto out;
}
......
......@@ -11,7 +11,7 @@ REPO_PARENT ?= ../..
DESTDIR ?= /usr/local
GIT_VERSION := $(shell git describe --dirty --long --tags)
CFLAGS += -I../kernel -Wno-trigraphs -Wall -ggdb -O2 $(EXTRACFLAGS)
CFLAGS += -I../kernel -Wno-trigraphs -Wall -Werror -ggdb -O2 $(EXTRACFLAGS)
CFLAGS += -DGIT_VERSION="\"$(GIT_VERSION)\""
CC ?= $(CROSS_COMPILE)gcc
......
......@@ -18,7 +18,6 @@
#include <fmc-adc-100m14b4cha.h>
static const char program_name[] = "fau-calibration";
static char options[] = "hf:o:D:b";
static const char help_msg[] =
"Usage: fau-calibration [options]\n"
......@@ -55,8 +54,6 @@ static int fau_calibration_read(char *path, struct fa_calib *calib,
{
int fd;
int ret = 0;
uint16_t *data16 = (uint16_t *)calib;
int i;
fd = open(path, O_RDONLY);
if (fd < 0)
......@@ -66,10 +63,6 @@ static int fau_calibration_read(char *path, struct fa_calib *calib,
ret = read(fd, calib, sizeof(*calib));
close(fd);
/* Fix endianess */
for (i = 0; i < sizeof(*calib) / sizeof(uint16_t); ++i)
data16[i] = le16toh(data16[i]);
return ret;
}
......@@ -95,6 +88,13 @@ static void fau_calibration_dump_stanza(struct fa_calib_stanza *stanza)
*/
static void fau_calibration_dump_human(struct fa_calib *calib)
{
uint16_t *data16 = (uint16_t *)calib;
int i;
/* Fix endianess */
for (i = 0; i < sizeof(*calib) / sizeof(uint16_t); ++i)
data16[i] = le16toh(data16[i]);
fputs("ADC Range 10V\n", stdout);
fau_calibration_dump_stanza(&calib->adc[FA100M14B4C_RANGE_10V]);
fputs("DAC Range 10V\n", stdout);
......@@ -116,9 +116,9 @@ static void fau_calibration_dump_human(struct fa_calib *calib)
* Print binary calibration data on stdout
* @calib: calibration data
*/
static void fau_calibration_dump_machine(struct fa_calib *calib)
static int fau_calibration_dump_machine(struct fa_calib *calib)
{
write(fileno(stdout), calib, sizeof(*calib));
return write(fileno(stdout), calib, sizeof(*calib));
}
/**
......@@ -130,10 +130,7 @@ static void fau_calibration_dump_machine(struct fa_calib *calib)
*/
static int fau_calibration_write(unsigned int devid, struct fa_calib *calib)
{
struct fa_calib calib_cp;
char path[128];
uint16_t *data16;
int i;
int fd;
int ret;
......@@ -141,16 +138,10 @@ static int fau_calibration_write(unsigned int devid, struct fa_calib *calib)
"/sys/bus/zio/devices/adc-100m14b-%04x/calibration_data",
devid);
/* Fix endianess */
memcpy(&calib_cp, calib, sizeof(calib_cp));
data16 = (uint16_t *) &calib_cp;
for (i = 0; i < sizeof(calib_cp) / sizeof(uint16_t); ++i)
data16[i] = htole16(data16[i]);
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
ret = write(fd, &calib_cp, sizeof(calib_cp));
ret = write(fd, calib, sizeof(*calib));
close(fd);
return ret;
......
......@@ -53,7 +53,7 @@ const char *attribute[] = {
/* Write a sysfs attribute */
int fau_write_attribute(enum fau_attribute attr, uint32_t val)
{
int fd;
int ret, fd;
char buf[buf_len], fullpath[200];
/* convert val to string */
......@@ -66,9 +66,9 @@ int fau_write_attribute(enum fau_attribute attr, uint32_t val)
fd = open(fullpath, O_WRONLY);
if (fd < 0)
return -ENOENT;
write(fd, buf, strlen(buf));
ret = write(fd, buf, strlen(buf));
close(fd);
return 0;
return ret;
}
static void fau_help()
......
......@@ -41,7 +41,7 @@ static void print_version(char *pname)
int main(int argc, char **argv)
{
int fd, addr, count, usec;
int fd, addr, count, usec, ret;
int64_t tics;
if ((argc == 2) &&
......@@ -83,15 +83,15 @@ int main(int argc, char **argv)
char b[]={0x00, 0xff};
lseek(fd, addr, SEEK_SET);
write(fd, b + 1, 1);
ret = write(fd, b + 1, 1);
lseek(fd, addr, SEEK_SET);
write(fd, b + 0, 1);
ret = write(fd, b + 0, 1);
if (count > 1) {
tics += usec;
delay_to(tics);
}
} while (--count);
return 0;
return ret;
}
Subproject commit d8bef4d89361194c2e5644e751add9bd9ffa106d
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