blob: 35ba93989a36e1c55854280214f668bfbddbdc62 [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>
Calin Culianu6baef152009-02-19 09:13:10 -080080#include "../comedidev.h"
Bill Pemberton0b8f7542009-05-14 15:24:29 -040081#include "pcm_common.h"
Calin Culianu6baef152009-02-19 09:13:10 -080082#include <linux/pci.h> /* for PCI devices */
83
Calin Culianu6baef152009-02-19 09:13:10 -080084/* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
85#define CHANS_PER_PORT 8
86#define PORTS_PER_ASIC 6
87#define INTR_PORTS_PER_ASIC 3
88#define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
89#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
90#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
91#define INTR_CHANS_PER_ASIC 24
92#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
93#define MAX_DIO_CHANS (PORTS_PER_ASIC*1*CHANS_PER_PORT)
94#define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
95#define SDEV_NO ((int)(s - dev->subdevices))
96#define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
97/* IO Memory sizes */
98#define ASIC_IOSIZE (0x0B)
99#define PCMMIO48_IOSIZE ASIC_IOSIZE
100
101/* Some offsets - these are all in the 16byte IO memory offset from
102 the base address. Note that there is a paging scheme to swap out
103 offsets 0x8-0xA using the PAGELOCK register. See the table below.
104
105 Register(s) Pages R/W? Description
106 --------------------------------------------------------------
107 REG_PORTx All R/W Read/Write/Configure IO
108 REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int.
109 REG_PAGELOCK All WriteOnly Select a page
110 REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity
111 REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int.
112 REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints.
113 */
114#define REG_PORT0 0x0
115#define REG_PORT1 0x1
116#define REG_PORT2 0x2
117#define REG_PORT3 0x3
118#define REG_PORT4 0x4
119#define REG_PORT5 0x5
120#define REG_INT_PENDING 0x6
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000121#define REG_PAGELOCK 0x7 /*
122 * page selector register, upper 2 bits select
123 * a page and bits 0-5 are used to 'lock down'
124 * a particular port above to make it readonly.
125 */
Calin Culianu6baef152009-02-19 09:13:10 -0800126#define REG_POL0 0x8
127#define REG_POL1 0x9
128#define REG_POL2 0xA
129#define REG_ENAB0 0x8
130#define REG_ENAB1 0x9
131#define REG_ENAB2 0xA
132#define REG_INT_ID0 0x8
133#define REG_INT_ID1 0x9
134#define REG_INT_ID2 0xA
135
136#define NUM_PAGED_REGS 3
137#define NUM_PAGES 4
138#define FIRST_PAGED_REG 0x8
139#define REG_PAGE_BITOFFSET 6
140#define REG_LOCK_BITOFFSET 0
141#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000142#define REG_LOCK_MASK (~(REG_PAGE_MASK))
Calin Culianu6baef152009-02-19 09:13:10 -0800143#define PAGE_POL 1
144#define PAGE_ENAB 2
145#define PAGE_INT_ID 3
146
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530147typedef int (*comedi_insn_fn_t) (struct comedi_device *,
148 struct comedi_subdevice *,
149 struct comedi_insn *, unsigned int *);
Calin Culianu6baef152009-02-19 09:13:10 -0800150
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530151static int ai_rinsn(struct comedi_device *, struct comedi_subdevice *,
152 struct comedi_insn *, unsigned int *);
153static int ao_rinsn(struct comedi_device *, struct comedi_subdevice *,
154 struct comedi_insn *, unsigned int *);
155static int ao_winsn(struct comedi_device *, struct comedi_subdevice *,
156 struct comedi_insn *, unsigned int *);
Calin Culianu6baef152009-02-19 09:13:10 -0800157
158/*
159 * Board descriptions for two imaginary boards. Describing the
160 * boards in this way is optional, and completely driver-dependent.
161 * Some drivers use arrays such as this, other do not.
162 */
Bill Pemberton657f81e2009-03-16 22:19:04 -0400163struct pcmmio_board {
Calin Culianu6baef152009-02-19 09:13:10 -0800164 const char *name;
165 const int dio_num_asics;
166 const int dio_num_ports;
167 const int total_iosize;
168 const int ai_bits;
169 const int ao_bits;
170 const int n_ai_chans;
171 const int n_ao_chans;
Bill Pemberton9ced1de2009-03-16 22:05:31 -0400172 const struct comedi_lrange *ai_range_table, *ao_range_table;
Calin Culianu6baef152009-02-19 09:13:10 -0800173 comedi_insn_fn_t ai_rinsn, ao_rinsn, ao_winsn;
Bill Pemberton657f81e2009-03-16 22:19:04 -0400174};
Calin Culianu6baef152009-02-19 09:13:10 -0800175
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000176static const struct comedi_lrange ranges_ai = {
177 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0., 10.)}
Calin Culianu6baef152009-02-19 09:13:10 -0800178};
179
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000180static const struct comedi_lrange ranges_ao = {
181 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530182 RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
Calin Culianu6baef152009-02-19 09:13:10 -0800183};
184
Bill Pemberton657f81e2009-03-16 22:19:04 -0400185static const struct pcmmio_board pcmmio_boards[] = {
Calin Culianu6baef152009-02-19 09:13:10 -0800186 {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530187 .name = "pcmmio",
188 .dio_num_asics = 1,
189 .dio_num_ports = 6,
190 .total_iosize = 32,
191 .ai_bits = 16,
192 .ao_bits = 16,
193 .n_ai_chans = 16,
194 .n_ao_chans = 8,
195 .ai_range_table = &ranges_ai,
196 .ao_range_table = &ranges_ao,
197 .ai_rinsn = ai_rinsn,
198 .ao_rinsn = ao_rinsn,
199 .ao_winsn = ao_winsn},
Calin Culianu6baef152009-02-19 09:13:10 -0800200};
201
202/*
203 * Useful for shorthand access to the particular board structure
204 */
Bill Pemberton657f81e2009-03-16 22:19:04 -0400205#define thisboard ((const struct pcmmio_board *)dev->board_ptr)
Calin Culianu6baef152009-02-19 09:13:10 -0800206
207/* this structure is for data unique to this subdevice. */
Bill Pemberton4467df92009-03-16 22:19:37 -0400208struct pcmmio_subdev_private {
Calin Culianu6baef152009-02-19 09:13:10 -0800209
210 union {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000211 /* for DIO: mapping of halfwords (bytes)
212 in port/chanarray to iobase */
Calin Culianu6baef152009-02-19 09:13:10 -0800213 unsigned long iobases[PORTS_PER_SUBDEV];
214
215 /* for AI/AO */
216 unsigned long iobase;
217 };
218 union {
219 struct {
220
221 /* The below is only used for intr subdevices */
222 struct {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000223 /*
224 * if non-negative, this subdev has an
225 * interrupt asic
226 */
227 int asic;
228 /*
229 * if nonnegative, the first channel id for
230 * interrupts.
231 */
232 int first_chan;
233 /*
234 * the number of asic channels in this subdev
235 * that have interrutps
236 */
237 int num_asic_chans;
238 /*
239 * if nonnegative, the first channel id with
240 * respect to the asic that has interrupts
241 */
242 int asic_chan;
243 /*
244 * subdev-relative channel mask for channels
245 * we are interested in
246 */
247 int enabled_mask;
Calin Culianu6baef152009-02-19 09:13:10 -0800248 int active;
249 int stop_count;
250 int continuous;
251 spinlock_t spinlock;
252 } intr;
253 } dio;
254 struct {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000255 /* the last unsigned int data written */
256 unsigned int shadow_samples[8];
Calin Culianu6baef152009-02-19 09:13:10 -0800257 } ao;
258 };
Bill Pemberton4467df92009-03-16 22:19:37 -0400259};
Calin Culianu6baef152009-02-19 09:13:10 -0800260
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000261/*
262 * this structure is for data unique to this hardware driver. If
263 * several hardware drivers keep similar information in this structure,
264 * feel free to suggest moving the variable to the struct comedi_device struct.
265 */
Bill Pembertone56ab712009-03-16 22:20:30 -0400266struct pcmmio_private {
Calin Culianu6baef152009-02-19 09:13:10 -0800267 /* stuff for DIO */
268 struct {
269 unsigned char pagelock; /* current page and lock */
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000270 /* shadow of POLx registers */
271 unsigned char pol[NUM_PAGED_REGS];
272 /* shadow of ENABx registers */
273 unsigned char enab[NUM_PAGED_REGS];
Calin Culianu6baef152009-02-19 09:13:10 -0800274 int num;
275 unsigned long iobase;
276 unsigned int irq;
277 spinlock_t spinlock;
278 } asics[MAX_ASICS];
Bill Pemberton4467df92009-03-16 22:19:37 -0400279 struct pcmmio_subdev_private *sprivs;
Bill Pembertone56ab712009-03-16 22:20:30 -0400280};
Calin Culianu6baef152009-02-19 09:13:10 -0800281
282/*
283 * most drivers define the following macro to make it easy to
284 * access the private structure.
285 */
Bill Pembertone56ab712009-03-16 22:20:30 -0400286#define devpriv ((struct pcmmio_private *)dev->private)
Bill Pemberton4467df92009-03-16 22:19:37 -0400287#define subpriv ((struct pcmmio_subdev_private *)s->private)
Calin Culianu6baef152009-02-19 09:13:10 -0800288/*
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400289 * The struct comedi_driver structure tells the Comedi core module
Calin Culianu6baef152009-02-19 09:13:10 -0800290 * which functions to call to configure/deconfigure (attach/detach)
291 * the board, and also about the kernel module that contains
292 * the device code.
293 */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530294static int pcmmio_attach(struct comedi_device *dev,
295 struct comedi_devconfig *it);
Bill Pembertonda91b262009-04-09 16:07:03 -0400296static int pcmmio_detach(struct comedi_device *dev);
Calin Culianu6baef152009-02-19 09:13:10 -0800297
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400298static struct comedi_driver driver = {
Bill Pemberton68c3dbf2009-04-22 21:11:49 -0400299 .driver_name = "pcmmio",
300 .module = THIS_MODULE,
301 .attach = pcmmio_attach,
302 .detach = pcmmio_detach,
Calin Culianu6baef152009-02-19 09:13:10 -0800303/* It is not necessary to implement the following members if you are
304 * writing a driver for a ISA PnP or PCI card */
305 /* Most drivers will support multiple types of boards by
306 * having an array of board structures. These were defined
307 * in pcmmio_boards[] above. Note that the element 'name'
308 * was first in the structure -- Comedi uses this fact to
309 * extract the name of the board without knowing any details
310 * about the structure except for its length.
311 * When a device is attached (by comedi_config), the name
312 * of the device is given to Comedi, and Comedi tries to
313 * match it by going through the list of board names. If
314 * there is a match, the address of the pointer is put
315 * into dev->board_ptr and driver->attach() is called.
316 *
317 * Note that these are not necessary if you can determine
318 * the type of board in software. ISA PnP, PCI, and PCMCIA
319 * devices are such boards.
320 */
Bill Pemberton68c3dbf2009-04-22 21:11:49 -0400321 .board_name = &pcmmio_boards[0].name,
322 .offset = sizeof(struct pcmmio_board),
Bill Pemberton8629efa2009-04-23 15:54:56 -0400323 .num_names = ARRAY_SIZE(pcmmio_boards),
Calin Culianu6baef152009-02-19 09:13:10 -0800324};
325
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530326static int pcmmio_dio_insn_bits(struct comedi_device *dev,
327 struct comedi_subdevice *s,
328 struct comedi_insn *insn, unsigned int *data);
329static int pcmmio_dio_insn_config(struct comedi_device *dev,
330 struct comedi_subdevice *s,
331 struct comedi_insn *insn, unsigned int *data);
Calin Culianu6baef152009-02-19 09:13:10 -0800332
Jiri Slaby70265d22009-03-26 09:34:06 +0100333static irqreturn_t interrupt_pcmmio(int irq, void *d);
Bill Pemberton34c43922009-03-16 22:05:14 -0400334static void pcmmio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
Bill Pemberton814900c2009-04-23 15:54:54 -0400335static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
336static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
337static int pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530338 struct comedi_cmd *cmd);
Calin Culianu6baef152009-02-19 09:13:10 -0800339
340/* some helper functions to deal with specifics of this device's registers */
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000341/* sets up/clears ASIC chips to defaults */
342static void init_asics(struct comedi_device *dev);
Bill Pemberton814900c2009-04-23 15:54:54 -0400343static void switch_page(struct comedi_device *dev, int asic, int page);
Calin Culianu6baef152009-02-19 09:13:10 -0800344#ifdef notused
Bill Pemberton814900c2009-04-23 15:54:54 -0400345static void lock_port(struct comedi_device *dev, int asic, int port);
346static void unlock_port(struct comedi_device *dev, int asic, int port);
Calin Culianu6baef152009-02-19 09:13:10 -0800347#endif
348
349/*
350 * Attach is called by the Comedi core to configure the driver
351 * for a particular board. If you specified a board_name array
352 * in the driver structure, dev->board_ptr contains that
353 * address.
354 */
Bill Pembertonda91b262009-04-09 16:07:03 -0400355static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
Calin Culianu6baef152009-02-19 09:13:10 -0800356{
Bill Pemberton34c43922009-03-16 22:05:14 -0400357 struct comedi_subdevice *s;
Calin Culianu6baef152009-02-19 09:13:10 -0800358 int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530359 thisasic_chanct = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800360 unsigned long iobase;
361 unsigned int irq[MAX_ASICS];
362
363 iobase = it->options[0];
364 irq[0] = it->options[1];
365
366 printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530367 iobase);
Calin Culianu6baef152009-02-19 09:13:10 -0800368
369 dev->iobase = iobase;
370
371 if (!iobase || !request_region(iobase,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530372 thisboard->total_iosize,
373 driver.driver_name)) {
Calin Culianu6baef152009-02-19 09:13:10 -0800374 printk("I/O port conflict\n");
375 return -EIO;
376 }
377
378/*
379 * Initialize dev->board_name. Note that we can use the "thisboard"
380 * macro now, since we just initialized it in the last line.
381 */
382 dev->board_name = thisboard->name;
383
384/*
385 * Allocate the private structure area. alloc_private() is a
386 * convenient macro defined in comedidev.h.
387 */
Bill Pembertone56ab712009-03-16 22:20:30 -0400388 if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
Calin Culianu6baef152009-02-19 09:13:10 -0800389 printk("cannot allocate private data structure\n");
390 return -ENOMEM;
391 }
392
393 for (asic = 0; asic < MAX_ASICS; ++asic) {
394 devpriv->asics[asic].num = asic;
395 devpriv->asics[asic].iobase =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530396 dev->iobase + 16 + asic * ASIC_IOSIZE;
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000397 /*
398 * this gets actually set at the end of this function when we
399 * request_irqs
400 */
401 devpriv->asics[asic].irq = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800402 spin_lock_init(&devpriv->asics[asic].spinlock);
403 }
404
405 chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
406 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
407 n_subdevs = n_dio_subdevs + 2;
408 devpriv->sprivs =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530409 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
410 GFP_KERNEL);
Calin Culianu6baef152009-02-19 09:13:10 -0800411 if (!devpriv->sprivs) {
412 printk("cannot allocate subdevice private data structures\n");
413 return -ENOMEM;
414 }
415 /*
416 * Allocate the subdevice structures. alloc_subdevice() is a
417 * convenient macro defined in comedidev.h.
418 *
419 * Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
420 */
421 if (alloc_subdevices(dev, n_subdevs) < 0) {
422 printk("cannot allocate subdevice data structures\n");
423 return -ENOMEM;
424 }
425
426 /* First, AI */
427 sdev_no = 0;
428 s = dev->subdevices + sdev_no;
429 s->private = devpriv->sprivs + sdev_no;
430 s->maxdata = (1 << thisboard->ai_bits) - 1;
431 s->range_table = thisboard->ai_range_table;
432 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
433 s->type = COMEDI_SUBD_AI;
434 s->n_chan = thisboard->n_ai_chans;
435 s->len_chanlist = s->n_chan;
436 s->insn_read = thisboard->ai_rinsn;
437 subpriv->iobase = dev->iobase + 0;
438 /* initialize the resource enable register by clearing it */
439 outb(0, subpriv->iobase + 3);
440 outb(0, subpriv->iobase + 4 + 3);
441
442 /* Next, AO */
443 ++sdev_no;
444 s = dev->subdevices + sdev_no;
445 s->private = devpriv->sprivs + sdev_no;
446 s->maxdata = (1 << thisboard->ao_bits) - 1;
447 s->range_table = thisboard->ao_range_table;
448 s->subdev_flags = SDF_READABLE;
449 s->type = COMEDI_SUBD_AO;
450 s->n_chan = thisboard->n_ao_chans;
451 s->len_chanlist = s->n_chan;
452 s->insn_read = thisboard->ao_rinsn;
453 s->insn_write = thisboard->ao_winsn;
454 subpriv->iobase = dev->iobase + 8;
455 /* initialize the resource enable register by clearing it */
456 outb(0, subpriv->iobase + 3);
457 outb(0, subpriv->iobase + 4 + 3);
458
459 ++sdev_no;
460 port = 0;
461 asic = 0;
462 for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
463 int byte_no;
464
465 s = dev->subdevices + sdev_no;
466 s->private = devpriv->sprivs + sdev_no;
467 s->maxdata = 1;
468 s->range_table = &range_digital;
469 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
470 s->type = COMEDI_SUBD_DIO;
471 s->insn_bits = pcmmio_dio_insn_bits;
472 s->insn_config = pcmmio_dio_insn_config;
Bill Pemberton214e7b52009-05-14 15:24:28 -0400473 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
Calin Culianu6baef152009-02-19 09:13:10 -0800474 subpriv->dio.intr.asic = -1;
475 subpriv->dio.intr.first_chan = -1;
476 subpriv->dio.intr.asic_chan = -1;
477 subpriv->dio.intr.num_asic_chans = -1;
478 subpriv->dio.intr.active = 0;
479 s->len_chanlist = 1;
480
481 /* save the ioport address for each 'port' of 8 channels in the
482 subdevice */
483 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
484 if (port >= PORTS_PER_ASIC) {
485 port = 0;
486 ++asic;
487 thisasic_chanct = 0;
488 }
489 subpriv->iobases[byte_no] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530490 devpriv->asics[asic].iobase + port;
Calin Culianu6baef152009-02-19 09:13:10 -0800491
492 if (thisasic_chanct <
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530493 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
494 && subpriv->dio.intr.asic < 0) {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000495 /*
496 * this is an interrupt subdevice,
497 * so setup the struct
498 */
Calin Culianu6baef152009-02-19 09:13:10 -0800499 subpriv->dio.intr.asic = asic;
500 subpriv->dio.intr.active = 0;
501 subpriv->dio.intr.stop_count = 0;
502 subpriv->dio.intr.first_chan = byte_no * 8;
503 subpriv->dio.intr.asic_chan = thisasic_chanct;
504 subpriv->dio.intr.num_asic_chans =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530505 s->n_chan - subpriv->dio.intr.first_chan;
Calin Culianu6baef152009-02-19 09:13:10 -0800506 s->cancel = pcmmio_cancel;
507 s->do_cmd = pcmmio_cmd;
508 s->do_cmdtest = pcmmio_cmdtest;
509 s->len_chanlist =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530510 subpriv->dio.intr.num_asic_chans;
Calin Culianu6baef152009-02-19 09:13:10 -0800511 }
512 thisasic_chanct += CHANS_PER_PORT;
513 }
514 spin_lock_init(&subpriv->dio.intr.spinlock);
515
516 chans_left -= s->n_chan;
517
518 if (!chans_left) {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000519 /*
520 * reset the asic to our first asic,
521 * to do intr subdevs
522 */
523 asic = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800524 port = 0;
525 }
526
527 }
528
529 init_asics(dev); /* clear out all the registers, basically */
530
531 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
532 if (irq[asic]
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530533 && request_irq(irq[asic], interrupt_pcmmio,
534 IRQF_SHARED, thisboard->name, dev)) {
Calin Culianu6baef152009-02-19 09:13:10 -0800535 int i;
536 /* unroll the allocated irqs.. */
537 for (i = asic - 1; i >= 0; --i) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700538 free_irq(irq[i], dev);
Calin Culianu6baef152009-02-19 09:13:10 -0800539 devpriv->asics[i].irq = irq[i] = 0;
540 }
541 irq[asic] = 0;
542 }
543 devpriv->asics[asic].irq = irq[asic];
544 }
545
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000546 dev->irq = irq[0]; /*
547 * grr.. wish comedi dev struct supported
548 * multiple irqs..
549 */
Calin Culianu6baef152009-02-19 09:13:10 -0800550
551 if (irq[0]) {
552 printk("irq: %u ", irq[0]);
553 if (irq[1] && thisboard->dio_num_asics == 2)
554 printk("second ASIC irq: %u ", irq[1]);
555 } else {
556 printk("(IRQ mode disabled) ");
557 }
558
559 printk("attached\n");
560
561 return 1;
562}
563
564/*
565 * _detach is called to deconfigure a device. It should deallocate
566 * resources.
567 * This function is also called when _attach() fails, so it should be
568 * careful not to release resources that were not necessarily
569 * allocated by _attach(). dev->private and dev->subdevices are
570 * deallocated automatically by the core.
571 */
Bill Pembertonda91b262009-04-09 16:07:03 -0400572static int pcmmio_detach(struct comedi_device *dev)
Calin Culianu6baef152009-02-19 09:13:10 -0800573{
574 int i;
575
576 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
577 if (dev->iobase)
578 release_region(dev->iobase, thisboard->total_iosize);
579
580 for (i = 0; i < MAX_ASICS; ++i) {
581 if (devpriv && devpriv->asics[i].irq)
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700582 free_irq(devpriv->asics[i].irq, dev);
Calin Culianu6baef152009-02-19 09:13:10 -0800583 }
584
585 if (devpriv && devpriv->sprivs)
586 kfree(devpriv->sprivs);
587
588 return 0;
589}
590
591/* DIO devices are slightly special. Although it is possible to
592 * implement the insn_read/insn_write interface, it is much more
593 * useful to applications if you implement the insn_bits interface.
594 * This allows packed reading/writing of the DIO channels. The
595 * comedi core can convert between insn_bits and insn_read/write */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530596static int pcmmio_dio_insn_bits(struct comedi_device *dev,
597 struct comedi_subdevice *s,
598 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -0800599{
600 int byte_no;
601 if (insn->n != 2)
602 return -EINVAL;
603
604 /* NOTE:
605 reading a 0 means this channel was high
606 writine a 0 sets the channel high
607 reading a 1 means this channel was low
608 writing a 1 means set this channel low
609
610 Therefore everything is always inverted. */
611
612 /* The insn data is a mask in data[0] and the new data
613 * in data[1], each channel cooresponding to a bit. */
614
615#ifdef DAMMIT_ITS_BROKEN
616 /* DEBUG */
617 printk("write mask: %08x data: %08x\n", data[0], data[1]);
618#endif
619
620 s->state = 0;
621
622 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
623 /* address of 8-bit port */
624 unsigned long ioaddr = subpriv->iobases[byte_no],
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530625 /* bit offset of port in 32-bit doubleword */
626 offset = byte_no * 8;
Calin Culianu6baef152009-02-19 09:13:10 -0800627 /* this 8-bit port's data */
628 unsigned char byte = 0,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530629 /* The write mask for this port (if any) */
630 write_mask_byte = (data[0] >> offset) & 0xff,
631 /* The data byte for this port */
632 data_byte = (data[1] >> offset) & 0xff;
Calin Culianu6baef152009-02-19 09:13:10 -0800633
634 byte = inb(ioaddr); /* read all 8-bits for this port */
635
636#ifdef DAMMIT_ITS_BROKEN
637 /* DEBUG */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530638 printk
639 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
640 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
641 offset, ioaddr, (unsigned)byte);
Calin Culianu6baef152009-02-19 09:13:10 -0800642#endif
643
644 if (write_mask_byte) {
Daniel Patrick Johnsond2d08952009-10-14 02:04:23 +0000645 /*
646 * this byte has some write_bits
647 * -- so set the output lines
648 */
649 /* clear bits for write mask */
650 byte &= ~write_mask_byte;
651 /* set to inverted data_byte */
652 byte |= ~data_byte & write_mask_byte;
Calin Culianu6baef152009-02-19 09:13:10 -0800653 /* Write out the new digital output state */
654 outb(byte, ioaddr);
655 }
656#ifdef DAMMIT_ITS_BROKEN
657 /* DEBUG */
658 printk("data_out_byte %02x\n", (unsigned)byte);
659#endif
660 /* save the digital input lines for this byte.. */
661 s->state |= ((unsigned int)byte) << offset;
662 }
663
664 /* now return the DIO lines to data[1] - note they came inverted! */
665 data[1] = ~s->state;
666
667#ifdef DAMMIT_ITS_BROKEN
668 /* DEBUG */
669 printk("s->state %08x data_out %08x\n", s->state, data[1]);
670#endif
671
672 return 2;
673}
674
675/* The input or output configuration of each digital line is
676 * configured by a special insn_config instruction. chanspec
677 * contains the channel to be changed, and data[0] contains the
678 * value COMEDI_INPUT or COMEDI_OUTPUT. */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530679static int pcmmio_dio_insn_config(struct comedi_device *dev,
680 struct comedi_subdevice *s,
681 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -0800682{
683 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530684 chan % 8;
Calin Culianu6baef152009-02-19 09:13:10 -0800685 unsigned long ioaddr;
686 unsigned char byte;
687
688 /* Compute ioaddr for this channel */
689 ioaddr = subpriv->iobases[byte_no];
690
691 /* NOTE:
692 writing a 0 an IO channel's bit sets the channel to INPUT
693 and pulls the line high as well
694
695 writing a 1 to an IO channel's bit pulls the line low
696
697 All channels are implicitly always in OUTPUT mode -- but when
698 they are high they can be considered to be in INPUT mode..
699
700 Thus, we only force channels low if the config request was INPUT,
701 otherwise we do nothing to the hardware. */
702
703 switch (data[0]) {
704 case INSN_CONFIG_DIO_OUTPUT:
705 /* save to io_bits -- don't actually do anything since
706 all input channels are also output channels... */
707 s->io_bits |= 1 << chan;
708 break;
709 case INSN_CONFIG_DIO_INPUT:
710 /* write a 0 to the actual register representing the channel
711 to set it to 'input'. 0 means "float high". */
712 byte = inb(ioaddr);
713 byte &= ~(1 << bit_no);
714 /**< set input channel to '0' */
715
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000716 /*
717 * write out byte -- this is the only time we actually affect
718 * the hardware as all channels are implicitly output
719 * -- but input channels are set to float-high
720 */
Calin Culianu6baef152009-02-19 09:13:10 -0800721 outb(byte, ioaddr);
722
723 /* save to io_bits */
724 s->io_bits &= ~(1 << chan);
725 break;
726
727 case INSN_CONFIG_DIO_QUERY:
728 /* retreive from shadow register */
729 data[1] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530730 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
Calin Culianu6baef152009-02-19 09:13:10 -0800731 return insn->n;
732 break;
733
734 default:
735 return -EINVAL;
736 break;
737 }
738
739 return insn->n;
740}
741
Bill Pembertonda91b262009-04-09 16:07:03 -0400742static void init_asics(struct comedi_device *dev)
Calin Culianu6baef152009-02-19 09:13:10 -0800743{ /* sets up an
744 ASIC chip to defaults */
745 int asic;
746
747 for (asic = 0; asic < thisboard->dio_num_asics; ++asic) {
748 int port, page;
749 unsigned long baseaddr = devpriv->asics[asic].iobase;
750
751 switch_page(dev, asic, 0); /* switch back to page 0 */
752
753 /* first, clear all the DIO port bits */
754 for (port = 0; port < PORTS_PER_ASIC; ++port)
755 outb(0, baseaddr + REG_PORT0 + port);
756
757 /* Next, clear all the paged registers for each page */
758 for (page = 1; page < NUM_PAGES; ++page) {
759 int reg;
760 /* now clear all the paged registers */
761 switch_page(dev, asic, page);
762 for (reg = FIRST_PAGED_REG;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530763 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
Calin Culianu6baef152009-02-19 09:13:10 -0800764 outb(0, baseaddr + reg);
765 }
766
767 /* DEBUG set rising edge interrupts on port0 of both asics */
768 /*switch_page(dev, asic, PAGE_POL);
769 outb(0xff, baseaddr + REG_POL0);
770 switch_page(dev, asic, PAGE_ENAB);
771 outb(0xff, baseaddr + REG_ENAB0); */
772 /* END DEBUG */
773
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000774 /* switch back to default page 0 */
775 switch_page(dev, asic, 0);
Calin Culianu6baef152009-02-19 09:13:10 -0800776 }
777}
778
Bill Pembertonda91b262009-04-09 16:07:03 -0400779static void switch_page(struct comedi_device *dev, int asic, int page)
Calin Culianu6baef152009-02-19 09:13:10 -0800780{
781 if (asic < 0 || asic >= thisboard->dio_num_asics)
782 return; /* paranoia */
783 if (page < 0 || page >= NUM_PAGES)
784 return; /* more paranoia */
785
786 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
787 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
788
789 /* now write out the shadow register */
790 outb(devpriv->asics[asic].pagelock,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530791 devpriv->asics[asic].iobase + REG_PAGELOCK);
Calin Culianu6baef152009-02-19 09:13:10 -0800792}
793
794#ifdef notused
Bill Pembertonda91b262009-04-09 16:07:03 -0400795static void lock_port(struct comedi_device *dev, int asic, int port)
Calin Culianu6baef152009-02-19 09:13:10 -0800796{
797 if (asic < 0 || asic >= thisboard->dio_num_asics)
798 return; /* paranoia */
799 if (port < 0 || port >= PORTS_PER_ASIC)
800 return; /* more paranoia */
801
802 devpriv->asics[asic].pagelock |= 0x1 << port;
803 /* now write out the shadow register */
804 outb(devpriv->asics[asic].pagelock,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530805 devpriv->asics[asic].iobase + REG_PAGELOCK);
Calin Culianu6baef152009-02-19 09:13:10 -0800806 return;
807}
808
Bill Pembertonda91b262009-04-09 16:07:03 -0400809static void unlock_port(struct comedi_device *dev, int asic, int port)
Calin Culianu6baef152009-02-19 09:13:10 -0800810{
811 if (asic < 0 || asic >= thisboard->dio_num_asics)
812 return; /* paranoia */
813 if (port < 0 || port >= PORTS_PER_ASIC)
814 return; /* more paranoia */
815 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
816 /* now write out the shadow register */
817 outb(devpriv->asics[asic].pagelock,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530818 devpriv->asics[asic].iobase + REG_PAGELOCK);
Calin Culianu6baef152009-02-19 09:13:10 -0800819}
820#endif /* notused */
821
Jiri Slaby70265d22009-03-26 09:34:06 +0100822static irqreturn_t interrupt_pcmmio(int irq, void *d)
Calin Culianu6baef152009-02-19 09:13:10 -0800823{
824 int asic, got1 = 0;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530825 struct comedi_device *dev = (struct comedi_device *)d;
Calin Culianu6baef152009-02-19 09:13:10 -0800826
827 for (asic = 0; asic < MAX_ASICS; ++asic) {
828 if (irq == devpriv->asics[asic].irq) {
829 unsigned long flags;
830 unsigned triggered = 0;
831 unsigned long iobase = devpriv->asics[asic].iobase;
832 /* it is an interrupt for ASIC #asic */
833 unsigned char int_pend;
834
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530835 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
836 flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800837
838 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
839
840 if (int_pend) {
841 int port;
842 for (port = 0; port < INTR_PORTS_PER_ASIC;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530843 ++port) {
Calin Culianu6baef152009-02-19 09:13:10 -0800844 if (int_pend & (0x1 << port)) {
845 unsigned char
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530846 io_lines_with_edges = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800847 switch_page(dev, asic,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530848 PAGE_INT_ID);
Calin Culianu6baef152009-02-19 09:13:10 -0800849 io_lines_with_edges =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530850 inb(iobase +
Calin Culianu6baef152009-02-19 09:13:10 -0800851 REG_INT_ID0 + port);
852
853 if (io_lines_with_edges)
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000854 /*
855 * clear pending
856 * interrupt
857 */
Calin Culianu6baef152009-02-19 09:13:10 -0800858 outb(0, iobase +
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530859 REG_INT_ID0 +
860 port);
Calin Culianu6baef152009-02-19 09:13:10 -0800861
862 triggered |=
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530863 io_lines_with_edges <<
864 port * 8;
Calin Culianu6baef152009-02-19 09:13:10 -0800865 }
866 }
867
868 ++got1;
869 }
870
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530871 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
872 flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800873
874 if (triggered) {
Bill Pemberton34c43922009-03-16 22:05:14 -0400875 struct comedi_subdevice *s;
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000876 /*
877 * TODO here: dispatch io lines to subdevs
878 * with commands..
879 */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530880 printk
881 ("PCMMIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
882 irq, asic, triggered);
Calin Culianu6baef152009-02-19 09:13:10 -0800883 for (s = dev->subdevices + 2;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530884 s < dev->subdevices + dev->n_subdevices;
885 ++s) {
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000886 /*
887 * this is an interrupt subdev,
888 * and it matches this asic!
889 */
890 if (subpriv->dio.intr.asic == asic) {
Calin Culianu6baef152009-02-19 09:13:10 -0800891 unsigned long flags;
892 unsigned oldevents;
893
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530894 spin_lock_irqsave(&subpriv->dio.
895 intr.spinlock,
896 flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800897
898 oldevents = s->async->events;
899
900 if (subpriv->dio.intr.active) {
901 unsigned mytrig =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530902 ((triggered >>
903 subpriv->dio.intr.asic_chan)
904 &
905 ((0x1 << subpriv->
906 dio.intr.
907 num_asic_chans) -
908 1)) << subpriv->
909 dio.intr.first_chan;
910 if (mytrig &
911 subpriv->dio.
912 intr.enabled_mask) {
913 unsigned int val
914 = 0;
Calin Culianu6baef152009-02-19 09:13:10 -0800915 unsigned int n,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530916 ch, len;
Calin Culianu6baef152009-02-19 09:13:10 -0800917
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530918 len =
919 s->
920 async->cmd.chanlist_len;
Calin Culianu6baef152009-02-19 09:13:10 -0800921 for (n = 0;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530922 n < len;
923 n++) {
Calin Culianu6baef152009-02-19 09:13:10 -0800924 ch = CR_CHAN(s->async->cmd.chanlist[n]);
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000925 if (mytrig & (1U << ch))
Calin Culianu6baef152009-02-19 09:13:10 -0800926 val |= (1U << n);
Calin Culianu6baef152009-02-19 09:13:10 -0800927 }
928 /* Write the scan to the buffer. */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530929 if (comedi_buf_put(s->async, ((short *)&val)[0])
930 &&
931 comedi_buf_put
932 (s->async,
933 ((short *)
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +0000934 &val)[1])) {
Calin Culianu6baef152009-02-19 09:13:10 -0800935 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
936 } else {
937 /* Overflow! Stop acquisition!! */
938 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
939 pcmmio_stop_intr
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530940 (dev,
941 s);
Calin Culianu6baef152009-02-19 09:13:10 -0800942 }
943
944 /* Check for end of acquisition. */
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530945 if (!subpriv->dio.intr.continuous) {
Calin Culianu6baef152009-02-19 09:13:10 -0800946 /* stop_src == TRIG_COUNT */
947 if (subpriv->dio.intr.stop_count > 0) {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530948 subpriv->dio.intr.stop_count--;
Calin Culianu6baef152009-02-19 09:13:10 -0800949 if (subpriv->dio.intr.stop_count == 0) {
950 s->async->events |= COMEDI_CB_EOA;
951 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
952 pcmmio_stop_intr
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530953 (dev,
954 s);
Calin Culianu6baef152009-02-19 09:13:10 -0800955 }
956 }
957 }
958 }
959 }
960
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530961 spin_unlock_irqrestore
962 (&subpriv->dio.intr.
963 spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -0800964
965 if (oldevents !=
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530966 s->async->events) {
Calin Culianu6baef152009-02-19 09:13:10 -0800967 comedi_event(dev, s);
968 }
969
970 }
971
972 }
973 }
974
975 }
976 }
977 if (!got1)
978 return IRQ_NONE; /* interrupt from other source */
979 return IRQ_HANDLED;
980}
981
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530982static void pcmmio_stop_intr(struct comedi_device *dev,
983 struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -0800984{
985 int nports, firstport, asic, port;
986
Bill Pembertonc3744132009-04-22 21:11:47 -0400987 asic = subpriv->dio.intr.asic;
988 if (asic < 0)
Calin Culianu6baef152009-02-19 09:13:10 -0800989 return; /* not an interrupt subdev */
990
991 subpriv->dio.intr.enabled_mask = 0;
992 subpriv->dio.intr.active = 0;
993 s->async->inttrig = 0;
994 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
995 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
996 switch_page(dev, asic, PAGE_ENAB);
997 for (port = firstport; port < firstport + nports; ++port) {
998 /* disable all intrs for this subdev.. */
999 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
1000 }
1001}
1002
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301003static int pcmmio_start_intr(struct comedi_device *dev,
1004 struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -08001005{
1006 if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
1007 /* An empty acquisition! */
1008 s->async->events |= COMEDI_CB_EOA;
1009 subpriv->dio.intr.active = 0;
1010 return 1;
1011 } else {
1012 unsigned bits = 0, pol_bits = 0, n;
1013 int nports, firstport, asic, port;
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001014 struct comedi_cmd *cmd = &s->async->cmd;
Calin Culianu6baef152009-02-19 09:13:10 -08001015
Bill Pembertonc3744132009-04-22 21:11:47 -04001016 asic = subpriv->dio.intr.asic;
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301017 if (asic < 0)
Calin Culianu6baef152009-02-19 09:13:10 -08001018 return 1; /* not an interrupt
1019 subdev */
1020 subpriv->dio.intr.enabled_mask = 0;
1021 subpriv->dio.intr.active = 1;
1022 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
1023 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
1024 if (cmd->chanlist) {
1025 for (n = 0; n < cmd->chanlist_len; n++) {
1026 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
1027 pol_bits |= (CR_AREF(cmd->chanlist[n])
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301028 || CR_RANGE(cmd->
1029 chanlist[n]) ? 1U : 0U)
1030 << CR_CHAN(cmd->chanlist[n]);
Calin Culianu6baef152009-02-19 09:13:10 -08001031 }
1032 }
1033 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301034 1) << subpriv->dio.intr.first_chan;
Calin Culianu6baef152009-02-19 09:13:10 -08001035 subpriv->dio.intr.enabled_mask = bits;
1036
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001037 {
1038 /*
1039 * the below code configures the board
1040 * to use a specific IRQ from 0-15.
1041 */
Calin Culianu6baef152009-02-19 09:13:10 -08001042 unsigned char b;
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001043 /*
1044 * set resource enable register
1045 * to enable IRQ operation
1046 */
Calin Culianu6baef152009-02-19 09:13:10 -08001047 outb(1 << 4, dev->iobase + 3);
1048 /* set bits 0-3 of b to the irq number from 0-15 */
1049 b = dev->irq & ((1 << 4) - 1);
1050 outb(b, dev->iobase + 2);
1051 /* done, we told the board what irq to use */
1052 }
1053
1054 switch_page(dev, asic, PAGE_ENAB);
1055 for (port = firstport; port < firstport + nports; ++port) {
1056 unsigned enab =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301057 bits >> (subpriv->dio.intr.first_chan + (port -
1058 firstport)
1059 * 8) & 0xff, pol =
1060 pol_bits >> (subpriv->dio.intr.first_chan +
1061 (port - firstport) * 8) & 0xff;
Calin Culianu6baef152009-02-19 09:13:10 -08001062 /* set enab intrs for this subdev.. */
1063 outb(enab,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301064 devpriv->asics[asic].iobase + REG_ENAB0 + port);
Calin Culianu6baef152009-02-19 09:13:10 -08001065 switch_page(dev, asic, PAGE_POL);
1066 outb(pol,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301067 devpriv->asics[asic].iobase + REG_ENAB0 + port);
Calin Culianu6baef152009-02-19 09:13:10 -08001068 }
1069 }
1070 return 0;
1071}
1072
Bill Pembertonda91b262009-04-09 16:07:03 -04001073static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -08001074{
1075 unsigned long flags;
1076
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001077 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001078 if (subpriv->dio.intr.active)
1079 pcmmio_stop_intr(dev, s);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001080 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001081
1082 return 0;
1083}
1084
1085/*
1086 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
1087 */
1088static int
Bill Pembertonda91b262009-04-09 16:07:03 -04001089pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301090 unsigned int trignum)
Calin Culianu6baef152009-02-19 09:13:10 -08001091{
1092 unsigned long flags;
1093 int event = 0;
1094
1095 if (trignum != 0)
1096 return -EINVAL;
1097
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001098 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001099 s->async->inttrig = 0;
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001100 if (subpriv->dio.intr.active)
Calin Culianu6baef152009-02-19 09:13:10 -08001101 event = pcmmio_start_intr(dev, s);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001102 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001103
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001104 if (event)
Calin Culianu6baef152009-02-19 09:13:10 -08001105 comedi_event(dev, s);
Calin Culianu6baef152009-02-19 09:13:10 -08001106
1107 return 1;
1108}
1109
1110/*
1111 * 'do_cmd' function for an 'INTERRUPT' subdevice.
1112 */
Bill Pembertonda91b262009-04-09 16:07:03 -04001113static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
Calin Culianu6baef152009-02-19 09:13:10 -08001114{
Bill Pembertonea6d0d42009-03-16 22:05:47 -04001115 struct comedi_cmd *cmd = &s->async->cmd;
Calin Culianu6baef152009-02-19 09:13:10 -08001116 unsigned long flags;
1117 int event = 0;
1118
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001119 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001120 subpriv->dio.intr.active = 1;
1121
1122 /* Set up end of acquisition. */
1123 switch (cmd->stop_src) {
1124 case TRIG_COUNT:
1125 subpriv->dio.intr.continuous = 0;
1126 subpriv->dio.intr.stop_count = cmd->stop_arg;
1127 break;
1128 default:
1129 /* TRIG_NONE */
1130 subpriv->dio.intr.continuous = 1;
1131 subpriv->dio.intr.stop_count = 0;
1132 break;
1133 }
1134
1135 /* Set up start of acquisition. */
1136 switch (cmd->start_src) {
1137 case TRIG_INT:
1138 s->async->inttrig = pcmmio_inttrig_start_intr;
1139 break;
1140 default:
1141 /* TRIG_NOW */
1142 event = pcmmio_start_intr(dev, s);
1143 break;
1144 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001145 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
Calin Culianu6baef152009-02-19 09:13:10 -08001146
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001147 if (event)
Calin Culianu6baef152009-02-19 09:13:10 -08001148 comedi_event(dev, s);
Calin Culianu6baef152009-02-19 09:13:10 -08001149
1150 return 0;
1151}
1152
Calin Culianu6baef152009-02-19 09:13:10 -08001153static int
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301154pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1155 struct comedi_cmd *cmd)
Calin Culianu6baef152009-02-19 09:13:10 -08001156{
Bill Pemberton0b8f7542009-05-14 15:24:29 -04001157 return comedi_pcm_cmdtest(dev, s, cmd);
Calin Culianu6baef152009-02-19 09:13:10 -08001158}
1159
1160static int adc_wait_ready(unsigned long iobase)
1161{
1162 unsigned long retry = 100000;
1163 while (retry--)
1164 if (inb(iobase + 3) & 0x80)
1165 return 0;
1166 return 1;
1167}
1168
1169/* All this is for AI and AO */
Bill Pembertonda91b262009-04-09 16:07:03 -04001170static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301171 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001172{
1173 int n;
1174 unsigned long iobase = subpriv->iobase;
1175
1176 /*
1177 1. write the CMD byte (to BASE+2)
1178 2. read junk lo byte (BASE+0)
1179 3. read junk hi byte (BASE+1)
1180 4. (mux settled so) write CMD byte again (BASE+2)
1181 5. read valid lo byte(BASE+0)
1182 6. read valid hi byte(BASE+1)
1183
1184 Additionally note that the BASE += 4 if the channel >= 8
1185 */
1186
1187 /* convert n samples */
1188 for (n = 0; n < insn->n; n++) {
1189 unsigned chan = CR_CHAN(insn->chanspec), range =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301190 CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
Calin Culianu6baef152009-02-19 09:13:10 -08001191 unsigned char command_byte = 0;
1192 unsigned iooffset = 0;
Bill Pemberton790c5542009-03-16 22:05:02 -04001193 short sample, adc_adjust = 0;
Calin Culianu6baef152009-02-19 09:13:10 -08001194
1195 if (chan > 7)
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001196 chan -= 8, iooffset = 4; /*
1197 * use the second dword
1198 * for channels > 7
1199 */
Calin Culianu6baef152009-02-19 09:13:10 -08001200
1201 if (aref != AREF_DIFF) {
1202 aref = AREF_GROUND;
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001203 command_byte |= 1 << 7; /*
1204 * set bit 7 to indicate
1205 * single-ended
1206 */
Calin Culianu6baef152009-02-19 09:13:10 -08001207 }
1208 if (range < 2)
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001209 adc_adjust = 0x8000; /*
1210 * bipolar ranges
1211 * (-5,5 .. -10,10 need to be
1212 * adjusted -- that is.. they
1213 * need to wrap around by
1214 * adding 0x8000
1215 */
Calin Culianu6baef152009-02-19 09:13:10 -08001216
1217 if (chan % 2) {
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001218 command_byte |= 1 << 6; /*
1219 * odd-numbered channels
1220 * have bit 6 set
1221 */
Calin Culianu6baef152009-02-19 09:13:10 -08001222 }
1223
1224 /* select the channel, bits 4-5 == chan/2 */
1225 command_byte |= ((chan / 2) & 0x3) << 4;
1226
1227 /* set the range, bits 2-3 */
1228 command_byte |= (range & 0x3) << 2;
1229
1230 /* need to do this twice to make sure mux settled */
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001231 /* chan/range/aref select */
1232 outb(command_byte, iobase + iooffset + 2);
Calin Culianu6baef152009-02-19 09:13:10 -08001233
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001234 /* wait for the adc to say it finised the conversion */
1235 adc_wait_ready(iobase + iooffset);
Calin Culianu6baef152009-02-19 09:13:10 -08001236
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001237 /* select the chan/range/aref AGAIN */
1238 outb(command_byte, iobase + iooffset + 2);
Calin Culianu6baef152009-02-19 09:13:10 -08001239
1240 adc_wait_ready(iobase + iooffset);
1241
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001242 /* read data lo byte */
1243 sample = inb(iobase + iooffset + 0);
1244
1245 /* read data hi byte */
1246 sample |= inb(iobase + iooffset + 1) << 8;
Calin Culianu6baef152009-02-19 09:13:10 -08001247 sample += adc_adjust; /* adjustment .. munge data */
1248 data[n] = sample;
1249 }
1250 /* return the number of samples read/written */
1251 return n;
1252}
1253
Bill Pembertonda91b262009-04-09 16:07:03 -04001254static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301255 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001256{
1257 int n;
1258 for (n = 0; n < insn->n; n++) {
1259 unsigned chan = CR_CHAN(insn->chanspec);
1260 if (chan < s->n_chan)
1261 data[n] = subpriv->ao.shadow_samples[chan];
1262 }
1263 return n;
1264}
1265
1266static int wait_dac_ready(unsigned long iobase)
1267{
1268 unsigned long retry = 100000L;
1269
1270 /* This may seem like an absurd way to handle waiting and violates the
1271 "no busy waiting" policy. The fact is that the hardware is
1272 normally so fast that we usually only need one time through the loop
1273 anyway. The longer timeout is for rare occasions and for detecting
1274 non-existant hardware. */
1275
1276 while (retry--) {
1277 if (inb(iobase + 3) & 0x80)
1278 return 0;
1279
1280 }
1281 return 1;
1282}
1283
Bill Pembertonda91b262009-04-09 16:07:03 -04001284static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301285 struct comedi_insn *insn, unsigned int *data)
Calin Culianu6baef152009-02-19 09:13:10 -08001286{
1287 int n;
1288 unsigned iobase = subpriv->iobase, iooffset = 0;
1289
1290 for (n = 0; n < insn->n; n++) {
1291 unsigned chan = CR_CHAN(insn->chanspec), range =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301292 CR_RANGE(insn->chanspec);
Calin Culianu6baef152009-02-19 09:13:10 -08001293 if (chan < s->n_chan) {
1294 unsigned char command_byte = 0, range_byte =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301295 range & ((1 << 4) - 1);
Calin Culianu6baef152009-02-19 09:13:10 -08001296 if (chan >= 4)
1297 chan -= 4, iooffset += 4;
1298 /* set the range.. */
1299 outb(range_byte, iobase + iooffset + 0);
1300 outb(0, iobase + iooffset + 1);
1301
1302 /* tell it to begin */
1303 command_byte = (chan << 1) | 0x60;
1304 outb(command_byte, iobase + iooffset + 2);
1305
1306 wait_dac_ready(iobase + iooffset);
1307
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001308 /* low order byte */
1309 outb(data[n] & 0xff, iobase + iooffset + 0);
1310
1311 /* high order byte */
1312 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);
1313
1314 /*
1315 * set bit 4 of command byte to indicate
1316 * data is loaded and trigger conversion
1317 */
1318 command_byte = 0x70 | (chan << 1);
Calin Culianu6baef152009-02-19 09:13:10 -08001319 /* trigger converion */
1320 outb(command_byte, iobase + iooffset + 2);
1321
1322 wait_dac_ready(iobase + iooffset);
1323
Daniel Patrick Johnson013f2302009-10-14 02:04:24 +00001324 /* save to shadow register for ao_rinsn */
1325 subpriv->ao.shadow_samples[chan] = data[n];
Calin Culianu6baef152009-02-19 09:13:10 -08001326 }
1327 }
1328 return n;
1329}
1330
1331/*
1332 * A convenient macro that defines init_module() and cleanup_module(),
1333 * as necessary.
1334 */
1335COMEDI_INITCLEANUP(driver);