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 | ||
ce157f80 | 45 | #include <linux/module.h> |
33782dd5 HS |
46 | #include <linux/pci.h> |
47 | #include <linux/delay.h> | |
25436dc9 | 48 | #include <linux/interrupt.h> |
33782dd5 | 49 | |
f26c569b | 50 | #include "../comedidev.h" |
f26c569b | 51 | |
f26c569b FMH |
52 | #include "plx9080.h" |
53 | #include "comedi_fc.h" | |
54 | ||
c52c19c3 | 55 | #define TIMER_BASE 50 /* 20MHz master clock */ |
f26c569b FMH |
56 | #define DMA_BUFFER_SIZE 0x10000 |
57 | #define NUM_DMA_BUFFERS 4 | |
58 | #define NUM_DMA_DESCRIPTORS 256 | |
59 | ||
f26c569b FMH |
60 | enum hpdi_registers { |
61 | FIRMWARE_REV_REG = 0x0, | |
62 | BOARD_CONTROL_REG = 0x4, | |
63 | BOARD_STATUS_REG = 0x8, | |
64 | TX_PROG_ALMOST_REG = 0xc, | |
65 | RX_PROG_ALMOST_REG = 0x10, | |
66 | FEATURES_REG = 0x14, | |
67 | FIFO_REG = 0x18, | |
68 | TX_STATUS_COUNT_REG = 0x1c, | |
69 | TX_LINE_VALID_COUNT_REG = 0x20, | |
70 | TX_LINE_INVALID_COUNT_REG = 0x24, | |
71 | RX_STATUS_COUNT_REG = 0x28, | |
72 | RX_LINE_COUNT_REG = 0x2c, | |
73 | INTERRUPT_CONTROL_REG = 0x30, | |
74 | INTERRUPT_STATUS_REG = 0x34, | |
75 | TX_CLOCK_DIVIDER_REG = 0x38, | |
76 | TX_FIFO_SIZE_REG = 0x40, | |
77 | RX_FIFO_SIZE_REG = 0x44, | |
78 | TX_FIFO_WORDS_REG = 0x48, | |
79 | RX_FIFO_WORDS_REG = 0x4c, | |
80 | INTERRUPT_EDGE_LEVEL_REG = 0x50, | |
81 | INTERRUPT_POLARITY_REG = 0x54, | |
82 | }; | |
83 | ||
c52c19c3 | 84 | /* bit definitions */ |
f26c569b FMH |
85 | |
86 | enum firmware_revision_bits { | |
87 | FEATURES_REG_PRESENT_BIT = 0x8000, | |
88 | }; | |
f26c569b FMH |
89 | |
90 | enum board_control_bits { | |
91 | BOARD_RESET_BIT = 0x1, /* wait 10usec before accessing fifos */ | |
92 | TX_FIFO_RESET_BIT = 0x2, | |
93 | RX_FIFO_RESET_BIT = 0x4, | |
94 | TX_ENABLE_BIT = 0x10, | |
95 | RX_ENABLE_BIT = 0x20, | |
95a2572f DH |
96 | DEMAND_DMA_DIRECTION_TX_BIT = 0x40, |
97 | /* for ch 0, ch 1 can only transmit (when present) */ | |
f26c569b FMH |
98 | LINE_VALID_ON_STATUS_VALID_BIT = 0x80, |
99 | START_TX_BIT = 0x10, | |
100 | CABLE_THROTTLE_ENABLE_BIT = 0x20, | |
101 | TEST_MODE_ENABLE_BIT = 0x80000000, | |
102 | }; | |
f26c569b FMH |
103 | |
104 | enum board_status_bits { | |
105 | COMMAND_LINE_STATUS_MASK = 0x7f, | |
106 | TX_IN_PROGRESS_BIT = 0x80, | |
107 | TX_NOT_EMPTY_BIT = 0x100, | |
108 | TX_NOT_ALMOST_EMPTY_BIT = 0x200, | |
109 | TX_NOT_ALMOST_FULL_BIT = 0x400, | |
110 | TX_NOT_FULL_BIT = 0x800, | |
111 | RX_NOT_EMPTY_BIT = 0x1000, | |
112 | RX_NOT_ALMOST_EMPTY_BIT = 0x2000, | |
113 | RX_NOT_ALMOST_FULL_BIT = 0x4000, | |
114 | RX_NOT_FULL_BIT = 0x8000, | |
115 | BOARD_JUMPER0_INSTALLED_BIT = 0x10000, | |
116 | BOARD_JUMPER1_INSTALLED_BIT = 0x20000, | |
117 | TX_OVERRUN_BIT = 0x200000, | |
118 | RX_UNDERRUN_BIT = 0x400000, | |
119 | RX_OVERRUN_BIT = 0x800000, | |
120 | }; | |
121 | ||
b776d05b | 122 | static uint32_t almost_full_bits(unsigned int num_words) |
f26c569b | 123 | { |
b776d05b | 124 | /* XXX need to add or subtract one? */ |
f26c569b FMH |
125 | return (num_words << 16) & 0xff0000; |
126 | } | |
127 | ||
b776d05b | 128 | static uint32_t almost_empty_bits(unsigned int num_words) |
f26c569b FMH |
129 | { |
130 | return num_words & 0xffff; | |
131 | } | |
0a85b6f0 | 132 | |
f26c569b FMH |
133 | enum features_bits { |
134 | FIFO_SIZE_PRESENT_BIT = 0x1, | |
135 | FIFO_WORDS_PRESENT_BIT = 0x2, | |
136 | LEVEL_EDGE_INTERRUPTS_PRESENT_BIT = 0x4, | |
137 | GPIO_SUPPORTED_BIT = 0x8, | |
138 | PLX_DMA_CH1_SUPPORTED_BIT = 0x10, | |
139 | OVERRUN_UNDERRUN_SUPPORTED_BIT = 0x20, | |
140 | }; | |
141 | ||
142 | enum interrupt_sources { | |
143 | FRAME_VALID_START_INTR = 0, | |
144 | FRAME_VALID_END_INTR = 1, | |
145 | TX_FIFO_EMPTY_INTR = 8, | |
146 | TX_FIFO_ALMOST_EMPTY_INTR = 9, | |
147 | TX_FIFO_ALMOST_FULL_INTR = 10, | |
148 | TX_FIFO_FULL_INTR = 11, | |
149 | RX_EMPTY_INTR = 12, | |
150 | RX_ALMOST_EMPTY_INTR = 13, | |
151 | RX_ALMOST_FULL_INTR = 14, | |
152 | RX_FULL_INTR = 15, | |
153 | }; | |
f26c569b | 154 | |
b776d05b | 155 | static uint32_t intr_bit(int interrupt_source) |
f26c569b FMH |
156 | { |
157 | return 0x1 << interrupt_source; | |
158 | } | |
159 | ||
b776d05b | 160 | static unsigned int fifo_size(uint32_t fifo_size_bits) |
f26c569b FMH |
161 | { |
162 | return fifo_size_bits & 0xfffff; | |
163 | } | |
164 | ||
52b3e348 | 165 | struct hpdi_board { |
21309dab | 166 | const char *name; /* board name */ |
c52c19c3 BP |
167 | int device_id; /* pci device id */ |
168 | int subdevice_id; /* pci subdevice id */ | |
52b3e348 BP |
169 | }; |
170 | ||
52b3e348 | 171 | static const struct hpdi_board hpdi_boards[] = { |
f26c569b | 172 | { |
0a85b6f0 MT |
173 | .name = "pci-hpdi32", |
174 | .device_id = PCI_DEVICE_ID_PLX_9080, | |
175 | .subdevice_id = 0x2400, | |
176 | }, | |
f26c569b FMH |
177 | #if 0 |
178 | { | |
0a85b6f0 MT |
179 | .name = "pxi-hpdi32", |
180 | .device_id = 0x9656, | |
181 | .subdevice_id = 0x2705, | |
182 | }, | |
f26c569b FMH |
183 | #endif |
184 | }; | |
185 | ||
352dec62 | 186 | struct hpdi_private { |
c52c19c3 | 187 | /* base addresses (ioremapped) */ |
ff450314 HS |
188 | void __iomem *plx9080_iobase; |
189 | void __iomem *hpdi_iobase; | |
c52c19c3 | 190 | uint32_t *dio_buffer[NUM_DMA_BUFFERS]; /* dma buffers */ |
4c67da06 MR |
191 | /* physical addresses of dma buffers */ |
192 | dma_addr_t dio_buffer_phys_addr[NUM_DMA_BUFFERS]; | |
193 | /* array of dma descriptors read by plx9080, allocated to get proper | |
194 | * alignment */ | |
195 | struct plx_dma_desc *dma_desc; | |
196 | /* physical address of dma descriptor array */ | |
197 | dma_addr_t dma_desc_phys_addr; | |
f26c569b | 198 | unsigned int num_dma_descriptors; |
4c67da06 MR |
199 | /* pointer to start of buffers indexed by descriptor */ |
200 | uint32_t *desc_dio_buffer[NUM_DMA_DESCRIPTORS]; | |
201 | /* index of the dma descriptor that is currently being used */ | |
202 | volatile unsigned int dma_desc_index; | |
f26c569b FMH |
203 | unsigned int tx_fifo_size; |
204 | unsigned int rx_fifo_size; | |
205 | volatile unsigned long dio_count; | |
4c67da06 MR |
206 | /* software copies of values written to hpdi registers */ |
207 | volatile uint32_t bits[24]; | |
208 | /* number of bytes at which to generate COMEDI_CB_BLOCK events */ | |
209 | volatile unsigned int block_size; | |
352dec62 BP |
210 | }; |
211 | ||
da91b269 | 212 | static void disable_plx_interrupts(struct comedi_device *dev) |
f26c569b | 213 | { |
aa3d9473 HS |
214 | struct hpdi_private *devpriv = dev->private; |
215 | ||
216 | writel(0, devpriv->plx9080_iobase + PLX_INTRCS_REG); | |
f26c569b FMH |
217 | } |
218 | ||
da91b269 | 219 | static inline void hpdi_writel(struct comedi_device *dev, uint32_t bits, |
0a85b6f0 | 220 | unsigned int offset) |
f26c569b | 221 | { |
aa3d9473 HS |
222 | struct hpdi_private *devpriv = dev->private; |
223 | ||
224 | writel(bits | devpriv->bits[offset / sizeof(uint32_t)], | |
225 | devpriv->hpdi_iobase + offset); | |
f26c569b FMH |
226 | } |
227 | ||
9fe635cd | 228 | static void gsc_hpdi_drain_dma(struct comedi_device *dev, unsigned int channel) |
f26c569b | 229 | { |
aa3d9473 | 230 | struct hpdi_private *devpriv = dev->private; |
d163679c | 231 | struct comedi_async *async = dev->read_subdev->async; |
f26c569b FMH |
232 | uint32_t next_transfer_addr; |
233 | int j; | |
234 | int num_samples = 0; | |
ff450314 | 235 | void __iomem *pci_addr_reg; |
f26c569b FMH |
236 | |
237 | if (channel) | |
238 | pci_addr_reg = | |
aa3d9473 | 239 | devpriv->plx9080_iobase + PLX_DMA1_PCI_ADDRESS_REG; |
f26c569b FMH |
240 | else |
241 | pci_addr_reg = | |
aa3d9473 | 242 | devpriv->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG; |
f26c569b | 243 | |
c52c19c3 | 244 | /* loop until we have read all the full buffers */ |
f26c569b FMH |
245 | j = 0; |
246 | for (next_transfer_addr = readl(pci_addr_reg); | |
0a85b6f0 | 247 | (next_transfer_addr < |
aa3d9473 | 248 | le32_to_cpu(devpriv->dma_desc[devpriv->dma_desc_index]. |
0a85b6f0 MT |
249 | pci_start_addr) |
250 | || next_transfer_addr >= | |
aa3d9473 HS |
251 | le32_to_cpu(devpriv->dma_desc[devpriv->dma_desc_index]. |
252 | pci_start_addr) + devpriv->block_size) | |
253 | && j < devpriv->num_dma_descriptors; j++) { | |
c52c19c3 | 254 | /* transfer data from dma buffer to comedi buffer */ |
aa3d9473 | 255 | num_samples = devpriv->block_size / sizeof(uint32_t); |
f26c569b | 256 | if (async->cmd.stop_src == TRIG_COUNT) { |
aa3d9473 HS |
257 | if (num_samples > devpriv->dio_count) |
258 | num_samples = devpriv->dio_count; | |
259 | devpriv->dio_count -= num_samples; | |
f26c569b FMH |
260 | } |
261 | cfc_write_array_to_buffer(dev->read_subdev, | |
aa3d9473 | 262 | devpriv->desc_dio_buffer[devpriv-> |
0a85b6f0 MT |
263 | dma_desc_index], |
264 | num_samples * sizeof(uint32_t)); | |
aa3d9473 HS |
265 | devpriv->dma_desc_index++; |
266 | devpriv->dma_desc_index %= devpriv->num_dma_descriptors; | |
f26c569b | 267 | } |
c52c19c3 | 268 | /* XXX check for buffer overrun somehow */ |
f26c569b FMH |
269 | } |
270 | ||
3521d454 | 271 | static irqreturn_t gsc_hpdi_interrupt(int irq, void *d) |
f26c569b | 272 | { |
71b5f4f1 | 273 | struct comedi_device *dev = d; |
aa3d9473 | 274 | struct hpdi_private *devpriv = dev->private; |
34c43922 | 275 | struct comedi_subdevice *s = dev->read_subdev; |
d163679c | 276 | struct comedi_async *async = s->async; |
f26c569b FMH |
277 | uint32_t hpdi_intr_status, hpdi_board_status; |
278 | uint32_t plx_status; | |
279 | uint32_t plx_bits; | |
280 | uint8_t dma0_status, dma1_status; | |
281 | unsigned long flags; | |
282 | ||
95a2572f | 283 | if (!dev->attached) |
f26c569b | 284 | return IRQ_NONE; |
f26c569b | 285 | |
aa3d9473 | 286 | plx_status = readl(devpriv->plx9080_iobase + PLX_INTRCS_REG); |
95a2572f | 287 | if ((plx_status & (ICS_DMA0_A | ICS_DMA1_A | ICS_LIA)) == 0) |
f26c569b | 288 | return IRQ_NONE; |
f26c569b | 289 | |
aa3d9473 HS |
290 | hpdi_intr_status = readl(devpriv->hpdi_iobase + INTERRUPT_STATUS_REG); |
291 | hpdi_board_status = readl(devpriv->hpdi_iobase + BOARD_STATUS_REG); | |
f26c569b | 292 | |
f26c569b | 293 | if (hpdi_intr_status) { |
f26c569b | 294 | writel(hpdi_intr_status, |
aa3d9473 | 295 | devpriv->hpdi_iobase + INTERRUPT_STATUS_REG); |
f26c569b | 296 | } |
25985edc | 297 | /* spin lock makes sure no one else changes plx dma control reg */ |
5f74ea14 | 298 | spin_lock_irqsave(&dev->spinlock, flags); |
aa3d9473 | 299 | dma0_status = readb(devpriv->plx9080_iobase + PLX_DMA0_CS_REG); |
c52c19c3 | 300 | if (plx_status & ICS_DMA0_A) { /* dma chan 0 interrupt */ |
f26c569b | 301 | writeb((dma0_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT, |
aa3d9473 | 302 | devpriv->plx9080_iobase + PLX_DMA0_CS_REG); |
f26c569b | 303 | |
95a2572f | 304 | if (dma0_status & PLX_DMA_EN_BIT) |
9fe635cd | 305 | gsc_hpdi_drain_dma(dev, 0); |
f26c569b | 306 | } |
5f74ea14 | 307 | spin_unlock_irqrestore(&dev->spinlock, flags); |
f26c569b | 308 | |
25985edc | 309 | /* spin lock makes sure no one else changes plx dma control reg */ |
5f74ea14 | 310 | spin_lock_irqsave(&dev->spinlock, flags); |
aa3d9473 | 311 | dma1_status = readb(devpriv->plx9080_iobase + PLX_DMA1_CS_REG); |
0a85b6f0 | 312 | if (plx_status & ICS_DMA1_A) { /* XXX *//* dma chan 1 interrupt */ |
f26c569b | 313 | writeb((dma1_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT, |
aa3d9473 | 314 | devpriv->plx9080_iobase + PLX_DMA1_CS_REG); |
f26c569b | 315 | } |
5f74ea14 | 316 | spin_unlock_irqrestore(&dev->spinlock, flags); |
f26c569b | 317 | |
c52c19c3 BP |
318 | /* clear possible plx9080 interrupt sources */ |
319 | if (plx_status & ICS_LDIA) { /* clear local doorbell interrupt */ | |
aa3d9473 HS |
320 | plx_bits = readl(devpriv->plx9080_iobase + PLX_DBR_OUT_REG); |
321 | writel(plx_bits, devpriv->plx9080_iobase + PLX_DBR_OUT_REG); | |
f26c569b FMH |
322 | } |
323 | ||
324 | if (hpdi_board_status & RX_OVERRUN_BIT) { | |
325 | comedi_error(dev, "rx fifo overrun"); | |
326 | async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; | |
f26c569b FMH |
327 | } |
328 | ||
329 | if (hpdi_board_status & RX_UNDERRUN_BIT) { | |
330 | comedi_error(dev, "rx fifo underrun"); | |
331 | async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; | |
332 | } | |
333 | ||
aa3d9473 | 334 | if (devpriv->dio_count == 0) |
f26c569b FMH |
335 | async->events |= COMEDI_CB_EOA; |
336 | ||
f26c569b FMH |
337 | cfc_handle_events(dev, s); |
338 | ||
339 | return IRQ_HANDLED; | |
340 | } | |
341 | ||
9cbf6697 | 342 | static void gsc_hpdi_abort_dma(struct comedi_device *dev, unsigned int channel) |
f26c569b | 343 | { |
aa3d9473 | 344 | struct hpdi_private *devpriv = dev->private; |
f26c569b FMH |
345 | unsigned long flags; |
346 | ||
c52c19c3 | 347 | /* spinlock for plx dma control/status reg */ |
5f74ea14 | 348 | spin_lock_irqsave(&dev->spinlock, flags); |
f26c569b | 349 | |
aa3d9473 | 350 | plx9080_abort_dma(devpriv->plx9080_iobase, channel); |
f26c569b | 351 | |
5f74ea14 | 352 | spin_unlock_irqrestore(&dev->spinlock, flags); |
f26c569b FMH |
353 | } |
354 | ||
35474739 HS |
355 | static int gsc_hpdi_cancel(struct comedi_device *dev, |
356 | struct comedi_subdevice *s) | |
f26c569b | 357 | { |
aa3d9473 HS |
358 | struct hpdi_private *devpriv = dev->private; |
359 | ||
f26c569b FMH |
360 | hpdi_writel(dev, 0, BOARD_CONTROL_REG); |
361 | ||
aa3d9473 | 362 | writel(0, devpriv->hpdi_iobase + INTERRUPT_CONTROL_REG); |
f26c569b | 363 | |
9cbf6697 | 364 | gsc_hpdi_abort_dma(dev, 0); |
f26c569b FMH |
365 | |
366 | return 0; | |
367 | } | |
90f703d3 | 368 | |
627e52f7 HS |
369 | static int gsc_hpdi_cmd(struct comedi_device *dev, |
370 | struct comedi_subdevice *s) | |
371 | { | |
372 | struct hpdi_private *devpriv = dev->private; | |
373 | struct comedi_async *async = s->async; | |
374 | struct comedi_cmd *cmd = &async->cmd; | |
375 | unsigned long flags; | |
376 | uint32_t bits; | |
377 | ||
378 | if (s->io_bits) | |
379 | return -EINVAL; | |
380 | ||
381 | hpdi_writel(dev, RX_FIFO_RESET_BIT, BOARD_CONTROL_REG); | |
382 | ||
9cbf6697 | 383 | gsc_hpdi_abort_dma(dev, 0); |
627e52f7 HS |
384 | |
385 | devpriv->dma_desc_index = 0; | |
386 | ||
387 | /* | |
388 | * These register are supposedly unused during chained dma, | |
389 | * but I have found that left over values from last operation | |
390 | * occasionally cause problems with transfer of first dma | |
391 | * block. Initializing them to zero seems to fix the problem. | |
392 | */ | |
393 | writel(0, devpriv->plx9080_iobase + PLX_DMA0_TRANSFER_SIZE_REG); | |
394 | writel(0, devpriv->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG); | |
395 | writel(0, devpriv->plx9080_iobase + PLX_DMA0_LOCAL_ADDRESS_REG); | |
396 | ||
397 | /* give location of first dma descriptor */ | |
398 | bits = devpriv->dma_desc_phys_addr | PLX_DESC_IN_PCI_BIT | | |
399 | PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI; | |
400 | writel(bits, devpriv->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG); | |
401 | ||
402 | /* enable dma transfer */ | |
403 | spin_lock_irqsave(&dev->spinlock, flags); | |
404 | writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT | PLX_CLEAR_DMA_INTR_BIT, | |
405 | devpriv->plx9080_iobase + PLX_DMA0_CS_REG); | |
406 | spin_unlock_irqrestore(&dev->spinlock, flags); | |
407 | ||
408 | if (cmd->stop_src == TRIG_COUNT) | |
409 | devpriv->dio_count = cmd->stop_arg; | |
410 | else | |
411 | devpriv->dio_count = 1; | |
412 | ||
413 | /* clear over/under run status flags */ | |
414 | writel(RX_UNDERRUN_BIT | RX_OVERRUN_BIT, | |
415 | devpriv->hpdi_iobase + BOARD_STATUS_REG); | |
416 | ||
417 | /* enable interrupts */ | |
418 | writel(intr_bit(RX_FULL_INTR), | |
419 | devpriv->hpdi_iobase + INTERRUPT_CONTROL_REG); | |
420 | ||
421 | hpdi_writel(dev, RX_ENABLE_BIT, BOARD_CONTROL_REG); | |
422 | ||
423 | return 0; | |
424 | } | |
425 | ||
6b0cca01 HS |
426 | static int gsc_hpdi_cmd_test(struct comedi_device *dev, |
427 | struct comedi_subdevice *s, | |
428 | struct comedi_cmd *cmd) | |
429 | { | |
430 | int err = 0; | |
431 | int i; | |
432 | ||
433 | if (s->io_bits) | |
434 | return -EINVAL; | |
435 | ||
436 | /* Step 1 : check if triggers are trivially valid */ | |
437 | ||
438 | err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW); | |
439 | err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); | |
440 | err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW); | |
441 | err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
442 | err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
443 | ||
444 | if (err) | |
445 | return 1; | |
446 | ||
447 | /* Step 2a : make sure trigger sources are unique */ | |
448 | ||
449 | err |= cfc_check_trigger_is_unique(cmd->stop_src); | |
450 | ||
451 | /* Step 2b : and mutually compatible */ | |
452 | ||
453 | if (err) | |
454 | return 2; | |
455 | ||
456 | /* Step 3: check if arguments are trivially valid */ | |
457 | ||
458 | if (!cmd->chanlist_len || !cmd->chanlist) { | |
459 | cmd->chanlist_len = 32; | |
460 | err |= -EINVAL; | |
461 | } | |
462 | err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); | |
463 | ||
464 | if (cmd->stop_src == TRIG_COUNT) | |
465 | err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1); | |
466 | else /* TRIG_NONE */ | |
467 | err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); | |
468 | ||
469 | if (err) | |
470 | return 3; | |
471 | ||
472 | /* step 4: fix up any arguments */ | |
473 | ||
474 | if (err) | |
475 | return 4; | |
476 | ||
477 | /* step 5: complain about special chanlist considerations */ | |
478 | ||
479 | for (i = 0; i < cmd->chanlist_len; i++) { | |
480 | if (CR_CHAN(cmd->chanlist[i]) != i) { | |
481 | /* XXX could support 8 or 16 channels */ | |
482 | dev_err(dev->class_dev, | |
483 | "chanlist must be ch 0 to 31 in order"); | |
484 | err |= -EINVAL; | |
485 | break; | |
486 | } | |
487 | } | |
488 | ||
489 | if (err) | |
490 | return 5; | |
491 | ||
492 | return 0; | |
493 | ||
494 | } | |
495 | ||
66951e05 HS |
496 | /* setup dma descriptors so a link completes every 'len' bytes */ |
497 | static int gsc_hpdi_setup_dma_descriptors(struct comedi_device *dev, | |
498 | unsigned int len) | |
499 | { | |
500 | struct hpdi_private *devpriv = dev->private; | |
501 | dma_addr_t phys_addr = devpriv->dma_desc_phys_addr; | |
502 | uint32_t next_bits = PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT | | |
503 | PLX_XFER_LOCAL_TO_PCI; | |
504 | unsigned int offset = 0; | |
505 | unsigned int idx = 0; | |
506 | unsigned int i; | |
507 | ||
508 | if (len > DMA_BUFFER_SIZE) | |
509 | len = DMA_BUFFER_SIZE; | |
510 | len -= len % sizeof(uint32_t); | |
511 | if (len == 0) | |
512 | return -EINVAL; | |
513 | ||
514 | for (i = 0; i < NUM_DMA_DESCRIPTORS && idx < NUM_DMA_BUFFERS; i++) { | |
515 | devpriv->dma_desc[i].pci_start_addr = | |
516 | cpu_to_le32(devpriv->dio_buffer_phys_addr[idx] + offset); | |
517 | devpriv->dma_desc[i].local_start_addr = cpu_to_le32(FIFO_REG); | |
518 | devpriv->dma_desc[i].transfer_size = cpu_to_le32(len); | |
519 | devpriv->dma_desc[i].next = cpu_to_le32((phys_addr + | |
520 | (i + 1) * sizeof(devpriv->dma_desc[0])) | next_bits); | |
521 | ||
522 | devpriv->desc_dio_buffer[i] = devpriv->dio_buffer[idx] + | |
523 | (offset / sizeof(uint32_t)); | |
524 | ||
525 | offset += len; | |
526 | if (len + offset > DMA_BUFFER_SIZE) { | |
527 | offset = 0; | |
528 | idx++; | |
529 | } | |
530 | } | |
531 | devpriv->num_dma_descriptors = i; | |
532 | /* fix last descriptor to point back to first */ | |
533 | devpriv->dma_desc[i - 1].next = cpu_to_le32(phys_addr | next_bits); | |
534 | ||
535 | devpriv->block_size = len; | |
536 | ||
537 | return len; | |
538 | } | |
539 | ||
b0360a98 HS |
540 | static int gsc_hpdi_dio_insn_config(struct comedi_device *dev, |
541 | struct comedi_subdevice *s, | |
542 | struct comedi_insn *insn, | |
543 | unsigned int *data) | |
544 | { | |
545 | int ret; | |
546 | ||
547 | switch (data[0]) { | |
548 | case INSN_CONFIG_BLOCK_SIZE: | |
66951e05 | 549 | ret = gsc_hpdi_setup_dma_descriptors(dev, data[1]); |
b0360a98 HS |
550 | if (ret) |
551 | return ret; | |
552 | ||
553 | data[1] = ret; | |
554 | break; | |
555 | default: | |
556 | ret = comedi_dio_insn_config(dev, s, insn, data, 0xffffffff); | |
557 | if (ret) | |
558 | return ret; | |
559 | break; | |
560 | } | |
561 | ||
562 | return insn->n; | |
563 | } | |
564 | ||
d987d372 HS |
565 | static int init_hpdi(struct comedi_device *dev) |
566 | { | |
567 | struct hpdi_private *devpriv = dev->private; | |
568 | uint32_t plx_intcsr_bits; | |
569 | ||
570 | writel(BOARD_RESET_BIT, devpriv->hpdi_iobase + BOARD_CONTROL_REG); | |
571 | udelay(10); | |
572 | ||
573 | writel(almost_empty_bits(32) | almost_full_bits(32), | |
574 | devpriv->hpdi_iobase + RX_PROG_ALMOST_REG); | |
575 | writel(almost_empty_bits(32) | almost_full_bits(32), | |
576 | devpriv->hpdi_iobase + TX_PROG_ALMOST_REG); | |
577 | ||
578 | devpriv->tx_fifo_size = fifo_size(readl(devpriv->hpdi_iobase + | |
579 | TX_FIFO_SIZE_REG)); | |
580 | devpriv->rx_fifo_size = fifo_size(readl(devpriv->hpdi_iobase + | |
581 | RX_FIFO_SIZE_REG)); | |
582 | ||
583 | writel(0, devpriv->hpdi_iobase + INTERRUPT_CONTROL_REG); | |
584 | ||
585 | /* enable interrupts */ | |
586 | plx_intcsr_bits = | |
587 | ICS_AERR | ICS_PERR | ICS_PIE | ICS_PLIE | ICS_PAIE | ICS_LIE | | |
588 | ICS_DMA0_E; | |
589 | writel(plx_intcsr_bits, devpriv->plx9080_iobase + PLX_INTRCS_REG); | |
590 | ||
591 | return 0; | |
592 | } | |
593 | ||
594 | static void init_plx9080(struct comedi_device *dev) | |
595 | { | |
596 | struct hpdi_private *devpriv = dev->private; | |
597 | uint32_t bits; | |
598 | void __iomem *plx_iobase = devpriv->plx9080_iobase; | |
599 | ||
600 | #ifdef __BIG_ENDIAN | |
601 | bits = BIGEND_DMA0 | BIGEND_DMA1; | |
602 | #else | |
603 | bits = 0; | |
604 | #endif | |
605 | writel(bits, devpriv->plx9080_iobase + PLX_BIGEND_REG); | |
606 | ||
607 | disable_plx_interrupts(dev); | |
608 | ||
9cbf6697 HS |
609 | gsc_hpdi_abort_dma(dev, 0); |
610 | gsc_hpdi_abort_dma(dev, 1); | |
d987d372 HS |
611 | |
612 | /* configure dma0 mode */ | |
613 | bits = 0; | |
614 | /* enable ready input */ | |
615 | bits |= PLX_DMA_EN_READYIN_BIT; | |
616 | /* enable dma chaining */ | |
617 | bits |= PLX_EN_CHAIN_BIT; | |
618 | /* enable interrupt on dma done | |
619 | * (probably don't need this, since chain never finishes) */ | |
620 | bits |= PLX_EN_DMA_DONE_INTR_BIT; | |
621 | /* don't increment local address during transfers | |
622 | * (we are transferring from a fixed fifo register) */ | |
623 | bits |= PLX_LOCAL_ADDR_CONST_BIT; | |
624 | /* route dma interrupt to pci bus */ | |
625 | bits |= PLX_DMA_INTR_PCI_BIT; | |
626 | /* enable demand mode */ | |
627 | bits |= PLX_DEMAND_MODE_BIT; | |
628 | /* enable local burst mode */ | |
629 | bits |= PLX_DMA_LOCAL_BURST_EN_BIT; | |
630 | bits |= PLX_LOCAL_BUS_32_WIDE_BITS; | |
631 | writel(bits, plx_iobase + PLX_DMA0_MODE_REG); | |
632 | } | |
633 | ||
6a23558b | 634 | static const struct hpdi_board *gsc_hpdi_find_board(struct pci_dev *pcidev) |
d987d372 HS |
635 | { |
636 | unsigned int i; | |
637 | ||
638 | for (i = 0; i < ARRAY_SIZE(hpdi_boards); i++) | |
639 | if (pcidev->device == hpdi_boards[i].device_id && | |
640 | pcidev->subsystem_device == hpdi_boards[i].subdevice_id) | |
641 | return &hpdi_boards[i]; | |
642 | return NULL; | |
643 | } | |
644 | ||
1335cee5 HS |
645 | static int gsc_hpdi_auto_attach(struct comedi_device *dev, |
646 | unsigned long context_unused) | |
4ddc6ba4 HS |
647 | { |
648 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); | |
649 | const struct hpdi_board *thisboard; | |
650 | struct hpdi_private *devpriv; | |
7de0b0df | 651 | struct comedi_subdevice *s; |
4ddc6ba4 HS |
652 | int i; |
653 | int retval; | |
654 | ||
6a23558b | 655 | thisboard = gsc_hpdi_find_board(pcidev); |
4ddc6ba4 HS |
656 | if (!thisboard) { |
657 | dev_err(dev->class_dev, "gsc_hpdi: pci %s not supported\n", | |
658 | pci_name(pcidev)); | |
659 | return -EINVAL; | |
660 | } | |
661 | dev->board_ptr = thisboard; | |
662 | dev->board_name = thisboard->name; | |
663 | ||
664 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); | |
665 | if (!devpriv) | |
666 | return -ENOMEM; | |
667 | ||
668 | retval = comedi_pci_enable(dev); | |
669 | if (retval) | |
670 | return retval; | |
671 | pci_set_master(pcidev); | |
672 | ||
673 | devpriv->plx9080_iobase = pci_ioremap_bar(pcidev, 0); | |
674 | devpriv->hpdi_iobase = pci_ioremap_bar(pcidev, 2); | |
675 | if (!devpriv->plx9080_iobase || !devpriv->hpdi_iobase) { | |
676 | dev_warn(dev->class_dev, "failed to remap io memory\n"); | |
677 | return -ENOMEM; | |
678 | } | |
679 | ||
680 | init_plx9080(dev); | |
681 | ||
682 | /* get irq */ | |
3521d454 | 683 | if (request_irq(pcidev->irq, gsc_hpdi_interrupt, IRQF_SHARED, |
4ddc6ba4 HS |
684 | dev->board_name, dev)) { |
685 | dev_warn(dev->class_dev, | |
686 | "unable to allocate irq %u\n", pcidev->irq); | |
687 | return -EINVAL; | |
688 | } | |
689 | dev->irq = pcidev->irq; | |
690 | ||
691 | dev_dbg(dev->class_dev, " irq %u\n", dev->irq); | |
692 | ||
693 | /* allocate pci dma buffers */ | |
694 | for (i = 0; i < NUM_DMA_BUFFERS; i++) { | |
695 | devpriv->dio_buffer[i] = | |
696 | pci_alloc_consistent(pcidev, DMA_BUFFER_SIZE, | |
697 | &devpriv->dio_buffer_phys_addr[i]); | |
698 | } | |
699 | /* allocate dma descriptors */ | |
700 | devpriv->dma_desc = pci_alloc_consistent(pcidev, | |
701 | sizeof(struct plx_dma_desc) * | |
702 | NUM_DMA_DESCRIPTORS, | |
703 | &devpriv->dma_desc_phys_addr); | |
704 | if (devpriv->dma_desc_phys_addr & 0xf) { | |
705 | dev_warn(dev->class_dev, | |
706 | " dma descriptors not quad-word aligned (bug)\n"); | |
707 | return -EIO; | |
708 | } | |
709 | ||
66951e05 | 710 | retval = gsc_hpdi_setup_dma_descriptors(dev, 0x1000); |
4ddc6ba4 HS |
711 | if (retval < 0) |
712 | return retval; | |
713 | ||
7de0b0df HS |
714 | retval = comedi_alloc_subdevices(dev, 1); |
715 | if (retval) | |
4ddc6ba4 HS |
716 | return retval; |
717 | ||
7de0b0df HS |
718 | /* Digital I/O subdevice */ |
719 | s = &dev->subdevices[0]; | |
720 | dev->read_subdev = s; | |
721 | s->type = COMEDI_SUBD_DIO; | |
722 | s->subdev_flags = SDF_READABLE | SDF_WRITEABLE | SDF_LSAMPL | | |
723 | SDF_CMD_READ; | |
724 | s->n_chan = 32; | |
725 | s->len_chanlist = 32; | |
726 | s->maxdata = 1; | |
727 | s->range_table = &range_digital; | |
b0360a98 | 728 | s->insn_config = gsc_hpdi_dio_insn_config; |
627e52f7 | 729 | s->do_cmd = gsc_hpdi_cmd; |
6b0cca01 | 730 | s->do_cmdtest = gsc_hpdi_cmd_test; |
35474739 | 731 | s->cancel = gsc_hpdi_cancel; |
7de0b0df | 732 | |
4ddc6ba4 HS |
733 | return init_hpdi(dev); |
734 | } | |
735 | ||
1335cee5 | 736 | static void gsc_hpdi_detach(struct comedi_device *dev) |
4ddc6ba4 HS |
737 | { |
738 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); | |
739 | struct hpdi_private *devpriv = dev->private; | |
740 | unsigned int i; | |
741 | ||
742 | if (dev->irq) | |
743 | free_irq(dev->irq, dev); | |
744 | if (devpriv) { | |
745 | if (devpriv->plx9080_iobase) { | |
746 | disable_plx_interrupts(dev); | |
747 | iounmap(devpriv->plx9080_iobase); | |
748 | } | |
749 | if (devpriv->hpdi_iobase) | |
750 | iounmap(devpriv->hpdi_iobase); | |
751 | /* free pci dma buffers */ | |
752 | for (i = 0; i < NUM_DMA_BUFFERS; i++) { | |
753 | if (devpriv->dio_buffer[i]) | |
754 | pci_free_consistent(pcidev, | |
755 | DMA_BUFFER_SIZE, | |
756 | devpriv->dio_buffer[i], | |
757 | devpriv-> | |
758 | dio_buffer_phys_addr[i]); | |
759 | } | |
760 | /* free dma descriptors */ | |
761 | if (devpriv->dma_desc) | |
762 | pci_free_consistent(pcidev, | |
763 | sizeof(struct plx_dma_desc) * | |
764 | NUM_DMA_DESCRIPTORS, | |
765 | devpriv->dma_desc, | |
766 | devpriv->dma_desc_phys_addr); | |
767 | } | |
768 | comedi_pci_disable(dev); | |
769 | } | |
770 | ||
613e9121 HS |
771 | static struct comedi_driver gsc_hpdi_driver = { |
772 | .driver_name = "gsc_hpdi", | |
773 | .module = THIS_MODULE, | |
1335cee5 HS |
774 | .auto_attach = gsc_hpdi_auto_attach, |
775 | .detach = gsc_hpdi_detach, | |
613e9121 HS |
776 | }; |
777 | ||
a690b7e5 | 778 | static int gsc_hpdi_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 779 | const struct pci_device_id *id) |
613e9121 | 780 | { |
b8f4ac23 | 781 | return comedi_pci_auto_config(dev, &gsc_hpdi_driver, id->driver_data); |
613e9121 HS |
782 | } |
783 | ||
41e043fc | 784 | static const struct pci_device_id gsc_hpdi_pci_table[] = { |
613e9121 HS |
785 | { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9080, PCI_VENDOR_ID_PLX, |
786 | 0x2400, 0, 0, 0}, | |
787 | { 0 } | |
788 | }; | |
789 | MODULE_DEVICE_TABLE(pci, gsc_hpdi_pci_table); | |
790 | ||
791 | static struct pci_driver gsc_hpdi_pci_driver = { | |
792 | .name = "gsc_hpdi", | |
793 | .id_table = gsc_hpdi_pci_table, | |
794 | .probe = gsc_hpdi_pci_probe, | |
9901a4d7 | 795 | .remove = comedi_pci_auto_unconfig, |
613e9121 HS |
796 | }; |
797 | module_comedi_pci_driver(gsc_hpdi_driver, gsc_hpdi_pci_driver); | |
798 | ||
90f703d3 AT |
799 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
800 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
801 | MODULE_LICENSE("GPL"); |