From: Ian Abbott Date: Mon, 28 May 2012 09:40:42 +0000 (+0100) Subject: amplc_dio200: Implement timer subdevice for new PCIe boards. X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=66aa274205ae0076475ba7a9988825d958394952;p=comedi.git amplc_dio200: Implement timer subdevice for new PCIe boards. Signed-off-by: Ian Abbott --- diff --git a/comedi/drivers/amplc_dio200.c b/comedi/drivers/amplc_dio200.c index 1b9c6607..ed62eadc 100644 --- a/comedi/drivers/amplc_dio200.c +++ b/comedi/drivers/amplc_dio200.c @@ -273,6 +273,8 @@ order they appear in the channel list. /* Extra registers for new PCIe boards */ #define DIO200_ENHANCE 0x20 /* 1 to enable enhanced features */ #define DIO200_VERSION 0x24 /* Hardware version */ +#define DIO200_TS_CONFIG 0x600 /* Timestamp timer config register */ +#define DIO200_TS_COUNT 0x602 /* Timestamp timer count register */ /* * Macros for constructing value for DIO_200_?CLK_SCE and @@ -288,7 +290,23 @@ order they appear in the channel list. (((source) & 030) << 3) | ((source) & 007)) /* - * Periods of the internal clock sources in nanoseconds. + * Timestamp timer configuration register (for new PCIe boards). + */ +#define TS_CONFIG_RESET 0x100 /* Reset counter to zero. */ +#define TS_CONFIG_CLK_SRC_MASK 0x0FF /* Clock source. */ +#define TS_CONFIG_MAX_CLK_SRC 2 /* Maximum clock source value. */ + +/* + * Periods of the timestamp timer clock sources in nanoseconds. + */ +static const unsigned ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = { + 1, /* 1 nanosecond (but with 20 ns granularity). */ + 1000, /* 1 microsecond. */ + 1000000, /* 1 millisecond. */ +}; + +/* + * Periods of the internal counter clock sources in nanoseconds. */ static const unsigned clock_period[32] = { 0, /* dedicated clock input/output pin */ @@ -1715,6 +1733,131 @@ dio200_subdev_8255_cleanup(comedi_device * dev, comedi_subdevice * s) } } +/* + * Handle 'insn_read' for a timer subdevice. + */ +#ifdef CONFIG_COMEDI_PCI +static int +dio200_subdev_timer_read(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int n; + + for (n = 0; n < insn->n; n++) { + data[n] = dio200_read32(dev, DIO200_TS_COUNT); + } + return n; +} +#endif + +/* + * Reset timer subdevice. + */ +#ifdef CONFIG_COMEDI_PCI +static void +dio200_subdev_timer_reset(comedi_device * dev, comedi_subdevice * s) +{ + unsigned int clock; + + clock = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK; + dio200_write32(dev, DIO200_TS_CONFIG, clock | TS_CONFIG_RESET); + dio200_write32(dev, DIO200_TS_CONFIG, clock); +} +#endif + +/* + * Get timer subdevice clock source and period. + */ +#ifdef CONFIG_COMEDI_PCI +static void +dio200_subdev_timer_get_clock_src(comedi_device * dev, comedi_subdevice * s, + lsampl_t *src, lsampl_t *period) +{ + unsigned int clk; + + clk = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK; + *src = clk; + *period = (clk < ARRAY_SIZE(ts_clock_period)) + ? ts_clock_period[clk] : 0; +} +#endif + +/* + * Set timer subdevice clock source. + */ +#ifdef CONFIG_COMEDI_PCI +static int +dio200_subdev_timer_set_clock_src(comedi_device * dev, comedi_subdevice * s, + lsampl_t src) +{ + if (src > TS_CONFIG_MAX_CLK_SRC) { + return -EINVAL; + } + dio200_write32(dev, DIO200_TS_CONFIG, src); + return 0; +} +#endif + +/* + * Handle 'insn_config' for a timer subdevice. + */ +#ifdef CONFIG_COMEDI_PCI +static int +dio200_subdev_timer_config(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int ret = 0; + + switch (data[0]) { + case INSN_CONFIG_RESET: + dio200_subdev_timer_reset(dev, s); + break; + case INSN_CONFIG_SET_CLOCK_SRC: + ret = dio200_subdev_timer_set_clock_src(dev, s, data[1]); + if (ret < 0) + ret = -EINVAL; + break; + case INSN_CONFIG_GET_CLOCK_SRC: + dio200_subdev_timer_get_clock_src(dev, s, &data[1], &data[2]); + break; + default: + ret = -EINVAL; + break; + } + return ret < 0 ? ret : insn->n; +} +#endif + +/* + * This function initializes a timer subdevice. + * + * Uses the timestamp timer registers. There is only one timestamp timer. + */ +#ifdef CONFIG_COMEDI_PCI +static int +dio200_subdev_timer_init(comedi_device * dev, comedi_subdevice * s) +{ + s->type = COMEDI_SUBD_COUNTER; + s->subdev_flags = SDF_READABLE | SDF_LSAMPL; + s->n_chan = 1; + s->maxdata = 0xFFFFFFFF; + s->insn_read = dio200_subdev_timer_read; + s->insn_config = dio200_subdev_timer_config; + return 0; +} +#endif + +/* + * This function cleans up a timer subdevice. + */ +#ifdef CONFIG_COMEDI_PCI +static void +dio200_subdev_timer_cleanup(comedi_device * dev, comedi_subdevice * s) +{ + /* Nothing to do. */ +} +#endif + #ifdef CONFIG_COMEDI_PCI /* * This function does some special set-up for the PCIe boards @@ -1923,7 +2066,16 @@ static int dio200_attach(comedi_device * dev, comedi_devconfig * it) } break; case sd_timer: - /* TODO. Fall-thru to default for now. */ + /* timer subdevice */ +#ifdef CONFIG_COMEDI_PCI + ret = dio200_subdev_timer_init(dev, s); + if (ret < 0) { + return ret; + } +#else + s->type = COMEDI_SUBD_UNUSED; +#endif + break; default: s->type = COMEDI_SUBD_UNUSED; break; @@ -2002,6 +2154,11 @@ static int dio200_detach(comedi_device * dev) case sd_intr: dio200_subdev_intr_cleanup(dev, s); break; + case sd_timer: +#ifdef CONFIG_COMEDI_PCI + dio200_subdev_timer_cleanup(dev, s); +#endif + break; default: break; }