Commit | Line | Data |
---|---|---|
6eb78a1b ML |
1 | /* |
2 | comedi/drivers/adl_pci8164.c | |
3 | ||
4 | Hardware comedi driver fot PCI-8164 Adlink card | |
5 | Copyright (C) 2004 Michel Lachine <mike@mikelachaine.ca> | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | ||
21 | */ | |
22 | /* | |
23 | Driver: adl_pci8164 | |
24 | Description: Driver for the Adlink PCI-8164 4 Axes Motion Control board | |
25 | Devices: [ADLink] PCI-8164 (adl_pci8164) | |
26 | Author: Michel Lachaine <mike@mikelachaine.ca> | |
27 | Status: experimental | |
28 | Updated: Mon, 14 Apr 2008 15:10:32 +0100 | |
29 | ||
30 | Configuration Options: | |
31 | [0] - PCI bus of device (optional) | |
32 | [1] - PCI slot of device (optional) | |
33 | If bus/slot is not specified, the first supported | |
34 | PCI device found will be used. | |
35 | */ | |
36 | ||
37 | #include "../comedidev.h" | |
bacf58a8 | 38 | #include <linux/kernel.h> |
6eb78a1b ML |
39 | #include <linux/delay.h> |
40 | #include "comedi_fc.h" | |
41 | #include "comedi_pci.h" | |
42 | #include "8253.h" | |
43 | ||
44 | #define PCI8164_AXIS_X 0x00 | |
45 | #define PCI8164_AXIS_Y 0x08 | |
46 | #define PCI8164_AXIS_Z 0x10 | |
47 | #define PCI8164_AXIS_U 0x18 | |
48 | ||
49 | #define PCI8164_MSTS 0x00 | |
50 | #define PCI8164_SSTS 0x02 | |
51 | #define PCI8164_BUF0 0x04 | |
52 | #define PCI8164_BUF1 0x06 | |
53 | ||
54 | #define PCI8164_CMD 0x00 | |
55 | #define PCI8164_OTP 0x02 | |
56 | ||
57 | #define PCI_DEVICE_ID_PCI8164 0x8164 | |
58 | ||
59 | static DEFINE_PCI_DEVICE_TABLE(adl_pci8164_pci_table) = { | |
0a85b6f0 MT |
60 | { |
61 | PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI8164, PCI_ANY_ID, | |
62 | PCI_ANY_ID, 0, 0, 0}, { | |
63 | 0} | |
6eb78a1b ML |
64 | }; |
65 | ||
66 | MODULE_DEVICE_TABLE(pci, adl_pci8164_pci_table); | |
67 | ||
07b666b7 | 68 | struct adl_pci8164_private { |
6eb78a1b ML |
69 | int data; |
70 | struct pci_dev *pci_dev; | |
07b666b7 | 71 | }; |
6eb78a1b | 72 | |
07b666b7 | 73 | #define devpriv ((struct adl_pci8164_private *)dev->private) |
6eb78a1b | 74 | |
0a85b6f0 MT |
75 | static int adl_pci8164_attach(struct comedi_device *dev, |
76 | struct comedi_devconfig *it); | |
da91b269 | 77 | static int adl_pci8164_detach(struct comedi_device *dev); |
139dfbdf | 78 | static struct comedi_driver driver_adl_pci8164 = { |
68c3dbff BP |
79 | .driver_name = "adl_pci8164", |
80 | .module = THIS_MODULE, | |
81 | .attach = adl_pci8164_attach, | |
82 | .detach = adl_pci8164_detach, | |
6eb78a1b ML |
83 | }; |
84 | ||
0a85b6f0 MT |
85 | static int adl_pci8164_insn_read_msts(struct comedi_device *dev, |
86 | struct comedi_subdevice *s, | |
87 | struct comedi_insn *insn, | |
88 | unsigned int *data); | |
6eb78a1b | 89 | |
0a85b6f0 MT |
90 | static int adl_pci8164_insn_read_ssts(struct comedi_device *dev, |
91 | struct comedi_subdevice *s, | |
92 | struct comedi_insn *insn, | |
93 | unsigned int *data); | |
6eb78a1b | 94 | |
0a85b6f0 MT |
95 | static int adl_pci8164_insn_read_buf0(struct comedi_device *dev, |
96 | struct comedi_subdevice *s, | |
97 | struct comedi_insn *insn, | |
98 | unsigned int *data); | |
6eb78a1b | 99 | |
0a85b6f0 MT |
100 | static int adl_pci8164_insn_read_buf1(struct comedi_device *dev, |
101 | struct comedi_subdevice *s, | |
102 | struct comedi_insn *insn, | |
103 | unsigned int *data); | |
6eb78a1b | 104 | |
0a85b6f0 MT |
105 | static int adl_pci8164_insn_write_cmd(struct comedi_device *dev, |
106 | struct comedi_subdevice *s, | |
107 | struct comedi_insn *insn, | |
108 | unsigned int *data); | |
6eb78a1b | 109 | |
0a85b6f0 MT |
110 | static int adl_pci8164_insn_write_otp(struct comedi_device *dev, |
111 | struct comedi_subdevice *s, | |
112 | struct comedi_insn *insn, | |
113 | unsigned int *data); | |
6eb78a1b | 114 | |
da91b269 | 115 | static int adl_pci8164_insn_write_buf0(struct comedi_device *dev, |
0a85b6f0 MT |
116 | struct comedi_subdevice *s, |
117 | struct comedi_insn *insn, | |
118 | unsigned int *data); | |
6eb78a1b | 119 | |
da91b269 | 120 | static int adl_pci8164_insn_write_buf1(struct comedi_device *dev, |
0a85b6f0 MT |
121 | struct comedi_subdevice *s, |
122 | struct comedi_insn *insn, | |
123 | unsigned int *data); | |
6eb78a1b | 124 | |
0a85b6f0 MT |
125 | static int adl_pci8164_attach(struct comedi_device *dev, |
126 | struct comedi_devconfig *it) | |
6eb78a1b ML |
127 | { |
128 | struct pci_dev *pcidev; | |
34c43922 | 129 | struct comedi_subdevice *s; |
6eb78a1b ML |
130 | int bus, slot; |
131 | ||
bacf58a8 BB |
132 | printk(KERN_INFO "comedi: attempt to attach...\n"); |
133 | printk(KERN_INFO "comedi%d: adl_pci8164\n", dev->minor); | |
6eb78a1b ML |
134 | |
135 | dev->board_name = "pci8164"; | |
136 | bus = it->options[0]; | |
137 | slot = it->options[1]; | |
138 | ||
07b666b7 | 139 | if (alloc_private(dev, sizeof(struct adl_pci8164_private)) < 0) |
6eb78a1b ML |
140 | return -ENOMEM; |
141 | ||
142 | if (alloc_subdevices(dev, 4) < 0) | |
143 | return -ENOMEM; | |
144 | ||
145 | for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); | |
0a85b6f0 MT |
146 | pcidev != NULL; |
147 | pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) { | |
6eb78a1b ML |
148 | |
149 | if (pcidev->vendor == PCI_VENDOR_ID_ADLINK && | |
0a85b6f0 | 150 | pcidev->device == PCI_DEVICE_ID_PCI8164) { |
6eb78a1b ML |
151 | if (bus || slot) { |
152 | /* requested particular bus/slot */ | |
153 | if (pcidev->bus->number != bus | |
bacf58a8 | 154 | || PCI_SLOT(pcidev->devfn) != slot) |
6eb78a1b | 155 | continue; |
6eb78a1b ML |
156 | } |
157 | devpriv->pci_dev = pcidev; | |
158 | if (comedi_pci_enable(pcidev, "adl_pci8164") < 0) { | |
bacf58a8 BB |
159 | printk(KERN_ERR "comedi%d: Failed to enable " |
160 | "PCI device and request regions\n", dev->minor); | |
6eb78a1b ML |
161 | return -EIO; |
162 | } | |
163 | dev->iobase = pci_resource_start(pcidev, 2); | |
bacf58a8 BB |
164 | printk(KERN_DEBUG "comedi: base addr %4lx\n", |
165 | dev->iobase); | |
6eb78a1b ML |
166 | |
167 | s = dev->subdevices + 0; | |
168 | s->type = COMEDI_SUBD_PROC; | |
169 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
170 | s->n_chan = 4; | |
171 | s->maxdata = 0xffff; | |
172 | s->len_chanlist = 4; | |
2696fb57 | 173 | /* s->range_table = &range_axis; */ |
6eb78a1b ML |
174 | s->insn_read = adl_pci8164_insn_read_msts; |
175 | s->insn_write = adl_pci8164_insn_write_cmd; | |
176 | ||
177 | s = dev->subdevices + 1; | |
178 | s->type = COMEDI_SUBD_PROC; | |
179 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
180 | s->n_chan = 4; | |
181 | s->maxdata = 0xffff; | |
182 | s->len_chanlist = 4; | |
2696fb57 | 183 | /* s->range_table = &range_axis; */ |
6eb78a1b ML |
184 | s->insn_read = adl_pci8164_insn_read_ssts; |
185 | s->insn_write = adl_pci8164_insn_write_otp; | |
186 | ||
187 | s = dev->subdevices + 2; | |
188 | s->type = COMEDI_SUBD_PROC; | |
189 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
190 | s->n_chan = 4; | |
191 | s->maxdata = 0xffff; | |
192 | s->len_chanlist = 4; | |
2696fb57 | 193 | /* s->range_table = &range_axis; */ |
6eb78a1b ML |
194 | s->insn_read = adl_pci8164_insn_read_buf0; |
195 | s->insn_write = adl_pci8164_insn_write_buf0; | |
196 | ||
197 | s = dev->subdevices + 3; | |
198 | s->type = COMEDI_SUBD_PROC; | |
199 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
200 | s->n_chan = 4; | |
201 | s->maxdata = 0xffff; | |
202 | s->len_chanlist = 4; | |
2696fb57 | 203 | /* s->range_table = &range_axis; */ |
6eb78a1b ML |
204 | s->insn_read = adl_pci8164_insn_read_buf1; |
205 | s->insn_write = adl_pci8164_insn_write_buf1; | |
206 | ||
bacf58a8 | 207 | printk(KERN_INFO "comedi: attached\n"); |
6eb78a1b ML |
208 | |
209 | return 1; | |
210 | } | |
211 | } | |
212 | ||
bacf58a8 BB |
213 | printk(KERN_ERR "comedi%d: no supported board found!" |
214 | "(req. bus/slot : %d/%d)\n", dev->minor, bus, slot); | |
6eb78a1b ML |
215 | return -EIO; |
216 | } | |
217 | ||
da91b269 | 218 | static int adl_pci8164_detach(struct comedi_device *dev) |
6eb78a1b | 219 | { |
bacf58a8 | 220 | printk(KERN_INFO "comedi%d: pci8164: remove\n", dev->minor); |
6eb78a1b ML |
221 | |
222 | if (devpriv && devpriv->pci_dev) { | |
bacf58a8 | 223 | if (dev->iobase) |
6eb78a1b | 224 | comedi_pci_disable(devpriv->pci_dev); |
6eb78a1b ML |
225 | pci_dev_put(devpriv->pci_dev); |
226 | } | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
ba7834b3 BP |
231 | /* |
232 | all the read commands are the same except for the addition a constant | |
233 | * const to the data for inw() | |
234 | */ | |
235 | static void adl_pci8164_insn_read(struct comedi_device *dev, | |
236 | struct comedi_subdevice *s, | |
237 | struct comedi_insn *insn, | |
238 | unsigned int *data, | |
0a85b6f0 | 239 | char *action, unsigned short offset) |
6eb78a1b ML |
240 | { |
241 | int axis, axis_reg; | |
242 | char *axisname; | |
243 | ||
244 | axis = CR_CHAN(insn->chanspec); | |
245 | ||
246 | switch (axis) { | |
247 | case 0: | |
248 | axis_reg = PCI8164_AXIS_X; | |
249 | axisname = "X"; | |
250 | break; | |
251 | case 1: | |
252 | axis_reg = PCI8164_AXIS_Y; | |
253 | axisname = "Y"; | |
254 | break; | |
255 | case 2: | |
256 | axis_reg = PCI8164_AXIS_Z; | |
257 | axisname = "Z"; | |
258 | break; | |
259 | case 3: | |
260 | axis_reg = PCI8164_AXIS_U; | |
261 | axisname = "U"; | |
262 | break; | |
263 | default: | |
264 | axis_reg = PCI8164_AXIS_X; | |
265 | axisname = "X"; | |
266 | } | |
267 | ||
ba7834b3 | 268 | data[0] = inw(dev->iobase + axis_reg + offset); |
bacf58a8 BB |
269 | printk(KERN_DEBUG "comedi: pci8164 %s read -> " |
270 | "%04X:%04X on axis %s\n", | |
271 | action, data[0], data[1], axisname); | |
ba7834b3 | 272 | } |
6eb78a1b | 273 | |
ba7834b3 BP |
274 | static int adl_pci8164_insn_read_msts(struct comedi_device *dev, |
275 | struct comedi_subdevice *s, | |
276 | struct comedi_insn *insn, | |
277 | unsigned int *data) | |
278 | { | |
279 | adl_pci8164_insn_read(dev, s, insn, data, "MSTS", PCI8164_MSTS); | |
6eb78a1b ML |
280 | return 2; |
281 | } | |
282 | ||
0a85b6f0 MT |
283 | static int adl_pci8164_insn_read_ssts(struct comedi_device *dev, |
284 | struct comedi_subdevice *s, | |
285 | struct comedi_insn *insn, | |
286 | unsigned int *data) | |
6eb78a1b | 287 | { |
ba7834b3 | 288 | adl_pci8164_insn_read(dev, s, insn, data, "SSTS", PCI8164_SSTS); |
6eb78a1b ML |
289 | return 2; |
290 | } | |
291 | ||
0a85b6f0 MT |
292 | static int adl_pci8164_insn_read_buf0(struct comedi_device *dev, |
293 | struct comedi_subdevice *s, | |
294 | struct comedi_insn *insn, | |
295 | unsigned int *data) | |
6eb78a1b | 296 | { |
ba7834b3 | 297 | adl_pci8164_insn_read(dev, s, insn, data, "BUF0", PCI8164_BUF0); |
6eb78a1b ML |
298 | return 2; |
299 | } | |
300 | ||
0a85b6f0 MT |
301 | static int adl_pci8164_insn_read_buf1(struct comedi_device *dev, |
302 | struct comedi_subdevice *s, | |
303 | struct comedi_insn *insn, | |
304 | unsigned int *data) | |
6eb78a1b | 305 | { |
ba7834b3 | 306 | adl_pci8164_insn_read(dev, s, insn, data, "BUF1", PCI8164_BUF1); |
6eb78a1b ML |
307 | return 2; |
308 | } | |
309 | ||
ba7834b3 BP |
310 | /* |
311 | all the write commands are the same except for the addition a constant | |
312 | * const to the data for outw() | |
313 | */ | |
314 | static void adl_pci8164_insn_out(struct comedi_device *dev, | |
0a85b6f0 MT |
315 | struct comedi_subdevice *s, |
316 | struct comedi_insn *insn, | |
317 | unsigned int *data, | |
318 | char *action, unsigned short offset) | |
6eb78a1b ML |
319 | { |
320 | unsigned int axis, axis_reg; | |
321 | ||
322 | char *axisname; | |
323 | ||
324 | axis = CR_CHAN(insn->chanspec); | |
325 | ||
326 | switch (axis) { | |
327 | case 0: | |
328 | axis_reg = PCI8164_AXIS_X; | |
329 | axisname = "X"; | |
330 | break; | |
331 | case 1: | |
332 | axis_reg = PCI8164_AXIS_Y; | |
333 | axisname = "Y"; | |
334 | break; | |
335 | case 2: | |
336 | axis_reg = PCI8164_AXIS_Z; | |
337 | axisname = "Z"; | |
338 | break; | |
339 | case 3: | |
340 | axis_reg = PCI8164_AXIS_U; | |
341 | axisname = "U"; | |
342 | break; | |
343 | default: | |
344 | axis_reg = PCI8164_AXIS_X; | |
345 | axisname = "X"; | |
346 | } | |
347 | ||
ba7834b3 | 348 | outw(data[0], dev->iobase + axis_reg + offset); |
6eb78a1b | 349 | |
bacf58a8 BB |
350 | printk(KERN_DEBUG "comedi: pci8164 %s write -> " |
351 | "%04X:%04X on axis %s\n", | |
352 | action, data[0], data[1], axisname); | |
ba7834b3 BP |
353 | |
354 | } | |
355 | ||
0a85b6f0 MT |
356 | static int adl_pci8164_insn_write_cmd(struct comedi_device *dev, |
357 | struct comedi_subdevice *s, | |
358 | struct comedi_insn *insn, | |
359 | unsigned int *data) | |
ba7834b3 BP |
360 | { |
361 | adl_pci8164_insn_out(dev, s, insn, data, "CMD", PCI8164_CMD); | |
6eb78a1b ML |
362 | return 2; |
363 | } | |
364 | ||
0a85b6f0 MT |
365 | static int adl_pci8164_insn_write_otp(struct comedi_device *dev, |
366 | struct comedi_subdevice *s, | |
367 | struct comedi_insn *insn, | |
368 | unsigned int *data) | |
6eb78a1b | 369 | { |
ba7834b3 | 370 | adl_pci8164_insn_out(dev, s, insn, data, "OTP", PCI8164_OTP); |
6eb78a1b ML |
371 | return 2; |
372 | } | |
373 | ||
da91b269 | 374 | static int adl_pci8164_insn_write_buf0(struct comedi_device *dev, |
0a85b6f0 MT |
375 | struct comedi_subdevice *s, |
376 | struct comedi_insn *insn, | |
377 | unsigned int *data) | |
6eb78a1b | 378 | { |
ba7834b3 | 379 | adl_pci8164_insn_out(dev, s, insn, data, "BUF0", PCI8164_BUF0); |
6eb78a1b ML |
380 | return 2; |
381 | } | |
382 | ||
da91b269 | 383 | static int adl_pci8164_insn_write_buf1(struct comedi_device *dev, |
0a85b6f0 MT |
384 | struct comedi_subdevice *s, |
385 | struct comedi_insn *insn, | |
386 | unsigned int *data) | |
6eb78a1b | 387 | { |
ba7834b3 | 388 | adl_pci8164_insn_out(dev, s, insn, data, "BUF1", PCI8164_BUF1); |
6eb78a1b ML |
389 | return 2; |
390 | } | |
391 | ||
727b286b AT |
392 | static int __devinit driver_adl_pci8164_pci_probe(struct pci_dev *dev, |
393 | const struct pci_device_id | |
394 | *ent) | |
395 | { | |
396 | return comedi_pci_auto_config(dev, driver_adl_pci8164.driver_name); | |
397 | } | |
398 | ||
399 | static void __devexit driver_adl_pci8164_pci_remove(struct pci_dev *dev) | |
400 | { | |
401 | comedi_pci_auto_unconfig(dev); | |
402 | } | |
403 | ||
404 | static struct pci_driver driver_adl_pci8164_pci_driver = { | |
405 | .id_table = adl_pci8164_pci_table, | |
406 | .probe = &driver_adl_pci8164_pci_probe, | |
407 | .remove = __devexit_p(&driver_adl_pci8164_pci_remove) | |
408 | }; | |
409 | ||
410 | static int __init driver_adl_pci8164_init_module(void) | |
411 | { | |
412 | int retval; | |
413 | ||
414 | retval = comedi_driver_register(&driver_adl_pci8164); | |
415 | if (retval < 0) | |
416 | return retval; | |
417 | ||
418 | driver_adl_pci8164_pci_driver.name = | |
419 | (char *)driver_adl_pci8164.driver_name; | |
420 | return pci_register_driver(&driver_adl_pci8164_pci_driver); | |
421 | } | |
422 | ||
423 | static void __exit driver_adl_pci8164_cleanup_module(void) | |
424 | { | |
425 | pci_unregister_driver(&driver_adl_pci8164_pci_driver); | |
426 | comedi_driver_unregister(&driver_adl_pci8164); | |
427 | } | |
428 | ||
429 | module_init(driver_adl_pci8164_init_module); | |
430 | module_exit(driver_adl_pci8164_cleanup_module); | |
90f703d3 AT |
431 | |
432 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
433 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
434 | MODULE_LICENSE("GPL"); |