Mention branches and keyring.
[releases.git] / drivers / ni_6527.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * ni_6527.c
4  * Comedi driver for National Instruments PCI-6527
5  *
6  * COMEDI - Linux Control and Measurement Device Interface
7  * Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
8  */
9
10 /*
11  * Driver: ni_6527
12  * Description: National Instruments 6527
13  * Devices: [National Instruments] PCI-6527 (pci-6527), PXI-6527 (pxi-6527)
14  * Author: David A. Schleef <ds@schleef.org>
15  * Updated: Sat, 25 Jan 2003 13:24:40 -0800
16  * Status: works
17  *
18  * Configuration Options: not applicable, uses PCI auto config
19  */
20
21 #include <linux/module.h>
22 #include <linux/interrupt.h>
23 #include <linux/comedi/comedi_pci.h>
24
25 /*
26  * PCI BAR1 - Register memory map
27  *
28  * Manuals (available from ftp://ftp.natinst.com/support/manuals)
29  *      370106b.pdf     6527 Register Level Programmer Manual
30  */
31 #define NI6527_DI_REG(x)                (0x00 + (x))
32 #define NI6527_DO_REG(x)                (0x03 + (x))
33 #define NI6527_ID_REG                   0x06
34 #define NI6527_CLR_REG                  0x07
35 #define NI6527_CLR_EDGE                 BIT(3)
36 #define NI6527_CLR_OVERFLOW             BIT(2)
37 #define NI6527_CLR_FILT                 BIT(1)
38 #define NI6527_CLR_INTERVAL             BIT(0)
39 #define NI6527_CLR_IRQS                 (NI6527_CLR_EDGE | NI6527_CLR_OVERFLOW)
40 #define NI6527_CLR_RESET_FILT           (NI6527_CLR_FILT | NI6527_CLR_INTERVAL)
41 #define NI6527_FILT_INTERVAL_REG(x)     (0x08 + (x))
42 #define NI6527_FILT_ENA_REG(x)          (0x0c + (x))
43 #define NI6527_STATUS_REG               0x14
44 #define NI6527_STATUS_IRQ               BIT(2)
45 #define NI6527_STATUS_OVERFLOW          BIT(1)
46 #define NI6527_STATUS_EDGE              BIT(0)
47 #define NI6527_CTRL_REG                 0x15
48 #define NI6527_CTRL_FALLING             BIT(4)
49 #define NI6527_CTRL_RISING              BIT(3)
50 #define NI6527_CTRL_IRQ                 BIT(2)
51 #define NI6527_CTRL_OVERFLOW            BIT(1)
52 #define NI6527_CTRL_EDGE                BIT(0)
53 #define NI6527_CTRL_DISABLE_IRQS        0
54 #define NI6527_CTRL_ENABLE_IRQS         (NI6527_CTRL_FALLING | \
55                                          NI6527_CTRL_RISING | \
56                                          NI6527_CTRL_IRQ | NI6527_CTRL_EDGE)
57 #define NI6527_RISING_EDGE_REG(x)       (0x18 + (x))
58 #define NI6527_FALLING_EDGE_REG(x)      (0x20 + (x))
59
60 enum ni6527_boardid {
61         BOARD_PCI6527,
62         BOARD_PXI6527,
63 };
64
65 struct ni6527_board {
66         const char *name;
67 };
68
69 static const struct ni6527_board ni6527_boards[] = {
70         [BOARD_PCI6527] = {
71                 .name           = "pci-6527",
72         },
73         [BOARD_PXI6527] = {
74                 .name           = "pxi-6527",
75         },
76 };
77
78 struct ni6527_private {
79         unsigned int filter_interval;
80         unsigned int filter_enable;
81 };
82
83 static void ni6527_set_filter_interval(struct comedi_device *dev,
84                                        unsigned int val)
85 {
86         struct ni6527_private *devpriv = dev->private;
87
88         if (val != devpriv->filter_interval) {
89                 writeb(val & 0xff, dev->mmio + NI6527_FILT_INTERVAL_REG(0));
90                 writeb((val >> 8) & 0xff,
91                        dev->mmio + NI6527_FILT_INTERVAL_REG(1));
92                 writeb((val >> 16) & 0x0f,
93                        dev->mmio + NI6527_FILT_INTERVAL_REG(2));
94
95                 writeb(NI6527_CLR_INTERVAL, dev->mmio + NI6527_CLR_REG);
96
97                 devpriv->filter_interval = val;
98         }
99 }
100
101 static void ni6527_set_filter_enable(struct comedi_device *dev,
102                                      unsigned int val)
103 {
104         writeb(val & 0xff, dev->mmio + NI6527_FILT_ENA_REG(0));
105         writeb((val >> 8) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(1));
106         writeb((val >> 16) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(2));
107 }
108
109 static int ni6527_di_insn_config(struct comedi_device *dev,
110                                  struct comedi_subdevice *s,
111                                  struct comedi_insn *insn,
112                                  unsigned int *data)
113 {
114         struct ni6527_private *devpriv = dev->private;
115         unsigned int chan = CR_CHAN(insn->chanspec);
116         unsigned int interval;
117
118         switch (data[0]) {
119         case INSN_CONFIG_FILTER:
120                 /*
121                  * The deglitch filter interval is specified in nanoseconds.
122                  * The hardware supports intervals in 200ns increments. Round
123                  * the user values up and return the actual interval.
124                  */
125                 interval = (data[1] + 100) / 200;
126                 data[1] = interval * 200;
127
128                 if (interval) {
129                         ni6527_set_filter_interval(dev, interval);
130                         devpriv->filter_enable |= 1 << chan;
131                 } else {
132                         devpriv->filter_enable &= ~(1 << chan);
133                 }
134                 ni6527_set_filter_enable(dev, devpriv->filter_enable);
135                 break;
136         default:
137                 return -EINVAL;
138         }
139
140         return insn->n;
141 }
142
143 static int ni6527_di_insn_bits(struct comedi_device *dev,
144                                struct comedi_subdevice *s,
145                                struct comedi_insn *insn,
146                                unsigned int *data)
147 {
148         unsigned int val;
149
150         val = readb(dev->mmio + NI6527_DI_REG(0));
151         val |= (readb(dev->mmio + NI6527_DI_REG(1)) << 8);
152         val |= (readb(dev->mmio + NI6527_DI_REG(2)) << 16);
153
154         data[1] = val;
155
156         return insn->n;
157 }
158
159 static int ni6527_do_insn_bits(struct comedi_device *dev,
160                                struct comedi_subdevice *s,
161                                struct comedi_insn *insn,
162                                unsigned int *data)
163 {
164         unsigned int mask;
165
166         mask = comedi_dio_update_state(s, data);
167         if (mask) {
168                 /* Outputs are inverted */
169                 unsigned int val = s->state ^ 0xffffff;
170
171                 if (mask & 0x0000ff)
172                         writeb(val & 0xff, dev->mmio + NI6527_DO_REG(0));
173                 if (mask & 0x00ff00)
174                         writeb((val >> 8) & 0xff,
175                                dev->mmio + NI6527_DO_REG(1));
176                 if (mask & 0xff0000)
177                         writeb((val >> 16) & 0xff,
178                                dev->mmio + NI6527_DO_REG(2));
179         }
180
181         data[1] = s->state;
182
183         return insn->n;
184 }
185
186 static irqreturn_t ni6527_interrupt(int irq, void *d)
187 {
188         struct comedi_device *dev = d;
189         struct comedi_subdevice *s = dev->read_subdev;
190         unsigned int status;
191
192         status = readb(dev->mmio + NI6527_STATUS_REG);
193         if (!(status & NI6527_STATUS_IRQ))
194                 return IRQ_NONE;
195
196         if (status & NI6527_STATUS_EDGE) {
197                 unsigned short val = 0;
198
199                 comedi_buf_write_samples(s, &val, 1);
200                 comedi_handle_events(dev, s);
201         }
202
203         writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG);
204
205         return IRQ_HANDLED;
206 }
207
208 static int ni6527_intr_cmdtest(struct comedi_device *dev,
209                                struct comedi_subdevice *s,
210                                struct comedi_cmd *cmd)
211 {
212         int err = 0;
213
214         /* Step 1 : check if triggers are trivially valid */
215
216         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
217         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_OTHER);
218         err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
219         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
220         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
221
222         if (err)
223                 return 1;
224
225         /* Step 2a : make sure trigger sources are unique */
226         /* Step 2b : and mutually compatible */
227
228         /* Step 3: check if arguments are trivially valid */
229
230         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
231         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
232         err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
233         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
234                                            cmd->chanlist_len);
235         err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
236
237         if (err)
238                 return 3;
239
240         /* Step 4: fix up any arguments */
241
242         /* Step 5: check channel list if it exists */
243
244         return 0;
245 }
246
247 static int ni6527_intr_cmd(struct comedi_device *dev,
248                            struct comedi_subdevice *s)
249 {
250         writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG);
251         writeb(NI6527_CTRL_ENABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
252
253         return 0;
254 }
255
256 static int ni6527_intr_cancel(struct comedi_device *dev,
257                               struct comedi_subdevice *s)
258 {
259         writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
260
261         return 0;
262 }
263
264 static int ni6527_intr_insn_bits(struct comedi_device *dev,
265                                  struct comedi_subdevice *s,
266                                  struct comedi_insn *insn, unsigned int *data)
267 {
268         data[1] = 0;
269         return insn->n;
270 }
271
272 static void ni6527_set_edge_detection(struct comedi_device *dev,
273                                       unsigned int mask,
274                                       unsigned int rising,
275                                       unsigned int falling)
276 {
277         unsigned int i;
278
279         rising &= mask;
280         falling &= mask;
281         for (i = 0; i < 2; i++) {
282                 if (mask & 0xff) {
283                         if (~mask & 0xff) {
284                                 /* preserve rising-edge detection channels */
285                                 rising |= readb(dev->mmio +
286                                                 NI6527_RISING_EDGE_REG(i)) &
287                                           (~mask & 0xff);
288                                 /* preserve falling-edge detection channels */
289                                 falling |= readb(dev->mmio +
290                                                  NI6527_FALLING_EDGE_REG(i)) &
291                                            (~mask & 0xff);
292                         }
293                         /* update rising-edge detection channels */
294                         writeb(rising & 0xff,
295                                dev->mmio + NI6527_RISING_EDGE_REG(i));
296                         /* update falling-edge detection channels */
297                         writeb(falling & 0xff,
298                                dev->mmio + NI6527_FALLING_EDGE_REG(i));
299                 }
300                 rising >>= 8;
301                 falling >>= 8;
302                 mask >>= 8;
303         }
304 }
305
306 static int ni6527_intr_insn_config(struct comedi_device *dev,
307                                    struct comedi_subdevice *s,
308                                    struct comedi_insn *insn,
309                                    unsigned int *data)
310 {
311         unsigned int mask = 0xffffffff;
312         unsigned int rising, falling, shift;
313
314         switch (data[0]) {
315         case INSN_CONFIG_CHANGE_NOTIFY:
316                 /* check_insn_config_length() does not check this instruction */
317                 if (insn->n != 3)
318                         return -EINVAL;
319                 rising = data[1];
320                 falling = data[2];
321                 ni6527_set_edge_detection(dev, mask, rising, falling);
322                 break;
323         case INSN_CONFIG_DIGITAL_TRIG:
324                 /* check trigger number */
325                 if (data[1] != 0)
326                         return -EINVAL;
327                 /* check digital trigger operation */
328                 switch (data[2]) {
329                 case COMEDI_DIGITAL_TRIG_DISABLE:
330                         rising = 0;
331                         falling = 0;
332                         break;
333                 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
334                         /* check shift amount */
335                         shift = data[3];
336                         if (shift >= 32) {
337                                 mask = 0;
338                                 rising = 0;
339                                 falling = 0;
340                         } else {
341                                 mask <<= shift;
342                                 rising = data[4] << shift;
343                                 falling = data[5] << shift;
344                         }
345                         break;
346                 default:
347                         return -EINVAL;
348                 }
349                 ni6527_set_edge_detection(dev, mask, rising, falling);
350                 break;
351         default:
352                 return -EINVAL;
353         }
354
355         return insn->n;
356 }
357
358 static void ni6527_reset(struct comedi_device *dev)
359 {
360         /* disable deglitch filters on all channels */
361         ni6527_set_filter_enable(dev, 0);
362
363         /* disable edge detection */
364         ni6527_set_edge_detection(dev, 0xffffffff, 0, 0);
365
366         writeb(NI6527_CLR_IRQS | NI6527_CLR_RESET_FILT,
367                dev->mmio + NI6527_CLR_REG);
368         writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
369 }
370
371 static int ni6527_auto_attach(struct comedi_device *dev,
372                               unsigned long context)
373 {
374         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
375         const struct ni6527_board *board = NULL;
376         struct ni6527_private *devpriv;
377         struct comedi_subdevice *s;
378         int ret;
379
380         if (context < ARRAY_SIZE(ni6527_boards))
381                 board = &ni6527_boards[context];
382         if (!board)
383                 return -ENODEV;
384         dev->board_ptr = board;
385         dev->board_name = board->name;
386
387         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
388         if (!devpriv)
389                 return -ENOMEM;
390
391         ret = comedi_pci_enable(dev);
392         if (ret)
393                 return ret;
394
395         dev->mmio = pci_ioremap_bar(pcidev, 1);
396         if (!dev->mmio)
397                 return -ENOMEM;
398
399         /* make sure this is actually a 6527 device */
400         if (readb(dev->mmio + NI6527_ID_REG) != 0x27)
401                 return -ENODEV;
402
403         ni6527_reset(dev);
404
405         ret = request_irq(pcidev->irq, ni6527_interrupt, IRQF_SHARED,
406                           dev->board_name, dev);
407         if (ret == 0)
408                 dev->irq = pcidev->irq;
409
410         ret = comedi_alloc_subdevices(dev, 3);
411         if (ret)
412                 return ret;
413
414         /* Digital Input subdevice */
415         s = &dev->subdevices[0];
416         s->type         = COMEDI_SUBD_DI;
417         s->subdev_flags = SDF_READABLE;
418         s->n_chan       = 24;
419         s->maxdata      = 1;
420         s->range_table  = &range_digital;
421         s->insn_config  = ni6527_di_insn_config;
422         s->insn_bits    = ni6527_di_insn_bits;
423
424         /* Digital Output subdevice */
425         s = &dev->subdevices[1];
426         s->type         = COMEDI_SUBD_DO;
427         s->subdev_flags = SDF_WRITABLE;
428         s->n_chan       = 24;
429         s->maxdata      = 1;
430         s->range_table  = &range_digital;
431         s->insn_bits    = ni6527_do_insn_bits;
432
433         /* Edge detection interrupt subdevice */
434         s = &dev->subdevices[2];
435         if (dev->irq) {
436                 dev->read_subdev = s;
437                 s->type         = COMEDI_SUBD_DI;
438                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
439                 s->n_chan       = 1;
440                 s->maxdata      = 1;
441                 s->range_table  = &range_digital;
442                 s->insn_config  = ni6527_intr_insn_config;
443                 s->insn_bits    = ni6527_intr_insn_bits;
444                 s->len_chanlist = 1;
445                 s->do_cmdtest   = ni6527_intr_cmdtest;
446                 s->do_cmd       = ni6527_intr_cmd;
447                 s->cancel       = ni6527_intr_cancel;
448         } else {
449                 s->type = COMEDI_SUBD_UNUSED;
450         }
451
452         return 0;
453 }
454
455 static void ni6527_detach(struct comedi_device *dev)
456 {
457         if (dev->mmio)
458                 ni6527_reset(dev);
459         comedi_pci_detach(dev);
460 }
461
462 static struct comedi_driver ni6527_driver = {
463         .driver_name    = "ni_6527",
464         .module         = THIS_MODULE,
465         .auto_attach    = ni6527_auto_attach,
466         .detach         = ni6527_detach,
467 };
468
469 static int ni6527_pci_probe(struct pci_dev *dev,
470                             const struct pci_device_id *id)
471 {
472         return comedi_pci_auto_config(dev, &ni6527_driver, id->driver_data);
473 }
474
475 static const struct pci_device_id ni6527_pci_table[] = {
476         { PCI_VDEVICE(NI, 0x2b10), BOARD_PXI6527 },
477         { PCI_VDEVICE(NI, 0x2b20), BOARD_PCI6527 },
478         { 0 }
479 };
480 MODULE_DEVICE_TABLE(pci, ni6527_pci_table);
481
482 static struct pci_driver ni6527_pci_driver = {
483         .name           = "ni_6527",
484         .id_table       = ni6527_pci_table,
485         .probe          = ni6527_pci_probe,
486         .remove         = comedi_pci_auto_unconfig,
487 };
488 module_comedi_pci_driver(ni6527_driver, ni6527_pci_driver);
489
490 MODULE_AUTHOR("Comedi https://www.comedi.org");
491 MODULE_DESCRIPTION("Comedi driver for National Instruments PCI-6527");
492 MODULE_LICENSE("GPL");