Merge branch 'upstream' into for-linus
[pandora-kernel.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     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 */
23 /*
24 Driver: dt2817
25 Description: Data Translation DT2817
26 Author: ds
27 Status: complete
28 Devices: [Data Translation] DT2817 (dt2817)
29
30 A very simple digital I/O card.  Four banks of 8 lines, each bank
31 is configurable for input or output.  One wonders why it takes a
32 50 page manual to describe this thing.
33
34 The driver (which, btw, is much less than 50 pages) has 1 subdevice
35 with 32 channels, configurable in groups of 8.
36
37 Configuration options:
38   [0] - I/O port base base address
39 */
40
41 #include "../comedidev.h"
42
43 #include <linux/ioport.h>
44
45 #define DT2817_SIZE 5
46
47 #define DT2817_CR 0
48 #define DT2817_DATA 1
49
50 static int dt2817_attach(struct comedi_device *dev,
51                          struct comedi_devconfig *it);
52 static int dt2817_detach(struct comedi_device *dev);
53 static struct comedi_driver driver_dt2817 = {
54         .driver_name = "dt2817",
55         .module = THIS_MODULE,
56         .attach = dt2817_attach,
57         .detach = dt2817_detach,
58 };
59
60 COMEDI_INITCLEANUP(driver_dt2817);
61
62 static int dt2817_dio_insn_config(struct comedi_device *dev,
63                                   struct comedi_subdevice *s,
64                                   struct comedi_insn *insn, unsigned int *data)
65 {
66         int mask;
67         int chan;
68         int oe = 0;
69
70         if (insn->n != 1)
71                 return -EINVAL;
72
73         chan = CR_CHAN(insn->chanspec);
74         if (chan < 8) {
75                 mask = 0xff;
76         } else if (chan < 16) {
77                 mask = 0xff00;
78         } else if (chan < 24) {
79                 mask = 0xff0000;
80         } else
81                 mask = 0xff000000;
82         if (data[0])
83                 s->io_bits |= mask;
84         else
85                 s->io_bits &= ~mask;
86
87         if (s->io_bits & 0x000000ff)
88                 oe |= 0x1;
89         if (s->io_bits & 0x0000ff00)
90                 oe |= 0x2;
91         if (s->io_bits & 0x00ff0000)
92                 oe |= 0x4;
93         if (s->io_bits & 0xff000000)
94                 oe |= 0x8;
95
96         outb(oe, dev->iobase + DT2817_CR);
97
98         return 1;
99 }
100
101 static int dt2817_dio_insn_bits(struct comedi_device *dev,
102                                 struct comedi_subdevice *s,
103                                 struct comedi_insn *insn, unsigned int *data)
104 {
105         unsigned int changed;
106
107         /* It's questionable whether it is more important in
108          * a driver like this to be deterministic or fast.
109          * We choose fast. */
110
111         if (data[0]) {
112                 changed = s->state;
113                 s->state &= ~data[0];
114                 s->state |= (data[0] & data[1]);
115                 changed ^= s->state;
116                 changed &= s->io_bits;
117                 if (changed & 0x000000ff)
118                         outb(s->state & 0xff, dev->iobase + DT2817_DATA + 0);
119                 if (changed & 0x0000ff00)
120                         outb((s->state >> 8) & 0xff,
121                              dev->iobase + DT2817_DATA + 1);
122                 if (changed & 0x00ff0000)
123                         outb((s->state >> 16) & 0xff,
124                              dev->iobase + DT2817_DATA + 2);
125                 if (changed & 0xff000000)
126                         outb((s->state >> 24) & 0xff,
127                              dev->iobase + DT2817_DATA + 3);
128         }
129         data[1] = inb(dev->iobase + DT2817_DATA + 0);
130         data[1] |= (inb(dev->iobase + DT2817_DATA + 1) << 8);
131         data[1] |= (inb(dev->iobase + DT2817_DATA + 2) << 16);
132         data[1] |= (inb(dev->iobase + DT2817_DATA + 3) << 24);
133
134         return 2;
135 }
136
137 static int dt2817_attach(struct comedi_device *dev, struct comedi_devconfig *it)
138 {
139         int ret;
140         struct comedi_subdevice *s;
141         unsigned long iobase;
142
143         iobase = it->options[0];
144         printk("comedi%d: dt2817: 0x%04lx ", dev->minor, iobase);
145         if (!request_region(iobase, DT2817_SIZE, "dt2817")) {
146                 printk("I/O port conflict\n");
147                 return -EIO;
148         }
149         dev->iobase = iobase;
150         dev->board_name = "dt2817";
151
152         ret = alloc_subdevices(dev, 1);
153         if (ret < 0)
154                 return ret;
155
156         s = dev->subdevices + 0;
157
158         s->n_chan = 32;
159         s->type = COMEDI_SUBD_DIO;
160         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
161         s->range_table = &range_digital;
162         s->maxdata = 1;
163         s->insn_bits = dt2817_dio_insn_bits;
164         s->insn_config = dt2817_dio_insn_config;
165
166         s->state = 0;
167         outb(0, dev->iobase + DT2817_CR);
168
169         printk("\n");
170
171         return 0;
172 }
173
174 static int dt2817_detach(struct comedi_device *dev)
175 {
176         printk("comedi%d: dt2817: remove\n", dev->minor);
177
178         if (dev->iobase)
179                 release_region(dev->iobase, DT2817_SIZE);
180
181         return 0;
182 }