Commit | Line | Data |
---|---|---|
8b93f903 | 1 | /* |
2 | comedi/drivers/adl_pci6208.c | |
3 | ||
4 | Hardware driver for ADLink 6208 series cards: | |
5 | card | voltage output | current output | |
6 | -------------+-------------------+--------------- | |
7 | PCI-6208V | 8 channels | - | |
8 | PCI-6216V | 16 channels | - | |
9 | PCI-6208A | 8 channels | 8 channels | |
10 | ||
11 | COMEDI - Linux Control and Measurement Device Interface | |
12 | Copyright (C) 2000 David A. Schleef <ds@schleef.org> | |
13 | ||
14 | This program is free software; you can redistribute it and/or modify | |
15 | it under the terms of the GNU General Public License as published by | |
16 | the Free Software Foundation; either version 2 of the License, or | |
17 | (at your option) any later version. | |
18 | ||
19 | This program is distributed in the hope that it will be useful, | |
20 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | GNU General Public License for more details. | |
23 | ||
24 | You should have received a copy of the GNU General Public License | |
25 | along with this program; if not, write to the Free Software | |
26 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
27 | */ | |
28 | /* | |
29 | Driver: adl_pci6208 | |
0a1e6c1f HS |
30 | Description: ADLink PCI-6208/6216 Series Multi-channel Analog Output Cards |
31 | Devices: (ADLink) PCI-6208 [adl_pci6208] | |
32 | (ADLink) PCI-6216 [adl_pci6216] | |
8b93f903 | 33 | Author: nsyeow <nsyeow@pd.jaring.my> |
34 | Updated: Fri, 30 Jan 2004 14:44:27 +0800 | |
35 | Status: untested | |
36 | ||
744a8398 | 37 | Configuration Options: not applicable, uses PCI auto config |
8b93f903 | 38 | |
39 | References: | |
40 | - ni_660x.c | |
41 | - adl_pci9111.c copied the entire pci setup section | |
42 | - adl_pci9118.c | |
43 | */ | |
a7c6de4c | 44 | |
8b93f903 | 45 | #include "../comedidev.h" |
8b93f903 | 46 | |
0a1e6c1f HS |
47 | /* |
48 | * ADLINK PCI Device ID's supported by this driver | |
49 | */ | |
50 | #define PCI_DEVICE_ID_PCI6208 0x6208 | |
51 | #define PCI_DEVICE_ID_PCI6216 0x6216 | |
52 | ||
7b6afad1 HS |
53 | /* |
54 | * PCI-6208/6216-GL register map | |
55 | */ | |
56 | #define PCI6208_AO_CONTROL(x) (0x00 + (2 * (x))) | |
57 | #define PCI6208_AO_STATUS 0x00 | |
58 | #define PCI6208_AO_STATUS_DATA_SEND (1 << 0) | |
59 | #define PCI6208_DIO 0x40 | |
60 | #define PCI6208_DIO_DO_MASK (0x0f) | |
61 | #define PCI6208_DIO_DO_SHIFT (0) | |
62 | #define PCI6208_DIO_DI_MASK (0xf0) | |
63 | #define PCI6208_DIO_DI_SHIFT (4) | |
64 | ||
0a1e6c1f | 65 | #define PCI6208_MAX_AO_CHANNELS 16 |
5c67df8b | 66 | |
c1b31c44 | 67 | struct pci6208_board { |
8b93f903 | 68 | const char *name; |
a7c6de4c | 69 | unsigned short dev_id; |
8b93f903 | 70 | int ao_chans; |
c1b31c44 BP |
71 | }; |
72 | ||
73 | static const struct pci6208_board pci6208_boards[] = { | |
8b93f903 | 74 | { |
0a1e6c1f HS |
75 | .name = "adl_pci6208", |
76 | .dev_id = PCI_DEVICE_ID_PCI6208, | |
dcff1681 | 77 | .ao_chans = 8, |
0a1e6c1f HS |
78 | }, { |
79 | .name = "adl_pci6216", | |
80 | .dev_id = PCI_DEVICE_ID_PCI6216, | |
81 | .ao_chans = 16, | |
dcff1681 | 82 | }, |
8b93f903 | 83 | }; |
84 | ||
3d393c86 | 85 | struct pci6208_private { |
5c67df8b | 86 | unsigned int ao_readback[PCI6208_MAX_AO_CHANNELS]; |
3d393c86 | 87 | }; |
8b93f903 | 88 | |
0a85b6f0 MT |
89 | static int pci6208_ao_winsn(struct comedi_device *dev, |
90 | struct comedi_subdevice *s, | |
91 | struct comedi_insn *insn, unsigned int *data) | |
8b93f903 | 92 | { |
949a18d3 | 93 | struct pci6208_private *devpriv = dev->private; |
8366404b | 94 | int chan = CR_CHAN(insn->chanspec); |
8b93f903 | 95 | unsigned long invert = 1 << (16 - 1); |
8366404b HS |
96 | unsigned long value = 0; |
97 | unsigned short status; | |
98 | int i; | |
a7c6de4c | 99 | |
8b93f903 | 100 | for (i = 0; i < insn->n; i++) { |
8366404b HS |
101 | value = data[i] ^ invert; |
102 | ||
8b93f903 | 103 | do { |
8366404b HS |
104 | status = inw(dev->iobase + PCI6208_AO_STATUS); |
105 | } while (status & PCI6208_AO_STATUS_DATA_SEND); | |
106 | ||
107 | outw(value, dev->iobase + PCI6208_AO_CONTROL(chan)); | |
8b93f903 | 108 | } |
8366404b | 109 | devpriv->ao_readback[chan] = value; |
8b93f903 | 110 | |
8366404b | 111 | return insn->n; |
8b93f903 | 112 | } |
113 | ||
0a85b6f0 MT |
114 | static int pci6208_ao_rinsn(struct comedi_device *dev, |
115 | struct comedi_subdevice *s, | |
116 | struct comedi_insn *insn, unsigned int *data) | |
8b93f903 | 117 | { |
949a18d3 | 118 | struct pci6208_private *devpriv = dev->private; |
8b93f903 | 119 | int chan = CR_CHAN(insn->chanspec); |
7c1e5bc7 | 120 | int i; |
8b93f903 | 121 | |
122 | for (i = 0; i < insn->n; i++) | |
123 | data[i] = devpriv->ao_readback[chan]; | |
124 | ||
7c1e5bc7 | 125 | return insn->n; |
8b93f903 | 126 | } |
127 | ||
270809a8 HS |
128 | static int pci6208_di_insn_bits(struct comedi_device *dev, |
129 | struct comedi_subdevice *s, | |
130 | struct comedi_insn *insn, | |
131 | unsigned int *data) | |
fc2536fd | 132 | { |
270809a8 HS |
133 | unsigned int val; |
134 | ||
135 | val = inw(dev->iobase + PCI6208_DIO); | |
136 | val = (val & PCI6208_DIO_DI_MASK) >> PCI6208_DIO_DI_SHIFT; | |
137 | ||
138 | data[1] = val; | |
139 | ||
140 | return insn->n; | |
141 | } | |
142 | ||
143 | static int pci6208_do_insn_bits(struct comedi_device *dev, | |
144 | struct comedi_subdevice *s, | |
145 | struct comedi_insn *insn, | |
146 | unsigned int *data) | |
147 | { | |
148 | unsigned int mask = data[0]; | |
fc2536fd HS |
149 | unsigned int bits = data[1]; |
150 | ||
151 | if (mask) { | |
152 | s->state &= ~mask; | |
270809a8 | 153 | s->state |= (bits & mask); |
fc2536fd HS |
154 | |
155 | outw(s->state, dev->iobase + PCI6208_DIO); | |
156 | } | |
157 | ||
fc2536fd HS |
158 | data[1] = s->state; |
159 | ||
160 | return insn->n; | |
161 | } | |
162 | ||
744a8398 HS |
163 | static const void *pci6208_find_boardinfo(struct comedi_device *dev, |
164 | struct pci_dev *pcidev) | |
8b93f903 | 165 | { |
744a8398 | 166 | const struct pci6208_board *boardinfo; |
8b93f903 | 167 | int i; |
168 | ||
744a8398 HS |
169 | for (i = 0; i < ARRAY_SIZE(pci6208_boards); i++) { |
170 | boardinfo = &pci6208_boards[i]; | |
171 | if (boardinfo->dev_id == pcidev->device) | |
172 | return boardinfo; | |
8b93f903 | 173 | } |
cde6f08a | 174 | return NULL; |
8b93f903 | 175 | } |
176 | ||
744a8398 HS |
177 | static int pci6208_attach_pci(struct comedi_device *dev, |
178 | struct pci_dev *pcidev) | |
c8d87bcc | 179 | { |
744a8398 | 180 | const struct pci6208_board *boardinfo; |
949a18d3 | 181 | struct pci6208_private *devpriv; |
c8d87bcc | 182 | struct comedi_subdevice *s; |
270809a8 | 183 | unsigned int val; |
9d639b6b | 184 | int ret; |
c8d87bcc | 185 | |
744a8398 HS |
186 | comedi_set_hw_dev(dev, &pcidev->dev); |
187 | ||
188 | boardinfo = pci6208_find_boardinfo(dev, pcidev); | |
189 | if (!boardinfo) | |
190 | return -ENODEV; | |
191 | dev->board_ptr = boardinfo; | |
192 | dev->board_name = boardinfo->name; | |
193 | ||
c34fa261 HS |
194 | devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); |
195 | if (!devpriv) | |
196 | return -ENOMEM; | |
197 | dev->private = devpriv; | |
c8d87bcc | 198 | |
3cf05ddb HS |
199 | ret = comedi_pci_enable(pcidev, dev->board_name); |
200 | if (ret) | |
9d639b6b | 201 | return ret; |
f8ec6392 | 202 | dev->iobase = pci_resource_start(pcidev, 2); |
c8d87bcc | 203 | |
270809a8 | 204 | ret = comedi_alloc_subdevices(dev, 3); |
9d639b6b HS |
205 | if (ret) |
206 | return ret; | |
c8d87bcc | 207 | |
1f6115a4 | 208 | s = &dev->subdevices[0]; |
c8d87bcc | 209 | /* analog output subdevice */ |
111b62e4 HS |
210 | s->type = COMEDI_SUBD_AO; |
211 | s->subdev_flags = SDF_WRITABLE; | |
744a8398 | 212 | s->n_chan = boardinfo->ao_chans; |
111b62e4 HS |
213 | s->maxdata = 0xffff; |
214 | s->range_table = &range_bipolar10; | |
215 | s->insn_write = pci6208_ao_winsn; | |
216 | s->insn_read = pci6208_ao_rinsn; | |
c8d87bcc | 217 | |
1f6115a4 | 218 | s = &dev->subdevices[1]; |
270809a8 HS |
219 | /* digital input subdevice */ |
220 | s->type = COMEDI_SUBD_DI; | |
221 | s->subdev_flags = SDF_READABLE; | |
222 | s->n_chan = 4; | |
111b62e4 HS |
223 | s->maxdata = 1; |
224 | s->range_table = &range_digital; | |
270809a8 | 225 | s->insn_bits = pci6208_di_insn_bits; |
111b62e4 | 226 | |
1f6115a4 | 227 | s = &dev->subdevices[2]; |
270809a8 HS |
228 | /* digital output subdevice */ |
229 | s->type = COMEDI_SUBD_DO; | |
230 | s->subdev_flags = SDF_WRITABLE; | |
231 | s->n_chan = 4; | |
232 | s->maxdata = 1; | |
233 | s->range_table = &range_digital; | |
234 | s->insn_bits = pci6208_do_insn_bits; | |
235 | ||
236 | /* | |
237 | * Get the read back signals from the digital outputs | |
238 | * and save it as the initial state for the subdevice. | |
239 | */ | |
240 | val = inw(dev->iobase + PCI6208_DIO); | |
241 | val = (val & PCI6208_DIO_DO_MASK) >> PCI6208_DIO_DO_SHIFT; | |
242 | s->state = val; | |
111b62e4 | 243 | s->io_bits = 0x0f; |
c8d87bcc | 244 | |
b4dda059 HS |
245 | dev_info(dev->class_dev, "%s: %s, I/O base=0x%04lx\n", |
246 | dev->driver->driver_name, dev->board_name, dev->iobase); | |
c8d87bcc | 247 | |
98bcf911 | 248 | return 0; |
c8d87bcc HS |
249 | } |
250 | ||
484ecc95 | 251 | static void pci6208_detach(struct comedi_device *dev) |
c8d87bcc | 252 | { |
f8ec6392 | 253 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
949a18d3 | 254 | |
f8ec6392 | 255 | if (pcidev) { |
c8d87bcc | 256 | if (dev->iobase) |
f8ec6392 | 257 | comedi_pci_disable(pcidev); |
c8d87bcc | 258 | } |
c8d87bcc HS |
259 | } |
260 | ||
75e6301b HS |
261 | static struct comedi_driver adl_pci6208_driver = { |
262 | .driver_name = "adl_pci6208", | |
c8d87bcc | 263 | .module = THIS_MODULE, |
744a8398 | 264 | .attach_pci = pci6208_attach_pci, |
c8d87bcc HS |
265 | .detach = pci6208_detach, |
266 | }; | |
267 | ||
75e6301b HS |
268 | static int __devinit adl_pci6208_pci_probe(struct pci_dev *dev, |
269 | const struct pci_device_id *ent) | |
c8d87bcc | 270 | { |
75e6301b | 271 | return comedi_pci_auto_config(dev, &adl_pci6208_driver); |
c8d87bcc HS |
272 | } |
273 | ||
75e6301b | 274 | static void __devexit adl_pci6208_pci_remove(struct pci_dev *dev) |
c8d87bcc HS |
275 | { |
276 | comedi_pci_auto_unconfig(dev); | |
277 | } | |
278 | ||
75e6301b | 279 | static DEFINE_PCI_DEVICE_TABLE(adl_pci6208_pci_table) = { |
0a1e6c1f HS |
280 | { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI6208) }, |
281 | { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI6216) }, | |
c8d87bcc HS |
282 | { 0 } |
283 | }; | |
75e6301b | 284 | MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table); |
c8d87bcc | 285 | |
75e6301b HS |
286 | static struct pci_driver adl_pci6208_pci_driver = { |
287 | .name = "adl_pci6208", | |
288 | .id_table = adl_pci6208_pci_table, | |
289 | .probe = adl_pci6208_pci_probe, | |
290 | .remove = __devexit_p(adl_pci6208_pci_remove), | |
c8d87bcc | 291 | }; |
75e6301b | 292 | module_comedi_pci_driver(adl_pci6208_driver, adl_pci6208_pci_driver); |
c8d87bcc | 293 | |
90f703d3 AT |
294 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
295 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
296 | MODULE_LICENSE("GPL"); |