packaging: release out (3.8.3)
[profile/ivi/kernel-adaptation-intel-automotive.git] / drivers / staging / comedi / drivers / cb_pcimdas.c
1 /*
2     comedi/drivers/cb_pcimdas.c
3     Comedi driver for Computer Boards PCIM-DAS1602/16
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: cb_pcimdas
25 Description: Measurement Computing PCI Migration series boards
26 Devices: [ComputerBoards] PCIM-DAS1602/16 (cb_pcimdas)
27 Author: Richard Bytheway
28 Updated: Wed, 13 Nov 2002 12:34:56 +0000
29 Status: experimental
30
31 Written to support the PCIM-DAS1602/16 on a 2.4 series kernel.
32
33 Configuration Options:
34     [0] - PCI bus number
35     [1] - PCI slot number
36
37 Developed from cb_pcidas and skel by Richard Bytheway (mocelet@sucs.org).
38 Only supports DIO, AO and simple AI in it's present form.
39 No interrupts, multi channel or FIFO AI, although the card looks like it could support this.
40 See http://www.mccdaq.com/PDFs/Manuals/pcim-das1602-16.pdf for more details.
41 */
42
43 #include "../comedidev.h"
44
45 #include <linux/delay.h>
46 #include <linux/interrupt.h>
47
48 #include "plx9052.h"
49 #include "8255.h"
50
51 /* #define CBPCIMDAS_DEBUG */
52 #undef CBPCIMDAS_DEBUG
53
54 /* Registers for the PCIM-DAS1602/16 */
55
56 /* sizes of io regions (bytes) */
57 #define BADR3_SIZE 16
58
59 /* DAC Offsets */
60 #define ADC_TRIG 0
61 #define DAC0_OFFSET 2
62 #define DAC1_OFFSET 4
63
64 /* AI and Counter Constants */
65 #define MUX_LIMITS 0
66 #define MAIN_CONN_DIO 1
67 #define ADC_STAT 2
68 #define ADC_CONV_STAT 3
69 #define ADC_INT 4
70 #define ADC_PACER 5
71 #define BURST_MODE 6
72 #define PROG_GAIN 7
73 #define CLK8254_1_DATA 8
74 #define CLK8254_2_DATA 9
75 #define CLK8254_3_DATA 10
76 #define CLK8254_CONTROL 11
77 #define USER_COUNTER 12
78 #define RESID_COUNT_H 13
79 #define RESID_COUNT_L 14
80
81 /*
82  * this structure is for data unique to this hardware driver.  If
83  * several hardware drivers keep similar information in this structure,
84  * feel free to suggest moving the variable to the struct comedi_device
85  * struct.
86  */
87 struct cb_pcimdas_private {
88         /* base addresses */
89         unsigned long BADR3;
90
91         /* Used for AO readback */
92         unsigned int ao_readback[2];
93 };
94
95 /*
96  * "instructions" read/write data in "one-shot" or "software-triggered"
97  * mode.
98  */
99 static int cb_pcimdas_ai_rinsn(struct comedi_device *dev,
100                                struct comedi_subdevice *s,
101                                struct comedi_insn *insn, unsigned int *data)
102 {
103         struct cb_pcimdas_private *devpriv = dev->private;
104         int n, i;
105         unsigned int d;
106         unsigned int busy;
107         int chan = CR_CHAN(insn->chanspec);
108         unsigned short chanlims;
109         int maxchans;
110
111         /*  only support sw initiated reads from a single channel */
112
113         /* check channel number */
114         if ((inb(devpriv->BADR3 + 2) & 0x20) == 0)      /* differential mode */
115                 maxchans = s->n_chan / 2;
116         else
117                 maxchans = s->n_chan;
118
119         if (chan > (maxchans - 1))
120                 return -ETIMEDOUT;      /* *** Wrong error code. Fixme. */
121
122         /* configure for sw initiated read */
123         d = inb(devpriv->BADR3 + 5);
124         if ((d & 0x03) > 0) {   /* only reset if needed. */
125                 d = d & 0xfd;
126                 outb(d, devpriv->BADR3 + 5);
127         }
128         outb(0x01, devpriv->BADR3 + 6); /* set bursting off, conversions on */
129         outb(0x00, devpriv->BADR3 + 7); /* set range to 10V. UP/BP is controlled by a switch on the board */
130
131         /*
132          * write channel limits to multiplexer, set Low (bits 0-3) and
133          * High (bits 4-7) channels to chan.
134          */
135         chanlims = chan | (chan << 4);
136         outb(chanlims, devpriv->BADR3 + 0);
137
138         /* convert n samples */
139         for (n = 0; n < insn->n; n++) {
140                 /* trigger conversion */
141                 outw(0, dev->iobase + 0);
142
143 #define TIMEOUT 1000            /* typically takes 5 loops on a lightly loaded Pentium 100MHz, */
144                 /* this is likely to be 100 loops on a 2GHz machine, so set 1000 as the limit. */
145
146                 /* wait for conversion to end */
147                 for (i = 0; i < TIMEOUT; i++) {
148                         busy = inb(devpriv->BADR3 + 2) & 0x80;
149                         if (!busy)
150                                 break;
151                 }
152                 if (i == TIMEOUT) {
153                         printk("timeout\n");
154                         return -ETIMEDOUT;
155                 }
156                 /* read data */
157                 data[n] = inw(dev->iobase + 0);
158         }
159
160         /* return the number of samples read/written */
161         return n;
162 }
163
164 static int cb_pcimdas_ao_winsn(struct comedi_device *dev,
165                                struct comedi_subdevice *s,
166                                struct comedi_insn *insn, unsigned int *data)
167 {
168         struct cb_pcimdas_private *devpriv = dev->private;
169         int i;
170         int chan = CR_CHAN(insn->chanspec);
171
172         /* Writing a list of values to an AO channel is probably not
173          * very useful, but that's how the interface is defined. */
174         for (i = 0; i < insn->n; i++) {
175                 switch (chan) {
176                 case 0:
177                         outw(data[i] & 0x0FFF, dev->iobase + DAC0_OFFSET);
178                         break;
179                 case 1:
180                         outw(data[i] & 0x0FFF, dev->iobase + DAC1_OFFSET);
181                         break;
182                 default:
183                         return -1;
184                 }
185                 devpriv->ao_readback[chan] = data[i];
186         }
187
188         /* return the number of samples read/written */
189         return i;
190 }
191
192 /* AO subdevices should have a read insn as well as a write insn.
193  * Usually this means copying a value stored in devpriv. */
194 static int cb_pcimdas_ao_rinsn(struct comedi_device *dev,
195                                struct comedi_subdevice *s,
196                                struct comedi_insn *insn, unsigned int *data)
197 {
198         struct cb_pcimdas_private *devpriv = dev->private;
199         int i;
200         int chan = CR_CHAN(insn->chanspec);
201
202         for (i = 0; i < insn->n; i++)
203                 data[i] = devpriv->ao_readback[chan];
204
205         return i;
206 }
207
208 static int cb_pcimdas_auto_attach(struct comedi_device *dev,
209                                             unsigned long context_unused)
210 {
211         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
212         struct cb_pcimdas_private *devpriv;
213         struct comedi_subdevice *s;
214         unsigned long iobase_8255;
215         int ret;
216
217         dev->board_name = dev->driver->driver_name;
218
219         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
220         if (!devpriv)
221                 return -ENOMEM;
222         dev->private = devpriv;
223
224         ret = comedi_pci_enable(pcidev, dev->board_name);
225         if (ret)
226                 return ret;
227
228         dev->iobase = pci_resource_start(pcidev, 2);
229         devpriv->BADR3 = pci_resource_start(pcidev, 3);
230         iobase_8255 = pci_resource_start(pcidev, 4);
231
232 /* Dont support IRQ yet */
233 /*  get irq */
234 /* if(request_irq(pcidev->irq, cb_pcimdas_interrupt, IRQF_SHARED, "cb_pcimdas", dev )) */
235 /* { */
236 /* printk(" unable to allocate irq %u\n", pcidev->irq); */
237 /* return -EINVAL; */
238 /* } */
239 /* dev->irq = pcidev->irq; */
240
241         ret = comedi_alloc_subdevices(dev, 3);
242         if (ret)
243                 return ret;
244
245         s = &dev->subdevices[0];
246         /* dev->read_subdev=s; */
247         /*  analog input subdevice */
248         s->type = COMEDI_SUBD_AI;
249         s->subdev_flags = SDF_READABLE | SDF_GROUND;
250         s->n_chan = 16;
251         s->maxdata = 0xffff;
252         s->range_table = &range_unknown;
253         s->len_chanlist = 1;    /*  This is the maximum chanlist length that */
254         /*  the board can handle */
255         s->insn_read = cb_pcimdas_ai_rinsn;
256
257         s = &dev->subdevices[1];
258         /*  analog output subdevice */
259         s->type = COMEDI_SUBD_AO;
260         s->subdev_flags = SDF_WRITABLE;
261         s->n_chan = 2;
262         s->maxdata = 0xfff;
263         /* ranges are hardware settable, but not software readable. */
264         s->range_table = &range_unknown;
265         s->insn_write = &cb_pcimdas_ao_winsn;
266         s->insn_read = &cb_pcimdas_ao_rinsn;
267
268         s = &dev->subdevices[2];
269         /* digital i/o subdevice */
270         subdev_8255_init(dev, s, NULL, iobase_8255);
271
272         dev_info(dev->class_dev, "%s attached\n", dev->board_name);
273
274         return 0;
275 }
276
277 static void cb_pcimdas_detach(struct comedi_device *dev)
278 {
279         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
280
281         if (dev->irq)
282                 free_irq(dev->irq, dev);
283         if (pcidev) {
284                 if (dev->iobase)
285                         comedi_pci_disable(pcidev);
286         }
287 }
288
289 static struct comedi_driver cb_pcimdas_driver = {
290         .driver_name    = "cb_pcimdas",
291         .module         = THIS_MODULE,
292         .auto_attach    = cb_pcimdas_auto_attach,
293         .detach         = cb_pcimdas_detach,
294 };
295
296 static int cb_pcimdas_pci_probe(struct pci_dev *dev,
297                                           const struct pci_device_id *ent)
298 {
299         return comedi_pci_auto_config(dev, &cb_pcimdas_driver);
300 }
301
302 static void cb_pcimdas_pci_remove(struct pci_dev *dev)
303 {
304         comedi_pci_auto_unconfig(dev);
305 }
306
307 static DEFINE_PCI_DEVICE_TABLE(cb_pcimdas_pci_table) = {
308         { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0056) },
309         { 0 }
310 };
311 MODULE_DEVICE_TABLE(pci, cb_pcimdas_pci_table);
312
313 static struct pci_driver cb_pcimdas_pci_driver = {
314         .name           = "cb_pcimdas",
315         .id_table       = cb_pcimdas_pci_table,
316         .probe          = cb_pcimdas_pci_probe,
317         .remove         = cb_pcimdas_pci_remove,
318 };
319 module_comedi_pci_driver(cb_pcimdas_driver, cb_pcimdas_pci_driver);
320
321 MODULE_AUTHOR("Comedi http://www.comedi.org");
322 MODULE_DESCRIPTION("Comedi low-level driver");
323 MODULE_LICENSE("GPL");