Merge tag 'staging-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / dt2817.c
1 /*
2     comedi/drivers/dt2817.c
3     Hardware driver for Data Translation DT2817
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 1998 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: dt2817
20 Description: Data Translation DT2817
21 Author: ds
22 Status: complete
23 Devices: [Data Translation] DT2817 (dt2817)
24
25 A very simple digital I/O card.  Four banks of 8 lines, each bank
26 is configurable for input or output.  One wonders why it takes a
27 50 page manual to describe this thing.
28
29 The driver (which, btw, is much less than 50 pages) has 1 subdevice
30 with 32 channels, configurable in groups of 8.
31
32 Configuration options:
33   [0] - I/O port base base address
34 */
35
36 #include "../comedidev.h"
37
38 #include <linux/ioport.h>
39
40 #define DT2817_SIZE 5
41
42 #define DT2817_CR 0
43 #define DT2817_DATA 1
44
45 static int dt2817_dio_insn_config(struct comedi_device *dev,
46                                   struct comedi_subdevice *s,
47                                   struct comedi_insn *insn, unsigned int *data)
48 {
49         int mask;
50         int chan;
51         int oe = 0;
52
53         if (insn->n != 1)
54                 return -EINVAL;
55
56         chan = CR_CHAN(insn->chanspec);
57         if (chan < 8)
58                 mask = 0xff;
59         else if (chan < 16)
60                 mask = 0xff00;
61         else if (chan < 24)
62                 mask = 0xff0000;
63         else
64                 mask = 0xff000000;
65         if (data[0])
66                 s->io_bits |= mask;
67         else
68                 s->io_bits &= ~mask;
69
70         if (s->io_bits & 0x000000ff)
71                 oe |= 0x1;
72         if (s->io_bits & 0x0000ff00)
73                 oe |= 0x2;
74         if (s->io_bits & 0x00ff0000)
75                 oe |= 0x4;
76         if (s->io_bits & 0xff000000)
77                 oe |= 0x8;
78
79         outb(oe, dev->iobase + DT2817_CR);
80
81         return 1;
82 }
83
84 static int dt2817_dio_insn_bits(struct comedi_device *dev,
85                                 struct comedi_subdevice *s,
86                                 struct comedi_insn *insn, unsigned int *data)
87 {
88         unsigned int changed;
89
90         /* It's questionable whether it is more important in
91          * a driver like this to be deterministic or fast.
92          * We choose fast. */
93
94         if (data[0]) {
95                 changed = s->state;
96                 s->state &= ~data[0];
97                 s->state |= (data[0] & data[1]);
98                 changed ^= s->state;
99                 changed &= s->io_bits;
100                 if (changed & 0x000000ff)
101                         outb(s->state & 0xff, dev->iobase + DT2817_DATA + 0);
102                 if (changed & 0x0000ff00)
103                         outb((s->state >> 8) & 0xff,
104                              dev->iobase + DT2817_DATA + 1);
105                 if (changed & 0x00ff0000)
106                         outb((s->state >> 16) & 0xff,
107                              dev->iobase + DT2817_DATA + 2);
108                 if (changed & 0xff000000)
109                         outb((s->state >> 24) & 0xff,
110                              dev->iobase + DT2817_DATA + 3);
111         }
112         data[1] = inb(dev->iobase + DT2817_DATA + 0);
113         data[1] |= (inb(dev->iobase + DT2817_DATA + 1) << 8);
114         data[1] |= (inb(dev->iobase + DT2817_DATA + 2) << 16);
115         data[1] |= (inb(dev->iobase + DT2817_DATA + 3) << 24);
116
117         return insn->n;
118 }
119
120 static int dt2817_attach(struct comedi_device *dev, struct comedi_devconfig *it)
121 {
122         int ret;
123         struct comedi_subdevice *s;
124
125         ret = comedi_request_region(dev, it->options[0], DT2817_SIZE);
126         if (ret)
127                 return ret;
128
129         ret = comedi_alloc_subdevices(dev, 1);
130         if (ret)
131                 return ret;
132
133         s = &dev->subdevices[0];
134
135         s->n_chan = 32;
136         s->type = COMEDI_SUBD_DIO;
137         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
138         s->range_table = &range_digital;
139         s->maxdata = 1;
140         s->insn_bits = dt2817_dio_insn_bits;
141         s->insn_config = dt2817_dio_insn_config;
142
143         s->state = 0;
144         outb(0, dev->iobase + DT2817_CR);
145
146         return 0;
147 }
148
149 static struct comedi_driver dt2817_driver = {
150         .driver_name    = "dt2817",
151         .module         = THIS_MODULE,
152         .attach         = dt2817_attach,
153         .detach         = comedi_legacy_detach,
154 };
155 module_comedi_driver(dt2817_driver);
156
157 MODULE_AUTHOR("Comedi http://www.comedi.org");
158 MODULE_DESCRIPTION("Comedi low-level driver");
159 MODULE_LICENSE("GPL");