Commit f7f60598 authored by Alessandro Rubini's avatar Alessandro Rubini

calibrate: now in a separate file (still not working)

parent 8779d604
...@@ -13,7 +13,7 @@ obj-m := spec-fine-delay.o ...@@ -13,7 +13,7 @@ obj-m := spec-fine-delay.o
spec-fine-delay-objs = fd-zio.o fd-spec.o fd-core.o spec-fine-delay-objs = fd-zio.o fd-spec.o fd-core.o
spec-fine-delay-objs += onewire.o spi.o i2c.o gpio.o spec-fine-delay-objs += onewire.o spi.o i2c.o gpio.o
spec-fine-delay-objs += acam.o pll.o time.o spec-fine-delay-objs += acam.o calibrate.o pll.o time.o
all: modules all: modules
......
...@@ -92,14 +92,14 @@ static void acam_set_address(struct spec_fd *fd, int addr) ...@@ -92,14 +92,14 @@ static void acam_set_address(struct spec_fd *fd, int addr)
} }
/* Warning: acam_readl and acam_writel only work if GCR.BYPASS is set */ /* Warning: acam_readl and acam_writel only work if GCR.BYPASS is set */
static uint32_t acam_readl(struct spec_fd *fd, int reg) uint32_t acam_readl(struct spec_fd *fd, int reg)
{ {
acam_set_address(fd, reg); acam_set_address(fd, reg);
fd_writel(fd, FD_TDCSR_READ, FD_REG_TDCSR); fd_writel(fd, FD_TDCSR_READ, FD_REG_TDCSR);
return fd_readl(fd, FD_REG_TDR) & ACAM_MASK; return fd_readl(fd, FD_REG_TDR) & ACAM_MASK;
} }
static void acam_writel(struct spec_fd *fd, int val, int reg) void acam_writel(struct spec_fd *fd, int val, int reg)
{ {
acam_set_address(fd, reg); acam_set_address(fd, reg);
fd_writel(fd, val, FD_REG_TDR); fd_writel(fd, val, FD_REG_TDR);
...@@ -183,48 +183,6 @@ out: ...@@ -183,48 +183,6 @@ out:
return -EIO; return -EIO;
} }
static int acam_test_delay_transfer_function(struct spec_fd *fd)
{
/* FIXME */
return 0;
}
/* Evaluates 2nd order polynomial. Coefs have 32 fractional bits. */
static int fd_eval_polynomial(struct spec_fd *fd)
{
int64_t x = fd->temp;
int64_t *coef = fd->calib.frr_poly;
return (coef[0] * x * x + coef[1] * x + coef[2]) >> 32;
}
static int fd_find_8ns_tap(struct spec_fd *fd, int ch)
{
/* FIXME */
return 0;
}
static int acam_calibrate_outputs(struct spec_fd *fd)
{
int ret, ch;
int measured, fitted;
if ((ret = acam_test_delay_transfer_function(fd)) < 0)
return ret;
fitted = fd_eval_polynomial(fd);
for (ch = FD_CH_1; ch <= FD_CH_LAST; ch++) {
fd_read_temp(fd, 0);
measured = fd_find_8ns_tap(fd, ch);
fd->ch[ch].frr_cur = measured;
fd->ch[ch].frr_offset = measured - fitted;
pr_info("%s: ch %i: 8ns @ %i (f %i, offset %i, t %i.%02i)\n",
__func__, FD_CH_EXT(ch),
fd->ch[ch].frr_cur, fitted, fd->ch[ch].frr_offset,
fd->temp / 16, (fd->temp & 0xf) * 100 / 16);
}
return 0;
}
/* We need to write come static configuration in the registers */ /* We need to write come static configuration in the registers */
struct acam_init_data { struct acam_init_data {
...@@ -288,16 +246,16 @@ static struct acam_mode_setup fd_acam_table[] = { ...@@ -288,16 +246,16 @@ static struct acam_mode_setup fd_acam_table[] = {
/* To configure the thing, follow the table, but treat 5 and 7 as special */ /* To configure the thing, follow the table, but treat 5 and 7 as special */
static int __acam_config(struct spec_fd *fd, struct acam_mode_setup *s) static int __acam_config(struct spec_fd *fd, struct acam_mode_setup *s)
{ {
int i, bin, hsdiv, refdiv, reg7val; int i, hsdiv, refdiv, reg7val;
struct acam_init_data *p; struct acam_init_data *p;
uint32_t regval; uint32_t regval;
unsigned long j; unsigned long j;
bin = acam_calc_pll(ACAM_FP_TREF, ACAM_FP_BIN, &hsdiv, &refdiv); fd->bin = acam_calc_pll(ACAM_FP_TREF, ACAM_FP_BIN, &hsdiv, &refdiv);
reg7val = AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv); reg7val = AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv);
pr_debug("%s: config for %s-mode (bin 0x%x, hsdiv %i, refdiv %i)\n", pr_debug("%s: config for %s-mode (bin 0x%x, hsdiv %i, refdiv %i)\n",
__func__, s->name, bin, hsdiv, refdiv); __func__, s->name, fd->bin, hsdiv, refdiv);
/* Disable TDC inputs prior to configuring */ /* Disable TDC inputs prior to configuring */
fd_writel(fd, FD_TDCSR_STOP_DIS | FD_TDCSR_START_DIS, FD_REG_TDCSR); fd_writel(fd, FD_TDCSR_STOP_DIS | FD_TDCSR_START_DIS, FD_REG_TDCSR);
...@@ -352,7 +310,7 @@ int fd_acam_init(struct spec_fd *fd) ...@@ -352,7 +310,7 @@ int fd_acam_init(struct spec_fd *fd)
if ( (ret = fd_acam_config(fd, ACAM_IMODE)) ) if ( (ret = fd_acam_config(fd, ACAM_IMODE)) )
return ret; return ret;
if ( (ret = acam_calibrate_outputs(fd)) ) if ( (ret = fd_calibrate_outputs(fd)) )
return ret; return ret;
if ( (ret = fd_acam_config(fd, ACAM_RMODE)) ) if ( (ret = fd_acam_config(fd, ACAM_RMODE)) )
......
/*
* Calibrate the output path.
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@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/kernel.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/delay.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
#include "hw/acam_gpx.h"
#include "hw/fd_channel_regs.h"
/* TEMP! */
static void acam_set_bypass(struct spec_fd *fd, int on)
{
/* FIXME: this zeroes all other GCR bits */
fd_writel(fd, on ? FD_GCR_BYPASS : 0, FD_REG_GCR);
}
static int acam_test_delay_transfer_function(struct spec_fd *fd)
{
/* FIXME */
return 0;
}
/* Evaluates 2nd order polynomial. Coefs have 32 fractional bits. */
static int fd_eval_polynomial(struct spec_fd *fd)
{
int64_t x = fd->temp;
int64_t *coef = fd->calib.frr_poly;
return (coef[0] * x * x + coef[1] * x + coef[2]) >> 32;
}
/*
* Measures the the FPGA-generated TDC start and the output of one of
* the fine delay chips (channel) at a pre-defined number of taps
* (fine). Retuns the delay in picoseconds. The measurement is
* repeated and averaged (n_avgs) times. Also, the standard deviation
* of the result can be written to (sdev) if it's not NULL.
*/
struct delay_stats {
uint32_t avg;
uint32_t min;
uint32_t max;
};
/* Note: channel is the "internal" one: 0..3 */
static uint32_t output_delay_ps(struct spec_fd *fd, int ch, int fine, int n,
struct delay_stats *stats)
{
int i;
uint32_t res, *results;
uint64_t acc = 0;
results = kmalloc(n * sizeof(*results), GFP_KERNEL);
if (!results)
return -ENOMEM;
/* Disable the output for the channel being calibrated */
fd_gpio_clr(fd, FD_GPIO_OUTPUT_EN(FD_CH_EXT(ch)));
/* Enable the stop input in ACAM for the channel being calibrated */
acam_writel(fd, AR0_TRiseEn(0) | AR0_TRiseEn(FD_CH_EXT(ch))
| AR0_HQSel | AR0_ROsc, 0);
/* Program the output delay line setpoint */
fd_ch_writel(fd, ch, fine, FD_REG_FRR);
fd_ch_writel(fd, ch, FD_DCR_ENABLE | FD_DCR_MODE | FD_DCR_UPDATE,
FD_REG_DCR);
fd_ch_writel(fd, ch, FD_DCR_FORCE_DLY | FD_DCR_ENABLE, FD_REG_DCR);
/*
* Set the calibration pulse mask to genrate calibration
* pulses only on one channel at a time. This minimizes the
* crosstalk in the output buffer which can severely decrease
* the accuracy of calibration measurements
*/
fd_writel(fd, FD_CALR_PSEL_W(1 << ch), FD_REG_CALR);
udelay(100);
/* Do n_avgs single measurements and average */
for (i = 0; i < n; i++) {
uint32_t fr;
/* Re-arm the ACAM (it's working in a single-shot mode) */
fd_writel(fd, FD_TDCSR_ALUTRIG, FD_REG_TDCSR);
udelay(100);
/* Produce a calib pulse on the TDC start and the output ch */
fd_writel(fd, FD_CALR_CAL_PULSE |
FD_CALR_PSEL_W(1 << ch), FD_REG_CALR);
udelay(10000);
/* read the tag, convert to picoseconds (fixed point: 16.16) */
fr = acam_readl(fd, 8 /* fifo */);
res = fr & 0x1ffff * fd->bin * 3; /* bin is 16.16 already */
printk("%i: %08x, 0x%08x\n", fine, fr, res);
results[i] = res;
acc += res;
}
fd_ch_writel(fd, ch, 0, FD_REG_DCR);
/* Calculate avg, min max */
acc = (acc + n / 2) / n;
if (stats) {
stats->avg = acc;
stats->min = ~0;
stats->max = 0;
for (i = 0; i < n; i++) {
if (results[i] > stats->max) stats->max = results[i];
if (results[i] < stats->min) stats->min = results[i];
}
}
kfree(results);
return acc;
}
static int fd_find_8ns_tap(struct spec_fd *fd, int ch)
{
int l = 0, mid, r = FD_NUM_TAPS - 1;
uint32_t bias, dly;
/*
* Measure the delay at zero setting, so it can be further
* subtracted to get only the delay part introduced by the
* delay line (ingoring the TDC, FPGA and routing delays).
*/
bias = output_delay_ps(fd, ch, 0, FD_CAL_STEPS, NULL);
while( r - l > 1) {
mid = ( l + r) / 2;
dly = output_delay_ps(fd, ch, mid, FD_CAL_STEPS, NULL)
- bias;
printk("%i %i %i %08xx (bias %08x)\n", l, r, mid, dly, bias);
if(dly < 8000 << 16)
l = mid;
else
r = mid;
}
return l;
}
int fd_calibrate_outputs(struct spec_fd *fd)
{
int ret, ch;
int measured, fitted;
acam_set_bypass(fd, 1); /* not useful */
fd_writel(fd, FD_TDCSR_START_EN | FD_TDCSR_STOP_EN, FD_REG_TDCSR);
if ((ret = acam_test_delay_transfer_function(fd)) < 0)
return ret;
fitted = fd_eval_polynomial(fd);
for (ch = FD_CH_1; ch <= FD_CH_LAST; ch++) {
fd_read_temp(fd, 0);
measured = fd_find_8ns_tap(fd, ch);
fd->ch[ch].frr_cur = measured;
fd->ch[ch].frr_offset = measured - fitted;
pr_info("%s: ch %i: 8ns @ %i (f %i, offset %i, t %i.%02i)\n",
__func__, FD_CH_EXT(ch),
fd->ch[ch].frr_cur, fitted, fd->ch[ch].frr_offset,
fd->temp / 16, (fd->temp & 0xf) * 100 / 16);
}
return 0;
}
...@@ -20,6 +20,9 @@ struct fd_calib { ...@@ -20,6 +20,9 @@ struct fd_calib {
#define FD_CH_INT(i) ((i) - 1) #define FD_CH_INT(i) ((i) - 1)
#define FD_CH_EXT(i) ((i) + 1) #define FD_CH_EXT(i) ((i) + 1)
#define FD_NUM_TAPS 1024
#define FD_CAL_STEPS 16 //1024
struct fd_ch { struct fd_ch {
/* Offset between FRR measured at known T at startup and poly-fitted */ /* Offset between FRR measured at known T at startup and poly-fitted */
uint32_t frr_offset; uint32_t frr_offset;
...@@ -36,6 +39,7 @@ struct spec_fd { ...@@ -36,6 +39,7 @@ struct spec_fd {
unsigned char __iomem *base; /* regs files are byte-oriented */ unsigned char __iomem *base; /* regs files are byte-oriented */
unsigned char __iomem *regs; unsigned char __iomem *regs;
unsigned char __iomem *ow_regs; unsigned char __iomem *ow_regs;
uint32_t bin;
int acam_addr; /* cache of currently active addr */ int acam_addr; /* cache of currently active addr */
uint8_t ds18_id[8]; uint8_t ds18_id[8];
unsigned long next_t; unsigned long next_t;
...@@ -61,6 +65,26 @@ static inline void fd_writel(struct spec_fd *fd, uint32_t v, unsigned long reg) ...@@ -61,6 +65,26 @@ static inline void fd_writel(struct spec_fd *fd, uint32_t v, unsigned long reg)
writel(v, fd->regs + reg); writel(v, fd->regs + reg);
} }
static inline void __check_chan(int x)
{
BUG_ON(x < 0 || x > 3);
}
static inline uint32_t fd_ch_readl(struct spec_fd *fd, int ch,
unsigned long reg)
{
__check_chan(ch);
return fd_readl(fd, 0x100 + ch * 0x100 + reg);
}
static inline void fd_ch_writel(struct spec_fd *fd, int ch,
uint32_t v, unsigned long reg)
{
__check_chan(ch);
fd_writel(fd, v, 0x100 + ch * 0x100 + reg);
}
#define FD_REGS_OFFSET 0x84000 #define FD_REGS_OFFSET 0x84000
#define FD_MAGIC_FPGA 0xf19ede1a /* FD_REG_IDR content */ #define FD_MAGIC_FPGA 0xf19ede1a /* FD_REG_IDR content */
...@@ -134,6 +158,11 @@ extern int fd_read_temp(struct spec_fd *fd, int verbose); ...@@ -134,6 +158,11 @@ extern int fd_read_temp(struct spec_fd *fd, int verbose);
/* Functions exported by acam.c */ /* Functions exported by acam.c */
extern int fd_acam_init(struct spec_fd *fd); extern int fd_acam_init(struct spec_fd *fd);
extern void fd_acam_exit(struct spec_fd *fd); extern void fd_acam_exit(struct spec_fd *fd);
extern uint32_t acam_readl(struct spec_fd *fd, int reg);
extern void acam_writel(struct spec_fd *fd, int val, int reg);
/* Functions exported by calibrate.c, called within acam.c */
extern int fd_calibrate_outputs(struct spec_fd *fd);
/* Functions exported by gpio.c */ /* Functions exported by gpio.c */
extern int fd_gpio_init(struct spec_fd *fd); extern int fd_gpio_init(struct spec_fd *fd);
...@@ -152,8 +181,6 @@ extern int fd_time_set(struct spec_fd *fd, struct fd_time *t, ...@@ -152,8 +181,6 @@ extern int fd_time_set(struct spec_fd *fd, struct fd_time *t,
extern int fd_time_get(struct spec_fd *fd, struct fd_time *t, extern int fd_time_get(struct spec_fd *fd, struct fd_time *t,
struct timespec *ts); struct timespec *ts);
/* Functions exported by fd-zio.c */ /* Functions exported by fd-zio.c */
extern int fd_zio_register(void); extern int fd_zio_register(void);
extern void fd_zio_unregister(void); extern void fd_zio_unregister(void);
......
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