Commit 7bd454c9 authored by Alessandro Rubini's avatar Alessandro Rubini

fd-zio: initial input support (incomplete, bugged)

parent 5a3a351b
......@@ -13,33 +13,133 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/io.h>
#include <linux/zio.h>
#include <linux/zio-buffer.h>
#include <linux/zio-trigger.h>
#include "spec.h"
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
/*
* We have a number of attributes here. For input channels they are:
*
* UTC-h (expected to be 0 untile 2038 a.d.),
* UTC-l
* coarse time
* fractional time
* sequential ID
* channel
*
* See the enum in "fine-delay.h"
*/
static struct zio_attribute fd_zattr_input[] = {
ZATTR_EXT_REG("utc-h", S_IRUGO, FD_ATTR_IN_UTC_H, 0),
ZATTR_EXT_REG("utc-l", S_IRUGO, FD_ATTR_IN_UTC_L, 0),
ZATTR_EXT_REG("coarse", S_IRUGO, FD_ATTR_IN_COARSE, 0),
ZATTR_EXT_REG("frac", S_IRUGO, FD_ATTR_IN_FRAC, 0),
ZATTR_EXT_REG("seq", S_IRUGO, FD_ATTR_IN_SEQ, 0),
ZATTR_EXT_REG("chan", S_IRUGO, FD_ATTR_IN_CHAN, 0),
};
/* FIXME: all the attributes */
/* The sample size. Mandatory, device-wide */
DEFINE_ZATTR_STD(ZDEV, fd_zattr_dev) = {
ZATTR_REG(zdev, ZATTR_NBITS, S_IRUGO, 0, 1), /* 1 bit. bah... */
ZATTR_REG(zdev, ZATTR_NBITS, S_IRUGO, 0, 32), /* 32 bits. Really? */
};
static int fd_read_fifo(struct spec_fd *fd, struct zio_channel *chan)
{
struct zio_control *ctrl;
uint32_t *v, reg;
if ((fd_readl(fd, FD_REG_TSBCR) & FD_TSBCR_EMPTY))
return -EAGAIN;
if (!chan->active_block)
return 0;
ctrl = zio_get_ctrl(chan->active_block);
/* The input data is written to attributes */
v = ctrl->attr_channel.ext_val;
v[FD_ATTR_IN_UTC_H] = fd_readl(fd, FD_REG_TSBR_SECH) & 0xff;
v[FD_ATTR_IN_UTC_L] = fd_readl(fd, FD_REG_TSBR_SECL);
v[FD_ATTR_IN_COARSE] = fd_readl(fd, FD_REG_TSBR_CYCLES) & 0xfffffff;
reg = fd_readl(fd, FD_REG_TSBR_FID);
v[FD_ATTR_IN_FRAC] = FD_TSBR_FID_FINE_R(reg);
v[FD_ATTR_IN_SEQ] = FD_TSBR_FID_SEQID_R(reg);
v[FD_ATTR_IN_CHAN] = FD_TSBR_FID_CHANNEL_R(reg);
return 0;
}
/*
* We have a timer, used to poll for input samples, until the interrupt
* is there. A timer duration of 0 selects the interrupt.
*/
static int fd_timer_period_ms = 100;
module_param_named(timer_ms, fd_timer_period_ms, int, 0444);
static int fd_timer_period_jiffies; /* converted from ms at init time */
static struct zio_device *__HACK__ZDEV__; /* don't do it! */
static struct spec_fd *__HACK__FD__; /* don't do it! */
static void fd_timer_fn(unsigned long arg)
{
struct spec_fd *fd = (void *)arg;
struct zio_channel *chan = NULL;
struct zio_device *zdev = __HACK__ZDEV__;
if (zdev) {
chan = zdev->cset[0].chan;
} else {
/* nobody read the device so far: we lack the information */
goto out;
}
/* FIXME: manage an array of input samples */
if (fd_read_fifo(fd, chan) == 0) {
if (chan->active_block)
chan->cset->trig->t_op->data_done(chan->cset);
else
pr_err("data and no block\n");
}
out:
mod_timer(&fd->timer, jiffies + fd_timer_period_jiffies);
}
static int fd_output(struct zio_cset *cset)
{
/* FIXME: use chan->active_block */
/* FIXME: the output channels */
return 0; /* Already done, as the trigger is hardware */
}
/*
* The input method will return immediately, because input is
* asynchronous. The data_done callback is invoked when the block is
* full.
*/
static int fd_input(struct zio_cset *cset)
{
/* FIXME: fill chan->active_block */
struct spec_fd *fd;
return 0; /* Already done, as the trigger is hardware */
__HACK__ZDEV__ = cset->zdev;
fd = __HACK__FD__;
/* Configure the device for input */
if (!fd->flags & FD_FLAG_INPUT) {
fd_writel(fd, FD_TSBCR_PURGE | FD_TSBCR_RST_SEQ, FD_REG_TSBCR);
fd_writel(fd, FD_TSBCR_CHAN_MASK_W(1) | FD_TSBCR_ENABLE,
FD_REG_TSBCR);
fd->flags |= FD_FLAG_INPUT;
}
return -EAGAIN; /* Will be completed over time */
}
/* We have 5 csets, since each output triggers separately */
......@@ -48,41 +148,46 @@ static struct zio_cset fd_cset[] = {
SET_OBJECT_NAME("fd-input"),
.raw_io = fd_input,
.n_chan = 1,
.ssize = 1, /* FIXME: 0? */
.ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME,
.zattr_set = {
.ext_zattr = fd_zattr_input,
.n_ext_attr = ARRAY_SIZE(fd_zattr_input),
},
},
{
SET_OBJECT_NAME("fd-ch1"),
.raw_io = fd_output,
.n_chan = 1,
.ssize = 1, /* FIXME: 0? */
.ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_OUTPUT | ZCSET_TYPE_TIME,
},
{
SET_OBJECT_NAME("fd-ch2"),
.raw_io = fd_output,
.n_chan = 1,
.ssize = 1, /* FIXME: 0? */
.ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_OUTPUT | ZCSET_TYPE_TIME,
},
{
SET_OBJECT_NAME("fd-ch3"),
.raw_io = fd_output,
.n_chan = 1,
.ssize = 1, /* FIXME: 0? */
.ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_OUTPUT | ZCSET_TYPE_TIME,
},
{
SET_OBJECT_NAME("fd-ch4"),
.raw_io = fd_output,
.n_chan = 1,
.ssize = 1, /* FIXME: 0? */
.ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_OUTPUT | ZCSET_TYPE_TIME,
},
};
static struct zio_device fd_tmpl = {
.owner = THIS_MODULE,
.preferred_trigger = "user",
.cset = fd_cset,
.n_cset = ARRAY_SIZE(fd_cset),
.zattr_set = {
......@@ -90,7 +195,6 @@ static struct zio_device fd_tmpl = {
},
};
static const struct zio_device_id fd_table[] = {
{"fd", &fd_tmpl},
{},
......@@ -114,7 +218,15 @@ int fd_zio_register(void)
if (err)
return err;
/* FIXME: register a trigger too */
fd_timer_period_jiffies = msecs_to_jiffies(fd_timer_period_ms);
if (fd_timer_period_ms) {
pr_info("%s: using a timer for input stamps (%i ms)\n",
KBUILD_MODNAME, fd_timer_period_ms);
} else {
pr_info("%s: NOT using interrupt (not implemented)\n",
KBUILD_MODNAME);
return -EINVAL;
}
return 0;
}
......@@ -129,21 +241,34 @@ void fd_zio_unregister(void)
int fd_zio_init(struct spec_fd *fd)
{
int err = 0;
struct pci_dev *pdev;
int dev_id;
fd->zdev = zio_allocate_device();
if (IS_ERR(fd->zdev))
return PTR_ERR(fd->zdev);
/* Our dev_id is bus+devfn */
pdev = fd->spec->pdev;
dev_id = (pdev->bus->number << 8) | pdev->devfn;
fd->zdev->owner = THIS_MODULE;
err = zio_register_device(fd->zdev, "fd", 0);
err = zio_register_device(fd->zdev, "fd", dev_id);
if (err) {
zio_free_device(fd->zdev);
return err;
}
__HACK__FD__ = fd;
setup_timer(&fd->timer, fd_timer_fn, (unsigned long)fd);
if (fd_timer_period_ms)
mod_timer(&fd->timer, jiffies + fd_timer_period_jiffies);
return 0;
}
void fd_zio_exit(struct spec_fd *fd)
{
del_timer_sync(&fd->timer);
zio_unregister_device(fd->zdev);
zio_free_device(fd->zdev);
}
......@@ -2,6 +2,7 @@
#define __FINE_DELAY_H__
#include <linux/spinlock.h>
#include <linux/timer.h>
struct fd_calib {
int64_t frr_poly[3]; /* SY89295 delay/temp poly coeffs */
......@@ -33,8 +34,10 @@ struct fd_ch {
/* This is the device we use all around */
struct spec_fd {
spinlock_t lock;
unsigned long flags;
struct spec_dev *spec;
struct zio_device *zdev;
struct timer_list timer;
struct fd_calib calib;
struct fd_ch ch[FD_CH_NUMBER];
unsigned char __iomem *base; /* regs files are byte-oriented */
......@@ -47,8 +50,9 @@ struct spec_fd {
int temp; /* temperature: scaled by 4 bits */
int verbose;
};
#define FD_FLAG_INPUT 1 /* temporary: check flags */
/* Internal time: the first three fields are just converted to zio time */
/* Internal time: the first three fields should be converted to zio time */
struct fd_time {
int64_t utc;
int32_t coarse;
......@@ -57,6 +61,16 @@ struct fd_time {
uint16_t seq_id;
};
/* input ZIO attributes (actually, the internal time is represented as attrs */
enum fd_zattr_in_idx {
FD_ATTR_IN_UTC_H = 0,
FD_ATTR_IN_UTC_L,
FD_ATTR_IN_COARSE,
FD_ATTR_IN_FRAC,
FD_ATTR_IN_SEQ,
FD_ATTR_IN_CHAN,
};
static inline uint32_t fd_readl(struct spec_fd *fd, unsigned long reg)
{
......
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