aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRostislav Lisovy <lisovy@gmail.com>2014-01-09 17:46:46 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-01-13 18:03:12 -0500
commit04b565021a83ae1940f1f9b801d3ce5b5fc8ee1e (patch)
treec40d00e579187ddbd277cab05f58b8f35c25c7a1
parent197ba5f406cc29000c70de98eb40d7243b9f9f03 (diff)
comedi: Humusoft MF634 and MF624 DAQ cards driver
This patch adds Comedi driver for Humusoft MF634 (PCIe) and MF624 (PCI) data acquisition cards. The legacy card Humusoft MF614 is not supported. More info about the cards may be found at http://humusoft.cz/produkty/datacq/ The driver was tested with both cards. Everything seems to work properly. Just the basic functionality of the card (DIO, ADC, DAC) is supported by this driver. Signed-off-by: Rostislav Lisovy <lisovy@gmail.com> Reviewed-by: Ian Abbott <abbotti@mev.co.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/staging/comedi/Kconfig6
-rw-r--r--drivers/staging/comedi/comedidev.h1
-rw-r--r--drivers/staging/comedi/drivers/Makefile1
-rw-r--r--drivers/staging/comedi/drivers/mf6x4.c354
4 files changed, 362 insertions, 0 deletions
diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig
index bfa27e7fc016..89e25b4203ad 100644
--- a/drivers/staging/comedi/Kconfig
+++ b/drivers/staging/comedi/Kconfig
@@ -884,6 +884,12 @@ config COMEDI_GSC_HPDI
884 To compile this driver as a module, choose M here: the module will be 884 To compile this driver as a module, choose M here: the module will be
885 called gsc_hpdi. 885 called gsc_hpdi.
886 886
887config COMEDI_MF6X4
888 tristate "Humusoft MF634 and MF624 DAQ Card support"
889 ---help---
890 This driver supports both Humusoft MF634 and MF624 Data acquisition
891 cards. The legacy Humusoft MF614 card is not supported.
892
887config COMEDI_ICP_MULTI 893config COMEDI_ICP_MULTI
888 tristate "Inova ICP_MULTI support" 894 tristate "Inova ICP_MULTI support"
889 ---help--- 895 ---help---
diff --git a/drivers/staging/comedi/comedidev.h b/drivers/staging/comedi/comedidev.h
index f00b43c955f4..f82bd4256d51 100644
--- a/drivers/staging/comedi/comedidev.h
+++ b/drivers/staging/comedi/comedidev.h
@@ -407,6 +407,7 @@ void comedi_driver_unregister(struct comedi_driver *);
407#define PCI_VENDOR_ID_IOTECH 0x1616 407#define PCI_VENDOR_ID_IOTECH 0x1616
408#define PCI_VENDOR_ID_CONTEC 0x1221 408#define PCI_VENDOR_ID_CONTEC 0x1221
409#define PCI_VENDOR_ID_RTD 0x1435 409#define PCI_VENDOR_ID_RTD 0x1435
410#define PCI_VENDOR_ID_HUMUSOFT 0x186c
410 411
411struct pci_dev; 412struct pci_dev;
412struct pci_driver; 413struct pci_driver;
diff --git a/drivers/staging/comedi/drivers/Makefile b/drivers/staging/comedi/drivers/Makefile
index a16e674e2a2b..2706f583d8f0 100644
--- a/drivers/staging/comedi/drivers/Makefile
+++ b/drivers/staging/comedi/drivers/Makefile
@@ -111,6 +111,7 @@ obj-$(CONFIG_COMEDI_NI_PCIMIO) += ni_pcimio.o
111obj-$(CONFIG_COMEDI_RTD520) += rtd520.o 111obj-$(CONFIG_COMEDI_RTD520) += rtd520.o
112obj-$(CONFIG_COMEDI_S626) += s626.o 112obj-$(CONFIG_COMEDI_S626) += s626.o
113obj-$(CONFIG_COMEDI_SSV_DNP) += ssv_dnp.o 113obj-$(CONFIG_COMEDI_SSV_DNP) += ssv_dnp.o
114obj-$(CONFIG_COMEDI_MF6X4) += mf6x4.o
114 115
115# Comedi PCMCIA drivers 116# Comedi PCMCIA drivers
116obj-$(CONFIG_COMEDI_CB_DAS16_CS) += cb_das16_cs.o 117obj-$(CONFIG_COMEDI_CB_DAS16_CS) += cb_das16_cs.o
diff --git a/drivers/staging/comedi/drivers/mf6x4.c b/drivers/staging/comedi/drivers/mf6x4.c
new file mode 100644
index 000000000000..81b78e053f4e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/mf6x4.c
@@ -0,0 +1,354 @@
1/*
2 * comedi/drivers/mf6x4.c
3 * Driver for Humusoft MF634 and MF624 Data acquisition cards
4 *
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18/*
19 * Driver: mf6x4
20 * Description: Humusoft MF634 and MF624 Data acquisition card driver
21 * Devices: Humusoft MF634, Humusoft MF624
22 * Author: Rostislav Lisovy <lisovy@gmail.com>
23 * Status: works
24 * Updated:
25 * Configuration Options: none
26 */
27
28#include <linux/module.h>
29#include <linux/pci.h>
30#include <linux/delay.h>
31#include "../comedidev.h"
32
33/* Registers present in BAR0 memory region */
34#define MF624_GPIOC_R 0x54
35
36#define MF6X4_GPIOC_EOLC /* End Of Last Conversion */ (1 << 17)
37#define MF6X4_GPIOC_LDAC /* Load DACs */ (1 << 23)
38#define MF6X4_GPIOC_DACEN (1 << 26)
39
40/* BAR1 registers */
41#define MF6X4_DIN_R 0x10
42#define MF6X4_DIN_M 0xff
43#define MF6X4_DOUT_R 0x10
44#define MF6X4_DOUT_M 0xff
45
46#define MF6X4_ADSTART_R 0x20
47#define MF6X4_ADDATA_R 0x00
48#define MF6X4_ADCTRL_R 0x00
49#define MF6X4_ADCTRL_M 0xff
50
51#define MF6X4_DA0_R 0x20
52#define MF6X4_DA1_R 0x22
53#define MF6X4_DA2_R 0x24
54#define MF6X4_DA3_R 0x26
55#define MF6X4_DA4_R 0x28
56#define MF6X4_DA5_R 0x2a
57#define MF6X4_DA6_R 0x2c
58#define MF6X4_DA7_R 0x2e
59/* Map DAC cahnnel id to real HW-dependent offset value */
60#define MF6X4_DAC_R(x) (0x20 + ((x) * 2))
61#define MF6X4_DA_M 0x3fff
62
63/* BAR2 registers */
64#define MF634_GPIOC_R 0x68
65
66enum mf6x4_boardid {
67 BOARD_MF634,
68 BOARD_MF624,
69};
70
71struct mf6x4_board {
72 const char *name;
73 unsigned int bar_nums[3]; /* We need to keep track of the
74 order of BARs used by the cards */
75};
76
77static const struct mf6x4_board mf6x4_boards[] = {
78 [BOARD_MF634] = {
79 .name = "mf634",
80 .bar_nums = {0, 2, 3},
81 },
82 [BOARD_MF624] = {
83 .name = "mf624",
84 .bar_nums = {0, 2, 4},
85 },
86};
87
88struct mf6x4_private {
89 /*
90 * Documentation for both MF634 and MF624 describes registers
91 * present in BAR0, 1 and 2 regions.
92 * The real (i.e. in HW) BAR numbers are different for MF624
93 * and MF634 yet we will call them 0, 1, 2
94 */
95 void __iomem *bar0_mem;
96 void __iomem *bar1_mem;
97 void __iomem *bar2_mem;
98
99 /*
100 * This configuration register has the same function and fields
101 * for both cards however it lies in different BARs on different
102 * offsets -- this variable makes the access easier
103 */
104 void __iomem *gpioc_R;
105
106 /* DAC value cache -- used for insn_read function */
107 int ao_readback[8];
108};
109
110static int mf6x4_di_insn_bits(struct comedi_device *dev,
111 struct comedi_subdevice *s,
112 struct comedi_insn *insn, unsigned int *data)
113{
114 struct mf6x4_private *devpriv = dev->private;
115
116 data[1] = ioread16(devpriv->bar1_mem + MF6X4_DIN_R) & MF6X4_DIN_M;
117
118 return insn->n;
119}
120
121static int mf6x4_do_insn_bits(struct comedi_device *dev,
122 struct comedi_subdevice *s,
123 struct comedi_insn *insn, unsigned int *data)
124{
125 struct mf6x4_private *devpriv = dev->private;
126
127 if (comedi_dio_update_state(s, data))
128 iowrite16(s->state & MF6X4_DOUT_M,
129 devpriv->bar1_mem + MF6X4_DOUT_R);
130
131 data[1] = s->state;
132
133 return insn->n;
134}
135
136static int mf6x4_ai_wait_for_eoc(struct comedi_device *dev,
137 unsigned int timeout)
138{
139 struct mf6x4_private *devpriv = dev->private;
140 unsigned int eolc;
141
142 while (timeout--) {
143 eolc = ioread32(devpriv->gpioc_R) & MF6X4_GPIOC_EOLC;
144 if (eolc)
145 return 0;
146
147 udelay(1);
148 }
149
150 return -ETIME;
151}
152
153static int mf6x4_ai_insn_read(struct comedi_device *dev,
154 struct comedi_subdevice *s,
155 struct comedi_insn *insn, unsigned int *data)
156{
157 struct mf6x4_private *devpriv = dev->private;
158 int chan = CR_CHAN(insn->chanspec);
159 int ret;
160 int i;
161 int d;
162
163 /* Set the ADC channel number in the scan list */
164 iowrite16((1 << chan) & MF6X4_ADCTRL_M,
165 devpriv->bar1_mem + MF6X4_ADCTRL_R);
166
167 for (i = 0; i < insn->n; i++) {
168 /* Trigger ADC conversion by reading ADSTART */
169 ioread16(devpriv->bar1_mem + MF6X4_ADSTART_R);
170
171 ret = mf6x4_ai_wait_for_eoc(dev, 100);
172 if (ret)
173 return ret;
174
175 /* Read the actual value */
176 d = ioread16(devpriv->bar1_mem + MF6X4_ADDATA_R);
177 d &= s->maxdata;
178 data[i] = d;
179 }
180
181 iowrite16(0x0, devpriv->bar1_mem + MF6X4_ADCTRL_R);
182
183 return insn->n;
184}
185
186static int mf6x4_ao_insn_write(struct comedi_device *dev,
187 struct comedi_subdevice *s,
188 struct comedi_insn *insn, unsigned int *data)
189{
190 struct mf6x4_private *devpriv = dev->private;
191 unsigned int chan = CR_CHAN(insn->chanspec);
192 uint32_t gpioc;
193 int i;
194
195 /* Enable instantaneous update of converters outputs + Enable DACs */
196 gpioc = ioread32(devpriv->gpioc_R);
197 iowrite32((gpioc & ~MF6X4_GPIOC_LDAC) | MF6X4_GPIOC_DACEN,
198 devpriv->gpioc_R);
199
200 for (i = 0; i < insn->n; i++) {
201 iowrite16(data[i] & MF6X4_DA_M,
202 devpriv->bar1_mem + MF6X4_DAC_R(chan));
203
204 devpriv->ao_readback[chan] = data[i];
205 }
206
207 return insn->n;
208}
209
210static int mf6x4_ao_insn_read(struct comedi_device *dev,
211 struct comedi_subdevice *s,
212 struct comedi_insn *insn, unsigned int *data)
213{
214 struct mf6x4_private *devpriv = dev->private;
215 unsigned int chan = CR_CHAN(insn->chanspec);
216 int i;
217
218 for (i = 0; i < insn->n; i++)
219 data[i] = devpriv->ao_readback[chan];
220
221 return insn->n;
222}
223
224static int mf6x4_auto_attach(struct comedi_device *dev, unsigned long context)
225{
226 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
227 const struct mf6x4_board *board = NULL;
228 struct mf6x4_private *devpriv;
229 struct comedi_subdevice *s;
230 int ret;
231
232 if (context < ARRAY_SIZE(mf6x4_boards))
233 board = &mf6x4_boards[context];
234 else
235 return -ENODEV;
236
237 dev->board_ptr = board;
238 dev->board_name = board->name;
239
240 ret = comedi_pci_enable(dev);
241 if (ret)
242 return ret;
243
244 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
245 if (!devpriv)
246 return -ENOMEM;
247
248 devpriv->bar0_mem = pci_ioremap_bar(pcidev, board->bar_nums[0]);
249 if (!devpriv->bar0_mem)
250 return -ENODEV;
251
252 devpriv->bar1_mem = pci_ioremap_bar(pcidev, board->bar_nums[1]);
253 if (!devpriv->bar1_mem)
254 return -ENODEV;
255
256 devpriv->bar2_mem = pci_ioremap_bar(pcidev, board->bar_nums[2]);
257 if (!devpriv->bar2_mem)
258 return -ENODEV;
259
260 if (board == &mf6x4_boards[BOARD_MF634])
261 devpriv->gpioc_R = devpriv->bar2_mem + MF634_GPIOC_R;
262 else
263 devpriv->gpioc_R = devpriv->bar0_mem + MF624_GPIOC_R;
264
265
266 ret = comedi_alloc_subdevices(dev, 4);
267 if (ret)
268 return ret;
269
270 /* ADC */
271 s = &dev->subdevices[0];
272 s->type = COMEDI_SUBD_AI;
273 s->subdev_flags = SDF_READABLE | SDF_GROUND;
274 s->n_chan = 8;
275 s->maxdata = 0x3fff; /* 14 bits ADC */
276 s->range_table = &range_bipolar10;
277 s->insn_read = mf6x4_ai_insn_read;
278
279 /* DAC */
280 s = &dev->subdevices[1];
281 s->type = COMEDI_SUBD_AO;
282 s->subdev_flags = SDF_WRITABLE;
283 s->n_chan = 8;
284 s->maxdata = 0x3fff; /* 14 bits DAC */
285 s->range_table = &range_bipolar10;
286 s->insn_write = mf6x4_ao_insn_write;
287 s->insn_read = mf6x4_ao_insn_read;
288
289 /* DIN */
290 s = &dev->subdevices[2];
291 s->type = COMEDI_SUBD_DI;
292 s->subdev_flags = SDF_READABLE;
293 s->n_chan = 8;
294 s->maxdata = 1;
295 s->range_table = &range_digital;
296 s->insn_bits = mf6x4_di_insn_bits;
297
298 /* DOUT */
299 s = &dev->subdevices[3];
300 s->type = COMEDI_SUBD_DO;
301 s->subdev_flags = SDF_WRITABLE;
302 s->n_chan = 8;
303 s->maxdata = 1;
304 s->range_table = &range_digital;
305 s->insn_bits = mf6x4_do_insn_bits;
306
307 return 0;
308}
309
310static void mf6x4_detach(struct comedi_device *dev)
311{
312 struct mf6x4_private *devpriv = dev->private;
313
314 if (devpriv->bar0_mem)
315 iounmap(devpriv->bar0_mem);
316 if (devpriv->bar1_mem)
317 iounmap(devpriv->bar1_mem);
318 if (devpriv->bar2_mem)
319 iounmap(devpriv->bar2_mem);
320
321 comedi_pci_disable(dev);
322}
323
324static struct comedi_driver mf6x4_driver = {
325 .driver_name = "mf6x4",
326 .module = THIS_MODULE,
327 .auto_attach = mf6x4_auto_attach,
328 .detach = mf6x4_detach,
329};
330
331static int mf6x4_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
332{
333 return comedi_pci_auto_config(dev, &mf6x4_driver, id->driver_data);
334}
335
336static const struct pci_device_id mf6x4_pci_table[] = {
337 { PCI_VDEVICE(HUMUSOFT, 0x0634), BOARD_MF634 },
338 { PCI_VDEVICE(HUMUSOFT, 0x0624), BOARD_MF624 },
339 { 0 }
340};
341MODULE_DEVICE_TABLE(pci, mf6x4_pci_table);
342
343static struct pci_driver mf6x4_pci_driver = {
344 .name = "mf6x4",
345 .id_table = mf6x4_pci_table,
346 .probe = mf6x4_pci_probe,
347 .remove = comedi_pci_auto_unconfig,
348};
349
350module_comedi_pci_driver(mf6x4_driver, mf6x4_pci_driver);
351
352MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
353MODULE_DESCRIPTION("Comedi MF634 and MF624 DAQ cards driver");
354MODULE_LICENSE("GPL");