Commit 6aa4d0fd authored by Alessandro Rubini's avatar Alessandro Rubini

kernel: massive change towards new calibration code

This is a massive change, spread across several files.
     - the calibration structure is changed
     - it doesn't live at offset 0x1800 any more
     - it must beloaded by SDB structures (not there yet)
     - all calibration users are fixed
     - calibration.c is a new file dealing with it
     - i2c.c has some material less, because of calibration.c
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent 5c3ad5c5
......@@ -23,6 +23,7 @@ obj-m := fmc-fine-delay.o
fmc-fine-delay-objs = fd-zio.o fd-irq.o fd-core.o
fmc-fine-delay-objs += onewire.o spi.o i2c.o gpio.o
fmc-fine-delay-objs += acam.o calibrate.o pll.o time.o
fmc-fine-delay-objs += calibration.o
fmc-fine-delay-objs += ../sdb-lib/access.o
fmc-fine-delay-objs += ../sdb-lib/glue.o
......
......@@ -311,8 +311,8 @@ static int __acam_config(struct fd_dev *fd, struct acam_mode_setup *s)
regval = p->val;
if (p->addr == 7)
regval |= reg7val;
if (p->addr == 5 && s->mode == ACAM_RMODE)
regval |= AR5_StartOff1(fd->calib.acam_start_offset);
if (p->addr == 5 && s->mode == ACAM_RMODE) /* FIXME: gmode? */
regval |= AR5_StartOff1(ACAM_GMODE_START_OFFSET);
if (p->addr == 5 && s->mode == ACAM_GMODE)
regval |= AR5_StartOff1(ACAM_GMODE_START_OFFSET);
if (p->addr == 6 && s->mode == ACAM_GMODE)
......
/*
* Code related to on-eeprom calibration: retrieving, defaulting, updating.
*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/moduleparam.h>
#include <linux/time.h>
#include <linux/firmware.h>
#include <linux/jhash.h>
#include "fine-delay.h"
/* At factory config time, it's possible to load a file and/or write eeprom */
static char *calibration_load;
static int calibration_save;
static int calibration_check;
static int calibration_default;
module_param(calibration_load, charp, 0444);
module_param(calibration_default, int, 0444);
module_param(calibration_save, int, 0444);
module_param(calibration_check, int, 0444);
/* Stupid dumping tool */
static void dumpstruct(char *name, void *ptr, int size)
{
int i;
unsigned char *p = ptr;
printk("%s: (size 0x%x)\n", name, size);
for (i = 0; i < size; ) {
printk("%02x", p[i]);
i++;
printk(i & 3 ? " " : i & 0xf ? " " : "\n");
}
if (i & 0xf)
printk("\n");
}
/* The user requested to load the configuration from file */
static void fd_i2c_load_calib(struct fd_dev *fd,
struct fd_calibration *calib, char *name)
{
const struct firmware *fw;
int err;
err = request_firmware(&fw, name, fd->fmc->hwdev);
if (err < 0) {
dev_warn(fd->fmc->hwdev, "can't load \"%s\"\n", name);
return;
}
if (fw->size != sizeof(*calib)) {
dev_warn(fd->fmc->hwdev, "File \"%s\" has size != %i\n",
name, sizeof(*calib));
} else {
memcpy(&calib, fw->data, fw->size);
dev_info(fd->fmc->hwdev,
"calibration data loaded from \"%s\"\n", name);
}
release_firmware(fw);
return;
}
static struct fd_calibration fd_calib_default = {
.magic = 0xf19ede1a,
.version = 3,
.date = 0x20130427,
/* .. FIXME .. */
};
int fd_handle_eeprom_calibration(struct fd_dev *fd)
{
struct fd_calibration *calib;
struct device *d = fd->fmc->hwdev;
//u32 hash;
/* Retrieve and validate the calibration */
calib = &fd->calib;
/* FIXME: run SDB on the eeprom */
dev_warn(d, "Calibration NOT yet read from eeprom\n");
if (calibration_check)
dumpstruct("Calibration data from eeprom:", calib,
sizeof(*calib));
/* FIXME: verify hash */
/* FIXME: validate with gateware version */
if (calibration_load)
fd_i2c_load_calib(fd, calib, calibration_load);
/* FIXME: convert endianneess */
if (calibration_default) {
dev_info(d, "Overriding with default calibration\n");
*calib = fd_calib_default;
}
if (calibration_check) {
/* FIXME: dump human-readable values */
}
/* FIXME: recreate hash */
if (calibration_save) {
/* FIXME: save to eeprom */
dev_warn(d, "Saving is not supported\n");
}
return 0;
}
......@@ -41,22 +41,6 @@ module_param_named(show_sdb, fd_show_sdb, int, 0444);
/* FIXME: add parameters "file=" and "wrc=" like wr-nic-core does */
/* This is pre-set at load time (data by Tomasz) */
static struct fd_calib fd_default_calib = {
.frr_poly = {
[0] = -165202LL,
[1] = -29825595LL,
[2] = 3801939743082LL,
},
.tdc_zero_offset = 35600 -63100,
.atmcr_val = 4 | (1500 << 4),
.adsfr_val = 56648,
.acam_start_offset = 10000,
.zero_offset = {
14400, 14400, 14400, 14400
},
};
/* The reset function (by Tomasz) */
static void fd_do_reset(struct fd_dev *fd, int hw_reset)
{
......@@ -195,7 +179,6 @@ int fd_probe(struct fmc_device *fmc)
fmc->mezzanine_data = fd;
fd->fmc = fmc;
fd->verbose = fd_verbose;
fd->calib = fd_default_calib;
/* Check the binary is there */
if (fd_readl(fd, FD_REG_IDR) != FD_MAGIC_FPGA) {
......@@ -205,6 +188,11 @@ int fd_probe(struct fmc_device *fmc)
dev_info(dev, "%s: initializing\n", KBUILD_MODNAME);
}
/* Retrieve calibration from the eeprom, and validate */
ret = fd_handle_eeprom_calibration(fd);
if (ret < 0)
return ret;
/* First, hardware reset */
fd_do_reset(fd, 1);
......
......@@ -109,11 +109,11 @@ int fd_read_sw_fifo(struct fd_dev *fd, struct zio_channel *chan)
v[FD_ATTR_TDC_FRAC] = t.frac;
v[FD_ATTR_TDC_SEQ] = t.seq_id;
v[FD_ATTR_TDC_CHAN] = t.channel;
v[FD_ATTR_TDC_FLAGS] = fd->calib.tdc_flags;
v[FD_ATTR_TDC_FLAGS] = fd->tdc_flags;
v[FD_ATTR_TDC_OFFSET] = fd->calib.tdc_zero_offset;
v[FD_ATTR_TDC_USER_OFF] = fd->calib.tdc_user_offset;
v[FD_ATTR_TDC_USER_OFF] = fd->tdc_user_offset;
fd_apply_offset(v + FD_ATTR_TDC_UTC_H, fd->calib.tdc_user_offset);
fd_apply_offset(v + FD_ATTR_TDC_UTC_H, fd->tdc_user_offset);
/* We also need a copy within the device, so sysfs can read it */
memcpy(fd->tdc_attrs, v + FD_ATTR_DEV__LAST, sizeof(fd->tdc_attrs));
......
......@@ -112,11 +112,11 @@ static int fd_zio_info_tdc(struct device *dev, struct zio_attribute *zattr,
fd = cset->zdev->priv_d;
if (zattr->id == FD_ATTR_TDC_USER_OFF) {
*usr_val = fd->calib.tdc_user_offset;
*usr_val = fd->tdc_user_offset;
return 0;
}
if (zattr->id == FD_ATTR_TDC_FLAGS) {
*usr_val = fd->calib.tdc_flags;
*usr_val = fd->tdc_flags;
return 0;
}
/*
......@@ -147,7 +147,7 @@ static int fd_zio_info_output(struct device *dev, struct zio_attribute *zattr,
return 0;
}
if (zattr->id == FD_ATTR_OUT_USER_OFF) {
*usr_val = fd->calib.ch_user_offset[ch];
*usr_val = fd->ch_user_offset[ch];
return 0;
}
/* Reading the mode tells wether it triggered or not */
......@@ -240,7 +240,7 @@ static int fd_zio_conf_tdc(struct device *dev, struct zio_attribute *zattr,
goto out;
case FD_ATTR_TDC_USER_OFF:
fd->calib.tdc_user_offset = usr_val;
fd->tdc_user_offset = usr_val;
goto out;
case FD_ATTR_TDC_FLAGS:
......@@ -250,7 +250,7 @@ static int fd_zio_conf_tdc(struct device *dev, struct zio_attribute *zattr,
}
/* This code is only about FD_ATTR_TDC_FLAGS */
change = fd->calib.tdc_flags ^ usr_val; /* old xor new */
change = fd->tdc_flags ^ usr_val; /* old xor new */
/* No need to lock, as configuration is serialized by zio-core */
if (change & FD_TDCF_DISABLE_INPUT) {
......@@ -279,7 +279,7 @@ static int fd_zio_conf_tdc(struct device *dev, struct zio_attribute *zattr,
}
out:
/* We need to store in the local array too (see info_tdc() above) */
fd->calib.tdc_flags = usr_val;
fd->tdc_flags = usr_val;
return 0;
}
......@@ -301,7 +301,7 @@ static int fd_zio_conf_output(struct device *dev, struct zio_attribute *zattr,
return 0;
}
if (zattr->id == FD_ATTR_OUT_USER_OFF) {
fd->calib.ch_user_offset[ch] = usr_val;
fd->ch_user_offset[ch] = usr_val;
return 0;
}
return 0;
......@@ -442,7 +442,7 @@ static void __fd_zio_output(struct fd_dev *fd, int index1_4, uint32_t *attrs)
}
fd_apply_offset(attrs + FD_ATTR_OUT_START_H,
fd->calib.ch_user_offset[ch]);
fd->ch_user_offset[ch]);
fd_ch_writel(fd, ch, fd->ch[ch].frr_cur, FD_REG_FRR);
......
......@@ -125,25 +125,24 @@ static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)
# endif
#endif
struct fd_calib {
int64_t frr_poly[3]; /* SY89295 delay/temp poly coeffs */
struct fd_calibration { /* All of these are big endian */
uint32_t magic; /* magic ID: 0xf19ede1a */
uint32_t zero_offset[4]; /* Output zero offset, fixed point */
uint32_t adsfr_val; /* ADSFR register value */
uint32_t acam_start_offset; /* ACAM Start offset value */
uint32_t atmcr_val; /* ATMCR register value */
uint32_t tdc_zero_offset; /* Zero offset of the TDC, in ps */
/* The user can add a signed offset, in picoseconds */
int32_t tdc_user_offset;
int32_t ch_user_offset[4];
int32_t tdc_flags;
};
uint32_t hash; /* jhash of it all, with this zeroed */
uint16_t size;
uint16_t version;
uint32_t date; /* hex: 0x20130410 = Apr 4th 2013 */
/* SY89295 delay/temperature polynomial coefficients */
int64_t frr_poly[3];
/* Output-to-internal-timebase offset in ps. Add to start/end output */
int32_t zero_offset[4];
struct fd_calib_on_eeprom {
u32 hash;
u16 size;
u16 version;
struct fd_calib calib;
/* TDC-to-internal-timebase offset in ps. Add to stamps and delays */
int32_t tdc_zero_offset;
/* Default DAC value for VCXO. Set during init and for local timing */
uint32_t vcxo_default_tune;
};
/* Channels are called 1..4 in all docs. Internally it's 0..3 */
......@@ -191,7 +190,7 @@ struct fd_dev {
struct timer_list fifo_timer;
struct timer_list temp_timer;
struct tasklet_struct tlet;
struct fd_calib calib;
struct fd_calibration calib; /* a copy of what we have in flash */
struct fd_ch ch[FD_CH_NUMBER];
uint32_t bin;
int acam_addr; /* cache of currently active addr */
......@@ -202,6 +201,11 @@ struct fd_dev {
uint32_t tdc_attrs[FD_ATTR_TDC__LAST - FD_ATTR_DEV__LAST];
uint16_t mcp_iodir, mcp_olat;
struct fd_sw_fifo sw_fifo;
/* The following fields used to live in fd_calib */
int32_t tdc_user_offset;
int32_t ch_user_offset[4];
int32_t tdc_flags;
};
/* We act on flags using atomic ops, so flag is the number, not the mask */
......@@ -372,5 +376,8 @@ extern int fd_eeprom_read(struct fd_dev *fd, int i2c_addr, uint32_t offset,
extern int fd_eeprom_write(struct fd_dev *fd, int i2c_addr, uint32_t offset,
void *buf, size_t size);
/* Function exported by calibration.c */
int fd_handle_eeprom_calibration(struct fd_dev *fd);
#endif /* __KERNEL__ */
#endif /* __FINE_DELAY_H__ */
......@@ -11,47 +11,13 @@
* option, any later version.
*/
#include <linux/moduleparam.h>
#include <linux/io.h>
#include <linux/time.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/firmware.h>
#include <linux/jhash.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
/* The eeprom is geographically addressed, and the structure lives at 6kB */
#define I2C_OFFSET (6*1024)
/* At factory config time, it's possible to load a file and/or write eeprom */
static char *calibration_load;
static int calibration_save;
static int calibration_check;
static int calibration_default;
module_param(calibration_load, charp, 0444);
module_param(calibration_default, int, 0444);
module_param(calibration_save, int, 0444);
module_param(calibration_check, int, 0444);
/* Stupid dumping tool */
static void dumpstruct(char *name, void *ptr, int size)
{
int i;
unsigned char *p = ptr;
printk("%s: (size 0x%x)\n", name, size);
for (i = 0; i < size; ) {
printk("%02x", p[i]);
i++;
printk(i & 3 ? " " : i & 0xf ? " " : "\n");
}
if (i & 0xf)
printk("\n");
}
static void set_sda(struct fd_dev *fd, int val)
{
uint32_t reg;
......@@ -211,96 +177,9 @@ int fd_eeprom_write(struct fd_dev *fd, int i2c_addr, uint32_t offset,
return size;
}
/* The user requested to load the configuration from file */
static void fd_i2c_load_calib(struct fd_dev *fd,
struct fd_calib_on_eeprom *cal_ee)
{
const struct firmware *fw;
char *fwname, *newname = NULL;
int err;
/* the calibration_load string is known to be valid */
fwname = calibration_load;
err = request_firmware(&fw, calibration_load, fd->fmc->hwdev);
if (err < 0) {
dev_warn(fd->fmc->hwdev, "can't load \"%s\"\n",
calibration_load);
return;
}
if (fw->size != sizeof(cal_ee->calib)) {
dev_warn(fd->fmc->hwdev, "File \"%s\" has wrong size\n",
fwname);
} else {
memcpy(&cal_ee->calib, fw->data, fw->size);
dev_info(fd->fmc->hwdev,
"calibration data loaded from \"%s\"\n", fwname);
}
release_firmware(fw);
kfree(newname);
return;
}
int fd_i2c_init(struct fd_dev *fd)
{
struct fd_calib_on_eeprom *cal_ee;
u32 hash;
int i;
mi2c_scan(fd);
/* Retrieve and validate the calibration */
cal_ee = kzalloc(sizeof(*cal_ee), GFP_KERNEL);
if (!cal_ee)
return -ENOMEM;
i = fd_eeprom_read(fd, fd->fmc->eeprom_addr, I2C_OFFSET,
cal_ee, sizeof(*cal_ee));
if (i != sizeof(*cal_ee)) {
pr_err("%s: cannot read_eeprom\n", __func__);
goto load;
}
if (calibration_check)
dumpstruct("Calibration data from eeprom:", cal_ee,
sizeof(*cal_ee));
hash = jhash(&cal_ee->calib, sizeof(cal_ee->calib), 0);
/* FIXME: this is original-endian only (little endian I fear) */
if ((cal_ee->size != sizeof(cal_ee->calib))
|| (cal_ee->hash != hash)
|| (cal_ee->version != 1)) {
pr_err("%s: calibration on eeprom is invalid\n", __func__);
goto load;
}
if (!calibration_default)
fd->calib = cal_ee->calib; /* override compile-time default */
load:
cal_ee->calib = fd->calib;
if (calibration_load)
fd_i2c_load_calib(fd, cal_ee);
/* Fix the local copy, for verification and maybe saving */
cal_ee->hash = jhash(&cal_ee->calib, sizeof(cal_ee->calib), 0);
cal_ee->size = sizeof(cal_ee->calib);
cal_ee->version = 1;
if (calibration_save) {
i = fd_eeprom_write(fd, fd->fmc->eeprom_addr, I2C_OFFSET,
cal_ee, sizeof(*cal_ee));
if (i != sizeof(*cal_ee)) {
pr_err("%s: error in writing calibration to eeprom\n",
__func__);
} else {
pr_info("%s: saved calibration to eeprom\n", __func__);
}
}
if (calibration_check)
dumpstruct("Current calibration data:", cal_ee,
sizeof(*cal_ee));
kfree(cal_ee);
return 0;
}
......
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