Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / comedi_parport.c
1 /*
2  * comedi_parport.c
3  * Comedi driver for standard parallel port
4  *
5  * For more information see:
6  *      http://retired.beyondlogic.org/spp/parallel.htm
7  *
8  * COMEDI - Linux Control and Measurement Device Interface
9  * Copyright (C) 1998,2001 David A. Schleef <ds@schleef.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  */
21
22 /*
23  * Driver: comedi_parport
24  * Description: Standard PC parallel port
25  * Author: ds
26  * Status: works in immediate mode
27  * Devices: [standard] parallel port (comedi_parport)
28  * Updated: Tue, 30 Apr 2002 21:11:45 -0700
29  *
30  * A cheap and easy way to get a few more digital I/O lines. Steal
31  * additional parallel ports from old computers or your neighbors'
32  * computers.
33  *
34  * Option list:
35  *   0: I/O port base for the parallel port.
36  *   1: IRQ (optional)
37  *
38  * Parallel Port Lines:
39  *
40  *       pin   subdev  chan  type  name
41  *      -----  ------  ----  ----  --------------
42  *        1      2       0    DO   strobe
43  *        2      0       0    DIO  data 0
44  *        3      0       1    DIO  data 1
45  *        4      0       2    DIO  data 2
46  *        5      0       3    DIO  data 3
47  *        6      0       4    DIO  data 4
48  *        7      0       5    DIO  data 5
49  *        8      0       6    DIO  data 6
50  *        9      0       7    DIO  data 7
51  *       10      1       3    DI   ack
52  *       11      1       4    DI   busy
53  *       12      1       2    DI   paper out
54  *       13      1       1    DI   select in
55  *       14      2       1    DO   auto LF
56  *       15      1       0    DI   error
57  *       16      2       2    DO   init
58  *       17      2       3    DO   select printer
59  *      18-25                      ground
60  *
61  * When an IRQ is configured subdevice 3 pretends to be a digital
62  * input subdevice, but it always returns 0 when read. However, if
63  * you run a command with scan_begin_src=TRIG_EXT, it uses pin 10
64  * as a external trigger, which can be used to wake up tasks.
65  */
66
67 #include <linux/module.h>
68 #include <linux/interrupt.h>
69
70 #include "../comedidev.h"
71
72 /*
73  * Register map
74  */
75 #define PARPORT_DATA_REG        0x00
76 #define PARPORT_STATUS_REG      0x01
77 #define PARPORT_CTRL_REG        0x02
78 #define PARPORT_CTRL_IRQ_ENA    (1 << 4)
79 #define PARPORT_CTRL_BIDIR_ENA  (1 << 5)
80
81 static int parport_data_reg_insn_bits(struct comedi_device *dev,
82                                       struct comedi_subdevice *s,
83                                       struct comedi_insn *insn,
84                                       unsigned int *data)
85 {
86         if (comedi_dio_update_state(s, data))
87                 outb(s->state, dev->iobase + PARPORT_DATA_REG);
88
89         data[1] = inb(dev->iobase + PARPORT_DATA_REG);
90
91         return insn->n;
92 }
93
94 static int parport_data_reg_insn_config(struct comedi_device *dev,
95                                         struct comedi_subdevice *s,
96                                         struct comedi_insn *insn,
97                                         unsigned int *data)
98 {
99         unsigned int ctrl;
100         int ret;
101
102         ret = comedi_dio_insn_config(dev, s, insn, data, 0xff);
103         if (ret)
104                 return ret;
105
106         ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
107         if (s->io_bits)
108                 ctrl &= ~PARPORT_CTRL_BIDIR_ENA;
109         else
110                 ctrl |= PARPORT_CTRL_BIDIR_ENA;
111         outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
112
113         return insn->n;
114 }
115
116 static int parport_status_reg_insn_bits(struct comedi_device *dev,
117                                         struct comedi_subdevice *s,
118                                         struct comedi_insn *insn,
119                                         unsigned int *data)
120 {
121         data[1] = inb(dev->iobase + PARPORT_STATUS_REG) >> 3;
122
123         return insn->n;
124 }
125
126 static int parport_ctrl_reg_insn_bits(struct comedi_device *dev,
127                                       struct comedi_subdevice *s,
128                                       struct comedi_insn *insn,
129                                       unsigned int *data)
130 {
131         unsigned int ctrl;
132
133         if (comedi_dio_update_state(s, data)) {
134                 ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
135                 ctrl &= (PARPORT_CTRL_IRQ_ENA | PARPORT_CTRL_BIDIR_ENA);
136                 ctrl |= s->state;
137                 outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
138         }
139
140         data[1] = s->state;
141
142         return insn->n;
143 }
144
145 static int parport_intr_insn_bits(struct comedi_device *dev,
146                                   struct comedi_subdevice *s,
147                                   struct comedi_insn *insn,
148                                   unsigned int *data)
149 {
150         data[1] = 0;
151         return insn->n;
152 }
153
154 static int parport_intr_cmdtest(struct comedi_device *dev,
155                                 struct comedi_subdevice *s,
156                                 struct comedi_cmd *cmd)
157 {
158         int err = 0;
159
160         /* Step 1 : check if triggers are trivially valid */
161
162         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
163         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
164         err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
165         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
166         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
167
168         if (err)
169                 return 1;
170
171         /* Step 2a : make sure trigger sources are unique */
172         /* Step 2b : and mutually compatible */
173
174         /* Step 3: check if arguments are trivially valid */
175
176         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
177         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
178         err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
179         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
180                                            cmd->chanlist_len);
181         err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
182
183         if (err)
184                 return 3;
185
186         /* Step 4: fix up any arguments */
187
188         /* Step 5: check channel list if it exists */
189
190         return 0;
191 }
192
193 static int parport_intr_cmd(struct comedi_device *dev,
194                             struct comedi_subdevice *s)
195 {
196         unsigned int ctrl;
197
198         ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
199         ctrl |= PARPORT_CTRL_IRQ_ENA;
200         outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
201
202         return 0;
203 }
204
205 static int parport_intr_cancel(struct comedi_device *dev,
206                                struct comedi_subdevice *s)
207 {
208         unsigned int ctrl;
209
210         ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
211         ctrl &= ~PARPORT_CTRL_IRQ_ENA;
212         outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
213
214         return 0;
215 }
216
217 static irqreturn_t parport_interrupt(int irq, void *d)
218 {
219         struct comedi_device *dev = d;
220         struct comedi_subdevice *s = dev->read_subdev;
221         unsigned int ctrl;
222
223         ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
224         if (!(ctrl & PARPORT_CTRL_IRQ_ENA))
225                 return IRQ_NONE;
226
227         comedi_buf_write_samples(s, &s->state, 1);
228         comedi_handle_events(dev, s);
229
230         return IRQ_HANDLED;
231 }
232
233 static int parport_attach(struct comedi_device *dev,
234                           struct comedi_devconfig *it)
235 {
236         struct comedi_subdevice *s;
237         int ret;
238
239         ret = comedi_request_region(dev, it->options[0], 0x03);
240         if (ret)
241                 return ret;
242
243         if (it->options[1]) {
244                 ret = request_irq(it->options[1], parport_interrupt, 0,
245                                   dev->board_name, dev);
246                 if (ret == 0)
247                         dev->irq = it->options[1];
248         }
249
250         ret = comedi_alloc_subdevices(dev, dev->irq ? 4 : 3);
251         if (ret)
252                 return ret;
253
254         /* Digial I/O subdevice - Parallel port DATA register */
255         s = &dev->subdevices[0];
256         s->type         = COMEDI_SUBD_DIO;
257         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
258         s->n_chan       = 8;
259         s->maxdata      = 1;
260         s->range_table  = &range_digital;
261         s->insn_bits    = parport_data_reg_insn_bits;
262         s->insn_config  = parport_data_reg_insn_config;
263
264         /* Digial Input subdevice - Parallel port STATUS register */
265         s = &dev->subdevices[1];
266         s->type         = COMEDI_SUBD_DI;
267         s->subdev_flags = SDF_READABLE;
268         s->n_chan       = 5;
269         s->maxdata      = 1;
270         s->range_table  = &range_digital;
271         s->insn_bits    = parport_status_reg_insn_bits;
272
273         /* Digial Output subdevice - Parallel port CONTROL register */
274         s = &dev->subdevices[2];
275         s->type         = COMEDI_SUBD_DO;
276         s->subdev_flags = SDF_WRITABLE;
277         s->n_chan       = 4;
278         s->maxdata      = 1;
279         s->range_table  = &range_digital;
280         s->insn_bits    = parport_ctrl_reg_insn_bits;
281
282         if (dev->irq) {
283                 /* Digial Input subdevice - Interrupt support */
284                 s = &dev->subdevices[3];
285                 dev->read_subdev = s;
286                 s->type         = COMEDI_SUBD_DI;
287                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
288                 s->n_chan       = 1;
289                 s->maxdata      = 1;
290                 s->range_table  = &range_digital;
291                 s->insn_bits    = parport_intr_insn_bits;
292                 s->len_chanlist = 1;
293                 s->do_cmdtest   = parport_intr_cmdtest;
294                 s->do_cmd       = parport_intr_cmd;
295                 s->cancel       = parport_intr_cancel;
296         }
297
298         outb(0, dev->iobase + PARPORT_DATA_REG);
299         outb(0, dev->iobase + PARPORT_CTRL_REG);
300
301         return 0;
302 }
303
304 static struct comedi_driver parport_driver = {
305         .driver_name    = "comedi_parport",
306         .module         = THIS_MODULE,
307         .attach         = parport_attach,
308         .detach         = comedi_legacy_detach,
309 };
310 module_comedi_driver(parport_driver);
311
312 MODULE_AUTHOR("Comedi http://www.comedi.org");
313 MODULE_DESCRIPTION("Comedi: Standard parallel port driver");
314 MODULE_LICENSE("GPL");