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