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