1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * comedi/drivers/amplc_pci236.c 4 * Driver for Amplicon PCI236 DIO boards. 5 * 6 * Copyright (C) 2002-2014 MEV Ltd. <https://www.mev.co.uk/> 7 * 8 * COMEDI - Linux Control and Measurement Device Interface 9 * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 10 */ 11/* 12 * Driver: amplc_pci236 13 * Description: Amplicon PCI236 14 * Author: Ian Abbott <abbotti@mev.co.uk> 15 * Devices: [Amplicon] PCI236 (amplc_pci236) 16 * Updated: Fri, 25 Jul 2014 15:32:40 +0000 17 * Status: works 18 * 19 * Configuration options: 20 * none 21 * 22 * Manual configuration of PCI board (PCI236) is not supported; it is 23 * configured automatically. 24 * 25 * The PCI236 board has a single 8255 appearing as subdevice 0. 26 * 27 * Subdevice 1 pretends to be a digital input device, but it always 28 * returns 0 when read. However, if you run a command with 29 * scan_begin_src=TRIG_EXT, a rising edge on port C bit 3 acts as an 30 * external trigger, which can be used to wake up tasks. This is like 31 * the comedi_parport device. If no interrupt is connected, then 32 * subdevice 1 is unused. 33 */ 34 35#include <linux/module.h> 36#include <linux/interrupt.h> 37#include <linux/comedi/comedi_pci.h> 38 39#include "amplc_pc236.h" 40#include "plx9052.h" 41 42/* Disable, and clear, interrupts */ 43#define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1POL | \ 44 PLX9052_INTCSR_LI2POL | \ 45 PLX9052_INTCSR_LI1SEL | \ 46 PLX9052_INTCSR_LI1CLRINT) 47 48/* Enable, and clear, interrupts */ 49#define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB | \ 50 PLX9052_INTCSR_LI1POL | \ 51 PLX9052_INTCSR_LI2POL | \ 52 PLX9052_INTCSR_PCIENAB | \ 53 PLX9052_INTCSR_LI1SEL | \ 54 PLX9052_INTCSR_LI1CLRINT) 55 56static void pci236_intr_update_cb(struct comedi_device *dev, bool enable) 57{ 58 struct pc236_private *devpriv = dev->private; 59 60 /* this will also clear the "local interrupt 1" latch */ 61 outl(enable ? PCI236_INTR_ENABLE : PCI236_INTR_DISABLE, 62 devpriv->lcr_iobase + PLX9052_INTCSR); 63} 64 65static bool pci236_intr_chk_clr_cb(struct comedi_device *dev) 66{ 67 struct pc236_private *devpriv = dev->private; 68 69 /* check if interrupt occurred */ 70 if (!(inl(devpriv->lcr_iobase + PLX9052_INTCSR) & 71 PLX9052_INTCSR_LI1STAT)) 72 return false; 73 /* clear the interrupt */ 74 pci236_intr_update_cb(dev, devpriv->enable_irq); 75 return true; 76} 77 78static const struct pc236_board pc236_pci_board = { 79 .name = "pci236", 80 .intr_update_cb = pci236_intr_update_cb, 81 .intr_chk_clr_cb = pci236_intr_chk_clr_cb, 82}; 83 84static int pci236_auto_attach(struct comedi_device *dev, 85 unsigned long context_unused) 86{ 87 struct pci_dev *pci_dev = comedi_to_pci_dev(dev); 88 struct pc236_private *devpriv; 89 unsigned long iobase; 90 int ret; 91 92 dev_info(dev->class_dev, "amplc_pci236: attach pci %s\n", 93 pci_name(pci_dev)); 94 95 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 96 if (!devpriv) 97 return -ENOMEM; 98 99 dev->board_ptr = &pc236_pci_board; 100 dev->board_name = pc236_pci_board.name; 101 ret = comedi_pci_enable(dev); 102 if (ret) 103 return ret; 104 105 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1); 106 iobase = pci_resource_start(pci_dev, 2); 107 return amplc_pc236_common_attach(dev, iobase, pci_dev->irq, 108 IRQF_SHARED); 109} 110 111static struct comedi_driver amplc_pci236_driver = { 112 .driver_name = "amplc_pci236", 113 .module = THIS_MODULE, 114 .auto_attach = pci236_auto_attach, 115 .detach = comedi_pci_detach, 116}; 117 118static const struct pci_device_id pci236_pci_table[] = { 119 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, 0x0009) }, 120 { 0 } 121}; 122 123MODULE_DEVICE_TABLE(pci, pci236_pci_table); 124 125static int amplc_pci236_pci_probe(struct pci_dev *dev, 126 const struct pci_device_id *id) 127{ 128 return comedi_pci_auto_config(dev, &lc_pci236_driver, 129 id->driver_data); 130} 131 132static struct pci_driver amplc_pci236_pci_driver = { 133 .name = "amplc_pci236", 134 .id_table = pci236_pci_table, 135 .probe = &lc_pci236_pci_probe, 136 .remove = comedi_pci_auto_unconfig, 137}; 138 139module_comedi_pci_driver(amplc_pci236_driver, amplc_pci236_pci_driver); 140 141MODULE_AUTHOR("Comedi https://www.comedi.org"); 142MODULE_DESCRIPTION("Comedi driver for Amplicon PCI236 DIO boards"); 143MODULE_LICENSE("GPL"); 144