--- /dev/null
+/*
+ comedi/drivers/ni_at_ao.c
+ Driver for NI AT-AO-6/10 boards
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000,2002 David A. Schleef <ds@schleef.org>
+
+ This program 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 program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: ni_at_ao.o
+Description: National Instruments AT-AO-6/10
+Devices: [National Instruments] AT-AO-6 (at-ao-6), AT-AO-10 (at-ao-10)
+Author: ds
+
+20
+*/
+/*
+ * Register-level programming information can be found in NI
+ * document 320379.pdf.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/comedidev.h>
+
+
+/* board egisters */
+/* registers with _2_ are accessed when GRP2WR is set in CFG1 */
+
+#define ATAO_SIZE 0x20
+
+#define ATAO_2_DMATCCLR 0x00 /* W 16 */
+#define ATAO_DIN 0x00 /* R 16 */
+#define ATAO_DOUT 0x00 /* W 16 */
+
+#define ATAO_CFG2 0x02 /* W 16 */
+ #define CALLD1 0x8000
+ #define CALLD0 0x4000
+ #define FFRTEN 0x2000
+ #define DAC2S8 0x1000
+ #define DAC2S6 0x0800
+ #define DAC2S4 0x0400
+ #define DAC2S2 0x0200
+ #define DAC2S0 0x0100
+ #define LDAC8 0x0080
+ #define LDAC6 0x0040
+ #define LDAC4 0x0020
+ #define LDAC2 0x0010
+ #define LDAC0 0x0008
+ #define PROMEN 0x0004
+ #define SCLK 0x0002
+ #define SDATA 0x0001
+
+#define ATAO_2_INT1CLR 0x02 /* W 16 */
+
+#define ATAO_CFG3 0x04 /* W 16 */
+ #define DMAMODE 0x0040
+ #define CLKOUT 0x0020
+ #define RCLKEN 0x0010
+ #define DOUTEN2 0x0008
+ #define DOUTEN1 0x0004
+ #define EN2_5V 0x0002
+ #define SCANEN 0x0001
+
+#define ATAO_2_INT2CLR 0x04 /* W 16 */
+
+#define ATAO_82C53_BASE 0x06 /* RW 8 */
+
+#define ATAO_82C53_CNTR1 0x06 /* RW 8 */
+#define ATAO_82C53_CNTR2 0x07 /* RW 8 */
+#define ATAO_82C53_CNTR3 0x08 /* RW 8 */
+#define ATAO_82C53_CNTRCMD 0x09 /* W 8 */
+ #define CNTRSEL1 0x80
+ #define CNTRSEL0 0x40
+ #define RWSEL1 0x20
+ #define RWSEL0 0x10
+ #define MODESEL2 0x08
+ #define MODESEL1 0x04
+ #define MODESEL0 0x02
+ #define BCDSEL 0x01
+ /* read-back command */
+ #define COUNT 0x20
+ #define STATUS 0x10
+ #define CNTR3 0x08
+ #define CNTR2 0x04
+ #define CNTR1 0x02
+ /* status */
+ #define OUT 0x80
+ #define _NULL 0x40
+ #define RW1 0x20
+ #define RW0 0x10
+ #define MODE2 0x08
+ #define MODE1 0x04
+ #define MODE0 0x02
+ #define BCD 0x01
+
+#define ATAO_2_RTSISHFT 0x06 /* W 8 */
+ #define RSI 0x01
+
+#define ATAO_2_RTSISTRB 0x07 /* W 8 */
+
+#define ATAO_CFG1 0x0a /* W 16 */
+ #define EXTINT2EN 0x8000
+ #define EXTINT1EN 0x4000
+ #define CNTINT2EN 0x2000
+ #define CNTINT1EN 0x1000
+ #define TCINTEN 0x0800
+ #define CNT1SRC 0x0400
+ #define CNT2SRC 0x0200
+ #define FIFOEN 0x0100
+ #define GRP2WR 0x0080
+ #define EXTUPDEN 0x0040
+ #define DMARQ 0x0020
+ #define DMAEN 0x0010
+ #define CH_mask 0x000f
+#define ATAO_STATUS 0x0a /* R 16 */
+ #define FH 0x0040
+ #define FE 0x0020
+ #define FF 0x0010
+ #define INT2 0x0008
+ #define INT1 0x0004
+ #define TCINT 0x0002
+ #define PROMOUT 0x0001
+
+#define ATAO_FIFO_WRITE 0x0c /* W 16 */
+#define ATAO_FIFO_CLEAR 0x0c /* R 16 */
+#define ATAO_DACn(x) (0x0c + 2*(x)) /* W */
+
+/*
+ * Board descriptions for two imaginary boards. Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+typedef struct atao_board_struct{
+ char *name;
+ int n_ao_chans;
+}atao_board;
+static atao_board atao_boards[] = {
+ {
+ name: "ai-ao-6",
+ n_ao_chans: 6,
+ },
+ {
+ name: "ai-ao-10",
+ n_ao_chans: 10,
+ },
+};
+#define thisboard ((atao_board *)dev->board_ptr)
+
+typedef struct{
+ unsigned short cfg1;
+ unsigned short cfg2;
+ unsigned short cfg3;
+
+ /* Used for AO readback */
+ lsampl_t ao_readback[10];
+}atao_private;
+#define devpriv ((atao_private *)dev->private)
+
+static int atao_attach(comedi_device *dev,comedi_devconfig *it);
+static int atao_detach(comedi_device *dev);
+static comedi_driver driver_atao={
+ driver_name: "ni_at_ao",
+ module: THIS_MODULE,
+ attach: atao_attach,
+ detach: atao_detach,
+ board_name: atao_boards,
+ offset: sizeof(atao_board),
+ num_names: sizeof(atao_boards) / sizeof(atao_board),
+};
+COMEDI_INITCLEANUP(driver_atao);
+
+static void atao_reset(comedi_device *dev);
+
+static int atao_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
+static int atao_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
+static int atao_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+static int atao_dio_insn_config(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+static int atao_calib_insn_read(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+static int atao_calib_insn_write(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+
+static int atao_attach(comedi_device *dev,comedi_devconfig *it)
+{
+ comedi_subdevice *s;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+ if(iobase==0)iobase = 0x1c0;
+
+ printk("comedi%d: ni_at_ao: 0x%04lx",dev->minor,iobase);
+
+ if(check_region(iobase, ATAO_SIZE) < 0){
+ printk(" I/O port conflict\n");
+ return -EIO;
+ }
+ request_region(iobase, ATAO_SIZE, "ni_at_ao");
+ dev->iobase = iobase;
+
+ //dev->board_ptr = atao_probe(dev);
+
+ dev->board_name = thisboard->name;
+
+ if(alloc_private(dev,sizeof(atao_private))<0)
+ return -ENOMEM;
+
+ dev->n_subdevices=4;
+ if(alloc_subdevices(dev)<0)
+ return -ENOMEM;
+
+ s=dev->subdevices+0;
+ /* analog output subdevice */
+ s->type=COMEDI_SUBD_AO;
+ s->subdev_flags=SDF_WRITEABLE;
+ s->n_chan=thisboard->n_ao_chans;
+ s->maxdata=(1<<12)-1;
+ s->range_table=&range_bipolar10;
+ s->insn_write = &atao_ao_winsn;
+ s->insn_read = &atao_ao_rinsn;
+
+ s=dev->subdevices+1;
+ /* digital i/o subdevice */
+ s->type=COMEDI_SUBD_DIO;
+ s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;
+ s->n_chan=8;
+ s->maxdata=1;
+ s->range_table=&range_digital;
+ s->insn_bits = atao_dio_insn_bits;
+ s->insn_config = atao_dio_insn_config;
+
+ s=dev->subdevices+2;
+ /* caldac subdevice */
+ s->type=COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_WRITEABLE|SDF_INTERNAL;
+ s->n_chan = 21;
+ s->maxdata = 0xff;
+ s->insn_read = atao_calib_insn_read;
+ s->insn_write = atao_calib_insn_write;
+
+ s=dev->subdevices+3;
+ /* eeprom subdevice */
+ //s->type=COMEDI_SUBD_EEPROM;
+ s->type=COMEDI_SUBD_UNUSED;
+
+ atao_reset(dev);
+
+ printk("\n");
+
+ return 0;
+}
+
+
+static int atao_detach(comedi_device *dev)
+{
+ printk("comedi%d: atao: remove\n",dev->minor);
+
+ if(dev->iobase)
+ release_region(dev->iobase, ATAO_SIZE);
+
+ return 0;
+}
+
+
+static void atao_reset(comedi_device *dev)
+{
+ /* This is the reset sequence described in the manual */
+
+ devpriv->cfg1 = 0;
+ outw(devpriv->cfg1, dev->iobase + ATAO_CFG1);
+
+ outb(RWSEL0 | MODESEL2, dev->iobase + ATAO_82C53_CNTRCMD);
+ outb(0x03, dev->iobase + ATAO_82C53_CNTR1);
+ outb(CNTRSEL0 | RWSEL0 | MODESEL2, dev->iobase + ATAO_82C53_CNTRCMD);
+
+ devpriv->cfg2 = 0;
+ outw(devpriv->cfg2, dev->iobase + ATAO_CFG2);
+
+ devpriv->cfg3 = 0;
+ outw(devpriv->cfg3, dev->iobase + ATAO_CFG3);
+
+ inw(dev->iobase + ATAO_FIFO_CLEAR);
+
+ devpriv->cfg1 = GRP2WR;
+ outw(devpriv->cfg1, dev->iobase + ATAO_CFG1);
+
+ outw(0, dev->iobase + ATAO_2_INT1CLR);
+ outw(0, dev->iobase + ATAO_2_INT2CLR);
+ outw(0, dev->iobase + ATAO_2_DMATCCLR);
+
+ devpriv->cfg1 = 0;
+ outw(devpriv->cfg1, dev->iobase + ATAO_CFG1);
+}
+
+
+static int atao_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for(i=0;i<insn->n;i++){
+ outw(data[i], dev->iobase + ATAO_DACn(chan));
+ devpriv->ao_readback[chan] = data[i];
+ }
+
+ return i;
+}
+
+static int atao_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for(i=0;i<insn->n;i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+static int atao_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ if(insn->n!=2)return -EINVAL;
+
+ if(data[0]){
+ s->state &= ~data[0];
+ s->state |= data[0]&data[1];
+ outw(s->state,dev->iobase + ATAO_DOUT);
+ }
+
+ data[1]=inw(dev->iobase + ATAO_DIN);
+
+ return 2;
+}
+
+static int atao_dio_insn_config(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ int chan=CR_CHAN(insn->chanspec);
+ unsigned int mask, bit;
+
+ if(insn->n!=1)return -EINVAL;
+
+ /* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+
+ mask = (chan < 4) ? 0x0f : 0xf0;
+ bit = (chan < 4) ? DOUTEN1 : DOUTEN2;
+
+ if(data[0]==COMEDI_OUTPUT){
+ s->io_bits |= mask;
+ devpriv->cfg3 |= bit;
+ }else{
+ s->io_bits &= ~mask;
+ devpriv->cfg3 &= ~bit;
+ }
+
+ outw(devpriv->cfg3, dev->iobase + ATAO_CFG3);
+
+ return 1;
+}
+
+
+/*
+ * Figure 2-1 in the manual shows 3 chips labeled DAC8800, which
+ * are 8-channel 8-bit DACs. These are most likely the calibration
+ * DACs. It is not explicitly stated in the manual how to access
+ * the caldacs, but we can guess.
+ */
+static int atao_calib_insn_read(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ int i;
+ for(i=0;i<insn->n;i++){
+ data[i]=0; /* XXX */
+ }
+ return insn->n;
+}
+
+static int atao_calib_insn_write(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ unsigned int bitstring, bit;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ bitstring = ((chan&0x7)<<8)|(insn->data[insn->n-1]&0xff);
+
+ for(bit=1<<(11-1);bit;bit>>=1){
+ outw(devpriv->cfg2 | ((bit&bitstring)?SDATA:0), dev->iobase + ATAO_CFG2);
+ outw(devpriv->cfg2 | SCLK | ((bit&bitstring)?SDATA:0), dev->iobase + ATAO_CFG2);
+ }
+ /* strobe the appropriate caldac */
+ outw(devpriv->cfg2 | (((chan>>3) + 1)<<14), dev->iobase + ATAO_CFG2);
+ outw(devpriv->cfg2, dev->iobase + ATAO_CFG2);
+
+ return insn->n;
+}
+
+