Merge tag 'mmc-fixes-for-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
[cascardo/linux.git] / drivers / staging / comedi / drivers / ni_daq_700.c
1 /*
2  *     comedi/drivers/ni_daq_700.c
3  *     Driver for DAQCard-700 DIO/AI
4  *     copied from 8255
5  *
6  *     COMEDI - Linux Control and Measurement Device Interface
7  *     Copyright (C) 1998 David A. Schleef <ds@schleef.org>
8  *
9  *     This program is free software; you can redistribute it and/or modify
10  *     it under the terms of the GNU General Public License as published by
11  *     the Free Software Foundation; either version 2 of the License, or
12  *     (at your option) any later version.
13  *
14  *     This program is distributed in the hope that it will be useful,
15  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *     GNU General Public License for more details.
18  *
19  *     You should have received a copy of the GNU General Public License
20  *     along with this program; if not, write to the Free Software
21  *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  */
24
25 /*
26 Driver: ni_daq_700
27 Description: National Instruments PCMCIA DAQCard-700 DIO only
28 Author: Fred Brooks <nsaspook@nsaspook.com>,
29   based on ni_daq_dio24 by Daniel Vecino Castel <dvecino@able.es>
30 Devices: [National Instruments] PCMCIA DAQ-Card-700 (ni_daq_700)
31 Status: works
32 Updated: Wed, 19 Sep 2012 12:07:20 +0000
33
34 The daqcard-700 appears in Comedi as a  digital I/O subdevice (0) with
35 16 channels and a analog input subdevice (1) with 16 single-ended channels.
36
37 Digital:  The channel 0 corresponds to the daqcard-700's output
38 port, bit 0; channel 8 corresponds to the input port, bit 0.
39
40 Digital direction configuration: channels 0-7 output, 8-15 input (8225 device
41 emu as port A output, port B input, port C N/A).
42
43 Analog: The input  range is 0 to 4095 for -10 to +10 volts 
44 IRQ is assigned but not used.
45
46 Version 0.1     Original DIO only driver
47 Version 0.2     DIO and basic AI analog input support on 16 se channels
48
49 Manuals:        Register level: http://www.ni.com/pdf/manuals/340698.pdf
50                 User Manual:    http://www.ni.com/pdf/manuals/320676d.pdf
51 */
52
53 #include <linux/interrupt.h>
54 #include <linux/slab.h>
55 #include "../comedidev.h"
56
57 #include <linux/ioport.h>
58
59 #include <pcmcia/cistpl.h>
60 #include <pcmcia/cisreg.h>
61 #include <pcmcia/ds.h>
62
63 static struct pcmcia_device *pcmcia_cur_dev;
64
65 struct daq700_board {
66         const char *name;
67 };
68
69 /* daqcard700 registers */
70 #define DIO_W           0x04    /* WO 8bit */
71 #define DIO_R           0x05    /* RO 8bit */
72 #define CMD_R1          0x00    /* WO 8bit */
73 #define CMD_R2          0x07    /* RW 8bit */
74 #define CMD_R3          0x05    /* W0 8bit */
75 #define STA_R1          0x00    /* RO 8bit */
76 #define STA_R2          0x01    /* RO 8bit */
77 #define ADFIFO_R        0x02    /* RO 16bit */
78 #define ADCLEAR_R       0x01    /* WO 8bit */
79 #define CDA_R0          0x08    /* RW 8bit */
80 #define CDA_R1          0x09    /* RW 8bit */
81 #define CDA_R2          0x0A    /* RW 8bit */
82 #define CMO_R           0x0B    /* RO 8bit */
83 #define TIC_R           0x06    /* WO 8bit */
84
85 static int daq700_dio_insn_bits(struct comedi_device *dev,
86                                 struct comedi_subdevice *s,
87                                 struct comedi_insn *insn, unsigned int *data)
88 {
89         if (data[0]) {
90                 s->state &= ~data[0];
91                 s->state |= (data[0] & data[1]);
92
93                 if (data[0] & 0xff)
94                         outb(s->state & 0xff, dev->iobase + DIO_W);
95         }
96
97         data[1] = s->state & 0xff;
98         data[1] |= inb(dev->iobase + DIO_R) << 8;
99
100         return insn->n;
101 }
102
103 static int daq700_dio_insn_config(struct comedi_device *dev,
104                                   struct comedi_subdevice *s,
105                                   struct comedi_insn *insn, unsigned int *data)
106 {
107         unsigned int chan = 1 << CR_CHAN(insn->chanspec);
108
109         switch (data[0]) {
110         case INSN_CONFIG_DIO_INPUT:
111                 break;
112         case INSN_CONFIG_DIO_OUTPUT:
113                 break;
114         case INSN_CONFIG_DIO_QUERY:
115                 data[1] = (s->io_bits & chan) ? COMEDI_OUTPUT : COMEDI_INPUT;
116                 break;
117         default:
118                 return -EINVAL;
119         }
120
121         return insn->n;
122 }
123
124 static int daq700_ai_rinsn(struct comedi_device *dev,
125                            struct comedi_subdevice *s,
126                            struct comedi_insn *insn, unsigned int *data)
127 {
128         int n, i, chan;
129         int d;
130         unsigned int status;
131         enum { TIMEOUT = 100 };
132
133         chan = CR_CHAN(insn->chanspec);
134         /* write channel to multiplexer */
135         /* set mask scan bit high to disable scanning */
136         outb(chan | 0x80, dev->iobase + CMD_R1);
137
138         /* convert n samples */
139         for (n = 0; n < insn->n; n++) {
140                 /* trigger conversion with out0 L to H */
141                 outb(0x00, dev->iobase + CMD_R2); /* enable ADC conversions */
142                 outb(0x30, dev->iobase + CMO_R); /* mode 0 out0 L, from H */
143                 /* mode 1 out0 H, L to H, start conversion */
144                 outb(0x32, dev->iobase + CMO_R);
145                 /* wait for conversion to end */
146                 for (i = 0; i < TIMEOUT; i++) {
147                         status = inb(dev->iobase + STA_R2);
148                         if ((status & 0x03) != 0) {
149                                 dev_info(dev->class_dev,
150                                          "Overflow/run Error\n");
151                                 return -EOVERFLOW;
152                         }
153                         status = inb(dev->iobase + STA_R1);
154                         if ((status & 0x02) != 0) {
155                                 dev_info(dev->class_dev, "Data Error\n");
156                                 return -ENODATA;
157                         }
158                         if ((status & 0x11) == 0x01) {
159                                 /* ADC conversion complete */
160                                 break;
161                         }
162                         udelay(1);
163                 }
164                 if (i == TIMEOUT) {
165                         dev_info(dev->class_dev,
166                                  "timeout during ADC conversion\n");
167                         return -ETIMEDOUT;
168                 }
169                 /* read data */
170                 d = inw(dev->iobase + ADFIFO_R);
171                 /* mangle the data as necessary */
172                 /* Bipolar Offset Binary: 0 to 4095 for -10 to +10 */
173                 d &= 0x0fff;
174                 d ^= 0x0800;
175                 data[n] = d;
176         }
177         return n;
178 }
179
180 /*
181  * Data acquisition is enabled.
182  * The counter 0 output is high.
183  * The I/O connector pin CLK1 drives counter 1 source.
184  * Multiple-channel scanning is disabled.
185  * All interrupts are disabled.
186  * The analog input range is set to +-10 V
187  * The analog input mode is single-ended.
188  * The analog input circuitry is initialized to channel 0.
189  * The A/D FIFO is cleared.
190  */
191 static void daq700_ai_config(struct comedi_device *dev,
192                              struct comedi_subdevice *s)
193 {                       
194         unsigned long iobase = dev->iobase;
195
196         outb(0x80, iobase + CMD_R1);    /* disable scanning, ADC to chan 0 */
197         outb(0x00, iobase + CMD_R2);    /* clear all bits */
198         outb(0x00, iobase + CMD_R3);    /* set +-10 range */
199         outb(0x32, iobase + CMO_R);     /* config counter mode1, out0 to H */
200         outb(0x00, iobase + TIC_R);     /* clear counter interrupt */
201         outb(0x00, iobase + ADCLEAR_R); /* clear the ADC FIFO */
202         inw(iobase + ADFIFO_R);         /* read 16bit junk from FIFO to clear */
203 }
204
205 static int daq700_attach(struct comedi_device *dev, struct comedi_devconfig *it)
206 {
207         const struct daq700_board *thisboard = comedi_board(dev);
208         struct comedi_subdevice *s;
209         struct pcmcia_device *link;
210         int ret;
211
212         link = pcmcia_cur_dev;  /* XXX hack */
213         if (!link)
214                 return -EIO;
215
216         dev->iobase = link->resource[0]->start;
217         if (!dev->iobase) {
218                 dev_err(dev->class_dev, "io base address is zero!\n");
219                 return -EINVAL;
220         }
221
222         dev->board_name = thisboard->name;
223
224         ret = comedi_alloc_subdevices(dev, 2);
225         if (ret)
226                 return ret;
227
228         /* DAQCard-700 dio */
229         s = &dev->subdevices[0];
230         s->type         = COMEDI_SUBD_DIO;
231         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
232         s->n_chan       = 16;
233         s->range_table  = &range_digital;
234         s->maxdata      = 1;
235         s->insn_bits    = daq700_dio_insn_bits;
236         s->insn_config  = daq700_dio_insn_config;
237         s->state        = 0;
238         s->io_bits      = 0x00ff;
239
240         /* DAQCard-700 ai */
241         s = &dev->subdevices[1];
242         s->type = COMEDI_SUBD_AI;
243         /* we support single-ended (ground)  */
244         s->subdev_flags = SDF_READABLE | SDF_GROUND;
245         s->n_chan = 16;
246         s->maxdata = (1 << 12) - 1;
247         s->range_table = &range_bipolar10;
248         s->insn_read = daq700_ai_rinsn;
249         daq700_ai_config(dev, s);
250
251         dev_info(dev->class_dev, "%s: %s, io 0x%lx\n",
252                 dev->driver->driver_name,
253                 dev->board_name,
254                 dev->iobase);
255
256         return 0;
257 }
258
259 static void daq700_detach(struct comedi_device *dev)
260 {
261         /* nothing to cleanup */
262 }
263
264 static const struct daq700_board daq700_boards[] = {
265         {
266                 .name           = "daqcard-700",
267         }, {
268                 .name           = "ni_daq_700",
269         },
270 };
271
272 static struct comedi_driver daq700_driver = {
273         .driver_name    = "ni_daq_700",
274         .module         = THIS_MODULE,
275         .attach         = daq700_attach,
276         .detach         = daq700_detach,
277         .board_name     = &daq700_boards[0].name,
278         .num_names      = ARRAY_SIZE(daq700_boards),
279         .offset         = sizeof(struct daq700_board),
280 };
281
282 static int daq700_pcmcia_config_loop(struct pcmcia_device *p_dev,
283                                 void *priv_data)
284 {
285         if (p_dev->config_index == 0)
286                 return -EINVAL;
287
288         return pcmcia_request_io(p_dev);
289 }
290
291 static int daq700_cs_attach(struct pcmcia_device *link)
292 {
293         int ret;
294
295         link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_AUDIO |
296                 CONF_AUTO_SET_IO;
297
298         ret = pcmcia_loop_config(link, daq700_pcmcia_config_loop, NULL);
299         if (ret)
300                 goto failed;
301
302         if (!link->irq)
303                 goto failed;
304
305         ret = pcmcia_enable_device(link);
306         if (ret)
307                 goto failed;
308
309         pcmcia_cur_dev = link;
310         return 0;
311
312 failed:
313         pcmcia_disable_device(link);
314         return ret;
315 }
316
317 static void daq700_cs_detach(struct pcmcia_device *link)
318 {
319         pcmcia_disable_device(link);
320         pcmcia_cur_dev = NULL;
321 }
322
323 static const struct pcmcia_device_id daq700_cs_ids[] = {
324         PCMCIA_DEVICE_MANF_CARD(0x010b, 0x4743),
325         PCMCIA_DEVICE_NULL
326 };
327 MODULE_DEVICE_TABLE(pcmcia, daq700_cs_ids);
328
329 static struct pcmcia_driver daq700_cs_driver = {
330         .name           = "ni_daq_700",
331         .owner          = THIS_MODULE,
332         .probe          = daq700_cs_attach,
333         .remove         = daq700_cs_detach,
334         .id_table       = daq700_cs_ids,
335 };
336
337 static int __init daq700_cs_init(void)
338 {
339         int ret;
340
341         ret = comedi_driver_register(&daq700_driver);
342         if (ret < 0)
343                 return ret;
344
345         ret = pcmcia_register_driver(&daq700_cs_driver);
346         if (ret < 0) {
347                 comedi_driver_unregister(&daq700_driver);
348                 return ret;
349         }
350
351         return 0;
352 }
353 module_init(daq700_cs_init);
354
355 static void __exit daq700_cs_exit(void)
356 {
357         pcmcia_unregister_driver(&daq700_cs_driver);
358         comedi_driver_unregister(&daq700_driver);
359 }
360 module_exit(daq700_cs_exit);
361
362 MODULE_AUTHOR("Fred Brooks <nsaspook@nsaspook.com>");
363 MODULE_DESCRIPTION(
364         "Comedi driver for National Instruments PCMCIA DAQCard-700 DIO/AI");
365 MODULE_VERSION("0.2.00");
366 MODULE_LICENSE("GPL");