1/* 2 * addi_apci_1516.c 3 * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. 4 * Project manager: Eric Stolz 5 * 6 * ADDI-DATA GmbH 7 * Dieselstrasse 3 8 * D-77833 Ottersweier 9 * Tel: +19(0)7223/9493-0 10 * Fax: +49(0)7223/9493-92 11 * http://www.addi-data.com 12 * info@addi-data.com 13 * 14 * This program is free software; you can redistribute it and/or modify it 15 * under the terms of the GNU General Public License as published by the 16 * Free Software Foundation; either version 2 of the License, or (at your 17 * option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, but WITHOUT 20 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 21 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 22 * more details. 23 */ 24 25#include <linux/module.h> 26#include <linux/pci.h> 27 28#include "../comedidev.h" 29#include "addi_watchdog.h" 30#include "comedi_fc.h" 31 32/* 33 * PCI bar 1 I/O Register map - Digital input/output 34 */ 35#define APCI1516_DI_REG 0x00 36#define APCI1516_DO_REG 0x04 37 38/* 39 * PCI bar 2 I/O Register map - Watchdog (APCI-1516 and APCI-2016) 40 */ 41#define APCI1516_WDOG_REG 0x00 42 43enum apci1516_boardid { 44 BOARD_APCI1016, 45 BOARD_APCI1516, 46 BOARD_APCI2016, 47}; 48 49struct apci1516_boardinfo { 50 const char *name; 51 int di_nchan; 52 int do_nchan; 53 int has_wdog; 54}; 55 56static const struct apci1516_boardinfo apci1516_boardtypes[] = { 57 [BOARD_APCI1016] = { 58 .name = "apci1016", 59 .di_nchan = 16, 60 }, 61 [BOARD_APCI1516] = { 62 .name = "apci1516", 63 .di_nchan = 8, 64 .do_nchan = 8, 65 .has_wdog = 1, 66 }, 67 [BOARD_APCI2016] = { 68 .name = "apci2016", 69 .do_nchan = 16, 70 .has_wdog = 1, 71 }, 72}; 73 74struct apci1516_private { 75 unsigned long wdog_iobase; 76}; 77 78static int apci1516_di_insn_bits(struct comedi_device *dev, 79 struct comedi_subdevice *s, 80 struct comedi_insn *insn, 81 unsigned int *data) 82{ 83 data[1] = inw(dev->iobase + APCI1516_DI_REG); 84 85 return insn->n; 86} 87 88static int apci1516_do_insn_bits(struct comedi_device *dev, 89 struct comedi_subdevice *s, 90 struct comedi_insn *insn, 91 unsigned int *data) 92{ 93 s->state = inw(dev->iobase + APCI1516_DO_REG); 94 95 if (comedi_dio_update_state(s, data)) 96 outw(s->state, dev->iobase + APCI1516_DO_REG); 97 98 data[1] = s->state; 99 100 return insn->n; 101} 102 103static int apci1516_reset(struct comedi_device *dev) 104{ 105 const struct apci1516_boardinfo *this_board = dev->board_ptr; 106 struct apci1516_private *devpriv = dev->private; 107 108 if (!this_board->has_wdog) 109 return 0; 110 111 outw(0x0, dev->iobase + APCI1516_DO_REG); 112 113 addi_watchdog_reset(devpriv->wdog_iobase); 114 115 return 0; 116} 117 118static int apci1516_auto_attach(struct comedi_device *dev, 119 unsigned long context) 120{ 121 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 122 const struct apci1516_boardinfo *this_board = NULL; 123 struct apci1516_private *devpriv; 124 struct comedi_subdevice *s; 125 int ret; 126 127 if (context < ARRAY_SIZE(apci1516_boardtypes)) 128 this_board = &apci1516_boardtypes[context]; 129 if (!this_board) 130 return -ENODEV; 131 dev->board_ptr = this_board; 132 dev->board_name = this_board->name; 133 134 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 135 if (!devpriv) 136 return -ENOMEM; 137 138 ret = comedi_pci_enable(dev); 139 if (ret) 140 return ret; 141 142 dev->iobase = pci_resource_start(pcidev, 1); 143 devpriv->wdog_iobase = pci_resource_start(pcidev, 2); 144 145 ret = comedi_alloc_subdevices(dev, 3); 146 if (ret) 147 return ret; 148 149 /* Initialize the digital input subdevice */ 150 s = &dev->subdevices[0]; 151 if (this_board->di_nchan) { 152 s->type = COMEDI_SUBD_DI; 153 s->subdev_flags = SDF_READABLE; 154 s->n_chan = this_board->di_nchan; 155 s->maxdata = 1; 156 s->range_table = &range_digital; 157 s->insn_bits = apci1516_di_insn_bits; 158 } else { 159 s->type = COMEDI_SUBD_UNUSED; 160 } 161 162 /* Initialize the digital output subdevice */ 163 s = &dev->subdevices[1]; 164 if (this_board->do_nchan) { 165 s->type = COMEDI_SUBD_DO; 166 s->subdev_flags = SDF_WRITEABLE; 167 s->n_chan = this_board->do_nchan; 168 s->maxdata = 1; 169 s->range_table = &range_digital; 170 s->insn_bits = apci1516_do_insn_bits; 171 } else { 172 s->type = COMEDI_SUBD_UNUSED; 173 } 174 175 /* Initialize the watchdog subdevice */ 176 s = &dev->subdevices[2]; 177 if (this_board->has_wdog) { 178 ret = addi_watchdog_init(s, devpriv->wdog_iobase); 179 if (ret) 180 return ret; 181 } else { 182 s->type = COMEDI_SUBD_UNUSED; 183 } 184 185 apci1516_reset(dev); 186 return 0; 187} 188 189static void apci1516_detach(struct comedi_device *dev) 190{ 191 if (dev->iobase) 192 apci1516_reset(dev); 193 comedi_pci_detach(dev); 194} 195 196static struct comedi_driver apci1516_driver = { 197 .driver_name = "addi_apci_1516", 198 .module = THIS_MODULE, 199 .auto_attach = apci1516_auto_attach, 200 .detach = apci1516_detach, 201}; 202 203static int apci1516_pci_probe(struct pci_dev *dev, 204 const struct pci_device_id *id) 205{ 206 return comedi_pci_auto_config(dev, &apci1516_driver, id->driver_data); 207} 208 209static const struct pci_device_id apci1516_pci_table[] = { 210 { PCI_VDEVICE(ADDIDATA, 0x1000), BOARD_APCI1016 }, 211 { PCI_VDEVICE(ADDIDATA, 0x1001), BOARD_APCI1516 }, 212 { PCI_VDEVICE(ADDIDATA, 0x1002), BOARD_APCI2016 }, 213 { 0 } 214}; 215MODULE_DEVICE_TABLE(pci, apci1516_pci_table); 216 217static struct pci_driver apci1516_pci_driver = { 218 .name = "addi_apci_1516", 219 .id_table = apci1516_pci_table, 220 .probe = apci1516_pci_probe, 221 .remove = comedi_pci_auto_unconfig, 222}; 223module_comedi_pci_driver(apci1516_driver, apci1516_pci_driver); 224 225MODULE_DESCRIPTION("ADDI-DATA APCI-1016/1516/2016, 16 channel DIO boards"); 226MODULE_AUTHOR("Comedi http://www.comedi.org"); 227MODULE_LICENSE("GPL"); 228