]> rtime.felk.cvut.cz Git - mf6xx.git/blob - src/comedi/mf624_simple_driver/kernel/mf624.c
Changed some directory names for easier understanding.
[mf6xx.git] / src / comedi / mf624_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 typedef unsigned int lsampl_t;
59
60 #define EXTDEBUG 
61 #define MF624_EXIT_FAILURE      -1
62
63 #define PCI_VENDOR_ID_MF624 0x186c
64 #define PCI_DEVICE_ID_MF624 0x0624
65
66
67 /* BAR0 Memory Map */
68 #define MF624_GPIOC             0x54
69 #define MF624_INTCSR            0x4C
70
71 /* BAR2 Memory Map */
72 #define MF624_ADCTRL            0x00
73 #define MF624_ADDATA            0x00
74 #define MF624_DIN               0x10
75 #define MF624_DOUT              0x10
76 #define MF624_ADSTART           0x20
77 #define MF624_DA0               0x20
78
79 /* BAR4 Memory Map */
80 #define MF624_CTRXMODE          0x00
81 #define MF624_CTRXSTATUS        0x00
82 #define MF624_CTRXCTRL          0x60
83 #define MF624_CTRXA             0x04
84 #define MF624_CTRXDATA          0x04
85 #define MF624_CTRXB             0x08
86 #define MF624_IRCCTRL           0x6C
87 #define MF624_IRCSTATUS         0x6C
88 #define MF624_ENC_READ          0x70
89
90
91 /* MASK */
92 #define MF624_GPIOC_EOLC        (1 << 17) /* End of last A/D conversion */
93 #define MF624_GPIOC_LDAC        (1 << 23) /* Enable Load D/A converter */
94 #define MF624_GPIOC_DACEN       (1 << 26) /* Enable D/A outputs */
95
96
97 typedef struct mf624_board_struct{
98         char *name;
99         unsigned short device_id;       
100         unsigned int ai_chans;
101         unsigned int ai_bits;
102         unsigned int ao_chans;
103         unsigned int ao_bits;
104         unsigned int di_chans;
105         unsigned int do_chans;
106 } mf624_board;
107
108
109 static const mf624_board mf624_boards[] = {
110         {
111                 name:           "mf624",        /* device name          */
112                 device_id:      PCI_DEVICE_ID_MF624,    /* PCI dev ID   */
113                 ai_chans:       8,              /* Num of ADC channels  */
114                 ai_bits:        14,             /* Num of ADC bits      */
115                 ao_chans:       8,              /* Num of DAC channels  */
116                 ao_bits:        14,             /* Num of DAC bits      */
117                 di_chans:       8,              /* Num of digital in    */
118                 do_chans:       8,              /* Num of digital out   */
119         },
120 };
121
122 /* Number of boards */
123 #define N_BOARDS        (sizeof(mf624_boards) / sizeof(mf624_board))
124
125
126 /* Private data structure */
127 typedef struct{
128         int data;
129
130         /* would be useful for a PCI device */
131         struct pci_dev *pci_dev;
132
133         /* base addresses */
134         //resource_size_t iobase;
135
136         unsigned long BAR0;     
137         unsigned long BAR2;
138         unsigned long BAR4;     
139
140         void* IO_BAR0;  
141         void* IO_BAR2;
142         void* IO_BAR4;  
143
144         /* Used for AO readback */
145         lsampl_t ao_readback[8];
146 } mf624_private;
147
148 #define devpriv         ((mf624_private *) dev->private)
149
150
151 /* Attach/detach functions declaration */
152 /*static int mf624_attach(struct comedi_device *dev, struct comedi_devconfig *it);
153 static int mf624_detach(struct comedi_device *dev);
154 */
155
156 /* Analog functions operation to be attached */
157 static int mf624_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 
158         struct comedi_insn *insn, lsampl_t *data);
159 static int mf624_ai_cfg(struct comedi_device *dev, struct comedi_subdevice *s, 
160         struct comedi_insn *insn, lsampl_t *data);
161 static int mf624_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 
162         struct comedi_insn *insn, lsampl_t *data);
163 static int mf624_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 
164         struct comedi_insn *insn, lsampl_t *data);
165 static int mf624_ao_cfg(struct comedi_device *dev, struct comedi_subdevice *s, 
166         struct comedi_insn *insn, lsampl_t *data);
167
168
169 /* Digital functions operation to be attached */
170 static int mf624_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 
171         struct comedi_insn *insn, lsampl_t *data);
172 static int mf624_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 
173         struct comedi_insn *insn, lsampl_t *data);
174
175 /*static irqreturn_t mf624_interrupt(int irq, void *d, struct pt_regs *regs);*/
176
177
178
179 #define thisboard       ((mf624_board *)dev->board_ptr)
180 /*
181  * Attach is called by the Comedi core to configure the driver
182  * for a particular board.  If you specified a board_name array
183  * in the driver structure, dev->board_ptr contains that
184  * address.
185  */
186 static int mf624_attach(struct comedi_device *dev, struct comedi_devconfig *it)
187 {
188         struct comedi_subdevice *s;
189         struct pci_dev* pcidev;
190         unsigned int index, channel, status;
191
192         printk("comedi%d: mf624: driver: Bourgeot - Poulain 2006-2007\n", dev->minor);
193         printk("This is an experimental version, you can report \
194                 some remarks or problems to fpoulain@gmail.com\n");
195
196         
197         /* Allocate the private structure area.  alloc_private() is a
198            convenient macro defined in comedidev.h. */
199         if(alloc_private(dev, sizeof(mf624_private)) < 0) {
200                 return -ENOMEM;
201         }
202
203         /* Probe the device to determine what device in the series it is */
204         for(pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); 
205                 pcidev != NULL; 
206                 pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
207
208                 if(pcidev->vendor != PCI_VENDOR_ID_MF624) {
209                         continue;
210                 }
211
212                 /* loop through cards supported by this driver */
213                 for(index = 0; index < N_BOARDS ; index++)
214                 {
215                         if(mf624_boards[index].device_id != pcidev->device) {
216                                 continue;
217                         }
218
219                         /* was a particular bus/slot requested ? */
220                         if(it->options[0] || it->options[1])
221                         {
222                                 /* are we on the wrong bus/slot ? */
223                                 if(pcidev->bus->number != it->options[0] ||
224                                    PCI_SLOT(pcidev->devfn) != it->options[1]) {
225                                         continue;
226                                 }
227                         }
228
229                         devpriv->pci_dev = pcidev;
230                         dev->board_ptr = mf624_boards + index;
231                         goto found;
232                 }
233         }
234 #ifdef EXTDEBUG
235         printk("comedi%d: mf624: No supported Humusoft card \
236                         found on requested position\n", dev->minor);
237 #endif
238         comedi_error(dev, "No supported Humusoft card found on requested position\n");
239         return -EIO;
240
241 found:
242         printk("comedi%d: mf624: Found %s on bus %i, slot %i\n", dev->minor, 
243                 mf624_boards[index].name, pcidev->bus->number, PCI_SLOT(pcidev->devfn));
244
245         /* Enable PCI device and reserve I/O ports. */
246         if(pci_enable_device(pcidev)) {
247 #ifdef EXTDEBUG
248                 printk("comedi%d: mf624: Failed to enable PCI device\n", dev->minor);
249 #endif
250                 comedi_error(dev, "Failed to enable PCI device\n");
251                 return -EIO;
252         }
253         if(pci_request_regions(pcidev, "mf624")) {
254 #ifdef EXTDEBUG
255                 printk("comedi%d: mf624: I/O port conflict\n", dev->minor);
256 #endif
257                 comedi_error(dev, "I/O port conflict\n");
258                 return -EIO;
259         }
260
261         devpriv->BAR0 = pci_resource_start(devpriv->pci_dev, 0);
262         devpriv->BAR2 = pci_resource_start(devpriv->pci_dev, 2);
263         devpriv->BAR4 = pci_resource_start(devpriv->pci_dev, 4);
264
265         devpriv->IO_BAR0 = pci_ioremap_bar(devpriv->pci_dev, 0);
266         devpriv->IO_BAR2 = pci_ioremap_bar(devpriv->pci_dev, 2);
267         devpriv->IO_BAR4 = pci_ioremap_bar(devpriv->pci_dev, 4);
268
269 #ifdef EXTDEBUG
270         printk("comedi%d: mf624: PCI Resource 0    addr %lx \n", dev->minor, devpriv->BAR0);
271         printk("comedi%d: mf624: PCI Resource 2    addr %lx \n", dev->minor, devpriv->BAR2);
272         printk("comedi%d: mf624: PCI Resource 4    addr %lx \n", dev->minor, devpriv->BAR4);
273
274         printk("comedi%d: mf624: IO_BAR0    addr %p \n", dev->minor, devpriv->IO_BAR0);
275         printk("comedi%d: mf624: IO_BAR2(2) addr %p \n", dev->minor, devpriv->IO_BAR2);
276         printk("comedi%d: mf624: IO_BAR4(4) addr %p \n", dev->minor, devpriv->IO_BAR4); 
277 #endif
278
279         dev->board_name = thisboard->name;
280
281         /*
282          * Allocate the subdevice structures.  alloc_subdevice() is a
283          * convenient macro defined in comedidev.h.
284          */
285         if(alloc_subdevices(dev, 4) < 0) {
286                 return -ENOMEM;
287         }
288
289         /* Analog single ended (ground) input subdevice */
290         s = dev->subdevices + 0;
291         s->type = COMEDI_SUBD_AI;
292         s->subdev_flags = SDF_READABLE|SDF_GROUND;
293         s->n_chan = thisboard->ai_chans;
294         s->maxdata = (1<<thisboard->ai_bits)-1;
295         s->range_table = &range_bipolar10;
296         s->len_chanlist = 8;
297         s->insn_read = mf624_ai_rinsn;
298         s->insn_config = mf624_ai_cfg;
299
300         /* Analog output subdevice */
301         s = dev->subdevices + 1;
302         s->type = COMEDI_SUBD_AO;
303         s->subdev_flags = SDF_WRITABLE;
304         s->n_chan = thisboard->ao_chans;
305         s->maxdata = (1<<thisboard->ao_bits)-1;
306         s->range_table = &range_bipolar10;
307         s->insn_write = mf624_ao_winsn;
308         s->insn_read = mf624_ao_rinsn;
309         s->insn_config = mf624_ao_cfg;
310
311         /* Digital input subdevice */   
312         s = dev->subdevices + 2;
313         s->type = COMEDI_SUBD_DI;
314         s->subdev_flags = SDF_READABLE;
315         s->n_chan = thisboard->di_chans;
316         s->maxdata = 1;
317         s->range_table = &range_digital;
318         s->insn_bits = mf624_di_insn_bits;
319
320         /* Digital output subdevice */  
321         s = dev->subdevices + 3;
322         s->type = COMEDI_SUBD_DO;
323         s->subdev_flags = SDF_WRITABLE;
324         s->n_chan = thisboard->do_chans;
325         s->maxdata = 1;
326         s->range_table = &range_digital;
327         s->insn_bits  = mf624_do_insn_bits;
328
329
330         printk("comedi%d: mf624: Driver attached\n", dev->minor);
331
332         /* Enable DAC */
333         status = ioread32(devpriv->IO_BAR0 + MF624_GPIOC) | MF624_GPIOC_DACEN;  
334         iowrite32(status, devpriv->IO_BAR0 + MF624_GPIOC);
335
336         /* Initialise Interrupt Control Status */
337         iowrite32(0x00000000, devpriv->IO_BAR0 + MF624_INTCSR);
338
339         /* Initialise analog outputs to zero */
340         for(channel = 0; channel < thisboard->ao_chans; channel ++) {
341                 iowrite16(0x2000, devpriv->IO_BAR2 + MF624_DA0 + 2*channel);
342                 /* Readback save */
343                 devpriv->ao_readback[channel] = 0x2000;
344         }
345
346         //printk("comedi%d: mf624: Board initialized\n", dev->minor);
347         return 1;
348 }
349
350
351 /*
352  * _detach is called to deconfigure a device. It should deallocate
353  * resources.  
354  * This function is also called when _attach() fails, so it should be
355  * careful not to release resources that were not necessarily
356  * allocated by _attach(). dev->private and dev->subdevices are
357  * deallocated automatically by the core.
358  */
359 static int mf624_detach(struct comedi_device *dev)
360 {
361         //printk("comedi%d: mf624: remove\n", dev->minor);
362
363         if(devpriv && devpriv->pci_dev)
364         {
365                 if(devpriv->BAR0)
366                 {
367                         pci_release_regions(devpriv->pci_dev);
368                         pci_disable_device(devpriv->pci_dev);
369                         //printk("comedi%d: mf624: remove2\n", dev->minor);
370                 }
371                 pci_dev_put(devpriv->pci_dev);
372                 //printk("comedi%d: mf624: remove3\n", dev->minor);
373         }
374
375         return 0;
376 }
377
378 /* read n samples on Analog Input channel */
379 static int mf624_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 
380         struct comedi_insn *insn, lsampl_t *data)
381 {
382         unsigned int dat, n, i, status;
383         unsigned int chan = CR_CHAN(insn->chanspec);
384
385 #ifdef EXTDEBUG
386         printk("comedi%d: mf624: mf624_ai_rinsn called \n", dev->minor);
387 #endif
388
389         /* write channel to multiplexer */
390         iowrite16(1 << chan, devpriv->IO_BAR2 + MF624_ADCTRL);
391
392         /* convert n samples */
393         for(n = 0; n < insn->n; n ++){
394                 /* trigger conversion */
395                 dat = ioread16(devpriv->IO_BAR2 + MF624_ADSTART);
396
397 #define TIMEOUT 100 
398                 /* wait for conversion to end */
399                 for(i = 0; i < TIMEOUT; i++){
400                         status = 1;
401                         status = ioread32(devpriv->IO_BAR0 + MF624_GPIOC);
402                         if(!(status & MF624_GPIOC_EOLC)) {
403                                 break;
404                         }
405                 }
406                 if(i == TIMEOUT) {
407                         printk("comedi%d: mf624: _ai_rinsn: conversion timeout !\n", dev->minor);
408                         comedi_error(dev, "Conversion timeout !\n");
409                         return -ETIMEDOUT;
410                 }
411
412                 /* read data */
413                 dat = ioread16(devpriv->IO_BAR2 + MF624_ADDATA);
414
415                 /* mangle the data as necessary */
416                 dat ^= 1 << (thisboard->ai_bits-1);
417
418                 data[n] = dat;
419         }
420
421         /* return the number of samples read/written */
422         return n;
423 }
424
425
426 /* Analog input configuration */
427 static int mf624_ai_cfg(struct comedi_device *dev, struct comedi_subdevice *s, 
428         struct comedi_insn *insn, lsampl_t *data)
429 {
430 #ifdef EXTDEBUG
431         printk("comedi%d: mf624: _insn_ai_cfg called\n", dev->minor);
432 #endif
433         return insn->n;
434 }
435
436
437 /* write n samples on Analog Output channel */
438 static int mf624_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 
439         struct comedi_insn *insn, lsampl_t *data)
440 {
441         unsigned int i, status;
442         unsigned int chan = CR_CHAN(insn->chanspec);
443 #ifdef EXTDEBUG
444         printk("comedi%d: mf624: _ao_winsn called\n", dev->minor);
445 #endif
446         status = ioread32(devpriv->IO_BAR0 + MF624_GPIOC) | MF624_GPIOC_DACEN;
447         iowrite32(status, devpriv->IO_BAR0 + MF624_GPIOC);
448
449         /* Writing a list of values to an AO channel is probably not
450          * very useful, but that's how the interface is defined. */
451         for(i = 0; i < insn->n; i++){
452                 iowrite16(data[i], devpriv->IO_BAR2 + MF624_DA0 + 2*chan);
453                 devpriv->ao_readback[chan] = data[i];
454 #ifdef EXTDEBUG
455                 printk("comedi%d: mf624: _ao_winsn: wrote at address \
456                         %i data %i  ", dev->minor, MF624_DA0 + 2*chan , data[i] );
457 #endif
458         }
459
460         /* return the number of samples read/written */
461         return i;
462 }
463
464
465 /* Analog output configuration */
466 static int mf624_ao_cfg(struct comedi_device *dev, struct comedi_subdevice *s, 
467         struct comedi_insn *insn, lsampl_t *data)
468 {
469 #ifdef EXTDEBUG
470         printk("comedi%d: mf624: _ao_cfg called\n", dev->minor);
471 #endif
472         return insn->n;
473 }
474
475
476 /* AO subdevices should have a read insn as well as a write insn.
477  * Usually this means copying a value stored in devpriv. */
478 static int mf624_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 
479         struct comedi_insn *insn, lsampl_t *data)
480 {
481         unsigned int i, chan = CR_CHAN(insn->chanspec);
482
483         for(i = 0; i < insn->n; i++) {
484                 data[i] = devpriv->ao_readback[chan];
485         }
486
487         return i;
488 }
489
490 /* Write digital data */
491 static int mf624_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 
492         struct comedi_insn *insn, lsampl_t *data)
493 {
494 #ifdef EXTDEBUG
495         printk("comedi%d: mf624: _do_insn_bits called \
496                 with data: %d %d\n", dev->minor, data[0], data[1]);
497 #endif
498         if(insn->n != 2) {
499                 return -EINVAL;
500         }
501
502         if(data[0]) {
503                 s->state &= ~data[0];
504                 s->state |= data[0] & data[1];
505 #ifdef EXTDEBUG
506                 printk ("comedi%d: mf624: _do_insn_bits: out: %d \
507                         on %lx\n", dev->minor, s->state, devpriv->IO_BAR2 + MF624_DOUT );
508 #endif
509                 iowrite16(s->state, devpriv->IO_BAR2 + MF624_DOUT);                                     
510         }
511         return 2;
512 }
513
514 /* Read digital data */
515 static int mf624_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 
516         struct comedi_insn *insn, lsampl_t *data)
517 {
518 #ifdef EXTDEBUG
519         printk("comedi%d: mf624: _di_insn_bits called with \
520                 data: %d %d\n", dev->minor, data[0], data[1]);
521 #endif
522         if(insn->n != 2) {
523                 return -EINVAL;
524         }
525
526         data[1] = ioread16(devpriv->IO_BAR2 + MF624_DIN);
527 #ifdef EXTDEBUG
528         printk("comedi%d: mf624: _di_insn_bits read \
529                 data: %d\n", dev->minor, data[1]);
530 #endif
531         return 2;
532 }
533
534
535 static struct pci_device_id mf624_pci_table[] __devinitdata = {
536         { PCI_VENDOR_ID_MF624, PCI_DEVICE_ID_MF624, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
537         { 0 }
538 };
539 MODULE_DEVICE_TABLE(pci, mf624_pci_table);
540
541 /*
542  * The comedi_driver structure tells the Comedi core module
543  * which functions to call to configure/deconfigure (attach/detach)
544  * the board, and also about the kernel module that contains
545  * the device code.
546  */
547 static struct comedi_driver driver_mf624 = {
548         driver_name:    "mf624",
549         module:         THIS_MODULE,
550         attach:         mf624_attach,
551         detach:         mf624_detach,
552 };
553
554
555 MODULE_AUTHOR("Francois Poulain <fpoulain@gmail.com>;"
556                  "(Rostislav Lisovy <lisovy@gmail.com>)");
557 MODULE_DESCRIPTION("Humusoft MF624 Multifunction I/O Card");
558 MODULE_LICENSE("GPL");
559
560 COMEDI_INITCLEANUP(driver_mf624);