Commit | Line | Data |
---|---|---|
8b93f903 | 1 | /* |
2 | comedi/drivers/adl_pci6208.c | |
3 | ||
4 | Hardware driver for ADLink 6208 series cards: | |
5 | card | voltage output | current output | |
6 | -------------+-------------------+--------------- | |
7 | PCI-6208V | 8 channels | - | |
8 | PCI-6216V | 16 channels | - | |
9 | PCI-6208A | 8 channels | 8 channels | |
10 | ||
11 | COMEDI - Linux Control and Measurement Device Interface | |
12 | Copyright (C) 2000 David A. Schleef <ds@schleef.org> | |
13 | ||
14 | This program is free software; you can redistribute it and/or modify | |
15 | it under the terms of the GNU General Public License as published by | |
16 | the Free Software Foundation; either version 2 of the License, or | |
17 | (at your option) any later version. | |
18 | ||
19 | This program is distributed in the hope that it will be useful, | |
20 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | GNU General Public License for more details. | |
23 | ||
24 | You should have received a copy of the GNU General Public License | |
25 | along with this program; if not, write to the Free Software | |
26 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
27 | */ | |
28 | /* | |
29 | Driver: adl_pci6208 | |
30 | Description: ADLink PCI-6208A | |
31 | Devices: [ADLink] PCI-6208A (adl_pci6208) | |
32 | Author: nsyeow <nsyeow@pd.jaring.my> | |
33 | Updated: Fri, 30 Jan 2004 14:44:27 +0800 | |
34 | Status: untested | |
35 | ||
36 | Configuration Options: | |
37 | none | |
38 | ||
39 | References: | |
40 | - ni_660x.c | |
41 | - adl_pci9111.c copied the entire pci setup section | |
42 | - adl_pci9118.c | |
43 | */ | |
a7c6de4c | 44 | |
8b93f903 | 45 | #include "../comedidev.h" |
8b93f903 | 46 | |
7b6afad1 HS |
47 | /* |
48 | * PCI-6208/6216-GL register map | |
49 | */ | |
50 | #define PCI6208_AO_CONTROL(x) (0x00 + (2 * (x))) | |
51 | #define PCI6208_AO_STATUS 0x00 | |
52 | #define PCI6208_AO_STATUS_DATA_SEND (1 << 0) | |
53 | #define PCI6208_DIO 0x40 | |
54 | #define PCI6208_DIO_DO_MASK (0x0f) | |
55 | #define PCI6208_DIO_DO_SHIFT (0) | |
56 | #define PCI6208_DIO_DI_MASK (0xf0) | |
57 | #define PCI6208_DIO_DI_SHIFT (4) | |
58 | ||
5c67df8b HS |
59 | #define PCI6208_MAX_AO_CHANNELS 8 |
60 | ||
c1b31c44 | 61 | struct pci6208_board { |
8b93f903 | 62 | const char *name; |
a7c6de4c | 63 | unsigned short dev_id; |
8b93f903 | 64 | int ao_chans; |
c1b31c44 BP |
65 | }; |
66 | ||
67 | static const struct pci6208_board pci6208_boards[] = { | |
8b93f903 | 68 | { |
dcff1681 HS |
69 | .name = "pci6208a", |
70 | .dev_id = 0x6208, | |
71 | .ao_chans = 8, | |
72 | }, | |
8b93f903 | 73 | }; |
74 | ||
3d393c86 | 75 | struct pci6208_private { |
a7c6de4c | 76 | struct pci_dev *pci_dev; |
5c67df8b | 77 | unsigned int ao_readback[PCI6208_MAX_AO_CHANNELS]; |
3d393c86 | 78 | }; |
8b93f903 | 79 | |
0a85b6f0 MT |
80 | static int pci6208_ao_winsn(struct comedi_device *dev, |
81 | struct comedi_subdevice *s, | |
82 | struct comedi_insn *insn, unsigned int *data) | |
8b93f903 | 83 | { |
949a18d3 | 84 | struct pci6208_private *devpriv = dev->private; |
8366404b | 85 | int chan = CR_CHAN(insn->chanspec); |
8b93f903 | 86 | unsigned long invert = 1 << (16 - 1); |
8366404b HS |
87 | unsigned long value = 0; |
88 | unsigned short status; | |
89 | int i; | |
a7c6de4c | 90 | |
8b93f903 | 91 | for (i = 0; i < insn->n; i++) { |
8366404b HS |
92 | value = data[i] ^ invert; |
93 | ||
8b93f903 | 94 | do { |
8366404b HS |
95 | status = inw(dev->iobase + PCI6208_AO_STATUS); |
96 | } while (status & PCI6208_AO_STATUS_DATA_SEND); | |
97 | ||
98 | outw(value, dev->iobase + PCI6208_AO_CONTROL(chan)); | |
8b93f903 | 99 | } |
8366404b | 100 | devpriv->ao_readback[chan] = value; |
8b93f903 | 101 | |
8366404b | 102 | return insn->n; |
8b93f903 | 103 | } |
104 | ||
0a85b6f0 MT |
105 | static int pci6208_ao_rinsn(struct comedi_device *dev, |
106 | struct comedi_subdevice *s, | |
107 | struct comedi_insn *insn, unsigned int *data) | |
8b93f903 | 108 | { |
949a18d3 | 109 | struct pci6208_private *devpriv = dev->private; |
8b93f903 | 110 | int chan = CR_CHAN(insn->chanspec); |
7c1e5bc7 | 111 | int i; |
8b93f903 | 112 | |
113 | for (i = 0; i < insn->n; i++) | |
114 | data[i] = devpriv->ao_readback[chan]; | |
115 | ||
7c1e5bc7 | 116 | return insn->n; |
8b93f903 | 117 | } |
118 | ||
fc2536fd HS |
119 | static int pci6208_dio_insn_bits(struct comedi_device *dev, |
120 | struct comedi_subdevice *s, | |
121 | struct comedi_insn *insn, | |
122 | unsigned int *data) | |
123 | { | |
124 | unsigned int mask = data[0] & PCI6208_DIO_DO_MASK; | |
125 | unsigned int bits = data[1]; | |
126 | ||
127 | if (mask) { | |
128 | s->state &= ~mask; | |
129 | s->state |= bits & mask; | |
130 | ||
131 | outw(s->state, dev->iobase + PCI6208_DIO); | |
132 | } | |
133 | ||
134 | s->state = inw(dev->iobase + PCI6208_DIO); | |
135 | data[1] = s->state; | |
136 | ||
137 | return insn->n; | |
138 | } | |
139 | ||
140 | static int pci6208_dio_insn_config(struct comedi_device *dev, | |
141 | struct comedi_subdevice *s, | |
142 | struct comedi_insn *insn, | |
143 | unsigned int *data) | |
144 | { | |
145 | int chan = CR_CHAN(insn->chanspec); | |
146 | unsigned int mask = 1 << chan; | |
147 | ||
148 | switch (data[0]) { | |
149 | case INSN_CONFIG_DIO_QUERY: | |
150 | data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT; | |
151 | break; | |
152 | default: | |
153 | return -EINVAL; | |
154 | } | |
155 | ||
156 | return insn->n; | |
157 | } | |
8b93f903 | 158 | |
cde6f08a HS |
159 | static struct pci_dev *pci6208_find_device(struct comedi_device *dev, |
160 | struct comedi_devconfig *it) | |
8b93f903 | 161 | { |
cde6f08a | 162 | const struct pci6208_board *thisboard; |
20fb2280 | 163 | struct pci_dev *pci_dev = NULL; |
cde6f08a HS |
164 | int bus = it->options[0]; |
165 | int slot = it->options[1]; | |
8b93f903 | 166 | int i; |
167 | ||
20fb2280 | 168 | for_each_pci_dev(pci_dev) { |
cde6f08a HS |
169 | if (pci_dev->vendor != PCI_VENDOR_ID_ADLINK) |
170 | continue; | |
171 | for (i = 0; i < ARRAY_SIZE(pci6208_boards); i++) { | |
172 | thisboard = &pci6208_boards[i]; | |
173 | if (thisboard->dev_id != pci_dev->device) | |
174 | continue; | |
175 | /* was a particular bus/slot requested? */ | |
176 | if (bus || slot) { | |
177 | /* are we on the wrong bus/slot? */ | |
178 | if (pci_dev->bus->number != bus || | |
179 | PCI_SLOT(pci_dev->devfn) != slot) | |
180 | continue; | |
8b93f903 | 181 | } |
cde6f08a HS |
182 | dev_dbg(dev->class_dev, |
183 | "Found %s on bus %d, slot, %d, irq=%d\n", | |
184 | thisboard->name, | |
185 | pci_dev->bus->number, | |
186 | PCI_SLOT(pci_dev->devfn), | |
187 | pci_dev->irq); | |
188 | dev->board_ptr = thisboard; | |
189 | return pci_dev; | |
8b93f903 | 190 | } |
191 | } | |
cde6f08a HS |
192 | dev_err(dev->class_dev, |
193 | "No supported board found! (req. bus %d, slot %d)\n", | |
194 | bus, slot); | |
195 | return NULL; | |
8b93f903 | 196 | } |
197 | ||
c8d87bcc HS |
198 | static int pci6208_attach(struct comedi_device *dev, |
199 | struct comedi_devconfig *it) | |
200 | { | |
949a18d3 HS |
201 | const struct pci6208_board *thisboard; |
202 | struct pci6208_private *devpriv; | |
c8d87bcc | 203 | struct comedi_subdevice *s; |
9d639b6b | 204 | int ret; |
c8d87bcc | 205 | |
9d639b6b HS |
206 | ret = alloc_private(dev, sizeof(*devpriv)); |
207 | if (ret < 0) | |
208 | return ret; | |
949a18d3 | 209 | devpriv = dev->private; |
c8d87bcc | 210 | |
cde6f08a HS |
211 | devpriv->pci_dev = pci6208_find_device(dev, it); |
212 | if (!devpriv->pci_dev) | |
213 | return -EIO; | |
949a18d3 | 214 | thisboard = comedi_board(dev); |
c8d87bcc | 215 | |
2d993c24 HS |
216 | dev->board_name = thisboard->name; |
217 | ||
404f9991 | 218 | ret = comedi_pci_enable(devpriv->pci_dev, dev->driver->driver_name); |
9d639b6b | 219 | if (ret) { |
db7c275d HS |
220 | dev_err(dev->class_dev, |
221 | "Failed to enable PCI device and request regions\n"); | |
9d639b6b | 222 | return ret; |
db7c275d | 223 | } |
db7c275d | 224 | dev->iobase = pci_resource_start(devpriv->pci_dev, 2); |
c8d87bcc | 225 | |
9d639b6b HS |
226 | ret = comedi_alloc_subdevices(dev, 2); |
227 | if (ret) | |
228 | return ret; | |
c8d87bcc HS |
229 | |
230 | s = dev->subdevices + 0; | |
231 | /* analog output subdevice */ | |
111b62e4 HS |
232 | s->type = COMEDI_SUBD_AO; |
233 | s->subdev_flags = SDF_WRITABLE; | |
234 | s->n_chan = thisboard->ao_chans; | |
235 | s->maxdata = 0xffff; | |
236 | s->range_table = &range_bipolar10; | |
237 | s->insn_write = pci6208_ao_winsn; | |
238 | s->insn_read = pci6208_ao_rinsn; | |
c8d87bcc | 239 | |
fc2536fd | 240 | s = dev->subdevices + 1; |
c8d87bcc | 241 | /* digital i/o subdevice */ |
111b62e4 HS |
242 | s->type = COMEDI_SUBD_DIO; |
243 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
244 | s->n_chan = 8; | |
245 | s->maxdata = 1; | |
246 | s->range_table = &range_digital; | |
247 | s->insn_bits = pci6208_dio_insn_bits; | |
248 | s->insn_config = pci6208_dio_insn_config; | |
249 | ||
250 | s->io_bits = 0x0f; | |
251 | s->state = inw(dev->iobase + PCI6208_DIO); | |
c8d87bcc | 252 | |
b4dda059 HS |
253 | dev_info(dev->class_dev, "%s: %s, I/O base=0x%04lx\n", |
254 | dev->driver->driver_name, dev->board_name, dev->iobase); | |
c8d87bcc | 255 | |
98bcf911 | 256 | return 0; |
c8d87bcc HS |
257 | } |
258 | ||
484ecc95 | 259 | static void pci6208_detach(struct comedi_device *dev) |
c8d87bcc | 260 | { |
949a18d3 HS |
261 | struct pci6208_private *devpriv = dev->private; |
262 | ||
c8d87bcc HS |
263 | if (devpriv && devpriv->pci_dev) { |
264 | if (dev->iobase) | |
265 | comedi_pci_disable(devpriv->pci_dev); | |
266 | pci_dev_put(devpriv->pci_dev); | |
267 | } | |
c8d87bcc HS |
268 | } |
269 | ||
75e6301b HS |
270 | static struct comedi_driver adl_pci6208_driver = { |
271 | .driver_name = "adl_pci6208", | |
c8d87bcc HS |
272 | .module = THIS_MODULE, |
273 | .attach = pci6208_attach, | |
274 | .detach = pci6208_detach, | |
275 | }; | |
276 | ||
75e6301b HS |
277 | static int __devinit adl_pci6208_pci_probe(struct pci_dev *dev, |
278 | const struct pci_device_id *ent) | |
c8d87bcc | 279 | { |
75e6301b | 280 | return comedi_pci_auto_config(dev, &adl_pci6208_driver); |
c8d87bcc HS |
281 | } |
282 | ||
75e6301b | 283 | static void __devexit adl_pci6208_pci_remove(struct pci_dev *dev) |
c8d87bcc HS |
284 | { |
285 | comedi_pci_auto_unconfig(dev); | |
286 | } | |
287 | ||
75e6301b | 288 | static DEFINE_PCI_DEVICE_TABLE(adl_pci6208_pci_table) = { |
c8d87bcc HS |
289 | { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x6208) }, |
290 | { 0 } | |
291 | }; | |
75e6301b | 292 | MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table); |
c8d87bcc | 293 | |
75e6301b HS |
294 | static struct pci_driver adl_pci6208_pci_driver = { |
295 | .name = "adl_pci6208", | |
296 | .id_table = adl_pci6208_pci_table, | |
297 | .probe = adl_pci6208_pci_probe, | |
298 | .remove = __devexit_p(adl_pci6208_pci_remove), | |
c8d87bcc | 299 | }; |
75e6301b | 300 | module_comedi_pci_driver(adl_pci6208_driver, adl_pci6208_pci_driver); |
c8d87bcc | 301 | |
90f703d3 AT |
302 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
303 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
304 | MODULE_LICENSE("GPL"); |