162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ke_counter.c 462306a36Sopenharmony_ci * Comedi driver for Kolter-Electronic PCI Counter 1 Card 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * COMEDI - Linux Control and Measurement Device Interface 762306a36Sopenharmony_ci * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * Driver: ke_counter 1262306a36Sopenharmony_ci * Description: Driver for Kolter Electronic Counter Card 1362306a36Sopenharmony_ci * Devices: [Kolter Electronic] PCI Counter Card (ke_counter) 1462306a36Sopenharmony_ci * Author: Michael Hillmann 1562306a36Sopenharmony_ci * Updated: Mon, 14 Apr 2008 15:42:42 +0100 1662306a36Sopenharmony_ci * Status: tested 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Configuration Options: not applicable, uses PCI auto config 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/comedi/comedi_pci.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * PCI BAR 0 Register I/O map 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci#define KE_RESET_REG(x) (0x00 + ((x) * 0x20)) 2862306a36Sopenharmony_ci#define KE_LATCH_REG(x) (0x00 + ((x) * 0x20)) 2962306a36Sopenharmony_ci#define KE_LSB_REG(x) (0x04 + ((x) * 0x20)) 3062306a36Sopenharmony_ci#define KE_MID_REG(x) (0x08 + ((x) * 0x20)) 3162306a36Sopenharmony_ci#define KE_MSB_REG(x) (0x0c + ((x) * 0x20)) 3262306a36Sopenharmony_ci#define KE_SIGN_REG(x) (0x10 + ((x) * 0x20)) 3362306a36Sopenharmony_ci#define KE_OSC_SEL_REG 0xf8 3462306a36Sopenharmony_ci#define KE_OSC_SEL_CLK(x) (((x) & 0x3) << 0) 3562306a36Sopenharmony_ci#define KE_OSC_SEL_EXT KE_OSC_SEL_CLK(1) 3662306a36Sopenharmony_ci#define KE_OSC_SEL_4MHZ KE_OSC_SEL_CLK(2) 3762306a36Sopenharmony_ci#define KE_OSC_SEL_20MHZ KE_OSC_SEL_CLK(3) 3862306a36Sopenharmony_ci#define KE_DO_REG 0xfc 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int ke_counter_insn_write(struct comedi_device *dev, 4162306a36Sopenharmony_ci struct comedi_subdevice *s, 4262306a36Sopenharmony_ci struct comedi_insn *insn, 4362306a36Sopenharmony_ci unsigned int *data) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci unsigned int chan = CR_CHAN(insn->chanspec); 4662306a36Sopenharmony_ci unsigned int val; 4762306a36Sopenharmony_ci int i; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci for (i = 0; i < insn->n; i++) { 5062306a36Sopenharmony_ci val = data[0]; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* Order matters */ 5362306a36Sopenharmony_ci outb((val >> 24) & 0xff, dev->iobase + KE_SIGN_REG(chan)); 5462306a36Sopenharmony_ci outb((val >> 16) & 0xff, dev->iobase + KE_MSB_REG(chan)); 5562306a36Sopenharmony_ci outb((val >> 8) & 0xff, dev->iobase + KE_MID_REG(chan)); 5662306a36Sopenharmony_ci outb((val >> 0) & 0xff, dev->iobase + KE_LSB_REG(chan)); 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return insn->n; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int ke_counter_insn_read(struct comedi_device *dev, 6362306a36Sopenharmony_ci struct comedi_subdevice *s, 6462306a36Sopenharmony_ci struct comedi_insn *insn, 6562306a36Sopenharmony_ci unsigned int *data) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci unsigned int chan = CR_CHAN(insn->chanspec); 6862306a36Sopenharmony_ci unsigned int val; 6962306a36Sopenharmony_ci int i; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci for (i = 0; i < insn->n; i++) { 7262306a36Sopenharmony_ci /* Order matters */ 7362306a36Sopenharmony_ci inb(dev->iobase + KE_LATCH_REG(chan)); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci val = inb(dev->iobase + KE_LSB_REG(chan)); 7662306a36Sopenharmony_ci val |= (inb(dev->iobase + KE_MID_REG(chan)) << 8); 7762306a36Sopenharmony_ci val |= (inb(dev->iobase + KE_MSB_REG(chan)) << 16); 7862306a36Sopenharmony_ci val |= (inb(dev->iobase + KE_SIGN_REG(chan)) << 24); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci data[i] = val; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return insn->n; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void ke_counter_reset(struct comedi_device *dev) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci unsigned int chan; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci for (chan = 0; chan < 3; chan++) 9162306a36Sopenharmony_ci outb(0, dev->iobase + KE_RESET_REG(chan)); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int ke_counter_insn_config(struct comedi_device *dev, 9562306a36Sopenharmony_ci struct comedi_subdevice *s, 9662306a36Sopenharmony_ci struct comedi_insn *insn, 9762306a36Sopenharmony_ci unsigned int *data) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci unsigned char src; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci switch (data[0]) { 10262306a36Sopenharmony_ci case INSN_CONFIG_SET_CLOCK_SRC: 10362306a36Sopenharmony_ci switch (data[1]) { 10462306a36Sopenharmony_ci case KE_CLK_20MHZ: /* default */ 10562306a36Sopenharmony_ci src = KE_OSC_SEL_20MHZ; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case KE_CLK_4MHZ: /* option */ 10862306a36Sopenharmony_ci src = KE_OSC_SEL_4MHZ; 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci case KE_CLK_EXT: /* Pin 21 on D-sub */ 11162306a36Sopenharmony_ci src = KE_OSC_SEL_EXT; 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci default: 11462306a36Sopenharmony_ci return -EINVAL; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci outb(src, dev->iobase + KE_OSC_SEL_REG); 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci case INSN_CONFIG_GET_CLOCK_SRC: 11962306a36Sopenharmony_ci src = inb(dev->iobase + KE_OSC_SEL_REG); 12062306a36Sopenharmony_ci switch (src) { 12162306a36Sopenharmony_ci case KE_OSC_SEL_20MHZ: 12262306a36Sopenharmony_ci data[1] = KE_CLK_20MHZ; 12362306a36Sopenharmony_ci data[2] = 50; /* 50ns */ 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci case KE_OSC_SEL_4MHZ: 12662306a36Sopenharmony_ci data[1] = KE_CLK_4MHZ; 12762306a36Sopenharmony_ci data[2] = 250; /* 250ns */ 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case KE_OSC_SEL_EXT: 13062306a36Sopenharmony_ci data[1] = KE_CLK_EXT; 13162306a36Sopenharmony_ci data[2] = 0; /* Unknown */ 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci default: 13462306a36Sopenharmony_ci return -EINVAL; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci case INSN_CONFIG_RESET: 13862306a36Sopenharmony_ci ke_counter_reset(dev); 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci default: 14162306a36Sopenharmony_ci return -EINVAL; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return insn->n; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int ke_counter_do_insn_bits(struct comedi_device *dev, 14862306a36Sopenharmony_ci struct comedi_subdevice *s, 14962306a36Sopenharmony_ci struct comedi_insn *insn, 15062306a36Sopenharmony_ci unsigned int *data) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (comedi_dio_update_state(s, data)) 15362306a36Sopenharmony_ci outb(s->state, dev->iobase + KE_DO_REG); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci data[1] = s->state; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return insn->n; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int ke_counter_auto_attach(struct comedi_device *dev, 16162306a36Sopenharmony_ci unsigned long context_unused) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct pci_dev *pcidev = comedi_to_pci_dev(dev); 16462306a36Sopenharmony_ci struct comedi_subdevice *s; 16562306a36Sopenharmony_ci int ret; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ret = comedi_pci_enable(dev); 16862306a36Sopenharmony_ci if (ret) 16962306a36Sopenharmony_ci return ret; 17062306a36Sopenharmony_ci dev->iobase = pci_resource_start(pcidev, 0); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci ret = comedi_alloc_subdevices(dev, 2); 17362306a36Sopenharmony_ci if (ret) 17462306a36Sopenharmony_ci return ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci s = &dev->subdevices[0]; 17762306a36Sopenharmony_ci s->type = COMEDI_SUBD_COUNTER; 17862306a36Sopenharmony_ci s->subdev_flags = SDF_READABLE; 17962306a36Sopenharmony_ci s->n_chan = 3; 18062306a36Sopenharmony_ci s->maxdata = 0x01ffffff; 18162306a36Sopenharmony_ci s->range_table = &range_unknown; 18262306a36Sopenharmony_ci s->insn_read = ke_counter_insn_read; 18362306a36Sopenharmony_ci s->insn_write = ke_counter_insn_write; 18462306a36Sopenharmony_ci s->insn_config = ke_counter_insn_config; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci s = &dev->subdevices[1]; 18762306a36Sopenharmony_ci s->type = COMEDI_SUBD_DO; 18862306a36Sopenharmony_ci s->subdev_flags = SDF_WRITABLE; 18962306a36Sopenharmony_ci s->n_chan = 3; 19062306a36Sopenharmony_ci s->maxdata = 1; 19162306a36Sopenharmony_ci s->range_table = &range_digital; 19262306a36Sopenharmony_ci s->insn_bits = ke_counter_do_insn_bits; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci outb(KE_OSC_SEL_20MHZ, dev->iobase + KE_OSC_SEL_REG); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci ke_counter_reset(dev); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic struct comedi_driver ke_counter_driver = { 20262306a36Sopenharmony_ci .driver_name = "ke_counter", 20362306a36Sopenharmony_ci .module = THIS_MODULE, 20462306a36Sopenharmony_ci .auto_attach = ke_counter_auto_attach, 20562306a36Sopenharmony_ci .detach = comedi_pci_detach, 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int ke_counter_pci_probe(struct pci_dev *dev, 20962306a36Sopenharmony_ci const struct pci_device_id *id) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci return comedi_pci_auto_config(dev, &ke_counter_driver, 21262306a36Sopenharmony_ci id->driver_data); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic const struct pci_device_id ke_counter_pci_table[] = { 21662306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_KOLTER, 0x0014) }, 21762306a36Sopenharmony_ci { 0 } 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ke_counter_pci_table); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic struct pci_driver ke_counter_pci_driver = { 22262306a36Sopenharmony_ci .name = "ke_counter", 22362306a36Sopenharmony_ci .id_table = ke_counter_pci_table, 22462306a36Sopenharmony_ci .probe = ke_counter_pci_probe, 22562306a36Sopenharmony_ci .remove = comedi_pci_auto_unconfig, 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_cimodule_comedi_pci_driver(ke_counter_driver, ke_counter_pci_driver); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ciMODULE_AUTHOR("Comedi https://www.comedi.org"); 23062306a36Sopenharmony_ciMODULE_DESCRIPTION("Comedi driver for Kolter Electronic Counter Card"); 23162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 232