]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blob - drivers/staging/comedi/drivers/cb_das16_cs.c
481b2f335ea37a7f72b37792ac97a9fd426d8c5a
[mirror_ubuntu-hirsute-kernel.git] / drivers / staging / comedi / drivers / cb_das16_cs.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * cb_das16_cs.c
4 * Driver for Computer Boards PC-CARD DAS16/16.
5 *
6 * COMEDI - Linux Control and Measurement Device Interface
7 * Copyright (C) 2000, 2001, 2002 David A. Schleef <ds@schleef.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * PCMCIA support code for this driver is adapted from the dummy_cs.c
20 * driver of the Linux PCMCIA Card Services package.
21 *
22 * The initial developer of the original code is David A. Hinds
23 * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
24 * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
25 */
26
27 /*
28 * Driver: cb_das16_cs
29 * Description: Computer Boards PC-CARD DAS16/16
30 * Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs),
31 * PC-CARD DAS16/16-AO
32 * Author: ds
33 * Updated: Mon, 04 Nov 2002 20:04:21 -0800
34 * Status: experimental
35 */
36
37 #include <linux/module.h>
38 #include <linux/interrupt.h>
39 #include <linux/delay.h>
40
41 #include "../comedi_pcmcia.h"
42
43 #include "comedi_8254.h"
44
45 /*
46 * Register I/O map
47 */
48 #define DAS16CS_AI_DATA_REG 0x00
49 #define DAS16CS_AI_MUX_REG 0x02
50 #define DAS16CS_AI_MUX_HI_CHAN(x) (((x) & 0xf) << 4)
51 #define DAS16CS_AI_MUX_LO_CHAN(x) (((x) & 0xf) << 0)
52 #define DAS16CS_AI_MUX_SINGLE_CHAN(x) (DAS16CS_AI_MUX_HI_CHAN(x) | \
53 DAS16CS_AI_MUX_LO_CHAN(x))
54 #define DAS16CS_MISC1_REG 0x04
55 #define DAS16CS_MISC1_INTE BIT(15) /* 1=enable; 0=disable */
56 #define DAS16CS_MISC1_INT_SRC(x) (((x) & 0x7) << 12) /* interrupt src */
57 #define DAS16CS_MISC1_INT_SRC_NONE DAS16CS_MISC1_INT_SRC(0)
58 #define DAS16CS_MISC1_INT_SRC_PACER DAS16CS_MISC1_INT_SRC(1)
59 #define DAS16CS_MISC1_INT_SRC_EXT DAS16CS_MISC1_INT_SRC(2)
60 #define DAS16CS_MISC1_INT_SRC_FNE DAS16CS_MISC1_INT_SRC(3)
61 #define DAS16CS_MISC1_INT_SRC_FHF DAS16CS_MISC1_INT_SRC(4)
62 #define DAS16CS_MISC1_INT_SRC_EOS DAS16CS_MISC1_INT_SRC(5)
63 #define DAS16CS_MISC1_INT_SRC_MASK DAS16CS_MISC1_INT_SRC(7)
64 #define DAS16CS_MISC1_OVR BIT(10) /* ro - 1=FIFO overflow */
65 #define DAS16CS_MISC1_AI_CONV(x) (((x) & 0x3) << 8) /* AI convert src */
66 #define DAS16CS_MISC1_AI_CONV_SW DAS16CS_MISC1_AI_CONV(0)
67 #define DAS16CS_MISC1_AI_CONV_EXT_NEG DAS16CS_MISC1_AI_CONV(1)
68 #define DAS16CS_MISC1_AI_CONV_EXT_POS DAS16CS_MISC1_AI_CONV(2)
69 #define DAS16CS_MISC1_AI_CONV_PACER DAS16CS_MISC1_AI_CONV(3)
70 #define DAS16CS_MISC1_AI_CONV_MASK DAS16CS_MISC1_AI_CONV(3)
71 #define DAS16CS_MISC1_EOC BIT(7) /* ro - 0=busy; 1=ready */
72 #define DAS16CS_MISC1_SEDIFF BIT(5) /* 0=diff; 1=se */
73 #define DAS16CS_MISC1_INTB BIT(4) /* ro - 0=latched; 1=cleared */
74 #define DAS16CS_MISC1_MA_MASK (0xf << 0) /* ro - current ai mux */
75 #define DAS16CS_MISC1_DAC1CS BIT(3) /* wo - DAC1 chip select */
76 #define DAS16CS_MISC1_DACCLK BIT(2) /* wo - Serial DAC clock */
77 #define DAS16CS_MISC1_DACSD BIT(1) /* wo - Serial DAC data */
78 #define DAS16CS_MISC1_DAC0CS BIT(0) /* wo - DAC0 chip select */
79 #define DAS16CS_MISC1_DAC_MASK (0x0f << 0)
80 #define DAS16CS_MISC2_REG 0x06
81 #define DAS16CS_MISC2_BME BIT(14) /* 1=burst enable; 0=disable */
82 #define DAS16CS_MISC2_AI_GAIN(x) (((x) & 0xf) << 8) /* AI gain */
83 #define DAS16CS_MISC2_AI_GAIN_1 DAS16CS_MISC2_AI_GAIN(4) /* +/-10V */
84 #define DAS16CS_MISC2_AI_GAIN_2 DAS16CS_MISC2_AI_GAIN(0) /* +/-5V */
85 #define DAS16CS_MISC2_AI_GAIN_4 DAS16CS_MISC2_AI_GAIN(1) /* +/-2.5V */
86 #define DAS16CS_MISC2_AI_GAIN_8 DAS16CS_MISC2_AI_GAIN(2) /* +-1.25V */
87 #define DAS16CS_MISC2_AI_GAIN_MASK DAS16CS_MISC2_AI_GAIN(0xf)
88 #define DAS16CS_MISC2_UDIR BIT(7) /* 1=dio7:4 output; 0=input */
89 #define DAS16CS_MISC2_LDIR BIT(6) /* 1=dio3:0 output; 0=input */
90 #define DAS16CS_MISC2_TRGPOL BIT(5) /* 1=active lo; 0=hi */
91 #define DAS16CS_MISC2_TRGSEL BIT(4) /* 1=edge; 0=level */
92 #define DAS16CS_MISC2_FFNE BIT(3) /* ro - 1=FIFO not empty */
93 #define DAS16CS_MISC2_TRGCLR BIT(3) /* wo - 1=clr (monstable) */
94 #define DAS16CS_MISC2_CLK2 BIT(2) /* 1=10 MHz; 0=1 MHz */
95 #define DAS16CS_MISC2_CTR1 BIT(1) /* 1=int. 100 kHz; 0=ext. clk */
96 #define DAS16CS_MISC2_TRG0 BIT(0) /* 1=enable; 0=disable */
97 #define DAS16CS_TIMER_BASE 0x08
98 #define DAS16CS_DIO_REG 0x10
99
100 struct das16cs_board {
101 const char *name;
102 int device_id;
103 unsigned int has_ao:1;
104 unsigned int has_4dio:1;
105 };
106
107 static const struct das16cs_board das16cs_boards[] = {
108 {
109 .name = "PC-CARD DAS16/16-AO",
110 .device_id = 0x0039,
111 .has_ao = 1,
112 .has_4dio = 1,
113 }, {
114 .name = "PCM-DAS16s/16",
115 .device_id = 0x4009,
116 }, {
117 .name = "PC-CARD DAS16/16",
118 .device_id = 0x0000, /* unknown */
119 },
120 };
121
122 struct das16cs_private {
123 unsigned short misc1;
124 unsigned short misc2;
125 };
126
127 static const struct comedi_lrange das16cs_ai_range = {
128 4, {
129 BIP_RANGE(10),
130 BIP_RANGE(5),
131 BIP_RANGE(2.5),
132 BIP_RANGE(1.25),
133 }
134 };
135
136 static int das16cs_ai_eoc(struct comedi_device *dev,
137 struct comedi_subdevice *s,
138 struct comedi_insn *insn,
139 unsigned long context)
140 {
141 unsigned int status;
142
143 status = inw(dev->iobase + DAS16CS_MISC1_REG);
144 if (status & DAS16CS_MISC1_EOC)
145 return 0;
146 return -EBUSY;
147 }
148
149 static int das16cs_ai_insn_read(struct comedi_device *dev,
150 struct comedi_subdevice *s,
151 struct comedi_insn *insn,
152 unsigned int *data)
153 {
154 struct das16cs_private *devpriv = dev->private;
155 int chan = CR_CHAN(insn->chanspec);
156 int range = CR_RANGE(insn->chanspec);
157 int aref = CR_AREF(insn->chanspec);
158 int ret;
159 int i;
160
161 outw(DAS16CS_AI_MUX_SINGLE_CHAN(chan),
162 dev->iobase + DAS16CS_AI_MUX_REG);
163
164 /* disable interrupts, software convert */
165 devpriv->misc1 &= ~(DAS16CS_MISC1_INTE | DAS16CS_MISC1_INT_SRC_MASK |
166 DAS16CS_MISC1_AI_CONV_MASK);
167 if (aref == AREF_DIFF)
168 devpriv->misc1 &= ~DAS16CS_MISC1_SEDIFF;
169 else
170 devpriv->misc1 |= DAS16CS_MISC1_SEDIFF;
171 outw(devpriv->misc1, dev->iobase + DAS16CS_MISC1_REG);
172
173 devpriv->misc2 &= ~(DAS16CS_MISC2_BME | DAS16CS_MISC2_AI_GAIN_MASK);
174 switch (range) {
175 case 0:
176 devpriv->misc2 |= DAS16CS_MISC2_AI_GAIN_1;
177 break;
178 case 1:
179 devpriv->misc2 |= DAS16CS_MISC2_AI_GAIN_2;
180 break;
181 case 2:
182 devpriv->misc2 |= DAS16CS_MISC2_AI_GAIN_4;
183 break;
184 case 3:
185 devpriv->misc2 |= DAS16CS_MISC2_AI_GAIN_8;
186 break;
187 }
188 outw(devpriv->misc2, dev->iobase + DAS16CS_MISC2_REG);
189
190 for (i = 0; i < insn->n; i++) {
191 outw(0, dev->iobase + DAS16CS_AI_DATA_REG);
192
193 ret = comedi_timeout(dev, s, insn, das16cs_ai_eoc, 0);
194 if (ret)
195 return ret;
196
197 data[i] = inw(dev->iobase + DAS16CS_AI_DATA_REG);
198 }
199
200 return i;
201 }
202
203 static int das16cs_ao_insn_write(struct comedi_device *dev,
204 struct comedi_subdevice *s,
205 struct comedi_insn *insn,
206 unsigned int *data)
207 {
208 struct das16cs_private *devpriv = dev->private;
209 unsigned int chan = CR_CHAN(insn->chanspec);
210 unsigned int val = s->readback[chan];
211 unsigned short misc1;
212 int bit;
213 int i;
214
215 for (i = 0; i < insn->n; i++) {
216 val = data[i];
217
218 outw(devpriv->misc1, dev->iobase + DAS16CS_MISC1_REG);
219 udelay(1);
220
221 /* raise the DACxCS line for the non-selected channel */
222 misc1 = devpriv->misc1 & ~DAS16CS_MISC1_DAC_MASK;
223 if (chan)
224 misc1 |= DAS16CS_MISC1_DAC0CS;
225 else
226 misc1 |= DAS16CS_MISC1_DAC1CS;
227
228 outw(misc1, dev->iobase + DAS16CS_MISC1_REG);
229 udelay(1);
230
231 for (bit = 15; bit >= 0; bit--) {
232 if ((val >> bit) & 0x1)
233 misc1 |= DAS16CS_MISC1_DACSD;
234 else
235 misc1 &= ~DAS16CS_MISC1_DACSD;
236 outw(misc1, dev->iobase + DAS16CS_MISC1_REG);
237 udelay(1);
238 outw(misc1 | DAS16CS_MISC1_DACCLK,
239 dev->iobase + DAS16CS_MISC1_REG);
240 udelay(1);
241 }
242 /*
243 * Make both DAC0CS and DAC1CS high to load
244 * the new data and update analog the output
245 */
246 outw(misc1 | DAS16CS_MISC1_DAC0CS | DAS16CS_MISC1_DAC1CS,
247 dev->iobase + DAS16CS_MISC1_REG);
248 }
249 s->readback[chan] = val;
250
251 return insn->n;
252 }
253
254 static int das16cs_dio_insn_bits(struct comedi_device *dev,
255 struct comedi_subdevice *s,
256 struct comedi_insn *insn,
257 unsigned int *data)
258 {
259 if (comedi_dio_update_state(s, data))
260 outw(s->state, dev->iobase + DAS16CS_DIO_REG);
261
262 data[1] = inw(dev->iobase + DAS16CS_DIO_REG);
263
264 return insn->n;
265 }
266
267 static int das16cs_dio_insn_config(struct comedi_device *dev,
268 struct comedi_subdevice *s,
269 struct comedi_insn *insn,
270 unsigned int *data)
271 {
272 struct das16cs_private *devpriv = dev->private;
273 unsigned int chan = CR_CHAN(insn->chanspec);
274 unsigned int mask;
275 int ret;
276
277 if (chan < 4)
278 mask = 0x0f;
279 else
280 mask = 0xf0;
281
282 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
283 if (ret)
284 return ret;
285
286 if (s->io_bits & 0xf0)
287 devpriv->misc2 |= DAS16CS_MISC2_UDIR;
288 else
289 devpriv->misc2 &= ~DAS16CS_MISC2_UDIR;
290 if (s->io_bits & 0x0f)
291 devpriv->misc2 |= DAS16CS_MISC2_LDIR;
292 else
293 devpriv->misc2 &= ~DAS16CS_MISC2_LDIR;
294 outw(devpriv->misc2, dev->iobase + DAS16CS_MISC2_REG);
295
296 return insn->n;
297 }
298
299 static int das16cs_counter_insn_config(struct comedi_device *dev,
300 struct comedi_subdevice *s,
301 struct comedi_insn *insn,
302 unsigned int *data)
303 {
304 struct das16cs_private *devpriv = dev->private;
305
306 switch (data[0]) {
307 case INSN_CONFIG_SET_CLOCK_SRC:
308 switch (data[1]) {
309 case 0: /* internal 100 kHz */
310 devpriv->misc2 |= DAS16CS_MISC2_CTR1;
311 break;
312 case 1: /* external */
313 devpriv->misc2 &= ~DAS16CS_MISC2_CTR1;
314 break;
315 default:
316 return -EINVAL;
317 }
318 outw(devpriv->misc2, dev->iobase + DAS16CS_MISC2_REG);
319 break;
320 case INSN_CONFIG_GET_CLOCK_SRC:
321 if (devpriv->misc2 & DAS16CS_MISC2_CTR1) {
322 data[1] = 0;
323 data[2] = I8254_OSC_BASE_100KHZ;
324 } else {
325 data[1] = 1;
326 data[2] = 0; /* unknown */
327 }
328 break;
329 default:
330 return -EINVAL;
331 }
332
333 return insn->n;
334 }
335
336 static const void *das16cs_find_boardinfo(struct comedi_device *dev,
337 struct pcmcia_device *link)
338 {
339 const struct das16cs_board *board;
340 int i;
341
342 for (i = 0; i < ARRAY_SIZE(das16cs_boards); i++) {
343 board = &das16cs_boards[i];
344 if (board->device_id == link->card_id)
345 return board;
346 }
347
348 return NULL;
349 }
350
351 static int das16cs_auto_attach(struct comedi_device *dev,
352 unsigned long context)
353 {
354 struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
355 const struct das16cs_board *board;
356 struct das16cs_private *devpriv;
357 struct comedi_subdevice *s;
358 int ret;
359
360 board = das16cs_find_boardinfo(dev, link);
361 if (!board)
362 return -ENODEV;
363 dev->board_ptr = board;
364 dev->board_name = board->name;
365
366 link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
367 ret = comedi_pcmcia_enable(dev, NULL);
368 if (ret)
369 return ret;
370 dev->iobase = link->resource[0]->start;
371
372 link->priv = dev;
373
374 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
375 if (!devpriv)
376 return -ENOMEM;
377
378 dev->pacer = comedi_8254_init(dev->iobase + DAS16CS_TIMER_BASE,
379 I8254_OSC_BASE_10MHZ, I8254_IO16, 0);
380 if (!dev->pacer)
381 return -ENOMEM;
382
383 ret = comedi_alloc_subdevices(dev, 4);
384 if (ret)
385 return ret;
386
387 /* Analog Input subdevice */
388 s = &dev->subdevices[0];
389 s->type = COMEDI_SUBD_AI;
390 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
391 s->n_chan = 16;
392 s->maxdata = 0xffff;
393 s->range_table = &das16cs_ai_range;
394 s->insn_read = das16cs_ai_insn_read;
395
396 /* Analog Output subdevice */
397 s = &dev->subdevices[1];
398 if (board->has_ao) {
399 s->type = COMEDI_SUBD_AO;
400 s->subdev_flags = SDF_WRITABLE;
401 s->n_chan = 2;
402 s->maxdata = 0xffff;
403 s->range_table = &range_bipolar10;
404 s->insn_write = &das16cs_ao_insn_write;
405
406 ret = comedi_alloc_subdev_readback(s);
407 if (ret)
408 return ret;
409 } else {
410 s->type = COMEDI_SUBD_UNUSED;
411 }
412
413 /* Digital I/O subdevice */
414 s = &dev->subdevices[2];
415 s->type = COMEDI_SUBD_DIO;
416 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
417 s->n_chan = board->has_4dio ? 4 : 8;
418 s->maxdata = 1;
419 s->range_table = &range_digital;
420 s->insn_bits = das16cs_dio_insn_bits;
421 s->insn_config = das16cs_dio_insn_config;
422
423 /* Counter subdevice (8254) */
424 s = &dev->subdevices[3];
425 comedi_8254_subdevice_init(s, dev->pacer);
426
427 dev->pacer->insn_config = das16cs_counter_insn_config;
428
429 /* counters 1 and 2 are used internally for the pacer */
430 comedi_8254_set_busy(dev->pacer, 1, true);
431 comedi_8254_set_busy(dev->pacer, 2, true);
432
433 return 0;
434 }
435
436 static struct comedi_driver driver_das16cs = {
437 .driver_name = "cb_das16_cs",
438 .module = THIS_MODULE,
439 .auto_attach = das16cs_auto_attach,
440 .detach = comedi_pcmcia_disable,
441 };
442
443 static int das16cs_pcmcia_attach(struct pcmcia_device *link)
444 {
445 return comedi_pcmcia_auto_config(link, &driver_das16cs);
446 }
447
448 static const struct pcmcia_device_id das16cs_id_table[] = {
449 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
450 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
451 PCMCIA_DEVICE_NULL
452 };
453 MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
454
455 static struct pcmcia_driver das16cs_driver = {
456 .name = "cb_das16_cs",
457 .owner = THIS_MODULE,
458 .id_table = das16cs_id_table,
459 .probe = das16cs_pcmcia_attach,
460 .remove = comedi_pcmcia_auto_unconfig,
461 };
462 module_comedi_pcmcia_driver(driver_das16cs, das16cs_driver);
463
464 MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
465 MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16");
466 MODULE_LICENSE("GPL");