amplc_dio200: Implement timer subdevice for new PCIe boards.
authorIan Abbott <abbotti@mev.co.uk>
Mon, 28 May 2012 09:40:42 +0000 (10:40 +0100)
committerIan Abbott <abbotti@mev.co.uk>
Mon, 28 May 2012 09:40:42 +0000 (10:40 +0100)
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
comedi/drivers/amplc_dio200.c

index 1b9c660740988dd2ffa76042d575d7c874bf3312..ed62eadcb496a43d304bdbee49b4301cf120c985 100644 (file)
@@ -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;
                        }