Merge remote-tracking branch 'asoc/fix/max98357a' into asoc-linus
[linux-2.6-block.git] / drivers / staging / comedi / drivers / rti800.c
CommitLineData
fc59905e 1/*
50f7ee9f
HS
2 * comedi/drivers/rti800.c
3 * Hardware driver for Analog Devices RTI-800/815 board
4 *
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 1998 David A. Schleef <ds@schleef.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.
fc59905e 17 */
50f7ee9f 18
fc59905e 19/*
50f7ee9f
HS
20 * Driver: rti800
21 * Description: Analog Devices RTI-800/815
9001ea3c 22 * Devices: [Analog Devices] RTI-800 (rti800), RTI-815 (rti815)
50f7ee9f
HS
23 * Author: David A. Schleef <ds@schleef.org>
24 * Status: unknown
25 * Updated: Fri, 05 Sep 2008 14:50:44 +0100
26 *
27 * Configuration options:
28 * [0] - I/O port base address
29 * [1] - IRQ (not supported / unused)
30 * [2] - A/D mux/reference (number of channels)
31 * 0 = differential
32 * 1 = pseudodifferential (common)
33 * 2 = single-ended
34 * [3] - A/D range
35 * 0 = [-10,10]
36 * 1 = [-5,5]
37 * 2 = [0,10]
38 * [4] - A/D encoding
39 * 0 = two's complement
40 * 1 = straight binary
41 * [5] - DAC 0 range
42 * 0 = [-10,10]
43 * 1 = [0,10]
44 * [6] - DAC 0 encoding
45 * 0 = two's complement
46 * 1 = straight binary
47 * [7] - DAC 1 range (same as DAC 0)
48 * [8] - DAC 1 encoding (same as DAC 0)
49 */
fc59905e 50
ce157f80
HS
51#include <linux/module.h>
52#include <linux/delay.h>
25436dc9 53#include <linux/interrupt.h>
fc59905e
DS
54#include "../comedidev.h"
55
fc59905e 56/*
096e490a 57 * Register map
fc59905e 58 */
096e490a
HS
59#define RTI800_CSR 0x00
60#define RTI800_CSR_BUSY (1 << 7)
61#define RTI800_CSR_DONE (1 << 6)
62#define RTI800_CSR_OVERRUN (1 << 5)
63#define RTI800_CSR_TCR (1 << 4)
64#define RTI800_CSR_DMA_ENAB (1 << 3)
65#define RTI800_CSR_INTR_TC (1 << 2)
66#define RTI800_CSR_INTR_EC (1 << 1)
67#define RTI800_CSR_INTR_OVRN (1 << 0)
68#define RTI800_MUXGAIN 0x01
69#define RTI800_CONVERT 0x02
70#define RTI800_ADCLO 0x03
71#define RTI800_ADCHI 0x04
72#define RTI800_DAC0LO 0x05
73#define RTI800_DAC0HI 0x06
74#define RTI800_DAC1LO 0x07
75#define RTI800_DAC1HI 0x08
76#define RTI800_CLRFLAGS 0x09
77#define RTI800_DI 0x0a
78#define RTI800_DO 0x0b
79#define RTI800_9513A_DATA 0x0c
80#define RTI800_9513A_CNTRL 0x0d
81#define RTI800_9513A_STATUS 0x0d
82
9e11a830
HS
83static const struct comedi_lrange range_rti800_ai_10_bipolar = {
84 4, {
85 BIP_RANGE(10),
86 BIP_RANGE(1),
87 BIP_RANGE(0.1),
88 BIP_RANGE(0.02)
89 }
fc59905e 90};
0a85b6f0 91
9e11a830
HS
92static const struct comedi_lrange range_rti800_ai_5_bipolar = {
93 4, {
94 BIP_RANGE(5),
95 BIP_RANGE(0.5),
96 BIP_RANGE(0.05),
97 BIP_RANGE(0.01)
98 }
fc59905e 99};
0a85b6f0 100
9e11a830
HS
101static const struct comedi_lrange range_rti800_ai_unipolar = {
102 4, {
103 UNI_RANGE(10),
104 UNI_RANGE(1),
105 UNI_RANGE(0.1),
106 UNI_RANGE(0.02)
107 }
fc59905e
DS
108};
109
630a334b
HS
110static const struct comedi_lrange *const rti800_ai_ranges[] = {
111 &range_rti800_ai_10_bipolar,
112 &range_rti800_ai_5_bipolar,
113 &range_rti800_ai_unipolar,
114};
115
116static const struct comedi_lrange *const rti800_ao_ranges[] = {
117 &range_bipolar10,
118 &range_unipolar10,
119};
120
1de6a6fd 121struct rti800_board {
fc59905e
DS
122 const char *name;
123 int has_ao;
1de6a6fd
BP
124};
125
5c7bfad8
HS
126static const struct rti800_board rti800_boardtypes[] = {
127 {
128 .name = "rti800",
5c7bfad8
HS
129 }, {
130 .name = "rti815",
131 .has_ao = 1,
132 },
133};
134
9335f261 135struct rti800_private {
c7c1161d 136 bool adc_2comp;
853fc892 137 bool dac_2comp[2];
9ced1de6 138 const struct comedi_lrange *ao_range_type_list[2];
0546f777 139 unsigned char muxgain_bits;
9335f261 140};
fc59905e 141
8c5bd90e
HS
142static int rti800_ai_eoc(struct comedi_device *dev,
143 struct comedi_subdevice *s,
144 struct comedi_insn *insn,
145 unsigned long context)
929b3432
HS
146{
147 unsigned char status;
929b3432 148
8c5bd90e
HS
149 status = inb(dev->iobase + RTI800_CSR);
150 if (status & RTI800_CSR_OVERRUN) {
151 outb(0, dev->iobase + RTI800_CLRFLAGS);
152 return -EOVERFLOW;
929b3432 153 }
8c5bd90e
HS
154 if (status & RTI800_CSR_DONE)
155 return 0;
156 return -EBUSY;
929b3432 157}
fc59905e 158
0a85b6f0
MT
159static int rti800_ai_insn_read(struct comedi_device *dev,
160 struct comedi_subdevice *s,
0546f777
HS
161 struct comedi_insn *insn,
162 unsigned int *data)
fc59905e 163{
9a1a6cf8 164 struct rti800_private *devpriv = dev->private;
ee352696
HS
165 unsigned int chan = CR_CHAN(insn->chanspec);
166 unsigned int gain = CR_RANGE(insn->chanspec);
0546f777 167 unsigned char muxgain_bits;
929b3432
HS
168 int ret;
169 int i;
fc59905e
DS
170
171 inb(dev->iobase + RTI800_ADCHI);
172 outb(0, dev->iobase + RTI800_CLRFLAGS);
173
174 muxgain_bits = chan | (gain << 5);
175 if (muxgain_bits != devpriv->muxgain_bits) {
176 devpriv->muxgain_bits = muxgain_bits;
177 outb(devpriv->muxgain_bits, dev->iobase + RTI800_MUXGAIN);
0546f777 178 /*
096e490a 179 * Without a delay here, the RTI_CSR_OVERRUN bit
0546f777
HS
180 * gets set, and you will have an error.
181 */
fc59905e 182 if (insn->n > 0) {
0546f777
HS
183 int delay = (gain == 0) ? 10 :
184 (gain == 1) ? 20 :
185 (gain == 2) ? 40 : 80;
186
187 udelay(delay);
fc59905e
DS
188 }
189 }
190
191 for (i = 0; i < insn->n; i++) {
192 outb(0, dev->iobase + RTI800_CONVERT);
8c5bd90e
HS
193
194 ret = comedi_timeout(dev, s, insn, rti800_ai_eoc, 0);
929b3432
HS
195 if (ret)
196 return ret;
197
fc59905e 198 data[i] = inb(dev->iobase + RTI800_ADCLO);
aac49c34 199 data[i] |= (inb(dev->iobase + RTI800_ADCHI) & 0xf) << 8;
fc59905e 200
c7c1161d 201 if (devpriv->adc_2comp)
fc59905e 202 data[i] ^= 0x800;
fc59905e
DS
203 }
204
0181ae77 205 return insn->n;
fc59905e
DS
206}
207
0a85b6f0
MT
208static int rti800_ao_insn_write(struct comedi_device *dev,
209 struct comedi_subdevice *s,
853fc892
HS
210 struct comedi_insn *insn,
211 unsigned int *data)
fc59905e 212{
9a1a6cf8 213 struct rti800_private *devpriv = dev->private;
ee352696 214 unsigned int chan = CR_CHAN(insn->chanspec);
853fc892
HS
215 int reg_lo = chan ? RTI800_DAC1LO : RTI800_DAC0LO;
216 int reg_hi = chan ? RTI800_DAC1HI : RTI800_DAC0HI;
fc59905e
DS
217 int i;
218
219 for (i = 0; i < insn->n; i++) {
6dc125d2
HS
220 unsigned int val = data[i];
221
35e769c4 222 s->readback[chan] = val;
6dc125d2 223
853fc892
HS
224 if (devpriv->dac_2comp[chan])
225 val ^= 0x800;
226
227 outb(val & 0xff, dev->iobase + reg_lo);
228 outb((val >> 8) & 0xff, dev->iobase + reg_hi);
fc59905e 229 }
853fc892 230
853fc892 231 return insn->n;
fc59905e
DS
232}
233
0a85b6f0
MT
234static int rti800_di_insn_bits(struct comedi_device *dev,
235 struct comedi_subdevice *s,
a4b279ba
HS
236 struct comedi_insn *insn,
237 unsigned int *data)
fc59905e 238{
fc59905e 239 data[1] = inb(dev->iobase + RTI800_DI);
a2714e3e 240 return insn->n;
fc59905e
DS
241}
242
0a85b6f0
MT
243static int rti800_do_insn_bits(struct comedi_device *dev,
244 struct comedi_subdevice *s,
bd838bea
HS
245 struct comedi_insn *insn,
246 unsigned int *data)
fc59905e 247{
97f4289a 248 if (comedi_dio_update_state(s, data)) {
fc59905e
DS
249 /* Outputs are inverted... */
250 outb(s->state ^ 0xff, dev->iobase + RTI800_DO);
251 }
252
253 data[1] = s->state;
254
a2714e3e 255 return insn->n;
fc59905e
DS
256}
257
da91b269 258static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
fc59905e 259{
723b68a9 260 const struct rti800_board *board = dev->board_ptr;
9a1a6cf8 261 struct rti800_private *devpriv;
34c43922 262 struct comedi_subdevice *s;
b5fb9e6d 263 int ret;
fc59905e 264
88634cd8 265 ret = comedi_request_region(dev, it->options[0], 0x10);
b5fb9e6d
HS
266 if (ret)
267 return ret;
fc59905e 268
fc59905e
DS
269 outb(0, dev->iobase + RTI800_CSR);
270 inb(dev->iobase + RTI800_ADCHI);
271 outb(0, dev->iobase + RTI800_CLRFLAGS);
272
0bdab509 273 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
c34fa261
HS
274 if (!devpriv)
275 return -ENOMEM;
fc59905e 276
c7c1161d 277 devpriv->adc_2comp = (it->options[4] == 0);
853fc892
HS
278 devpriv->dac_2comp[0] = (it->options[6] == 0);
279 devpriv->dac_2comp[1] = (it->options[8] == 0);
0546f777
HS
280 /* invalid, forces the MUXGAIN register to be set when first used */
281 devpriv->muxgain_bits = 0xff;
fc59905e 282
849d1ea4
HS
283 ret = comedi_alloc_subdevices(dev, 4);
284 if (ret)
285 return ret;
286
13bee03e 287 s = &dev->subdevices[0];
fc59905e 288 /* ai subdevice */
97f67708
HS
289 s->type = COMEDI_SUBD_AI;
290 s->subdev_flags = SDF_READABLE | SDF_GROUND;
291 s->n_chan = (it->options[2] ? 16 : 8);
292 s->insn_read = rti800_ai_insn_read;
293 s->maxdata = 0x0fff;
630a334b
HS
294 s->range_table = (it->options[3] < ARRAY_SIZE(rti800_ai_ranges))
295 ? rti800_ai_ranges[it->options[3]]
296 : &range_unknown;
fc59905e 297
13bee03e 298 s = &dev->subdevices[1];
354c00a6 299 if (board->has_ao) {
fc59905e 300 /* ao subdevice (only on rti815) */
97f67708
HS
301 s->type = COMEDI_SUBD_AO;
302 s->subdev_flags = SDF_WRITABLE;
303 s->n_chan = 2;
97f67708 304 s->maxdata = 0x0fff;
fc59905e 305 s->range_table_list = devpriv->ao_range_type_list;
630a334b
HS
306 devpriv->ao_range_type_list[0] =
307 (it->options[5] < ARRAY_SIZE(rti800_ao_ranges))
308 ? rti800_ao_ranges[it->options[5]]
309 : &range_unknown;
310 devpriv->ao_range_type_list[1] =
311 (it->options[7] < ARRAY_SIZE(rti800_ao_ranges))
312 ? rti800_ao_ranges[it->options[7]]
313 : &range_unknown;
35e769c4 314 s->insn_write = rti800_ao_insn_write;
35e769c4
HS
315
316 ret = comedi_alloc_subdev_readback(s);
317 if (ret)
318 return ret;
fc59905e 319 } else {
97f67708 320 s->type = COMEDI_SUBD_UNUSED;
fc59905e
DS
321 }
322
13bee03e 323 s = &dev->subdevices[2];
fc59905e 324 /* di */
97f67708
HS
325 s->type = COMEDI_SUBD_DI;
326 s->subdev_flags = SDF_READABLE;
327 s->n_chan = 8;
328 s->insn_bits = rti800_di_insn_bits;
329 s->maxdata = 1;
330 s->range_table = &range_digital;
fc59905e 331
13bee03e 332 s = &dev->subdevices[3];
fc59905e 333 /* do */
97f67708
HS
334 s->type = COMEDI_SUBD_DO;
335 s->subdev_flags = SDF_WRITABLE;
336 s->n_chan = 8;
337 s->insn_bits = rti800_do_insn_bits;
338 s->maxdata = 1;
339 s->range_table = &range_digital;
340
341 /*
342 * There is also an Am9513 timer on these boards. This subdevice
343 * is not currently supported.
344 */
fc59905e 345
fc59905e
DS
346 return 0;
347}
348
294f930d 349static struct comedi_driver rti800_driver = {
69d7c49e
HS
350 .driver_name = "rti800",
351 .module = THIS_MODULE,
352 .attach = rti800_attach,
21208519 353 .detach = comedi_legacy_detach,
5c7bfad8
HS
354 .num_names = ARRAY_SIZE(rti800_boardtypes),
355 .board_name = &rti800_boardtypes[0].name,
69d7c49e
HS
356 .offset = sizeof(struct rti800_board),
357};
294f930d 358module_comedi_driver(rti800_driver);
69d7c49e 359
04dc3ea5 360MODULE_DESCRIPTION("Comedi: RTI-800 Multifunction Analog/Digital board");
90f703d3 361MODULE_AUTHOR("Comedi http://www.comedi.org");
90f703d3 362MODULE_LICENSE("GPL");