1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * adv_pci1723.c |
4 | * Comedi driver for the Advantech PCI-1723 card. |
5 | * |
6 | * COMEDI - Linux Control and Measurement Device Interface |
7 | * Copyright (C) 2000 David A. Schleef <ds@schleef.org> |
8 | */ |
9 | |
10 | /* |
11 | * Driver: adv_pci1723 |
12 | * Description: Advantech PCI-1723 |
13 | * Author: yonggang <rsmgnu@gmail.com>, Ian Abbott <abbotti@mev.co.uk> |
14 | * Devices: [Advantech] PCI-1723 (adv_pci1723) |
15 | * Updated: Mon, 14 Apr 2008 15:12:56 +0100 |
16 | * Status: works |
17 | * |
18 | * Configuration Options: not applicable, uses comedi PCI auto config |
19 | * |
20 | * Subdevice 0 is 8-channel AO, 16-bit, range +/- 10 V. |
21 | * |
22 | * Subdevice 1 is 16-channel DIO. The channels are configurable as |
23 | * input or output in 2 groups (0 to 7, 8 to 15). Configuring any |
24 | * channel implicitly configures all channels in the same group. |
25 | * |
26 | * TODO: |
27 | * 1. Add the two milliamp ranges to the AO subdevice (0 to 20 mA, |
28 | * 4 to 20 mA). |
29 | * 2. Read the initial ranges and values of the AO subdevice at |
30 | * start-up instead of reinitializing them. |
31 | * 3. Implement calibration. |
32 | */ |
33 | |
34 | #include <linux/module.h> |
35 | #include <linux/comedi/comedi_pci.h> |
36 | |
37 | /* |
38 | * PCI Bar 2 I/O Register map (dev->iobase) |
39 | */ |
40 | #define PCI1723_AO_REG(x) (0x00 + ((x) * 2)) |
41 | #define PCI1723_BOARD_ID_REG 0x10 |
42 | #define PCI1723_BOARD_ID_MASK (0xf << 0) |
43 | #define PCI1723_SYNC_CTRL_REG 0x12 |
44 | #define PCI1723_SYNC_CTRL(x) (((x) & 0x1) << 0) |
45 | #define PCI1723_SYNC_CTRL_ASYNC PCI1723_SYNC_CTRL(0) |
46 | #define PCI1723_SYNC_CTRL_SYNC PCI1723_SYNC_CTRL(1) |
47 | #define PCI1723_CTRL_REG 0x14 |
48 | #define PCI1723_CTRL_BUSY BIT(15) |
49 | #define PCI1723_CTRL_INIT BIT(14) |
50 | #define PCI1723_CTRL_SELF BIT(8) |
51 | #define PCI1723_CTRL_IDX(x) (((x) & 0x3) << 6) |
52 | #define PCI1723_CTRL_RANGE(x) (((x) & 0x3) << 4) |
53 | #define PCI1723_CTRL_SEL(x) (((x) & 0x1) << 3) |
54 | #define PCI1723_CTRL_GAIN PCI1723_CTRL_SEL(0) |
55 | #define PCI1723_CTRL_OFFSET PCI1723_CTRL_SEL(1) |
56 | #define PCI1723_CTRL_CHAN(x) (((x) & 0x7) << 0) |
57 | #define PCI1723_CALIB_CTRL_REG 0x16 |
58 | #define PCI1723_CALIB_CTRL_CS BIT(2) |
59 | #define PCI1723_CALIB_CTRL_DAT BIT(1) |
60 | #define PCI1723_CALIB_CTRL_CLK BIT(0) |
61 | #define PCI1723_CALIB_STROBE_REG 0x18 |
62 | #define PCI1723_DIO_CTRL_REG 0x1a |
63 | #define PCI1723_DIO_CTRL_HDIO BIT(1) |
64 | #define PCI1723_DIO_CTRL_LDIO BIT(0) |
65 | #define PCI1723_DIO_DATA_REG 0x1c |
66 | #define PCI1723_CALIB_DATA_REG 0x1e |
67 | #define PCI1723_SYNC_STROBE_REG 0x20 |
68 | #define PCI1723_RESET_AO_STROBE_REG 0x22 |
69 | #define PCI1723_RESET_CALIB_STROBE_REG 0x24 |
70 | #define PCI1723_RANGE_STROBE_REG 0x26 |
71 | #define PCI1723_VREF_REG 0x28 |
72 | #define PCI1723_VREF(x) (((x) & 0x3) << 0) |
73 | #define PCI1723_VREF_NEG10V PCI1723_VREF(0) |
74 | #define PCI1723_VREF_0V PCI1723_VREF(1) |
75 | #define PCI1723_VREF_POS10V PCI1723_VREF(3) |
76 | |
77 | static int pci1723_ao_insn_write(struct comedi_device *dev, |
78 | struct comedi_subdevice *s, |
79 | struct comedi_insn *insn, |
80 | unsigned int *data) |
81 | { |
82 | unsigned int chan = CR_CHAN(insn->chanspec); |
83 | int i; |
84 | |
85 | for (i = 0; i < insn->n; i++) { |
86 | unsigned int val = data[i]; |
87 | |
88 | outw(value: val, port: dev->iobase + PCI1723_AO_REG(chan)); |
89 | s->readback[chan] = val; |
90 | } |
91 | |
92 | return insn->n; |
93 | } |
94 | |
95 | static int pci1723_dio_insn_config(struct comedi_device *dev, |
96 | struct comedi_subdevice *s, |
97 | struct comedi_insn *insn, |
98 | unsigned int *data) |
99 | { |
100 | unsigned int chan = CR_CHAN(insn->chanspec); |
101 | unsigned int mask = (chan < 8) ? 0x00ff : 0xff00; |
102 | unsigned short mode = 0x0000; /* assume output */ |
103 | int ret; |
104 | |
105 | ret = comedi_dio_insn_config(dev, s, insn, data, mask); |
106 | if (ret) |
107 | return ret; |
108 | |
109 | if (!(s->io_bits & 0x00ff)) |
110 | mode |= PCI1723_DIO_CTRL_LDIO; /* low byte input */ |
111 | if (!(s->io_bits & 0xff00)) |
112 | mode |= PCI1723_DIO_CTRL_HDIO; /* high byte input */ |
113 | outw(value: mode, port: dev->iobase + PCI1723_DIO_CTRL_REG); |
114 | |
115 | return insn->n; |
116 | } |
117 | |
118 | static int pci1723_dio_insn_bits(struct comedi_device *dev, |
119 | struct comedi_subdevice *s, |
120 | struct comedi_insn *insn, |
121 | unsigned int *data) |
122 | { |
123 | if (comedi_dio_update_state(s, data)) |
124 | outw(value: s->state, port: dev->iobase + PCI1723_DIO_DATA_REG); |
125 | |
126 | data[1] = inw(port: dev->iobase + PCI1723_DIO_DATA_REG); |
127 | |
128 | return insn->n; |
129 | } |
130 | |
131 | static int pci1723_auto_attach(struct comedi_device *dev, |
132 | unsigned long context_unused) |
133 | { |
134 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
135 | struct comedi_subdevice *s; |
136 | unsigned int val; |
137 | int ret; |
138 | int i; |
139 | |
140 | ret = comedi_pci_enable(dev); |
141 | if (ret) |
142 | return ret; |
143 | dev->iobase = pci_resource_start(pcidev, 2); |
144 | |
145 | ret = comedi_alloc_subdevices(dev, num_subdevices: 2); |
146 | if (ret) |
147 | return ret; |
148 | |
149 | s = &dev->subdevices[0]; |
150 | s->type = COMEDI_SUBD_AO; |
151 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; |
152 | s->n_chan = 8; |
153 | s->maxdata = 0xffff; |
154 | s->range_table = &range_bipolar10; |
155 | s->insn_write = pci1723_ao_insn_write; |
156 | |
157 | ret = comedi_alloc_subdev_readback(s); |
158 | if (ret) |
159 | return ret; |
160 | |
161 | /* synchronously reset all analog outputs to 0V, +/-10V range */ |
162 | outw(PCI1723_SYNC_CTRL_SYNC, port: dev->iobase + PCI1723_SYNC_CTRL_REG); |
163 | for (i = 0; i < s->n_chan; i++) { |
164 | outw(PCI1723_CTRL_RANGE(0) | PCI1723_CTRL_CHAN(i), |
165 | PCI1723_CTRL_REG); |
166 | outw(value: 0, port: dev->iobase + PCI1723_RANGE_STROBE_REG); |
167 | |
168 | outw(value: 0x8000, port: dev->iobase + PCI1723_AO_REG(i)); |
169 | s->readback[i] = 0x8000; |
170 | } |
171 | outw(value: 0, port: dev->iobase + PCI1723_SYNC_STROBE_REG); |
172 | |
173 | /* disable syncronous control */ |
174 | outw(PCI1723_SYNC_CTRL_ASYNC, port: dev->iobase + PCI1723_SYNC_CTRL_REG); |
175 | |
176 | s = &dev->subdevices[1]; |
177 | s->type = COMEDI_SUBD_DIO; |
178 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; |
179 | s->n_chan = 16; |
180 | s->maxdata = 1; |
181 | s->range_table = &range_digital; |
182 | s->insn_config = pci1723_dio_insn_config; |
183 | s->insn_bits = pci1723_dio_insn_bits; |
184 | |
185 | /* get initial DIO direction and state */ |
186 | val = inw(port: dev->iobase + PCI1723_DIO_CTRL_REG); |
187 | if (!(val & PCI1723_DIO_CTRL_LDIO)) |
188 | s->io_bits |= 0x00ff; /* low byte output */ |
189 | if (!(val & PCI1723_DIO_CTRL_HDIO)) |
190 | s->io_bits |= 0xff00; /* high byte output */ |
191 | s->state = inw(port: dev->iobase + PCI1723_DIO_DATA_REG); |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static struct comedi_driver adv_pci1723_driver = { |
197 | .driver_name = "adv_pci1723" , |
198 | .module = THIS_MODULE, |
199 | .auto_attach = pci1723_auto_attach, |
200 | .detach = comedi_pci_detach, |
201 | }; |
202 | |
203 | static int adv_pci1723_pci_probe(struct pci_dev *dev, |
204 | const struct pci_device_id *id) |
205 | { |
206 | return comedi_pci_auto_config(pcidev: dev, driver: &adv_pci1723_driver, |
207 | context: id->driver_data); |
208 | } |
209 | |
210 | static const struct pci_device_id adv_pci1723_pci_table[] = { |
211 | { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1723) }, |
212 | { 0 } |
213 | }; |
214 | MODULE_DEVICE_TABLE(pci, adv_pci1723_pci_table); |
215 | |
216 | static struct pci_driver adv_pci1723_pci_driver = { |
217 | .name = "adv_pci1723" , |
218 | .id_table = adv_pci1723_pci_table, |
219 | .probe = adv_pci1723_pci_probe, |
220 | .remove = comedi_pci_auto_unconfig, |
221 | }; |
222 | module_comedi_pci_driver(adv_pci1723_driver, adv_pci1723_pci_driver); |
223 | |
224 | MODULE_AUTHOR("Comedi https://www.comedi.org" ); |
225 | MODULE_DESCRIPTION("Advantech PCI-1723 Comedi driver" ); |
226 | MODULE_LICENSE("GPL" ); |
227 | |