1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55#include <linux/module.h>
56#include <linux/delay.h>
57#include <linux/pci.h>
58
59#include "../comedidev.h"
60
61#define PCI_VENDOR_ID_ADVANTECH 0x13fe
62
63#define NUM_AO_CHANNELS 32
64
65
66enum board_registers {
67 DAC_CONTROL_REG = 0x0,
68 SYNC_OUTPUT_REG = 0x4,
69 EEPROM_CONTROL_REG = 0x8,
70 SYNC_OUTPUT_TRIGGER_REG = 0xc,
71 BOARD_ID_REG = 0x10
72};
73
74
75enum dac_control_contents {
76 DAC_DATA_MASK = 0x3fff,
77 DAC_DESTINATION_MASK = 0xc000,
78 DAC_NORMAL_MODE = 0xc000,
79 DAC_OFFSET_MODE = 0x8000,
80 DAC_GAIN_MODE = 0x4000,
81 DAC_CHANNEL_SELECT_MASK = 0xf0000,
82 DAC_GROUP_SELECT_MASK = 0xf00000
83};
84
85static uint32_t dac_data_bits(uint16_t dac_data)
86{
87 return dac_data & DAC_DATA_MASK;
88}
89
90static uint32_t dac_channel_select_bits(unsigned channel)
91{
92 return (channel << 16) & DAC_CHANNEL_SELECT_MASK;
93}
94
95static uint32_t dac_group_select_bits(unsigned group)
96{
97 return (1 << (20 + group)) & DAC_GROUP_SELECT_MASK;
98}
99
100static uint32_t dac_channel_and_group_select_bits(unsigned comedi_channel)
101{
102 return dac_channel_select_bits(comedi_channel % 8) |
103 dac_group_select_bits(comedi_channel / 8);
104}
105
106enum sync_output_contents {
107 SYNC_MODE = 0x1,
108 DAC_BUSY = 0x2,
109};
110
111enum sync_output_trigger_contents {
112 SYNC_TRIGGER_BITS = 0x0
113};
114
115enum board_id_contents {
116 BOARD_ID_MASK = 0xf
117};
118
119static const struct comedi_lrange ao_ranges_1724 = {
120 4, {
121 BIP_RANGE(10),
122 RANGE_mA(0, 20),
123 RANGE_mA(4, 20),
124 RANGE_unitless(0, 1)
125 }
126};
127
128
129struct adv_pci1724_private {
130 int ao_value[NUM_AO_CHANNELS];
131 int offset_value[NUM_AO_CHANNELS];
132 int gain_value[NUM_AO_CHANNELS];
133};
134
135static int wait_for_dac_idle(struct comedi_device *dev)
136{
137 static const int timeout = 10000;
138 int i;
139
140 for (i = 0; i < timeout; ++i) {
141 if ((inl(dev->iobase + SYNC_OUTPUT_REG) & DAC_BUSY) == 0)
142 break;
143 udelay(1);
144 }
145 if (i == timeout) {
146 dev_err(dev->class_dev,
147 "Timed out waiting for dac to become idle\n");
148 return -EIO;
149 }
150 return 0;
151}
152
153static int set_dac(struct comedi_device *dev, unsigned mode, unsigned channel,
154 unsigned data)
155{
156 int retval;
157 unsigned control_bits;
158
159 retval = wait_for_dac_idle(dev);
160 if (retval < 0)
161 return retval;
162
163 control_bits = mode;
164 control_bits |= dac_channel_and_group_select_bits(channel);
165 control_bits |= dac_data_bits(data);
166 outl(control_bits, dev->iobase + DAC_CONTROL_REG);
167 return 0;
168}
169
170static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
171 struct comedi_insn *insn, unsigned int *data)
172{
173 struct adv_pci1724_private *devpriv = dev->private;
174 int channel = CR_CHAN(insn->chanspec);
175 int retval;
176 int i;
177
178
179 outl(0, dev->iobase + SYNC_OUTPUT_REG);
180
181 for (i = 0; i < insn->n; ++i) {
182 retval = set_dac(dev, DAC_NORMAL_MODE, channel, data[i]);
183 if (retval < 0)
184 return retval;
185 devpriv->ao_value[channel] = data[i];
186 }
187 return insn->n;
188}
189
190static int ao_readback_insn(struct comedi_device *dev,
191 struct comedi_subdevice *s,
192 struct comedi_insn *insn, unsigned int *data)
193{
194 struct adv_pci1724_private *devpriv = dev->private;
195 int channel = CR_CHAN(insn->chanspec);
196 int i;
197
198 if (devpriv->ao_value[channel] < 0) {
199 dev_err(dev->class_dev,
200 "Cannot read back channels which have not yet been written to\n");
201 return -EIO;
202 }
203 for (i = 0; i < insn->n; i++)
204 data[i] = devpriv->ao_value[channel];
205
206 return insn->n;
207}
208
209static int offset_write_insn(struct comedi_device *dev,
210 struct comedi_subdevice *s,
211 struct comedi_insn *insn, unsigned int *data)
212{
213 struct adv_pci1724_private *devpriv = dev->private;
214 int channel = CR_CHAN(insn->chanspec);
215 int retval;
216 int i;
217
218
219 outl(0, dev->iobase + SYNC_OUTPUT_REG);
220
221 for (i = 0; i < insn->n; ++i) {
222 retval = set_dac(dev, DAC_OFFSET_MODE, channel, data[i]);
223 if (retval < 0)
224 return retval;
225 devpriv->offset_value[channel] = data[i];
226 }
227
228 return insn->n;
229}
230
231static int offset_read_insn(struct comedi_device *dev,
232 struct comedi_subdevice *s,
233 struct comedi_insn *insn, unsigned int *data)
234{
235 struct adv_pci1724_private *devpriv = dev->private;
236 unsigned int channel = CR_CHAN(insn->chanspec);
237 int i;
238
239 if (devpriv->offset_value[channel] < 0) {
240 dev_err(dev->class_dev,
241 "Cannot read back channels which have not yet been written to\n");
242 return -EIO;
243 }
244 for (i = 0; i < insn->n; i++)
245 data[i] = devpriv->offset_value[channel];
246
247 return insn->n;
248}
249
250static int gain_write_insn(struct comedi_device *dev,
251 struct comedi_subdevice *s,
252 struct comedi_insn *insn, unsigned int *data)
253{
254 struct adv_pci1724_private *devpriv = dev->private;
255 int channel = CR_CHAN(insn->chanspec);
256 int retval;
257 int i;
258
259
260 outl(0, dev->iobase + SYNC_OUTPUT_REG);
261
262 for (i = 0; i < insn->n; ++i) {
263 retval = set_dac(dev, DAC_GAIN_MODE, channel, data[i]);
264 if (retval < 0)
265 return retval;
266 devpriv->gain_value[channel] = data[i];
267 }
268
269 return insn->n;
270}
271
272static int gain_read_insn(struct comedi_device *dev,
273 struct comedi_subdevice *s, struct comedi_insn *insn,
274 unsigned int *data)
275{
276 struct adv_pci1724_private *devpriv = dev->private;
277 unsigned int channel = CR_CHAN(insn->chanspec);
278 int i;
279
280 if (devpriv->gain_value[channel] < 0) {
281 dev_err(dev->class_dev,
282 "Cannot read back channels which have not yet been written to\n");
283 return -EIO;
284 }
285 for (i = 0; i < insn->n; i++)
286 data[i] = devpriv->gain_value[channel];
287
288 return insn->n;
289}
290
291
292
293static int setup_subdevices(struct comedi_device *dev)
294{
295 struct comedi_subdevice *s;
296 int ret;
297
298 ret = comedi_alloc_subdevices(dev, 3);
299 if (ret)
300 return ret;
301
302
303 s = &dev->subdevices[0];
304 s->type = COMEDI_SUBD_AO;
305 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
306 s->n_chan = NUM_AO_CHANNELS;
307 s->maxdata = 0x3fff;
308 s->range_table = &ao_ranges_1724;
309 s->insn_read = ao_readback_insn;
310 s->insn_write = ao_winsn;
311
312
313 s = &dev->subdevices[1];
314 s->type = COMEDI_SUBD_CALIB;
315 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
316 s->n_chan = NUM_AO_CHANNELS;
317 s->insn_read = offset_read_insn;
318 s->insn_write = offset_write_insn;
319 s->maxdata = 0x3fff;
320
321
322 s = &dev->subdevices[2];
323 s->type = COMEDI_SUBD_CALIB;
324 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
325 s->n_chan = NUM_AO_CHANNELS;
326 s->insn_read = gain_read_insn;
327 s->insn_write = gain_write_insn;
328 s->maxdata = 0x3fff;
329
330 return 0;
331}
332
333static int adv_pci1724_auto_attach(struct comedi_device *dev,
334 unsigned long context_unused)
335{
336 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
337 struct adv_pci1724_private *devpriv;
338 int i;
339 int retval;
340 unsigned int board_id;
341
342 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
343 if (!devpriv)
344 return -ENOMEM;
345
346
347
348 for (i = 0; i < NUM_AO_CHANNELS; ++i) {
349 devpriv->ao_value[i] = -1;
350 devpriv->offset_value[i] = -1;
351 devpriv->gain_value[i] = -1;
352 }
353
354 retval = comedi_pci_enable(dev);
355 if (retval)
356 return retval;
357
358 dev->iobase = pci_resource_start(pcidev, 2);
359 board_id = inl(dev->iobase + BOARD_ID_REG) & BOARD_ID_MASK;
360 dev_info(dev->class_dev, "board id: %d\n", board_id);
361
362 retval = setup_subdevices(dev);
363 if (retval < 0)
364 return retval;
365
366 dev_info(dev->class_dev, "%s (pci %s) attached, board id: %u\n",
367 dev->board_name, pci_name(pcidev), board_id);
368 return 0;
369}
370
371static struct comedi_driver adv_pci1724_driver = {
372 .driver_name = "adv_pci1724",
373 .module = THIS_MODULE,
374 .auto_attach = adv_pci1724_auto_attach,
375 .detach = comedi_pci_disable,
376};
377
378static int adv_pci1724_pci_probe(struct pci_dev *dev,
379 const struct pci_device_id *id)
380{
381 return comedi_pci_auto_config(dev, &adv_pci1724_driver,
382 id->driver_data);
383}
384
385static const struct pci_device_id adv_pci1724_pci_table[] = {
386 { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1724) },
387 { 0 }
388};
389MODULE_DEVICE_TABLE(pci, adv_pci1724_pci_table);
390
391static struct pci_driver adv_pci1724_pci_driver = {
392 .name = "adv_pci1724",
393 .id_table = adv_pci1724_pci_table,
394 .probe = adv_pci1724_pci_probe,
395 .remove = comedi_pci_auto_unconfig,
396};
397
398module_comedi_pci_driver(adv_pci1724_driver, adv_pci1724_pci_driver);
399
400MODULE_AUTHOR("Frank Mori Hess <fmh6jj@gmail.com>");
401MODULE_DESCRIPTION("Advantech PCI-1724U Comedi driver");
402MODULE_LICENSE("GPL");
403