Module: xenomai-abe
Branch: analogy
Commit: 927499752d3c552f50a1b3b1b68a5ec6baf4ae36
URL:    
http://git.xenomai.org/?p=xenomai-abe.git;a=commit;h=927499752d3c552f50a1b3b1b68a5ec6baf4ae36

Author: Simon Boulay <simon.bou...@gmail.com>
Date:   Sun Jan  3 15:47:29 2010 +0100

analogy: add s526 driver

---

 ksrc/drivers/analogy/Config.in          |    3 +-
 ksrc/drivers/analogy/Kconfig            |    1 +
 ksrc/drivers/analogy/Makefile           |    3 +-
 ksrc/drivers/analogy/sensoray/Config.in |    2 +
 ksrc/drivers/analogy/sensoray/Kconfig   |    5 +
 ksrc/drivers/analogy/sensoray/Makefile  |   30 +
 ksrc/drivers/analogy/sensoray/s526.c    |  975 +++++++++++++++++++++++++++++++
 7 files changed, 1017 insertions(+), 2 deletions(-)

diff --git a/ksrc/drivers/analogy/Config.in b/ksrc/drivers/analogy/Config.in
index 4bc1197..0336a85 100644
--- a/ksrc/drivers/analogy/Config.in
+++ b/ksrc/drivers/analogy/Config.in
@@ -7,10 +7,11 @@ comment 'ANALOGY drivers'
 
 dep_tristate 'ANALOGY interface' CONFIG_XENO_DRIVERS_ANALOGY 
$CONFIG_XENO_SKIN_RTDM
 
-if [ "$CONFIG_XENO_DRIVERS_ANALOGY" != "n" ]; then 
+if [ "$CONFIG_XENO_DRIVERS_ANALOGY" != "n" ]; then
    source drivers/xenomai/analogy/testing/Config.in
    source drivers/xenomai/analogy/intel/Config.in
    source drivers/xenomai/analogy/national_instruments/Config.in
+   source drivers/xenomai/analogy/sensoray/Config.in
 fi
 
 endmenu
diff --git a/ksrc/drivers/analogy/Kconfig b/ksrc/drivers/analogy/Kconfig
index 43d61a7..b33fd67 100644
--- a/ksrc/drivers/analogy/Kconfig
+++ b/ksrc/drivers/analogy/Kconfig
@@ -44,5 +44,6 @@ config XENO_DRIVERS_ANALOGY_DRIVER_DEBUG_LEVEL
 source drivers/xenomai/analogy/testing/Kconfig
 source drivers/xenomai/analogy/intel/Kconfig
 source drivers/xenomai/analogy/national_instruments/Kconfig
+source drivers/xenomai/analogy/sensoray/Kconfig
 
 endmenu
diff --git a/ksrc/drivers/analogy/Makefile b/ksrc/drivers/analogy/Makefile
index c51f5e1..363d625 100644
--- a/ksrc/drivers/analogy/Makefile
+++ b/ksrc/drivers/analogy/Makefile
@@ -4,7 +4,7 @@ ifeq ($(PATCHLEVEL),6)
 
 EXTRA_CFLAGS += -D__IN_XENOMAI__ -Iinclude/xenomai -Idrivers/xenomai/analogy
 
-obj-$(CONFIG_XENO_DRIVERS_ANALOGY) += xeno_analogy.o testing/ intel/ 
national_instruments/
+obj-$(CONFIG_XENO_DRIVERS_ANALOGY) += xeno_analogy.o testing/ intel/ 
national_instruments/ sensoray/
 
 xeno_analogy-y := \
        buffer.o \
@@ -29,6 +29,7 @@ subdir-$(CONFIG_XENO_DRIVERS_ANALOGY_FAKE) += testing
 subdir-$(CONFIG_XENO_DRIVERS_ANALOGY_LOOP) += testing
 subdir-$(CONFIG_XENO_DRIVERS_ANALOGY_8255) += intel
 subdir-$(CONFIG_XENO_DRIVERS_ANALOGY_NI_MITE) += national_instruments
+subdir-$(CONFIG_XENO_DRIVERS_ANALOGY_S526) += sensoray
 
 obj-$(CONFIG_XENO_DRIVERS_ANALOGY) += xeno_analogy.o
 
diff --git a/ksrc/drivers/analogy/sensoray/Config.in 
b/ksrc/drivers/analogy/sensoray/Config.in
new file mode 100644
index 0000000..00e956c
--- /dev/null
+++ b/ksrc/drivers/analogy/sensoray/Config.in
@@ -0,0 +1,2 @@
+
+dep_tristate 'Sensoray Model 526 driver' CONFIG_XENO_DRIVERS_ANALOGY_S526 
$CONFIG_XENO_DRIVERS_ANALOGY
diff --git a/ksrc/drivers/analogy/sensoray/Kconfig 
b/ksrc/drivers/analogy/sensoray/Kconfig
new file mode 100644
index 0000000..ce5aa51
--- /dev/null
+++ b/ksrc/drivers/analogy/sensoray/Kconfig
@@ -0,0 +1,5 @@
+
+config XENO_DRIVERS_ANALOGY_S526
+       depends on XENO_DRIVERS_ANALOGY
+       tristate "Sensoray Model 526 driver"
+       default n
diff --git a/ksrc/drivers/analogy/sensoray/Makefile 
b/ksrc/drivers/analogy/sensoray/Makefile
new file mode 100644
index 0000000..773da98
--- /dev/null
+++ b/ksrc/drivers/analogy/sensoray/Makefile
@@ -0,0 +1,30 @@
+ifeq ($(PATCHLEVEL),6)
+
+# Makefile flag for Linux v2.6
+
+EXTRA_CFLAGS += -D__IN_XENOMAI__ -Iinclude/xenomai
+
+obj-$(CONFIG_XENO_DRIVERS_ANALOGY_S526) += analogy_s526.o
+
+analogy_s526-y := s526.o
+
+else
+
+# Makefile flag for Linux v2.4
+
+O_TARGET := built-in.o
+
+obj-$(CONFIG_XENO_DRIVERS_ANALOGY_S526) += analogy_s526.o
+
+analogy_s526-objs := s526.o
+
+export-objs := $(analogy_s526-objs)
+
+EXTRA_CFLAGS += -D__IN_XENOMAI__ -I$(TOPDIR)/include/xenomai 
-I$(TOPDIR)/include/xenomai/compat
+
+include $(TOPDIR)/Rules.make
+
+analogy_s526.o: $(analogy_s526-objs)
+       $(LD) -r -o $@ $(analogy_s526-objs)
+
+endif
diff --git a/ksrc/drivers/analogy/sensoray/s526.c 
b/ksrc/drivers/analogy/sensoray/s526.c
new file mode 100644
index 0000000..ac95653
--- /dev/null
+++ b/ksrc/drivers/analogy/sensoray/s526.c
@@ -0,0 +1,975 @@
+/**
+ * @file
+ * Analogy driver for Sensoray Model 526 board
+ *
+ * Copyright (C) 2009 Simon Boulay <simon.bou...@gmail.com>
+ *
+ * Derived from comedi:
+ * Copyright (C) 2000 David A. Schleef <d...@schleef.org>
+ *               2006 Everett Wang <everett.w...@everteq.com>
+ *               2009 Ian Abbott <abbo...@mev.co.uk>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Xenomai; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+/*
+ * Original code comes from comedi linux-next staging driver (2009.12.20)
+ * Board documentation: http://www.sensoray.com/products/526data.htm
+ * Everything should work as in comedi:
+ *   - Encoder works
+ *   - Analog input works
+ *   - Analog output works
+ *   - PWM output works
+ *   - Commands are not supported yet.
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <asm/byteorder.h>
+#include <analogy/analogy_driver.h>
+
+#define BOARD_NAME "Sensoray Model 526"
+#define S526_GPCT_CHANS        4
+#define S526_GPCT_BITS 24
+#define S526_AI_CHANS  10      /* 8 regular differential inputs
+                                * channel 8 is "reference 0" (+10V)
+                                * channel 9 is "reference 1" (0V) */
+#define S526_AI_BITS   16
+#define S526_AO_CHANS  4
+#define S526_AO_BITS   16
+#define S526_HAVE_DIO  1
+#define S526_DIO_CHANS 8
+#define S526_DIO_BITS  1
+
+#define S526_START_AI_CONV  0
+#define S526_AI_READ        0
+
+/* Ports */
+#define S526_IOSIZE            0x40  /* 64 bytes */
+#define S526_DEFAULT_ADDRESS   0x2C0 /* Manufacturing default */
+#define S526_NUM_PORTS         27
+
+/* registers */
+#define REG_TCR 0x00
+#define REG_WDC 0x02
+#define REG_DAC 0x04
+#define REG_ADC 0x06
+#define REG_ADD 0x08
+#define REG_DIO 0x0A
+#define REG_IER 0x0C
+#define REG_ISR 0x0E
+#define REG_MSC 0x10
+#define REG_C0L 0x12
+#define REG_C0H 0x14
+#define REG_C0M 0x16
+#define REG_C0C 0x18
+#define REG_C1L 0x1A
+#define REG_C1H 0x1C
+#define REG_C1M 0x1E
+#define REG_C1C 0x20
+#define REG_C2L 0x22
+#define REG_C2H 0x24
+#define REG_C2M 0x26
+#define REG_C2C 0x28
+#define REG_C3L 0x2A
+#define REG_C3H 0x2C
+#define REG_C3M 0x2E
+#define REG_C3C 0x30
+#define REG_EED 0x32
+#define REG_EEC 0x34
+
+static const int s526_ports[] = {
+       REG_TCR,
+       REG_WDC,
+       REG_DAC,
+       REG_ADC,
+       REG_ADD,
+       REG_DIO,
+       REG_IER,
+       REG_ISR,
+       REG_MSC,
+       REG_C0L,
+       REG_C0H,
+       REG_C0M,
+       REG_C0C,
+       REG_C1L,
+       REG_C1H,
+       REG_C1M,
+       REG_C1C,
+       REG_C2L,
+       REG_C2H,
+       REG_C2M,
+       REG_C2C,
+       REG_C3L,
+       REG_C3H,
+       REG_C3M,
+       REG_C3C,
+       REG_EED,
+       REG_EEC
+};
+
+struct counter_mode_register_t {
+#if defined (__LITTLE_ENDIAN_BITFIELD)
+       unsigned short coutSource:1;
+       unsigned short coutPolarity:1;
+       unsigned short autoLoadResetRcap:3;
+       unsigned short hwCtEnableSource:2;
+       unsigned short ctEnableCtrl:2;
+       unsigned short clockSource:2;
+       unsigned short countDir:1;
+       unsigned short countDirCtrl:1;
+       unsigned short outputRegLatchCtrl:1;
+       unsigned short preloadRegSel:1;
+       unsigned short reserved:1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+       unsigned short reserved:1;
+       unsigned short preloadRegSel:1;
+       unsigned short outputRegLatchCtrl:1;
+       unsigned short countDirCtrl:1;
+       unsigned short countDir:1;
+       unsigned short clockSource:2;
+       unsigned short ctEnableCtrl:2;
+       unsigned short hwCtEnableSource:2;
+       unsigned short autoLoadResetRcap:3;
+       unsigned short coutPolarity:1;
+       unsigned short coutSource:1;
+#else
+#error Unknown bit field order
+#endif
+};
+
+union cmReg {
+       struct counter_mode_register_t reg;
+       unsigned short value;
+};
+
+#define MAX_GPCT_CONFIG_DATA 6
+
+/* Different Application Classes for GPCT Subdevices */
+/* The list is not exhaustive and needs discussion! */
+enum S526_GPCT_APP_CLASS {
+       CountingAndTimeMeasurement,
+       SinglePulseGeneration,
+       PulseTrainGeneration,
+       PositionMeasurement,
+       Miscellaneous
+};
+
+/* Config struct for different GPCT subdevice Application Classes and
+ * their options
+ */
+struct s526GPCTConfig {
+       enum S526_GPCT_APP_CLASS app;
+       int data[MAX_GPCT_CONFIG_DATA];
+};
+
+typedef struct s526_priv {
+       unsigned long io_base;
+} s526_priv_t;
+
+struct s526_subd_gpct_priv {
+       struct s526GPCTConfig config[4];
+};
+
+struct s526_subd_ai_priv {
+       uint16_t config;
+};
+
+struct s526_subd_ao_priv {
+       uint16_t readback[2];
+};
+
+struct s526_subd_dio_priv {
+       int io_bits;
+       unsigned int state;
+};
+
+#define devpriv ((s526_priv_t*)(dev->priv))
+
+#define ADDR_REG(reg) (devpriv->io_base + (reg))
+#define ADDR_CHAN_REG(reg, chan) (devpriv->io_base + (reg) + (chan) * 8)
+
+static int s526_gpct_insn_config(a4l_subd_t *subd, a4l_kinsn_t *insn);
+static int s526_gpct_rinsn(a4l_subd_t *subd, a4l_kinsn_t *insn);
+static int s526_gpct_winsn(a4l_subd_t *subd, a4l_kinsn_t *insn);
+static int s526_ai_insn_config(a4l_subd_t *subd, a4l_kinsn_t *insn);
+static int s526_ai_rinsn(a4l_subd_t *subd, a4l_kinsn_t *insn);
+static int s526_ao_winsn(a4l_subd_t *subd, a4l_kinsn_t *insn);
+static int s526_ao_rinsn(a4l_subd_t *subd, a4l_kinsn_t *insn);
+static int s526_dio_insn_bits(a4l_subd_t *subd, a4l_kinsn_t *insn);
+static int s526_dio_insn_config(a4l_subd_t *subd, a4l_kinsn_t *insn);
+
+static int s526_gpct_insn_config(a4l_subd_t *subd, a4l_kinsn_t *insn)
+{
+       a4l_dev_t *dev = subd->dev;
+       struct s526_subd_gpct_priv *subdpriv =
+           (struct s526_subd_gpct_priv *)subd->priv;
+       unsigned int *data = (unsigned int *)insn->data;
+       int subdev_channel = CR_CHAN(insn->chan_desc);
+       int i;
+       short value;
+       union cmReg cmReg;
+
+       /* a4l_info(dev, "s526_gpct_insn_config: Configuring Channel %d\n", 
subdev_channel); */
+
+       for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
+               /* subdpriv->config[subdev_channel].data[i] = insn->data[i]; */
+               subdpriv->config[subdev_channel].data[i] = data[i];
+               /* a4l_info(dev, "data[%d]=%x\n", i, insn->data[i]); */
+       }
+
+       /*  Check what type of Counter the user requested, data[0] contains */
+       /*  the Application type */
+       switch (data[0]) {
+       case A4L_INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
+               /*
+                  data[0]: Application Type
+                  data[1]: Counter Mode Register Value
+                  data[2]: Pre-load Register Value
+                  data[3]: Conter Control Register
+                */
+               /* a4l_info(dev, "s526_gpct_insn_config: Configuring 
Encoder\n"); */
+               subdpriv->config[subdev_channel].app = PositionMeasurement;
+
+#if 0
+               /* Example of Counter Application */
+               /* One-shot (software trigger) */
+               cmReg.reg.coutSource = 0;       /*  out RCAP */
+               cmReg.reg.coutPolarity = 1;     /*  Polarity inverted */
+               cmReg.reg.autoLoadResetRcap = 0;        /*  Auto load disabled 
*/
+               cmReg.reg.hwCtEnableSource = 3; /*  NOT RCAP */
+               cmReg.reg.ctEnableCtrl = 2;     /*  Hardware */
+               cmReg.reg.clockSource = 2;      /*  Internal */
+               cmReg.reg.countDir = 1; /*  Down */
+               cmReg.reg.countDirCtrl = 1;     /*  Software */
+               cmReg.reg.outputRegLatchCtrl = 0;       /*  latch on read */
+               cmReg.reg.preloadRegSel = 0;    /*  PR0 */
+               cmReg.reg.reserved = 0;
+
+               outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+               outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+               outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+               outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));   /*  
Reset the counter */
+               outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));   /*  
Load the counter from PR0 */
+
+               outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));   /*  
Reset RCAP (fires one-shot) */
+
+#endif
+
+#if 1
+               /*  Set Counter Mode Register */
+               cmReg.value = data[1] & 0xFFFF;
+
+               /* a44_info(dev, "Counter Mode register=%x\n", cmReg.value); */
+               outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+               /*  Reset the counter if it is software preload */
+               if (cmReg.reg.autoLoadResetRcap == 0) {
+                       outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));   
/* Reset the counter */
+                       /* outw(0x4000, ADDR_CHAN_REG(REG_C0C, 
subdev_channel));*/    /* Load the counter from PR0 */
+               }
+#else
+               cmReg.reg.countDirCtrl = 0;     /*  0 quadrature, 1 software 
control */
+
+               /*  data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
+               if (data[1] == GPCT_X2) {
+                       cmReg.reg.clockSource = 1;
+               } else if (data[1] == GPCT_X4) {
+                       cmReg.reg.clockSource = 2;
+               } else {
+                       cmReg.reg.clockSource = 0;
+               }
+
+               /*  When to take into account the indexpulse: */
+               if (data[2] == GPCT_IndexPhaseLowLow) {
+               } else if (data[2] == GPCT_IndexPhaseLowHigh) {
+               } else if (data[2] == GPCT_IndexPhaseHighLow) {
+               } else if (data[2] == GPCT_IndexPhaseHighHigh) {
+               }
+               /*  Take into account the index pulse? */
+               if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
+                       cmReg.reg.autoLoadResetRcap = 4;        /*  Auto load 
with INDEX^ */
+
+               /*  Set Counter Mode Register */
+               cmReg.value = (short)(data[1] & 0xFFFF);
+               outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+               /*  Load the pre-load register high word */
+               value = (short)((data[2] >> 16) & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+
+               /*  Load the pre-load register low word */
+               value = (short)(data[2] & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+               /*  Write the Counter Control Register */
+               if (data[3] != 0) {
+                       value = (short)(data[3] & 0xFFFF);
+                       outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+               }
+               /*  Reset the counter if it is software preload */
+               if (cmReg.reg.autoLoadResetRcap == 0) {
+                       outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));   
/*  Reset the counter */
+                       outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));   
/*  Load the counter from PR0 */
+               }
+#endif
+               break;
+
+       case A4L_INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
+               /*
+                  data[0]: Application Type
+                  data[1]: Counter Mode Register Value
+                  data[2]: Pre-load Register 0 Value
+                  data[3]: Pre-load Register 1 Value
+                  data[4]: Conter Control Register
+                */
+               /* a4l_info(dev, "s526_gpct_insn_config: Configuring SPG\n"); */
+               subdpriv->config[subdev_channel].app = SinglePulseGeneration;
+
+               /*  Set Counter Mode Register */
+               cmReg.value = (short)(data[1] & 0xFFFF);
+               cmReg.reg.preloadRegSel = 0;    /*  PR0 */
+               outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+               /*  Load the pre-load register 0 high word */
+               value = (short)((data[2] >> 16) & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+
+               /*  Load the pre-load register 0 low word */
+               value = (short)(data[2] & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+               /*  Set Counter Mode Register */
+               cmReg.value = (short)(data[1] & 0xFFFF);
+               cmReg.reg.preloadRegSel = 1;    /*  PR1 */
+               outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+               /*  Load the pre-load register 1 high word */
+               value = (short)((data[3] >> 16) & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+
+               /*  Load the pre-load register 1 low word */
+               value = (short)(data[3] & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+               /*  Write the Counter Control Register */
+               if (data[4] != 0) {
+                       value = (short)(data[4] & 0xFFFF);
+                       outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+               }
+               break;
+
+       case A4L_INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
+               /*
+                  data[0]: Application Type
+                  data[1]: Counter Mode Register Value
+                  data[2]: Pre-load Register 0 Value
+                  data[3]: Pre-load Register 1 Value
+                  data[4]: Conter Control Register
+                */
+               /* a4l_info(dev, "s526_gpct_insn_config: Configuring PTG\n"); */
+               subdpriv->config[subdev_channel].app = PulseTrainGeneration;
+
+               /*  Set Counter Mode Register */
+               cmReg.value = (short)(data[1] & 0xFFFF);
+               cmReg.reg.preloadRegSel = 0;    /*  PR0 */
+               outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+               /*  Load the pre-load register 0 high word */
+               value = (short)((data[2] >> 16) & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+
+               /*  Load the pre-load register 0 low word */
+               value = (short)(data[2] & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+               /*  Set Counter Mode Register */
+               cmReg.value = (short)(data[1] & 0xFFFF);
+               cmReg.reg.preloadRegSel = 1;    /*  PR1 */
+               outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+               /*  Load the pre-load register 1 high word */
+               value = (short)((data[3] >> 16) & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+
+               /*  Load the pre-load register 1 low word */
+               value = (short)(data[3] & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+               /*  Write the Counter Control Register */
+               if (data[4] != 0) {
+                       value = (short)(data[4] & 0xFFFF);
+                       outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+               }
+               break;
+
+       default:
+               a4l_err(dev, "s526_gpct_insn_config: unsupported 
GPCT_insn_config\n");
+               return -EINVAL;
+               break;
+       }
+
+       return 0;
+}
+
+static int s526_gpct_rinsn(a4l_subd_t *subd, a4l_kinsn_t *insn)
+{
+       a4l_dev_t *dev = subd->dev;
+       uint32_t *data = (uint32_t *)insn->data;
+       int counter_channel = CR_CHAN(insn->chan_desc);
+       unsigned short datalow;
+       unsigned short datahigh;
+       int i;
+
+       if (insn->data_size <= 0) {
+               a4l_err(dev, "s526_gpct_rinsn: n should be > 0\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < insn->data_size; i++) {
+               datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
+               datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
+               data[i] = (int)(datahigh & 0x00FF);
+               data[i] = (data[i] << 16) | (datalow & 0xFFFF);
+               /* a4l_info(dev, "s526_gpct_rinsn GPCT[%d]: %x(0x%04x, 
0x%04x)\n", counter_channel, data[i], datahigh, datalow); */
+       }
+
+       return 0;
+}
+
+static int s526_gpct_winsn(a4l_subd_t *subd, a4l_kinsn_t *insn)
+{
+       a4l_dev_t *dev = subd->dev;
+       struct s526_subd_gpct_priv *subdpriv =
+           (struct s526_subd_gpct_priv *)subd->priv;
+       uint32_t *data = (uint32_t *)insn->data;
+       int subdev_channel = CR_CHAN(insn->chan_desc);  /*  Unpack chanspec */
+       short value;
+       union cmReg cmReg;
+
+
+       /* a4l_info(dev, "s526_gpct_winsn: GPCT_INSN_WRITE on channel %d\n", 
subdev_channel); */
+
+       cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
+       /* a4l_info(dev, "s526_gpct_winsn: Counter Mode Register: %x\n", 
cmReg.value); */
+
+       /*  Check what Application of Counter this channel is configured for */
+       switch (subdpriv->config[subdev_channel].app) {
+       case PositionMeasurement:
+               /* a4l_info(dev, "s526_gpct_winsn: INSN_WRITE: PM\n"); */
+               outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
+                                                            subdev_channel));
+               outw(0xFFFF & (*data),
+                    ADDR_CHAN_REG(REG_C0L, subdev_channel));
+               break;
+
+       case SinglePulseGeneration:
+               /* a4l_info(dev, "s526_gpct_winsn: INSN_WRITE: SPG\n"); */
+               outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
+                                                            subdev_channel));
+               outw(0xFFFF & (*data),
+                    ADDR_CHAN_REG(REG_C0L, subdev_channel));
+               break;
+
+       case PulseTrainGeneration:
+               /* data[0] contains the PULSE_WIDTH
+                  data[1] contains the PULSE_PERIOD
+                  @pre PULSE_PERIOD > PULSE_WIDTH > 0
+                  The above periods must be expressed as a multiple of the
+                  pulse frequency on the selected source
+               */
+               /* a4l_info(dev, "s526_gpct_winsn: INSN_WRITE: PTG\n"); */
+               if ((data[1] > data[0]) && (data[0] > 0)) {
+                       (subdpriv->config[subdev_channel]).data[0] = data[0];
+                       (subdpriv->config[subdev_channel]).data[1] = data[1];
+               } else {
+                       a4l_err(dev, "s526_gpct_winsn: INSN_WRITE: PTG: Problem 
with Pulse params -> %du %du\n",
+                               data[0], data[1]);
+                       return -EINVAL;
+               }
+
+               value = (short)((*data >> 16) & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+               value = (short)(*data & 0xFFFF);
+               outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+               break;
+       default:                /*  Impossible */
+               a4l_err(dev, "s526_gpct_winsn: INSN_WRITE: Functionality %d not 
implemented yet\n",
+                        subdpriv->config[subdev_channel].app);
+               return -EINVAL;
+               break;
+       }
+
+       return 0;
+}
+
+#define ISR_ADC_DONE 0x4
+static int s526_ai_insn_config(a4l_subd_t *subd, a4l_kinsn_t *insn)
+{
+       a4l_dev_t *dev = subd->dev;
+       struct s526_subd_ai_priv *subdpriv =
+           (struct s526_subd_ai_priv *)subd->priv;
+       unsigned int *data = (unsigned int *)insn->data;
+
+       if (insn->data_size < 1)
+               return -EINVAL;
+
+       /* data[0] : channels was set in relevant bits.
+          data[1] : delay
+        */
+       /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
+        * enable channels here.  The channel should be enabled in the
+        * INSN_READ handler. */
+
+       /*  Enable ADC interrupt */
+       outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
+       /* a4l_info(dev, "s526_ai_insn_config: ADC current value: 0x%04x\n", 
inw(ADDR_REG(REG_ADC))); */
+       subdpriv->config = (data[0] & 0x3FF) << 5;
+       if (data[1] > 0)
+               subdpriv->config |= 0x8000;     /* set the delay */
+
+       subdpriv->config |= 0x0001;     /*  ADC start bit. */
+
+       return 0;
+}
+
+static int s526_ai_rinsn(a4l_subd_t *subd, a4l_kinsn_t *insn)
+{
+       a4l_dev_t *dev = subd->dev;
+       struct s526_subd_ai_priv *subdpriv =
+           (struct s526_subd_ai_priv *)subd->priv;
+       uint16_t *data = (uint16_t *)insn->data;
+       int n, i;
+       int chan = CR_CHAN(insn->chan_desc);
+       uint16_t value;
+       uint16_t d;
+       uint16_t status;
+
+       /* Set configured delay, enable channel for this channel only,
+        * select "ADC read" channel, set "ADC start" bit. */
+       value = (subdpriv->config & 0x8000) |
+           ((1 << 5) << chan) | (chan << 1) | 0x0001;
+
+       /* convert n samples */
+       for (n = 0; n < insn->data_size; n++) {
+               /* trigger conversion */
+               outw(value, ADDR_REG(REG_ADC));
+               /* a4l_info(dev, "s526_ai_rinsn: Wrote 0x%04x to ADC\n", 
value); */
+               /* a4l_info(dev, "s526_ai_rinsn: ADC reg=0x%04x\n", 
inw(ADDR_REG(REG_ADC))); */
+
+#define TIMEOUT 100
+
+               /* wait for conversion to end */
+               for (i = 0; i < TIMEOUT; i++) {
+                       status = inw(ADDR_REG(REG_ISR));
+                       if (status & ISR_ADC_DONE) {
+                               outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
+                               break;
+                       }
+               }
+               if (i == TIMEOUT) {
+                       a4l_warn(dev, "s526_ai_rinsn: ADC(0x%04x) timeout\n", 
inw(ADDR_REG(REG_ISR)));
+                       return -ETIMEDOUT;
+               }
+
+               /* read data */
+               d = inw(ADDR_REG(REG_ADD));
+               /* a4l_info(dev "s526_ai_rinsn: AI[%d]=0x%04x\n", n, (unsigned 
short)(d & 0xFFFF)); */
+
+               /* munge data */
+               data[n] = d ^ 0x8000;
+       }
+
+       return 0;
+}
+
+static int s526_ao_winsn(a4l_subd_t *subd, a4l_kinsn_t *insn)
+{
+       a4l_dev_t *dev = subd->dev;
+       struct s526_subd_ao_priv *subdpriv =
+           (struct s526_subd_ao_priv *)subd->priv;
+       uint16_t *data = (uint16_t *)insn->data;
+       int i;
+       int chan = CR_CHAN(insn->chan_desc);
+       uint16_t val;
+
+       val = chan << 1;
+       outw(val, ADDR_REG(REG_DAC));
+
+       /* Writing a list of values to an AO channel is probably not
+        * very useful, but that's how the interface is defined. */
+       for (i = 0; i < insn->data_size; i++) {
+               /* a typical programming sequence */
+               outw(data[i], ADDR_REG(REG_ADD));       /*  write the data to 
preload register */
+               subdpriv->readback[chan] = data[i];
+               outw(val + 1, ADDR_REG(REG_DAC));       /*  starts the D/A 
conversion. */
+       }
+
+       return 0;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int s526_ao_rinsn(a4l_subd_t *subd, a4l_kinsn_t *insn)
+{
+       struct s526_subd_ao_priv *subdpriv =
+               (struct s526_subd_ao_priv *)subd->priv;
+       uint16_t *data = (uint16_t *)insn->data;
+       int i;
+       int chan = CR_CHAN(insn->chan_desc);
+
+       for (i = 0; i < insn->data_size; i++)
+               data[i] = subdpriv->readback[chan];
+
+       return 0;
+}
+
+static int s526_dio_insn_config(a4l_subd_t *subd, a4l_kinsn_t *insn)
+{
+       a4l_dev_t *dev = subd->dev;
+       struct s526_subd_dio_priv *subdpriv =
+           (struct s526_subd_dio_priv *)subd->priv;
+       unsigned int *data = (unsigned int *)insn->data;
+       int chan = CR_CHAN(insn->chan_desc);
+       int group, mask;
+
+       group = chan >> 2;
+       mask = 0xF << (group << 2);
+       switch (data[0]) {
+       case A4L_INSN_CONFIG_DIO_OUTPUT:
+               subdpriv->state |= 1 << (group + 10);   /* bit 10/11 set the 
group
+                                                          1/2's mode */
+               subdpriv->io_bits |= mask;
+               break;
+       case A4L_INSN_CONFIG_DIO_INPUT:
+               subdpriv->state &= ~(1 << (group + 10));        /* 1 is output, 
0 is
+                                                                * input. */
+               subdpriv->io_bits &= ~mask;
+               break;
+       case A4L_INSN_CONFIG_DIO_QUERY:
+               data[1] =
+                   (subdpriv->io_bits & mask) ? A4L_OUTPUT : A4L_INPUT;
+               return insn->data_size;
+       default:
+               return -EINVAL;
+       }
+       outw(subdpriv->state, ADDR_REG(REG_DIO));
+
+       return 0;
+}
+
+/* DIO devices are slightly special.  Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels.  The
+ * comedi core can convert between insn_bits and insn_read/write */
+static int s526_dio_insn_bits(a4l_subd_t *subd, a4l_kinsn_t *insn)
+{
+       a4l_dev_t *dev = subd->dev;
+       struct s526_subd_dio_priv *subdpriv =
+               (struct s526_subd_dio_priv *)subd->priv;
+       uint8_t *data = (uint8_t *)insn->data;
+
+       if (insn->data_size != 2)
+               return -EINVAL;
+
+       /* The insn data is a mask in data[0] and the new data
+        * in data[1], each channel cooresponding to a bit. */
+       if (data[0]) {
+               subdpriv->state &= ~(data[0]);
+               subdpriv->state |= data[0] & data[1];
+               /* Write out the new digital output lines */
+               outw(subdpriv->state, ADDR_REG(REG_DIO));
+       }
+
+       /* on return, data[1] contains the value of the digital
+        * input and output lines. */
+       data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF;        /*  low 8 bits are the 
data */
+       /* or we could just return the software copy of the output values if
+        * it was a purely digital output subdevice */
+       /* insn->data[1]=subdpriv->state & 0xFF; */
+
+       return 0;
+}
+
+/* --- Channels descriptor --- */
+
+static a4l_chdesc_t s526_chan_desc_gpct = {
+       .mode = A4L_CHAN_GLOBAL_CHANDESC,
+       .length = S526_GPCT_CHANS,
+       .chans = {
+               {A4L_CHAN_AREF_GROUND, S526_GPCT_BITS},
+       },
+};
+
+static a4l_chdesc_t s526_chan_desc_ai = {
+       .mode = A4L_CHAN_GLOBAL_CHANDESC,
+       .length = S526_AI_CHANS,
+       .chans = {
+               {A4L_CHAN_AREF_GROUND, S526_AI_BITS},
+       },
+};
+
+static a4l_chdesc_t s526_chan_desc_ao = {
+       .mode = A4L_CHAN_GLOBAL_CHANDESC,
+       .length = S526_AO_CHANS,
+       .chans = {
+               {A4L_CHAN_AREF_GROUND, S526_AO_BITS},
+       },
+};
+
+static a4l_chdesc_t s526_chan_desc_dio = {
+       .mode = A4L_CHAN_GLOBAL_CHANDESC,
+       .length = S526_DIO_CHANS,
+       .chans = {
+               {A4L_CHAN_AREF_GROUND, S526_DIO_BITS},
+       },
+};
+
+/* --- Subdevice initialization functions --- */
+
+/* General purpose counter/timer (gpct) */
+static void setup_subd_gpct(a4l_subd_t *subd)
+{
+       subd->flags = A4L_SUBD_COUNTER;
+       subd->chan_desc = &s526_chan_desc_gpct;
+       subd->insn_read = s526_gpct_rinsn;
+       subd->insn_config = s526_gpct_insn_config;
+       subd->insn_write = s526_gpct_winsn;
+
+       /* Command are not implemented yet, however they are necessary to
+          allocate the necessary memory for the comedi_async struct (used
+          to trigger the GPCT in case of pulsegenerator function */
+       /* subd->do_cmd = s526_gpct_cmd; */
+       /* subd->do_cmdtest = s526_gpct_cmdtest; */
+       /* subd->cancel = s526_gpct_cancel; */
+}
+
+/* Analog input subdevice */
+static void setup_subd_ai(a4l_subd_t *subd)
+{
+       subd->flags = A4L_SUBD_AI;
+       subd->chan_desc = &s526_chan_desc_ai;
+       subd->rng_desc = &range_bipolar10;
+       subd->insn_read = s526_ai_rinsn;
+       subd->insn_config = s526_ai_insn_config;
+}
+
+/* Analog output subdevice */
+static void setup_subd_ao(a4l_subd_t *subd)
+{
+       subd->flags = A4L_SUBD_AO;
+       subd->chan_desc = &s526_chan_desc_ao;
+       subd->rng_desc = &range_bipolar10;
+       subd->insn_write = s526_ao_winsn;
+       subd->insn_read = s526_ao_rinsn;
+}
+
+/* Digital i/o subdevice */
+static void setup_subd_dio(a4l_subd_t *subd)
+{
+       if (S526_HAVE_DIO > 0) {
+               subd->flags = A4L_SUBD_DIO;
+               subd->chan_desc = &s526_chan_desc_dio;
+               subd->rng_desc = &range_digital;
+               subd->insn_bits = s526_dio_insn_bits;
+               subd->insn_config = s526_dio_insn_config;
+       } else {
+               subd->flags = A4L_SUBD_UNUSED;
+       }
+}
+
+struct setup_subd {
+       void (*setup_func) (a4l_subd_t *);
+       int sizeof_priv;
+};
+
+static struct setup_subd setup_subds[4] = {
+       {
+               .setup_func = setup_subd_gpct,
+               .sizeof_priv = sizeof(struct s526_subd_gpct_priv),
+       },
+       {
+               .setup_func = setup_subd_ai,
+               .sizeof_priv = sizeof(struct s526_subd_ai_priv),
+       },
+       {
+               .setup_func = setup_subd_ao,
+               .sizeof_priv = sizeof(struct s526_subd_ao_priv),
+       },
+       {
+               .setup_func = setup_subd_dio,
+               .sizeof_priv = sizeof(struct s526_subd_dio_priv),
+       },
+};
+
+static int dev_s526_attach(a4l_dev_t *dev, a4l_lnkdesc_t *arg)
+{
+       int io_base;
+       int i;
+       int err = 0;
+       int n;
+       union cmReg cmReg;
+
+       if (arg->opts == NULL || arg->opts_size < sizeof(unsigned long)) {
+               a4l_warn(dev,
+                        "dev_s526_attach: no attach options specified."
+                        "taking default options (addr=0x%x)\n",
+                        S526_DEFAULT_ADDRESS);
+
+               io_base = S526_DEFAULT_ADDRESS;
+       } else {
+               io_base = ((unsigned long *)arg->opts)[0];
+       }
+
+       if (!request_region(io_base, S526_IOSIZE, "s526")) {
+               a4l_err(dev, "dev_s526_attach: I/O port conflict");
+               return -EIO;
+       }
+
+       /* Allocate the subdevice structures. */
+       for (i = 0; i < 4; i++) {
+               a4l_subd_t *subd = a4l_alloc_subd(setup_subds[i].sizeof_priv,
+                                                 setup_subds[i].setup_func);
+
+               if (subd == NULL)
+                       return -ENOMEM;
+
+               err = a4l_add_subd(dev, subd);
+               if (err != i)
+                       return err;
+       }
+
+       devpriv->io_base = io_base;
+
+       a4l_info(dev, "dev_s526_attach: atached (address = 0x%x)\n", io_base);
+
+       return 0;
+
+#if 0
+       /* Example of Counter Application */
+       /* One-shot (software trigger) */
+       cmReg.reg.coutSource = 0;        /* out RCAP */
+       cmReg.reg.coutPolarity = 1;      /* Polarity inverted */
+       cmReg.reg.autoLoadResetRcap = 1; /* Auto load 0:disabled, 1:enabled */
+       cmReg.reg.hwCtEnableSource = 3;  /* NOT RCAP */
+       cmReg.reg.ctEnableCtrl = 2;      /* Hardware */
+       cmReg.reg.clockSource = 2;       /* Internal */
+       cmReg.reg.countDir = 1;          /* Down */
+       cmReg.reg.countDirCtrl = 1;      /* Software */
+       cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
+       cmReg.reg.preloadRegSel = 0;      /* PR0 */
+       cmReg.reg.reserved = 0;
+
+       outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+       outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+       outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+       outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the 
counter */
+       outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the 
counter from PR0 */
+
+       outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP 
(fires one-shot) */
+
+#else
+
+       /*  Set Counter Mode Register */
+       cmReg.reg.coutSource = 0;         /* out RCAP */
+       cmReg.reg.coutPolarity = 0;       /* Polarity inverted */
+       cmReg.reg.autoLoadResetRcap = 0;  /* Auto load disabled */
+       cmReg.reg.hwCtEnableSource = 2;   /* NOT RCAP */
+       cmReg.reg.ctEnableCtrl = 1;       /* 1: Software,  >1 : Hardware */
+       cmReg.reg.clockSource = 3;        /* x4 */
+       cmReg.reg.countDir = 0;           /* up */
+       cmReg.reg.countDirCtrl = 0;       /* quadrature */
+       cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
+       cmReg.reg.preloadRegSel = 0;      /* PR0 */
+       cmReg.reg.reserved = 0;
+
+       n = 0;
+       /* a4l_info(dev, "Mode reg=0x%04x, 0x%04lx\n", */
+       /*      cmReg.value, ADDR_CHAN_REG(REG_C0M, n)); */
+       outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
+       /* udelay(1000); */
+       /* a4l_info(dev, "Read back mode reg=0x%04x\n", */
+       /*      inw(ADDR_CHAN_REG(REG_C0M, n))); */
+
+       /*  Load the pre-load register high word */
+
+        /* value = (short) (0x55); */
+       /* outw(value, ADDR_CHAN_REG(REG_C0H, n)) */
+
+       /*  Load the pre-load register low word */
+       /* value = (short)(0xaa55); */
+       /* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
+
+       /*  Write the Counter Control Register */
+       /* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
+
+       /*  Reset the counter if it is software preload */
+       if (cmReg.reg.autoLoadResetRcap == 0) {
+               outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); /* Reset the counter */
+               outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); /* Load the counter 
from PR0 */
+       }
+
+       outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
+       /* udelay(1000); */
+       /* a4l_info(dev, "Read back mode reg=0x%04x\n", */
+       /*      inw(ADDR_CHAN_REG(REG_C0M, n))); */
+
+#endif
+       /* a4l_info(dev, "Current registres:\n"); */
+
+       /* for (i = 0; i < S526_NUM_PORTS; i++) { */
+       /*      a4l_info(dev, "0x%02lx: 0x%04x\n", */
+       /*              ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i]))); 
*/
+       /* } */
+       return 0;
+}
+
+static int dev_s526_detach(a4l_dev_t *dev)
+{
+       int err = 0;
+
+       if (devpriv->io_base != 0)
+               release_region(devpriv->io_base, S526_IOSIZE);
+
+       return err;
+}
+
+static a4l_drv_t drv_s526 = {
+       .owner = THIS_MODULE,
+       .board_name = BOARD_NAME,
+       .attach = dev_s526_attach,
+       .detach = dev_s526_detach,
+       .privdata_size = sizeof(s526_priv_t),
+};
+
+static int __init drv_s526_init(void)
+{
+       return a4l_register_drv(&drv_s526);
+}
+
+static void __exit drv_s526_cleanup(void)
+{
+       a4l_unregister_drv(&drv_s526);
+}
+
+MODULE_DESCRIPTION("Analogy driver for Sensoray Model 526 board.");
+MODULE_LICENSE("GPL");
+
+module_init(drv_s526_init);
+module_exit(drv_s526_cleanup);


_______________________________________________
Xenomai-git mailing list
Xenomai-git@gna.org
https://mail.gna.org/listinfo/xenomai-git

Reply via email to