]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/pcmda12.c
Merge branch 'next' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[karo-tx-linux.git] / drivers / staging / comedi / drivers / pcmda12.c
1 /*
2  * pcmda12.c
3  * Driver for Winsystems PC-104 based PCM-D/A-12 8-channel AO board.
4  *
5  * COMEDI - Linux Control and Measurement Device Interface
6  * Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18
19 /*
20  * Driver: pcmda12
21  * Description: A driver for the Winsystems PCM-D/A-12
22  * Devices: (Winsystems) PCM-D/A-12 [pcmda12]
23  * Author: Calin Culianu <calin@ajvar.org>
24  * Updated: Fri, 13 Jan 2006 12:01:01 -0500
25  * Status: works
26  *
27  * A driver for the relatively straightforward-to-program PCM-D/A-12.
28  * This board doesn't support commands, and the only way to set its
29  * analog output range is to jumper the board. As such,
30  * comedi_data_write() ignores the range value specified.
31  *
32  * The board uses 16 consecutive I/O addresses starting at the I/O port
33  * base address. Each address corresponds to the LSB then MSB of a
34  * particular channel from 0-7.
35  *
36  * Note that the board is not ISA-PNP capable and thus needs the I/O
37  * port comedi_config parameter.
38  *
39  * Note that passing a nonzero value as the second config option will
40  * enable "simultaneous xfer" mode for this board, in which AO writes
41  * will not take effect until a subsequent read of any AO channel. This
42  * is so that one can speed up programming by preloading all AO registers
43  * with values before simultaneously setting them to take effect with one
44  * read command.
45  *
46  * Configuration Options:
47  *   [0] - I/O port base address
48  *   [1] - Do Simultaneous Xfer (see description)
49  */
50
51 #include <linux/module.h>
52 #include "../comedidev.h"
53
54 /* AI range is not configurable, it's set by jumpers on the board */
55 static const struct comedi_lrange pcmda12_ranges = {
56         3, {
57                 UNI_RANGE(5),
58                 UNI_RANGE(10),
59                 BIP_RANGE(5)
60         }
61 };
62
63 struct pcmda12_private {
64         unsigned int ao_readback[8];
65         int simultaneous_xfer_mode;
66 };
67
68 static int pcmda12_ao_insn_write(struct comedi_device *dev,
69                                  struct comedi_subdevice *s,
70                                  struct comedi_insn *insn,
71                                  unsigned int *data)
72 {
73         struct pcmda12_private *devpriv = dev->private;
74         unsigned int chan = CR_CHAN(insn->chanspec);
75         unsigned int val = devpriv->ao_readback[chan];
76         unsigned long ioreg = dev->iobase + (chan * 2);
77         int i;
78
79         for (i = 0; i < insn->n; ++i) {
80                 val = data[i];
81                 outb(val & 0xff, ioreg);
82                 outb((val >> 8) & 0xff, ioreg + 1);
83
84                 /*
85                  * Initiate transfer if not in simultaneaous xfer
86                  * mode by reading one of the AO registers.
87                  */
88                 if (!devpriv->simultaneous_xfer_mode)
89                         inb(ioreg);
90         }
91         devpriv->ao_readback[chan] = val;
92
93         return insn->n;
94 }
95
96 static int pcmda12_ao_insn_read(struct comedi_device *dev,
97                                 struct comedi_subdevice *s,
98                                 struct comedi_insn *insn,
99                                 unsigned int *data)
100 {
101         struct pcmda12_private *devpriv = dev->private;
102         unsigned int chan = CR_CHAN(insn->chanspec);
103         int i;
104
105         /*
106          * Initiate simultaneaous xfer mode by reading one of the
107          * AO registers. All analog outputs will then be updated.
108          */
109         if (devpriv->simultaneous_xfer_mode)
110                 inb(dev->iobase);
111
112         for (i = 0; i < insn->n; i++)
113                 data[i] = devpriv->ao_readback[chan];
114
115         return insn->n;
116 }
117
118 static void pcmda12_ao_reset(struct comedi_device *dev,
119                              struct comedi_subdevice *s)
120 {
121         int i;
122
123         for (i = 0; i < s->n_chan; ++i) {
124                 outb(0, dev->iobase + (i * 2));
125                 outb(0, dev->iobase + (i * 2) + 1);
126         }
127         /* Initiate transfer by reading one of the AO registers. */
128         inb(dev->iobase);
129 }
130
131 static int pcmda12_attach(struct comedi_device *dev,
132                           struct comedi_devconfig *it)
133 {
134         struct pcmda12_private *devpriv;
135         struct comedi_subdevice *s;
136         int ret;
137
138         ret = comedi_request_region(dev, it->options[0], 0x10);
139         if (ret)
140                 return ret;
141
142         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
143         if (!devpriv)
144                 return -ENOMEM;
145
146         devpriv->simultaneous_xfer_mode = it->options[1];
147
148         ret = comedi_alloc_subdevices(dev, 1);
149         if (ret)
150                 return ret;
151
152         s = &dev->subdevices[0];
153         s->type         = COMEDI_SUBD_AO;
154         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
155         s->n_chan       = 8;
156         s->maxdata      = 0x0fff;
157         s->range_table  = &pcmda12_ranges;
158         s->insn_write   = pcmda12_ao_insn_write;
159         s->insn_read    = pcmda12_ao_insn_read;
160
161         pcmda12_ao_reset(dev, s);
162
163         return 0;
164 }
165
166 static struct comedi_driver pcmda12_driver = {
167         .driver_name    = "pcmda12",
168         .module         = THIS_MODULE,
169         .attach         = pcmda12_attach,
170         .detach         = comedi_legacy_detach,
171 };
172 module_comedi_driver(pcmda12_driver);
173
174 MODULE_AUTHOR("Comedi http://www.comedi.org");
175 MODULE_DESCRIPTION("Comedi low-level driver");
176 MODULE_LICENSE("GPL");