Commit cf9bcbc4 authored by Federico Vaga's avatar Federico Vaga

Merge branch 'feature/calibration' into develop

parents c8eccabe 1cf4b8e0
......@@ -91,3 +91,68 @@ void fa_read_eeprom_calib(struct fa_dev *fa)
fa_endian_calib(&fa->calib);
fa_verify_calib(&fa->fmc->dev, &fa->calib, &fa_identity_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)
{
int reg = zfad_get_chx_index(ZFA_CHx_CTL_RANGE, chan);
int range = fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[reg]);
zfad_set_range(fa, chan, range);
zfad_apply_offset(chan);
}
static ssize_t fa_write_eeprom(struct file *file, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
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_endian_calib(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]);
return count;
}
static ssize_t fa_read_eeprom(struct file *file, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct fa_dev *fa = get_zfadc(dev);
if (off != 0 || count < sizeof(fa->calib))
return -EINVAL;
memcpy(buf, &fa->calib, sizeof(fa->calib));
return count;
}
struct bin_attribute dev_attr_calibration = {
.attr = {
.name = "calibration_data",
.mode = 0744,
},
.size = sizeof(struct fa_calib),
.write = fa_write_eeprom,
.read = fa_read_eeprom,
};
......@@ -394,7 +394,7 @@ static int __fa_init(struct fa_dev *fa)
}
}
/* Retrieve calibration from the eeprom and validate*/
/* 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]);
......@@ -557,7 +557,7 @@ int fa_probe(struct fmc_device *fmc)
err = fa_setup_irqs(fa);
if (err < 0)
goto out;
goto out_irq;
/* Pin the carrier */
if (!try_module_get(fmc->owner))
......@@ -567,6 +567,7 @@ int fa_probe(struct fmc_device *fmc)
out_mod:
fa_free_irqs(fa);
out_irq:
out:
while (--m, --i >= 0)
if (m->exit)
......
......@@ -544,6 +544,10 @@ static int zfad_zio_probe(struct zio_device *zdev)
if (err)
return err;
err = device_create_bin_file(&zdev->cset->head.dev, &dev_attr_calibration);
if (err)
return err;
/* We don't have csets at this point, so don't do anything more */
return 0;
}
......@@ -556,6 +560,8 @@ static int zfad_zio_probe(struct zio_device *zdev)
*/
static int zfad_zio_remove(struct zio_device *zdev)
{
device_remove_bin_file(&zdev->cset->head.dev, &dev_attr_calibration);
return 0;
}
......
......@@ -8,6 +8,12 @@
#ifndef FMC_ADC_100M14B4C_H_
#define FMC_ADC_100M14B4C_H_
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
#ifndef BIT
#define BIT(nr) (1UL << (nr))
#endif
......@@ -128,6 +134,17 @@ enum fa100m14b4c_fsm_state {
FA100M14B4C_STATE_DECR,
};
/* ADC and DAC Calibration, from EEPROM */
struct fa_calib_stanza {
int16_t offset[4]; /* One per channel */
uint16_t gain[4]; /* One per channel */
uint16_t temperature;
};
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 */
};
#ifdef __KERNEL__ /* All the rest is only of kernel users */
#include <linux/dma-mapping.h>
......@@ -350,18 +367,6 @@ struct fa_carrier_op {
void (*dma_error)(struct zio_cset *cset);
};
/* ADC and DAC Calibration, from EEPROM */
struct fa_calib_stanza {
int16_t offset[4]; /* One per channel */
uint16_t gain[4]; /* One per channel */
uint16_t temperature;
};
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 */
};
/*
* fa_dev: is the descriptor of the FMC ADC mezzanine
*
......@@ -540,6 +545,8 @@ static inline void fa_writel(struct fa_dev *fa,
fa_iowrite(fa, val, base_off+field->offset);
}
extern struct bin_attribute dev_attr_calibration;
/* Global variable exported by fa-core.c */
extern struct workqueue_struct *fa_workqueue;
......
fau-acq-time
fau-trg-config
fau-calibration
parport-burst
......@@ -18,6 +18,7 @@ CC ?= $(CROSS_COMPILE)gcc
progs := fau-trg-config
progs += fau-acq-time
progs += fau-calibration
progs += parport-burst
# we are not in the kernel, so we need to piggy-back on "make modules"
......
// SPDX-License-Identifier: GPL-3.0-or-later
/*
* It lists all VME devices
* Copyright (C) 2018 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#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"
"\n"
"It reads calibration data from a file that contains it in binary\n"
"form and it shows it on STDOUT in binary form or in human readable\n"
"one (default).\n"
"This could be used to change the ADC calibration data at runtime\n"
"by redirectiong the binary output of this program to the proper \n"
"sysfs binary attribute\n"
"\n"
"General options:\n"
"-h Print this message\n"
"-b Show Calibration in binary form \n"
"\n"
"Read options:\n"
"-f Source file where to read calibration data from\n"
"-o Offset in bytes within the file (default 0)\n"
"Write options:\n"
"-D FMC ADC Target Device ID\n"
"\n";
/**
* Read calibration data from file
* @path: file path
* @data: data location
* @size: data size
* @offset: offset in file
*
* Return: number of bytes read
*/
static int fau_calibration_read(char *path, void *data,
size_t size, off_t offset)
{
int fd;
int ret = 0;
fd = open(path, O_RDONLY);
if (fd < 0)
return -1;
ret = lseek(fd, offset, SEEK_SET);
if (ret >= 0)
ret = read(fd, data, size);
close(fd);
return ret;
}
static void fau_calibration_dump_stanza(struct fa_calib_stanza *stanza)
{
fprintf(stdout, " temperature: 0x%x\n",
stanza->temperature);
fprintf(stdout, " gain: [0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16"]\n",
stanza->gain[0],
stanza->gain[1],
stanza->gain[2],
stanza->gain[3]);
fprintf(stdout, " offset: [0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16", 0x%04"PRIx16"]\n",
stanza->offset[0],
stanza->offset[1],
stanza->offset[2],
stanza->offset[3]);
}
/**
* Print calibration data on stdout in humand readable format
* @calib: calibration data
*/
static void fau_calibration_dump_human(struct fa_calib *calib)
{
fputs("ADC Range 10V\n", stdout);
fau_calibration_dump_stanza(&calib->adc[FA100M14B4C_RANGE_10V]);
fputs("DAC Range 10V\n", stdout);
fau_calibration_dump_stanza(&calib->dac[FA100M14B4C_RANGE_10V]);
fputs("ADC Range 1V\n", stdout);
fau_calibration_dump_stanza(&calib->adc[FA100M14B4C_RANGE_1V]);
fputs("DAC Range 1V\n", stdout);
fau_calibration_dump_stanza(&calib->dac[FA100M14B4C_RANGE_1V]);
fputs("ADC Range 100mV\n", stdout);
fau_calibration_dump_stanza(&calib->adc[FA100M14B4C_RANGE_100mV]);
fputs("DAC Range 100mV\n", stdout);
fau_calibration_dump_stanza(&calib->dac[FA100M14B4C_RANGE_100mV]);
fputc('\n', stdout);
}
/**
* Print binary calibration data on stdout
* @calib: calibration data
*/
static void fau_calibration_dump_machine(struct fa_calib *calib)
{
write(fileno(stdout), calib, sizeof(*calib));
}
/**
* Write calibration data to device
* @devid: Device ID
* @data: data location
* @size: data size
*
* Return: number of bytes wrote
*/
static int fau_calibration_write(unsigned int devid,
void *data, size_t size)
{
char path[128];
int fd;
int ret;
sprintf(path,
"/sys/bus/zio/devices/adc-100m14b-%04x/cset0/calibration_data",
devid);
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
ret = write(fd, data, size);
close(fd);
return ret;
}
int main(int argc, char *argv[])
{
char c;
int ret;
char *path = NULL;
unsigned int offset = 0;
unsigned int devid = 0;
int show_bin = 0, write = 0;
struct fa_calib calib;
while ((c = getopt(argc, argv, options)) != -1) {
switch (c) {
default:
case 'h':
fprintf(stderr, help_msg);
exit(EXIT_SUCCESS);
case 'D':
ret = sscanf(optarg, "0x%x", &devid);
if (ret != 1) {
fprintf(stderr,
"Invalid devid %s\n",
optarg);
exit(EXIT_FAILURE);
}
write = 1;
break;
case 'f':
path = optarg;
break;
case 'o':
ret = sscanf(optarg, "0x%x", &offset);
if (ret != 1) {
ret = sscanf(optarg, "%u", &offset);
if (ret != 1) {
fprintf(stderr,
"Invalid offset %s\n",
optarg);
exit(EXIT_FAILURE);
}
}
break;
case 'b':
show_bin = 1;
break;
}
}
if (!path) {
fputs("Calibration file is mandatory\n", stderr);
exit(EXIT_FAILURE);
}
/* Read EEPROM file */
ret = fau_calibration_read(path, &calib, sizeof(calib), offset);
if (ret < 0) {
fprintf(stderr, "Can't read calibration data from '%s'. %s\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
if (ret != sizeof(calib)) {
fprintf(stderr,
"Can't read all calibration data from '%s'. %s\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
/* Show calibration data*/
if (show_bin)
fau_calibration_dump_machine(&calib);
else if(!write)
fau_calibration_dump_human(&calib);
/* Write calibration data */
if (write) {
ret = fau_calibration_write(devid, &calib, sizeof(calib));
if (ret < 0) {
fprintf(stderr,
"Can't write calibration data to '0x%x'. %s\n",
devid, strerror(errno));
exit(EXIT_FAILURE);
}
if (ret != sizeof(calib)) {
fprintf(stderr,
"Can't write all calibration data to '0x%x'. %s\n",
devid, strerror(errno));
exit(EXIT_FAILURE);
}
}
exit(EXIT_SUCCESS);
}
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