staging: comedi: amplc_dio200: implement timer subdevice
authorIan Abbott <abbotti@mev.co.uk>
Wed, 24 Oct 2012 15:48:11 +0000 (16:48 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 Oct 2012 22:25:22 +0000 (15:25 -0700)
Implement the timer subdevice for the new PCIe boards.  The subdevice was
previously marked as unused, but was reserved for this purpose.

Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/drivers/amplc_dio200.c

index 768a269d48f816005eb0636e4818b9517234485c..d72a86b3e9ffc331a30ccf1a9f272090423606a1 100644 (file)
 /* Extra registers for new PCIe boards */
 #define DIO200_ENHANCE         0x20    /* 1 to enable enhanced features */
 #define DIO200_VERSION         0x24    /* Hardware version register */
+#define DIO200_TS_CONFIG       0x600   /* Timestamp timer config register */
+#define DIO200_TS_COUNT                0x602   /* Timestamp timer count register */
 
 /*
  * Functions for constructing value for DIO_200_?CLK_SCE and
@@ -342,6 +344,22 @@ static const unsigned int clock_period[32] = {
        /* clock sources 12 and later reserved for enhanced boards */
 };
 
+/*
+ * 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 int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = {
+       1,                      /* 1 nanosecond (but with 20 ns granularity). */
+       1000,                   /* 1 microsecond. */
+       1000000,                /* 1 millisecond. */
+};
+
 /*
  * Register region.
  */
@@ -1621,6 +1639,118 @@ static void dio200_subdev_8255_cleanup(struct comedi_device *dev,
        kfree(subpriv);
 }
 
+/*
+ * Handle 'insn_read' for a timer subdevice.
+ */
+static int dio200_subdev_timer_read(struct comedi_device *dev,
+                                   struct comedi_subdevice *s,
+                                   struct comedi_insn *insn,
+                                   unsigned int *data)
+{
+       unsigned int n;
+
+       for (n = 0; n < insn->n; n++)
+               data[n] = dio200_read32(dev, DIO200_TS_COUNT);
+       return n;
+}
+
+/*
+ * Reset timer subdevice.
+ */
+static void dio200_subdev_timer_reset(struct comedi_device *dev,
+                                     struct 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);
+}
+
+/*
+ * Get timer subdevice clock source and period.
+ */
+static void dio200_subdev_timer_get_clock_src(struct comedi_device *dev,
+                                             struct comedi_subdevice *s,
+                                             unsigned int *src,
+                                             unsigned int *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;
+}
+
+/*
+ * Set timer subdevice clock source.
+ */
+static int dio200_subdev_timer_set_clock_src(struct comedi_device *dev,
+                                            struct comedi_subdevice *s,
+                                            unsigned int src)
+{
+       if (src > TS_CONFIG_MAX_CLK_SRC)
+               return -EINVAL;
+       dio200_write32(dev, DIO200_TS_CONFIG, src);
+       return 0;
+}
+
+/*
+ * Handle 'insn_config' for a timer subdevice.
+ */
+static int dio200_subdev_timer_config(struct comedi_device *dev,
+                                     struct comedi_subdevice *s,
+                                     struct comedi_insn *insn,
+                                     unsigned int *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;
+}
+
+/*
+ * This function initializes a timer subdevice.
+ *
+ * Uses the timestamp timer registers.  There is only one timestamp timer.
+ */
+static int dio200_subdev_timer_init(struct comedi_device *dev,
+                                   struct comedi_subdevice *s)
+{
+       s->type = COMEDI_SUBD_TIMER;
+       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;
+}
+
+/*
+ * This function cleans up a timer subdevice.
+ */
+static void dio200_subdev_timer_cleanup(struct comedi_device *dev,
+                                       struct comedi_subdevice *s)
+{
+       /* Nothing to do. */
+}
+
 /*
  * This function does some special set-up for the PCIe boards
  * PCIe215, PCIe236, PCIe296.
@@ -1735,7 +1865,15 @@ static int dio200_common_attach(struct comedi_device *dev, unsigned int irq,
                        }
                        break;
                case sd_timer:
-                       /* TODO.  Fall-thru to default for now. */
+                       /* Only on PCIe boards. */
+                       if (DO_PCI) {
+                               ret = dio200_subdev_timer_init(dev, s);
+                               if (ret < 0)
+                                       return ret;
+                       } else {
+                               s->type = COMEDI_SUBD_UNUSED;
+                       }
+                       break;
                default:
                        s->type = COMEDI_SUBD_UNUSED;
                        break;
@@ -1897,6 +2035,11 @@ static void dio200_detach(struct comedi_device *dev)
                        case sd_intr:
                                dio200_subdev_intr_cleanup(dev, s);
                                break;
+                       case sd_timer:
+                               /* Only on PCIe boards. */
+                               if (DO_PCI)
+                                       dio200_subdev_timer_cleanup(dev, s);
+                               break;
                        default:
                                break;
                        }