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 | * | |
c8ea69c4 | 10 | */ |
3063d6de | 11 | |
c8ea69c4 SA |
12 | /* |
13 | * Driver: adl_pci9118 | |
14 | * Description: Adlink PCI-9118DG, PCI-9118HG, PCI-9118HR | |
15 | * Author: Michal Dobes <dobes@tesnet.cz> | |
16 | * Devices: [ADLink] PCI-9118DG (pci9118dg), PCI-9118HG (pci9118hg), | |
17 | * PCI-9118HR (pci9118hr) | |
18 | * Status: works | |
19 | * | |
20 | * This driver supports AI, AO, DI and DO subdevices. | |
21 | * AI subdevice supports cmd and insn interface, | |
22 | * other subdevices support only insn interface. | |
23 | * For AI: | |
24 | * - If cmd->scan_begin_src=TRIG_EXT then trigger input is TGIN (pin 46). | |
25 | * - If cmd->convert_src=TRIG_EXT then trigger input is EXTTRG (pin 44). | |
26 | * - If cmd->start_src/stop_src=TRIG_EXT then trigger input is TGIN (pin 46). | |
27 | * - It is not necessary to have cmd.scan_end_arg=cmd.chanlist_len but | |
28 | * cmd.scan_end_arg modulo cmd.chanlist_len must by 0. | |
29 | * - If return value of cmdtest is 5 then you've bad channel list | |
30 | * (it isn't possible mixture S.E. and DIFF inputs or bipolar and unipolar | |
31 | * ranges). | |
32 | * | |
33 | * There are some hardware limitations: | |
34 | * a) You cann't use mixture of unipolar/bipoar ranges or differencial/single | |
35 | * ended inputs. | |
36 | * b) DMA transfers must have the length aligned to two samples (32 bit), | |
37 | * so there is some problems if cmd->chanlist_len is odd. This driver tries | |
38 | * bypass this with adding one sample to the end of the every scan and discard | |
c25dec57 IA |
39 | * it on output but this can't be used if cmd->scan_begin_src=TRIG_FOLLOW |
40 | * and is used flag CMDF_WAKE_EOS, then driver switch to interrupt driven mode | |
c8ea69c4 SA |
41 | * with interrupt after every sample. |
42 | * c) If isn't used DMA then you can use only mode where | |
43 | * cmd->scan_begin_src=TRIG_FOLLOW. | |
44 | * | |
45 | * Configuration options: | |
46 | * [0] - PCI bus of device (optional) | |
47 | * [1] - PCI slot of device (optional) | |
48 | * If bus/slot is not specified, then first available PCI | |
49 | * card will be used. | |
50 | * [2] - 0= standard 8 DIFF/16 SE channels configuration | |
51 | * n = external multiplexer connected, 1 <= n <= 256 | |
cb578327 | 52 | * [3] - ignored |
c8ea69c4 SA |
53 | * [4] - sample&hold signal - card can generate signal for external S&H board |
54 | * 0 = use SSHO(pin 45) signal is generated in onboard hardware S&H logic | |
55 | * 0 != use ADCHN7(pin 23) signal is generated from driver, number say how | |
56 | * long delay is requested in ns and sign polarity of the hold | |
57 | * (in this case external multiplexor can serve only 128 channels) | |
9e531485 | 58 | * [5] - ignored |
c8ea69c4 | 59 | */ |
15358a7f IA |
60 | |
61 | /* | |
62 | * FIXME | |
63 | * | |
64 | * All the supported boards have the same PCI vendor and device IDs, so | |
65 | * auto-attachment of PCI devices will always find the first board type. | |
66 | * | |
67 | * Perhaps the boards have different subdevice IDs that we could use to | |
68 | * distinguish them? | |
69 | * | |
70 | * Need some device attributes so the board type can be corrected after | |
71 | * attachment if necessary, and possibly to set other options supported by | |
72 | * manual attachment. | |
73 | */ | |
3063d6de | 74 | |
ce157f80 | 75 | #include <linux/module.h> |
3063d6de | 76 | #include <linux/delay.h> |
5a0e3ad6 | 77 | #include <linux/gfp.h> |
70265d24 | 78 | #include <linux/interrupt.h> |
845d131e | 79 | #include <linux/io.h> |
3063d6de | 80 | |
b2ad146a | 81 | #include "../comedi_pci.h" |
33782dd5 | 82 | |
3063d6de | 83 | #include "amcc_s5933.h" |
a9da9d20 | 84 | #include "comedi_8254.h" |
3063d6de | 85 | |
6dc35953 HS |
86 | /* |
87 | * PCI BAR2 Register map (dev->iobase) | |
88 | */ | |
a9da9d20 | 89 | #define PCI9118_TIMER_BASE 0x00 |
7cc1110d | 90 | #define PCI9118_AI_FIFO_REG 0x10 |
c6908517 | 91 | #define PCI9118_AO_REG(x) (0x10 + ((x) * 4)) |
602c1729 | 92 | #define PCI9118_AI_STATUS_REG 0x18 |
7db6ed68 HS |
93 | #define PCI9118_AI_STATUS_NFULL BIT(8) /* 0=FIFO full (fatal) */ |
94 | #define PCI9118_AI_STATUS_NHFULL BIT(7) /* 0=FIFO half full */ | |
95 | #define PCI9118_AI_STATUS_NEPTY BIT(6) /* 0=FIFO empty */ | |
96 | #define PCI9118_AI_STATUS_ACMP BIT(5) /* 1=about trigger complete */ | |
97 | #define PCI9118_AI_STATUS_DTH BIT(4) /* 1=ext. digital trigger */ | |
98 | #define PCI9118_AI_STATUS_BOVER BIT(3) /* 1=burst overrun (fatal) */ | |
99 | #define PCI9118_AI_STATUS_ADOS BIT(2) /* 1=A/D over speed (warn) */ | |
100 | #define PCI9118_AI_STATUS_ADOR BIT(1) /* 1=A/D overrun (fatal) */ | |
101 | #define PCI9118_AI_STATUS_ADRDY BIT(0) /* 1=A/D ready */ | |
a2a1fc7a | 102 | #define PCI9118_AI_CTRL_REG 0x18 |
7db6ed68 HS |
103 | #define PCI9118_AI_CTRL_UNIP BIT(7) /* 1=unipolar */ |
104 | #define PCI9118_AI_CTRL_DIFF BIT(6) /* 1=differential inputs */ | |
105 | #define PCI9118_AI_CTRL_SOFTG BIT(5) /* 1=8254 software gate */ | |
106 | #define PCI9118_AI_CTRL_EXTG BIT(4) /* 1=8254 TGIN(pin 46) gate */ | |
107 | #define PCI9118_AI_CTRL_EXTM BIT(3) /* 1=ext. trigger (pin 44) */ | |
108 | #define PCI9118_AI_CTRL_TMRTR BIT(2) /* 1=8254 is trigger source */ | |
109 | #define PCI9118_AI_CTRL_INT BIT(1) /* 1=enable interrupt */ | |
110 | #define PCI9118_AI_CTRL_DMA BIT(0) /* 1=enable DMA */ | |
c7f499bd | 111 | #define PCI9118_DIO_REG 0x1c |
1750bed3 | 112 | #define PCI9118_SOFTTRG_REG 0x20 |
7e38f36b | 113 | #define PCI9118_AI_CHANLIST_REG 0x24 |
5fc6c95c HS |
114 | #define PCI9118_AI_CHANLIST_RANGE(x) (((x) & 0x3) << 8) |
115 | #define PCI9118_AI_CHANLIST_CHAN(x) ((x) << 0) | |
16d44e86 | 116 | #define PCI9118_AI_BURST_NUM_REG 0x28 |
89a7dc15 | 117 | #define PCI9118_AI_AUTOSCAN_MODE_REG 0x2c |
7b460e9e | 118 | #define PCI9118_AI_CFG_REG 0x30 |
7db6ed68 HS |
119 | #define PCI9118_AI_CFG_PDTRG BIT(7) /* 1=positive trigger */ |
120 | #define PCI9118_AI_CFG_PETRG BIT(6) /* 1=positive ext. trigger */ | |
121 | #define PCI9118_AI_CFG_BSSH BIT(5) /* 1=with sample & hold */ | |
122 | #define PCI9118_AI_CFG_BM BIT(4) /* 1=burst mode */ | |
123 | #define PCI9118_AI_CFG_BS BIT(3) /* 1=burst mode start */ | |
124 | #define PCI9118_AI_CFG_PM BIT(2) /* 1=post trigger */ | |
125 | #define PCI9118_AI_CFG_AM BIT(1) /* 1=about trigger */ | |
126 | #define PCI9118_AI_CFG_START BIT(0) /* 1=trigger start */ | |
582e59c0 | 127 | #define PCI9118_FIFO_RESET_REG 0x34 |
74ba15ed | 128 | #define PCI9118_INT_CTRL_REG 0x38 |
7db6ed68 HS |
129 | #define PCI9118_INT_CTRL_TIMER BIT(3) /* timer interrupt */ |
130 | #define PCI9118_INT_CTRL_ABOUT BIT(2) /* about trigger complete */ | |
131 | #define PCI9118_INT_CTRL_HFULL BIT(1) /* A/D FIFO half full */ | |
132 | #define PCI9118_INT_CTRL_DTRG BIT(0) /* ext. digital trigger */ | |
3063d6de MD |
133 | |
134 | #define START_AI_EXT 0x01 /* start measure on external trigger */ | |
135 | #define STOP_AI_EXT 0x02 /* stop measure on external trigger */ | |
3063d6de MD |
136 | #define STOP_AI_INT 0x08 /* stop measure on internal trigger */ |
137 | ||
344a15c1 | 138 | static const struct comedi_lrange pci9118_ai_range = { |
713d5512 HS |
139 | 8, { |
140 | BIP_RANGE(5), | |
141 | BIP_RANGE(2.5), | |
142 | BIP_RANGE(1.25), | |
143 | BIP_RANGE(0.625), | |
144 | UNI_RANGE(10), | |
145 | UNI_RANGE(5), | |
146 | UNI_RANGE(2.5), | |
147 | UNI_RANGE(1.25) | |
148 | } | |
3063d6de MD |
149 | }; |
150 | ||
344a15c1 | 151 | static const struct comedi_lrange pci9118hg_ai_range = { |
713d5512 HS |
152 | 8, { |
153 | BIP_RANGE(5), | |
154 | BIP_RANGE(0.5), | |
155 | BIP_RANGE(0.05), | |
156 | BIP_RANGE(0.005), | |
157 | UNI_RANGE(10), | |
158 | UNI_RANGE(1), | |
159 | UNI_RANGE(0.1), | |
160 | UNI_RANGE(0.01) | |
161 | } | |
3063d6de MD |
162 | }; |
163 | ||
83defe83 HS |
164 | enum pci9118_boardid { |
165 | BOARD_PCI9118DG, | |
166 | BOARD_PCI9118HG, | |
167 | BOARD_PCI9118HR, | |
168 | }; | |
169 | ||
25a8aaf0 HS |
170 | struct pci9118_boardinfo { |
171 | const char *name; | |
a0972008 | 172 | unsigned int ai_is_16bit:1; |
344a15c1 | 173 | unsigned int is_hg:1; |
193a21e4 | 174 | }; |
3063d6de | 175 | |
25a8aaf0 | 176 | static const struct pci9118_boardinfo pci9118_boards[] = { |
83defe83 | 177 | [BOARD_PCI9118DG] = { |
15358a7f | 178 | .name = "pci9118dg", |
83defe83 HS |
179 | }, |
180 | [BOARD_PCI9118HG] = { | |
15358a7f | 181 | .name = "pci9118hg", |
344a15c1 | 182 | .is_hg = 1, |
83defe83 HS |
183 | }, |
184 | [BOARD_PCI9118HR] = { | |
15358a7f | 185 | .name = "pci9118hr", |
a0972008 | 186 | .ai_is_16bit = 1, |
15358a7f IA |
187 | }, |
188 | }; | |
189 | ||
1f2cbe2c | 190 | struct pci9118_dmabuf { |
75fbdbf6 IA |
191 | unsigned short *virt; /* virtual address of buffer */ |
192 | dma_addr_t hw; /* hardware (bus) address of buffer */ | |
1f2cbe2c HS |
193 | unsigned int size; /* size of dma buffer in bytes */ |
194 | unsigned int use_size; /* which size we may now use for transfer */ | |
1f2cbe2c HS |
195 | }; |
196 | ||
5b5fc21b | 197 | struct pci9118_private { |
242467bd | 198 | unsigned long iobase_a; /* base+size for AMCC chip */ |
32502f5a HS |
199 | unsigned int master:1; |
200 | unsigned int dma_doublebuf:1; | |
201 | unsigned int ai_neverending:1; | |
202 | unsigned int usedma:1; | |
203 | unsigned int usemux:1; | |
559cc1ee | 204 | unsigned char ai_ctrl; |
26ec7765 | 205 | unsigned char int_ctrl; |
5bdee661 | 206 | unsigned char ai_cfg; |
242467bd | 207 | unsigned int ai_do; /* what do AI? 0=nothing, 1 to 4 mode */ |
242467bd MD |
208 | unsigned int ai_n_realscanlen; /* |
209 | * what we must transfer for one | |
210 | * outgoing scan include front/back adds | |
211 | */ | |
212 | unsigned int ai_act_dmapos; /* position in actual real stream */ | |
213 | unsigned int ai_add_front; /* | |
214 | * how many channels we must add | |
215 | * before scan to satisfy S&H? | |
216 | */ | |
217 | unsigned int ai_add_back; /* | |
218 | * how many channels we must add | |
219 | * before scan to satisfy DMA? | |
220 | */ | |
3063d6de | 221 | unsigned int ai_flags; |
242467bd MD |
222 | char ai12_startstop; /* |
223 | * measure can start/stop | |
224 | * on external trigger | |
225 | */ | |
242467bd | 226 | unsigned int dma_actbuf; /* which buffer is used now */ |
1f2cbe2c | 227 | struct pci9118_dmabuf dmabuf[2]; |
242467bd MD |
228 | int softsshdelay; /* |
229 | * >0 use software S&H, | |
230 | * numer is requested delay in ns | |
231 | */ | |
232 | unsigned char softsshsample; /* | |
233 | * polarity of S&H signal | |
234 | * in sample state | |
235 | */ | |
236 | unsigned char softsshhold; /* | |
237 | * polarity of S&H signal | |
238 | * in hold state | |
239 | */ | |
34607db8 | 240 | unsigned int ai_ns_min; |
5b5fc21b | 241 | }; |
3063d6de | 242 | |
959068c3 HS |
243 | static void pci9118_amcc_setup_dma(struct comedi_device *dev, unsigned int buf) |
244 | { | |
245 | struct pci9118_private *devpriv = dev->private; | |
1f2cbe2c | 246 | struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[buf]; |
959068c3 HS |
247 | |
248 | /* set the master write address and transfer count */ | |
1f2cbe2c HS |
249 | outl(dmabuf->hw, devpriv->iobase_a + AMCC_OP_REG_MWAR); |
250 | outl(dmabuf->use_size, devpriv->iobase_a + AMCC_OP_REG_MWTC); | |
959068c3 HS |
251 | } |
252 | ||
97d09d46 HS |
253 | static void pci9118_amcc_dma_ena(struct comedi_device *dev, bool enable) |
254 | { | |
255 | struct pci9118_private *devpriv = dev->private; | |
256 | unsigned int mcsr; | |
257 | ||
258 | mcsr = inl(devpriv->iobase_a + AMCC_OP_REG_MCSR); | |
259 | if (enable) | |
260 | mcsr |= RESET_A2P_FLAGS | A2P_HI_PRIORITY | EN_A2P_TRANSFERS; | |
261 | else | |
262 | mcsr &= ~EN_A2P_TRANSFERS; | |
263 | outl(mcsr, devpriv->iobase_a + AMCC_OP_REG_MCSR); | |
264 | } | |
265 | ||
312eaf0b HS |
266 | static void pci9118_amcc_int_ena(struct comedi_device *dev, bool enable) |
267 | { | |
268 | struct pci9118_private *devpriv = dev->private; | |
269 | unsigned int intcsr; | |
270 | ||
271 | /* enable/disable interrupt for AMCC Incoming Mailbox 4 (32-bit) */ | |
272 | intcsr = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR); | |
273 | if (enable) | |
274 | intcsr |= 0x1f00; | |
275 | else | |
276 | intcsr &= ~0x1f00; | |
277 | outl(intcsr, devpriv->iobase_a + AMCC_OP_REG_INTCSR); | |
278 | } | |
279 | ||
582e59c0 HS |
280 | static void pci9118_ai_reset_fifo(struct comedi_device *dev) |
281 | { | |
282 | /* writing any value resets the A/D FIFO */ | |
283 | outl(0, dev->iobase + PCI9118_FIFO_RESET_REG); | |
284 | } | |
285 | ||
0f3cb85a HS |
286 | static int pci9118_ai_check_chanlist(struct comedi_device *dev, |
287 | struct comedi_subdevice *s, | |
288 | struct comedi_cmd *cmd) | |
5e49e515 | 289 | { |
5e49e515 | 290 | struct pci9118_private *devpriv = dev->private; |
0f3cb85a HS |
291 | unsigned int range0 = CR_RANGE(cmd->chanlist[0]); |
292 | unsigned int aref0 = CR_AREF(cmd->chanlist[0]); | |
293 | int i; | |
5e49e515 | 294 | |
0f3cb85a HS |
295 | /* single channel scans are always ok */ |
296 | if (cmd->chanlist_len == 1) | |
5e49e515 | 297 | return 0; |
5e49e515 | 298 | |
0f3cb85a HS |
299 | for (i = 1; i < cmd->chanlist_len; i++) { |
300 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); | |
301 | unsigned int range = CR_RANGE(cmd->chanlist[i]); | |
302 | unsigned int aref = CR_AREF(cmd->chanlist[i]); | |
303 | ||
304 | if (aref != aref0) { | |
305 | dev_err(dev->class_dev, | |
306 | "Differential and single ended inputs can't be mixed!\n"); | |
307 | return -EINVAL; | |
308 | } | |
309 | if (comedi_range_is_bipolar(s, range) != | |
310 | comedi_range_is_bipolar(s, range0)) { | |
311 | dev_err(dev->class_dev, | |
312 | "Bipolar and unipolar ranges can't be mixed!\n"); | |
313 | return -EINVAL; | |
314 | } | |
315 | if (!devpriv->usemux && aref == AREF_DIFF && | |
316 | (chan >= (s->n_chan / 2))) { | |
317 | dev_err(dev->class_dev, | |
318 | "AREF_DIFF is only available for the first 8 channels!\n"); | |
319 | return -EINVAL; | |
5e49e515 | 320 | } |
0f3cb85a | 321 | } |
5e49e515 | 322 | |
0f3cb85a | 323 | return 0; |
5e49e515 HS |
324 | } |
325 | ||
7d62b548 HS |
326 | static void pci9118_set_chanlist(struct comedi_device *dev, |
327 | struct comedi_subdevice *s, | |
328 | int n_chan, unsigned int *chanlist, | |
329 | int frontadd, int backadd) | |
5e49e515 HS |
330 | { |
331 | struct pci9118_private *devpriv = dev->private; | |
5fc6c95c | 332 | unsigned int chan0 = CR_CHAN(chanlist[0]); |
b7a078e9 HS |
333 | unsigned int range0 = CR_RANGE(chanlist[0]); |
334 | unsigned int aref0 = CR_AREF(chanlist[0]); | |
5fc6c95c HS |
335 | unsigned int ssh = 0x00; |
336 | unsigned int val; | |
26318c1c | 337 | int i; |
5e49e515 | 338 | |
b7a078e9 HS |
339 | /* |
340 | * Configure analog input based on the first chanlist entry. | |
341 | * All entries are either unipolar or bipolar and single-ended | |
342 | * or differential. | |
343 | */ | |
344 | devpriv->ai_ctrl = 0; | |
345 | if (comedi_range_is_unipolar(s, range0)) | |
346 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_UNIP; | |
347 | if (aref0 == AREF_DIFF) | |
348 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_DIFF; | |
349 | outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG); | |
350 | ||
89a7dc15 HS |
351 | /* gods know why this sequence! */ |
352 | outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); | |
353 | outl(0, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); | |
354 | outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); | |
5e49e515 | 355 | |
5fc6c95c HS |
356 | /* insert channels for S&H */ |
357 | if (frontadd) { | |
358 | val = PCI9118_AI_CHANLIST_CHAN(chan0) | | |
359 | PCI9118_AI_CHANLIST_RANGE(range0); | |
5e49e515 HS |
360 | ssh = devpriv->softsshsample; |
361 | for (i = 0; i < frontadd; i++) { | |
5fc6c95c | 362 | outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG); |
5e49e515 HS |
363 | ssh = devpriv->softsshhold; |
364 | } | |
365 | } | |
366 | ||
5fc6c95c HS |
367 | /* store chanlist */ |
368 | for (i = 0; i < n_chan; i++) { | |
369 | unsigned int chan = CR_CHAN(chanlist[i]); | |
370 | unsigned int range = CR_RANGE(chanlist[i]); | |
371 | ||
372 | val = PCI9118_AI_CHANLIST_CHAN(chan) | | |
373 | PCI9118_AI_CHANLIST_RANGE(range); | |
374 | outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG); | |
5e49e515 HS |
375 | } |
376 | ||
5fc6c95c HS |
377 | /* insert channels to fit onto 32bit DMA */ |
378 | if (backadd) { | |
379 | val = PCI9118_AI_CHANLIST_CHAN(chan0) | | |
380 | PCI9118_AI_CHANLIST_RANGE(range0); | |
381 | for (i = 0; i < backadd; i++) | |
382 | outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG); | |
5e49e515 | 383 | } |
89a7dc15 HS |
384 | /* close scan queue */ |
385 | outl(0, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); | |
5e49e515 | 386 | /* udelay(100); important delay, or first sample will be crippled */ |
5e49e515 | 387 | } |
3063d6de | 388 | |
ad1a9646 HS |
389 | static void pci9118_ai_mode4_switch(struct comedi_device *dev, |
390 | unsigned int next_buf) | |
3063d6de | 391 | { |
ae34f6aa | 392 | struct pci9118_private *devpriv = dev->private; |
f9d208d3 | 393 | struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[next_buf]; |
ae34f6aa | 394 | |
5bdee661 HS |
395 | devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG | |
396 | PCI9118_AI_CFG_AM; | |
397 | outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); | |
a9da9d20 HS |
398 | comedi_8254_load(dev->pacer, 0, dmabuf->hw >> 1, |
399 | I8254_MODE0 | I8254_BINARY); | |
5bdee661 HS |
400 | devpriv->ai_cfg |= PCI9118_AI_CFG_START; |
401 | outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); | |
3063d6de MD |
402 | } |
403 | ||
9459ff2b HS |
404 | static unsigned int pci9118_ai_samples_ready(struct comedi_device *dev, |
405 | struct comedi_subdevice *s, | |
406 | unsigned int n_raw_samples) | |
bb4a51a1 IA |
407 | { |
408 | struct pci9118_private *devpriv = dev->private; | |
409 | struct comedi_cmd *cmd = &s->async->cmd; | |
410 | unsigned int start_pos = devpriv->ai_add_front; | |
411 | unsigned int stop_pos = start_pos + cmd->chanlist_len; | |
412 | unsigned int span_len = stop_pos + devpriv->ai_add_back; | |
413 | unsigned int dma_pos = devpriv->ai_act_dmapos; | |
414 | unsigned int whole_spans, n_samples, x; | |
415 | ||
416 | if (span_len == cmd->chanlist_len) | |
417 | return n_raw_samples; /* use all samples */ | |
418 | ||
419 | /* | |
420 | * Not all samples are to be used. Buffer contents consist of a | |
421 | * possibly non-whole number of spans and a region of each span | |
422 | * is to be used. | |
423 | * | |
424 | * Account for samples in whole number of spans. | |
425 | */ | |
426 | whole_spans = n_raw_samples / span_len; | |
427 | n_samples = whole_spans * cmd->chanlist_len; | |
428 | n_raw_samples -= whole_spans * span_len; | |
429 | ||
430 | /* | |
431 | * Deal with remaining samples which could overlap up to two spans. | |
432 | */ | |
433 | while (n_raw_samples) { | |
434 | if (dma_pos < start_pos) { | |
435 | /* Skip samples before start position. */ | |
436 | x = start_pos - dma_pos; | |
437 | if (x > n_raw_samples) | |
438 | x = n_raw_samples; | |
439 | dma_pos += x; | |
440 | n_raw_samples -= x; | |
441 | if (!n_raw_samples) | |
442 | break; | |
443 | } | |
444 | if (dma_pos < stop_pos) { | |
445 | /* Include samples before stop position. */ | |
446 | x = stop_pos - dma_pos; | |
447 | if (x > n_raw_samples) | |
448 | x = n_raw_samples; | |
449 | n_samples += x; | |
450 | dma_pos += x; | |
451 | n_raw_samples -= x; | |
452 | } | |
453 | /* Advance to next span. */ | |
454 | start_pos += span_len; | |
455 | stop_pos += span_len; | |
456 | } | |
457 | return n_samples; | |
458 | } | |
459 | ||
5c912f1f | 460 | static void pci9118_ai_dma_xfer(struct comedi_device *dev, |
e87f65b2 IA |
461 | struct comedi_subdevice *s, |
462 | unsigned short *dma_buffer, | |
463 | unsigned int n_raw_samples) | |
3063d6de | 464 | { |
ae34f6aa | 465 | struct pci9118_private *devpriv = dev->private; |
0642d080 | 466 | struct comedi_cmd *cmd = &s->async->cmd; |
e87f65b2 IA |
467 | unsigned int start_pos = devpriv->ai_add_front; |
468 | unsigned int stop_pos = start_pos + cmd->chanlist_len; | |
469 | unsigned int span_len = stop_pos + devpriv->ai_add_back; | |
470 | unsigned int dma_pos = devpriv->ai_act_dmapos; | |
471 | unsigned int x; | |
472 | ||
473 | if (span_len == cmd->chanlist_len) { | |
474 | /* All samples are to be copied. */ | |
475 | comedi_buf_write_samples(s, dma_buffer, n_raw_samples); | |
476 | dma_pos += n_raw_samples; | |
477 | } else { | |
478 | /* | |
479 | * Not all samples are to be copied. Buffer contents consist | |
480 | * of a possibly non-whole number of spans and a region of | |
481 | * each span is to be copied. | |
482 | */ | |
483 | while (n_raw_samples) { | |
484 | if (dma_pos < start_pos) { | |
485 | /* Skip samples before start position. */ | |
486 | x = start_pos - dma_pos; | |
487 | if (x > n_raw_samples) | |
488 | x = n_raw_samples; | |
489 | dma_pos += x; | |
490 | n_raw_samples -= x; | |
491 | if (!n_raw_samples) | |
492 | break; | |
493 | } | |
494 | if (dma_pos < stop_pos) { | |
495 | /* Copy samples before stop position. */ | |
496 | x = stop_pos - dma_pos; | |
497 | if (x > n_raw_samples) | |
498 | x = n_raw_samples; | |
499 | comedi_buf_write_samples(s, dma_buffer, x); | |
500 | dma_pos += x; | |
501 | n_raw_samples -= x; | |
502 | } | |
503 | /* Advance to next span. */ | |
504 | start_pos += span_len; | |
505 | stop_pos += span_len; | |
3063d6de | 506 | } |
3063d6de | 507 | } |
e87f65b2 IA |
508 | /* Update position in span for next time. */ |
509 | devpriv->ai_act_dmapos = dma_pos % span_len; | |
3063d6de MD |
510 | } |
511 | ||
11822f02 | 512 | static void pci9118_exttrg_enable(struct comedi_device *dev, bool enable) |
3063d6de | 513 | { |
ae34f6aa HS |
514 | struct pci9118_private *devpriv = dev->private; |
515 | ||
11822f02 HS |
516 | if (enable) |
517 | devpriv->int_ctrl |= PCI9118_INT_CTRL_DTRG; | |
518 | else | |
519 | devpriv->int_ctrl &= ~PCI9118_INT_CTRL_DTRG; | |
26ec7765 | 520 | outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG); |
a41aec1b | 521 | |
11822f02 HS |
522 | if (devpriv->int_ctrl) |
523 | pci9118_amcc_int_ena(dev, true); | |
524 | else | |
664e244c | 525 | pci9118_amcc_int_ena(dev, false); |
3063d6de MD |
526 | } |
527 | ||
f3d3dad6 | 528 | static void pci9118_calc_divisors(struct comedi_device *dev, |
5e49e515 HS |
529 | struct comedi_subdevice *s, |
530 | unsigned int *tim1, unsigned int *tim2, | |
531 | unsigned int flags, int chans, | |
532 | unsigned int *div1, unsigned int *div2, | |
f3f15e54 | 533 | unsigned int chnsshfront) |
3063d6de | 534 | { |
a9da9d20 | 535 | struct comedi_8254 *pacer = dev->pacer; |
f3f15e54 | 536 | struct comedi_cmd *cmd = &s->async->cmd; |
3063d6de | 537 | |
a9da9d20 HS |
538 | *div1 = *tim2 / pacer->osc_base; /* convert timer (burst) */ |
539 | *div2 = *tim1 / pacer->osc_base; /* scan timer */ | |
f3d3dad6 HS |
540 | *div2 = *div2 / *div1; /* major timer is c1*c2 */ |
541 | if (*div2 < chans) | |
542 | *div2 = chans; | |
5e49e515 | 543 | |
a9da9d20 | 544 | *tim2 = *div1 * pacer->osc_base; /* real convert timer */ |
f3d3dad6 HS |
545 | |
546 | if (cmd->convert_src == TRIG_NOW && !chnsshfront) { | |
547 | /* use BSSH signal */ | |
548 | if (*div2 < (chans + 2)) | |
549 | *div2 = chans + 2; | |
5e49e515 | 550 | } |
f3d3dad6 | 551 | |
a9da9d20 | 552 | *tim1 = *div1 * *div2 * pacer->osc_base; |
5e49e515 HS |
553 | } |
554 | ||
bd3772ec | 555 | static void pci9118_start_pacer(struct comedi_device *dev, int mode) |
5e49e515 | 556 | { |
a9da9d20 HS |
557 | if (mode == 1 || mode == 2 || mode == 4) |
558 | comedi_8254_pacer_enable(dev->pacer, 1, 2, true); | |
5e49e515 HS |
559 | } |
560 | ||
561 | static int pci9118_ai_cancel(struct comedi_device *dev, | |
562 | struct comedi_subdevice *s) | |
563 | { | |
564 | struct pci9118_private *devpriv = dev->private; | |
565 | ||
566 | if (devpriv->usedma) | |
97d09d46 | 567 | pci9118_amcc_dma_ena(dev, false); |
11822f02 | 568 | pci9118_exttrg_enable(dev, false); |
a9da9d20 | 569 | comedi_8254_pacer_enable(dev->pacer, 1, 2, false); |
4ed1bd5a | 570 | /* set default config (disable burst and triggers) */ |
5bdee661 HS |
571 | devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG; |
572 | outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); | |
4ed1bd5a | 573 | /* reset acqusition control */ |
559cc1ee HS |
574 | devpriv->ai_ctrl = 0; |
575 | outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG); | |
16d44e86 | 576 | outl(0, dev->iobase + PCI9118_AI_BURST_NUM_REG); |
89a7dc15 HS |
577 | /* reset scan queue */ |
578 | outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); | |
579 | outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); | |
582e59c0 | 580 | pci9118_ai_reset_fifo(dev); |
5e49e515 | 581 | |
312eaf0b HS |
582 | devpriv->int_ctrl = 0; |
583 | outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG); | |
584 | pci9118_amcc_int_ena(dev, false); | |
585 | ||
5e49e515 HS |
586 | devpriv->ai_do = 0; |
587 | devpriv->usedma = 0; | |
588 | ||
5e49e515 | 589 | devpriv->ai_act_dmapos = 0; |
5e49e515 | 590 | s->async->inttrig = NULL; |
5e49e515 HS |
591 | devpriv->ai_neverending = 0; |
592 | devpriv->dma_actbuf = 0; | |
593 | ||
5e49e515 HS |
594 | return 0; |
595 | } | |
596 | ||
5e49e515 HS |
597 | static void pci9118_ai_munge(struct comedi_device *dev, |
598 | struct comedi_subdevice *s, void *data, | |
599 | unsigned int num_bytes, | |
600 | unsigned int start_chan_index) | |
601 | { | |
602 | struct pci9118_private *devpriv = dev->private; | |
6cda0d26 | 603 | unsigned short *array = data; |
1d3d32a7 HS |
604 | unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes); |
605 | unsigned int i; | |
5e49e515 HS |
606 | |
607 | for (i = 0; i < num_samples; i++) { | |
608 | if (devpriv->usedma) | |
609 | array[i] = be16_to_cpu(array[i]); | |
1fb2082d | 610 | if (s->maxdata == 0xffff) |
5e49e515 HS |
611 | array[i] ^= 0x8000; |
612 | else | |
613 | array[i] = (array[i] >> 4) & 0x0fff; | |
5e49e515 HS |
614 | } |
615 | } | |
616 | ||
45037a95 HS |
617 | static void pci9118_ai_get_onesample(struct comedi_device *dev, |
618 | struct comedi_subdevice *s) | |
5e49e515 HS |
619 | { |
620 | struct pci9118_private *devpriv = dev->private; | |
dab18a96 | 621 | struct comedi_cmd *cmd = &s->async->cmd; |
6cda0d26 | 622 | unsigned short sampl; |
5e49e515 | 623 | |
7cc1110d | 624 | sampl = inl(dev->iobase + PCI9118_AI_FIFO_REG); |
3063d6de | 625 | |
28e8c898 | 626 | comedi_buf_write_samples(s, &sampl, 1); |
f8736ca4 | 627 | |
6250d982 HS |
628 | if (!devpriv->ai_neverending) { |
629 | if (s->async->scans_done >= cmd->stop_arg) | |
630 | s->async->events |= COMEDI_CB_EOA; | |
3063d6de | 631 | } |
3063d6de MD |
632 | } |
633 | ||
45037a95 HS |
634 | static void pci9118_ai_get_dma(struct comedi_device *dev, |
635 | struct comedi_subdevice *s) | |
3063d6de | 636 | { |
ae34f6aa | 637 | struct pci9118_private *devpriv = dev->private; |
80ffd625 | 638 | struct comedi_cmd *cmd = &s->async->cmd; |
1f2cbe2c | 639 | struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[devpriv->dma_actbuf]; |
bb4a51a1 IA |
640 | unsigned int n_all = comedi_bytes_to_samples(s, dmabuf->use_size); |
641 | unsigned int n_valid; | |
642 | bool more_dma; | |
643 | ||
644 | /* determine whether more DMA buffers to do after this one */ | |
9459ff2b | 645 | n_valid = pci9118_ai_samples_ready(dev, s, n_all); |
bb4a51a1 | 646 | more_dma = n_valid < comedi_nsamples_left(s, n_valid + 1); |
3063d6de | 647 | |
ac33c20d | 648 | /* switch DMA buffers and restart DMA if double buffering */ |
bb4a51a1 | 649 | if (more_dma && devpriv->dma_doublebuf) { |
ac33c20d IA |
650 | devpriv->dma_actbuf = 1 - devpriv->dma_actbuf; |
651 | pci9118_amcc_setup_dma(dev, devpriv->dma_actbuf); | |
ad1a9646 HS |
652 | if (devpriv->ai_do == 4) |
653 | pci9118_ai_mode4_switch(dev, devpriv->dma_actbuf); | |
3063d6de MD |
654 | } |
655 | ||
e87f65b2 | 656 | if (n_all) |
5c912f1f | 657 | pci9118_ai_dma_xfer(dev, s, dmabuf->virt, n_all); |
3063d6de | 658 | |
def69d7f | 659 | if (!devpriv->ai_neverending) { |
6250d982 | 660 | if (s->async->scans_done >= cmd->stop_arg) |
3063d6de | 661 | s->async->events |= COMEDI_CB_EOA; |
def69d7f | 662 | } |
3063d6de | 663 | |
bb4a51a1 IA |
664 | if (s->async->events & COMEDI_CB_CANCEL_MASK) |
665 | more_dma = false; | |
666 | ||
ac33c20d | 667 | /* restart DMA if not double buffering */ |
bb4a51a1 | 668 | if (more_dma && !devpriv->dma_doublebuf) { |
959068c3 | 669 | pci9118_amcc_setup_dma(dev, 0); |
3063d6de | 670 | if (devpriv->ai_do == 4) |
ad1a9646 | 671 | pci9118_ai_mode4_switch(dev, 0); |
3063d6de | 672 | } |
3063d6de MD |
673 | } |
674 | ||
c089d5af | 675 | static irqreturn_t pci9118_interrupt(int irq, void *d) |
3063d6de | 676 | { |
71b5f4f1 | 677 | struct comedi_device *dev = d; |
901be534 | 678 | struct comedi_subdevice *s = dev->read_subdev; |
ae34f6aa | 679 | struct pci9118_private *devpriv = dev->private; |
c089d5af HS |
680 | unsigned int intsrc; /* IRQ reasons from card */ |
681 | unsigned int intcsr; /* INT register from AMCC chip */ | |
682 | unsigned int adstat; /* STATUS register */ | |
3063d6de MD |
683 | |
684 | if (!dev->attached) | |
c089d5af HS |
685 | return IRQ_NONE; |
686 | ||
74ba15ed | 687 | intsrc = inl(dev->iobase + PCI9118_INT_CTRL_REG) & 0xf; |
c089d5af HS |
688 | intcsr = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR); |
689 | ||
690 | if (!intsrc && !(intcsr & ANY_S593X_INT)) | |
691 | return IRQ_NONE; | |
692 | ||
693 | outl(intcsr | 0x00ff0000, devpriv->iobase_a + AMCC_OP_REG_INTCSR); | |
694 | ||
318141a1 HS |
695 | if (intcsr & MASTER_ABORT_INT) { |
696 | dev_err(dev->class_dev, "AMCC IRQ - MASTER DMA ABORT!\n"); | |
3e6cb74f | 697 | s->async->events |= COMEDI_CB_ERROR; |
45ada8e8 | 698 | goto interrupt_exit; |
318141a1 HS |
699 | } |
700 | ||
701 | if (intcsr & TARGET_ABORT_INT) { | |
702 | dev_err(dev->class_dev, "AMCC IRQ - TARGET DMA ABORT!\n"); | |
3e6cb74f | 703 | s->async->events |= COMEDI_CB_ERROR; |
45ada8e8 | 704 | goto interrupt_exit; |
318141a1 HS |
705 | } |
706 | ||
f16a7b85 HS |
707 | adstat = inl(dev->iobase + PCI9118_AI_STATUS_REG); |
708 | if ((adstat & PCI9118_AI_STATUS_NFULL) == 0) { | |
709 | dev_err(dev->class_dev, | |
710 | "A/D FIFO Full status (Fatal Error!)\n"); | |
711 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; | |
45ada8e8 | 712 | goto interrupt_exit; |
f16a7b85 HS |
713 | } |
714 | if (adstat & PCI9118_AI_STATUS_BOVER) { | |
715 | dev_err(dev->class_dev, | |
716 | "A/D Burst Mode Overrun Status (Fatal Error!)\n"); | |
717 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; | |
45ada8e8 | 718 | goto interrupt_exit; |
f16a7b85 HS |
719 | } |
720 | if (adstat & PCI9118_AI_STATUS_ADOS) { | |
721 | dev_err(dev->class_dev, "A/D Over Speed Status (Warning!)\n"); | |
722 | s->async->events |= COMEDI_CB_ERROR; | |
45ada8e8 | 723 | goto interrupt_exit; |
f16a7b85 HS |
724 | } |
725 | if (adstat & PCI9118_AI_STATUS_ADOR) { | |
726 | dev_err(dev->class_dev, "A/D Overrun Status (Fatal Error!)\n"); | |
727 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; | |
45ada8e8 | 728 | goto interrupt_exit; |
f16a7b85 | 729 | } |
c089d5af HS |
730 | |
731 | if (!devpriv->ai_do) | |
732 | return IRQ_HANDLED; | |
733 | ||
734 | if (devpriv->ai12_startstop) { | |
00bd059f HS |
735 | if ((adstat & PCI9118_AI_STATUS_DTH) && |
736 | (intsrc & PCI9118_INT_CTRL_DTRG)) { | |
c089d5af HS |
737 | /* start/stop of measure */ |
738 | if (devpriv->ai12_startstop & START_AI_EXT) { | |
739 | /* deactivate EXT trigger */ | |
740 | devpriv->ai12_startstop &= ~START_AI_EXT; | |
741 | if (!(devpriv->ai12_startstop & STOP_AI_EXT)) | |
11822f02 | 742 | pci9118_exttrg_enable(dev, false); |
c089d5af HS |
743 | |
744 | /* start pacer */ | |
bd3772ec | 745 | pci9118_start_pacer(dev, devpriv->ai_do); |
559cc1ee | 746 | outl(devpriv->ai_ctrl, |
a2a1fc7a | 747 | dev->iobase + PCI9118_AI_CTRL_REG); |
c089d5af HS |
748 | } else if (devpriv->ai12_startstop & STOP_AI_EXT) { |
749 | /* deactivate EXT trigger */ | |
750 | devpriv->ai12_startstop &= ~STOP_AI_EXT; | |
11822f02 | 751 | pci9118_exttrg_enable(dev, false); |
c089d5af HS |
752 | |
753 | /* on next interrupt measure will stop */ | |
754 | devpriv->ai_neverending = 0; | |
3063d6de | 755 | } |
901be534 | 756 | } |
c089d5af | 757 | } |
3063d6de | 758 | |
c089d5af | 759 | if (devpriv->usedma) |
45037a95 | 760 | pci9118_ai_get_dma(dev, s); |
c089d5af | 761 | else |
45037a95 | 762 | pci9118_ai_get_onesample(dev, s); |
3063d6de | 763 | |
45ada8e8 | 764 | interrupt_exit: |
f5b5164d | 765 | comedi_handle_events(dev, s); |
3063d6de MD |
766 | return IRQ_HANDLED; |
767 | } | |
768 | ||
5e17ae87 HS |
769 | static void pci9118_ai_cmd_start(struct comedi_device *dev) |
770 | { | |
771 | struct pci9118_private *devpriv = dev->private; | |
772 | ||
773 | outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG); | |
774 | outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); | |
775 | if (devpriv->ai_do != 3) { | |
776 | pci9118_start_pacer(dev, devpriv->ai_do); | |
777 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_SOFTG; | |
778 | } | |
779 | outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG); | |
780 | } | |
781 | ||
0a85b6f0 | 782 | static int pci9118_ai_inttrig(struct comedi_device *dev, |
01dc2a05 HS |
783 | struct comedi_subdevice *s, |
784 | unsigned int trig_num) | |
3063d6de | 785 | { |
01dc2a05 | 786 | struct comedi_cmd *cmd = &s->async->cmd; |
ae34f6aa | 787 | |
01dc2a05 | 788 | if (trig_num != cmd->start_arg) |
3063d6de MD |
789 | return -EINVAL; |
790 | ||
3063d6de | 791 | s->async->inttrig = NULL; |
5e17ae87 | 792 | pci9118_ai_cmd_start(dev); |
3063d6de MD |
793 | |
794 | return 1; | |
795 | } | |
796 | ||
eb12dfc9 HS |
797 | static int pci9118_ai_setup_dma(struct comedi_device *dev, |
798 | struct comedi_subdevice *s) | |
3063d6de | 799 | { |
ae34f6aa | 800 | struct pci9118_private *devpriv = dev->private; |
80ffd625 | 801 | struct comedi_cmd *cmd = &s->async->cmd; |
1f2cbe2c HS |
802 | struct pci9118_dmabuf *dmabuf0 = &devpriv->dmabuf[0]; |
803 | struct pci9118_dmabuf *dmabuf1 = &devpriv->dmabuf[1]; | |
eb96c7fc HS |
804 | unsigned int dmalen0 = dmabuf0->size; |
805 | unsigned int dmalen1 = dmabuf1->size; | |
806 | unsigned int scan_bytes = devpriv->ai_n_realscanlen * | |
807 | comedi_bytes_per_sample(s); | |
3063d6de | 808 | |
242467bd | 809 | /* isn't output buff smaller that our DMA buff? */ |
cc06e241 HS |
810 | if (dmalen0 > s->async->prealloc_bufsz) { |
811 | /* align to 32bit down */ | |
812 | dmalen0 = s->async->prealloc_bufsz & ~3L; | |
3063d6de | 813 | } |
cc06e241 HS |
814 | if (dmalen1 > s->async->prealloc_bufsz) { |
815 | /* align to 32bit down */ | |
816 | dmalen1 = s->async->prealloc_bufsz & ~3L; | |
3063d6de | 817 | } |
3063d6de | 818 | |
242467bd | 819 | /* we want wake up every scan? */ |
c25dec57 | 820 | if (devpriv->ai_flags & CMDF_WAKE_EOS) { |
eb96c7fc | 821 | if (dmalen0 < scan_bytes) { |
242467bd | 822 | /* uff, too short DMA buffer, disable EOS support! */ |
c25dec57 | 823 | devpriv->ai_flags &= (~CMDF_WAKE_EOS); |
f41d2573 | 824 | dev_info(dev->class_dev, |
c25dec57 | 825 | "WAR: DMA0 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n", |
eb96c7fc | 826 | dmalen0, scan_bytes); |
3063d6de | 827 | } else { |
242467bd | 828 | /* short first DMA buffer to one scan */ |
eb96c7fc | 829 | dmalen0 = scan_bytes; |
3063d6de | 830 | if (dmalen0 < 4) { |
f41d2573 IA |
831 | dev_info(dev->class_dev, |
832 | "ERR: DMA0 buf len bug? (%d<4)\n", | |
833 | dmalen0); | |
3063d6de MD |
834 | dmalen0 = 4; |
835 | } | |
836 | } | |
837 | } | |
c25dec57 | 838 | if (devpriv->ai_flags & CMDF_WAKE_EOS) { |
eb96c7fc | 839 | if (dmalen1 < scan_bytes) { |
242467bd | 840 | /* uff, too short DMA buffer, disable EOS support! */ |
c25dec57 | 841 | devpriv->ai_flags &= (~CMDF_WAKE_EOS); |
f41d2573 | 842 | dev_info(dev->class_dev, |
c25dec57 | 843 | "WAR: DMA1 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n", |
eb96c7fc | 844 | dmalen1, scan_bytes); |
3063d6de | 845 | } else { |
242467bd | 846 | /* short second DMA buffer to one scan */ |
eb96c7fc | 847 | dmalen1 = scan_bytes; |
3063d6de | 848 | if (dmalen1 < 4) { |
f41d2573 IA |
849 | dev_info(dev->class_dev, |
850 | "ERR: DMA1 buf len bug? (%d<4)\n", | |
851 | dmalen1); | |
3063d6de MD |
852 | dmalen1 = 4; |
853 | } | |
854 | } | |
855 | } | |
856 | ||
c25dec57 IA |
857 | /* transfer without CMDF_WAKE_EOS */ |
858 | if (!(devpriv->ai_flags & CMDF_WAKE_EOS)) { | |
eb96c7fc HS |
859 | unsigned int tmp; |
860 | ||
25985edc | 861 | /* if it's possible then align DMA buffers to length of scan */ |
eb96c7fc HS |
862 | tmp = dmalen0; |
863 | dmalen0 = (dmalen0 / scan_bytes) * scan_bytes; | |
3063d6de MD |
864 | dmalen0 &= ~3L; |
865 | if (!dmalen0) | |
eb96c7fc HS |
866 | dmalen0 = tmp; /* uff. very long scan? */ |
867 | tmp = dmalen1; | |
868 | dmalen1 = (dmalen1 / scan_bytes) * scan_bytes; | |
3063d6de MD |
869 | dmalen1 &= ~3L; |
870 | if (!dmalen1) | |
eb96c7fc | 871 | dmalen1 = tmp; /* uff. very long scan? */ |
242467bd MD |
872 | /* |
873 | * if measure isn't neverending then test, if it fits whole | |
874 | * into one or two DMA buffers | |
875 | */ | |
3063d6de | 876 | if (!devpriv->ai_neverending) { |
eb96c7fc HS |
877 | unsigned long long scanlen; |
878 | ||
879 | scanlen = (unsigned long long)scan_bytes * | |
880 | cmd->stop_arg; | |
881 | ||
242467bd | 882 | /* fits whole measure into one DMA buffer? */ |
eb96c7fc HS |
883 | if (dmalen0 > scanlen) { |
884 | dmalen0 = scanlen; | |
3063d6de | 885 | dmalen0 &= ~3L; |
eb96c7fc HS |
886 | } else { |
887 | /* fits whole measure into two DMA buffer? */ | |
888 | if (dmalen1 > (scanlen - dmalen0)) { | |
889 | dmalen1 = scanlen - dmalen0; | |
890 | dmalen1 &= ~3L; | |
891 | } | |
3063d6de MD |
892 | } |
893 | } | |
894 | } | |
895 | ||
242467bd | 896 | /* these DMA buffer size will be used */ |
3063d6de | 897 | devpriv->dma_actbuf = 0; |
1f2cbe2c HS |
898 | dmabuf0->use_size = dmalen0; |
899 | dmabuf1->use_size = dmalen1; | |
3063d6de | 900 | |
97d09d46 | 901 | pci9118_amcc_dma_ena(dev, false); |
959068c3 | 902 | pci9118_amcc_setup_dma(dev, 0); |
242467bd | 903 | /* init DMA transfer */ |
3063d6de | 904 | outl(0x00000000 | AINT_WRITE_COMPL, |
0a85b6f0 | 905 | devpriv->iobase_a + AMCC_OP_REG_INTCSR); |
0f04c356 | 906 | /* outl(0x02000000|AINT_WRITE_COMPL, devpriv->iobase_a+AMCC_OP_REG_INTCSR); */ |
97d09d46 | 907 | pci9118_amcc_dma_ena(dev, true); |
242467bd | 908 | outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | EN_A2P_TRANSFERS, |
6c7d2c8b | 909 | devpriv->iobase_a + AMCC_OP_REG_INTCSR); |
242467bd | 910 | /* allow bus mastering */ |
3063d6de | 911 | |
3063d6de MD |
912 | return 0; |
913 | } | |
914 | ||
da91b269 | 915 | static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
3063d6de | 916 | { |
ae34f6aa | 917 | struct pci9118_private *devpriv = dev->private; |
a9da9d20 | 918 | struct comedi_8254 *pacer = dev->pacer; |
ea6d0d4c | 919 | struct comedi_cmd *cmd = &s->async->cmd; |
3063d6de | 920 | unsigned int addchans = 0; |
0f3cb85a | 921 | unsigned int scanlen; |
3063d6de | 922 | |
3063d6de MD |
923 | devpriv->ai12_startstop = 0; |
924 | devpriv->ai_flags = cmd->flags; | |
3063d6de MD |
925 | devpriv->ai_add_front = 0; |
926 | devpriv->ai_add_back = 0; | |
3063d6de | 927 | |
242467bd | 928 | /* prepare for start/stop conditions */ |
3063d6de MD |
929 | if (cmd->start_src == TRIG_EXT) |
930 | devpriv->ai12_startstop |= START_AI_EXT; | |
931 | if (cmd->stop_src == TRIG_EXT) { | |
932 | devpriv->ai_neverending = 1; | |
933 | devpriv->ai12_startstop |= STOP_AI_EXT; | |
934 | } | |
3063d6de MD |
935 | if (cmd->stop_src == TRIG_NONE) |
936 | devpriv->ai_neverending = 1; | |
80ffd625 | 937 | if (cmd->stop_src == TRIG_COUNT) |
3063d6de | 938 | devpriv->ai_neverending = 0; |
3063d6de | 939 | |
242467bd MD |
940 | /* |
941 | * use additional sample at end of every scan | |
942 | * to satisty DMA 32 bit transfer? | |
943 | */ | |
3063d6de MD |
944 | devpriv->ai_add_front = 0; |
945 | devpriv->ai_add_back = 0; | |
3063d6de MD |
946 | if (devpriv->master) { |
947 | devpriv->usedma = 1; | |
c25dec57 | 948 | if ((cmd->flags & CMDF_WAKE_EOS) && |
dab18a96 | 949 | (cmd->scan_end_arg == 1)) { |
beb50909 | 950 | if (cmd->convert_src == TRIG_NOW) |
3063d6de | 951 | devpriv->ai_add_back = 1; |
3063d6de | 952 | if (cmd->convert_src == TRIG_TIMER) { |
242467bd MD |
953 | devpriv->usedma = 0; |
954 | /* | |
955 | * use INT transfer if scanlist | |
956 | * have only one channel | |
957 | */ | |
3063d6de MD |
958 | } |
959 | } | |
c25dec57 | 960 | if ((cmd->flags & CMDF_WAKE_EOS) && |
dab18a96 HS |
961 | (cmd->scan_end_arg & 1) && |
962 | (cmd->scan_end_arg > 1)) { | |
3063d6de | 963 | if (cmd->scan_begin_src == TRIG_FOLLOW) { |
242467bd MD |
964 | devpriv->usedma = 0; |
965 | /* | |
966 | * XXX maybe can be corrected to use 16 bit DMA | |
967 | */ | |
968 | } else { /* | |
969 | * well, we must insert one sample | |
970 | * to end of EOS to meet 32 bit transfer | |
971 | */ | |
3063d6de MD |
972 | devpriv->ai_add_back = 1; |
973 | } | |
974 | } | |
242467bd | 975 | } else { /* interrupt transfer don't need any correction */ |
3063d6de MD |
976 | devpriv->usedma = 0; |
977 | } | |
978 | ||
242467bd MD |
979 | /* |
980 | * we need software S&H signal? | |
981 | * It adds two samples before every scan as minimum | |
982 | */ | |
f3f15e54 | 983 | if (cmd->convert_src == TRIG_NOW && devpriv->softsshdelay) { |
3063d6de | 984 | devpriv->ai_add_front = 2; |
242467bd MD |
985 | if ((devpriv->usedma == 1) && (devpriv->ai_add_back == 1)) { |
986 | /* move it to front */ | |
3063d6de MD |
987 | devpriv->ai_add_front++; |
988 | devpriv->ai_add_back = 0; | |
989 | } | |
34607db8 HS |
990 | if (cmd->convert_arg < devpriv->ai_ns_min) |
991 | cmd->convert_arg = devpriv->ai_ns_min; | |
3063d6de MD |
992 | addchans = devpriv->softsshdelay / cmd->convert_arg; |
993 | if (devpriv->softsshdelay % cmd->convert_arg) | |
994 | addchans++; | |
242467bd MD |
995 | if (addchans > (devpriv->ai_add_front - 1)) { |
996 | /* uff, still short */ | |
3063d6de MD |
997 | devpriv->ai_add_front = addchans + 1; |
998 | if (devpriv->usedma == 1) | |
999 | if ((devpriv->ai_add_front + | |
0642d080 | 1000 | cmd->chanlist_len + |
0a85b6f0 | 1001 | devpriv->ai_add_back) & 1) |
242467bd MD |
1002 | devpriv->ai_add_front++; |
1003 | /* round up to 32 bit */ | |
3063d6de | 1004 | } |
0a85b6f0 | 1005 | } |
242467bd | 1006 | /* well, we now know what must be all added */ |
0f3cb85a HS |
1007 | scanlen = devpriv->ai_add_front + cmd->chanlist_len + |
1008 | devpriv->ai_add_back; | |
1009 | /* | |
1010 | * what we must take from card in real to have cmd->scan_end_arg | |
1011 | * on output? | |
1012 | */ | |
1013 | devpriv->ai_n_realscanlen = scanlen * | |
1014 | (cmd->scan_end_arg / cmd->chanlist_len); | |
1015 | ||
1016 | if (scanlen > s->len_chanlist) { | |
1017 | dev_err(dev->class_dev, | |
1018 | "range/channel list is too long for actual configuration!\n"); | |
3063d6de | 1019 | return -EINVAL; |
0f3cb85a | 1020 | } |
26318c1c HS |
1021 | |
1022 | /* | |
b7a078e9 | 1023 | * Configure analog input and load the chanlist. |
26318c1c HS |
1024 | * The acqusition control bits are enabled later. |
1025 | */ | |
7d62b548 HS |
1026 | pci9118_set_chanlist(dev, s, cmd->chanlist_len, cmd->chanlist, |
1027 | devpriv->ai_add_front, devpriv->ai_add_back); | |
3063d6de | 1028 | |
f3d3dad6 | 1029 | /* Determine acqusition mode and calculate timing */ |
fa795752 | 1030 | devpriv->ai_do = 0; |
f3d3dad6 HS |
1031 | if (cmd->scan_begin_src != TRIG_TIMER && |
1032 | cmd->convert_src == TRIG_TIMER) { | |
1033 | /* cascaded timers 1 and 2 are used for convert timing */ | |
beb50909 | 1034 | if (cmd->scan_begin_src == TRIG_EXT) |
3063d6de | 1035 | devpriv->ai_do = 4; |
beb50909 | 1036 | else |
3063d6de | 1037 | devpriv->ai_do = 1; |
fa795752 | 1038 | |
a9da9d20 HS |
1039 | comedi_8254_cascade_ns_to_timer(pacer, &cmd->convert_arg, |
1040 | devpriv->ai_flags & | |
1041 | CMDF_ROUND_NEAREST); | |
1042 | comedi_8254_update_divisors(pacer); | |
fa795752 HS |
1043 | |
1044 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR; | |
1045 | ||
1046 | if (!devpriv->usedma) { | |
1047 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_INT; | |
1048 | devpriv->int_ctrl |= PCI9118_INT_CTRL_TIMER; | |
1049 | } | |
1050 | ||
1051 | if (cmd->scan_begin_src == TRIG_EXT) { | |
1052 | struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[0]; | |
1053 | ||
1054 | devpriv->ai_cfg |= PCI9118_AI_CFG_AM; | |
1055 | outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); | |
a9da9d20 HS |
1056 | comedi_8254_load(pacer, 0, dmabuf->hw >> 1, |
1057 | I8254_MODE0 | I8254_BINARY); | |
fa795752 HS |
1058 | devpriv->ai_cfg |= PCI9118_AI_CFG_START; |
1059 | } | |
3063d6de MD |
1060 | } |
1061 | ||
f3d3dad6 HS |
1062 | if (cmd->scan_begin_src == TRIG_TIMER && |
1063 | cmd->convert_src != TRIG_EXT) { | |
3063d6de | 1064 | if (!devpriv->usedma) { |
027a8ccf HS |
1065 | dev_err(dev->class_dev, |
1066 | "cmd->scan_begin_src=TRIG_TIMER works only with bus mastering!\n"); | |
3063d6de MD |
1067 | return -EIO; |
1068 | } | |
1069 | ||
f3d3dad6 | 1070 | /* double timed action */ |
3063d6de | 1071 | devpriv->ai_do = 2; |
fa795752 | 1072 | |
f3d3dad6 | 1073 | pci9118_calc_divisors(dev, s, |
0a85b6f0 MT |
1074 | &cmd->scan_begin_arg, &cmd->convert_arg, |
1075 | devpriv->ai_flags, | |
1076 | devpriv->ai_n_realscanlen, | |
a9da9d20 HS |
1077 | &pacer->divisor1, |
1078 | &pacer->divisor2, | |
0a85b6f0 | 1079 | devpriv->ai_add_front); |
fa795752 HS |
1080 | |
1081 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR; | |
1082 | devpriv->ai_cfg |= PCI9118_AI_CFG_BM | PCI9118_AI_CFG_BS; | |
1083 | if (cmd->convert_src == TRIG_NOW && !devpriv->softsshdelay) | |
1084 | devpriv->ai_cfg |= PCI9118_AI_CFG_BSSH; | |
1085 | outl(devpriv->ai_n_realscanlen, | |
1086 | dev->iobase + PCI9118_AI_BURST_NUM_REG); | |
3063d6de MD |
1087 | } |
1088 | ||
f3d3dad6 HS |
1089 | if (cmd->scan_begin_src == TRIG_FOLLOW && |
1090 | cmd->convert_src == TRIG_EXT) { | |
1091 | /* external trigger conversion */ | |
3063d6de | 1092 | devpriv->ai_do = 3; |
fa795752 HS |
1093 | |
1094 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_EXTM; | |
1095 | } | |
1096 | ||
1097 | if (devpriv->ai_do == 0) { | |
1098 | dev_err(dev->class_dev, | |
1099 | "Unable to determine acqusition mode! BUG in (*do_cmdtest)?\n"); | |
1100 | return -EINVAL; | |
3063d6de MD |
1101 | } |
1102 | ||
d900197e HS |
1103 | if (devpriv->usedma) |
1104 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_DMA; | |
1105 | ||
4ed1bd5a | 1106 | /* set default config (disable burst and triggers) */ |
5bdee661 | 1107 | devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG; |
5bdee661 | 1108 | outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); |
5f74ea14 | 1109 | udelay(1); |
582e59c0 | 1110 | pci9118_ai_reset_fifo(dev); |
602c1729 HS |
1111 | |
1112 | /* clear A/D and INT status registers */ | |
1113 | inl(dev->iobase + PCI9118_AI_STATUS_REG); | |
74ba15ed | 1114 | inl(dev->iobase + PCI9118_INT_CTRL_REG); |
3063d6de | 1115 | |
3063d6de | 1116 | devpriv->ai_act_dmapos = 0; |
3063d6de | 1117 | |
fa795752 | 1118 | if (devpriv->usedma) { |
eb12dfc9 | 1119 | pci9118_ai_setup_dma(dev, s); |
fa795752 HS |
1120 | |
1121 | outl(0x02000000 | AINT_WRITE_COMPL, | |
1122 | devpriv->iobase_a + AMCC_OP_REG_INTCSR); | |
1123 | } else { | |
1124 | pci9118_amcc_int_ena(dev, true); | |
1125 | } | |
3063d6de | 1126 | |
e7d2191b HS |
1127 | /* start async command now or wait for internal trigger */ |
1128 | if (cmd->start_src == TRIG_NOW) | |
1129 | pci9118_ai_cmd_start(dev); | |
1130 | else if (cmd->start_src == TRIG_INT) | |
1131 | s->async->inttrig = pci9118_ai_inttrig; | |
1132 | ||
1133 | /* enable external trigger for command start/stop */ | |
1134 | if (cmd->start_src == TRIG_EXT || cmd->stop_src == TRIG_EXT) | |
1135 | pci9118_exttrg_enable(dev, true); | |
1136 | ||
1137 | return 0; | |
3063d6de MD |
1138 | } |
1139 | ||
8c469410 HS |
1140 | static int pci9118_ai_cmdtest(struct comedi_device *dev, |
1141 | struct comedi_subdevice *s, | |
1142 | struct comedi_cmd *cmd) | |
1143 | { | |
1144 | struct pci9118_private *devpriv = dev->private; | |
1145 | int err = 0; | |
1146 | unsigned int flags; | |
1147 | unsigned int arg; | |
8c469410 HS |
1148 | |
1149 | /* Step 1 : check if triggers are trivially valid */ | |
1150 | ||
94fff03d | 1151 | err |= comedi_check_trigger_src(&cmd->start_src, |
8c469410 HS |
1152 | TRIG_NOW | TRIG_EXT | TRIG_INT); |
1153 | ||
1154 | flags = TRIG_FOLLOW; | |
1155 | if (devpriv->master) | |
1156 | flags |= TRIG_TIMER | TRIG_EXT; | |
94fff03d | 1157 | err |= comedi_check_trigger_src(&cmd->scan_begin_src, flags); |
8c469410 HS |
1158 | |
1159 | flags = TRIG_TIMER | TRIG_EXT; | |
1160 | if (devpriv->master) | |
1161 | flags |= TRIG_NOW; | |
94fff03d | 1162 | err |= comedi_check_trigger_src(&cmd->convert_src, flags); |
8c469410 | 1163 | |
94fff03d IA |
1164 | err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); |
1165 | err |= comedi_check_trigger_src(&cmd->stop_src, | |
8c469410 HS |
1166 | TRIG_COUNT | TRIG_NONE | TRIG_EXT); |
1167 | ||
1168 | if (err) | |
1169 | return 1; | |
1170 | ||
1171 | /* Step 2a : make sure trigger sources are unique */ | |
1172 | ||
94fff03d IA |
1173 | err |= comedi_check_trigger_is_unique(cmd->start_src); |
1174 | err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); | |
1175 | err |= comedi_check_trigger_is_unique(cmd->convert_src); | |
1176 | err |= comedi_check_trigger_is_unique(cmd->stop_src); | |
8c469410 HS |
1177 | |
1178 | /* Step 2b : and mutually compatible */ | |
1179 | ||
1180 | if (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) | |
1181 | err |= -EINVAL; | |
1182 | ||
8c469410 HS |
1183 | if ((cmd->scan_begin_src & (TRIG_TIMER | TRIG_EXT)) && |
1184 | (!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW)))) | |
1185 | err |= -EINVAL; | |
1186 | ||
1187 | if ((cmd->scan_begin_src == TRIG_FOLLOW) && | |
1188 | (!(cmd->convert_src & (TRIG_TIMER | TRIG_EXT)))) | |
1189 | err |= -EINVAL; | |
1190 | ||
1191 | if (cmd->stop_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) | |
1192 | err |= -EINVAL; | |
1193 | ||
1194 | if (err) | |
1195 | return 2; | |
1196 | ||
1197 | /* Step 3: check if arguments are trivially valid */ | |
1198 | ||
1199 | switch (cmd->start_src) { | |
1200 | case TRIG_NOW: | |
1201 | case TRIG_EXT: | |
94fff03d | 1202 | err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); |
8c469410 HS |
1203 | break; |
1204 | case TRIG_INT: | |
1205 | /* start_arg is the internal trigger (any value) */ | |
1206 | break; | |
1207 | } | |
1208 | ||
1209 | if (cmd->scan_begin_src & (TRIG_FOLLOW | TRIG_EXT)) | |
94fff03d | 1210 | err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); |
8c469410 HS |
1211 | |
1212 | if ((cmd->scan_begin_src == TRIG_TIMER) && | |
1213 | (cmd->convert_src == TRIG_TIMER) && (cmd->scan_end_arg == 1)) { | |
1214 | cmd->scan_begin_src = TRIG_FOLLOW; | |
1215 | cmd->convert_arg = cmd->scan_begin_arg; | |
1216 | cmd->scan_begin_arg = 0; | |
1217 | } | |
1218 | ||
94fff03d IA |
1219 | if (cmd->scan_begin_src == TRIG_TIMER) { |
1220 | err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, | |
1221 | devpriv->ai_ns_min); | |
1222 | } | |
8c469410 | 1223 | |
94fff03d | 1224 | if (cmd->scan_begin_src == TRIG_EXT) { |
8c469410 HS |
1225 | if (cmd->scan_begin_arg) { |
1226 | cmd->scan_begin_arg = 0; | |
1227 | err |= -EINVAL; | |
94fff03d IA |
1228 | err |= comedi_check_trigger_arg_max(&cmd->scan_end_arg, |
1229 | 65535); | |
8c469410 | 1230 | } |
94fff03d | 1231 | } |
8c469410 | 1232 | |
94fff03d IA |
1233 | if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) { |
1234 | err |= comedi_check_trigger_arg_min(&cmd->convert_arg, | |
1235 | devpriv->ai_ns_min); | |
1236 | } | |
8c469410 HS |
1237 | |
1238 | if (cmd->convert_src == TRIG_EXT) | |
94fff03d | 1239 | err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); |
8c469410 HS |
1240 | |
1241 | if (cmd->stop_src == TRIG_COUNT) | |
94fff03d | 1242 | err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); |
8c469410 | 1243 | else /* TRIG_NONE */ |
94fff03d | 1244 | err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); |
8c469410 | 1245 | |
94fff03d | 1246 | err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1); |
8c469410 | 1247 | |
94fff03d IA |
1248 | err |= comedi_check_trigger_arg_min(&cmd->scan_end_arg, |
1249 | cmd->chanlist_len); | |
8c469410 HS |
1250 | |
1251 | if ((cmd->scan_end_arg % cmd->chanlist_len)) { | |
1252 | cmd->scan_end_arg = | |
1253 | cmd->chanlist_len * (cmd->scan_end_arg / cmd->chanlist_len); | |
1254 | err |= -EINVAL; | |
1255 | } | |
1256 | ||
1257 | if (err) | |
1258 | return 3; | |
1259 | ||
1260 | /* step 4: fix up any arguments */ | |
1261 | ||
1262 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
1263 | arg = cmd->scan_begin_arg; | |
a9da9d20 | 1264 | comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); |
94fff03d | 1265 | err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg); |
8c469410 HS |
1266 | } |
1267 | ||
1268 | if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) { | |
1269 | arg = cmd->convert_arg; | |
a9da9d20 | 1270 | comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); |
94fff03d | 1271 | err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); |
8c469410 HS |
1272 | |
1273 | if (cmd->scan_begin_src == TRIG_TIMER && | |
1274 | cmd->convert_src == TRIG_NOW) { | |
1275 | if (cmd->convert_arg == 0) { | |
1276 | arg = devpriv->ai_ns_min * | |
1277 | (cmd->scan_end_arg + 2); | |
1278 | } else { | |
1279 | arg = cmd->convert_arg * cmd->chanlist_len; | |
1280 | } | |
94fff03d IA |
1281 | err |= comedi_check_trigger_arg_min(&cmd-> |
1282 | scan_begin_arg, | |
1283 | arg); | |
8c469410 HS |
1284 | } |
1285 | } | |
1286 | ||
1287 | if (err) | |
1288 | return 4; | |
1289 | ||
0f3cb85a HS |
1290 | /* Step 5: check channel list if it exists */ |
1291 | ||
8c469410 | 1292 | if (cmd->chanlist) |
0f3cb85a HS |
1293 | err |= pci9118_ai_check_chanlist(dev, s, cmd); |
1294 | ||
1295 | if (err) | |
1296 | return 5; | |
8c469410 HS |
1297 | |
1298 | return 0; | |
1299 | } | |
1300 | ||
649a7d15 HS |
1301 | static int pci9118_ai_eoc(struct comedi_device *dev, |
1302 | struct comedi_subdevice *s, | |
1303 | struct comedi_insn *insn, | |
1304 | unsigned long context) | |
1305 | { | |
1306 | unsigned int status; | |
1307 | ||
1308 | status = inl(dev->iobase + PCI9118_AI_STATUS_REG); | |
1309 | if (status & PCI9118_AI_STATUS_ADRDY) | |
1310 | return 0; | |
1311 | return -EBUSY; | |
1312 | } | |
1313 | ||
1314 | static void pci9118_ai_start_conv(struct comedi_device *dev) | |
1315 | { | |
1316 | /* writing any value triggers an A/D conversion */ | |
1317 | outl(0, dev->iobase + PCI9118_SOFTTRG_REG); | |
1318 | } | |
1319 | ||
1320 | static int pci9118_ai_insn_read(struct comedi_device *dev, | |
1321 | struct comedi_subdevice *s, | |
1322 | struct comedi_insn *insn, | |
1323 | unsigned int *data) | |
1324 | { | |
1325 | struct pci9118_private *devpriv = dev->private; | |
1326 | unsigned int val; | |
1327 | int ret; | |
1328 | int i; | |
1329 | ||
1330 | /* | |
1331 | * Configure analog input based on the chanspec. | |
1332 | * Acqusition is software controlled without interrupts. | |
1333 | */ | |
b7a078e9 | 1334 | pci9118_set_chanlist(dev, s, 1, &insn->chanspec, 0, 0); |
649a7d15 HS |
1335 | |
1336 | /* set default config (disable burst and triggers) */ | |
1337 | devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG; | |
1338 | outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); | |
1339 | ||
649a7d15 HS |
1340 | pci9118_ai_reset_fifo(dev); |
1341 | ||
1342 | for (i = 0; i < insn->n; i++) { | |
1343 | pci9118_ai_start_conv(dev); | |
1344 | ||
1345 | ret = comedi_timeout(dev, s, insn, pci9118_ai_eoc, 0); | |
1346 | if (ret) | |
1347 | return ret; | |
1348 | ||
1349 | val = inl(dev->iobase + PCI9118_AI_FIFO_REG); | |
1350 | if (s->maxdata == 0xffff) | |
1351 | data[i] = (val & 0xffff) ^ 0x8000; | |
1352 | else | |
1353 | data[i] = (val >> 4) & 0xfff; | |
1354 | } | |
1355 | ||
1356 | return insn->n; | |
1357 | } | |
1358 | ||
3cb3fc0a HS |
1359 | static int pci9118_ao_insn_write(struct comedi_device *dev, |
1360 | struct comedi_subdevice *s, | |
1361 | struct comedi_insn *insn, | |
1362 | unsigned int *data) | |
1363 | { | |
1364 | unsigned int chan = CR_CHAN(insn->chanspec); | |
1365 | unsigned int val = s->readback[chan]; | |
1366 | int i; | |
1367 | ||
1368 | for (i = 0; i < insn->n; i++) { | |
1369 | val = data[i]; | |
1370 | outl(val, dev->iobase + PCI9118_AO_REG(chan)); | |
1371 | } | |
1372 | s->readback[chan] = val; | |
1373 | ||
1374 | return insn->n; | |
1375 | } | |
1376 | ||
f300fab2 HS |
1377 | static int pci9118_di_insn_bits(struct comedi_device *dev, |
1378 | struct comedi_subdevice *s, | |
1379 | struct comedi_insn *insn, | |
1380 | unsigned int *data) | |
1381 | { | |
1382 | /* | |
1383 | * The digital inputs and outputs share the read register. | |
1384 | * bits [7:4] are the digital outputs | |
1385 | * bits [3:0] are the digital inputs | |
1386 | */ | |
1387 | data[1] = inl(dev->iobase + PCI9118_DIO_REG) & 0xf; | |
1388 | ||
1389 | return insn->n; | |
1390 | } | |
1391 | ||
8df6166b HS |
1392 | static int pci9118_do_insn_bits(struct comedi_device *dev, |
1393 | struct comedi_subdevice *s, | |
1394 | struct comedi_insn *insn, | |
1395 | unsigned int *data) | |
1396 | { | |
1397 | /* | |
1398 | * The digital outputs are set with the same register that | |
1399 | * the digital inputs and outputs are read from. But the | |
1400 | * outputs are set with bits [3:0] so we can simply write | |
1401 | * the s->state to set them. | |
1402 | */ | |
1403 | if (comedi_dio_update_state(s, data)) | |
1404 | outl(s->state, dev->iobase + PCI9118_DIO_REG); | |
1405 | ||
1406 | data[1] = s->state; | |
1407 | ||
1408 | return insn->n; | |
1409 | } | |
1410 | ||
5358e899 | 1411 | static void pci9118_reset(struct comedi_device *dev) |
3063d6de | 1412 | { |
5358e899 HS |
1413 | /* reset analog input subsystem */ |
1414 | outl(0, dev->iobase + PCI9118_INT_CTRL_REG); | |
1415 | outl(0, dev->iobase + PCI9118_AI_CTRL_REG); | |
1416 | outl(0, dev->iobase + PCI9118_AI_CFG_REG); | |
1417 | pci9118_ai_reset_fifo(dev); | |
ae34f6aa | 1418 | |
5358e899 | 1419 | /* clear any pending interrupts and status */ |
74ba15ed | 1420 | inl(dev->iobase + PCI9118_INT_CTRL_REG); |
5358e899 HS |
1421 | inl(dev->iobase + PCI9118_AI_STATUS_REG); |
1422 | ||
5358e899 | 1423 | /* reset DMA and scan queue */ |
16d44e86 | 1424 | outl(0, dev->iobase + PCI9118_AI_BURST_NUM_REG); |
89a7dc15 HS |
1425 | outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); |
1426 | outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); | |
3063d6de | 1427 | |
c6908517 | 1428 | /* reset analog outputs to 0V */ |
458a76f4 HS |
1429 | outl(2047, dev->iobase + PCI9118_AO_REG(0)); |
1430 | outl(2047, dev->iobase + PCI9118_AO_REG(1)); | |
3063d6de MD |
1431 | } |
1432 | ||
93b6e479 HS |
1433 | static struct pci_dev *pci9118_find_pci(struct comedi_device *dev, |
1434 | struct comedi_devconfig *it) | |
1435 | { | |
1436 | struct pci_dev *pcidev = NULL; | |
1437 | int bus = it->options[0]; | |
1438 | int slot = it->options[1]; | |
1439 | ||
1440 | for_each_pci_dev(pcidev) { | |
1441 | if (pcidev->vendor != PCI_VENDOR_ID_AMCC) | |
1442 | continue; | |
8bd1e1df | 1443 | if (pcidev->device != 0x80d9) |
93b6e479 HS |
1444 | continue; |
1445 | if (bus || slot) { | |
1446 | /* requested particular bus/slot */ | |
1447 | if (pcidev->bus->number != bus || | |
1448 | PCI_SLOT(pcidev->devfn) != slot) | |
1449 | continue; | |
1450 | } | |
93b6e479 HS |
1451 | return pcidev; |
1452 | } | |
f41d2573 IA |
1453 | dev_err(dev->class_dev, |
1454 | "no supported board found! (req. bus/slot : %d/%d)\n", | |
1455 | bus, slot); | |
93b6e479 HS |
1456 | return NULL; |
1457 | } | |
1458 | ||
b9ca5508 HS |
1459 | static void pci9118_alloc_dma(struct comedi_device *dev) |
1460 | { | |
1461 | struct pci9118_private *devpriv = dev->private; | |
1f2cbe2c | 1462 | struct pci9118_dmabuf *dmabuf; |
f39f87e9 | 1463 | int order; |
b9ca5508 HS |
1464 | int i; |
1465 | ||
1466 | for (i = 0; i < 2; i++) { | |
1f2cbe2c | 1467 | dmabuf = &devpriv->dmabuf[i]; |
f39f87e9 | 1468 | for (order = 2; order >= 0; order--) { |
75fbdbf6 IA |
1469 | dmabuf->virt = |
1470 | dma_alloc_coherent(dev->hw_dev, PAGE_SIZE << order, | |
1471 | &dmabuf->hw, GFP_KERNEL); | |
1f2cbe2c | 1472 | if (dmabuf->virt) |
b9ca5508 HS |
1473 | break; |
1474 | } | |
d5733baf IA |
1475 | if (!dmabuf->virt) |
1476 | break; | |
f39f87e9 | 1477 | dmabuf->size = PAGE_SIZE << order; |
d5733baf IA |
1478 | |
1479 | if (i == 0) | |
1480 | devpriv->master = 1; | |
1481 | if (i == 1) | |
1482 | devpriv->dma_doublebuf = 1; | |
b9ca5508 | 1483 | } |
b9ca5508 HS |
1484 | } |
1485 | ||
1486 | static void pci9118_free_dma(struct comedi_device *dev) | |
1487 | { | |
1488 | struct pci9118_private *devpriv = dev->private; | |
1f2cbe2c HS |
1489 | struct pci9118_dmabuf *dmabuf; |
1490 | int i; | |
b9ca5508 HS |
1491 | |
1492 | if (!devpriv) | |
1493 | return; | |
1494 | ||
1f2cbe2c HS |
1495 | for (i = 0; i < 2; i++) { |
1496 | dmabuf = &devpriv->dmabuf[i]; | |
75fbdbf6 IA |
1497 | if (dmabuf->virt) { |
1498 | dma_free_coherent(dev->hw_dev, dmabuf->size, | |
1499 | dmabuf->virt, dmabuf->hw); | |
1500 | } | |
1f2cbe2c | 1501 | } |
b9ca5508 HS |
1502 | } |
1503 | ||
cb578327 | 1504 | static int pci9118_common_attach(struct comedi_device *dev, |
9e531485 | 1505 | int ext_mux, int softsshdelay) |
3063d6de | 1506 | { |
0220d472 | 1507 | const struct pci9118_boardinfo *board = dev->board_ptr; |
f3b81d54 | 1508 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
f6009ae5 | 1509 | struct pci9118_private *devpriv; |
34c43922 | 1510 | struct comedi_subdevice *s; |
b9ca5508 HS |
1511 | int ret; |
1512 | int i; | |
3063d6de MD |
1513 | u16 u16w; |
1514 | ||
f6009ae5 HS |
1515 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
1516 | if (!devpriv) | |
1517 | return -ENOMEM; | |
1518 | ||
818f569f HS |
1519 | ret = comedi_pci_enable(dev); |
1520 | if (ret) | |
f3b81d54 | 1521 | return ret; |
cb578327 | 1522 | pci_set_master(pcidev); |
a41aec1b | 1523 | |
c945a1d1 HS |
1524 | devpriv->iobase_a = pci_resource_start(pcidev, 0); |
1525 | dev->iobase = pci_resource_start(pcidev, 2); | |
3063d6de | 1526 | |
a9da9d20 HS |
1527 | dev->pacer = comedi_8254_init(dev->iobase + PCI9118_TIMER_BASE, |
1528 | I8254_OSC_BASE_4MHZ, I8254_IO32, 0); | |
1529 | if (!dev->pacer) | |
1530 | return -ENOMEM; | |
1531 | ||
3063d6de MD |
1532 | pci9118_reset(dev); |
1533 | ||
cb578327 | 1534 | if (pcidev->irq) { |
6cf6b367 HS |
1535 | ret = request_irq(pcidev->irq, pci9118_interrupt, IRQF_SHARED, |
1536 | dev->board_name, dev); | |
1537 | if (ret == 0) { | |
1538 | dev->irq = pcidev->irq; | |
1539 | ||
cb578327 | 1540 | pci9118_alloc_dma(dev); |
6cf6b367 HS |
1541 | } |
1542 | } | |
f3b81d54 IA |
1543 | |
1544 | if (ext_mux > 0) { | |
1545 | if (ext_mux > 256) | |
1546 | ext_mux = 256; /* max 256 channels! */ | |
1547 | if (softsshdelay > 0) | |
1548 | if (ext_mux > 128) | |
1549 | ext_mux = 128; | |
32502f5a | 1550 | devpriv->usemux = 1; |
f3b81d54 IA |
1551 | } else { |
1552 | devpriv->usemux = 0; | |
3063d6de MD |
1553 | } |
1554 | ||
f3b81d54 IA |
1555 | if (softsshdelay < 0) { |
1556 | /* select sample&hold signal polarity */ | |
1557 | devpriv->softsshdelay = -softsshdelay; | |
3063d6de MD |
1558 | devpriv->softsshsample = 0x80; |
1559 | devpriv->softsshhold = 0x00; | |
1560 | } else { | |
f3b81d54 | 1561 | devpriv->softsshdelay = softsshdelay; |
3063d6de MD |
1562 | devpriv->softsshsample = 0x00; |
1563 | devpriv->softsshhold = 0x80; | |
1564 | } | |
1565 | ||
c945a1d1 HS |
1566 | pci_read_config_word(pcidev, PCI_COMMAND, &u16w); |
1567 | pci_write_config_word(pcidev, PCI_COMMAND, u16w | 64); | |
242467bd | 1568 | /* Enable parity check for parity error */ |
3063d6de | 1569 | |
2f0b9d08 | 1570 | ret = comedi_alloc_subdevices(dev, 4); |
8b6c5694 | 1571 | if (ret) |
3063d6de MD |
1572 | return ret; |
1573 | ||
76c1b7bc | 1574 | /* Analog Input subdevice */ |
17c7ac91 | 1575 | s = &dev->subdevices[0]; |
76c1b7bc HS |
1576 | s->type = COMEDI_SUBD_AI; |
1577 | s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF; | |
32502f5a | 1578 | s->n_chan = (devpriv->usemux) ? ext_mux : 16; |
76c1b7bc HS |
1579 | s->maxdata = board->ai_is_16bit ? 0xffff : 0x0fff; |
1580 | s->range_table = board->is_hg ? &pci9118hg_ai_range | |
1581 | : &pci9118_ai_range; | |
649a7d15 | 1582 | s->insn_read = pci9118_ai_insn_read; |
19cf54dd HS |
1583 | if (dev->irq) { |
1584 | dev->read_subdev = s; | |
76c1b7bc | 1585 | s->subdev_flags |= SDF_CMD_READ; |
dd2907c3 | 1586 | s->len_chanlist = 255; |
76c1b7bc HS |
1587 | s->do_cmdtest = pci9118_ai_cmdtest; |
1588 | s->do_cmd = pci9118_ai_cmd; | |
1589 | s->cancel = pci9118_ai_cancel; | |
1590 | s->munge = pci9118_ai_munge; | |
19cf54dd | 1591 | } |
3063d6de | 1592 | |
34607db8 HS |
1593 | if (s->maxdata == 0xffff) { |
1594 | /* | |
1595 | * 16-bit samples are from an ADS7805 A/D converter. | |
1596 | * Minimum sampling rate is 10us. | |
1597 | */ | |
1598 | devpriv->ai_ns_min = 10000; | |
1599 | } else { | |
1600 | /* | |
1601 | * 12-bit samples are from an ADS7800 A/D converter. | |
1602 | * Minimum sampling rate is 3us. | |
1603 | */ | |
1604 | devpriv->ai_ns_min = 3000; | |
1605 | } | |
1606 | ||
949dcfce | 1607 | /* Analog Output subdevice */ |
17c7ac91 | 1608 | s = &dev->subdevices[1]; |
949dcfce HS |
1609 | s->type = COMEDI_SUBD_AO; |
1610 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; | |
1611 | s->n_chan = 2; | |
1612 | s->maxdata = 0x0fff; | |
1613 | s->range_table = &range_bipolar10; | |
3cb3fc0a | 1614 | s->insn_write = pci9118_ao_insn_write; |
458a76f4 HS |
1615 | |
1616 | ret = comedi_alloc_subdev_readback(s); | |
1617 | if (ret) | |
1618 | return ret; | |
1619 | ||
1620 | /* the analog outputs were reset to 0V, make the readback match */ | |
1621 | for (i = 0; i < s->n_chan; i++) | |
1622 | s->readback[i] = 2047; | |
3063d6de | 1623 | |
32034aaf | 1624 | /* Digital Input subdevice */ |
17c7ac91 | 1625 | s = &dev->subdevices[2]; |
32034aaf HS |
1626 | s->type = COMEDI_SUBD_DI; |
1627 | s->subdev_flags = SDF_READABLE; | |
1628 | s->n_chan = 4; | |
1629 | s->maxdata = 1; | |
1630 | s->range_table = &range_digital; | |
f300fab2 | 1631 | s->insn_bits = pci9118_di_insn_bits; |
3063d6de | 1632 | |
1e9879f7 | 1633 | /* Digital Output subdevice */ |
17c7ac91 | 1634 | s = &dev->subdevices[3]; |
1e9879f7 HS |
1635 | s->type = COMEDI_SUBD_DO; |
1636 | s->subdev_flags = SDF_WRITABLE; | |
1637 | s->n_chan = 4; | |
1638 | s->maxdata = 1; | |
1639 | s->range_table = &range_digital; | |
8df6166b | 1640 | s->insn_bits = pci9118_do_insn_bits; |
3063d6de | 1641 | |
304e2be1 HS |
1642 | /* get the current state of the digital outputs */ |
1643 | s->state = inl(dev->iobase + PCI9118_DIO_REG) >> 4; | |
1644 | ||
3063d6de MD |
1645 | return 0; |
1646 | } | |
1647 | ||
f3b81d54 IA |
1648 | static int pci9118_attach(struct comedi_device *dev, |
1649 | struct comedi_devconfig *it) | |
1650 | { | |
f3b81d54 | 1651 | struct pci_dev *pcidev; |
9e531485 | 1652 | int ext_mux, softsshdelay; |
f3b81d54 IA |
1653 | |
1654 | ext_mux = it->options[2]; | |
f3b81d54 | 1655 | softsshdelay = it->options[4]; |
f3b81d54 | 1656 | |
f3b81d54 IA |
1657 | pcidev = pci9118_find_pci(dev, it); |
1658 | if (!pcidev) | |
1659 | return -EIO; | |
1660 | comedi_set_hw_dev(dev, &pcidev->dev); | |
1661 | ||
9e531485 | 1662 | return pci9118_common_attach(dev, ext_mux, softsshdelay); |
f3b81d54 IA |
1663 | } |
1664 | ||
a690b7e5 | 1665 | static int pci9118_auto_attach(struct comedi_device *dev, |
83defe83 | 1666 | unsigned long context) |
15358a7f IA |
1667 | { |
1668 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); | |
25a8aaf0 | 1669 | const struct pci9118_boardinfo *board = NULL; |
15358a7f | 1670 | |
25a8aaf0 HS |
1671 | if (context < ARRAY_SIZE(pci9118_boards)) |
1672 | board = &pci9118_boards[context]; | |
83defe83 HS |
1673 | if (!board) |
1674 | return -ENODEV; | |
1675 | dev->board_ptr = board; | |
1676 | dev->board_name = board->name; | |
1677 | ||
15358a7f IA |
1678 | /* |
1679 | * Need to 'get' the PCI device to match the 'put' in pci9118_detach(). | |
1680 | * (The 'put' also matches the implicit 'get' by pci9118_find_pci().) | |
1681 | */ | |
1682 | pci_dev_get(pcidev); | |
9e531485 HS |
1683 | /* no external mux, no sample-hold delay */ |
1684 | return pci9118_common_attach(dev, 0, 0); | |
15358a7f IA |
1685 | } |
1686 | ||
484ecc95 | 1687 | static void pci9118_detach(struct comedi_device *dev) |
3063d6de | 1688 | { |
c945a1d1 HS |
1689 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
1690 | ||
aac307f9 HS |
1691 | if (dev->iobase) |
1692 | pci9118_reset(dev); | |
1693 | comedi_pci_detach(dev); | |
b9ca5508 | 1694 | pci9118_free_dma(dev); |
7f072f54 | 1695 | if (pcidev) |
c945a1d1 | 1696 | pci_dev_put(pcidev); |
3063d6de MD |
1697 | } |
1698 | ||
618fc38f HS |
1699 | static struct comedi_driver adl_pci9118_driver = { |
1700 | .driver_name = "adl_pci9118", | |
1701 | .module = THIS_MODULE, | |
1702 | .attach = pci9118_attach, | |
15358a7f | 1703 | .auto_attach = pci9118_auto_attach, |
618fc38f | 1704 | .detach = pci9118_detach, |
25a8aaf0 HS |
1705 | .num_names = ARRAY_SIZE(pci9118_boards), |
1706 | .board_name = &pci9118_boards[0].name, | |
1707 | .offset = sizeof(struct pci9118_boardinfo), | |
618fc38f HS |
1708 | }; |
1709 | ||
a690b7e5 | 1710 | static int adl_pci9118_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 1711 | const struct pci_device_id *id) |
618fc38f | 1712 | { |
b8f4ac23 HS |
1713 | return comedi_pci_auto_config(dev, &adl_pci9118_driver, |
1714 | id->driver_data); | |
618fc38f HS |
1715 | } |
1716 | ||
83defe83 | 1717 | /* FIXME: All the supported board types have the same device ID! */ |
41e043fc | 1718 | static const struct pci_device_id adl_pci9118_pci_table[] = { |
83defe83 HS |
1719 | { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118DG }, |
1720 | /* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HG }, */ | |
1721 | /* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HR }, */ | |
618fc38f HS |
1722 | { 0 } |
1723 | }; | |
1724 | MODULE_DEVICE_TABLE(pci, adl_pci9118_pci_table); | |
1725 | ||
1726 | static struct pci_driver adl_pci9118_pci_driver = { | |
1727 | .name = "adl_pci9118", | |
1728 | .id_table = adl_pci9118_pci_table, | |
1729 | .probe = adl_pci9118_pci_probe, | |
9901a4d7 | 1730 | .remove = comedi_pci_auto_unconfig, |
618fc38f HS |
1731 | }; |
1732 | module_comedi_pci_driver(adl_pci9118_driver, adl_pci9118_pci_driver); | |
90f703d3 AT |
1733 | |
1734 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
1735 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
1736 | MODULE_LICENSE("GPL"); |