Commit | Line | Data |
---|---|---|
f26c569b FMH |
1 | /* |
2 | comedi/drivers/gsc_hpdi.c | |
3 | This is a driver for the General Standards Corporation High | |
4 | Speed Parallel Digital Interface rs485 boards. | |
5 | ||
6 | Author: Frank Mori Hess <fmhess@users.sourceforge.net> | |
7 | Copyright (C) 2003 Coherent Imaging Systems | |
8 | ||
9 | COMEDI - Linux Control and Measurement Device Interface | |
10 | Copyright (C) 1997-8 David A. Schleef <ds@schleef.org> | |
11 | ||
12 | This program is free software; you can redistribute it and/or modify | |
13 | it under the terms of the GNU General Public License as published by | |
14 | the Free Software Foundation; either version 2 of the License, or | |
15 | (at your option) any later version. | |
16 | ||
17 | This program is distributed in the hope that it will be useful, | |
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | GNU General Public License for more details. | |
641f064e | 21 | */ |
f26c569b FMH |
22 | |
23 | /* | |
50a24814 IA |
24 | * Driver: gsc_hpdi |
25 | * Description: General Standards Corporation High | |
26 | * Speed Parallel Digital Interface rs485 boards | |
27 | * Author: Frank Mori Hess <fmhess@users.sourceforge.net> | |
28 | * Status: only receive mode works, transmit not supported | |
29 | * Updated: Thu, 01 Nov 2012 16:17:38 +0000 | |
30 | * Devices: [General Standards Corporation] PCI-HPDI32 (gsc_hpdi), | |
31 | * PMC-HPDI32 | |
32 | * | |
33 | * Configuration options: | |
34 | * None. | |
35 | * | |
36 | * Manual configuration of supported devices is not supported; they are | |
37 | * configured automatically. | |
38 | * | |
39 | * There are some additional hpdi models available from GSC for which | |
40 | * support could be added to this driver. | |
41 | */ | |
f26c569b | 42 | |
871e1d05 IA |
43 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
44 | ||
33782dd5 HS |
45 | #include <linux/pci.h> |
46 | #include <linux/delay.h> | |
25436dc9 | 47 | #include <linux/interrupt.h> |
33782dd5 | 48 | |
f26c569b | 49 | #include "../comedidev.h" |
f26c569b | 50 | |
f26c569b FMH |
51 | #include "plx9080.h" |
52 | #include "comedi_fc.h" | |
53 | ||
64ca6a7e | 54 | static void abort_dma(struct comedi_device *dev, unsigned int channel); |
814900c9 BP |
55 | static int hpdi_cmd(struct comedi_device *dev, struct comedi_subdevice *s); |
56 | static int hpdi_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s, | |
0a85b6f0 | 57 | struct comedi_cmd *cmd); |
814900c9 | 58 | static int hpdi_cancel(struct comedi_device *dev, struct comedi_subdevice *s); |
70265d24 | 59 | static irqreturn_t handle_interrupt(int irq, void *d); |
814900c9 | 60 | static int dio_config_block_size(struct comedi_device *dev, unsigned int *data); |
f26c569b | 61 | |
c52c19c3 BP |
62 | #undef HPDI_DEBUG /* disable debugging messages */ |
63 | /* #define HPDI_DEBUG enable debugging code */ | |
f26c569b FMH |
64 | |
65 | #ifdef HPDI_DEBUG | |
871e1d05 | 66 | #define DEBUG_PRINT(format, args...) pr_debug(format , ## args) |
f26c569b | 67 | #else |
871e1d05 | 68 | #define DEBUG_PRINT(format, args...) no_printk(pr_fmt(format), ## args) |
f26c569b FMH |
69 | #endif |
70 | ||
c52c19c3 | 71 | #define TIMER_BASE 50 /* 20MHz master clock */ |
f26c569b FMH |
72 | #define DMA_BUFFER_SIZE 0x10000 |
73 | #define NUM_DMA_BUFFERS 4 | |
74 | #define NUM_DMA_DESCRIPTORS 256 | |
75 | ||
f26c569b FMH |
76 | enum hpdi_registers { |
77 | FIRMWARE_REV_REG = 0x0, | |
78 | BOARD_CONTROL_REG = 0x4, | |
79 | BOARD_STATUS_REG = 0x8, | |
80 | TX_PROG_ALMOST_REG = 0xc, | |
81 | RX_PROG_ALMOST_REG = 0x10, | |
82 | FEATURES_REG = 0x14, | |
83 | FIFO_REG = 0x18, | |
84 | TX_STATUS_COUNT_REG = 0x1c, | |
85 | TX_LINE_VALID_COUNT_REG = 0x20, | |
86 | TX_LINE_INVALID_COUNT_REG = 0x24, | |
87 | RX_STATUS_COUNT_REG = 0x28, | |
88 | RX_LINE_COUNT_REG = 0x2c, | |
89 | INTERRUPT_CONTROL_REG = 0x30, | |
90 | INTERRUPT_STATUS_REG = 0x34, | |
91 | TX_CLOCK_DIVIDER_REG = 0x38, | |
92 | TX_FIFO_SIZE_REG = 0x40, | |
93 | RX_FIFO_SIZE_REG = 0x44, | |
94 | TX_FIFO_WORDS_REG = 0x48, | |
95 | RX_FIFO_WORDS_REG = 0x4c, | |
96 | INTERRUPT_EDGE_LEVEL_REG = 0x50, | |
97 | INTERRUPT_POLARITY_REG = 0x54, | |
98 | }; | |
99 | ||
c52c19c3 | 100 | /* bit definitions */ |
f26c569b FMH |
101 | |
102 | enum firmware_revision_bits { | |
103 | FEATURES_REG_PRESENT_BIT = 0x8000, | |
104 | }; | |
f26c569b FMH |
105 | |
106 | enum board_control_bits { | |
107 | BOARD_RESET_BIT = 0x1, /* wait 10usec before accessing fifos */ | |
108 | TX_FIFO_RESET_BIT = 0x2, | |
109 | RX_FIFO_RESET_BIT = 0x4, | |
110 | TX_ENABLE_BIT = 0x10, | |
111 | RX_ENABLE_BIT = 0x20, | |
95a2572f DH |
112 | DEMAND_DMA_DIRECTION_TX_BIT = 0x40, |
113 | /* for ch 0, ch 1 can only transmit (when present) */ | |
f26c569b FMH |
114 | LINE_VALID_ON_STATUS_VALID_BIT = 0x80, |
115 | START_TX_BIT = 0x10, | |
116 | CABLE_THROTTLE_ENABLE_BIT = 0x20, | |
117 | TEST_MODE_ENABLE_BIT = 0x80000000, | |
118 | }; | |
f26c569b FMH |
119 | |
120 | enum board_status_bits { | |
121 | COMMAND_LINE_STATUS_MASK = 0x7f, | |
122 | TX_IN_PROGRESS_BIT = 0x80, | |
123 | TX_NOT_EMPTY_BIT = 0x100, | |
124 | TX_NOT_ALMOST_EMPTY_BIT = 0x200, | |
125 | TX_NOT_ALMOST_FULL_BIT = 0x400, | |
126 | TX_NOT_FULL_BIT = 0x800, | |
127 | RX_NOT_EMPTY_BIT = 0x1000, | |
128 | RX_NOT_ALMOST_EMPTY_BIT = 0x2000, | |
129 | RX_NOT_ALMOST_FULL_BIT = 0x4000, | |
130 | RX_NOT_FULL_BIT = 0x8000, | |
131 | BOARD_JUMPER0_INSTALLED_BIT = 0x10000, | |
132 | BOARD_JUMPER1_INSTALLED_BIT = 0x20000, | |
133 | TX_OVERRUN_BIT = 0x200000, | |
134 | RX_UNDERRUN_BIT = 0x400000, | |
135 | RX_OVERRUN_BIT = 0x800000, | |
136 | }; | |
137 | ||
b776d05b | 138 | static uint32_t almost_full_bits(unsigned int num_words) |
f26c569b | 139 | { |
b776d05b | 140 | /* XXX need to add or subtract one? */ |
f26c569b FMH |
141 | return (num_words << 16) & 0xff0000; |
142 | } | |
143 | ||
b776d05b | 144 | static uint32_t almost_empty_bits(unsigned int num_words) |
f26c569b FMH |
145 | { |
146 | return num_words & 0xffff; | |
147 | } | |
0a85b6f0 | 148 | |
f26c569b FMH |
149 | enum features_bits { |
150 | FIFO_SIZE_PRESENT_BIT = 0x1, | |
151 | FIFO_WORDS_PRESENT_BIT = 0x2, | |
152 | LEVEL_EDGE_INTERRUPTS_PRESENT_BIT = 0x4, | |
153 | GPIO_SUPPORTED_BIT = 0x8, | |
154 | PLX_DMA_CH1_SUPPORTED_BIT = 0x10, | |
155 | OVERRUN_UNDERRUN_SUPPORTED_BIT = 0x20, | |
156 | }; | |
157 | ||
158 | enum interrupt_sources { | |
159 | FRAME_VALID_START_INTR = 0, | |
160 | FRAME_VALID_END_INTR = 1, | |
161 | TX_FIFO_EMPTY_INTR = 8, | |
162 | TX_FIFO_ALMOST_EMPTY_INTR = 9, | |
163 | TX_FIFO_ALMOST_FULL_INTR = 10, | |
164 | TX_FIFO_FULL_INTR = 11, | |
165 | RX_EMPTY_INTR = 12, | |
166 | RX_ALMOST_EMPTY_INTR = 13, | |
167 | RX_ALMOST_FULL_INTR = 14, | |
168 | RX_FULL_INTR = 15, | |
169 | }; | |
f26c569b | 170 | |
b776d05b | 171 | static uint32_t intr_bit(int interrupt_source) |
f26c569b FMH |
172 | { |
173 | return 0x1 << interrupt_source; | |
174 | } | |
175 | ||
b776d05b | 176 | static unsigned int fifo_size(uint32_t fifo_size_bits) |
f26c569b FMH |
177 | { |
178 | return fifo_size_bits & 0xfffff; | |
179 | } | |
180 | ||
52b3e348 | 181 | struct hpdi_board { |
21309dab | 182 | const char *name; /* board name */ |
c52c19c3 BP |
183 | int device_id; /* pci device id */ |
184 | int subdevice_id; /* pci subdevice id */ | |
52b3e348 BP |
185 | }; |
186 | ||
52b3e348 | 187 | static const struct hpdi_board hpdi_boards[] = { |
f26c569b | 188 | { |
0a85b6f0 MT |
189 | .name = "pci-hpdi32", |
190 | .device_id = PCI_DEVICE_ID_PLX_9080, | |
191 | .subdevice_id = 0x2400, | |
192 | }, | |
f26c569b FMH |
193 | #if 0 |
194 | { | |
0a85b6f0 MT |
195 | .name = "pxi-hpdi32", |
196 | .device_id = 0x9656, | |
197 | .subdevice_id = 0x2705, | |
198 | }, | |
f26c569b FMH |
199 | #endif |
200 | }; | |
201 | ||
352dec62 | 202 | struct hpdi_private { |
c52c19c3 | 203 | /* base addresses (ioremapped) */ |
ff450314 HS |
204 | void __iomem *plx9080_iobase; |
205 | void __iomem *hpdi_iobase; | |
c52c19c3 | 206 | uint32_t *dio_buffer[NUM_DMA_BUFFERS]; /* dma buffers */ |
4c67da06 MR |
207 | /* physical addresses of dma buffers */ |
208 | dma_addr_t dio_buffer_phys_addr[NUM_DMA_BUFFERS]; | |
209 | /* array of dma descriptors read by plx9080, allocated to get proper | |
210 | * alignment */ | |
211 | struct plx_dma_desc *dma_desc; | |
212 | /* physical address of dma descriptor array */ | |
213 | dma_addr_t dma_desc_phys_addr; | |
f26c569b | 214 | unsigned int num_dma_descriptors; |
4c67da06 MR |
215 | /* pointer to start of buffers indexed by descriptor */ |
216 | uint32_t *desc_dio_buffer[NUM_DMA_DESCRIPTORS]; | |
217 | /* index of the dma descriptor that is currently being used */ | |
218 | volatile unsigned int dma_desc_index; | |
f26c569b FMH |
219 | unsigned int tx_fifo_size; |
220 | unsigned int rx_fifo_size; | |
221 | volatile unsigned long dio_count; | |
4c67da06 MR |
222 | /* software copies of values written to hpdi registers */ |
223 | volatile uint32_t bits[24]; | |
224 | /* number of bytes at which to generate COMEDI_CB_BLOCK events */ | |
225 | volatile unsigned int block_size; | |
f26c569b | 226 | unsigned dio_config_output:1; |
352dec62 BP |
227 | }; |
228 | ||
0a85b6f0 MT |
229 | static int dio_config_insn(struct comedi_device *dev, |
230 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
231 | unsigned int *data) | |
f26c569b | 232 | { |
aa3d9473 HS |
233 | struct hpdi_private *devpriv = dev->private; |
234 | ||
f26c569b FMH |
235 | switch (data[0]) { |
236 | case INSN_CONFIG_DIO_OUTPUT: | |
aa3d9473 | 237 | devpriv->dio_config_output = 1; |
f26c569b FMH |
238 | return insn->n; |
239 | break; | |
240 | case INSN_CONFIG_DIO_INPUT: | |
aa3d9473 | 241 | devpriv->dio_config_output = 0; |
f26c569b FMH |
242 | return insn->n; |
243 | break; | |
244 | case INSN_CONFIG_DIO_QUERY: | |
245 | data[1] = | |
aa3d9473 | 246 | devpriv->dio_config_output ? COMEDI_OUTPUT : COMEDI_INPUT; |
f26c569b FMH |
247 | return insn->n; |
248 | break; | |
249 | case INSN_CONFIG_BLOCK_SIZE: | |
250 | return dio_config_block_size(dev, data); | |
251 | break; | |
252 | default: | |
253 | break; | |
254 | } | |
255 | ||
256 | return -EINVAL; | |
257 | } | |
258 | ||
da91b269 | 259 | static void disable_plx_interrupts(struct comedi_device *dev) |
f26c569b | 260 | { |
aa3d9473 HS |
261 | struct hpdi_private *devpriv = dev->private; |
262 | ||
263 | writel(0, devpriv->plx9080_iobase + PLX_INTRCS_REG); | |
f26c569b FMH |
264 | } |
265 | ||
c52c19c3 | 266 | /* initialize plx9080 chip */ |
da91b269 | 267 | static void init_plx9080(struct comedi_device *dev) |
f26c569b | 268 | { |
aa3d9473 | 269 | struct hpdi_private *devpriv = dev->private; |
f26c569b | 270 | uint32_t bits; |
aa3d9473 | 271 | void __iomem *plx_iobase = devpriv->plx9080_iobase; |
f26c569b | 272 | |
c52c19c3 | 273 | /* plx9080 dump */ |
f26c569b | 274 | DEBUG_PRINT(" plx interrupt status 0x%x\n", |
0a85b6f0 | 275 | readl(plx_iobase + PLX_INTRCS_REG)); |
f26c569b FMH |
276 | DEBUG_PRINT(" plx id bits 0x%x\n", readl(plx_iobase + PLX_ID_REG)); |
277 | DEBUG_PRINT(" plx control reg 0x%x\n", | |
aa3d9473 | 278 | readl(devpriv->plx9080_iobase + PLX_CONTROL_REG)); |
f26c569b FMH |
279 | |
280 | DEBUG_PRINT(" plx revision 0x%x\n", | |
0a85b6f0 | 281 | readl(plx_iobase + PLX_REVISION_REG)); |
f26c569b | 282 | DEBUG_PRINT(" plx dma channel 0 mode 0x%x\n", |
0a85b6f0 | 283 | readl(plx_iobase + PLX_DMA0_MODE_REG)); |
f26c569b | 284 | DEBUG_PRINT(" plx dma channel 1 mode 0x%x\n", |
0a85b6f0 | 285 | readl(plx_iobase + PLX_DMA1_MODE_REG)); |
f26c569b | 286 | DEBUG_PRINT(" plx dma channel 0 pci address 0x%x\n", |
0a85b6f0 | 287 | readl(plx_iobase + PLX_DMA0_PCI_ADDRESS_REG)); |
f26c569b | 288 | DEBUG_PRINT(" plx dma channel 0 local address 0x%x\n", |
0a85b6f0 | 289 | readl(plx_iobase + PLX_DMA0_LOCAL_ADDRESS_REG)); |
f26c569b | 290 | DEBUG_PRINT(" plx dma channel 0 transfer size 0x%x\n", |
0a85b6f0 | 291 | readl(plx_iobase + PLX_DMA0_TRANSFER_SIZE_REG)); |
f26c569b | 292 | DEBUG_PRINT(" plx dma channel 0 descriptor 0x%x\n", |
0a85b6f0 | 293 | readl(plx_iobase + PLX_DMA0_DESCRIPTOR_REG)); |
f26c569b | 294 | DEBUG_PRINT(" plx dma channel 0 command status 0x%x\n", |
0a85b6f0 | 295 | readb(plx_iobase + PLX_DMA0_CS_REG)); |
f26c569b | 296 | DEBUG_PRINT(" plx dma channel 0 threshold 0x%x\n", |
0a85b6f0 | 297 | readl(plx_iobase + PLX_DMA0_THRESHOLD_REG)); |
f26c569b FMH |
298 | DEBUG_PRINT(" plx bigend 0x%x\n", readl(plx_iobase + PLX_BIGEND_REG)); |
299 | #ifdef __BIG_ENDIAN | |
300 | bits = BIGEND_DMA0 | BIGEND_DMA1; | |
301 | #else | |
302 | bits = 0; | |
303 | #endif | |
aa3d9473 | 304 | writel(bits, devpriv->plx9080_iobase + PLX_BIGEND_REG); |
f26c569b FMH |
305 | |
306 | disable_plx_interrupts(dev); | |
307 | ||
308 | abort_dma(dev, 0); | |
309 | abort_dma(dev, 1); | |
310 | ||
c52c19c3 | 311 | /* configure dma0 mode */ |
f26c569b | 312 | bits = 0; |
c52c19c3 | 313 | /* enable ready input */ |
f26c569b | 314 | bits |= PLX_DMA_EN_READYIN_BIT; |
c52c19c3 | 315 | /* enable dma chaining */ |
f26c569b | 316 | bits |= PLX_EN_CHAIN_BIT; |
95a2572f DH |
317 | /* enable interrupt on dma done |
318 | * (probably don't need this, since chain never finishes) */ | |
f26c569b | 319 | bits |= PLX_EN_DMA_DONE_INTR_BIT; |
95a2572f DH |
320 | /* don't increment local address during transfers |
321 | * (we are transferring from a fixed fifo register) */ | |
f26c569b | 322 | bits |= PLX_LOCAL_ADDR_CONST_BIT; |
c52c19c3 | 323 | /* route dma interrupt to pci bus */ |
f26c569b | 324 | bits |= PLX_DMA_INTR_PCI_BIT; |
c52c19c3 | 325 | /* enable demand mode */ |
f26c569b | 326 | bits |= PLX_DEMAND_MODE_BIT; |
c52c19c3 | 327 | /* enable local burst mode */ |
f26c569b FMH |
328 | bits |= PLX_DMA_LOCAL_BURST_EN_BIT; |
329 | bits |= PLX_LOCAL_BUS_32_WIDE_BITS; | |
330 | writel(bits, plx_iobase + PLX_DMA0_MODE_REG); | |
331 | } | |
332 | ||
333 | /* Allocate and initialize the subdevice structures. | |
334 | */ | |
da91b269 | 335 | static int setup_subdevices(struct comedi_device *dev) |
f26c569b | 336 | { |
34c43922 | 337 | struct comedi_subdevice *s; |
8b6c5694 | 338 | int ret; |
f26c569b | 339 | |
8b6c5694 HS |
340 | ret = comedi_alloc_subdevices(dev, 1); |
341 | if (ret) | |
342 | return ret; | |
f26c569b | 343 | |
2de13f1b | 344 | s = &dev->subdevices[0]; |
f26c569b FMH |
345 | /* analog input subdevice */ |
346 | dev->read_subdev = s; | |
347 | /* dev->write_subdev = s; */ | |
348 | s->type = COMEDI_SUBD_DIO; | |
349 | s->subdev_flags = | |
0a85b6f0 | 350 | SDF_READABLE | SDF_WRITEABLE | SDF_LSAMPL | SDF_CMD_READ; |
f26c569b FMH |
351 | s->n_chan = 32; |
352 | s->len_chanlist = 32; | |
353 | s->maxdata = 1; | |
354 | s->range_table = &range_digital; | |
355 | s->insn_config = dio_config_insn; | |
356 | s->do_cmd = hpdi_cmd; | |
357 | s->do_cmdtest = hpdi_cmd_test; | |
358 | s->cancel = hpdi_cancel; | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
da91b269 | 363 | static int init_hpdi(struct comedi_device *dev) |
f26c569b | 364 | { |
aa3d9473 | 365 | struct hpdi_private *devpriv = dev->private; |
f26c569b FMH |
366 | uint32_t plx_intcsr_bits; |
367 | ||
aa3d9473 | 368 | writel(BOARD_RESET_BIT, devpriv->hpdi_iobase + BOARD_CONTROL_REG); |
5f74ea14 | 369 | udelay(10); |
f26c569b FMH |
370 | |
371 | writel(almost_empty_bits(32) | almost_full_bits(32), | |
aa3d9473 | 372 | devpriv->hpdi_iobase + RX_PROG_ALMOST_REG); |
f26c569b | 373 | writel(almost_empty_bits(32) | almost_full_bits(32), |
aa3d9473 | 374 | devpriv->hpdi_iobase + TX_PROG_ALMOST_REG); |
f26c569b | 375 | |
aa3d9473 | 376 | devpriv->tx_fifo_size = fifo_size(readl(devpriv->hpdi_iobase + |
0a85b6f0 | 377 | TX_FIFO_SIZE_REG)); |
aa3d9473 | 378 | devpriv->rx_fifo_size = fifo_size(readl(devpriv->hpdi_iobase + |
0a85b6f0 | 379 | RX_FIFO_SIZE_REG)); |
f26c569b | 380 | |
aa3d9473 | 381 | writel(0, devpriv->hpdi_iobase + INTERRUPT_CONTROL_REG); |
f26c569b | 382 | |
c52c19c3 | 383 | /* enable interrupts */ |
f26c569b | 384 | plx_intcsr_bits = |
0a85b6f0 MT |
385 | ICS_AERR | ICS_PERR | ICS_PIE | ICS_PLIE | ICS_PAIE | ICS_LIE | |
386 | ICS_DMA0_E; | |
aa3d9473 | 387 | writel(plx_intcsr_bits, devpriv->plx9080_iobase + PLX_INTRCS_REG); |
f26c569b FMH |
388 | |
389 | return 0; | |
390 | } | |
391 | ||
c52c19c3 | 392 | /* setup dma descriptors so a link completes every 'transfer_size' bytes */ |
da91b269 | 393 | static int setup_dma_descriptors(struct comedi_device *dev, |
0a85b6f0 | 394 | unsigned int transfer_size) |
f26c569b | 395 | { |
aa3d9473 | 396 | struct hpdi_private *devpriv = dev->private; |
f26c569b FMH |
397 | unsigned int buffer_index, buffer_offset; |
398 | uint32_t next_bits = PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT | | |
0a85b6f0 | 399 | PLX_XFER_LOCAL_TO_PCI; |
f26c569b FMH |
400 | unsigned int i; |
401 | ||
402 | if (transfer_size > DMA_BUFFER_SIZE) | |
403 | transfer_size = DMA_BUFFER_SIZE; | |
404 | transfer_size -= transfer_size % sizeof(uint32_t); | |
405 | if (transfer_size == 0) | |
406 | return -1; | |
407 | ||
408 | DEBUG_PRINT(" transfer_size %i\n", transfer_size); | |
409 | DEBUG_PRINT(" descriptors at 0x%lx\n", | |
aa3d9473 | 410 | (unsigned long)devpriv->dma_desc_phys_addr); |
f26c569b FMH |
411 | |
412 | buffer_offset = 0; | |
413 | buffer_index = 0; | |
414 | for (i = 0; i < NUM_DMA_DESCRIPTORS && | |
0a85b6f0 | 415 | buffer_index < NUM_DMA_BUFFERS; i++) { |
aa3d9473 HS |
416 | devpriv->dma_desc[i].pci_start_addr = |
417 | cpu_to_le32(devpriv->dio_buffer_phys_addr[buffer_index] + | |
0a85b6f0 | 418 | buffer_offset); |
aa3d9473 HS |
419 | devpriv->dma_desc[i].local_start_addr = cpu_to_le32(FIFO_REG); |
420 | devpriv->dma_desc[i].transfer_size = | |
0a85b6f0 | 421 | cpu_to_le32(transfer_size); |
aa3d9473 HS |
422 | devpriv->dma_desc[i].next = |
423 | cpu_to_le32((devpriv->dma_desc_phys_addr + (i + | |
0a85b6f0 | 424 | 1) * |
aa3d9473 | 425 | sizeof(devpriv->dma_desc[0])) | next_bits); |
f26c569b | 426 | |
aa3d9473 HS |
427 | devpriv->desc_dio_buffer[i] = |
428 | devpriv->dio_buffer[buffer_index] + | |
0a85b6f0 | 429 | (buffer_offset / sizeof(uint32_t)); |
f26c569b FMH |
430 | |
431 | buffer_offset += transfer_size; | |
432 | if (transfer_size + buffer_offset > DMA_BUFFER_SIZE) { | |
433 | buffer_offset = 0; | |
434 | buffer_index++; | |
435 | } | |
436 | ||
437 | DEBUG_PRINT(" desc %i\n", i); | |
438 | DEBUG_PRINT(" start addr virt 0x%p, phys 0x%lx\n", | |
aa3d9473 HS |
439 | devpriv->desc_dio_buffer[i], |
440 | (unsigned long)devpriv->dma_desc[i]. | |
0a85b6f0 | 441 | pci_start_addr); |
f26c569b | 442 | DEBUG_PRINT(" next 0x%lx\n", |
aa3d9473 | 443 | (unsigned long)devpriv->dma_desc[i].next); |
f26c569b | 444 | } |
aa3d9473 | 445 | devpriv->num_dma_descriptors = i; |
c52c19c3 | 446 | /* fix last descriptor to point back to first */ |
aa3d9473 HS |
447 | devpriv->dma_desc[i - 1].next = |
448 | cpu_to_le32(devpriv->dma_desc_phys_addr | next_bits); | |
f26c569b | 449 | DEBUG_PRINT(" desc %i next fixup 0x%lx\n", i - 1, |
aa3d9473 | 450 | (unsigned long)devpriv->dma_desc[i - 1].next); |
f26c569b | 451 | |
aa3d9473 | 452 | devpriv->block_size = transfer_size; |
f26c569b FMH |
453 | |
454 | return transfer_size; | |
455 | } | |
456 | ||
4e95df1f | 457 | static const struct hpdi_board *hpdi_find_board(struct pci_dev *pcidev) |
f26c569b | 458 | { |
4e95df1f IA |
459 | unsigned int i; |
460 | ||
461 | for (i = 0; i < ARRAY_SIZE(hpdi_boards); i++) | |
462 | if (pcidev->device == hpdi_boards[i].device_id && | |
463 | pcidev->subsystem_device == hpdi_boards[i].subdevice_id) | |
464 | return &hpdi_boards[i]; | |
465 | return NULL; | |
466 | } | |
467 | ||
a690b7e5 | 468 | static int hpdi_auto_attach(struct comedi_device *dev, |
4e95df1f IA |
469 | unsigned long context_unused) |
470 | { | |
471 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); | |
6526cd1a | 472 | const struct hpdi_board *thisboard; |
aa3d9473 | 473 | struct hpdi_private *devpriv; |
f26c569b FMH |
474 | int i; |
475 | int retval; | |
476 | ||
6526cd1a IA |
477 | thisboard = hpdi_find_board(pcidev); |
478 | if (!thisboard) { | |
4e95df1f IA |
479 | dev_err(dev->class_dev, "gsc_hpdi: pci %s not supported\n", |
480 | pci_name(pcidev)); | |
481 | return -EINVAL; | |
482 | } | |
6526cd1a IA |
483 | dev->board_ptr = thisboard; |
484 | dev->board_name = thisboard->name; | |
f26c569b | 485 | |
c34fa261 HS |
486 | devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); |
487 | if (!devpriv) | |
488 | return -ENOMEM; | |
489 | dev->private = devpriv; | |
f26c569b | 490 | |
818f569f HS |
491 | retval = comedi_pci_enable(dev); |
492 | if (retval) | |
493 | return retval; | |
f26c569b FMH |
494 | pci_set_master(pcidev); |
495 | ||
925fe5a6 HS |
496 | devpriv->plx9080_iobase = pci_ioremap_bar(pcidev, 0); |
497 | devpriv->hpdi_iobase = pci_ioremap_bar(pcidev, 2); | |
aa3d9473 | 498 | if (!devpriv->plx9080_iobase || !devpriv->hpdi_iobase) { |
37a836b1 | 499 | dev_warn(dev->class_dev, "failed to remap io memory\n"); |
f26c569b FMH |
500 | return -ENOMEM; |
501 | } | |
502 | ||
aa3d9473 HS |
503 | DEBUG_PRINT(" plx9080 remapped to 0x%p\n", devpriv->plx9080_iobase); |
504 | DEBUG_PRINT(" hpdi remapped to 0x%p\n", devpriv->hpdi_iobase); | |
f26c569b FMH |
505 | |
506 | init_plx9080(dev); | |
507 | ||
c52c19c3 | 508 | /* get irq */ |
5f74ea14 | 509 | if (request_irq(pcidev->irq, handle_interrupt, IRQF_SHARED, |
3dd5ca83 | 510 | dev->board_name, dev)) { |
37a836b1 YT |
511 | dev_warn(dev->class_dev, |
512 | "unable to allocate irq %u\n", pcidev->irq); | |
f26c569b FMH |
513 | return -EINVAL; |
514 | } | |
515 | dev->irq = pcidev->irq; | |
516 | ||
37a836b1 | 517 | dev_dbg(dev->class_dev, " irq %u\n", dev->irq); |
f26c569b | 518 | |
bc04bec0 | 519 | /* allocate pci dma buffers */ |
f26c569b | 520 | for (i = 0; i < NUM_DMA_BUFFERS; i++) { |
aa3d9473 | 521 | devpriv->dio_buffer[i] = |
fd67ad44 | 522 | pci_alloc_consistent(pcidev, DMA_BUFFER_SIZE, |
aa3d9473 | 523 | &devpriv->dio_buffer_phys_addr[i]); |
f26c569b | 524 | DEBUG_PRINT("dio_buffer at virt 0x%p, phys 0x%lx\n", |
aa3d9473 HS |
525 | devpriv->dio_buffer[i], |
526 | (unsigned long)devpriv->dio_buffer_phys_addr[i]); | |
f26c569b | 527 | } |
c52c19c3 | 528 | /* allocate dma descriptors */ |
fd67ad44 IA |
529 | devpriv->dma_desc = pci_alloc_consistent(pcidev, |
530 | sizeof(struct plx_dma_desc) * | |
531 | NUM_DMA_DESCRIPTORS, | |
532 | &devpriv->dma_desc_phys_addr); | |
aa3d9473 | 533 | if (devpriv->dma_desc_phys_addr & 0xf) { |
37a836b1 YT |
534 | dev_warn(dev->class_dev, |
535 | " dma descriptors not quad-word aligned (bug)\n"); | |
f26c569b FMH |
536 | return -EIO; |
537 | } | |
538 | ||
539 | retval = setup_dma_descriptors(dev, 0x1000); | |
540 | if (retval < 0) | |
541 | return retval; | |
542 | ||
543 | retval = setup_subdevices(dev); | |
544 | if (retval < 0) | |
545 | return retval; | |
546 | ||
547 | return init_hpdi(dev); | |
548 | } | |
549 | ||
484ecc95 | 550 | static void hpdi_detach(struct comedi_device *dev) |
f26c569b | 551 | { |
fd67ad44 | 552 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
aa3d9473 | 553 | struct hpdi_private *devpriv = dev->private; |
f26c569b FMH |
554 | unsigned int i; |
555 | ||
f26c569b | 556 | if (dev->irq) |
5f74ea14 | 557 | free_irq(dev->irq, dev); |
fd67ad44 | 558 | if (devpriv) { |
aa3d9473 | 559 | if (devpriv->plx9080_iobase) { |
95a2572f | 560 | disable_plx_interrupts(dev); |
aa3d9473 | 561 | iounmap(devpriv->plx9080_iobase); |
95a2572f | 562 | } |
aa3d9473 HS |
563 | if (devpriv->hpdi_iobase) |
564 | iounmap(devpriv->hpdi_iobase); | |
95a2572f DH |
565 | /* free pci dma buffers */ |
566 | for (i = 0; i < NUM_DMA_BUFFERS; i++) { | |
aa3d9473 | 567 | if (devpriv->dio_buffer[i]) |
fd67ad44 IA |
568 | pci_free_consistent(pcidev, |
569 | DMA_BUFFER_SIZE, | |
570 | devpriv->dio_buffer[i], | |
571 | devpriv-> | |
572 | dio_buffer_phys_addr[i]); | |
f26c569b | 573 | } |
95a2572f | 574 | /* free dma descriptors */ |
aa3d9473 | 575 | if (devpriv->dma_desc) |
fd67ad44 IA |
576 | pci_free_consistent(pcidev, |
577 | sizeof(struct plx_dma_desc) * | |
578 | NUM_DMA_DESCRIPTORS, | |
579 | devpriv->dma_desc, | |
580 | devpriv->dma_desc_phys_addr); | |
f26c569b | 581 | } |
7f072f54 | 582 | comedi_pci_disable(dev); |
f26c569b FMH |
583 | } |
584 | ||
da91b269 | 585 | static int dio_config_block_size(struct comedi_device *dev, unsigned int *data) |
f26c569b FMH |
586 | { |
587 | unsigned int requested_block_size; | |
588 | int retval; | |
589 | ||
590 | requested_block_size = data[1]; | |
591 | ||
592 | retval = setup_dma_descriptors(dev, requested_block_size); | |
593 | if (retval < 0) | |
594 | return retval; | |
595 | ||
596 | data[1] = retval; | |
597 | ||
598 | return 2; | |
599 | } | |
600 | ||
da91b269 | 601 | static int di_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 602 | struct comedi_cmd *cmd) |
f26c569b FMH |
603 | { |
604 | int err = 0; | |
f26c569b FMH |
605 | int i; |
606 | ||
27020ffe | 607 | /* Step 1 : check if triggers are trivially valid */ |
f26c569b | 608 | |
27020ffe HS |
609 | err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW); |
610 | err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); | |
611 | err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW); | |
612 | err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
613 | err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
f26c569b FMH |
614 | |
615 | if (err) | |
616 | return 1; | |
617 | ||
27020ffe HS |
618 | /* Step 2a : make sure trigger sources are unique */ |
619 | ||
620 | err |= cfc_check_trigger_is_unique(cmd->stop_src); | |
f26c569b | 621 | |
27020ffe | 622 | /* Step 2b : and mutually compatible */ |
f26c569b FMH |
623 | |
624 | if (err) | |
625 | return 2; | |
626 | ||
bf1b2022 | 627 | /* Step 3: check if arguments are trivially valid */ |
f26c569b FMH |
628 | |
629 | if (!cmd->chanlist_len) { | |
630 | cmd->chanlist_len = 32; | |
bf1b2022 | 631 | err |= -EINVAL; |
f26c569b | 632 | } |
bf1b2022 | 633 | err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); |
f26c569b FMH |
634 | |
635 | switch (cmd->stop_src) { | |
636 | case TRIG_COUNT: | |
bf1b2022 | 637 | err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1); |
f26c569b FMH |
638 | break; |
639 | case TRIG_NONE: | |
bf1b2022 | 640 | err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); |
f26c569b FMH |
641 | break; |
642 | default: | |
643 | break; | |
644 | } | |
645 | ||
646 | if (err) | |
647 | return 3; | |
648 | ||
649 | /* step 4: fix up any arguments */ | |
650 | ||
651 | if (err) | |
652 | return 4; | |
653 | ||
95a2572f DH |
654 | if (!cmd->chanlist) |
655 | return 0; | |
656 | ||
657 | for (i = 1; i < cmd->chanlist_len; i++) { | |
658 | if (CR_CHAN(cmd->chanlist[i]) != i) { | |
659 | /* XXX could support 8 or 16 channels */ | |
660 | comedi_error(dev, | |
661 | "chanlist must be ch 0 to 31 in order"); | |
662 | err++; | |
663 | break; | |
f26c569b FMH |
664 | } |
665 | } | |
666 | ||
667 | if (err) | |
668 | return 5; | |
669 | ||
670 | return 0; | |
671 | } | |
672 | ||
da91b269 | 673 | static int hpdi_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 674 | struct comedi_cmd *cmd) |
f26c569b | 675 | { |
aa3d9473 HS |
676 | struct hpdi_private *devpriv = dev->private; |
677 | ||
678 | if (devpriv->dio_config_output) | |
f26c569b | 679 | return -EINVAL; |
95a2572f | 680 | else |
f26c569b FMH |
681 | return di_cmd_test(dev, s, cmd); |
682 | } | |
683 | ||
da91b269 | 684 | static inline void hpdi_writel(struct comedi_device *dev, uint32_t bits, |
0a85b6f0 | 685 | unsigned int offset) |
f26c569b | 686 | { |
aa3d9473 HS |
687 | struct hpdi_private *devpriv = dev->private; |
688 | ||
689 | writel(bits | devpriv->bits[offset / sizeof(uint32_t)], | |
690 | devpriv->hpdi_iobase + offset); | |
f26c569b FMH |
691 | } |
692 | ||
da91b269 | 693 | static int di_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
f26c569b | 694 | { |
aa3d9473 | 695 | struct hpdi_private *devpriv = dev->private; |
f26c569b FMH |
696 | uint32_t bits; |
697 | unsigned long flags; | |
d163679c | 698 | struct comedi_async *async = s->async; |
ea6d0d4c | 699 | struct comedi_cmd *cmd = &async->cmd; |
f26c569b FMH |
700 | |
701 | hpdi_writel(dev, RX_FIFO_RESET_BIT, BOARD_CONTROL_REG); | |
702 | ||
703 | DEBUG_PRINT("hpdi: in di_cmd\n"); | |
704 | ||
705 | abort_dma(dev, 0); | |
706 | ||
aa3d9473 | 707 | devpriv->dma_desc_index = 0; |
f26c569b FMH |
708 | |
709 | /* These register are supposedly unused during chained dma, | |
710 | * but I have found that left over values from last operation | |
711 | * occasionally cause problems with transfer of first dma | |
712 | * block. Initializing them to zero seems to fix the problem. */ | |
aa3d9473 HS |
713 | writel(0, devpriv->plx9080_iobase + PLX_DMA0_TRANSFER_SIZE_REG); |
714 | writel(0, devpriv->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG); | |
715 | writel(0, devpriv->plx9080_iobase + PLX_DMA0_LOCAL_ADDRESS_REG); | |
c52c19c3 | 716 | /* give location of first dma descriptor */ |
0a85b6f0 | 717 | bits = |
aa3d9473 | 718 | devpriv->dma_desc_phys_addr | PLX_DESC_IN_PCI_BIT | |
0a85b6f0 | 719 | PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI; |
aa3d9473 | 720 | writel(bits, devpriv->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG); |
f26c569b | 721 | |
c52c19c3 | 722 | /* spinlock for plx dma control/status reg */ |
5f74ea14 | 723 | spin_lock_irqsave(&dev->spinlock, flags); |
c52c19c3 | 724 | /* enable dma transfer */ |
f26c569b | 725 | writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT | PLX_CLEAR_DMA_INTR_BIT, |
aa3d9473 | 726 | devpriv->plx9080_iobase + PLX_DMA0_CS_REG); |
5f74ea14 | 727 | spin_unlock_irqrestore(&dev->spinlock, flags); |
f26c569b FMH |
728 | |
729 | if (cmd->stop_src == TRIG_COUNT) | |
aa3d9473 | 730 | devpriv->dio_count = cmd->stop_arg; |
f26c569b | 731 | else |
aa3d9473 | 732 | devpriv->dio_count = 1; |
f26c569b | 733 | |
c52c19c3 | 734 | /* clear over/under run status flags */ |
f26c569b | 735 | writel(RX_UNDERRUN_BIT | RX_OVERRUN_BIT, |
aa3d9473 | 736 | devpriv->hpdi_iobase + BOARD_STATUS_REG); |
c52c19c3 | 737 | /* enable interrupts */ |
f26c569b | 738 | writel(intr_bit(RX_FULL_INTR), |
aa3d9473 | 739 | devpriv->hpdi_iobase + INTERRUPT_CONTROL_REG); |
f26c569b FMH |
740 | |
741 | DEBUG_PRINT("hpdi: starting rx\n"); | |
742 | hpdi_writel(dev, RX_ENABLE_BIT, BOARD_CONTROL_REG); | |
743 | ||
744 | return 0; | |
745 | } | |
746 | ||
da91b269 | 747 | static int hpdi_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
f26c569b | 748 | { |
aa3d9473 HS |
749 | struct hpdi_private *devpriv = dev->private; |
750 | ||
751 | if (devpriv->dio_config_output) | |
f26c569b | 752 | return -EINVAL; |
95a2572f | 753 | else |
f26c569b FMH |
754 | return di_cmd(dev, s); |
755 | } | |
756 | ||
da91b269 | 757 | static void drain_dma_buffers(struct comedi_device *dev, unsigned int channel) |
f26c569b | 758 | { |
aa3d9473 | 759 | struct hpdi_private *devpriv = dev->private; |
d163679c | 760 | struct comedi_async *async = dev->read_subdev->async; |
f26c569b FMH |
761 | uint32_t next_transfer_addr; |
762 | int j; | |
763 | int num_samples = 0; | |
ff450314 | 764 | void __iomem *pci_addr_reg; |
f26c569b FMH |
765 | |
766 | if (channel) | |
767 | pci_addr_reg = | |
aa3d9473 | 768 | devpriv->plx9080_iobase + PLX_DMA1_PCI_ADDRESS_REG; |
f26c569b FMH |
769 | else |
770 | pci_addr_reg = | |
aa3d9473 | 771 | devpriv->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG; |
f26c569b | 772 | |
c52c19c3 | 773 | /* loop until we have read all the full buffers */ |
f26c569b FMH |
774 | j = 0; |
775 | for (next_transfer_addr = readl(pci_addr_reg); | |
0a85b6f0 | 776 | (next_transfer_addr < |
aa3d9473 | 777 | le32_to_cpu(devpriv->dma_desc[devpriv->dma_desc_index]. |
0a85b6f0 MT |
778 | pci_start_addr) |
779 | || next_transfer_addr >= | |
aa3d9473 HS |
780 | le32_to_cpu(devpriv->dma_desc[devpriv->dma_desc_index]. |
781 | pci_start_addr) + devpriv->block_size) | |
782 | && j < devpriv->num_dma_descriptors; j++) { | |
c52c19c3 | 783 | /* transfer data from dma buffer to comedi buffer */ |
aa3d9473 | 784 | num_samples = devpriv->block_size / sizeof(uint32_t); |
f26c569b | 785 | if (async->cmd.stop_src == TRIG_COUNT) { |
aa3d9473 HS |
786 | if (num_samples > devpriv->dio_count) |
787 | num_samples = devpriv->dio_count; | |
788 | devpriv->dio_count -= num_samples; | |
f26c569b FMH |
789 | } |
790 | cfc_write_array_to_buffer(dev->read_subdev, | |
aa3d9473 | 791 | devpriv->desc_dio_buffer[devpriv-> |
0a85b6f0 MT |
792 | dma_desc_index], |
793 | num_samples * sizeof(uint32_t)); | |
aa3d9473 HS |
794 | devpriv->dma_desc_index++; |
795 | devpriv->dma_desc_index %= devpriv->num_dma_descriptors; | |
f26c569b FMH |
796 | |
797 | DEBUG_PRINT("next desc addr 0x%lx\n", (unsigned long) | |
aa3d9473 | 798 | devpriv->dma_desc[devpriv->dma_desc_index]. |
0a85b6f0 | 799 | next); |
f26c569b FMH |
800 | DEBUG_PRINT("pci addr reg 0x%x\n", next_transfer_addr); |
801 | } | |
c52c19c3 | 802 | /* XXX check for buffer overrun somehow */ |
f26c569b FMH |
803 | } |
804 | ||
70265d24 | 805 | static irqreturn_t handle_interrupt(int irq, void *d) |
f26c569b | 806 | { |
71b5f4f1 | 807 | struct comedi_device *dev = d; |
aa3d9473 | 808 | struct hpdi_private *devpriv = dev->private; |
34c43922 | 809 | struct comedi_subdevice *s = dev->read_subdev; |
d163679c | 810 | struct comedi_async *async = s->async; |
f26c569b FMH |
811 | uint32_t hpdi_intr_status, hpdi_board_status; |
812 | uint32_t plx_status; | |
813 | uint32_t plx_bits; | |
814 | uint8_t dma0_status, dma1_status; | |
815 | unsigned long flags; | |
816 | ||
95a2572f | 817 | if (!dev->attached) |
f26c569b | 818 | return IRQ_NONE; |
f26c569b | 819 | |
aa3d9473 | 820 | plx_status = readl(devpriv->plx9080_iobase + PLX_INTRCS_REG); |
95a2572f | 821 | if ((plx_status & (ICS_DMA0_A | ICS_DMA1_A | ICS_LIA)) == 0) |
f26c569b | 822 | return IRQ_NONE; |
f26c569b | 823 | |
aa3d9473 HS |
824 | hpdi_intr_status = readl(devpriv->hpdi_iobase + INTERRUPT_STATUS_REG); |
825 | hpdi_board_status = readl(devpriv->hpdi_iobase + BOARD_STATUS_REG); | |
f26c569b FMH |
826 | |
827 | async->events = 0; | |
828 | ||
829 | if (hpdi_intr_status) { | |
830 | DEBUG_PRINT("hpdi: intr status 0x%x, ", hpdi_intr_status); | |
831 | writel(hpdi_intr_status, | |
aa3d9473 | 832 | devpriv->hpdi_iobase + INTERRUPT_STATUS_REG); |
f26c569b | 833 | } |
25985edc | 834 | /* spin lock makes sure no one else changes plx dma control reg */ |
5f74ea14 | 835 | spin_lock_irqsave(&dev->spinlock, flags); |
aa3d9473 | 836 | dma0_status = readb(devpriv->plx9080_iobase + PLX_DMA0_CS_REG); |
c52c19c3 | 837 | if (plx_status & ICS_DMA0_A) { /* dma chan 0 interrupt */ |
f26c569b | 838 | writeb((dma0_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT, |
aa3d9473 | 839 | devpriv->plx9080_iobase + PLX_DMA0_CS_REG); |
f26c569b FMH |
840 | |
841 | DEBUG_PRINT("dma0 status 0x%x\n", dma0_status); | |
95a2572f | 842 | if (dma0_status & PLX_DMA_EN_BIT) |
f26c569b | 843 | drain_dma_buffers(dev, 0); |
f26c569b FMH |
844 | DEBUG_PRINT(" cleared dma ch0 interrupt\n"); |
845 | } | |
5f74ea14 | 846 | spin_unlock_irqrestore(&dev->spinlock, flags); |
f26c569b | 847 | |
25985edc | 848 | /* spin lock makes sure no one else changes plx dma control reg */ |
5f74ea14 | 849 | spin_lock_irqsave(&dev->spinlock, flags); |
aa3d9473 | 850 | dma1_status = readb(devpriv->plx9080_iobase + PLX_DMA1_CS_REG); |
0a85b6f0 | 851 | if (plx_status & ICS_DMA1_A) { /* XXX *//* dma chan 1 interrupt */ |
f26c569b | 852 | writeb((dma1_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT, |
aa3d9473 | 853 | devpriv->plx9080_iobase + PLX_DMA1_CS_REG); |
f26c569b FMH |
854 | DEBUG_PRINT("dma1 status 0x%x\n", dma1_status); |
855 | ||
856 | DEBUG_PRINT(" cleared dma ch1 interrupt\n"); | |
857 | } | |
5f74ea14 | 858 | spin_unlock_irqrestore(&dev->spinlock, flags); |
f26c569b | 859 | |
c52c19c3 BP |
860 | /* clear possible plx9080 interrupt sources */ |
861 | if (plx_status & ICS_LDIA) { /* clear local doorbell interrupt */ | |
aa3d9473 HS |
862 | plx_bits = readl(devpriv->plx9080_iobase + PLX_DBR_OUT_REG); |
863 | writel(plx_bits, devpriv->plx9080_iobase + PLX_DBR_OUT_REG); | |
f26c569b FMH |
864 | DEBUG_PRINT(" cleared local doorbell bits 0x%x\n", plx_bits); |
865 | } | |
866 | ||
867 | if (hpdi_board_status & RX_OVERRUN_BIT) { | |
868 | comedi_error(dev, "rx fifo overrun"); | |
869 | async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; | |
870 | DEBUG_PRINT("dma0_status 0x%x\n", | |
aa3d9473 | 871 | (int)readb(devpriv->plx9080_iobase + |
0a85b6f0 | 872 | PLX_DMA0_CS_REG)); |
f26c569b FMH |
873 | } |
874 | ||
875 | if (hpdi_board_status & RX_UNDERRUN_BIT) { | |
876 | comedi_error(dev, "rx fifo underrun"); | |
877 | async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; | |
878 | } | |
879 | ||
aa3d9473 | 880 | if (devpriv->dio_count == 0) |
f26c569b FMH |
881 | async->events |= COMEDI_CB_EOA; |
882 | ||
883 | DEBUG_PRINT("board status 0x%x, ", hpdi_board_status); | |
884 | DEBUG_PRINT("plx status 0x%x\n", plx_status); | |
885 | if (async->events) | |
886 | DEBUG_PRINT(" events 0x%x\n", async->events); | |
887 | ||
888 | cfc_handle_events(dev, s); | |
889 | ||
890 | return IRQ_HANDLED; | |
891 | } | |
892 | ||
64ca6a7e | 893 | static void abort_dma(struct comedi_device *dev, unsigned int channel) |
f26c569b | 894 | { |
aa3d9473 | 895 | struct hpdi_private *devpriv = dev->private; |
f26c569b FMH |
896 | unsigned long flags; |
897 | ||
c52c19c3 | 898 | /* spinlock for plx dma control/status reg */ |
5f74ea14 | 899 | spin_lock_irqsave(&dev->spinlock, flags); |
f26c569b | 900 | |
aa3d9473 | 901 | plx9080_abort_dma(devpriv->plx9080_iobase, channel); |
f26c569b | 902 | |
5f74ea14 | 903 | spin_unlock_irqrestore(&dev->spinlock, flags); |
f26c569b FMH |
904 | } |
905 | ||
da91b269 | 906 | static int hpdi_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
f26c569b | 907 | { |
aa3d9473 HS |
908 | struct hpdi_private *devpriv = dev->private; |
909 | ||
f26c569b FMH |
910 | hpdi_writel(dev, 0, BOARD_CONTROL_REG); |
911 | ||
aa3d9473 | 912 | writel(0, devpriv->hpdi_iobase + INTERRUPT_CONTROL_REG); |
f26c569b FMH |
913 | |
914 | abort_dma(dev, 0); | |
915 | ||
916 | return 0; | |
917 | } | |
90f703d3 | 918 | |
613e9121 HS |
919 | static struct comedi_driver gsc_hpdi_driver = { |
920 | .driver_name = "gsc_hpdi", | |
921 | .module = THIS_MODULE, | |
4e95df1f | 922 | .auto_attach = hpdi_auto_attach, |
613e9121 HS |
923 | .detach = hpdi_detach, |
924 | }; | |
925 | ||
a690b7e5 | 926 | static int gsc_hpdi_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 927 | const struct pci_device_id *id) |
613e9121 | 928 | { |
b8f4ac23 | 929 | return comedi_pci_auto_config(dev, &gsc_hpdi_driver, id->driver_data); |
613e9121 HS |
930 | } |
931 | ||
613e9121 HS |
932 | static DEFINE_PCI_DEVICE_TABLE(gsc_hpdi_pci_table) = { |
933 | { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9080, PCI_VENDOR_ID_PLX, | |
934 | 0x2400, 0, 0, 0}, | |
935 | { 0 } | |
936 | }; | |
937 | MODULE_DEVICE_TABLE(pci, gsc_hpdi_pci_table); | |
938 | ||
939 | static struct pci_driver gsc_hpdi_pci_driver = { | |
940 | .name = "gsc_hpdi", | |
941 | .id_table = gsc_hpdi_pci_table, | |
942 | .probe = gsc_hpdi_pci_probe, | |
9901a4d7 | 943 | .remove = comedi_pci_auto_unconfig, |
613e9121 HS |
944 | }; |
945 | module_comedi_pci_driver(gsc_hpdi_driver, gsc_hpdi_pci_driver); | |
946 | ||
90f703d3 AT |
947 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
948 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
949 | MODULE_LICENSE("GPL"); |