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