Commit | Line | Data |
---|---|---|
ef2ccffb | 1 | /* |
17d30981 HS |
2 | * ni_6527.c |
3 | * Comedi driver for National Instruments PCI-6527 | |
4 | * | |
5 | * COMEDI - Linux Control and Measurement Device Interface | |
6 | * Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.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 | */ | |
ef2ccffb DS |
18 | |
19 | /* | |
17d30981 HS |
20 | * Driver: ni_6527 |
21 | * Description: National Instruments 6527 | |
bd3a1db5 | 22 | * Devices: [National Instruments] PCI-6527 (pci-6527), PXI-6527 (pxi-6527) |
17d30981 HS |
23 | * Author: David A. Schleef <ds@schleef.org> |
24 | * Updated: Sat, 25 Jan 2003 13:24:40 -0800 | |
25 | * Status: works | |
26 | * | |
27 | * Configuration Options: not applicable, uses PCI auto config | |
ef2ccffb DS |
28 | */ |
29 | ||
ce157f80 | 30 | #include <linux/module.h> |
25436dc9 | 31 | #include <linux/interrupt.h> |
33782dd5 | 32 | |
2bacde88 | 33 | #include "../comedi_pci.h" |
ef2ccffb | 34 | |
17d30981 HS |
35 | /* |
36 | * PCI BAR1 - Register memory map | |
37 | * | |
38 | * Manuals (available from ftp://ftp.natinst.com/support/manuals) | |
39 | * 370106b.pdf 6527 Register Level Programmer Manual | |
40 | */ | |
33b73a35 HS |
41 | #define NI6527_DI_REG(x) (0x00 + (x)) |
42 | #define NI6527_DO_REG(x) (0x03 + (x)) | |
94ff05bc | 43 | #define NI6527_ID_REG 0x06 |
73261fc0 HS |
44 | #define NI6527_CLR_REG 0x07 |
45 | #define NI6527_CLR_EDGE (1 << 3) | |
46 | #define NI6527_CLR_OVERFLOW (1 << 2) | |
47 | #define NI6527_CLR_FILT (1 << 1) | |
48 | #define NI6527_CLR_INTERVAL (1 << 0) | |
49 | #define NI6527_CLR_IRQS (NI6527_CLR_EDGE | NI6527_CLR_OVERFLOW) | |
50 | #define NI6527_CLR_RESET_FILT (NI6527_CLR_FILT | NI6527_CLR_INTERVAL) | |
054fd2f6 | 51 | #define NI6527_FILT_INTERVAL_REG(x) (0x08 + (x)) |
e3f6ce20 | 52 | #define NI6527_FILT_ENA_REG(x) (0x0c + (x)) |
8d7d72cc HS |
53 | #define NI6527_STATUS_REG 0x14 |
54 | #define NI6527_STATUS_IRQ (1 << 2) | |
55 | #define NI6527_STATUS_OVERFLOW (1 << 1) | |
56 | #define NI6527_STATUS_EDGE (1 << 0) | |
cd3295a9 HS |
57 | #define NI6527_CTRL_REG 0x15 |
58 | #define NI6527_CTRL_FALLING (1 << 4) | |
59 | #define NI6527_CTRL_RISING (1 << 3) | |
60 | #define NI6527_CTRL_IRQ (1 << 2) | |
61 | #define NI6527_CTRL_OVERFLOW (1 << 1) | |
62 | #define NI6527_CTRL_EDGE (1 << 0) | |
63 | #define NI6527_CTRL_DISABLE_IRQS 0 | |
64 | #define NI6527_CTRL_ENABLE_IRQS (NI6527_CTRL_FALLING | \ | |
65 | NI6527_CTRL_RISING | \ | |
66 | NI6527_CTRL_IRQ | NI6527_CTRL_EDGE) | |
e1eb3605 HS |
67 | #define NI6527_RISING_EDGE_REG(x) (0x18 + (x)) |
68 | #define NI6527_FALLING_EDGE_REG(x) (0x20 + (x)) | |
ef2ccffb | 69 | |
1787b7c1 HS |
70 | enum ni6527_boardid { |
71 | BOARD_PCI6527, | |
72 | BOARD_PXI6527, | |
73 | }; | |
16d38ca3 | 74 | |
1787b7c1 | 75 | struct ni6527_board { |
ef2ccffb | 76 | const char *name; |
16d38ca3 BP |
77 | }; |
78 | ||
79 | static const struct ni6527_board ni6527_boards[] = { | |
1787b7c1 HS |
80 | [BOARD_PCI6527] = { |
81 | .name = "pci-6527", | |
82 | }, | |
83 | [BOARD_PXI6527] = { | |
84 | .name = "pxi-6527", | |
85 | }, | |
ef2ccffb DS |
86 | }; |
87 | ||
f4c6b319 | 88 | struct ni6527_private { |
ef2ccffb DS |
89 | unsigned int filter_interval; |
90 | unsigned int filter_enable; | |
f4c6b319 BP |
91 | }; |
92 | ||
054fd2f6 HS |
93 | static void ni6527_set_filter_interval(struct comedi_device *dev, |
94 | unsigned int val) | |
95 | { | |
96 | struct ni6527_private *devpriv = dev->private; | |
054fd2f6 HS |
97 | |
98 | if (val != devpriv->filter_interval) { | |
53eeeaa6 HS |
99 | writeb(val & 0xff, dev->mmio + NI6527_FILT_INTERVAL_REG(0)); |
100 | writeb((val >> 8) & 0xff, | |
101 | dev->mmio + NI6527_FILT_INTERVAL_REG(1)); | |
102 | writeb((val >> 16) & 0x0f, | |
103 | dev->mmio + NI6527_FILT_INTERVAL_REG(2)); | |
054fd2f6 | 104 | |
53eeeaa6 | 105 | writeb(NI6527_CLR_INTERVAL, dev->mmio + NI6527_CLR_REG); |
054fd2f6 HS |
106 | |
107 | devpriv->filter_interval = val; | |
108 | } | |
109 | } | |
110 | ||
e3f6ce20 HS |
111 | static void ni6527_set_filter_enable(struct comedi_device *dev, |
112 | unsigned int val) | |
113 | { | |
53eeeaa6 HS |
114 | writeb(val & 0xff, dev->mmio + NI6527_FILT_ENA_REG(0)); |
115 | writeb((val >> 8) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(1)); | |
116 | writeb((val >> 16) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(2)); | |
e3f6ce20 HS |
117 | } |
118 | ||
0a85b6f0 MT |
119 | static int ni6527_di_insn_config(struct comedi_device *dev, |
120 | struct comedi_subdevice *s, | |
c6d9681b HS |
121 | struct comedi_insn *insn, |
122 | unsigned int *data) | |
ef2ccffb | 123 | { |
9a1a6cf8 | 124 | struct ni6527_private *devpriv = dev->private; |
c6d9681b | 125 | unsigned int chan = CR_CHAN(insn->chanspec); |
ef2ccffb DS |
126 | unsigned int interval; |
127 | ||
c6d9681b HS |
128 | switch (data[0]) { |
129 | case INSN_CONFIG_FILTER: | |
130 | /* | |
131 | * The deglitch filter interval is specified in nanoseconds. | |
132 | * The hardware supports intervals in 200ns increments. Round | |
133 | * the user values up and return the actual interval. | |
134 | */ | |
ef2ccffb DS |
135 | interval = (data[1] + 100) / 200; |
136 | data[1] = interval * 200; | |
137 | ||
c6d9681b HS |
138 | if (interval) { |
139 | ni6527_set_filter_interval(dev, interval); | |
140 | devpriv->filter_enable |= 1 << chan; | |
141 | } else { | |
142 | devpriv->filter_enable &= ~(1 << chan); | |
143 | } | |
144 | ni6527_set_filter_enable(dev, devpriv->filter_enable); | |
145 | break; | |
146 | default: | |
147 | return -EINVAL; | |
ef2ccffb DS |
148 | } |
149 | ||
c6d9681b | 150 | return insn->n; |
ef2ccffb DS |
151 | } |
152 | ||
0a85b6f0 MT |
153 | static int ni6527_di_insn_bits(struct comedi_device *dev, |
154 | struct comedi_subdevice *s, | |
33b73a35 HS |
155 | struct comedi_insn *insn, |
156 | unsigned int *data) | |
ef2ccffb | 157 | { |
33b73a35 HS |
158 | unsigned int val; |
159 | ||
53eeeaa6 HS |
160 | val = readb(dev->mmio + NI6527_DI_REG(0)); |
161 | val |= (readb(dev->mmio + NI6527_DI_REG(1)) << 8); | |
162 | val |= (readb(dev->mmio + NI6527_DI_REG(2)) << 16); | |
9a1a6cf8 | 163 | |
33b73a35 | 164 | data[1] = val; |
ef2ccffb | 165 | |
a2714e3e | 166 | return insn->n; |
ef2ccffb DS |
167 | } |
168 | ||
0a85b6f0 MT |
169 | static int ni6527_do_insn_bits(struct comedi_device *dev, |
170 | struct comedi_subdevice *s, | |
b3ff824a HS |
171 | struct comedi_insn *insn, |
172 | unsigned int *data) | |
ef2ccffb | 173 | { |
b3ff824a | 174 | unsigned int mask; |
9a1a6cf8 | 175 | |
b3ff824a HS |
176 | mask = comedi_dio_update_state(s, data); |
177 | if (mask) { | |
178 | /* Outputs are inverted */ | |
33b73a35 HS |
179 | unsigned int val = s->state ^ 0xffffff; |
180 | ||
181 | if (mask & 0x0000ff) | |
53eeeaa6 | 182 | writeb(val & 0xff, dev->mmio + NI6527_DO_REG(0)); |
33b73a35 | 183 | if (mask & 0x00ff00) |
53eeeaa6 HS |
184 | writeb((val >> 8) & 0xff, |
185 | dev->mmio + NI6527_DO_REG(1)); | |
33b73a35 | 186 | if (mask & 0xff0000) |
53eeeaa6 HS |
187 | writeb((val >> 16) & 0xff, |
188 | dev->mmio + NI6527_DO_REG(2)); | |
ef2ccffb | 189 | } |
b3ff824a | 190 | |
ef2ccffb DS |
191 | data[1] = s->state; |
192 | ||
a2714e3e | 193 | return insn->n; |
ef2ccffb DS |
194 | } |
195 | ||
70265d24 | 196 | static irqreturn_t ni6527_interrupt(int irq, void *d) |
ef2ccffb | 197 | { |
71b5f4f1 | 198 | struct comedi_device *dev = d; |
87fe6ebd | 199 | struct comedi_subdevice *s = dev->read_subdev; |
ef2ccffb DS |
200 | unsigned int status; |
201 | ||
53eeeaa6 | 202 | status = readb(dev->mmio + NI6527_STATUS_REG); |
8d7d72cc | 203 | if (!(status & NI6527_STATUS_IRQ)) |
ef2ccffb DS |
204 | return IRQ_NONE; |
205 | ||
8d7d72cc | 206 | if (status & NI6527_STATUS_EDGE) { |
191db090 | 207 | comedi_buf_write_samples(s, &s->state, 1); |
0c56f057 | 208 | comedi_handle_events(dev, s); |
8d7d72cc HS |
209 | } |
210 | ||
53eeeaa6 | 211 | writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG); |
ef2ccffb | 212 | |
ef2ccffb DS |
213 | return IRQ_HANDLED; |
214 | } | |
215 | ||
0a85b6f0 MT |
216 | static int ni6527_intr_cmdtest(struct comedi_device *dev, |
217 | struct comedi_subdevice *s, | |
218 | struct comedi_cmd *cmd) | |
ef2ccffb DS |
219 | { |
220 | int err = 0; | |
ef2ccffb | 221 | |
27020ffe | 222 | /* Step 1 : check if triggers are trivially valid */ |
ef2ccffb | 223 | |
6664e2ef IA |
224 | err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); |
225 | err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_OTHER); | |
226 | err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW); | |
227 | err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
228 | err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT); | |
ef2ccffb DS |
229 | |
230 | if (err) | |
231 | return 1; | |
232 | ||
27020ffe HS |
233 | /* Step 2a : make sure trigger sources are unique */ |
234 | /* Step 2b : and mutually compatible */ | |
ef2ccffb | 235 | |
effa7a28 | 236 | /* Step 3: check if arguments are trivially valid */ |
ef2ccffb | 237 | |
6664e2ef IA |
238 | err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); |
239 | err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); | |
240 | err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); | |
241 | err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, | |
242 | cmd->chanlist_len); | |
243 | err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); | |
ef2ccffb DS |
244 | |
245 | if (err) | |
246 | return 3; | |
247 | ||
88eb626a | 248 | /* Step 4: fix up any arguments */ |
ef2ccffb | 249 | |
88eb626a | 250 | /* Step 5: check channel list if it exists */ |
ef2ccffb DS |
251 | |
252 | return 0; | |
253 | } | |
254 | ||
0a85b6f0 MT |
255 | static int ni6527_intr_cmd(struct comedi_device *dev, |
256 | struct comedi_subdevice *s) | |
ef2ccffb | 257 | { |
53eeeaa6 HS |
258 | writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG); |
259 | writeb(NI6527_CTRL_ENABLE_IRQS, dev->mmio + NI6527_CTRL_REG); | |
ef2ccffb DS |
260 | |
261 | return 0; | |
262 | } | |
263 | ||
0a85b6f0 MT |
264 | static int ni6527_intr_cancel(struct comedi_device *dev, |
265 | struct comedi_subdevice *s) | |
ef2ccffb | 266 | { |
53eeeaa6 | 267 | writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG); |
ef2ccffb DS |
268 | |
269 | return 0; | |
270 | } | |
271 | ||
0a85b6f0 MT |
272 | static int ni6527_intr_insn_bits(struct comedi_device *dev, |
273 | struct comedi_subdevice *s, | |
274 | struct comedi_insn *insn, unsigned int *data) | |
ef2ccffb | 275 | { |
ef2ccffb | 276 | data[1] = 0; |
a2714e3e | 277 | return insn->n; |
ef2ccffb DS |
278 | } |
279 | ||
e1eb3605 | 280 | static void ni6527_set_edge_detection(struct comedi_device *dev, |
110f9e68 | 281 | unsigned int mask, |
e1eb3605 HS |
282 | unsigned int rising, |
283 | unsigned int falling) | |
ef2ccffb | 284 | { |
110f9e68 IA |
285 | unsigned int i; |
286 | ||
287 | rising &= mask; | |
288 | falling &= mask; | |
289 | for (i = 0; i < 2; i++) { | |
290 | if (mask & 0xff) { | |
291 | if (~mask & 0xff) { | |
292 | /* preserve rising-edge detection channels */ | |
53eeeaa6 | 293 | rising |= readb(dev->mmio + |
110f9e68 IA |
294 | NI6527_RISING_EDGE_REG(i)) & |
295 | (~mask & 0xff); | |
296 | /* preserve falling-edge detection channels */ | |
53eeeaa6 | 297 | falling |= readb(dev->mmio + |
110f9e68 IA |
298 | NI6527_FALLING_EDGE_REG(i)) & |
299 | (~mask & 0xff); | |
300 | } | |
301 | /* update rising-edge detection channels */ | |
53eeeaa6 HS |
302 | writeb(rising & 0xff, |
303 | dev->mmio + NI6527_RISING_EDGE_REG(i)); | |
110f9e68 IA |
304 | /* update falling-edge detection channels */ |
305 | writeb(falling & 0xff, | |
53eeeaa6 | 306 | dev->mmio + NI6527_FALLING_EDGE_REG(i)); |
110f9e68 IA |
307 | } |
308 | rising >>= 8; | |
309 | falling >>= 8; | |
310 | mask >>= 8; | |
311 | } | |
e1eb3605 HS |
312 | } |
313 | ||
314 | static int ni6527_intr_insn_config(struct comedi_device *dev, | |
315 | struct comedi_subdevice *s, | |
316 | struct comedi_insn *insn, | |
317 | unsigned int *data) | |
318 | { | |
110f9e68 IA |
319 | unsigned int mask = 0xffffffff; |
320 | unsigned int rising, falling, shift; | |
321 | ||
e1eb3605 HS |
322 | switch (data[0]) { |
323 | case INSN_CONFIG_CHANGE_NOTIFY: | |
324 | /* check_insn_config_length() does not check this instruction */ | |
325 | if (insn->n != 3) | |
326 | return -EINVAL; | |
110f9e68 IA |
327 | rising = data[1]; |
328 | falling = data[2]; | |
329 | ni6527_set_edge_detection(dev, mask, rising, falling); | |
330 | break; | |
331 | case INSN_CONFIG_DIGITAL_TRIG: | |
332 | /* check trigger number */ | |
333 | if (data[1] != 0) | |
334 | return -EINVAL; | |
335 | /* check digital trigger operation */ | |
336 | switch (data[2]) { | |
337 | case COMEDI_DIGITAL_TRIG_DISABLE: | |
338 | rising = 0; | |
339 | falling = 0; | |
340 | break; | |
341 | case COMEDI_DIGITAL_TRIG_ENABLE_EDGES: | |
342 | /* check shift amount */ | |
343 | shift = data[3]; | |
344 | if (shift >= s->n_chan) { | |
345 | mask = 0; | |
346 | rising = 0; | |
347 | falling = 0; | |
348 | } else { | |
349 | mask <<= shift; | |
350 | rising = data[4] << shift; | |
351 | falling = data[5] << shift; | |
352 | } | |
353 | break; | |
354 | default: | |
355 | return -EINVAL; | |
356 | } | |
357 | ni6527_set_edge_detection(dev, mask, rising, falling); | |
e1eb3605 HS |
358 | break; |
359 | default: | |
ef2ccffb | 360 | return -EINVAL; |
e1eb3605 | 361 | } |
ef2ccffb | 362 | |
e1eb3605 | 363 | return insn->n; |
ef2ccffb DS |
364 | } |
365 | ||
5dfbd505 HS |
366 | static void ni6527_reset(struct comedi_device *dev) |
367 | { | |
5dfbd505 HS |
368 | /* disable deglitch filters on all channels */ |
369 | ni6527_set_filter_enable(dev, 0); | |
370 | ||
e44c5392 IA |
371 | /* disable edge detection */ |
372 | ni6527_set_edge_detection(dev, 0xffffffff, 0, 0); | |
373 | ||
5dfbd505 | 374 | writeb(NI6527_CLR_IRQS | NI6527_CLR_RESET_FILT, |
53eeeaa6 HS |
375 | dev->mmio + NI6527_CLR_REG); |
376 | writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG); | |
5dfbd505 HS |
377 | } |
378 | ||
a690b7e5 | 379 | static int ni6527_auto_attach(struct comedi_device *dev, |
1787b7c1 | 380 | unsigned long context) |
ef2ccffb | 381 | { |
750af5e5 | 382 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
1787b7c1 | 383 | const struct ni6527_board *board = NULL; |
9a1a6cf8 | 384 | struct ni6527_private *devpriv; |
34c43922 | 385 | struct comedi_subdevice *s; |
ef2ccffb DS |
386 | int ret; |
387 | ||
1787b7c1 HS |
388 | if (context < ARRAY_SIZE(ni6527_boards)) |
389 | board = &ni6527_boards[context]; | |
390 | if (!board) | |
391 | return -ENODEV; | |
392 | dev->board_ptr = board; | |
393 | dev->board_name = board->name; | |
394 | ||
0bdab509 | 395 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
396 | if (!devpriv) |
397 | return -ENOMEM; | |
ef2ccffb | 398 | |
ea19a2a7 HS |
399 | ret = comedi_pci_enable(dev); |
400 | if (ret) | |
401 | return ret; | |
402 | ||
53eeeaa6 HS |
403 | dev->mmio = pci_ioremap_bar(pcidev, 1); |
404 | if (!dev->mmio) | |
6dc71205 | 405 | return -ENOMEM; |
ef2ccffb | 406 | |
94ff05bc | 407 | /* make sure this is actually a 6527 device */ |
53eeeaa6 | 408 | if (readb(dev->mmio + NI6527_ID_REG) != 0x27) |
94ff05bc | 409 | return -ENODEV; |
ef2ccffb | 410 | |
5dfbd505 HS |
411 | ni6527_reset(dev); |
412 | ||
21799633 HS |
413 | ret = request_irq(pcidev->irq, ni6527_interrupt, IRQF_SHARED, |
414 | dev->board_name, dev); | |
415 | if (ret == 0) | |
416 | dev->irq = pcidev->irq; | |
417 | ||
2f0b9d08 | 418 | ret = comedi_alloc_subdevices(dev, 3); |
8b6c5694 | 419 | if (ret) |
ef2ccffb DS |
420 | return ret; |
421 | ||
ea19a2a7 | 422 | /* Digital Input subdevice */ |
3d30dca5 | 423 | s = &dev->subdevices[0]; |
ea19a2a7 HS |
424 | s->type = COMEDI_SUBD_DI; |
425 | s->subdev_flags = SDF_READABLE; | |
426 | s->n_chan = 24; | |
427 | s->maxdata = 1; | |
428 | s->range_table = &range_digital; | |
429 | s->insn_config = ni6527_di_insn_config; | |
430 | s->insn_bits = ni6527_di_insn_bits; | |
431 | ||
432 | /* Digital Output subdevice */ | |
3d30dca5 | 433 | s = &dev->subdevices[1]; |
ea19a2a7 HS |
434 | s->type = COMEDI_SUBD_DO; |
435 | s->subdev_flags = SDF_WRITABLE; | |
436 | s->n_chan = 24; | |
437 | s->maxdata = 1; | |
438 | s->range_table = &range_digital; | |
439 | s->insn_bits = ni6527_do_insn_bits; | |
ef2ccffb | 440 | |
21799633 | 441 | /* Edge detection interrupt subdevice */ |
3d30dca5 | 442 | s = &dev->subdevices[2]; |
21799633 HS |
443 | if (dev->irq) { |
444 | dev->read_subdev = s; | |
445 | s->type = COMEDI_SUBD_DI; | |
446 | s->subdev_flags = SDF_READABLE | SDF_CMD_READ; | |
447 | s->n_chan = 1; | |
448 | s->maxdata = 1; | |
449 | s->range_table = &range_digital; | |
450 | s->insn_config = ni6527_intr_insn_config; | |
451 | s->insn_bits = ni6527_intr_insn_bits; | |
f50cebb9 | 452 | s->len_chanlist = 1; |
21799633 HS |
453 | s->do_cmdtest = ni6527_intr_cmdtest; |
454 | s->do_cmd = ni6527_intr_cmd; | |
455 | s->cancel = ni6527_intr_cancel; | |
456 | } else { | |
457 | s->type = COMEDI_SUBD_UNUSED; | |
458 | } | |
ef2ccffb | 459 | |
ef2ccffb DS |
460 | return 0; |
461 | } | |
462 | ||
484ecc95 | 463 | static void ni6527_detach(struct comedi_device *dev) |
ef2ccffb | 464 | { |
53eeeaa6 | 465 | if (dev->mmio) |
5dfbd505 | 466 | ni6527_reset(dev); |
aac307f9 | 467 | comedi_pci_detach(dev); |
ef2ccffb DS |
468 | } |
469 | ||
fcdd5ecd | 470 | static struct comedi_driver ni6527_driver = { |
903ac7a2 HS |
471 | .driver_name = "ni_6527", |
472 | .module = THIS_MODULE, | |
473 | .auto_attach = ni6527_auto_attach, | |
474 | .detach = ni6527_detach, | |
fcdd5ecd IA |
475 | }; |
476 | ||
a690b7e5 | 477 | static int ni6527_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 478 | const struct pci_device_id *id) |
727b286b | 479 | { |
b8f4ac23 | 480 | return comedi_pci_auto_config(dev, &ni6527_driver, id->driver_data); |
727b286b AT |
481 | } |
482 | ||
41e043fc | 483 | static const struct pci_device_id ni6527_pci_table[] = { |
1787b7c1 HS |
484 | { PCI_VDEVICE(NI, 0x2b10), BOARD_PXI6527 }, |
485 | { PCI_VDEVICE(NI, 0x2b20), BOARD_PCI6527 }, | |
dcf9cfd3 HS |
486 | { 0 } |
487 | }; | |
488 | MODULE_DEVICE_TABLE(pci, ni6527_pci_table); | |
489 | ||
7ae09413 | 490 | static struct pci_driver ni6527_pci_driver = { |
36edca14 | 491 | .name = "ni_6527", |
dcf9cfd3 HS |
492 | .id_table = ni6527_pci_table, |
493 | .probe = ni6527_pci_probe, | |
9901a4d7 | 494 | .remove = comedi_pci_auto_unconfig, |
727b286b | 495 | }; |
7ae09413 | 496 | module_comedi_pci_driver(ni6527_driver, ni6527_pci_driver); |
3c323c01 IA |
497 | |
498 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
8159fd1b | 499 | MODULE_DESCRIPTION("Comedi driver for National Instruments PCI-6527"); |
3c323c01 | 500 | MODULE_LICENSE("GPL"); |