1/* 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 */ 18 19/* 20 * Driver: ke_counter 21 * Description: Driver for Kolter Electronic Counter Card 22 * Devices: (Kolter Electronic) PCI Counter Card [ke_counter] 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 */ 29 30#include <linux/module.h> 31#include <linux/pci.h> 32 33#include "../comedidev.h" 34 35/* 36 * PCI BAR 0 Register I/O map 37 */ 38#define KE_RESET_REG(x) (0x00 + ((x) * 0x20)) 39#define KE_LATCH_REG(x) (0x00 + ((x) * 0x20)) 40#define KE_LSB_REG(x) (0x04 + ((x) * 0x20)) 41#define KE_MID_REG(x) (0x08 + ((x) * 0x20)) 42#define KE_MSB_REG(x) (0x0c + ((x) * 0x20)) 43#define KE_SIGN_REG(x) (0x10 + ((x) * 0x20)) 44#define KE_OSC_SEL_REG 0xf8 45#define KE_OSC_SEL_EXT (1 << 0) 46#define KE_OSC_SEL_4MHZ (2 << 0) 47#define KE_OSC_SEL_20MHZ (3 << 0) 48#define KE_DO_REG 0xfc 49 50static int ke_counter_insn_write(struct comedi_device *dev, 51 struct comedi_subdevice *s, 52 struct comedi_insn *insn, 53 unsigned int *data) 54{ 55 unsigned int chan = CR_CHAN(insn->chanspec); 56 unsigned int val; 57 int i; 58 59 for (i = 0; i < insn->n; i++) { 60 val = data[0]; 61 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; 70} 71 72static int ke_counter_insn_read(struct comedi_device *dev, 73 struct comedi_subdevice *s, 74 struct comedi_insn *insn, 75 unsigned int *data) 76{ 77 unsigned int chan = CR_CHAN(insn->chanspec); 78 unsigned int val; 79 int i; 80 81 for (i = 0; i < insn->n; i++) { 82 /* Order matters */ 83 inb(dev->iobase + KE_LATCH_REG(chan)); 84 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); 89 90 data[i] = val; 91 } 92 93 return insn->n; 94} 95 96static 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 104static int ke_counter_insn_config(struct comedi_device *dev, 105 struct comedi_subdevice *s, 106 struct comedi_insn *insn, 107 unsigned int *data) 108{ 109 unsigned char src; 110 111 switch (data[0]) { 112 case INSN_CONFIG_SET_CLOCK_SRC: 113 switch (data[1]) { 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; 122 break; 123 default: 124 return -EINVAL; 125 } 126 outb(src, dev->iobase + KE_OSC_SEL_REG); 127 break; 128 case INSN_CONFIG_GET_CLOCK_SRC: 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 */ 134 break; 135 case KE_OSC_SEL_4MHZ: 136 data[1] = KE_CLK_4MHZ; 137 data[2] = 250; /* 250ns */ 138 break; 139 case KE_OSC_SEL_EXT: 140 data[1] = KE_CLK_EXT; 141 data[2] = 0; /* Unknown */ 142 break; 143 default: 144 return -EINVAL; 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 157static 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 170static int ke_counter_auto_attach(struct comedi_device *dev, 171 unsigned long context_unused) 172{ 173 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 174 struct comedi_subdevice *s; 175 int ret; 176 177 ret = comedi_pci_enable(dev); 178 if (ret) 179 return ret; 180 dev->iobase = pci_resource_start(pcidev, 0); 181 182 ret = comedi_alloc_subdevices(dev, 2); 183 if (ret) 184 return ret; 185 186 s = &dev->subdevices[0]; 187 s->type = COMEDI_SUBD_COUNTER; 188 s->subdev_flags = SDF_READABLE; 189 s->n_chan = 3; 190 s->maxdata = 0x01ffffff; 191 s->range_table = &range_unknown; 192 s->insn_read = ke_counter_insn_read; 193 s->insn_write = ke_counter_insn_write; 194 s->insn_config = ke_counter_insn_config; 195 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 204 outb(KE_OSC_SEL_20MHZ, dev->iobase + KE_OSC_SEL_REG); 205 206 ke_counter_reset(dev); 207 208 return 0; 209} 210 211static struct comedi_driver ke_counter_driver = { 212 .driver_name = "ke_counter", 213 .module = THIS_MODULE, 214 .auto_attach = ke_counter_auto_attach, 215 .detach = comedi_pci_detach, 216}; 217 218static int ke_counter_pci_probe(struct pci_dev *dev, 219 const struct pci_device_id *id) 220{ 221 return comedi_pci_auto_config(dev, &ke_counter_driver, 222 id->driver_data); 223} 224 225static const struct pci_device_id ke_counter_pci_table[] = { 226 { PCI_DEVICE(PCI_VENDOR_ID_KOLTER, 0x0014) }, 227 { 0 } 228}; 229MODULE_DEVICE_TABLE(pci, ke_counter_pci_table); 230 231static struct pci_driver ke_counter_pci_driver = { 232 .name = "ke_counter", 233 .id_table = ke_counter_pci_table, 234 .probe = ke_counter_pci_probe, 235 .remove = comedi_pci_auto_unconfig, 236}; 237module_comedi_pci_driver(ke_counter_driver, ke_counter_pci_driver); 238 239MODULE_AUTHOR("Comedi http://www.comedi.org"); 240MODULE_DESCRIPTION("Comedi driver for Kolter Electronic Counter Card"); 241MODULE_LICENSE("GPL"); 242