staging: comedi: cb_das16_cs: add whitespace to the subdev init
[linux-2.6-block.git] / drivers / staging / comedi / drivers / cb_das16_cs.c
CommitLineData
f0922ec5
DS
1/*
2 comedi/drivers/das16cs.c
3 Driver for Computer Boards PC-CARD DAS16/16.
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2000, 2001, 2002 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.
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
1b19f53e
HS
22 PCMCIA support code for the is adapted from the dummy_cs.c driver
23 of the Linux PCMCIA Card Services package.
24
25 The initial developer of the original code is David A. Hinds
26 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
27 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
28
f0922ec5
DS
29*/
30/*
31Driver: cb_das16_cs
32Description: Computer Boards PC-CARD DAS16/16
33Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO
34Author: ds
35Updated: Mon, 04 Nov 2002 20:04:21 -0800
36Status: experimental
37
38
39*/
40
25436dc9 41#include <linux/interrupt.h>
5a0e3ad6 42#include <linux/slab.h>
f0922ec5
DS
43#include "../comedidev.h"
44#include <linux/delay.h>
f0922ec5 45
f0922ec5
DS
46#include <pcmcia/cistpl.h>
47#include <pcmcia/ds.h>
48
49#include "8253.h"
50
51#define DAS16CS_SIZE 18
52
53#define DAS16CS_ADC_DATA 0
54#define DAS16CS_DIO_MUX 2
55#define DAS16CS_MISC1 4
56#define DAS16CS_MISC2 6
57#define DAS16CS_CTR0 8
58#define DAS16CS_CTR1 10
59#define DAS16CS_CTR2 12
60#define DAS16CS_CTR_CONTROL 14
61#define DAS16CS_DIO 16
62
3281a63d 63struct das16cs_board {
f0922ec5
DS
64 const char *name;
65 int device_id;
66 int n_ao_chans;
3281a63d 67};
13ab179c 68
3281a63d 69static const struct das16cs_board das16cs_boards[] = {
f0922ec5 70 {
13ab179c
HS
71 .name = "PC-CARD DAS16/16-AO",
72 .device_id = 0x0039,
73 .n_ao_chans = 2,
74 }, {
75 .name = "PCM-DAS16s/16",
76 .device_id = 0x4009,
77 .n_ao_chans = 0,
78 }, {
79 .name = "PC-CARD DAS16/16",
80 .device_id = 0x0000, /* unknown */
81 .n_ao_chans = 0,
82 },
f0922ec5
DS
83};
84
bfae362a 85struct das16cs_private {
f0922ec5
DS
86 struct pcmcia_device *link;
87
790c5541 88 unsigned int ao_readback[2];
f0922ec5
DS
89 unsigned short status1;
90 unsigned short status2;
bfae362a 91};
f0922ec5 92
036f4195 93static struct pcmcia_device *cur_dev;
f0922ec5 94
9ced1de6 95static const struct comedi_lrange das16cs_ai_range = { 4, {
0a85b6f0
MT
96 RANGE(-10, 10),
97 RANGE(-5, 5),
98 RANGE(-2.5, 2.5),
99 RANGE(-1.25, 1.25),
100 }
f0922ec5
DS
101};
102
70265d24 103static irqreturn_t das16cs_interrupt(int irq, void *d)
f0922ec5 104{
2696fb57 105 /* struct comedi_device *dev = d; */
f0922ec5
DS
106 return IRQ_HANDLED;
107}
108
0a85b6f0
MT
109static int das16cs_ai_rinsn(struct comedi_device *dev,
110 struct comedi_subdevice *s,
111 struct comedi_insn *insn, unsigned int *data)
f0922ec5 112{
16a8cdfc 113 struct das16cs_private *devpriv = dev->private;
f0922ec5
DS
114 int i;
115 int to;
116 int aref;
117 int range;
118 int chan;
119 static int range_bits[] = { 0x800, 0x000, 0x100, 0x200 };
120
121 chan = CR_CHAN(insn->chanspec);
122 aref = CR_AREF(insn->chanspec);
123 range = CR_RANGE(insn->chanspec);
124
125 outw(chan, dev->iobase + 2);
126
127 devpriv->status1 &= ~0xf320;
128 devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020;
129 outw(devpriv->status1, dev->iobase + 4);
130
131 devpriv->status2 &= ~0xff00;
132 devpriv->status2 |= range_bits[range];
133 outw(devpriv->status2, dev->iobase + 6);
134
135 for (i = 0; i < insn->n; i++) {
136 outw(0, dev->iobase);
137
138#define TIMEOUT 1000
139 for (to = 0; to < TIMEOUT; to++) {
140 if (inw(dev->iobase + 4) & 0x0080)
141 break;
142 }
143 if (to == TIMEOUT) {
f41ad667 144 dev_dbg(dev->class_dev, "cb_das16_cs: ai timeout\n");
f0922ec5
DS
145 return -ETIME;
146 }
147 data[i] = (unsigned short)inw(dev->iobase + 0);
148 }
149
150 return i;
151}
152
da91b269 153static int das16cs_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
f0922ec5
DS
154{
155 return -EINVAL;
156}
157
0a85b6f0
MT
158static int das16cs_ai_cmdtest(struct comedi_device *dev,
159 struct comedi_subdevice *s,
160 struct comedi_cmd *cmd)
f0922ec5
DS
161{
162 int err = 0;
163 int tmp;
164
f0922ec5
DS
165 /* step 1: make sure trigger sources are trivially valid */
166
167 tmp = cmd->start_src;
168 cmd->start_src &= TRIG_NOW;
169 if (!cmd->start_src || tmp != cmd->start_src)
170 err++;
171
172 tmp = cmd->scan_begin_src;
173 cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
174 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
175 err++;
176
177 tmp = cmd->convert_src;
178 cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
179 if (!cmd->convert_src || tmp != cmd->convert_src)
180 err++;
181
182 tmp = cmd->scan_end_src;
183 cmd->scan_end_src &= TRIG_COUNT;
184 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
185 err++;
186
187 tmp = cmd->stop_src;
188 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
189 if (!cmd->stop_src || tmp != cmd->stop_src)
190 err++;
191
192 if (err)
193 return 1;
194
589cabe1
CR
195 /* step 2: make sure trigger sources are unique and
196 * mutually compatible */
f0922ec5 197
828684f9 198 /* note that mutual compatibility is not an issue here */
f0922ec5 199 if (cmd->scan_begin_src != TRIG_TIMER &&
0a85b6f0 200 cmd->scan_begin_src != TRIG_EXT)
f0922ec5
DS
201 err++;
202 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
203 err++;
204 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
205 err++;
206
207 if (err)
208 return 2;
209
210 /* step 3: make sure arguments are trivially compatible */
211
212 if (cmd->start_arg != 0) {
213 cmd->start_arg = 0;
214 err++;
215 }
216#define MAX_SPEED 10000 /* in nanoseconds */
217#define MIN_SPEED 1000000000 /* in nanoseconds */
218
219 if (cmd->scan_begin_src == TRIG_TIMER) {
220 if (cmd->scan_begin_arg < MAX_SPEED) {
221 cmd->scan_begin_arg = MAX_SPEED;
222 err++;
223 }
224 if (cmd->scan_begin_arg > MIN_SPEED) {
225 cmd->scan_begin_arg = MIN_SPEED;
226 err++;
227 }
228 } else {
229 /* external trigger */
230 /* should be level/edge, hi/lo specification here */
231 /* should specify multiple external triggers */
232 if (cmd->scan_begin_arg > 9) {
233 cmd->scan_begin_arg = 9;
234 err++;
235 }
236 }
237 if (cmd->convert_src == TRIG_TIMER) {
238 if (cmd->convert_arg < MAX_SPEED) {
239 cmd->convert_arg = MAX_SPEED;
240 err++;
241 }
242 if (cmd->convert_arg > MIN_SPEED) {
243 cmd->convert_arg = MIN_SPEED;
244 err++;
245 }
246 } else {
247 /* external trigger */
248 /* see above */
249 if (cmd->convert_arg > 9) {
250 cmd->convert_arg = 9;
251 err++;
252 }
253 }
254
255 if (cmd->scan_end_arg != cmd->chanlist_len) {
256 cmd->scan_end_arg = cmd->chanlist_len;
257 err++;
258 }
259 if (cmd->stop_src == TRIG_COUNT) {
260 if (cmd->stop_arg > 0x00ffffff) {
261 cmd->stop_arg = 0x00ffffff;
262 err++;
263 }
264 } else {
265 /* TRIG_NONE */
266 if (cmd->stop_arg != 0) {
267 cmd->stop_arg = 0;
268 err++;
269 }
270 }
271
272 if (err)
273 return 3;
274
275 /* step 4: fix up any arguments */
276
277 if (cmd->scan_begin_src == TRIG_TIMER) {
48b1aff5 278 unsigned int div1 = 0, div2 = 0;
f0922ec5
DS
279
280 tmp = cmd->scan_begin_arg;
281 i8253_cascade_ns_to_timer(100, &div1, &div2,
0a85b6f0
MT
282 &cmd->scan_begin_arg,
283 cmd->flags & TRIG_ROUND_MASK);
f0922ec5
DS
284 if (tmp != cmd->scan_begin_arg)
285 err++;
286 }
287 if (cmd->convert_src == TRIG_TIMER) {
48b1aff5 288 unsigned int div1 = 0, div2 = 0;
f0922ec5
DS
289
290 tmp = cmd->convert_arg;
291 i8253_cascade_ns_to_timer(100, &div1, &div2,
0a85b6f0
MT
292 &cmd->scan_begin_arg,
293 cmd->flags & TRIG_ROUND_MASK);
f0922ec5
DS
294 if (tmp != cmd->convert_arg)
295 err++;
296 if (cmd->scan_begin_src == TRIG_TIMER &&
0a85b6f0
MT
297 cmd->scan_begin_arg <
298 cmd->convert_arg * cmd->scan_end_arg) {
f0922ec5 299 cmd->scan_begin_arg =
0a85b6f0 300 cmd->convert_arg * cmd->scan_end_arg;
f0922ec5
DS
301 err++;
302 }
303 }
304
305 if (err)
306 return 4;
307
308 return 0;
309}
310
0a85b6f0
MT
311static int das16cs_ao_winsn(struct comedi_device *dev,
312 struct comedi_subdevice *s,
313 struct comedi_insn *insn, unsigned int *data)
f0922ec5 314{
16a8cdfc 315 struct das16cs_private *devpriv = dev->private;
f0922ec5
DS
316 int i;
317 int chan = CR_CHAN(insn->chanspec);
318 unsigned short status1;
319 unsigned short d;
320 int bit;
321
322 for (i = 0; i < insn->n; i++) {
323 devpriv->ao_readback[chan] = data[i];
324 d = data[i];
325
326 outw(devpriv->status1, dev->iobase + 4);
5f74ea14 327 udelay(1);
f0922ec5
DS
328
329 status1 = devpriv->status1 & ~0xf;
330 if (chan)
331 status1 |= 0x0001;
332 else
333 status1 |= 0x0008;
334
589cabe1 335/* printk("0x%04x\n",status1);*/
f0922ec5 336 outw(status1, dev->iobase + 4);
5f74ea14 337 udelay(1);
f0922ec5
DS
338
339 for (bit = 15; bit >= 0; bit--) {
340 int b = (d >> bit) & 0x1;
341 b <<= 1;
342/* printk("0x%04x\n",status1 | b | 0x0000);*/
343 outw(status1 | b | 0x0000, dev->iobase + 4);
5f74ea14 344 udelay(1);
f0922ec5
DS
345/* printk("0x%04x\n",status1 | b | 0x0004);*/
346 outw(status1 | b | 0x0004, dev->iobase + 4);
5f74ea14 347 udelay(1);
f0922ec5
DS
348 }
349/* make high both DAC0CS and DAC1CS to load
350 new data and update analog output*/
351 outw(status1 | 0x9, dev->iobase + 4);
352 }
353
354 return i;
355}
356
0a85b6f0
MT
357static int das16cs_ao_rinsn(struct comedi_device *dev,
358 struct comedi_subdevice *s,
359 struct comedi_insn *insn, unsigned int *data)
f0922ec5 360{
16a8cdfc 361 struct das16cs_private *devpriv = dev->private;
f0922ec5
DS
362 int i;
363 int chan = CR_CHAN(insn->chanspec);
364
365 for (i = 0; i < insn->n; i++)
366 data[i] = devpriv->ao_readback[chan];
367
368 return i;
369}
370
0a85b6f0
MT
371static int das16cs_dio_insn_bits(struct comedi_device *dev,
372 struct comedi_subdevice *s,
373 struct comedi_insn *insn, unsigned int *data)
f0922ec5 374{
f0922ec5
DS
375 if (data[0]) {
376 s->state &= ~data[0];
377 s->state |= data[0] & data[1];
378
379 outw(s->state, dev->iobase + 16);
380 }
381
f0922ec5
DS
382 data[1] = inw(dev->iobase + 16);
383
a2714e3e 384 return insn->n;
f0922ec5
DS
385}
386
0a85b6f0
MT
387static int das16cs_dio_insn_config(struct comedi_device *dev,
388 struct comedi_subdevice *s,
389 struct comedi_insn *insn, unsigned int *data)
f0922ec5 390{
16a8cdfc 391 struct das16cs_private *devpriv = dev->private;
f0922ec5
DS
392 int chan = CR_CHAN(insn->chanspec);
393 int bits;
394
395 if (chan < 4)
396 bits = 0x0f;
397 else
398 bits = 0xf0;
399
400 switch (data[0]) {
401 case INSN_CONFIG_DIO_OUTPUT:
402 s->io_bits |= bits;
403 break;
404 case INSN_CONFIG_DIO_INPUT:
405 s->io_bits &= bits;
406 break;
407 case INSN_CONFIG_DIO_QUERY:
408 data[1] =
0a85b6f0 409 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
f0922ec5
DS
410 return insn->n;
411 break;
412 default:
413 return -EINVAL;
414 break;
415 }
416
417 devpriv->status2 &= ~0x00c0;
418 devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0;
419 devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0;
420
421 outw(devpriv->status2, dev->iobase + 6);
422
423 return insn->n;
424}
425
ae7df434
HS
426static const struct das16cs_board *das16cs_probe(struct comedi_device *dev,
427 struct pcmcia_device *link)
428{
429 int i;
430
1efc5d53 431 for (i = 0; i < ARRAY_SIZE(das16cs_boards); i++) {
ae7df434
HS
432 if (das16cs_boards[i].device_id == link->card_id)
433 return das16cs_boards + i;
434 }
435
436 dev_dbg(dev->class_dev, "unknown board!\n");
437
438 return NULL;
439}
440
441static int das16cs_attach(struct comedi_device *dev,
442 struct comedi_devconfig *it)
443{
16a8cdfc 444 const struct das16cs_board *thisboard;
ae7df434
HS
445 struct pcmcia_device *link;
446 struct comedi_subdevice *s;
447 int ret;
ae7df434 448
ae7df434
HS
449 link = cur_dev; /* XXX hack */
450 if (!link)
451 return -EIO;
452
9a4d2115
HS
453 dev->board_ptr = das16cs_probe(dev, link);
454 if (!dev->board_ptr)
455 return -EIO;
456 thisboard = comedi_board(dev);
457
458 dev->board_name = thisboard->name;
459
ae7df434 460 dev->iobase = link->resource[0]->start;
ae7df434 461
ae7df434
HS
462 ret = request_irq(link->irq, das16cs_interrupt,
463 IRQF_SHARED, "cb_das16_cs", dev);
464 if (ret < 0)
465 return ret;
ae7df434
HS
466 dev->irq = link->irq;
467
ae7df434
HS
468 if (alloc_private(dev, sizeof(struct das16cs_private)) < 0)
469 return -ENOMEM;
470
5c416ef3 471 ret = comedi_alloc_subdevices(dev, 3);
ae7df434
HS
472 if (ret)
473 return ret;
474
475 s = dev->subdevices + 0;
476 dev->read_subdev = s;
477 /* analog input subdevice */
cf2592d0
HS
478 s->type = COMEDI_SUBD_AI;
479 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
480 s->n_chan = 16;
481 s->maxdata = 0xffff;
482 s->range_table = &das16cs_ai_range;
483 s->len_chanlist = 16;
484 s->insn_read = das16cs_ai_rinsn;
485 s->do_cmd = das16cs_ai_cmd;
486 s->do_cmdtest = das16cs_ai_cmdtest;
ae7df434
HS
487
488 s = dev->subdevices + 1;
489 /* analog output subdevice */
490 if (thisboard->n_ao_chans) {
cf2592d0
HS
491 s->type = COMEDI_SUBD_AO;
492 s->subdev_flags = SDF_WRITABLE;
493 s->n_chan = thisboard->n_ao_chans;
494 s->maxdata = 0xffff;
495 s->range_table = &range_bipolar10;
496 s->insn_write = &das16cs_ao_winsn;
497 s->insn_read = &das16cs_ao_rinsn;
da7fde2b 498 } else {
cf2592d0 499 s->type = COMEDI_SUBD_UNUSED;
ae7df434
HS
500 }
501
502 s = dev->subdevices + 2;
503 /* digital i/o subdevice */
cf2592d0
HS
504 s->type = COMEDI_SUBD_DIO;
505 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
506 s->n_chan = 8;
507 s->maxdata = 1;
508 s->range_table = &range_digital;
509 s->insn_bits = das16cs_dio_insn_bits;
510 s->insn_config = das16cs_dio_insn_config;
ae7df434 511
1fce3034
HS
512 dev_info(dev->class_dev, "%s: %s, I/O base=0x%04lx, irq=%u\n",
513 dev->driver->driver_name, dev->board_name,
514 dev->iobase, dev->irq);
515
a659643a 516 return 0;
ae7df434
HS
517}
518
519static void das16cs_detach(struct comedi_device *dev)
520{
521 if (dev->irq)
522 free_irq(dev->irq, dev);
523}
524
2f018267
HS
525static struct comedi_driver driver_das16cs = {
526 .driver_name = "cb_das16_cs",
527 .module = THIS_MODULE,
528 .attach = das16cs_attach,
529 .detach = das16cs_detach,
530};
531
55a19b39 532static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev,
55a19b39
DB
533 void *priv_data)
534{
00990e7c 535 if (p_dev->config_index == 0)
55a19b39 536 return -EINVAL;
f0922ec5 537
00990e7c 538 return pcmcia_request_io(p_dev);
55a19b39
DB
539}
540
c2107ff4 541static int das16cs_pcmcia_attach(struct pcmcia_device *link)
55a19b39 542{
55a19b39 543 int ret;
f0922ec5 544
00990e7c
DB
545 /* Do we need to allocate an interrupt? */
546 link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
547
55a19b39 548 ret = pcmcia_loop_config(link, das16cs_pcmcia_config_loop, NULL);
c2107ff4 549 if (ret)
55a19b39 550 goto failed;
f0922ec5 551
eb14120f
DB
552 if (!link->irq)
553 goto failed;
554
1ac71e5a 555 ret = pcmcia_enable_device(link);
55a19b39
DB
556 if (ret)
557 goto failed;
f0922ec5 558
f19d8578 559 cur_dev = link;
f19d8578 560 return 0;
c2107ff4
HS
561
562failed:
563 pcmcia_disable_device(link);
564 return ret;
f19d8578
HS
565}
566
567static void das16cs_pcmcia_detach(struct pcmcia_device *link)
568{
c2107ff4 569 pcmcia_disable_device(link);
c2107ff4 570 cur_dev = NULL;
1b19f53e 571}
f0922ec5 572
2202a5a7 573static const struct pcmcia_device_id das16cs_id_table[] = {
f0922ec5
DS
574 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
575 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
576 PCMCIA_DEVICE_NULL
577};
f0922ec5
DS
578MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
579
e8bed9c6
HS
580static struct pcmcia_driver das16cs_driver = {
581 .name = "cb_das16_cs",
582 .owner = THIS_MODULE,
583 .probe = das16cs_pcmcia_attach,
584 .remove = das16cs_pcmcia_detach,
e8bed9c6 585 .id_table = das16cs_id_table,
f0922ec5
DS
586};
587
3bbb82c4 588static int __init das16cs_init(void)
f0922ec5
DS
589{
590 int ret;
591
64c205f9 592 ret = comedi_driver_register(&driver_das16cs);
f0922ec5
DS
593 if (ret < 0)
594 return ret;
595
64c205f9
HS
596 ret = pcmcia_register_driver(&das16cs_driver);
597 if (ret < 0) {
598 comedi_driver_unregister(&driver_das16cs);
599 return ret;
600 }
601
602 return 0;
f0922ec5 603}
3bbb82c4 604module_init(das16cs_init);
f0922ec5 605
3bbb82c4 606static void __exit das16cs_exit(void)
f0922ec5 607{
6f47503d 608 pcmcia_unregister_driver(&das16cs_driver);
f0922ec5
DS
609 comedi_driver_unregister(&driver_das16cs);
610}
3bbb82c4 611module_exit(das16cs_exit);
16920671
HS
612
613MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
614MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16");
615MODULE_LICENSE("GPL");