arch, drivers: don't include <asm/io.h> directly, use <linux/io.h> instead
[deliverable/linux.git] / drivers / staging / comedi / drivers / ii_pci20kc.c
1 /*
2 * ii_pci20kc.c
3 * Driver for Intelligent Instruments PCI-20001C carrier board and modules.
4 *
5 * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de>
6 * with suggestions from David Schleef 16.06.2000
7 */
8
9 /*
10 * Driver: ii_pci20kc
11 * Description: Intelligent Instruments PCI-20001C carrier board
12 * Devices: [Intelligent Instrumentation] PCI-20001C (ii_pci20kc)
13 * Author: Markus Kempf <kempf@matsci.uni-sb.de>
14 * Status: works
15 *
16 * Supports the PCI-20001C-1a and PCI-20001C-2a carrier boards. The
17 * -2a version has 32 on-board DIO channels. Three add-on modules
18 * can be added to the carrier board for additional functionality.
19 *
20 * Supported add-on modules:
21 * PCI-20006M-1 1 channel, 16-bit analog output module
22 * PCI-20006M-2 2 channel, 16-bit analog output module
23 * PCI-20341M-1A 4 channel, 16-bit analog input module
24 *
25 * Options:
26 * 0 Board base address
27 * 1 IRQ (not-used)
28 */
29
30 #include <linux/module.h>
31 #include <linux/io.h>
32 #include "../comedidev.h"
33
34 /*
35 * Register I/O map
36 */
37 #define II20K_SIZE 0x400
38 #define II20K_MOD_OFFSET 0x100
39 #define II20K_ID_REG 0x00
40 #define II20K_ID_MOD1_EMPTY (1 << 7)
41 #define II20K_ID_MOD2_EMPTY (1 << 6)
42 #define II20K_ID_MOD3_EMPTY (1 << 5)
43 #define II20K_ID_MASK 0x1f
44 #define II20K_ID_PCI20001C_1A 0x1b /* no on-board DIO */
45 #define II20K_ID_PCI20001C_2A 0x1d /* on-board DIO */
46 #define II20K_MOD_STATUS_REG 0x40
47 #define II20K_MOD_STATUS_IRQ_MOD1 (1 << 7)
48 #define II20K_MOD_STATUS_IRQ_MOD2 (1 << 6)
49 #define II20K_MOD_STATUS_IRQ_MOD3 (1 << 5)
50 #define II20K_DIO0_REG 0x80
51 #define II20K_DIO1_REG 0x81
52 #define II20K_DIR_ENA_REG 0x82
53 #define II20K_DIR_DIO3_OUT (1 << 7)
54 #define II20K_DIR_DIO2_OUT (1 << 6)
55 #define II20K_BUF_DISAB_DIO3 (1 << 5)
56 #define II20K_BUF_DISAB_DIO2 (1 << 4)
57 #define II20K_DIR_DIO1_OUT (1 << 3)
58 #define II20K_DIR_DIO0_OUT (1 << 2)
59 #define II20K_BUF_DISAB_DIO1 (1 << 1)
60 #define II20K_BUF_DISAB_DIO0 (1 << 0)
61 #define II20K_CTRL01_REG 0x83
62 #define II20K_CTRL01_SET (1 << 7)
63 #define II20K_CTRL01_DIO0_IN (1 << 4)
64 #define II20K_CTRL01_DIO1_IN (1 << 1)
65 #define II20K_DIO2_REG 0xc0
66 #define II20K_DIO3_REG 0xc1
67 #define II20K_CTRL23_REG 0xc3
68 #define II20K_CTRL23_SET (1 << 7)
69 #define II20K_CTRL23_DIO2_IN (1 << 4)
70 #define II20K_CTRL23_DIO3_IN (1 << 1)
71
72 #define II20K_ID_PCI20006M_1 0xe2 /* 1 AO channels */
73 #define II20K_ID_PCI20006M_2 0xe3 /* 2 AO channels */
74 #define II20K_AO_STRB_REG(x) (0x0b + ((x) * 0x08))
75 #define II20K_AO_LSB_REG(x) (0x0d + ((x) * 0x08))
76 #define II20K_AO_MSB_REG(x) (0x0e + ((x) * 0x08))
77 #define II20K_AO_STRB_BOTH_REG 0x1b
78
79 #define II20K_ID_PCI20341M_1 0x77 /* 4 AI channels */
80 #define II20K_AI_STATUS_CMD_REG 0x01
81 #define II20K_AI_STATUS_CMD_BUSY (1 << 7)
82 #define II20K_AI_STATUS_CMD_HW_ENA (1 << 1)
83 #define II20K_AI_STATUS_CMD_EXT_START (1 << 0)
84 #define II20K_AI_LSB_REG 0x02
85 #define II20K_AI_MSB_REG 0x03
86 #define II20K_AI_PACER_RESET_REG 0x04
87 #define II20K_AI_16BIT_DATA_REG 0x06
88 #define II20K_AI_CONF_REG 0x10
89 #define II20K_AI_CONF_ENA (1 << 2)
90 #define II20K_AI_OPT_REG 0x11
91 #define II20K_AI_OPT_TRIG_ENA (1 << 5)
92 #define II20K_AI_OPT_TRIG_INV (1 << 4)
93 #define II20K_AI_OPT_TIMEBASE(x) (((x) & 0x3) << 1)
94 #define II20K_AI_OPT_BURST_MODE (1 << 0)
95 #define II20K_AI_STATUS_REG 0x12
96 #define II20K_AI_STATUS_INT (1 << 7)
97 #define II20K_AI_STATUS_TRIG (1 << 6)
98 #define II20K_AI_STATUS_TRIG_ENA (1 << 5)
99 #define II20K_AI_STATUS_PACER_ERR (1 << 2)
100 #define II20K_AI_STATUS_DATA_ERR (1 << 1)
101 #define II20K_AI_STATUS_SET_TIME_ERR (1 << 0)
102 #define II20K_AI_LAST_CHAN_ADDR_REG 0x13
103 #define II20K_AI_CUR_ADDR_REG 0x14
104 #define II20K_AI_SET_TIME_REG 0x15
105 #define II20K_AI_DELAY_LSB_REG 0x16
106 #define II20K_AI_DELAY_MSB_REG 0x17
107 #define II20K_AI_CHAN_ADV_REG 0x18
108 #define II20K_AI_CHAN_RESET_REG 0x19
109 #define II20K_AI_START_TRIG_REG 0x1a
110 #define II20K_AI_COUNT_RESET_REG 0x1b
111 #define II20K_AI_CHANLIST_REG 0x80
112 #define II20K_AI_CHANLIST_ONBOARD_ONLY (1 << 5)
113 #define II20K_AI_CHANLIST_GAIN(x) (((x) & 0x3) << 3)
114 #define II20K_AI_CHANLIST_MUX_ENA (1 << 2)
115 #define II20K_AI_CHANLIST_CHAN(x) (((x) & 0x3) << 0)
116 #define II20K_AI_CHANLIST_LEN 0x80
117
118 /* the AO range is set by jumpers on the 20006M module */
119 static const struct comedi_lrange ii20k_ao_ranges = {
120 3, {
121 BIP_RANGE(5), /* Chan 0 - W1/W3 in Chan 1 - W2/W4 in */
122 UNI_RANGE(10), /* Chan 0 - W1/W3 out Chan 1 - W2/W4 in */
123 BIP_RANGE(10) /* Chan 0 - W1/W3 in Chan 1 - W2/W4 out */
124 }
125 };
126
127 static const struct comedi_lrange ii20k_ai_ranges = {
128 4, {
129 BIP_RANGE(5), /* gain 1 */
130 BIP_RANGE(0.5), /* gain 10 */
131 BIP_RANGE(0.05), /* gain 100 */
132 BIP_RANGE(0.025) /* gain 200 */
133 },
134 };
135
136 static void __iomem *ii20k_module_iobase(struct comedi_device *dev,
137 struct comedi_subdevice *s)
138 {
139 return dev->mmio + (s->index + 1) * II20K_MOD_OFFSET;
140 }
141
142 static int ii20k_ao_insn_write(struct comedi_device *dev,
143 struct comedi_subdevice *s,
144 struct comedi_insn *insn,
145 unsigned int *data)
146 {
147 void __iomem *iobase = ii20k_module_iobase(dev, s);
148 unsigned int chan = CR_CHAN(insn->chanspec);
149 int i;
150
151 for (i = 0; i < insn->n; i++) {
152 unsigned int val = data[i];
153
154 s->readback[chan] = val;
155
156 /* munge data */
157 val += ((s->maxdata + 1) >> 1);
158 val &= s->maxdata;
159
160 writeb(val & 0xff, iobase + II20K_AO_LSB_REG(chan));
161 writeb((val >> 8) & 0xff, iobase + II20K_AO_MSB_REG(chan));
162 writeb(0x00, iobase + II20K_AO_STRB_REG(chan));
163 }
164
165 return insn->n;
166 }
167
168 static int ii20k_ai_eoc(struct comedi_device *dev,
169 struct comedi_subdevice *s,
170 struct comedi_insn *insn,
171 unsigned long context)
172 {
173 void __iomem *iobase = ii20k_module_iobase(dev, s);
174 unsigned char status;
175
176 status = readb(iobase + II20K_AI_STATUS_REG);
177 if ((status & II20K_AI_STATUS_INT) == 0)
178 return 0;
179 return -EBUSY;
180 }
181
182 static void ii20k_ai_setup(struct comedi_device *dev,
183 struct comedi_subdevice *s,
184 unsigned int chanspec)
185 {
186 void __iomem *iobase = ii20k_module_iobase(dev, s);
187 unsigned int chan = CR_CHAN(chanspec);
188 unsigned int range = CR_RANGE(chanspec);
189 unsigned char val;
190
191 /* initialize module */
192 writeb(II20K_AI_CONF_ENA, iobase + II20K_AI_CONF_REG);
193
194 /* software conversion */
195 writeb(0, iobase + II20K_AI_STATUS_CMD_REG);
196
197 /* set the time base for the settling time counter based on the gain */
198 val = (range < 3) ? II20K_AI_OPT_TIMEBASE(0) : II20K_AI_OPT_TIMEBASE(2);
199 writeb(val, iobase + II20K_AI_OPT_REG);
200
201 /* set the settling time counter based on the gain */
202 val = (range < 2) ? 0x58 : (range < 3) ? 0x93 : 0x99;
203 writeb(val, iobase + II20K_AI_SET_TIME_REG);
204
205 /* set number of input channels */
206 writeb(1, iobase + II20K_AI_LAST_CHAN_ADDR_REG);
207
208 /* set the channel list byte */
209 val = II20K_AI_CHANLIST_ONBOARD_ONLY |
210 II20K_AI_CHANLIST_MUX_ENA |
211 II20K_AI_CHANLIST_GAIN(range) |
212 II20K_AI_CHANLIST_CHAN(chan);
213 writeb(val, iobase + II20K_AI_CHANLIST_REG);
214
215 /* reset settling time counter and trigger delay counter */
216 writeb(0, iobase + II20K_AI_COUNT_RESET_REG);
217
218 /* reset channel scanner */
219 writeb(0, iobase + II20K_AI_CHAN_RESET_REG);
220 }
221
222 static int ii20k_ai_insn_read(struct comedi_device *dev,
223 struct comedi_subdevice *s,
224 struct comedi_insn *insn,
225 unsigned int *data)
226 {
227 void __iomem *iobase = ii20k_module_iobase(dev, s);
228 int ret;
229 int i;
230
231 ii20k_ai_setup(dev, s, insn->chanspec);
232
233 for (i = 0; i < insn->n; i++) {
234 unsigned int val;
235
236 /* generate a software start convert signal */
237 readb(iobase + II20K_AI_PACER_RESET_REG);
238
239 ret = comedi_timeout(dev, s, insn, ii20k_ai_eoc, 0);
240 if (ret)
241 return ret;
242
243 val = readb(iobase + II20K_AI_LSB_REG);
244 val |= (readb(iobase + II20K_AI_MSB_REG) << 8);
245
246 /* munge two's complement data */
247 val += ((s->maxdata + 1) >> 1);
248 val &= s->maxdata;
249
250 data[i] = val;
251 }
252
253 return insn->n;
254 }
255
256 static void ii20k_dio_config(struct comedi_device *dev,
257 struct comedi_subdevice *s)
258 {
259 unsigned char ctrl01 = 0;
260 unsigned char ctrl23 = 0;
261 unsigned char dir_ena = 0;
262
263 /* port 0 - channels 0-7 */
264 if (s->io_bits & 0x000000ff) {
265 /* output port */
266 ctrl01 &= ~II20K_CTRL01_DIO0_IN;
267 dir_ena &= ~II20K_BUF_DISAB_DIO0;
268 dir_ena |= II20K_DIR_DIO0_OUT;
269 } else {
270 /* input port */
271 ctrl01 |= II20K_CTRL01_DIO0_IN;
272 dir_ena &= ~II20K_DIR_DIO0_OUT;
273 }
274
275 /* port 1 - channels 8-15 */
276 if (s->io_bits & 0x0000ff00) {
277 /* output port */
278 ctrl01 &= ~II20K_CTRL01_DIO1_IN;
279 dir_ena &= ~II20K_BUF_DISAB_DIO1;
280 dir_ena |= II20K_DIR_DIO1_OUT;
281 } else {
282 /* input port */
283 ctrl01 |= II20K_CTRL01_DIO1_IN;
284 dir_ena &= ~II20K_DIR_DIO1_OUT;
285 }
286
287 /* port 2 - channels 16-23 */
288 if (s->io_bits & 0x00ff0000) {
289 /* output port */
290 ctrl23 &= ~II20K_CTRL23_DIO2_IN;
291 dir_ena &= ~II20K_BUF_DISAB_DIO2;
292 dir_ena |= II20K_DIR_DIO2_OUT;
293 } else {
294 /* input port */
295 ctrl23 |= II20K_CTRL23_DIO2_IN;
296 dir_ena &= ~II20K_DIR_DIO2_OUT;
297 }
298
299 /* port 3 - channels 24-31 */
300 if (s->io_bits & 0xff000000) {
301 /* output port */
302 ctrl23 &= ~II20K_CTRL23_DIO3_IN;
303 dir_ena &= ~II20K_BUF_DISAB_DIO3;
304 dir_ena |= II20K_DIR_DIO3_OUT;
305 } else {
306 /* input port */
307 ctrl23 |= II20K_CTRL23_DIO3_IN;
308 dir_ena &= ~II20K_DIR_DIO3_OUT;
309 }
310
311 ctrl23 |= II20K_CTRL01_SET;
312 ctrl23 |= II20K_CTRL23_SET;
313
314 /* order is important */
315 writeb(ctrl01, dev->mmio + II20K_CTRL01_REG);
316 writeb(ctrl23, dev->mmio + II20K_CTRL23_REG);
317 writeb(dir_ena, dev->mmio + II20K_DIR_ENA_REG);
318 }
319
320 static int ii20k_dio_insn_config(struct comedi_device *dev,
321 struct comedi_subdevice *s,
322 struct comedi_insn *insn,
323 unsigned int *data)
324 {
325 unsigned int chan = CR_CHAN(insn->chanspec);
326 unsigned int mask;
327 int ret;
328
329 if (chan < 8)
330 mask = 0x000000ff;
331 else if (chan < 16)
332 mask = 0x0000ff00;
333 else if (chan < 24)
334 mask = 0x00ff0000;
335 else
336 mask = 0xff000000;
337
338 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
339 if (ret)
340 return ret;
341
342 ii20k_dio_config(dev, s);
343
344 return insn->n;
345 }
346
347 static int ii20k_dio_insn_bits(struct comedi_device *dev,
348 struct comedi_subdevice *s,
349 struct comedi_insn *insn,
350 unsigned int *data)
351 {
352 unsigned int mask;
353
354 mask = comedi_dio_update_state(s, data);
355 if (mask) {
356 if (mask & 0x000000ff)
357 writeb((s->state >> 0) & 0xff,
358 dev->mmio + II20K_DIO0_REG);
359 if (mask & 0x0000ff00)
360 writeb((s->state >> 8) & 0xff,
361 dev->mmio + II20K_DIO1_REG);
362 if (mask & 0x00ff0000)
363 writeb((s->state >> 16) & 0xff,
364 dev->mmio + II20K_DIO2_REG);
365 if (mask & 0xff000000)
366 writeb((s->state >> 24) & 0xff,
367 dev->mmio + II20K_DIO3_REG);
368 }
369
370 data[1] = readb(dev->mmio + II20K_DIO0_REG);
371 data[1] |= readb(dev->mmio + II20K_DIO1_REG) << 8;
372 data[1] |= readb(dev->mmio + II20K_DIO2_REG) << 16;
373 data[1] |= readb(dev->mmio + II20K_DIO3_REG) << 24;
374
375 return insn->n;
376 }
377
378 static int ii20k_init_module(struct comedi_device *dev,
379 struct comedi_subdevice *s)
380 {
381 void __iomem *iobase = ii20k_module_iobase(dev, s);
382 unsigned char id;
383 int ret;
384
385 id = readb(iobase + II20K_ID_REG);
386 switch (id) {
387 case II20K_ID_PCI20006M_1:
388 case II20K_ID_PCI20006M_2:
389 /* Analog Output subdevice */
390 s->type = COMEDI_SUBD_AO;
391 s->subdev_flags = SDF_WRITABLE;
392 s->n_chan = (id == II20K_ID_PCI20006M_2) ? 2 : 1;
393 s->maxdata = 0xffff;
394 s->range_table = &ii20k_ao_ranges;
395 s->insn_write = ii20k_ao_insn_write;
396
397 ret = comedi_alloc_subdev_readback(s);
398 if (ret)
399 return ret;
400 break;
401 case II20K_ID_PCI20341M_1:
402 /* Analog Input subdevice */
403 s->type = COMEDI_SUBD_AI;
404 s->subdev_flags = SDF_READABLE | SDF_DIFF;
405 s->n_chan = 4;
406 s->maxdata = 0xffff;
407 s->range_table = &ii20k_ai_ranges;
408 s->insn_read = ii20k_ai_insn_read;
409 break;
410 default:
411 s->type = COMEDI_SUBD_UNUSED;
412 break;
413 }
414
415 return 0;
416 }
417
418 static int ii20k_attach(struct comedi_device *dev,
419 struct comedi_devconfig *it)
420 {
421 struct comedi_subdevice *s;
422 unsigned int membase;
423 unsigned char id;
424 bool has_dio;
425 int ret;
426
427 membase = it->options[0];
428 if (!membase || (membase & ~(0x100000 - II20K_SIZE))) {
429 dev_warn(dev->class_dev,
430 "%s: invalid memory address specified\n",
431 dev->board_name);
432 return -EINVAL;
433 }
434
435 if (!request_mem_region(membase, II20K_SIZE, dev->board_name)) {
436 dev_warn(dev->class_dev, "%s: I/O mem conflict (%#x,%u)\n",
437 dev->board_name, membase, II20K_SIZE);
438 return -EIO;
439 }
440 dev->iobase = membase; /* actually, a memory address */
441
442 dev->mmio = ioremap(membase, II20K_SIZE);
443 if (!dev->mmio)
444 return -ENOMEM;
445
446 id = readb(dev->mmio + II20K_ID_REG);
447 switch (id & II20K_ID_MASK) {
448 case II20K_ID_PCI20001C_1A:
449 has_dio = false;
450 break;
451 case II20K_ID_PCI20001C_2A:
452 has_dio = true;
453 break;
454 default:
455 return -ENODEV;
456 }
457
458 ret = comedi_alloc_subdevices(dev, 4);
459 if (ret)
460 return ret;
461
462 s = &dev->subdevices[0];
463 if (id & II20K_ID_MOD1_EMPTY) {
464 s->type = COMEDI_SUBD_UNUSED;
465 } else {
466 ret = ii20k_init_module(dev, s);
467 if (ret)
468 return ret;
469 }
470
471 s = &dev->subdevices[1];
472 if (id & II20K_ID_MOD2_EMPTY) {
473 s->type = COMEDI_SUBD_UNUSED;
474 } else {
475 ret = ii20k_init_module(dev, s);
476 if (ret)
477 return ret;
478 }
479
480 s = &dev->subdevices[2];
481 if (id & II20K_ID_MOD3_EMPTY) {
482 s->type = COMEDI_SUBD_UNUSED;
483 } else {
484 ret = ii20k_init_module(dev, s);
485 if (ret)
486 return ret;
487 }
488
489 /* Digital I/O subdevice */
490 s = &dev->subdevices[3];
491 if (has_dio) {
492 s->type = COMEDI_SUBD_DIO;
493 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
494 s->n_chan = 32;
495 s->maxdata = 1;
496 s->range_table = &range_digital;
497 s->insn_bits = ii20k_dio_insn_bits;
498 s->insn_config = ii20k_dio_insn_config;
499
500 /* default all channels to input */
501 ii20k_dio_config(dev, s);
502 } else {
503 s->type = COMEDI_SUBD_UNUSED;
504 }
505
506 return 0;
507 }
508
509 static void ii20k_detach(struct comedi_device *dev)
510 {
511 if (dev->mmio)
512 iounmap(dev->mmio);
513 if (dev->iobase) /* actually, a memory address */
514 release_mem_region(dev->iobase, II20K_SIZE);
515 }
516
517 static struct comedi_driver ii20k_driver = {
518 .driver_name = "ii_pci20kc",
519 .module = THIS_MODULE,
520 .attach = ii20k_attach,
521 .detach = ii20k_detach,
522 };
523 module_comedi_driver(ii20k_driver);
524
525 MODULE_AUTHOR("Comedi http://www.comedi.org");
526 MODULE_DESCRIPTION("Comedi low-level driver");
527 MODULE_LICENSE("GPL");
This page took 0.081243 seconds and 5 git commands to generate.