Commit 2e93c74f authored by Alessandro Rubini's avatar Alessandro Rubini

tdc and output: added user-offset attributes

parent 661065d7
......@@ -601,16 +601,21 @@ describing the cset:
@smallexample
spusa# ls -Ff /sys/bus/zio/devices/zio-fd-0200/fd-input/
./ enable utc-l chan trigger/
../ current_trigger coarse flags chan0/
uevent current_buffer frac offset
name utc-h seq power/
./ enable utc-l chan power/
../ current_trigger coarse flags trigger/
uevent current_buffer frac offset chan0/
name utc-h seq user-offset
@end smallexample
The timestamp-related values in this file reflect the last stamp that
has been enqueued to user space (this may be the next event to be
read by the actual reading process). The @i{offset} attribute
is the stamping offset, in picoseconds, for the TDC channe.
is the stamping offset, in picoseconds, for the TDC channel.
The @i{user-offset} attribute, which currently defaults to 0, is a
signed value that users can write to represent a number of picoseconds
to be added (or subtracted) to the hardware-reported stamps. This is
used to account for delays induced by cabling (range: -0.2s to 0.2s).
The @i{flags} attribute can be used to change three configuration
bits, defined by the respective macros. Please note that the default
......@@ -756,6 +761,9 @@ The following attributes are defined:
FD_ATTR_OUT_DELTA_L,
FD_ATTR_OUT_DELTA_COARSE,
FD_ATTR_OUT_DELTA_FINE,
/* The two offsets */
FD_ATTR_OUT_DELAY_OFF,
FD_ATTR_OUT_USER_OFF,
FD_ATTR_OUT__LAST,
};
enum fd_output_mode {
......@@ -768,10 +776,43 @@ The following attributes are defined:
So, to disable the output, just write 0 to the mode attribute.
To configure pulse or delay all attributes must be written.
@b{Note:} writing to @i{sysfs} is not working with this version of ZIO.
While recent developments introduced more complete consistency between
the various places where attributes live, with this version you can
only write attributes to the control block.
@b{Note:} writing the output configuration (mode, rep, start, end,
delta) to @i{sysfs} is not working with this version of ZIO. And I've
been too lazy to add code to do that. While recent developments
introduced more complete consistency between the various places where
attributes live, with this version you can only write attributes to
the control block.
The @i{delay-offset} attribute represents an offset that is subtracted
from the user-requested delay (@i{start} fields) when generating output
pulses. It represents internal card delays. The value can be modified
from @i{sysfs}.
@b{Note:} the @i{delay-offset} is not used for pulse-generation mode.
The @i{user-offset} attribute, which currently defaults to 0, is a
signed value that users can write to represent a number of picoseconds
to be added (or subtracted) to the every user-command (for both delay
and pulse generation). This is used to account for delays induced by
cabling (range: -0.2s to 0.2s). The value can be modified
from @i{sysfs}.
This is the unsorted content of the @i{sysfs} directory for each
of the output csets:
@smallexample
spusa# COLUMNS=60 ls -fF /sys/bus/zio/devices/zio-fd-0200/fd-ch1
./ mode end-l user-offset
../ rep end-coarse power/
uevent start-h end-fine trigger/
name start-l delta-l chan0/
enable start-coarse delta-coarse
current_trigger start-fine delta-fine
current_buffer end-h delay-offset
@end smallexample
As said, only @i{delay-offset} and @i{user-offset} are designed to
be written by the user.
@menu
* Using fd-raw-output::
......@@ -1133,15 +1174,13 @@ allows:
@itemize @bullet
@item There is no support for White Rabbit at this time.
@item Generation of delayed pulses is missing (it is not working)
@item The API for pulse generation is not yet available.
@item We need interrupt support. The input is performed with a kernel timer.
@item There is no EEPROM support. The driver uses default calibration
@item There is no EEPROM support. The driver uses default calibration.
settings.
@item We need a module parameter to avoid probing non-fine-delay SPEC
......
......@@ -38,7 +38,7 @@ DEFINE_ZATTR_STD(ZDEV, fd_zattr_dev_std) = {
/* Extended attributes for the device */
static struct zio_attribute fd_zattr_dev[] = {
ZATTR_EXT_REG("version", S_IRUGO, FD_ATTR_DEV_VERSION,
ZATTR_EXT_REG("version", S_IRUGO, FD_ATTR_DEV_VERSION,
FDELAY_VERSION),
ZATTR_EXT_REG("utc-h", _RW_, FD_ATTR_DEV_UTC_H, 0),
ZATTR_EXT_REG("utc-l", _RW_, FD_ATTR_DEV_UTC_L, 0),
......@@ -56,6 +56,7 @@ static struct zio_attribute fd_zattr_input[] = {
ZATTR_EXT_REG("chan", S_IRUGO, FD_ATTR_TDC_CHAN, 0),
ZATTR_EXT_REG("flags", _RW_, FD_ATTR_TDC_FLAGS, 0),
ZATTR_EXT_REG("offset", _RW_, FD_ATTR_TDC_OFFSET, 0),
ZATTR_EXT_REG("user-offset", _RW_, FD_ATTR_TDC_USER_OFF, 0),
};
/* Extended attributes for the output csets */
......@@ -73,6 +74,8 @@ static struct zio_attribute fd_zattr_output[] = {
ZATTR_EXT_REG("delta-l", _RW_, FD_ATTR_OUT_DELTA_L, 0),
ZATTR_EXT_REG("delta-coarse", _RW_, FD_ATTR_OUT_DELTA_COARSE, 0),
ZATTR_EXT_REG("delta-fine", _RW_, FD_ATTR_OUT_DELTA_FINE, 0),
ZATTR_EXT_REG("delay-offset", _RW_, FD_ATTR_OUT_DELAY_OFF, 0),
ZATTR_EXT_REG("user-offset", _RW_, FD_ATTR_OUT_USER_OFF, 0),
};
......@@ -96,7 +99,7 @@ static enum fd_devtype __fd_get_type(struct device *dev)
return FD_TYPE_OUTPUT;
}
/* TDC input attributes: only the offset is special */
/* TDC input attributes: only the user offset is special */
static int fd_zio_info_tdc(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
......@@ -106,6 +109,11 @@ static int fd_zio_info_tdc(struct device *dev, struct zio_attribute *zattr,
cset = to_zio_cset(dev);
fd = cset->zdev->private_data;
if (zattr->priv.addr == FD_ATTR_TDC_USER_OFF) {
*usr_val = fd->calib.tdc_user_offset;
return 0;
}
/*
* For efficiency reasons at read_fifo() time, we store an
* array of integers instead of filling attributes, so here
......@@ -116,6 +124,29 @@ static int fd_zio_info_tdc(struct device *dev, struct zio_attribute *zattr,
return 0;
}
/* output channel: only the two offsets */
static int fd_zio_info_output(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
struct zio_cset *cset;
struct spec_fd *fd;
int ch;
cset = to_zio_cset(dev);
ch = cset->index - 1;
fd = cset->zdev->private_data;
if (zattr->priv.addr == FD_ATTR_OUT_DELAY_OFF) {
*usr_val = fd->calib.zero_offset[ch];
return 0;
}
if (zattr->priv.addr == FD_ATTR_OUT_USER_OFF) {
*usr_val = fd->calib.ch_user_offset[ch];
return 0;
}
return 0;
}
static int fd_wr_mode(struct spec_fd *fd, int on)
{
if (on) {
......@@ -151,8 +182,10 @@ static int fd_zio_info_get(struct device *dev, struct zio_attribute *zattr,
if (__fd_get_type(dev) == FD_TYPE_INPUT)
return fd_zio_info_tdc(dev, zattr, usr_val);
if (__fd_get_type(dev) != FD_TYPE_WHOLEDEV)
return 0;
if (__fd_get_type(dev) == FD_TYPE_OUTPUT)
return fd_zio_info_output(dev, zattr, usr_val);
/* following is whole-dev */
if (zattr->priv.addr != FD_ATTR_DEV_UTC_H)
return 0;
/* reading utc-h calls an atomic get-time */
......@@ -182,8 +215,10 @@ static int fd_zio_conf_tdc(struct device *dev, struct zio_attribute *zattr,
switch (zattr->priv.addr) {
case FD_ATTR_TDC_OFFSET:
fd->calib.tdc_zero_offset = usr_val;
pr_info("%s: set new offset: %i\n", __func__,
fd->calib.tdc_zero_offset);
goto out;
case FD_ATTR_TDC_USER_OFF:
fd->calib.tdc_user_offset = usr_val;
goto out;
case FD_ATTR_TDC_FLAGS:
......@@ -227,6 +262,29 @@ out:
return 0;
}
/* only the two offsets */
static int fd_zio_conf_output(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
{
struct zio_cset *cset;
struct spec_fd *fd;
int ch;
cset = to_zio_cset(dev);
fd = cset->zdev->private_data;
ch = cset->index - 1;
if (zattr->priv.addr == FD_ATTR_OUT_DELAY_OFF) {
fd->calib.zero_offset[ch] = usr_val;
return 0;
}
if (zattr->priv.addr == FD_ATTR_OUT_USER_OFF) {
fd->calib.ch_user_offset[ch] = usr_val;
return 0;
}
return 0;
}
/* conf_set dispatcher and and device-wide attributes */
static int fd_zio_conf_set(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
......@@ -236,9 +294,12 @@ static int fd_zio_conf_set(struct device *dev, struct zio_attribute *zattr,
struct spec_fd *fd;
struct zio_attribute *attr;
if (__fd_get_type(dev) != FD_TYPE_WHOLEDEV)
if (__fd_get_type(dev) == FD_TYPE_INPUT)
return fd_zio_conf_tdc(dev, zattr, usr_val);
if (__fd_get_type(dev) == FD_TYPE_OUTPUT)
return fd_zio_conf_output(dev, zattr, usr_val);
/* Remains: wholedev */
zdev = to_zio_dev(dev);
attr = zdev->zattr_set.ext_zattr;
fd = zdev->private_data;
......@@ -295,6 +356,64 @@ static void fd_ts_sub(struct fd_time *t, uint64_t pico)
}
}
/* We need to change the time in attribute tuples, so here it is */
enum attrs {__UTC_H, __UTC_L, __COARSE, __FRAC}; /* the order of our attrs */
static void fd_attr_sub(uint32_t *a, uint32_t pico)
{
uint32_t coarse, frac;
fd_split_pico(pico, &coarse, &frac);
if (a[__FRAC] >= frac) {
a[__FRAC] -= frac;
} else {
a[__FRAC] += 4096;
a[__FRAC] -= frac;
coarse++;
}
if (a[__COARSE] >= coarse) {
a[__COARSE] -= coarse;
} else {
a[__COARSE] += 125*1000*1000;
a[__COARSE] -= coarse;
if (likely(a[__UTC_L] != 0)) {
a[__UTC_L]--;
} else {
a[__UTC_L] = ~0;
a[__UTC_H]--;
}
}
}
static void fd_attr_add(uint32_t *a, uint32_t pico)
{
uint32_t coarse, frac;
fd_split_pico(pico, &coarse, &frac);
a[__FRAC] += frac;
if (a[__FRAC] >= 4096) {
a[__FRAC] -= 4096;
coarse++;
}
a[__COARSE] += coarse;
if (a[__COARSE] > 125*1000*1000) {
a[__COARSE] -= 125*1000*1000;
a[__UTC_L]++;
if (unlikely(a[__UTC_L] == 0))
a[__UTC_H]++;
}
}
static inline void __fd_apply_offset(uint32_t *a, int32_t off_pico)
{
if (off_pico) {
if (off_pico > 0)
fd_attr_add(a, off_pico);
else
fd_attr_sub(a, -off_pico);
}
}
static int fd_read_fifo(struct spec_fd *fd, struct zio_channel *chan)
{
struct zio_control *ctrl;
......@@ -329,6 +448,8 @@ static int fd_read_fifo(struct spec_fd *fd, struct zio_channel *chan)
v[FD_ATTR_TDC_CHAN] = t.channel;
v[FD_ATTR_TDC_OFFSET] = fd->calib.tdc_zero_offset;
__fd_apply_offset(v + FD_ATTR_TDC_UTC_H, fd->calib.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));
......@@ -394,30 +515,13 @@ static void __fd_zio_output(struct spec_fd *fd, int index1_4, uint32_t *attrs)
}
if (mode == FD_OUT_MODE_DELAY) {
uint32_t coarse, frac;
/* To use fd_ts_sub(), but I'd need to convert twice */
fd_split_pico(fd->calib.zero_offset[ch], &coarse, &frac);
if (attrs[FD_ATTR_OUT_START_FINE] >= frac) {
attrs[FD_ATTR_OUT_START_FINE] -= frac;
} else {
attrs[FD_ATTR_OUT_START_FINE] += 4096;
attrs[FD_ATTR_OUT_START_FINE] -= frac;
coarse++;
}
if (attrs[FD_ATTR_OUT_START_COARSE] >= coarse) {
attrs[FD_ATTR_OUT_START_COARSE] -= coarse;
} else {
attrs[FD_ATTR_OUT_START_COARSE] += 125*1000*1000;
attrs[FD_ATTR_OUT_START_COARSE] -= coarse;
if (likely(attrs[FD_ATTR_OUT_START_L] != 0)) {
attrs[FD_ATTR_OUT_START_L]--;
} else {
attrs[FD_ATTR_OUT_START_L] = ~0;
attrs[FD_ATTR_OUT_START_H]--;
}
}
fd_attr_sub(attrs + FD_ATTR_OUT_START_H,
fd->calib.zero_offset[ch]);
}
__fd_apply_offset(attrs + FD_ATTR_OUT_START_H,
fd->calib.ch_user_offset[ch]);
fd_ch_writel(fd, ch, fd->ch[ch].frr_cur, FD_REG_FRR);
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_START_H], FD_REG_U_STARTH);
......
......@@ -11,6 +11,11 @@
* it when the layout of attributes changes in incompatible ways)
*/
/*
* NOTE: all tuples of 4 register must be enumerated in the proper order:
* utc-h, utc-l, coarse, frac __IN_THIS_ORDER__ because I make arith on them
*/
/* Device-wide ZIO attributes */
enum fd_zattr_dev_idx {
FD_ATTR_DEV_VERSION = 0,
......@@ -35,6 +40,7 @@ enum fd_command {
/* Input ZIO attributes (i.e. TDC attributes) */
enum fd_zattr_in_idx {
/* PLEASE check "NOTE:" above if you edit this*/
FD_ATTR_TDC_UTC_H = FD_ATTR_DEV__LAST,
FD_ATTR_TDC_UTC_L,
FD_ATTR_TDC_COARSE,
......@@ -43,6 +49,7 @@ enum fd_zattr_in_idx {
FD_ATTR_TDC_CHAN,
FD_ATTR_TDC_FLAGS, /* enable, termination, see below */
FD_ATTR_TDC_OFFSET,
FD_ATTR_TDC_USER_OFF,
FD_ATTR_TDC__LAST,
};
/* Names have been chosen so that 0 is the default at load time */
......@@ -54,6 +61,7 @@ enum fd_zattr_in_idx {
enum fd_zattr_out_idx {
FD_ATTR_OUT_MODE = FD_ATTR_DEV__LAST,
FD_ATTR_OUT_REP,
/* PLEASE check "NOTE:" above if you edit this*/
/* Start (or delay) is 4 registers */
FD_ATTR_OUT_START_H,
FD_ATTR_OUT_START_L,
......@@ -68,6 +76,9 @@ enum fd_zattr_out_idx {
FD_ATTR_OUT_DELTA_L,
FD_ATTR_OUT_DELTA_COARSE,
FD_ATTR_OUT_DELTA_FINE,
/* The two offsets */
FD_ATTR_OUT_DELAY_OFF,
FD_ATTR_OUT_USER_OFF,
FD_ATTR_OUT__LAST,
};
enum fd_output_mode {
......@@ -97,6 +108,9 @@ struct fd_calib {
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];
};
/* Channels are called 1..4 in all docs. Internally it's 0..3 */
......
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