1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * rti802.c |
4 | * Comedi driver for Analog Devices RTI-802 board |
5 | * |
6 | * COMEDI - Linux Control and Measurement Device Interface |
7 | * Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se> |
8 | */ |
9 | |
10 | /* |
11 | * Driver: rti802 |
12 | * Description: Analog Devices RTI-802 |
13 | * Author: Anders Blomdell <anders.blomdell@control.lth.se> |
14 | * Devices: [Analog Devices] RTI-802 (rti802) |
15 | * Status: works |
16 | * |
17 | * Configuration Options: |
18 | * [0] - i/o base |
19 | * [1] - unused |
20 | * [2,4,6,8,10,12,14,16] - dac#[0-7] 0=two's comp, 1=straight |
21 | * [3,5,7,9,11,13,15,17] - dac#[0-7] 0=bipolar, 1=unipolar |
22 | */ |
23 | |
24 | #include <linux/module.h> |
25 | #include <linux/comedi/comedidev.h> |
26 | |
27 | /* |
28 | * Register I/O map |
29 | */ |
30 | #define RTI802_SELECT 0x00 |
31 | #define RTI802_DATALOW 0x01 |
32 | #define RTI802_DATAHIGH 0x02 |
33 | |
34 | struct rti802_private { |
35 | enum { |
36 | dac_2comp, dac_straight |
37 | } dac_coding[8]; |
38 | const struct comedi_lrange *range_type_list[8]; |
39 | }; |
40 | |
41 | static int rti802_ao_insn_write(struct comedi_device *dev, |
42 | struct comedi_subdevice *s, |
43 | struct comedi_insn *insn, |
44 | unsigned int *data) |
45 | { |
46 | struct rti802_private *devpriv = dev->private; |
47 | unsigned int chan = CR_CHAN(insn->chanspec); |
48 | int i; |
49 | |
50 | outb(value: chan, port: dev->iobase + RTI802_SELECT); |
51 | |
52 | for (i = 0; i < insn->n; i++) { |
53 | unsigned int val = data[i]; |
54 | |
55 | s->readback[chan] = val; |
56 | |
57 | /* munge offset binary to two's complement if needed */ |
58 | if (devpriv->dac_coding[chan] == dac_2comp) |
59 | val = comedi_offset_munge(s, val); |
60 | |
61 | outb(value: val & 0xff, port: dev->iobase + RTI802_DATALOW); |
62 | outb(value: (val >> 8) & 0xff, port: dev->iobase + RTI802_DATAHIGH); |
63 | } |
64 | |
65 | return insn->n; |
66 | } |
67 | |
68 | static int rti802_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
69 | { |
70 | struct rti802_private *devpriv; |
71 | struct comedi_subdevice *s; |
72 | int i; |
73 | int ret; |
74 | |
75 | ret = comedi_request_region(dev, start: it->options[0], len: 0x04); |
76 | if (ret) |
77 | return ret; |
78 | |
79 | devpriv = comedi_alloc_devpriv(dev, size: sizeof(*devpriv)); |
80 | if (!devpriv) |
81 | return -ENOMEM; |
82 | |
83 | ret = comedi_alloc_subdevices(dev, num_subdevices: 1); |
84 | if (ret) |
85 | return ret; |
86 | |
87 | /* Analog Output subdevice */ |
88 | s = &dev->subdevices[0]; |
89 | s->type = COMEDI_SUBD_AO; |
90 | s->subdev_flags = SDF_WRITABLE; |
91 | s->maxdata = 0xfff; |
92 | s->n_chan = 8; |
93 | s->insn_write = rti802_ao_insn_write; |
94 | |
95 | ret = comedi_alloc_subdev_readback(s); |
96 | if (ret) |
97 | return ret; |
98 | |
99 | s->range_table_list = devpriv->range_type_list; |
100 | for (i = 0; i < 8; i++) { |
101 | devpriv->dac_coding[i] = (it->options[3 + 2 * i]) |
102 | ? (dac_straight) : (dac_2comp); |
103 | devpriv->range_type_list[i] = (it->options[2 + 2 * i]) |
104 | ? &range_unipolar10 : &range_bipolar10; |
105 | } |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | static struct comedi_driver rti802_driver = { |
111 | .driver_name = "rti802" , |
112 | .module = THIS_MODULE, |
113 | .attach = rti802_attach, |
114 | .detach = comedi_legacy_detach, |
115 | }; |
116 | module_comedi_driver(rti802_driver); |
117 | |
118 | MODULE_AUTHOR("Comedi https://www.comedi.org" ); |
119 | MODULE_DESCRIPTION("Comedi driver for Analog Devices RTI-802 board" ); |
120 | MODULE_LICENSE("GPL" ); |
121 | |