blob: 2ff2fe94b7042afa342ad61b5f5ed2e9ef5ccc75 [file] [log] [blame]
Calin Culianu6baef152009-02-19 09:13:10 -08001/*
2 comedi/drivers/pcmmio.c
3 Driver for Winsystems PC-104 based multifunction IO board.
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2007 Calin A. Culianu <calin@ajvar.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/*
23Driver: pcmmio
24Description: A driver for the PCM-MIO multifunction board
25Devices: [Winsystems] PCM-MIO (pcmmio)
26Author: Calin Culianu <calin@ajvar.org>
27Updated: Wed, May 16 2007 16:21:10 -0500
28Status: works
29
30A driver for the relatively new PCM-MIO multifunction board from
31Winsystems. This board is a PC-104 based I/O board. It contains
32four subdevices:
33 subdevice 0 - 16 channels of 16-bit AI
34 subdevice 1 - 8 channels of 16-bit AO
35 subdevice 2 - first 24 channels of the 48 channel of DIO (with edge-triggered interrupt support)
36 subdevice 3 - last 24 channels of the 48 channel DIO (no interrupt support for this bank of channels)
37
38 Some notes:
39
40 Synchronous reads and writes are the only things implemented for AI and AO,
41 even though the hardware itself can do streaming acquisition, etc. Anyone
42 want to add asynchronous I/O for AI/AO as a feature? Be my guest...
43
44 Asynchronous I/O for the DIO subdevices *is* implemented, however! They are
45 basically edge-triggered interrupts for any configuration of the first
46 24 DIO-lines.
47
48 Also note that this interrupt support is untested.
49
50 A few words about edge-detection IRQ support (commands on DIO):
51
52 * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
53 of the board to the comedi_config command. The board IRQ is not jumpered
54 but rather configured through software, so any IRQ from 1-15 is OK.
55
56 * Due to the genericity of the comedi API, you need to create a special
57 comedi_command in order to use edge-triggered interrupts for DIO.
58
59 * Use comedi_commands with TRIG_NOW. Your callback will be called each
60 time an edge is detected on the specified DIO line(s), and the data
61 values will be two sample_t's, which should be concatenated to form
62 one 32-bit unsigned int. This value is the mask of channels that had
63 edges detected from your channel list. Note that the bits positions
64 in the mask correspond to positions in your chanlist when you
65 specified the command and *not* channel id's!
66
67 * To set the polarity of the edge-detection interrupts pass a nonzero value
68 for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
69 value for both CR_RANGE and CR_AREF if you want edge-down polarity.
70
71Configuration Options:
72 [0] - I/O port base address
73 [1] - IRQ (optional -- for edge-detect interrupt support only, leave out if you don't need this feature)
74*/
75
76#include "../comedidev.h"
77#include <linux/pci.h> /* for PCI devices */
78
Bill Pemberton53106ae2009-04-09 16:07:21 -040079#define MIN(a, b) (((a) < (b)) ? (a) : (b))
Calin Culianu6baef152009-02-19 09:13:10 -080080
81/* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
82#define CHANS_PER_PORT 8
83#define PORTS_PER_ASIC 6
84#define INTR_PORTS_PER_ASIC 3
85#define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
86#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
87#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
88#define INTR_CHANS_PER_ASIC 24
89#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
90#define MAX_DIO_CHANS (PORTS_PER_ASIC*1*CHANS_PER_PORT)
91#define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
92#define SDEV_NO ((int)(s - dev->subdevices))
93#define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
94/* IO Memory sizes */
95#define ASIC_IOSIZE (0x0B)
96#define PCMMIO48_IOSIZE ASIC_IOSIZE
97
98/* Some offsets - these are all in the 16byte IO memory offset from
99 the base address. Note that there is a paging scheme to swap out
100 offsets 0x8-0xA using the PAGELOCK register. See the table below.
101
102 Register(s) Pages R/W? Description
103 --------------------------------------------------------------
104 REG_PORTx All R/W Read/Write/Configure IO
105 REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int.
106 REG_PAGELOCK All WriteOnly Select a page
107 REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity
108 REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int.
109 REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints.
110 */
111#define REG_PORT0 0x0
112#define REG_PORT1 0x1
113#define REG_PORT2 0x2
114#define REG_PORT3 0x3
115#define REG_PORT4 0x4
116#define REG_PORT5 0x5
117#define REG_INT_PENDING 0x6
118#define REG_PAGELOCK 0x7 /* page selector register, upper 2 bits select a page
119 and bits 0-5 are used to 'lock down' a particular
120 port above to make it readonly. */
121#define REG_POL0 0x8
122#define REG_POL1 0x9
123#define REG_POL2 0xA
124#define REG_ENAB0 0x8
125#define REG_ENAB1 0x9
126#define REG_ENAB2 0xA
127#define REG_INT_ID0 0x8
128#define REG_INT_ID1 0x9
129#define REG_INT_ID2 0xA
130
131#define NUM_PAGED_REGS 3
132#define NUM_PAGES 4
133#define FIRST_PAGED_REG 0x8
134#define REG_PAGE_BITOFFSET 6
135#define REG_LOCK_BITOFFSET 0
136#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
137#define REG_LOCK_MASK ~(REG_PAGE_MASK)
138#define PAGE_POL 1
139#define PAGE_ENAB 2
140#define PAGE_INT_ID 3
141
Bill Pemberton34c43922009-03-16 22:05:14 -0400142typedef int (*comedi_insn_fn_t) (struct comedi_device *, struct comedi_subdevice *,
Bill Pemberton90035c02009-03-16 22:05:53 -0400143 struct comedi_insn *, unsigned int *);
Calin Culianu6baef152009-02-19 09:13:10 -0800144
Bill Pemberton90035c02009-03-16 22:05:53 -0400145static int ai_rinsn(struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
Bill Pemberton790c5542009-03-16 22:05:02 -0400146 unsigned int *);
Bill Pemberton90035c02009-03-16 22:05:53 -0400147static int ao_rinsn(struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
Bill Pemberton790c5542009-03-16 22:05:02 -0400148 unsigned int *);
Bill Pemberton90035c02009-03-16 22:05:53 -0400149static int ao_winsn(struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
Bill Pemberton790c5542009-03-16 22:05:02 -0400150 unsigned int *);
Calin Culianu6baef152009-02-19 09:13:10 -0800151
152/*
153 * Board descriptions for two imaginary boards. Describing the
154 * boards in this way is optional, and completely driver-dependent.
155 * Some drivers use arrays such as this, other do not.
156 */
Bill Pemberton657f81e2009-03-16 22:19:04 -0400157struct pcmmio_board {
Calin Culianu6baef152009-02-19 09:13:10 -0800158 const char *name;
159 const int dio_num_asics;
160 const int dio_num_ports;
161 const int total_iosize;
162 const int ai_bits;
163 const int ao_bits;
164 const int n_ai_chans;
165 const int n_ao_chans;
Bill Pemberton9ced1de2009-03-16 22:05:31 -0400166 const struct comedi_lrange *ai_range_table, *ao_range_table;
Calin Culianu6baef152009-02-19 09:13:10 -0800167 comedi_insn_fn_t ai_rinsn, ao_rinsn, ao_winsn;
Bill Pemberton657f81e2009-03-16 22:19:04 -0400168};
Calin Culianu6baef152009-02-19 09:13:10 -0800169
Bill Pemberton9ced1de2009-03-16 22:05:31 -0400170static const struct comedi_lrange ranges_ai =
Calin Culianu6baef152009-02-19 09:13:10 -0800171 { 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0.,
172 10.)}
173};
174
Bill Pemberton9ced1de2009-03-16 22:05:31 -0400175static const struct comedi_lrange ranges_ao =
Calin Culianu6baef152009-02-19 09:13:10 -0800176 { 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
177 RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
178};
179
Bill Pemberton657f81e2009-03-16 22:19:04 -0400180static const struct pcmmio_board pcmmio_boards[] = {
Calin Culianu6baef152009-02-19 09:13:10 -0800181 {
Bill Pemberton68c3dbf2009-04-22 21:11:49 -0400182 .name = "pcmmio",
183 .dio_num_asics = 1,
184 .dio_num_ports = 6,
185 .total_iosize = 32,
186 .ai_bits = 16,
187 .ao_bits = 16,
188 .n_ai_chans = 16,
189 .n_ao_chans = 8,
190 .ai_range_table = &ranges_ai,
191 .ao_range_table = &ranges_ao,
192 .ai_rinsn = ai_rinsn,
193 .ao_rinsn = ao_rinsn,
194 .ao_winsn = ao_winsn},
Calin Culianu6baef152009-02-19 09:13:10 -0800195};
196
197/*
198 * Useful for shorthand access to the particular board structure
199 */
Bill Pemberton657f81e2009-03-16 22:19:04 -0400200#define thisboard ((const struct pcmmio_board *)dev->board_ptr)
Calin Culianu6baef152009-02-19 09:13:10 -0800201
202/* this structure is for data unique to this subdevice. */
Bill Pemberton4467df92009-03-16 22:19:37 -0400203struct pcmmio_subdev_private {
Calin Culianu6baef152009-02-19 09:13:10 -0800204
205 union {
206 /* for DIO: mapping of halfwords (bytes) in port/chanarray to iobase */
207 unsigned long iobases[PORTS_PER_SUBDEV];
208
209 /* for AI/AO */
210 unsigned long iobase;
211 };
212 union {
213 struct {
214
215 /* The below is only used for intr subdevices */
216 struct {
217 int asic; /* if non-negative, this subdev has an interrupt asic */
218 int first_chan; /* if nonnegative, the first channel id for
219 interrupts. */
220 int num_asic_chans; /* the number of asic channels in this subdev
221 that have interrutps */
222 int asic_chan; /* if nonnegative, the first channel id with
223 respect to the asic that has interrupts */
224 int enabled_mask; /* subdev-relative channel mask for channels
225 we are interested in */
226 int active;
227 int stop_count;
228 int continuous;
229 spinlock_t spinlock;
230 } intr;
231 } dio;
232 struct {
Bill Pemberton790c5542009-03-16 22:05:02 -0400233 unsigned int shadow_samples[8]; /* the last unsigned int data written */
Calin Culianu6baef152009-02-19 09:13:10 -0800234 } ao;
235 };
Bill Pemberton4467df92009-03-16 22:19:37 -0400236};
Calin Culianu6baef152009-02-19 09:13:10 -0800237
238/* this structure is for data unique to this hardware driver. If
239 several hardware drivers keep similar information in this structure,
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400240 feel free to suggest moving the variable to the struct comedi_device struct. */
Bill Pembertone56ab712009-03-16 22:20:30 -0400241struct pcmmio_private {
Calin Culianu6baef152009-02-19 09:13:10 -0800242 /* stuff for DIO */
243 struct {
244 unsigned char pagelock; /* current page and lock */
245 unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */
246 unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */
247 int num;
248 unsigned long iobase;
249 unsigned int irq;
250 spinlock_t spinlock;
251 } asics[MAX_ASICS];
Bill Pemberton4467df92009-03-16 22:19:37 -0400252 struct pcmmio_subdev_private *sprivs;
Bill Pembertone56ab712009-03-16 22:20:30 -0400253};
Calin Culianu6baef152009-02-19 09:13:10 -0800254
255/*
256 * most drivers define the following macro to make it easy to
257 * access the private structure.
258 */
Bill Pembertone56ab712009-03-16 22:20:30 -0400259#define devpriv ((struct pcmmio_private *)dev->private)
Bill Pemberton4467df92009-03-16 22:19:37 -0400260#define subpriv ((struct pcmmio_subdev_private *)s->private)
Calin Culianu6baef152009-02-19 09:13:10 -0800261/*
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400262 * The struct comedi_driver structure tells the Comedi core module
Calin Culianu6baef152009-02-19 09:13:10 -0800263 * which functions to call to configure/deconfigure (attach/detach)
264 * the board, and also about the kernel module that contains
265 * the device code.
266 */
Bill Pembertonda91b262009-04-09 16:07:03 -0400267static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it);
268static int pcmmio_detach(struct comedi_device *dev);
Calin Culianu6baef152009-02-19 09:13:10 -0800269
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400270static struct comedi_driver driver = {
Bill Pemberton68c3dbf2009-04-22 21:11:49 -0400271 .driver_name = "pcmmio",
272 .module = THIS_MODULE,
273 .attach = pcmmio_attach,
274 .detach = pcmmio_detach,
Calin Culianu6baef152009-02-19 09:13:10 -0800275/* It is not necessary to implement the following members if you are
276 * writing a driver for a ISA PnP or PCI card */
277 /* Most drivers will support multiple types of boards by
278 * having an array of board structures. These were defined
279 * in pcmmio_boards[] above. Note that the element 'name'
280 * was first in the structure -- Comedi uses this fact to
281 * extract the name of the board without knowing any details
282 * about the structure except for its length.
283 * When a device is attached (by comedi_config), the name
284 * of the device is given to Comedi, and Comedi tries to
285 * match it by going through the list of board names. If
286 * there is a match, the address of the pointer is put
287 * into dev->board_ptr and driver->attach() is called.
288 *
289 * Note that these are not necessary if you can determine
290 * the type of board in software. ISA PnP, PCI, and PCMCIA
291 * devices are such boards.
292 */
Bill Pemberton68c3dbf2009-04-22 21:11:49 -0400293 .board_name = &pcmmio_boards[0].name,
294 .offset = sizeof(struct pcmmio_board),
Bill Pemberton8629efa2009-04-23 15:54:56 -0400295 .num_names = ARRAY_SIZE(pcmmio_boards),
Calin Culianu6baef152009-02-19 09:13:10 -0800296};
297
Bill Pemberton814900c2009-04-23 15:54:54 -0400298static int pcmmio_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
299 struct comedi_insn *insn, unsigned int *data);
300static int pcmmio_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
301 struct comedi_insn *insn, unsigned int *data);
Calin Culianu6baef152009-02-19 09:13:10 -0800302
Jiri Slaby70265d22009-03-26 09:34:06 +0100303static irqreturn_t interrupt_pcmmio(int irq, void *d);
Bill Pemberton34c43922009-03-16 22:05:14 -0400304static void pcmmio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
Bill Pemberton814900c2009-04-23 15:54:54 -0400305static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
306static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
307static int pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
308 struct comedi_cmd *cmd);
Calin Culianu6baef152009-02-19 09:13:10 -0800309
310/* some helper functions to deal with specifics of this device's registers */
Bill Pemberton814900c2009-04-23 15:54:54 -0400311static void init_asics(struct comedi_device *dev); /* sets up/clears ASIC chips to defaults */
312static void switch_page(struct comedi_device *dev, int asic, int page);
Calin Culianu6baef152009-02-19 09:13:10 -0800313#ifdef notused
Bill Pemberton814900c2009-04-23 15:54:54 -0400314static void lock_port(struct comedi_device *dev, int asic, int port);
315static void unlock_port(struct comedi_device *dev, int asic, int port);
Calin Culianu6baef152009-02-19 09:13:10 -0800316#endif
317
318/*
319 * Attach is called by the Comedi core to configure the driver
320 * for a particular board. If you specified a board_name array
321 * in the driver structure, dev->board_ptr contains that
322 * address.
323 */
Bill Pembertonda91b262009-04-09 16:07:03 -0400324static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
Calin Culianu6baef152009-02-19 09:13:10 -0800325{
Bill Pemberton34c43922009-03-16 22:05:14 -0400326 struct comedi_subdevice *s;
Calin Culianu6baef152009-02-19 09:13:10 -0800327 int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
328 thisasic_chanct = 0;
329 unsigned long iobase;
330 unsigned int irq[MAX_ASICS];
331
332 iobase = it->options[0];
333 irq[0] = it->options[1];
334
335 printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
336 iobase);
337
338 dev->iobase = iobase;
339
340 if (!iobase || !request_region(iobase,
341 thisboard->total_iosize, driver.driver_name)) {
342 printk("I/O port conflict\n");
343 return -EIO;
344 }
345
346/*
347 * Initialize dev->board_name. Note that we can use the "thisboard"
348 * macro now, since we just initialized it in the last line.
349 */
350 dev->board_name = thisboard->name;
351
352/*
353 * Allocate the private structure area. alloc_private() is a
354 * convenient macro defined in comedidev.h.
355 */
Bill Pembertone56ab712009-03-16 22:20:30 -0400356 if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
Calin Culianu6baef152009-02-19 09:13:10 -0800357 printk("cannot allocate private data structure\n");
358 return -ENOMEM;
359 }
360
361 for (asic = 0; asic < MAX_ASICS; ++asic) {
362 devpriv->asics[asic].num = asic;
363 devpriv->asics[asic].iobase =
364 dev->iobase + 16 + asic * ASIC_IOSIZE;
365 devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
366 this function when we
367 comedi_request_irqs */
368 spin_lock_init(&devpriv->asics[asic].spinlock);
369 }
370
371 chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
372 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
373 n_subdevs = n_dio_subdevs + 2;
374 devpriv->sprivs =
Bill Pemberton4467df92009-03-16 22:19:37 -0400375 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private), GFP_KERNEL);
Calin Culianu6baef152009-02-19 09:13:10 -0800376 if (!devpriv->sprivs) {
377 printk("cannot allocate subdevice private data structures\n");
378 return -ENOMEM;
379 }
380 /*
381 * Allocate the subdevice structures. alloc_subdevice() is a
382 * convenient macro defined in comedidev.h.
383 *
384 * Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
385 */
386 if (alloc_subdevices(dev, n_subdevs) < 0) {
387 printk("cannot allocate subdevice data structures\n");
388 return -ENOMEM;
389 }
390
391 /* First, AI */
392 sdev_no = 0;
393 s = dev->subdevices + sdev_no;
394 s->private = devpriv->sprivs + sdev_no;
395 s->maxdata = (1 << thisboard->ai_bits) - 1;
396 s->range_table = thisboard->ai_range_table;
397 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
398 s->type = COMEDI_SUBD_AI;
399 s->n_chan = thisboard->n_ai_chans;
400 s->len_chanlist = s->n_chan;
401 s->insn_read = thisboard->ai_rinsn;
402 subpriv->iobase = dev->iobase + 0;
403 /* initialize the resource enable register by clearing it */
404 outb(0, subpriv->iobase + 3);
405 outb(0, subpriv->iobase + 4 + 3);
406
407 /* Next, AO */
408 ++sdev_no;
409 s = dev->subdevices + sdev_no;
410 s->private = devpriv->sprivs + sdev_no;
411 s->maxdata = (1 << thisboard->ao_bits) - 1;
412 s->range_table = thisboard->ao_range_table;
413 s->subdev_flags = SDF_READABLE;
414 s->type = COMEDI_SUBD_AO;
415 s->n_chan = thisboard->n_ao_chans;
416 s->len_chanlist = s->n_chan;
417 s->insn_read = thisboard->ao_rinsn;
418 s->insn_write = thisboard->ao_winsn;
419 subpriv->iobase = dev->iobase + 8;
420 /* initialize the resource enable register by clearing it */
421 outb(0, subpriv->iobase + 3);
422 outb(0, subpriv->iobase + 4 + 3);
423
424 ++sdev_no;
425 port = 0;
426 asic = 0;
427 for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
428 int byte_no;
429
430 s = dev->subdevices + sdev_no;
431 s->private = devpriv->sprivs + sdev_no;
432 s->maxdata = 1;
433 s->range_table = &range_digital;
434 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
435 s->type = COMEDI_SUBD_DIO;
436 s->insn_bits = pcmmio_dio_insn_bits;
437 s->insn_config = pcmmio_dio_insn_config;
438 s->n_chan = MIN(chans_left, MAX_CHANS_PER_SUBDEV);
439 subpriv->dio.intr.asic = -1;
440 subpriv->dio.intr.first_chan = -1;
441 subpriv->dio.intr.asic_chan = -1;
442 subpriv->dio.intr.num_asic_chans = -1;
443 subpriv->dio.intr.active = 0;
444 s->len_chanlist = 1;
445
446 /* save the ioport address for each 'port' of 8 channels in the
447 subdevice */
448 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
449 if (port >= PORTS_PER_ASIC) {
450 port = 0;
451 ++asic;
452 thisasic_chanct = 0;
453 }
454 subpriv->iobases[byte_no] =
455 devpriv->asics[asic].iobase + port;
456
457 if (thisasic_chanct <
458 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
459 && subpriv->dio.intr.asic < 0) {
460 /* this is an interrupt subdevice, so setup the struct */
461 subpriv->dio.intr.asic = asic;
462 subpriv->dio.intr.active = 0;
463 subpriv->dio.intr.stop_count = 0;
464 subpriv->dio.intr.first_chan = byte_no * 8;
465 subpriv->dio.intr.asic_chan = thisasic_chanct;
466 subpriv->dio.intr.num_asic_chans =
467 s->n_chan -
468 subpriv->dio.intr.first_chan;
469 s->cancel = pcmmio_cancel;
470 s->do_cmd = pcmmio_cmd;
471 s->do_cmdtest = pcmmio_cmdtest;
472 s->len_chanlist =
473 subpriv->dio.intr.num_asic_chans;
474 }
475 thisasic_chanct += CHANS_PER_PORT;
476 }
477 spin_lock_init(&subpriv->dio.intr.spinlock);
478
479 chans_left -= s->n_chan;
480
481 if (!chans_left) {
482 asic = 0; /* reset the asic to our first asic, to do intr subdevs */
483 port = 0;
484 }
485
486 }
487
488 init_asics(dev); /* clear out all the registers, basically */
489
490 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
491 if (irq[asic]
492 && comedi_request_irq(irq[asic], interrupt_pcmmio,
493 IRQF_SHARED, thisboard->name, dev)) {
494 int i;
495 /* unroll the allocated irqs.. */
496 for (i = asic - 1; i >= 0; --i) {
497 comedi_free_irq(irq[i], dev);
498 devpriv->asics[i].irq = irq[i] = 0;
499 }
500 irq[asic] = 0;
501 }
502 devpriv->asics[asic].irq = irq[asic];
503 }
504
505 dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple
506 irqs.. */
507
508 if (irq[0]) {
509 printk("irq: %u ", irq[0]);
510 if (irq[1] && thisboard->dio_num_asics == 2)
511 printk("second ASIC irq: %u ", irq[1]);
512 } else {
513 printk("(IRQ mode disabled) ");
514 }
515
516 printk("attached\n");
517
518 return 1;
519}
520
521/*
522 * _detach is called to deconfigure a device. It should deallocate
523 * resources.
524 * This function is also called when _attach() fails, so it should be
525 * careful not to release resources that were not necessarily
526 * allocated by _attach(). dev->private and dev->subdevices are
527 * deallocated automatically by the core.
528 */
Bill Pembertonda91b262009-04-09 16:07:03 -0400529static int pcmmio_detach(struct comedi_device *dev)
Calin Culianu6baef152009-02-19 09:13:10 -0800530{
531 int i;
532
533 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
534 if (dev->iobase)
535 release_region(dev->iobase, thisboard->total_iosize);
536
537 for (i = 0; i < MAX_ASICS; ++i) {
538 if (devpriv && devpriv->asics[i].irq)
539 comedi_free_irq(devpriv->asics[i].irq, dev);
540 }
541
542 if (devpriv && devpriv->sprivs)
543 kfree(devpriv->sprivs);
544
545 return 0;
546}
547
548/* DIO devices are slightly special. Although it is possible to
549 * implement the insn_read/insn_write interface, it is much more
550 * useful to applications if you implement the insn_bits interface.
551 * This allows packed reading/writing of the DIO channels. The
552 * comedi core can convert between insn_bits and insn_read/write */
Bill Pembertonda91b262009-04-09 16:07:03 -0400553static int pcmmio_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
554 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -0800555{
556 int byte_no;
557 if (insn->n != 2)
558 return -EINVAL;
559
560 /* NOTE:
561 reading a 0 means this channel was high
562 writine a 0 sets the channel high
563 reading a 1 means this channel was low
564 writing a 1 means set this channel low
565
566 Therefore everything is always inverted. */
567
568 /* The insn data is a mask in data[0] and the new data
569 * in data[1], each channel cooresponding to a bit. */
570
571#ifdef DAMMIT_ITS_BROKEN
572 /* DEBUG */
573 printk("write mask: %08x data: %08x\n", data[0], data[1]);
574#endif
575
576 s->state = 0;
577
578 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
579 /* address of 8-bit port */
580 unsigned long ioaddr = subpriv->iobases[byte_no],
581 /* bit offset of port in 32-bit doubleword */
582 offset = byte_no * 8;
583 /* this 8-bit port's data */
584 unsigned char byte = 0,
585 /* The write mask for this port (if any) */
586 write_mask_byte = (data[0] >> offset) & 0xff,
587 /* The data byte for this port */
588 data_byte = (data[1] >> offset) & 0xff;
589
590 byte = inb(ioaddr); /* read all 8-bits for this port */
591
592#ifdef DAMMIT_ITS_BROKEN
593 /* DEBUG */
594 printk("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ", byte_no, (unsigned)write_mask_byte, (unsigned)data_byte, offset, ioaddr, (unsigned)byte);
595#endif
596
597 if (write_mask_byte) {
598 /* this byte has some write_bits -- so set the output lines */
599 byte &= ~write_mask_byte; /* clear bits for write mask */
600 byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
601 /* Write out the new digital output state */
602 outb(byte, ioaddr);
603 }
604#ifdef DAMMIT_ITS_BROKEN
605 /* DEBUG */
606 printk("data_out_byte %02x\n", (unsigned)byte);
607#endif
608 /* save the digital input lines for this byte.. */
609 s->state |= ((unsigned int)byte) << offset;
610 }
611
612 /* now return the DIO lines to data[1] - note they came inverted! */
613 data[1] = ~s->state;
614
615#ifdef DAMMIT_ITS_BROKEN
616 /* DEBUG */
617 printk("s->state %08x data_out %08x\n", s->state, data[1]);
618#endif
619
620 return 2;
621}
622
623/* The input or output configuration of each digital line is
624 * configured by a special insn_config instruction. chanspec
625 * contains the channel to be changed, and data[0] contains the
626 * value COMEDI_INPUT or COMEDI_OUTPUT. */
Bill Pembertonda91b262009-04-09 16:07:03 -0400627static int pcmmio_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
628 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -0800629{
630 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
631 chan % 8;
632 unsigned long ioaddr;
633 unsigned char byte;
634
635 /* Compute ioaddr for this channel */
636 ioaddr = subpriv->iobases[byte_no];
637
638 /* NOTE:
639 writing a 0 an IO channel's bit sets the channel to INPUT
640 and pulls the line high as well
641
642 writing a 1 to an IO channel's bit pulls the line low
643
644 All channels are implicitly always in OUTPUT mode -- but when
645 they are high they can be considered to be in INPUT mode..
646
647 Thus, we only force channels low if the config request was INPUT,
648 otherwise we do nothing to the hardware. */
649
650 switch (data[0]) {
651 case INSN_CONFIG_DIO_OUTPUT:
652 /* save to io_bits -- don't actually do anything since
653 all input channels are also output channels... */
654 s->io_bits |= 1 << chan;
655 break;
656 case INSN_CONFIG_DIO_INPUT:
657 /* write a 0 to the actual register representing the channel
658 to set it to 'input'. 0 means "float high". */
659 byte = inb(ioaddr);
660 byte &= ~(1 << bit_no);
661 /**< set input channel to '0' */
662
663 /* write out byte -- this is the only time we actually affect the
664 hardware as all channels are implicitly output -- but input
665 channels are set to float-high */
666 outb(byte, ioaddr);
667
668 /* save to io_bits */
669 s->io_bits &= ~(1 << chan);
670 break;
671
672 case INSN_CONFIG_DIO_QUERY:
673 /* retreive from shadow register */
674 data[1] =
675 (s->
676 io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
677 return insn->n;
678 break;
679
680 default:
681 return -EINVAL;
682 break;
683 }
684
685 return insn->n;
686}
687
Bill Pembertonda91b262009-04-09 16:07:03 -0400688static void init_asics(struct comedi_device *dev)
Calin Culianu6baef152009-02-19 09:13:10 -0800689{ /* sets up an
690 ASIC chip to defaults */
691 int asic;
692
693 for (asic = 0; asic < thisboard->dio_num_asics; ++asic) {
694 int port, page;
695 unsigned long baseaddr = devpriv->asics[asic].iobase;
696
697 switch_page(dev, asic, 0); /* switch back to page 0 */
698
699 /* first, clear all the DIO port bits */
700 for (port = 0; port < PORTS_PER_ASIC; ++port)
701 outb(0, baseaddr + REG_PORT0 + port);
702
703 /* Next, clear all the paged registers for each page */
704 for (page = 1; page < NUM_PAGES; ++page) {
705 int reg;
706 /* now clear all the paged registers */
707 switch_page(dev, asic, page);
708 for (reg = FIRST_PAGED_REG;
709 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
710 outb(0, baseaddr + reg);
711 }
712
713 /* DEBUG set rising edge interrupts on port0 of both asics */
714 /*switch_page(dev, asic, PAGE_POL);
715 outb(0xff, baseaddr + REG_POL0);
716 switch_page(dev, asic, PAGE_ENAB);
717 outb(0xff, baseaddr + REG_ENAB0); */
718 /* END DEBUG */
719
720 switch_page(dev, asic, 0); /* switch back to default page 0 */
721
722 }
723}
724
Bill Pembertonda91b262009-04-09 16:07:03 -0400725static void switch_page(struct comedi_device *dev, int asic, int page)
Calin Culianu6baef152009-02-19 09:13:10 -0800726{
727 if (asic < 0 || asic >= thisboard->dio_num_asics)
728 return; /* paranoia */
729 if (page < 0 || page >= NUM_PAGES)
730 return; /* more paranoia */
731
732 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
733 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
734
735 /* now write out the shadow register */
736 outb(devpriv->asics[asic].pagelock,
737 devpriv->asics[asic].iobase + REG_PAGELOCK);
738}
739
740#ifdef notused
Bill Pembertonda91b262009-04-09 16:07:03 -0400741static void lock_port(struct comedi_device *dev, int asic, int port)
Calin Culianu6baef152009-02-19 09:13:10 -0800742{
743 if (asic < 0 || asic >= thisboard->dio_num_asics)
744 return; /* paranoia */
745 if (port < 0 || port >= PORTS_PER_ASIC)
746 return; /* more paranoia */
747
748 devpriv->asics[asic].pagelock |= 0x1 << port;
749 /* now write out the shadow register */
750 outb(devpriv->asics[asic].pagelock,
751 devpriv->asics[asic].iobase + REG_PAGELOCK);
752 return;
753}
754
Bill Pembertonda91b262009-04-09 16:07:03 -0400755static void unlock_port(struct comedi_device *dev, int asic, int port)
Calin Culianu6baef152009-02-19 09:13:10 -0800756{
757 if (asic < 0 || asic >= thisboard->dio_num_asics)
758 return; /* paranoia */
759 if (port < 0 || port >= PORTS_PER_ASIC)
760 return; /* more paranoia */
761 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
762 /* now write out the shadow register */
763 outb(devpriv->asics[asic].pagelock,
764 devpriv->asics[asic].iobase + REG_PAGELOCK);
765}
766#endif /* notused */
767
Jiri Slaby70265d22009-03-26 09:34:06 +0100768static irqreturn_t interrupt_pcmmio(int irq, void *d)
Calin Culianu6baef152009-02-19 09:13:10 -0800769{
770 int asic, got1 = 0;
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400771 struct comedi_device *dev = (struct comedi_device *) d;
Calin Culianu6baef152009-02-19 09:13:10 -0800772
773 for (asic = 0; asic < MAX_ASICS; ++asic) {
774 if (irq == devpriv->asics[asic].irq) {
775 unsigned long flags;
776 unsigned triggered = 0;
777 unsigned long iobase = devpriv->asics[asic].iobase;
778 /* it is an interrupt for ASIC #asic */
779 unsigned char int_pend;
780
781 comedi_spin_lock_irqsave(&devpriv->asics[asic].spinlock,
782 flags);
783
784 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
785
786 if (int_pend) {
787 int port;
788 for (port = 0; port < INTR_PORTS_PER_ASIC;
789 ++port) {
790 if (int_pend & (0x1 << port)) {
791 unsigned char
792 io_lines_with_edges = 0;
793 switch_page(dev, asic,
794 PAGE_INT_ID);
795 io_lines_with_edges =
796 inb(iobase +
797 REG_INT_ID0 + port);
798
799 if (io_lines_with_edges)
800 /* clear pending interrupt */
801 outb(0, iobase +
802 REG_INT_ID0 +
803 port);
804
805 triggered |=
806 io_lines_with_edges <<
807 port * 8;
808 }
809 }
810
811 ++got1;
812 }
813
814 comedi_spin_unlock_irqrestore(&devpriv->asics[asic].
815 spinlock, flags);
816
817 if (triggered) {
Bill Pemberton34c43922009-03-16 22:05:14 -0400818 struct comedi_subdevice *s;
Calin Culianu6baef152009-02-19 09:13:10 -0800819 /* TODO here: dispatch io lines to subdevs with commands.. */
820 printk("PCMMIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n", irq, asic, triggered);
821 for (s = dev->subdevices + 2;
822 s < dev->subdevices + dev->n_subdevices;
823 ++s) {
824 if (subpriv->dio.intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
825 unsigned long flags;
826 unsigned oldevents;
827
828 comedi_spin_lock_irqsave
829 (&subpriv->dio.intr.
830 spinlock, flags);
831
832 oldevents = s->async->events;
833
834 if (subpriv->dio.intr.active) {
835 unsigned mytrig =
836 ((triggered >>
837 subpriv->
838 dio.
839 intr.
840 asic_chan)
841 & ((0x1 << subpriv->dio.intr.num_asic_chans) - 1)) << subpriv->dio.intr.first_chan;
842 if (mytrig & subpriv->
843 dio.intr.
844 enabled_mask) {
Bill Pemberton790c5542009-03-16 22:05:02 -0400845 unsigned int val =
Calin Culianu6baef152009-02-19 09:13:10 -0800846 0;
847 unsigned int n,
848 ch, len;
849
850 len = s->async->
851 cmd.
852 chanlist_len;
853 for (n = 0;
854 n < len;
855 n++) {
856 ch = CR_CHAN(s->async->cmd.chanlist[n]);
857 if (mytrig & (1U << ch)) {
858 val |= (1U << n);
859 }
860 }
861 /* Write the scan to the buffer. */
Bill Pemberton9b9bcba2009-04-23 15:54:53 -0400862 if (comedi_buf_put(s->async, ((short *) &val)[0])
Calin Culianu6baef152009-02-19 09:13:10 -0800863 &&
864 comedi_buf_put
Bill Pemberton9b9bcba2009-04-23 15:54:53 -0400865 (s->async, ((short *) &val)[1])) {
Calin Culianu6baef152009-02-19 09:13:10 -0800866 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
867 } else {
868 /* Overflow! Stop acquisition!! */
869 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
870 pcmmio_stop_intr
871 (dev,
872 s);
873 }
874
875 /* Check for end of acquisition. */
876 if (!subpriv->
877 dio.
878 intr.
879 continuous)
880 {
881 /* stop_src == TRIG_COUNT */
882 if (subpriv->dio.intr.stop_count > 0) {
883 subpriv->
884 dio.
885 intr.
886 stop_count--;
887 if (subpriv->dio.intr.stop_count == 0) {
888 s->async->events |= COMEDI_CB_EOA;
889 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
890 pcmmio_stop_intr
891 (dev,
892 s);
893 }
894 }
895 }
896 }
897 }
898
899 comedi_spin_unlock_irqrestore
900 (&subpriv->dio.intr.
901 spinlock, flags);
902
903 if (oldevents !=
904 s->async->events) {
905 comedi_event(dev, s);
906 }
907
908 }
909
910 }
911 }
912
913 }
914 }
915 if (!got1)
916 return IRQ_NONE; /* interrupt from other source */
917 return IRQ_HANDLED;
918}
919
Bill Pembertonda91b262009-04-09 16:07:03 -0400920static void pcmmio_stop_intr(struct comedi_device *dev, struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -0800921{
922 int nports, firstport, asic, port;
923
Bill Pembertonc3744132009-04-22 21:11:47 -0400924 asic = subpriv->dio.intr.asic;
925 if (asic < 0)
Calin Culianu6baef152009-02-19 09:13:10 -0800926 return; /* not an interrupt subdev */
927
928 subpriv->dio.intr.enabled_mask = 0;
929 subpriv->dio.intr.active = 0;
930 s->async->inttrig = 0;
931 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
932 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
933 switch_page(dev, asic, PAGE_ENAB);
934 for (port = firstport; port < firstport + nports; ++port) {
935 /* disable all intrs for this subdev.. */
936 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
937 }
938}
939
Bill Pembertonda91b262009-04-09 16:07:03 -0400940static int pcmmio_start_intr(struct comedi_device *dev, struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -0800941{
942 if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
943 /* An empty acquisition! */
944 s->async->events |= COMEDI_CB_EOA;
945 subpriv->dio.intr.active = 0;
946 return 1;
947 } else {
948 unsigned bits = 0, pol_bits = 0, n;
949 int nports, firstport, asic, port;
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400950 struct comedi_cmd *cmd = &s->async->cmd;
Calin Culianu6baef152009-02-19 09:13:10 -0800951
Bill Pembertonc3744132009-04-22 21:11:47 -0400952 asic = subpriv->dio.intr.asic;
953 if (asic < 0)
Calin Culianu6baef152009-02-19 09:13:10 -0800954 return 1; /* not an interrupt
955 subdev */
956 subpriv->dio.intr.enabled_mask = 0;
957 subpriv->dio.intr.active = 1;
958 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
959 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
960 if (cmd->chanlist) {
961 for (n = 0; n < cmd->chanlist_len; n++) {
962 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
963 pol_bits |= (CR_AREF(cmd->chanlist[n])
964 || CR_RANGE(cmd->chanlist[n]) ? 1U : 0U)
965 << CR_CHAN(cmd->chanlist[n]);
966 }
967 }
968 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
969 1) << subpriv->dio.intr.first_chan;
970 subpriv->dio.intr.enabled_mask = bits;
971
972 { /* the below code configures the board to use a specific IRQ from 0-15. */
973 unsigned char b;
974 /* set resource enable register to enable IRQ operation */
975 outb(1 << 4, dev->iobase + 3);
976 /* set bits 0-3 of b to the irq number from 0-15 */
977 b = dev->irq & ((1 << 4) - 1);
978 outb(b, dev->iobase + 2);
979 /* done, we told the board what irq to use */
980 }
981
982 switch_page(dev, asic, PAGE_ENAB);
983 for (port = firstport; port < firstport + nports; ++port) {
984 unsigned enab =
985 bits >> (subpriv->dio.intr.first_chan + (port -
986 firstport) * 8) & 0xff, pol =
987 pol_bits >> (subpriv->dio.intr.first_chan +
988 (port - firstport) * 8) & 0xff;
989 /* set enab intrs for this subdev.. */
990 outb(enab,
991 devpriv->asics[asic].iobase + REG_ENAB0 + port);
992 switch_page(dev, asic, PAGE_POL);
993 outb(pol,
994 devpriv->asics[asic].iobase + REG_ENAB0 + port);
995 }
996 }
997 return 0;
998}
999
Bill Pembertonda91b262009-04-09 16:07:03 -04001000static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -08001001{
1002 unsigned long flags;
1003
1004 comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1005 if (subpriv->dio.intr.active)
1006 pcmmio_stop_intr(dev, s);
1007 comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1008
1009 return 0;
1010}
1011
1012/*
1013 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
1014 */
1015static int
Bill Pembertonda91b262009-04-09 16:07:03 -04001016pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
Calin Culianu6baef152009-02-19 09:13:10 -08001017 unsigned int trignum)
1018{
1019 unsigned long flags;
1020 int event = 0;
1021
1022 if (trignum != 0)
1023 return -EINVAL;
1024
1025 comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1026 s->async->inttrig = 0;
1027 if (subpriv->dio.intr.active) {
1028 event = pcmmio_start_intr(dev, s);
1029 }
1030 comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1031
1032 if (event) {
1033 comedi_event(dev, s);
1034 }
1035
1036 return 1;
1037}
1038
1039/*
1040 * 'do_cmd' function for an 'INTERRUPT' subdevice.
1041 */
Bill Pembertonda91b262009-04-09 16:07:03 -04001042static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -08001043{
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001044 struct comedi_cmd *cmd = &s->async->cmd;
Calin Culianu6baef152009-02-19 09:13:10 -08001045 unsigned long flags;
1046 int event = 0;
1047
1048 comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1049 subpriv->dio.intr.active = 1;
1050
1051 /* Set up end of acquisition. */
1052 switch (cmd->stop_src) {
1053 case TRIG_COUNT:
1054 subpriv->dio.intr.continuous = 0;
1055 subpriv->dio.intr.stop_count = cmd->stop_arg;
1056 break;
1057 default:
1058 /* TRIG_NONE */
1059 subpriv->dio.intr.continuous = 1;
1060 subpriv->dio.intr.stop_count = 0;
1061 break;
1062 }
1063
1064 /* Set up start of acquisition. */
1065 switch (cmd->start_src) {
1066 case TRIG_INT:
1067 s->async->inttrig = pcmmio_inttrig_start_intr;
1068 break;
1069 default:
1070 /* TRIG_NOW */
1071 event = pcmmio_start_intr(dev, s);
1072 break;
1073 }
1074 comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1075
1076 if (event) {
1077 comedi_event(dev, s);
1078 }
1079
1080 return 0;
1081}
1082
1083/*
1084 * 'do_cmdtest' function for an 'INTERRUPT' subdevice.
1085 */
1086static int
Bill Pembertonda91b262009-04-09 16:07:03 -04001087pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd)
Calin Culianu6baef152009-02-19 09:13:10 -08001088{
1089 int err = 0;
1090 unsigned int tmp;
1091
1092 /* step 1: make sure trigger sources are trivially valid */
1093
1094 tmp = cmd->start_src;
1095 cmd->start_src &= (TRIG_NOW | TRIG_INT);
1096 if (!cmd->start_src || tmp != cmd->start_src)
1097 err++;
1098
1099 tmp = cmd->scan_begin_src;
1100 cmd->scan_begin_src &= TRIG_EXT;
1101 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
1102 err++;
1103
1104 tmp = cmd->convert_src;
1105 cmd->convert_src &= TRIG_NOW;
1106 if (!cmd->convert_src || tmp != cmd->convert_src)
1107 err++;
1108
1109 tmp = cmd->scan_end_src;
1110 cmd->scan_end_src &= TRIG_COUNT;
1111 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
1112 err++;
1113
1114 tmp = cmd->stop_src;
1115 cmd->stop_src &= (TRIG_COUNT | TRIG_NONE);
1116 if (!cmd->stop_src || tmp != cmd->stop_src)
1117 err++;
1118
1119 if (err)
1120 return 1;
1121
1122 /* step 2: make sure trigger sources are unique and mutually compatible */
1123
1124 /* these tests are true if more than one _src bit is set */
1125 if ((cmd->start_src & (cmd->start_src - 1)) != 0)
1126 err++;
1127 if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
1128 err++;
1129 if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
1130 err++;
1131 if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
1132 err++;
1133 if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
1134 err++;
1135
1136 if (err)
1137 return 2;
1138
1139 /* step 3: make sure arguments are trivially compatible */
1140
1141 /* cmd->start_src == TRIG_NOW || cmd->start_src == TRIG_INT */
1142 if (cmd->start_arg != 0) {
1143 cmd->start_arg = 0;
1144 err++;
1145 }
1146
1147 /* cmd->scan_begin_src == TRIG_EXT */
1148 if (cmd->scan_begin_arg != 0) {
1149 cmd->scan_begin_arg = 0;
1150 err++;
1151 }
1152
1153 /* cmd->convert_src == TRIG_NOW */
1154 if (cmd->convert_arg != 0) {
1155 cmd->convert_arg = 0;
1156 err++;
1157 }
1158
1159 /* cmd->scan_end_src == TRIG_COUNT */
1160 if (cmd->scan_end_arg != cmd->chanlist_len) {
1161 cmd->scan_end_arg = cmd->chanlist_len;
1162 err++;
1163 }
1164
1165 switch (cmd->stop_src) {
1166 case TRIG_COUNT:
1167 /* any count allowed */
1168 break;
1169 case TRIG_NONE:
1170 if (cmd->stop_arg != 0) {
1171 cmd->stop_arg = 0;
1172 err++;
1173 }
1174 break;
1175 default:
1176 break;
1177 }
1178
1179 if (err)
1180 return 3;
1181
1182 /* step 4: fix up any arguments */
1183
1184 /* if (err) return 4; */
1185
1186 return 0;
1187}
1188
1189static int adc_wait_ready(unsigned long iobase)
1190{
1191 unsigned long retry = 100000;
1192 while (retry--)
1193 if (inb(iobase + 3) & 0x80)
1194 return 0;
1195 return 1;
1196}
1197
1198/* All this is for AI and AO */
Bill Pembertonda91b262009-04-09 16:07:03 -04001199static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
1200 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001201{
1202 int n;
1203 unsigned long iobase = subpriv->iobase;
1204
1205 /*
1206 1. write the CMD byte (to BASE+2)
1207 2. read junk lo byte (BASE+0)
1208 3. read junk hi byte (BASE+1)
1209 4. (mux settled so) write CMD byte again (BASE+2)
1210 5. read valid lo byte(BASE+0)
1211 6. read valid hi byte(BASE+1)
1212
1213 Additionally note that the BASE += 4 if the channel >= 8
1214 */
1215
1216 /* convert n samples */
1217 for (n = 0; n < insn->n; n++) {
1218 unsigned chan = CR_CHAN(insn->chanspec), range =
1219 CR_RANGE(insn->chanspec), aref =
1220 CR_AREF(insn->chanspec);
1221 unsigned char command_byte = 0;
1222 unsigned iooffset = 0;
Bill Pemberton790c5542009-03-16 22:05:02 -04001223 short sample, adc_adjust = 0;
Calin Culianu6baef152009-02-19 09:13:10 -08001224
1225 if (chan > 7)
1226 chan -= 8, iooffset = 4; /* use the second dword for channels > 7 */
1227
1228 if (aref != AREF_DIFF) {
1229 aref = AREF_GROUND;
1230 command_byte |= 1 << 7; /* set bit 7 to indicate single-ended */
1231 }
1232 if (range < 2)
1233 adc_adjust = 0x8000; /* bipolar ranges (-5,5 .. -10,10 need to be adjusted -- that is.. they need to wrap around by adding 0x8000 */
1234
1235 if (chan % 2) {
1236 command_byte |= 1 << 6; /* odd-numbered channels have bit 6 set */
1237 }
1238
1239 /* select the channel, bits 4-5 == chan/2 */
1240 command_byte |= ((chan / 2) & 0x3) << 4;
1241
1242 /* set the range, bits 2-3 */
1243 command_byte |= (range & 0x3) << 2;
1244
1245 /* need to do this twice to make sure mux settled */
1246 outb(command_byte, iobase + iooffset + 2); /* chan/range/aref select */
1247
1248 adc_wait_ready(iobase + iooffset); /* wait for the adc to say it finised the conversion */
1249
1250 outb(command_byte, iobase + iooffset + 2); /* select the chan/range/aref AGAIN */
1251
1252 adc_wait_ready(iobase + iooffset);
1253
1254 sample = inb(iobase + iooffset + 0); /* read data lo byte */
1255 sample |= inb(iobase + iooffset + 1) << 8; /* read data hi byte */
1256 sample += adc_adjust; /* adjustment .. munge data */
1257 data[n] = sample;
1258 }
1259 /* return the number of samples read/written */
1260 return n;
1261}
1262
Bill Pembertonda91b262009-04-09 16:07:03 -04001263static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
1264 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001265{
1266 int n;
1267 for (n = 0; n < insn->n; n++) {
1268 unsigned chan = CR_CHAN(insn->chanspec);
1269 if (chan < s->n_chan)
1270 data[n] = subpriv->ao.shadow_samples[chan];
1271 }
1272 return n;
1273}
1274
1275static int wait_dac_ready(unsigned long iobase)
1276{
1277 unsigned long retry = 100000L;
1278
1279 /* This may seem like an absurd way to handle waiting and violates the
1280 "no busy waiting" policy. The fact is that the hardware is
1281 normally so fast that we usually only need one time through the loop
1282 anyway. The longer timeout is for rare occasions and for detecting
1283 non-existant hardware. */
1284
1285 while (retry--) {
1286 if (inb(iobase + 3) & 0x80)
1287 return 0;
1288
1289 }
1290 return 1;
1291}
1292
Bill Pembertonda91b262009-04-09 16:07:03 -04001293static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
1294 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001295{
1296 int n;
1297 unsigned iobase = subpriv->iobase, iooffset = 0;
1298
1299 for (n = 0; n < insn->n; n++) {
1300 unsigned chan = CR_CHAN(insn->chanspec), range =
1301 CR_RANGE(insn->chanspec);
1302 if (chan < s->n_chan) {
1303 unsigned char command_byte = 0, range_byte =
1304 range & ((1 << 4) - 1);
1305 if (chan >= 4)
1306 chan -= 4, iooffset += 4;
1307 /* set the range.. */
1308 outb(range_byte, iobase + iooffset + 0);
1309 outb(0, iobase + iooffset + 1);
1310
1311 /* tell it to begin */
1312 command_byte = (chan << 1) | 0x60;
1313 outb(command_byte, iobase + iooffset + 2);
1314
1315 wait_dac_ready(iobase + iooffset);
1316
1317 outb(data[n] & 0xff, iobase + iooffset + 0); /* low order byte */
1318 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1); /* high order byte */
1319 command_byte = 0x70 | (chan << 1); /* set bit 4 of command byte to indicate data is loaded and trigger conversion */
1320 /* trigger converion */
1321 outb(command_byte, iobase + iooffset + 2);
1322
1323 wait_dac_ready(iobase + iooffset);
1324
1325 subpriv->ao.shadow_samples[chan] = data[n]; /* save to shadow register for ao_rinsn */
1326 }
1327 }
1328 return n;
1329}
1330
1331/*
1332 * A convenient macro that defines init_module() and cleanup_module(),
1333 * as necessary.
1334 */
1335COMEDI_INITCLEANUP(driver);