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
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 : Supported on subdevice 4,
47 Quadrature Encoder Inputs : Supported on subdevice 5,
48 IRQ : Not yet supported.
51 Simultaneous D/A update not yet supported.
53 Configuration Options:
57 #include <linux/comedidev.h>
58 #include <linux/pci.h>
61 MODULE_AUTHOR("Francois Poulain <fpoulain AT gmail DOT com>");
62 MODULE_DESCRIPTION("Humusoft MF624 Multifunction I/O Card");
63 MODULE_LICENSE("GPL");
65 /* Some definition ... */
66 /* FIXME : adopt a REAL guideline for naming macros ... */
67 /* FIXME : adopt a REAL guideline for presenting functions */
68 /* FIXME : verify ALL types used in the functions */
72 #define MF624_EXIT_FAILURE -1
74 /* Addresses of the registers and usefull definitions
75 * for more informations, see the programming manual
76 * available near http://www2.humusoft.cz/www/datacq/manuals/mf624um.pdf */
78 /* BADR0 Memory Map */
79 #define MF624_GPIOC 0x54
80 #define MF624_INTCSR 0x4C
82 /* BADR1 Memory Map */
83 #define MF624_ADCTRL 0x00
84 #define MF624_ADDATA 0x00
85 #define MF624_DIN 0x10
86 #define MF624_DOUT 0x10
87 #define MF624_ADSTART 0x20
88 #define MF624_DA0 0x20
90 /* BADR2 Memory Map */
91 #define MF624_CTRXMODE 0x00
92 #define MF624_CTRXSTATUS 0x00
93 #define MF624_CTRXCTRL 0x60
94 #define MF624_CTRXA 0x04
95 #define MF624_CTRXDATA 0x04
96 #define MF624_CTRXB 0x08
97 #define MF624_IRCCTRL 0x6C
98 #define MF624_IRCSTATUS 0x6C
99 #define MF624_ENC_READ 0x70
102 #define MF624_GPIOC_EOLC 0x00020000 /* End of last conversion, bit 17 */
103 #define MF624_GPIOC_LDAC 0x00800000 /* Enable Load D/A converter, bit 23 */
104 #define MF624_GPIOC_DACEN 0x04000000 /* Enable D/A outputs, bit 26 */
106 /* Some constants for handling Counter Control Register */
107 #define MF624_CTR_START 0x01
108 #define MF624_CTR_STOP 0x02
109 #define MF624_CTR_LOAD 0x04
110 #define MF624_CTR_RESET 0x08
111 #define MF624_CTR_OUT_SET 0x10
112 #define MF624_CTR_OUT_RESET 0x20
114 /* Multiply the following constant by one above to obtain global behavior on all timers' channels */
115 #define MF624_CTR_GLOB 0x01041041
117 /* Some constants for handling IRC Control Register (quadrature encoders) */
118 /* All are not present, it's under heavy development */
119 #define MF624_IRC_MODE_RAISING 0x01
120 #define MF624_IRC_COUNT_ENABLE 0x00
121 #define MF624_IRC_COUNT_DISABLE 0x04
122 #define MF624_IRC_RESET_ENABLE 0x10
123 #define MF624_IRC_RESET_DISABLE 0x00
124 #define MF624_IRC_RESET_ON_TOP 0x30
125 #define MF624_IRC_FILTER 0x80
127 /* Multiply the following constant by one above to obtain global behavior on all encoders' channels */
128 #define MF624_IRC_GLOB 0x01010101
130 /* Structure for board description */
131 typedef struct mf624_board_struct{
133 unsigned short device_id;
134 unsigned int ai_chans;
135 unsigned int ai_bits;
136 unsigned int ao_chans;
137 unsigned int ao_bits;
138 unsigned int di_chans;
139 unsigned int do_chans;
140 unsigned int have_dio;
141 unsigned int timer_chans;
142 unsigned int timer_config[5];
143 unsigned int timer_loadA[5];
144 unsigned int timer_loadB[5];
145 unsigned int timer_control;
146 unsigned int enc_chans;
147 unsigned int enc_config;
148 unsigned int cnt_bits;
151 /* Board description */
152 /* FIXME: timer_config, timer_control, and enc_config should be in the private structure */
153 static const mf624_board mf624_boards[] = {
155 name: "mf624", /* device name */
156 device_id: 0x0624, /* PCI dev ID */
157 ai_chans: 8, /* Num of ADC channels */
158 ai_bits: 14, /* Num of ADC bits */
159 ao_chans: 8, /* Num of DAC channels */
160 ao_bits: 14, /* Num of DAC bits */
161 di_chans: 8, /* Num of digital in */
162 do_chans: 8, /* Num of digital out */
163 have_dio: 0, /* Num of DIO */
164 timer_chans: 5, /* Num of timers/cnt */
165 timer_config: {0,0,0,0}, /* Save of the timer configuration */
166 timer_loadA: {0,0,0,0}, /* Save of the timer loadA registers */
167 timer_loadB: {0,0,0,0}, /* Save of the timer loadB registers */
168 timer_control: 0, /* Save counter state control */
169 enc_chans: 6, /* Num of encoders : 4 + 1 virtual channel containing index register
170 + 1 virtual channel containing IRC status */
171 enc_config: 0, /* Save of the encoder configuration */
172 cnt_bits: 32, /* Size of timers/enc */
176 /* Number of boards */
177 #define N_BOARDS (sizeof(mf624_boards) / sizeof(mf624_board))
179 /* PCI vendor and device ID */
180 #define PCI_VENDOR_ID_MF624 0x186c
181 #define PCI_DEVICE_ID_MF624 0x0624
183 static struct pci_device_id mf624_pci_table[] __devinitdata = {
184 { PCI_VENDOR_ID_MF624, PCI_DEVICE_ID_MF624, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
187 MODULE_DEVICE_TABLE(pci, mf624_pci_table);
189 /* Useful for shorthand access to the particular board structure */
190 #define thisboard ((mf624_board *)dev->board_ptr)
192 /* Private data structure */
196 /* would be useful for a PCI device */
197 struct pci_dev *pci_dev;
200 resource_size_t iobase;
210 /* Used for AO readback */
211 lsampl_t ao_readback[8];
215 * most drivers define the following macro to make it easy to
216 * access the private structure.
218 #define devpriv ((mf624_private *)dev->private)
220 /* Attach/detach functions declaration */
221 static int mf624_attach(comedi_device *dev,comedi_devconfig *it);
222 static int mf624_detach(comedi_device *dev);
224 /* Analog functions operation to be attached */
225 static int mf624_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
226 static int mf624_ai_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
227 static int mf624_ao_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
228 static int mf624_ao_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
229 static int mf624_ao_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
231 /* Digital functions operation to be attached */
232 static int mf624_di_insn_bits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
233 static int mf624_do_insn_bits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
235 /* Timers/counters functions operation to be attached */
236 static int mf624_timer_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
237 static int mf624_timer_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
238 static int mf624_timer_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
240 /* Encoder functions operation to be attached */
241 static int mf624_enc_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
242 static int mf624_enc_cfg(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
244 /*static irqreturn_t mf624_interrupt(int irq, void *d, struct pt_regs *regs);*/
247 * The comedi_driver structure tells the Comedi core module
248 * which functions to call to configure/deconfigure (attach/detach)
249 * the board, and also about the kernel module that contains
252 static comedi_driver driver_mf624={
253 driver_name: "mf624",
255 attach: mf624_attach,
256 detach: mf624_detach,
260 * Attach is called by the Comedi core to configure the driver
261 * for a particular board. If you specified a board_name array
262 * in the driver structure, dev->board_ptr contains that
265 static int mf624_attach(comedi_device *dev,comedi_devconfig *it){
267 struct pci_dev* pcidev;
268 unsigned int index, channel, status;
272 rt_printk("comedi%d: mf624: driver: Bourgeot - Poulain 2006-2007\n", dev->minor);
273 rt_printk("This is an experimental version, you can report some remarks or problems to fpoulain@gmail.com\n");
276 * Allocate the private structure area. alloc_private() is a
277 * convenient macro defined in comedidev.h.
278 * FIXME : do this allocation in a dedicated function
280 if(alloc_private(dev,sizeof(mf624_private))<0) {return -ENOMEM;}
282 /* Probe the device to determine what device in the series it is */
283 for(pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); pcidev != NULL;
284 pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
285 /* is it not a computer boards card ? */
286 if(pcidev->vendor != PCI_VENDOR_ID_MF624){continue;}
287 /* loop through cards supported by this driver */
288 for(index = 0; index < N_BOARDS ; index++)
290 if(mf624_boards[index].device_id != pcidev->device){continue;}
291 /* was a particular bus/slot requested ? */
292 if(it->options[0] || it->options[1])
294 /* are we on the wrong bus/slot ? */
295 if(pcidev->bus->number != it->options[0] ||
296 PCI_SLOT(pcidev->devfn) != it->options[1]){continue;}
298 devpriv->pci_dev = pcidev;
299 dev->board_ptr = mf624_boards + index;
304 rt_printk("comedi%d: mf624: No supported Humusoft card found on requested position\n", dev->minor);
306 comedi_error(dev, "No supported Humusoft card found on requested position\n");
310 rt_printk("comedi%d: mf624: Found %s on bus %i, slot %i\n", dev->minor, mf624_boards[index].name, pcidev->bus->number, PCI_SLOT(pcidev->devfn));
312 /* Enable PCI device and reserve I/O ports. */
313 if(pci_enable_device(pcidev)){
315 rt_printk("comedi%d: mf624: Failed to enable PCI device\n", dev->minor);
317 comedi_error(dev, "Failed to enable PCI device\n");
320 if(pci_request_regions(pcidev, "mf624")){
322 rt_printk("comedi%d: mf624: I/O port conflict\n", dev->minor);
324 comedi_error(dev, "I/O port conflict\n");
328 /* Initialize devpriv->control_status and to point to their base address */
329 devpriv->BADR0 = pci_resource_start(devpriv->pci_dev, 0);
330 devpriv->BADR1 = pci_resource_start(devpriv->pci_dev, 1);
331 devpriv->BADR2 = pci_resource_start(devpriv->pci_dev, 2);
333 devpriv->IO_BADR0 = ioremap_nocache(pci_resource_start(devpriv->pci_dev, 0),
334 pci_resource_len(devpriv->pci_dev, 0));
335 devpriv->IO_BADR1 = ioremap_nocache(pci_resource_start(devpriv->pci_dev, 2),
336 pci_resource_len(devpriv->pci_dev, 2));
337 devpriv->IO_BADR2 = ioremap_nocache(pci_resource_start(devpriv->pci_dev, 4),
338 pci_resource_len(devpriv->pci_dev, 4));
341 rt_printk ( "comedi%d: mf624: PCI Resource 0 addr %lx \n", dev->minor, devpriv->BADR0 );
342 rt_printk ( "comedi%d: mf624: PCI Resource 1 addr %lx \n", dev->minor, devpriv->BADR1 );
343 rt_printk ( "comedi%d: mf624: PCI Resource 2 addr %lx \n", dev->minor, devpriv->BADR2 );
345 rt_printk ( "comedi%d: mf624: IO_BADR0 addr %p \n", dev->minor, devpriv->IO_BADR0 );
346 rt_printk ( "comedi%d: mf624: IO_BADR1(2) addr %p \n", dev->minor, devpriv->IO_BADR1 );
347 rt_printk ( "comedi%d: mf624: IO_BADR2(4) addr %p \n", dev->minor, devpriv->IO_BADR2 );
351 * Initialize dev->board_name. Note that we can use the "thisboard"
352 * macro now, since we just initialized it in the last line.
354 dev->board_name = thisboard->name;
357 * Allocate the subdevice structures. alloc_subdevice() is a
358 * convenient macro defined in comedidev.h.
360 if(alloc_subdevices(dev, 6)<0)
364 /* analog single ended (ground) input subdevice */
365 s->type = COMEDI_SUBD_AI;
366 s->subdev_flags = SDF_READABLE|SDF_GROUND;
367 s->n_chan = thisboard->ai_chans;
368 s->maxdata = (1<<thisboard->ai_bits)-1;
369 s->range_table = &range_bipolar10;
371 s->insn_read = mf624_ai_rinsn;
372 s->insn_config = mf624_ai_cfg;
375 /* analog output subdevice */
376 s->type = COMEDI_SUBD_AO;
377 s->subdev_flags = SDF_WRITABLE;
378 s->n_chan = thisboard->ao_chans;
379 s->maxdata = (1<<thisboard->ao_bits)-1;
380 s->range_table = &range_bipolar10;
381 s->insn_write = mf624_ao_winsn;
382 s->insn_read = mf624_ao_rinsn;
383 s->insn_config = mf624_ao_cfg;
386 /* digital input subdevice */
387 s->type = COMEDI_SUBD_DI;
388 s->subdev_flags = SDF_READABLE;
389 s->n_chan = thisboard->di_chans;
391 s->range_table = &range_digital;
392 s->insn_bits = mf624_di_insn_bits;
395 /* digital output subdevice */
396 s->type = COMEDI_SUBD_DO;
397 s->subdev_flags = SDF_WRITABLE;
398 s->n_chan = thisboard->do_chans;
400 s->range_table = &range_digital;
401 s->insn_bits = mf624_do_insn_bits;
405 s->type = COMEDI_SUBD_TIMER;
406 s->subdev_flags = SDF_WRITABLE|SDF_READABLE;
407 s->n_chan = thisboard->timer_chans;
408 s->maxdata = (1<<thisboard->cnt_bits)-1;
409 s->range_table = &range_digital;
410 s->insn_config = mf624_timer_cfg;
411 s->insn_write = mf624_timer_winsn;
412 s->insn_read = mf624_timer_rinsn;
416 s->type = COMEDI_SUBD_COUNTER;
417 s->subdev_flags = SDF_READABLE|SDF_GROUND|SDF_DIFF;
418 s->n_chan = thisboard->enc_chans;
419 s->maxdata = (1<<thisboard->cnt_bits)-1;
420 s->range_table = &range_digital;
421 s->insn_config = mf624_enc_cfg;
422 s->insn_read = mf624_enc_rinsn;
424 rt_printk("comedi%d: mf624: Driver attached\n", dev->minor);
426 /* FIXME : do those initialisation in some dedicated functions */
429 status = readl(devpriv->IO_BADR0 + MF624_GPIOC) | MF624_GPIOC_DACEN;
430 writel(status, devpriv->IO_BADR0 + MF624_GPIOC);
432 /* Initialise Interrupt Control Status */
433 writel(0x00000000, devpriv->IO_BADR0 + MF624_INTCSR);
435 /* Initialise analog outputs to zero */
436 for(channel=0 ; channel<thisboard->ao_chans ; channel++){
437 writew(0x2000,devpriv->IO_BADR1 + MF624_DA0 + 2*channel);
439 devpriv->ao_readback[channel] = 0x2000;
442 /* Initialise counters to initial state and reset them */
443 for(channel=0 ; channel<thisboard->timer_chans ; channel++){
444 /* Restore initial state */
445 writel(0x00000020, devpriv->IO_BADR2 + MF624_CTRXMODE + 0x10*channel);
446 /* Reset A register */
447 writel(0x00000000, devpriv->IO_BADR2 + MF624_CTRXB + 0x10*channel);
448 /* Reset B register (don't exist for timer 4) */
449 if(channel!=4) {writel(0x00000000, devpriv->IO_BADR2 + MF624_CTRXB + 0x10*channel);}
451 /* Reset and stop all timers in one shot !*/
452 writel((MF624_CTR_RESET|MF624_CTR_OUT_RESET|MF624_CTR_STOP) * MF624_CTR_GLOB, devpriv->IO_BADR2 + MF624_CTRXCTRL);
454 /* test output 1kHz frequency */
455 /* calc preset register : Reg = (long)(1<<chanBits) -
457 #define RAPPORT_CYCLIQUE 0.5
458 #define PERIODE 0.001
459 regA = (long)(1<<32) - 100000*PERIODE*RAPPORT_CYCLIQUE;
460 regB = (long)(1<<32) - 100000*PERIODE*(1-RAPPORT_CYCLIQUE);
461 rt_printk("Preset registers for PWM output : A = %x and B = %x \n", regA, regB);
462 /* load preset in A reg */
463 writel(regA, devpriv->IO_BADR2 + MF624_CTRXA);
464 /* load preset/2 in B reg */
465 writel(regB, devpriv->IO_BADR2 + MF624_CTRXB);
467 writel(0x0000C00F, devpriv->IO_BADR2 + MF624_CTRXMODE);
468 /* Load register and start counter 0 */
469 writel(MF624_CTR_LOAD|MF624_CTR_START, devpriv->IO_BADR2 + MF624_CTRXCTRL);
470 rt_printk("Counter 0 status = %x\n", readl(devpriv->IO_BADR2));
472 /* Stop and keep reset all encoders */
473 thisboard->enc_config = (MF624_IRC_COUNT_DISABLE|MF624_IRC_RESET_ENABLE) * MF624_IRC_GLOB;
474 writel(thisboard->enc_config, devpriv->IO_BADR2 + MF624_IRCCTRL);
476 /* End of initialisation */
477 rt_printk("comedi%d: mf624: Board initialized\n", dev->minor);
483 * _detach is called to deconfigure a device. It should deallocate
485 * This function is also called when _attach() fails, so it should be
486 * careful not to release resources that were not necessarily
487 * allocated by _attach(). dev->private and dev->subdevices are
488 * deallocated automatically by the core.
490 static int mf624_detach(comedi_device *dev)
492 rt_printk("comedi%d: mf624: remove\n", dev->minor);
494 if(dev->irq){comedi_free_irq(dev->irq, dev);}
496 if(devpriv && devpriv->pci_dev)
500 pci_release_regions(devpriv->pci_dev);
501 pci_disable_device(devpriv->pci_dev);
502 rt_printk("comedi%d: mf624: remove2\n", dev->minor);
504 pci_dev_put(devpriv->pci_dev);
505 rt_printk("comedi%d: mf624: remove3\n", dev->minor);
510 /* read n samples on Analog Input channel */
511 static int mf624_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
513 unsigned int dat, n, i, status, chan = CR_CHAN(insn->chanspec);
516 rt_printk("comedi%d: mf624: mf624_ai_rinsn called \n", dev->minor);
519 /* write channel to multiplexer */
520 writew(1<<chan,devpriv->IO_BADR1 + MF624_ADCTRL);
522 /* convert n samples */
523 for(n=0;n<insn->n;n++){
524 /* trigger conversion */
525 dat = readw(devpriv->IO_BADR1 + MF624_ADSTART);
528 /* wait for conversion to end */
529 for(i=0;i<TIMEOUT;i++){
531 status = readl(devpriv->IO_BADR0 + MF624_GPIOC);
532 if(!(status & MF624_GPIOC_EOLC))break;
535 rt_printk("comedi%d: mf624: _ai_rinsn: conversion timeout !\n", dev->minor);
536 comedi_error(dev, "Conversion timeout !\n");
541 dat = readw(devpriv->IO_BADR1 + MF624_ADDATA);
543 /* mangle the data as necessary */
544 dat ^= 1<<(thisboard->ai_bits-1);
549 /* return the number of samples read/written */
553 /* Analog input configuration */
554 static int mf624_ai_cfg(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data){
556 rt_printk("comedi%d: mf624: _insn_ai_cfg called\n", dev->minor);
561 /* write n samples on Analog Output channel */
562 static int mf624_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
564 unsigned int i, chan = CR_CHAN(insn->chanspec), status;
566 rt_printk("comedi%d: mf624: _ao_winsn called\n", dev->minor);
568 status = readl(devpriv->IO_BADR0 + MF624_GPIOC);
569 status = status | MF624_GPIOC_DACEN;
570 writel(status, devpriv->IO_BADR0 + MF624_GPIOC);
571 /* Writing a list of values to an AO channel is probably not
572 * very useful, but that's how the interface is defined. */
573 for(i=0;i<insn->n;i++){
574 /* a typical programming sequence */
575 writew(data[i],devpriv->IO_BADR1 + MF624_DA0 + 2*chan);
576 devpriv->ao_readback[chan] = data[i];
578 rt_printk("comedi%d: mf624: _ao_winsn: wrote at address %i data %i ", dev->minor, MF624_DA0 + 2*chan , data[i] );
582 /* return the number of samples read/written */
586 /* Analog output configuration */
587 static int mf624_ao_cfg(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data){
589 rt_printk("comedi%d: mf624: _ao_cfg called\n, dev->minor");
594 /* AO subdevices should have a read insn as well as a write insn.
595 * Usually this means copying a value stored in devpriv. */
596 static int mf624_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
598 unsigned int i, chan = CR_CHAN(insn->chanspec);
600 for(i=0;i<insn->n;i++)
601 data[i] = devpriv->ao_readback[chan];
606 /* Write digital data */
607 static int mf624_do_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
610 rt_printk("comedi%d: mf624: _do_insn_bits called with data: %d %d\n", dev->minor, data[0], data[1]);
612 if(insn->n!=2)return -EINVAL;
615 s->state &= ~data[0];
616 s->state |= data[0]&data[1];
618 rt_printk ("comedi%d: mf624: _do_insn_bits: out: %d on %lx\n", dev->minor, s->state, devpriv->IO_BADR1 + MF624_DOUT );
620 writew(s->state, devpriv->IO_BADR1 + MF624_DOUT);
625 /* Read digital data */
626 static int mf624_di_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
629 rt_printk("comedi%d: mf624: _di_insn_bits called with data: %d %d\n", dev->minor, data[0], data[1]);
631 if(insn->n!=2)return -EINVAL;
633 data[1] = readw(devpriv->IO_BADR1 + MF624_DIN );
635 rt_printk("comedi%d: mf624: _di_insn_bits read data: %d\n", dev->minor, data[1]);
640 /* Set the preload registers */
641 static int mf624_timer_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
642 unsigned int channel = CR_CHAN(insn->chanspec);
645 rt_printk("comedi%d: mf624: _timer_winsn called for channel %d, with n = %d\n", dev->minor, channel, insn->n);
648 if(insn->n > 2)return -EINVAL;
650 thisboard->timer_loadA[channel] = data[0];
651 writel(thisboard->timer_loadA[channel], devpriv->IO_BADR2 + MF624_CTRXA + 0x10*channel);
654 rt_printk("comedi%d: mf624: _timer_winsn: preset register A set at %8x\n", dev->minor, thisboard->timer_loadA[channel]);
658 thisboard->timer_loadB[channel] = data[1];
659 writel(thisboard->timer_loadB[channel], devpriv->IO_BADR2 + MF624_CTRXB + 0x10*channel);
662 rt_printk("comedi%d: mf624: _timer_winsn: preset register B set at %8x\n", dev->minor, thisboard->timer_loadB[channel]);
669 /* Read the timer or the preload registers */
670 static int mf624_timer_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
671 unsigned int channel = CR_CHAN(insn->chanspec), i = 0, status = 0, loadA = 0, loadB = 0, config = 0;
674 rt_printk("comedi%d: mf624: _timer_rinsn called for channel %d, with n = %d\n", dev->minor, channel, insn->n);
678 /* Read the counter status, the preloads registers and the counter configuration */
679 config = thisboard->timer_config[channel];
680 loadA = thisboard->timer_loadA[channel];
681 loadB = thisboard->timer_loadB[channel];
682 status = readl(devpriv->IO_BADR2 + MF624_CTRXSTATUS + 0x10*channel);
684 rt_printk("comedi%d: mf624: _timer_rinsn: status = %8x, loadA = %8x, loadB = %8x, config = %8x\n", dev->minor, status, loadA, loadB, config);
693 for(i = 0 ; i < insn->n ; i++) data[i] = readl(devpriv->IO_BADR2 + MF624_CTRXDATA + 0x10*channel);
696 rt_printk("comedi%d: mf624: _timer_rinsn: readed %x\n", dev->minor, data[insn->n-1]);
701 /* Configure timers */
702 static int mf624_timer_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
704 unsigned int bitfield = 0, bitfield2 = 0, status = 0, mask = 0, mask2 = 0, i = 0, dataIndex = 0, channel = CR_CHAN(insn->chanspec);
707 rt_printk("comedi%d: mf624: _timer_cfg called with %d data for channel %d and datas : ", dev->minor, insn->n, channel);
708 for(i=0 ; i < insn->n ; i++) rt_printk("%x ", data[i]);
712 /* For more informations, see the programming manual
713 * available near http://www2.humusoft.cz/www/datacq/manuals/mf624um.pdf */
715 /* FIXME : test and fix if maximum one opcode is given for each subconfiguration */
716 /* FIXME : thereis an *nicer* way to do this, using some unnecessary flags defined in mf624.h */
717 for(dataIndex = 0 ; dataIndex < insn->n ; dataIndex++){
718 switch(data[dataIndex]){
719 /* Mode register configuration */
721 /* Configure direction */
722 case MF624_CTR_DIRECTION_DOWN : bitfield |= 0x00; mask |= 0x01; break ;
723 case MF624_CTR_DIRECTION_UP : bitfield |= 0x01; mask |= 0x01; break ;
725 /* Configure repetition */
726 case MF624_CTR_REPETITION_OFF : bitfield |= 0x00<<1; mask |= 0x01<<1; break ;
727 case MF624_CTR_REPETITION_ON : bitfield |= 0x01<<1; mask |= 0x01<<1; break ;
729 /* Configure load toggle */
730 case MF624_CTR_PRELOAD_A : bitfield |= 0x00<<2; mask |= 0x01<<2; break ;
731 case MF624_CTR_PRELOAD_AB : bitfield |= 0x01<<2; mask |= 0x01<<2; break ;
733 /* Configure output toggle */
734 case MF624_CTR_OUTPUT_MONOSTABLE : bitfield |= 0x00<<3; mask |= 0x01<<3; break ;
735 case MF624_CTR_OUTPUT_BISTABLE : bitfield |= 0x01<<3; mask |= 0x01<<3; break ;
737 /* Configure output control */
738 case MF624_CTR_OUTPUT_DIRECT : bitfield |= 0x00<<4; mask |= 0x03<<4; break ;
739 case MF624_CTR_OUTPUT_INVERSE : bitfield |= 0x01<<4; mask |= 0x03<<4; break ;
740 case MF624_CTR_OUTPUT_OFF : bitfield |= 0x02<<4; mask |= 0x03<<4; break ;
741 case MF624_CTR_OUTPUT_ON : bitfield |= 0x03<<4; mask |= 0x03<<4; break ;
743 /* Configure trigger source */
744 case MF624_CTR_TRIGGER_DISABLED : bitfield |= 0x00<<6; mask |= 0x03<<6; break ;
745 case MF624_CTR_TRIGGER_BYINPUT : bitfield |= 0x01<<6; mask |= 0x03<<6; break ;
746 case MF624_CTR_TRIGGER_BYLOWER : bitfield |= 0x02<<6; mask |= 0x03<<6; break ;
747 case MF624_CTR_TRIGGER_BYUPPER : bitfield |= 0x03<<6; mask |= 0x03<<6; break ;
749 /* Configure trigger type */
750 case MF624_CTR_TRIGGER_DISABLE : bitfield |= 0x00<<8; mask |= 0x03<<8; break ;
751 case MF624_CTR_TRIGGER_RAISING : bitfield |= 0x01<<8; mask |= 0x03<<8; break ;
752 case MF624_CTR_TRIGGER_FALLING : bitfield |= 0x02<<8; mask |= 0x03<<8; break ;
753 case MF624_CTR_TRIGGER_EITHER : bitfield |= 0x03<<8; mask |= 0x03<<8; break ;
755 /* Configure retrigger */
756 case MF624_CTR_RETRIGGER_OFF : bitfield |= 0x00<<10; mask |= 0x01<<10; break ;
757 case MF624_CTR_RETRIGGER_ON : bitfield |= 0x01<<10; mask |= 0x01<<10; break ;
759 /* Configure gate source */
760 case MF624_CTR_GATE_HIGHT : bitfield |= 0x00<<11; mask |= 0x03<<11; break ;
761 case MF624_CTR_GATE_BYINPUT : bitfield |= 0x01<<11; mask |= 0x03<<11; break ;
762 case MF624_CTR_GATE_BYLOWER : bitfield |= 0x02<<11; mask |= 0x03<<11; break ;
763 case MF624_CTR_GATE_BYUPPER : bitfield |= 0x03<<11; mask |= 0x03<<11; break ;
765 /* Configure gate polarity */
766 case MF624_CTR_GATEPOLARITY_LOW : bitfield |= 0x00<<13; mask |= 0x01<<13; break ;
767 case MF624_CTR_GATEPOLARITY_HIGH : bitfield |= 0x01<<13; mask |= 0x01<<13; break ;
769 /* Configure clock source */
770 case MF624_CTR_CKSOURCE_50M : bitfield |= 0x00<<14; mask |=0x0F<<14 ; break ;
771 case MF624_CTR_CKSOURCE_10M : bitfield |= 0x01<<14; mask |=0x0F<<14 ; break ;
772 case MF624_CTR_CKSOURCE_1M : bitfield |= 0x02<<14; mask |=0x0F<<14 ; break ;
773 case MF624_CTR_CKSOURCE_100K : bitfield |= 0x03<<14; mask |=0x0F<<14 ; break ;
774 case MF624_CTR_CKSOURCE_RAISINGINPUT : bitfield |= 0x05<<14; mask |=0x0F<<14 ; break ;
775 case MF624_CTR_CKSOURCE_FALLINGINPUT : bitfield |= 0x06<<14; mask |=0x0F<<14 ; break ;
776 case MF624_CTR_CKSOURCE_EITHERINPUT : bitfield |= 0x07<<14; mask |=0x0F<<14 ; break ;
777 case MF624_CTR_CKSOURCE_RAISINGLOWER : bitfield |= 0x09<<14; mask |=0x0F<<14 ; break ;
778 case MF624_CTR_CKSOURCE_FALLINGLOWER : bitfield |= 0x0A<<14; mask |=0x0F<<14 ; break ;
779 case MF624_CTR_CKSOURCE_EITHERLOWER : bitfield |= 0x0B<<14; mask |=0x0F<<14 ; break ;
780 case MF624_CTR_CKSOURCE_RAISINGUPPER : bitfield |= 0x0D<<14; mask |=0x0F<<14 ; break ;
781 case MF624_CTR_CKSOURCE_FALLINGUPPER : bitfield |= 0x0E<<14; mask |=0x0F<<14 ; break ;
782 case MF624_CTR_CKSOURCE_EITHERUPPER : bitfield |= 0x0F<<14; mask |=0x0F<<14 ; break ;
784 /* Configure analog trigger */
785 case MF624_CTR_ADTRIGSRC_INT : bitfield |= 0x00<<30; mask |= 0x01<<30; break ;
786 case MF624_CTR_ADTRIGSRC_EXT : bitfield |= 0x01<<30; mask |= 0x01<<30; break ;
788 /* Configure CTR4 interrupt source */
789 case MF624_CTR_CTR4INTSRC_INT : bitfield |= 0x00<<31; mask |= 0x01<<31; break ;
790 case MF624_CTR_CTR4INTSRC_EXT : bitfield |= 0x01<<31; mask |= 0x01<<31; break ;
792 /* Control register affection */
793 case MF624_CTR_CTRL_START : bitfield2 |= 0x01; break;
794 case MF624_CTR_CTRL_STOP : bitfield2 |= 0x02; break;
795 case MF624_CTR_CTRL_LOAD : bitfield2 |= 0x04; break;
796 case MF624_CTR_CTRL_RESET : bitfield2 |= 0x08; break;
797 case MF624_CTR_CTRL_TSET : bitfield2 |= 0x10; break;
798 case MF624_CTR_CTRL_TRESET : bitfield2 |= 0x20; break;
801 default : return MF624_EXIT_FAILURE;
804 status = thisboard->timer_config[channel];
808 rt_printk("comedi%d: mf624: _timer_cfg: bitfield = %8x, mask = %8x, status = %8x\n", dev->minor, bitfield, mask, status);
809 rt_printk("comedi%d: mf624: _timer_cfg: writing %8x in %8x \n", dev->minor, (status & mask) | bitfield, devpriv->IO_BADR2 + MF624_CTRXMODE + 0x10*channel);
812 thisboard->timer_config[channel] = (status & mask) | bitfield;
813 if (channel != 4 && (thisboard->timer_config[channel] & 0xC0000000) !=0){
814 rt_printk("comedi%d: mf624: _timer_cfg: Only 4th counter implement those features!\n", dev->minor);
815 comedi_error(dev, "Only 4th counter implement those features!\n");
817 writel(thisboard->timer_config[channel], devpriv->IO_BADR2 + MF624_CTRXMODE + 0x10*channel);
819 mask2 = ~(0x3F << (6*channel));
820 status = thisboard->timer_control;
823 rt_printk("comedi%d: mf624: _timer_cfg: bitfield2 = %8x, mask2 = %8x, status2 = %8x\n", dev->minor, bitfield2, mask2, status);
824 rt_printk("comedi%d: mf624: _timer_cfg: writing %8x in %8x \n", dev->minor, (status & mask2) | (bitfield2 << (6*channel)), devpriv->IO_BADR2 + MF624_CTRXCTRL);
827 thisboard->timer_control = (status & mask2) + (bitfield << (6*channel));
828 writel(thisboard->timer_control, devpriv->IO_BADR2 + MF624_CTRXCTRL);
833 /* Read an encoder */
834 static int mf624_enc_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
835 unsigned int i, channel = CR_CHAN(insn->chanspec);
837 rt_printk("comedi%d: mf624: _enc_rinsn called for channel %d, with n = %d\n", dev->minor, channel, insn->n);
839 for(i=0;i<insn->n;i++){
840 if(channel == (thisboard->enc_chans - 1)){
841 /* virtual channel 5 : read index register */
842 data[i] = readl(devpriv->IO_BADR2 + MF624_IRCSTATUS) & 0x01010101;
844 if(channel == (thisboard->enc_chans - 2)){
845 /* virtual channel 4 : read configuration register */
846 data[i] = thisboard->enc_config;
849 data[i] = readl(devpriv->IO_BADR2 + MF624_ENC_READ + 0x04*channel);
853 rt_printk("comedi%d: mf624: _enc_rinsn: readed %x\n", dev->minor, data[insn->n-1]);
859 /* Configure encoders */
860 static int mf624_enc_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
862 unsigned char bitfield = 0;
863 unsigned int status = 0, mask = 0, i = 0, dataIndex = 0, channel = CR_CHAN(insn->chanspec);
865 /* You cannot configure virtual channel */
866 if(channel >= (thisboard->enc_chans - 2)){
867 rt_printk("comedi%d: mf624: trying to configure virtual channel!\n", dev->minor);
868 comedi_error(dev, "Trying to configure virtual channel!\n");
869 return MF624_EXIT_FAILURE;
873 rt_printk("comedi%d: mf624: _enc_cfg called with %d data for channel %d and datas : ", dev->minor, insn->n, channel);
874 for(i=0 ; i < insn->n ; i++) rt_printk("%x ", data[i]);
878 /* For more informations, see the programming manual
879 * available near http://www2.humusoft.cz/www/datacq/manuals/mf624um.pdf */
881 /* FIXME : test and fix if maximum one opcode is given for each subconfiguration */
882 for(dataIndex = 0 ; dataIndex < insn->n ; dataIndex++){
883 switch(data[dataIndex]){
884 /* Configure encoder mode */
885 case MF624_ENC_MODE_4EDGE : bitfield |= 0x00 ; break ;
886 case MF624_ENC_MODE_RISING : bitfield |= 0x01 ; break ;
887 case MF624_ENC_MODE_FALLING : bitfield |= 0x02 ; break ;
888 case MF624_ENC_MODE_EITHER : bitfield |= 0x03 ; break ;
890 /* Configure encoder counting */
891 case MF624_ENC_COUNT_ENABLE : bitfield |= 0x00 ; break ;
892 case MF624_ENC_COUNT_DISABLE : bitfield |= 0x04 ; break ;
893 case MF624_ENC_COUNT_ENABLE_BYINPUT : bitfield |= 0x08 ; break ;
894 case MF624_ENC_COUNT_DISABLE_BYINPUT : bitfield |= 0x0C ; break ;
896 /* Configure encoder reset */
897 case MF624_ENC_RESET_DISABLE : bitfield |= 0x00 ; break ;
898 case MF624_ENC_RESET_ENABLE : bitfield |= 0x10 ; break ;
899 case MF624_ENC_RESET_DISABLE_BYINPUT : bitfield |= 0x20 ; break ;
900 case MF624_ENC_RESET_ENABLE_BYINPUT : bitfield |= 0x30 ; break ;
901 case MF624_ENC_RESET_ONRAISING_INPUT : bitfield |= 0x40 ; break ;
902 case MF624_ENC_RESET_ONFALLING_INPUT : bitfield |= 0x50 ; break ;
903 case MF624_ENC_RESET_ONEITHER_INPUT : bitfield |= 0x60 ; break ;
905 /* Configure encoder reset */
906 case MF624_ENC_FILTER_OFF : bitfield |= 0x00 ; break ;
907 case MF624_ENC_FILTER_ON : bitfield |= 0x80 ; break ;
910 default : return MF624_EXIT_FAILURE;
914 mask = ~(0xFF << (8*channel));
915 status = thisboard->enc_config;
918 rt_printk("comedi%d: mf624: _enc_cfg: bitfield = %8x, mask = %8x, status = %8x\n", dev->minor, bitfield, mask, status);
919 rt_printk("comedi%d: mf624: _enc_cfg: writing %8x in %8x \n", dev->minor, (status & mask) + (bitfield << (8*channel)), devpriv->IO_BADR2 + MF624_IRCCTRL);
922 thisboard->enc_config = (status & mask) + (bitfield << (8*channel));
923 writel(thisboard->enc_config, devpriv->IO_BADR2 + MF624_IRCCTRL);
929 * A convenient macro that defines init_module() and cleanup_module(),
932 COMEDI_INITCLEANUP(driver_mf624);