Commit | Line | Data |
---|---|---|
011f01bf IA |
1 | /******************************************************************************* |
2 | comedi/drivers/pci1723.c | |
3 | ||
4 | COMEDI - Linux Control and Measurement Device Interface | |
5 | Copyright (C) 2000 David A. Schleef <ds@schleef.org> | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | ||
21 | *******************************************************************************/ | |
22 | /* | |
23 | Driver: adv_pci1723 | |
24 | Description: Advantech PCI-1723 | |
25 | Author: yonggang <rsmgnu@gmail.com>, Ian Abbott <abbotti@mev.co.uk> | |
26 | Devices: [Advantech] PCI-1723 (adv_pci1723) | |
27 | Updated: Mon, 14 Apr 2008 15:12:56 +0100 | |
28 | Status: works | |
29 | ||
30 | Configuration Options: | |
31 | [0] - PCI bus of device (optional) | |
32 | [1] - PCI slot of device (optional) | |
33 | ||
34 | If bus/slot is not specified, the first supported | |
35 | PCI device found will be used. | |
36 | ||
37 | Subdevice 0 is 8-channel AO, 16-bit, range +/- 10 V. | |
38 | ||
39 | Subdevice 1 is 16-channel DIO. The channels are configurable as input or | |
40 | output in 2 groups (0 to 7, 8 to 15). Configuring any channel implicitly | |
41 | configures all channels in the same group. | |
42 | ||
43 | TODO: | |
44 | ||
45 | 1. Add the two milliamp ranges to the AO subdevice (0 to 20 mA, 4 to 20 mA). | |
46 | 2. Read the initial ranges and values of the AO subdevice at start-up instead | |
47 | of reinitializing them. | |
48 | 3. Implement calibration. | |
49 | */ | |
50 | ||
51 | #include "../comedidev.h" | |
52 | ||
53 | #include "comedi_pci.h" | |
54 | ||
55 | #define ADVANTECH_VENDOR 0x13fe /* Advantech PCI vendor ID */ | |
56 | ||
d6d9bd32 | 57 | /* hardware types of the cards */ |
011f01bf IA |
58 | #define TYPE_PCI1723 0 |
59 | ||
60 | #define IORANGE_1723 0x2A | |
61 | ||
62 | /* all the registers for the pci1723 board */ | |
63 | #define PCI1723_DA(N) ((N)<<1) /* W: D/A register N (0 to 7) */ | |
64 | ||
65 | #define PCI1723_SYN_SET 0x12 /*synchronized set register */ | |
66 | #define PCI1723_ALL_CHNNELE_SYN_STROBE 0x12 /*synchronized status register */ | |
67 | ||
68 | #define PCI1723_RANGE_CALIBRATION_MODE 0x14 /* range and calibration mode */ | |
69 | #define PCI1723_RANGE_CALIBRATION_STATUS 0x14 /* range and calibration status */ | |
70 | ||
71 | #define PCI1723_CONTROL_CMD_CALIBRATION_FUN 0x16 /* SADC control command for calibration function */ | |
72 | #define PCI1723_STATUS_CMD_CALIBRATION_FUN 0x16 /* SADC control status for calibration function */ | |
73 | ||
74 | #define PCI1723_CALIBRATION_PARA_STROBE 0x18 /* Calibration parameter strobe */ | |
75 | ||
76 | #define PCI1723_DIGITAL_IO_PORT_SET 0x1A /* Digital I/O port setting */ | |
77 | #define PCI1723_DIGITAL_IO_PORT_MODE 0x1A /* Digital I/O port mode */ | |
78 | ||
79 | #define PCI1723_WRITE_DIGITAL_OUTPUT_CMD 0x1C /* Write digital output command */ | |
80 | #define PCI1723_READ_DIGITAL_INPUT_DATA 0x1C /* Read digital input data */ | |
81 | ||
82 | #define PCI1723_WRITE_CAL_CMD 0x1E /* Write calibration command */ | |
83 | #define PCI1723_READ_CAL_STATUS 0x1E /* Read calibration status */ | |
84 | ||
85 | #define PCI1723_SYN_STROBE 0x20 /* Synchronized strobe */ | |
86 | ||
87 | #define PCI1723_RESET_ALL_CHN_STROBE 0x22 /* Reset all D/A channels strobe */ | |
88 | ||
89 | #define PCI1723_RESET_CAL_CONTROL_STROBE 0x24 /* Reset the calibration controller strobe */ | |
90 | ||
91 | #define PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE 0x26 /* Change D/A channels output type strobe */ | |
92 | ||
93 | #define PCI1723_SELECT_CALIBRATION 0x28 /* Select the calibration Ref_V */ | |
94 | ||
d6d9bd32 | 95 | /* static unsigned short pci_list_builded=0; =1 list of card is know */ |
011f01bf | 96 | |
9ced1de6 | 97 | static const struct comedi_lrange range_pci1723 = { 1, { |
011f01bf IA |
98 | BIP_RANGE(10) |
99 | } | |
100 | }; | |
101 | ||
102 | /* | |
103 | * Board descriptions for pci1723 boards. | |
104 | */ | |
006449f0 | 105 | struct pci1723_board { |
011f01bf | 106 | const char *name; |
d6d9bd32 | 107 | int vendor_id; /* PCI vendor a device ID of card */ |
011f01bf IA |
108 | int device_id; |
109 | int iorange; | |
110 | char cardtype; | |
d6d9bd32 BP |
111 | int n_aochan; /* num of D/A chans */ |
112 | int n_diochan; /* num of DIO chans */ | |
113 | int ao_maxdata; /* resolution of D/A */ | |
114 | const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */ | |
006449f0 | 115 | }; |
011f01bf | 116 | |
006449f0 | 117 | static const struct pci1723_board boardtypes[] = { |
011f01bf IA |
118 | { |
119 | name: "pci1723", | |
120 | vendor_id:ADVANTECH_VENDOR, | |
121 | device_id:0x1723, | |
122 | iorange: IORANGE_1723, | |
123 | cardtype:TYPE_PCI1723, | |
124 | n_aochan:8, | |
125 | n_diochan:16, | |
126 | ao_maxdata:0xffff, | |
127 | rangelist_ao:&range_pci1723, | |
128 | }, | |
129 | }; | |
130 | ||
131 | /* This is used by modprobe to translate PCI IDs to drivers. Should | |
132 | * only be used for PCI and ISA-PnP devices */ | |
133 | static DEFINE_PCI_DEVICE_TABLE(pci1723_pci_table) = { | |
134 | {PCI_VENDOR_ID_ADVANTECH, 0x1723, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
135 | {0} | |
136 | }; | |
137 | ||
138 | MODULE_DEVICE_TABLE(pci, pci1723_pci_table); | |
139 | ||
140 | /* | |
139dfbdf | 141 | * The struct comedi_driver structure tells the Comedi core module |
011f01bf IA |
142 | * which functions to call to configure/deconfigure (attach/detach) |
143 | * the board, and also about the kernel module that contains | |
144 | * the device code. | |
145 | */ | |
da91b269 BP |
146 | static int pci1723_attach(struct comedi_device *dev, struct comedi_devconfig *it); |
147 | static int pci1723_detach(struct comedi_device *dev); | |
011f01bf | 148 | |
006449f0 | 149 | #define n_boardtypes (sizeof(boardtypes)/sizeof(struct pci1723_board)) |
011f01bf | 150 | |
139dfbdf | 151 | static struct comedi_driver driver_pci1723 = { |
011f01bf IA |
152 | driver_name:"adv_pci1723", |
153 | module:THIS_MODULE, | |
154 | attach:pci1723_attach, | |
155 | detach:pci1723_detach, | |
156 | }; | |
157 | ||
158 | /* this structure is for data unique to this hardware driver. */ | |
006449f0 | 159 | struct pci1723_private { |
d6d9bd32 | 160 | int valid; /* card is usable; */ |
011f01bf IA |
161 | |
162 | struct pci_dev *pcidev; | |
d6d9bd32 | 163 | unsigned char da_range[8]; /* D/A output range for each channel */ |
011f01bf | 164 | |
d6d9bd32 | 165 | short ao_data[8]; /* data output buffer */ |
006449f0 | 166 | }; |
011f01bf IA |
167 | |
168 | /*the following macro to make it easy to | |
169 | * access the private structure. | |
170 | */ | |
006449f0 | 171 | #define devpriv ((struct pci1723_private *)dev->private) |
011f01bf IA |
172 | |
173 | #define this_board boardtypes | |
174 | ||
175 | /* | |
176 | * the pci1723 card reset; | |
177 | */ | |
da91b269 | 178 | static int pci1723_reset(struct comedi_device *dev) |
011f01bf IA |
179 | { |
180 | int i; | |
181 | DPRINTK("adv_pci1723 EDBG: BGN: pci1723_reset(...)\n"); | |
182 | ||
d6d9bd32 | 183 | outw(0x01, dev->iobase + PCI1723_SYN_SET); /* set synchronous output mode */ |
011f01bf IA |
184 | |
185 | for (i = 0; i < 8; i++) { | |
d6d9bd32 | 186 | /* set all outputs to 0V */ |
011f01bf IA |
187 | devpriv->ao_data[i] = 0x8000; |
188 | outw(devpriv->ao_data[i], dev->iobase + PCI1723_DA(i)); | |
d6d9bd32 | 189 | /* set all ranges to +/- 10V */ |
011f01bf IA |
190 | devpriv->da_range[i] = 0; |
191 | outw(((devpriv->da_range[i] << 4) | i), | |
192 | PCI1723_RANGE_CALIBRATION_MODE); | |
193 | } | |
194 | ||
d6d9bd32 BP |
195 | outw(0, dev->iobase + PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE); /* update ranges */ |
196 | outw(0, dev->iobase + PCI1723_SYN_STROBE); /* update outputs */ | |
011f01bf | 197 | |
d6d9bd32 | 198 | /* set asynchronous output mode */ |
011f01bf IA |
199 | outw(0, dev->iobase + PCI1723_SYN_SET); |
200 | ||
201 | DPRINTK("adv_pci1723 EDBG: END: pci1723_reset(...)\n"); | |
202 | return 0; | |
203 | } | |
204 | ||
da91b269 BP |
205 | static int pci1723_insn_read_ao(struct comedi_device *dev, struct comedi_subdevice *s, |
206 | struct comedi_insn *insn, unsigned int *data) | |
011f01bf IA |
207 | { |
208 | int n, chan; | |
209 | ||
210 | chan = CR_CHAN(insn->chanspec); | |
211 | DPRINTK(" adv_PCI1723 DEBUG: pci1723_insn_read_ao() ----- \n"); | |
212 | for (n = 0; n < insn->n; n++) | |
213 | data[n] = devpriv->ao_data[chan]; | |
214 | ||
215 | return n; | |
216 | } | |
217 | ||
218 | /* | |
219 | analog data output; | |
220 | */ | |
da91b269 BP |
221 | static int pci1723_ao_write_winsn(struct comedi_device *dev, struct comedi_subdevice *s, |
222 | struct comedi_insn *insn, unsigned int *data) | |
011f01bf IA |
223 | { |
224 | int n, chan; | |
225 | chan = CR_CHAN(insn->chanspec); | |
226 | ||
227 | DPRINTK("PCI1723: the pci1723_ao_write_winsn() ------\n"); | |
228 | ||
229 | for (n = 0; n < insn->n; n++) { | |
230 | ||
231 | devpriv->ao_data[chan] = data[n]; | |
232 | outw(data[n], dev->iobase + PCI1723_DA(chan)); | |
233 | } | |
234 | ||
235 | return n; | |
236 | } | |
237 | ||
238 | /* | |
239 | digital i/o config/query | |
240 | */ | |
da91b269 BP |
241 | static int pci1723_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, |
242 | struct comedi_insn *insn, unsigned int *data) | |
011f01bf IA |
243 | { |
244 | unsigned int mask; | |
245 | unsigned int bits; | |
246 | unsigned short dio_mode; | |
247 | ||
248 | mask = 1 << CR_CHAN(insn->chanspec); | |
249 | if (mask & 0x00FF) { | |
250 | bits = 0x00FF; | |
251 | } else { | |
252 | bits = 0xFF00; | |
253 | } | |
254 | switch (data[0]) { | |
255 | case INSN_CONFIG_DIO_INPUT: | |
256 | s->io_bits &= ~bits; | |
257 | break; | |
258 | case INSN_CONFIG_DIO_OUTPUT: | |
259 | s->io_bits |= bits; | |
260 | break; | |
261 | case INSN_CONFIG_DIO_QUERY: | |
262 | data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; | |
263 | return insn->n; | |
264 | default: | |
265 | return -EINVAL; | |
266 | } | |
267 | ||
d6d9bd32 BP |
268 | /* update hardware DIO mode */ |
269 | dio_mode = 0x0000; /* low byte output, high byte output */ | |
011f01bf | 270 | if ((s->io_bits & 0x00FF) == 0) |
d6d9bd32 | 271 | dio_mode |= 0x0001; /* low byte input */ |
011f01bf | 272 | if ((s->io_bits & 0xFF00) == 0) |
d6d9bd32 | 273 | dio_mode |= 0x0002; /* high byte input */ |
011f01bf IA |
274 | outw(dio_mode, dev->iobase + PCI1723_DIGITAL_IO_PORT_SET); |
275 | return 1; | |
276 | } | |
277 | ||
278 | /* | |
279 | digital i/o bits read/write | |
280 | */ | |
da91b269 BP |
281 | static int pci1723_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, |
282 | struct comedi_insn *insn, unsigned int *data) | |
011f01bf IA |
283 | { |
284 | if (data[0]) { | |
285 | s->state &= ~data[0]; | |
286 | s->state |= (data[0] & data[1]); | |
287 | outw(s->state, dev->iobase + PCI1723_WRITE_DIGITAL_OUTPUT_CMD); | |
288 | } | |
289 | data[1] = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA); | |
290 | return 2; | |
291 | } | |
292 | ||
293 | /* | |
294 | * Attach is called by the Comedi core to configure the driver | |
295 | * for a pci1723 board. | |
296 | */ | |
da91b269 | 297 | static int pci1723_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
011f01bf | 298 | { |
34c43922 | 299 | struct comedi_subdevice *s; |
011f01bf IA |
300 | int ret, subdev, n_subdevices; |
301 | struct pci_dev *pcidev; | |
302 | unsigned int iobase; | |
303 | unsigned char pci_bus, pci_slot, pci_func; | |
304 | int opt_bus, opt_slot; | |
305 | const char *errstr; | |
306 | ||
307 | rt_printk("comedi%d: adv_pci1723: board=%s", dev->minor, | |
308 | this_board->name); | |
309 | ||
310 | opt_bus = it->options[0]; | |
311 | opt_slot = it->options[1]; | |
312 | ||
006449f0 | 313 | if ((ret = alloc_private(dev, sizeof(struct pci1723_private))) < 0) { |
011f01bf IA |
314 | rt_printk(" - Allocation failed!\n"); |
315 | return -ENOMEM; | |
316 | } | |
317 | ||
318 | /* Look for matching PCI device */ | |
319 | errstr = "not found!"; | |
320 | pcidev = NULL; | |
321 | while (NULL != (pcidev = | |
322 | pci_get_device(PCI_VENDOR_ID_ADVANTECH, | |
323 | this_board->device_id, pcidev))) { | |
324 | /* Found matching vendor/device. */ | |
325 | if (opt_bus || opt_slot) { | |
326 | /* Check bus/slot. */ | |
327 | if (opt_bus != pcidev->bus->number | |
328 | || opt_slot != PCI_SLOT(pcidev->devfn)) | |
329 | continue; /* no match */ | |
330 | } | |
331 | /* | |
332 | * Look for device that isn't in use. | |
333 | * Enable PCI device and request regions. | |
334 | */ | |
335 | if (comedi_pci_enable(pcidev, "adv_pci1723")) { | |
336 | errstr = "failed to enable PCI device and request regions!"; | |
337 | continue; | |
338 | } | |
339 | break; | |
340 | } | |
341 | ||
342 | if (!pcidev) { | |
343 | if (opt_bus || opt_slot) { | |
344 | rt_printk(" - Card at b:s %d:%d %s\n", | |
345 | opt_bus, opt_slot, errstr); | |
346 | } else { | |
347 | rt_printk(" - Card %s\n", errstr); | |
348 | } | |
349 | return -EIO; | |
350 | } | |
351 | ||
352 | pci_bus = pcidev->bus->number; | |
353 | pci_slot = PCI_SLOT(pcidev->devfn); | |
354 | pci_func = PCI_FUNC(pcidev->devfn); | |
355 | iobase = pci_resource_start(pcidev, 2); | |
356 | ||
357 | rt_printk(", b:s:f=%d:%d:%d, io=0x%4x", pci_bus, pci_slot, pci_func, | |
358 | iobase); | |
359 | ||
360 | dev->iobase = iobase; | |
361 | ||
362 | dev->board_name = this_board->name; | |
363 | devpriv->pcidev = pcidev; | |
364 | ||
365 | n_subdevices = 0; | |
366 | ||
367 | if (this_board->n_aochan) | |
368 | n_subdevices++; | |
369 | if (this_board->n_diochan) | |
370 | n_subdevices++; | |
371 | ||
372 | if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) { | |
373 | rt_printk(" - Allocation failed!\n"); | |
374 | return ret; | |
375 | } | |
376 | ||
377 | pci1723_reset(dev); | |
378 | subdev = 0; | |
379 | if (this_board->n_aochan) { | |
380 | s = dev->subdevices + subdev; | |
381 | dev->write_subdev = s; | |
382 | s->type = COMEDI_SUBD_AO; | |
383 | s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; | |
384 | s->n_chan = this_board->n_aochan; | |
385 | s->maxdata = this_board->ao_maxdata; | |
386 | s->len_chanlist = this_board->n_aochan; | |
387 | s->range_table = this_board->rangelist_ao; | |
388 | ||
389 | s->insn_write = pci1723_ao_write_winsn; | |
390 | s->insn_read = pci1723_insn_read_ao; | |
391 | ||
d6d9bd32 | 392 | /* read DIO config */ |
011f01bf | 393 | switch (inw(dev->iobase + PCI1723_DIGITAL_IO_PORT_MODE) & 0x03) { |
d6d9bd32 | 394 | case 0x00: /* low byte output, high byte output */ |
011f01bf IA |
395 | s->io_bits = 0xFFFF; |
396 | break; | |
d6d9bd32 | 397 | case 0x01: /* low byte input, high byte output */ |
011f01bf IA |
398 | s->io_bits = 0xFF00; |
399 | break; | |
d6d9bd32 | 400 | case 0x02: /* low byte output, high byte input */ |
011f01bf IA |
401 | s->io_bits = 0x00FF; |
402 | break; | |
d6d9bd32 | 403 | case 0x03: /* low byte input, high byte input */ |
011f01bf IA |
404 | s->io_bits = 0x0000; |
405 | break; | |
406 | } | |
d6d9bd32 | 407 | /* read DIO port state */ |
011f01bf IA |
408 | s->state = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA); |
409 | ||
410 | subdev++; | |
411 | } | |
412 | ||
413 | if (this_board->n_diochan) { | |
414 | s = dev->subdevices + subdev; | |
415 | s->type = COMEDI_SUBD_DIO; | |
416 | s->subdev_flags = | |
417 | SDF_READABLE | SDF_WRITABLE | SDF_GROUND | SDF_COMMON; | |
418 | s->n_chan = this_board->n_diochan; | |
419 | s->maxdata = 1; | |
420 | s->len_chanlist = this_board->n_diochan; | |
421 | s->range_table = &range_digital; | |
422 | s->insn_config = pci1723_dio_insn_config; | |
423 | s->insn_bits = pci1723_dio_insn_bits; | |
424 | subdev++; | |
425 | } | |
426 | ||
427 | devpriv->valid = 1; | |
428 | ||
429 | pci1723_reset(dev); | |
430 | ||
431 | return 0; | |
432 | } | |
433 | ||
434 | /* | |
435 | * _detach is called to deconfigure a device. It should deallocate | |
436 | * resources. | |
437 | * This function is also called when _attach() fails, so it should be | |
438 | * careful not to release resources that were not necessarily | |
439 | * allocated by _attach(). dev->private and dev->subdevices are | |
440 | * deallocated automatically by the core. | |
441 | */ | |
da91b269 | 442 | static int pci1723_detach(struct comedi_device *dev) |
011f01bf IA |
443 | { |
444 | printk("comedi%d: pci1723: remove\n", dev->minor); | |
445 | ||
446 | if (dev->private) { | |
447 | if (devpriv->valid) | |
448 | pci1723_reset(dev); | |
449 | ||
450 | if (devpriv->pcidev) { | |
451 | if (dev->iobase) { | |
452 | comedi_pci_disable(devpriv->pcidev); | |
453 | } | |
454 | pci_dev_put(devpriv->pcidev); | |
455 | } | |
456 | } | |
457 | ||
458 | return 0; | |
459 | } | |
460 | ||
461 | /* | |
462 | * A convenient macro that defines init_module() and cleanup_module(), | |
463 | * as necessary. | |
464 | */ | |
465 | COMEDI_PCI_INITCLEANUP(driver_pci1723, pci1723_pci_table); |