blob: 025a52e8981d1868d68497359b4a165d96bfc24a [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
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +000035 subdevice 2 - first 24 channels of the 48 channel of DIO
36 (with edge-triggered interrupt support)
37 subdevice 3 - last 24 channels of the 48 channel DIO
38 (no interrupt support for this bank of channels)
Calin Culianu6baef152009-02-19 09:13:10 -080039
40 Some notes:
41
42 Synchronous reads and writes are the only things implemented for AI and AO,
43 even though the hardware itself can do streaming acquisition, etc. Anyone
44 want to add asynchronous I/O for AI/AO as a feature? Be my guest...
45
46 Asynchronous I/O for the DIO subdevices *is* implemented, however! They are
47 basically edge-triggered interrupts for any configuration of the first
48 24 DIO-lines.
49
50 Also note that this interrupt support is untested.
51
52 A few words about edge-detection IRQ support (commands on DIO):
53
54 * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
55 of the board to the comedi_config command. The board IRQ is not jumpered
56 but rather configured through software, so any IRQ from 1-15 is OK.
57
58 * Due to the genericity of the comedi API, you need to create a special
59 comedi_command in order to use edge-triggered interrupts for DIO.
60
61 * Use comedi_commands with TRIG_NOW. Your callback will be called each
62 time an edge is detected on the specified DIO line(s), and the data
63 values will be two sample_t's, which should be concatenated to form
64 one 32-bit unsigned int. This value is the mask of channels that had
65 edges detected from your channel list. Note that the bits positions
66 in the mask correspond to positions in your chanlist when you
67 specified the command and *not* channel id's!
68
69 * To set the polarity of the edge-detection interrupts pass a nonzero value
70 for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
71 value for both CR_RANGE and CR_AREF if you want edge-down polarity.
72
73Configuration Options:
74 [0] - I/O port base address
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +000075 [1] - IRQ (optional -- for edge-detect interrupt support only,
76 leave out if you don't need this feature)
Calin Culianu6baef152009-02-19 09:13:10 -080077*/
78
Greg Kroah-Hartman25436dc2009-04-27 15:14:34 -070079#include <linux/interrupt.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090080#include <linux/slab.h>
Calin Culianu6baef152009-02-19 09:13:10 -080081#include "../comedidev.h"
Bill Pemberton0b8f7542009-05-14 15:24:29 -040082#include "pcm_common.h"
Calin Culianu6baef152009-02-19 09:13:10 -080083#include <linux/pci.h> /* for PCI devices */
84
Calin Culianu6baef152009-02-19 09:13:10 -080085/* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
86#define CHANS_PER_PORT 8
87#define PORTS_PER_ASIC 6
88#define INTR_PORTS_PER_ASIC 3
89#define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
90#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
91#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
92#define INTR_CHANS_PER_ASIC 24
93#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
94#define MAX_DIO_CHANS (PORTS_PER_ASIC*1*CHANS_PER_PORT)
95#define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
96#define SDEV_NO ((int)(s - dev->subdevices))
97#define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
98/* IO Memory sizes */
99#define ASIC_IOSIZE (0x0B)
100#define PCMMIO48_IOSIZE ASIC_IOSIZE
101
102/* Some offsets - these are all in the 16byte IO memory offset from
103 the base address. Note that there is a paging scheme to swap out
104 offsets 0x8-0xA using the PAGELOCK register. See the table below.
105
106 Register(s) Pages R/W? Description
107 --------------------------------------------------------------
108 REG_PORTx All R/W Read/Write/Configure IO
109 REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int.
110 REG_PAGELOCK All WriteOnly Select a page
111 REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity
112 REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int.
113 REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints.
114 */
115#define REG_PORT0 0x0
116#define REG_PORT1 0x1
117#define REG_PORT2 0x2
118#define REG_PORT3 0x3
119#define REG_PORT4 0x4
120#define REG_PORT5 0x5
121#define REG_INT_PENDING 0x6
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000122#define REG_PAGELOCK 0x7 /*
123 * page selector register, upper 2 bits select
124 * a page and bits 0-5 are used to 'lock down'
125 * a particular port above to make it readonly.
126 */
Calin Culianu6baef152009-02-19 09:13:10 -0800127#define REG_POL0 0x8
128#define REG_POL1 0x9
129#define REG_POL2 0xA
130#define REG_ENAB0 0x8
131#define REG_ENAB1 0x9
132#define REG_ENAB2 0xA
133#define REG_INT_ID0 0x8
134#define REG_INT_ID1 0x9
135#define REG_INT_ID2 0xA
136
137#define NUM_PAGED_REGS 3
138#define NUM_PAGES 4
139#define FIRST_PAGED_REG 0x8
140#define REG_PAGE_BITOFFSET 6
141#define REG_LOCK_BITOFFSET 0
142#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000143#define REG_LOCK_MASK (~(REG_PAGE_MASK))
Calin Culianu6baef152009-02-19 09:13:10 -0800144#define PAGE_POL 1
145#define PAGE_ENAB 2
146#define PAGE_INT_ID 3
147
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530148typedef int (*comedi_insn_fn_t) (struct comedi_device *,
149 struct comedi_subdevice *,
150 struct comedi_insn *, unsigned int *);
Calin Culianu6baef152009-02-19 09:13:10 -0800151
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530152static int ai_rinsn(struct comedi_device *, struct comedi_subdevice *,
153 struct comedi_insn *, unsigned int *);
154static int ao_rinsn(struct comedi_device *, struct comedi_subdevice *,
155 struct comedi_insn *, unsigned int *);
156static int ao_winsn(struct comedi_device *, struct comedi_subdevice *,
157 struct comedi_insn *, unsigned int *);
Calin Culianu6baef152009-02-19 09:13:10 -0800158
159/*
160 * Board descriptions for two imaginary boards. Describing the
161 * boards in this way is optional, and completely driver-dependent.
162 * Some drivers use arrays such as this, other do not.
163 */
Bill Pemberton657f81e2009-03-16 22:19:04 -0400164struct pcmmio_board {
Calin Culianu6baef152009-02-19 09:13:10 -0800165 const char *name;
166 const int dio_num_asics;
167 const int dio_num_ports;
168 const int total_iosize;
169 const int ai_bits;
170 const int ao_bits;
171 const int n_ai_chans;
172 const int n_ao_chans;
Bill Pemberton9ced1de2009-03-16 22:05:31 -0400173 const struct comedi_lrange *ai_range_table, *ao_range_table;
Calin Culianu6baef152009-02-19 09:13:10 -0800174 comedi_insn_fn_t ai_rinsn, ao_rinsn, ao_winsn;
Bill Pemberton657f81e2009-03-16 22:19:04 -0400175};
Calin Culianu6baef152009-02-19 09:13:10 -0800176
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000177static const struct comedi_lrange ranges_ai = {
178 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0., 10.)}
Calin Culianu6baef152009-02-19 09:13:10 -0800179};
180
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000181static const struct comedi_lrange ranges_ao = {
182 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530183 RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
Calin Culianu6baef152009-02-19 09:13:10 -0800184};
185
Bill Pemberton657f81e2009-03-16 22:19:04 -0400186static const struct pcmmio_board pcmmio_boards[] = {
Calin Culianu6baef152009-02-19 09:13:10 -0800187 {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530188 .name = "pcmmio",
189 .dio_num_asics = 1,
190 .dio_num_ports = 6,
191 .total_iosize = 32,
192 .ai_bits = 16,
193 .ao_bits = 16,
194 .n_ai_chans = 16,
195 .n_ao_chans = 8,
196 .ai_range_table = &ranges_ai,
197 .ao_range_table = &ranges_ao,
198 .ai_rinsn = ai_rinsn,
199 .ao_rinsn = ao_rinsn,
200 .ao_winsn = ao_winsn},
Calin Culianu6baef152009-02-19 09:13:10 -0800201};
202
203/*
204 * Useful for shorthand access to the particular board structure
205 */
Bill Pemberton657f81e2009-03-16 22:19:04 -0400206#define thisboard ((const struct pcmmio_board *)dev->board_ptr)
Calin Culianu6baef152009-02-19 09:13:10 -0800207
208/* this structure is for data unique to this subdevice. */
Bill Pemberton4467df92009-03-16 22:19:37 -0400209struct pcmmio_subdev_private {
Calin Culianu6baef152009-02-19 09:13:10 -0800210
211 union {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000212 /* for DIO: mapping of halfwords (bytes)
213 in port/chanarray to iobase */
Calin Culianu6baef152009-02-19 09:13:10 -0800214 unsigned long iobases[PORTS_PER_SUBDEV];
215
216 /* for AI/AO */
217 unsigned long iobase;
218 };
219 union {
220 struct {
221
222 /* The below is only used for intr subdevices */
223 struct {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000224 /*
225 * if non-negative, this subdev has an
226 * interrupt asic
227 */
228 int asic;
229 /*
230 * if nonnegative, the first channel id for
231 * interrupts.
232 */
233 int first_chan;
234 /*
235 * the number of asic channels in this subdev
236 * that have interrutps
237 */
238 int num_asic_chans;
239 /*
240 * if nonnegative, the first channel id with
241 * respect to the asic that has interrupts
242 */
243 int asic_chan;
244 /*
245 * subdev-relative channel mask for channels
246 * we are interested in
247 */
248 int enabled_mask;
Calin Culianu6baef152009-02-19 09:13:10 -0800249 int active;
250 int stop_count;
251 int continuous;
252 spinlock_t spinlock;
253 } intr;
254 } dio;
255 struct {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000256 /* the last unsigned int data written */
257 unsigned int shadow_samples[8];
Calin Culianu6baef152009-02-19 09:13:10 -0800258 } ao;
259 };
Bill Pemberton4467df92009-03-16 22:19:37 -0400260};
Calin Culianu6baef152009-02-19 09:13:10 -0800261
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000262/*
263 * this structure is for data unique to this hardware driver. If
264 * several hardware drivers keep similar information in this structure,
265 * feel free to suggest moving the variable to the struct comedi_device struct.
266 */
Bill Pembertone56ab712009-03-16 22:20:30 -0400267struct pcmmio_private {
Calin Culianu6baef152009-02-19 09:13:10 -0800268 /* stuff for DIO */
269 struct {
270 unsigned char pagelock; /* current page and lock */
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000271 /* shadow of POLx registers */
272 unsigned char pol[NUM_PAGED_REGS];
273 /* shadow of ENABx registers */
274 unsigned char enab[NUM_PAGED_REGS];
Calin Culianu6baef152009-02-19 09:13:10 -0800275 int num;
276 unsigned long iobase;
277 unsigned int irq;
278 spinlock_t spinlock;
279 } asics[MAX_ASICS];
Bill Pemberton4467df92009-03-16 22:19:37 -0400280 struct pcmmio_subdev_private *sprivs;
Bill Pembertone56ab712009-03-16 22:20:30 -0400281};
Calin Culianu6baef152009-02-19 09:13:10 -0800282
283/*
284 * most drivers define the following macro to make it easy to
285 * access the private structure.
286 */
Bill Pembertone56ab712009-03-16 22:20:30 -0400287#define devpriv ((struct pcmmio_private *)dev->private)
Bill Pemberton4467df92009-03-16 22:19:37 -0400288#define subpriv ((struct pcmmio_subdev_private *)s->private)
Calin Culianu6baef152009-02-19 09:13:10 -0800289/*
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400290 * The struct comedi_driver structure tells the Comedi core module
Calin Culianu6baef152009-02-19 09:13:10 -0800291 * which functions to call to configure/deconfigure (attach/detach)
292 * the board, and also about the kernel module that contains
293 * the device code.
294 */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530295static int pcmmio_attach(struct comedi_device *dev,
296 struct comedi_devconfig *it);
Bill Pembertonda91b262009-04-09 16:07:03 -0400297static int pcmmio_detach(struct comedi_device *dev);
Calin Culianu6baef152009-02-19 09:13:10 -0800298
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400299static struct comedi_driver driver = {
Bill Pemberton68c3dbf2009-04-22 21:11:49 -0400300 .driver_name = "pcmmio",
301 .module = THIS_MODULE,
302 .attach = pcmmio_attach,
303 .detach = pcmmio_detach,
Calin Culianu6baef152009-02-19 09:13:10 -0800304/* It is not necessary to implement the following members if you are
305 * writing a driver for a ISA PnP or PCI card */
306 /* Most drivers will support multiple types of boards by
307 * having an array of board structures. These were defined
308 * in pcmmio_boards[] above. Note that the element 'name'
309 * was first in the structure -- Comedi uses this fact to
310 * extract the name of the board without knowing any details
311 * about the structure except for its length.
312 * When a device is attached (by comedi_config), the name
313 * of the device is given to Comedi, and Comedi tries to
314 * match it by going through the list of board names. If
315 * there is a match, the address of the pointer is put
316 * into dev->board_ptr and driver->attach() is called.
317 *
318 * Note that these are not necessary if you can determine
319 * the type of board in software. ISA PnP, PCI, and PCMCIA
320 * devices are such boards.
321 */
Bill Pemberton68c3dbf2009-04-22 21:11:49 -0400322 .board_name = &pcmmio_boards[0].name,
323 .offset = sizeof(struct pcmmio_board),
Bill Pemberton8629efa2009-04-23 15:54:56 -0400324 .num_names = ARRAY_SIZE(pcmmio_boards),
Calin Culianu6baef152009-02-19 09:13:10 -0800325};
326
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530327static int pcmmio_dio_insn_bits(struct comedi_device *dev,
328 struct comedi_subdevice *s,
329 struct comedi_insn *insn, unsigned int *data);
330static int pcmmio_dio_insn_config(struct comedi_device *dev,
331 struct comedi_subdevice *s,
332 struct comedi_insn *insn, unsigned int *data);
Calin Culianu6baef152009-02-19 09:13:10 -0800333
Jiri Slaby70265d22009-03-26 09:34:06 +0100334static irqreturn_t interrupt_pcmmio(int irq, void *d);
Bill Pemberton34c43922009-03-16 22:05:14 -0400335static void pcmmio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
Bill Pemberton814900c2009-04-23 15:54:54 -0400336static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
337static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
338static int pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530339 struct comedi_cmd *cmd);
Calin Culianu6baef152009-02-19 09:13:10 -0800340
341/* some helper functions to deal with specifics of this device's registers */
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000342/* sets up/clears ASIC chips to defaults */
343static void init_asics(struct comedi_device *dev);
Bill Pemberton814900c2009-04-23 15:54:54 -0400344static void switch_page(struct comedi_device *dev, int asic, int page);
Calin Culianu6baef152009-02-19 09:13:10 -0800345#ifdef notused
Bill Pemberton814900c2009-04-23 15:54:54 -0400346static void lock_port(struct comedi_device *dev, int asic, int port);
347static void unlock_port(struct comedi_device *dev, int asic, int port);
Calin Culianu6baef152009-02-19 09:13:10 -0800348#endif
349
350/*
351 * Attach is called by the Comedi core to configure the driver
352 * for a particular board. If you specified a board_name array
353 * in the driver structure, dev->board_ptr contains that
354 * address.
355 */
Bill Pembertonda91b262009-04-09 16:07:03 -0400356static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
Calin Culianu6baef152009-02-19 09:13:10 -0800357{
Bill Pemberton34c43922009-03-16 22:05:14 -0400358 struct comedi_subdevice *s;
Calin Culianu6baef152009-02-19 09:13:10 -0800359 int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530360 thisasic_chanct = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800361 unsigned long iobase;
362 unsigned int irq[MAX_ASICS];
363
364 iobase = it->options[0];
365 irq[0] = it->options[1];
366
367 printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530368 iobase);
Calin Culianu6baef152009-02-19 09:13:10 -0800369
370 dev->iobase = iobase;
371
372 if (!iobase || !request_region(iobase,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530373 thisboard->total_iosize,
374 driver.driver_name)) {
Calin Culianu6baef152009-02-19 09:13:10 -0800375 printk("I/O port conflict\n");
376 return -EIO;
377 }
378
379/*
380 * Initialize dev->board_name. Note that we can use the "thisboard"
381 * macro now, since we just initialized it in the last line.
382 */
383 dev->board_name = thisboard->name;
384
385/*
386 * Allocate the private structure area. alloc_private() is a
387 * convenient macro defined in comedidev.h.
388 */
Bill Pembertone56ab712009-03-16 22:20:30 -0400389 if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
Calin Culianu6baef152009-02-19 09:13:10 -0800390 printk("cannot allocate private data structure\n");
391 return -ENOMEM;
392 }
393
394 for (asic = 0; asic < MAX_ASICS; ++asic) {
395 devpriv->asics[asic].num = asic;
396 devpriv->asics[asic].iobase =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530397 dev->iobase + 16 + asic * ASIC_IOSIZE;
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000398 /*
399 * this gets actually set at the end of this function when we
400 * request_irqs
401 */
402 devpriv->asics[asic].irq = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800403 spin_lock_init(&devpriv->asics[asic].spinlock);
404 }
405
406 chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
407 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
408 n_subdevs = n_dio_subdevs + 2;
409 devpriv->sprivs =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530410 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
411 GFP_KERNEL);
Calin Culianu6baef152009-02-19 09:13:10 -0800412 if (!devpriv->sprivs) {
413 printk("cannot allocate subdevice private data structures\n");
414 return -ENOMEM;
415 }
416 /*
417 * Allocate the subdevice structures. alloc_subdevice() is a
418 * convenient macro defined in comedidev.h.
419 *
420 * Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
421 */
422 if (alloc_subdevices(dev, n_subdevs) < 0) {
423 printk("cannot allocate subdevice data structures\n");
424 return -ENOMEM;
425 }
426
427 /* First, AI */
428 sdev_no = 0;
429 s = dev->subdevices + sdev_no;
430 s->private = devpriv->sprivs + sdev_no;
431 s->maxdata = (1 << thisboard->ai_bits) - 1;
432 s->range_table = thisboard->ai_range_table;
433 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
434 s->type = COMEDI_SUBD_AI;
435 s->n_chan = thisboard->n_ai_chans;
436 s->len_chanlist = s->n_chan;
437 s->insn_read = thisboard->ai_rinsn;
438 subpriv->iobase = dev->iobase + 0;
439 /* initialize the resource enable register by clearing it */
440 outb(0, subpriv->iobase + 3);
441 outb(0, subpriv->iobase + 4 + 3);
442
443 /* Next, AO */
444 ++sdev_no;
445 s = dev->subdevices + sdev_no;
446 s->private = devpriv->sprivs + sdev_no;
447 s->maxdata = (1 << thisboard->ao_bits) - 1;
448 s->range_table = thisboard->ao_range_table;
449 s->subdev_flags = SDF_READABLE;
450 s->type = COMEDI_SUBD_AO;
451 s->n_chan = thisboard->n_ao_chans;
452 s->len_chanlist = s->n_chan;
453 s->insn_read = thisboard->ao_rinsn;
454 s->insn_write = thisboard->ao_winsn;
455 subpriv->iobase = dev->iobase + 8;
456 /* initialize the resource enable register by clearing it */
457 outb(0, subpriv->iobase + 3);
458 outb(0, subpriv->iobase + 4 + 3);
459
460 ++sdev_no;
461 port = 0;
462 asic = 0;
463 for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
464 int byte_no;
465
466 s = dev->subdevices + sdev_no;
467 s->private = devpriv->sprivs + sdev_no;
468 s->maxdata = 1;
469 s->range_table = &range_digital;
470 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
471 s->type = COMEDI_SUBD_DIO;
472 s->insn_bits = pcmmio_dio_insn_bits;
473 s->insn_config = pcmmio_dio_insn_config;
Bill Pemberton214e7b52009-05-14 15:24:28 -0400474 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
Calin Culianu6baef152009-02-19 09:13:10 -0800475 subpriv->dio.intr.asic = -1;
476 subpriv->dio.intr.first_chan = -1;
477 subpriv->dio.intr.asic_chan = -1;
478 subpriv->dio.intr.num_asic_chans = -1;
479 subpriv->dio.intr.active = 0;
480 s->len_chanlist = 1;
481
482 /* save the ioport address for each 'port' of 8 channels in the
483 subdevice */
484 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
485 if (port >= PORTS_PER_ASIC) {
486 port = 0;
487 ++asic;
488 thisasic_chanct = 0;
489 }
490 subpriv->iobases[byte_no] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530491 devpriv->asics[asic].iobase + port;
Calin Culianu6baef152009-02-19 09:13:10 -0800492
493 if (thisasic_chanct <
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530494 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
495 && subpriv->dio.intr.asic < 0) {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000496 /*
497 * this is an interrupt subdevice,
498 * so setup the struct
499 */
Calin Culianu6baef152009-02-19 09:13:10 -0800500 subpriv->dio.intr.asic = asic;
501 subpriv->dio.intr.active = 0;
502 subpriv->dio.intr.stop_count = 0;
503 subpriv->dio.intr.first_chan = byte_no * 8;
504 subpriv->dio.intr.asic_chan = thisasic_chanct;
505 subpriv->dio.intr.num_asic_chans =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530506 s->n_chan - subpriv->dio.intr.first_chan;
Calin Culianu6baef152009-02-19 09:13:10 -0800507 s->cancel = pcmmio_cancel;
508 s->do_cmd = pcmmio_cmd;
509 s->do_cmdtest = pcmmio_cmdtest;
510 s->len_chanlist =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530511 subpriv->dio.intr.num_asic_chans;
Calin Culianu6baef152009-02-19 09:13:10 -0800512 }
513 thisasic_chanct += CHANS_PER_PORT;
514 }
515 spin_lock_init(&subpriv->dio.intr.spinlock);
516
517 chans_left -= s->n_chan;
518
519 if (!chans_left) {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000520 /*
521 * reset the asic to our first asic,
522 * to do intr subdevs
523 */
524 asic = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800525 port = 0;
526 }
527
528 }
529
530 init_asics(dev); /* clear out all the registers, basically */
531
532 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
533 if (irq[asic]
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530534 && request_irq(irq[asic], interrupt_pcmmio,
535 IRQF_SHARED, thisboard->name, dev)) {
Calin Culianu6baef152009-02-19 09:13:10 -0800536 int i;
537 /* unroll the allocated irqs.. */
538 for (i = asic - 1; i >= 0; --i) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700539 free_irq(irq[i], dev);
Calin Culianu6baef152009-02-19 09:13:10 -0800540 devpriv->asics[i].irq = irq[i] = 0;
541 }
542 irq[asic] = 0;
543 }
544 devpriv->asics[asic].irq = irq[asic];
545 }
546
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000547 dev->irq = irq[0]; /*
548 * grr.. wish comedi dev struct supported
549 * multiple irqs..
550 */
Calin Culianu6baef152009-02-19 09:13:10 -0800551
552 if (irq[0]) {
553 printk("irq: %u ", irq[0]);
Dan Carpenter4b2ba242009-12-28 18:59:01 +0200554 if (thisboard->dio_num_asics == 2 && irq[1])
Calin Culianu6baef152009-02-19 09:13:10 -0800555 printk("second ASIC irq: %u ", irq[1]);
556 } else {
557 printk("(IRQ mode disabled) ");
558 }
559
560 printk("attached\n");
561
562 return 1;
563}
564
565/*
566 * _detach is called to deconfigure a device. It should deallocate
567 * resources.
568 * This function is also called when _attach() fails, so it should be
569 * careful not to release resources that were not necessarily
570 * allocated by _attach(). dev->private and dev->subdevices are
571 * deallocated automatically by the core.
572 */
Bill Pembertonda91b262009-04-09 16:07:03 -0400573static int pcmmio_detach(struct comedi_device *dev)
Calin Culianu6baef152009-02-19 09:13:10 -0800574{
575 int i;
576
577 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
578 if (dev->iobase)
579 release_region(dev->iobase, thisboard->total_iosize);
580
581 for (i = 0; i < MAX_ASICS; ++i) {
582 if (devpriv && devpriv->asics[i].irq)
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700583 free_irq(devpriv->asics[i].irq, dev);
Calin Culianu6baef152009-02-19 09:13:10 -0800584 }
585
586 if (devpriv && devpriv->sprivs)
587 kfree(devpriv->sprivs);
588
589 return 0;
590}
591
592/* DIO devices are slightly special. Although it is possible to
593 * implement the insn_read/insn_write interface, it is much more
594 * useful to applications if you implement the insn_bits interface.
595 * This allows packed reading/writing of the DIO channels. The
596 * comedi core can convert between insn_bits and insn_read/write */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530597static int pcmmio_dio_insn_bits(struct comedi_device *dev,
598 struct comedi_subdevice *s,
599 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -0800600{
601 int byte_no;
602 if (insn->n != 2)
603 return -EINVAL;
604
605 /* NOTE:
606 reading a 0 means this channel was high
607 writine a 0 sets the channel high
608 reading a 1 means this channel was low
609 writing a 1 means set this channel low
610
611 Therefore everything is always inverted. */
612
613 /* The insn data is a mask in data[0] and the new data
614 * in data[1], each channel cooresponding to a bit. */
615
616#ifdef DAMMIT_ITS_BROKEN
617 /* DEBUG */
618 printk("write mask: %08x data: %08x\n", data[0], data[1]);
619#endif
620
621 s->state = 0;
622
623 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
624 /* address of 8-bit port */
625 unsigned long ioaddr = subpriv->iobases[byte_no],
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530626 /* bit offset of port in 32-bit doubleword */
627 offset = byte_no * 8;
Calin Culianu6baef152009-02-19 09:13:10 -0800628 /* this 8-bit port's data */
629 unsigned char byte = 0,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530630 /* The write mask for this port (if any) */
631 write_mask_byte = (data[0] >> offset) & 0xff,
632 /* The data byte for this port */
633 data_byte = (data[1] >> offset) & 0xff;
Calin Culianu6baef152009-02-19 09:13:10 -0800634
635 byte = inb(ioaddr); /* read all 8-bits for this port */
636
637#ifdef DAMMIT_ITS_BROKEN
638 /* DEBUG */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530639 printk
640 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
641 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
642 offset, ioaddr, (unsigned)byte);
Calin Culianu6baef152009-02-19 09:13:10 -0800643#endif
644
645 if (write_mask_byte) {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000646 /*
647 * this byte has some write_bits
648 * -- so set the output lines
649 */
650 /* clear bits for write mask */
651 byte &= ~write_mask_byte;
652 /* set to inverted data_byte */
653 byte |= ~data_byte & write_mask_byte;
Calin Culianu6baef152009-02-19 09:13:10 -0800654 /* Write out the new digital output state */
655 outb(byte, ioaddr);
656 }
657#ifdef DAMMIT_ITS_BROKEN
658 /* DEBUG */
659 printk("data_out_byte %02x\n", (unsigned)byte);
660#endif
661 /* save the digital input lines for this byte.. */
662 s->state |= ((unsigned int)byte) << offset;
663 }
664
665 /* now return the DIO lines to data[1] - note they came inverted! */
666 data[1] = ~s->state;
667
668#ifdef DAMMIT_ITS_BROKEN
669 /* DEBUG */
670 printk("s->state %08x data_out %08x\n", s->state, data[1]);
671#endif
672
673 return 2;
674}
675
676/* The input or output configuration of each digital line is
677 * configured by a special insn_config instruction. chanspec
678 * contains the channel to be changed, and data[0] contains the
679 * value COMEDI_INPUT or COMEDI_OUTPUT. */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530680static int pcmmio_dio_insn_config(struct comedi_device *dev,
681 struct comedi_subdevice *s,
682 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -0800683{
684 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530685 chan % 8;
Calin Culianu6baef152009-02-19 09:13:10 -0800686 unsigned long ioaddr;
687 unsigned char byte;
688
689 /* Compute ioaddr for this channel */
690 ioaddr = subpriv->iobases[byte_no];
691
692 /* NOTE:
693 writing a 0 an IO channel's bit sets the channel to INPUT
694 and pulls the line high as well
695
696 writing a 1 to an IO channel's bit pulls the line low
697
698 All channels are implicitly always in OUTPUT mode -- but when
699 they are high they can be considered to be in INPUT mode..
700
701 Thus, we only force channels low if the config request was INPUT,
702 otherwise we do nothing to the hardware. */
703
704 switch (data[0]) {
705 case INSN_CONFIG_DIO_OUTPUT:
706 /* save to io_bits -- don't actually do anything since
707 all input channels are also output channels... */
708 s->io_bits |= 1 << chan;
709 break;
710 case INSN_CONFIG_DIO_INPUT:
711 /* write a 0 to the actual register representing the channel
712 to set it to 'input'. 0 means "float high". */
713 byte = inb(ioaddr);
714 byte &= ~(1 << bit_no);
715 /**< set input channel to '0' */
716
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000717 /*
718 * write out byte -- this is the only time we actually affect
719 * the hardware as all channels are implicitly output
720 * -- but input channels are set to float-high
721 */
Calin Culianu6baef152009-02-19 09:13:10 -0800722 outb(byte, ioaddr);
723
724 /* save to io_bits */
725 s->io_bits &= ~(1 << chan);
726 break;
727
728 case INSN_CONFIG_DIO_QUERY:
729 /* retreive from shadow register */
730 data[1] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530731 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
Calin Culianu6baef152009-02-19 09:13:10 -0800732 return insn->n;
733 break;
734
735 default:
736 return -EINVAL;
737 break;
738 }
739
740 return insn->n;
741}
742
Bill Pembertonda91b262009-04-09 16:07:03 -0400743static void init_asics(struct comedi_device *dev)
Calin Culianu6baef152009-02-19 09:13:10 -0800744{ /* sets up an
745 ASIC chip to defaults */
746 int asic;
747
748 for (asic = 0; asic < thisboard->dio_num_asics; ++asic) {
749 int port, page;
750 unsigned long baseaddr = devpriv->asics[asic].iobase;
751
752 switch_page(dev, asic, 0); /* switch back to page 0 */
753
754 /* first, clear all the DIO port bits */
755 for (port = 0; port < PORTS_PER_ASIC; ++port)
756 outb(0, baseaddr + REG_PORT0 + port);
757
758 /* Next, clear all the paged registers for each page */
759 for (page = 1; page < NUM_PAGES; ++page) {
760 int reg;
761 /* now clear all the paged registers */
762 switch_page(dev, asic, page);
763 for (reg = FIRST_PAGED_REG;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530764 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
Calin Culianu6baef152009-02-19 09:13:10 -0800765 outb(0, baseaddr + reg);
766 }
767
768 /* DEBUG set rising edge interrupts on port0 of both asics */
769 /*switch_page(dev, asic, PAGE_POL);
770 outb(0xff, baseaddr + REG_POL0);
771 switch_page(dev, asic, PAGE_ENAB);
772 outb(0xff, baseaddr + REG_ENAB0); */
773 /* END DEBUG */
774
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000775 /* switch back to default page 0 */
776 switch_page(dev, asic, 0);
Calin Culianu6baef152009-02-19 09:13:10 -0800777 }
778}
779
Bill Pembertonda91b262009-04-09 16:07:03 -0400780static void switch_page(struct comedi_device *dev, int asic, int page)
Calin Culianu6baef152009-02-19 09:13:10 -0800781{
782 if (asic < 0 || asic >= thisboard->dio_num_asics)
783 return; /* paranoia */
784 if (page < 0 || page >= NUM_PAGES)
785 return; /* more paranoia */
786
787 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
788 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
789
790 /* now write out the shadow register */
791 outb(devpriv->asics[asic].pagelock,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530792 devpriv->asics[asic].iobase + REG_PAGELOCK);
Calin Culianu6baef152009-02-19 09:13:10 -0800793}
794
795#ifdef notused
Bill Pembertonda91b262009-04-09 16:07:03 -0400796static void lock_port(struct comedi_device *dev, int asic, int port)
Calin Culianu6baef152009-02-19 09:13:10 -0800797{
798 if (asic < 0 || asic >= thisboard->dio_num_asics)
799 return; /* paranoia */
800 if (port < 0 || port >= PORTS_PER_ASIC)
801 return; /* more paranoia */
802
803 devpriv->asics[asic].pagelock |= 0x1 << port;
804 /* now write out the shadow register */
805 outb(devpriv->asics[asic].pagelock,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530806 devpriv->asics[asic].iobase + REG_PAGELOCK);
Calin Culianu6baef152009-02-19 09:13:10 -0800807 return;
808}
809
Bill Pembertonda91b262009-04-09 16:07:03 -0400810static void unlock_port(struct comedi_device *dev, int asic, int port)
Calin Culianu6baef152009-02-19 09:13:10 -0800811{
812 if (asic < 0 || asic >= thisboard->dio_num_asics)
813 return; /* paranoia */
814 if (port < 0 || port >= PORTS_PER_ASIC)
815 return; /* more paranoia */
816 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
817 /* now write out the shadow register */
818 outb(devpriv->asics[asic].pagelock,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530819 devpriv->asics[asic].iobase + REG_PAGELOCK);
Calin Culianu6baef152009-02-19 09:13:10 -0800820}
821#endif /* notused */
822
Jiri Slaby70265d22009-03-26 09:34:06 +0100823static irqreturn_t interrupt_pcmmio(int irq, void *d)
Calin Culianu6baef152009-02-19 09:13:10 -0800824{
825 int asic, got1 = 0;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530826 struct comedi_device *dev = (struct comedi_device *)d;
Calin Culianu6baef152009-02-19 09:13:10 -0800827
828 for (asic = 0; asic < MAX_ASICS; ++asic) {
829 if (irq == devpriv->asics[asic].irq) {
830 unsigned long flags;
831 unsigned triggered = 0;
832 unsigned long iobase = devpriv->asics[asic].iobase;
833 /* it is an interrupt for ASIC #asic */
834 unsigned char int_pend;
835
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530836 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
837 flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800838
839 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
840
841 if (int_pend) {
842 int port;
843 for (port = 0; port < INTR_PORTS_PER_ASIC;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530844 ++port) {
Calin Culianu6baef152009-02-19 09:13:10 -0800845 if (int_pend & (0x1 << port)) {
846 unsigned char
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530847 io_lines_with_edges = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800848 switch_page(dev, asic,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530849 PAGE_INT_ID);
Calin Culianu6baef152009-02-19 09:13:10 -0800850 io_lines_with_edges =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530851 inb(iobase +
Calin Culianu6baef152009-02-19 09:13:10 -0800852 REG_INT_ID0 + port);
853
854 if (io_lines_with_edges)
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000855 /*
856 * clear pending
857 * interrupt
858 */
Calin Culianu6baef152009-02-19 09:13:10 -0800859 outb(0, iobase +
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530860 REG_INT_ID0 +
861 port);
Calin Culianu6baef152009-02-19 09:13:10 -0800862
863 triggered |=
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530864 io_lines_with_edges <<
865 port * 8;
Calin Culianu6baef152009-02-19 09:13:10 -0800866 }
867 }
868
869 ++got1;
870 }
871
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530872 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
873 flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800874
875 if (triggered) {
Bill Pemberton34c43922009-03-16 22:05:14 -0400876 struct comedi_subdevice *s;
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000877 /*
878 * TODO here: dispatch io lines to subdevs
879 * with commands..
880 */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530881 printk
882 ("PCMMIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
883 irq, asic, triggered);
Calin Culianu6baef152009-02-19 09:13:10 -0800884 for (s = dev->subdevices + 2;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530885 s < dev->subdevices + dev->n_subdevices;
886 ++s) {
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000887 /*
888 * this is an interrupt subdev,
889 * and it matches this asic!
890 */
891 if (subpriv->dio.intr.asic == asic) {
Calin Culianu6baef152009-02-19 09:13:10 -0800892 unsigned long flags;
893 unsigned oldevents;
894
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530895 spin_lock_irqsave(&subpriv->dio.
896 intr.spinlock,
897 flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800898
899 oldevents = s->async->events;
900
901 if (subpriv->dio.intr.active) {
902 unsigned mytrig =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530903 ((triggered >>
904 subpriv->dio.intr.asic_chan)
905 &
906 ((0x1 << subpriv->
907 dio.intr.
908 num_asic_chans) -
909 1)) << subpriv->
910 dio.intr.first_chan;
911 if (mytrig &
912 subpriv->dio.
913 intr.enabled_mask) {
914 unsigned int val
915 = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800916 unsigned int n,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530917 ch, len;
Calin Culianu6baef152009-02-19 09:13:10 -0800918
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530919 len =
920 s->
921 async->cmd.chanlist_len;
Calin Culianu6baef152009-02-19 09:13:10 -0800922 for (n = 0;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530923 n < len;
924 n++) {
Calin Culianu6baef152009-02-19 09:13:10 -0800925 ch = CR_CHAN(s->async->cmd.chanlist[n]);
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000926 if (mytrig & (1U << ch))
Calin Culianu6baef152009-02-19 09:13:10 -0800927 val |= (1U << n);
Calin Culianu6baef152009-02-19 09:13:10 -0800928 }
929 /* Write the scan to the buffer. */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530930 if (comedi_buf_put(s->async, ((short *)&val)[0])
931 &&
932 comedi_buf_put
933 (s->async,
934 ((short *)
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000935 &val)[1])) {
Calin Culianu6baef152009-02-19 09:13:10 -0800936 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
937 } else {
938 /* Overflow! Stop acquisition!! */
939 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
940 pcmmio_stop_intr
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530941 (dev,
942 s);
Calin Culianu6baef152009-02-19 09:13:10 -0800943 }
944
945 /* Check for end of acquisition. */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530946 if (!subpriv->dio.intr.continuous) {
Calin Culianu6baef152009-02-19 09:13:10 -0800947 /* stop_src == TRIG_COUNT */
948 if (subpriv->dio.intr.stop_count > 0) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530949 subpriv->dio.intr.stop_count--;
Calin Culianu6baef152009-02-19 09:13:10 -0800950 if (subpriv->dio.intr.stop_count == 0) {
951 s->async->events |= COMEDI_CB_EOA;
952 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
953 pcmmio_stop_intr
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530954 (dev,
955 s);
Calin Culianu6baef152009-02-19 09:13:10 -0800956 }
957 }
958 }
959 }
960 }
961
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530962 spin_unlock_irqrestore
963 (&subpriv->dio.intr.
964 spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800965
966 if (oldevents !=
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530967 s->async->events) {
Calin Culianu6baef152009-02-19 09:13:10 -0800968 comedi_event(dev, s);
969 }
970
971 }
972
973 }
974 }
975
976 }
977 }
978 if (!got1)
979 return IRQ_NONE; /* interrupt from other source */
980 return IRQ_HANDLED;
981}
982
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530983static void pcmmio_stop_intr(struct comedi_device *dev,
984 struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -0800985{
986 int nports, firstport, asic, port;
987
Bill Pembertonc3744132009-04-22 21:11:47 -0400988 asic = subpriv->dio.intr.asic;
989 if (asic < 0)
Calin Culianu6baef152009-02-19 09:13:10 -0800990 return; /* not an interrupt subdev */
991
992 subpriv->dio.intr.enabled_mask = 0;
993 subpriv->dio.intr.active = 0;
994 s->async->inttrig = 0;
995 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
996 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
997 switch_page(dev, asic, PAGE_ENAB);
998 for (port = firstport; port < firstport + nports; ++port) {
999 /* disable all intrs for this subdev.. */
1000 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
1001 }
1002}
1003
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301004static int pcmmio_start_intr(struct comedi_device *dev,
1005 struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -08001006{
1007 if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
1008 /* An empty acquisition! */
1009 s->async->events |= COMEDI_CB_EOA;
1010 subpriv->dio.intr.active = 0;
1011 return 1;
1012 } else {
1013 unsigned bits = 0, pol_bits = 0, n;
1014 int nports, firstport, asic, port;
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001015 struct comedi_cmd *cmd = &s->async->cmd;
Calin Culianu6baef152009-02-19 09:13:10 -08001016
Bill Pembertonc3744132009-04-22 21:11:47 -04001017 asic = subpriv->dio.intr.asic;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301018 if (asic < 0)
Calin Culianu6baef152009-02-19 09:13:10 -08001019 return 1; /* not an interrupt
1020 subdev */
1021 subpriv->dio.intr.enabled_mask = 0;
1022 subpriv->dio.intr.active = 1;
1023 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
1024 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
1025 if (cmd->chanlist) {
1026 for (n = 0; n < cmd->chanlist_len; n++) {
1027 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
1028 pol_bits |= (CR_AREF(cmd->chanlist[n])
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301029 || CR_RANGE(cmd->
1030 chanlist[n]) ? 1U : 0U)
1031 << CR_CHAN(cmd->chanlist[n]);
Calin Culianu6baef152009-02-19 09:13:10 -08001032 }
1033 }
1034 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301035 1) << subpriv->dio.intr.first_chan;
Calin Culianu6baef152009-02-19 09:13:10 -08001036 subpriv->dio.intr.enabled_mask = bits;
1037
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001038 {
1039 /*
1040 * the below code configures the board
1041 * to use a specific IRQ from 0-15.
1042 */
Calin Culianu6baef152009-02-19 09:13:10 -08001043 unsigned char b;
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001044 /*
1045 * set resource enable register
1046 * to enable IRQ operation
1047 */
Calin Culianu6baef152009-02-19 09:13:10 -08001048 outb(1 << 4, dev->iobase + 3);
1049 /* set bits 0-3 of b to the irq number from 0-15 */
1050 b = dev->irq & ((1 << 4) - 1);
1051 outb(b, dev->iobase + 2);
1052 /* done, we told the board what irq to use */
1053 }
1054
1055 switch_page(dev, asic, PAGE_ENAB);
1056 for (port = firstport; port < firstport + nports; ++port) {
1057 unsigned enab =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301058 bits >> (subpriv->dio.intr.first_chan + (port -
1059 firstport)
1060 * 8) & 0xff, pol =
1061 pol_bits >> (subpriv->dio.intr.first_chan +
1062 (port - firstport) * 8) & 0xff;
Calin Culianu6baef152009-02-19 09:13:10 -08001063 /* set enab intrs for this subdev.. */
1064 outb(enab,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301065 devpriv->asics[asic].iobase + REG_ENAB0 + port);
Calin Culianu6baef152009-02-19 09:13:10 -08001066 switch_page(dev, asic, PAGE_POL);
1067 outb(pol,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301068 devpriv->asics[asic].iobase + REG_ENAB0 + port);
Calin Culianu6baef152009-02-19 09:13:10 -08001069 }
1070 }
1071 return 0;
1072}
1073
Bill Pembertonda91b262009-04-09 16:07:03 -04001074static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -08001075{
1076 unsigned long flags;
1077
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001078 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001079 if (subpriv->dio.intr.active)
1080 pcmmio_stop_intr(dev, s);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001081 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001082
1083 return 0;
1084}
1085
1086/*
1087 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
1088 */
1089static int
Bill Pembertonda91b262009-04-09 16:07:03 -04001090pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301091 unsigned int trignum)
Calin Culianu6baef152009-02-19 09:13:10 -08001092{
1093 unsigned long flags;
1094 int event = 0;
1095
1096 if (trignum != 0)
1097 return -EINVAL;
1098
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001099 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001100 s->async->inttrig = 0;
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001101 if (subpriv->dio.intr.active)
Calin Culianu6baef152009-02-19 09:13:10 -08001102 event = pcmmio_start_intr(dev, s);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001103 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001104
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001105 if (event)
Calin Culianu6baef152009-02-19 09:13:10 -08001106 comedi_event(dev, s);
Calin Culianu6baef152009-02-19 09:13:10 -08001107
1108 return 1;
1109}
1110
1111/*
1112 * 'do_cmd' function for an 'INTERRUPT' subdevice.
1113 */
Bill Pembertonda91b262009-04-09 16:07:03 -04001114static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -08001115{
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001116 struct comedi_cmd *cmd = &s->async->cmd;
Calin Culianu6baef152009-02-19 09:13:10 -08001117 unsigned long flags;
1118 int event = 0;
1119
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001120 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001121 subpriv->dio.intr.active = 1;
1122
1123 /* Set up end of acquisition. */
1124 switch (cmd->stop_src) {
1125 case TRIG_COUNT:
1126 subpriv->dio.intr.continuous = 0;
1127 subpriv->dio.intr.stop_count = cmd->stop_arg;
1128 break;
1129 default:
1130 /* TRIG_NONE */
1131 subpriv->dio.intr.continuous = 1;
1132 subpriv->dio.intr.stop_count = 0;
1133 break;
1134 }
1135
1136 /* Set up start of acquisition. */
1137 switch (cmd->start_src) {
1138 case TRIG_INT:
1139 s->async->inttrig = pcmmio_inttrig_start_intr;
1140 break;
1141 default:
1142 /* TRIG_NOW */
1143 event = pcmmio_start_intr(dev, s);
1144 break;
1145 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001146 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001147
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001148 if (event)
Calin Culianu6baef152009-02-19 09:13:10 -08001149 comedi_event(dev, s);
Calin Culianu6baef152009-02-19 09:13:10 -08001150
1151 return 0;
1152}
1153
Calin Culianu6baef152009-02-19 09:13:10 -08001154static int
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301155pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1156 struct comedi_cmd *cmd)
Calin Culianu6baef152009-02-19 09:13:10 -08001157{
Bill Pemberton0b8f7542009-05-14 15:24:29 -04001158 return comedi_pcm_cmdtest(dev, s, cmd);
Calin Culianu6baef152009-02-19 09:13:10 -08001159}
1160
1161static int adc_wait_ready(unsigned long iobase)
1162{
1163 unsigned long retry = 100000;
1164 while (retry--)
1165 if (inb(iobase + 3) & 0x80)
1166 return 0;
1167 return 1;
1168}
1169
1170/* All this is for AI and AO */
Bill Pembertonda91b262009-04-09 16:07:03 -04001171static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301172 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001173{
1174 int n;
1175 unsigned long iobase = subpriv->iobase;
1176
1177 /*
1178 1. write the CMD byte (to BASE+2)
1179 2. read junk lo byte (BASE+0)
1180 3. read junk hi byte (BASE+1)
1181 4. (mux settled so) write CMD byte again (BASE+2)
1182 5. read valid lo byte(BASE+0)
1183 6. read valid hi byte(BASE+1)
1184
1185 Additionally note that the BASE += 4 if the channel >= 8
1186 */
1187
1188 /* convert n samples */
1189 for (n = 0; n < insn->n; n++) {
1190 unsigned chan = CR_CHAN(insn->chanspec), range =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301191 CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
Calin Culianu6baef152009-02-19 09:13:10 -08001192 unsigned char command_byte = 0;
1193 unsigned iooffset = 0;
Bill Pemberton790c5542009-03-16 22:05:02 -04001194 short sample, adc_adjust = 0;
Calin Culianu6baef152009-02-19 09:13:10 -08001195
1196 if (chan > 7)
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001197 chan -= 8, iooffset = 4; /*
1198 * use the second dword
1199 * for channels > 7
1200 */
Calin Culianu6baef152009-02-19 09:13:10 -08001201
1202 if (aref != AREF_DIFF) {
1203 aref = AREF_GROUND;
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001204 command_byte |= 1 << 7; /*
1205 * set bit 7 to indicate
1206 * single-ended
1207 */
Calin Culianu6baef152009-02-19 09:13:10 -08001208 }
1209 if (range < 2)
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001210 adc_adjust = 0x8000; /*
1211 * bipolar ranges
1212 * (-5,5 .. -10,10 need to be
1213 * adjusted -- that is.. they
1214 * need to wrap around by
1215 * adding 0x8000
1216 */
Calin Culianu6baef152009-02-19 09:13:10 -08001217
1218 if (chan % 2) {
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001219 command_byte |= 1 << 6; /*
1220 * odd-numbered channels
1221 * have bit 6 set
1222 */
Calin Culianu6baef152009-02-19 09:13:10 -08001223 }
1224
1225 /* select the channel, bits 4-5 == chan/2 */
1226 command_byte |= ((chan / 2) & 0x3) << 4;
1227
1228 /* set the range, bits 2-3 */
1229 command_byte |= (range & 0x3) << 2;
1230
1231 /* need to do this twice to make sure mux settled */
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001232 /* chan/range/aref select */
1233 outb(command_byte, iobase + iooffset + 2);
Calin Culianu6baef152009-02-19 09:13:10 -08001234
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001235 /* wait for the adc to say it finised the conversion */
1236 adc_wait_ready(iobase + iooffset);
Calin Culianu6baef152009-02-19 09:13:10 -08001237
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001238 /* select the chan/range/aref AGAIN */
1239 outb(command_byte, iobase + iooffset + 2);
Calin Culianu6baef152009-02-19 09:13:10 -08001240
1241 adc_wait_ready(iobase + iooffset);
1242
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001243 /* read data lo byte */
1244 sample = inb(iobase + iooffset + 0);
1245
1246 /* read data hi byte */
1247 sample |= inb(iobase + iooffset + 1) << 8;
Calin Culianu6baef152009-02-19 09:13:10 -08001248 sample += adc_adjust; /* adjustment .. munge data */
1249 data[n] = sample;
1250 }
1251 /* return the number of samples read/written */
1252 return n;
1253}
1254
Bill Pembertonda91b262009-04-09 16:07:03 -04001255static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301256 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001257{
1258 int n;
1259 for (n = 0; n < insn->n; n++) {
1260 unsigned chan = CR_CHAN(insn->chanspec);
1261 if (chan < s->n_chan)
1262 data[n] = subpriv->ao.shadow_samples[chan];
1263 }
1264 return n;
1265}
1266
1267static int wait_dac_ready(unsigned long iobase)
1268{
1269 unsigned long retry = 100000L;
1270
1271 /* This may seem like an absurd way to handle waiting and violates the
1272 "no busy waiting" policy. The fact is that the hardware is
1273 normally so fast that we usually only need one time through the loop
1274 anyway. The longer timeout is for rare occasions and for detecting
1275 non-existant hardware. */
1276
1277 while (retry--) {
1278 if (inb(iobase + 3) & 0x80)
1279 return 0;
1280
1281 }
1282 return 1;
1283}
1284
Bill Pembertonda91b262009-04-09 16:07:03 -04001285static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301286 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001287{
1288 int n;
1289 unsigned iobase = subpriv->iobase, iooffset = 0;
1290
1291 for (n = 0; n < insn->n; n++) {
1292 unsigned chan = CR_CHAN(insn->chanspec), range =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301293 CR_RANGE(insn->chanspec);
Calin Culianu6baef152009-02-19 09:13:10 -08001294 if (chan < s->n_chan) {
1295 unsigned char command_byte = 0, range_byte =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301296 range & ((1 << 4) - 1);
Calin Culianu6baef152009-02-19 09:13:10 -08001297 if (chan >= 4)
1298 chan -= 4, iooffset += 4;
1299 /* set the range.. */
1300 outb(range_byte, iobase + iooffset + 0);
1301 outb(0, iobase + iooffset + 1);
1302
1303 /* tell it to begin */
1304 command_byte = (chan << 1) | 0x60;
1305 outb(command_byte, iobase + iooffset + 2);
1306
1307 wait_dac_ready(iobase + iooffset);
1308
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001309 /* low order byte */
1310 outb(data[n] & 0xff, iobase + iooffset + 0);
1311
1312 /* high order byte */
1313 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);
1314
1315 /*
1316 * set bit 4 of command byte to indicate
1317 * data is loaded and trigger conversion
1318 */
1319 command_byte = 0x70 | (chan << 1);
Calin Culianu6baef152009-02-19 09:13:10 -08001320 /* trigger converion */
1321 outb(command_byte, iobase + iooffset + 2);
1322
1323 wait_dac_ready(iobase + iooffset);
1324
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001325 /* save to shadow register for ao_rinsn */
1326 subpriv->ao.shadow_samples[chan] = data[n];
Calin Culianu6baef152009-02-19 09:13:10 -08001327 }
1328 }
1329 return n;
1330}
1331
1332/*
1333 * A convenient macro that defines init_module() and cleanup_module(),
1334 * as necessary.
1335 */
1336COMEDI_INITCLEANUP(driver);