]> Pileus Git - ~andy/linux/blob - drivers/staging/comedi/drivers/ii_pci20kc.c
Merge branch 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[~andy/linux] / drivers / staging / comedi / drivers / ii_pci20kc.c
1 /*
2  * ii_pci20kc.c
3  * Driver for Intelligent Instruments PCI-20001C carrier board and modules.
4  *
5  * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de>
6  * with suggestions from David Schleef          16.06.2000
7  */
8
9 /*
10  * Driver: ii_pci20kc
11  * Description: Intelligent Instruments PCI-20001C carrier board
12  * Devices: (Intelligent Instrumentation) PCI-20001C [ii_pci20kc]
13  * Author: Markus Kempf <kempf@matsci.uni-sb.de>
14  * Status: works
15  *
16  * Supports the PCI-20001C-1a and PCI-20001C-2a carrier boards. The
17  * -2a version has 32 on-board DIO channels. Three add-on modules
18  * can be added to the carrier board for additional functionality.
19  *
20  * Supported add-on modules:
21  *      PCI-20006M-1   1 channel, 16-bit analog output module
22  *      PCI-20006M-2   2 channel, 16-bit analog output module
23  *      PCI-20341M-1A  4 channel, 16-bit analog input module
24  *
25  * Options:
26  *   0   Board base address
27  *   1   IRQ (not-used)
28  */
29
30 #include <linux/module.h>
31 #include "../comedidev.h"
32
33 /*
34  * Register I/O map
35  */
36 #define II20K_MOD_OFFSET                0x100
37 #define II20K_ID_REG                    0x00
38 #define II20K_ID_MOD1_EMPTY             (1 << 7)
39 #define II20K_ID_MOD2_EMPTY             (1 << 6)
40 #define II20K_ID_MOD3_EMPTY             (1 << 5)
41 #define II20K_ID_MASK                   0x1f
42 #define II20K_ID_PCI20001C_1A           0x1b    /* no on-board DIO */
43 #define II20K_ID_PCI20001C_2A           0x1d    /* on-board DIO */
44 #define II20K_MOD_STATUS_REG            0x40
45 #define II20K_MOD_STATUS_IRQ_MOD1       (1 << 7)
46 #define II20K_MOD_STATUS_IRQ_MOD2       (1 << 6)
47 #define II20K_MOD_STATUS_IRQ_MOD3       (1 << 5)
48 #define II20K_DIO0_REG                  0x80
49 #define II20K_DIO1_REG                  0x81
50 #define II20K_DIR_ENA_REG               0x82
51 #define II20K_DIR_DIO3_OUT              (1 << 7)
52 #define II20K_DIR_DIO2_OUT              (1 << 6)
53 #define II20K_BUF_DISAB_DIO3            (1 << 5)
54 #define II20K_BUF_DISAB_DIO2            (1 << 4)
55 #define II20K_DIR_DIO1_OUT              (1 << 3)
56 #define II20K_DIR_DIO0_OUT              (1 << 2)
57 #define II20K_BUF_DISAB_DIO1            (1 << 1)
58 #define II20K_BUF_DISAB_DIO0            (1 << 0)
59 #define II20K_CTRL01_REG                0x83
60 #define II20K_CTRL01_SET                (1 << 7)
61 #define II20K_CTRL01_DIO0_IN            (1 << 4)
62 #define II20K_CTRL01_DIO1_IN            (1 << 1)
63 #define II20K_DIO2_REG                  0xc0
64 #define II20K_DIO3_REG                  0xc1
65 #define II20K_CTRL23_REG                0xc3
66 #define II20K_CTRL23_SET                (1 << 7)
67 #define II20K_CTRL23_DIO2_IN            (1 << 4)
68 #define II20K_CTRL23_DIO3_IN            (1 << 1)
69
70 #define II20K_ID_PCI20006M_1            0xe2    /* 1 AO channels */
71 #define II20K_ID_PCI20006M_2            0xe3    /* 2 AO channels */
72 #define II20K_AO_STRB_REG(x)            (0x0b + ((x) * 0x08))
73 #define II20K_AO_LSB_REG(x)             (0x0d + ((x) * 0x08))
74 #define II20K_AO_MSB_REG(x)             (0x0e + ((x) * 0x08))
75 #define II20K_AO_STRB_BOTH_REG          0x1b
76
77 #define II20K_ID_PCI20341M_1            0x77    /* 4 AI channels */
78 #define II20K_AI_STATUS_CMD_REG         0x01
79 #define II20K_AI_STATUS_CMD_BUSY        (1 << 7)
80 #define II20K_AI_STATUS_CMD_HW_ENA      (1 << 1)
81 #define II20K_AI_STATUS_CMD_EXT_START   (1 << 0)
82 #define II20K_AI_LSB_REG                0x02
83 #define II20K_AI_MSB_REG                0x03
84 #define II20K_AI_PACER_RESET_REG        0x04
85 #define II20K_AI_16BIT_DATA_REG         0x06
86 #define II20K_AI_CONF_REG               0x10
87 #define II20K_AI_CONF_ENA               (1 << 2)
88 #define II20K_AI_OPT_REG                0x11
89 #define II20K_AI_OPT_TRIG_ENA           (1 << 5)
90 #define II20K_AI_OPT_TRIG_INV           (1 << 4)
91 #define II20K_AI_OPT_TIMEBASE(x)        (((x) & 0x3) << 1)
92 #define II20K_AI_OPT_BURST_MODE         (1 << 0)
93 #define II20K_AI_STATUS_REG             0x12
94 #define II20K_AI_STATUS_INT             (1 << 7)
95 #define II20K_AI_STATUS_TRIG            (1 << 6)
96 #define II20K_AI_STATUS_TRIG_ENA        (1 << 5)
97 #define II20K_AI_STATUS_PACER_ERR       (1 << 2)
98 #define II20K_AI_STATUS_DATA_ERR        (1 << 1)
99 #define II20K_AI_STATUS_SET_TIME_ERR    (1 << 0)
100 #define II20K_AI_LAST_CHAN_ADDR_REG     0x13
101 #define II20K_AI_CUR_ADDR_REG           0x14
102 #define II20K_AI_SET_TIME_REG           0x15
103 #define II20K_AI_DELAY_LSB_REG          0x16
104 #define II20K_AI_DELAY_MSB_REG          0x17
105 #define II20K_AI_CHAN_ADV_REG           0x18
106 #define II20K_AI_CHAN_RESET_REG         0x19
107 #define II20K_AI_START_TRIG_REG         0x1a
108 #define II20K_AI_COUNT_RESET_REG        0x1b
109 #define II20K_AI_CHANLIST_REG           0x80
110 #define II20K_AI_CHANLIST_ONBOARD_ONLY  (1 << 5)
111 #define II20K_AI_CHANLIST_GAIN(x)       (((x) & 0x3) << 3)
112 #define II20K_AI_CHANLIST_MUX_ENA       (1 << 2)
113 #define II20K_AI_CHANLIST_CHAN(x)       (((x) & 0x3) << 0)
114 #define II20K_AI_CHANLIST_LEN           0x80
115
116 /* the AO range is set by jumpers on the 20006M module */
117 static const struct comedi_lrange ii20k_ao_ranges = {
118         3, {
119                 BIP_RANGE(5),   /* Chan 0 - W1/W3 in   Chan 1 - W2/W4 in  */
120                 UNI_RANGE(10),  /* Chan 0 - W1/W3 out  Chan 1 - W2/W4 in  */
121                 BIP_RANGE(10)   /* Chan 0 - W1/W3 in   Chan 1 - W2/W4 out */
122         }
123 };
124
125 static const struct comedi_lrange ii20k_ai_ranges = {
126         4, {
127                 BIP_RANGE(5),           /* gain 1 */
128                 BIP_RANGE(0.5),         /* gain 10 */
129                 BIP_RANGE(0.05),        /* gain 100 */
130                 BIP_RANGE(0.025)        /* gain 200 */
131         },
132 };
133
134 struct ii20k_ao_private {
135         unsigned int last_data[2];
136 };
137
138 struct ii20k_private {
139         void __iomem *ioaddr;
140 };
141
142 static void __iomem *ii20k_module_iobase(struct comedi_device *dev,
143                                          struct comedi_subdevice *s)
144 {
145         struct ii20k_private *devpriv = dev->private;
146
147         return devpriv->ioaddr + (s->index + 1) * II20K_MOD_OFFSET;
148 }
149
150 static int ii20k_ao_insn_read(struct comedi_device *dev,
151                               struct comedi_subdevice *s,
152                               struct comedi_insn *insn,
153                               unsigned int *data)
154 {
155         struct ii20k_ao_private *ao_spriv = s->private;
156         unsigned int chan = CR_CHAN(insn->chanspec);
157         int i;
158
159         for (i = 0; i < insn->n; i++)
160                 data[i] = ao_spriv->last_data[chan];
161
162         return insn->n;
163 }
164
165 static int ii20k_ao_insn_write(struct comedi_device *dev,
166                                struct comedi_subdevice *s,
167                                struct comedi_insn *insn,
168                                unsigned int *data)
169 {
170         struct ii20k_ao_private *ao_spriv = s->private;
171         void __iomem *iobase = ii20k_module_iobase(dev, s);
172         unsigned int chan = CR_CHAN(insn->chanspec);
173         unsigned int val = ao_spriv->last_data[chan];
174         int i;
175
176         for (i = 0; i < insn->n; i++) {
177                 val = data[i];
178
179                 /* munge data */
180                 val += ((s->maxdata + 1) >> 1);
181                 val &= s->maxdata;
182
183                 writeb(val & 0xff, iobase + II20K_AO_LSB_REG(chan));
184                 writeb((val >> 8) & 0xff, iobase + II20K_AO_MSB_REG(chan));
185                 writeb(0x00, iobase + II20K_AO_STRB_REG(chan));
186         }
187
188         ao_spriv->last_data[chan] = val;
189
190         return insn->n;
191 }
192
193 static int ii20k_ai_wait_eoc(struct comedi_device *dev,
194                              struct comedi_subdevice *s,
195                              int timeout)
196 {
197         void __iomem *iobase = ii20k_module_iobase(dev, s);
198         unsigned char status;
199
200         do {
201                 status = readb(iobase + II20K_AI_STATUS_REG);
202                 if ((status & II20K_AI_STATUS_INT) == 0)
203                         return 0;
204         } while (timeout--);
205
206         return -ETIME;
207 }
208
209 static void ii20k_ai_setup(struct comedi_device *dev,
210                            struct comedi_subdevice *s,
211                            unsigned int chanspec)
212 {
213         void __iomem *iobase = ii20k_module_iobase(dev, s);
214         unsigned int chan = CR_CHAN(chanspec);
215         unsigned int range = CR_RANGE(chanspec);
216         unsigned char val;
217
218         /* initialize module */
219         writeb(II20K_AI_CONF_ENA, iobase + II20K_AI_CONF_REG);
220
221         /* software conversion */
222         writeb(0, iobase + II20K_AI_STATUS_CMD_REG);
223
224         /* set the time base for the settling time counter based on the gain */
225         val = (range < 3) ? II20K_AI_OPT_TIMEBASE(0) : II20K_AI_OPT_TIMEBASE(2);
226         writeb(val, iobase + II20K_AI_OPT_REG);
227
228         /* set the settling time counter based on the gain */
229         val = (range < 2) ? 0x58 : (range < 3) ? 0x93 : 0x99;
230         writeb(val, iobase + II20K_AI_SET_TIME_REG);
231
232         /* set number of input channels */
233         writeb(1, iobase + II20K_AI_LAST_CHAN_ADDR_REG);
234
235         /* set the channel list byte */
236         val = II20K_AI_CHANLIST_ONBOARD_ONLY |
237               II20K_AI_CHANLIST_MUX_ENA |
238               II20K_AI_CHANLIST_GAIN(range) |
239               II20K_AI_CHANLIST_CHAN(chan);
240         writeb(val, iobase + II20K_AI_CHANLIST_REG);
241
242         /* reset settling time counter and trigger delay counter */
243         writeb(0, iobase + II20K_AI_COUNT_RESET_REG);
244
245         /* reset channel scanner */
246         writeb(0, iobase + II20K_AI_CHAN_RESET_REG);
247 }
248
249 static int ii20k_ai_insn_read(struct comedi_device *dev,
250                               struct comedi_subdevice *s,
251                               struct comedi_insn *insn,
252                               unsigned int *data)
253 {
254         void __iomem *iobase = ii20k_module_iobase(dev, s);
255         int ret;
256         int i;
257
258         ii20k_ai_setup(dev, s, insn->chanspec);
259
260         for (i = 0; i < insn->n; i++) {
261                 unsigned int val;
262
263                 /* generate a software start convert signal */
264                 readb(iobase + II20K_AI_PACER_RESET_REG);
265
266                 ret = ii20k_ai_wait_eoc(dev, s, 100);
267                 if (ret)
268                         return ret;
269
270                 val = readb(iobase + II20K_AI_LSB_REG);
271                 val |= (readb(iobase + II20K_AI_MSB_REG) << 8);
272
273                 /* munge two's complement data */
274                 val += ((s->maxdata + 1) >> 1);
275                 val &= s->maxdata;
276
277                 data[i] = val;
278         }
279
280         return insn->n;
281 }
282
283 static void ii20k_dio_config(struct comedi_device *dev,
284                              struct comedi_subdevice *s)
285 {
286         struct ii20k_private *devpriv = dev->private;
287         unsigned char ctrl01 = 0;
288         unsigned char ctrl23 = 0;
289         unsigned char dir_ena = 0;
290
291         /* port 0 - channels 0-7 */
292         if (s->io_bits & 0x000000ff) {
293                 /* output port */
294                 ctrl01 &= ~II20K_CTRL01_DIO0_IN;
295                 dir_ena &= ~II20K_BUF_DISAB_DIO0;
296                 dir_ena |= II20K_DIR_DIO0_OUT;
297         } else {
298                 /* input port */
299                 ctrl01 |= II20K_CTRL01_DIO0_IN;
300                 dir_ena &= ~II20K_DIR_DIO0_OUT;
301         }
302
303         /* port 1 - channels 8-15 */
304         if (s->io_bits & 0x0000ff00) {
305                 /* output port */
306                 ctrl01 &= ~II20K_CTRL01_DIO1_IN;
307                 dir_ena &= ~II20K_BUF_DISAB_DIO1;
308                 dir_ena |= II20K_DIR_DIO1_OUT;
309         } else {
310                 /* input port */
311                 ctrl01 |= II20K_CTRL01_DIO1_IN;
312                 dir_ena &= ~II20K_DIR_DIO1_OUT;
313         }
314
315         /* port 2 - channels 16-23 */
316         if (s->io_bits & 0x00ff0000) {
317                 /* output port */
318                 ctrl23 &= ~II20K_CTRL23_DIO2_IN;
319                 dir_ena &= ~II20K_BUF_DISAB_DIO2;
320                 dir_ena |= II20K_DIR_DIO2_OUT;
321         } else {
322                 /* input port */
323                 ctrl23 |= II20K_CTRL23_DIO2_IN;
324                 dir_ena &= ~II20K_DIR_DIO2_OUT;
325         }
326
327         /* port 3 - channels 24-31 */
328         if (s->io_bits & 0xff000000) {
329                 /* output port */
330                 ctrl23 &= ~II20K_CTRL23_DIO3_IN;
331                 dir_ena &= ~II20K_BUF_DISAB_DIO3;
332                 dir_ena |= II20K_DIR_DIO3_OUT;
333         } else {
334                 /* input port */
335                 ctrl23 |= II20K_CTRL23_DIO3_IN;
336                 dir_ena &= ~II20K_DIR_DIO3_OUT;
337         }
338
339         ctrl23 |= II20K_CTRL01_SET;
340         ctrl23 |= II20K_CTRL23_SET;
341
342         /* order is important */
343         writeb(ctrl01, devpriv->ioaddr + II20K_CTRL01_REG);
344         writeb(ctrl23, devpriv->ioaddr + II20K_CTRL23_REG);
345         writeb(dir_ena, devpriv->ioaddr + II20K_DIR_ENA_REG);
346 }
347
348 static int ii20k_dio_insn_config(struct comedi_device *dev,
349                                  struct comedi_subdevice *s,
350                                  struct comedi_insn *insn,
351                                  unsigned int *data)
352 {
353         unsigned int chan = CR_CHAN(insn->chanspec);
354         unsigned int mask;
355         int ret;
356
357         if (chan < 8)
358                 mask = 0x000000ff;
359         else if (chan < 16)
360                 mask = 0x0000ff00;
361         else if (chan < 24)
362                 mask = 0x00ff0000;
363         else
364                 mask = 0xff000000;
365
366         ret = comedi_dio_insn_config(dev, s, insn, data, mask);
367         if (ret)
368                 return ret;
369
370         ii20k_dio_config(dev, s);
371
372         return insn->n;
373 }
374
375 static int ii20k_dio_insn_bits(struct comedi_device *dev,
376                                struct comedi_subdevice *s,
377                                struct comedi_insn *insn,
378                                unsigned int *data)
379 {
380         struct ii20k_private *devpriv = dev->private;
381         unsigned int mask = data[0] & s->io_bits;       /* outputs only */
382         unsigned int bits = data[1];
383
384         if (mask) {
385                 s->state &= ~mask;
386                 s->state |= (bits & mask);
387
388                 if (mask & 0x000000ff)
389                         writeb((s->state >> 0) & 0xff,
390                                devpriv->ioaddr + II20K_DIO0_REG);
391                 if (mask & 0x0000ff00)
392                         writeb((s->state >> 8) & 0xff,
393                                devpriv->ioaddr + II20K_DIO1_REG);
394                 if (mask & 0x00ff0000)
395                         writeb((s->state >> 16) & 0xff,
396                                devpriv->ioaddr + II20K_DIO2_REG);
397                 if (mask & 0xff000000)
398                         writeb((s->state >> 24) & 0xff,
399                                devpriv->ioaddr + II20K_DIO3_REG);
400         }
401
402         data[1] = readb(devpriv->ioaddr + II20K_DIO0_REG);
403         data[1] |= readb(devpriv->ioaddr + II20K_DIO1_REG) << 8;
404         data[1] |= readb(devpriv->ioaddr + II20K_DIO2_REG) << 16;
405         data[1] |= readb(devpriv->ioaddr + II20K_DIO3_REG) << 24;
406
407         return insn->n;
408 }
409
410 static int ii20k_init_module(struct comedi_device *dev,
411                              struct comedi_subdevice *s)
412 {
413         struct ii20k_ao_private *ao_spriv;
414         void __iomem *iobase = ii20k_module_iobase(dev, s);
415         unsigned char id;
416
417         id = readb(iobase + II20K_ID_REG);
418         switch (id) {
419         case II20K_ID_PCI20006M_1:
420         case II20K_ID_PCI20006M_2:
421                 ao_spriv = comedi_alloc_spriv(s, sizeof(*ao_spriv));
422                 if (!ao_spriv)
423                         return -ENOMEM;
424
425                 /* Analog Output subdevice */
426                 s->type         = COMEDI_SUBD_AO;
427                 s->subdev_flags = SDF_WRITABLE;
428                 s->n_chan       = (id == II20K_ID_PCI20006M_2) ? 2 : 1;
429                 s->maxdata      = 0xffff;
430                 s->range_table  = &ii20k_ao_ranges;
431                 s->insn_read    = ii20k_ao_insn_read;
432                 s->insn_write   = ii20k_ao_insn_write;
433                 break;
434         case II20K_ID_PCI20341M_1:
435                 /* Analog Input subdevice */
436                 s->type         = COMEDI_SUBD_AI;
437                 s->subdev_flags = SDF_READABLE | SDF_DIFF;
438                 s->n_chan       = 4;
439                 s->maxdata      = 0xffff;
440                 s->range_table  = &ii20k_ai_ranges;
441                 s->insn_read    = ii20k_ai_insn_read;
442                 break;
443         default:
444                 s->type = COMEDI_SUBD_UNUSED;
445                 break;
446         }
447
448         return 0;
449 }
450
451 static int ii20k_attach(struct comedi_device *dev,
452                         struct comedi_devconfig *it)
453 {
454         struct ii20k_private *devpriv;
455         struct comedi_subdevice *s;
456         unsigned char id;
457         bool has_dio;
458         int ret;
459
460         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
461         if (!devpriv)
462                 return -ENOMEM;
463
464         devpriv->ioaddr = (void __iomem *)(unsigned long)it->options[0];
465
466         id = readb(devpriv->ioaddr + II20K_ID_REG);
467         switch (id & II20K_ID_MASK) {
468         case II20K_ID_PCI20001C_1A:
469                 break;
470         case II20K_ID_PCI20001C_2A:
471                 has_dio = true;
472                 break;
473         default:
474                 return -ENODEV;
475         }
476
477         ret = comedi_alloc_subdevices(dev, 4);
478         if (ret)
479                 return ret;
480
481         s = &dev->subdevices[0];
482         if (id & II20K_ID_MOD1_EMPTY) {
483                 s->type = COMEDI_SUBD_UNUSED;
484         } else {
485                 ret = ii20k_init_module(dev, s);
486                 if (ret)
487                         return ret;
488         }
489
490         s = &dev->subdevices[1];
491         if (id & II20K_ID_MOD2_EMPTY) {
492                 s->type = COMEDI_SUBD_UNUSED;
493         } else {
494                 ret = ii20k_init_module(dev, s);
495                 if (ret)
496                         return ret;
497         }
498
499         s = &dev->subdevices[2];
500         if (id & II20K_ID_MOD3_EMPTY) {
501                 s->type = COMEDI_SUBD_UNUSED;
502         } else {
503                 ret = ii20k_init_module(dev, s);
504                 if (ret)
505                         return ret;
506         }
507
508         /* Digital I/O subdevice */
509         s = &dev->subdevices[3];
510         if (has_dio) {
511                 s->type         = COMEDI_SUBD_DIO;
512                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
513                 s->n_chan       = 32;
514                 s->maxdata      = 1;
515                 s->range_table  = &range_digital;
516                 s->insn_bits    = ii20k_dio_insn_bits;
517                 s->insn_config  = ii20k_dio_insn_config;
518
519                 /* default all channels to input */
520                 ii20k_dio_config(dev, s);
521         } else {
522                 s->type = COMEDI_SUBD_UNUSED;
523         }
524
525         return 0;
526 }
527
528 static struct comedi_driver ii20k_driver = {
529         .driver_name    = "ii_pci20kc",
530         .module         = THIS_MODULE,
531         .attach         = ii20k_attach,
532         .detach         = comedi_legacy_detach,
533 };
534 module_comedi_driver(ii20k_driver);
535
536 MODULE_AUTHOR("Comedi http://www.comedi.org");
537 MODULE_DESCRIPTION("Comedi low-level driver");
538 MODULE_LICENSE("GPL");