3 * Comedi driver for ADLink 6208 series cards
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
21 * Description: ADLink PCI-6208/6216 Series Multi-channel Analog Output Cards
22 * Devices: (ADLink) PCI-6208 [adl_pci6208]
23 * (ADLink) PCI-6216 [adl_pci6216]
24 * Author: nsyeow <nsyeow@pd.jaring.my>
25 * Updated: Fri, 30 Jan 2004 14:44:27 +0800
28 * Configuration Options: not applicable, uses PCI auto config
31 #include <linux/module.h>
32 #include <linux/delay.h>
33 #include <linux/pci.h>
35 #include "../comedidev.h"
38 * PCI-6208/6216-GL register map
40 #define PCI6208_AO_CONTROL(x) (0x00 + (2 * (x)))
41 #define PCI6208_AO_STATUS 0x00
42 #define PCI6208_AO_STATUS_DATA_SEND (1 << 0)
43 #define PCI6208_DIO 0x40
44 #define PCI6208_DIO_DO_MASK (0x0f)
45 #define PCI6208_DIO_DO_SHIFT (0)
46 #define PCI6208_DIO_DI_MASK (0xf0)
47 #define PCI6208_DIO_DI_SHIFT (4)
49 #define PCI6208_MAX_AO_CHANNELS 16
51 enum pci6208_boardid
{
56 struct pci6208_board
{
61 static const struct pci6208_board pci6208_boards
[] = {
63 .name
= "adl_pci6208",
67 .name
= "adl_pci6216",
72 struct pci6208_private
{
73 unsigned int ao_readback
[PCI6208_MAX_AO_CHANNELS
];
76 static int pci6208_ao_eoc(struct comedi_device
*dev
,
77 struct comedi_subdevice
*s
,
78 struct comedi_insn
*insn
,
79 unsigned long context
)
83 status
= inw(dev
->iobase
+ PCI6208_AO_STATUS
);
84 if ((status
& PCI6208_AO_STATUS_DATA_SEND
) == 0)
89 static int pci6208_ao_insn_write(struct comedi_device
*dev
,
90 struct comedi_subdevice
*s
,
91 struct comedi_insn
*insn
,
94 struct pci6208_private
*devpriv
= dev
->private;
95 unsigned int chan
= CR_CHAN(insn
->chanspec
);
96 unsigned int val
= devpriv
->ao_readback
[chan
];
100 for (i
= 0; i
< insn
->n
; i
++) {
103 /* D/A transfer rate is 2.2us */
104 ret
= comedi_timeout(dev
, s
, insn
, pci6208_ao_eoc
, 0);
108 /* the hardware expects two's complement values */
109 outw(comedi_offset_munge(s
, val
),
110 dev
->iobase
+ PCI6208_AO_CONTROL(chan
));
112 devpriv
->ao_readback
[chan
] = val
;
117 static int pci6208_ao_insn_read(struct comedi_device
*dev
,
118 struct comedi_subdevice
*s
,
119 struct comedi_insn
*insn
,
122 struct pci6208_private
*devpriv
= dev
->private;
123 unsigned int chan
= CR_CHAN(insn
->chanspec
);
126 for (i
= 0; i
< insn
->n
; i
++)
127 data
[i
] = devpriv
->ao_readback
[chan
];
132 static int pci6208_di_insn_bits(struct comedi_device
*dev
,
133 struct comedi_subdevice
*s
,
134 struct comedi_insn
*insn
,
139 val
= inw(dev
->iobase
+ PCI6208_DIO
);
140 val
= (val
& PCI6208_DIO_DI_MASK
) >> PCI6208_DIO_DI_SHIFT
;
147 static int pci6208_do_insn_bits(struct comedi_device
*dev
,
148 struct comedi_subdevice
*s
,
149 struct comedi_insn
*insn
,
152 if (comedi_dio_update_state(s
, data
))
153 outw(s
->state
, dev
->iobase
+ PCI6208_DIO
);
160 static int pci6208_auto_attach(struct comedi_device
*dev
,
161 unsigned long context
)
163 struct pci_dev
*pcidev
= comedi_to_pci_dev(dev
);
164 const struct pci6208_board
*boardinfo
= NULL
;
165 struct pci6208_private
*devpriv
;
166 struct comedi_subdevice
*s
;
170 if (context
< ARRAY_SIZE(pci6208_boards
))
171 boardinfo
= &pci6208_boards
[context
];
174 dev
->board_ptr
= boardinfo
;
175 dev
->board_name
= boardinfo
->name
;
177 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
181 ret
= comedi_pci_enable(dev
);
184 dev
->iobase
= pci_resource_start(pcidev
, 2);
186 ret
= comedi_alloc_subdevices(dev
, 3);
190 s
= &dev
->subdevices
[0];
191 /* analog output subdevice */
192 s
->type
= COMEDI_SUBD_AO
;
193 s
->subdev_flags
= SDF_WRITABLE
;
194 s
->n_chan
= boardinfo
->ao_chans
;
196 s
->range_table
= &range_bipolar10
;
197 s
->insn_write
= pci6208_ao_insn_write
;
198 s
->insn_read
= pci6208_ao_insn_read
;
200 s
= &dev
->subdevices
[1];
201 /* digital input subdevice */
202 s
->type
= COMEDI_SUBD_DI
;
203 s
->subdev_flags
= SDF_READABLE
;
206 s
->range_table
= &range_digital
;
207 s
->insn_bits
= pci6208_di_insn_bits
;
209 s
= &dev
->subdevices
[2];
210 /* digital output subdevice */
211 s
->type
= COMEDI_SUBD_DO
;
212 s
->subdev_flags
= SDF_WRITABLE
;
215 s
->range_table
= &range_digital
;
216 s
->insn_bits
= pci6208_do_insn_bits
;
219 * Get the read back signals from the digital outputs
220 * and save it as the initial state for the subdevice.
222 val
= inw(dev
->iobase
+ PCI6208_DIO
);
223 val
= (val
& PCI6208_DIO_DO_MASK
) >> PCI6208_DIO_DO_SHIFT
;
229 static struct comedi_driver adl_pci6208_driver
= {
230 .driver_name
= "adl_pci6208",
231 .module
= THIS_MODULE
,
232 .auto_attach
= pci6208_auto_attach
,
233 .detach
= comedi_pci_disable
,
236 static int adl_pci6208_pci_probe(struct pci_dev
*dev
,
237 const struct pci_device_id
*id
)
239 return comedi_pci_auto_config(dev
, &adl_pci6208_driver
,
243 static const struct pci_device_id adl_pci6208_pci_table
[] = {
244 { PCI_VDEVICE(ADLINK
, 0x6208), BOARD_PCI6208
},
245 { PCI_VDEVICE(ADLINK
, 0x6216), BOARD_PCI6216
},
248 MODULE_DEVICE_TABLE(pci
, adl_pci6208_pci_table
);
250 static struct pci_driver adl_pci6208_pci_driver
= {
251 .name
= "adl_pci6208",
252 .id_table
= adl_pci6208_pci_table
,
253 .probe
= adl_pci6208_pci_probe
,
254 .remove
= comedi_pci_auto_unconfig
,
256 module_comedi_pci_driver(adl_pci6208_driver
, adl_pci6208_pci_driver
);
258 MODULE_AUTHOR("Comedi http://www.comedi.org");
259 MODULE_DESCRIPTION("Comedi driver for ADLink 6208 series cards");
260 MODULE_LICENSE("GPL");