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