2 * comedi/drivers/mf624.c
3 * Code for a Humusoft MF624 driver
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
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.
25 Description: A comedi driver for Humusoft MF624 PCI Card
26 Devices: Humusoft MF624 Multifunction I/O Card
27 Author: Jean-Matthieu Bourgeot, Francois Poulain, (Minor changes -- Rostislav Lisovy)
28 Updated: Wed, 19 Sep 2007 14:29:43 +0200
31 This driver is a driver for the Humusoft MF624 PCI Card.
34 * 8 channel 14 bits ADC,
35 * 8 channel 14 bits DAC,
38 * 4 quadrature encoder input,
42 A/D Converter : Supported on subdevice 0,
43 D/A Converter : Supported on subdevice 1,
44 Digital Inputs : Supported on subdevice 2,
45 Digital Outputs : Supported on subdevice 3,
46 Counters/Timers : Not yet supported.
47 Quadrature Encoder Inputs : Not yet supported
48 IRQ : Not yet supported.
51 Simultaneous D/A update not yet supported.
53 Configuration Options:
56 #include "../comedidev.h"
57 #include <linux/pci.h>
59 typedef unsigned int lsampl_t;
62 #define MF624_EXIT_FAILURE -1
64 #define PCI_VENDOR_ID_MF624 0x186c
65 #define PCI_DEVICE_ID_MF624 0x0624
67 /* Subdevice numbers */
68 #define MF624_SUBDEV_COUNT 4 /* Nr of subdevices */
69 #define MF624_DO_SUBDEV 3
70 #define MF624_DI_SUBDEV 2
71 #define MF624_AO_SUBDEV 1
72 #define MF624_AI_SUBDEV 0
75 /* BADR0 Memory Map */
76 #define MF624_GPIOC 0x54
77 #define MF624_INTCSR 0x4C
79 /* BADR1 Memory Map */
80 #define MF624_ADCTRL 0x00
81 #define MF624_ADDATA 0x00
82 #define MF624_DIN 0x10
83 #define MF624_DOUT 0x10
84 #define MF624_ADSTART 0x20
85 #define MF624_DA0 0x20
87 /* BADR2 Memory Map */
88 #define MF624_CTRXMODE 0x00
89 #define MF624_CTRXSTATUS 0x00
90 #define MF624_CTRXCTRL 0x60
91 #define MF624_CTRXA 0x04
92 #define MF624_CTRXDATA 0x04
93 #define MF624_CTRXB 0x08
94 #define MF624_IRCCTRL 0x6C
95 #define MF624_IRCSTATUS 0x6C
96 #define MF624_ENC_READ 0x70
100 #define MF624_GPIOC_EOLC (1 << 17) /* End of last A/D conversion */
101 #define MF624_GPIOC_LDAC (1 << 23) /* Enable Load D/A converter */
102 #define MF624_GPIOC_DACEN (1 << 26) /* Enable D/A outputs */
105 typedef struct mf624_board_struct{
107 unsigned short device_id;
108 unsigned int ai_chans;
109 unsigned int ai_bits;
110 unsigned int ao_chans;
111 unsigned int ao_bits;
112 unsigned int di_chans;
113 unsigned int do_chans;
117 static const mf624_board mf624_boards[] = {
119 name: "mf624", /* device name */
120 device_id: PCI_DEVICE_ID_MF624, /* PCI dev ID */
121 ai_chans: 8, /* Num of ADC channels */
122 ai_bits: 14, /* Num of ADC bits */
123 ao_chans: 8, /* Num of DAC channels */
124 ao_bits: 14, /* Num of DAC bits */
125 di_chans: 8, /* Num of digital in */
126 do_chans: 8, /* Num of digital out */
130 /* Number of boards */
131 #define N_BOARDS (sizeof(mf624_boards) / sizeof(mf624_board))
134 /* Private data structure */
138 /* would be useful for a PCI device */
139 struct pci_dev *pci_dev;
142 //resource_size_t iobase;
152 /* Used for AO readback */
153 lsampl_t ao_readback[8];
156 #define devpriv ((mf624_private *) dev->private)
159 /* Attach/detach functions declaration */
160 /*static int mf624_attach(struct comedi_device *dev, struct comedi_devconfig *it);
161 static int mf624_detach(struct comedi_device *dev);
164 /* Analog functions operation to be attached */
165 static int mf624_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
166 struct comedi_insn *insn, lsampl_t *data);
167 static int mf624_ai_cfg(struct comedi_device *dev, struct comedi_subdevice *s,
168 struct comedi_insn *insn, lsampl_t *data);
169 static int mf624_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
170 struct comedi_insn *insn, lsampl_t *data);
171 static int mf624_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
172 struct comedi_insn *insn, lsampl_t *data);
173 static int mf624_ao_cfg(struct comedi_device *dev, struct comedi_subdevice *s,
174 struct comedi_insn *insn, lsampl_t *data);
177 /* Digital functions operation to be attached */
178 static int mf624_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
179 struct comedi_insn *insn, lsampl_t *data);
180 static int mf624_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
181 struct comedi_insn *insn, lsampl_t *data);
183 /*static irqreturn_t mf624_interrupt(int irq, void *d, struct pt_regs *regs);*/
187 #define thisboard ((mf624_board *)dev->board_ptr)
189 * Attach is called by the Comedi core to configure the driver
190 * for a particular board. If you specified a board_name array
191 * in the driver structure, dev->board_ptr contains that
194 static int mf624_attach(struct comedi_device *dev, struct comedi_devconfig *it)
196 struct comedi_subdevice *s;
197 struct pci_dev* pcidev;
198 unsigned int index, channel, status;
200 printk("comedi%d: mf624: driver: Bourgeot - Poulain 2006-2007\n", dev->minor);
201 printk("This is an experimental version, you can report \
202 some remarks or problems to fpoulain@gmail.com\n");
205 /* Allocate the private structure area. alloc_private() is a
206 convenient macro defined in comedidev.h. */
207 if(alloc_private(dev, sizeof(mf624_private)) < 0) {
211 /* Probe the device to determine what device in the series it is */
212 for(pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
214 pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
216 if(pcidev->vendor != PCI_VENDOR_ID_MF624) {
220 /* loop through cards supported by this driver */
221 for(index = 0; index < N_BOARDS ; index++)
223 if(mf624_boards[index].device_id != pcidev->device) {
227 /* was a particular bus/slot requested ? */
228 if(it->options[0] || it->options[1])
230 /* are we on the wrong bus/slot ? */
231 if(pcidev->bus->number != it->options[0] ||
232 PCI_SLOT(pcidev->devfn) != it->options[1]) {
237 devpriv->pci_dev = pcidev;
238 dev->board_ptr = mf624_boards + index;
243 printk("comedi%d: mf624: No supported Humusoft card \
244 found on requested position\n", dev->minor);
246 comedi_error(dev, "No supported Humusoft card found on requested position\n");
250 printk("comedi%d: mf624: Found %s on bus %i, slot %i\n", dev->minor,
251 mf624_boards[index].name, pcidev->bus->number, PCI_SLOT(pcidev->devfn));
253 /* Enable PCI device and reserve I/O ports. */
254 if(pci_enable_device(pcidev)) {
256 printk("comedi%d: mf624: Failed to enable PCI device\n", dev->minor);
258 comedi_error(dev, "Failed to enable PCI device\n");
261 if(pci_request_regions(pcidev, "mf624")) {
263 printk("comedi%d: mf624: I/O port conflict\n", dev->minor);
265 comedi_error(dev, "I/O port conflict\n");
269 devpriv->BADR0 = pci_resource_start(devpriv->pci_dev, 0);
270 devpriv->BADR1 = pci_resource_start(devpriv->pci_dev, 2);
271 devpriv->BADR2 = pci_resource_start(devpriv->pci_dev, 4);
273 devpriv->IO_BADR0 = pci_ioremap_bar(devpriv->pci_dev, 0);
274 devpriv->IO_BADR1 = pci_ioremap_bar(devpriv->pci_dev, 2);
275 devpriv->IO_BADR2 = pci_ioremap_bar(devpriv->pci_dev, 4);
278 printk ("comedi%d: mf624: PCI Resource 0 addr %lx \n", dev->minor, devpriv->BADR0);
279 printk ("comedi%d: mf624: PCI Resource 1 addr %lx \n", dev->minor, devpriv->BADR1);
280 printk ("comedi%d: mf624: PCI Resource 2 addr %lx \n", dev->minor, devpriv->BADR2);
282 printk ("comedi%d: mf624: IO_BADR0 addr %p \n", dev->minor, devpriv->IO_BADR0);
283 printk ("comedi%d: mf624: IO_BADR1(2) addr %p \n", dev->minor, devpriv->IO_BADR1);
284 printk ("comedi%d: mf624: IO_BADR2(4) addr %p \n", dev->minor, devpriv->IO_BADR2);
287 dev->board_name = thisboard->name;
290 * Allocate the subdevice structures. alloc_subdevice() is a
291 * convenient macro defined in comedidev.h.
293 if(alloc_subdevices(dev, MF624_SUBDEV_COUNT) < 0) {
297 /* Analog single ended (ground) input subdevice */
298 s = dev->subdevices + 0;
299 s->type = COMEDI_SUBD_AI;
300 s->subdev_flags = SDF_READABLE|SDF_GROUND;
301 s->n_chan = thisboard->ai_chans;
302 s->maxdata = (1<<thisboard->ai_bits)-1;
303 s->range_table = &range_bipolar10;
305 s->insn_read = mf624_ai_rinsn;
306 s->insn_config = mf624_ai_cfg;
308 /* Analog output subdevice */
309 s = dev->subdevices + 1;
310 s->type = COMEDI_SUBD_AO;
311 s->subdev_flags = SDF_WRITABLE;
312 s->n_chan = thisboard->ao_chans;
313 s->maxdata = (1<<thisboard->ao_bits)-1;
314 s->range_table = &range_bipolar10;
315 s->insn_write = mf624_ao_winsn;
316 s->insn_read = mf624_ao_rinsn;
317 s->insn_config = mf624_ao_cfg;
319 /* Digital input subdevice */
320 s = dev->subdevices + 2;
321 s->type = COMEDI_SUBD_DI;
322 s->subdev_flags = SDF_READABLE;
323 s->n_chan = thisboard->di_chans;
325 s->range_table = &range_digital;
326 s->insn_bits = mf624_di_insn_bits;
328 /* Digital output subdevice */
329 s = dev->subdevices + 3;
330 s->type = COMEDI_SUBD_DO;
331 s->subdev_flags = SDF_WRITABLE;
332 s->n_chan = thisboard->do_chans;
334 s->range_table = &range_digital;
335 s->insn_bits = mf624_do_insn_bits;
338 printk("comedi%d: mf624: Driver attached\n", dev->minor);
341 status = ioread32(devpriv->IO_BADR0 + MF624_GPIOC) | MF624_GPIOC_DACEN;
342 iowrite32(status, devpriv->IO_BADR0 + MF624_GPIOC);
344 /* Initialise Interrupt Control Status */
345 iowrite32(0x00000000, devpriv->IO_BADR0 + MF624_INTCSR);
347 /* Initialise analog outputs to zero */
348 for(channel = 0; channel < thisboard->ao_chans; channel ++) {
349 iowrite16(0x2000, devpriv->IO_BADR1 + MF624_DA0 + 2*channel);
351 devpriv->ao_readback[channel] = 0x2000;
354 //printk("comedi%d: mf624: Board initialized\n", dev->minor);
360 * _detach is called to deconfigure a device. It should deallocate
362 * This function is also called when _attach() fails, so it should be
363 * careful not to release resources that were not necessarily
364 * allocated by _attach(). dev->private and dev->subdevices are
365 * deallocated automatically by the core.
367 static int mf624_detach(struct comedi_device *dev)
369 //printk("comedi%d: mf624: remove\n", dev->minor);
371 if(devpriv && devpriv->pci_dev)
375 pci_release_regions(devpriv->pci_dev);
376 pci_disable_device(devpriv->pci_dev);
377 //printk("comedi%d: mf624: remove2\n", dev->minor);
379 pci_dev_put(devpriv->pci_dev);
380 //printk("comedi%d: mf624: remove3\n", dev->minor);
386 /* read n samples on Analog Input channel */
387 static int mf624_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
388 struct comedi_insn *insn, lsampl_t *data)
390 unsigned int dat, n, i, status;
391 unsigned int chan = CR_CHAN(insn->chanspec);
394 printk("comedi%d: mf624: mf624_ai_rinsn called \n", dev->minor);
397 /* write channel to multiplexer */
398 iowrite16(1 << chan, devpriv->IO_BADR1 + MF624_ADCTRL);
400 /* convert n samples */
401 for(n = 0; n < insn->n; n ++){
402 /* trigger conversion */
403 dat = ioread16(devpriv->IO_BADR1 + MF624_ADSTART);
406 /* wait for conversion to end */
407 for(i = 0; i < TIMEOUT; i++){
409 status = ioread32(devpriv->IO_BADR0 + MF624_GPIOC);
410 if(!(status & MF624_GPIOC_EOLC)) {
415 printk("comedi%d: mf624: _ai_rinsn: conversion timeout !\n", dev->minor);
416 comedi_error(dev, "Conversion timeout !\n");
421 dat = ioread16(devpriv->IO_BADR1 + MF624_ADDATA);
423 /* mangle the data as necessary */
424 dat ^= 1 << (thisboard->ai_bits-1);
429 /* return the number of samples read/written */
434 /* Analog input configuration */
435 static int mf624_ai_cfg(struct comedi_device *dev, struct comedi_subdevice *s,
436 struct comedi_insn *insn, lsampl_t *data)
439 printk("comedi%d: mf624: _insn_ai_cfg called\n", dev->minor);
445 /* write n samples on Analog Output channel */
446 static int mf624_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
447 struct comedi_insn *insn, lsampl_t *data)
449 unsigned int i, status;
450 unsigned int chan = CR_CHAN(insn->chanspec);
452 printk("comedi%d: mf624: _ao_winsn called\n", dev->minor);
454 status = ioread32(devpriv->IO_BADR0 + MF624_GPIOC) | MF624_GPIOC_DACEN;
455 iowrite32(status, devpriv->IO_BADR0 + MF624_GPIOC);
457 /* Writing a list of values to an AO channel is probably not
458 * very useful, but that's how the interface is defined. */
459 for(i = 0; i < insn->n; i++){
460 iowrite16(data[i], devpriv->IO_BADR1 + MF624_DA0 + 2*chan);
461 devpriv->ao_readback[chan] = data[i];
463 printk("comedi%d: mf624: _ao_winsn: wrote at address \
464 %i data %i ", dev->minor, MF624_DA0 + 2*chan , data[i] );
468 /* return the number of samples read/written */
473 /* Analog output configuration */
474 static int mf624_ao_cfg(struct comedi_device *dev, struct comedi_subdevice *s,
475 struct comedi_insn *insn, lsampl_t *data)
478 printk("comedi%d: mf624: _ao_cfg called\n", dev->minor);
484 /* AO subdevices should have a read insn as well as a write insn.
485 * Usually this means copying a value stored in devpriv. */
486 static int mf624_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
487 struct comedi_insn *insn, lsampl_t *data)
489 unsigned int i, chan = CR_CHAN(insn->chanspec);
491 for(i = 0; i < insn->n; i++) {
492 data[i] = devpriv->ao_readback[chan];
498 /* Write digital data */
499 static int mf624_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
500 struct comedi_insn *insn, lsampl_t *data)
503 printk("comedi%d: mf624: _do_insn_bits called \
504 with data: %d %d\n", dev->minor, data[0], data[1]);
511 s->state &= ~data[0];
512 s->state |= data[0] & data[1];
514 printk ("comedi%d: mf624: _do_insn_bits: out: %d \
515 on %lx\n", dev->minor, s->state, devpriv->IO_BADR1 + MF624_DOUT );
517 iowrite16(s->state, devpriv->IO_BADR1 + MF624_DOUT);
522 /* Read digital data */
523 static int mf624_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
524 struct comedi_insn *insn, lsampl_t *data)
527 printk("comedi%d: mf624: _di_insn_bits called with \
528 data: %d %d\n", dev->minor, data[0], data[1]);
534 data[1] = ioread16(devpriv->IO_BADR1 + MF624_DIN);
536 printk("comedi%d: mf624: _di_insn_bits read \
537 data: %d\n", dev->minor, data[1]);
543 static struct pci_device_id mf624_pci_table[] __devinitdata = {
544 { PCI_VENDOR_ID_MF624, PCI_DEVICE_ID_MF624, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
547 MODULE_DEVICE_TABLE(pci, mf624_pci_table);
550 * The comedi_driver structure tells the Comedi core module
551 * which functions to call to configure/deconfigure (attach/detach)
552 * the board, and also about the kernel module that contains
555 static struct comedi_driver driver_mf624 = {
556 driver_name: "mf624",
558 attach: mf624_attach,
559 detach: mf624_detach,
563 MODULE_AUTHOR("Francois Poulain <fpoulain@gmail.com>;\
564 (Rostislav Lisovy <lisovy@gmail.com>)");
565 MODULE_DESCRIPTION("Humusoft MF624 Multifunction I/O Card");
566 MODULE_LICENSE("GPL");
568 COMEDI_INITCLEANUP(driver_mf624);