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