Commit 66a090be authored by Federico Vaga's avatar Federico Vaga

drv: add support to set/get run-time calibration data

Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent 9c2c4765
......@@ -17,6 +17,7 @@
#include <linux/jhash.h>
#include <linux/slab.h>
#include <linux/ipmi-fru.h>
#include <linux/zio.h>
#include "libsdbfs.h"
#include "fmc-tdc.h"
......@@ -109,59 +110,136 @@ out:
return year;
}
/**
* @calib: calibration data
*
* We know for sure that our structure is only made of 32bit fields
*/
static void ft_calib_le32_to_cpus(struct ft_calibration_raw *calib)
{
int i;
uint32_t *p = (uint32_t *)calib;
for (i = 0; i < sizeof(*calib) / sizeof(uint32_t); i++)
le32_to_cpus(p + i); /* s == in situ */
}
/**
* @calib: calibration data
*
* We know for sure that our structure is only made of 32bit fields
*/
static void ft_calib_cpu_to_le32s(struct ft_calibration_raw *calib)
{
int i;
uint32_t *p = (uint32_t *)calib;
for (i = 0; i < sizeof(*calib) / sizeof(uint32_t); i++)
cpu_to_le32s(p + i); /* s == in situ */
}
static void ft_calib_cpy_from_raw(struct ft_calibration *calib,
struct ft_calibration_raw *calib_raw)
{
int i;
ft_calib_le32_to_cpus(calib_raw);
calib->zero_offset[0] = 0;
for (i = 1; i < FT_NUM_CHANNELS; i++)
calib->zero_offset[i] = calib_raw->zero_offset[i - 1] / 100;
calib->vcxo_default_tune = calib_raw->vcxo_default_tune / 100;
calib->calibration_temp = calib_raw->calibration_temp;
calib->wr_offset = calib_raw->wr_offset / 100;
calib->wr_offset += wr_calibration_offset_carrier;
}
static void ft_calib_cpy_to_raw(struct ft_calibration_raw *calib_raw,
struct ft_calibration *calib)
{
int i;
for (i = 1; i < FT_NUM_CHANNELS; i++)
calib_raw->zero_offset[i - 1] = calib->zero_offset[i] * 100;
calib_raw->vcxo_default_tune = calib->vcxo_default_tune * 100;
calib_raw->calibration_temp = calib->calibration_temp;
calib_raw->wr_offset = (calib->wr_offset - wr_calibration_offset_carrier) * 100;
ft_calib_cpu_to_le32s(calib_raw);
}
/* This is the only thing called by outside */
int ft_handle_eeprom_calibration(struct fmctdc_dev *ft)
{
struct ft_calibration *calib;
struct ft_calibration_raw calib_raw;
struct device *d = &ft->fmc->dev;
int i;
u32 raw_calib[7], year;
/* Retrieve and validate the calibration */
calib = &ft->calib;
memcpy(calib, &default_calibration, sizeof(struct ft_calibration));
i = ft_read_calibration_eeprom(ft->fmc, raw_calib, sizeof(raw_calib));
if (i < 0) {
i = ft_read_calibration_eeprom(ft->fmc, &calib_raw, sizeof(calib_raw));
if (i >= 0) {
u32 year;
ft_calib_cpy_from_raw(calib, &calib_raw);
year = __get_ipmi_fru_id_year(ft);
if (year < WR_OFFSET_FIX_YEAR) {
calib->wr_offset = wr_calibration_offset;
calib->wr_offset += wr_calibration_offset_carrier;
dev_warn(d,
"Apply default calibration correction to White-Rabbit offset if done before 2018 (%d)\n",
year);
}
} else {
dev_err(d,
"Failed to read calibration EEPROM. Using default.\n");
for (i = 0; i < FT_NUM_CHANNELS; i++)
calib->zero_offset[i] = 0;
calib->vcxo_default_tune = 32000;
} else {
calib->zero_offset[0] = 0;
for (i = FT_CH_1 + 1; i < FT_NUM_CHANNELS; i++)
calib->zero_offset[i] =
le32_to_cpu(raw_calib[i - 1]) / 100;
calib->vcxo_default_tune = le32_to_cpu(raw_calib[4]);
memcpy(calib, &default_calibration, sizeof(*calib));
calib->wr_offset += wr_calibration_offset_carrier;
}
calib->calibration_temp = le32_to_cpu(raw_calib[5]);
calib->wr_offset = le32_to_cpu(raw_calib[6]) / 100;
return 0;
}
year = __get_ipmi_fru_id_year(ft);
if (year < WR_OFFSET_FIX_YEAR) {
calib->wr_offset = wr_calibration_offset;
dev_warn(d,
"Apply default calibration correction to White-Rabbit offset if done before 2018 (%d)\n",
year);
}
calib->wr_offset += wr_calibration_offset_carrier;
static ssize_t ft_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 fmctdc_dev *ft = to_zio_dev(dev)->priv_d;
struct ft_calibration_raw *calib_raw = (struct ft_calibration_raw *) buf;
if (ft->verbose) {
/* Print verbose messages */
for (i = 0; i < ARRAY_SIZE(calib->zero_offset); i++)
dev_info(d, "calib: zero_offset[%i] = %i ps\n",
i, calib->zero_offset[i]);
if (off != 0 || count != sizeof(*calib_raw))
return -EINVAL;
dev_info(d, "calib: vcxo_default_tune %i\n",
calib->vcxo_default_tune);
dev_info(d, "calib: wr offset = %i ps\n",
calib->wr_offset);
}
return 0;
ft_calib_cpy_from_raw(&ft->calib, calib_raw);
return count;
}
static ssize_t ft_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 fmctdc_dev *ft = to_zio_dev(dev)->priv_d;
struct ft_calibration_raw *calib_raw = (struct ft_calibration_raw *) buf;
if (off != 0 || count < sizeof(*calib_raw))
return -EINVAL;
ft_calib_cpy_to_raw(calib_raw, &ft->calib);
return count;
}
struct bin_attribute dev_attr_calibration = {
.attr = {
.name = "calibration_data",
.mode = 0644,
},
.size = sizeof(struct ft_calibration_raw),
.write = ft_write_eeprom,
.read = ft_read_eeprom,
};
......@@ -14,6 +14,12 @@
#ifndef __FMC_TDC_H__
#define __FMC_TDC_H__
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
#define FT_VERSION_MAJ 2 /* version of the driver */
#define FT_VERSION_MIN 1
......@@ -64,6 +70,23 @@ enum ft_command {
FT_CMD_IDENTIFY_OFF
};
/**
* struct ft_calibration - TDC calibration data
* @zero_offset: time difference to channel 1
* @vcxo_default_tune: Default DAC value for VCXO. Set during init and for
* local timing
* @calibration_temp: Temperature at which the device has been calibrated
* @wr_offset: White Rabbit timescale offset in ps
*
* All of these are little endian in the EEPROM
*/
struct ft_calibration_raw {
int32_t zero_offset[FT_NUM_CHANNELS - 1];
uint32_t vcxo_default_tune;
uint32_t calibration_temp;
int32_t wr_offset;
};
/* rest of the file is kernel-only */
#ifdef __KERNEL__
......@@ -84,17 +107,20 @@ enum ft_channel_flags {
struct fmctdc_dev;
struct ft_calibration { /* All of these are big endian in the EEPROM */
/* Input-to-WR timebase offset in ps. */
int32_t zero_offset[5];
/* Default DAC value for VCXO. Set during init and for local timing */
/**
* struct ft_calibration - TDC calibration data
* @zero_offset: Input-to-WR timebase offset in ps
* @vcxo_default_tune: Default DAC value for VCXO. Set during init and for
* local timing
* @calibration_temp: Temperature at which the device has been calibrated
* @wr_offset: White Rabbit timescale offset in ps
*
* All of these are little endian in the EEPROM
*/
struct ft_calibration {
int32_t zero_offset[FT_NUM_CHANNELS];
uint32_t vcxo_default_tune;
/* Temperature at which the device has been calibrated */
uint32_t calibration_temp;
/* White Rabbit timescale offset in ps */
int32_t wr_offset;
};
......@@ -212,6 +238,7 @@ int ft_wr_mode(struct fmctdc_dev *ft, int on);
int ft_wr_query(struct fmctdc_dev *ft);
int ft_handle_eeprom_calibration(struct fmctdc_dev *ft);
extern struct bin_attribute dev_attr_calibration;
int ft_irq_init(struct fmctdc_dev *ft);
void ft_irq_exit(struct fmctdc_dev *ft);
......
......@@ -250,15 +250,32 @@ static int ft_zio_conf_set(struct device *dev, struct zio_attribute *zattr,
static int ft_zio_probe(struct zio_device *zdev)
{
struct fmctdc_dev *ft;
int err;
/* link the new device from the fd structure */
ft = zdev->priv_d;
ft->zdev = zdev;
err = device_create_bin_file(&zdev->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;
}
/*
* zfad_zio_remove
* @zdev: the real zio device
*/
static int ft_zio_remove(struct zio_device *zdev)
{
device_remove_bin_file(&zdev->head.dev, &dev_attr_calibration);
return 0;
}
/* Our sysfs operations to access internal settings */
static const struct zio_sysfs_operations ft_zio_sysfs_ops = {
.conf_set = ft_zio_conf_set,
......@@ -366,6 +383,7 @@ static struct zio_driver ft_zdrv = {
},
.id_table = ft_table,
.probe = ft_zio_probe,
.remove = ft_zio_remove,
/* Take the version from ZIO git sub-module */
.min_version = ZIO_VERSION(__ZIO_MIN_MAJOR_VERSION,
__ZIO_MIN_MINOR_VERSION,
......
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