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