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 | 127 | { |
20fb2280 | 128 | struct pci_dev *pcidev = NULL; |
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 | ||
20fb2280 | 145 | for_each_pci_dev(pcidev) { |
6eb78a1b | 146 | if (pcidev->vendor == PCI_VENDOR_ID_ADLINK && |
0a85b6f0 | 147 | pcidev->device == PCI_DEVICE_ID_PCI8164) { |
6eb78a1b ML |
148 | if (bus || slot) { |
149 | /* requested particular bus/slot */ | |
150 | if (pcidev->bus->number != bus | |
bacf58a8 | 151 | || PCI_SLOT(pcidev->devfn) != slot) |
6eb78a1b | 152 | continue; |
6eb78a1b ML |
153 | } |
154 | devpriv->pci_dev = pcidev; | |
155 | if (comedi_pci_enable(pcidev, "adl_pci8164") < 0) { | |
bacf58a8 BB |
156 | printk(KERN_ERR "comedi%d: Failed to enable " |
157 | "PCI device and request regions\n", dev->minor); | |
6eb78a1b ML |
158 | return -EIO; |
159 | } | |
160 | dev->iobase = pci_resource_start(pcidev, 2); | |
bacf58a8 BB |
161 | printk(KERN_DEBUG "comedi: base addr %4lx\n", |
162 | dev->iobase); | |
6eb78a1b ML |
163 | |
164 | s = dev->subdevices + 0; | |
165 | s->type = COMEDI_SUBD_PROC; | |
166 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
167 | s->n_chan = 4; | |
168 | s->maxdata = 0xffff; | |
169 | s->len_chanlist = 4; | |
2696fb57 | 170 | /* s->range_table = &range_axis; */ |
6eb78a1b ML |
171 | s->insn_read = adl_pci8164_insn_read_msts; |
172 | s->insn_write = adl_pci8164_insn_write_cmd; | |
173 | ||
174 | s = dev->subdevices + 1; | |
175 | s->type = COMEDI_SUBD_PROC; | |
176 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
177 | s->n_chan = 4; | |
178 | s->maxdata = 0xffff; | |
179 | s->len_chanlist = 4; | |
2696fb57 | 180 | /* s->range_table = &range_axis; */ |
6eb78a1b ML |
181 | s->insn_read = adl_pci8164_insn_read_ssts; |
182 | s->insn_write = adl_pci8164_insn_write_otp; | |
183 | ||
184 | s = dev->subdevices + 2; | |
185 | s->type = COMEDI_SUBD_PROC; | |
186 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
187 | s->n_chan = 4; | |
188 | s->maxdata = 0xffff; | |
189 | s->len_chanlist = 4; | |
2696fb57 | 190 | /* s->range_table = &range_axis; */ |
6eb78a1b ML |
191 | s->insn_read = adl_pci8164_insn_read_buf0; |
192 | s->insn_write = adl_pci8164_insn_write_buf0; | |
193 | ||
194 | s = dev->subdevices + 3; | |
195 | s->type = COMEDI_SUBD_PROC; | |
196 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
197 | s->n_chan = 4; | |
198 | s->maxdata = 0xffff; | |
199 | s->len_chanlist = 4; | |
2696fb57 | 200 | /* s->range_table = &range_axis; */ |
6eb78a1b ML |
201 | s->insn_read = adl_pci8164_insn_read_buf1; |
202 | s->insn_write = adl_pci8164_insn_write_buf1; | |
203 | ||
bacf58a8 | 204 | printk(KERN_INFO "comedi: attached\n"); |
6eb78a1b ML |
205 | |
206 | return 1; | |
207 | } | |
208 | } | |
209 | ||
bacf58a8 BB |
210 | printk(KERN_ERR "comedi%d: no supported board found!" |
211 | "(req. bus/slot : %d/%d)\n", dev->minor, bus, slot); | |
6eb78a1b ML |
212 | return -EIO; |
213 | } | |
214 | ||
da91b269 | 215 | static int adl_pci8164_detach(struct comedi_device *dev) |
6eb78a1b | 216 | { |
bacf58a8 | 217 | printk(KERN_INFO "comedi%d: pci8164: remove\n", dev->minor); |
6eb78a1b ML |
218 | |
219 | if (devpriv && devpriv->pci_dev) { | |
bacf58a8 | 220 | if (dev->iobase) |
6eb78a1b | 221 | comedi_pci_disable(devpriv->pci_dev); |
6eb78a1b ML |
222 | pci_dev_put(devpriv->pci_dev); |
223 | } | |
224 | ||
225 | return 0; | |
226 | } | |
227 | ||
ba7834b3 BP |
228 | /* |
229 | all the read commands are the same except for the addition a constant | |
230 | * const to the data for inw() | |
231 | */ | |
232 | static void adl_pci8164_insn_read(struct comedi_device *dev, | |
233 | struct comedi_subdevice *s, | |
234 | struct comedi_insn *insn, | |
235 | unsigned int *data, | |
0a85b6f0 | 236 | char *action, unsigned short offset) |
6eb78a1b ML |
237 | { |
238 | int axis, axis_reg; | |
239 | char *axisname; | |
240 | ||
241 | axis = CR_CHAN(insn->chanspec); | |
242 | ||
243 | switch (axis) { | |
244 | case 0: | |
245 | axis_reg = PCI8164_AXIS_X; | |
246 | axisname = "X"; | |
247 | break; | |
248 | case 1: | |
249 | axis_reg = PCI8164_AXIS_Y; | |
250 | axisname = "Y"; | |
251 | break; | |
252 | case 2: | |
253 | axis_reg = PCI8164_AXIS_Z; | |
254 | axisname = "Z"; | |
255 | break; | |
256 | case 3: | |
257 | axis_reg = PCI8164_AXIS_U; | |
258 | axisname = "U"; | |
259 | break; | |
260 | default: | |
261 | axis_reg = PCI8164_AXIS_X; | |
262 | axisname = "X"; | |
263 | } | |
264 | ||
ba7834b3 | 265 | data[0] = inw(dev->iobase + axis_reg + offset); |
bacf58a8 BB |
266 | printk(KERN_DEBUG "comedi: pci8164 %s read -> " |
267 | "%04X:%04X on axis %s\n", | |
268 | action, data[0], data[1], axisname); | |
ba7834b3 | 269 | } |
6eb78a1b | 270 | |
ba7834b3 BP |
271 | static int adl_pci8164_insn_read_msts(struct comedi_device *dev, |
272 | struct comedi_subdevice *s, | |
273 | struct comedi_insn *insn, | |
274 | unsigned int *data) | |
275 | { | |
276 | adl_pci8164_insn_read(dev, s, insn, data, "MSTS", PCI8164_MSTS); | |
6eb78a1b ML |
277 | return 2; |
278 | } | |
279 | ||
0a85b6f0 MT |
280 | static int adl_pci8164_insn_read_ssts(struct comedi_device *dev, |
281 | struct comedi_subdevice *s, | |
282 | struct comedi_insn *insn, | |
283 | unsigned int *data) | |
6eb78a1b | 284 | { |
ba7834b3 | 285 | adl_pci8164_insn_read(dev, s, insn, data, "SSTS", PCI8164_SSTS); |
6eb78a1b ML |
286 | return 2; |
287 | } | |
288 | ||
0a85b6f0 MT |
289 | static int adl_pci8164_insn_read_buf0(struct comedi_device *dev, |
290 | struct comedi_subdevice *s, | |
291 | struct comedi_insn *insn, | |
292 | unsigned int *data) | |
6eb78a1b | 293 | { |
ba7834b3 | 294 | adl_pci8164_insn_read(dev, s, insn, data, "BUF0", PCI8164_BUF0); |
6eb78a1b ML |
295 | return 2; |
296 | } | |
297 | ||
0a85b6f0 MT |
298 | static int adl_pci8164_insn_read_buf1(struct comedi_device *dev, |
299 | struct comedi_subdevice *s, | |
300 | struct comedi_insn *insn, | |
301 | unsigned int *data) | |
6eb78a1b | 302 | { |
ba7834b3 | 303 | adl_pci8164_insn_read(dev, s, insn, data, "BUF1", PCI8164_BUF1); |
6eb78a1b ML |
304 | return 2; |
305 | } | |
306 | ||
ba7834b3 BP |
307 | /* |
308 | all the write commands are the same except for the addition a constant | |
309 | * const to the data for outw() | |
310 | */ | |
311 | static void adl_pci8164_insn_out(struct comedi_device *dev, | |
0a85b6f0 MT |
312 | struct comedi_subdevice *s, |
313 | struct comedi_insn *insn, | |
314 | unsigned int *data, | |
315 | char *action, unsigned short offset) | |
6eb78a1b ML |
316 | { |
317 | unsigned int axis, axis_reg; | |
318 | ||
319 | char *axisname; | |
320 | ||
321 | axis = CR_CHAN(insn->chanspec); | |
322 | ||
323 | switch (axis) { | |
324 | case 0: | |
325 | axis_reg = PCI8164_AXIS_X; | |
326 | axisname = "X"; | |
327 | break; | |
328 | case 1: | |
329 | axis_reg = PCI8164_AXIS_Y; | |
330 | axisname = "Y"; | |
331 | break; | |
332 | case 2: | |
333 | axis_reg = PCI8164_AXIS_Z; | |
334 | axisname = "Z"; | |
335 | break; | |
336 | case 3: | |
337 | axis_reg = PCI8164_AXIS_U; | |
338 | axisname = "U"; | |
339 | break; | |
340 | default: | |
341 | axis_reg = PCI8164_AXIS_X; | |
342 | axisname = "X"; | |
343 | } | |
344 | ||
ba7834b3 | 345 | outw(data[0], dev->iobase + axis_reg + offset); |
6eb78a1b | 346 | |
bacf58a8 BB |
347 | printk(KERN_DEBUG "comedi: pci8164 %s write -> " |
348 | "%04X:%04X on axis %s\n", | |
349 | action, data[0], data[1], axisname); | |
ba7834b3 BP |
350 | |
351 | } | |
352 | ||
0a85b6f0 MT |
353 | static int adl_pci8164_insn_write_cmd(struct comedi_device *dev, |
354 | struct comedi_subdevice *s, | |
355 | struct comedi_insn *insn, | |
356 | unsigned int *data) | |
ba7834b3 BP |
357 | { |
358 | adl_pci8164_insn_out(dev, s, insn, data, "CMD", PCI8164_CMD); | |
6eb78a1b ML |
359 | return 2; |
360 | } | |
361 | ||
0a85b6f0 MT |
362 | static int adl_pci8164_insn_write_otp(struct comedi_device *dev, |
363 | struct comedi_subdevice *s, | |
364 | struct comedi_insn *insn, | |
365 | unsigned int *data) | |
6eb78a1b | 366 | { |
ba7834b3 | 367 | adl_pci8164_insn_out(dev, s, insn, data, "OTP", PCI8164_OTP); |
6eb78a1b ML |
368 | return 2; |
369 | } | |
370 | ||
da91b269 | 371 | static int adl_pci8164_insn_write_buf0(struct comedi_device *dev, |
0a85b6f0 MT |
372 | struct comedi_subdevice *s, |
373 | struct comedi_insn *insn, | |
374 | unsigned int *data) | |
6eb78a1b | 375 | { |
ba7834b3 | 376 | adl_pci8164_insn_out(dev, s, insn, data, "BUF0", PCI8164_BUF0); |
6eb78a1b ML |
377 | return 2; |
378 | } | |
379 | ||
da91b269 | 380 | static int adl_pci8164_insn_write_buf1(struct comedi_device *dev, |
0a85b6f0 MT |
381 | struct comedi_subdevice *s, |
382 | struct comedi_insn *insn, | |
383 | unsigned int *data) | |
6eb78a1b | 384 | { |
ba7834b3 | 385 | adl_pci8164_insn_out(dev, s, insn, data, "BUF1", PCI8164_BUF1); |
6eb78a1b ML |
386 | return 2; |
387 | } | |
388 | ||
727b286b AT |
389 | static int __devinit driver_adl_pci8164_pci_probe(struct pci_dev *dev, |
390 | const struct pci_device_id | |
391 | *ent) | |
392 | { | |
393 | return comedi_pci_auto_config(dev, driver_adl_pci8164.driver_name); | |
394 | } | |
395 | ||
396 | static void __devexit driver_adl_pci8164_pci_remove(struct pci_dev *dev) | |
397 | { | |
398 | comedi_pci_auto_unconfig(dev); | |
399 | } | |
400 | ||
401 | static struct pci_driver driver_adl_pci8164_pci_driver = { | |
402 | .id_table = adl_pci8164_pci_table, | |
403 | .probe = &driver_adl_pci8164_pci_probe, | |
404 | .remove = __devexit_p(&driver_adl_pci8164_pci_remove) | |
405 | }; | |
406 | ||
407 | static int __init driver_adl_pci8164_init_module(void) | |
408 | { | |
409 | int retval; | |
410 | ||
411 | retval = comedi_driver_register(&driver_adl_pci8164); | |
412 | if (retval < 0) | |
413 | return retval; | |
414 | ||
415 | driver_adl_pci8164_pci_driver.name = | |
416 | (char *)driver_adl_pci8164.driver_name; | |
417 | return pci_register_driver(&driver_adl_pci8164_pci_driver); | |
418 | } | |
419 | ||
420 | static void __exit driver_adl_pci8164_cleanup_module(void) | |
421 | { | |
422 | pci_unregister_driver(&driver_adl_pci8164_pci_driver); | |
423 | comedi_driver_unregister(&driver_adl_pci8164); | |
424 | } | |
425 | ||
426 | module_init(driver_adl_pci8164_init_module); | |
427 | module_exit(driver_adl_pci8164_cleanup_module); | |
90f703d3 AT |
428 | |
429 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
430 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
431 | MODULE_LICENSE("GPL"); |