1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60#include <linux/module.h>
61#include <linux/interrupt.h>
62
63#include "../comedidev.h"
64
65#define PCL726_AO_MSB_REG(x) (0x00 + ((x) * 2))
66#define PCL726_AO_LSB_REG(x) (0x01 + ((x) * 2))
67#define PCL726_DO_MSB_REG 0x0c
68#define PCL726_DO_LSB_REG 0x0d
69#define PCL726_DI_MSB_REG 0x0e
70#define PCL726_DI_LSB_REG 0x0f
71
72#define PCL727_DI_MSB_REG 0x00
73#define PCL727_DI_LSB_REG 0x01
74#define PCL727_DO_MSB_REG 0x18
75#define PCL727_DO_LSB_REG 0x19
76
77static const struct comedi_lrange *const rangelist_726[] = {
78 &range_unipolar5,
79 &range_unipolar10,
80 &range_bipolar5,
81 &range_bipolar10,
82 &range_4_20mA,
83 &range_unknown
84};
85
86static const struct comedi_lrange *const rangelist_727[] = {
87 &range_unipolar5,
88 &range_unipolar10,
89 &range_bipolar5,
90 &range_4_20mA
91};
92
93static const struct comedi_lrange *const rangelist_728[] = {
94 &range_unipolar5,
95 &range_unipolar10,
96 &range_bipolar5,
97 &range_bipolar10,
98 &range_4_20mA,
99 &range_0_20mA
100};
101
102struct pcl726_board {
103 const char *name;
104 unsigned long io_len;
105 unsigned int irq_mask;
106 const struct comedi_lrange *const *ao_ranges;
107 int ao_num_ranges;
108 int ao_nchan;
109 unsigned int have_dio:1;
110 unsigned int is_pcl727:1;
111};
112
113static const struct pcl726_board pcl726_boards[] = {
114 {
115 .name = "pcl726",
116 .io_len = 0x10,
117 .ao_ranges = &rangelist_726[0],
118 .ao_num_ranges = ARRAY_SIZE(rangelist_726),
119 .ao_nchan = 6,
120 .have_dio = 1,
121 }, {
122 .name = "pcl727",
123 .io_len = 0x20,
124 .ao_ranges = &rangelist_727[0],
125 .ao_num_ranges = ARRAY_SIZE(rangelist_727),
126 .ao_nchan = 12,
127 .have_dio = 1,
128 .is_pcl727 = 1,
129 }, {
130 .name = "pcl728",
131 .io_len = 0x08,
132 .ao_num_ranges = ARRAY_SIZE(rangelist_728),
133 .ao_ranges = &rangelist_728[0],
134 .ao_nchan = 2,
135 }, {
136 .name = "acl6126",
137 .io_len = 0x10,
138 .irq_mask = 0x96e8,
139 .ao_num_ranges = ARRAY_SIZE(rangelist_726),
140 .ao_ranges = &rangelist_726[0],
141 .ao_nchan = 6,
142 .have_dio = 1,
143 }, {
144 .name = "acl6128",
145 .io_len = 0x08,
146 .ao_num_ranges = ARRAY_SIZE(rangelist_728),
147 .ao_ranges = &rangelist_728[0],
148 .ao_nchan = 2,
149 },
150};
151
152struct pcl726_private {
153 const struct comedi_lrange *rangelist[12];
154 unsigned int cmd_running:1;
155};
156
157static int pcl726_intr_insn_bits(struct comedi_device *dev,
158 struct comedi_subdevice *s,
159 struct comedi_insn *insn,
160 unsigned int *data)
161{
162 data[1] = 0;
163 return insn->n;
164}
165
166static int pcl726_intr_cmdtest(struct comedi_device *dev,
167 struct comedi_subdevice *s,
168 struct comedi_cmd *cmd)
169{
170 int err = 0;
171
172
173
174 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
175 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
176 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
177 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
178 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
179
180 if (err)
181 return 1;
182
183
184
185
186
187
188 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
189 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
190 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
191 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
192 cmd->chanlist_len);
193 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
194
195 if (err)
196 return 3;
197
198
199
200
201
202 return 0;
203}
204
205static int pcl726_intr_cmd(struct comedi_device *dev,
206 struct comedi_subdevice *s)
207{
208 struct pcl726_private *devpriv = dev->private;
209
210 devpriv->cmd_running = 1;
211
212 return 0;
213}
214
215static int pcl726_intr_cancel(struct comedi_device *dev,
216 struct comedi_subdevice *s)
217{
218 struct pcl726_private *devpriv = dev->private;
219
220 devpriv->cmd_running = 0;
221
222 return 0;
223}
224
225static irqreturn_t pcl726_interrupt(int irq, void *d)
226{
227 struct comedi_device *dev = d;
228 struct comedi_subdevice *s = dev->read_subdev;
229 struct pcl726_private *devpriv = dev->private;
230
231 if (devpriv->cmd_running) {
232 pcl726_intr_cancel(dev, s);
233
234 comedi_buf_write_samples(s, &s->state, 1);
235 comedi_handle_events(dev, s);
236 }
237
238 return IRQ_HANDLED;
239}
240
241static int pcl726_ao_insn_write(struct comedi_device *dev,
242 struct comedi_subdevice *s,
243 struct comedi_insn *insn,
244 unsigned int *data)
245{
246 unsigned int chan = CR_CHAN(insn->chanspec);
247 unsigned int range = CR_RANGE(insn->chanspec);
248 int i;
249
250 for (i = 0; i < insn->n; i++) {
251 unsigned int val = data[i];
252
253 s->readback[chan] = val;
254
255
256 if (comedi_chan_range_is_bipolar(s, chan, range))
257 val = comedi_offset_munge(s, val);
258
259
260 outb((val >> 8) & 0xff, dev->iobase + PCL726_AO_MSB_REG(chan));
261 outb(val & 0xff, dev->iobase + PCL726_AO_LSB_REG(chan));
262 }
263
264 return insn->n;
265}
266
267static int pcl726_di_insn_bits(struct comedi_device *dev,
268 struct comedi_subdevice *s,
269 struct comedi_insn *insn,
270 unsigned int *data)
271{
272 const struct pcl726_board *board = dev->board_ptr;
273 unsigned int val;
274
275 if (board->is_pcl727) {
276 val = inb(dev->iobase + PCL727_DI_LSB_REG);
277 val |= (inb(dev->iobase + PCL727_DI_MSB_REG) << 8);
278 } else {
279 val = inb(dev->iobase + PCL726_DI_LSB_REG);
280 val |= (inb(dev->iobase + PCL726_DI_MSB_REG) << 8);
281 }
282
283 data[1] = val;
284
285 return insn->n;
286}
287
288static int pcl726_do_insn_bits(struct comedi_device *dev,
289 struct comedi_subdevice *s,
290 struct comedi_insn *insn,
291 unsigned int *data)
292{
293 const struct pcl726_board *board = dev->board_ptr;
294 unsigned long io = dev->iobase;
295 unsigned int mask;
296
297 mask = comedi_dio_update_state(s, data);
298 if (mask) {
299 if (board->is_pcl727) {
300 if (mask & 0x00ff)
301 outb(s->state & 0xff, io + PCL727_DO_LSB_REG);
302 if (mask & 0xff00)
303 outb((s->state >> 8), io + PCL727_DO_MSB_REG);
304 } else {
305 if (mask & 0x00ff)
306 outb(s->state & 0xff, io + PCL726_DO_LSB_REG);
307 if (mask & 0xff00)
308 outb((s->state >> 8), io + PCL726_DO_MSB_REG);
309 }
310 }
311
312 data[1] = s->state;
313
314 return insn->n;
315}
316
317static int pcl726_attach(struct comedi_device *dev,
318 struct comedi_devconfig *it)
319{
320 const struct pcl726_board *board = dev->board_ptr;
321 struct pcl726_private *devpriv;
322 struct comedi_subdevice *s;
323 int subdev;
324 int ret;
325 int i;
326
327 ret = comedi_request_region(dev, it->options[0], board->io_len);
328 if (ret)
329 return ret;
330
331 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
332 if (!devpriv)
333 return -ENOMEM;
334
335
336
337
338
339 if (it->options[1] && (board->irq_mask & (1 << it->options[1]))) {
340 ret = request_irq(it->options[1], pcl726_interrupt, 0,
341 dev->board_name, dev);
342 if (ret == 0) {
343
344 dev->irq = it->options[1];
345 }
346 }
347
348
349 for (i = 0; i < 12; i++) {
350 unsigned int opt = it->options[2 + i];
351
352 if (opt < board->ao_num_ranges && i < board->ao_nchan)
353 devpriv->rangelist[i] = board->ao_ranges[opt];
354 else
355 devpriv->rangelist[i] = &range_unknown;
356 }
357
358 subdev = board->have_dio ? 3 : 1;
359 if (dev->irq)
360 subdev++;
361 ret = comedi_alloc_subdevices(dev, subdev);
362 if (ret)
363 return ret;
364
365 subdev = 0;
366
367
368 s = &dev->subdevices[subdev++];
369 s->type = COMEDI_SUBD_AO;
370 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
371 s->n_chan = board->ao_nchan;
372 s->maxdata = 0x0fff;
373 s->range_table_list = devpriv->rangelist;
374 s->insn_write = pcl726_ao_insn_write;
375
376 ret = comedi_alloc_subdev_readback(s);
377 if (ret)
378 return ret;
379
380 if (board->have_dio) {
381
382 s = &dev->subdevices[subdev++];
383 s->type = COMEDI_SUBD_DI;
384 s->subdev_flags = SDF_READABLE;
385 s->n_chan = 16;
386 s->maxdata = 1;
387 s->insn_bits = pcl726_di_insn_bits;
388 s->range_table = &range_digital;
389
390
391 s = &dev->subdevices[subdev++];
392 s->type = COMEDI_SUBD_DO;
393 s->subdev_flags = SDF_WRITABLE;
394 s->n_chan = 16;
395 s->maxdata = 1;
396 s->insn_bits = pcl726_do_insn_bits;
397 s->range_table = &range_digital;
398 }
399
400 if (dev->irq) {
401
402 s = &dev->subdevices[subdev++];
403 dev->read_subdev = s;
404 s->type = COMEDI_SUBD_DI;
405 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
406 s->n_chan = 1;
407 s->maxdata = 1;
408 s->range_table = &range_digital;
409 s->insn_bits = pcl726_intr_insn_bits;
410 s->len_chanlist = 1;
411 s->do_cmdtest = pcl726_intr_cmdtest;
412 s->do_cmd = pcl726_intr_cmd;
413 s->cancel = pcl726_intr_cancel;
414 }
415
416 return 0;
417}
418
419static struct comedi_driver pcl726_driver = {
420 .driver_name = "pcl726",
421 .module = THIS_MODULE,
422 .attach = pcl726_attach,
423 .detach = comedi_legacy_detach,
424 .board_name = &pcl726_boards[0].name,
425 .num_names = ARRAY_SIZE(pcl726_boards),
426 .offset = sizeof(struct pcl726_board),
427};
428module_comedi_driver(pcl726_driver);
429
430MODULE_AUTHOR("Comedi http://www.comedi.org");
431MODULE_DESCRIPTION("Comedi driver for Advantech PCL-726 & compatibles");
432MODULE_LICENSE("GPL");
433