]> Pileus Git - ~andy/linux/blob - drivers/staging/comedi/drivers/pcmda12.c
staging: comedi: conditionally build in PCI driver support
[~andy/linux] / drivers / staging / comedi / drivers / pcmda12.c
1 /*
2     comedi/drivers/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     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22 /*
23 Driver: pcmda12
24 Description: A driver for the Winsystems PCM-D/A-12
25 Devices: [Winsystems] PCM-D/A-12 (pcmda12)
26 Author: Calin Culianu <calin@ajvar.org>
27 Updated: Fri, 13 Jan 2006 12:01:01 -0500
28 Status: works
29
30 A driver for the relatively straightforward-to-program PCM-D/A-12.
31 This board doesn't support commands, and the only way to set its
32 analog output range is to jumper the board.  As such,
33 comedi_data_write() ignores the range value specified.
34
35 The board uses 16 consecutive I/O addresses starting at the I/O port
36 base address.  Each address corresponds to the LSB then MSB of a
37 particular channel from 0-7.
38
39 Note that the board is not ISA-PNP capable and thus
40 needs the I/O port comedi_config parameter.
41
42 Note that passing a nonzero value as the second config option will
43 enable "simultaneous xfer" mode for this board, in which AO writes
44 will not take effect until a subsequent read of any AO channel.  This
45 is so that one can speed up programming by preloading all AO registers
46 with values before simultaneously setting them to take effect with one
47 read command.
48
49 Configuration Options:
50   [0] - I/O port base address
51   [1] - Do Simultaneous Xfer (see description)
52 */
53
54 #include "../comedidev.h"
55
56 #define CHANS 8
57 #define IOSIZE 16
58 #define LSB(x) ((unsigned char)((x) & 0xff))
59 #define MSB(x) ((unsigned char)((((unsigned short)(x))>>8) & 0xff))
60 #define LSB_PORT(chan) (dev->iobase + (chan)*2)
61 #define MSB_PORT(chan) (LSB_PORT(chan)+1)
62 #define BITS 12
63
64 /* note these have no effect and are merely here for reference..
65    these are configured by jumpering the board! */
66 static const struct comedi_lrange pcmda12_ranges = {
67         3,
68         {
69          UNI_RANGE(5), UNI_RANGE(10), BIP_RANGE(5)
70          }
71 };
72
73 struct pcmda12_private {
74
75         unsigned int ao_readback[CHANS];
76         int simultaneous_xfer_mode;
77 };
78
79 static void zero_chans(struct comedi_device *dev)
80 {                               /* sets up an
81                                    ASIC chip to defaults */
82         int i;
83         for (i = 0; i < CHANS; ++i) {
84 /*      /\* do this as one instruction?? *\/ */
85 /*      outw(0, LSB_PORT(chan)); */
86                 outb(0, LSB_PORT(i));
87                 outb(0, MSB_PORT(i));
88         }
89         inb(LSB_PORT(0));       /* update chans. */
90 }
91
92 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
93                     struct comedi_insn *insn, unsigned int *data)
94 {
95         struct pcmda12_private *devpriv = dev->private;
96         int i;
97         int chan = CR_CHAN(insn->chanspec);
98
99         /* Writing a list of values to an AO channel is probably not
100          * very useful, but that's how the interface is defined. */
101         for (i = 0; i < insn->n; ++i) {
102
103 /*      /\* do this as one instruction?? *\/ */
104 /*      outw(data[i], LSB_PORT(chan)); */
105
106                 /* Need to do this as two instructions due to 8-bit bus?? */
107                 /*  first, load the low byte */
108                 outb(LSB(data[i]), LSB_PORT(chan));
109                 /*  next, write the high byte */
110                 outb(MSB(data[i]), MSB_PORT(chan));
111
112                 /* save shadow register */
113                 devpriv->ao_readback[chan] = data[i];
114
115                 if (!devpriv->simultaneous_xfer_mode)
116                         inb(LSB_PORT(chan));
117         }
118
119         /* return the number of samples written */
120         return i;
121 }
122
123 /* AO subdevices should have a read insn as well as a write insn.
124
125    Usually this means copying a value stored in devpriv->ao_readback.
126    However, since this driver supports simultaneous xfer then sometimes
127    this function actually accomplishes work.
128
129    Simultaneaous xfer mode is accomplished by loading ALL the values
130    you want for AO in all the channels, then READing off one of the AO
131    registers to initiate the instantaneous simultaneous update of all
132    DAC outputs, which makes all AO channels update simultaneously.
133    This is useful for some control applications, I would imagine.
134 */
135 static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
136                     struct comedi_insn *insn, unsigned int *data)
137 {
138         struct pcmda12_private *devpriv = dev->private;
139         int i;
140         int chan = CR_CHAN(insn->chanspec);
141
142         for (i = 0; i < insn->n; i++) {
143                 if (devpriv->simultaneous_xfer_mode)
144                         inb(LSB_PORT(chan));
145                 /* read back shadow register */
146                 data[i] = devpriv->ao_readback[chan];
147         }
148
149         return i;
150 }
151
152 static int pcmda12_attach(struct comedi_device *dev,
153                           struct comedi_devconfig *it)
154 {
155         struct pcmda12_private *devpriv;
156         struct comedi_subdevice *s;
157         unsigned long iobase;
158         int ret;
159
160         iobase = it->options[0];
161         printk(KERN_INFO
162                "comedi%d: %s: io: %lx %s ", dev->minor, dev->driver->driver_name,
163                iobase, it->options[1] ? "simultaneous xfer mode enabled" : "");
164
165         if (!request_region(iobase, IOSIZE, dev->driver->driver_name)) {
166                 printk("I/O port conflict\n");
167                 return -EIO;
168         }
169         dev->iobase = iobase;
170
171         dev->board_name = dev->driver->driver_name;
172
173         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
174         if (!devpriv)
175                 return -ENOMEM;
176         dev->private = devpriv;
177
178         devpriv->simultaneous_xfer_mode = it->options[1];
179
180         ret = comedi_alloc_subdevices(dev, 1);
181         if (ret)
182                 return ret;
183
184         s = &dev->subdevices[0];
185         s->private = NULL;
186         s->maxdata = (0x1 << BITS) - 1;
187         s->range_table = &pcmda12_ranges;
188         s->type = COMEDI_SUBD_AO;
189         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
190         s->n_chan = CHANS;
191         s->insn_write = &ao_winsn;
192         s->insn_read = &ao_rinsn;
193
194         zero_chans(dev);        /* clear out all the registers, basically */
195
196         printk(KERN_INFO "attached\n");
197
198         return 1;
199 }
200
201 static void pcmda12_detach(struct comedi_device *dev)
202 {
203         if (dev->iobase)
204                 release_region(dev->iobase, IOSIZE);
205 }
206
207 static struct comedi_driver pcmda12_driver = {
208         .driver_name    = "pcmda12",
209         .module         = THIS_MODULE,
210         .attach         = pcmda12_attach,
211         .detach         = pcmda12_detach,
212 };
213 module_comedi_driver(pcmda12_driver);
214
215 MODULE_AUTHOR("Comedi http://www.comedi.org");
216 MODULE_DESCRIPTION("Comedi low-level driver");
217 MODULE_LICENSE("GPL");