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