// SPDX-License-Identifier: GPL-2.0+ /* * addi_apci_1564.c * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. * * ADDI-DATA GmbH * Dieselstrasse 3 * D-77833 Ottersweier * Tel: +19(0)7223/9493-0 * Fax: +49(0)7223/9493-92 * http://www.addi-data.com * [email protected] */ /* * Driver: addi_apci_1564 * Description: ADDI-DATA APCI-1564 Digital I/O board * Devices: [ADDI-DATA] APCI-1564 (addi_apci_1564) * Author: H Hartley Sweeten <[email protected]> * Updated: Thu, 02 Jun 2016 13:12:46 -0700 * Status: untested * * Configuration Options: not applicable, uses comedi PCI auto config * * This board has the following features: * - 32 optically isolated digital inputs (24V), 16 of which can * generate change-of-state (COS) interrupts (channels 4 to 19) * - 32 optically isolated digital outputs (10V to 36V) * - 1 8-bit watchdog for resetting the outputs * - 1 12-bit timer * - 3 32-bit counters * - 2 diagnostic inputs * * The COS, timer, and counter subdevices all use the dev->read_subdev to * return the interrupt status. The sample data is updated and returned when * any of these subdevices generate an interrupt. The sample data format is: * * Bit Description * ----- ------------------------------------------ * 31 COS interrupt * 30 timer interrupt * 29 counter 2 interrupt * 28 counter 1 interrupt * 27 counter 0 interrupt * 26:20 not used * 19:4 COS digital input state (channels 19 to 4) * 3:0 not used * * The COS interrupts must be configured using an INSN_CONFIG_DIGITAL_TRIG * instruction before they can be enabled by an async command. The COS * interrupts will stay active until canceled. * * The timer subdevice does not use an async command. All control is handled * by the (*insn_config). * * FIXME: The format of the ADDI_TCW_TIMEBASE_REG is not descibed in the * datasheet I have. The INSN_CONFIG_SET_CLOCK_SRC currently just writes * the raw data[1] to this register along with the raw data[2] value to the * ADDI_TCW_RELOAD_REG. If anyone tests this and can determine the actual * timebase/reload operation please let me know. * * The counter subdevice also does not use an async command. All control is * handled by the (*insn_config). * * FIXME: The operation of the counters is not really described in the * datasheet I have. The (*insn_config) needs more work. */ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/comedi/comedi_pci.h> #include "addi_tcw.h" #include "addi_watchdog.h" /* * PCI BAR 0 * * PLD Revision 1.0 I/O Mapping * 0x00 93C76 EEPROM * 0x04 - 0x18 Timer 12-Bit * * PLD Revision 2.x I/O Mapping * 0x00 93C76 EEPROM * 0x04 - 0x14 Digital Input * 0x18 - 0x25 Digital Output * 0x28 - 0x44 Watchdog 8-Bit * 0x48 - 0x64 Timer 12-Bit */ #define APCI1564_EEPROM_REG … #define APCI1564_EEPROM_VCC_STATUS … #define APCI1564_EEPROM_TO_REV(x) … #define APCI1564_EEPROM_DI … #define APCI1564_EEPROM_DO … #define APCI1564_EEPROM_CS … #define APCI1564_EEPROM_CLK … #define APCI1564_REV1_TIMER_IOBASE … #define APCI1564_REV2_MAIN_IOBASE … #define APCI1564_REV2_TIMER_IOBASE … /* * PCI BAR 1 * * PLD Revision 1.0 I/O Mapping * 0x00 - 0x10 Digital Input * 0x14 - 0x20 Digital Output * 0x24 - 0x3c Watchdog 8-Bit * * PLD Revision 2.x I/O Mapping * 0x00 Counter_0 * 0x20 Counter_1 * 0x30 Counter_3 */ #define APCI1564_REV1_MAIN_IOBASE … /* * dev->iobase Register Map * PLD Revision 1.0 - PCI BAR 1 + 0x00 * PLD Revision 2.x - PCI BAR 0 + 0x04 */ #define APCI1564_DI_REG … #define APCI1564_DI_INT_MODE1_REG … #define APCI1564_DI_INT_MODE2_REG … #define APCI1564_DI_INT_MODE_MASK … #define APCI1564_DI_INT_STATUS_REG … #define APCI1564_DI_IRQ_REG … #define APCI1564_DI_IRQ_ENA … #define APCI1564_DI_IRQ_MODE … #define APCI1564_DO_REG … #define APCI1564_DO_INT_CTRL_REG … #define APCI1564_DO_INT_CTRL_CC_INT_ENA … #define APCI1564_DO_INT_CTRL_VCC_INT_ENA … #define APCI1564_DO_INT_STATUS_REG … #define APCI1564_DO_INT_STATUS_CC … #define APCI1564_DO_INT_STATUS_VCC … #define APCI1564_DO_IRQ_REG … #define APCI1564_DO_IRQ_INTR … #define APCI1564_WDOG_IOBASE … /* * devpriv->timer Register Map (see addi_tcw.h for register/bit defines) * PLD Revision 1.0 - PCI BAR 0 + 0x04 * PLD Revision 2.x - PCI BAR 0 + 0x48 */ /* * devpriv->counters Register Map (see addi_tcw.h for register/bit defines) * PLD Revision 2.x - PCI BAR 1 + 0x00 */ #define APCI1564_COUNTER(x) … /* * The dev->read_subdev is used to return the interrupt events along with * the state of the interrupt capable inputs. */ #define APCI1564_EVENT_COS … #define APCI1564_EVENT_TIMER … #define APCI1564_EVENT_COUNTER(x) … #define APCI1564_EVENT_MASK … struct apci1564_private { … }; static int apci1564_reset(struct comedi_device *dev) { … } static irqreturn_t apci1564_interrupt(int irq, void *d) { … } static int apci1564_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { … } static int apci1564_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { … } static int apci1564_diag_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { … } /* * Change-Of-State (COS) interrupt configuration * * Channels 4 to 19 are interruptible. These channels can be configured * to generate interrupts based on AND/OR logic for the desired channels. * * OR logic * - reacts to rising or falling edges * - interrupt is generated when any enabled channel * meet the desired interrupt condition * * AND logic * - reacts to changes in level of the selected inputs * - interrupt is generated when all enabled channels * meet the desired interrupt condition * - after an interrupt, a change in level must occur on * the selected inputs to release the IRQ logic * * The COS interrupt must be configured before it can be enabled. * * data[0] : INSN_CONFIG_DIGITAL_TRIG * data[1] : trigger number (= 0) * data[2] : configuration operation: * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts * data[3] : left-shift for data[4] and data[5] * data[4] : rising-edge/high level channels * data[5] : falling-edge/low level channels */ static int apci1564_cos_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { … } static int apci1564_cos_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { … } static int apci1564_cos_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) { … } /* * Change-Of-State (COS) 'do_cmd' operation * * Enable the COS interrupt as configured by apci1564_cos_insn_config(). */ static int apci1564_cos_cmd(struct comedi_device *dev, struct comedi_subdevice *s) { … } static int apci1564_cos_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { … } static int apci1564_timer_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { … } static int apci1564_timer_insn_write(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { … } static int apci1564_timer_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { … } static int apci1564_counter_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { … } static int apci1564_counter_insn_write(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { … } static int apci1564_counter_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { … } static int apci1564_auto_attach(struct comedi_device *dev, unsigned long context_unused) { … } static void apci1564_detach(struct comedi_device *dev) { … } static struct comedi_driver apci1564_driver = …; static int apci1564_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { … } static const struct pci_device_id apci1564_pci_table[] = …; MODULE_DEVICE_TABLE(pci, apci1564_pci_table); static struct pci_driver apci1564_pci_driver = …; module_comedi_pci_driver(…); MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;