Commit | Line | Data |
---|---|---|
2f82613d | 1 | /* |
cbc71791 HS |
2 | * ke_counter.c |
3 | * Comedi driver for Kolter-Electronic PCI Counter 1 Card | |
4 | * | |
5 | * COMEDI - Linux Control and Measurement Device Interface | |
6 | * Copyright (C) 2000 David A. Schleef <ds@schleef.org> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | */ | |
2f82613d | 18 | |
2f82613d | 19 | /* |
cbc71791 HS |
20 | * Driver: ke_counter |
21 | * Description: Driver for Kolter Electronic Counter Card | |
b717ffee | 22 | * Devices: [Kolter Electronic] PCI Counter Card (ke_counter) |
cbc71791 HS |
23 | * Author: Michael Hillmann |
24 | * Updated: Mon, 14 Apr 2008 15:42:42 +0100 | |
25 | * Status: tested | |
26 | * | |
27 | * Configuration Options: not applicable, uses PCI auto config | |
28 | */ | |
2f82613d | 29 | |
ce157f80 | 30 | #include <linux/module.h> |
33782dd5 | 31 | |
f13d862c | 32 | #include "../comedi_pci.h" |
2f82613d | 33 | |
4761fb4e HS |
34 | /* |
35 | * PCI BAR 0 Register I/O map | |
36 | */ | |
37 | #define KE_RESET_REG(x) (0x00 + ((x) * 0x20)) | |
38 | #define KE_LATCH_REG(x) (0x00 + ((x) * 0x20)) | |
39 | #define KE_LSB_REG(x) (0x04 + ((x) * 0x20)) | |
40 | #define KE_MID_REG(x) (0x08 + ((x) * 0x20)) | |
41 | #define KE_MSB_REG(x) (0x0c + ((x) * 0x20)) | |
42 | #define KE_SIGN_REG(x) (0x10 + ((x) * 0x20)) | |
43 | #define KE_OSC_SEL_REG 0xf8 | |
37776713 HS |
44 | #define KE_OSC_SEL_CLK(x) (((x) & 0x3) << 0) |
45 | #define KE_OSC_SEL_EXT KE_OSC_SEL_CLK(1) | |
46 | #define KE_OSC_SEL_4MHZ KE_OSC_SEL_CLK(2) | |
47 | #define KE_OSC_SEL_20MHZ KE_OSC_SEL_CLK(3) | |
4761fb4e HS |
48 | #define KE_DO_REG 0xfc |
49 | ||
89d9dcd0 HS |
50 | static int ke_counter_insn_write(struct comedi_device *dev, |
51 | struct comedi_subdevice *s, | |
52 | struct comedi_insn *insn, | |
53 | unsigned int *data) | |
2f82613d | 54 | { |
99a1b98b HS |
55 | unsigned int chan = CR_CHAN(insn->chanspec); |
56 | unsigned int val; | |
57 | int i; | |
2f82613d | 58 | |
99a1b98b HS |
59 | for (i = 0; i < insn->n; i++) { |
60 | val = data[0]; | |
2f82613d | 61 | |
99a1b98b HS |
62 | /* Order matters */ |
63 | outb((val >> 24) & 0xff, dev->iobase + KE_SIGN_REG(chan)); | |
64 | outb((val >> 16) & 0xff, dev->iobase + KE_MSB_REG(chan)); | |
65 | outb((val >> 8) & 0xff, dev->iobase + KE_MID_REG(chan)); | |
66 | outb((val >> 0) & 0xff, dev->iobase + KE_LSB_REG(chan)); | |
67 | } | |
68 | ||
69 | return insn->n; | |
2f82613d MH |
70 | } |
71 | ||
89d9dcd0 HS |
72 | static int ke_counter_insn_read(struct comedi_device *dev, |
73 | struct comedi_subdevice *s, | |
74 | struct comedi_insn *insn, | |
75 | unsigned int *data) | |
2f82613d | 76 | { |
d250972f HS |
77 | unsigned int chan = CR_CHAN(insn->chanspec); |
78 | unsigned int val; | |
79 | int i; | |
2f82613d | 80 | |
d250972f HS |
81 | for (i = 0; i < insn->n; i++) { |
82 | /* Order matters */ | |
83 | inb(dev->iobase + KE_LATCH_REG(chan)); | |
2f82613d | 84 | |
d250972f HS |
85 | val = inb(dev->iobase + KE_LSB_REG(chan)); |
86 | val |= (inb(dev->iobase + KE_MID_REG(chan)) << 8); | |
87 | val |= (inb(dev->iobase + KE_MSB_REG(chan)) << 16); | |
88 | val |= (inb(dev->iobase + KE_SIGN_REG(chan)) << 24); | |
2f82613d | 89 | |
d250972f HS |
90 | data[i] = val; |
91 | } | |
2f82613d | 92 | |
d250972f | 93 | return insn->n; |
2f82613d MH |
94 | } |
95 | ||
f40c283a HS |
96 | static void ke_counter_reset(struct comedi_device *dev) |
97 | { | |
98 | unsigned int chan; | |
99 | ||
100 | for (chan = 0; chan < 3; chan++) | |
101 | outb(0, dev->iobase + KE_RESET_REG(chan)); | |
102 | } | |
103 | ||
104 | static int ke_counter_insn_config(struct comedi_device *dev, | |
105 | struct comedi_subdevice *s, | |
106 | struct comedi_insn *insn, | |
107 | unsigned int *data) | |
108 | { | |
5fc39185 HS |
109 | unsigned char src; |
110 | ||
f40c283a HS |
111 | switch (data[0]) { |
112 | case INSN_CONFIG_SET_CLOCK_SRC: | |
113 | switch (data[1]) { | |
5fc39185 HS |
114 | case KE_CLK_20MHZ: /* default */ |
115 | src = KE_OSC_SEL_20MHZ; | |
116 | break; | |
117 | case KE_CLK_4MHZ: /* option */ | |
118 | src = KE_OSC_SEL_4MHZ; | |
119 | break; | |
120 | case KE_CLK_EXT: /* Pin 21 on D-sub */ | |
121 | src = KE_OSC_SEL_EXT; | |
f40c283a HS |
122 | break; |
123 | default: | |
124 | return -EINVAL; | |
125 | } | |
5fc39185 | 126 | outb(src, dev->iobase + KE_OSC_SEL_REG); |
f40c283a HS |
127 | break; |
128 | case INSN_CONFIG_GET_CLOCK_SRC: | |
5fc39185 HS |
129 | src = inb(dev->iobase + KE_OSC_SEL_REG); |
130 | switch (src) { | |
131 | case KE_OSC_SEL_20MHZ: | |
132 | data[1] = KE_CLK_20MHZ; | |
133 | data[2] = 50; /* 50ns */ | |
f40c283a HS |
134 | break; |
135 | case KE_OSC_SEL_4MHZ: | |
5fc39185 | 136 | data[1] = KE_CLK_4MHZ; |
f40c283a HS |
137 | data[2] = 250; /* 250ns */ |
138 | break; | |
5fc39185 HS |
139 | case KE_OSC_SEL_EXT: |
140 | data[1] = KE_CLK_EXT; | |
141 | data[2] = 0; /* Unknown */ | |
f40c283a HS |
142 | break; |
143 | default: | |
5fc39185 | 144 | return -EINVAL; |
f40c283a HS |
145 | } |
146 | break; | |
147 | case INSN_CONFIG_RESET: | |
148 | ke_counter_reset(dev); | |
149 | break; | |
150 | default: | |
151 | return -EINVAL; | |
152 | } | |
153 | ||
154 | return insn->n; | |
155 | } | |
156 | ||
661b9dbb HS |
157 | static int ke_counter_do_insn_bits(struct comedi_device *dev, |
158 | struct comedi_subdevice *s, | |
159 | struct comedi_insn *insn, | |
160 | unsigned int *data) | |
161 | { | |
162 | if (comedi_dio_update_state(s, data)) | |
163 | outb(s->state, dev->iobase + KE_DO_REG); | |
164 | ||
165 | data[1] = s->state; | |
166 | ||
167 | return insn->n; | |
168 | } | |
169 | ||
7556dc37 HS |
170 | static int ke_counter_auto_attach(struct comedi_device *dev, |
171 | unsigned long context_unused) | |
bd8a9bc1 | 172 | { |
750af5e5 | 173 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
30c4d3b8 | 174 | struct comedi_subdevice *s; |
5afbfa63 | 175 | int ret; |
bd8a9bc1 | 176 | |
818f569f | 177 | ret = comedi_pci_enable(dev); |
5afbfa63 HS |
178 | if (ret) |
179 | return ret; | |
180 | dev->iobase = pci_resource_start(pcidev, 0); | |
2f82613d | 181 | |
661b9dbb | 182 | ret = comedi_alloc_subdevices(dev, 2); |
5afbfa63 HS |
183 | if (ret) |
184 | return ret; | |
2f82613d | 185 | |
bce27067 | 186 | s = &dev->subdevices[0]; |
89d9dcd0 HS |
187 | s->type = COMEDI_SUBD_COUNTER; |
188 | s->subdev_flags = SDF_READABLE; | |
189 | s->n_chan = 3; | |
99a1b98b | 190 | s->maxdata = 0x01ffffff; |
89d9dcd0 HS |
191 | s->range_table = &range_unknown; |
192 | s->insn_read = ke_counter_insn_read; | |
193 | s->insn_write = ke_counter_insn_write; | |
f40c283a | 194 | s->insn_config = ke_counter_insn_config; |
2f82613d | 195 | |
661b9dbb HS |
196 | s = &dev->subdevices[1]; |
197 | s->type = COMEDI_SUBD_DO; | |
198 | s->subdev_flags = SDF_WRITABLE; | |
199 | s->n_chan = 3; | |
200 | s->maxdata = 1; | |
201 | s->range_table = &range_digital; | |
202 | s->insn_bits = ke_counter_do_insn_bits; | |
203 | ||
4761fb4e | 204 | outb(KE_OSC_SEL_20MHZ, dev->iobase + KE_OSC_SEL_REG); |
2f82613d | 205 | |
f40c283a | 206 | ke_counter_reset(dev); |
2f82613d | 207 | |
2f82613d MH |
208 | return 0; |
209 | } | |
210 | ||
75e6301b HS |
211 | static struct comedi_driver ke_counter_driver = { |
212 | .driver_name = "ke_counter", | |
236788fc | 213 | .module = THIS_MODULE, |
7556dc37 | 214 | .auto_attach = ke_counter_auto_attach, |
aac307f9 | 215 | .detach = comedi_pci_detach, |
236788fc HS |
216 | }; |
217 | ||
a690b7e5 | 218 | static int ke_counter_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 219 | const struct pci_device_id *id) |
236788fc | 220 | { |
b8f4ac23 HS |
221 | return comedi_pci_auto_config(dev, &ke_counter_driver, |
222 | id->driver_data); | |
236788fc HS |
223 | } |
224 | ||
41e043fc | 225 | static const struct pci_device_id ke_counter_pci_table[] = { |
8fff5ac6 | 226 | { PCI_DEVICE(PCI_VENDOR_ID_KOLTER, 0x0014) }, |
236788fc HS |
227 | { 0 } |
228 | }; | |
75e6301b | 229 | MODULE_DEVICE_TABLE(pci, ke_counter_pci_table); |
236788fc | 230 | |
75e6301b HS |
231 | static struct pci_driver ke_counter_pci_driver = { |
232 | .name = "ke_counter", | |
233 | .id_table = ke_counter_pci_table, | |
234 | .probe = ke_counter_pci_probe, | |
9901a4d7 | 235 | .remove = comedi_pci_auto_unconfig, |
236788fc | 236 | }; |
75e6301b | 237 | module_comedi_pci_driver(ke_counter_driver, ke_counter_pci_driver); |
236788fc | 238 | |
90f703d3 | 239 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
ba835c16 | 240 | MODULE_DESCRIPTION("Comedi driver for Kolter Electronic Counter Card"); |
90f703d3 | 241 | MODULE_LICENSE("GPL"); |