amplc_dio200: Implement timer subdevice for new PCIe boards.
[comedi.git] / comedi / drivers / amplc_dio200.c
index ae06b6ff6177cb772f4df40fd8d32a76f6c181ef..ed62eadcb496a43d304bdbee49b4301cf120c985 100644 (file)
@@ -176,20 +176,20 @@ INTERRUPT SOURCES
   Sources              6              1              6
    0               PPI-X-C0       JUMPER-J5      PPI-X-C0
    1               PPI-X-C3                      PPI-X-C3
-   2              CTR-Y1-OUT                     PPI-Y-C0
-   3              CTR-Y2-OUT                     PPI-Y-C3
-   4              CTR-Z1-OUT                    CTR-Z1-OUT
-   5              CTR-Z2-OUT                    CTR-Z2-OUT
+   2              CTR-Y1-OUT1                    PPI-Y-C0
+   3              CTR-Y2-OUT1                    PPI-Y-C3
+   4              CTR-Z1-OUT1                   CTR-Z1-OUT1
+   5              CTR-Z2-OUT1                   CTR-Z2-OUT1
 
                     PCIe215        PC218E         PCIe236
                  -------------  -------------  -------------
   Sources              6              6              6
-   0               PPI-X-C0      CTR-X1-OUT      PPI-X-C0
-   1               PPI-X-C3      CTR-X2-OUT      PPI-X-C3
-   2               PPI-Y-C0      CTR-Y1-OUT       unused
-   3               PPI-Y-C3      CTR-Y2-OUT       unused
-   4              CTR-Z1-OUT     CTR-Z1-OUT     CTR-Z1-OUT
-   5              CTR-Z2-OUT     CTR-Z2-OUT     CTR-Z2-OUT
+   0               PPI-X-C0      CTR-X1-OUT1     PPI-X-C0
+   1               PPI-X-C3      CTR-X2-OUT1     PPI-X-C3
+   2               PPI-Y-C0      CTR-Y1-OUT1      unused
+   3               PPI-Y-C3      CTR-Y2-OUT1      unused
+   4              CTR-Z1-OUT1    CTR-Z1-OUT1    CTR-Z1-OUT1
+   5              CTR-Z2-OUT1    CTR-Z2-OUT1    CTR-Z2-OUT1
 
                  PC272E/PCI272     PCIe296
                  -------------  -------------
@@ -198,8 +198,8 @@ INTERRUPT SOURCES
    1               PPI-X-C3       PPI-X1-C3
    2               PPI-Y-C0       PPI-Y1-C0
    3               PPI-Y-C3       PPI-Y2-C3
-   4               PPI-Z-C0      CTR-Z1-OUT
-   5               PPI-Z-C3      CTR-Z2-OUT
+   4               PPI-Z-C0      CTR-Z1-OUT1
+   5               PPI-Z-C3      CTR-Z2-OUT1
 
 When an interrupt source is enabled in the interrupt source enable
 register, a rising edge on the source signal latches the corresponding
@@ -270,6 +270,11 @@ order they appear in the channel list.
 #define DIO200_YGAT_SCE                0x1c    /* Group Y gate selection register */
 #define DIO200_ZGAT_SCE                0x1d    /* Group Z gate selection register */
 #define DIO200_INT_SCE         0x1e    /* Interrupt enable/status register */
+/* 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
@@ -277,15 +282,33 @@ order they appear in the channel list.
  *
  * 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2.
  * 'chan' is the channel: 0, 1 or 2.
- * 'source' is the signal source: 0 to 7.
+ * 'source' is the signal source: 0 to 7, or 0 to 31 for "enhanced" boards.
  */
-#define CLK_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | (source))
-#define GAT_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | (source))
+#define CLK_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | \
+               (((source) & 030) << 3) | ((source) & 007))
+#define GAT_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | \
+               (((source) & 030) << 3) | ((source) & 007))
 
 /*
- * Periods of the internal clock sources in nanoseconds.
+ * Timestamp timer configuration register (for new PCIe boards).
  */
-static const unsigned clock_period[8] = {
+#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 */
        100,                    /* 10 MHz */
        1000,                   /* 1 MHz */
@@ -293,7 +316,12 @@ static const unsigned clock_period[8] = {
        100000,                 /* 10 kHz */
        1000000,                /* 1 kHz */
        0,                      /* OUT N-1 */
-       0                       /* group clock input pin */
+       0,                      /* group clock input pin */
+       0,                      /* HIGH (VCC) (enhanced) */
+       0,                      /* LOW (GND) (enhanced) */
+       0,                      /* pattern present (enhanced) */
+       50,                     /* 20 MHz (enhanced) */
+       /* remaining clock sources reserved (enhanced) */
 };
 
 /*
@@ -471,6 +499,7 @@ typedef struct dio200_layout_struct {
        unsigned char sdinfo[DIO200_MAX_SUBDEVS];       /* depends on sdtype */
        char has_int_sce;       /* has interrupt enable/status register */
        char has_clk_gat_sce;   /* has clock/gate selection registers */
+       char has_enhancements;  /* has enhanced features */
 } dio200_layout;
 
 static const dio200_layout dio200_layouts[] = {
@@ -483,6 +512,7 @@ static const dio200_layout dio200_layouts[] = {
                                0x3F},
              has_int_sce:1,
              has_clk_gat_sce:1,
+             has_enhancements:0,
                },
        [pc214_layout] = {
              n_subdevs:4,
@@ -491,6 +521,7 @@ static const dio200_layout dio200_layouts[] = {
              sdinfo:   {0x00, 0x08, 0x10, 0x01},
              has_int_sce:0,
              has_clk_gat_sce:0,
+             has_enhancements:0,
                },
        [pc215_layout] = {
              n_subdevs:5,
@@ -500,6 +531,7 @@ static const dio200_layout dio200_layouts[] = {
              sdinfo:   {0x00, 0x08, 0x10, 0x14, 0x3F},
              has_int_sce:1,
              has_clk_gat_sce:1,
+             has_enhancements:0,
                },
        [pc218_layout] = {
              n_subdevs:7,
@@ -511,6 +543,7 @@ static const dio200_layout dio200_layouts[] = {
                                0x3F},
              has_int_sce:1,
              has_clk_gat_sce:1,
+             has_enhancements:0,
                },
        [pc272_layout] = {
              n_subdevs:4,
@@ -519,6 +552,7 @@ static const dio200_layout dio200_layouts[] = {
              sdinfo:   {0x00, 0x08, 0x10, 0x3F},
              has_int_sce:1,
              has_clk_gat_sce:0,
+             has_enhancements:0,
                },
 #ifdef CONFIG_COMEDI_PCI
        [pcie215_layout] = {
@@ -528,6 +562,7 @@ static const dio200_layout dio200_layouts[] = {
              sdinfo:   {0x00, 0x00, 0x08, 0x00, 0x10, 0x14, 0x00, 0x3F},
              has_int_sce:1,
              has_clk_gat_sce:1,
+             has_enhancements:1,
                },
        [pcie236_layout] = {
              n_subdevs:8,
@@ -536,6 +571,7 @@ static const dio200_layout dio200_layouts[] = {
              sdinfo:   {0x00, 0x00, 0x00, 0x00, 0x10, 0x14, 0x00, 0x3F},
              has_int_sce:1,
              has_clk_gat_sce:1,
+             has_enhancements:1,
                },
        [pcie296_layout] = {
              n_subdevs:8,
@@ -544,6 +580,7 @@ static const dio200_layout dio200_layouts[] = {
              sdinfo:   {0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x00, 0x3F},
              has_int_sce:1,
              has_clk_gat_sce:1,
+             has_enhancements:1,
                },
 #endif
 };
@@ -662,6 +699,35 @@ static void dio200_write8(comedi_device * dev, unsigned int offset,
                writeb(val, devpriv->io.u.membase + offset);
 }
 
+/*
+ * Read 32-bit register.
+ */
+#ifdef CONFIG_COMEDI_PCI
+static unsigned int dio200_read32(comedi_device * dev, unsigned int offset)
+{
+       offset <<= devpriv->io.regshift;
+       if (devpriv->io.regtype == io_regtype)
+               return inl(devpriv->io.u.iobase + offset);
+       else
+               return readl(devpriv->io.u.membase + offset);
+}
+#endif
+
+/*
+ * Write 32-bit register.
+ */
+#ifdef CONFIG_COMEDI_PCI
+static void dio200_write32(comedi_device * dev, unsigned int offset,
+               unsigned int val)
+{
+       offset <<= devpriv->io.regshift;
+       if (devpriv->io.regtype == io_regtype)
+               outl(val, devpriv->io.u.iobase + offset);
+       else
+               writel(val, devpriv->io.u.membase + offset);
+}
+#endif
+
 /*
  * This function looks for a PCI device matching the requested board name,
  * bus and slot.
@@ -1325,7 +1391,7 @@ dio200_subdev_8254_set_gate_src(comedi_device * dev, comedi_subdevice *s,
                return -1;
        if (counter_number > 2)
                return -1;
-       if (gate_src > 7)
+       if (gate_src > (thislayout->has_enhancements ? 31 : 7))
                return -1;
 
        subpriv->gate_src[counter_number] = gate_src;
@@ -1366,7 +1432,7 @@ dio200_subdev_8254_set_clock_src(comedi_device * dev, comedi_subdevice *s,
                return -1;
        if (counter_number > 2)
                return -1;
-       if (clock_src > 7)
+       if (clock_src > (thislayout->has_enhancements ? 31 : 7))
                return -1;
 
        subpriv->clock_src[counter_number] = clock_src;
@@ -1667,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
@@ -1704,6 +1895,10 @@ dio200_pcie_board_setup(comedi_device * dev)
        }
        writel(0x80, brbase + 0x50);
        iounmap(brbase);
+       /*
+        * Enable "enhanced" features of board.
+        */
+       dio200_write8(dev, DIO200_ENHANCE, 1);
        return 0;
 }
 #endif
@@ -1871,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;
@@ -1950,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;
                        }