1 // SPDX-License-Identifier: GPL-2.0+
4 * Comedi driver for Kolter-Electronic PCI Counter 1 Card
6 * COMEDI - Linux Control and Measurement Device Interface
7 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
12 * Description: Driver for Kolter Electronic Counter Card
13 * Devices: [Kolter Electronic] PCI Counter Card (ke_counter)
14 * Author: Michael Hillmann
15 * Updated: Mon, 14 Apr 2008 15:42:42 +0100
18 * Configuration Options: not applicable, uses PCI auto config
21 #include <linux/module.h>
23 #include "../comedi_pci.h"
26 * PCI BAR 0 Register I/O map
28 #define KE_RESET_REG(x) (0x00 + ((x) * 0x20))
29 #define KE_LATCH_REG(x) (0x00 + ((x) * 0x20))
30 #define KE_LSB_REG(x) (0x04 + ((x) * 0x20))
31 #define KE_MID_REG(x) (0x08 + ((x) * 0x20))
32 #define KE_MSB_REG(x) (0x0c + ((x) * 0x20))
33 #define KE_SIGN_REG(x) (0x10 + ((x) * 0x20))
34 #define KE_OSC_SEL_REG 0xf8
35 #define KE_OSC_SEL_CLK(x) (((x) & 0x3) << 0)
36 #define KE_OSC_SEL_EXT KE_OSC_SEL_CLK(1)
37 #define KE_OSC_SEL_4MHZ KE_OSC_SEL_CLK(2)
38 #define KE_OSC_SEL_20MHZ KE_OSC_SEL_CLK(3)
39 #define KE_DO_REG 0xfc
41 static int ke_counter_insn_write(struct comedi_device
*dev
,
42 struct comedi_subdevice
*s
,
43 struct comedi_insn
*insn
,
46 unsigned int chan
= CR_CHAN(insn
->chanspec
);
50 for (i
= 0; i
< insn
->n
; i
++) {
54 outb((val
>> 24) & 0xff, dev
->iobase
+ KE_SIGN_REG(chan
));
55 outb((val
>> 16) & 0xff, dev
->iobase
+ KE_MSB_REG(chan
));
56 outb((val
>> 8) & 0xff, dev
->iobase
+ KE_MID_REG(chan
));
57 outb((val
>> 0) & 0xff, dev
->iobase
+ KE_LSB_REG(chan
));
63 static int ke_counter_insn_read(struct comedi_device
*dev
,
64 struct comedi_subdevice
*s
,
65 struct comedi_insn
*insn
,
68 unsigned int chan
= CR_CHAN(insn
->chanspec
);
72 for (i
= 0; i
< insn
->n
; i
++) {
74 inb(dev
->iobase
+ KE_LATCH_REG(chan
));
76 val
= inb(dev
->iobase
+ KE_LSB_REG(chan
));
77 val
|= (inb(dev
->iobase
+ KE_MID_REG(chan
)) << 8);
78 val
|= (inb(dev
->iobase
+ KE_MSB_REG(chan
)) << 16);
79 val
|= (inb(dev
->iobase
+ KE_SIGN_REG(chan
)) << 24);
87 static void ke_counter_reset(struct comedi_device
*dev
)
91 for (chan
= 0; chan
< 3; chan
++)
92 outb(0, dev
->iobase
+ KE_RESET_REG(chan
));
95 static int ke_counter_insn_config(struct comedi_device
*dev
,
96 struct comedi_subdevice
*s
,
97 struct comedi_insn
*insn
,
103 case INSN_CONFIG_SET_CLOCK_SRC
:
105 case KE_CLK_20MHZ
: /* default */
106 src
= KE_OSC_SEL_20MHZ
;
108 case KE_CLK_4MHZ
: /* option */
109 src
= KE_OSC_SEL_4MHZ
;
111 case KE_CLK_EXT
: /* Pin 21 on D-sub */
112 src
= KE_OSC_SEL_EXT
;
117 outb(src
, dev
->iobase
+ KE_OSC_SEL_REG
);
119 case INSN_CONFIG_GET_CLOCK_SRC
:
120 src
= inb(dev
->iobase
+ KE_OSC_SEL_REG
);
122 case KE_OSC_SEL_20MHZ
:
123 data
[1] = KE_CLK_20MHZ
;
124 data
[2] = 50; /* 50ns */
126 case KE_OSC_SEL_4MHZ
:
127 data
[1] = KE_CLK_4MHZ
;
128 data
[2] = 250; /* 250ns */
131 data
[1] = KE_CLK_EXT
;
132 data
[2] = 0; /* Unknown */
138 case INSN_CONFIG_RESET
:
139 ke_counter_reset(dev
);
148 static int ke_counter_do_insn_bits(struct comedi_device
*dev
,
149 struct comedi_subdevice
*s
,
150 struct comedi_insn
*insn
,
153 if (comedi_dio_update_state(s
, data
))
154 outb(s
->state
, dev
->iobase
+ KE_DO_REG
);
161 static int ke_counter_auto_attach(struct comedi_device
*dev
,
162 unsigned long context_unused
)
164 struct pci_dev
*pcidev
= comedi_to_pci_dev(dev
);
165 struct comedi_subdevice
*s
;
168 ret
= comedi_pci_enable(dev
);
171 dev
->iobase
= pci_resource_start(pcidev
, 0);
173 ret
= comedi_alloc_subdevices(dev
, 2);
177 s
= &dev
->subdevices
[0];
178 s
->type
= COMEDI_SUBD_COUNTER
;
179 s
->subdev_flags
= SDF_READABLE
;
181 s
->maxdata
= 0x01ffffff;
182 s
->range_table
= &range_unknown
;
183 s
->insn_read
= ke_counter_insn_read
;
184 s
->insn_write
= ke_counter_insn_write
;
185 s
->insn_config
= ke_counter_insn_config
;
187 s
= &dev
->subdevices
[1];
188 s
->type
= COMEDI_SUBD_DO
;
189 s
->subdev_flags
= SDF_WRITABLE
;
192 s
->range_table
= &range_digital
;
193 s
->insn_bits
= ke_counter_do_insn_bits
;
195 outb(KE_OSC_SEL_20MHZ
, dev
->iobase
+ KE_OSC_SEL_REG
);
197 ke_counter_reset(dev
);
202 static struct comedi_driver ke_counter_driver
= {
203 .driver_name
= "ke_counter",
204 .module
= THIS_MODULE
,
205 .auto_attach
= ke_counter_auto_attach
,
206 .detach
= comedi_pci_detach
,
209 static int ke_counter_pci_probe(struct pci_dev
*dev
,
210 const struct pci_device_id
*id
)
212 return comedi_pci_auto_config(dev
, &ke_counter_driver
,
216 static const struct pci_device_id ke_counter_pci_table
[] = {
217 { PCI_DEVICE(PCI_VENDOR_ID_KOLTER
, 0x0014) },
220 MODULE_DEVICE_TABLE(pci
, ke_counter_pci_table
);
222 static struct pci_driver ke_counter_pci_driver
= {
223 .name
= "ke_counter",
224 .id_table
= ke_counter_pci_table
,
225 .probe
= ke_counter_pci_probe
,
226 .remove
= comedi_pci_auto_unconfig
,
228 module_comedi_pci_driver(ke_counter_driver
, ke_counter_pci_driver
);
230 MODULE_AUTHOR("Comedi https://www.comedi.org");
231 MODULE_DESCRIPTION("Comedi driver for Kolter Electronic Counter Card");
232 MODULE_LICENSE("GPL");