70304bdb536aba1b00c8f7ab95c20449683ed626
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / ni_6527.c
1 /*
2     comedi/drivers/ni_6527.c
3     driver for National Instruments PCI-6527
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
7
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 */
18 /*
19 Driver: ni_6527
20 Description: National Instruments 6527
21 Author: ds
22 Status: works
23 Devices: [National Instruments] PCI-6527 (ni6527), PXI-6527
24 Updated: Sat, 25 Jan 2003 13:24:40 -0800
25
26
27 */
28
29 /*
30    Manuals (available from ftp://ftp.natinst.com/support/manuals)
31
32         370106b.pdf     6527 Register Level Programmer Manual
33
34  */
35
36 #include <linux/module.h>
37 #include <linux/pci.h>
38 #include <linux/interrupt.h>
39
40 #include "../comedidev.h"
41
42 #include "comedi_fc.h"
43 #include "mite.h"
44
45 #define Port_Register(x)                        (0x00+(x))
46 #define NI6527_ID_REG                   0x06
47
48 #define Clear_Register                          0x07
49 #define ClrEdge                         0x08
50 #define ClrOverflow                     0x04
51 #define ClrFilter                       0x02
52 #define ClrInterval                     0x01
53
54 #define NI6527_FILT_INTERVAL_REG(x)     (0x08 + (x))
55 #define Filter_Enable(x)                        (0x0c+(x))
56
57 #define Change_Status                           0x14
58 #define MasterInterruptStatus           0x04
59 #define Overflow                        0x02
60 #define EdgeStatus                      0x01
61
62 #define Master_Interrupt_Control                0x15
63 #define FallingEdgeIntEnable            0x10
64 #define RisingEdgeIntEnable             0x08
65 #define MasterInterruptEnable           0x04
66 #define OverflowIntEnable               0x02
67 #define EdgeIntEnable                   0x01
68
69 #define Rising_Edge_Detection_Enable(x)         (0x018+(x))
70 #define Falling_Edge_Detection_Enable(x)        (0x020+(x))
71
72 enum ni6527_boardid {
73         BOARD_PCI6527,
74         BOARD_PXI6527,
75 };
76
77 struct ni6527_board {
78         const char *name;
79 };
80
81 static const struct ni6527_board ni6527_boards[] = {
82         [BOARD_PCI6527] = {
83                 .name           = "pci-6527",
84         },
85         [BOARD_PXI6527] = {
86                 .name           = "pxi-6527",
87         },
88 };
89
90 struct ni6527_private {
91         struct mite_struct *mite;
92         unsigned int filter_interval;
93         unsigned int filter_enable;
94 };
95
96 static void ni6527_set_filter_interval(struct comedi_device *dev,
97                                        unsigned int val)
98 {
99         struct ni6527_private *devpriv = dev->private;
100         void __iomem *mmio = devpriv->mite->daq_io_addr;
101
102         if (val != devpriv->filter_interval) {
103                 writeb(val & 0xff, mmio + NI6527_FILT_INTERVAL_REG(0));
104                 writeb((val >> 8) & 0xff, mmio + NI6527_FILT_INTERVAL_REG(1));
105                 writeb((val >> 16) & 0x0f, mmio + NI6527_FILT_INTERVAL_REG(2));
106
107                 writeb(ClrInterval, mmio + Clear_Register);
108
109                 devpriv->filter_interval = val;
110         }
111 }
112
113 static int ni6527_di_insn_config(struct comedi_device *dev,
114                                  struct comedi_subdevice *s,
115                                  struct comedi_insn *insn, unsigned int *data)
116 {
117         struct ni6527_private *devpriv = dev->private;
118         int chan = CR_CHAN(insn->chanspec);
119         unsigned int interval;
120
121         if (insn->n != 2)
122                 return -EINVAL;
123
124         if (data[0] != INSN_CONFIG_FILTER)
125                 return -EINVAL;
126
127         if (data[1]) {
128                 interval = (data[1] + 100) / 200;
129                 data[1] = interval * 200;
130
131                 ni6527_set_filter_interval(dev, interval);
132
133                 devpriv->filter_enable |= 1 << chan;
134         } else {
135                 devpriv->filter_enable &= ~(1 << chan);
136         }
137
138         writeb(devpriv->filter_enable,
139                devpriv->mite->daq_io_addr + Filter_Enable(0));
140         writeb(devpriv->filter_enable >> 8,
141                devpriv->mite->daq_io_addr + Filter_Enable(1));
142         writeb(devpriv->filter_enable >> 16,
143                devpriv->mite->daq_io_addr + Filter_Enable(2));
144
145         return 2;
146 }
147
148 static int ni6527_di_insn_bits(struct comedi_device *dev,
149                                struct comedi_subdevice *s,
150                                struct comedi_insn *insn, unsigned int *data)
151 {
152         struct ni6527_private *devpriv = dev->private;
153
154         data[1] = readb(devpriv->mite->daq_io_addr + Port_Register(0));
155         data[1] |= readb(devpriv->mite->daq_io_addr + Port_Register(1)) << 8;
156         data[1] |= readb(devpriv->mite->daq_io_addr + Port_Register(2)) << 16;
157
158         return insn->n;
159 }
160
161 static int ni6527_do_insn_bits(struct comedi_device *dev,
162                                struct comedi_subdevice *s,
163                                struct comedi_insn *insn,
164                                unsigned int *data)
165 {
166         struct ni6527_private *devpriv = dev->private;
167         unsigned int mask;
168
169         mask = comedi_dio_update_state(s, data);
170         if (mask) {
171                 /* Outputs are inverted */
172                 if (mask & 0x0000ff) {
173                         writeb(s->state ^ 0xff,
174                                devpriv->mite->daq_io_addr + Port_Register(3));
175                 }
176                 if (mask & 0x00ff00) {
177                         writeb((s->state >> 8) ^ 0xff,
178                                devpriv->mite->daq_io_addr + Port_Register(4));
179                 }
180                 if (mask & 0xff0000) {
181                         writeb((s->state >> 16) ^ 0xff,
182                                devpriv->mite->daq_io_addr + Port_Register(5));
183                 }
184         }
185
186         data[1] = s->state;
187
188         return insn->n;
189 }
190
191 static irqreturn_t ni6527_interrupt(int irq, void *d)
192 {
193         struct comedi_device *dev = d;
194         struct ni6527_private *devpriv = dev->private;
195         struct comedi_subdevice *s = &dev->subdevices[2];
196         unsigned int status;
197
198         status = readb(devpriv->mite->daq_io_addr + Change_Status);
199         if ((status & MasterInterruptStatus) == 0)
200                 return IRQ_NONE;
201         if ((status & EdgeStatus) == 0)
202                 return IRQ_NONE;
203
204         writeb(ClrEdge | ClrOverflow,
205                devpriv->mite->daq_io_addr + Clear_Register);
206
207         comedi_buf_put(s->async, 0);
208         s->async->events |= COMEDI_CB_EOS;
209         comedi_event(dev, s);
210         return IRQ_HANDLED;
211 }
212
213 static int ni6527_intr_cmdtest(struct comedi_device *dev,
214                                struct comedi_subdevice *s,
215                                struct comedi_cmd *cmd)
216 {
217         int err = 0;
218
219         /* Step 1 : check if triggers are trivially valid */
220
221         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
222         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_OTHER);
223         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
224         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
225         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
226
227         if (err)
228                 return 1;
229
230         /* Step 2a : make sure trigger sources are unique */
231         /* Step 2b : and mutually compatible */
232
233         if (err)
234                 return 2;
235
236         /* Step 3: check if arguments are trivially valid */
237
238         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
239         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
240         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
241         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, 1);
242         err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
243
244         if (err)
245                 return 3;
246
247         /* step 4: fix up any arguments */
248
249         if (err)
250                 return 4;
251
252         return 0;
253 }
254
255 static int ni6527_intr_cmd(struct comedi_device *dev,
256                            struct comedi_subdevice *s)
257 {
258         struct ni6527_private *devpriv = dev->private;
259         /* struct comedi_cmd *cmd = &s->async->cmd; */
260
261         writeb(ClrEdge | ClrOverflow,
262                devpriv->mite->daq_io_addr + Clear_Register);
263         writeb(FallingEdgeIntEnable | RisingEdgeIntEnable |
264                MasterInterruptEnable | EdgeIntEnable,
265                devpriv->mite->daq_io_addr + Master_Interrupt_Control);
266
267         return 0;
268 }
269
270 static int ni6527_intr_cancel(struct comedi_device *dev,
271                               struct comedi_subdevice *s)
272 {
273         struct ni6527_private *devpriv = dev->private;
274
275         writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control);
276
277         return 0;
278 }
279
280 static int ni6527_intr_insn_bits(struct comedi_device *dev,
281                                  struct comedi_subdevice *s,
282                                  struct comedi_insn *insn, unsigned int *data)
283 {
284         data[1] = 0;
285         return insn->n;
286 }
287
288 static int ni6527_intr_insn_config(struct comedi_device *dev,
289                                    struct comedi_subdevice *s,
290                                    struct comedi_insn *insn, unsigned int *data)
291 {
292         struct ni6527_private *devpriv = dev->private;
293
294         if (insn->n < 1)
295                 return -EINVAL;
296         if (data[0] != INSN_CONFIG_CHANGE_NOTIFY)
297                 return -EINVAL;
298
299         writeb(data[1],
300                devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(0));
301         writeb(data[1] >> 8,
302                devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(1));
303         writeb(data[1] >> 16,
304                devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(2));
305
306         writeb(data[2],
307                devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(0));
308         writeb(data[2] >> 8,
309                devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(1));
310         writeb(data[2] >> 16,
311                devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(2));
312
313         return 2;
314 }
315
316 static int ni6527_auto_attach(struct comedi_device *dev,
317                               unsigned long context)
318 {
319         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
320         const struct ni6527_board *board = NULL;
321         struct ni6527_private *devpriv;
322         struct comedi_subdevice *s;
323         int ret;
324
325         if (context < ARRAY_SIZE(ni6527_boards))
326                 board = &ni6527_boards[context];
327         if (!board)
328                 return -ENODEV;
329         dev->board_ptr = board;
330         dev->board_name = board->name;
331
332         ret = comedi_pci_enable(dev);
333         if (ret)
334                 return ret;
335
336         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
337         if (!devpriv)
338                 return -ENOMEM;
339
340         devpriv->mite = mite_alloc(pcidev);
341         if (!devpriv->mite)
342                 return -ENOMEM;
343
344         ret = mite_setup(devpriv->mite);
345         if (ret < 0) {
346                 dev_err(dev->class_dev, "error setting up mite\n");
347                 return ret;
348         }
349
350         /* make sure this is actually a 6527 device */
351         if (readb(devpriv->mite->daq_io_addr + NI6527_ID_REG) != 0x27)
352                 return -ENODEV;
353
354         ret = comedi_alloc_subdevices(dev, 3);
355         if (ret)
356                 return ret;
357
358         s = &dev->subdevices[0];
359         s->type = COMEDI_SUBD_DI;
360         s->subdev_flags = SDF_READABLE;
361         s->n_chan = 24;
362         s->range_table = &range_digital;
363         s->maxdata = 1;
364         s->insn_config = ni6527_di_insn_config;
365         s->insn_bits = ni6527_di_insn_bits;
366
367         s = &dev->subdevices[1];
368         s->type = COMEDI_SUBD_DO;
369         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
370         s->n_chan = 24;
371         s->range_table = &range_unknown;  /* FIXME: actually conductance */
372         s->maxdata = 1;
373         s->insn_bits = ni6527_do_insn_bits;
374
375         s = &dev->subdevices[2];
376         dev->read_subdev = s;
377         s->type = COMEDI_SUBD_DI;
378         s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
379         s->n_chan = 1;
380         s->range_table = &range_unknown;
381         s->maxdata = 1;
382         s->do_cmdtest = ni6527_intr_cmdtest;
383         s->do_cmd = ni6527_intr_cmd;
384         s->cancel = ni6527_intr_cancel;
385         s->insn_bits = ni6527_intr_insn_bits;
386         s->insn_config = ni6527_intr_insn_config;
387
388         writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(0));
389         writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(1));
390         writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(2));
391
392         writeb(ClrEdge | ClrOverflow | ClrFilter | ClrInterval,
393                devpriv->mite->daq_io_addr + Clear_Register);
394         writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control);
395
396         ret = request_irq(mite_irq(devpriv->mite), ni6527_interrupt,
397                           IRQF_SHARED, dev->board_name, dev);
398         if (ret < 0)
399                 dev_warn(dev->class_dev, "irq not available\n");
400         else
401                 dev->irq = mite_irq(devpriv->mite);
402
403         return 0;
404 }
405
406 static void ni6527_detach(struct comedi_device *dev)
407 {
408         struct ni6527_private *devpriv = dev->private;
409
410         if (devpriv && devpriv->mite && devpriv->mite->daq_io_addr)
411                 writeb(0x00,
412                        devpriv->mite->daq_io_addr + Master_Interrupt_Control);
413         if (dev->irq)
414                 free_irq(dev->irq, dev);
415         if (devpriv && devpriv->mite) {
416                 mite_unsetup(devpriv->mite);
417                 mite_free(devpriv->mite);
418         }
419         comedi_pci_disable(dev);
420 }
421
422 static struct comedi_driver ni6527_driver = {
423         .driver_name = "ni_6527",
424         .module = THIS_MODULE,
425         .auto_attach = ni6527_auto_attach,
426         .detach = ni6527_detach,
427 };
428
429 static int ni6527_pci_probe(struct pci_dev *dev,
430                             const struct pci_device_id *id)
431 {
432         return comedi_pci_auto_config(dev, &ni6527_driver, id->driver_data);
433 }
434
435 static DEFINE_PCI_DEVICE_TABLE(ni6527_pci_table) = {
436         { PCI_VDEVICE(NI, 0x2b10), BOARD_PXI6527 },
437         { PCI_VDEVICE(NI, 0x2b20), BOARD_PCI6527 },
438         { 0 }
439 };
440 MODULE_DEVICE_TABLE(pci, ni6527_pci_table);
441
442 static struct pci_driver ni6527_pci_driver = {
443         .name           = "ni_6527",
444         .id_table       = ni6527_pci_table,
445         .probe          = ni6527_pci_probe,
446         .remove         = comedi_pci_auto_unconfig,
447 };
448 module_comedi_pci_driver(ni6527_driver, ni6527_pci_driver);
449
450 MODULE_AUTHOR("Comedi http://www.comedi.org");
451 MODULE_DESCRIPTION("Comedi low-level driver");
452 MODULE_LICENSE("GPL");