staging: comedi: comedi_test: simplify time since last AI scan
[deliverable/linux.git] / drivers / staging / comedi / drivers / ii_pci20kc.c
CommitLineData
eb3a3c1b 1/*
c325f9a9
HS
2 * ii_pci20kc.c
3 * Driver for Intelligent Instruments PCI-20001C carrier board and modules.
eb3a3c1b 4 *
c325f9a9
HS
5 * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de>
6 * with suggestions from David Schleef 16.06.2000
7 */
8
9/*
10 * Driver: ii_pci20kc
11 * Description: Intelligent Instruments PCI-20001C carrier board
2a669fac 12 * Devices: [Intelligent Instrumentation] PCI-20001C (ii_pci20kc)
c325f9a9
HS
13 * Author: Markus Kempf <kempf@matsci.uni-sb.de>
14 * Status: works
eb3a3c1b 15 *
c325f9a9
HS
16 * Supports the PCI-20001C-1a and PCI-20001C-2a carrier boards. The
17 * -2a version has 32 on-board DIO channels. Three add-on modules
18 * can be added to the carrier board for additional functionality.
eb3a3c1b 19 *
c325f9a9
HS
20 * Supported add-on modules:
21 * PCI-20006M-1 1 channel, 16-bit analog output module
22 * PCI-20006M-2 2 channel, 16-bit analog output module
23 * PCI-20341M-1A 4 channel, 16-bit analog input module
eb3a3c1b 24 *
c325f9a9
HS
25 * Options:
26 * 0 Board base address
27 * 1 IRQ (not-used)
eb3a3c1b 28 */
eb3a3c1b 29
ce157f80 30#include <linux/module.h>
2584cf83 31#include <linux/io.h>
eb3a3c1b
MK
32#include "../comedidev.h"
33
9abeb196
HS
34/*
35 * Register I/O map
36 */
27fc26f9 37#define II20K_SIZE 0x400
f0e32df3 38#define II20K_MOD_OFFSET 0x100
9abeb196 39#define II20K_ID_REG 0x00
c98f4011
HS
40#define II20K_ID_MOD1_EMPTY BIT(7)
41#define II20K_ID_MOD2_EMPTY BIT(6)
42#define II20K_ID_MOD3_EMPTY BIT(5)
9abeb196
HS
43#define II20K_ID_MASK 0x1f
44#define II20K_ID_PCI20001C_1A 0x1b /* no on-board DIO */
45#define II20K_ID_PCI20001C_2A 0x1d /* on-board DIO */
46#define II20K_MOD_STATUS_REG 0x40
c98f4011
HS
47#define II20K_MOD_STATUS_IRQ_MOD1 BIT(7)
48#define II20K_MOD_STATUS_IRQ_MOD2 BIT(6)
49#define II20K_MOD_STATUS_IRQ_MOD3 BIT(5)
9abeb196
HS
50#define II20K_DIO0_REG 0x80
51#define II20K_DIO1_REG 0x81
52#define II20K_DIR_ENA_REG 0x82
c98f4011
HS
53#define II20K_DIR_DIO3_OUT BIT(7)
54#define II20K_DIR_DIO2_OUT BIT(6)
55#define II20K_BUF_DISAB_DIO3 BIT(5)
56#define II20K_BUF_DISAB_DIO2 BIT(4)
57#define II20K_DIR_DIO1_OUT BIT(3)
58#define II20K_DIR_DIO0_OUT BIT(2)
59#define II20K_BUF_DISAB_DIO1 BIT(1)
60#define II20K_BUF_DISAB_DIO0 BIT(0)
9abeb196 61#define II20K_CTRL01_REG 0x83
c98f4011
HS
62#define II20K_CTRL01_SET BIT(7)
63#define II20K_CTRL01_DIO0_IN BIT(4)
64#define II20K_CTRL01_DIO1_IN BIT(1)
9abeb196
HS
65#define II20K_DIO2_REG 0xc0
66#define II20K_DIO3_REG 0xc1
67#define II20K_CTRL23_REG 0xc3
c98f4011
HS
68#define II20K_CTRL23_SET BIT(7)
69#define II20K_CTRL23_DIO2_IN BIT(4)
70#define II20K_CTRL23_DIO3_IN BIT(1)
9abeb196 71
87a61614
HS
72#define II20K_ID_PCI20006M_1 0xe2 /* 1 AO channels */
73#define II20K_ID_PCI20006M_2 0xe3 /* 2 AO channels */
74#define II20K_AO_STRB_REG(x) (0x0b + ((x) * 0x08))
75#define II20K_AO_LSB_REG(x) (0x0d + ((x) * 0x08))
76#define II20K_AO_MSB_REG(x) (0x0e + ((x) * 0x08))
77#define II20K_AO_STRB_BOTH_REG 0x1b
78
c325f9a9 79#define II20K_ID_PCI20341M_1 0x77 /* 4 AI channels */
3aa5e811 80#define II20K_AI_STATUS_CMD_REG 0x01
c98f4011
HS
81#define II20K_AI_STATUS_CMD_BUSY BIT(7)
82#define II20K_AI_STATUS_CMD_HW_ENA BIT(1)
83#define II20K_AI_STATUS_CMD_EXT_START BIT(0)
3aa5e811
HS
84#define II20K_AI_LSB_REG 0x02
85#define II20K_AI_MSB_REG 0x03
86#define II20K_AI_PACER_RESET_REG 0x04
87#define II20K_AI_16BIT_DATA_REG 0x06
88#define II20K_AI_CONF_REG 0x10
c98f4011 89#define II20K_AI_CONF_ENA BIT(2)
3aa5e811 90#define II20K_AI_OPT_REG 0x11
c98f4011
HS
91#define II20K_AI_OPT_TRIG_ENA BIT(5)
92#define II20K_AI_OPT_TRIG_INV BIT(4)
3aa5e811 93#define II20K_AI_OPT_TIMEBASE(x) (((x) & 0x3) << 1)
c98f4011 94#define II20K_AI_OPT_BURST_MODE BIT(0)
3aa5e811 95#define II20K_AI_STATUS_REG 0x12
c98f4011
HS
96#define II20K_AI_STATUS_INT BIT(7)
97#define II20K_AI_STATUS_TRIG BIT(6)
98#define II20K_AI_STATUS_TRIG_ENA BIT(5)
99#define II20K_AI_STATUS_PACER_ERR BIT(2)
100#define II20K_AI_STATUS_DATA_ERR BIT(1)
101#define II20K_AI_STATUS_SET_TIME_ERR BIT(0)
3aa5e811
HS
102#define II20K_AI_LAST_CHAN_ADDR_REG 0x13
103#define II20K_AI_CUR_ADDR_REG 0x14
104#define II20K_AI_SET_TIME_REG 0x15
105#define II20K_AI_DELAY_LSB_REG 0x16
106#define II20K_AI_DELAY_MSB_REG 0x17
107#define II20K_AI_CHAN_ADV_REG 0x18
108#define II20K_AI_CHAN_RESET_REG 0x19
109#define II20K_AI_START_TRIG_REG 0x1a
110#define II20K_AI_COUNT_RESET_REG 0x1b
111#define II20K_AI_CHANLIST_REG 0x80
c98f4011 112#define II20K_AI_CHANLIST_ONBOARD_ONLY BIT(5)
3aa5e811 113#define II20K_AI_CHANLIST_GAIN(x) (((x) & 0x3) << 3)
c98f4011 114#define II20K_AI_CHANLIST_MUX_ENA BIT(2)
3aa5e811
HS
115#define II20K_AI_CHANLIST_CHAN(x) (((x) & 0x3) << 0)
116#define II20K_AI_CHANLIST_LEN 0x80
eb3a3c1b 117
87a61614
HS
118/* the AO range is set by jumpers on the 20006M module */
119static const struct comedi_lrange ii20k_ao_ranges = {
120 3, {
121 BIP_RANGE(5), /* Chan 0 - W1/W3 in Chan 1 - W2/W4 in */
122 UNI_RANGE(10), /* Chan 0 - W1/W3 out Chan 1 - W2/W4 in */
123 BIP_RANGE(10) /* Chan 0 - W1/W3 in Chan 1 - W2/W4 out */
124 }
125};
33662447 126
3aa5e811
HS
127static const struct comedi_lrange ii20k_ai_ranges = {
128 4, {
129 BIP_RANGE(5), /* gain 1 */
130 BIP_RANGE(0.5), /* gain 10 */
131 BIP_RANGE(0.05), /* gain 100 */
132 BIP_RANGE(0.025) /* gain 200 */
133 },
33662447
HS
134};
135
57b2161f
HS
136static void __iomem *ii20k_module_iobase(struct comedi_device *dev,
137 struct comedi_subdevice *s)
138{
d6e497b9 139 return dev->mmio + (s->index + 1) * II20K_MOD_OFFSET;
57b2161f
HS
140}
141
f0e32df3 142static int ii20k_ao_insn_write(struct comedi_device *dev,
0a85b6f0 143 struct comedi_subdevice *s,
f0e32df3
HS
144 struct comedi_insn *insn,
145 unsigned int *data)
eb3a3c1b 146{
57b2161f 147 void __iomem *iobase = ii20k_module_iobase(dev, s);
87a61614 148 unsigned int chan = CR_CHAN(insn->chanspec);
87a61614 149 int i;
eb3a3c1b 150
87a61614 151 for (i = 0; i < insn->n; i++) {
1fd142ce 152 unsigned int val = data[i];
87a61614 153
1fd142ce 154 s->readback[chan] = val;
c3c17d2e 155
d172b63a
HS
156 /* munge the offset binary data to 2's complement */
157 val = comedi_offset_munge(s, val);
87a61614
HS
158
159 writeb(val & 0xff, iobase + II20K_AO_LSB_REG(chan));
160 writeb((val >> 8) & 0xff, iobase + II20K_AO_MSB_REG(chan));
161 writeb(0x00, iobase + II20K_AO_STRB_REG(chan));
eb3a3c1b
MK
162 }
163
87a61614 164 return insn->n;
eb3a3c1b
MK
165}
166
83b4d991
HS
167static int ii20k_ai_eoc(struct comedi_device *dev,
168 struct comedi_subdevice *s,
169 struct comedi_insn *insn,
170 unsigned long context)
3aa5e811
HS
171{
172 void __iomem *iobase = ii20k_module_iobase(dev, s);
173 unsigned char status;
174
83b4d991
HS
175 status = readb(iobase + II20K_AI_STATUS_REG);
176 if ((status & II20K_AI_STATUS_INT) == 0)
177 return 0;
178 return -EBUSY;
3aa5e811
HS
179}
180
181static void ii20k_ai_setup(struct comedi_device *dev,
182 struct comedi_subdevice *s,
183 unsigned int chanspec)
184{
185 void __iomem *iobase = ii20k_module_iobase(dev, s);
186 unsigned int chan = CR_CHAN(chanspec);
187 unsigned int range = CR_RANGE(chanspec);
188 unsigned char val;
189
190 /* initialize module */
191 writeb(II20K_AI_CONF_ENA, iobase + II20K_AI_CONF_REG);
192
193 /* software conversion */
194 writeb(0, iobase + II20K_AI_STATUS_CMD_REG);
195
196 /* set the time base for the settling time counter based on the gain */
197 val = (range < 3) ? II20K_AI_OPT_TIMEBASE(0) : II20K_AI_OPT_TIMEBASE(2);
198 writeb(val, iobase + II20K_AI_OPT_REG);
199
200 /* set the settling time counter based on the gain */
201 val = (range < 2) ? 0x58 : (range < 3) ? 0x93 : 0x99;
202 writeb(val, iobase + II20K_AI_SET_TIME_REG);
203
204 /* set number of input channels */
205 writeb(1, iobase + II20K_AI_LAST_CHAN_ADDR_REG);
206
207 /* set the channel list byte */
208 val = II20K_AI_CHANLIST_ONBOARD_ONLY |
209 II20K_AI_CHANLIST_MUX_ENA |
210 II20K_AI_CHANLIST_GAIN(range) |
211 II20K_AI_CHANLIST_CHAN(chan);
212 writeb(val, iobase + II20K_AI_CHANLIST_REG);
213
214 /* reset settling time counter and trigger delay counter */
215 writeb(0, iobase + II20K_AI_COUNT_RESET_REG);
216
217 /* reset channel scanner */
218 writeb(0, iobase + II20K_AI_CHAN_RESET_REG);
219}
220
f0e32df3 221static int ii20k_ai_insn_read(struct comedi_device *dev,
0a85b6f0 222 struct comedi_subdevice *s,
f0e32df3
HS
223 struct comedi_insn *insn,
224 unsigned int *data)
eb3a3c1b 225{
57b2161f 226 void __iomem *iobase = ii20k_module_iobase(dev, s);
3aa5e811
HS
227 int ret;
228 int i;
3555a4c3 229
3aa5e811 230 ii20k_ai_setup(dev, s, insn->chanspec);
3555a4c3 231
3aa5e811
HS
232 for (i = 0; i < insn->n; i++) {
233 unsigned int val;
eb3a3c1b 234
3aa5e811
HS
235 /* generate a software start convert signal */
236 readb(iobase + II20K_AI_PACER_RESET_REG);
eb3a3c1b 237
83b4d991 238 ret = comedi_timeout(dev, s, insn, ii20k_ai_eoc, 0);
3aa5e811
HS
239 if (ret)
240 return ret;
eb3a3c1b 241
3aa5e811
HS
242 val = readb(iobase + II20K_AI_LSB_REG);
243 val |= (readb(iobase + II20K_AI_MSB_REG) << 8);
eb3a3c1b 244
d172b63a
HS
245 /* munge the 2's complement data to offset binary */
246 data[i] = comedi_offset_munge(s, val);
3aa5e811
HS
247 }
248
249 return insn->n;
842525c6
HS
250}
251
9abeb196
HS
252static void ii20k_dio_config(struct comedi_device *dev,
253 struct comedi_subdevice *s)
eb3a3c1b 254{
9abeb196
HS
255 unsigned char ctrl01 = 0;
256 unsigned char ctrl23 = 0;
257 unsigned char dir_ena = 0;
eb3a3c1b 258
9abeb196 259 /* port 0 - channels 0-7 */
eb3a3c1b 260 if (s->io_bits & 0x000000ff) {
9abeb196
HS
261 /* output port */
262 ctrl01 &= ~II20K_CTRL01_DIO0_IN;
263 dir_ena &= ~II20K_BUF_DISAB_DIO0;
264 dir_ena |= II20K_DIR_DIO0_OUT;
eb3a3c1b 265 } else {
9abeb196
HS
266 /* input port */
267 ctrl01 |= II20K_CTRL01_DIO0_IN;
268 dir_ena &= ~II20K_DIR_DIO0_OUT;
eb3a3c1b 269 }
9abeb196
HS
270
271 /* port 1 - channels 8-15 */
eb3a3c1b 272 if (s->io_bits & 0x0000ff00) {
9abeb196
HS
273 /* output port */
274 ctrl01 &= ~II20K_CTRL01_DIO1_IN;
275 dir_ena &= ~II20K_BUF_DISAB_DIO1;
276 dir_ena |= II20K_DIR_DIO1_OUT;
eb3a3c1b 277 } else {
9abeb196
HS
278 /* input port */
279 ctrl01 |= II20K_CTRL01_DIO1_IN;
280 dir_ena &= ~II20K_DIR_DIO1_OUT;
eb3a3c1b 281 }
9abeb196
HS
282
283 /* port 2 - channels 16-23 */
eb3a3c1b 284 if (s->io_bits & 0x00ff0000) {
9abeb196
HS
285 /* output port */
286 ctrl23 &= ~II20K_CTRL23_DIO2_IN;
287 dir_ena &= ~II20K_BUF_DISAB_DIO2;
288 dir_ena |= II20K_DIR_DIO2_OUT;
eb3a3c1b 289 } else {
9abeb196
HS
290 /* input port */
291 ctrl23 |= II20K_CTRL23_DIO2_IN;
292 dir_ena &= ~II20K_DIR_DIO2_OUT;
eb3a3c1b 293 }
9abeb196
HS
294
295 /* port 3 - channels 24-31 */
eb3a3c1b 296 if (s->io_bits & 0xff000000) {
9abeb196
HS
297 /* output port */
298 ctrl23 &= ~II20K_CTRL23_DIO3_IN;
299 dir_ena &= ~II20K_BUF_DISAB_DIO3;
300 dir_ena |= II20K_DIR_DIO3_OUT;
eb3a3c1b 301 } else {
9abeb196
HS
302 /* input port */
303 ctrl23 |= II20K_CTRL23_DIO3_IN;
304 dir_ena &= ~II20K_DIR_DIO3_OUT;
eb3a3c1b 305 }
9abeb196
HS
306
307 ctrl23 |= II20K_CTRL01_SET;
308 ctrl23 |= II20K_CTRL23_SET;
309
310 /* order is important */
d6e497b9
HS
311 writeb(ctrl01, dev->mmio + II20K_CTRL01_REG);
312 writeb(ctrl23, dev->mmio + II20K_CTRL23_REG);
313 writeb(dir_ena, dev->mmio + II20K_DIR_ENA_REG);
eb3a3c1b
MK
314}
315
9abeb196
HS
316static int ii20k_dio_insn_config(struct comedi_device *dev,
317 struct comedi_subdevice *s,
318 struct comedi_insn *insn,
319 unsigned int *data)
eb3a3c1b 320{
5dacadcc
HS
321 unsigned int chan = CR_CHAN(insn->chanspec);
322 unsigned int mask;
323 int ret;
324
325 if (chan < 8)
326 mask = 0x000000ff;
327 else if (chan < 16)
328 mask = 0x0000ff00;
329 else if (chan < 24)
330 mask = 0x00ff0000;
63d6e8ff 331 else
5dacadcc
HS
332 mask = 0xff000000;
333
334 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
335 if (ret)
336 return ret;
63d6e8ff 337
9abeb196
HS
338 ii20k_dio_config(dev, s);
339
340 return insn->n;
eb3a3c1b
MK
341}
342
9abeb196
HS
343static int ii20k_dio_insn_bits(struct comedi_device *dev,
344 struct comedi_subdevice *s,
345 struct comedi_insn *insn,
346 unsigned int *data)
eb3a3c1b 347{
9b13bc1d 348 unsigned int mask;
9abeb196 349
9b13bc1d 350 mask = comedi_dio_update_state(s, data);
9abeb196 351 if (mask) {
9abeb196
HS
352 if (mask & 0x000000ff)
353 writeb((s->state >> 0) & 0xff,
d6e497b9 354 dev->mmio + II20K_DIO0_REG);
9abeb196
HS
355 if (mask & 0x0000ff00)
356 writeb((s->state >> 8) & 0xff,
d6e497b9 357 dev->mmio + II20K_DIO1_REG);
9abeb196
HS
358 if (mask & 0x00ff0000)
359 writeb((s->state >> 16) & 0xff,
d6e497b9 360 dev->mmio + II20K_DIO2_REG);
9abeb196
HS
361 if (mask & 0xff000000)
362 writeb((s->state >> 24) & 0xff,
d6e497b9 363 dev->mmio + II20K_DIO3_REG);
9abeb196 364 }
eb3a3c1b 365
d6e497b9
HS
366 data[1] = readb(dev->mmio + II20K_DIO0_REG);
367 data[1] |= readb(dev->mmio + II20K_DIO1_REG) << 8;
368 data[1] |= readb(dev->mmio + II20K_DIO2_REG) << 16;
369 data[1] |= readb(dev->mmio + II20K_DIO3_REG) << 24;
63d6e8ff
HS
370
371 return insn->n;
372}
373
f0e32df3 374static int ii20k_init_module(struct comedi_device *dev,
3aa5e811 375 struct comedi_subdevice *s)
f0e32df3 376{
57b2161f 377 void __iomem *iobase = ii20k_module_iobase(dev, s);
f0e32df3 378 unsigned char id;
1fd142ce 379 int ret;
f0e32df3 380
f0e32df3
HS
381 id = readb(iobase + II20K_ID_REG);
382 switch (id) {
87a61614
HS
383 case II20K_ID_PCI20006M_1:
384 case II20K_ID_PCI20006M_2:
f0e32df3
HS
385 /* Analog Output subdevice */
386 s->type = COMEDI_SUBD_AO;
387 s->subdev_flags = SDF_WRITABLE;
87a61614 388 s->n_chan = (id == II20K_ID_PCI20006M_2) ? 2 : 1;
f0e32df3 389 s->maxdata = 0xffff;
87a61614 390 s->range_table = &ii20k_ao_ranges;
f0e32df3 391 s->insn_write = ii20k_ao_insn_write;
1fd142ce
HS
392
393 ret = comedi_alloc_subdev_readback(s);
394 if (ret)
395 return ret;
f0e32df3 396 break;
c325f9a9 397 case II20K_ID_PCI20341M_1:
f0e32df3
HS
398 /* Analog Input subdevice */
399 s->type = COMEDI_SUBD_AI;
3aa5e811 400 s->subdev_flags = SDF_READABLE | SDF_DIFF;
f0e32df3
HS
401 s->n_chan = 4;
402 s->maxdata = 0xffff;
3aa5e811 403 s->range_table = &ii20k_ai_ranges;
f0e32df3 404 s->insn_read = ii20k_ai_insn_read;
f0e32df3 405 break;
f0e32df3
HS
406 default:
407 s->type = COMEDI_SUBD_UNUSED;
408 break;
409 }
410
411 return 0;
412}
413
c325f9a9
HS
414static int ii20k_attach(struct comedi_device *dev,
415 struct comedi_devconfig *it)
0f8409dc 416{
9abeb196 417 struct comedi_subdevice *s;
27fc26f9 418 unsigned int membase;
9abeb196
HS
419 unsigned char id;
420 bool has_dio;
421 int ret;
0f8409dc 422
27fc26f9
IA
423 membase = it->options[0];
424 if (!membase || (membase & ~(0x100000 - II20K_SIZE))) {
425 dev_warn(dev->class_dev,
426 "%s: invalid memory address specified\n",
427 dev->board_name);
428 return -EINVAL;
429 }
430
431 if (!request_mem_region(membase, II20K_SIZE, dev->board_name)) {
432 dev_warn(dev->class_dev, "%s: I/O mem conflict (%#x,%u)\n",
433 dev->board_name, membase, II20K_SIZE);
434 return -EIO;
435 }
436 dev->iobase = membase; /* actually, a memory address */
437
438 dev->mmio = ioremap(membase, II20K_SIZE);
439 if (!dev->mmio)
440 return -ENOMEM;
0f8409dc 441
d6e497b9 442 id = readb(dev->mmio + II20K_ID_REG);
9abeb196
HS
443 switch (id & II20K_ID_MASK) {
444 case II20K_ID_PCI20001C_1A:
27a2234e 445 has_dio = false;
9abeb196
HS
446 break;
447 case II20K_ID_PCI20001C_2A:
448 has_dio = true;
449 break;
450 default:
451 return -ENODEV;
0f8409dc 452 }
0f8409dc 453
9abeb196
HS
454 ret = comedi_alloc_subdevices(dev, 4);
455 if (ret)
456 return ret;
457
f0e32df3
HS
458 s = &dev->subdevices[0];
459 if (id & II20K_ID_MOD1_EMPTY) {
460 s->type = COMEDI_SUBD_UNUSED;
461 } else {
3aa5e811 462 ret = ii20k_init_module(dev, s);
f0e32df3
HS
463 if (ret)
464 return ret;
465 }
466
467 s = &dev->subdevices[1];
468 if (id & II20K_ID_MOD2_EMPTY) {
469 s->type = COMEDI_SUBD_UNUSED;
470 } else {
3aa5e811 471 ret = ii20k_init_module(dev, s);
f0e32df3
HS
472 if (ret)
473 return ret;
474 }
475
476 s = &dev->subdevices[2];
477 if (id & II20K_ID_MOD3_EMPTY) {
478 s->type = COMEDI_SUBD_UNUSED;
479 } else {
3aa5e811 480 ret = ii20k_init_module(dev, s);
f0e32df3
HS
481 if (ret)
482 return ret;
0f8409dc
HS
483 }
484
9abeb196
HS
485 /* Digital I/O subdevice */
486 s = &dev->subdevices[3];
487 if (has_dio) {
488 s->type = COMEDI_SUBD_DIO;
489 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
490 s->n_chan = 32;
491 s->maxdata = 1;
492 s->range_table = &range_digital;
493 s->insn_bits = ii20k_dio_insn_bits;
494 s->insn_config = ii20k_dio_insn_config;
495
496 /* default all channels to input */
497 ii20k_dio_config(dev, s);
498 } else {
499 s->type = COMEDI_SUBD_UNUSED;
500 }
0f8409dc 501
c325f9a9 502 return 0;
0f8409dc
HS
503}
504
27fc26f9
IA
505static void ii20k_detach(struct comedi_device *dev)
506{
507 if (dev->mmio)
508 iounmap(dev->mmio);
509 if (dev->iobase) /* actually, a memory address */
510 release_mem_region(dev->iobase, II20K_SIZE);
511}
512
c325f9a9 513static struct comedi_driver ii20k_driver = {
2396472f
HS
514 .driver_name = "ii_pci20kc",
515 .module = THIS_MODULE,
c325f9a9 516 .attach = ii20k_attach,
27fc26f9 517 .detach = ii20k_detach,
2396472f 518};
c325f9a9 519module_comedi_driver(ii20k_driver);
90f703d3
AT
520
521MODULE_AUTHOR("Comedi http://www.comedi.org");
bb46c4ec 522MODULE_DESCRIPTION("Comedi driver for Intelligent Instruments PCI-20001C");
90f703d3 523MODULE_LICENSE("GPL");
This page took 0.935443 seconds and 5 git commands to generate.