Commit | Line | Data |
---|---|---|
e55c95a3 GG |
1 | /* |
2 | comedi/drivers/me4000.c | |
3 | Source code for the Meilhaus ME-4000 board family. | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 2000 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 | ||
22 | */ | |
23 | /* | |
24 | Driver: me4000 | |
25 | Description: Meilhaus ME-4000 series boards | |
26 | Devices: [Meilhaus] ME-4650 (me4000), ME-4670i, ME-4680, ME-4680i, ME-4680is | |
27 | Author: gg (Guenter Gebhardt <g.gebhardt@meilhaus.com>) | |
28 | Updated: Mon, 18 Mar 2002 15:34:01 -0800 | |
29 | Status: broken (no support for loading firmware) | |
30 | ||
31 | Supports: | |
32 | ||
33 | - Analog Input | |
34 | - Analog Output | |
35 | - Digital I/O | |
36 | - Counter | |
37 | ||
38 | Configuration Options: | |
39 | ||
40 | [0] - PCI bus number (optional) | |
41 | [1] - PCI slot number (optional) | |
42 | ||
43 | If bus/slot is not specified, the first available PCI | |
44 | device will be used. | |
45 | ||
46 | The firmware required by these boards is available in the | |
47 | comedi_nonfree_firmware tarball available from | |
48 | http://www.comedi.org. However, the driver's support for | |
49 | loading the firmware through comedi_config is currently | |
50 | broken. | |
51 | ||
52 | */ | |
53 | ||
54 | #include "../comedidev.h" | |
55 | ||
56 | #include <linux/delay.h> | |
57 | #include <linux/list.h> | |
58 | #include <linux/spinlock.h> | |
59 | ||
60 | #include "comedi_pci.h" | |
61 | #include "me4000.h" | |
62 | #if 0 | |
63 | /* file removed due to GPL incompatibility */ | |
64 | #include "me4000_fw.h" | |
65 | #endif | |
66 | ||
67 | /*============================================================================= | |
68 | PCI device table. | |
69 | This is used by modprobe to translate PCI IDs to drivers. | |
70 | ===========================================================================*/ | |
71 | ||
72 | static DEFINE_PCI_DEVICE_TABLE(me4000_pci_table) = { | |
73 | {PCI_VENDOR_ID_MEILHAUS, 0x4650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
74 | ||
75 | {PCI_VENDOR_ID_MEILHAUS, 0x4660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
76 | {PCI_VENDOR_ID_MEILHAUS, 0x4661, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
77 | {PCI_VENDOR_ID_MEILHAUS, 0x4662, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
78 | {PCI_VENDOR_ID_MEILHAUS, 0x4663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
79 | ||
80 | {PCI_VENDOR_ID_MEILHAUS, 0x4670, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
81 | {PCI_VENDOR_ID_MEILHAUS, 0x4671, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
82 | {PCI_VENDOR_ID_MEILHAUS, 0x4672, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
83 | {PCI_VENDOR_ID_MEILHAUS, 0x4673, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
84 | ||
85 | {PCI_VENDOR_ID_MEILHAUS, 0x4680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
86 | {PCI_VENDOR_ID_MEILHAUS, 0x4681, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
87 | {PCI_VENDOR_ID_MEILHAUS, 0x4682, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
88 | {PCI_VENDOR_ID_MEILHAUS, 0x4683, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
89 | ||
90 | {0} | |
91 | }; | |
92 | ||
93 | MODULE_DEVICE_TABLE(pci, me4000_pci_table); | |
94 | ||
95 | static const me4000_board_t me4000_boards[] = { | |
96 | {"ME-4650", 0x4650, {0, 0}, {16, 0, 0, 0}, {4}, {0}}, | |
97 | ||
98 | {"ME-4660", 0x4660, {0, 0}, {32, 0, 16, 0}, {4}, {3}}, | |
99 | {"ME-4660i", 0x4661, {0, 0}, {32, 0, 16, 0}, {4}, {3}}, | |
100 | {"ME-4660s", 0x4662, {0, 0}, {32, 8, 16, 0}, {4}, {3}}, | |
101 | {"ME-4660is", 0x4663, {0, 0}, {32, 8, 16, 0}, {4}, {3}}, | |
102 | ||
103 | {"ME-4670", 0x4670, {4, 0}, {32, 0, 16, 1}, {4}, {3}}, | |
104 | {"ME-4670i", 0x4671, {4, 0}, {32, 0, 16, 1}, {4}, {3}}, | |
105 | {"ME-4670s", 0x4672, {4, 0}, {32, 8, 16, 1}, {4}, {3}}, | |
106 | {"ME-4670is", 0x4673, {4, 0}, {32, 8, 16, 1}, {4}, {3}}, | |
107 | ||
108 | {"ME-4680", 0x4680, {4, 4}, {32, 0, 16, 1}, {4}, {3}}, | |
109 | {"ME-4680i", 0x4681, {4, 4}, {32, 0, 16, 1}, {4}, {3}}, | |
110 | {"ME-4680s", 0x4682, {4, 4}, {32, 8, 16, 1}, {4}, {3}}, | |
111 | {"ME-4680is", 0x4683, {4, 4}, {32, 8, 16, 1}, {4}, {3}}, | |
112 | ||
113 | {0}, | |
114 | }; | |
115 | ||
116 | #define ME4000_BOARD_VERSIONS (sizeof(me4000_boards) / sizeof(me4000_board_t) - 1) | |
117 | ||
118 | /*----------------------------------------------------------------------------- | |
119 | Comedi function prototypes | |
120 | ---------------------------------------------------------------------------*/ | |
e473e912 BP |
121 | static int me4000_attach(comedi_device *dev, comedi_devconfig *it); |
122 | static int me4000_detach(comedi_device *dev); | |
e55c95a3 GG |
123 | static comedi_driver driver_me4000 = { |
124 | driver_name:"me4000", | |
125 | module:THIS_MODULE, | |
126 | attach:me4000_attach, | |
127 | detach:me4000_detach, | |
128 | }; | |
129 | ||
130 | /*----------------------------------------------------------------------------- | |
131 | Meilhaus function prototypes | |
132 | ---------------------------------------------------------------------------*/ | |
e473e912 BP |
133 | static int me4000_probe(comedi_device *dev, comedi_devconfig *it); |
134 | static int get_registers(comedi_device *dev, struct pci_dev *pci_dev_p); | |
135 | static int init_board_info(comedi_device *dev, struct pci_dev *pci_dev_p); | |
136 | static int init_ao_context(comedi_device *dev); | |
137 | static int init_ai_context(comedi_device *dev); | |
138 | static int init_dio_context(comedi_device *dev); | |
139 | static int init_cnt_context(comedi_device *dev); | |
140 | static int xilinx_download(comedi_device *dev); | |
141 | static int reset_board(comedi_device *dev); | |
e55c95a3 | 142 | |
e473e912 BP |
143 | static int me4000_dio_insn_bits(comedi_device *dev, |
144 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); | |
e55c95a3 | 145 | |
e473e912 BP |
146 | static int me4000_dio_insn_config(comedi_device *dev, |
147 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); | |
e55c95a3 | 148 | |
e473e912 | 149 | static int cnt_reset(comedi_device *dev, unsigned int channel); |
e55c95a3 | 150 | |
e473e912 | 151 | static int cnt_config(comedi_device *dev, |
e55c95a3 GG |
152 | unsigned int channel, unsigned int mode); |
153 | ||
e473e912 BP |
154 | static int me4000_cnt_insn_config(comedi_device *dev, |
155 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); | |
e55c95a3 | 156 | |
e473e912 BP |
157 | static int me4000_cnt_insn_write(comedi_device *dev, |
158 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); | |
e55c95a3 | 159 | |
e473e912 BP |
160 | static int me4000_cnt_insn_read(comedi_device *dev, |
161 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); | |
e55c95a3 | 162 | |
e473e912 BP |
163 | static int me4000_ai_insn_read(comedi_device *dev, |
164 | comedi_subdevice *subdevice, comedi_insn *insn, lsampl_t *data); | |
e55c95a3 | 165 | |
e473e912 | 166 | static int me4000_ai_cancel(comedi_device *dev, comedi_subdevice *s); |
e55c95a3 | 167 | |
e473e912 BP |
168 | static int ai_check_chanlist(comedi_device *dev, |
169 | comedi_subdevice *s, comedi_cmd *cmd); | |
e55c95a3 | 170 | |
e473e912 BP |
171 | static int ai_round_cmd_args(comedi_device *dev, |
172 | comedi_subdevice *s, | |
173 | comedi_cmd *cmd, | |
e55c95a3 GG |
174 | unsigned int *init_ticks, |
175 | unsigned int *scan_ticks, unsigned int *chan_ticks); | |
176 | ||
e473e912 BP |
177 | static int ai_prepare(comedi_device *dev, |
178 | comedi_subdevice *s, | |
179 | comedi_cmd *cmd, | |
e55c95a3 GG |
180 | unsigned int init_ticks, |
181 | unsigned int scan_ticks, unsigned int chan_ticks); | |
182 | ||
e473e912 BP |
183 | static int ai_write_chanlist(comedi_device *dev, |
184 | comedi_subdevice *s, comedi_cmd *cmd); | |
e55c95a3 GG |
185 | |
186 | static irqreturn_t me4000_ai_isr(int irq, void *dev_id PT_REGS_ARG); | |
187 | ||
e473e912 BP |
188 | static int me4000_ai_do_cmd_test(comedi_device *dev, |
189 | comedi_subdevice *s, comedi_cmd *cmd); | |
e55c95a3 | 190 | |
e473e912 | 191 | static int me4000_ai_do_cmd(comedi_device *dev, comedi_subdevice *s); |
e55c95a3 | 192 | |
e473e912 BP |
193 | static int me4000_ao_insn_write(comedi_device *dev, |
194 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); | |
e55c95a3 | 195 | |
e473e912 BP |
196 | static int me4000_ao_insn_read(comedi_device *dev, |
197 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); | |
e55c95a3 GG |
198 | |
199 | /*----------------------------------------------------------------------------- | |
200 | Meilhaus inline functions | |
201 | ---------------------------------------------------------------------------*/ | |
202 | ||
e473e912 | 203 | static inline void me4000_outb(comedi_device *dev, unsigned char value, |
e55c95a3 GG |
204 | unsigned long port) |
205 | { | |
206 | PORT_PDEBUG("--> 0x%02X port 0x%04lX\n", value, port); | |
207 | outb(value, port); | |
208 | } | |
209 | ||
e473e912 | 210 | static inline void me4000_outl(comedi_device *dev, unsigned long value, |
e55c95a3 GG |
211 | unsigned long port) |
212 | { | |
213 | PORT_PDEBUG("--> 0x%08lX port 0x%04lX\n", value, port); | |
214 | outl(value, port); | |
215 | } | |
216 | ||
e473e912 | 217 | static inline unsigned long me4000_inl(comedi_device *dev, unsigned long port) |
e55c95a3 GG |
218 | { |
219 | unsigned long value; | |
220 | value = inl(port); | |
221 | PORT_PDEBUG("<-- 0x%08lX port 0x%04lX\n", value, port); | |
222 | return value; | |
223 | } | |
224 | ||
e473e912 | 225 | static inline unsigned char me4000_inb(comedi_device *dev, unsigned long port) |
e55c95a3 GG |
226 | { |
227 | unsigned char value; | |
228 | value = inb(port); | |
229 | PORT_PDEBUG("<-- 0x%08X port 0x%04lX\n", value, port); | |
230 | return value; | |
231 | } | |
232 | ||
233 | static const comedi_lrange me4000_ai_range = { | |
234 | 4, | |
235 | { | |
236 | UNI_RANGE(2.5), | |
237 | UNI_RANGE(10), | |
238 | BIP_RANGE(2.5), | |
239 | BIP_RANGE(10), | |
240 | } | |
241 | }; | |
242 | ||
243 | static const comedi_lrange me4000_ao_range = { | |
244 | 1, | |
245 | { | |
246 | BIP_RANGE(10), | |
247 | } | |
248 | }; | |
249 | ||
e473e912 | 250 | static int me4000_attach(comedi_device *dev, comedi_devconfig *it) |
e55c95a3 GG |
251 | { |
252 | comedi_subdevice *s; | |
253 | int result; | |
254 | ||
255 | CALL_PDEBUG("In me4000_attach()\n"); | |
256 | ||
257 | result = me4000_probe(dev, it); | |
258 | if (result) | |
259 | return result; | |
260 | ||
261 | /* | |
262 | * Allocate the subdevice structures. alloc_subdevice() is a | |
263 | * convenient macro defined in comedidev.h. It relies on | |
264 | * n_subdevices being set correctly. | |
265 | */ | |
266 | if (alloc_subdevices(dev, 4) < 0) | |
267 | return -ENOMEM; | |
268 | ||
269 | /*========================================================================= | |
270 | Analog input subdevice | |
271 | ========================================================================*/ | |
272 | ||
273 | s = dev->subdevices + 0; | |
274 | ||
275 | if (thisboard->ai.count) { | |
276 | s->type = COMEDI_SUBD_AI; | |
277 | s->subdev_flags = | |
278 | SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF; | |
279 | s->n_chan = thisboard->ai.count; | |
b6c77757 | 280 | s->maxdata = 0xFFFF; /* 16 bit ADC */ |
e55c95a3 GG |
281 | s->len_chanlist = ME4000_AI_CHANNEL_LIST_COUNT; |
282 | s->range_table = &me4000_ai_range; | |
283 | s->insn_read = me4000_ai_insn_read; | |
284 | ||
285 | if (info->irq > 0) { | |
286 | if (comedi_request_irq(info->irq, me4000_ai_isr, | |
287 | IRQF_SHARED, "ME-4000", dev)) { | |
288 | printk("comedi%d: me4000: me4000_attach(): Unable to allocate irq\n", dev->minor); | |
289 | } else { | |
290 | dev->read_subdev = s; | |
291 | s->subdev_flags |= SDF_CMD_READ; | |
292 | s->cancel = me4000_ai_cancel; | |
293 | s->do_cmdtest = me4000_ai_do_cmd_test; | |
294 | s->do_cmd = me4000_ai_do_cmd; | |
295 | } | |
296 | } else { | |
297 | printk(KERN_WARNING | |
298 | "comedi%d: me4000: me4000_attach(): No interrupt available\n", | |
299 | dev->minor); | |
300 | } | |
301 | } else { | |
302 | s->type = COMEDI_SUBD_UNUSED; | |
303 | } | |
304 | ||
305 | /*========================================================================= | |
306 | Analog output subdevice | |
307 | ========================================================================*/ | |
308 | ||
309 | s = dev->subdevices + 1; | |
310 | ||
311 | if (thisboard->ao.count) { | |
312 | s->type = COMEDI_SUBD_AO; | |
313 | s->subdev_flags = SDF_WRITEABLE | SDF_COMMON | SDF_GROUND; | |
314 | s->n_chan = thisboard->ao.count; | |
b6c77757 | 315 | s->maxdata = 0xFFFF; /* 16 bit DAC */ |
e55c95a3 GG |
316 | s->range_table = &me4000_ao_range; |
317 | s->insn_write = me4000_ao_insn_write; | |
318 | s->insn_read = me4000_ao_insn_read; | |
319 | } else { | |
320 | s->type = COMEDI_SUBD_UNUSED; | |
321 | } | |
322 | ||
323 | /*========================================================================= | |
324 | Digital I/O subdevice | |
325 | ========================================================================*/ | |
326 | ||
327 | s = dev->subdevices + 2; | |
328 | ||
329 | if (thisboard->dio.count) { | |
330 | s->type = COMEDI_SUBD_DIO; | |
331 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
332 | s->n_chan = thisboard->dio.count * 8; | |
333 | s->maxdata = 1; | |
334 | s->range_table = &range_digital; | |
335 | s->insn_bits = me4000_dio_insn_bits; | |
336 | s->insn_config = me4000_dio_insn_config; | |
337 | } else { | |
338 | s->type = COMEDI_SUBD_UNUSED; | |
339 | } | |
340 | ||
341 | /* | |
342 | * Check for optoisolated ME-4000 version. If one the first | |
343 | * port is a fixed output port and the second is a fixed input port. | |
344 | */ | |
345 | if (!me4000_inl(dev, info->dio_context.dir_reg)) { | |
346 | s->io_bits |= 0xFF; | |
347 | me4000_outl(dev, ME4000_DIO_CTRL_BIT_MODE_0, | |
348 | info->dio_context.dir_reg); | |
349 | } | |
350 | ||
351 | /*========================================================================= | |
352 | Counter subdevice | |
353 | ========================================================================*/ | |
354 | ||
355 | s = dev->subdevices + 3; | |
356 | ||
357 | if (thisboard->cnt.count) { | |
358 | s->type = COMEDI_SUBD_COUNTER; | |
359 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
360 | s->n_chan = thisboard->cnt.count; | |
b6c77757 | 361 | s->maxdata = 0xFFFF; /* 16 bit counters */ |
e55c95a3 GG |
362 | s->insn_read = me4000_cnt_insn_read; |
363 | s->insn_write = me4000_cnt_insn_write; | |
364 | s->insn_config = me4000_cnt_insn_config; | |
365 | } else { | |
366 | s->type = COMEDI_SUBD_UNUSED; | |
367 | } | |
368 | ||
369 | return 0; | |
370 | } | |
371 | ||
e473e912 | 372 | static int me4000_probe(comedi_device *dev, comedi_devconfig *it) |
e55c95a3 GG |
373 | { |
374 | struct pci_dev *pci_device; | |
375 | int result, i; | |
376 | me4000_board_t *board; | |
377 | ||
378 | CALL_PDEBUG("In me4000_probe()\n"); | |
379 | ||
380 | /* Allocate private memory */ | |
381 | if (alloc_private(dev, sizeof(me4000_info_t)) < 0) { | |
382 | return -ENOMEM; | |
383 | } | |
384 | /* | |
385 | * Probe the device to determine what device in the series it is. | |
386 | */ | |
387 | for (pci_device = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); | |
388 | pci_device != NULL; | |
389 | pci_device = | |
390 | pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_device)) { | |
391 | if (pci_device->vendor == PCI_VENDOR_ID_MEILHAUS) { | |
392 | for (i = 0; i < ME4000_BOARD_VERSIONS; i++) { | |
393 | if (me4000_boards[i].device_id == | |
394 | pci_device->device) { | |
395 | /* Was a particular bus/slot requested? */ | |
396 | if ((it->options[0] != 0) | |
397 | || (it->options[1] != 0)) { | |
398 | /* Are we on the wrong bus/slot? */ | |
399 | if (pci_device->bus->number != | |
400 | it->options[0] | |
401 | || PCI_SLOT(pci_device-> | |
402 | devfn) != | |
403 | it->options[1]) { | |
404 | continue; | |
405 | } | |
406 | } | |
407 | dev->board_ptr = me4000_boards + i; | |
408 | board = (me4000_board_t *) dev-> | |
409 | board_ptr; | |
410 | info->pci_dev_p = pci_device; | |
411 | goto found; | |
412 | } | |
413 | } | |
414 | } | |
415 | } | |
416 | ||
417 | printk(KERN_ERR | |
418 | "comedi%d: me4000: me4000_probe(): No supported board found (req. bus/slot : %d/%d)\n", | |
419 | dev->minor, it->options[0], it->options[1]); | |
420 | return -ENODEV; | |
421 | ||
422 | found: | |
423 | ||
424 | printk(KERN_INFO | |
425 | "comedi%d: me4000: me4000_probe(): Found %s at PCI bus %d, slot %d\n", | |
426 | dev->minor, me4000_boards[i].name, pci_device->bus->number, | |
427 | PCI_SLOT(pci_device->devfn)); | |
428 | ||
429 | /* Set data in device structure */ | |
430 | dev->board_name = board->name; | |
431 | ||
432 | /* Enable PCI device and request regions */ | |
433 | result = comedi_pci_enable(pci_device, dev->board_name); | |
434 | if (result) { | |
435 | printk(KERN_ERR | |
436 | "comedi%d: me4000: me4000_probe(): Cannot enable PCI device and request I/O regions\n", | |
437 | dev->minor); | |
438 | return result; | |
439 | } | |
440 | ||
441 | /* Get the PCI base registers */ | |
442 | result = get_registers(dev, pci_device); | |
443 | if (result) { | |
444 | printk(KERN_ERR | |
445 | "comedi%d: me4000: me4000_probe(): Cannot get registers\n", | |
446 | dev->minor); | |
447 | return result; | |
448 | } | |
449 | /* Initialize board info */ | |
450 | result = init_board_info(dev, pci_device); | |
451 | if (result) { | |
452 | printk(KERN_ERR | |
453 | "comedi%d: me4000: me4000_probe(): Cannot init baord info\n", | |
454 | dev->minor); | |
455 | return result; | |
456 | } | |
457 | ||
458 | /* Init analog output context */ | |
459 | result = init_ao_context(dev); | |
460 | if (result) { | |
461 | printk(KERN_ERR | |
462 | "comedi%d: me4000: me4000_probe(): Cannot init ao context\n", | |
463 | dev->minor); | |
464 | return result; | |
465 | } | |
466 | ||
467 | /* Init analog input context */ | |
468 | result = init_ai_context(dev); | |
469 | if (result) { | |
470 | printk(KERN_ERR | |
471 | "comedi%d: me4000: me4000_probe(): Cannot init ai context\n", | |
472 | dev->minor); | |
473 | return result; | |
474 | } | |
475 | ||
476 | /* Init digital I/O context */ | |
477 | result = init_dio_context(dev); | |
478 | if (result) { | |
479 | printk(KERN_ERR | |
480 | "comedi%d: me4000: me4000_probe(): Cannot init dio context\n", | |
481 | dev->minor); | |
482 | return result; | |
483 | } | |
484 | ||
485 | /* Init counter context */ | |
486 | result = init_cnt_context(dev); | |
487 | if (result) { | |
488 | printk(KERN_ERR | |
489 | "comedi%d: me4000: me4000_probe(): Cannot init cnt context\n", | |
490 | dev->minor); | |
491 | return result; | |
492 | } | |
493 | ||
494 | /* Download the xilinx firmware */ | |
495 | result = xilinx_download(dev); | |
496 | if (result) { | |
497 | printk(KERN_ERR | |
498 | "comedi%d: me4000: me4000_probe(): Can't download firmware\n", | |
499 | dev->minor); | |
500 | return result; | |
501 | } | |
502 | ||
503 | /* Make a hardware reset */ | |
504 | result = reset_board(dev); | |
505 | if (result) { | |
506 | printk(KERN_ERR | |
507 | "comedi%d: me4000: me4000_probe(): Can't reset board\n", | |
508 | dev->minor); | |
509 | return result; | |
510 | } | |
511 | ||
512 | return 0; | |
513 | } | |
514 | ||
e473e912 | 515 | static int get_registers(comedi_device *dev, struct pci_dev *pci_dev_p) |
e55c95a3 GG |
516 | { |
517 | ||
518 | CALL_PDEBUG("In get_registers()\n"); | |
519 | ||
520 | /*--------------------------- plx regbase ---------------------------------*/ | |
521 | ||
522 | info->plx_regbase = pci_resource_start(pci_dev_p, 1); | |
523 | if (info->plx_regbase == 0) { | |
524 | printk(KERN_ERR | |
525 | "comedi%d: me4000: get_registers(): PCI base address 1 is not available\n", | |
526 | dev->minor); | |
527 | return -ENODEV; | |
528 | } | |
529 | info->plx_regbase_size = pci_resource_len(pci_dev_p, 1); | |
530 | ||
531 | /*--------------------------- me4000 regbase ------------------------------*/ | |
532 | ||
533 | info->me4000_regbase = pci_resource_start(pci_dev_p, 2); | |
534 | if (info->me4000_regbase == 0) { | |
535 | printk(KERN_ERR | |
536 | "comedi%d: me4000: get_registers(): PCI base address 2 is not available\n", | |
537 | dev->minor); | |
538 | return -ENODEV; | |
539 | } | |
540 | info->me4000_regbase_size = pci_resource_len(pci_dev_p, 2); | |
541 | ||
542 | /*--------------------------- timer regbase ------------------------------*/ | |
543 | ||
544 | info->timer_regbase = pci_resource_start(pci_dev_p, 3); | |
545 | if (info->timer_regbase == 0) { | |
546 | printk(KERN_ERR | |
547 | "comedi%d: me4000: get_registers(): PCI base address 3 is not available\n", | |
548 | dev->minor); | |
549 | return -ENODEV; | |
550 | } | |
551 | info->timer_regbase_size = pci_resource_len(pci_dev_p, 3); | |
552 | ||
553 | /*--------------------------- program regbase ------------------------------*/ | |
554 | ||
555 | info->program_regbase = pci_resource_start(pci_dev_p, 5); | |
556 | if (info->program_regbase == 0) { | |
557 | printk(KERN_ERR | |
558 | "comedi%d: me4000: get_registers(): PCI base address 5 is not available\n", | |
559 | dev->minor); | |
560 | return -ENODEV; | |
561 | } | |
562 | info->program_regbase_size = pci_resource_len(pci_dev_p, 5); | |
563 | ||
564 | return 0; | |
565 | } | |
566 | ||
e473e912 | 567 | static int init_board_info(comedi_device *dev, struct pci_dev *pci_dev_p) |
e55c95a3 GG |
568 | { |
569 | int result; | |
570 | ||
571 | CALL_PDEBUG("In init_board_info()\n"); | |
572 | ||
573 | /* Init spin locks */ | |
b6c77757 BP |
574 | /* spin_lock_init(&info->preload_lock); */ |
575 | /* spin_lock_init(&info->ai_ctrl_lock); */ | |
e55c95a3 GG |
576 | |
577 | /* Get the serial number */ | |
578 | result = pci_read_config_dword(pci_dev_p, 0x2C, &info->serial_no); | |
579 | if (result != PCIBIOS_SUCCESSFUL) { | |
580 | return result; | |
581 | } | |
582 | ||
583 | /* Get the hardware revision */ | |
584 | result = pci_read_config_byte(pci_dev_p, 0x08, &info->hw_revision); | |
585 | if (result != PCIBIOS_SUCCESSFUL) { | |
586 | return result; | |
587 | } | |
588 | ||
589 | /* Get the vendor id */ | |
590 | info->vendor_id = pci_dev_p->vendor; | |
591 | ||
592 | /* Get the device id */ | |
593 | info->device_id = pci_dev_p->device; | |
594 | ||
595 | /* Get the irq assigned to the board */ | |
596 | info->irq = pci_dev_p->irq; | |
597 | ||
598 | return 0; | |
599 | } | |
600 | ||
e473e912 | 601 | static int init_ao_context(comedi_device *dev) |
e55c95a3 GG |
602 | { |
603 | int i; | |
604 | ||
605 | CALL_PDEBUG("In init_ao_context()\n"); | |
606 | ||
607 | for (i = 0; i < thisboard->ao.count; i++) { | |
b6c77757 | 608 | /* spin_lock_init(&info->ao_context[i].use_lock); */ |
e55c95a3 GG |
609 | info->ao_context[i].irq = info->irq; |
610 | ||
611 | switch (i) { | |
612 | case 0: | |
613 | info->ao_context[i].ctrl_reg = | |
614 | info->me4000_regbase + ME4000_AO_00_CTRL_REG; | |
615 | info->ao_context[i].status_reg = | |
616 | info->me4000_regbase + ME4000_AO_00_STATUS_REG; | |
617 | info->ao_context[i].fifo_reg = | |
618 | info->me4000_regbase + ME4000_AO_00_FIFO_REG; | |
619 | info->ao_context[i].single_reg = | |
620 | info->me4000_regbase + ME4000_AO_00_SINGLE_REG; | |
621 | info->ao_context[i].timer_reg = | |
622 | info->me4000_regbase + ME4000_AO_00_TIMER_REG; | |
623 | info->ao_context[i].irq_status_reg = | |
624 | info->me4000_regbase + ME4000_IRQ_STATUS_REG; | |
625 | info->ao_context[i].preload_reg = | |
626 | info->me4000_regbase + ME4000_AO_LOADSETREG_XX; | |
627 | break; | |
628 | case 1: | |
629 | info->ao_context[i].ctrl_reg = | |
630 | info->me4000_regbase + ME4000_AO_01_CTRL_REG; | |
631 | info->ao_context[i].status_reg = | |
632 | info->me4000_regbase + ME4000_AO_01_STATUS_REG; | |
633 | info->ao_context[i].fifo_reg = | |
634 | info->me4000_regbase + ME4000_AO_01_FIFO_REG; | |
635 | info->ao_context[i].single_reg = | |
636 | info->me4000_regbase + ME4000_AO_01_SINGLE_REG; | |
637 | info->ao_context[i].timer_reg = | |
638 | info->me4000_regbase + ME4000_AO_01_TIMER_REG; | |
639 | info->ao_context[i].irq_status_reg = | |
640 | info->me4000_regbase + ME4000_IRQ_STATUS_REG; | |
641 | info->ao_context[i].preload_reg = | |
642 | info->me4000_regbase + ME4000_AO_LOADSETREG_XX; | |
643 | break; | |
644 | case 2: | |
645 | info->ao_context[i].ctrl_reg = | |
646 | info->me4000_regbase + ME4000_AO_02_CTRL_REG; | |
647 | info->ao_context[i].status_reg = | |
648 | info->me4000_regbase + ME4000_AO_02_STATUS_REG; | |
649 | info->ao_context[i].fifo_reg = | |
650 | info->me4000_regbase + ME4000_AO_02_FIFO_REG; | |
651 | info->ao_context[i].single_reg = | |
652 | info->me4000_regbase + ME4000_AO_02_SINGLE_REG; | |
653 | info->ao_context[i].timer_reg = | |
654 | info->me4000_regbase + ME4000_AO_02_TIMER_REG; | |
655 | info->ao_context[i].irq_status_reg = | |
656 | info->me4000_regbase + ME4000_IRQ_STATUS_REG; | |
657 | info->ao_context[i].preload_reg = | |
658 | info->me4000_regbase + ME4000_AO_LOADSETREG_XX; | |
659 | break; | |
660 | case 3: | |
661 | info->ao_context[i].ctrl_reg = | |
662 | info->me4000_regbase + ME4000_AO_03_CTRL_REG; | |
663 | info->ao_context[i].status_reg = | |
664 | info->me4000_regbase + ME4000_AO_03_STATUS_REG; | |
665 | info->ao_context[i].fifo_reg = | |
666 | info->me4000_regbase + ME4000_AO_03_FIFO_REG; | |
667 | info->ao_context[i].single_reg = | |
668 | info->me4000_regbase + ME4000_AO_03_SINGLE_REG; | |
669 | info->ao_context[i].timer_reg = | |
670 | info->me4000_regbase + ME4000_AO_03_TIMER_REG; | |
671 | info->ao_context[i].irq_status_reg = | |
672 | info->me4000_regbase + ME4000_IRQ_STATUS_REG; | |
673 | info->ao_context[i].preload_reg = | |
674 | info->me4000_regbase + ME4000_AO_LOADSETREG_XX; | |
675 | break; | |
676 | default: | |
677 | break; | |
678 | } | |
679 | } | |
680 | ||
681 | return 0; | |
682 | } | |
683 | ||
e473e912 | 684 | static int init_ai_context(comedi_device *dev) |
e55c95a3 GG |
685 | { |
686 | ||
687 | CALL_PDEBUG("In init_ai_context()\n"); | |
688 | ||
689 | info->ai_context.irq = info->irq; | |
690 | ||
691 | info->ai_context.ctrl_reg = info->me4000_regbase + ME4000_AI_CTRL_REG; | |
692 | info->ai_context.status_reg = | |
693 | info->me4000_regbase + ME4000_AI_STATUS_REG; | |
694 | info->ai_context.channel_list_reg = | |
695 | info->me4000_regbase + ME4000_AI_CHANNEL_LIST_REG; | |
696 | info->ai_context.data_reg = info->me4000_regbase + ME4000_AI_DATA_REG; | |
697 | info->ai_context.chan_timer_reg = | |
698 | info->me4000_regbase + ME4000_AI_CHAN_TIMER_REG; | |
699 | info->ai_context.chan_pre_timer_reg = | |
700 | info->me4000_regbase + ME4000_AI_CHAN_PRE_TIMER_REG; | |
701 | info->ai_context.scan_timer_low_reg = | |
702 | info->me4000_regbase + ME4000_AI_SCAN_TIMER_LOW_REG; | |
703 | info->ai_context.scan_timer_high_reg = | |
704 | info->me4000_regbase + ME4000_AI_SCAN_TIMER_HIGH_REG; | |
705 | info->ai_context.scan_pre_timer_low_reg = | |
706 | info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_LOW_REG; | |
707 | info->ai_context.scan_pre_timer_high_reg = | |
708 | info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_HIGH_REG; | |
709 | info->ai_context.start_reg = info->me4000_regbase + ME4000_AI_START_REG; | |
710 | info->ai_context.irq_status_reg = | |
711 | info->me4000_regbase + ME4000_IRQ_STATUS_REG; | |
712 | info->ai_context.sample_counter_reg = | |
713 | info->me4000_regbase + ME4000_AI_SAMPLE_COUNTER_REG; | |
714 | ||
715 | return 0; | |
716 | } | |
717 | ||
e473e912 | 718 | static int init_dio_context(comedi_device *dev) |
e55c95a3 GG |
719 | { |
720 | ||
721 | CALL_PDEBUG("In init_dio_context()\n"); | |
722 | ||
723 | info->dio_context.dir_reg = info->me4000_regbase + ME4000_DIO_DIR_REG; | |
724 | info->dio_context.ctrl_reg = info->me4000_regbase + ME4000_DIO_CTRL_REG; | |
725 | info->dio_context.port_0_reg = | |
726 | info->me4000_regbase + ME4000_DIO_PORT_0_REG; | |
727 | info->dio_context.port_1_reg = | |
728 | info->me4000_regbase + ME4000_DIO_PORT_1_REG; | |
729 | info->dio_context.port_2_reg = | |
730 | info->me4000_regbase + ME4000_DIO_PORT_2_REG; | |
731 | info->dio_context.port_3_reg = | |
732 | info->me4000_regbase + ME4000_DIO_PORT_3_REG; | |
733 | ||
734 | return 0; | |
735 | } | |
736 | ||
e473e912 | 737 | static int init_cnt_context(comedi_device *dev) |
e55c95a3 GG |
738 | { |
739 | ||
740 | CALL_PDEBUG("In init_cnt_context()\n"); | |
741 | ||
742 | info->cnt_context.ctrl_reg = info->timer_regbase + ME4000_CNT_CTRL_REG; | |
743 | info->cnt_context.counter_0_reg = | |
744 | info->timer_regbase + ME4000_CNT_COUNTER_0_REG; | |
745 | info->cnt_context.counter_1_reg = | |
746 | info->timer_regbase + ME4000_CNT_COUNTER_1_REG; | |
747 | info->cnt_context.counter_2_reg = | |
748 | info->timer_regbase + ME4000_CNT_COUNTER_2_REG; | |
749 | ||
750 | return 0; | |
751 | } | |
752 | ||
753 | #define FIRMWARE_NOT_AVAILABLE 1 | |
754 | #if FIRMWARE_NOT_AVAILABLE | |
755 | extern unsigned char *xilinx_firm; | |
756 | #endif | |
757 | ||
e473e912 | 758 | static int xilinx_download(comedi_device *dev) |
e55c95a3 GG |
759 | { |
760 | u32 value = 0; | |
761 | wait_queue_head_t queue; | |
762 | int idx = 0; | |
763 | int size = 0; | |
764 | ||
765 | CALL_PDEBUG("In xilinx_download()\n"); | |
766 | ||
767 | init_waitqueue_head(&queue); | |
768 | ||
769 | /* | |
770 | * Set PLX local interrupt 2 polarity to high. | |
771 | * Interrupt is thrown by init pin of xilinx. | |
772 | */ | |
773 | outl(0x10, info->plx_regbase + PLX_INTCSR); | |
774 | ||
775 | /* Set /CS and /WRITE of the Xilinx */ | |
776 | value = inl(info->plx_regbase + PLX_ICR); | |
777 | value |= 0x100; | |
778 | outl(value, info->plx_regbase + PLX_ICR); | |
779 | ||
780 | /* Init Xilinx with CS1 */ | |
781 | inb(info->program_regbase + 0xC8); | |
782 | ||
783 | /* Wait until /INIT pin is set */ | |
784 | udelay(20); | |
d86d3a01 | 785 | if (!(inl(info->plx_regbase + PLX_INTCSR) & 0x20)) { |
e55c95a3 GG |
786 | printk(KERN_ERR |
787 | "comedi%d: me4000: xilinx_download(): Can't init Xilinx\n", | |
788 | dev->minor); | |
789 | return -EIO; | |
790 | } | |
791 | ||
792 | /* Reset /CS and /WRITE of the Xilinx */ | |
793 | value = inl(info->plx_regbase + PLX_ICR); | |
794 | value &= ~0x100; | |
795 | outl(value, info->plx_regbase + PLX_ICR); | |
796 | if (FIRMWARE_NOT_AVAILABLE) { | |
797 | comedi_error(dev, | |
798 | "xilinx firmware unavailable due to licensing, aborting"); | |
799 | return -EIO; | |
800 | } else { | |
801 | /* Download Xilinx firmware */ | |
802 | size = (xilinx_firm[0] << 24) + (xilinx_firm[1] << 16) + | |
803 | (xilinx_firm[2] << 8) + xilinx_firm[3]; | |
804 | udelay(10); | |
805 | ||
806 | for (idx = 0; idx < size; idx++) { | |
807 | outb(xilinx_firm[16 + idx], info->program_regbase); | |
808 | udelay(10); | |
809 | ||
810 | /* Check if BUSY flag is low */ | |
811 | if (inl(info->plx_regbase + PLX_ICR) & 0x20) { | |
812 | printk(KERN_ERR | |
813 | "comedi%d: me4000: xilinx_download(): Xilinx is still busy (idx = %d)\n", | |
814 | dev->minor, idx); | |
815 | return -EIO; | |
816 | } | |
817 | } | |
818 | } | |
819 | ||
820 | /* If done flag is high download was successful */ | |
821 | if (inl(info->plx_regbase + PLX_ICR) & 0x4) { | |
822 | } else { | |
823 | printk(KERN_ERR | |
824 | "comedi%d: me4000: xilinx_download(): DONE flag is not set\n", | |
825 | dev->minor); | |
826 | printk(KERN_ERR | |
827 | "comedi%d: me4000: xilinx_download(): Download not succesful\n", | |
828 | dev->minor); | |
829 | return -EIO; | |
830 | } | |
831 | ||
832 | /* Set /CS and /WRITE */ | |
833 | value = inl(info->plx_regbase + PLX_ICR); | |
834 | value |= 0x100; | |
835 | outl(value, info->plx_regbase + PLX_ICR); | |
836 | ||
837 | return 0; | |
838 | } | |
839 | ||
e473e912 | 840 | static int reset_board(comedi_device *dev) |
e55c95a3 GG |
841 | { |
842 | unsigned long icr; | |
843 | ||
844 | CALL_PDEBUG("In reset_board()\n"); | |
845 | ||
846 | /* Make a hardware reset */ | |
847 | icr = me4000_inl(dev, info->plx_regbase + PLX_ICR); | |
848 | icr |= 0x40000000; | |
849 | me4000_outl(dev, icr, info->plx_regbase + PLX_ICR); | |
850 | icr &= ~0x40000000; | |
851 | me4000_outl(dev, icr, info->plx_regbase + PLX_ICR); | |
852 | ||
853 | /* 0x8000 to the DACs means an output voltage of 0V */ | |
854 | me4000_outl(dev, 0x8000, | |
855 | info->me4000_regbase + ME4000_AO_00_SINGLE_REG); | |
856 | me4000_outl(dev, 0x8000, | |
857 | info->me4000_regbase + ME4000_AO_01_SINGLE_REG); | |
858 | me4000_outl(dev, 0x8000, | |
859 | info->me4000_regbase + ME4000_AO_02_SINGLE_REG); | |
860 | me4000_outl(dev, 0x8000, | |
861 | info->me4000_regbase + ME4000_AO_03_SINGLE_REG); | |
862 | ||
863 | /* Set both stop bits in the analog input control register */ | |
864 | me4000_outl(dev, | |
865 | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP, | |
866 | info->me4000_regbase + ME4000_AI_CTRL_REG); | |
867 | ||
868 | /* Set both stop bits in the analog output control register */ | |
869 | me4000_outl(dev, | |
870 | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, | |
871 | info->me4000_regbase + ME4000_AO_00_CTRL_REG); | |
872 | me4000_outl(dev, | |
873 | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, | |
874 | info->me4000_regbase + ME4000_AO_01_CTRL_REG); | |
875 | me4000_outl(dev, | |
876 | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, | |
877 | info->me4000_regbase + ME4000_AO_02_CTRL_REG); | |
878 | me4000_outl(dev, | |
879 | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, | |
880 | info->me4000_regbase + ME4000_AO_03_CTRL_REG); | |
881 | ||
882 | /* Enable interrupts on the PLX */ | |
883 | me4000_outl(dev, 0x43, info->plx_regbase + PLX_INTCSR); | |
884 | ||
885 | /* Set the adustment register for AO demux */ | |
886 | me4000_outl(dev, ME4000_AO_DEMUX_ADJUST_VALUE, | |
887 | info->me4000_regbase + ME4000_AO_DEMUX_ADJUST_REG); | |
888 | ||
889 | /* Set digital I/O direction for port 0 to output on isolated versions */ | |
890 | if (!(me4000_inl(dev, info->me4000_regbase + ME4000_DIO_DIR_REG) & 0x1)) { | |
891 | me4000_outl(dev, 0x1, | |
892 | info->me4000_regbase + ME4000_DIO_CTRL_REG); | |
893 | } | |
894 | ||
895 | return 0; | |
896 | } | |
897 | ||
e473e912 | 898 | static int me4000_detach(comedi_device *dev) |
e55c95a3 GG |
899 | { |
900 | CALL_PDEBUG("In me4000_detach()\n"); | |
901 | ||
902 | if (info) { | |
903 | if (info->pci_dev_p) { | |
904 | reset_board(dev); | |
905 | if (info->plx_regbase) { | |
906 | comedi_pci_disable(info->pci_dev_p); | |
907 | } | |
908 | pci_dev_put(info->pci_dev_p); | |
909 | } | |
910 | } | |
911 | ||
912 | return 0; | |
913 | } | |
914 | ||
915 | /*============================================================================= | |
916 | Analog input section | |
917 | ===========================================================================*/ | |
918 | ||
e473e912 BP |
919 | static int me4000_ai_insn_read(comedi_device *dev, |
920 | comedi_subdevice *subdevice, comedi_insn *insn, lsampl_t *data) | |
e55c95a3 GG |
921 | { |
922 | ||
923 | int chan = CR_CHAN(insn->chanspec); | |
924 | int rang = CR_RANGE(insn->chanspec); | |
925 | int aref = CR_AREF(insn->chanspec); | |
926 | ||
927 | unsigned long entry = 0; | |
928 | unsigned long tmp; | |
929 | long lval; | |
930 | ||
931 | CALL_PDEBUG("In me4000_ai_insn_read()\n"); | |
932 | ||
933 | if (insn->n == 0) { | |
934 | return 0; | |
935 | } else if (insn->n > 1) { | |
936 | printk(KERN_ERR | |
937 | "comedi%d: me4000: me4000_ai_insn_read(): Invalid instruction length %d\n", | |
938 | dev->minor, insn->n); | |
939 | return -EINVAL; | |
940 | } | |
941 | ||
942 | switch (rang) { | |
943 | case 0: | |
944 | entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5; | |
945 | break; | |
946 | case 1: | |
947 | entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10; | |
948 | break; | |
949 | case 2: | |
950 | entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5; | |
951 | break; | |
952 | case 3: | |
953 | entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10; | |
954 | break; | |
955 | default: | |
956 | printk(KERN_ERR | |
957 | "comedi%d: me4000: me4000_ai_insn_read(): Invalid range specified\n", | |
958 | dev->minor); | |
959 | return -EINVAL; | |
960 | } | |
961 | ||
962 | switch (aref) { | |
963 | case AREF_GROUND: | |
964 | case AREF_COMMON: | |
965 | if (chan >= thisboard->ai.count) { | |
966 | printk(KERN_ERR | |
967 | "comedi%d: me4000: me4000_ai_insn_read(): Analog input is not available\n", | |
968 | dev->minor); | |
969 | return -EINVAL; | |
970 | } | |
971 | entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED | chan; | |
972 | break; | |
973 | ||
974 | case AREF_DIFF: | |
975 | if (rang == 0 || rang == 1) { | |
976 | printk(KERN_ERR | |
977 | "comedi%d: me4000: me4000_ai_insn_read(): Range must be bipolar when aref = diff\n", | |
978 | dev->minor); | |
979 | return -EINVAL; | |
980 | } | |
981 | ||
982 | if (chan >= thisboard->ai.diff_count) { | |
983 | printk(KERN_ERR | |
984 | "comedi%d: me4000: me4000_ai_insn_read(): Analog input is not available\n", | |
985 | dev->minor); | |
986 | return -EINVAL; | |
987 | } | |
988 | entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL | chan; | |
989 | break; | |
990 | default: | |
991 | printk(KERN_ERR | |
992 | "comedi%d: me4000: me4000_ai_insn_read(): Invalid aref specified\n", | |
993 | dev->minor); | |
994 | return -EINVAL; | |
995 | } | |
996 | ||
997 | entry |= ME4000_AI_LIST_LAST_ENTRY; | |
998 | ||
999 | /* Clear channel list, data fifo and both stop bits */ | |
1000 | tmp = me4000_inl(dev, info->ai_context.ctrl_reg); | |
1001 | tmp &= ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO | | |
1002 | ME4000_AI_CTRL_BIT_DATA_FIFO | | |
1003 | ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP); | |
1004 | me4000_outl(dev, tmp, info->ai_context.ctrl_reg); | |
1005 | ||
1006 | /* Set the acquisition mode to single */ | |
1007 | tmp &= ~(ME4000_AI_CTRL_BIT_MODE_0 | ME4000_AI_CTRL_BIT_MODE_1 | | |
1008 | ME4000_AI_CTRL_BIT_MODE_2); | |
1009 | me4000_outl(dev, tmp, info->ai_context.ctrl_reg); | |
1010 | ||
1011 | /* Enable channel list and data fifo */ | |
1012 | tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO; | |
1013 | me4000_outl(dev, tmp, info->ai_context.ctrl_reg); | |
1014 | ||
1015 | /* Generate channel list entry */ | |
1016 | me4000_outl(dev, entry, info->ai_context.channel_list_reg); | |
1017 | ||
1018 | /* Set the timer to maximum sample rate */ | |
1019 | me4000_outl(dev, ME4000_AI_MIN_TICKS, info->ai_context.chan_timer_reg); | |
1020 | me4000_outl(dev, ME4000_AI_MIN_TICKS, | |
1021 | info->ai_context.chan_pre_timer_reg); | |
1022 | ||
1023 | /* Start conversion by dummy read */ | |
1024 | me4000_inl(dev, info->ai_context.start_reg); | |
1025 | ||
1026 | /* Wait until ready */ | |
1027 | udelay(10); | |
1028 | if (!(me4000_inl(dev, info->ai_context. | |
1029 | status_reg) & ME4000_AI_STATUS_BIT_EF_DATA)) { | |
1030 | printk(KERN_ERR | |
1031 | "comedi%d: me4000: me4000_ai_insn_read(): Value not available after wait\n", | |
1032 | dev->minor); | |
1033 | return -EIO; | |
1034 | } | |
1035 | ||
1036 | /* Read value from data fifo */ | |
1037 | lval = me4000_inl(dev, info->ai_context.data_reg) & 0xFFFF; | |
1038 | data[0] = lval ^ 0x8000; | |
1039 | ||
1040 | return 1; | |
1041 | } | |
1042 | ||
e473e912 | 1043 | static int me4000_ai_cancel(comedi_device *dev, comedi_subdevice *s) |
e55c95a3 GG |
1044 | { |
1045 | unsigned long tmp; | |
1046 | ||
1047 | CALL_PDEBUG("In me4000_ai_cancel()\n"); | |
1048 | ||
1049 | /* Stop any running conversion */ | |
1050 | tmp = me4000_inl(dev, info->ai_context.ctrl_reg); | |
1051 | tmp &= ~(ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP); | |
1052 | me4000_outl(dev, tmp, info->ai_context.ctrl_reg); | |
1053 | ||
1054 | /* Clear the control register */ | |
1055 | me4000_outl(dev, 0x0, info->ai_context.ctrl_reg); | |
1056 | ||
1057 | return 0; | |
1058 | } | |
1059 | ||
e473e912 BP |
1060 | static int ai_check_chanlist(comedi_device *dev, |
1061 | comedi_subdevice *s, comedi_cmd *cmd) | |
e55c95a3 GG |
1062 | { |
1063 | int aref; | |
1064 | int i; | |
1065 | ||
1066 | CALL_PDEBUG("In ai_check_chanlist()\n"); | |
1067 | ||
1068 | /* Check whether a channel list is available */ | |
1069 | if (!cmd->chanlist_len) { | |
1070 | printk(KERN_ERR | |
1071 | "comedi%d: me4000: ai_check_chanlist(): No channel list available\n", | |
1072 | dev->minor); | |
1073 | return -EINVAL; | |
1074 | } | |
1075 | ||
1076 | /* Check the channel list size */ | |
1077 | if (cmd->chanlist_len > ME4000_AI_CHANNEL_LIST_COUNT) { | |
1078 | printk(KERN_ERR | |
1079 | "comedi%d: me4000: ai_check_chanlist(): Channel list is to large\n", | |
1080 | dev->minor); | |
1081 | return -EINVAL; | |
1082 | } | |
1083 | ||
1084 | /* Check the pointer */ | |
1085 | if (!cmd->chanlist) { | |
1086 | printk(KERN_ERR | |
1087 | "comedi%d: me4000: ai_check_chanlist(): NULL pointer to channel list\n", | |
1088 | dev->minor); | |
1089 | return -EFAULT; | |
1090 | } | |
1091 | ||
1092 | /* Check whether aref is equal for all entries */ | |
1093 | aref = CR_AREF(cmd->chanlist[0]); | |
1094 | for (i = 0; i < cmd->chanlist_len; i++) { | |
1095 | if (CR_AREF(cmd->chanlist[i]) != aref) { | |
1096 | printk(KERN_ERR | |
1097 | "comedi%d: me4000: ai_check_chanlist(): Mode is not equal for all entries\n", | |
1098 | dev->minor); | |
1099 | return -EINVAL; | |
1100 | } | |
1101 | } | |
1102 | ||
1103 | /* Check whether channels are available for this ending */ | |
1104 | if (aref == SDF_DIFF) { | |
1105 | for (i = 0; i < cmd->chanlist_len; i++) { | |
1106 | if (CR_CHAN(cmd->chanlist[i]) >= | |
1107 | thisboard->ai.diff_count) { | |
1108 | printk(KERN_ERR | |
1109 | "comedi%d: me4000: ai_check_chanlist(): Channel number to high\n", | |
1110 | dev->minor); | |
1111 | return -EINVAL; | |
1112 | } | |
1113 | } | |
1114 | } else { | |
1115 | for (i = 0; i < cmd->chanlist_len; i++) { | |
1116 | if (CR_CHAN(cmd->chanlist[i]) >= thisboard->ai.count) { | |
1117 | printk(KERN_ERR | |
1118 | "comedi%d: me4000: ai_check_chanlist(): Channel number to high\n", | |
1119 | dev->minor); | |
1120 | return -EINVAL; | |
1121 | } | |
1122 | } | |
1123 | } | |
1124 | ||
1125 | /* Check if bipolar is set for all entries when in differential mode */ | |
1126 | if (aref == SDF_DIFF) { | |
1127 | for (i = 0; i < cmd->chanlist_len; i++) { | |
1128 | if (CR_RANGE(cmd->chanlist[i]) != 1 && | |
1129 | CR_RANGE(cmd->chanlist[i]) != 2) { | |
1130 | printk(KERN_ERR | |
1131 | "comedi%d: me4000: ai_check_chanlist(): Bipolar is not selected in differential mode\n", | |
1132 | dev->minor); | |
1133 | return -EINVAL; | |
1134 | } | |
1135 | } | |
1136 | } | |
1137 | ||
1138 | return 0; | |
1139 | } | |
1140 | ||
e473e912 BP |
1141 | static int ai_round_cmd_args(comedi_device *dev, |
1142 | comedi_subdevice *s, | |
1143 | comedi_cmd *cmd, | |
e55c95a3 GG |
1144 | unsigned int *init_ticks, |
1145 | unsigned int *scan_ticks, unsigned int *chan_ticks) | |
1146 | { | |
1147 | ||
1148 | int rest; | |
1149 | ||
1150 | CALL_PDEBUG("In ai_round_cmd_args()\n"); | |
1151 | ||
1152 | *init_ticks = 0; | |
1153 | *scan_ticks = 0; | |
1154 | *chan_ticks = 0; | |
1155 | ||
1156 | PDEBUG("ai_round_cmd_arg(): start_arg = %d\n", cmd->start_arg); | |
1157 | PDEBUG("ai_round_cmd_arg(): scan_begin_arg = %d\n", | |
1158 | cmd->scan_begin_arg); | |
1159 | PDEBUG("ai_round_cmd_arg(): convert_arg = %d\n", cmd->convert_arg); | |
1160 | ||
1161 | if (cmd->start_arg) { | |
1162 | *init_ticks = (cmd->start_arg * 33) / 1000; | |
1163 | rest = (cmd->start_arg * 33) % 1000; | |
1164 | ||
1165 | if (cmd->flags & TRIG_ROUND_NEAREST) { | |
1166 | if (rest > 33) { | |
1167 | (*init_ticks)++; | |
1168 | } | |
1169 | } else if (cmd->flags & TRIG_ROUND_UP) { | |
1170 | if (rest) | |
1171 | (*init_ticks)++; | |
1172 | } | |
1173 | } | |
1174 | ||
1175 | if (cmd->scan_begin_arg) { | |
1176 | *scan_ticks = (cmd->scan_begin_arg * 33) / 1000; | |
1177 | rest = (cmd->scan_begin_arg * 33) % 1000; | |
1178 | ||
1179 | if (cmd->flags & TRIG_ROUND_NEAREST) { | |
1180 | if (rest > 33) { | |
1181 | (*scan_ticks)++; | |
1182 | } | |
1183 | } else if (cmd->flags & TRIG_ROUND_UP) { | |
1184 | if (rest) | |
1185 | (*scan_ticks)++; | |
1186 | } | |
1187 | } | |
1188 | ||
1189 | if (cmd->convert_arg) { | |
1190 | *chan_ticks = (cmd->convert_arg * 33) / 1000; | |
1191 | rest = (cmd->convert_arg * 33) % 1000; | |
1192 | ||
1193 | if (cmd->flags & TRIG_ROUND_NEAREST) { | |
1194 | if (rest > 33) { | |
1195 | (*chan_ticks)++; | |
1196 | } | |
1197 | } else if (cmd->flags & TRIG_ROUND_UP) { | |
1198 | if (rest) | |
1199 | (*chan_ticks)++; | |
1200 | } | |
1201 | } | |
1202 | ||
1203 | PDEBUG("ai_round_cmd_args(): init_ticks = %d\n", *init_ticks); | |
1204 | PDEBUG("ai_round_cmd_args(): scan_ticks = %d\n", *scan_ticks); | |
1205 | PDEBUG("ai_round_cmd_args(): chan_ticks = %d\n", *chan_ticks); | |
1206 | ||
1207 | return 0; | |
1208 | } | |
1209 | ||
e473e912 | 1210 | static void ai_write_timer(comedi_device *dev, |
e55c95a3 GG |
1211 | unsigned int init_ticks, |
1212 | unsigned int scan_ticks, unsigned int chan_ticks) | |
1213 | { | |
1214 | ||
1215 | CALL_PDEBUG("In ai_write_timer()\n"); | |
1216 | ||
1217 | me4000_outl(dev, init_ticks - 1, | |
1218 | info->ai_context.scan_pre_timer_low_reg); | |
1219 | me4000_outl(dev, 0x0, info->ai_context.scan_pre_timer_high_reg); | |
1220 | ||
1221 | if (scan_ticks) { | |
1222 | me4000_outl(dev, scan_ticks - 1, | |
1223 | info->ai_context.scan_timer_low_reg); | |
1224 | me4000_outl(dev, 0x0, info->ai_context.scan_timer_high_reg); | |
1225 | } | |
1226 | ||
1227 | me4000_outl(dev, chan_ticks - 1, info->ai_context.chan_pre_timer_reg); | |
1228 | me4000_outl(dev, chan_ticks - 1, info->ai_context.chan_timer_reg); | |
1229 | } | |
1230 | ||
e473e912 BP |
1231 | static int ai_prepare(comedi_device *dev, |
1232 | comedi_subdevice *s, | |
1233 | comedi_cmd *cmd, | |
e55c95a3 GG |
1234 | unsigned int init_ticks, |
1235 | unsigned int scan_ticks, unsigned int chan_ticks) | |
1236 | { | |
1237 | ||
1238 | unsigned long tmp = 0; | |
1239 | ||
1240 | CALL_PDEBUG("In ai_prepare()\n"); | |
1241 | ||
1242 | /* Write timer arguments */ | |
1243 | ai_write_timer(dev, init_ticks, scan_ticks, chan_ticks); | |
1244 | ||
1245 | /* Reset control register */ | |
1246 | me4000_outl(dev, tmp, info->ai_context.ctrl_reg); | |
1247 | ||
1248 | /* Start sources */ | |
1249 | if ((cmd->start_src == TRIG_EXT && | |
1250 | cmd->scan_begin_src == TRIG_TIMER && | |
1251 | cmd->convert_src == TRIG_TIMER) || | |
1252 | (cmd->start_src == TRIG_EXT && | |
1253 | cmd->scan_begin_src == TRIG_FOLLOW && | |
1254 | cmd->convert_src == TRIG_TIMER)) { | |
1255 | tmp = ME4000_AI_CTRL_BIT_MODE_1 | | |
1256 | ME4000_AI_CTRL_BIT_CHANNEL_FIFO | | |
1257 | ME4000_AI_CTRL_BIT_DATA_FIFO; | |
1258 | } else if (cmd->start_src == TRIG_EXT && | |
1259 | cmd->scan_begin_src == TRIG_EXT && | |
1260 | cmd->convert_src == TRIG_TIMER) { | |
1261 | tmp = ME4000_AI_CTRL_BIT_MODE_2 | | |
1262 | ME4000_AI_CTRL_BIT_CHANNEL_FIFO | | |
1263 | ME4000_AI_CTRL_BIT_DATA_FIFO; | |
1264 | } else if (cmd->start_src == TRIG_EXT && | |
1265 | cmd->scan_begin_src == TRIG_EXT && | |
1266 | cmd->convert_src == TRIG_EXT) { | |
1267 | tmp = ME4000_AI_CTRL_BIT_MODE_0 | | |
1268 | ME4000_AI_CTRL_BIT_MODE_1 | | |
1269 | ME4000_AI_CTRL_BIT_CHANNEL_FIFO | | |
1270 | ME4000_AI_CTRL_BIT_DATA_FIFO; | |
1271 | } else { | |
1272 | tmp = ME4000_AI_CTRL_BIT_MODE_0 | | |
1273 | ME4000_AI_CTRL_BIT_CHANNEL_FIFO | | |
1274 | ME4000_AI_CTRL_BIT_DATA_FIFO; | |
1275 | } | |
1276 | ||
1277 | /* Stop triggers */ | |
1278 | if (cmd->stop_src == TRIG_COUNT) { | |
1279 | me4000_outl(dev, cmd->chanlist_len * cmd->stop_arg, | |
1280 | info->ai_context.sample_counter_reg); | |
1281 | tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ; | |
1282 | } else if (cmd->stop_src == TRIG_NONE && | |
1283 | cmd->scan_end_src == TRIG_COUNT) { | |
1284 | me4000_outl(dev, cmd->scan_end_arg, | |
1285 | info->ai_context.sample_counter_reg); | |
1286 | tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ; | |
1287 | } else { | |
1288 | tmp |= ME4000_AI_CTRL_BIT_HF_IRQ; | |
1289 | } | |
1290 | ||
1291 | /* Write the setup to the control register */ | |
1292 | me4000_outl(dev, tmp, info->ai_context.ctrl_reg); | |
1293 | ||
1294 | /* Write the channel list */ | |
1295 | ai_write_chanlist(dev, s, cmd); | |
1296 | ||
1297 | return 0; | |
1298 | } | |
1299 | ||
e473e912 BP |
1300 | static int ai_write_chanlist(comedi_device *dev, |
1301 | comedi_subdevice *s, comedi_cmd *cmd) | |
e55c95a3 GG |
1302 | { |
1303 | unsigned int entry; | |
1304 | unsigned int chan; | |
1305 | unsigned int rang; | |
1306 | unsigned int aref; | |
1307 | int i; | |
1308 | ||
1309 | CALL_PDEBUG("In ai_write_chanlist()\n"); | |
1310 | ||
1311 | for (i = 0; i < cmd->chanlist_len; i++) { | |
1312 | chan = CR_CHAN(cmd->chanlist[i]); | |
1313 | rang = CR_RANGE(cmd->chanlist[i]); | |
1314 | aref = CR_AREF(cmd->chanlist[i]); | |
1315 | ||
1316 | entry = chan; | |
1317 | ||
1318 | if (rang == 0) { | |
1319 | entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5; | |
1320 | } else if (rang == 1) { | |
1321 | entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10; | |
1322 | } else if (rang == 2) { | |
1323 | entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5; | |
1324 | } else { | |
1325 | entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10; | |
1326 | } | |
1327 | ||
1328 | if (aref == SDF_DIFF) { | |
1329 | entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL; | |
1330 | } else { | |
1331 | entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED; | |
1332 | } | |
1333 | ||
1334 | me4000_outl(dev, entry, info->ai_context.channel_list_reg); | |
1335 | } | |
1336 | ||
1337 | return 0; | |
1338 | } | |
1339 | ||
e473e912 | 1340 | static int me4000_ai_do_cmd(comedi_device *dev, comedi_subdevice *s) |
e55c95a3 GG |
1341 | { |
1342 | int err; | |
1343 | unsigned int init_ticks = 0; | |
1344 | unsigned int scan_ticks = 0; | |
1345 | unsigned int chan_ticks = 0; | |
1346 | comedi_cmd *cmd = &s->async->cmd; | |
1347 | ||
1348 | CALL_PDEBUG("In me4000_ai_do_cmd()\n"); | |
1349 | ||
1350 | /* Reset the analog input */ | |
1351 | err = me4000_ai_cancel(dev, s); | |
1352 | if (err) | |
1353 | return err; | |
1354 | ||
1355 | /* Round the timer arguments */ | |
1356 | err = ai_round_cmd_args(dev, | |
1357 | s, cmd, &init_ticks, &scan_ticks, &chan_ticks); | |
1358 | if (err) | |
1359 | return err; | |
1360 | ||
1361 | /* Prepare the AI for acquisition */ | |
1362 | err = ai_prepare(dev, s, cmd, init_ticks, scan_ticks, chan_ticks); | |
1363 | if (err) | |
1364 | return err; | |
1365 | ||
1366 | /* Start acquistion by dummy read */ | |
1367 | me4000_inl(dev, info->ai_context.start_reg); | |
1368 | ||
1369 | return 0; | |
1370 | } | |
1371 | ||
1372 | /* | |
1373 | * me4000_ai_do_cmd_test(): | |
1374 | * | |
1375 | * The demo cmd.c in ./comedilib/demo specifies 6 return values: | |
1376 | * - success | |
1377 | * - invalid source | |
1378 | * - source conflict | |
1379 | * - invalid argument | |
1380 | * - argument conflict | |
1381 | * - invalid chanlist | |
1382 | * So I tried to adopt this scheme. | |
1383 | */ | |
e473e912 BP |
1384 | static int me4000_ai_do_cmd_test(comedi_device *dev, |
1385 | comedi_subdevice *s, comedi_cmd *cmd) | |
e55c95a3 GG |
1386 | { |
1387 | ||
1388 | unsigned int init_ticks; | |
1389 | unsigned int chan_ticks; | |
1390 | unsigned int scan_ticks; | |
1391 | int err = 0; | |
1392 | ||
1393 | CALL_PDEBUG("In me4000_ai_do_cmd_test()\n"); | |
1394 | ||
1395 | PDEBUG("me4000_ai_do_cmd_test(): subdev = %d\n", cmd->subdev); | |
1396 | PDEBUG("me4000_ai_do_cmd_test(): flags = %08X\n", cmd->flags); | |
1397 | PDEBUG("me4000_ai_do_cmd_test(): start_src = %08X\n", | |
1398 | cmd->start_src); | |
1399 | PDEBUG("me4000_ai_do_cmd_test(): start_arg = %d\n", | |
1400 | cmd->start_arg); | |
1401 | PDEBUG("me4000_ai_do_cmd_test(): scan_begin_src = %08X\n", | |
1402 | cmd->scan_begin_src); | |
1403 | PDEBUG("me4000_ai_do_cmd_test(): scan_begin_arg = %d\n", | |
1404 | cmd->scan_begin_arg); | |
1405 | PDEBUG("me4000_ai_do_cmd_test(): convert_src = %08X\n", | |
1406 | cmd->convert_src); | |
1407 | PDEBUG("me4000_ai_do_cmd_test(): convert_arg = %d\n", | |
1408 | cmd->convert_arg); | |
1409 | PDEBUG("me4000_ai_do_cmd_test(): scan_end_src = %08X\n", | |
1410 | cmd->scan_end_src); | |
1411 | PDEBUG("me4000_ai_do_cmd_test(): scan_end_arg = %d\n", | |
1412 | cmd->scan_end_arg); | |
1413 | PDEBUG("me4000_ai_do_cmd_test(): stop_src = %08X\n", | |
1414 | cmd->stop_src); | |
1415 | PDEBUG("me4000_ai_do_cmd_test(): stop_arg = %d\n", cmd->stop_arg); | |
1416 | PDEBUG("me4000_ai_do_cmd_test(): chanlist = %d\n", | |
1417 | (unsigned int)cmd->chanlist); | |
1418 | PDEBUG("me4000_ai_do_cmd_test(): chanlist_len = %d\n", | |
1419 | cmd->chanlist_len); | |
1420 | ||
1421 | /* Only rounding flags are implemented */ | |
1422 | cmd->flags &= TRIG_ROUND_NEAREST | TRIG_ROUND_UP | TRIG_ROUND_DOWN; | |
1423 | ||
1424 | /* Round the timer arguments */ | |
1425 | ai_round_cmd_args(dev, s, cmd, &init_ticks, &scan_ticks, &chan_ticks); | |
1426 | ||
1427 | /* | |
1428 | * Stage 1. Check if the trigger sources are generally valid. | |
1429 | */ | |
1430 | switch (cmd->start_src) { | |
1431 | case TRIG_NOW: | |
1432 | case TRIG_EXT: | |
1433 | break; | |
1434 | case TRIG_ANY: | |
1435 | cmd->start_src &= TRIG_NOW | TRIG_EXT; | |
1436 | err++; | |
1437 | break; | |
1438 | default: | |
1439 | printk(KERN_ERR | |
1440 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start source\n", | |
1441 | dev->minor); | |
1442 | cmd->start_src = TRIG_NOW; | |
1443 | err++; | |
1444 | } | |
1445 | switch (cmd->scan_begin_src) { | |
1446 | case TRIG_FOLLOW: | |
1447 | case TRIG_TIMER: | |
1448 | case TRIG_EXT: | |
1449 | break; | |
1450 | case TRIG_ANY: | |
1451 | cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT; | |
1452 | err++; | |
1453 | break; | |
1454 | default: | |
1455 | printk(KERN_ERR | |
1456 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan begin source\n", | |
1457 | dev->minor); | |
1458 | cmd->scan_begin_src = TRIG_FOLLOW; | |
1459 | err++; | |
1460 | } | |
1461 | switch (cmd->convert_src) { | |
1462 | case TRIG_TIMER: | |
1463 | case TRIG_EXT: | |
1464 | break; | |
1465 | case TRIG_ANY: | |
1466 | cmd->convert_src &= TRIG_TIMER | TRIG_EXT; | |
1467 | err++; | |
1468 | break; | |
1469 | default: | |
1470 | printk(KERN_ERR | |
1471 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert source\n", | |
1472 | dev->minor); | |
1473 | cmd->convert_src = TRIG_TIMER; | |
1474 | err++; | |
1475 | } | |
1476 | switch (cmd->scan_end_src) { | |
1477 | case TRIG_NONE: | |
1478 | case TRIG_COUNT: | |
1479 | break; | |
1480 | case TRIG_ANY: | |
1481 | cmd->scan_end_src &= TRIG_NONE | TRIG_COUNT; | |
1482 | err++; | |
1483 | break; | |
1484 | default: | |
1485 | printk(KERN_ERR | |
1486 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan end source\n", | |
1487 | dev->minor); | |
1488 | cmd->scan_end_src = TRIG_NONE; | |
1489 | err++; | |
1490 | } | |
1491 | switch (cmd->stop_src) { | |
1492 | case TRIG_NONE: | |
1493 | case TRIG_COUNT: | |
1494 | break; | |
1495 | case TRIG_ANY: | |
1496 | cmd->stop_src &= TRIG_NONE | TRIG_COUNT; | |
1497 | err++; | |
1498 | break; | |
1499 | default: | |
1500 | printk(KERN_ERR | |
1501 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid stop source\n", | |
1502 | dev->minor); | |
1503 | cmd->stop_src = TRIG_NONE; | |
1504 | err++; | |
1505 | } | |
1506 | if (err) { | |
1507 | return 1; | |
1508 | } | |
1509 | ||
1510 | /* | |
1511 | * Stage 2. Check for trigger source conflicts. | |
1512 | */ | |
1513 | if (cmd->start_src == TRIG_NOW && | |
1514 | cmd->scan_begin_src == TRIG_TIMER && | |
1515 | cmd->convert_src == TRIG_TIMER) { | |
1516 | } else if (cmd->start_src == TRIG_NOW && | |
1517 | cmd->scan_begin_src == TRIG_FOLLOW && | |
1518 | cmd->convert_src == TRIG_TIMER) { | |
1519 | } else if (cmd->start_src == TRIG_EXT && | |
1520 | cmd->scan_begin_src == TRIG_TIMER && | |
1521 | cmd->convert_src == TRIG_TIMER) { | |
1522 | } else if (cmd->start_src == TRIG_EXT && | |
1523 | cmd->scan_begin_src == TRIG_FOLLOW && | |
1524 | cmd->convert_src == TRIG_TIMER) { | |
1525 | } else if (cmd->start_src == TRIG_EXT && | |
1526 | cmd->scan_begin_src == TRIG_EXT && | |
1527 | cmd->convert_src == TRIG_TIMER) { | |
1528 | } else if (cmd->start_src == TRIG_EXT && | |
1529 | cmd->scan_begin_src == TRIG_EXT && | |
1530 | cmd->convert_src == TRIG_EXT) { | |
1531 | } else { | |
1532 | printk(KERN_ERR | |
1533 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start trigger combination\n", | |
1534 | dev->minor); | |
1535 | cmd->start_src = TRIG_NOW; | |
1536 | cmd->scan_begin_src = TRIG_FOLLOW; | |
1537 | cmd->convert_src = TRIG_TIMER; | |
1538 | err++; | |
1539 | } | |
1540 | ||
1541 | if (cmd->stop_src == TRIG_NONE && cmd->scan_end_src == TRIG_NONE) { | |
1542 | } else if (cmd->stop_src == TRIG_COUNT && | |
1543 | cmd->scan_end_src == TRIG_NONE) { | |
1544 | } else if (cmd->stop_src == TRIG_NONE && | |
1545 | cmd->scan_end_src == TRIG_COUNT) { | |
1546 | } else if (cmd->stop_src == TRIG_COUNT && | |
1547 | cmd->scan_end_src == TRIG_COUNT) { | |
1548 | } else { | |
1549 | printk(KERN_ERR | |
1550 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid stop trigger combination\n", | |
1551 | dev->minor); | |
1552 | cmd->stop_src = TRIG_NONE; | |
1553 | cmd->scan_end_src = TRIG_NONE; | |
1554 | err++; | |
1555 | } | |
1556 | if (err) { | |
1557 | return 2; | |
1558 | } | |
1559 | ||
1560 | /* | |
1561 | * Stage 3. Check if arguments are generally valid. | |
1562 | */ | |
1563 | if (cmd->chanlist_len < 1) { | |
1564 | printk(KERN_ERR | |
1565 | "comedi%d: me4000: me4000_ai_do_cmd_test(): No channel list\n", | |
1566 | dev->minor); | |
1567 | cmd->chanlist_len = 1; | |
1568 | err++; | |
1569 | } | |
1570 | if (init_ticks < 66) { | |
1571 | printk(KERN_ERR | |
1572 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Start arg to low\n", | |
1573 | dev->minor); | |
1574 | cmd->start_arg = 2000; | |
1575 | err++; | |
1576 | } | |
1577 | if (scan_ticks && scan_ticks < 67) { | |
1578 | printk(KERN_ERR | |
1579 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Scan begin arg to low\n", | |
1580 | dev->minor); | |
1581 | cmd->scan_begin_arg = 2031; | |
1582 | err++; | |
1583 | } | |
1584 | if (chan_ticks < 66) { | |
1585 | printk(KERN_ERR | |
1586 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Convert arg to low\n", | |
1587 | dev->minor); | |
1588 | cmd->convert_arg = 2000; | |
1589 | err++; | |
1590 | } | |
1591 | if (err) { | |
1592 | return 3; | |
1593 | } | |
1594 | ||
1595 | /* | |
1596 | * Stage 4. Check for argument conflicts. | |
1597 | */ | |
1598 | if (cmd->start_src == TRIG_NOW && | |
1599 | cmd->scan_begin_src == TRIG_TIMER && | |
1600 | cmd->convert_src == TRIG_TIMER) { | |
1601 | ||
1602 | /* Check timer arguments */ | |
1603 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
1604 | printk(KERN_ERR | |
1605 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n", | |
1606 | dev->minor); | |
b6c77757 | 1607 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1608 | err++; |
1609 | } | |
1610 | if (chan_ticks < ME4000_AI_MIN_TICKS) { | |
1611 | printk(KERN_ERR | |
1612 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n", | |
1613 | dev->minor); | |
b6c77757 | 1614 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1615 | err++; |
1616 | } | |
1617 | if (scan_ticks <= cmd->chanlist_len * chan_ticks) { | |
1618 | printk(KERN_ERR | |
1619 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan end arg\n", | |
1620 | dev->minor); | |
b6c77757 | 1621 | cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; /* At least one tick more */ |
e55c95a3 GG |
1622 | err++; |
1623 | } | |
1624 | } else if (cmd->start_src == TRIG_NOW && | |
1625 | cmd->scan_begin_src == TRIG_FOLLOW && | |
1626 | cmd->convert_src == TRIG_TIMER) { | |
1627 | ||
1628 | /* Check timer arguments */ | |
1629 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
1630 | printk(KERN_ERR | |
1631 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n", | |
1632 | dev->minor); | |
b6c77757 | 1633 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1634 | err++; |
1635 | } | |
1636 | if (chan_ticks < ME4000_AI_MIN_TICKS) { | |
1637 | printk(KERN_ERR | |
1638 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n", | |
1639 | dev->minor); | |
b6c77757 | 1640 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1641 | err++; |
1642 | } | |
1643 | } else if (cmd->start_src == TRIG_EXT && | |
1644 | cmd->scan_begin_src == TRIG_TIMER && | |
1645 | cmd->convert_src == TRIG_TIMER) { | |
1646 | ||
1647 | /* Check timer arguments */ | |
1648 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
1649 | printk(KERN_ERR | |
1650 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n", | |
1651 | dev->minor); | |
b6c77757 | 1652 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1653 | err++; |
1654 | } | |
1655 | if (chan_ticks < ME4000_AI_MIN_TICKS) { | |
1656 | printk(KERN_ERR | |
1657 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n", | |
1658 | dev->minor); | |
b6c77757 | 1659 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1660 | err++; |
1661 | } | |
1662 | if (scan_ticks <= cmd->chanlist_len * chan_ticks) { | |
1663 | printk(KERN_ERR | |
1664 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan end arg\n", | |
1665 | dev->minor); | |
b6c77757 | 1666 | cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; /* At least one tick more */ |
e55c95a3 GG |
1667 | err++; |
1668 | } | |
1669 | } else if (cmd->start_src == TRIG_EXT && | |
1670 | cmd->scan_begin_src == TRIG_FOLLOW && | |
1671 | cmd->convert_src == TRIG_TIMER) { | |
1672 | ||
1673 | /* Check timer arguments */ | |
1674 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
1675 | printk(KERN_ERR | |
1676 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n", | |
1677 | dev->minor); | |
b6c77757 | 1678 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1679 | err++; |
1680 | } | |
1681 | if (chan_ticks < ME4000_AI_MIN_TICKS) { | |
1682 | printk(KERN_ERR | |
1683 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n", | |
1684 | dev->minor); | |
b6c77757 | 1685 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1686 | err++; |
1687 | } | |
1688 | } else if (cmd->start_src == TRIG_EXT && | |
1689 | cmd->scan_begin_src == TRIG_EXT && | |
1690 | cmd->convert_src == TRIG_TIMER) { | |
1691 | ||
1692 | /* Check timer arguments */ | |
1693 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
1694 | printk(KERN_ERR | |
1695 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n", | |
1696 | dev->minor); | |
b6c77757 | 1697 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1698 | err++; |
1699 | } | |
1700 | if (chan_ticks < ME4000_AI_MIN_TICKS) { | |
1701 | printk(KERN_ERR | |
1702 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n", | |
1703 | dev->minor); | |
b6c77757 | 1704 | cmd->convert_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1705 | err++; |
1706 | } | |
1707 | } else if (cmd->start_src == TRIG_EXT && | |
1708 | cmd->scan_begin_src == TRIG_EXT && | |
1709 | cmd->convert_src == TRIG_EXT) { | |
1710 | ||
1711 | /* Check timer arguments */ | |
1712 | if (init_ticks < ME4000_AI_MIN_TICKS) { | |
1713 | printk(KERN_ERR | |
1714 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n", | |
1715 | dev->minor); | |
b6c77757 | 1716 | cmd->start_arg = 2000; /* 66 ticks at least */ |
e55c95a3 GG |
1717 | err++; |
1718 | } | |
1719 | } | |
1720 | if (cmd->stop_src == TRIG_COUNT) { | |
1721 | if (cmd->stop_arg == 0) { | |
1722 | printk(KERN_ERR | |
1723 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid stop arg\n", | |
1724 | dev->minor); | |
1725 | cmd->stop_arg = 1; | |
1726 | err++; | |
1727 | } | |
1728 | } | |
1729 | if (cmd->scan_end_src == TRIG_COUNT) { | |
1730 | if (cmd->scan_end_arg == 0) { | |
1731 | printk(KERN_ERR | |
1732 | "comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan end arg\n", | |
1733 | dev->minor); | |
1734 | cmd->scan_end_arg = 1; | |
1735 | err++; | |
1736 | } | |
1737 | } | |
1738 | if (err) { | |
1739 | return 4; | |
1740 | } | |
1741 | ||
1742 | /* | |
1743 | * Stage 5. Check the channel list. | |
1744 | */ | |
1745 | if (ai_check_chanlist(dev, s, cmd)) | |
1746 | return 5; | |
1747 | ||
1748 | return 0; | |
1749 | } | |
1750 | ||
1751 | static irqreturn_t me4000_ai_isr(int irq, void *dev_id PT_REGS_ARG) | |
1752 | { | |
1753 | unsigned int tmp; | |
1754 | comedi_device *dev = dev_id; | |
1755 | comedi_subdevice *s = dev->subdevices; | |
1756 | me4000_ai_context_t *ai_context = &info->ai_context; | |
1757 | int i; | |
1758 | int c = 0; | |
1759 | long lval; | |
1760 | ||
1761 | ISR_PDEBUG("me4000_ai_isr() is executed\n"); | |
1762 | ||
1763 | if (!dev->attached) { | |
1764 | ISR_PDEBUG("me4000_ai_isr() premature interrupt\n"); | |
1765 | return IRQ_NONE; | |
1766 | } | |
1767 | ||
1768 | /* Reset all events */ | |
1769 | s->async->events = 0; | |
1770 | ||
1771 | /* Check if irq number is right */ | |
1772 | if (irq != ai_context->irq) { | |
1773 | printk(KERN_ERR | |
1774 | "comedi%d: me4000: me4000_ai_isr(): Incorrect interrupt num: %d\n", | |
1775 | dev->minor, irq); | |
1776 | return IRQ_HANDLED; | |
1777 | } | |
1778 | ||
1779 | if (me4000_inl(dev, | |
1780 | ai_context-> | |
1781 | irq_status_reg) & ME4000_IRQ_STATUS_BIT_AI_HF) { | |
1782 | ISR_PDEBUG | |
1783 | ("me4000_ai_isr(): Fifo half full interrupt occured\n"); | |
1784 | ||
1785 | /* Read status register to find out what happened */ | |
1786 | tmp = me4000_inl(dev, ai_context->ctrl_reg); | |
1787 | ||
1788 | if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) && | |
1789 | !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) && | |
1790 | (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { | |
1791 | ISR_PDEBUG("me4000_ai_isr(): Fifo full\n"); | |
1792 | c = ME4000_AI_FIFO_COUNT; | |
1793 | ||
1794 | /* FIFO overflow, so stop conversion and disable all interrupts */ | |
1795 | tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; | |
1796 | tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | | |
1797 | ME4000_AI_CTRL_BIT_SC_IRQ); | |
1798 | me4000_outl(dev, tmp, ai_context->ctrl_reg); | |
1799 | ||
1800 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; | |
1801 | ||
1802 | printk(KERN_ERR | |
1803 | "comedi%d: me4000: me4000_ai_isr(): FIFO overflow\n", | |
1804 | dev->minor); | |
1805 | } else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA) | |
1806 | && !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) | |
1807 | && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { | |
1808 | ISR_PDEBUG("me4000_ai_isr(): Fifo half full\n"); | |
1809 | ||
1810 | s->async->events |= COMEDI_CB_BLOCK; | |
1811 | ||
1812 | c = ME4000_AI_FIFO_COUNT / 2; | |
1813 | } else { | |
1814 | printk(KERN_ERR | |
1815 | "comedi%d: me4000: me4000_ai_isr(): Can't determine state of fifo\n", | |
1816 | dev->minor); | |
1817 | c = 0; | |
1818 | ||
1819 | /* Undefined state, so stop conversion and disable all interrupts */ | |
1820 | tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; | |
1821 | tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | | |
1822 | ME4000_AI_CTRL_BIT_SC_IRQ); | |
1823 | me4000_outl(dev, tmp, ai_context->ctrl_reg); | |
1824 | ||
1825 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; | |
1826 | ||
1827 | printk(KERN_ERR | |
1828 | "comedi%d: me4000: me4000_ai_isr(): Undefined FIFO state\n", | |
1829 | dev->minor); | |
1830 | } | |
1831 | ||
1832 | ISR_PDEBUG("me4000_ai_isr(): Try to read %d values\n", c); | |
1833 | ||
1834 | for (i = 0; i < c; i++) { | |
1835 | /* Read value from data fifo */ | |
1836 | lval = inl(ai_context->data_reg) & 0xFFFF; | |
1837 | lval ^= 0x8000; | |
1838 | ||
1839 | if (!comedi_buf_put(s->async, lval)) { | |
1840 | /* Buffer overflow, so stop conversion and disable all interrupts */ | |
1841 | tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; | |
1842 | tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | | |
1843 | ME4000_AI_CTRL_BIT_SC_IRQ); | |
1844 | me4000_outl(dev, tmp, ai_context->ctrl_reg); | |
1845 | ||
1846 | s->async->events |= COMEDI_CB_OVERFLOW; | |
1847 | ||
1848 | printk(KERN_ERR | |
1849 | "comedi%d: me4000: me4000_ai_isr(): Buffer overflow\n", | |
1850 | dev->minor); | |
1851 | ||
1852 | break; | |
1853 | } | |
1854 | } | |
1855 | ||
1856 | /* Work is done, so reset the interrupt */ | |
1857 | ISR_PDEBUG("me4000_ai_isr(): Reset fifo half full interrupt\n"); | |
1858 | tmp |= ME4000_AI_CTRL_BIT_HF_IRQ_RESET; | |
1859 | me4000_outl(dev, tmp, ai_context->ctrl_reg); | |
1860 | tmp &= ~ME4000_AI_CTRL_BIT_HF_IRQ_RESET; | |
1861 | me4000_outl(dev, tmp, ai_context->ctrl_reg); | |
1862 | } | |
1863 | ||
1864 | if (me4000_inl(dev, | |
1865 | ai_context-> | |
1866 | irq_status_reg) & ME4000_IRQ_STATUS_BIT_SC) { | |
1867 | ISR_PDEBUG | |
1868 | ("me4000_ai_isr(): Sample counter interrupt occured\n"); | |
1869 | ||
1870 | s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOA; | |
1871 | ||
1872 | /* Acquisition is complete, so stop conversion and disable all interrupts */ | |
1873 | tmp = me4000_inl(dev, ai_context->ctrl_reg); | |
1874 | tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; | |
1875 | tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ); | |
1876 | me4000_outl(dev, tmp, ai_context->ctrl_reg); | |
1877 | ||
1878 | /* Poll data until fifo empty */ | |
1879 | while (inl(ai_context->ctrl_reg) & ME4000_AI_STATUS_BIT_EF_DATA) { | |
1880 | /* Read value from data fifo */ | |
1881 | lval = inl(ai_context->data_reg) & 0xFFFF; | |
1882 | lval ^= 0x8000; | |
1883 | ||
1884 | if (!comedi_buf_put(s->async, lval)) { | |
1885 | printk(KERN_ERR | |
1886 | "comedi%d: me4000: me4000_ai_isr(): Buffer overflow\n", | |
1887 | dev->minor); | |
1888 | s->async->events |= COMEDI_CB_OVERFLOW; | |
1889 | break; | |
1890 | } | |
1891 | } | |
1892 | ||
1893 | /* Work is done, so reset the interrupt */ | |
1894 | ISR_PDEBUG | |
1895 | ("me4000_ai_isr(): Reset interrupt from sample counter\n"); | |
1896 | tmp |= ME4000_AI_CTRL_BIT_SC_IRQ_RESET; | |
1897 | me4000_outl(dev, tmp, ai_context->ctrl_reg); | |
1898 | tmp &= ~ME4000_AI_CTRL_BIT_SC_IRQ_RESET; | |
1899 | me4000_outl(dev, tmp, ai_context->ctrl_reg); | |
1900 | } | |
1901 | ||
1902 | ISR_PDEBUG("me4000_ai_isr(): Events = 0x%X\n", s->async->events); | |
1903 | ||
1904 | if (s->async->events) | |
1905 | comedi_event(dev, s); | |
1906 | ||
1907 | return IRQ_HANDLED; | |
1908 | } | |
1909 | ||
1910 | /*============================================================================= | |
1911 | Analog output section | |
1912 | ===========================================================================*/ | |
1913 | ||
e473e912 BP |
1914 | static int me4000_ao_insn_write(comedi_device *dev, |
1915 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) | |
e55c95a3 GG |
1916 | { |
1917 | ||
1918 | int chan = CR_CHAN(insn->chanspec); | |
1919 | int rang = CR_RANGE(insn->chanspec); | |
1920 | int aref = CR_AREF(insn->chanspec); | |
1921 | unsigned long tmp; | |
1922 | ||
1923 | CALL_PDEBUG("In me4000_ao_insn_write()\n"); | |
1924 | ||
1925 | if (insn->n == 0) { | |
1926 | return 0; | |
1927 | } else if (insn->n > 1) { | |
1928 | printk(KERN_ERR | |
1929 | "comedi%d: me4000: me4000_ao_insn_write(): Invalid instruction length %d\n", | |
1930 | dev->minor, insn->n); | |
1931 | return -EINVAL; | |
1932 | } | |
1933 | ||
1934 | if (chan >= thisboard->ao.count) { | |
1935 | printk(KERN_ERR | |
1936 | "comedi%d: me4000: me4000_ao_insn_write(): Invalid channel %d\n", | |
1937 | dev->minor, insn->n); | |
1938 | return -EINVAL; | |
1939 | } | |
1940 | ||
1941 | if (rang != 0) { | |
1942 | printk(KERN_ERR | |
1943 | "comedi%d: me4000: me4000_ao_insn_write(): Invalid range %d\n", | |
1944 | dev->minor, insn->n); | |
1945 | return -EINVAL; | |
1946 | } | |
1947 | ||
1948 | if (aref != AREF_GROUND && aref != AREF_COMMON) { | |
1949 | printk(KERN_ERR | |
1950 | "comedi%d: me4000: me4000_ao_insn_write(): Invalid aref %d\n", | |
1951 | dev->minor, insn->n); | |
1952 | return -EINVAL; | |
1953 | } | |
1954 | ||
1955 | /* Stop any running conversion */ | |
1956 | tmp = me4000_inl(dev, info->ao_context[chan].ctrl_reg); | |
1957 | tmp |= ME4000_AO_CTRL_BIT_IMMEDIATE_STOP; | |
1958 | me4000_outl(dev, tmp, info->ao_context[chan].ctrl_reg); | |
1959 | ||
1960 | /* Clear control register and set to single mode */ | |
1961 | me4000_outl(dev, 0x0, info->ao_context[chan].ctrl_reg); | |
1962 | ||
1963 | /* Write data value */ | |
1964 | me4000_outl(dev, data[0], info->ao_context[chan].single_reg); | |
1965 | ||
1966 | /* Store in the mirror */ | |
1967 | info->ao_context[chan].mirror = data[0]; | |
1968 | ||
1969 | return 1; | |
1970 | } | |
1971 | ||
e473e912 BP |
1972 | static int me4000_ao_insn_read(comedi_device *dev, |
1973 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) | |
e55c95a3 GG |
1974 | { |
1975 | int chan = CR_CHAN(insn->chanspec); | |
1976 | ||
1977 | if (insn->n == 0) { | |
1978 | return 0; | |
1979 | } else if (insn->n > 1) { | |
1980 | printk("comedi%d: me4000: me4000_ao_insn_read(): Invalid instruction length\n", dev->minor); | |
1981 | return -EINVAL; | |
1982 | } | |
1983 | ||
1984 | data[0] = info->ao_context[chan].mirror; | |
1985 | ||
1986 | return 1; | |
1987 | } | |
1988 | ||
1989 | /*============================================================================= | |
1990 | Digital I/O section | |
1991 | ===========================================================================*/ | |
1992 | ||
e473e912 BP |
1993 | static int me4000_dio_insn_bits(comedi_device *dev, |
1994 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) | |
e55c95a3 GG |
1995 | { |
1996 | ||
1997 | CALL_PDEBUG("In me4000_dio_insn_bits()\n"); | |
1998 | ||
1999 | /* Length of data must be 2 (mask and new data, see below) */ | |
2000 | if (insn->n == 0) { | |
2001 | return 0; | |
2002 | } | |
2003 | if (insn->n != 2) { | |
2004 | printk("comedi%d: me4000: me4000_dio_insn_bits(): Invalid instruction length\n", dev->minor); | |
2005 | return -EINVAL; | |
2006 | } | |
2007 | ||
2008 | /* | |
2009 | * The insn data consists of a mask in data[0] and the new data | |
2010 | * in data[1]. The mask defines which bits we are concerning about. | |
2011 | * The new data must be anded with the mask. | |
2012 | * Each channel corresponds to a bit. | |
2013 | */ | |
2014 | if (data[0]) { | |
2015 | /* Check if requested ports are configured for output */ | |
2016 | if ((s->io_bits & data[0]) != data[0]) | |
2017 | return -EIO; | |
2018 | ||
2019 | s->state &= ~data[0]; | |
2020 | s->state |= data[0] & data[1]; | |
2021 | ||
2022 | /* Write out the new digital output lines */ | |
2023 | me4000_outl(dev, (s->state >> 0) & 0xFF, | |
2024 | info->dio_context.port_0_reg); | |
2025 | me4000_outl(dev, (s->state >> 8) & 0xFF, | |
2026 | info->dio_context.port_1_reg); | |
2027 | me4000_outl(dev, (s->state >> 16) & 0xFF, | |
2028 | info->dio_context.port_2_reg); | |
2029 | me4000_outl(dev, (s->state >> 24) & 0xFF, | |
2030 | info->dio_context.port_3_reg); | |
2031 | } | |
2032 | ||
2033 | /* On return, data[1] contains the value of | |
2034 | the digital input and output lines. */ | |
2035 | data[1] = | |
2036 | ((me4000_inl(dev, info->dio_context.port_0_reg) & 0xFF) << 0) | | |
2037 | ((me4000_inl(dev, info->dio_context.port_1_reg) & 0xFF) << 8) | | |
2038 | ((me4000_inl(dev, info->dio_context.port_2_reg) & 0xFF) << 16) | | |
2039 | ((me4000_inl(dev, info->dio_context.port_3_reg) & 0xFF) << 24); | |
2040 | ||
2041 | return 2; | |
2042 | } | |
2043 | ||
e473e912 BP |
2044 | static int me4000_dio_insn_config(comedi_device *dev, |
2045 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) | |
e55c95a3 GG |
2046 | { |
2047 | unsigned long tmp; | |
2048 | int chan = CR_CHAN(insn->chanspec); | |
2049 | ||
2050 | CALL_PDEBUG("In me4000_dio_insn_config()\n"); | |
2051 | ||
2052 | if (data[0] == INSN_CONFIG_DIO_QUERY) { | |
2053 | data[1] = | |
2054 | (s-> | |
2055 | io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; | |
2056 | return insn->n; | |
2057 | } | |
2058 | ||
2059 | /* | |
2060 | * The input or output configuration of each digital line is | |
2061 | * configured by a special insn_config instruction. chanspec | |
2062 | * contains the channel to be changed, and data[0] contains the | |
2063 | * value COMEDI_INPUT or COMEDI_OUTPUT. | |
2064 | * On the ME-4000 it is only possible to switch port wise (8 bit) | |
2065 | */ | |
2066 | ||
2067 | tmp = me4000_inl(dev, info->dio_context.ctrl_reg); | |
2068 | ||
2069 | if (data[0] == COMEDI_OUTPUT) { | |
2070 | if (chan < 8) { | |
2071 | s->io_bits |= 0xFF; | |
2072 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_0 | | |
2073 | ME4000_DIO_CTRL_BIT_MODE_1); | |
2074 | tmp |= ME4000_DIO_CTRL_BIT_MODE_0; | |
2075 | } else if (chan < 16) { | |
2076 | /* | |
2077 | * Chech for optoisolated ME-4000 version. If one the first | |
2078 | * port is a fixed output port and the second is a fixed input port. | |
2079 | */ | |
2080 | if (!me4000_inl(dev, info->dio_context.dir_reg)) | |
2081 | return -ENODEV; | |
2082 | ||
2083 | s->io_bits |= 0xFF00; | |
2084 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_2 | | |
2085 | ME4000_DIO_CTRL_BIT_MODE_3); | |
2086 | tmp |= ME4000_DIO_CTRL_BIT_MODE_2; | |
2087 | } else if (chan < 24) { | |
2088 | s->io_bits |= 0xFF0000; | |
2089 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_4 | | |
2090 | ME4000_DIO_CTRL_BIT_MODE_5); | |
2091 | tmp |= ME4000_DIO_CTRL_BIT_MODE_4; | |
2092 | } else if (chan < 32) { | |
2093 | s->io_bits |= 0xFF000000; | |
2094 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_6 | | |
2095 | ME4000_DIO_CTRL_BIT_MODE_7); | |
2096 | tmp |= ME4000_DIO_CTRL_BIT_MODE_6; | |
2097 | } else { | |
2098 | return -EINVAL; | |
2099 | } | |
2100 | } else { | |
2101 | if (chan < 8) { | |
2102 | /* | |
2103 | * Chech for optoisolated ME-4000 version. If one the first | |
2104 | * port is a fixed output port and the second is a fixed input port. | |
2105 | */ | |
2106 | if (!me4000_inl(dev, info->dio_context.dir_reg)) | |
2107 | return -ENODEV; | |
2108 | ||
2109 | s->io_bits &= ~0xFF; | |
2110 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_0 | | |
2111 | ME4000_DIO_CTRL_BIT_MODE_1); | |
2112 | } else if (chan < 16) { | |
2113 | s->io_bits &= ~0xFF00; | |
2114 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_2 | | |
2115 | ME4000_DIO_CTRL_BIT_MODE_3); | |
2116 | } else if (chan < 24) { | |
2117 | s->io_bits &= ~0xFF0000; | |
2118 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_4 | | |
2119 | ME4000_DIO_CTRL_BIT_MODE_5); | |
2120 | } else if (chan < 32) { | |
2121 | s->io_bits &= ~0xFF000000; | |
2122 | tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_6 | | |
2123 | ME4000_DIO_CTRL_BIT_MODE_7); | |
2124 | } else { | |
2125 | return -EINVAL; | |
2126 | } | |
2127 | } | |
2128 | ||
2129 | me4000_outl(dev, tmp, info->dio_context.ctrl_reg); | |
2130 | ||
2131 | return 1; | |
2132 | } | |
2133 | ||
2134 | /*============================================================================= | |
2135 | Counter section | |
2136 | ===========================================================================*/ | |
2137 | ||
e473e912 | 2138 | static int cnt_reset(comedi_device *dev, unsigned int channel) |
e55c95a3 GG |
2139 | { |
2140 | ||
2141 | CALL_PDEBUG("In cnt_reset()\n"); | |
2142 | ||
2143 | switch (channel) { | |
2144 | case 0: | |
2145 | me4000_outb(dev, 0x30, info->cnt_context.ctrl_reg); | |
2146 | me4000_outb(dev, 0x00, info->cnt_context.counter_0_reg); | |
2147 | me4000_outb(dev, 0x00, info->cnt_context.counter_0_reg); | |
2148 | break; | |
2149 | case 1: | |
2150 | me4000_outb(dev, 0x70, info->cnt_context.ctrl_reg); | |
2151 | me4000_outb(dev, 0x00, info->cnt_context.counter_1_reg); | |
2152 | me4000_outb(dev, 0x00, info->cnt_context.counter_1_reg); | |
2153 | break; | |
2154 | case 2: | |
2155 | me4000_outb(dev, 0xB0, info->cnt_context.ctrl_reg); | |
2156 | me4000_outb(dev, 0x00, info->cnt_context.counter_2_reg); | |
2157 | me4000_outb(dev, 0x00, info->cnt_context.counter_2_reg); | |
2158 | break; | |
2159 | default: | |
2160 | printk(KERN_ERR | |
2161 | "comedi%d: me4000: cnt_reset(): Invalid channel\n", | |
2162 | dev->minor); | |
2163 | return -EINVAL; | |
2164 | } | |
2165 | ||
2166 | return 0; | |
2167 | } | |
2168 | ||
e473e912 | 2169 | static int cnt_config(comedi_device *dev, unsigned int channel, |
e55c95a3 GG |
2170 | unsigned int mode) |
2171 | { | |
2172 | int tmp = 0; | |
2173 | ||
2174 | CALL_PDEBUG("In cnt_config()\n"); | |
2175 | ||
2176 | switch (channel) { | |
2177 | case 0: | |
2178 | tmp |= ME4000_CNT_COUNTER_0; | |
2179 | break; | |
2180 | case 1: | |
2181 | tmp |= ME4000_CNT_COUNTER_1; | |
2182 | break; | |
2183 | case 2: | |
2184 | tmp |= ME4000_CNT_COUNTER_2; | |
2185 | break; | |
2186 | default: | |
2187 | printk(KERN_ERR | |
2188 | "comedi%d: me4000: cnt_config(): Invalid channel\n", | |
2189 | dev->minor); | |
2190 | return -EINVAL; | |
2191 | } | |
2192 | ||
2193 | switch (mode) { | |
2194 | case 0: | |
2195 | tmp |= ME4000_CNT_MODE_0; | |
2196 | break; | |
2197 | case 1: | |
2198 | tmp |= ME4000_CNT_MODE_1; | |
2199 | break; | |
2200 | case 2: | |
2201 | tmp |= ME4000_CNT_MODE_2; | |
2202 | break; | |
2203 | case 3: | |
2204 | tmp |= ME4000_CNT_MODE_3; | |
2205 | break; | |
2206 | case 4: | |
2207 | tmp |= ME4000_CNT_MODE_4; | |
2208 | break; | |
2209 | case 5: | |
2210 | tmp |= ME4000_CNT_MODE_5; | |
2211 | break; | |
2212 | default: | |
2213 | printk(KERN_ERR | |
2214 | "comedi%d: me4000: cnt_config(): Invalid counter mode\n", | |
2215 | dev->minor); | |
2216 | return -EINVAL; | |
2217 | } | |
2218 | ||
2219 | /* Write the control word */ | |
2220 | tmp |= 0x30; | |
2221 | me4000_outb(dev, tmp, info->cnt_context.ctrl_reg); | |
2222 | ||
2223 | return 0; | |
2224 | } | |
2225 | ||
e473e912 BP |
2226 | static int me4000_cnt_insn_config(comedi_device *dev, |
2227 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) | |
e55c95a3 GG |
2228 | { |
2229 | ||
2230 | int err; | |
2231 | ||
2232 | CALL_PDEBUG("In me4000_cnt_insn_config()\n"); | |
2233 | ||
2234 | switch (data[0]) { | |
2235 | case GPCT_RESET: | |
2236 | if (insn->n != 1) { | |
2237 | printk(KERN_ERR | |
2238 | "comedi%d: me4000: me4000_cnt_insn_config(): Invalid instruction length%d\n", | |
2239 | dev->minor, insn->n); | |
2240 | return -EINVAL; | |
2241 | } | |
2242 | ||
2243 | err = cnt_reset(dev, insn->chanspec); | |
2244 | if (err) | |
2245 | return err; | |
2246 | break; | |
2247 | case GPCT_SET_OPERATION: | |
2248 | if (insn->n != 2) { | |
2249 | printk(KERN_ERR | |
2250 | "comedi%d: me4000: me4000_cnt_insn_config(): Invalid instruction length%d\n", | |
2251 | dev->minor, insn->n); | |
2252 | return -EINVAL; | |
2253 | } | |
2254 | ||
2255 | err = cnt_config(dev, insn->chanspec, data[1]); | |
2256 | if (err) | |
2257 | return err; | |
2258 | break; | |
2259 | default: | |
2260 | printk(KERN_ERR | |
2261 | "comedi%d: me4000: me4000_cnt_insn_config(): Invalid instruction\n", | |
2262 | dev->minor); | |
2263 | return -EINVAL; | |
2264 | } | |
2265 | ||
2266 | return 2; | |
2267 | } | |
2268 | ||
e473e912 BP |
2269 | static int me4000_cnt_insn_read(comedi_device *dev, |
2270 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) | |
e55c95a3 GG |
2271 | { |
2272 | ||
2273 | unsigned short tmp; | |
2274 | ||
2275 | CALL_PDEBUG("In me4000_cnt_insn_read()\n"); | |
2276 | ||
2277 | if (insn->n == 0) { | |
2278 | return 0; | |
2279 | } | |
2280 | if (insn->n > 1) { | |
2281 | printk(KERN_ERR | |
2282 | "comedi%d: me4000: me4000_cnt_insn_read(): Invalid instruction length %d\n", | |
2283 | dev->minor, insn->n); | |
2284 | return -EINVAL; | |
2285 | } | |
2286 | ||
2287 | switch (insn->chanspec) { | |
2288 | case 0: | |
2289 | tmp = me4000_inb(dev, info->cnt_context.counter_0_reg); | |
2290 | data[0] = tmp; | |
2291 | tmp = me4000_inb(dev, info->cnt_context.counter_0_reg); | |
2292 | data[0] |= tmp << 8; | |
2293 | break; | |
2294 | case 1: | |
2295 | tmp = me4000_inb(dev, info->cnt_context.counter_1_reg); | |
2296 | data[0] = tmp; | |
2297 | tmp = me4000_inb(dev, info->cnt_context.counter_1_reg); | |
2298 | data[0] |= tmp << 8; | |
2299 | break; | |
2300 | case 2: | |
2301 | tmp = me4000_inb(dev, info->cnt_context.counter_2_reg); | |
2302 | data[0] = tmp; | |
2303 | tmp = me4000_inb(dev, info->cnt_context.counter_2_reg); | |
2304 | data[0] |= tmp << 8; | |
2305 | break; | |
2306 | default: | |
2307 | printk(KERN_ERR | |
2308 | "comedi%d: me4000: me4000_cnt_insn_read(): Invalid channel %d\n", | |
2309 | dev->minor, insn->chanspec); | |
2310 | return -EINVAL; | |
2311 | } | |
2312 | ||
2313 | return 1; | |
2314 | } | |
2315 | ||
e473e912 BP |
2316 | static int me4000_cnt_insn_write(comedi_device *dev, |
2317 | comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) | |
e55c95a3 GG |
2318 | { |
2319 | ||
2320 | unsigned short tmp; | |
2321 | ||
2322 | CALL_PDEBUG("In me4000_cnt_insn_write()\n"); | |
2323 | ||
2324 | if (insn->n == 0) { | |
2325 | return 0; | |
2326 | } else if (insn->n > 1) { | |
2327 | printk(KERN_ERR | |
2328 | "comedi%d: me4000: me4000_cnt_insn_write(): Invalid instruction length %d\n", | |
2329 | dev->minor, insn->n); | |
2330 | return -EINVAL; | |
2331 | } | |
2332 | ||
2333 | switch (insn->chanspec) { | |
2334 | case 0: | |
2335 | tmp = data[0] & 0xFF; | |
2336 | me4000_outb(dev, tmp, info->cnt_context.counter_0_reg); | |
2337 | tmp = (data[0] >> 8) & 0xFF; | |
2338 | me4000_outb(dev, tmp, info->cnt_context.counter_0_reg); | |
2339 | break; | |
2340 | case 1: | |
2341 | tmp = data[0] & 0xFF; | |
2342 | me4000_outb(dev, tmp, info->cnt_context.counter_1_reg); | |
2343 | tmp = (data[0] >> 8) & 0xFF; | |
2344 | me4000_outb(dev, tmp, info->cnt_context.counter_1_reg); | |
2345 | break; | |
2346 | case 2: | |
2347 | tmp = data[0] & 0xFF; | |
2348 | me4000_outb(dev, tmp, info->cnt_context.counter_2_reg); | |
2349 | tmp = (data[0] >> 8) & 0xFF; | |
2350 | me4000_outb(dev, tmp, info->cnt_context.counter_2_reg); | |
2351 | break; | |
2352 | default: | |
2353 | printk(KERN_ERR | |
2354 | "comedi%d: me4000: me4000_cnt_insn_write(): Invalid channel %d\n", | |
2355 | dev->minor, insn->chanspec); | |
2356 | return -EINVAL; | |
2357 | } | |
2358 | ||
2359 | return 1; | |
2360 | } | |
2361 | ||
2362 | COMEDI_PCI_INITCLEANUP(driver_me4000, me4000_pci_table); |