Commit | Line | Data |
---|---|---|
3063d6de MD |
1 | /* |
2 | * comedi/drivers/adl_pci9118.c | |
3 | * | |
4 | * hardware driver for ADLink cards: | |
5 | * card: PCI-9118DG, PCI-9118HG, PCI-9118HR | |
6 | * driver: pci9118dg, pci9118hg, pci9118hr | |
7 | * | |
8 | * Author: Michal Dobes <dobes@tesnet.cz> | |
9 | * | |
10 | */ | |
11 | /* | |
12 | Driver: adl_pci9118 | |
13 | Description: Adlink PCI-9118DG, PCI-9118HG, PCI-9118HR | |
14 | Author: Michal Dobes <dobes@tesnet.cz> | |
15 | Devices: [ADLink] PCI-9118DG (pci9118dg), PCI-9118HG (pci9118hg), | |
16 | PCI-9118HR (pci9118hr) | |
17 | Status: works | |
18 | ||
19 | This driver supports AI, AO, DI and DO subdevices. | |
20 | AI subdevice supports cmd and insn interface, | |
21 | other subdevices support only insn interface. | |
22 | For AI: | |
23 | - If cmd->scan_begin_src=TRIG_EXT then trigger input is TGIN (pin 46). | |
24 | - If cmd->convert_src=TRIG_EXT then trigger input is EXTTRG (pin 44). | |
25 | - If cmd->start_src/stop_src=TRIG_EXT then trigger input is TGIN (pin 46). | |
26 | - It is not neccessary to have cmd.scan_end_arg=cmd.chanlist_len but | |
27 | cmd.scan_end_arg modulo cmd.chanlist_len must by 0. | |
28 | - If return value of cmdtest is 5 then you've bad channel list | |
29 | (it isn't possible mixture S.E. and DIFF inputs or bipolar and unipolar | |
30 | ranges). | |
31 | ||
32 | There are some hardware limitations: | |
33 | a) You cann't use mixture of unipolar/bipoar ranges or differencial/single | |
34 | ended inputs. | |
35 | b) DMA transfers must have the length aligned to two samples (32 bit), | |
36 | so there is some problems if cmd->chanlist_len is odd. This driver tries | |
37 | bypass this with adding one sample to the end of the every scan and discard | |
38 | it on output but this cann't be used if cmd->scan_begin_src=TRIG_FOLLOW | |
39 | and is used flag TRIG_WAKE_EOS, then driver switch to interrupt driven mode | |
40 | with interrupt after every sample. | |
41 | c) If isn't used DMA then you can use only mode where | |
42 | cmd->scan_begin_src=TRIG_FOLLOW. | |
43 | ||
44 | Configuration options: | |
45 | [0] - PCI bus of device (optional) | |
46 | [1] - PCI slot of device (optional) | |
47 | If bus/slot is not specified, then first available PCI | |
48 | card will be used. | |
49 | [2] - 0= standard 8 DIFF/16 SE channels configuration | |
50 | n= external multiplexer connected, 1<=n<=256 | |
51 | [3] - 0=autoselect DMA or EOC interrupts operation | |
52 | 1=disable DMA mode | |
53 | 3=disable DMA and INT, only insn interface will work | |
54 | [4] - sample&hold signal - card can generate signal for external S&H board | |
55 | 0=use SSHO (pin 45) signal is generated in onboard hardware S&H logic | |
56 | 0!=use ADCHN7 (pin 23) signal is generated from driver, number | |
57 | say how long delay is requested in ns and sign polarity of the hold | |
58 | (in this case external multiplexor can serve only 128 channels) | |
59 | [5] - 0=stop measure on all hardware errors | |
60 | 2|=ignore ADOR - A/D Overrun status | |
61 | 8|=ignore Bover - A/D Burst Mode Overrun status | |
62 | 256|=ignore nFull - A/D FIFO Full status | |
63 | ||
64 | */ | |
65 | #include "../comedidev.h" | |
66 | #include "../pci_ids.h" | |
67 | ||
68 | #include <linux/delay.h> | |
70265d24 | 69 | #include <linux/interrupt.h> |
3063d6de MD |
70 | |
71 | #include "amcc_s5933.h" | |
72 | #include "8253.h" | |
73 | #include "comedi_pci.h" | |
74 | #include "comedi_fc.h" | |
75 | ||
76 | /* paranoid checks are broken */ | |
77 | #undef PCI9118_PARANOIDCHECK /* if defined, then is used code which control correct channel number on every 12 bit sample */ | |
78 | ||
79 | #undef PCI9118_EXTDEBUG /* if defined then driver prints a lot of messages */ | |
80 | ||
81 | #undef DPRINTK | |
82 | #ifdef PCI9118_EXTDEBUG | |
83 | #define DPRINTK(fmt, args...) rt_printk(fmt, ## args) | |
84 | #else | |
85 | #define DPRINTK(fmt, args...) | |
86 | #endif | |
87 | ||
88 | #define IORANGE_9118 64 /* I hope */ | |
89 | #define PCI9118_CHANLEN 255 /* len of chanlist, some source say 256, but reality looks like 255 :-( */ | |
90 | ||
91 | #define PCI9118_CNT0 0x00 /* R/W: 8254 couter 0 */ | |
92 | #define PCI9118_CNT1 0x04 /* R/W: 8254 couter 0 */ | |
93 | #define PCI9118_CNT2 0x08 /* R/W: 8254 couter 0 */ | |
94 | #define PCI9118_CNTCTRL 0x0c /* W: 8254 counter control */ | |
95 | #define PCI9118_AD_DATA 0x10 /* R: A/D data */ | |
96 | #define PCI9118_DA1 0x10 /* W: D/A registers */ | |
97 | #define PCI9118_DA2 0x14 | |
98 | #define PCI9118_ADSTAT 0x18 /* R: A/D status register */ | |
99 | #define PCI9118_ADCNTRL 0x18 /* W: A/D control register */ | |
100 | #define PCI9118_DI 0x1c /* R: digi input register */ | |
101 | #define PCI9118_DO 0x1c /* W: digi output register */ | |
102 | #define PCI9118_SOFTTRG 0x20 /* W: soft trigger for A/D */ | |
103 | #define PCI9118_GAIN 0x24 /* W: A/D gain/channel register */ | |
104 | #define PCI9118_BURST 0x28 /* W: A/D burst number register */ | |
105 | #define PCI9118_SCANMOD 0x2c /* W: A/D auto scan mode */ | |
106 | #define PCI9118_ADFUNC 0x30 /* W: A/D function register */ | |
107 | #define PCI9118_DELFIFO 0x34 /* W: A/D data FIFO reset */ | |
108 | #define PCI9118_INTSRC 0x38 /* R: interrupt reason register */ | |
109 | #define PCI9118_INTCTRL 0x38 /* W: interrupt control register */ | |
110 | ||
0f04c356 | 111 | /* bits from A/D control register (PCI9118_ADCNTRL) */ |
3063d6de MD |
112 | #define AdControl_UniP 0x80 /* 1=bipolar, 0=unipolar */ |
113 | #define AdControl_Diff 0x40 /* 1=differential, 0= single end inputs */ | |
114 | #define AdControl_SoftG 0x20 /* 1=8254 counter works, 0=counter stops */ | |
115 | #define AdControl_ExtG 0x10 /* 1=8254 countrol controlled by TGIN(pin 46), 0=controled by SoftG */ | |
116 | #define AdControl_ExtM 0x08 /* 1=external hardware trigger (pin 44), 0=internal trigger */ | |
117 | #define AdControl_TmrTr 0x04 /* 1=8254 is iternal trigger source, 0=software trigger is source (register PCI9118_SOFTTRG) */ | |
118 | #define AdControl_Int 0x02 /* 1=enable INT, 0=disable */ | |
119 | #define AdControl_Dma 0x01 /* 1=enable DMA, 0=disable */ | |
120 | ||
0f04c356 | 121 | /* bits from A/D function register (PCI9118_ADFUNC) */ |
3063d6de MD |
122 | #define AdFunction_PDTrg 0x80 /* 1=positive, 0=negative digital trigger (only positive is correct) */ |
123 | #define AdFunction_PETrg 0x40 /* 1=positive, 0=negative external trigger (only positive is correct) */ | |
124 | #define AdFunction_BSSH 0x20 /* 1=with sample&hold, 0=without */ | |
125 | #define AdFunction_BM 0x10 /* 1=burst mode, 0=normal mode */ | |
126 | #define AdFunction_BS 0x08 /* 1=burst mode start, 0=burst mode stop */ | |
127 | #define AdFunction_PM 0x04 /* 1=post trigger mode, 0=not post trigger */ | |
128 | #define AdFunction_AM 0x02 /* 1=about trigger mode, 0=not about trigger */ | |
129 | #define AdFunction_Start 0x01 /* 1=trigger start, 0=trigger stop */ | |
130 | ||
0f04c356 | 131 | /* bits from A/D status register (PCI9118_ADSTAT) */ |
3063d6de MD |
132 | #define AdStatus_nFull 0x100 /* 0=FIFO full (fatal), 1=not full */ |
133 | #define AdStatus_nHfull 0x080 /* 0=FIFO half full, 1=FIFO not half full */ | |
134 | #define AdStatus_nEpty 0x040 /* 0=FIFO empty, 1=FIFO not empty */ | |
135 | #define AdStatus_Acmp 0x020 /* */ | |
136 | #define AdStatus_DTH 0x010 /* 1=external digital trigger */ | |
137 | #define AdStatus_Bover 0x008 /* 1=burst mode overrun (fatal) */ | |
138 | #define AdStatus_ADOS 0x004 /* 1=A/D over speed (warning) */ | |
139 | #define AdStatus_ADOR 0x002 /* 1=A/D overrun (fatal) */ | |
140 | #define AdStatus_ADrdy 0x001 /* 1=A/D already ready, 0=not ready */ | |
141 | ||
0f04c356 BP |
142 | /* bits for interrupt reason and control (PCI9118_INTSRC, PCI9118_INTCTRL) */ |
143 | /* 1=interrupt occur, enable source, 0=interrupt not occur, disable source */ | |
3063d6de MD |
144 | #define Int_Timer 0x08 /* timer interrupt */ |
145 | #define Int_About 0x04 /* about trigger complete */ | |
146 | #define Int_Hfull 0x02 /* A/D FIFO hlaf full */ | |
147 | #define Int_DTrg 0x01 /* external digital trigger */ | |
148 | ||
149 | #define START_AI_EXT 0x01 /* start measure on external trigger */ | |
150 | #define STOP_AI_EXT 0x02 /* stop measure on external trigger */ | |
151 | #define START_AI_INT 0x04 /* start measure on internal trigger */ | |
152 | #define STOP_AI_INT 0x08 /* stop measure on internal trigger */ | |
153 | ||
154 | #define EXTTRG_AI 0 /* ext trg is used by AI */ | |
155 | ||
9ced1de6 | 156 | static const struct comedi_lrange range_pci9118dg_hr = { 8, { |
3063d6de MD |
157 | BIP_RANGE(5), |
158 | BIP_RANGE(2.5), | |
159 | BIP_RANGE(1.25), | |
160 | BIP_RANGE(0.625), | |
161 | UNI_RANGE(10), | |
162 | UNI_RANGE(5), | |
163 | UNI_RANGE(2.5), | |
164 | UNI_RANGE(1.25) | |
165 | } | |
166 | }; | |
167 | ||
9ced1de6 | 168 | static const struct comedi_lrange range_pci9118hg = { 8, { |
3063d6de MD |
169 | BIP_RANGE(5), |
170 | BIP_RANGE(0.5), | |
171 | BIP_RANGE(0.05), | |
172 | BIP_RANGE(0.005), | |
173 | UNI_RANGE(10), | |
174 | UNI_RANGE(1), | |
175 | UNI_RANGE(0.1), | |
176 | UNI_RANGE(0.01) | |
177 | } | |
178 | }; | |
179 | ||
180 | #define PCI9118_BIPOLAR_RANGES 4 /* used for test on mixture of BIP/UNI ranges */ | |
181 | ||
0707bb04 | 182 | static int pci9118_attach(struct comedi_device * dev, struct comedi_devconfig * it); |
71b5f4f1 | 183 | static int pci9118_detach(struct comedi_device * dev); |
3063d6de | 184 | |
193a21e4 | 185 | struct boardtype { |
0f04c356 BP |
186 | const char *name; /* board name */ |
187 | int vendor_id; /* PCI vendor a device ID of card */ | |
3063d6de | 188 | int device_id; |
0f04c356 BP |
189 | int iorange_amcc; /* iorange for own S5933 region */ |
190 | int iorange_9118; /* pass thru card region size */ | |
191 | int n_aichan; /* num of A/D chans */ | |
192 | int n_aichand; /* num of A/D chans in diff mode */ | |
193 | int mux_aichan; /* num of A/D chans with external multiplexor */ | |
194 | int n_aichanlist; /* len of chanlist */ | |
195 | int n_aochan; /* num of D/A chans */ | |
196 | int ai_maxdata; /* resolution of A/D */ | |
197 | int ao_maxdata; /* resolution of D/A */ | |
198 | const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */ | |
199 | const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */ | |
200 | unsigned int ai_ns_min; /* max sample speed of card v ns */ | |
201 | unsigned int ai_pacer_min; /* minimal pacer value (c1*c2 or c1 in burst) */ | |
202 | int half_fifo_size; /* size of FIFO/2 */ | |
3063d6de | 203 | |
193a21e4 | 204 | }; |
3063d6de MD |
205 | |
206 | static DEFINE_PCI_DEVICE_TABLE(pci9118_pci_table) = { | |
207 | {PCI_VENDOR_ID_AMCC, 0x80d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
208 | {0} | |
209 | }; | |
210 | ||
211 | MODULE_DEVICE_TABLE(pci, pci9118_pci_table); | |
212 | ||
193a21e4 | 213 | static const struct boardtype boardtypes[] = { |
3063d6de MD |
214 | {"pci9118dg", PCI_VENDOR_ID_AMCC, 0x80d9, |
215 | AMCC_OP_REG_SIZE, IORANGE_9118, | |
216 | 16, 8, 256, PCI9118_CHANLEN, 2, 0x0fff, 0x0fff, | |
217 | &range_pci9118dg_hr, &range_bipolar10, | |
218 | 3000, 12, 512}, | |
219 | {"pci9118hg", PCI_VENDOR_ID_AMCC, 0x80d9, | |
220 | AMCC_OP_REG_SIZE, IORANGE_9118, | |
221 | 16, 8, 256, PCI9118_CHANLEN, 2, 0x0fff, 0x0fff, | |
222 | &range_pci9118hg, &range_bipolar10, | |
223 | 3000, 12, 512}, | |
224 | {"pci9118hr", PCI_VENDOR_ID_AMCC, 0x80d9, | |
225 | AMCC_OP_REG_SIZE, IORANGE_9118, | |
226 | 16, 8, 256, PCI9118_CHANLEN, 2, 0xffff, 0x0fff, | |
227 | &range_pci9118dg_hr, &range_bipolar10, | |
228 | 10000, 40, 512}, | |
229 | }; | |
230 | ||
193a21e4 | 231 | #define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype)) |
3063d6de | 232 | |
139dfbdf | 233 | static struct comedi_driver driver_pci9118 = { |
3063d6de MD |
234 | driver_name:"adl_pci9118", |
235 | module:THIS_MODULE, | |
236 | attach:pci9118_attach, | |
237 | detach:pci9118_detach, | |
238 | num_names:n_boardtypes, | |
239 | board_name:&boardtypes[0].name, | |
193a21e4 | 240 | offset:sizeof(struct boardtype), |
3063d6de MD |
241 | }; |
242 | ||
243 | COMEDI_PCI_INITCLEANUP(driver_pci9118, pci9118_pci_table); | |
244 | ||
5b5fc21b | 245 | struct pci9118_private { |
0f04c356 BP |
246 | unsigned long iobase_a; /* base+size for AMCC chip */ |
247 | unsigned int master; /* master capable */ | |
248 | struct pci_dev *pcidev; /* ptr to actual pcidev */ | |
249 | unsigned int usemux; /* we want to use external multiplexor! */ | |
3063d6de | 250 | #ifdef PCI9118_PARANOIDCHECK |
0f04c356 BP |
251 | unsigned short chanlist[PCI9118_CHANLEN + 1]; /* list of scaned channel */ |
252 | unsigned char chanlistlen; /* number of scanlist */ | |
3063d6de | 253 | #endif |
0f04c356 BP |
254 | unsigned char AdControlReg; /* A/D control register */ |
255 | unsigned char IntControlReg; /* Interrupt control register */ | |
256 | unsigned char AdFunctionReg; /* A/D function register */ | |
257 | char valid; /* driver is ok */ | |
258 | char ai_neverending; /* we do unlimited AI */ | |
259 | unsigned int i8254_osc_base; /* frequence of onboard oscilator */ | |
260 | unsigned int ai_do; /* what do AI? 0=nothing, 1 to 4 mode */ | |
261 | unsigned int ai_act_scan; /* how many scans we finished */ | |
262 | unsigned int ai_buf_ptr; /* data buffer ptr in samples */ | |
263 | unsigned int ai_n_chan; /* how many channels is measured */ | |
264 | unsigned int ai_n_scanlen; /* len of actual scanlist */ | |
265 | unsigned int ai_n_realscanlen; /* what we must transfer for one outgoing scan include front/back adds */ | |
266 | unsigned int ai_act_dmapos; /* position in actual real stream */ | |
267 | unsigned int ai_add_front; /* how many channels we must add before scan to satisfy S&H? */ | |
268 | unsigned int ai_add_back; /* how many channels we must add before scan to satisfy DMA? */ | |
269 | unsigned int *ai_chanlist; /* actaul chanlist */ | |
3063d6de MD |
270 | unsigned int ai_timer1; |
271 | unsigned int ai_timer2; | |
272 | unsigned int ai_flags; | |
0f04c356 BP |
273 | char ai12_startstop; /* measure can start/stop on external trigger */ |
274 | unsigned int ai_divisor1, ai_divisor2; /* divisors for start of measure on external start */ | |
3063d6de | 275 | unsigned int ai_data_len; |
790c5541 | 276 | short *ai_data; |
0f04c356 BP |
277 | short ao_data[2]; /* data output buffer */ |
278 | unsigned int ai_scans; /* number of scans to do */ | |
279 | char dma_doublebuf; /* we can use double buffring */ | |
280 | unsigned int dma_actbuf; /* which buffer is used now */ | |
281 | short *dmabuf_virt[2]; /* pointers to begin of DMA buffer */ | |
282 | unsigned long dmabuf_hw[2]; /* hw address of DMA buff */ | |
283 | unsigned int dmabuf_size[2]; /* size of dma buffer in bytes */ | |
284 | unsigned int dmabuf_use_size[2]; /* which size we may now used for transfer */ | |
285 | unsigned int dmabuf_used_size[2]; /* which size was trully used */ | |
3063d6de | 286 | unsigned int dmabuf_panic_size[2]; |
0f04c356 BP |
287 | unsigned int dmabuf_samples[2]; /* size in samples */ |
288 | int dmabuf_pages[2]; /* number of pages in buffer */ | |
289 | unsigned char cnt0_users; /* bit field of 8254 CNT0 users (0-unused, 1-AO, 2-DI, 3-DO) */ | |
290 | unsigned char exttrg_users; /* bit field of external trigger users (0-AI, 1-AO, 2-DI, 3-DO) */ | |
291 | unsigned int cnt0_divisor; /* actual CNT0 divisor */ | |
292 | void (*int_ai_func) (struct comedi_device *, struct comedi_subdevice *, unsigned short, unsigned int, unsigned short); /* ptr to actual interrupt AI function */ | |
293 | unsigned char ai16bits; /* =1 16 bit card */ | |
294 | unsigned char usedma; /* =1 use DMA transfer and not INT */ | |
295 | unsigned char useeoshandle; /* =1 change WAKE_EOS DMA transfer to fit on every second */ | |
296 | unsigned char usessh; /* =1 turn on S&H support */ | |
297 | int softsshdelay; /* >0 use software S&H, numer is requested delay in ns */ | |
298 | unsigned char softsshsample; /* polarity of S&H signal in sample state */ | |
299 | unsigned char softsshhold; /* polarity of S&H signal in hold state */ | |
300 | unsigned int ai_maskerr; /* which warning was printed */ | |
301 | unsigned int ai_maskharderr; /* on which error bits stops */ | |
302 | unsigned int ai_inttrig_start; /* TRIG_INT for start */ | |
5b5fc21b | 303 | }; |
3063d6de | 304 | |
5b5fc21b | 305 | #define devpriv ((struct pci9118_private *)dev->private) |
193a21e4 | 306 | #define this_board ((struct boardtype *)dev->board_ptr) |
3063d6de MD |
307 | |
308 | /* | |
309 | ============================================================================== | |
310 | */ | |
311 | ||
34c43922 | 312 | static int check_channel_list(struct comedi_device * dev, struct comedi_subdevice * s, |
3063d6de | 313 | int n_chan, unsigned int *chanlist, int frontadd, int backadd); |
34c43922 | 314 | static int setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s, |
3063d6de MD |
315 | int n_chan, unsigned int *chanlist, int rot, int frontadd, int backadd, |
316 | int usedma, char eoshandle); | |
71b5f4f1 | 317 | static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1, |
3063d6de | 318 | unsigned int divisor2); |
71b5f4f1 BP |
319 | static int pci9118_reset(struct comedi_device * dev); |
320 | static int pci9118_exttrg_add(struct comedi_device * dev, unsigned char source); | |
321 | static int pci9118_exttrg_del(struct comedi_device * dev, unsigned char source); | |
34c43922 | 322 | static int pci9118_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s); |
71b5f4f1 | 323 | static void pci9118_calc_divisors(char mode, struct comedi_device * dev, |
34c43922 | 324 | struct comedi_subdevice * s, unsigned int *tim1, unsigned int *tim2, |
3063d6de MD |
325 | unsigned int flags, int chans, unsigned int *div1, unsigned int *div2, |
326 | char usessh, unsigned int chnsshfront); | |
327 | ||
328 | /* | |
329 | ============================================================================== | |
330 | */ | |
34c43922 | 331 | static int pci9118_insn_read_ai(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 332 | struct comedi_insn * insn, unsigned int * data) |
3063d6de MD |
333 | { |
334 | ||
335 | int n, timeout; | |
336 | ||
337 | devpriv->AdControlReg = AdControl_Int & 0xff; | |
338 | devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg; | |
0f04c356 | 339 | outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); /* positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop */ |
3063d6de MD |
340 | |
341 | if (!setup_channel_list(dev, s, 1, &insn->chanspec, 0, 0, 0, 0, 0)) | |
342 | return -EINVAL; | |
343 | ||
0f04c356 | 344 | outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */ |
3063d6de MD |
345 | |
346 | for (n = 0; n < insn->n; n++) { | |
347 | outw(0, dev->iobase + PCI9118_SOFTTRG); /* start conversion */ | |
348 | comedi_udelay(2); | |
349 | timeout = 100; | |
350 | while (timeout--) { | |
351 | if (inl(dev->iobase + PCI9118_ADSTAT) & AdStatus_ADrdy) | |
352 | goto conv_finish; | |
353 | comedi_udelay(1); | |
354 | } | |
355 | ||
356 | comedi_error(dev, "A/D insn timeout"); | |
357 | data[n] = 0; | |
0f04c356 | 358 | outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */ |
3063d6de MD |
359 | return -ETIME; |
360 | ||
361 | conv_finish: | |
362 | if (devpriv->ai16bits) { | |
363 | data[n] = | |
364 | (inl(dev->iobase + | |
365 | PCI9118_AD_DATA) & 0xffff) ^ 0x8000; | |
366 | } else { | |
367 | data[n] = | |
368 | (inw(dev->iobase + | |
369 | PCI9118_AD_DATA) >> 4) & 0xfff; | |
370 | } | |
371 | } | |
372 | ||
0f04c356 | 373 | outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */ |
3063d6de MD |
374 | return n; |
375 | ||
376 | } | |
377 | ||
378 | /* | |
379 | ============================================================================== | |
380 | */ | |
34c43922 | 381 | static int pci9118_insn_write_ao(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 382 | struct comedi_insn * insn, unsigned int * data) |
3063d6de MD |
383 | { |
384 | int n, chanreg, ch; | |
385 | ||
386 | ch = CR_CHAN(insn->chanspec); | |
387 | if (ch) { | |
388 | chanreg = PCI9118_DA2; | |
389 | } else { | |
390 | chanreg = PCI9118_DA1; | |
391 | } | |
392 | ||
393 | for (n = 0; n < insn->n; n++) { | |
394 | outl(data[n], dev->iobase + chanreg); | |
395 | devpriv->ao_data[ch] = data[n]; | |
396 | } | |
397 | ||
398 | return n; | |
399 | } | |
400 | ||
401 | /* | |
402 | ============================================================================== | |
403 | */ | |
34c43922 | 404 | static int pci9118_insn_read_ao(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 405 | struct comedi_insn * insn, unsigned int * data) |
3063d6de MD |
406 | { |
407 | int n, chan; | |
408 | ||
409 | chan = CR_CHAN(insn->chanspec); | |
410 | for (n = 0; n < insn->n; n++) | |
411 | data[n] = devpriv->ao_data[chan]; | |
412 | ||
413 | return n; | |
414 | } | |
415 | ||
416 | /* | |
417 | ============================================================================== | |
418 | */ | |
34c43922 | 419 | static int pci9118_insn_bits_di(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 420 | struct comedi_insn * insn, unsigned int * data) |
3063d6de MD |
421 | { |
422 | data[1] = inl(dev->iobase + PCI9118_DI) & 0xf; | |
423 | ||
424 | return 2; | |
425 | } | |
426 | ||
427 | /* | |
428 | ============================================================================== | |
429 | */ | |
34c43922 | 430 | static int pci9118_insn_bits_do(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 431 | struct comedi_insn * insn, unsigned int * data) |
3063d6de MD |
432 | { |
433 | if (data[0]) { | |
434 | s->state &= ~data[0]; | |
435 | s->state |= (data[0] & data[1]); | |
436 | outl(s->state & 0x0f, dev->iobase + PCI9118_DO); | |
437 | } | |
438 | data[1] = s->state; | |
439 | ||
440 | return 2; | |
441 | } | |
442 | ||
443 | /* | |
444 | ============================================================================== | |
445 | */ | |
71b5f4f1 | 446 | static void interrupt_pci9118_ai_mode4_switch(struct comedi_device * dev) |
3063d6de MD |
447 | { |
448 | devpriv->AdFunctionReg = | |
449 | AdFunction_PDTrg | AdFunction_PETrg | AdFunction_AM; | |
450 | outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); | |
451 | outl(0x30, dev->iobase + PCI9118_CNTCTRL); | |
452 | outl((devpriv->dmabuf_hw[1 - devpriv->dma_actbuf] >> 1) & 0xff, | |
453 | dev->iobase + PCI9118_CNT0); | |
454 | outl((devpriv->dmabuf_hw[1 - devpriv->dma_actbuf] >> 9) & 0xff, | |
455 | dev->iobase + PCI9118_CNT0); | |
456 | devpriv->AdFunctionReg |= AdFunction_Start; | |
457 | outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); | |
458 | } | |
459 | ||
71b5f4f1 | 460 | static unsigned int defragment_dma_buffer(struct comedi_device * dev, |
34c43922 | 461 | struct comedi_subdevice * s, short * dma_buffer, unsigned int num_samples) |
3063d6de MD |
462 | { |
463 | unsigned int i = 0, j = 0; | |
464 | unsigned int start_pos = devpriv->ai_add_front, | |
465 | stop_pos = devpriv->ai_add_front + devpriv->ai_n_chan; | |
466 | unsigned int raw_scanlen = devpriv->ai_add_front + devpriv->ai_n_chan + | |
467 | devpriv->ai_add_back; | |
468 | ||
469 | for (i = 0; i < num_samples; i++) { | |
470 | if (devpriv->ai_act_dmapos >= start_pos && | |
471 | devpriv->ai_act_dmapos < stop_pos) { | |
472 | dma_buffer[j++] = dma_buffer[i]; | |
473 | } | |
474 | devpriv->ai_act_dmapos++; | |
475 | devpriv->ai_act_dmapos %= raw_scanlen; | |
476 | } | |
477 | ||
478 | return j; | |
479 | } | |
480 | ||
481 | /* | |
482 | ============================================================================== | |
483 | */ | |
71b5f4f1 | 484 | static unsigned int move_block_from_dma(struct comedi_device * dev, |
34c43922 | 485 | struct comedi_subdevice * s, short * dma_buffer, unsigned int num_samples) |
3063d6de MD |
486 | { |
487 | unsigned int num_bytes; | |
488 | ||
489 | num_samples = defragment_dma_buffer(dev, s, dma_buffer, num_samples); | |
490 | devpriv->ai_act_scan += | |
491 | (s->async->cur_chan + num_samples) / devpriv->ai_n_scanlen; | |
492 | s->async->cur_chan += num_samples; | |
493 | s->async->cur_chan %= devpriv->ai_n_scanlen; | |
494 | num_bytes = | |
495 | cfc_write_array_to_buffer(s, dma_buffer, | |
790c5541 BP |
496 | num_samples * sizeof(short)); |
497 | if (num_bytes < num_samples * sizeof(short)) | |
3063d6de MD |
498 | return -1; |
499 | return 0; | |
500 | } | |
501 | ||
502 | /* | |
503 | ============================================================================== | |
504 | */ | |
71b5f4f1 | 505 | static char pci9118_decode_error_status(struct comedi_device * dev, |
34c43922 | 506 | struct comedi_subdevice * s, unsigned char m) |
3063d6de MD |
507 | { |
508 | if (m & 0x100) { | |
509 | comedi_error(dev, "A/D FIFO Full status (Fatal Error!)"); | |
510 | devpriv->ai_maskerr &= ~0x100L; | |
511 | } | |
512 | if (m & 0x008) { | |
513 | comedi_error(dev, | |
514 | "A/D Burst Mode Overrun Status (Fatal Error!)"); | |
515 | devpriv->ai_maskerr &= ~0x008L; | |
516 | } | |
517 | if (m & 0x004) { | |
518 | comedi_error(dev, "A/D Over Speed Status (Warning!)"); | |
519 | devpriv->ai_maskerr &= ~0x004L; | |
520 | } | |
521 | if (m & 0x002) { | |
522 | comedi_error(dev, "A/D Overrun Status (Fatal Error!)"); | |
523 | devpriv->ai_maskerr &= ~0x002L; | |
524 | } | |
525 | if (m & devpriv->ai_maskharderr) { | |
526 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; | |
527 | pci9118_ai_cancel(dev, s); | |
528 | comedi_event(dev, s); | |
529 | return 1; | |
530 | } | |
531 | ||
532 | return 0; | |
533 | } | |
534 | ||
34c43922 | 535 | static void pci9118_ai_munge(struct comedi_device * dev, struct comedi_subdevice * s, |
3063d6de MD |
536 | void *data, unsigned int num_bytes, unsigned int start_chan_index) |
537 | { | |
790c5541 BP |
538 | unsigned int i, num_samples = num_bytes / sizeof(short); |
539 | short *array = data; | |
3063d6de MD |
540 | |
541 | for (i = 0; i < num_samples; i++) { | |
542 | if (devpriv->usedma) | |
543 | array[i] = be16_to_cpu(array[i]); | |
544 | if (devpriv->ai16bits) { | |
545 | array[i] ^= 0x8000; | |
546 | } else { | |
547 | array[i] = (array[i] >> 4) & 0x0fff; | |
548 | } | |
549 | } | |
550 | } | |
551 | ||
552 | /* | |
553 | ============================================================================== | |
554 | */ | |
71b5f4f1 | 555 | static void interrupt_pci9118_ai_onesample(struct comedi_device * dev, |
34c43922 | 556 | struct comedi_subdevice * s, unsigned short int_adstat, unsigned int int_amcc, |
3063d6de MD |
557 | unsigned short int_daq) |
558 | { | |
790c5541 | 559 | register short sampl; |
3063d6de MD |
560 | |
561 | s->async->events = 0; | |
562 | ||
563 | if (int_adstat & devpriv->ai_maskerr) | |
564 | if (pci9118_decode_error_status(dev, s, int_adstat)) | |
565 | return; | |
566 | ||
567 | sampl = inw(dev->iobase + PCI9118_AD_DATA); | |
568 | ||
569 | #ifdef PCI9118_PARANOIDCHECK | |
570 | if (devpriv->ai16bits == 0) { | |
0f04c356 | 571 | if ((sampl & 0x000f) != devpriv->chanlist[s->async->cur_chan]) { /* data dropout! */ |
3063d6de MD |
572 | rt_printk |
573 | ("comedi: A/D SAMPL - data dropout: received channel %d, expected %d!\n", | |
574 | sampl & 0x000f, | |
575 | devpriv->chanlist[s->async->cur_chan]); | |
576 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; | |
577 | pci9118_ai_cancel(dev, s); | |
578 | comedi_event(dev, s); | |
579 | return; | |
580 | } | |
581 | } | |
582 | #endif | |
583 | cfc_write_to_buffer(s, sampl); | |
584 | s->async->cur_chan++; | |
585 | if (s->async->cur_chan >= devpriv->ai_n_scanlen) { /* one scan done */ | |
586 | s->async->cur_chan %= devpriv->ai_n_scanlen; | |
587 | devpriv->ai_act_scan++; | |
588 | if (!(devpriv->ai_neverending)) | |
589 | if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */ | |
590 | pci9118_ai_cancel(dev, s); | |
591 | s->async->events |= COMEDI_CB_EOA; | |
592 | } | |
593 | } | |
594 | ||
595 | if (s->async->events) | |
596 | comedi_event(dev, s); | |
597 | } | |
598 | ||
599 | /* | |
600 | ============================================================================== | |
601 | */ | |
34c43922 | 602 | static void interrupt_pci9118_ai_dma(struct comedi_device * dev, struct comedi_subdevice * s, |
3063d6de MD |
603 | unsigned short int_adstat, unsigned int int_amcc, |
604 | unsigned short int_daq) | |
605 | { | |
606 | unsigned int next_dma_buf, samplesinbuf, sampls, m; | |
607 | ||
608 | if (int_amcc & MASTER_ABORT_INT) { | |
609 | comedi_error(dev, "AMCC IRQ - MASTER DMA ABORT!"); | |
610 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; | |
611 | pci9118_ai_cancel(dev, s); | |
612 | comedi_event(dev, s); | |
613 | return; | |
614 | } | |
615 | ||
616 | if (int_amcc & TARGET_ABORT_INT) { | |
617 | comedi_error(dev, "AMCC IRQ - TARGET DMA ABORT!"); | |
618 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; | |
619 | pci9118_ai_cancel(dev, s); | |
620 | comedi_event(dev, s); | |
621 | return; | |
622 | } | |
623 | ||
624 | if (int_adstat & devpriv->ai_maskerr) | |
0f04c356 | 625 | /* if (int_adstat & 0x106) */ |
3063d6de MD |
626 | if (pci9118_decode_error_status(dev, s, int_adstat)) |
627 | return; | |
628 | ||
0f04c356 BP |
629 | samplesinbuf = devpriv->dmabuf_use_size[devpriv->dma_actbuf] >> 1; /* number of received real samples */ |
630 | /* DPRINTK("dma_actbuf=%d\n",devpriv->dma_actbuf); */ | |
3063d6de | 631 | |
0f04c356 | 632 | if (devpriv->dma_doublebuf) { /* switch DMA buffers if is used double buffering */ |
3063d6de MD |
633 | next_dma_buf = 1 - devpriv->dma_actbuf; |
634 | outl(devpriv->dmabuf_hw[next_dma_buf], | |
635 | devpriv->iobase_a + AMCC_OP_REG_MWAR); | |
636 | outl(devpriv->dmabuf_use_size[next_dma_buf], | |
637 | devpriv->iobase_a + AMCC_OP_REG_MWTC); | |
638 | devpriv->dmabuf_used_size[next_dma_buf] = | |
639 | devpriv->dmabuf_use_size[next_dma_buf]; | |
640 | if (devpriv->ai_do == 4) | |
641 | interrupt_pci9118_ai_mode4_switch(dev); | |
642 | } | |
643 | ||
644 | if (samplesinbuf) { | |
0f04c356 BP |
645 | m = devpriv->ai_data_len >> 1; /* how many samples is to end of buffer */ |
646 | /* DPRINTK("samps=%d m=%d %d %d\n",samplesinbuf,m,s->async->buf_int_count,s->async->buf_int_ptr); */ | |
3063d6de MD |
647 | sampls = m; |
648 | move_block_from_dma(dev, s, | |
649 | devpriv->dmabuf_virt[devpriv->dma_actbuf], | |
650 | samplesinbuf); | |
0f04c356 | 651 | m = m - sampls; /* m= how many samples was transfered */ |
3063d6de | 652 | } |
0f04c356 | 653 | /* DPRINTK("YYY\n"); */ |
3063d6de MD |
654 | |
655 | if (!devpriv->ai_neverending) | |
656 | if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */ | |
657 | pci9118_ai_cancel(dev, s); | |
658 | s->async->events |= COMEDI_CB_EOA; | |
659 | } | |
660 | ||
0f04c356 | 661 | if (devpriv->dma_doublebuf) { /* switch dma buffers */ |
3063d6de | 662 | devpriv->dma_actbuf = 1 - devpriv->dma_actbuf; |
0f04c356 | 663 | } else { /* restart DMA if is not used double buffering */ |
3063d6de MD |
664 | outl(devpriv->dmabuf_hw[0], |
665 | devpriv->iobase_a + AMCC_OP_REG_MWAR); | |
666 | outl(devpriv->dmabuf_use_size[0], | |
667 | devpriv->iobase_a + AMCC_OP_REG_MWTC); | |
668 | if (devpriv->ai_do == 4) | |
669 | interrupt_pci9118_ai_mode4_switch(dev); | |
670 | } | |
671 | ||
672 | comedi_event(dev, s); | |
673 | } | |
674 | ||
675 | /* | |
676 | ============================================================================== | |
677 | */ | |
70265d24 | 678 | static irqreturn_t interrupt_pci9118(int irq, void *d) |
3063d6de | 679 | { |
71b5f4f1 | 680 | struct comedi_device *dev = d; |
3063d6de MD |
681 | unsigned int int_daq = 0, int_amcc, int_adstat; |
682 | ||
683 | if (!dev->attached) | |
0f04c356 | 684 | return IRQ_NONE; /* not fully initialized */ |
3063d6de | 685 | |
0f04c356 BP |
686 | int_daq = inl(dev->iobase + PCI9118_INTSRC) & 0xf; /* get IRQ reasons from card */ |
687 | int_amcc = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* get INT register from AMCC chip */ | |
3063d6de | 688 | |
0f04c356 | 689 | /* DPRINTK("INT daq=0x%01x amcc=0x%08x MWAR=0x%08x MWTC=0x%08x ADSTAT=0x%02x ai_do=%d\n", int_daq, int_amcc, inl(devpriv->iobase_a+AMCC_OP_REG_MWAR), inl(devpriv->iobase_a+AMCC_OP_REG_MWTC), inw(dev->iobase+PCI9118_ADSTAT)&0x1ff,devpriv->ai_do); */ |
3063d6de MD |
690 | |
691 | if ((!int_daq) && (!(int_amcc & ANY_S593X_INT))) | |
0f04c356 | 692 | return IRQ_NONE; /* interrupt from other source */ |
3063d6de | 693 | |
0f04c356 | 694 | outl(int_amcc | 0x00ff0000, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* shutdown IRQ reasons in AMCC */ |
3063d6de | 695 | |
0f04c356 | 696 | int_adstat = inw(dev->iobase + PCI9118_ADSTAT) & 0x1ff; /* get STATUS register */ |
3063d6de MD |
697 | |
698 | if (devpriv->ai_do) { | |
699 | if (devpriv->ai12_startstop) | |
0f04c356 | 700 | if ((int_adstat & AdStatus_DTH) && (int_daq & Int_DTrg)) { /* start stop of measure */ |
3063d6de MD |
701 | if (devpriv->ai12_startstop & START_AI_EXT) { |
702 | devpriv->ai12_startstop &= | |
703 | ~START_AI_EXT; | |
704 | if (!(devpriv->ai12_startstop & | |
705 | STOP_AI_EXT)) | |
0f04c356 BP |
706 | pci9118_exttrg_del(dev, EXTTRG_AI); /* deactivate EXT trigger */ |
707 | start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1, devpriv->ai_divisor2); /* start pacer */ | |
3063d6de MD |
708 | outl(devpriv->AdControlReg, |
709 | dev->iobase + PCI9118_ADCNTRL); | |
710 | } else { | |
711 | if (devpriv-> | |
712 | ai12_startstop & STOP_AI_EXT) { | |
713 | devpriv->ai12_startstop &= | |
714 | ~STOP_AI_EXT; | |
0f04c356 BP |
715 | pci9118_exttrg_del(dev, EXTTRG_AI); /* deactivate EXT trigger */ |
716 | devpriv->ai_neverending = 0; /* well, on next interrupt from DMA/EOC measure will stop */ | |
3063d6de MD |
717 | } |
718 | } | |
719 | } | |
720 | ||
721 | (devpriv->int_ai_func) (dev, dev->subdevices + 0, int_adstat, | |
722 | int_amcc, int_daq); | |
723 | ||
724 | } | |
725 | return IRQ_HANDLED; | |
726 | } | |
727 | ||
728 | /* | |
729 | ============================================================================== | |
730 | */ | |
34c43922 | 731 | static int pci9118_ai_inttrig(struct comedi_device * dev, struct comedi_subdevice * s, |
3063d6de MD |
732 | unsigned int trignum) |
733 | { | |
734 | if (trignum != devpriv->ai_inttrig_start) | |
735 | return -EINVAL; | |
736 | ||
737 | devpriv->ai12_startstop &= ~START_AI_INT; | |
738 | s->async->inttrig = NULL; | |
739 | ||
740 | outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); | |
741 | outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); | |
742 | if (devpriv->ai_do != 3) { | |
743 | start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1, | |
744 | devpriv->ai_divisor2); | |
745 | devpriv->AdControlReg |= AdControl_SoftG; | |
746 | } | |
747 | outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); | |
748 | ||
749 | return 1; | |
750 | } | |
751 | ||
752 | /* | |
753 | ============================================================================== | |
754 | */ | |
34c43922 | 755 | static int pci9118_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s, |
ea6d0d4c | 756 | struct comedi_cmd * cmd) |
3063d6de MD |
757 | { |
758 | int err = 0; | |
759 | int tmp, divisor1, divisor2; | |
760 | ||
761 | /* step 1: make sure trigger sources are trivially valid */ | |
762 | ||
763 | tmp = cmd->start_src; | |
764 | cmd->start_src &= TRIG_NOW | TRIG_EXT | TRIG_INT; | |
765 | if (!cmd->start_src || tmp != cmd->start_src) | |
766 | err++; | |
767 | ||
768 | tmp = cmd->scan_begin_src; | |
769 | if (devpriv->master) { | |
770 | cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW; | |
771 | } else { | |
772 | cmd->scan_begin_src &= TRIG_FOLLOW; | |
773 | } | |
774 | if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) | |
775 | err++; | |
776 | ||
777 | tmp = cmd->convert_src; | |
778 | if (devpriv->master) { | |
779 | cmd->convert_src &= TRIG_TIMER | TRIG_EXT | TRIG_NOW; | |
780 | } else { | |
781 | cmd->convert_src &= TRIG_TIMER | TRIG_EXT; | |
782 | } | |
783 | if (!cmd->convert_src || tmp != cmd->convert_src) | |
784 | err++; | |
785 | ||
786 | tmp = cmd->scan_end_src; | |
787 | cmd->scan_end_src &= TRIG_COUNT; | |
788 | if (!cmd->scan_end_src || tmp != cmd->scan_end_src) | |
789 | err++; | |
790 | ||
791 | tmp = cmd->stop_src; | |
792 | cmd->stop_src &= TRIG_COUNT | TRIG_NONE | TRIG_EXT; | |
793 | if (!cmd->stop_src || tmp != cmd->stop_src) | |
794 | err++; | |
795 | ||
796 | if (err) | |
797 | return 1; | |
798 | ||
799 | /* step 2: make sure trigger sources are unique and mutually compatible */ | |
800 | ||
801 | if (cmd->start_src != TRIG_NOW && | |
802 | cmd->start_src != TRIG_INT && cmd->start_src != TRIG_EXT) { | |
803 | cmd->start_src = TRIG_NOW; | |
804 | err++; | |
805 | } | |
806 | ||
807 | if (cmd->scan_begin_src != TRIG_TIMER && | |
808 | cmd->scan_begin_src != TRIG_EXT && | |
809 | cmd->scan_begin_src != TRIG_INT && | |
810 | cmd->scan_begin_src != TRIG_FOLLOW) { | |
811 | cmd->scan_begin_src = TRIG_FOLLOW; | |
812 | err++; | |
813 | } | |
814 | ||
815 | if (cmd->convert_src != TRIG_TIMER && | |
816 | cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW) { | |
817 | cmd->convert_src = TRIG_TIMER; | |
818 | err++; | |
819 | } | |
820 | ||
821 | if (cmd->scan_end_src != TRIG_COUNT) { | |
822 | cmd->scan_end_src = TRIG_COUNT; | |
823 | err++; | |
824 | } | |
825 | ||
826 | if (cmd->stop_src != TRIG_NONE && | |
827 | cmd->stop_src != TRIG_COUNT && | |
828 | cmd->stop_src != TRIG_INT && cmd->stop_src != TRIG_EXT) { | |
829 | cmd->stop_src = TRIG_COUNT; | |
830 | err++; | |
831 | } | |
832 | ||
833 | if (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) { | |
834 | cmd->start_src = TRIG_NOW; | |
835 | err++; | |
836 | } | |
837 | ||
838 | if (cmd->start_src == TRIG_INT && cmd->scan_begin_src == TRIG_INT) { | |
839 | cmd->start_src = TRIG_NOW; | |
840 | err++; | |
841 | } | |
842 | ||
843 | if ((cmd->scan_begin_src & (TRIG_TIMER | TRIG_EXT)) && | |
844 | (!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW)))) { | |
845 | cmd->convert_src = TRIG_TIMER; | |
846 | err++; | |
847 | } | |
848 | ||
849 | if ((cmd->scan_begin_src == TRIG_FOLLOW) && | |
850 | (!(cmd->convert_src & (TRIG_TIMER | TRIG_EXT)))) { | |
851 | cmd->convert_src = TRIG_TIMER; | |
852 | err++; | |
853 | } | |
854 | ||
855 | if (cmd->stop_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) { | |
856 | cmd->stop_src = TRIG_COUNT; | |
857 | err++; | |
858 | } | |
859 | ||
860 | if (err) | |
861 | return 2; | |
862 | ||
863 | /* step 3: make sure arguments are trivially compatible */ | |
864 | ||
865 | if (cmd->start_src & (TRIG_NOW | TRIG_EXT)) | |
866 | if (cmd->start_arg != 0) { | |
867 | cmd->start_arg = 0; | |
868 | err++; | |
869 | } | |
870 | ||
871 | if (cmd->scan_begin_src & (TRIG_FOLLOW | TRIG_EXT)) | |
872 | if (cmd->scan_begin_arg != 0) { | |
873 | cmd->scan_begin_arg = 0; | |
874 | err++; | |
875 | } | |
876 | ||
877 | if ((cmd->scan_begin_src == TRIG_TIMER) && | |
878 | (cmd->convert_src == TRIG_TIMER) && (cmd->scan_end_arg == 1)) { | |
879 | cmd->scan_begin_src = TRIG_FOLLOW; | |
880 | cmd->convert_arg = cmd->scan_begin_arg; | |
881 | cmd->scan_begin_arg = 0; | |
882 | } | |
883 | ||
884 | if (cmd->scan_begin_src == TRIG_TIMER) | |
885 | if (cmd->scan_begin_arg < this_board->ai_ns_min) { | |
886 | cmd->scan_begin_arg = this_board->ai_ns_min; | |
887 | err++; | |
888 | } | |
889 | ||
890 | if (cmd->scan_begin_src == TRIG_EXT) | |
891 | if (cmd->scan_begin_arg) { | |
892 | cmd->scan_begin_arg = 0; | |
893 | err++; | |
894 | if (cmd->scan_end_arg > 65535) { | |
895 | cmd->scan_end_arg = 65535; | |
896 | err++; | |
897 | } | |
898 | } | |
899 | ||
900 | if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) | |
901 | if (cmd->convert_arg < this_board->ai_ns_min) { | |
902 | cmd->convert_arg = this_board->ai_ns_min; | |
903 | err++; | |
904 | } | |
905 | ||
906 | if (cmd->convert_src == TRIG_EXT) | |
907 | if (cmd->convert_arg) { | |
908 | cmd->convert_arg = 0; | |
909 | err++; | |
910 | } | |
911 | ||
912 | if (cmd->stop_src == TRIG_COUNT) { | |
913 | if (!cmd->stop_arg) { | |
914 | cmd->stop_arg = 1; | |
915 | err++; | |
916 | } | |
917 | } else { /* TRIG_NONE */ | |
918 | if (cmd->stop_arg != 0) { | |
919 | cmd->stop_arg = 0; | |
920 | err++; | |
921 | } | |
922 | } | |
923 | ||
924 | if (!cmd->chanlist_len) { | |
925 | cmd->chanlist_len = 1; | |
926 | err++; | |
927 | } | |
928 | ||
929 | if (cmd->chanlist_len > this_board->n_aichanlist) { | |
930 | cmd->chanlist_len = this_board->n_aichanlist; | |
931 | err++; | |
932 | } | |
933 | ||
934 | if (cmd->scan_end_arg < cmd->chanlist_len) { | |
935 | cmd->scan_end_arg = cmd->chanlist_len; | |
936 | err++; | |
937 | } | |
938 | ||
939 | if ((cmd->scan_end_arg % cmd->chanlist_len)) { | |
940 | cmd->scan_end_arg = | |
941 | cmd->chanlist_len * (cmd->scan_end_arg / | |
942 | cmd->chanlist_len); | |
943 | err++; | |
944 | } | |
945 | ||
946 | if (err) | |
947 | return 3; | |
948 | ||
949 | /* step 4: fix up any arguments */ | |
950 | ||
951 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
952 | tmp = cmd->scan_begin_arg; | |
0f04c356 | 953 | /* rt_printk("S1 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */ |
3063d6de MD |
954 | i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1, |
955 | &divisor2, &cmd->scan_begin_arg, | |
956 | cmd->flags & TRIG_ROUND_MASK); | |
0f04c356 | 957 | /* rt_printk("S2 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */ |
3063d6de MD |
958 | if (cmd->scan_begin_arg < this_board->ai_ns_min) |
959 | cmd->scan_begin_arg = this_board->ai_ns_min; | |
960 | if (tmp != cmd->scan_begin_arg) | |
961 | err++; | |
962 | } | |
963 | ||
964 | if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) { | |
965 | tmp = cmd->convert_arg; | |
966 | i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1, | |
967 | &divisor2, &cmd->convert_arg, | |
968 | cmd->flags & TRIG_ROUND_MASK); | |
0f04c356 | 969 | /* rt_printk("s1 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */ |
3063d6de MD |
970 | if (cmd->convert_arg < this_board->ai_ns_min) |
971 | cmd->convert_arg = this_board->ai_ns_min; | |
972 | if (tmp != cmd->convert_arg) | |
973 | err++; | |
974 | if (cmd->scan_begin_src == TRIG_TIMER | |
975 | && cmd->convert_src == TRIG_NOW) { | |
976 | if (cmd->convert_arg == 0) { | |
977 | if (cmd->scan_begin_arg < | |
978 | this_board->ai_ns_min * | |
979 | (cmd->scan_end_arg + 2)) { | |
980 | cmd->scan_begin_arg = | |
981 | this_board->ai_ns_min * | |
982 | (cmd->scan_end_arg + 2); | |
0f04c356 | 983 | /* rt_printk("s2 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */ |
3063d6de MD |
984 | err++; |
985 | } | |
986 | } else { | |
987 | if (cmd->scan_begin_arg < | |
988 | cmd->convert_arg * cmd->chanlist_len) { | |
989 | cmd->scan_begin_arg = | |
990 | cmd->convert_arg * | |
991 | cmd->chanlist_len; | |
0f04c356 | 992 | /* rt_printk("s3 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */ |
3063d6de MD |
993 | err++; |
994 | } | |
995 | } | |
996 | } | |
997 | } | |
998 | ||
999 | if (err) | |
1000 | return 4; | |
1001 | ||
1002 | if (cmd->chanlist) | |
1003 | if (!check_channel_list(dev, s, cmd->chanlist_len, | |
1004 | cmd->chanlist, 0, 0)) | |
0f04c356 | 1005 | return 5; /* incorrect channels list */ |
3063d6de MD |
1006 | |
1007 | return 0; | |
1008 | } | |
1009 | ||
1010 | /* | |
1011 | ============================================================================== | |
1012 | */ | |
71b5f4f1 | 1013 | static int Compute_and_setup_dma(struct comedi_device * dev) |
3063d6de MD |
1014 | { |
1015 | unsigned int dmalen0, dmalen1, i; | |
1016 | ||
1017 | DPRINTK("adl_pci9118 EDBG: BGN: Compute_and_setup_dma()\n"); | |
1018 | dmalen0 = devpriv->dmabuf_size[0]; | |
1019 | dmalen1 = devpriv->dmabuf_size[1]; | |
1020 | DPRINTK("1 dmalen0=%d dmalen1=%d ai_data_len=%d\n", dmalen0, dmalen1, | |
1021 | devpriv->ai_data_len); | |
0f04c356 | 1022 | /* isn't output buff smaller that our DMA buff? */ |
3063d6de | 1023 | if (dmalen0 > (devpriv->ai_data_len)) { |
0f04c356 | 1024 | dmalen0 = devpriv->ai_data_len & ~3L; /* allign to 32bit down */ |
3063d6de MD |
1025 | } |
1026 | if (dmalen1 > (devpriv->ai_data_len)) { | |
0f04c356 | 1027 | dmalen1 = devpriv->ai_data_len & ~3L; /* allign to 32bit down */ |
3063d6de MD |
1028 | } |
1029 | DPRINTK("2 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1); | |
1030 | ||
0f04c356 | 1031 | /* we want wake up every scan? */ |
3063d6de MD |
1032 | if (devpriv->ai_flags & TRIG_WAKE_EOS) { |
1033 | if (dmalen0 < (devpriv->ai_n_realscanlen << 1)) { | |
0f04c356 | 1034 | /* uff, too short DMA buffer, disable EOS support! */ |
3063d6de MD |
1035 | devpriv->ai_flags &= (~TRIG_WAKE_EOS); |
1036 | rt_printk | |
1037 | ("comedi%d: WAR: DMA0 buf too short, cann't support TRIG_WAKE_EOS (%d<%d)\n", | |
1038 | dev->minor, dmalen0, | |
1039 | devpriv->ai_n_realscanlen << 1); | |
1040 | } else { | |
0f04c356 | 1041 | /* short first DMA buffer to one scan */ |
3063d6de MD |
1042 | dmalen0 = devpriv->ai_n_realscanlen << 1; |
1043 | DPRINTK("21 dmalen0=%d ai_n_realscanlen=%d useeoshandle=%d\n", dmalen0, devpriv->ai_n_realscanlen, devpriv->useeoshandle); | |
1044 | if (devpriv->useeoshandle) | |
1045 | dmalen0 += 2; | |
1046 | if (dmalen0 < 4) { | |
1047 | rt_printk | |
1048 | ("comedi%d: ERR: DMA0 buf len bug? (%d<4)\n", | |
1049 | dev->minor, dmalen0); | |
1050 | dmalen0 = 4; | |
1051 | } | |
1052 | } | |
1053 | } | |
1054 | if (devpriv->ai_flags & TRIG_WAKE_EOS) { | |
1055 | if (dmalen1 < (devpriv->ai_n_realscanlen << 1)) { | |
0f04c356 | 1056 | /* uff, too short DMA buffer, disable EOS support! */ |
3063d6de MD |
1057 | devpriv->ai_flags &= (~TRIG_WAKE_EOS); |
1058 | rt_printk | |
1059 | ("comedi%d: WAR: DMA1 buf too short, cann't support TRIG_WAKE_EOS (%d<%d)\n", | |
1060 | dev->minor, dmalen1, | |
1061 | devpriv->ai_n_realscanlen << 1); | |
1062 | } else { | |
0f04c356 | 1063 | /* short second DMA buffer to one scan */ |
3063d6de MD |
1064 | dmalen1 = devpriv->ai_n_realscanlen << 1; |
1065 | DPRINTK("22 dmalen1=%d ai_n_realscanlen=%d useeoshandle=%d\n", dmalen1, devpriv->ai_n_realscanlen, devpriv->useeoshandle); | |
1066 | if (devpriv->useeoshandle) | |
1067 | dmalen1 -= 2; | |
1068 | if (dmalen1 < 4) { | |
1069 | rt_printk | |
1070 | ("comedi%d: ERR: DMA1 buf len bug? (%d<4)\n", | |
1071 | dev->minor, dmalen1); | |
1072 | dmalen1 = 4; | |
1073 | } | |
1074 | } | |
1075 | } | |
1076 | ||
1077 | DPRINTK("3 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1); | |
0f04c356 | 1078 | /* transfer without TRIG_WAKE_EOS */ |
3063d6de | 1079 | if (!(devpriv->ai_flags & TRIG_WAKE_EOS)) { |
0f04c356 | 1080 | /* if it's possible then allign DMA buffers to length of scan */ |
3063d6de MD |
1081 | i = dmalen0; |
1082 | dmalen0 = | |
1083 | (dmalen0 / (devpriv->ai_n_realscanlen << 1)) * | |
1084 | (devpriv->ai_n_realscanlen << 1); | |
1085 | dmalen0 &= ~3L; | |
1086 | if (!dmalen0) | |
0f04c356 | 1087 | dmalen0 = i; /* uff. very long scan? */ |
3063d6de MD |
1088 | i = dmalen1; |
1089 | dmalen1 = | |
1090 | (dmalen1 / (devpriv->ai_n_realscanlen << 1)) * | |
1091 | (devpriv->ai_n_realscanlen << 1); | |
1092 | dmalen1 &= ~3L; | |
1093 | if (!dmalen1) | |
0f04c356 BP |
1094 | dmalen1 = i; /* uff. very long scan? */ |
1095 | /* if measure isn't neverending then test, if it whole fits into one or two DMA buffers */ | |
3063d6de | 1096 | if (!devpriv->ai_neverending) { |
0f04c356 | 1097 | /* fits whole measure into one DMA buffer? */ |
3063d6de MD |
1098 | if (dmalen0 > |
1099 | ((devpriv->ai_n_realscanlen << 1) * | |
1100 | devpriv->ai_scans)) { | |
1101 | DPRINTK("3.0 ai_n_realscanlen=%d ai_scans=%d \n", devpriv->ai_n_realscanlen, devpriv->ai_scans); | |
1102 | dmalen0 = | |
1103 | (devpriv->ai_n_realscanlen << 1) * | |
1104 | devpriv->ai_scans; | |
1105 | DPRINTK("3.1 dmalen0=%d dmalen1=%d \n", dmalen0, | |
1106 | dmalen1); | |
1107 | dmalen0 &= ~3L; | |
0f04c356 | 1108 | } else { /* fits whole measure into two DMA buffer? */ |
3063d6de MD |
1109 | if (dmalen1 > |
1110 | ((devpriv->ai_n_realscanlen << 1) * | |
1111 | devpriv->ai_scans - dmalen0)) | |
1112 | dmalen1 = | |
1113 | (devpriv-> | |
1114 | ai_n_realscanlen << 1) * | |
1115 | devpriv->ai_scans - dmalen0; | |
1116 | DPRINTK("3.2 dmalen0=%d dmalen1=%d \n", dmalen0, | |
1117 | dmalen1); | |
1118 | dmalen1 &= ~3L; | |
1119 | } | |
1120 | } | |
1121 | } | |
1122 | ||
1123 | DPRINTK("4 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1); | |
1124 | ||
0f04c356 | 1125 | /* these DMA buffer size we'll be used */ |
3063d6de MD |
1126 | devpriv->dma_actbuf = 0; |
1127 | devpriv->dmabuf_use_size[0] = dmalen0; | |
1128 | devpriv->dmabuf_use_size[1] = dmalen1; | |
1129 | ||
1130 | DPRINTK("5 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1); | |
1131 | #if 0 | |
1132 | if (devpriv->ai_n_scanlen < this_board->half_fifo_size) { | |
1133 | devpriv->dmabuf_panic_size[0] = | |
1134 | (this_board->half_fifo_size / devpriv->ai_n_scanlen + | |
790c5541 | 1135 | 1) * devpriv->ai_n_scanlen * sizeof(short); |
3063d6de MD |
1136 | devpriv->dmabuf_panic_size[1] = |
1137 | (this_board->half_fifo_size / devpriv->ai_n_scanlen + | |
790c5541 | 1138 | 1) * devpriv->ai_n_scanlen * sizeof(short); |
3063d6de MD |
1139 | } else { |
1140 | devpriv->dmabuf_panic_size[0] = | |
1141 | (devpriv->ai_n_scanlen << 1) % devpriv->dmabuf_size[0]; | |
1142 | devpriv->dmabuf_panic_size[1] = | |
1143 | (devpriv->ai_n_scanlen << 1) % devpriv->dmabuf_size[1]; | |
1144 | } | |
1145 | #endif | |
1146 | ||
0f04c356 | 1147 | outl(inl(devpriv->iobase_a + AMCC_OP_REG_MCSR) & (~EN_A2P_TRANSFERS), devpriv->iobase_a + AMCC_OP_REG_MCSR); /* stop DMA */ |
3063d6de MD |
1148 | outl(devpriv->dmabuf_hw[0], devpriv->iobase_a + AMCC_OP_REG_MWAR); |
1149 | outl(devpriv->dmabuf_use_size[0], devpriv->iobase_a + AMCC_OP_REG_MWTC); | |
0f04c356 | 1150 | /* init DMA transfer */ |
3063d6de MD |
1151 | outl(0x00000000 | AINT_WRITE_COMPL, |
1152 | devpriv->iobase_a + AMCC_OP_REG_INTCSR); | |
0f04c356 | 1153 | /* outl(0x02000000|AINT_WRITE_COMPL, devpriv->iobase_a+AMCC_OP_REG_INTCSR); */ |
3063d6de MD |
1154 | |
1155 | outl(inl(devpriv->iobase_a + | |
1156 | AMCC_OP_REG_MCSR) | RESET_A2P_FLAGS | A2P_HI_PRIORITY | | |
1157 | EN_A2P_TRANSFERS, devpriv->iobase_a + AMCC_OP_REG_MCSR); | |
0f04c356 | 1158 | outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | EN_A2P_TRANSFERS, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* allow bus mastering */ |
3063d6de MD |
1159 | |
1160 | DPRINTK("adl_pci9118 EDBG: END: Compute_and_setup_dma()\n"); | |
1161 | return 0; | |
1162 | } | |
1163 | ||
1164 | /* | |
1165 | ============================================================================== | |
1166 | */ | |
34c43922 | 1167 | static int pci9118_ai_docmd_sampl(struct comedi_device * dev, struct comedi_subdevice * s) |
3063d6de MD |
1168 | { |
1169 | DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_sampl(%d,) [%d]\n", | |
1170 | dev->minor, devpriv->ai_do); | |
1171 | switch (devpriv->ai_do) { | |
1172 | case 1: | |
1173 | devpriv->AdControlReg |= AdControl_TmrTr; | |
1174 | break; | |
1175 | case 2: | |
1176 | comedi_error(dev, "pci9118_ai_docmd_sampl() mode 2 bug!\n"); | |
1177 | return -EIO; | |
1178 | case 3: | |
1179 | devpriv->AdControlReg |= AdControl_ExtM; | |
1180 | break; | |
1181 | case 4: | |
1182 | comedi_error(dev, "pci9118_ai_docmd_sampl() mode 4 bug!\n"); | |
1183 | return -EIO; | |
1184 | default: | |
1185 | comedi_error(dev, | |
1186 | "pci9118_ai_docmd_sampl() mode number bug!\n"); | |
1187 | return -EIO; | |
1188 | }; | |
1189 | ||
0f04c356 | 1190 | devpriv->int_ai_func = interrupt_pci9118_ai_onesample; /* transfer function */ |
3063d6de MD |
1191 | |
1192 | if (devpriv->ai12_startstop) | |
0f04c356 | 1193 | pci9118_exttrg_add(dev, EXTTRG_AI); /* activate EXT trigger */ |
3063d6de MD |
1194 | |
1195 | if ((devpriv->ai_do == 1) || (devpriv->ai_do == 2)) | |
1196 | devpriv->IntControlReg |= Int_Timer; | |
1197 | ||
1198 | devpriv->AdControlReg |= AdControl_Int; | |
1199 | ||
0f04c356 | 1200 | outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* allow INT in AMCC */ |
3063d6de MD |
1201 | |
1202 | if (!(devpriv->ai12_startstop & (START_AI_EXT | START_AI_INT))) { | |
1203 | outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); | |
1204 | outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); | |
1205 | if (devpriv->ai_do != 3) { | |
1206 | start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1, | |
1207 | devpriv->ai_divisor2); | |
1208 | devpriv->AdControlReg |= AdControl_SoftG; | |
1209 | } | |
1210 | outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); | |
1211 | } | |
1212 | ||
1213 | DPRINTK("adl_pci9118 EDBG: END: pci9118_ai_docmd_sampl()\n"); | |
1214 | return 0; | |
1215 | } | |
1216 | ||
1217 | /* | |
1218 | ============================================================================== | |
1219 | */ | |
34c43922 | 1220 | static int pci9118_ai_docmd_dma(struct comedi_device * dev, struct comedi_subdevice * s) |
3063d6de MD |
1221 | { |
1222 | DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_dma(%d,) [%d,%d]\n", | |
1223 | dev->minor, devpriv->ai_do, devpriv->usedma); | |
1224 | Compute_and_setup_dma(dev); | |
1225 | ||
1226 | switch (devpriv->ai_do) { | |
1227 | case 1: | |
1228 | devpriv->AdControlReg |= | |
1229 | ((AdControl_TmrTr | AdControl_Dma) & 0xff); | |
1230 | break; | |
1231 | case 2: | |
1232 | devpriv->AdControlReg |= | |
1233 | ((AdControl_TmrTr | AdControl_Dma) & 0xff); | |
1234 | devpriv->AdFunctionReg = | |
1235 | AdFunction_PDTrg | AdFunction_PETrg | AdFunction_BM | | |
1236 | AdFunction_BS; | |
1237 | if (devpriv->usessh && (!devpriv->softsshdelay)) | |
1238 | devpriv->AdFunctionReg |= AdFunction_BSSH; | |
1239 | outl(devpriv->ai_n_realscanlen, dev->iobase + PCI9118_BURST); | |
1240 | break; | |
1241 | case 3: | |
1242 | devpriv->AdControlReg |= | |
1243 | ((AdControl_ExtM | AdControl_Dma) & 0xff); | |
1244 | devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg; | |
1245 | break; | |
1246 | case 4: | |
1247 | devpriv->AdControlReg |= | |
1248 | ((AdControl_TmrTr | AdControl_Dma) & 0xff); | |
1249 | devpriv->AdFunctionReg = | |
1250 | AdFunction_PDTrg | AdFunction_PETrg | AdFunction_AM; | |
1251 | outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); | |
1252 | outl(0x30, dev->iobase + PCI9118_CNTCTRL); | |
1253 | outl((devpriv->dmabuf_hw[0] >> 1) & 0xff, | |
1254 | dev->iobase + PCI9118_CNT0); | |
1255 | outl((devpriv->dmabuf_hw[0] >> 9) & 0xff, | |
1256 | dev->iobase + PCI9118_CNT0); | |
1257 | devpriv->AdFunctionReg |= AdFunction_Start; | |
1258 | break; | |
1259 | default: | |
1260 | comedi_error(dev, "pci9118_ai_docmd_dma() mode number bug!\n"); | |
1261 | return -EIO; | |
1262 | }; | |
1263 | ||
1264 | if (devpriv->ai12_startstop) { | |
0f04c356 | 1265 | pci9118_exttrg_add(dev, EXTTRG_AI); /* activate EXT trigger */ |
3063d6de MD |
1266 | } |
1267 | ||
0f04c356 | 1268 | devpriv->int_ai_func = interrupt_pci9118_ai_dma; /* transfer function */ |
3063d6de MD |
1269 | |
1270 | outl(0x02000000 | AINT_WRITE_COMPL, | |
1271 | devpriv->iobase_a + AMCC_OP_REG_INTCSR); | |
1272 | ||
1273 | if (!(devpriv->ai12_startstop & (START_AI_EXT | START_AI_INT))) { | |
1274 | outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); | |
1275 | outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); | |
1276 | if (devpriv->ai_do != 3) { | |
1277 | start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1, | |
1278 | devpriv->ai_divisor2); | |
1279 | devpriv->AdControlReg |= AdControl_SoftG; | |
1280 | } | |
1281 | outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); | |
1282 | } | |
1283 | ||
1284 | DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_dma()\n"); | |
1285 | return 0; | |
1286 | } | |
1287 | ||
1288 | /* | |
1289 | ============================================================================== | |
1290 | */ | |
34c43922 | 1291 | static int pci9118_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s) |
3063d6de | 1292 | { |
ea6d0d4c | 1293 | struct comedi_cmd *cmd = &s->async->cmd; |
3063d6de MD |
1294 | unsigned int addchans = 0; |
1295 | int ret = 0; | |
1296 | ||
1297 | DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_cmd(%d,)\n", dev->minor); | |
1298 | devpriv->ai12_startstop = 0; | |
1299 | devpriv->ai_flags = cmd->flags; | |
1300 | devpriv->ai_n_chan = cmd->chanlist_len; | |
1301 | devpriv->ai_n_scanlen = cmd->scan_end_arg; | |
1302 | devpriv->ai_chanlist = cmd->chanlist; | |
1303 | devpriv->ai_data = s->async->prealloc_buf; | |
1304 | devpriv->ai_data_len = s->async->prealloc_bufsz; | |
1305 | devpriv->ai_timer1 = 0; | |
1306 | devpriv->ai_timer2 = 0; | |
1307 | devpriv->ai_add_front = 0; | |
1308 | devpriv->ai_add_back = 0; | |
1309 | devpriv->ai_maskerr = 0x10e; | |
1310 | ||
0f04c356 | 1311 | /* prepare for start/stop conditions */ |
3063d6de MD |
1312 | if (cmd->start_src == TRIG_EXT) |
1313 | devpriv->ai12_startstop |= START_AI_EXT; | |
1314 | if (cmd->stop_src == TRIG_EXT) { | |
1315 | devpriv->ai_neverending = 1; | |
1316 | devpriv->ai12_startstop |= STOP_AI_EXT; | |
1317 | } | |
1318 | if (cmd->start_src == TRIG_INT) { | |
1319 | devpriv->ai12_startstop |= START_AI_INT; | |
1320 | devpriv->ai_inttrig_start = cmd->start_arg; | |
1321 | s->async->inttrig = pci9118_ai_inttrig; | |
1322 | } | |
1323 | #if 0 | |
1324 | if (cmd->stop_src == TRIG_INT) { | |
1325 | devpriv->ai_neverending = 1; | |
1326 | devpriv->ai12_startstop |= STOP_AI_INT; | |
1327 | } | |
1328 | #endif | |
1329 | if (cmd->stop_src == TRIG_NONE) | |
1330 | devpriv->ai_neverending = 1; | |
1331 | if (cmd->stop_src == TRIG_COUNT) { | |
1332 | devpriv->ai_scans = cmd->stop_arg; | |
1333 | devpriv->ai_neverending = 0; | |
1334 | } else { | |
1335 | devpriv->ai_scans = 0; | |
1336 | } | |
1337 | ||
0f04c356 | 1338 | /* use sample&hold signal? */ |
3063d6de MD |
1339 | if (cmd->convert_src == TRIG_NOW) { |
1340 | devpriv->usessh = 1; | |
0f04c356 | 1341 | } /* yes */ |
3063d6de MD |
1342 | else { |
1343 | devpriv->usessh = 0; | |
0f04c356 | 1344 | } /* no */ |
3063d6de MD |
1345 | |
1346 | DPRINTK("1 neverending=%d scans=%u usessh=%d ai_startstop=0x%2x\n", | |
1347 | devpriv->ai_neverending, devpriv->ai_scans, devpriv->usessh, | |
1348 | devpriv->ai12_startstop); | |
1349 | ||
0f04c356 | 1350 | /* use additional sample at end of every scan to satisty DMA 32 bit transfer? */ |
3063d6de MD |
1351 | devpriv->ai_add_front = 0; |
1352 | devpriv->ai_add_back = 0; | |
1353 | devpriv->useeoshandle = 0; | |
1354 | if (devpriv->master) { | |
1355 | devpriv->usedma = 1; | |
1356 | if ((cmd->flags & TRIG_WAKE_EOS) && | |
1357 | (devpriv->ai_n_scanlen == 1)) { | |
1358 | if (cmd->convert_src == TRIG_NOW) { | |
1359 | devpriv->ai_add_back = 1; | |
1360 | } | |
1361 | if (cmd->convert_src == TRIG_TIMER) { | |
0f04c356 | 1362 | devpriv->usedma = 0; /* use INT transfer if scanlist have only one channel */ |
3063d6de MD |
1363 | } |
1364 | } | |
1365 | if ((cmd->flags & TRIG_WAKE_EOS) && | |
1366 | (devpriv->ai_n_scanlen & 1) && | |
1367 | (devpriv->ai_n_scanlen > 1)) { | |
1368 | if (cmd->scan_begin_src == TRIG_FOLLOW) { | |
0f04c356 BP |
1369 | /* vpriv->useeoshandle=1; // change DMA transfer block to fit EOS on every second call */ |
1370 | devpriv->usedma = 0; /* XXX maybe can be corrected to use 16 bit DMA */ | |
1371 | } else { /* well, we must insert one sample to end of EOS to meet 32 bit transfer */ | |
3063d6de MD |
1372 | devpriv->ai_add_back = 1; |
1373 | } | |
1374 | } | |
0f04c356 | 1375 | } else { /* interrupt transfer don't need any correction */ |
3063d6de MD |
1376 | devpriv->usedma = 0; |
1377 | } | |
1378 | ||
0f04c356 | 1379 | /* we need software S&H signal? It add two samples before every scan as minimum */ |
3063d6de MD |
1380 | if (devpriv->usessh && devpriv->softsshdelay) { |
1381 | devpriv->ai_add_front = 2; | |
0f04c356 | 1382 | if ((devpriv->usedma == 1) && (devpriv->ai_add_back == 1)) { /* move it to front */ |
3063d6de MD |
1383 | devpriv->ai_add_front++; |
1384 | devpriv->ai_add_back = 0; | |
1385 | } | |
1386 | if (cmd->convert_arg < this_board->ai_ns_min) | |
1387 | cmd->convert_arg = this_board->ai_ns_min; | |
1388 | addchans = devpriv->softsshdelay / cmd->convert_arg; | |
1389 | if (devpriv->softsshdelay % cmd->convert_arg) | |
1390 | addchans++; | |
0f04c356 | 1391 | if (addchans > (devpriv->ai_add_front - 1)) { /* uff, still short :-( */ |
3063d6de MD |
1392 | devpriv->ai_add_front = addchans + 1; |
1393 | if (devpriv->usedma == 1) | |
1394 | if ((devpriv->ai_add_front + | |
1395 | devpriv->ai_n_chan + | |
1396 | devpriv->ai_add_back) & 1) | |
0f04c356 | 1397 | devpriv->ai_add_front++; /* round up to 32 bit */ |
3063d6de | 1398 | } |
0f04c356 | 1399 | } /* well, we now know what must be all added */ |
3063d6de | 1400 | |
0f04c356 | 1401 | devpriv->ai_n_realscanlen = /* what we must take from card in real to have ai_n_scanlen on output? */ |
3063d6de MD |
1402 | (devpriv->ai_add_front + devpriv->ai_n_chan + |
1403 | devpriv->ai_add_back) * (devpriv->ai_n_scanlen / | |
1404 | devpriv->ai_n_chan); | |
1405 | ||
1406 | DPRINTK("2 usedma=%d realscan=%d af=%u n_chan=%d ab=%d n_scanlen=%d\n", | |
1407 | devpriv->usedma, | |
1408 | devpriv->ai_n_realscanlen, devpriv->ai_add_front, | |
1409 | devpriv->ai_n_chan, devpriv->ai_add_back, | |
1410 | devpriv->ai_n_scanlen); | |
1411 | ||
0f04c356 | 1412 | /* check and setup channel list */ |
3063d6de MD |
1413 | if (!check_channel_list(dev, s, devpriv->ai_n_chan, |
1414 | devpriv->ai_chanlist, devpriv->ai_add_front, | |
1415 | devpriv->ai_add_back)) | |
1416 | return -EINVAL; | |
1417 | if (!setup_channel_list(dev, s, devpriv->ai_n_chan, | |
1418 | devpriv->ai_chanlist, 0, devpriv->ai_add_front, | |
1419 | devpriv->ai_add_back, devpriv->usedma, | |
1420 | devpriv->useeoshandle)) | |
1421 | return -EINVAL; | |
1422 | ||
0f04c356 BP |
1423 | /* compute timers settings */ |
1424 | /* simplest way, fr=4Mhz/(tim1*tim2), channel manipulation without timers effect */ | |
1425 | if (((cmd->scan_begin_src == TRIG_FOLLOW) || (cmd->scan_begin_src == TRIG_EXT) || (cmd->scan_begin_src == TRIG_INT)) && (cmd->convert_src == TRIG_TIMER)) { /* both timer is used for one time */ | |
3063d6de MD |
1426 | if (cmd->scan_begin_src == TRIG_EXT) { |
1427 | devpriv->ai_do = 4; | |
1428 | } else { | |
1429 | devpriv->ai_do = 1; | |
1430 | } | |
1431 | pci9118_calc_divisors(devpriv->ai_do, dev, s, | |
1432 | &cmd->scan_begin_arg, &cmd->convert_arg, | |
1433 | devpriv->ai_flags, devpriv->ai_n_realscanlen, | |
1434 | &devpriv->ai_divisor1, &devpriv->ai_divisor2, | |
1435 | devpriv->usessh, devpriv->ai_add_front); | |
1436 | devpriv->ai_timer2 = cmd->convert_arg; | |
1437 | } | |
1438 | ||
0f04c356 | 1439 | if ((cmd->scan_begin_src == TRIG_TIMER) && ((cmd->convert_src == TRIG_TIMER) || (cmd->convert_src == TRIG_NOW))) { /* double timed action */ |
3063d6de MD |
1440 | if (!devpriv->usedma) { |
1441 | comedi_error(dev, | |
1442 | "cmd->scan_begin_src=TRIG_TIMER works only with bus mastering!"); | |
1443 | return -EIO; | |
1444 | } | |
1445 | ||
1446 | devpriv->ai_do = 2; | |
1447 | pci9118_calc_divisors(devpriv->ai_do, dev, s, | |
1448 | &cmd->scan_begin_arg, &cmd->convert_arg, | |
1449 | devpriv->ai_flags, devpriv->ai_n_realscanlen, | |
1450 | &devpriv->ai_divisor1, &devpriv->ai_divisor2, | |
1451 | devpriv->usessh, devpriv->ai_add_front); | |
1452 | devpriv->ai_timer1 = cmd->scan_begin_arg; | |
1453 | devpriv->ai_timer2 = cmd->convert_arg; | |
1454 | } | |
1455 | ||
1456 | if ((cmd->scan_begin_src == TRIG_FOLLOW) | |
1457 | && (cmd->convert_src == TRIG_EXT)) { | |
1458 | devpriv->ai_do = 3; | |
1459 | } | |
1460 | ||
0f04c356 | 1461 | start_pacer(dev, -1, 0, 0); /* stop pacer */ |
3063d6de | 1462 | |
0f04c356 | 1463 | devpriv->AdControlReg = 0; /* bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable DMA */ |
3063d6de | 1464 | outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); |
0f04c356 | 1465 | devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg; /* positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop */ |
3063d6de MD |
1466 | outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); |
1467 | comedi_udelay(1); | |
0f04c356 BP |
1468 | outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */ |
1469 | inl(dev->iobase + PCI9118_ADSTAT); /* flush A/D and INT status register */ | |
3063d6de MD |
1470 | inl(dev->iobase + PCI9118_INTSRC); |
1471 | ||
1472 | devpriv->ai_act_scan = 0; | |
1473 | devpriv->ai_act_dmapos = 0; | |
1474 | s->async->cur_chan = 0; | |
1475 | devpriv->ai_buf_ptr = 0; | |
1476 | ||
1477 | if (devpriv->usedma) { | |
1478 | ret = pci9118_ai_docmd_dma(dev, s); | |
1479 | } else { | |
1480 | ret = pci9118_ai_docmd_sampl(dev, s); | |
1481 | } | |
1482 | ||
1483 | DPRINTK("adl_pci9118 EDBG: END: pci9118_ai_cmd()\n"); | |
1484 | return ret; | |
1485 | } | |
1486 | ||
1487 | /* | |
1488 | ============================================================================== | |
1489 | */ | |
34c43922 | 1490 | static int check_channel_list(struct comedi_device * dev, struct comedi_subdevice * s, |
3063d6de MD |
1491 | int n_chan, unsigned int *chanlist, int frontadd, int backadd) |
1492 | { | |
1493 | unsigned int i, differencial = 0, bipolar = 0; | |
1494 | ||
1495 | /* correct channel and range number check itself comedi/range.c */ | |
1496 | if (n_chan < 1) { | |
1497 | comedi_error(dev, "range/channel list is empty!"); | |
1498 | return 0; | |
1499 | } | |
1500 | if ((frontadd + n_chan + backadd) > s->len_chanlist) { | |
1501 | rt_printk | |
1502 | ("comedi%d: range/channel list is too long for actual configuration (%d>%d)!", | |
1503 | dev->minor, n_chan, | |
1504 | s->len_chanlist - frontadd - backadd); | |
1505 | return 0; | |
1506 | } | |
1507 | ||
1508 | if (CR_AREF(chanlist[0]) == AREF_DIFF) | |
0f04c356 | 1509 | differencial = 1; /* all input must be diff */ |
3063d6de | 1510 | if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES) |
0f04c356 | 1511 | bipolar = 1; /* all input must be bipolar */ |
3063d6de | 1512 | if (n_chan > 1) |
0f04c356 | 1513 | for (i = 1; i < n_chan; i++) { /* check S.E/diff */ |
3063d6de MD |
1514 | if ((CR_AREF(chanlist[i]) == AREF_DIFF) != |
1515 | (differencial)) { | |
1516 | comedi_error(dev, | |
1517 | "Differencial and single ended inputs cann't be mixtured!"); | |
1518 | return 0; | |
1519 | } | |
1520 | if ((CR_RANGE(chanlist[i]) < PCI9118_BIPOLAR_RANGES) != | |
1521 | (bipolar)) { | |
1522 | comedi_error(dev, | |
1523 | "Bipolar and unipolar ranges cann't be mixtured!"); | |
1524 | return 0; | |
1525 | } | |
1526 | if ((!devpriv->usemux) & (differencial) & | |
1527 | (CR_CHAN(chanlist[i]) >= | |
1528 | this_board->n_aichand)) { | |
1529 | comedi_error(dev, | |
1530 | "If AREF_DIFF is used then is available only first 8 channels!"); | |
1531 | return 0; | |
1532 | } | |
1533 | } | |
1534 | ||
1535 | return 1; | |
1536 | } | |
1537 | ||
1538 | /* | |
1539 | ============================================================================== | |
1540 | */ | |
34c43922 | 1541 | static int setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s, |
3063d6de MD |
1542 | int n_chan, unsigned int *chanlist, int rot, int frontadd, int backadd, |
1543 | int usedma, char useeos) | |
1544 | { | |
1545 | unsigned int i, differencial = 0, bipolar = 0; | |
1546 | unsigned int scanquad, gain, ssh = 0x00; | |
1547 | ||
1548 | DPRINTK("adl_pci9118 EDBG: BGN: setup_channel_list(%d,.,%d,.,%d,%d,%d,%d)\n", dev->minor, n_chan, rot, frontadd, backadd, usedma); | |
1549 | ||
1550 | if (usedma == 1) { | |
1551 | rot = 8; | |
1552 | usedma = 0; | |
1553 | } | |
1554 | ||
1555 | if (CR_AREF(chanlist[0]) == AREF_DIFF) | |
0f04c356 | 1556 | differencial = 1; /* all input must be diff */ |
3063d6de | 1557 | if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES) |
0f04c356 | 1558 | bipolar = 1; /* all input must be bipolar */ |
3063d6de | 1559 | |
0f04c356 | 1560 | /* All is ok, so we can setup channel/range list */ |
3063d6de MD |
1561 | |
1562 | if (!bipolar) { | |
0f04c356 | 1563 | devpriv->AdControlReg |= AdControl_UniP; /* set unibipolar */ |
3063d6de | 1564 | } else { |
0f04c356 | 1565 | devpriv->AdControlReg &= ((~AdControl_UniP) & 0xff); /* enable bipolar */ |
3063d6de MD |
1566 | } |
1567 | ||
1568 | if (differencial) { | |
0f04c356 | 1569 | devpriv->AdControlReg |= AdControl_Diff; /* enable diff inputs */ |
3063d6de | 1570 | } else { |
0f04c356 | 1571 | devpriv->AdControlReg &= ((~AdControl_Diff) & 0xff); /* set single ended inputs */ |
3063d6de MD |
1572 | } |
1573 | ||
0f04c356 | 1574 | outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); /* setup mode */ |
3063d6de | 1575 | |
0f04c356 | 1576 | outl(2, dev->iobase + PCI9118_SCANMOD); /* gods know why this sequence! */ |
3063d6de MD |
1577 | outl(0, dev->iobase + PCI9118_SCANMOD); |
1578 | outl(1, dev->iobase + PCI9118_SCANMOD); | |
1579 | ||
1580 | #ifdef PCI9118_PARANOIDCHECK | |
1581 | devpriv->chanlistlen = n_chan; | |
1582 | for (i = 0; i < (PCI9118_CHANLEN + 1); i++) | |
1583 | devpriv->chanlist[i] = 0x55aa; | |
1584 | #endif | |
1585 | ||
0f04c356 | 1586 | if (frontadd) { /* insert channels for S&H */ |
3063d6de MD |
1587 | ssh = devpriv->softsshsample; |
1588 | DPRINTK("FA: %04x: ", ssh); | |
0f04c356 BP |
1589 | for (i = 0; i < frontadd; i++) { /* store range list to card */ |
1590 | scanquad = CR_CHAN(chanlist[0]); /* get channel number; */ | |
1591 | gain = CR_RANGE(chanlist[0]); /* get gain number */ | |
3063d6de MD |
1592 | scanquad |= ((gain & 0x03) << 8); |
1593 | outl(scanquad | ssh, dev->iobase + PCI9118_GAIN); | |
1594 | DPRINTK("%02x ", scanquad | ssh); | |
1595 | ssh = devpriv->softsshhold; | |
1596 | } | |
1597 | DPRINTK("\n "); | |
1598 | } | |
1599 | ||
1600 | DPRINTK("SL: ", ssh); | |
0f04c356 BP |
1601 | for (i = 0; i < n_chan; i++) { /* store range list to card */ |
1602 | scanquad = CR_CHAN(chanlist[i]); /* get channel number; */ | |
3063d6de MD |
1603 | #ifdef PCI9118_PARANOIDCHECK |
1604 | devpriv->chanlist[i ^ usedma] = (scanquad & 0xf) << rot; | |
1605 | #endif | |
0f04c356 | 1606 | gain = CR_RANGE(chanlist[i]); /* get gain number */ |
3063d6de MD |
1607 | scanquad |= ((gain & 0x03) << 8); |
1608 | outl(scanquad | ssh, dev->iobase + PCI9118_GAIN); | |
1609 | DPRINTK("%02x ", scanquad | ssh); | |
1610 | } | |
1611 | DPRINTK("\n "); | |
1612 | ||
0f04c356 | 1613 | if (backadd) { /* insert channels for fit onto 32bit DMA */ |
3063d6de | 1614 | DPRINTK("BA: %04x: ", ssh); |
0f04c356 BP |
1615 | for (i = 0; i < backadd; i++) { /* store range list to card */ |
1616 | scanquad = CR_CHAN(chanlist[0]); /* get channel number; */ | |
1617 | gain = CR_RANGE(chanlist[0]); /* get gain number */ | |
3063d6de MD |
1618 | scanquad |= ((gain & 0x03) << 8); |
1619 | outl(scanquad | ssh, dev->iobase + PCI9118_GAIN); | |
1620 | DPRINTK("%02x ", scanquad | ssh); | |
1621 | } | |
1622 | DPRINTK("\n "); | |
1623 | } | |
1624 | #ifdef PCI9118_PARANOIDCHECK | |
0f04c356 | 1625 | devpriv->chanlist[n_chan ^ usedma] = devpriv->chanlist[0 ^ usedma]; /* for 32bit oerations */ |
3063d6de | 1626 | if (useeos) { |
0f04c356 | 1627 | for (i = 1; i < n_chan; i++) { /* store range list to card */ |
3063d6de MD |
1628 | devpriv->chanlist[(n_chan + i) ^ usedma] = |
1629 | (CR_CHAN(chanlist[i]) & 0xf) << rot; | |
1630 | } | |
0f04c356 | 1631 | devpriv->chanlist[(2 * n_chan) ^ usedma] = devpriv->chanlist[0 ^ usedma]; /* for 32bit oerations */ |
3063d6de MD |
1632 | useeos = 2; |
1633 | } else { | |
1634 | useeos = 1; | |
1635 | } | |
1636 | #ifdef PCI9118_EXTDEBUG | |
1637 | DPRINTK("CHL: "); | |
1638 | for (i = 0; i <= (useeos * n_chan); i++) { | |
1639 | DPRINTK("%04x ", devpriv->chanlist[i]); | |
1640 | } | |
1641 | DPRINTK("\n "); | |
1642 | #endif | |
1643 | #endif | |
0f04c356 BP |
1644 | outl(0, dev->iobase + PCI9118_SCANMOD); /* close scan queue */ |
1645 | /* comedi_udelay(100); important delay, or first sample will be cripled */ | |
3063d6de MD |
1646 | |
1647 | DPRINTK("adl_pci9118 EDBG: END: setup_channel_list()\n"); | |
0f04c356 | 1648 | return 1; /* we can serve this with scan logic */ |
3063d6de MD |
1649 | } |
1650 | ||
1651 | /* | |
1652 | ============================================================================== | |
1653 | calculate 8254 divisors if they are used for dual timing | |
1654 | */ | |
71b5f4f1 | 1655 | static void pci9118_calc_divisors(char mode, struct comedi_device * dev, |
34c43922 | 1656 | struct comedi_subdevice * s, unsigned int *tim1, unsigned int *tim2, |
3063d6de MD |
1657 | unsigned int flags, int chans, unsigned int *div1, unsigned int *div2, |
1658 | char usessh, unsigned int chnsshfront) | |
1659 | { | |
1660 | DPRINTK("adl_pci9118 EDBG: BGN: pci9118_calc_divisors(%d,%d,.,%u,%u,%u,%d,.,.,,%u,%u)\n", mode, dev->minor, *tim1, *tim2, flags, chans, usessh, chnsshfront); | |
1661 | switch (mode) { | |
1662 | case 1: | |
1663 | case 4: | |
1664 | if (*tim2 < this_board->ai_ns_min) | |
1665 | *tim2 = this_board->ai_ns_min; | |
1666 | i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, div1, div2, | |
1667 | tim2, flags & TRIG_ROUND_NEAREST); | |
1668 | DPRINTK("OSC base=%u div1=%u div2=%u timer1=%u\n", | |
1669 | devpriv->i8254_osc_base, *div1, *div2, *tim1); | |
1670 | break; | |
1671 | case 2: | |
1672 | if (*tim2 < this_board->ai_ns_min) | |
1673 | *tim2 = this_board->ai_ns_min; | |
1674 | DPRINTK("1 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, | |
1675 | *tim1, *tim2); | |
0f04c356 | 1676 | *div1 = *tim2 / devpriv->i8254_osc_base; /* convert timer (burst) */ |
3063d6de MD |
1677 | DPRINTK("2 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, |
1678 | *tim1, *tim2); | |
1679 | if (*div1 < this_board->ai_pacer_min) | |
1680 | *div1 = this_board->ai_pacer_min; | |
1681 | DPRINTK("3 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, | |
1682 | *tim1, *tim2); | |
0f04c356 | 1683 | *div2 = *tim1 / devpriv->i8254_osc_base; /* scan timer */ |
3063d6de MD |
1684 | DPRINTK("4 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, |
1685 | *tim1, *tim2); | |
0f04c356 | 1686 | *div2 = *div2 / *div1; /* major timer is c1*c2 */ |
3063d6de MD |
1687 | DPRINTK("5 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, |
1688 | *tim1, *tim2); | |
1689 | if (*div2 < chans) | |
1690 | *div2 = chans; | |
1691 | DPRINTK("6 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, | |
1692 | *tim1, *tim2); | |
1693 | ||
0f04c356 | 1694 | *tim2 = *div1 * devpriv->i8254_osc_base; /* real convert timer */ |
3063d6de | 1695 | |
0f04c356 | 1696 | if (usessh & (chnsshfront == 0)) /* use BSSH signal */ |
3063d6de MD |
1697 | if (*div2 < (chans + 2)) |
1698 | *div2 = chans + 2; | |
1699 | ||
1700 | DPRINTK("7 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, | |
1701 | *tim1, *tim2); | |
1702 | *tim1 = *div1 * *div2 * devpriv->i8254_osc_base; | |
1703 | DPRINTK("OSC base=%u div1=%u div2=%u timer1=%u timer2=%u\n", | |
1704 | devpriv->i8254_osc_base, *div1, *div2, *tim1, *tim2); | |
1705 | break; | |
1706 | } | |
1707 | DPRINTK("adl_pci9118 EDBG: END: pci9118_calc_divisors(%u,%u)\n", | |
1708 | *div1, *div2); | |
1709 | } | |
1710 | ||
1711 | /* | |
1712 | ============================================================================== | |
1713 | */ | |
71b5f4f1 | 1714 | static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1, |
3063d6de MD |
1715 | unsigned int divisor2) |
1716 | { | |
1717 | outl(0x74, dev->iobase + PCI9118_CNTCTRL); | |
1718 | outl(0xb4, dev->iobase + PCI9118_CNTCTRL); | |
0f04c356 | 1719 | /* outl(0x30, dev->iobase + PCI9118_CNTCTRL); */ |
3063d6de MD |
1720 | comedi_udelay(1); |
1721 | ||
1722 | if ((mode == 1) || (mode == 2) || (mode == 4)) { | |
1723 | outl(divisor2 & 0xff, dev->iobase + PCI9118_CNT2); | |
1724 | outl((divisor2 >> 8) & 0xff, dev->iobase + PCI9118_CNT2); | |
1725 | outl(divisor1 & 0xff, dev->iobase + PCI9118_CNT1); | |
1726 | outl((divisor1 >> 8) & 0xff, dev->iobase + PCI9118_CNT1); | |
1727 | } | |
1728 | } | |
1729 | ||
1730 | /* | |
1731 | ============================================================================== | |
1732 | */ | |
71b5f4f1 | 1733 | static int pci9118_exttrg_add(struct comedi_device * dev, unsigned char source) |
3063d6de MD |
1734 | { |
1735 | if (source > 3) | |
0f04c356 | 1736 | return -1; /* incorrect source */ |
3063d6de MD |
1737 | devpriv->exttrg_users |= (1 << source); |
1738 | devpriv->IntControlReg |= Int_DTrg; | |
1739 | outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); | |
0f04c356 | 1740 | outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* allow INT in AMCC */ |
3063d6de MD |
1741 | return 0; |
1742 | } | |
1743 | ||
1744 | /* | |
1745 | ============================================================================== | |
1746 | */ | |
71b5f4f1 | 1747 | static int pci9118_exttrg_del(struct comedi_device * dev, unsigned char source) |
3063d6de MD |
1748 | { |
1749 | if (source > 3) | |
0f04c356 | 1750 | return -1; /* incorrect source */ |
3063d6de | 1751 | devpriv->exttrg_users &= ~(1 << source); |
0f04c356 | 1752 | if (!devpriv->exttrg_users) { /* shutdown ext trg intterrupts */ |
3063d6de | 1753 | devpriv->IntControlReg &= ~Int_DTrg; |
0f04c356 BP |
1754 | if (!devpriv->IntControlReg) /* all IRQ disabled */ |
1755 | outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) & (~0x00001f00), devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* disable int in AMCC */ | |
3063d6de MD |
1756 | outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); |
1757 | } | |
1758 | return 0; | |
1759 | } | |
1760 | ||
1761 | /* | |
1762 | ============================================================================== | |
1763 | */ | |
34c43922 | 1764 | static int pci9118_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s) |
3063d6de MD |
1765 | { |
1766 | if (devpriv->usedma) | |
0f04c356 | 1767 | outl(inl(devpriv->iobase_a + AMCC_OP_REG_MCSR) & (~EN_A2P_TRANSFERS), devpriv->iobase_a + AMCC_OP_REG_MCSR); /* stop DMA */ |
3063d6de | 1768 | pci9118_exttrg_del(dev, EXTTRG_AI); |
0f04c356 | 1769 | start_pacer(dev, 0, 0, 0); /* stop 8254 counters */ |
3063d6de | 1770 | devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg; |
0f04c356 | 1771 | outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); /* positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop */ |
3063d6de | 1772 | devpriv->AdControlReg = 0x00; |
0f04c356 | 1773 | outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); /* bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA */ |
3063d6de MD |
1774 | outl(0, dev->iobase + PCI9118_BURST); |
1775 | outl(1, dev->iobase + PCI9118_SCANMOD); | |
0f04c356 BP |
1776 | outl(2, dev->iobase + PCI9118_SCANMOD); /* reset scan queue */ |
1777 | outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */ | |
3063d6de MD |
1778 | |
1779 | devpriv->ai_do = 0; | |
1780 | devpriv->usedma = 0; | |
1781 | ||
1782 | devpriv->ai_act_scan = 0; | |
1783 | devpriv->ai_act_dmapos = 0; | |
1784 | s->async->cur_chan = 0; | |
1785 | s->async->inttrig = NULL; | |
1786 | devpriv->ai_buf_ptr = 0; | |
1787 | devpriv->ai_neverending = 0; | |
1788 | devpriv->dma_actbuf = 0; | |
1789 | ||
1790 | if (!devpriv->IntControlReg) | |
0f04c356 | 1791 | outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00, devpriv->iobase_a + AMCC_OP_REG_INTCSR); /* allow INT in AMCC */ |
3063d6de MD |
1792 | |
1793 | return 0; | |
1794 | } | |
1795 | ||
1796 | /* | |
1797 | ============================================================================== | |
1798 | */ | |
71b5f4f1 | 1799 | static int pci9118_reset(struct comedi_device * dev) |
3063d6de MD |
1800 | { |
1801 | devpriv->IntControlReg = 0; | |
1802 | devpriv->exttrg_users = 0; | |
1803 | inl(dev->iobase + PCI9118_INTCTRL); | |
0f04c356 | 1804 | outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); /* disable interrupts source */ |
3063d6de | 1805 | outl(0x30, dev->iobase + PCI9118_CNTCTRL); |
0f04c356 BP |
1806 | /* outl(0xb4, dev->iobase + PCI9118_CNTCTRL); */ |
1807 | start_pacer(dev, 0, 0, 0); /* stop 8254 counters */ | |
3063d6de | 1808 | devpriv->AdControlReg = 0; |
0f04c356 | 1809 | outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); /* bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA */ |
3063d6de MD |
1810 | outl(0, dev->iobase + PCI9118_BURST); |
1811 | outl(1, dev->iobase + PCI9118_SCANMOD); | |
0f04c356 | 1812 | outl(2, dev->iobase + PCI9118_SCANMOD); /* reset scan queue */ |
3063d6de | 1813 | devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg; |
0f04c356 | 1814 | outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); /* positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop */ |
3063d6de MD |
1815 | |
1816 | devpriv->ao_data[0] = 2047; | |
1817 | devpriv->ao_data[1] = 2047; | |
0f04c356 | 1818 | outl(devpriv->ao_data[0], dev->iobase + PCI9118_DA1); /* reset A/D outs to 0V */ |
3063d6de | 1819 | outl(devpriv->ao_data[1], dev->iobase + PCI9118_DA2); |
0f04c356 | 1820 | outl(0, dev->iobase + PCI9118_DO); /* reset digi outs to L */ |
3063d6de MD |
1821 | comedi_udelay(10); |
1822 | inl(dev->iobase + PCI9118_AD_DATA); | |
0f04c356 BP |
1823 | outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */ |
1824 | outl(0, dev->iobase + PCI9118_INTSRC); /* remove INT requests */ | |
1825 | inl(dev->iobase + PCI9118_ADSTAT); /* flush A/D status register */ | |
1826 | inl(dev->iobase + PCI9118_INTSRC); /* flush INT requests */ | |
3063d6de | 1827 | devpriv->AdControlReg = 0; |
0f04c356 | 1828 | outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); /* bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA */ |
3063d6de MD |
1829 | |
1830 | devpriv->cnt0_users = 0; | |
1831 | devpriv->exttrg_users = 0; | |
1832 | ||
1833 | return 0; | |
1834 | } | |
1835 | ||
1836 | /* | |
1837 | ============================================================================== | |
1838 | */ | |
0707bb04 | 1839 | static int pci9118_attach(struct comedi_device * dev, struct comedi_devconfig * it) |
3063d6de | 1840 | { |
34c43922 | 1841 | struct comedi_subdevice *s; |
3063d6de MD |
1842 | int ret, pages, i; |
1843 | unsigned short master; | |
1844 | unsigned int irq; | |
1845 | unsigned long iobase_a, iobase_9; | |
1846 | struct pci_dev *pcidev; | |
1847 | int opt_bus, opt_slot; | |
1848 | const char *errstr; | |
1849 | unsigned char pci_bus, pci_slot, pci_func; | |
1850 | u16 u16w; | |
1851 | ||
1852 | rt_printk("comedi%d: adl_pci9118: board=%s", dev->minor, | |
1853 | this_board->name); | |
1854 | ||
1855 | opt_bus = it->options[0]; | |
1856 | opt_slot = it->options[1]; | |
1857 | if (it->options[3] & 1) { | |
0f04c356 | 1858 | master = 0; /* user don't want use bus master */ |
3063d6de MD |
1859 | } else { |
1860 | master = 1; | |
1861 | } | |
1862 | ||
5b5fc21b | 1863 | if ((ret = alloc_private(dev, sizeof(struct pci9118_private))) < 0) { |
3063d6de MD |
1864 | rt_printk(" - Allocation failed!\n"); |
1865 | return -ENOMEM; | |
1866 | } | |
1867 | ||
1868 | /* Look for matching PCI device */ | |
1869 | errstr = "not found!"; | |
1870 | pcidev = NULL; | |
1871 | while (NULL != (pcidev = pci_get_device(PCI_VENDOR_ID_AMCC, | |
1872 | this_board->device_id, pcidev))) { | |
1873 | /* Found matching vendor/device. */ | |
1874 | if (opt_bus || opt_slot) { | |
1875 | /* Check bus/slot. */ | |
1876 | if (opt_bus != pcidev->bus->number | |
1877 | || opt_slot != PCI_SLOT(pcidev->devfn)) | |
1878 | continue; /* no match */ | |
1879 | } | |
1880 | /* | |
1881 | * Look for device that isn't in use. | |
1882 | * Enable PCI device and request regions. | |
1883 | */ | |
1884 | if (comedi_pci_enable(pcidev, "adl_pci9118")) { | |
1885 | errstr = "failed to enable PCI device and request regions!"; | |
1886 | continue; | |
1887 | } | |
1888 | break; | |
1889 | } | |
1890 | ||
1891 | if (!pcidev) { | |
1892 | if (opt_bus || opt_slot) { | |
1893 | rt_printk(" - Card at b:s %d:%d %s\n", | |
1894 | opt_bus, opt_slot, errstr); | |
1895 | } else { | |
1896 | rt_printk(" - Card %s\n", errstr); | |
1897 | } | |
1898 | return -EIO; | |
1899 | } | |
1900 | ||
1901 | if (master) { | |
1902 | pci_set_master(pcidev); | |
1903 | } | |
1904 | ||
1905 | pci_bus = pcidev->bus->number; | |
1906 | pci_slot = PCI_SLOT(pcidev->devfn); | |
1907 | pci_func = PCI_FUNC(pcidev->devfn); | |
1908 | irq = pcidev->irq; | |
1909 | iobase_a = pci_resource_start(pcidev, 0); | |
1910 | iobase_9 = pci_resource_start(pcidev, 2); | |
1911 | ||
1912 | rt_printk(", b:s:f=%d:%d:%d, io=0x%4lx, 0x%4lx", pci_bus, pci_slot, | |
1913 | pci_func, iobase_9, iobase_a); | |
1914 | ||
1915 | dev->iobase = iobase_9; | |
1916 | dev->board_name = this_board->name; | |
1917 | ||
1918 | devpriv->pcidev = pcidev; | |
1919 | devpriv->iobase_a = iobase_a; | |
1920 | ||
1921 | pci9118_reset(dev); | |
1922 | ||
1923 | if (it->options[3] & 2) | |
0f04c356 | 1924 | irq = 0; /* user don't want use IRQ */ |
3063d6de MD |
1925 | if (irq > 0) { |
1926 | if (comedi_request_irq(irq, interrupt_pci9118, IRQF_SHARED, | |
1927 | "ADLink PCI-9118", dev)) { | |
1928 | rt_printk(", unable to allocate IRQ %d, DISABLING IT", | |
1929 | irq); | |
1930 | irq = 0; /* Can't use IRQ */ | |
1931 | } else { | |
1932 | rt_printk(", irq=%u", irq); | |
1933 | } | |
1934 | } else { | |
1935 | rt_printk(", IRQ disabled"); | |
1936 | } | |
1937 | ||
1938 | dev->irq = irq; | |
1939 | ||
0f04c356 | 1940 | if (master) { /* alloc DMA buffers */ |
3063d6de MD |
1941 | devpriv->dma_doublebuf = 0; |
1942 | for (i = 0; i < 2; i++) { | |
1943 | for (pages = 4; pages >= 0; pages--) | |
790c5541 | 1944 | if ((devpriv->dmabuf_virt[i] = (short *) |
3063d6de MD |
1945 | __get_free_pages(GFP_KERNEL, |
1946 | pages))) | |
1947 | break; | |
1948 | if (devpriv->dmabuf_virt[i]) { | |
1949 | devpriv->dmabuf_pages[i] = pages; | |
1950 | devpriv->dmabuf_size[i] = PAGE_SIZE * pages; | |
1951 | devpriv->dmabuf_samples[i] = | |
1952 | devpriv->dmabuf_size[i] >> 1; | |
1953 | devpriv->dmabuf_hw[i] = | |
1954 | virt_to_bus((void *)devpriv-> | |
1955 | dmabuf_virt[i]); | |
1956 | } | |
1957 | } | |
1958 | if (!devpriv->dmabuf_virt[0]) { | |
1959 | rt_printk(", Can't allocate DMA buffer, DMA disabled!"); | |
1960 | master = 0; | |
1961 | } | |
1962 | ||
1963 | if (devpriv->dmabuf_virt[1]) | |
1964 | devpriv->dma_doublebuf = 1; | |
1965 | ||
1966 | } | |
1967 | ||
1968 | if ((devpriv->master = master)) { | |
1969 | rt_printk(", bus master"); | |
1970 | } else { | |
1971 | rt_printk(", no bus master"); | |
1972 | } | |
1973 | ||
1974 | devpriv->usemux = 0; | |
1975 | if (it->options[2] > 0) { | |
1976 | devpriv->usemux = it->options[2]; | |
1977 | if (devpriv->usemux > 256) | |
0f04c356 | 1978 | devpriv->usemux = 256; /* max 256 channels! */ |
3063d6de MD |
1979 | if (it->options[4] > 0) |
1980 | if (devpriv->usemux > 128) { | |
0f04c356 | 1981 | devpriv->usemux = 128; /* max 128 channels with softare S&H! */ |
3063d6de MD |
1982 | } |
1983 | rt_printk(", ext. mux %d channels", devpriv->usemux); | |
1984 | } | |
1985 | ||
1986 | devpriv->softsshdelay = it->options[4]; | |
0f04c356 | 1987 | if (devpriv->softsshdelay < 0) { /* select sample&hold signal polarity */ |
3063d6de MD |
1988 | devpriv->softsshdelay = -devpriv->softsshdelay; |
1989 | devpriv->softsshsample = 0x80; | |
1990 | devpriv->softsshhold = 0x00; | |
1991 | } else { | |
1992 | devpriv->softsshsample = 0x00; | |
1993 | devpriv->softsshhold = 0x80; | |
1994 | } | |
1995 | ||
1996 | rt_printk(".\n"); | |
1997 | ||
1998 | pci_read_config_word(devpriv->pcidev, PCI_COMMAND, &u16w); | |
0f04c356 | 1999 | pci_write_config_word(devpriv->pcidev, PCI_COMMAND, u16w | 64); /* Enable parity check for parity error */ |
3063d6de MD |
2000 | |
2001 | if ((ret = alloc_subdevices(dev, 4)) < 0) | |
2002 | return ret; | |
2003 | ||
2004 | s = dev->subdevices + 0; | |
2005 | dev->read_subdev = s; | |
2006 | s->type = COMEDI_SUBD_AI; | |
2007 | s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF; | |
2008 | if (devpriv->usemux) { | |
2009 | s->n_chan = devpriv->usemux; | |
2010 | } else { | |
2011 | s->n_chan = this_board->n_aichan; | |
2012 | } | |
2013 | s->maxdata = this_board->ai_maxdata; | |
2014 | s->len_chanlist = this_board->n_aichanlist; | |
2015 | s->range_table = this_board->rangelist_ai; | |
2016 | s->cancel = pci9118_ai_cancel; | |
2017 | s->insn_read = pci9118_insn_read_ai; | |
2018 | if (dev->irq) { | |
2019 | s->subdev_flags |= SDF_CMD_READ; | |
2020 | s->do_cmdtest = pci9118_ai_cmdtest; | |
2021 | s->do_cmd = pci9118_ai_cmd; | |
2022 | s->munge = pci9118_ai_munge; | |
2023 | } | |
2024 | ||
2025 | s = dev->subdevices + 1; | |
2026 | s->type = COMEDI_SUBD_AO; | |
2027 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; | |
2028 | s->n_chan = this_board->n_aochan; | |
2029 | s->maxdata = this_board->ao_maxdata; | |
2030 | s->len_chanlist = this_board->n_aochan; | |
2031 | s->range_table = this_board->rangelist_ao; | |
2032 | s->insn_write = pci9118_insn_write_ao; | |
2033 | s->insn_read = pci9118_insn_read_ao; | |
2034 | ||
2035 | s = dev->subdevices + 2; | |
2036 | s->type = COMEDI_SUBD_DI; | |
2037 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON; | |
2038 | s->n_chan = 4; | |
2039 | s->maxdata = 1; | |
2040 | s->len_chanlist = 4; | |
2041 | s->range_table = &range_digital; | |
2042 | s->io_bits = 0; /* all bits input */ | |
2043 | s->insn_bits = pci9118_insn_bits_di; | |
2044 | ||
2045 | s = dev->subdevices + 3; | |
2046 | s->type = COMEDI_SUBD_DO; | |
2047 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; | |
2048 | s->n_chan = 4; | |
2049 | s->maxdata = 1; | |
2050 | s->len_chanlist = 4; | |
2051 | s->range_table = &range_digital; | |
2052 | s->io_bits = 0xf; /* all bits output */ | |
2053 | s->insn_bits = pci9118_insn_bits_do; | |
2054 | ||
2055 | devpriv->valid = 1; | |
0f04c356 BP |
2056 | devpriv->i8254_osc_base = 250; /* 250ns=4MHz */ |
2057 | devpriv->ai_maskharderr = 0x10a; /* default measure crash condition */ | |
2058 | if (it->options[5]) /* disable some requested */ | |
3063d6de MD |
2059 | devpriv->ai_maskharderr &= ~it->options[5]; |
2060 | ||
2061 | switch (this_board->ai_maxdata) { | |
2062 | case 0xffff: | |
2063 | devpriv->ai16bits = 1; | |
2064 | break; | |
2065 | default: | |
2066 | devpriv->ai16bits = 0; | |
2067 | break; | |
2068 | } | |
2069 | return 0; | |
2070 | } | |
2071 | ||
2072 | /* | |
2073 | ============================================================================== | |
2074 | */ | |
71b5f4f1 | 2075 | static int pci9118_detach(struct comedi_device * dev) |
3063d6de MD |
2076 | { |
2077 | if (dev->private) { | |
2078 | if (devpriv->valid) | |
2079 | pci9118_reset(dev); | |
2080 | if (dev->irq) | |
2081 | comedi_free_irq(dev->irq, dev); | |
2082 | if (devpriv->pcidev) { | |
2083 | if (dev->iobase) { | |
2084 | comedi_pci_disable(devpriv->pcidev); | |
2085 | } | |
2086 | pci_dev_put(devpriv->pcidev); | |
2087 | } | |
2088 | if (devpriv->dmabuf_virt[0]) | |
2089 | free_pages((unsigned long)devpriv->dmabuf_virt[0], | |
2090 | devpriv->dmabuf_pages[0]); | |
2091 | if (devpriv->dmabuf_virt[1]) | |
2092 | free_pages((unsigned long)devpriv->dmabuf_virt[1], | |
2093 | devpriv->dmabuf_pages[1]); | |
2094 | } | |
2095 | ||
2096 | return 0; | |
2097 | } | |
2098 | ||
2099 | /* | |
2100 | ============================================================================== | |
2101 | */ |