1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * comedi/drivers/dyna_pci10xx.c |
4 | * Copyright (C) 2011 Prashant Shah, pshah.mumbai@gmail.com |
5 | */ |
6 | |
7 | /* |
8 | * Driver: dyna_pci10xx |
9 | * Description: Dynalog India PCI DAQ Cards, http://www.dynalogindia.com/ |
10 | * Devices: [Dynalog] PCI-1050 (dyna_pci1050) |
11 | * Author: Prashant Shah <pshah.mumbai@gmail.com> |
12 | * Status: Stable |
13 | * |
14 | * Developed at Automation Labs, Chemical Dept., IIT Bombay, India. |
15 | * Prof. Kannan Moudgalya <kannan@iitb.ac.in> |
16 | * http://www.iitb.ac.in |
17 | * |
18 | * Notes : |
19 | * - Dynalog India Pvt. Ltd. does not have a registered PCI Vendor ID and |
20 | * they are using the PLX Technlogies Vendor ID since that is the PCI Chip |
21 | * used in the card. |
22 | * - Dynalog India Pvt. Ltd. has provided the internal register specification |
23 | * for their cards in their manuals. |
24 | */ |
25 | |
26 | #include <linux/module.h> |
27 | #include <linux/delay.h> |
28 | #include <linux/mutex.h> |
29 | #include <linux/comedi/comedi_pci.h> |
30 | |
31 | #define READ_TIMEOUT 50 |
32 | |
33 | static const struct comedi_lrange range_pci1050_ai = { |
34 | 3, { |
35 | BIP_RANGE(10), |
36 | BIP_RANGE(5), |
37 | UNI_RANGE(10) |
38 | } |
39 | }; |
40 | |
41 | static const char range_codes_pci1050_ai[] = { 0x00, 0x10, 0x30 }; |
42 | |
43 | struct dyna_pci10xx_private { |
44 | struct mutex mutex; |
45 | unsigned long BADR3; |
46 | }; |
47 | |
48 | static int dyna_pci10xx_ai_eoc(struct comedi_device *dev, |
49 | struct comedi_subdevice *s, |
50 | struct comedi_insn *insn, |
51 | unsigned long context) |
52 | { |
53 | unsigned int status; |
54 | |
55 | status = inw_p(port: dev->iobase); |
56 | if (status & BIT(15)) |
57 | return 0; |
58 | return -EBUSY; |
59 | } |
60 | |
61 | static int dyna_pci10xx_insn_read_ai(struct comedi_device *dev, |
62 | struct comedi_subdevice *s, |
63 | struct comedi_insn *insn, |
64 | unsigned int *data) |
65 | { |
66 | struct dyna_pci10xx_private *devpriv = dev->private; |
67 | int n; |
68 | u16 d = 0; |
69 | int ret = 0; |
70 | unsigned int chan, range; |
71 | |
72 | /* get the channel number and range */ |
73 | chan = CR_CHAN(insn->chanspec); |
74 | range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))]; |
75 | |
76 | mutex_lock(&devpriv->mutex); |
77 | /* convert n samples */ |
78 | for (n = 0; n < insn->n; n++) { |
79 | /* trigger conversion */ |
80 | smp_mb(); |
81 | outw_p(value: 0x0000 + range + chan, port: dev->iobase + 2); |
82 | usleep_range(min: 10, max: 20); |
83 | |
84 | ret = comedi_timeout(dev, s, insn, cb: dyna_pci10xx_ai_eoc, context: 0); |
85 | if (ret) |
86 | break; |
87 | |
88 | /* read data */ |
89 | d = inw_p(port: dev->iobase); |
90 | /* mask the first 4 bits - EOC bits */ |
91 | d &= 0x0FFF; |
92 | data[n] = d; |
93 | } |
94 | mutex_unlock(lock: &devpriv->mutex); |
95 | |
96 | /* return the number of samples read/written */ |
97 | return ret ? ret : n; |
98 | } |
99 | |
100 | /* analog output callback */ |
101 | static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev, |
102 | struct comedi_subdevice *s, |
103 | struct comedi_insn *insn, |
104 | unsigned int *data) |
105 | { |
106 | struct dyna_pci10xx_private *devpriv = dev->private; |
107 | int n; |
108 | |
109 | mutex_lock(&devpriv->mutex); |
110 | for (n = 0; n < insn->n; n++) { |
111 | smp_mb(); |
112 | /* trigger conversion and write data */ |
113 | outw_p(value: data[n], port: dev->iobase); |
114 | usleep_range(min: 10, max: 20); |
115 | } |
116 | mutex_unlock(lock: &devpriv->mutex); |
117 | return n; |
118 | } |
119 | |
120 | /* digital input bit interface */ |
121 | static int dyna_pci10xx_di_insn_bits(struct comedi_device *dev, |
122 | struct comedi_subdevice *s, |
123 | struct comedi_insn *insn, |
124 | unsigned int *data) |
125 | { |
126 | struct dyna_pci10xx_private *devpriv = dev->private; |
127 | u16 d = 0; |
128 | |
129 | mutex_lock(&devpriv->mutex); |
130 | smp_mb(); |
131 | d = inw_p(port: devpriv->BADR3); |
132 | usleep_range(min: 10, max: 100); |
133 | |
134 | /* on return the data[0] contains output and data[1] contains input */ |
135 | data[1] = d; |
136 | data[0] = s->state; |
137 | mutex_unlock(lock: &devpriv->mutex); |
138 | return insn->n; |
139 | } |
140 | |
141 | static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev, |
142 | struct comedi_subdevice *s, |
143 | struct comedi_insn *insn, |
144 | unsigned int *data) |
145 | { |
146 | struct dyna_pci10xx_private *devpriv = dev->private; |
147 | |
148 | mutex_lock(&devpriv->mutex); |
149 | if (comedi_dio_update_state(s, data)) { |
150 | smp_mb(); |
151 | outw_p(value: s->state, port: devpriv->BADR3); |
152 | usleep_range(min: 10, max: 100); |
153 | } |
154 | |
155 | data[1] = s->state; |
156 | mutex_unlock(lock: &devpriv->mutex); |
157 | |
158 | return insn->n; |
159 | } |
160 | |
161 | static int dyna_pci10xx_auto_attach(struct comedi_device *dev, |
162 | unsigned long context_unused) |
163 | { |
164 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
165 | struct dyna_pci10xx_private *devpriv; |
166 | struct comedi_subdevice *s; |
167 | int ret; |
168 | |
169 | devpriv = comedi_alloc_devpriv(dev, size: sizeof(*devpriv)); |
170 | if (!devpriv) |
171 | return -ENOMEM; |
172 | |
173 | ret = comedi_pci_enable(dev); |
174 | if (ret) |
175 | return ret; |
176 | dev->iobase = pci_resource_start(pcidev, 2); |
177 | devpriv->BADR3 = pci_resource_start(pcidev, 3); |
178 | |
179 | mutex_init(&devpriv->mutex); |
180 | |
181 | ret = comedi_alloc_subdevices(dev, num_subdevices: 4); |
182 | if (ret) |
183 | return ret; |
184 | |
185 | /* analog input */ |
186 | s = &dev->subdevices[0]; |
187 | s->type = COMEDI_SUBD_AI; |
188 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; |
189 | s->n_chan = 16; |
190 | s->maxdata = 0x0FFF; |
191 | s->range_table = &range_pci1050_ai; |
192 | s->insn_read = dyna_pci10xx_insn_read_ai; |
193 | |
194 | /* analog output */ |
195 | s = &dev->subdevices[1]; |
196 | s->type = COMEDI_SUBD_AO; |
197 | s->subdev_flags = SDF_WRITABLE; |
198 | s->n_chan = 1; |
199 | s->maxdata = 0x0FFF; |
200 | s->range_table = &range_unipolar10; |
201 | s->insn_write = dyna_pci10xx_insn_write_ao; |
202 | |
203 | /* digital input */ |
204 | s = &dev->subdevices[2]; |
205 | s->type = COMEDI_SUBD_DI; |
206 | s->subdev_flags = SDF_READABLE; |
207 | s->n_chan = 16; |
208 | s->maxdata = 1; |
209 | s->range_table = &range_digital; |
210 | s->insn_bits = dyna_pci10xx_di_insn_bits; |
211 | |
212 | /* digital output */ |
213 | s = &dev->subdevices[3]; |
214 | s->type = COMEDI_SUBD_DO; |
215 | s->subdev_flags = SDF_WRITABLE; |
216 | s->n_chan = 16; |
217 | s->maxdata = 1; |
218 | s->range_table = &range_digital; |
219 | s->state = 0; |
220 | s->insn_bits = dyna_pci10xx_do_insn_bits; |
221 | |
222 | return 0; |
223 | } |
224 | |
225 | static void dyna_pci10xx_detach(struct comedi_device *dev) |
226 | { |
227 | struct dyna_pci10xx_private *devpriv = dev->private; |
228 | |
229 | comedi_pci_detach(dev); |
230 | if (devpriv) |
231 | mutex_destroy(lock: &devpriv->mutex); |
232 | } |
233 | |
234 | static struct comedi_driver dyna_pci10xx_driver = { |
235 | .driver_name = "dyna_pci10xx" , |
236 | .module = THIS_MODULE, |
237 | .auto_attach = dyna_pci10xx_auto_attach, |
238 | .detach = dyna_pci10xx_detach, |
239 | }; |
240 | |
241 | static int dyna_pci10xx_pci_probe(struct pci_dev *dev, |
242 | const struct pci_device_id *id) |
243 | { |
244 | return comedi_pci_auto_config(pcidev: dev, driver: &dyna_pci10xx_driver, |
245 | context: id->driver_data); |
246 | } |
247 | |
248 | static const struct pci_device_id dyna_pci10xx_pci_table[] = { |
249 | { PCI_DEVICE(PCI_VENDOR_ID_PLX, 0x1050) }, |
250 | { 0 } |
251 | }; |
252 | MODULE_DEVICE_TABLE(pci, dyna_pci10xx_pci_table); |
253 | |
254 | static struct pci_driver dyna_pci10xx_pci_driver = { |
255 | .name = "dyna_pci10xx" , |
256 | .id_table = dyna_pci10xx_pci_table, |
257 | .probe = dyna_pci10xx_pci_probe, |
258 | .remove = comedi_pci_auto_unconfig, |
259 | }; |
260 | module_comedi_pci_driver(dyna_pci10xx_driver, dyna_pci10xx_pci_driver); |
261 | |
262 | MODULE_LICENSE("GPL" ); |
263 | MODULE_AUTHOR("Prashant Shah <pshah.mumbai@gmail.com>" ); |
264 | MODULE_DESCRIPTION("Comedi based drivers for Dynalog PCI DAQ cards" ); |
265 | |