blob: d812c2c3af12f64a554fcecd562ab7fe8dca09c8 [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
Greg Kroah-Hartman25436dc2009-04-27 15:14:34 -070076#include <linux/interrupt.h>
Calin Culianu6baef152009-02-19 09:13:10 -080077#include "../comedidev.h"
Bill Pemberton0b8f7542009-05-14 15:24:29 -040078#include "pcm_common.h"
Calin Culianu6baef152009-02-19 09:13:10 -080079#include <linux/pci.h> /* for PCI devices */
80
Calin Culianu6baef152009-02-19 09:13:10 -080081/* 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
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530142typedef int (*comedi_insn_fn_t) (struct comedi_device *,
143 struct comedi_subdevice *,
144 struct comedi_insn *, unsigned int *);
Calin Culianu6baef152009-02-19 09:13:10 -0800145
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530146static int ai_rinsn(struct comedi_device *, struct comedi_subdevice *,
147 struct comedi_insn *, unsigned int *);
148static int ao_rinsn(struct comedi_device *, struct comedi_subdevice *,
149 struct comedi_insn *, unsigned int *);
150static int ao_winsn(struct comedi_device *, struct comedi_subdevice *,
151 struct comedi_insn *, unsigned int *);
Calin Culianu6baef152009-02-19 09:13:10 -0800152
153/*
154 * Board descriptions for two imaginary boards. Describing the
155 * boards in this way is optional, and completely driver-dependent.
156 * Some drivers use arrays such as this, other do not.
157 */
Bill Pemberton657f81e2009-03-16 22:19:04 -0400158struct pcmmio_board {
Calin Culianu6baef152009-02-19 09:13:10 -0800159 const char *name;
160 const int dio_num_asics;
161 const int dio_num_ports;
162 const int total_iosize;
163 const int ai_bits;
164 const int ao_bits;
165 const int n_ai_chans;
166 const int n_ao_chans;
Bill Pemberton9ced1de2009-03-16 22:05:31 -0400167 const struct comedi_lrange *ai_range_table, *ao_range_table;
Calin Culianu6baef152009-02-19 09:13:10 -0800168 comedi_insn_fn_t ai_rinsn, ao_rinsn, ao_winsn;
Bill Pemberton657f81e2009-03-16 22:19:04 -0400169};
Calin Culianu6baef152009-02-19 09:13:10 -0800170
Bill Pemberton9ced1de2009-03-16 22:05:31 -0400171static const struct comedi_lrange ranges_ai =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530172 { 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0.,
173 10.)}
Calin Culianu6baef152009-02-19 09:13:10 -0800174};
175
Bill Pemberton9ced1de2009-03-16 22:05:31 -0400176static const struct comedi_lrange ranges_ao =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530177 { 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
178 RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
Calin Culianu6baef152009-02-19 09:13:10 -0800179};
180
Bill Pemberton657f81e2009-03-16 22:19:04 -0400181static const struct pcmmio_board pcmmio_boards[] = {
Calin Culianu6baef152009-02-19 09:13:10 -0800182 {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530183 .name = "pcmmio",
184 .dio_num_asics = 1,
185 .dio_num_ports = 6,
186 .total_iosize = 32,
187 .ai_bits = 16,
188 .ao_bits = 16,
189 .n_ai_chans = 16,
190 .n_ao_chans = 8,
191 .ai_range_table = &ranges_ai,
192 .ao_range_table = &ranges_ao,
193 .ai_rinsn = ai_rinsn,
194 .ao_rinsn = ao_rinsn,
195 .ao_winsn = ao_winsn},
Calin Culianu6baef152009-02-19 09:13:10 -0800196};
197
198/*
199 * Useful for shorthand access to the particular board structure
200 */
Bill Pemberton657f81e2009-03-16 22:19:04 -0400201#define thisboard ((const struct pcmmio_board *)dev->board_ptr)
Calin Culianu6baef152009-02-19 09:13:10 -0800202
203/* this structure is for data unique to this subdevice. */
Bill Pemberton4467df92009-03-16 22:19:37 -0400204struct pcmmio_subdev_private {
Calin Culianu6baef152009-02-19 09:13:10 -0800205
206 union {
207 /* for DIO: mapping of halfwords (bytes) in port/chanarray to iobase */
208 unsigned long iobases[PORTS_PER_SUBDEV];
209
210 /* for AI/AO */
211 unsigned long iobase;
212 };
213 union {
214 struct {
215
216 /* The below is only used for intr subdevices */
217 struct {
218 int asic; /* if non-negative, this subdev has an interrupt asic */
219 int first_chan; /* if nonnegative, the first channel id for
220 interrupts. */
221 int num_asic_chans; /* the number of asic channels in this subdev
222 that have interrutps */
223 int asic_chan; /* if nonnegative, the first channel id with
224 respect to the asic that has interrupts */
225 int enabled_mask; /* subdev-relative channel mask for channels
226 we are interested in */
227 int active;
228 int stop_count;
229 int continuous;
230 spinlock_t spinlock;
231 } intr;
232 } dio;
233 struct {
Bill Pemberton790c5542009-03-16 22:05:02 -0400234 unsigned int shadow_samples[8]; /* the last unsigned int data written */
Calin Culianu6baef152009-02-19 09:13:10 -0800235 } ao;
236 };
Bill Pemberton4467df92009-03-16 22:19:37 -0400237};
Calin Culianu6baef152009-02-19 09:13:10 -0800238
239/* this structure is for data unique to this hardware driver. If
240 several hardware drivers keep similar information in this structure,
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400241 feel free to suggest moving the variable to the struct comedi_device struct. */
Bill Pembertone56ab712009-03-16 22:20:30 -0400242struct pcmmio_private {
Calin Culianu6baef152009-02-19 09:13:10 -0800243 /* stuff for DIO */
244 struct {
245 unsigned char pagelock; /* current page and lock */
246 unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */
247 unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */
248 int num;
249 unsigned long iobase;
250 unsigned int irq;
251 spinlock_t spinlock;
252 } asics[MAX_ASICS];
Bill Pemberton4467df92009-03-16 22:19:37 -0400253 struct pcmmio_subdev_private *sprivs;
Bill Pembertone56ab712009-03-16 22:20:30 -0400254};
Calin Culianu6baef152009-02-19 09:13:10 -0800255
256/*
257 * most drivers define the following macro to make it easy to
258 * access the private structure.
259 */
Bill Pembertone56ab712009-03-16 22:20:30 -0400260#define devpriv ((struct pcmmio_private *)dev->private)
Bill Pemberton4467df92009-03-16 22:19:37 -0400261#define subpriv ((struct pcmmio_subdev_private *)s->private)
Calin Culianu6baef152009-02-19 09:13:10 -0800262/*
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400263 * The struct comedi_driver structure tells the Comedi core module
Calin Culianu6baef152009-02-19 09:13:10 -0800264 * which functions to call to configure/deconfigure (attach/detach)
265 * the board, and also about the kernel module that contains
266 * the device code.
267 */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530268static int pcmmio_attach(struct comedi_device *dev,
269 struct comedi_devconfig *it);
Bill Pembertonda91b262009-04-09 16:07:03 -0400270static int pcmmio_detach(struct comedi_device *dev);
Calin Culianu6baef152009-02-19 09:13:10 -0800271
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400272static struct comedi_driver driver = {
Bill Pemberton68c3dbf2009-04-22 21:11:49 -0400273 .driver_name = "pcmmio",
274 .module = THIS_MODULE,
275 .attach = pcmmio_attach,
276 .detach = pcmmio_detach,
Calin Culianu6baef152009-02-19 09:13:10 -0800277/* It is not necessary to implement the following members if you are
278 * writing a driver for a ISA PnP or PCI card */
279 /* Most drivers will support multiple types of boards by
280 * having an array of board structures. These were defined
281 * in pcmmio_boards[] above. Note that the element 'name'
282 * was first in the structure -- Comedi uses this fact to
283 * extract the name of the board without knowing any details
284 * about the structure except for its length.
285 * When a device is attached (by comedi_config), the name
286 * of the device is given to Comedi, and Comedi tries to
287 * match it by going through the list of board names. If
288 * there is a match, the address of the pointer is put
289 * into dev->board_ptr and driver->attach() is called.
290 *
291 * Note that these are not necessary if you can determine
292 * the type of board in software. ISA PnP, PCI, and PCMCIA
293 * devices are such boards.
294 */
Bill Pemberton68c3dbf2009-04-22 21:11:49 -0400295 .board_name = &pcmmio_boards[0].name,
296 .offset = sizeof(struct pcmmio_board),
Bill Pemberton8629efa2009-04-23 15:54:56 -0400297 .num_names = ARRAY_SIZE(pcmmio_boards),
Calin Culianu6baef152009-02-19 09:13:10 -0800298};
299
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530300static int pcmmio_dio_insn_bits(struct comedi_device *dev,
301 struct comedi_subdevice *s,
302 struct comedi_insn *insn, unsigned int *data);
303static int pcmmio_dio_insn_config(struct comedi_device *dev,
304 struct comedi_subdevice *s,
305 struct comedi_insn *insn, unsigned int *data);
Calin Culianu6baef152009-02-19 09:13:10 -0800306
Jiri Slaby70265d22009-03-26 09:34:06 +0100307static irqreturn_t interrupt_pcmmio(int irq, void *d);
Bill Pemberton34c43922009-03-16 22:05:14 -0400308static void pcmmio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
Bill Pemberton814900c2009-04-23 15:54:54 -0400309static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
310static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
311static int pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530312 struct comedi_cmd *cmd);
Calin Culianu6baef152009-02-19 09:13:10 -0800313
314/* some helper functions to deal with specifics of this device's registers */
Bill Pemberton814900c2009-04-23 15:54:54 -0400315static void init_asics(struct comedi_device *dev); /* sets up/clears ASIC chips to defaults */
316static void switch_page(struct comedi_device *dev, int asic, int page);
Calin Culianu6baef152009-02-19 09:13:10 -0800317#ifdef notused
Bill Pemberton814900c2009-04-23 15:54:54 -0400318static void lock_port(struct comedi_device *dev, int asic, int port);
319static void unlock_port(struct comedi_device *dev, int asic, int port);
Calin Culianu6baef152009-02-19 09:13:10 -0800320#endif
321
322/*
323 * Attach is called by the Comedi core to configure the driver
324 * for a particular board. If you specified a board_name array
325 * in the driver structure, dev->board_ptr contains that
326 * address.
327 */
Bill Pembertonda91b262009-04-09 16:07:03 -0400328static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
Calin Culianu6baef152009-02-19 09:13:10 -0800329{
Bill Pemberton34c43922009-03-16 22:05:14 -0400330 struct comedi_subdevice *s;
Calin Culianu6baef152009-02-19 09:13:10 -0800331 int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530332 thisasic_chanct = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800333 unsigned long iobase;
334 unsigned int irq[MAX_ASICS];
335
336 iobase = it->options[0];
337 irq[0] = it->options[1];
338
339 printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530340 iobase);
Calin Culianu6baef152009-02-19 09:13:10 -0800341
342 dev->iobase = iobase;
343
344 if (!iobase || !request_region(iobase,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530345 thisboard->total_iosize,
346 driver.driver_name)) {
Calin Culianu6baef152009-02-19 09:13:10 -0800347 printk("I/O port conflict\n");
348 return -EIO;
349 }
350
351/*
352 * Initialize dev->board_name. Note that we can use the "thisboard"
353 * macro now, since we just initialized it in the last line.
354 */
355 dev->board_name = thisboard->name;
356
357/*
358 * Allocate the private structure area. alloc_private() is a
359 * convenient macro defined in comedidev.h.
360 */
Bill Pembertone56ab712009-03-16 22:20:30 -0400361 if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
Calin Culianu6baef152009-02-19 09:13:10 -0800362 printk("cannot allocate private data structure\n");
363 return -ENOMEM;
364 }
365
366 for (asic = 0; asic < MAX_ASICS; ++asic) {
367 devpriv->asics[asic].num = asic;
368 devpriv->asics[asic].iobase =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530369 dev->iobase + 16 + asic * ASIC_IOSIZE;
Calin Culianu6baef152009-02-19 09:13:10 -0800370 devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
371 this function when we
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700372 request_irqs */
Calin Culianu6baef152009-02-19 09:13:10 -0800373 spin_lock_init(&devpriv->asics[asic].spinlock);
374 }
375
376 chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
377 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
378 n_subdevs = n_dio_subdevs + 2;
379 devpriv->sprivs =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530380 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
381 GFP_KERNEL);
Calin Culianu6baef152009-02-19 09:13:10 -0800382 if (!devpriv->sprivs) {
383 printk("cannot allocate subdevice private data structures\n");
384 return -ENOMEM;
385 }
386 /*
387 * Allocate the subdevice structures. alloc_subdevice() is a
388 * convenient macro defined in comedidev.h.
389 *
390 * Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
391 */
392 if (alloc_subdevices(dev, n_subdevs) < 0) {
393 printk("cannot allocate subdevice data structures\n");
394 return -ENOMEM;
395 }
396
397 /* First, AI */
398 sdev_no = 0;
399 s = dev->subdevices + sdev_no;
400 s->private = devpriv->sprivs + sdev_no;
401 s->maxdata = (1 << thisboard->ai_bits) - 1;
402 s->range_table = thisboard->ai_range_table;
403 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
404 s->type = COMEDI_SUBD_AI;
405 s->n_chan = thisboard->n_ai_chans;
406 s->len_chanlist = s->n_chan;
407 s->insn_read = thisboard->ai_rinsn;
408 subpriv->iobase = dev->iobase + 0;
409 /* initialize the resource enable register by clearing it */
410 outb(0, subpriv->iobase + 3);
411 outb(0, subpriv->iobase + 4 + 3);
412
413 /* Next, AO */
414 ++sdev_no;
415 s = dev->subdevices + sdev_no;
416 s->private = devpriv->sprivs + sdev_no;
417 s->maxdata = (1 << thisboard->ao_bits) - 1;
418 s->range_table = thisboard->ao_range_table;
419 s->subdev_flags = SDF_READABLE;
420 s->type = COMEDI_SUBD_AO;
421 s->n_chan = thisboard->n_ao_chans;
422 s->len_chanlist = s->n_chan;
423 s->insn_read = thisboard->ao_rinsn;
424 s->insn_write = thisboard->ao_winsn;
425 subpriv->iobase = dev->iobase + 8;
426 /* initialize the resource enable register by clearing it */
427 outb(0, subpriv->iobase + 3);
428 outb(0, subpriv->iobase + 4 + 3);
429
430 ++sdev_no;
431 port = 0;
432 asic = 0;
433 for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
434 int byte_no;
435
436 s = dev->subdevices + sdev_no;
437 s->private = devpriv->sprivs + sdev_no;
438 s->maxdata = 1;
439 s->range_table = &range_digital;
440 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
441 s->type = COMEDI_SUBD_DIO;
442 s->insn_bits = pcmmio_dio_insn_bits;
443 s->insn_config = pcmmio_dio_insn_config;
Bill Pemberton214e7b52009-05-14 15:24:28 -0400444 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
Calin Culianu6baef152009-02-19 09:13:10 -0800445 subpriv->dio.intr.asic = -1;
446 subpriv->dio.intr.first_chan = -1;
447 subpriv->dio.intr.asic_chan = -1;
448 subpriv->dio.intr.num_asic_chans = -1;
449 subpriv->dio.intr.active = 0;
450 s->len_chanlist = 1;
451
452 /* save the ioport address for each 'port' of 8 channels in the
453 subdevice */
454 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
455 if (port >= PORTS_PER_ASIC) {
456 port = 0;
457 ++asic;
458 thisasic_chanct = 0;
459 }
460 subpriv->iobases[byte_no] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530461 devpriv->asics[asic].iobase + port;
Calin Culianu6baef152009-02-19 09:13:10 -0800462
463 if (thisasic_chanct <
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530464 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
465 && subpriv->dio.intr.asic < 0) {
Calin Culianu6baef152009-02-19 09:13:10 -0800466 /* this is an interrupt subdevice, so setup the struct */
467 subpriv->dio.intr.asic = asic;
468 subpriv->dio.intr.active = 0;
469 subpriv->dio.intr.stop_count = 0;
470 subpriv->dio.intr.first_chan = byte_no * 8;
471 subpriv->dio.intr.asic_chan = thisasic_chanct;
472 subpriv->dio.intr.num_asic_chans =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530473 s->n_chan - subpriv->dio.intr.first_chan;
Calin Culianu6baef152009-02-19 09:13:10 -0800474 s->cancel = pcmmio_cancel;
475 s->do_cmd = pcmmio_cmd;
476 s->do_cmdtest = pcmmio_cmdtest;
477 s->len_chanlist =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530478 subpriv->dio.intr.num_asic_chans;
Calin Culianu6baef152009-02-19 09:13:10 -0800479 }
480 thisasic_chanct += CHANS_PER_PORT;
481 }
482 spin_lock_init(&subpriv->dio.intr.spinlock);
483
484 chans_left -= s->n_chan;
485
486 if (!chans_left) {
487 asic = 0; /* reset the asic to our first asic, to do intr subdevs */
488 port = 0;
489 }
490
491 }
492
493 init_asics(dev); /* clear out all the registers, basically */
494
495 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
496 if (irq[asic]
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530497 && request_irq(irq[asic], interrupt_pcmmio,
498 IRQF_SHARED, thisboard->name, dev)) {
Calin Culianu6baef152009-02-19 09:13:10 -0800499 int i;
500 /* unroll the allocated irqs.. */
501 for (i = asic - 1; i >= 0; --i) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700502 free_irq(irq[i], dev);
Calin Culianu6baef152009-02-19 09:13:10 -0800503 devpriv->asics[i].irq = irq[i] = 0;
504 }
505 irq[asic] = 0;
506 }
507 devpriv->asics[asic].irq = irq[asic];
508 }
509
510 dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple
511 irqs.. */
512
513 if (irq[0]) {
514 printk("irq: %u ", irq[0]);
515 if (irq[1] && thisboard->dio_num_asics == 2)
516 printk("second ASIC irq: %u ", irq[1]);
517 } else {
518 printk("(IRQ mode disabled) ");
519 }
520
521 printk("attached\n");
522
523 return 1;
524}
525
526/*
527 * _detach is called to deconfigure a device. It should deallocate
528 * resources.
529 * This function is also called when _attach() fails, so it should be
530 * careful not to release resources that were not necessarily
531 * allocated by _attach(). dev->private and dev->subdevices are
532 * deallocated automatically by the core.
533 */
Bill Pembertonda91b262009-04-09 16:07:03 -0400534static int pcmmio_detach(struct comedi_device *dev)
Calin Culianu6baef152009-02-19 09:13:10 -0800535{
536 int i;
537
538 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
539 if (dev->iobase)
540 release_region(dev->iobase, thisboard->total_iosize);
541
542 for (i = 0; i < MAX_ASICS; ++i) {
543 if (devpriv && devpriv->asics[i].irq)
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700544 free_irq(devpriv->asics[i].irq, dev);
Calin Culianu6baef152009-02-19 09:13:10 -0800545 }
546
547 if (devpriv && devpriv->sprivs)
548 kfree(devpriv->sprivs);
549
550 return 0;
551}
552
553/* DIO devices are slightly special. Although it is possible to
554 * implement the insn_read/insn_write interface, it is much more
555 * useful to applications if you implement the insn_bits interface.
556 * This allows packed reading/writing of the DIO channels. The
557 * comedi core can convert between insn_bits and insn_read/write */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530558static int pcmmio_dio_insn_bits(struct comedi_device *dev,
559 struct comedi_subdevice *s,
560 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -0800561{
562 int byte_no;
563 if (insn->n != 2)
564 return -EINVAL;
565
566 /* NOTE:
567 reading a 0 means this channel was high
568 writine a 0 sets the channel high
569 reading a 1 means this channel was low
570 writing a 1 means set this channel low
571
572 Therefore everything is always inverted. */
573
574 /* The insn data is a mask in data[0] and the new data
575 * in data[1], each channel cooresponding to a bit. */
576
577#ifdef DAMMIT_ITS_BROKEN
578 /* DEBUG */
579 printk("write mask: %08x data: %08x\n", data[0], data[1]);
580#endif
581
582 s->state = 0;
583
584 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
585 /* address of 8-bit port */
586 unsigned long ioaddr = subpriv->iobases[byte_no],
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530587 /* bit offset of port in 32-bit doubleword */
588 offset = byte_no * 8;
Calin Culianu6baef152009-02-19 09:13:10 -0800589 /* this 8-bit port's data */
590 unsigned char byte = 0,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530591 /* The write mask for this port (if any) */
592 write_mask_byte = (data[0] >> offset) & 0xff,
593 /* The data byte for this port */
594 data_byte = (data[1] >> offset) & 0xff;
Calin Culianu6baef152009-02-19 09:13:10 -0800595
596 byte = inb(ioaddr); /* read all 8-bits for this port */
597
598#ifdef DAMMIT_ITS_BROKEN
599 /* DEBUG */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530600 printk
601 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
602 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
603 offset, ioaddr, (unsigned)byte);
Calin Culianu6baef152009-02-19 09:13:10 -0800604#endif
605
606 if (write_mask_byte) {
607 /* this byte has some write_bits -- so set the output lines */
608 byte &= ~write_mask_byte; /* clear bits for write mask */
609 byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
610 /* Write out the new digital output state */
611 outb(byte, ioaddr);
612 }
613#ifdef DAMMIT_ITS_BROKEN
614 /* DEBUG */
615 printk("data_out_byte %02x\n", (unsigned)byte);
616#endif
617 /* save the digital input lines for this byte.. */
618 s->state |= ((unsigned int)byte) << offset;
619 }
620
621 /* now return the DIO lines to data[1] - note they came inverted! */
622 data[1] = ~s->state;
623
624#ifdef DAMMIT_ITS_BROKEN
625 /* DEBUG */
626 printk("s->state %08x data_out %08x\n", s->state, data[1]);
627#endif
628
629 return 2;
630}
631
632/* The input or output configuration of each digital line is
633 * configured by a special insn_config instruction. chanspec
634 * contains the channel to be changed, and data[0] contains the
635 * value COMEDI_INPUT or COMEDI_OUTPUT. */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530636static int pcmmio_dio_insn_config(struct comedi_device *dev,
637 struct comedi_subdevice *s,
638 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -0800639{
640 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530641 chan % 8;
Calin Culianu6baef152009-02-19 09:13:10 -0800642 unsigned long ioaddr;
643 unsigned char byte;
644
645 /* Compute ioaddr for this channel */
646 ioaddr = subpriv->iobases[byte_no];
647
648 /* NOTE:
649 writing a 0 an IO channel's bit sets the channel to INPUT
650 and pulls the line high as well
651
652 writing a 1 to an IO channel's bit pulls the line low
653
654 All channels are implicitly always in OUTPUT mode -- but when
655 they are high they can be considered to be in INPUT mode..
656
657 Thus, we only force channels low if the config request was INPUT,
658 otherwise we do nothing to the hardware. */
659
660 switch (data[0]) {
661 case INSN_CONFIG_DIO_OUTPUT:
662 /* save to io_bits -- don't actually do anything since
663 all input channels are also output channels... */
664 s->io_bits |= 1 << chan;
665 break;
666 case INSN_CONFIG_DIO_INPUT:
667 /* write a 0 to the actual register representing the channel
668 to set it to 'input'. 0 means "float high". */
669 byte = inb(ioaddr);
670 byte &= ~(1 << bit_no);
671 /**< set input channel to '0' */
672
673 /* write out byte -- this is the only time we actually affect the
674 hardware as all channels are implicitly output -- but input
675 channels are set to float-high */
676 outb(byte, ioaddr);
677
678 /* save to io_bits */
679 s->io_bits &= ~(1 << chan);
680 break;
681
682 case INSN_CONFIG_DIO_QUERY:
683 /* retreive from shadow register */
684 data[1] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530685 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
Calin Culianu6baef152009-02-19 09:13:10 -0800686 return insn->n;
687 break;
688
689 default:
690 return -EINVAL;
691 break;
692 }
693
694 return insn->n;
695}
696
Bill Pembertonda91b262009-04-09 16:07:03 -0400697static void init_asics(struct comedi_device *dev)
Calin Culianu6baef152009-02-19 09:13:10 -0800698{ /* sets up an
699 ASIC chip to defaults */
700 int asic;
701
702 for (asic = 0; asic < thisboard->dio_num_asics; ++asic) {
703 int port, page;
704 unsigned long baseaddr = devpriv->asics[asic].iobase;
705
706 switch_page(dev, asic, 0); /* switch back to page 0 */
707
708 /* first, clear all the DIO port bits */
709 for (port = 0; port < PORTS_PER_ASIC; ++port)
710 outb(0, baseaddr + REG_PORT0 + port);
711
712 /* Next, clear all the paged registers for each page */
713 for (page = 1; page < NUM_PAGES; ++page) {
714 int reg;
715 /* now clear all the paged registers */
716 switch_page(dev, asic, page);
717 for (reg = FIRST_PAGED_REG;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530718 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
Calin Culianu6baef152009-02-19 09:13:10 -0800719 outb(0, baseaddr + reg);
720 }
721
722 /* DEBUG set rising edge interrupts on port0 of both asics */
723 /*switch_page(dev, asic, PAGE_POL);
724 outb(0xff, baseaddr + REG_POL0);
725 switch_page(dev, asic, PAGE_ENAB);
726 outb(0xff, baseaddr + REG_ENAB0); */
727 /* END DEBUG */
728
729 switch_page(dev, asic, 0); /* switch back to default page 0 */
730
731 }
732}
733
Bill Pembertonda91b262009-04-09 16:07:03 -0400734static void switch_page(struct comedi_device *dev, int asic, int page)
Calin Culianu6baef152009-02-19 09:13:10 -0800735{
736 if (asic < 0 || asic >= thisboard->dio_num_asics)
737 return; /* paranoia */
738 if (page < 0 || page >= NUM_PAGES)
739 return; /* more paranoia */
740
741 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
742 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
743
744 /* now write out the shadow register */
745 outb(devpriv->asics[asic].pagelock,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530746 devpriv->asics[asic].iobase + REG_PAGELOCK);
Calin Culianu6baef152009-02-19 09:13:10 -0800747}
748
749#ifdef notused
Bill Pembertonda91b262009-04-09 16:07:03 -0400750static void lock_port(struct comedi_device *dev, int asic, int port)
Calin Culianu6baef152009-02-19 09:13:10 -0800751{
752 if (asic < 0 || asic >= thisboard->dio_num_asics)
753 return; /* paranoia */
754 if (port < 0 || port >= PORTS_PER_ASIC)
755 return; /* more paranoia */
756
757 devpriv->asics[asic].pagelock |= 0x1 << port;
758 /* now write out the shadow register */
759 outb(devpriv->asics[asic].pagelock,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530760 devpriv->asics[asic].iobase + REG_PAGELOCK);
Calin Culianu6baef152009-02-19 09:13:10 -0800761 return;
762}
763
Bill Pembertonda91b262009-04-09 16:07:03 -0400764static void unlock_port(struct comedi_device *dev, int asic, int port)
Calin Culianu6baef152009-02-19 09:13:10 -0800765{
766 if (asic < 0 || asic >= thisboard->dio_num_asics)
767 return; /* paranoia */
768 if (port < 0 || port >= PORTS_PER_ASIC)
769 return; /* more paranoia */
770 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
771 /* now write out the shadow register */
772 outb(devpriv->asics[asic].pagelock,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530773 devpriv->asics[asic].iobase + REG_PAGELOCK);
Calin Culianu6baef152009-02-19 09:13:10 -0800774}
775#endif /* notused */
776
Jiri Slaby70265d22009-03-26 09:34:06 +0100777static irqreturn_t interrupt_pcmmio(int irq, void *d)
Calin Culianu6baef152009-02-19 09:13:10 -0800778{
779 int asic, got1 = 0;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530780 struct comedi_device *dev = (struct comedi_device *)d;
Calin Culianu6baef152009-02-19 09:13:10 -0800781
782 for (asic = 0; asic < MAX_ASICS; ++asic) {
783 if (irq == devpriv->asics[asic].irq) {
784 unsigned long flags;
785 unsigned triggered = 0;
786 unsigned long iobase = devpriv->asics[asic].iobase;
787 /* it is an interrupt for ASIC #asic */
788 unsigned char int_pend;
789
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530790 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
791 flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800792
793 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
794
795 if (int_pend) {
796 int port;
797 for (port = 0; port < INTR_PORTS_PER_ASIC;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530798 ++port) {
Calin Culianu6baef152009-02-19 09:13:10 -0800799 if (int_pend & (0x1 << port)) {
800 unsigned char
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530801 io_lines_with_edges = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800802 switch_page(dev, asic,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530803 PAGE_INT_ID);
Calin Culianu6baef152009-02-19 09:13:10 -0800804 io_lines_with_edges =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530805 inb(iobase +
Calin Culianu6baef152009-02-19 09:13:10 -0800806 REG_INT_ID0 + port);
807
808 if (io_lines_with_edges)
809 /* clear pending interrupt */
810 outb(0, iobase +
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530811 REG_INT_ID0 +
812 port);
Calin Culianu6baef152009-02-19 09:13:10 -0800813
814 triggered |=
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530815 io_lines_with_edges <<
816 port * 8;
Calin Culianu6baef152009-02-19 09:13:10 -0800817 }
818 }
819
820 ++got1;
821 }
822
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530823 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
824 flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800825
826 if (triggered) {
Bill Pemberton34c43922009-03-16 22:05:14 -0400827 struct comedi_subdevice *s;
Calin Culianu6baef152009-02-19 09:13:10 -0800828 /* TODO here: dispatch io lines to subdevs with commands.. */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530829 printk
830 ("PCMMIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
831 irq, asic, triggered);
Calin Culianu6baef152009-02-19 09:13:10 -0800832 for (s = dev->subdevices + 2;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530833 s < dev->subdevices + dev->n_subdevices;
834 ++s) {
Calin Culianu6baef152009-02-19 09:13:10 -0800835 if (subpriv->dio.intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
836 unsigned long flags;
837 unsigned oldevents;
838
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530839 spin_lock_irqsave(&subpriv->dio.
840 intr.spinlock,
841 flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800842
843 oldevents = s->async->events;
844
845 if (subpriv->dio.intr.active) {
846 unsigned mytrig =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530847 ((triggered >>
848 subpriv->dio.intr.asic_chan)
849 &
850 ((0x1 << subpriv->
851 dio.intr.
852 num_asic_chans) -
853 1)) << subpriv->
854 dio.intr.first_chan;
855 if (mytrig &
856 subpriv->dio.
857 intr.enabled_mask) {
858 unsigned int val
859 = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800860 unsigned int n,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530861 ch, len;
Calin Culianu6baef152009-02-19 09:13:10 -0800862
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530863 len =
864 s->
865 async->cmd.chanlist_len;
Calin Culianu6baef152009-02-19 09:13:10 -0800866 for (n = 0;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530867 n < len;
868 n++) {
Calin Culianu6baef152009-02-19 09:13:10 -0800869 ch = CR_CHAN(s->async->cmd.chanlist[n]);
870 if (mytrig & (1U << ch)) {
871 val |= (1U << n);
872 }
873 }
874 /* Write the scan to the buffer. */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530875 if (comedi_buf_put(s->async, ((short *)&val)[0])
876 &&
877 comedi_buf_put
878 (s->async,
879 ((short *)
880 &val)[1]))
881 {
Calin Culianu6baef152009-02-19 09:13:10 -0800882 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
883 } else {
884 /* Overflow! Stop acquisition!! */
885 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
886 pcmmio_stop_intr
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530887 (dev,
888 s);
Calin Culianu6baef152009-02-19 09:13:10 -0800889 }
890
891 /* Check for end of acquisition. */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530892 if (!subpriv->dio.intr.continuous) {
Calin Culianu6baef152009-02-19 09:13:10 -0800893 /* stop_src == TRIG_COUNT */
894 if (subpriv->dio.intr.stop_count > 0) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530895 subpriv->dio.intr.stop_count--;
Calin Culianu6baef152009-02-19 09:13:10 -0800896 if (subpriv->dio.intr.stop_count == 0) {
897 s->async->events |= COMEDI_CB_EOA;
898 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
899 pcmmio_stop_intr
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530900 (dev,
901 s);
Calin Culianu6baef152009-02-19 09:13:10 -0800902 }
903 }
904 }
905 }
906 }
907
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530908 spin_unlock_irqrestore
909 (&subpriv->dio.intr.
910 spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800911
912 if (oldevents !=
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530913 s->async->events) {
Calin Culianu6baef152009-02-19 09:13:10 -0800914 comedi_event(dev, s);
915 }
916
917 }
918
919 }
920 }
921
922 }
923 }
924 if (!got1)
925 return IRQ_NONE; /* interrupt from other source */
926 return IRQ_HANDLED;
927}
928
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530929static void pcmmio_stop_intr(struct comedi_device *dev,
930 struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -0800931{
932 int nports, firstport, asic, port;
933
Bill Pembertonc3744132009-04-22 21:11:47 -0400934 asic = subpriv->dio.intr.asic;
935 if (asic < 0)
Calin Culianu6baef152009-02-19 09:13:10 -0800936 return; /* not an interrupt subdev */
937
938 subpriv->dio.intr.enabled_mask = 0;
939 subpriv->dio.intr.active = 0;
940 s->async->inttrig = 0;
941 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
942 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
943 switch_page(dev, asic, PAGE_ENAB);
944 for (port = firstport; port < firstport + nports; ++port) {
945 /* disable all intrs for this subdev.. */
946 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
947 }
948}
949
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530950static int pcmmio_start_intr(struct comedi_device *dev,
951 struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -0800952{
953 if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
954 /* An empty acquisition! */
955 s->async->events |= COMEDI_CB_EOA;
956 subpriv->dio.intr.active = 0;
957 return 1;
958 } else {
959 unsigned bits = 0, pol_bits = 0, n;
960 int nports, firstport, asic, port;
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400961 struct comedi_cmd *cmd = &s->async->cmd;
Calin Culianu6baef152009-02-19 09:13:10 -0800962
Bill Pembertonc3744132009-04-22 21:11:47 -0400963 asic = subpriv->dio.intr.asic;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530964 if (asic < 0)
Calin Culianu6baef152009-02-19 09:13:10 -0800965 return 1; /* not an interrupt
966 subdev */
967 subpriv->dio.intr.enabled_mask = 0;
968 subpriv->dio.intr.active = 1;
969 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
970 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
971 if (cmd->chanlist) {
972 for (n = 0; n < cmd->chanlist_len; n++) {
973 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
974 pol_bits |= (CR_AREF(cmd->chanlist[n])
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530975 || CR_RANGE(cmd->
976 chanlist[n]) ? 1U : 0U)
977 << CR_CHAN(cmd->chanlist[n]);
Calin Culianu6baef152009-02-19 09:13:10 -0800978 }
979 }
980 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530981 1) << subpriv->dio.intr.first_chan;
Calin Culianu6baef152009-02-19 09:13:10 -0800982 subpriv->dio.intr.enabled_mask = bits;
983
984 { /* the below code configures the board to use a specific IRQ from 0-15. */
985 unsigned char b;
986 /* set resource enable register to enable IRQ operation */
987 outb(1 << 4, dev->iobase + 3);
988 /* set bits 0-3 of b to the irq number from 0-15 */
989 b = dev->irq & ((1 << 4) - 1);
990 outb(b, dev->iobase + 2);
991 /* done, we told the board what irq to use */
992 }
993
994 switch_page(dev, asic, PAGE_ENAB);
995 for (port = firstport; port < firstport + nports; ++port) {
996 unsigned enab =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530997 bits >> (subpriv->dio.intr.first_chan + (port -
998 firstport)
999 * 8) & 0xff, pol =
1000 pol_bits >> (subpriv->dio.intr.first_chan +
1001 (port - firstport) * 8) & 0xff;
Calin Culianu6baef152009-02-19 09:13:10 -08001002 /* set enab intrs for this subdev.. */
1003 outb(enab,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301004 devpriv->asics[asic].iobase + REG_ENAB0 + port);
Calin Culianu6baef152009-02-19 09:13:10 -08001005 switch_page(dev, asic, PAGE_POL);
1006 outb(pol,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301007 devpriv->asics[asic].iobase + REG_ENAB0 + port);
Calin Culianu6baef152009-02-19 09:13:10 -08001008 }
1009 }
1010 return 0;
1011}
1012
Bill Pembertonda91b262009-04-09 16:07:03 -04001013static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -08001014{
1015 unsigned long flags;
1016
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001017 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001018 if (subpriv->dio.intr.active)
1019 pcmmio_stop_intr(dev, s);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001020 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001021
1022 return 0;
1023}
1024
1025/*
1026 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
1027 */
1028static int
Bill Pembertonda91b262009-04-09 16:07:03 -04001029pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301030 unsigned int trignum)
Calin Culianu6baef152009-02-19 09:13:10 -08001031{
1032 unsigned long flags;
1033 int event = 0;
1034
1035 if (trignum != 0)
1036 return -EINVAL;
1037
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001038 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001039 s->async->inttrig = 0;
1040 if (subpriv->dio.intr.active) {
1041 event = pcmmio_start_intr(dev, s);
1042 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001043 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001044
1045 if (event) {
1046 comedi_event(dev, s);
1047 }
1048
1049 return 1;
1050}
1051
1052/*
1053 * 'do_cmd' function for an 'INTERRUPT' subdevice.
1054 */
Bill Pembertonda91b262009-04-09 16:07:03 -04001055static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -08001056{
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001057 struct comedi_cmd *cmd = &s->async->cmd;
Calin Culianu6baef152009-02-19 09:13:10 -08001058 unsigned long flags;
1059 int event = 0;
1060
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001061 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001062 subpriv->dio.intr.active = 1;
1063
1064 /* Set up end of acquisition. */
1065 switch (cmd->stop_src) {
1066 case TRIG_COUNT:
1067 subpriv->dio.intr.continuous = 0;
1068 subpriv->dio.intr.stop_count = cmd->stop_arg;
1069 break;
1070 default:
1071 /* TRIG_NONE */
1072 subpriv->dio.intr.continuous = 1;
1073 subpriv->dio.intr.stop_count = 0;
1074 break;
1075 }
1076
1077 /* Set up start of acquisition. */
1078 switch (cmd->start_src) {
1079 case TRIG_INT:
1080 s->async->inttrig = pcmmio_inttrig_start_intr;
1081 break;
1082 default:
1083 /* TRIG_NOW */
1084 event = pcmmio_start_intr(dev, s);
1085 break;
1086 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001087 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001088
1089 if (event) {
1090 comedi_event(dev, s);
1091 }
1092
1093 return 0;
1094}
1095
Calin Culianu6baef152009-02-19 09:13:10 -08001096static int
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301097pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1098 struct comedi_cmd *cmd)
Calin Culianu6baef152009-02-19 09:13:10 -08001099{
Bill Pemberton0b8f7542009-05-14 15:24:29 -04001100 return comedi_pcm_cmdtest(dev, s, cmd);
Calin Culianu6baef152009-02-19 09:13:10 -08001101}
1102
1103static int adc_wait_ready(unsigned long iobase)
1104{
1105 unsigned long retry = 100000;
1106 while (retry--)
1107 if (inb(iobase + 3) & 0x80)
1108 return 0;
1109 return 1;
1110}
1111
1112/* All this is for AI and AO */
Bill Pembertonda91b262009-04-09 16:07:03 -04001113static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301114 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001115{
1116 int n;
1117 unsigned long iobase = subpriv->iobase;
1118
1119 /*
1120 1. write the CMD byte (to BASE+2)
1121 2. read junk lo byte (BASE+0)
1122 3. read junk hi byte (BASE+1)
1123 4. (mux settled so) write CMD byte again (BASE+2)
1124 5. read valid lo byte(BASE+0)
1125 6. read valid hi byte(BASE+1)
1126
1127 Additionally note that the BASE += 4 if the channel >= 8
1128 */
1129
1130 /* convert n samples */
1131 for (n = 0; n < insn->n; n++) {
1132 unsigned chan = CR_CHAN(insn->chanspec), range =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301133 CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
Calin Culianu6baef152009-02-19 09:13:10 -08001134 unsigned char command_byte = 0;
1135 unsigned iooffset = 0;
Bill Pemberton790c5542009-03-16 22:05:02 -04001136 short sample, adc_adjust = 0;
Calin Culianu6baef152009-02-19 09:13:10 -08001137
1138 if (chan > 7)
1139 chan -= 8, iooffset = 4; /* use the second dword for channels > 7 */
1140
1141 if (aref != AREF_DIFF) {
1142 aref = AREF_GROUND;
1143 command_byte |= 1 << 7; /* set bit 7 to indicate single-ended */
1144 }
1145 if (range < 2)
1146 adc_adjust = 0x8000; /* bipolar ranges (-5,5 .. -10,10 need to be adjusted -- that is.. they need to wrap around by adding 0x8000 */
1147
1148 if (chan % 2) {
1149 command_byte |= 1 << 6; /* odd-numbered channels have bit 6 set */
1150 }
1151
1152 /* select the channel, bits 4-5 == chan/2 */
1153 command_byte |= ((chan / 2) & 0x3) << 4;
1154
1155 /* set the range, bits 2-3 */
1156 command_byte |= (range & 0x3) << 2;
1157
1158 /* need to do this twice to make sure mux settled */
1159 outb(command_byte, iobase + iooffset + 2); /* chan/range/aref select */
1160
1161 adc_wait_ready(iobase + iooffset); /* wait for the adc to say it finised the conversion */
1162
1163 outb(command_byte, iobase + iooffset + 2); /* select the chan/range/aref AGAIN */
1164
1165 adc_wait_ready(iobase + iooffset);
1166
1167 sample = inb(iobase + iooffset + 0); /* read data lo byte */
1168 sample |= inb(iobase + iooffset + 1) << 8; /* read data hi byte */
1169 sample += adc_adjust; /* adjustment .. munge data */
1170 data[n] = sample;
1171 }
1172 /* return the number of samples read/written */
1173 return n;
1174}
1175
Bill Pembertonda91b262009-04-09 16:07:03 -04001176static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301177 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001178{
1179 int n;
1180 for (n = 0; n < insn->n; n++) {
1181 unsigned chan = CR_CHAN(insn->chanspec);
1182 if (chan < s->n_chan)
1183 data[n] = subpriv->ao.shadow_samples[chan];
1184 }
1185 return n;
1186}
1187
1188static int wait_dac_ready(unsigned long iobase)
1189{
1190 unsigned long retry = 100000L;
1191
1192 /* This may seem like an absurd way to handle waiting and violates the
1193 "no busy waiting" policy. The fact is that the hardware is
1194 normally so fast that we usually only need one time through the loop
1195 anyway. The longer timeout is for rare occasions and for detecting
1196 non-existant hardware. */
1197
1198 while (retry--) {
1199 if (inb(iobase + 3) & 0x80)
1200 return 0;
1201
1202 }
1203 return 1;
1204}
1205
Bill Pembertonda91b262009-04-09 16:07:03 -04001206static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301207 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001208{
1209 int n;
1210 unsigned iobase = subpriv->iobase, iooffset = 0;
1211
1212 for (n = 0; n < insn->n; n++) {
1213 unsigned chan = CR_CHAN(insn->chanspec), range =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301214 CR_RANGE(insn->chanspec);
Calin Culianu6baef152009-02-19 09:13:10 -08001215 if (chan < s->n_chan) {
1216 unsigned char command_byte = 0, range_byte =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301217 range & ((1 << 4) - 1);
Calin Culianu6baef152009-02-19 09:13:10 -08001218 if (chan >= 4)
1219 chan -= 4, iooffset += 4;
1220 /* set the range.. */
1221 outb(range_byte, iobase + iooffset + 0);
1222 outb(0, iobase + iooffset + 1);
1223
1224 /* tell it to begin */
1225 command_byte = (chan << 1) | 0x60;
1226 outb(command_byte, iobase + iooffset + 2);
1227
1228 wait_dac_ready(iobase + iooffset);
1229
1230 outb(data[n] & 0xff, iobase + iooffset + 0); /* low order byte */
1231 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1); /* high order byte */
1232 command_byte = 0x70 | (chan << 1); /* set bit 4 of command byte to indicate data is loaded and trigger conversion */
1233 /* trigger converion */
1234 outb(command_byte, iobase + iooffset + 2);
1235
1236 wait_dac_ready(iobase + iooffset);
1237
1238 subpriv->ao.shadow_samples[chan] = data[n]; /* save to shadow register for ao_rinsn */
1239 }
1240 }
1241 return n;
1242}
1243
1244/*
1245 * A convenient macro that defines init_module() and cleanup_module(),
1246 * as necessary.
1247 */
1248COMEDI_INITCLEANUP(driver);