Commit | Line | Data |
---|---|---|
07b509e6 | 1 | /* |
fb999e7f RKM |
2 | * comedi/drivers/jr3_pci.c |
3 | * hardware driver for JR3/PCI force sensor board | |
4 | * | |
5 | * COMEDI - Linux Control and Measurement Device Interface | |
6 | * Copyright (C) 2007 Anders Blomdell <anders.blomdell@control.lth.se> | |
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 | */ | |
07b509e6 | 18 | /* |
10ba619d IA |
19 | * Driver: jr3_pci |
20 | * Description: JR3/PCI force sensor board | |
21 | * Author: Anders Blomdell <anders.blomdell@control.lth.se> | |
22 | * Updated: Thu, 01 Nov 2012 17:34:55 +0000 | |
23 | * Status: works | |
24 | * Devices: [JR3] PCI force sensor board (jr3_pci) | |
25 | * | |
26 | * Configuration options: | |
27 | * None | |
28 | * | |
29 | * Manual configuration of comedi devices is not supported by this | |
30 | * driver; supported PCI devices are configured as comedi devices | |
31 | * automatically. | |
32 | * | |
33 | * The DSP on the board requires initialization code, which can be | |
34 | * loaded by placing it in /lib/firmware/comedi. The initialization | |
35 | * code should be somewhere on the media you got with your card. One | |
36 | * version is available from http://www.comedi.org in the | |
37 | * comedi_nonfree_firmware tarball. The file is called "jr3pci.idm". | |
38 | */ | |
07b509e6 | 39 | |
33782dd5 | 40 | #include <linux/kernel.h> |
ce157f80 | 41 | #include <linux/module.h> |
07b509e6 AB |
42 | #include <linux/delay.h> |
43 | #include <linux/ctype.h> | |
9b5de0a0 | 44 | #include <linux/jiffies.h> |
5a0e3ad6 | 45 | #include <linux/slab.h> |
9b5de0a0 | 46 | #include <linux/timer.h> |
33782dd5 | 47 | |
e4c296dc | 48 | #include "../comedi_pci.h" |
33782dd5 | 49 | |
07b509e6 AB |
50 | #include "jr3_pci.h" |
51 | ||
07b509e6 | 52 | #define PCI_VENDOR_ID_JR3 0x1762 |
7211806a HS |
53 | |
54 | enum jr3_pci_boardid { | |
55 | BOARD_JR3_1, | |
56 | BOARD_JR3_2, | |
57 | BOARD_JR3_3, | |
58 | BOARD_JR3_4, | |
59 | }; | |
60 | ||
61 | struct jr3_pci_board { | |
62 | const char *name; | |
63 | int n_subdevs; | |
64 | }; | |
65 | ||
66 | static const struct jr3_pci_board jr3_pci_boards[] = { | |
67 | [BOARD_JR3_1] = { | |
68 | .name = "jr3_pci_1", | |
69 | .n_subdevs = 1, | |
70 | }, | |
71 | [BOARD_JR3_2] = { | |
72 | .name = "jr3_pci_2", | |
73 | .n_subdevs = 2, | |
74 | }, | |
75 | [BOARD_JR3_3] = { | |
76 | .name = "jr3_pci_3", | |
77 | .n_subdevs = 3, | |
78 | }, | |
79 | [BOARD_JR3_4] = { | |
80 | .name = "jr3_pci_4", | |
81 | .n_subdevs = 4, | |
82 | }, | |
83 | }; | |
07b509e6 | 84 | |
0a44493f HS |
85 | struct jr3_pci_transform { |
86 | struct { | |
87 | u16 link_type; | |
88 | s16 link_amount; | |
89 | } link[8]; | |
90 | }; | |
91 | ||
da1331a5 HS |
92 | struct jr3_pci_poll_delay { |
93 | int min; | |
94 | int max; | |
95 | }; | |
96 | ||
217fbbbc | 97 | struct jr3_pci_dev_private { |
95b24682 | 98 | struct jr3_t __iomem *iobase; |
07b509e6 | 99 | struct timer_list timer; |
217fbbbc BP |
100 | }; |
101 | ||
c6a3b7b6 | 102 | struct jr3_pci_subdev_private { |
95b24682 | 103 | struct jr3_channel __iomem *channel; |
07b509e6 AB |
104 | unsigned long next_time_min; |
105 | unsigned long next_time_max; | |
106 | enum { state_jr3_poll, | |
107 | state_jr3_init_wait_for_offset, | |
108 | state_jr3_init_transform_complete, | |
109 | state_jr3_init_set_full_scale_complete, | |
110 | state_jr3_init_use_offset_complete, | |
111 | state_jr3_done | |
112 | } state; | |
07b509e6 AB |
113 | int serial_no; |
114 | int model_no; | |
115 | struct { | |
116 | int length; | |
1f6325d6 | 117 | struct comedi_krange range; |
07b509e6 | 118 | } range[9]; |
9ced1de6 | 119 | const struct comedi_lrange *range_table_list[8 * 7 + 2]; |
790c5541 | 120 | unsigned int maxdata_list[8 * 7 + 2]; |
07b509e6 AB |
121 | u16 errors; |
122 | int retries; | |
c6a3b7b6 | 123 | }; |
07b509e6 | 124 | |
da1331a5 | 125 | static struct jr3_pci_poll_delay poll_delay_min_max(int min, int max) |
07b509e6 | 126 | { |
da1331a5 | 127 | struct jr3_pci_poll_delay result; |
07b509e6 AB |
128 | |
129 | result.min = min; | |
130 | result.max = max; | |
131 | return result; | |
132 | } | |
133 | ||
95b24682 | 134 | static int is_complete(struct jr3_channel __iomem *channel) |
07b509e6 AB |
135 | { |
136 | return get_s16(&channel->command_word0) == 0; | |
137 | } | |
138 | ||
95b24682 | 139 | static void set_transforms(struct jr3_channel __iomem *channel, |
0a44493f | 140 | struct jr3_pci_transform transf, short num) |
07b509e6 AB |
141 | { |
142 | int i; | |
143 | ||
d5e59c96 | 144 | num &= 0x000f; /* Make sure that 0 <= num <= 15 */ |
07b509e6 | 145 | for (i = 0; i < 8; i++) { |
07b509e6 AB |
146 | set_u16(&channel->transforms[num].link[i].link_type, |
147 | transf.link[i].link_type); | |
5f74ea14 | 148 | udelay(1); |
07b509e6 AB |
149 | set_s16(&channel->transforms[num].link[i].link_amount, |
150 | transf.link[i].link_amount); | |
5f74ea14 | 151 | udelay(1); |
c77049ef | 152 | if (transf.link[i].link_type == end_x_form) |
07b509e6 | 153 | break; |
07b509e6 AB |
154 | } |
155 | } | |
156 | ||
95b24682 | 157 | static void use_transform(struct jr3_channel __iomem *channel, |
0a85b6f0 | 158 | short transf_num) |
07b509e6 AB |
159 | { |
160 | set_s16(&channel->command_word0, 0x0500 + (transf_num & 0x000f)); | |
161 | } | |
162 | ||
95b24682 | 163 | static void use_offset(struct jr3_channel __iomem *channel, short offset_num) |
07b509e6 AB |
164 | { |
165 | set_s16(&channel->command_word0, 0x0600 + (offset_num & 0x000f)); | |
166 | } | |
167 | ||
95b24682 | 168 | static void set_offset(struct jr3_channel __iomem *channel) |
07b509e6 AB |
169 | { |
170 | set_s16(&channel->command_word0, 0x0700); | |
171 | } | |
172 | ||
050509fa | 173 | struct six_axis_t { |
07b509e6 AB |
174 | s16 fx; |
175 | s16 fy; | |
176 | s16 fz; | |
177 | s16 mx; | |
178 | s16 my; | |
179 | s16 mz; | |
050509fa | 180 | }; |
07b509e6 | 181 | |
95b24682 | 182 | static void set_full_scales(struct jr3_channel __iomem *channel, |
0a85b6f0 | 183 | struct six_axis_t full_scale) |
07b509e6 | 184 | { |
07b509e6 AB |
185 | set_s16(&channel->full_scale.fx, full_scale.fx); |
186 | set_s16(&channel->full_scale.fy, full_scale.fy); | |
187 | set_s16(&channel->full_scale.fz, full_scale.fz); | |
188 | set_s16(&channel->full_scale.mx, full_scale.mx); | |
189 | set_s16(&channel->full_scale.my, full_scale.my); | |
190 | set_s16(&channel->full_scale.mz, full_scale.mz); | |
191 | set_s16(&channel->command_word0, 0x0a00); | |
192 | } | |
193 | ||
95b24682 | 194 | static struct six_axis_t get_min_full_scales(struct jr3_channel __iomem |
0a85b6f0 | 195 | *channel) |
07b509e6 | 196 | { |
050509fa | 197 | struct six_axis_t result; |
cd9da617 | 198 | |
07b509e6 AB |
199 | result.fx = get_s16(&channel->min_full_scale.fx); |
200 | result.fy = get_s16(&channel->min_full_scale.fy); | |
201 | result.fz = get_s16(&channel->min_full_scale.fz); | |
202 | result.mx = get_s16(&channel->min_full_scale.mx); | |
203 | result.my = get_s16(&channel->min_full_scale.my); | |
204 | result.mz = get_s16(&channel->min_full_scale.mz); | |
205 | return result; | |
206 | } | |
207 | ||
95b24682 | 208 | static struct six_axis_t get_max_full_scales(struct jr3_channel __iomem |
0a85b6f0 | 209 | *channel) |
07b509e6 | 210 | { |
050509fa | 211 | struct six_axis_t result; |
cd9da617 | 212 | |
07b509e6 AB |
213 | result.fx = get_s16(&channel->max_full_scale.fx); |
214 | result.fy = get_s16(&channel->max_full_scale.fy); | |
215 | result.fz = get_s16(&channel->max_full_scale.fz); | |
216 | result.mx = get_s16(&channel->max_full_scale.mx); | |
217 | result.my = get_s16(&channel->max_full_scale.my); | |
218 | result.mz = get_s16(&channel->max_full_scale.mz); | |
219 | return result; | |
220 | } | |
221 | ||
16a86abe HS |
222 | static unsigned int jr3_pci_ai_read_chan(struct comedi_device *dev, |
223 | struct comedi_subdevice *s, | |
224 | unsigned int chan) | |
225 | { | |
226 | struct jr3_pci_subdev_private *spriv = s->private; | |
227 | unsigned int val = 0; | |
228 | ||
229 | if (spriv->state != state_jr3_done) | |
230 | return 0; | |
231 | ||
232 | if (chan < 56) { | |
233 | unsigned int axis = chan % 8; | |
5efe1159 | 234 | unsigned int filter = chan / 8; |
16a86abe HS |
235 | |
236 | switch (axis) { | |
237 | case 0: | |
238 | val = get_s16(&spriv->channel->filter[filter].fx); | |
239 | break; | |
240 | case 1: | |
241 | val = get_s16(&spriv->channel->filter[filter].fy); | |
242 | break; | |
243 | case 2: | |
244 | val = get_s16(&spriv->channel->filter[filter].fz); | |
245 | break; | |
246 | case 3: | |
247 | val = get_s16(&spriv->channel->filter[filter].mx); | |
248 | break; | |
249 | case 4: | |
250 | val = get_s16(&spriv->channel->filter[filter].my); | |
251 | break; | |
252 | case 5: | |
253 | val = get_s16(&spriv->channel->filter[filter].mz); | |
254 | break; | |
255 | case 6: | |
256 | val = get_s16(&spriv->channel->filter[filter].v1); | |
257 | break; | |
258 | case 7: | |
259 | val = get_s16(&spriv->channel->filter[filter].v2); | |
260 | break; | |
261 | } | |
262 | val += 0x4000; | |
263 | } else if (chan == 56) { | |
264 | val = get_u16(&spriv->channel->model_no); | |
265 | } else if (chan == 57) { | |
266 | val = get_u16(&spriv->channel->serial_no); | |
267 | } | |
268 | ||
269 | return val; | |
270 | } | |
271 | ||
0a85b6f0 MT |
272 | static int jr3_pci_ai_insn_read(struct comedi_device *dev, |
273 | struct comedi_subdevice *s, | |
16a86abe HS |
274 | struct comedi_insn *insn, |
275 | unsigned int *data) | |
07b509e6 | 276 | { |
16a86abe HS |
277 | struct jr3_pci_subdev_private *spriv = s->private; |
278 | unsigned int chan = CR_CHAN(insn->chanspec); | |
279 | u16 errors; | |
280 | int i; | |
281 | ||
282 | if (!spriv) | |
283 | return -EINVAL; | |
284 | ||
285 | errors = get_u16(&spriv->channel->errors); | |
286 | if (spriv->state != state_jr3_done || | |
287 | (errors & (watch_dog | watch_dog2 | sensor_change))) { | |
288 | /* No sensor or sensor changed */ | |
289 | if (spriv->state == state_jr3_done) { | |
290 | /* Restart polling */ | |
291 | spriv->state = state_jr3_poll; | |
07b509e6 | 292 | } |
16a86abe | 293 | return -EAGAIN; |
07b509e6 | 294 | } |
16a86abe HS |
295 | |
296 | for (i = 0; i < insn->n; i++) | |
297 | data[i] = jr3_pci_ai_read_chan(dev, s, chan); | |
298 | ||
299 | return insn->n; | |
07b509e6 AB |
300 | } |
301 | ||
3c17ba07 | 302 | static int jr3_pci_open(struct comedi_device *dev) |
07b509e6 | 303 | { |
617cd653 HS |
304 | struct jr3_pci_subdev_private *spriv; |
305 | struct comedi_subdevice *s; | |
306 | int i; | |
07b509e6 | 307 | |
f41ad667 | 308 | dev_dbg(dev->class_dev, "jr3_pci_open\n"); |
ed8cd560 | 309 | for (i = 0; i < dev->n_subdevices; i++) { |
617cd653 HS |
310 | s = &dev->subdevices[i]; |
311 | spriv = s->private; | |
312 | if (spriv) | |
313 | dev_dbg(dev->class_dev, "serial: %p %d (%d)\n", | |
d6ae5062 | 314 | spriv, spriv->serial_no, s->index); |
07b509e6 | 315 | } |
3c17ba07 | 316 | return 0; |
07b509e6 AB |
317 | } |
318 | ||
2ca9bc2e HS |
319 | static int read_idm_word(const u8 *data, size_t size, int *pos, |
320 | unsigned int *val) | |
07b509e6 AB |
321 | { |
322 | int result = 0; | |
cd9da617 HS |
323 | int value; |
324 | ||
57991b6b | 325 | if (pos && val) { |
d5e59c96 | 326 | /* Skip over non hex */ |
abcdc99f IA |
327 | for (; *pos < size && !isxdigit(data[*pos]); (*pos)++) |
328 | ; | |
d5e59c96 | 329 | /* Collect value */ |
07b509e6 | 330 | *val = 0; |
3ff16c25 | 331 | for (; *pos < size; (*pos)++) { |
3ff16c25 AS |
332 | value = hex_to_bin(data[*pos]); |
333 | if (value >= 0) { | |
334 | result = 1; | |
335 | *val = (*val << 4) + value; | |
abcdc99f | 336 | } else { |
3ff16c25 | 337 | break; |
abcdc99f | 338 | } |
07b509e6 AB |
339 | } |
340 | } | |
341 | return result; | |
342 | } | |
343 | ||
127301cb HS |
344 | static int jr3_check_firmware(struct comedi_device *dev, |
345 | const u8 *data, size_t size) | |
07b509e6 | 346 | { |
127301cb HS |
347 | int more = 1; |
348 | int pos = 0; | |
349 | ||
07b509e6 AB |
350 | /* |
351 | * IDM file format is: | |
352 | * { count, address, data <count> } * | |
353 | * ffff | |
354 | */ | |
07b509e6 | 355 | while (more) { |
127301cb HS |
356 | unsigned int count = 0; |
357 | unsigned int addr = 0; | |
07b509e6 AB |
358 | |
359 | more = more && read_idm_word(data, size, &pos, &count); | |
127301cb HS |
360 | if (more && count == 0xffff) |
361 | return 0; | |
362 | ||
07b509e6 AB |
363 | more = more && read_idm_word(data, size, &pos, &addr); |
364 | while (more && count > 0) { | |
127301cb HS |
365 | unsigned int dummy = 0; |
366 | ||
07b509e6 AB |
367 | more = more && read_idm_word(data, size, &pos, &dummy); |
368 | count--; | |
369 | } | |
370 | } | |
371 | ||
127301cb HS |
372 | return -ENODATA; |
373 | } | |
374 | ||
375 | static void jr3_write_firmware(struct comedi_device *dev, | |
376 | int subdev, const u8 *data, size_t size) | |
377 | { | |
378 | struct jr3_pci_dev_private *devpriv = dev->private; | |
379 | struct jr3_t __iomem *iobase = devpriv->iobase; | |
380 | u32 __iomem *lo; | |
381 | u32 __iomem *hi; | |
382 | int more = 1; | |
383 | int pos = 0; | |
384 | ||
385 | while (more) { | |
386 | unsigned int count = 0; | |
387 | unsigned int addr = 0; | |
388 | ||
389 | more = more && read_idm_word(data, size, &pos, &count); | |
390 | if (more && count == 0xffff) | |
391 | return; | |
392 | ||
393 | more = more && read_idm_word(data, size, &pos, &addr); | |
394 | ||
395 | dev_dbg(dev->class_dev, "Loading#%d %4.4x bytes at %4.4x\n", | |
396 | subdev, count, addr); | |
397 | ||
398 | while (more && count > 0) { | |
399 | if (addr & 0x4000) { | |
400 | /* 16 bit data, never seen in real life!! */ | |
401 | unsigned int data1 = 0; | |
402 | ||
403 | more = more && | |
404 | read_idm_word(data, size, &pos, &data1); | |
405 | count--; | |
406 | /* jr3[addr + 0x20000 * pnum] = data1; */ | |
407 | } else { | |
408 | /* Download 24 bit program */ | |
409 | unsigned int data1 = 0; | |
410 | unsigned int data2 = 0; | |
07b509e6 | 411 | |
127301cb HS |
412 | lo = &iobase->channel[subdev].program_lo[addr]; |
413 | hi = &iobase->channel[subdev].program_hi[addr]; | |
07b509e6 | 414 | |
abcdc99f | 415 | more = more && |
127301cb | 416 | read_idm_word(data, size, &pos, &data1); |
abcdc99f | 417 | more = more && |
127301cb HS |
418 | read_idm_word(data, size, &pos, &data2); |
419 | count -= 2; | |
420 | if (more) { | |
421 | set_u16(lo, data1); | |
422 | udelay(1); | |
423 | set_u16(hi, data2); | |
424 | udelay(1); | |
07b509e6 AB |
425 | } |
426 | } | |
127301cb | 427 | addr++; |
07b509e6 AB |
428 | } |
429 | } | |
127301cb HS |
430 | } |
431 | ||
432 | static int jr3_download_firmware(struct comedi_device *dev, | |
433 | const u8 *data, size_t size, | |
434 | unsigned long context) | |
435 | { | |
127301cb HS |
436 | int subdev; |
437 | int ret; | |
438 | ||
439 | /* verify IDM file format */ | |
440 | ret = jr3_check_firmware(dev, data, size); | |
441 | if (ret) | |
442 | return ret; | |
443 | ||
444 | /* write firmware to each subdevice */ | |
ed8cd560 | 445 | for (subdev = 0; subdev < dev->n_subdevices; subdev++) |
127301cb HS |
446 | jr3_write_firmware(dev, subdev, data, size); |
447 | ||
448 | return 0; | |
07b509e6 AB |
449 | } |
450 | ||
d5e59c96 AJ |
451 | static struct jr3_pci_poll_delay |
452 | jr3_pci_poll_subdevice(struct comedi_subdevice *s) | |
07b509e6 | 453 | { |
01fca473 | 454 | struct jr3_pci_subdev_private *spriv = s->private; |
da1331a5 | 455 | struct jr3_pci_poll_delay result = poll_delay_min_max(1000, 2000); |
01fca473 HS |
456 | struct jr3_channel __iomem *channel; |
457 | u16 model_no; | |
458 | u16 serial_no; | |
459 | int errors; | |
b1f68dc1 | 460 | int i; |
07b509e6 | 461 | |
01fca473 HS |
462 | if (!spriv) |
463 | return result; | |
464 | ||
465 | channel = spriv->channel; | |
466 | errors = get_u16(&channel->errors); | |
467 | ||
468 | if (errors != spriv->errors) | |
469 | spriv->errors = errors; | |
470 | ||
471 | /* Sensor communication lost? force poll mode */ | |
472 | if (errors & (watch_dog | watch_dog2 | sensor_change)) | |
473 | spriv->state = state_jr3_poll; | |
474 | ||
475 | switch (spriv->state) { | |
476 | case state_jr3_poll: | |
477 | model_no = get_u16(&channel->model_no); | |
478 | serial_no = get_u16(&channel->serial_no); | |
479 | ||
480 | if ((errors & (watch_dog | watch_dog2)) || | |
481 | model_no == 0 || serial_no == 0) { | |
482 | /* | |
483 | * Still no sensor, keep on polling. | |
484 | * Since it takes up to 10 seconds for offsets to | |
485 | * stabilize, polling each second should suffice. | |
486 | */ | |
487 | } else { | |
488 | spriv->retries = 0; | |
489 | spriv->state = state_jr3_init_wait_for_offset; | |
490 | } | |
491 | break; | |
492 | case state_jr3_init_wait_for_offset: | |
493 | spriv->retries++; | |
494 | if (spriv->retries < 10) { | |
495 | /* | |
496 | * Wait for offeset to stabilize | |
497 | * (< 10 s according to manual) | |
498 | */ | |
499 | } else { | |
0a44493f | 500 | struct jr3_pci_transform transf; |
01fca473 HS |
501 | |
502 | spriv->model_no = get_u16(&channel->model_no); | |
503 | spriv->serial_no = get_u16(&channel->serial_no); | |
504 | ||
505 | /* Transformation all zeros */ | |
506 | for (i = 0; i < ARRAY_SIZE(transf.link); i++) { | |
507 | transf.link[i].link_type = (enum link_types)0; | |
508 | transf.link[i].link_amount = 0; | |
07b509e6 | 509 | } |
abcdc99f | 510 | |
01fca473 HS |
511 | set_transforms(channel, transf, 0); |
512 | use_transform(channel, 0); | |
513 | spriv->state = state_jr3_init_transform_complete; | |
514 | /* Allow 20 ms for completion */ | |
515 | result = poll_delay_min_max(20, 100); | |
516 | } | |
517 | break; | |
518 | case state_jr3_init_transform_complete: | |
519 | if (!is_complete(channel)) { | |
520 | result = poll_delay_min_max(20, 100); | |
521 | } else { | |
522 | /* Set full scale */ | |
523 | struct six_axis_t min_full_scale; | |
524 | struct six_axis_t max_full_scale; | |
525 | ||
526 | min_full_scale = get_min_full_scales(channel); | |
527 | max_full_scale = get_max_full_scales(channel); | |
528 | set_full_scales(channel, max_full_scale); | |
529 | ||
530 | spriv->state = state_jr3_init_set_full_scale_complete; | |
531 | /* Allow 20 ms for completion */ | |
532 | result = poll_delay_min_max(20, 100); | |
07b509e6 | 533 | } |
01fca473 HS |
534 | break; |
535 | case state_jr3_init_set_full_scale_complete: | |
536 | if (!is_complete(channel)) { | |
537 | result = poll_delay_min_max(20, 100); | |
538 | } else { | |
539 | struct force_array __iomem *fs = &channel->full_scale; | |
540 | ||
541 | /* Use ranges in kN or we will overflow around 2000N! */ | |
542 | spriv->range[0].range.min = -get_s16(&fs->fx) * 1000; | |
543 | spriv->range[0].range.max = get_s16(&fs->fx) * 1000; | |
544 | spriv->range[1].range.min = -get_s16(&fs->fy) * 1000; | |
545 | spriv->range[1].range.max = get_s16(&fs->fy) * 1000; | |
546 | spriv->range[2].range.min = -get_s16(&fs->fz) * 1000; | |
547 | spriv->range[2].range.max = get_s16(&fs->fz) * 1000; | |
548 | spriv->range[3].range.min = -get_s16(&fs->mx) * 100; | |
549 | spriv->range[3].range.max = get_s16(&fs->mx) * 100; | |
550 | spriv->range[4].range.min = -get_s16(&fs->my) * 100; | |
551 | spriv->range[4].range.max = get_s16(&fs->my) * 100; | |
552 | spriv->range[5].range.min = -get_s16(&fs->mz) * 100; | |
553 | /* the next five are questionable */ | |
554 | spriv->range[5].range.max = get_s16(&fs->mz) * 100; | |
555 | spriv->range[6].range.min = -get_s16(&fs->v1) * 100; | |
556 | spriv->range[6].range.max = get_s16(&fs->v1) * 100; | |
557 | spriv->range[7].range.min = -get_s16(&fs->v2) * 100; | |
558 | spriv->range[7].range.max = get_s16(&fs->v2) * 100; | |
559 | spriv->range[8].range.min = 0; | |
560 | spriv->range[8].range.max = 65535; | |
561 | ||
562 | use_offset(channel, 0); | |
563 | spriv->state = state_jr3_init_use_offset_complete; | |
564 | /* Allow 40 ms for completion */ | |
565 | result = poll_delay_min_max(40, 100); | |
566 | } | |
567 | break; | |
568 | case state_jr3_init_use_offset_complete: | |
569 | if (!is_complete(channel)) { | |
570 | result = poll_delay_min_max(20, 100); | |
571 | } else { | |
572 | set_s16(&channel->offsets.fx, 0); | |
573 | set_s16(&channel->offsets.fy, 0); | |
574 | set_s16(&channel->offsets.fz, 0); | |
575 | set_s16(&channel->offsets.mx, 0); | |
576 | set_s16(&channel->offsets.my, 0); | |
577 | set_s16(&channel->offsets.mz, 0); | |
578 | ||
579 | set_offset(channel); | |
580 | ||
581 | spriv->state = state_jr3_done; | |
582 | } | |
583 | break; | |
584 | case state_jr3_done: | |
585 | result = poll_delay_min_max(10000, 20000); | |
586 | break; | |
587 | default: | |
588 | break; | |
07b509e6 | 589 | } |
01fca473 | 590 | |
07b509e6 AB |
591 | return result; |
592 | } | |
593 | ||
594 | static void jr3_pci_poll_dev(unsigned long data) | |
595 | { | |
0a85b6f0 | 596 | struct comedi_device *dev = (struct comedi_device *)data; |
217fbbbc | 597 | struct jr3_pci_dev_private *devpriv = dev->private; |
a448376e HS |
598 | struct jr3_pci_subdev_private *spriv; |
599 | struct comedi_subdevice *s; | |
600 | unsigned long flags; | |
07b509e6 AB |
601 | unsigned long now; |
602 | int delay; | |
603 | int i; | |
604 | ||
5f74ea14 | 605 | spin_lock_irqsave(&dev->spinlock, flags); |
07b509e6 AB |
606 | delay = 1000; |
607 | now = jiffies; | |
a448376e HS |
608 | |
609 | /* Poll all channels that are ready to be polled */ | |
ed8cd560 | 610 | for (i = 0; i < dev->n_subdevices; i++) { |
a448376e HS |
611 | s = &dev->subdevices[i]; |
612 | spriv = s->private; | |
613 | ||
614 | if (now > spriv->next_time_min) { | |
da1331a5 | 615 | struct jr3_pci_poll_delay sub_delay; |
07b509e6 | 616 | |
a448376e HS |
617 | sub_delay = jr3_pci_poll_subdevice(s); |
618 | ||
619 | spriv->next_time_min = jiffies + | |
620 | msecs_to_jiffies(sub_delay.min); | |
621 | spriv->next_time_max = jiffies + | |
622 | msecs_to_jiffies(sub_delay.max); | |
623 | ||
abcdc99f IA |
624 | if (sub_delay.max && sub_delay.max < delay) |
625 | /* | |
626 | * Wake up as late as possible -> | |
627 | * poll as many channels as possible at once. | |
628 | */ | |
07b509e6 | 629 | delay = sub_delay.max; |
07b509e6 AB |
630 | } |
631 | } | |
5f74ea14 | 632 | spin_unlock_irqrestore(&dev->spinlock, flags); |
07b509e6 AB |
633 | |
634 | devpriv->timer.expires = jiffies + msecs_to_jiffies(delay); | |
635 | add_timer(&devpriv->timer); | |
636 | } | |
637 | ||
9e4d6742 HS |
638 | static struct jr3_pci_subdev_private * |
639 | jr3_pci_alloc_spriv(struct comedi_device *dev, struct comedi_subdevice *s) | |
640 | { | |
641 | struct jr3_pci_dev_private *devpriv = dev->private; | |
642 | struct jr3_pci_subdev_private *spriv; | |
643 | int j; | |
644 | int k; | |
645 | ||
646 | spriv = comedi_alloc_spriv(s, sizeof(*spriv)); | |
647 | if (!spriv) | |
648 | return NULL; | |
649 | ||
650 | spriv->channel = &devpriv->iobase->channel[s->index].data; | |
9e4d6742 HS |
651 | |
652 | for (j = 0; j < 8; j++) { | |
653 | spriv->range[j].length = 1; | |
654 | spriv->range[j].range.min = -1000000; | |
655 | spriv->range[j].range.max = 1000000; | |
656 | ||
657 | for (k = 0; k < 7; k++) { | |
658 | spriv->range_table_list[j + k * 8] = | |
659 | (struct comedi_lrange *)&spriv->range[j]; | |
660 | spriv->maxdata_list[j + k * 8] = 0x7fff; | |
661 | } | |
662 | } | |
663 | spriv->range[8].length = 1; | |
664 | spriv->range[8].range.min = 0; | |
665 | spriv->range[8].range.max = 65536; | |
666 | ||
667 | spriv->range_table_list[56] = (struct comedi_lrange *)&spriv->range[8]; | |
668 | spriv->range_table_list[57] = (struct comedi_lrange *)&spriv->range[8]; | |
669 | spriv->maxdata_list[56] = 0xffff; | |
670 | spriv->maxdata_list[57] = 0xffff; | |
671 | ||
672 | dev_dbg(dev->class_dev, "p->channel %p %p (%tx)\n", | |
673 | spriv->channel, devpriv->iobase, | |
674 | ((char __iomem *)spriv->channel - | |
675 | (char __iomem *)devpriv->iobase)); | |
676 | ||
677 | return spriv; | |
678 | } | |
679 | ||
a690b7e5 | 680 | static int jr3_pci_auto_attach(struct comedi_device *dev, |
7211806a | 681 | unsigned long context) |
07b509e6 | 682 | { |
6af0cf76 | 683 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
165356c7 | 684 | static const struct jr3_pci_board *board; |
217fbbbc | 685 | struct jr3_pci_dev_private *devpriv; |
3c77274c | 686 | struct jr3_pci_subdev_private *spriv; |
050389f5 | 687 | struct comedi_subdevice *s; |
3c77274c | 688 | int ret; |
050389f5 | 689 | int i; |
07b509e6 | 690 | |
67080790 | 691 | if (sizeof(struct jr3_channel) != 0xc00) { |
f41ad667 IA |
692 | dev_err(dev->class_dev, |
693 | "sizeof(struct jr3_channel) = %x [expected %x]\n", | |
5efe1159 | 694 | (unsigned int)sizeof(struct jr3_channel), 0xc00); |
07b509e6 AB |
695 | return -EINVAL; |
696 | } | |
697 | ||
7211806a HS |
698 | if (context < ARRAY_SIZE(jr3_pci_boards)) |
699 | board = &jr3_pci_boards[context]; | |
700 | if (!board) | |
701 | return -ENODEV; | |
702 | dev->board_ptr = board; | |
703 | dev->board_name = board->name; | |
704 | ||
0bdab509 | 705 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
706 | if (!devpriv) |
707 | return -ENOMEM; | |
9a1a6cf8 | 708 | |
3c77274c HS |
709 | ret = comedi_pci_enable(dev); |
710 | if (ret) | |
711 | return ret; | |
818f569f | 712 | |
8567a851 | 713 | devpriv->iobase = pci_ioremap_bar(pcidev, 0); |
fa5c5f4c IA |
714 | if (!devpriv->iobase) |
715 | return -ENOMEM; | |
716 | ||
ed8cd560 | 717 | ret = comedi_alloc_subdevices(dev, board->n_subdevs); |
3c77274c HS |
718 | if (ret) |
719 | return ret; | |
07b509e6 AB |
720 | |
721 | dev->open = jr3_pci_open; | |
ed8cd560 | 722 | for (i = 0; i < dev->n_subdevices; i++) { |
050389f5 HS |
723 | s = &dev->subdevices[i]; |
724 | s->type = COMEDI_SUBD_AI; | |
725 | s->subdev_flags = SDF_READABLE | SDF_GROUND; | |
726 | s->n_chan = 8 * 7 + 2; | |
727 | s->insn_read = jr3_pci_ai_insn_read; | |
728 | ||
3c77274c HS |
729 | spriv = jr3_pci_alloc_spriv(dev, s); |
730 | if (spriv) { | |
9e4d6742 | 731 | /* Channel specific range and maxdata */ |
3c77274c HS |
732 | s->range_table_list = spriv->range_table_list; |
733 | s->maxdata_list = spriv->maxdata_list; | |
07b509e6 AB |
734 | } |
735 | } | |
736 | ||
d5e59c96 | 737 | /* Reset DSP card */ |
e1878957 | 738 | writel(0, &devpriv->iobase->channel[0].reset); |
07b509e6 | 739 | |
3c77274c HS |
740 | ret = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev, |
741 | "comedi/jr3pci.idm", | |
742 | jr3_download_firmware, 0); | |
cb14a0b4 | 743 | dev_dbg(dev->class_dev, "Firmware load %d\n", ret); |
3c77274c HS |
744 | if (ret < 0) |
745 | return ret; | |
abcdc99f IA |
746 | /* |
747 | * TODO: use firmware to load preferred offset tables. Suggested | |
748 | * format: | |
749 | * model serial Fx Fy Fz Mx My Mz\n | |
750 | * | |
9ff8b151 HS |
751 | * comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev, |
752 | * "comedi/jr3_offsets_table", | |
d569541e | 753 | * jr3_download_firmware, 1); |
abcdc99f | 754 | */ |
07b509e6 | 755 | |
abcdc99f IA |
756 | /* |
757 | * It takes a few milliseconds for software to settle as much as we | |
758 | * can read firmware version | |
759 | */ | |
07b509e6 AB |
760 | msleep_interruptible(25); |
761 | for (i = 0; i < 0x18; i++) { | |
f41ad667 | 762 | dev_dbg(dev->class_dev, "%c\n", |
351a1d35 RM |
763 | get_u16(&devpriv->iobase->channel[0]. |
764 | data.copyright[i]) >> 8); | |
07b509e6 AB |
765 | } |
766 | ||
d5e59c96 | 767 | /* Start card timer */ |
ed8cd560 | 768 | for (i = 0; i < dev->n_subdevices; i++) { |
050389f5 | 769 | s = &dev->subdevices[i]; |
3c77274c | 770 | spriv = s->private; |
07b509e6 | 771 | |
3c77274c HS |
772 | spriv->next_time_min = jiffies + msecs_to_jiffies(500); |
773 | spriv->next_time_max = jiffies + msecs_to_jiffies(2000); | |
07b509e6 AB |
774 | } |
775 | ||
81906c35 | 776 | setup_timer(&devpriv->timer, jr3_pci_poll_dev, (unsigned long)dev); |
07b509e6 AB |
777 | devpriv->timer.expires = jiffies + msecs_to_jiffies(1000); |
778 | add_timer(&devpriv->timer); | |
779 | ||
fb780d21 | 780 | return 0; |
07b509e6 AB |
781 | } |
782 | ||
484ecc95 | 783 | static void jr3_pci_detach(struct comedi_device *dev) |
07b509e6 | 784 | { |
217fbbbc | 785 | struct jr3_pci_dev_private *devpriv = dev->private; |
07b509e6 | 786 | |
07b509e6 AB |
787 | if (devpriv) { |
788 | del_timer_sync(&devpriv->timer); | |
789 | ||
c77049ef | 790 | if (devpriv->iobase) |
95b24682 | 791 | iounmap(devpriv->iobase); |
07b509e6 | 792 | } |
7f072f54 | 793 | comedi_pci_disable(dev); |
07b509e6 AB |
794 | } |
795 | ||
75e6301b | 796 | static struct comedi_driver jr3_pci_driver = { |
df61178c HS |
797 | .driver_name = "jr3_pci", |
798 | .module = THIS_MODULE, | |
b7703d7d | 799 | .auto_attach = jr3_pci_auto_attach, |
df61178c HS |
800 | .detach = jr3_pci_detach, |
801 | }; | |
802 | ||
a690b7e5 | 803 | static int jr3_pci_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 804 | const struct pci_device_id *id) |
727b286b | 805 | { |
b8f4ac23 | 806 | return comedi_pci_auto_config(dev, &jr3_pci_driver, id->driver_data); |
727b286b AT |
807 | } |
808 | ||
41e043fc | 809 | static const struct pci_device_id jr3_pci_pci_table[] = { |
7211806a HS |
810 | { PCI_VDEVICE(JR3, 0x1111), BOARD_JR3_1 }, |
811 | { PCI_VDEVICE(JR3, 0x3111), BOARD_JR3_1 }, | |
812 | { PCI_VDEVICE(JR3, 0x3112), BOARD_JR3_2 }, | |
813 | { PCI_VDEVICE(JR3, 0x3113), BOARD_JR3_3 }, | |
814 | { PCI_VDEVICE(JR3, 0x3114), BOARD_JR3_4 }, | |
df61178c HS |
815 | { 0 } |
816 | }; | |
817 | MODULE_DEVICE_TABLE(pci, jr3_pci_pci_table); | |
818 | ||
75e6301b HS |
819 | static struct pci_driver jr3_pci_pci_driver = { |
820 | .name = "jr3_pci", | |
df61178c | 821 | .id_table = jr3_pci_pci_table, |
75e6301b | 822 | .probe = jr3_pci_pci_probe, |
9901a4d7 | 823 | .remove = comedi_pci_auto_unconfig, |
727b286b | 824 | }; |
75e6301b | 825 | module_comedi_pci_driver(jr3_pci_driver, jr3_pci_pci_driver); |
90f703d3 AT |
826 | |
827 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
828 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
829 | MODULE_LICENSE("GPL"); | |
df61178c | 830 | MODULE_FIRMWARE("comedi/jr3pci.idm"); |