1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * addi_apci_2200.c 4 * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. 5 * Project manager: Eric Stolz 6 * 7 * ADDI-DATA GmbH 8 * Dieselstrasse 3 9 * D-77833 Ottersweier 10 * Tel: +19(0)7223/9493-0 11 * Fax: +49(0)7223/9493-92 12 * http://www.addi-data.com 13 * info@addi-data.com 14 */ 15 16 #include <linux/module.h> 17 #include <linux/comedi/comedi_pci.h> 18 19 #include "addi_watchdog.h" 20 21 /* 22 * I/O Register Map 23 */ 24 #define APCI2200_DI_REG 0x00 25 #define APCI2200_DO_REG 0x04 26 #define APCI2200_WDOG_REG 0x08 27 28 static int apci2200_di_insn_bits(struct comedi_device *dev, 29 struct comedi_subdevice *s, 30 struct comedi_insn *insn, 31 unsigned int *data) 32 { 33 data[1] = inw(dev->iobase + APCI2200_DI_REG); 34 35 return insn->n; 36 } 37 38 static int apci2200_do_insn_bits(struct comedi_device *dev, 39 struct comedi_subdevice *s, 40 struct comedi_insn *insn, 41 unsigned int *data) 42 { 43 s->state = inw(dev->iobase + APCI2200_DO_REG); 44 45 if (comedi_dio_update_state(s, data)) 46 outw(s->state, dev->iobase + APCI2200_DO_REG); 47 48 data[1] = s->state; 49 50 return insn->n; 51 } 52 53 static int apci2200_reset(struct comedi_device *dev) 54 { 55 outw(0x0, dev->iobase + APCI2200_DO_REG); 56 57 addi_watchdog_reset(dev->iobase + APCI2200_WDOG_REG); 58 59 return 0; 60 } 61 62 static int apci2200_auto_attach(struct comedi_device *dev, 63 unsigned long context_unused) 64 { 65 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 66 struct comedi_subdevice *s; 67 int ret; 68 69 ret = comedi_pci_enable(dev); 70 if (ret) 71 return ret; 72 73 dev->iobase = pci_resource_start(pcidev, 1); 74 75 ret = comedi_alloc_subdevices(dev, 3); 76 if (ret) 77 return ret; 78 79 /* Initialize the digital input subdevice */ 80 s = &dev->subdevices[0]; 81 s->type = COMEDI_SUBD_DI; 82 s->subdev_flags = SDF_READABLE; 83 s->n_chan = 8; 84 s->maxdata = 1; 85 s->range_table = &range_digital; 86 s->insn_bits = apci2200_di_insn_bits; 87 88 /* Initialize the digital output subdevice */ 89 s = &dev->subdevices[1]; 90 s->type = COMEDI_SUBD_DO; 91 s->subdev_flags = SDF_WRITABLE; 92 s->n_chan = 16; 93 s->maxdata = 1; 94 s->range_table = &range_digital; 95 s->insn_bits = apci2200_do_insn_bits; 96 97 /* Initialize the watchdog subdevice */ 98 s = &dev->subdevices[2]; 99 ret = addi_watchdog_init(s, dev->iobase + APCI2200_WDOG_REG); 100 if (ret) 101 return ret; 102 103 apci2200_reset(dev); 104 return 0; 105 } 106 107 static void apci2200_detach(struct comedi_device *dev) 108 { 109 if (dev->iobase) 110 apci2200_reset(dev); 111 comedi_pci_detach(dev); 112 } 113 114 static struct comedi_driver apci2200_driver = { 115 .driver_name = "addi_apci_2200", 116 .module = THIS_MODULE, 117 .auto_attach = apci2200_auto_attach, 118 .detach = apci2200_detach, 119 }; 120 121 static int apci2200_pci_probe(struct pci_dev *dev, 122 const struct pci_device_id *id) 123 { 124 return comedi_pci_auto_config(dev, &apci2200_driver, id->driver_data); 125 } 126 127 static const struct pci_device_id apci2200_pci_table[] = { 128 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1005) }, 129 { 0 } 130 }; 131 MODULE_DEVICE_TABLE(pci, apci2200_pci_table); 132 133 static struct pci_driver apci2200_pci_driver = { 134 .name = "addi_apci_2200", 135 .id_table = apci2200_pci_table, 136 .probe = apci2200_pci_probe, 137 .remove = comedi_pci_auto_unconfig, 138 }; 139 module_comedi_pci_driver(apci2200_driver, apci2200_pci_driver); 140 141 MODULE_DESCRIPTION("ADDI-DATA APCI-2200 Relay board, optically isolated"); 142 MODULE_AUTHOR("Comedi https://www.comedi.org"); 143 MODULE_LICENSE("GPL"); 144