]> rtime.felk.cvut.cz Git - mf6xx.git/blob - src/comedi/fpoulain/mf624.c
Initial commit of Comedi driver for MF624 card; Original author Francois Poulain...
[mf6xx.git] / src / comedi / fpoulain / mf624.c
1 /*
2  * comedi/drivers/mf624.c
3  * Code for a Humusoft MF624 driver
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: mf624.o
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
29 Status: experimental
30
31 This driver is a driver for the Humusoft MF624 PCI Card.
32
33 It has :
34  * 8 channel 14 bits ADC,
35  * 8 channel 14 bits DAC,
36  * 8 digital inputs,
37  * 8 digital outputs,
38  * 4 quadrature encoder input,
39  * 5 timers/counter.
40
41 Status:
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.
49
50 Remarks:
51 Simultaneous D/A update not yet supported.
52
53 Configuration Options:
54 none
55  */
56
57 #include <linux/comedidev.h>
58 #include <linux/pci.h>
59 #include "mf624.h"
60
61 MODULE_AUTHOR("Francois Poulain <fpoulain AT gmail DOT com>");
62 MODULE_DESCRIPTION("Humusoft MF624 Multifunction I/O Card");
63 MODULE_LICENSE("GPL");
64
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 */
69
70 #define EXTDEBUG 
71
72 #define MF624_EXIT_FAILURE      -1
73
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 */
77
78 /* BADR0 Memory Map */
79 #define MF624_GPIOC             0x54
80 #define MF624_INTCSR            0x4C
81
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
89
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
100
101 /* MASK */
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 */
105
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
113
114 /* Multiply the following constant by one above to obtain global behavior on all timers' channels */
115 #define MF624_CTR_GLOB          0x01041041
116
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
126
127 /* Multiply the following constant by one above to obtain global behavior on all encoders' channels */
128 #define MF624_IRC_GLOB          0x01010101
129
130 /* Structure for board description */
131 typedef struct mf624_board_struct{
132         char *name;
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;
149 }mf624_board;
150
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[] = {
154         {
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   */
173         },
174 };
175
176 /* Number of boards */
177 #define N_BOARDS        (sizeof(mf624_boards) / sizeof(mf624_board))
178
179 /* PCI vendor and device ID */
180 #define PCI_VENDOR_ID_MF624 0x186c
181 #define PCI_DEVICE_ID_MF624 0x0624
182
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 },
185         { 0 }
186 };
187 MODULE_DEVICE_TABLE(pci, mf624_pci_table);
188
189 /* Useful for shorthand access to the particular board structure */
190 #define thisboard ((mf624_board *)dev->board_ptr)
191
192 /* Private data structure */
193 typedef struct{
194         int data;
195
196         /* would be useful for a PCI device */
197         struct pci_dev *pci_dev;
198
199         /* base addresses */
200         resource_size_t iobase;
201
202         unsigned long BADR0;    
203         unsigned long BADR1;
204         unsigned long BADR2;    
205
206         void* IO_BADR0; 
207         void* IO_BADR1;
208         void* IO_BADR2; 
209
210         /* Used for AO readback */
211         lsampl_t ao_readback[8];
212 }mf624_private;
213
214 /*
215  * most drivers define the following macro to make it easy to
216  * access the private structure.
217  */
218 #define devpriv ((mf624_private *)dev->private)
219
220 /* Attach/detach functions declaration */
221 static int mf624_attach(comedi_device *dev,comedi_devconfig *it);
222 static int mf624_detach(comedi_device *dev);
223
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);
230
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);
234
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);
239
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);
243
244 /*static irqreturn_t mf624_interrupt(int irq, void *d, struct pt_regs *regs);*/
245
246 /*
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
250  * the device code.
251  */
252 static comedi_driver driver_mf624={
253         driver_name:    "mf624",
254         module:         THIS_MODULE,
255         attach:         mf624_attach,
256         detach:         mf624_detach,
257 };
258
259 /*
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
263  * address.
264  */
265 static int mf624_attach(comedi_device *dev,comedi_devconfig *it){
266         comedi_subdevice *s;
267         struct pci_dev* pcidev;
268         unsigned int index, channel, status;
269
270         int regA, regB;
271
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");
274
275         /*
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
279          */
280         if(alloc_private(dev,sizeof(mf624_private))<0) {return -ENOMEM;}
281
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++)
289                 {
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])
293                         {
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;}
297                         }
298                         devpriv->pci_dev = pcidev;
299                         dev->board_ptr = mf624_boards + index;
300                         goto found;
301                 }
302         }
303 #ifdef EXTDEBUG
304         rt_printk("comedi%d: mf624: No supported Humusoft card found on requested position\n", dev->minor);
305 #endif
306         comedi_error(dev, "No supported Humusoft card found on requested position\n");
307         return -EIO;
308
309 found:
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));
311
312         /* Enable PCI device and reserve I/O ports. */
313         if(pci_enable_device(pcidev)){
314 #ifdef EXTDEBUG
315                 rt_printk("comedi%d: mf624: Failed to enable PCI device\n", dev->minor);
316 #endif
317                 comedi_error(dev, "Failed to enable PCI device\n");
318                 return -EIO;
319         }
320         if(pci_request_regions(pcidev, "mf624")){
321 #ifdef EXTDEBUG
322                 rt_printk("comedi%d: mf624: I/O port conflict\n", dev->minor);
323 #endif
324                 comedi_error(dev, "I/O port conflict\n");
325                 return -EIO;
326         }
327
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);
332
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)); 
339
340 #ifdef EXTDEBUG
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 );
344
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 ); 
348 #endif
349
350         /*
351          * Initialize dev->board_name.  Note that we can use the "thisboard"
352          * macro now, since we just initialized it in the last line.
353          */
354         dev->board_name = thisboard->name;
355
356         /*
357          * Allocate the subdevice structures.  alloc_subdevice() is a
358          * convenient macro defined in comedidev.h.
359          */
360         if(alloc_subdevices(dev, 6)<0)
361                 return -ENOMEM;
362
363         s=dev->subdevices+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;
370         s->len_chanlist = 8;
371         s->insn_read = mf624_ai_rinsn;
372         s->insn_config = mf624_ai_cfg;
373
374         s=dev->subdevices+1;
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;
384
385         s=dev->subdevices+2;
386         /* digital input subdevice */   
387         s->type = COMEDI_SUBD_DI;
388         s->subdev_flags = SDF_READABLE;
389         s->n_chan = thisboard->di_chans;
390         s->maxdata = 1;
391         s->range_table = &range_digital;
392         s->insn_bits = mf624_di_insn_bits;
393
394         s=dev->subdevices+3;
395         /* digital output subdevice */  
396         s->type = COMEDI_SUBD_DO;
397         s->subdev_flags = SDF_WRITABLE;
398         s->n_chan = thisboard->do_chans;
399         s->maxdata = 1;
400         s->range_table = &range_digital;
401         s->insn_bits  = mf624_do_insn_bits;
402
403         s=dev->subdevices+4;
404         /* timer device */
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;
413
414         s=dev->subdevices+5;
415         /* encoder device */
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;
423
424         rt_printk("comedi%d: mf624: Driver attached\n", dev->minor);
425
426         /* FIXME : do those initialisation in some dedicated functions */
427
428         /* Enable DAC */
429         status = readl(devpriv->IO_BADR0 + MF624_GPIOC) | MF624_GPIOC_DACEN;  
430         writel(status, devpriv->IO_BADR0 + MF624_GPIOC);
431
432         /* Initialise Interrupt Control Status */
433         writel(0x00000000, devpriv->IO_BADR0 + MF624_INTCSR);
434
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);
438                 /* Readback save */
439                 devpriv->ao_readback[channel] = 0x2000;
440         }
441
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);}
450         }
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);
453
454         /* test output 1kHz frequency */
455         /* calc preset register : Reg = (long)(1<<chanBits) -
456            Fclk/Fout */
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);
466         /* Configure CTR0 */
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));
471
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);
475
476         /* End of initialisation */
477         rt_printk("comedi%d: mf624: Board initialized\n", dev->minor);
478         return 1;
479 }
480
481
482 /*
483  * _detach is called to deconfigure a device.  It should deallocate
484  * resources.  
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.
489  */
490 static int mf624_detach(comedi_device *dev)
491 {
492         rt_printk("comedi%d: mf624: remove\n", dev->minor);
493
494         if(dev->irq){comedi_free_irq(dev->irq, dev);}
495
496         if(devpriv && devpriv->pci_dev)
497         {
498                 if(devpriv->BADR0)
499                 {
500                         pci_release_regions(devpriv->pci_dev);
501                         pci_disable_device(devpriv->pci_dev);
502                         rt_printk("comedi%d: mf624: remove2\n", dev->minor);
503                 }
504                 pci_dev_put(devpriv->pci_dev);
505                 rt_printk("comedi%d: mf624: remove3\n", dev->minor);
506         }
507 return 0;
508 }
509
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)
512 {
513         unsigned int dat, n, i, status, chan = CR_CHAN(insn->chanspec);
514
515 #ifdef EXTDEBUG
516         rt_printk("comedi%d: mf624: mf624_ai_rinsn called \n", dev->minor);
517 #endif
518
519         /* write channel to multiplexer */
520         writew(1<<chan,devpriv->IO_BADR1 + MF624_ADCTRL);
521
522         /* convert n samples */
523         for(n=0;n<insn->n;n++){
524                 /* trigger conversion */
525                 dat = readw(devpriv->IO_BADR1 + MF624_ADSTART);
526
527 #define TIMEOUT 100 
528                 /* wait for conversion to end */
529                 for(i=0;i<TIMEOUT;i++){
530                         status = 1;
531                         status = readl(devpriv->IO_BADR0 + MF624_GPIOC);
532                         if(!(status & MF624_GPIOC_EOLC))break;
533                 }
534                 if(i==TIMEOUT){
535                         rt_printk("comedi%d: mf624: _ai_rinsn: conversion timeout !\n", dev->minor);
536                         comedi_error(dev, "Conversion timeout !\n");
537                         return -ETIMEDOUT;
538                 }
539
540                 /* read data */
541                 dat = readw(devpriv->IO_BADR1 + MF624_ADDATA);
542
543                 /* mangle the data as necessary */
544                 dat ^= 1<<(thisboard->ai_bits-1);
545
546                 data[n] = dat;
547         }
548
549         /* return the number of samples read/written */
550         return n;
551 }
552
553 /* Analog input configuration */
554 static int mf624_ai_cfg(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data){
555 #ifdef EXTDEBUG
556         rt_printk("comedi%d: mf624: _insn_ai_cfg called\n", dev->minor);
557 #endif
558         return insn->n;
559 }
560
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)
563 {
564         unsigned int i, chan = CR_CHAN(insn->chanspec), status;
565 #ifdef EXTDEBUG
566         rt_printk("comedi%d: mf624: _ao_winsn called\n", dev->minor);
567 #endif
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];
577 #ifdef EXTDEBUG
578                 rt_printk("comedi%d: mf624: _ao_winsn: wrote at address %i data %i  ", dev->minor, MF624_DA0 + 2*chan , data[i] );
579 #endif
580         }
581
582         /* return the number of samples read/written */
583         return i;
584 }
585
586 /* Analog output configuration */
587 static int mf624_ao_cfg(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data){
588 #ifdef EXTDEBUG
589         rt_printk("comedi%d: mf624: _ao_cfg called\n, dev->minor");
590 #endif
591         return insn->n;
592 }
593
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)
597 {
598         unsigned int i, chan = CR_CHAN(insn->chanspec);
599
600         for(i=0;i<insn->n;i++)
601                 data[i] = devpriv->ao_readback[chan];
602
603         return i;
604 }
605
606 /* Write digital data */
607 static int mf624_do_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
608 {
609 #ifdef EXTDEBUG
610         rt_printk("comedi%d: mf624: _do_insn_bits called with data: %d %d\n", dev->minor, data[0], data[1]);
611 #endif
612         if(insn->n!=2)return -EINVAL;
613
614         if(data[0]){
615                 s->state &= ~data[0];
616                 s->state |= data[0]&data[1];
617 #ifdef EXTDEBUG
618                 rt_printk ("comedi%d: mf624: _do_insn_bits: out: %d on %lx\n", dev->minor, s->state, devpriv->IO_BADR1 + MF624_DOUT );
619 #endif
620                 writew(s->state, devpriv->IO_BADR1 + MF624_DOUT);                                       
621         }
622         return 2;
623 }
624
625 /* Read digital data */
626 static int mf624_di_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
627 {
628 #ifdef EXTDEBUG
629         rt_printk("comedi%d: mf624: _di_insn_bits called with data: %d %d\n", dev->minor, data[0], data[1]);
630 #endif
631         if(insn->n!=2)return -EINVAL;
632
633         data[1] = readw(devpriv->IO_BADR1 + MF624_DIN );
634 #ifdef EXTDEBUG
635         rt_printk("comedi%d: mf624: _di_insn_bits read data: %d\n", dev->minor, data[1]);
636 #endif
637         return 2;
638 }
639
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);
643
644 #ifdef EXTDEBUG
645         rt_printk("comedi%d: mf624: _timer_winsn called for channel %d, with n = %d\n", dev->minor, channel, insn->n);
646 #endif
647
648         if(insn->n > 2)return -EINVAL;
649
650         thisboard->timer_loadA[channel] = data[0];
651         writel(thisboard->timer_loadA[channel], devpriv->IO_BADR2 + MF624_CTRXA + 0x10*channel);
652
653 #ifdef EXTDEBUG
654         rt_printk("comedi%d: mf624: _timer_winsn: preset register A set at %8x\n", dev->minor, thisboard->timer_loadA[channel]);
655 #endif
656
657         if(insn->n == 2){
658                 thisboard->timer_loadB[channel] = data[1];
659                 writel(thisboard->timer_loadB[channel], devpriv->IO_BADR2 + MF624_CTRXB + 0x10*channel);
660
661 #ifdef EXTDEBUG
662                 rt_printk("comedi%d: mf624: _timer_winsn: preset register B set at %8x\n", dev->minor, thisboard->timer_loadB[channel]);
663 #endif
664         }
665
666         return insn->n;
667 }
668
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;
672
673 #ifdef EXTDEBUG
674         rt_printk("comedi%d: mf624: _timer_rinsn called for channel %d, with n = %d\n", dev->minor, channel, insn->n);
675 #endif
676
677         if(insn->n == 4){
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);
683 #ifdef EXTDEBUG
684                 rt_printk("comedi%d: mf624: _timer_rinsn: status = %8x, loadA = %8x, loadB = %8x, config = %8x\n", dev->minor, status, loadA, loadB, config);
685 #endif
686
687                 data[0] = status;
688                 data[1] = loadA;
689                 data[2] = loadB;
690                 data[3] = config;
691         }
692         else
693                 for(i = 0 ; i < insn->n ; i++) data[i] = readl(devpriv->IO_BADR2 + MF624_CTRXDATA + 0x10*channel);                      
694
695 #ifdef EXTDEBUG
696         rt_printk("comedi%d: mf624: _timer_rinsn: readed %x\n", dev->minor, data[insn->n-1]);
697 #endif
698         return insn->n;
699 }
700
701 /* Configure timers */
702 static int mf624_timer_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
703
704         unsigned int bitfield = 0, bitfield2 = 0, status = 0, mask = 0, mask2 = 0, i = 0, dataIndex = 0, channel = CR_CHAN(insn->chanspec);
705
706 #ifdef EXTDEBUG
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]);
709         rt_printk("\n");
710 #endif
711
712         /*  For more informations, see the programming manual
713          *  available near http://www2.humusoft.cz/www/datacq/manuals/mf624um.pdf */
714
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 */
720
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 ;
724
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 ;
728
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 ;
732
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 ;
736
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 ;
742
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 ;
748
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 ;
754
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 ;
758
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 ;
764
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 ;
768
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 ;
783
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 ;
787
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 ;
791
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;
799
800                         /* Error handling                       */
801                         default : return MF624_EXIT_FAILURE;
802                 }
803         }
804         status = thisboard->timer_config[channel];
805         mask = ~mask;
806
807 #ifdef EXTDEBUG
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);
810 #endif
811
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");
816         }
817         writel(thisboard->timer_config[channel], devpriv->IO_BADR2 + MF624_CTRXMODE + 0x10*channel);
818
819         mask2 = ~(0x3F << (6*channel));
820         status = thisboard->timer_control;
821
822 #ifdef EXTDEBUG
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);
825 #endif
826
827         thisboard->timer_control = (status & mask2) + (bitfield << (6*channel));
828         writel(thisboard->timer_control, devpriv->IO_BADR2 + MF624_CTRXCTRL);
829
830         return insn->n;
831 }
832
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);
836 #ifdef EXTDEBUG
837         rt_printk("comedi%d: mf624: _enc_rinsn called for channel %d, with n = %d\n", dev->minor, channel, insn->n);
838 #endif
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;
843                 }
844                 if(channel == (thisboard->enc_chans - 2)){
845                         /* virtual channel 4 : read configuration register */
846                         data[i] = thisboard->enc_config;
847                 }
848                 else
849                         data[i] = readl(devpriv->IO_BADR2 + MF624_ENC_READ + 0x04*channel);
850         }
851
852 #ifdef EXTDEBUG
853         rt_printk("comedi%d: mf624: _enc_rinsn: readed %x\n", dev->minor, data[insn->n-1]);
854 #endif
855
856         return insn->n;
857 }
858
859 /* Configure encoders */
860 static int mf624_enc_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
861
862         unsigned char bitfield = 0;
863         unsigned int status = 0, mask = 0, i = 0, dataIndex = 0, channel = CR_CHAN(insn->chanspec);
864
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;
870         }
871
872 #ifdef EXTDEBUG
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]);
875         rt_printk("\n");
876 #endif
877
878         /*  For more informations, see the programming manual
879          *  available near http://www2.humusoft.cz/www/datacq/manuals/mf624um.pdf */
880
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 ;
889
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 ;
895
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 ;
904
905                         /* Configure encoder reset      */
906                         case MF624_ENC_FILTER_OFF :             bitfield |= 0x00 ; break ;
907                         case MF624_ENC_FILTER_ON :              bitfield |= 0x80 ; break ;
908
909                         /* Error handling               */
910                         default : return MF624_EXIT_FAILURE;
911                 }
912         }
913
914         mask = ~(0xFF << (8*channel));
915         status = thisboard->enc_config;
916
917 #ifdef EXTDEBUG
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);
920 #endif
921
922         thisboard->enc_config = (status & mask) + (bitfield << (8*channel));
923         writel(thisboard->enc_config, devpriv->IO_BADR2 + MF624_IRCCTRL);
924
925         return insn->n;
926 }
927
928 /*
929  * A convenient macro that defines init_module() and cleanup_module(),
930  * as necessary.
931  */
932 COMEDI_INITCLEANUP(driver_mf624);