]> rtime.felk.cvut.cz Git - mf6xx.git/blob - src/comedi/simple_driver/kernel/mf624.c
Compilable version
[mf6xx.git] / src / comedi / simple_driver / kernel / 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, (Minor changes -- Rostislav Lisovy)
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                 : Not yet supported.
47 Quadrature Encoder Inputs       : Not yet supported
48 IRQ                             : Not yet supported.
49
50 Remarks:
51 Simultaneous D/A update not yet supported.
52
53 Configuration Options:
54 none
55 */
56 #include "../comedidev.h"
57 #include <linux/pci.h>
58 #include "mf624.h"
59 typedef unsigned int lsampl_t;
60
61 #define EXTDEBUG 
62 #define MF624_EXIT_FAILURE      -1
63
64 #define PCI_VENDOR_ID_MF624 0x186c
65 #define PCI_DEVICE_ID_MF624 0x0624
66
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
73
74
75 /* BADR0 Memory Map */
76 #define MF624_GPIOC             0x54
77 #define MF624_INTCSR            0x4C
78
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
86
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
97
98
99 /* MASK */
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 */
103
104
105 typedef struct mf624_board_struct{
106         char *name;
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;
114 } mf624_board;
115
116
117 static const mf624_board mf624_boards[] = {
118         {
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   */
127         },
128 };
129
130 /* Number of boards */
131 #define N_BOARDS        (sizeof(mf624_boards) / sizeof(mf624_board))
132
133
134 /* Private data structure */
135 typedef struct{
136         int data;
137
138         /* would be useful for a PCI device */
139         struct pci_dev *pci_dev;
140
141         /* base addresses */
142         //resource_size_t iobase;
143
144         unsigned long BADR0;    
145         unsigned long BADR1;
146         unsigned long BADR2;    
147
148         void* IO_BADR0; 
149         void* IO_BADR1;
150         void* IO_BADR2; 
151
152         /* Used for AO readback */
153         lsampl_t ao_readback[8];
154 } mf624_private;
155
156 #define devpriv         ((mf624_private *) dev->private)
157
158
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);
162 */
163
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);
175
176
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);
182
183 /*static irqreturn_t mf624_interrupt(int irq, void *d, struct pt_regs *regs);*/
184
185
186
187 #define thisboard       ((mf624_board *)dev->board_ptr)
188 /*
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
192  * address.
193  */
194 static int mf624_attach(struct comedi_device *dev, struct comedi_devconfig *it)
195 {
196         struct comedi_subdevice *s;
197         struct pci_dev* pcidev;
198         unsigned int index, channel, status;
199
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");
203
204         
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) {
208                 return -ENOMEM;
209         }
210
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); 
213                 pcidev != NULL; 
214                 pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
215
216                 if(pcidev->vendor != PCI_VENDOR_ID_MF624) {
217                         continue;
218                 }
219
220                 /* loop through cards supported by this driver */
221                 for(index = 0; index < N_BOARDS ; index++)
222                 {
223                         if(mf624_boards[index].device_id != pcidev->device) {
224                                 continue;
225                         }
226
227                         /* was a particular bus/slot requested ? */
228                         if(it->options[0] || it->options[1])
229                         {
230                                 /* are we on the wrong bus/slot ? */
231                                 if(pcidev->bus->number != it->options[0] ||
232                                    PCI_SLOT(pcidev->devfn) != it->options[1]) {
233                                         continue;
234                                 }
235                         }
236
237                         devpriv->pci_dev = pcidev;
238                         dev->board_ptr = mf624_boards + index;
239                         goto found;
240                 }
241         }
242 #ifdef EXTDEBUG
243         printk("comedi%d: mf624: No supported Humusoft card \
244                         found on requested position\n", dev->minor);
245 #endif
246         comedi_error(dev, "No supported Humusoft card found on requested position\n");
247         return -EIO;
248
249 found:
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));
252
253         /* Enable PCI device and reserve I/O ports. */
254         if(pci_enable_device(pcidev)) {
255 #ifdef EXTDEBUG
256                 printk("comedi%d: mf624: Failed to enable PCI device\n", dev->minor);
257 #endif
258                 comedi_error(dev, "Failed to enable PCI device\n");
259                 return -EIO;
260         }
261         if(pci_request_regions(pcidev, "mf624")) {
262 #ifdef EXTDEBUG
263                 printk("comedi%d: mf624: I/O port conflict\n", dev->minor);
264 #endif
265                 comedi_error(dev, "I/O port conflict\n");
266                 return -EIO;
267         }
268
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);
272
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);
276
277 #ifdef EXTDEBUG
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);
281
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);      
285 #endif
286
287         dev->board_name = thisboard->name;
288
289         /*
290          * Allocate the subdevice structures.  alloc_subdevice() is a
291          * convenient macro defined in comedidev.h.
292          */
293         if(alloc_subdevices(dev, MF624_SUBDEV_COUNT) < 0) {
294                 return -ENOMEM;
295         }
296
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;
304         s->len_chanlist = 8;
305         s->insn_read = mf624_ai_rinsn;
306         s->insn_config = mf624_ai_cfg;
307
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;
318
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;
324         s->maxdata = 1;
325         s->range_table = &range_digital;
326         s->insn_bits = mf624_di_insn_bits;
327
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;
333         s->maxdata = 1;
334         s->range_table = &range_digital;
335         s->insn_bits  = mf624_do_insn_bits;
336
337
338         printk("comedi%d: mf624: Driver attached\n", dev->minor);
339
340         /* Enable DAC */
341         status = ioread32(devpriv->IO_BADR0 + MF624_GPIOC) | MF624_GPIOC_DACEN;  
342         iowrite32(status, devpriv->IO_BADR0 + MF624_GPIOC);
343
344         /* Initialise Interrupt Control Status */
345         iowrite32(0x00000000, devpriv->IO_BADR0 + MF624_INTCSR);
346
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);
350                 /* Readback save */
351                 devpriv->ao_readback[channel] = 0x2000;
352         }
353
354         //printk("comedi%d: mf624: Board initialized\n", dev->minor);
355         return 1;
356 }
357
358
359 /*
360  * _detach is called to deconfigure a device. It should deallocate
361  * resources.  
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.
366  */
367 static int mf624_detach(struct comedi_device *dev)
368 {
369         //printk("comedi%d: mf624: remove\n", dev->minor);
370
371         if(devpriv && devpriv->pci_dev)
372         {
373                 if(devpriv->BADR0)
374                 {
375                         pci_release_regions(devpriv->pci_dev);
376                         pci_disable_device(devpriv->pci_dev);
377                         //printk("comedi%d: mf624: remove2\n", dev->minor);
378                 }
379                 pci_dev_put(devpriv->pci_dev);
380                 //printk("comedi%d: mf624: remove3\n", dev->minor);
381         }
382
383         return 0;
384 }
385
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)
389 {
390         unsigned int dat, n, i, status;
391         unsigned int chan = CR_CHAN(insn->chanspec);
392
393 #ifdef EXTDEBUG
394         printk("comedi%d: mf624: mf624_ai_rinsn called \n", dev->minor);
395 #endif
396
397         /* write channel to multiplexer */
398         iowrite16(1 << chan, devpriv->IO_BADR1 + MF624_ADCTRL);
399
400         /* convert n samples */
401         for(n = 0; n < insn->n; n ++){
402                 /* trigger conversion */
403                 dat = ioread16(devpriv->IO_BADR1 + MF624_ADSTART);
404
405 #define TIMEOUT 100 
406                 /* wait for conversion to end */
407                 for(i = 0; i < TIMEOUT; i++){
408                         status = 1;
409                         status = ioread32(devpriv->IO_BADR0 + MF624_GPIOC);
410                         if(!(status & MF624_GPIOC_EOLC)) {
411                                 break;
412                         }
413                 }
414                 if(i == TIMEOUT) {
415                         printk("comedi%d: mf624: _ai_rinsn: conversion timeout !\n", dev->minor);
416                         comedi_error(dev, "Conversion timeout !\n");
417                         return -ETIMEDOUT;
418                 }
419
420                 /* read data */
421                 dat = ioread16(devpriv->IO_BADR1 + MF624_ADDATA);
422
423                 /* mangle the data as necessary */
424                 dat ^= 1 << (thisboard->ai_bits-1);
425
426                 data[n] = dat;
427         }
428
429         /* return the number of samples read/written */
430         return n;
431 }
432
433
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)
437 {
438 #ifdef EXTDEBUG
439         printk("comedi%d: mf624: _insn_ai_cfg called\n", dev->minor);
440 #endif
441         return insn->n;
442 }
443
444
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)
448 {
449         unsigned int i, status;
450         unsigned int chan = CR_CHAN(insn->chanspec);
451 #ifdef EXTDEBUG
452         printk("comedi%d: mf624: _ao_winsn called\n", dev->minor);
453 #endif
454         status = ioread32(devpriv->IO_BADR0 + MF624_GPIOC) | MF624_GPIOC_DACEN;
455         iowrite32(status, devpriv->IO_BADR0 + MF624_GPIOC);
456
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];
462 #ifdef EXTDEBUG
463                 printk("comedi%d: mf624: _ao_winsn: wrote at address \
464                         %i data %i  ", dev->minor, MF624_DA0 + 2*chan , data[i] );
465 #endif
466         }
467
468         /* return the number of samples read/written */
469         return i;
470 }
471
472
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)
476 {
477 #ifdef EXTDEBUG
478         printk("comedi%d: mf624: _ao_cfg called\n", dev->minor);
479 #endif
480         return insn->n;
481 }
482
483
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)
488 {
489         unsigned int i, chan = CR_CHAN(insn->chanspec);
490
491         for(i = 0; i < insn->n; i++) {
492                 data[i] = devpriv->ao_readback[chan];
493         }
494
495         return i;
496 }
497
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)
501 {
502 #ifdef EXTDEBUG
503         printk("comedi%d: mf624: _do_insn_bits called \
504                 with data: %d %d\n", dev->minor, data[0], data[1]);
505 #endif
506         if(insn->n != 2) {
507                 return -EINVAL;
508         }
509
510         if(data[0]) {
511                 s->state &= ~data[0];
512                 s->state |= data[0] & data[1];
513 #ifdef EXTDEBUG
514                 printk ("comedi%d: mf624: _do_insn_bits: out: %d \
515                         on %lx\n", dev->minor, s->state, devpriv->IO_BADR1 + MF624_DOUT );
516 #endif
517                 iowrite16(s->state, devpriv->IO_BADR1 + MF624_DOUT);                                    
518         }
519         return 2;
520 }
521
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)
525 {
526 #ifdef EXTDEBUG
527         printk("comedi%d: mf624: _di_insn_bits called with \
528                 data: %d %d\n", dev->minor, data[0], data[1]);
529 #endif
530         if(insn->n != 2) {
531                 return -EINVAL;
532         }
533
534         data[1] = ioread16(devpriv->IO_BADR1 + MF624_DIN);
535 #ifdef EXTDEBUG
536         printk("comedi%d: mf624: _di_insn_bits read \
537                 data: %d\n", dev->minor, data[1]);
538 #endif
539         return 2;
540 }
541
542
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 },
545         { 0 }
546 };
547 MODULE_DEVICE_TABLE(pci, mf624_pci_table);
548
549 /*
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
553  * the device code.
554  */
555 static struct comedi_driver driver_mf624 = {
556         driver_name:    "mf624",
557         module:         THIS_MODULE,
558         attach:         mf624_attach,
559         detach:         mf624_detach,
560 };
561
562
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");
567
568 COMEDI_INITCLEANUP(driver_mf624);