Commit | Line | Data |
---|---|---|
086df5b5 | 1 | /* |
3bb18724 HS |
2 | * comedi/drivers/cb_pcidda.c |
3 | * Driver for the ComputerBoards / MeasurementComputing PCI-DDA series. | |
4 | * | |
5 | * Copyright (C) 2001 Ivan Martinez <ivanmr@altavista.com> | |
6 | * Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net> | |
7 | * | |
8 | * COMEDI - Linux Control and Measurement Device Interface | |
9 | * Copyright (C) 1997-8 David A. Schleef <ds@schleef.org> | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License as published by | |
13 | * the Free Software Foundation; either version 2 of the License, or | |
14 | * (at your option) any later version. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License | |
22 | * along with this program; if not, write to the Free Software | |
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
24 | */ | |
086df5b5 | 25 | |
086df5b5 | 26 | /* |
3bb18724 HS |
27 | * Driver: cb_pcidda |
28 | * Description: MeasurementComputing PCI-DDA series | |
29 | * Devices: (Measurement Computing) PCI-DDA08/12 [pci-dda08/12] | |
30 | * (Measurement Computing) PCI-DDA04/12 [pci-dda04/12] | |
31 | * (Measurement Computing) PCI-DDA02/12 [pci-dda02/12] | |
32 | * (Measurement Computing) PCI-DDA08/16 [pci-dda08/16] | |
33 | * (Measurement Computing) PCI-DDA04/16 [pci-dda04/16] | |
34 | * (Measurement Computing) PCI-DDA02/16 [pci-dda02/16] | |
35 | * Author: Ivan Martinez <ivanmr@altavista.com> | |
36 | * Frank Mori Hess <fmhess@users.sourceforge.net> | |
37 | * Status: works | |
38 | * | |
39 | * Configuration options: not applicable, uses PCI auto config | |
40 | * | |
41 | * Only simple analog output writing is supported. | |
42 | */ | |
086df5b5 IM |
43 | |
44 | #include "../comedidev.h" | |
45 | ||
27020ffe | 46 | #include "comedi_fc.h" |
086df5b5 IM |
47 | #include "8255.h" |
48 | ||
b9287a30 HS |
49 | /* |
50 | * ComputerBoards PCI Device ID's supported by this driver | |
51 | */ | |
b9287a30 HS |
52 | #define PCI_DEVICE_ID_DDA02_12 0x0020 |
53 | #define PCI_DEVICE_ID_DDA04_12 0x0021 | |
54 | #define PCI_DEVICE_ID_DDA08_12 0x0022 | |
55 | #define PCI_DEVICE_ID_DDA02_16 0x0023 | |
56 | #define PCI_DEVICE_ID_DDA04_16 0x0024 | |
57 | #define PCI_DEVICE_ID_DDA08_16 0x0025 | |
58 | ||
2696fb57 | 59 | #define EEPROM_SIZE 128 /* number of entries in eeprom */ |
3c5510ba RKM |
60 | /* maximum number of ao channels for supported boards */ |
61 | #define MAX_AO_CHANNELS 8 | |
086df5b5 | 62 | |
086df5b5 | 63 | /* Digital I/O registers */ |
2696fb57 | 64 | #define PORT1A 0 /* PORT 1A DATA */ |
086df5b5 | 65 | |
2696fb57 | 66 | #define PORT1B 1 /* PORT 1B DATA */ |
086df5b5 | 67 | |
2696fb57 | 68 | #define PORT1C 2 /* PORT 1C DATA */ |
086df5b5 | 69 | |
2696fb57 | 70 | #define CONTROL1 3 /* CONTROL REGISTER 1 */ |
086df5b5 | 71 | |
2696fb57 | 72 | #define PORT2A 4 /* PORT 2A DATA */ |
086df5b5 | 73 | |
2696fb57 | 74 | #define PORT2B 5 /* PORT 2B DATA */ |
086df5b5 | 75 | |
2696fb57 | 76 | #define PORT2C 6 /* PORT 2C DATA */ |
086df5b5 | 77 | |
2696fb57 | 78 | #define CONTROL2 7 /* CONTROL REGISTER 2 */ |
086df5b5 IM |
79 | |
80 | /* DAC registers */ | |
2696fb57 BP |
81 | #define DACONTROL 0 /* D/A CONTROL REGISTER */ |
82 | #define SU 0000001 /* Simultaneous update enabled */ | |
83 | #define NOSU 0000000 /* Simultaneous update disabled */ | |
84 | #define ENABLEDAC 0000002 /* Enable specified DAC */ | |
85 | #define DISABLEDAC 0000000 /* Disable specified DAC */ | |
86 | #define RANGE2V5 0000000 /* 2.5V */ | |
87 | #define RANGE5V 0000200 /* 5V */ | |
88 | #define RANGE10V 0000300 /* 10V */ | |
89 | #define UNIP 0000400 /* Unipolar outputs */ | |
90 | #define BIP 0000000 /* Bipolar outputs */ | |
91 | ||
92 | #define DACALIBRATION1 4 /* D/A CALIBRATION REGISTER 1 */ | |
93 | /* write bits */ | |
3c5510ba RKM |
94 | /* serial data input for eeprom, caldacs, reference dac */ |
95 | #define SERIAL_IN_BIT 0x1 | |
086df5b5 IM |
96 | #define CAL_CHANNEL_MASK (0x7 << 1) |
97 | #define CAL_CHANNEL_BITS(channel) (((channel) << 1) & CAL_CHANNEL_MASK) | |
2696fb57 | 98 | /* read bits */ |
086df5b5 | 99 | #define CAL_COUNTER_MASK 0x1f |
3c5510ba RKM |
100 | /* calibration counter overflow status bit */ |
101 | #define CAL_COUNTER_OVERFLOW_BIT 0x20 | |
102 | /* analog output is less than reference dac voltage */ | |
103 | #define AO_BELOW_REF_BIT 0x40 | |
2696fb57 | 104 | #define SERIAL_OUT_BIT 0x80 /* serial data out, for reading from eeprom */ |
086df5b5 | 105 | |
2696fb57 BP |
106 | #define DACALIBRATION2 6 /* D/A CALIBRATION REGISTER 2 */ |
107 | #define SELECT_EEPROM_BIT 0x1 /* send serial data in to eeprom */ | |
3c5510ba RKM |
108 | /* don't send serial data to MAX542 reference dac */ |
109 | #define DESELECT_REF_DAC_BIT 0x2 | |
110 | /* don't send serial data to caldac n */ | |
111 | #define DESELECT_CALDAC_BIT(n) (0x4 << (n)) | |
112 | /* manual says to set this bit with no explanation */ | |
113 | #define DUMMY_BIT 0x40 | |
086df5b5 | 114 | |
2696fb57 | 115 | #define DADATA 8 /* FIRST D/A DATA REGISTER (0) */ |
086df5b5 | 116 | |
9ced1de6 | 117 | static const struct comedi_lrange cb_pcidda_ranges = { |
f9c62f3f HS |
118 | 6, { |
119 | BIP_RANGE(10), | |
120 | BIP_RANGE(5), | |
121 | BIP_RANGE(2.5), | |
122 | UNI_RANGE(10), | |
123 | UNI_RANGE(5), | |
124 | UNI_RANGE(2.5) | |
125 | } | |
086df5b5 IM |
126 | }; |
127 | ||
128 | /* | |
129 | * Board descriptions for two imaginary boards. Describing the | |
130 | * boards in this way is optional, and completely driver-dependent. | |
131 | * Some drivers use arrays such as this, other do not. | |
132 | */ | |
1657e325 | 133 | struct cb_pcidda_board { |
086df5b5 | 134 | const char *name; |
086df5b5 IM |
135 | unsigned short device_id; |
136 | int ao_chans; | |
137 | int ao_bits; | |
1657e325 | 138 | }; |
2696fb57 | 139 | |
1657e325 | 140 | static const struct cb_pcidda_board cb_pcidda_boards[] = { |
086df5b5 | 141 | { |
0a85b6f0 | 142 | .name = "pci-dda02/12", |
b9287a30 | 143 | .device_id = PCI_DEVICE_ID_DDA02_12, |
0a85b6f0 MT |
144 | .ao_chans = 2, |
145 | .ao_bits = 12, | |
0a85b6f0 | 146 | }, |
086df5b5 | 147 | { |
0a85b6f0 | 148 | .name = "pci-dda04/12", |
b9287a30 | 149 | .device_id = PCI_DEVICE_ID_DDA04_12, |
0a85b6f0 MT |
150 | .ao_chans = 4, |
151 | .ao_bits = 12, | |
0a85b6f0 | 152 | }, |
086df5b5 | 153 | { |
0a85b6f0 | 154 | .name = "pci-dda08/12", |
b9287a30 | 155 | .device_id = PCI_DEVICE_ID_DDA08_12, |
0a85b6f0 MT |
156 | .ao_chans = 8, |
157 | .ao_bits = 12, | |
0a85b6f0 | 158 | }, |
086df5b5 | 159 | { |
0a85b6f0 | 160 | .name = "pci-dda02/16", |
b9287a30 | 161 | .device_id = PCI_DEVICE_ID_DDA02_16, |
0a85b6f0 MT |
162 | .ao_chans = 2, |
163 | .ao_bits = 16, | |
0a85b6f0 | 164 | }, |
086df5b5 | 165 | { |
0a85b6f0 | 166 | .name = "pci-dda04/16", |
b9287a30 | 167 | .device_id = PCI_DEVICE_ID_DDA04_16, |
0a85b6f0 MT |
168 | .ao_chans = 4, |
169 | .ao_bits = 16, | |
0a85b6f0 | 170 | }, |
086df5b5 | 171 | { |
0a85b6f0 | 172 | .name = "pci-dda08/16", |
b9287a30 | 173 | .device_id = PCI_DEVICE_ID_DDA08_16, |
0a85b6f0 MT |
174 | .ao_chans = 8, |
175 | .ao_bits = 16, | |
0a85b6f0 | 176 | }, |
086df5b5 IM |
177 | }; |
178 | ||
cc7bb61e | 179 | struct cb_pcidda_private { |
3c5510ba RKM |
180 | /* bits last written to da calibration register 1 */ |
181 | unsigned int dac_cal1_bits; | |
182 | /* current range settings for output channels */ | |
183 | unsigned int ao_range[MAX_AO_CHANNELS]; | |
2696fb57 | 184 | u16 eeprom_data[EEPROM_SIZE]; /* software copy of board's eeprom */ |
cc7bb61e | 185 | }; |
086df5b5 | 186 | |
2696fb57 | 187 | /* lowlevel read from eeprom */ |
da91b269 | 188 | static unsigned int cb_pcidda_serial_in(struct comedi_device *dev) |
086df5b5 IM |
189 | { |
190 | unsigned int value = 0; | |
191 | int i; | |
2696fb57 | 192 | const int value_width = 16; /* number of bits wide values are */ |
086df5b5 IM |
193 | |
194 | for (i = 1; i <= value_width; i++) { | |
2696fb57 | 195 | /* read bits most significant bit first */ |
a74531a5 | 196 | if (inw_p(dev->iobase + DACALIBRATION1) & SERIAL_OUT_BIT) |
086df5b5 | 197 | value |= 1 << (value_width - i); |
086df5b5 IM |
198 | } |
199 | ||
200 | return value; | |
201 | } | |
202 | ||
2696fb57 | 203 | /* lowlevel write to eeprom/dac */ |
da91b269 | 204 | static void cb_pcidda_serial_out(struct comedi_device *dev, unsigned int value, |
0a85b6f0 | 205 | unsigned int num_bits) |
086df5b5 | 206 | { |
c6ad306b | 207 | struct cb_pcidda_private *devpriv = dev->private; |
086df5b5 IM |
208 | int i; |
209 | ||
210 | for (i = 1; i <= num_bits; i++) { | |
2696fb57 | 211 | /* send bits most significant bit first */ |
086df5b5 IM |
212 | if (value & (1 << (num_bits - i))) |
213 | devpriv->dac_cal1_bits |= SERIAL_IN_BIT; | |
214 | else | |
215 | devpriv->dac_cal1_bits &= ~SERIAL_IN_BIT; | |
a74531a5 | 216 | outw_p(devpriv->dac_cal1_bits, dev->iobase + DACALIBRATION1); |
086df5b5 IM |
217 | } |
218 | } | |
219 | ||
2696fb57 | 220 | /* reads a 16 bit value from board's eeprom */ |
da91b269 | 221 | static unsigned int cb_pcidda_read_eeprom(struct comedi_device *dev, |
0a85b6f0 | 222 | unsigned int address) |
086df5b5 IM |
223 | { |
224 | unsigned int i; | |
225 | unsigned int cal2_bits; | |
226 | unsigned int value; | |
3c5510ba RKM |
227 | /* one caldac for every two dac channels */ |
228 | const int max_num_caldacs = 4; | |
229 | /* bits to send to tell eeprom we want to read */ | |
230 | const int read_instruction = 0x6; | |
086df5b5 IM |
231 | const int instruction_length = 3; |
232 | const int address_length = 8; | |
233 | ||
2696fb57 | 234 | /* send serial output stream to eeprom */ |
086df5b5 | 235 | cal2_bits = SELECT_EEPROM_BIT | DESELECT_REF_DAC_BIT | DUMMY_BIT; |
2696fb57 | 236 | /* deactivate caldacs (one caldac for every two channels) */ |
20db7d7d | 237 | for (i = 0; i < max_num_caldacs; i++) |
086df5b5 | 238 | cal2_bits |= DESELECT_CALDAC_BIT(i); |
a74531a5 | 239 | outw_p(cal2_bits, dev->iobase + DACALIBRATION2); |
086df5b5 | 240 | |
2696fb57 | 241 | /* tell eeprom we want to read */ |
086df5b5 | 242 | cb_pcidda_serial_out(dev, read_instruction, instruction_length); |
2696fb57 | 243 | /* send address we want to read from */ |
086df5b5 IM |
244 | cb_pcidda_serial_out(dev, address, address_length); |
245 | ||
246 | value = cb_pcidda_serial_in(dev); | |
247 | ||
2696fb57 | 248 | /* deactivate eeprom */ |
086df5b5 | 249 | cal2_bits &= ~SELECT_EEPROM_BIT; |
a74531a5 | 250 | outw_p(cal2_bits, dev->iobase + DACALIBRATION2); |
086df5b5 IM |
251 | |
252 | return value; | |
253 | } | |
254 | ||
2696fb57 | 255 | /* writes to 8 bit calibration dacs */ |
0a85b6f0 MT |
256 | static void cb_pcidda_write_caldac(struct comedi_device *dev, |
257 | unsigned int caldac, unsigned int channel, | |
258 | unsigned int value) | |
086df5b5 IM |
259 | { |
260 | unsigned int cal2_bits; | |
261 | unsigned int i; | |
3c5510ba RKM |
262 | /* caldacs use 3 bit channel specification */ |
263 | const int num_channel_bits = 3; | |
2696fb57 | 264 | const int num_caldac_bits = 8; /* 8 bit calibration dacs */ |
3c5510ba RKM |
265 | /* one caldac for every two dac channels */ |
266 | const int max_num_caldacs = 4; | |
086df5b5 IM |
267 | |
268 | /* write 3 bit channel */ | |
269 | cb_pcidda_serial_out(dev, channel, num_channel_bits); | |
2696fb57 | 270 | /* write 8 bit caldac value */ |
086df5b5 IM |
271 | cb_pcidda_serial_out(dev, value, num_caldac_bits); |
272 | ||
2696fb57 BP |
273 | /* |
274 | * latch stream into appropriate caldac deselect reference dac | |
275 | */ | |
086df5b5 | 276 | cal2_bits = DESELECT_REF_DAC_BIT | DUMMY_BIT; |
2696fb57 | 277 | /* deactivate caldacs (one caldac for every two channels) */ |
20db7d7d | 278 | for (i = 0; i < max_num_caldacs; i++) |
086df5b5 | 279 | cal2_bits |= DESELECT_CALDAC_BIT(i); |
2696fb57 | 280 | /* activate the caldac we want */ |
086df5b5 | 281 | cal2_bits &= ~DESELECT_CALDAC_BIT(caldac); |
a74531a5 | 282 | outw_p(cal2_bits, dev->iobase + DACALIBRATION2); |
2696fb57 | 283 | /* deactivate caldac */ |
086df5b5 | 284 | cal2_bits |= DESELECT_CALDAC_BIT(caldac); |
a74531a5 | 285 | outw_p(cal2_bits, dev->iobase + DACALIBRATION2); |
086df5b5 IM |
286 | } |
287 | ||
2696fb57 | 288 | /* returns caldac that calibrates given analog out channel */ |
086df5b5 IM |
289 | static unsigned int caldac_number(unsigned int channel) |
290 | { | |
291 | return channel / 2; | |
292 | } | |
293 | ||
2696fb57 | 294 | /* returns caldac channel that provides fine gain for given ao channel */ |
086df5b5 IM |
295 | static unsigned int fine_gain_channel(unsigned int ao_channel) |
296 | { | |
297 | return 4 * (ao_channel % 2); | |
298 | } | |
299 | ||
2696fb57 | 300 | /* returns caldac channel that provides coarse gain for given ao channel */ |
086df5b5 IM |
301 | static unsigned int coarse_gain_channel(unsigned int ao_channel) |
302 | { | |
303 | return 1 + 4 * (ao_channel % 2); | |
304 | } | |
305 | ||
2696fb57 | 306 | /* returns caldac channel that provides coarse offset for given ao channel */ |
086df5b5 IM |
307 | static unsigned int coarse_offset_channel(unsigned int ao_channel) |
308 | { | |
309 | return 2 + 4 * (ao_channel % 2); | |
310 | } | |
311 | ||
2696fb57 | 312 | /* returns caldac channel that provides fine offset for given ao channel */ |
086df5b5 IM |
313 | static unsigned int fine_offset_channel(unsigned int ao_channel) |
314 | { | |
315 | return 3 + 4 * (ao_channel % 2); | |
316 | } | |
317 | ||
2696fb57 | 318 | /* returns eeprom address that provides offset for given ao channel and range */ |
086df5b5 | 319 | static unsigned int offset_eeprom_address(unsigned int ao_channel, |
0a85b6f0 | 320 | unsigned int range) |
086df5b5 IM |
321 | { |
322 | return 0x7 + 2 * range + 12 * ao_channel; | |
323 | } | |
324 | ||
3c5510ba RKM |
325 | /* |
326 | * returns eeprom address that provides gain calibration for given ao | |
327 | * channel and range | |
328 | */ | |
086df5b5 | 329 | static unsigned int gain_eeprom_address(unsigned int ao_channel, |
0a85b6f0 | 330 | unsigned int range) |
086df5b5 IM |
331 | { |
332 | return 0x8 + 2 * range + 12 * ao_channel; | |
333 | } | |
334 | ||
3c5510ba RKM |
335 | /* |
336 | * returns upper byte of eeprom entry, which gives the coarse adjustment | |
337 | * values | |
338 | */ | |
086df5b5 IM |
339 | static unsigned int eeprom_coarse_byte(unsigned int word) |
340 | { | |
341 | return (word >> 8) & 0xff; | |
342 | } | |
343 | ||
2696fb57 | 344 | /* returns lower byte of eeprom entry, which gives the fine adjustment values */ |
086df5b5 IM |
345 | static unsigned int eeprom_fine_byte(unsigned int word) |
346 | { | |
347 | return word & 0xff; | |
348 | } | |
349 | ||
2696fb57 | 350 | /* set caldacs to eeprom values for given channel and range */ |
da91b269 | 351 | static void cb_pcidda_calibrate(struct comedi_device *dev, unsigned int channel, |
0a85b6f0 | 352 | unsigned int range) |
086df5b5 | 353 | { |
c6ad306b | 354 | struct cb_pcidda_private *devpriv = dev->private; |
086df5b5 IM |
355 | unsigned int coarse_offset, fine_offset, coarse_gain, fine_gain; |
356 | ||
3c5510ba | 357 | /* remember range so we can tell when we need to readjust calibration */ |
086df5b5 IM |
358 | devpriv->ao_range[channel] = range; |
359 | ||
2696fb57 | 360 | /* get values from eeprom data */ |
086df5b5 | 361 | coarse_offset = |
0a85b6f0 MT |
362 | eeprom_coarse_byte(devpriv->eeprom_data |
363 | [offset_eeprom_address(channel, range)]); | |
086df5b5 | 364 | fine_offset = |
0a85b6f0 MT |
365 | eeprom_fine_byte(devpriv->eeprom_data |
366 | [offset_eeprom_address(channel, range)]); | |
086df5b5 | 367 | coarse_gain = |
0a85b6f0 MT |
368 | eeprom_coarse_byte(devpriv->eeprom_data |
369 | [gain_eeprom_address(channel, range)]); | |
086df5b5 | 370 | fine_gain = |
0a85b6f0 MT |
371 | eeprom_fine_byte(devpriv->eeprom_data |
372 | [gain_eeprom_address(channel, range)]); | |
086df5b5 | 373 | |
2696fb57 | 374 | /* set caldacs */ |
086df5b5 | 375 | cb_pcidda_write_caldac(dev, caldac_number(channel), |
0a85b6f0 | 376 | coarse_offset_channel(channel), coarse_offset); |
086df5b5 | 377 | cb_pcidda_write_caldac(dev, caldac_number(channel), |
0a85b6f0 | 378 | fine_offset_channel(channel), fine_offset); |
086df5b5 | 379 | cb_pcidda_write_caldac(dev, caldac_number(channel), |
0a85b6f0 | 380 | coarse_gain_channel(channel), coarse_gain); |
086df5b5 | 381 | cb_pcidda_write_caldac(dev, caldac_number(channel), |
0a85b6f0 | 382 | fine_gain_channel(channel), fine_gain); |
086df5b5 IM |
383 | } |
384 | ||
6d336a56 HS |
385 | static int cb_pcidda_ao_winsn(struct comedi_device *dev, |
386 | struct comedi_subdevice *s, | |
387 | struct comedi_insn *insn, unsigned int *data) | |
388 | { | |
389 | struct cb_pcidda_private *devpriv = dev->private; | |
390 | unsigned int command; | |
391 | unsigned int channel, range; | |
392 | ||
393 | channel = CR_CHAN(insn->chanspec); | |
394 | range = CR_RANGE(insn->chanspec); | |
395 | ||
396 | /* adjust calibration dacs if range has changed */ | |
397 | if (range != devpriv->ao_range[channel]) | |
398 | cb_pcidda_calibrate(dev, channel, range); | |
399 | ||
400 | /* output channel configuration */ | |
401 | command = NOSU | ENABLEDAC; | |
402 | ||
403 | /* output channel range */ | |
404 | switch (range) { | |
405 | case 0: | |
406 | command |= BIP | RANGE10V; | |
407 | break; | |
408 | case 1: | |
409 | command |= BIP | RANGE5V; | |
410 | break; | |
411 | case 2: | |
412 | command |= BIP | RANGE2V5; | |
413 | break; | |
414 | case 3: | |
415 | command |= UNIP | RANGE10V; | |
416 | break; | |
417 | case 4: | |
418 | command |= UNIP | RANGE5V; | |
419 | break; | |
420 | case 5: | |
421 | command |= UNIP | RANGE2V5; | |
422 | break; | |
423 | } | |
424 | ||
425 | /* output channel specification */ | |
426 | command |= channel << 2; | |
a74531a5 | 427 | outw(command, dev->iobase + DACONTROL); |
6d336a56 HS |
428 | |
429 | /* write data */ | |
a74531a5 | 430 | outw(data[0], dev->iobase + DADATA + channel * 2); |
6d336a56 HS |
431 | |
432 | /* return the number of samples read/written */ | |
433 | return 1; | |
434 | } | |
435 | ||
2c0db011 HS |
436 | static const void *cb_pcidda_find_boardinfo(struct comedi_device *dev, |
437 | struct pci_dev *pcidev) | |
6d336a56 | 438 | { |
2c0db011 | 439 | const struct cb_pcidda_board *thisboard; |
6d336a56 HS |
440 | int i; |
441 | ||
2c0db011 HS |
442 | for (i = 0; i < ARRAY_SIZE(cb_pcidda_boards); i++) { |
443 | thisboard = &cb_pcidda_boards[i]; | |
444 | if (thisboard->device_id != pcidev->device) | |
445 | return thisboard; | |
6d336a56 | 446 | } |
6d336a56 HS |
447 | return NULL; |
448 | } | |
449 | ||
2c0db011 HS |
450 | static int cb_pcidda_attach_pci(struct comedi_device *dev, |
451 | struct pci_dev *pcidev) | |
6d336a56 HS |
452 | { |
453 | const struct cb_pcidda_board *thisboard; | |
454 | struct cb_pcidda_private *devpriv; | |
6d336a56 | 455 | struct comedi_subdevice *s; |
08f082ed | 456 | unsigned long iobase_8255; |
2730c736 | 457 | int i; |
6d336a56 HS |
458 | int ret; |
459 | ||
2c0db011 | 460 | thisboard = cb_pcidda_find_boardinfo(dev, pcidev); |
ff331f7d | 461 | if (!thisboard) |
2c0db011 HS |
462 | return -ENODEV; |
463 | dev->board_ptr = thisboard; | |
464 | dev->board_name = thisboard->name; | |
465 | ||
c34fa261 HS |
466 | devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); |
467 | if (!devpriv) | |
468 | return -ENOMEM; | |
469 | dev->private = devpriv; | |
6d336a56 | 470 | |
2c0db011 HS |
471 | ret = comedi_pci_enable(pcidev, dev->board_name); |
472 | if (ret) | |
473 | return ret; | |
a74531a5 | 474 | dev->iobase = pci_resource_start(pcidev, 3); |
08f082ed | 475 | iobase_8255 = pci_resource_start(pcidev, 2); |
6d336a56 | 476 | |
6d336a56 HS |
477 | ret = comedi_alloc_subdevices(dev, 3); |
478 | if (ret) | |
479 | return ret; | |
480 | ||
82ec595a | 481 | s = &dev->subdevices[0]; |
6d336a56 HS |
482 | /* analog output subdevice */ |
483 | s->type = COMEDI_SUBD_AO; | |
484 | s->subdev_flags = SDF_WRITABLE; | |
485 | s->n_chan = thisboard->ao_chans; | |
486 | s->maxdata = (1 << thisboard->ao_bits) - 1; | |
f9c62f3f | 487 | s->range_table = &cb_pcidda_ranges; |
6d336a56 HS |
488 | s->insn_write = cb_pcidda_ao_winsn; |
489 | ||
618351ae HS |
490 | /* two 8255 digital io subdevices */ |
491 | for (i = 0; i < 2; i++) { | |
492 | s = &dev->subdevices[1 + i]; | |
493 | ret = subdev_8255_init(dev, s, NULL, iobase_8255 + (i * 4)); | |
494 | if (ret) | |
495 | return ret; | |
496 | } | |
6d336a56 | 497 | |
66483378 | 498 | /* Read the caldac eeprom data */ |
2730c736 HS |
499 | for (i = 0; i < EEPROM_SIZE; i++) |
500 | devpriv->eeprom_data[i] = cb_pcidda_read_eeprom(dev, i); | |
6d336a56 HS |
501 | |
502 | /* set calibrations dacs */ | |
2730c736 HS |
503 | for (i = 0; i < thisboard->ao_chans; i++) |
504 | cb_pcidda_calibrate(dev, i, devpriv->ao_range[i]); | |
6d336a56 | 505 | |
2c0db011 HS |
506 | dev_info(dev->class_dev, "%s attached\n", dev->board_name); |
507 | ||
508 | return 0; | |
6d336a56 HS |
509 | } |
510 | ||
511 | static void cb_pcidda_detach(struct comedi_device *dev) | |
512 | { | |
513 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); | |
514 | ||
6d336a56 | 515 | if (dev->subdevices) { |
82ec595a HS |
516 | subdev_8255_cleanup(dev, &dev->subdevices[1]); |
517 | subdev_8255_cleanup(dev, &dev->subdevices[2]); | |
6d336a56 | 518 | } |
2c0db011 HS |
519 | if (pcidev) { |
520 | if (dev->iobase) | |
521 | comedi_pci_disable(pcidev); | |
522 | } | |
6d336a56 HS |
523 | } |
524 | ||
954ca6c9 HS |
525 | static struct comedi_driver cb_pcidda_driver = { |
526 | .driver_name = "cb_pcidda", | |
527 | .module = THIS_MODULE, | |
2c0db011 | 528 | .attach_pci = cb_pcidda_attach_pci, |
954ca6c9 HS |
529 | .detach = cb_pcidda_detach, |
530 | }; | |
531 | ||
532 | static int __devinit cb_pcidda_pci_probe(struct pci_dev *dev, | |
533 | const struct pci_device_id *ent) | |
727b286b | 534 | { |
954ca6c9 | 535 | return comedi_pci_auto_config(dev, &cb_pcidda_driver); |
727b286b AT |
536 | } |
537 | ||
954ca6c9 | 538 | static void __devexit cb_pcidda_pci_remove(struct pci_dev *dev) |
727b286b AT |
539 | { |
540 | comedi_pci_auto_unconfig(dev); | |
541 | } | |
542 | ||
954ca6c9 | 543 | static DEFINE_PCI_DEVICE_TABLE(cb_pcidda_pci_table) = { |
b9287a30 HS |
544 | { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_DDA02_12) }, |
545 | { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_DDA04_12) }, | |
546 | { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_DDA08_12) }, | |
547 | { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_DDA02_16) }, | |
548 | { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_DDA04_16) }, | |
549 | { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_DDA08_16) }, | |
954ca6c9 | 550 | { 0 } |
727b286b | 551 | }; |
954ca6c9 | 552 | MODULE_DEVICE_TABLE(pci, cb_pcidda_pci_table); |
727b286b | 553 | |
954ca6c9 HS |
554 | static struct pci_driver cb_pcidda_pci_driver = { |
555 | .name = "cb_pcidda", | |
556 | .id_table = cb_pcidda_pci_table, | |
557 | .probe = cb_pcidda_pci_probe, | |
558 | .remove = __devexit_p(cb_pcidda_pci_remove), | |
559 | }; | |
560 | module_comedi_pci_driver(cb_pcidda_driver, cb_pcidda_pci_driver); | |
90f703d3 AT |
561 | |
562 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
563 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
564 | MODULE_LICENSE("GPL"); |