From a8c66b684efaf628752262ed2c48cb096f1cf26d Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Tue, 20 Jan 2015 12:01:54 -0700 Subject: [PATCH] staging: comedi: addi_apci_1500: rewrite the subdevice support functions This driver is a mess. It violates the comedi API so much that I doubt anything actually works. Drop the addi-data/hwdrv_apci1500.c file and rewrite the subdevice support functions. This board has 16 digital inputs (subdevice 0) and 16 digital outputs (subdevice 1). It also has three 16-bit timer/counters provided by a Z8536 CIO chip (subdevice 2). The Z8536 chip is also used to support pattern match interrupt detection of the first 14 digital input channels. Signed-off-by: H Hartley Sweeten Reviewed-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- .../comedi/drivers/addi-data/hwdrv_apci1500.c | 1620 ----------------- .../staging/comedi/drivers/addi_apci_1500.c | 812 ++++++++- drivers/staging/comedi/drivers/z8536.h | 202 ++ 3 files changed, 974 insertions(+), 1660 deletions(-) delete mode 100644 drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.c create mode 100644 drivers/staging/comedi/drivers/z8536.h diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.c deleted file mode 100644 index 5bf943dc1224..000000000000 --- a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.c +++ /dev/null @@ -1,1620 +0,0 @@ -/* - * 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 - * info@addi-data.com - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - */ - -/* DIGITAL INPUT-OUTPUT DEFINE */ - -#define APCI1500_AND 2 -#define APCI1500_OR 4 -#define APCI1500_OR_PRIORITY 6 -#define COUNTER1 0 -#define COUNTER2 1 -#define COUNTER3 2 -#define APCI1500_COUNTER 0x20 -#define APCI1500_TIMER 0 -#define APCI1500_WATCHDOG 0 -#define APCI1500_SINGLE 0 -#define APCI1500_CONTINUOUS 0x80 -#define APCI1500_DISABLE 0 -#define APCI1500_ENABLE 1 -#define APCI1500_SOFTWARE_TRIGGER 0x4 -#define APCI1500_HARDWARE_TRIGGER 0x10 -#define APCI1500_SOFTWARE_GATE 0 -#define APCI1500_HARDWARE_GATE 0x8 -#define START 0 -#define STOP 1 -#define TRIGGER 2 - -/* - * Z8536 CIO Internal Address - */ -enum { - APCI1500_RW_MASTER_INTERRUPT_CONTROL, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL, - APCI1500_RW_PORT_A_INTERRUPT_CONTROL, - APCI1500_RW_PORT_B_INTERRUPT_CONTROL, - APCI1500_RW_TIMER_COUNTER_INTERRUPT_VECTOR, - APCI1500_RW_PORT_C_DATA_PCITCH_POLARITY, - APCI1500_RW_PORT_C_DATA_DIRECTION, - APCI1500_RW_PORT_C_SPECIAL_IO_CONTROL, - - APCI1500_RW_PORT_A_COMMAND_AND_STATUS, - APCI1500_RW_PORT_B_COMMAND_AND_STATUS, - APCI1500_RW_CPT_TMR1_CMD_STATUS, - APCI1500_RW_CPT_TMR2_CMD_STATUS, - APCI1500_RW_CPT_TMR3_CMD_STATUS, - APCI1500_RW_PORT_A_DATA, - APCI1500_RW_PORT_B_DATA, - APCI1500_RW_PORT_C_DATA, - - APCI1500_R_CPT_TMR1_VALUE_HIGH, - APCI1500_R_CPT_TMR1_VALUE_LOW, - APCI1500_R_CPT_TMR2_VALUE_HIGH, - APCI1500_R_CPT_TMR2_VALUE_LOW, - APCI1500_R_CPT_TMR3_VALUE_HIGH, - APCI1500_R_CPT_TMR3_VALUE_LOW, - APCI1500_RW_CPT_TMR1_TIME_CST_HIGH, - APCI1500_RW_CPT_TMR1_TIME_CST_LOW, - APCI1500_RW_CPT_TMR2_TIME_CST_HIGH, - APCI1500_RW_CPT_TMR2_TIME_CST_LOW, - APCI1500_RW_CPT_TMR3_TIME_CST_HIGH, - APCI1500_RW_CPT_TMR3_TIME_CST_LOW, - APCI1500_RW_CPT_TMR1_MODE_SPECIFICATION, - APCI1500_RW_CPT_TMR2_MODE_SPECIFICATION, - APCI1500_RW_CPT_TMR3_MODE_SPECIFICATION, - APCI1500_R_CURRENT_VECTOR, - - APCI1500_RW_PORT_A_SPECIFICATION, - APCI1500_RW_PORT_A_HANDSHAKE_SPECIFICATION, - APCI1500_RW_PORT_A_DATA_PCITCH_POLARITY, - APCI1500_RW_PORT_A_DATA_DIRECTION, - APCI1500_RW_PORT_A_SPECIAL_IO_CONTROL, - APCI1500_RW_PORT_A_PATTERN_POLARITY, - APCI1500_RW_PORT_A_PATTERN_TRANSITION, - APCI1500_RW_PORT_A_PATTERN_MASK, - - APCI1500_RW_PORT_B_SPECIFICATION, - APCI1500_RW_PORT_B_HANDSHAKE_SPECIFICATION, - APCI1500_RW_PORT_B_DATA_PCITCH_POLARITY, - APCI1500_RW_PORT_B_DATA_DIRECTION, - APCI1500_RW_PORT_B_SPECIAL_IO_CONTROL, - APCI1500_RW_PORT_B_PATTERN_POLARITY, - APCI1500_RW_PORT_B_PATTERN_TRANSITION, - APCI1500_RW_PORT_B_PATTERN_MASK -}; - -static int i_TimerCounter1Init; -static int i_TimerCounter2Init; -static int i_WatchdogCounter3Init; -static int i_Event1Status, i_Event2Status; -static int i_TimerCounterWatchdogInterrupt; -static int i_Logic, i_CounterLogic; -static int i_InterruptMask; -static int i_InputChannel; -static int i_TimerCounter1Enabled, i_TimerCounter2Enabled, - i_WatchdogCounter3Enabled; - -static unsigned int z8536_read(struct comedi_device *dev, unsigned int reg) -{ - unsigned long flags; - unsigned int val; - - spin_lock_irqsave(&dev->spinlock, flags); - outb(reg, dev->iobase + APCI1500_Z8536_CTRL_REG); - val = inb(dev->iobase + APCI1500_Z8536_CTRL_REG); - spin_unlock_irqrestore(&dev->spinlock, flags); - - return val; -} - -static void z8536_write(struct comedi_device *dev, - unsigned int val, unsigned int reg) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->spinlock, flags); - outb(reg, dev->iobase + APCI1500_Z8536_CTRL_REG); - outb(val, dev->iobase + APCI1500_Z8536_CTRL_REG); - spin_unlock_irqrestore(&dev->spinlock, flags); -} - -static void z8536_reset(struct comedi_device *dev) -{ - unsigned long flags; - - /* - * Even if the state of the Z8536 is not known, the following - * sequence will reset it and put it in State 0. - */ - spin_lock_irqsave(&dev->spinlock, flags); - inb(dev->iobase + APCI1500_Z8536_CTRL_REG); - outb(0, dev->iobase + APCI1500_Z8536_CTRL_REG); - inb(dev->iobase + APCI1500_Z8536_CTRL_REG); - outb(0, dev->iobase + APCI1500_Z8536_CTRL_REG); - outb(1, dev->iobase + APCI1500_Z8536_CTRL_REG); - outb(0, dev->iobase + APCI1500_Z8536_CTRL_REG); - spin_unlock_irqrestore(&dev->spinlock, flags); - - z8536_write(dev, 0xf4, APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - - z8536_write(dev, 0x10, APCI1500_RW_PORT_A_SPECIFICATION); - /* High level of port A means 1 */ - z8536_write(dev, 0xff, APCI1500_RW_PORT_A_DATA_PCITCH_POLARITY); - /* All bits used as inputs */ - z8536_write(dev, 0xff, APCI1500_RW_PORT_A_DATA_DIRECTION); - /* Deletes IP and IUS */ - z8536_write(dev, 0x20, APCI1500_RW_PORT_A_COMMAND_AND_STATUS); - /* Deactivates the interrupt management of port A */ - z8536_write(dev, 0xe0, APCI1500_RW_PORT_A_COMMAND_AND_STATUS); - /* Deletes the register */ - z8536_write(dev, 0x00, APCI1500_RW_PORT_A_HANDSHAKE_SPECIFICATION); - - z8536_write(dev, 0x10, APCI1500_RW_PORT_B_SPECIFICATION); - /* A high level of port B means 1 */ - z8536_write(dev, 0x7f, APCI1500_RW_PORT_B_DATA_PCITCH_POLARITY); - /* All bits used as inputs */ - z8536_write(dev, 0xff, APCI1500_RW_PORT_B_DATA_DIRECTION); - /* Deletes IP and IUS */ - z8536_write(dev, 0x20, APCI1500_RW_PORT_B_COMMAND_AND_STATUS); - /* Deactivates the interrupt management of port B */ - z8536_write(dev, 0xe0, APCI1500_RW_PORT_B_COMMAND_AND_STATUS); - /* Deletes the register */ - z8536_write(dev, 0x00, APCI1500_RW_PORT_B_HANDSHAKE_SPECIFICATION); - - /* High level of port C means 1 */ - z8536_write(dev, 0x09, APCI1500_RW_PORT_C_DATA_PCITCH_POLARITY); - /* All bits used as inputs except channel 1 */ - z8536_write(dev, 0x0e, APCI1500_RW_PORT_C_DATA_DIRECTION); - /* Deletes it */ - z8536_write(dev, 0x00, APCI1500_RW_PORT_C_SPECIAL_IO_CONTROL); - - /* Deletes IP and IUS */ - z8536_write(dev, 0x20, APCI1500_RW_CPT_TMR1_CMD_STATUS); - /* Deactivates the interrupt management of timer 1 */ - z8536_write(dev, 0xe0, APCI1500_RW_CPT_TMR1_CMD_STATUS); - - /* Deletes IP and IUS */ - z8536_write(dev, 0x20, APCI1500_RW_CPT_TMR2_CMD_STATUS); - /* Deactivates Timer 2 interrupt management */ - z8536_write(dev, 0xe0, APCI1500_RW_CPT_TMR2_CMD_STATUS); - - /* Deletes IP and IUS */ - z8536_write(dev, 0x20, APCI1500_RW_CPT_TMR3_CMD_STATUS); - /* Deactivates interrupt management of timer 3 */ - z8536_write(dev, 0xe0, APCI1500_RW_CPT_TMR3_CMD_STATUS); - - /* Deletes all interrupts */ - z8536_write(dev, 0x00, APCI1500_RW_MASTER_INTERRUPT_CONTROL); -} - -/* - * An event can be generated for each port. The first event is related to the - * first 8 channels (port 1) and the second to the following 6 channels (port 2) - * An interrupt is generated when one or both events have occurred. - * - * data[0] Number of the input port on which the event will take place (1 or 2) - * data[1] The event logic for port 1 has three possibilities: - * APCI1500_AND This logic links the inputs with an AND logic. - * APCI1500_OR This logic links the inputs with a OR logic. - * APCI1500_OR_PRIORITY This logic links the inputs with a priority OR - * logic. Input 1 has the highest priority level - * and input 8 the smallest. - * For the second port the user has 1 possibility: - * APCI1500_OR This logic links the inputs with a polarity OR logic - * data[2] These 8-character word for port1 and 6-character word for port 2 - * give the mask of the event. Each place gives the state of the input - * channels and can have one of these six characters - * 0 This input must be on 0 - * 1 This input must be on 1 - * 2 This input reacts to a falling edge - * 3 This input reacts to a rising edge - * 4 This input reacts to both edges - * 5 This input is not used for event - */ -static int apci1500_di_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - int i_PatternPolarity = 0, i_PatternTransition = 0, i_PatternMask = 0; - int i_MaxChannel = 0, i_Count = 0, i_EventMask = 0; - int i_PatternTransitionCount = 0, i_RegValue; - int i; - - /* Disables the main interrupt on the board */ - z8536_write(dev, 0x00, APCI1500_RW_MASTER_INTERRUPT_CONTROL); - - if (data[0] == 1) { - i_MaxChannel = 8; - } else { - if (data[0] == 2) { - i_MaxChannel = 6; - } else { - dev_warn(dev->class_dev, - "The specified port event does not exist\n"); - return -EINVAL; - } - } - switch (data[1]) { - case 0: - data[1] = APCI1500_AND; - break; - case 1: - data[1] = APCI1500_OR; - break; - case 2: - data[1] = APCI1500_OR_PRIORITY; - break; - default: - dev_warn(dev->class_dev, - "The specified interrupt logic does not exist\n"); - return -EINVAL; - } - - i_Logic = data[1]; - for (i_Count = i_MaxChannel, i = 0; i_Count > 0; i_Count--, i++) { - i_EventMask = data[2 + i]; - switch (i_EventMask) { - case 0: - i_PatternMask = - i_PatternMask | (1 << (i_MaxChannel - i_Count)); - break; - case 1: - i_PatternMask = - i_PatternMask | (1 << (i_MaxChannel - i_Count)); - i_PatternPolarity = - i_PatternPolarity | (1 << (i_MaxChannel - - i_Count)); - break; - case 2: - i_PatternMask = - i_PatternMask | (1 << (i_MaxChannel - i_Count)); - i_PatternTransition = - i_PatternTransition | (1 << (i_MaxChannel - - i_Count)); - break; - case 3: - i_PatternMask = - i_PatternMask | (1 << (i_MaxChannel - i_Count)); - i_PatternPolarity = - i_PatternPolarity | (1 << (i_MaxChannel - - i_Count)); - i_PatternTransition = - i_PatternTransition | (1 << (i_MaxChannel - - i_Count)); - break; - case 4: - i_PatternTransition = - i_PatternTransition | (1 << (i_MaxChannel - - i_Count)); - break; - case 5: - break; - default: - dev_warn(dev->class_dev, - "The option indicated in the event mask does not exist\n"); - return -EINVAL; - } - } - - if (data[0] == 1) { - /* Test the interrupt logic */ - - if (data[1] == APCI1500_AND || - data[1] == APCI1500_OR || - data[1] == APCI1500_OR_PRIORITY) { - /* Tests if a transition was declared */ - /* for a OR PRIORITY logic */ - - if (data[1] == APCI1500_OR_PRIORITY - && i_PatternTransition != 0) { - dev_warn(dev->class_dev, - "Transition error on an OR PRIORITY logic\n"); - return -EINVAL; - } - - /* Tests if more than one transition */ - /* was declared for an AND logic */ - - if (data[1] == APCI1500_AND) { - for (i_Count = 0; i_Count < 8; i_Count++) { - i_PatternTransitionCount = - i_PatternTransitionCount + - ((i_PatternTransition >> - i_Count) & 0x1); - - } - - if (i_PatternTransitionCount > 1) { - dev_warn(dev->class_dev, - "Transition error on an AND logic\n"); - return -EINVAL; - } - } - - /* Disable Port A */ - z8536_write(dev, 0xf0, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - - z8536_write(dev, i_PatternPolarity, - APCI1500_RW_PORT_A_PATTERN_POLARITY); - z8536_write(dev, i_PatternMask, - APCI1500_RW_PORT_A_PATTERN_MASK); - z8536_write(dev, i_PatternTransition, - APCI1500_RW_PORT_A_PATTERN_TRANSITION); - - /* Port A new mode */ - i_RegValue = z8536_read(dev, - APCI1500_RW_PORT_A_SPECIFICATION); - i_RegValue = (i_RegValue & 0xF9) | data[1] | 0x9; - z8536_write(dev, i_RegValue, - APCI1500_RW_PORT_A_SPECIFICATION); - - i_Event1Status = 1; - - /* Enable Port A */ - z8536_write(dev, 0xf4, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - } else { - dev_warn(dev->class_dev, - "The choice for interrupt logic does not exist\n"); - return -EINVAL; - } - } - - /* Test if event setting for port 2 */ - - if (data[0] == 2) { - /* Test the event logic */ - - if (data[1] == APCI1500_OR) { - /* Disable Port B */ - z8536_write(dev, 0x74, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - - i_RegValue = z8536_read(dev, - APCI1500_RW_PORT_B_SPECIFICATION); - i_RegValue = i_RegValue & 0xF9; - z8536_write(dev, i_RegValue, - APCI1500_RW_PORT_B_SPECIFICATION); - - /* Selects error channels 1 and 2 */ - i_PatternMask = (i_PatternMask | 0xC0); - i_PatternPolarity = (i_PatternPolarity | 0xC0); - i_PatternTransition = (i_PatternTransition | 0xC0); - - z8536_write(dev, i_PatternPolarity, - APCI1500_RW_PORT_B_PATTERN_POLARITY); - z8536_write(dev, i_PatternTransition, - APCI1500_RW_PORT_B_PATTERN_TRANSITION); - z8536_write(dev, i_PatternMask, - APCI1500_RW_PORT_B_PATTERN_MASK); - - i_RegValue = z8536_read(dev, - APCI1500_RW_PORT_B_SPECIFICATION); - i_RegValue = (i_RegValue & 0xF9) | 4; - z8536_write(dev, i_RegValue, - APCI1500_RW_PORT_B_SPECIFICATION); - - i_Event2Status = 1; - - /* Enable Port B */ - z8536_write(dev, 0xf4, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - } else { - dev_warn(dev->class_dev, - "The choice for interrupt logic does not exist\n"); - return -EINVAL; - } - } - - return insn->n; -} - -/* - * Allows or disallows a port event - * - * data[0] 0 = Start input event, 1 = Stop input event - * data[1] Number of port (1 or 2) - */ -static int apci1500_di_write(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - int i_Event1InterruptStatus = 0, i_Event2InterruptStatus = - 0, i_RegValue; - - switch (data[0]) { - case START: - /* Tests the port number */ - - if (data[1] == 1 || data[1] == 2) { - /* Test if port 1 selected */ - - if (data[1] == 1) { - /* Test if event initialised */ - if (i_Event1Status == 1) { - /* Disable Port A */ - z8536_write(dev, 0xf0, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - /* Allows the pattern interrupt */ - z8536_write(dev, 0xc0, - APCI1500_RW_PORT_A_COMMAND_AND_STATUS); - /* Enable Port A */ - z8536_write(dev, 0xf4, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - - i_Event1InterruptStatus = 1; - - i_RegValue = z8536_read(dev, - APCI1500_RW_PORT_A_SPECIFICATION); - - /* Authorizes the main interrupt on the board */ - z8536_write(dev, 0xd0, - APCI1500_RW_MASTER_INTERRUPT_CONTROL); - } else { - dev_warn(dev->class_dev, - "Event 1 not initialised\n"); - return -EINVAL; - } - } - if (data[1] == 2) { - - if (i_Event2Status == 1) { - /* Disable Port B */ - z8536_write(dev, 0x74, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - /* Allows the pattern interrupt */ - z8536_write(dev, 0xc0, - APCI1500_RW_PORT_B_COMMAND_AND_STATUS); - /* Enable Port B */ - z8536_write(dev, 0xf4, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - - /* Authorizes the main interrupt on the board */ - z8536_write(dev, 0xd0, - APCI1500_RW_MASTER_INTERRUPT_CONTROL); - - i_Event2InterruptStatus = 1; - } else { - dev_warn(dev->class_dev, - "Event 2 not initialised\n"); - return -EINVAL; - } - } - } else { - dev_warn(dev->class_dev, - "The port parameter is in error\n"); - return -EINVAL; - } - - break; - - case STOP: - /* Tests the port number */ - - if (data[1] == 1 || data[1] == 2) { - /* Test if port 1 selected */ - - if (data[1] == 1) { - /* Test if event initialised */ - if (i_Event1Status == 1) { - /* Disable Port A */ - z8536_write(dev, 0xf0, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - /* Inhibits the pattern interrupt */ - z8536_write(dev, 0xe0, - APCI1500_RW_PORT_A_COMMAND_AND_STATUS); - /* Enable Port A */ - z8536_write(dev, 0xf4, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - - i_Event1InterruptStatus = 0; - } else { - dev_warn(dev->class_dev, - "Event 1 not initialised\n"); - return -EINVAL; - } - } - if (data[1] == 2) { - /* Test if event initialised */ - if (i_Event2Status == 1) { - /* Disable Port B */ - z8536_write(dev, 0x74, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - /* Inhibits the pattern interrupt */ - z8536_write(dev, 0xe0, - APCI1500_RW_PORT_B_COMMAND_AND_STATUS); - /* Enable Port B */ - z8536_write(dev, 0xf4, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - - i_Event2InterruptStatus = 0; - } else { - - dev_warn(dev->class_dev, - "Event 2 not initialised\n"); - return -EINVAL; - } - } - - } else { - dev_warn(dev->class_dev, - "The port parameter is in error\n"); - return -EINVAL; - } - break; - default: - dev_warn(dev->class_dev, - "The option of START/STOP logic does not exist\n"); - return -EINVAL; - } - - return insn->n; -} - -/* - * Return the status of the digital input - */ -static int apci1500_di_read(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - /* Software reset */ - z8536_reset(dev); - - return insn->n; -} - -static int apci1500_di_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - struct apci1500_private *devpriv = dev->private; - - data[1] = inw(devpriv->addon + APCI1500_DI_REG); - - return insn->n; -} - -/* - * Configures the digital output memory and the digital output error interrupt - * - * data[1] 1 = Enable the voltage error interrupt - * 2 = Disable the voltage error interrupt - */ -static int apci1500_do_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - struct apci1500_private *devpriv = dev->private; - - devpriv->b_OutputMemoryStatus = data[0]; - return insn->n; -} - -/* - * Writes port value to the selected port - */ -static int apci1500_do_write(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - struct apci1500_private *devpriv = dev->private; - static unsigned int ui_Temp; - unsigned int ui_Temp1; - unsigned int ui_NoOfChannel = CR_CHAN(insn->chanspec); /* get the channel */ - - if (!devpriv->b_OutputMemoryStatus) - ui_Temp = 0; - - if (data[3] == 0) { - if (data[1] == 0) { - data[0] = (data[0] << ui_NoOfChannel) | ui_Temp; - outw(data[0], devpriv->addon + APCI1500_DO_REG); - } else { - if (data[1] == 1) { - switch (ui_NoOfChannel) { - - case 2: - data[0] = - (data[0] << (2 * - data[2])) | ui_Temp; - break; - - case 4: - data[0] = - (data[0] << (4 * - data[2])) | ui_Temp; - break; - - case 8: - data[0] = - (data[0] << (8 * - data[2])) | ui_Temp; - break; - - case 15: - data[0] = data[0] | ui_Temp; - break; - - default: - dev_err(dev->class_dev, - "chan spec wrong\n"); - return -EINVAL; /* "sorry channel spec wrong " */ - - } - - outw(data[0], devpriv->addon + APCI1500_DO_REG); - } else { - dev_warn(dev->class_dev, - "Specified channel not supported\n"); - return -EINVAL; - } - } - } else { - if (data[3] == 1) { - if (data[1] == 0) { - data[0] = ~data[0] & 0x1; - ui_Temp1 = 1; - ui_Temp1 = ui_Temp1 << ui_NoOfChannel; - ui_Temp = ui_Temp | ui_Temp1; - data[0] = - (data[0] << ui_NoOfChannel) ^ - 0xffffffff; - data[0] = data[0] & ui_Temp; - outw(data[0], devpriv->addon + APCI1500_DO_REG); - } else { - if (data[1] == 1) { - switch (ui_NoOfChannel) { - - case 2: - data[0] = ~data[0] & 0x3; - ui_Temp1 = 3; - ui_Temp1 = - ui_Temp1 << 2 * data[2]; - ui_Temp = ui_Temp | ui_Temp1; - data[0] = - ((data[0] << (2 * - data - [2])) ^ - 0xffffffff) & ui_Temp; - break; - - case 4: - data[0] = ~data[0] & 0xf; - ui_Temp1 = 15; - ui_Temp1 = - ui_Temp1 << 4 * data[2]; - ui_Temp = ui_Temp | ui_Temp1; - data[0] = - ((data[0] << (4 * - data - [2])) ^ - 0xffffffff) & ui_Temp; - break; - - case 8: - data[0] = ~data[0] & 0xff; - ui_Temp1 = 255; - ui_Temp1 = - ui_Temp1 << 8 * data[2]; - ui_Temp = ui_Temp | ui_Temp1; - data[0] = - ((data[0] << (8 * - data - [2])) ^ - 0xffffffff) & ui_Temp; - break; - - case 15: - break; - - default: - dev_err(dev->class_dev, - "chan spec wrong\n"); - return -EINVAL; /* "sorry channel spec wrong " */ - - } - - outw(data[0], - devpriv->addon + APCI1500_DO_REG); - } else { - dev_warn(dev->class_dev, - "Specified channel not supported\n"); - return -EINVAL; - } - } - } else { - dev_warn(dev->class_dev, - "Specified functionality does not exist\n"); - return -EINVAL; - } - } - ui_Temp = data[0]; - return insn->n; -} - -/* - * Configures The Watchdog - * - * data[0] 0 = APCI1500_115_KHZ, 1 = APCI1500_3_6_KHZ, 2 = APCI1500_1_8_KHZ - * data[1] 0 = Counter1/Timer1, 1 = Counter2/Timer2, 2 = Counter3/Watchdog - * data[2] 0 = Counter, 1 = Timer/Watchdog - * data[3] This parameter has two meanings. If the counter/timer is used as - * a counter the limit value of the counter is given. If the counter/timer - * is used as a timer, the divider factor for the output is given. - * data[4] 0 = APCI1500_CONTINUOUS, 1 = APCI1500_SINGLE - * data[5] 0 = Software Trigger, 1 = Hardware Trigger - * data[6] 0 = Software gate, 1 = Hardware gate - * data[7] 0 = Interrupt Disable, 1 = Interrupt Enable - */ -static int apci1500_timer_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - struct apci1500_private *devpriv = dev->private; - int i_TimerCounterMode, i_MasterConfiguration; - - devpriv->tsk_Current = current; - - /* Selection of the input clock */ - if (data[0] == 0 || data[0] == 1 || data[0] == 2) { - outw(data[0], devpriv->addon + APCI1500_CLK_SEL_REG); - } else { - if (data[0] != 3) { - dev_warn(dev->class_dev, - "The option for input clock selection does not exist\n"); - return -EINVAL; - } - } - /* Select the counter/timer */ - switch (data[1]) { - case COUNTER1: - /* selecting counter or timer */ - switch (data[2]) { - case 0: - data[2] = APCI1500_COUNTER; - break; - case 1: - data[2] = APCI1500_TIMER; - break; - default: - dev_warn(dev->class_dev, - "This choice is not a timer nor a counter\n"); - return -EINVAL; - } - - /* Selecting single or continuous mode */ - switch (data[4]) { - case 0: - data[4] = APCI1500_CONTINUOUS; - break; - case 1: - data[4] = APCI1500_SINGLE; - break; - default: - dev_warn(dev->class_dev, - "This option for single/continuous mode does not exist\n"); - return -EINVAL; - } - - i_TimerCounterMode = data[2] | data[4] | 7; - /* Test the reload value */ - - if ((data[3] >= 0) && (data[3] <= 65535)) { - if (data[7] == APCI1500_ENABLE || - data[7] == APCI1500_DISABLE) { - /* Writes the new mode */ - z8536_write(dev, i_TimerCounterMode, - APCI1500_RW_CPT_TMR1_MODE_SPECIFICATION); - - /* Writes the low value */ - z8536_write(dev, data[3], - APCI1500_RW_CPT_TMR1_TIME_CST_LOW); - /* Writes the high value */ - data[3] = data[3] >> 8; - z8536_write(dev, data[3], - APCI1500_RW_CPT_TMR1_TIME_CST_HIGH); - - /* Enables timer/counter 1 and triggers timer/counter 1 */ - i_MasterConfiguration = z8536_read(dev, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - i_MasterConfiguration = - i_MasterConfiguration | 0x40; - z8536_write(dev, i_MasterConfiguration, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - - /* Disable timer/counter 1 */ - z8536_write(dev, 0x00, - APCI1500_RW_CPT_TMR1_CMD_STATUS); - /* Trigger timer/counter 1 */ - z8536_write(dev, 0x02, - APCI1500_RW_CPT_TMR1_CMD_STATUS); - } else { - dev_warn(dev->class_dev, - "Error in selection of interrupt enable or disable\n"); - return -EINVAL; - } - } else { - dev_warn(dev->class_dev, - "Error in selection of reload value\n"); - return -EINVAL; - } - i_TimerCounterWatchdogInterrupt = data[7]; - i_TimerCounter1Init = 1; - break; - - case COUNTER2: /* selecting counter or timer */ - switch (data[2]) { - case 0: - data[2] = APCI1500_COUNTER; - break; - case 1: - data[2] = APCI1500_TIMER; - break; - default: - dev_warn(dev->class_dev, - "This choice is not a timer nor a counter\n"); - return -EINVAL; - } - - /* Selecting single or continuous mode */ - switch (data[4]) { - case 0: - data[4] = APCI1500_CONTINUOUS; - break; - case 1: - data[4] = APCI1500_SINGLE; - break; - default: - dev_warn(dev->class_dev, - "This option for single/continuous mode does not exist\n"); - return -EINVAL; - } - - /* Selecting software or hardware trigger */ - switch (data[5]) { - case 0: - data[5] = APCI1500_SOFTWARE_TRIGGER; - break; - case 1: - data[5] = APCI1500_HARDWARE_TRIGGER; - break; - default: - dev_warn(dev->class_dev, - "This choice for software or hardware trigger does not exist\n"); - return -EINVAL; - } - - /* Selecting software or hardware gate */ - switch (data[6]) { - case 0: - data[6] = APCI1500_SOFTWARE_GATE; - break; - case 1: - data[6] = APCI1500_HARDWARE_GATE; - break; - default: - dev_warn(dev->class_dev, - "This choice for software or hardware gate does not exist\n"); - return -EINVAL; - } - - i_TimerCounterMode = data[2] | data[4] | data[5] | data[6] | 7; - - /* Test the reload value */ - - if ((data[3] >= 0) && (data[3] <= 65535)) { - if (data[7] == APCI1500_ENABLE || - data[7] == APCI1500_DISABLE) { - /* Writes the new mode */ - z8536_write(dev, i_TimerCounterMode, - APCI1500_RW_CPT_TMR2_MODE_SPECIFICATION); - - /* Writes the low value */ - z8536_write(dev, data[3], - APCI1500_RW_CPT_TMR2_TIME_CST_LOW); - /* Writes the high value */ - data[3] = data[3] >> 8; - z8536_write(dev, data[3], - APCI1500_RW_CPT_TMR2_TIME_CST_HIGH); - - /* Enables timer/counter 2 and triggers timer/counter 2 */ - i_MasterConfiguration = z8536_read(dev, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - i_MasterConfiguration = - i_MasterConfiguration | 0x20; - z8536_write(dev, i_MasterConfiguration, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - - /* Disable timer/counter 2 */ - z8536_write(dev, 0x00, - APCI1500_RW_CPT_TMR2_CMD_STATUS); - /* Trigger timer/counter 1 */ - z8536_write(dev, 0x02, - APCI1500_RW_CPT_TMR2_CMD_STATUS); - } else { - dev_warn(dev->class_dev, - "Error in selection of interrupt enable or disable\n"); - return -EINVAL; - } - } else { - dev_warn(dev->class_dev, - "Error in selection of reload value\n"); - return -EINVAL; - } - i_TimerCounterWatchdogInterrupt = data[7]; - i_TimerCounter2Init = 1; - break; - - case COUNTER3: /* selecting counter or watchdog */ - switch (data[2]) { - case 0: - data[2] = APCI1500_COUNTER; - break; - case 1: - data[2] = APCI1500_WATCHDOG; - break; - default: - dev_warn(dev->class_dev, - "This choice is not a watchdog nor a counter\n"); - return -EINVAL; - } - - /* Selecting single or continuous mode */ - switch (data[4]) { - case 0: - data[4] = APCI1500_CONTINUOUS; - break; - case 1: - data[4] = APCI1500_SINGLE; - break; - default: - dev_warn(dev->class_dev, - "This option for single/continuous mode does not exist\n"); - return -EINVAL; - } - - /* Selecting software or hardware gate */ - switch (data[6]) { - case 0: - data[6] = APCI1500_SOFTWARE_GATE; - break; - case 1: - data[6] = APCI1500_HARDWARE_GATE; - break; - default: - dev_warn(dev->class_dev, - "This choice for software or hardware gate does not exist\n"); - return -EINVAL; - } - - /* Test if used for watchdog */ - - if (data[2] == APCI1500_WATCHDOG) { - /* - Enables the output line */ - /* - Enables retrigger */ - /* - Pulses output */ - i_TimerCounterMode = data[2] | data[4] | 0x54; - } else { - i_TimerCounterMode = data[2] | data[4] | data[6] | 7; - } - /* Test the reload value */ - - if ((data[3] >= 0) && (data[3] <= 65535)) { - if (data[7] == APCI1500_ENABLE || - data[7] == APCI1500_DISABLE) { - /* Writes the new mode */ - z8536_write(dev, i_TimerCounterMode, - APCI1500_RW_CPT_TMR3_MODE_SPECIFICATION); - - /* Writes the low value */ - z8536_write(dev, data[3], - APCI1500_RW_CPT_TMR3_TIME_CST_LOW); - /* Writes the high value */ - data[3] = data[3] >> 8; - z8536_write(dev, data[3], - APCI1500_RW_CPT_TMR3_TIME_CST_HIGH); - - /* Enables watchdog/counter 3 and triggers watchdog/counter 3 */ - i_MasterConfiguration = z8536_read(dev, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - i_MasterConfiguration = - i_MasterConfiguration | 0x10; - z8536_write(dev, i_MasterConfiguration, - APCI1500_RW_MASTER_CONFIGURATION_CONTROL); - - /* Test if COUNTER */ - if (data[2] == APCI1500_COUNTER) { - /* Disable the watchdog/counter 3 and starts it */ - z8536_write(dev, 0x00, - APCI1500_RW_CPT_TMR3_CMD_STATUS); - /* Trigger the watchdog/counter 3 and starts it */ - z8536_write(dev, 0x02, - APCI1500_RW_CPT_TMR3_CMD_STATUS); - } - - } else { - - dev_warn(dev->class_dev, - "Error in selection of interrupt enable or disable\n"); - return -EINVAL; - } - } else { - dev_warn(dev->class_dev, - "Error in selection of reload value\n"); - return -EINVAL; - } - i_TimerCounterWatchdogInterrupt = data[7]; - i_WatchdogCounter3Init = 1; - break; - - default: - dev_warn(dev->class_dev, - "The specified counter/timer option does not exist\n"); - return -EINVAL; - } - i_CounterLogic = data[2]; - return insn->n; -} - -/* - * Start / Stop or trigger the timer counter or Watchdog - * - * data[0] 0 = Counter1/Timer1, 1 = Counter2/Timer2, 2 = Counter3/Watchdog - * data[1] 0 = Start, 1 = Stop, 2 = Trigger - * data[2] 0 = Counter, 1 = Timer/Watchdog - */ -static int apci1500_timer_write(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - int i_CommandAndStatusValue; - - switch (data[0]) { - case COUNTER1: - switch (data[1]) { - case START: - if (i_TimerCounter1Init == 1) { - if (i_TimerCounterWatchdogInterrupt == 1) - i_CommandAndStatusValue = 0xC4; /* Enable the interrupt */ - else - i_CommandAndStatusValue = 0xE4; /* disable the interrupt */ - - /* Starts timer/counter 1 */ - i_TimerCounter1Enabled = 1; - z8536_write(dev, i_CommandAndStatusValue, - APCI1500_RW_CPT_TMR1_CMD_STATUS); - } else { - dev_warn(dev->class_dev, - "Counter/Timer1 not configured\n"); - return -EINVAL; - } - break; - - case STOP: - /* Stop timer/counter 1 */ - z8536_write(dev, 0x00, APCI1500_RW_CPT_TMR1_CMD_STATUS); - i_TimerCounter1Enabled = 0; - break; - - case TRIGGER: - if (i_TimerCounter1Init == 1) { - if (i_TimerCounter1Enabled == 1) { - /* Set Trigger and gate */ - - i_CommandAndStatusValue = 0x6; - } else { - /* Set Trigger */ - - i_CommandAndStatusValue = 0x2; - } - z8536_write(dev, i_CommandAndStatusValue, - APCI1500_RW_CPT_TMR1_CMD_STATUS); - } else { - dev_warn(dev->class_dev, - "Counter/Timer1 not configured\n"); - return -EINVAL; - } - break; - - default: - dev_warn(dev->class_dev, - "The specified option for start/stop/trigger does not exist\n"); - return -EINVAL; - } - break; - - case COUNTER2: - switch (data[1]) { - case START: - if (i_TimerCounter2Init == 1) { - if (i_TimerCounterWatchdogInterrupt == 1) - i_CommandAndStatusValue = 0xC4; /* Enable the interrupt */ - else - i_CommandAndStatusValue = 0xE4; /* disable the interrupt */ - - /* Starts timer/counter 2 */ - i_TimerCounter2Enabled = 1; - z8536_write(dev, i_CommandAndStatusValue, - APCI1500_RW_CPT_TMR2_CMD_STATUS); - } else { - dev_warn(dev->class_dev, - "Counter/Timer2 not configured\n"); - return -EINVAL; - } - break; - - case STOP: - /* Stop timer/counter 2 */ - z8536_write(dev, 0x00, APCI1500_RW_CPT_TMR2_CMD_STATUS); - i_TimerCounter2Enabled = 0; - break; - case TRIGGER: - if (i_TimerCounter2Init == 1) { - if (i_TimerCounter2Enabled == 1) { - /* Set Trigger and gate */ - - i_CommandAndStatusValue = 0x6; - } else { - /* Set Trigger */ - - i_CommandAndStatusValue = 0x2; - } - z8536_write(dev, i_CommandAndStatusValue, - APCI1500_RW_CPT_TMR2_CMD_STATUS); - } else { - dev_warn(dev->class_dev, - "Counter/Timer2 not configured\n"); - return -EINVAL; - } - break; - default: - dev_warn(dev->class_dev, - "The specified option for start/stop/trigger does not exist\n"); - return -EINVAL; - } - break; - case COUNTER3: - switch (data[1]) { - case START: - if (i_WatchdogCounter3Init == 1) { - - if (i_TimerCounterWatchdogInterrupt == 1) - i_CommandAndStatusValue = 0xC4; /* Enable the interrupt */ - else - i_CommandAndStatusValue = 0xE4; /* disable the interrupt */ - - /* Starts Watchdog/counter 3 */ - i_WatchdogCounter3Enabled = 1; - z8536_write(dev, i_CommandAndStatusValue, - APCI1500_RW_CPT_TMR3_CMD_STATUS); - } else { - dev_warn(dev->class_dev, - "Watchdog/Counter3 not configured\n"); - return -EINVAL; - } - break; - - case STOP: - /* Stop Watchdog/counter 3 */ - z8536_write(dev, 0x00, APCI1500_RW_CPT_TMR3_CMD_STATUS); - i_WatchdogCounter3Enabled = 0; - break; - - case TRIGGER: - switch (data[2]) { - case 0: /* triggering counter 3 */ - if (i_WatchdogCounter3Init == 1) { - if (i_WatchdogCounter3Enabled == 1) { - /* Set Trigger and gate */ - - i_CommandAndStatusValue = 0x6; - } else { - /* Set Trigger */ - - i_CommandAndStatusValue = 0x2; - } - z8536_write(dev, i_CommandAndStatusValue, - APCI1500_RW_CPT_TMR3_CMD_STATUS); - } else { - dev_warn(dev->class_dev, - "Counter3 not configured\n"); - return -EINVAL; - } - break; - case 1: - /* triggering Watchdog 3 */ - if (i_WatchdogCounter3Init == 1) { - z8536_write(dev, 0x06, - APCI1500_RW_CPT_TMR3_CMD_STATUS); - } else { - dev_warn(dev->class_dev, - "Watchdog 3 not configured\n"); - return -EINVAL; - } - break; - default: - dev_warn(dev->class_dev, - "Wrong choice of watchdog/counter3\n"); - return -EINVAL; - } - break; - default: - dev_warn(dev->class_dev, - "The specified option for start/stop/trigger does not exist\n"); - return -EINVAL; - } - break; - default: - dev_warn(dev->class_dev, - "The specified choice for counter/watchdog/timer does not exist\n"); - return -EINVAL; - } - return insn->n; -} - -/* - * Read The Watchdog - * - * data[0] 0 = Counter1/Timer1, 1 = Counter2/Timer2, 2 = Counter3/Watchdog - */ -static int apci1500_timer_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - int i_CommandAndStatusValue; - - switch (data[0]) { - case COUNTER1: - /* Read counter/timer1 */ - if (i_TimerCounter1Init == 1) { - if (i_TimerCounter1Enabled == 1) { - /* Set RCC and gate */ - - i_CommandAndStatusValue = 0xC; - } else { - /* Set RCC */ - - i_CommandAndStatusValue = 0x8; - } - z8536_write(dev, i_CommandAndStatusValue, - APCI1500_RW_CPT_TMR1_CMD_STATUS); - - data[0] = z8536_read(dev, - APCI1500_R_CPT_TMR1_VALUE_HIGH); - data[0] = data[0] << 8; - data[0] = data[0] & 0xff00; - data[0] |= z8536_read(dev, - APCI1500_R_CPT_TMR1_VALUE_LOW); - } else { - dev_warn(dev->class_dev, - "Timer/Counter1 not configured\n"); - return -EINVAL; - } - break; - case COUNTER2: - /* Read counter/timer2 */ - if (i_TimerCounter2Init == 1) { - if (i_TimerCounter2Enabled == 1) { - /* Set RCC and gate */ - - i_CommandAndStatusValue = 0xC; - } else { - /* Set RCC */ - - i_CommandAndStatusValue = 0x8; - } - z8536_write(dev, i_CommandAndStatusValue, - APCI1500_RW_CPT_TMR2_CMD_STATUS); - - data[0] = z8536_read(dev, - APCI1500_R_CPT_TMR2_VALUE_HIGH); - data[0] = data[0] << 8; - data[0] = data[0] & 0xff00; - data[0] |= z8536_read(dev, - APCI1500_R_CPT_TMR2_VALUE_LOW); - } else { - dev_warn(dev->class_dev, - "Timer/Counter2 not configured\n"); - return -EINVAL; - } - break; - case COUNTER3: - /* Read counter/watchdog2 */ - if (i_WatchdogCounter3Init == 1) { - if (i_WatchdogCounter3Enabled == 1) { - /* Set RCC and gate */ - - i_CommandAndStatusValue = 0xC; - } else { - /* Set RCC */ - - i_CommandAndStatusValue = 0x8; - } - z8536_write(dev, i_CommandAndStatusValue, - APCI1500_RW_CPT_TMR3_CMD_STATUS); - - data[0] = z8536_read(dev, - APCI1500_R_CPT_TMR3_VALUE_HIGH); - data[0] = data[0] << 8; - data[0] = data[0] & 0xff00; - data[0] |= z8536_read(dev, - APCI1500_R_CPT_TMR3_VALUE_LOW); - } else { - dev_warn(dev->class_dev, - "WatchdogCounter3 not configured\n"); - return -EINVAL; - } - break; - default: - dev_warn(dev->class_dev, - "The choice of timer/counter/watchdog does not exist\n"); - return -EINVAL; - } - - return insn->n; -} - -/* - * Read the interrupt mask - * - * data[0] The interrupt mask value - * data[1] Channel Number - */ -static int apci1500_timer_read(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - data[0] = i_InterruptMask; - data[1] = i_InputChannel; - i_InterruptMask = 0; - return insn->n; -} - -/* - * Configures the interrupt registers - */ -static int apci1500_do_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - struct apci1500_private *devpriv = dev->private; - int i_RegValue; - int i_Constant; - - devpriv->tsk_Current = current; - outl(0x0, devpriv->amcc + AMCC_OP_REG_INTCSR); - if (data[0] == 1) { - i_Constant = 0xC0; - } else { - if (data[0] == 0) { - i_Constant = 0x00; - } else { - dev_warn(dev->class_dev, - "The parameter passed to driver is in error for enabling the voltage interrupt\n"); - return -EINVAL; - } - } - - /* Writes the new configuration (APCI1500_OR) */ - i_RegValue = z8536_read(dev, APCI1500_RW_PORT_B_SPECIFICATION); - i_RegValue = (i_RegValue & 0xF9) | APCI1500_OR; - z8536_write(dev, i_RegValue, APCI1500_RW_PORT_B_SPECIFICATION); - - /* Authorises the interrupt on the board */ - z8536_write(dev, 0xc0, APCI1500_RW_PORT_B_COMMAND_AND_STATUS); - - z8536_write(dev, i_Constant, APCI1500_RW_PORT_B_PATTERN_POLARITY); - z8536_write(dev, i_Constant, APCI1500_RW_PORT_B_PATTERN_TRANSITION); - z8536_write(dev, i_Constant, APCI1500_RW_PORT_B_PATTERN_MASK); - - /* Deletes the interrupt of port A */ - i_RegValue = z8536_read(dev, APCI1500_RW_PORT_A_COMMAND_AND_STATUS); - i_RegValue = (i_RegValue & 0x0F) | 0x20; - z8536_write(dev, i_RegValue, APCI1500_RW_PORT_A_COMMAND_AND_STATUS); - - /* Deletes the interrupt of port B */ - i_RegValue = z8536_read(dev, APCI1500_RW_PORT_B_COMMAND_AND_STATUS); - i_RegValue = (i_RegValue & 0x0F) | 0x20; - z8536_write(dev, i_RegValue, APCI1500_RW_PORT_B_COMMAND_AND_STATUS); - - /* Deletes the interrupt of timer 1 */ - i_RegValue = z8536_read(dev, APCI1500_RW_CPT_TMR1_CMD_STATUS); - i_RegValue = (i_RegValue & 0x0F) | 0x20; - z8536_write(dev, i_RegValue, APCI1500_RW_CPT_TMR1_CMD_STATUS); - - /* Deletes the interrupt of timer 2 */ - i_RegValue = z8536_read(dev, APCI1500_RW_CPT_TMR2_CMD_STATUS); - i_RegValue = (i_RegValue & 0x0F) | 0x20; - z8536_write(dev, i_RegValue, APCI1500_RW_CPT_TMR2_CMD_STATUS); - - /* Deletes the interrupt of timer 3 */ - i_RegValue = z8536_read(dev, APCI1500_RW_CPT_TMR3_CMD_STATUS); - i_RegValue = (i_RegValue & 0x0F) | 0x20; - z8536_write(dev, i_RegValue, APCI1500_RW_CPT_TMR3_CMD_STATUS); - - /* Authorizes the main interrupt on the board */ - z8536_write(dev, 0xd0, APCI1500_RW_MASTER_INTERRUPT_CONTROL); - - /* Enables the PCI interrupt */ - outl(0x2000 | INTCSR_INBOX_FULL_INT, - devpriv->amcc + AMCC_OP_REG_INTCSR); - inl(devpriv->amcc + AMCC_OP_REG_IMB1); - inl(devpriv->amcc + AMCC_OP_REG_INTCSR); - outl(INTCSR_INBOX_INTR_STATUS | 0x2000 | INTCSR_INBOX_FULL_INT, - devpriv->amcc + AMCC_OP_REG_INTCSR); - - return insn->n; -} - -static irqreturn_t apci1500_interrupt(int irq, void *d) -{ - - struct comedi_device *dev = d; - struct apci1500_private *devpriv = dev->private; - unsigned int val; - - /* Clear the interrupt mask */ - i_InterruptMask = 0; - - val = inl(devpriv->amcc + AMCC_OP_REG_INTCSR); - if (!(val & INTCSR_INTR_ASSERTED)) - return IRQ_NONE; - - /* Disable all Interrupt */ - /* Selects the master interrupt control register */ - /* Disables the main interrupt on the board */ - val = z8536_read(dev, APCI1500_RW_PORT_A_COMMAND_AND_STATUS); - if ((val & 0x60) == 0x60) { - /* Deletes the interrupt of port A */ - val &= 0x0f; - val |= 0x20; - z8536_write(dev, val, APCI1500_RW_PORT_A_COMMAND_AND_STATUS); - i_InterruptMask = i_InterruptMask | 1; - if (i_Logic == APCI1500_OR_PRIORITY) { - val = z8536_read(dev, APCI1500_RW_PORT_A_SPECIFICATION); - - val = z8536_read(dev, - APCI1500_RW_PORT_A_INTERRUPT_CONTROL); - - i_InputChannel = 1 + (val >> 1); - - } else { - i_InputChannel = 0; - } - } - - val = z8536_read(dev, APCI1500_RW_PORT_B_COMMAND_AND_STATUS); - if ((val & 0x60) == 0x60) { - /* Deletes the interrupt of port B */ - val &= 0x0f; - val |= 0x20; - z8536_write(dev, val, APCI1500_RW_PORT_B_COMMAND_AND_STATUS); - - /* Reads port B */ - val = inb(dev->iobase + APCI1500_Z8536_PORTB_REG); - val &= 0xc0; - /* Tests if this is an external error */ - if (val) { - /* Disable the interrupt */ - /* Selects the command and status register of port B */ - outl(0x0, devpriv->amcc + AMCC_OP_REG_INTCSR); - - if (val & 0x80) - i_InterruptMask |= 0x40; - - if (val & 0x40) { - i_InterruptMask |= 0x80; - } - } else { - i_InterruptMask |= 0x02; - } - } - - val = z8536_read(dev, APCI1500_RW_CPT_TMR1_CMD_STATUS); - if ((val & 0x60) == 0x60) { - /* Deletes the interrupt of timer 1 */ - val &= 0x0f; - val |= 0x20; - z8536_write(dev, val, APCI1500_RW_CPT_TMR1_CMD_STATUS); - - i_InterruptMask |= 0x04; - } - - val = z8536_read(dev, APCI1500_RW_CPT_TMR2_CMD_STATUS); - if ((val & 0x60) == 0x60) { - /* Deletes the interrupt of timer 2 */ - val &= 0x0f; - val |= 0x20; - z8536_write(dev, val, APCI1500_RW_CPT_TMR2_CMD_STATUS); - - i_InterruptMask |= 0x08; - } - - val = z8536_read(dev, APCI1500_RW_CPT_TMR3_CMD_STATUS); - if ((val & 0x60) == 0x60) { - /* Deletes the interrupt of timer 3 */ - val &= 0x0f; - val |= 0x20; - z8536_write(dev, val, APCI1500_RW_CPT_TMR3_CMD_STATUS); - - if (i_CounterLogic == APCI1500_COUNTER) - i_InterruptMask |= 0x10; - else - i_InterruptMask |= 0x20; - } - - /* send signal to the sample */ - send_sig(SIGIO, devpriv->tsk_Current, 0); - - /* Authorizes the main interrupt on the board */ - z8536_write(dev, 0xd0, APCI1500_RW_MASTER_INTERRUPT_CONTROL); - - return IRQ_HANDLED; -} - -static int apci1500_reset(struct comedi_device *dev) -{ - struct apci1500_private *devpriv = dev->private; - - i_TimerCounter1Init = 0; - i_TimerCounter2Init = 0; - i_WatchdogCounter3Init = 0; - i_Event1Status = 0; - i_Event2Status = 0; - i_TimerCounterWatchdogInterrupt = 0; - i_Logic = 0; - i_CounterLogic = 0; - i_InterruptMask = 0; - i_InputChannel = 0; - i_TimerCounter1Enabled = 0; - i_TimerCounter2Enabled = 0; - i_WatchdogCounter3Enabled = 0; - - /* Software reset */ - z8536_reset(dev); - - /* reset all the digital outputs */ - outw(0x0, devpriv->addon + APCI1500_DO_REG); - - /* Deactivates all interrupts */ - z8536_write(dev, 0x00, APCI1500_RW_MASTER_INTERRUPT_CONTROL); - z8536_write(dev, 0x00, APCI1500_RW_PORT_A_COMMAND_AND_STATUS); - z8536_write(dev, 0x00, APCI1500_RW_PORT_B_COMMAND_AND_STATUS); - z8536_write(dev, 0x00, APCI1500_RW_CPT_TMR1_CMD_STATUS); - z8536_write(dev, 0x00, APCI1500_RW_CPT_TMR2_CMD_STATUS); - z8536_write(dev, 0x00, APCI1500_RW_CPT_TMR3_CMD_STATUS); - - return 0; -} diff --git a/drivers/staging/comedi/drivers/addi_apci_1500.c b/drivers/staging/comedi/drivers/addi_apci_1500.c index 6892c0a72ce0..fc7db1de0b90 100644 --- a/drivers/staging/comedi/drivers/addi_apci_1500.c +++ b/drivers/staging/comedi/drivers/addi_apci_1500.c @@ -1,11 +1,34 @@ +/* + * addi_apci_1500.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 + * info@addi-data.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + #include #include -#include #include #include "../comedidev.h" #include "comedi_fc.h" #include "amcc_s5933.h" +#include "z8536.h" /* * PCI Bar 0 Register map (devpriv->amcc) @@ -14,6 +37,7 @@ /* * PCI Bar 1 Register map (dev->iobase) + * see z8536.h for Z8536 internal registers and bit defines */ #define APCI1500_Z8536_PORTC_REG 0x00 #define APCI1500_Z8536_PORTB_REG 0x01 @@ -30,11 +54,702 @@ struct apci1500_private { unsigned long amcc; unsigned long addon; - unsigned char b_OutputMemoryStatus; - struct task_struct *tsk_Current; + + unsigned int clk_src; + + /* Digital trigger configuration [0]=AND [1]=OR */ + unsigned int pm[2]; /* Pattern Mask */ + unsigned int pt[2]; /* Pattern Transition */ + unsigned int pp[2]; /* Pattern Polarity */ }; -#include "addi-data/hwdrv_apci1500.c" +static unsigned int z8536_read(struct comedi_device *dev, unsigned int reg) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&dev->spinlock, flags); + outb(reg, dev->iobase + APCI1500_Z8536_CTRL_REG); + val = inb(dev->iobase + APCI1500_Z8536_CTRL_REG); + spin_unlock_irqrestore(&dev->spinlock, flags); + + return val; +} + +static void z8536_write(struct comedi_device *dev, + unsigned int val, unsigned int reg) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->spinlock, flags); + outb(reg, dev->iobase + APCI1500_Z8536_CTRL_REG); + outb(val, dev->iobase + APCI1500_Z8536_CTRL_REG); + spin_unlock_irqrestore(&dev->spinlock, flags); +} + +static void z8536_reset(struct comedi_device *dev) +{ + unsigned long flags; + + /* + * Even if the state of the Z8536 is not known, the following + * sequence will reset it and put it in State 0. + */ + spin_lock_irqsave(&dev->spinlock, flags); + inb(dev->iobase + APCI1500_Z8536_CTRL_REG); + outb(0, dev->iobase + APCI1500_Z8536_CTRL_REG); + inb(dev->iobase + APCI1500_Z8536_CTRL_REG); + outb(0, dev->iobase + APCI1500_Z8536_CTRL_REG); + outb(1, dev->iobase + APCI1500_Z8536_CTRL_REG); + outb(0, dev->iobase + APCI1500_Z8536_CTRL_REG); + spin_unlock_irqrestore(&dev->spinlock, flags); + + /* Disable all Ports and Counter/Timers */ + z8536_write(dev, 0x00, Z8536_CFG_CTRL_REG); + + /* + * Port A is connected to Ditial Input channels 0-7. + * Configure the port to allow interrupt detection. + */ + z8536_write(dev, Z8536_PAB_MODE_PTS_BIT | + Z8536_PAB_MODE_SB | + Z8536_PAB_MODE_PMS_DISABLE, + Z8536_PA_MODE_REG); + z8536_write(dev, 0xff, Z8536_PB_DPP_REG); + z8536_write(dev, 0xff, Z8536_PA_DD_REG); + + /* + * Port B is connected to Ditial Input channels 8-13. + * Configure the port to allow interrupt detection. + * + * NOTE: Bits 7 and 6 of Port B are connected to internal + * diagnostic signals and bit 7 is inverted. + */ + z8536_write(dev, Z8536_PAB_MODE_PTS_BIT | + Z8536_PAB_MODE_SB | + Z8536_PAB_MODE_PMS_DISABLE, + Z8536_PB_MODE_REG); + z8536_write(dev, 0x7f, Z8536_PB_DPP_REG); + z8536_write(dev, 0xff, Z8536_PB_DD_REG); + + /* + * Not sure what Port C is connected to... + */ + z8536_write(dev, 0x09, Z8536_PC_DPP_REG); + z8536_write(dev, 0x0e, Z8536_PC_DD_REG); + + /* + * Clear and disable all interrupt sources. + * + * Just in case, the reset of the Z8536 should have already + * done this. + */ + z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_PA_CMDSTAT_REG); + z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PA_CMDSTAT_REG); + + z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_PB_CMDSTAT_REG); + z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PB_CMDSTAT_REG); + + z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_CT_CMDSTAT_REG(0)); + z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_CT_CMDSTAT_REG(0)); + + z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_CT_CMDSTAT_REG(1)); + z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_CT_CMDSTAT_REG(1)); + + z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_CT_CMDSTAT_REG(2)); + z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_CT_CMDSTAT_REG(2)); + + /* Disable all interrupts */ + z8536_write(dev, 0x00, Z8536_INT_CTRL_REG); +} + +static void apci1500_port_enable(struct comedi_device *dev, bool enable) +{ + unsigned int cfg; + + cfg = z8536_read(dev, Z8536_CFG_CTRL_REG); + if (enable) + cfg |= (Z8536_CFG_CTRL_PAE | Z8536_CFG_CTRL_PBE); + else + cfg &= ~(Z8536_CFG_CTRL_PAE | Z8536_CFG_CTRL_PBE); + z8536_write(dev, cfg, Z8536_CFG_CTRL_REG); +} + +static void apci1500_timer_enable(struct comedi_device *dev, + unsigned int chan, bool enable) +{ + unsigned int bit; + unsigned int cfg; + + if (chan == 0) + bit = Z8536_CFG_CTRL_CT1E; + else if (chan == 1) + bit = Z8536_CFG_CTRL_CT2E; + else + bit = Z8536_CFG_CTRL_PCE_CT3E; + + cfg = z8536_read(dev, Z8536_CFG_CTRL_REG); + if (enable) { + cfg |= bit; + } else { + cfg &= ~bit; + z8536_write(dev, 0x00, Z8536_CT_CMDSTAT_REG(chan)); + } + z8536_write(dev, cfg, Z8536_CFG_CTRL_REG); +} + +static bool apci1500_ack_irq(struct comedi_device *dev, + unsigned int reg) +{ + unsigned int val; + + val = z8536_read(dev, reg); + if ((val & Z8536_STAT_IE_IP) == Z8536_STAT_IE_IP) { + val &= 0x0f; /* preserve any write bits */ + val |= Z8536_CMD_CLR_IP_IUS; + z8536_write(dev, val, reg); + + return true; + } + return false; +} + +static irqreturn_t apci1500_interrupt(int irq, void *d) +{ + struct comedi_device *dev = d; + struct apci1500_private *devpriv = dev->private; + struct comedi_subdevice *s = dev->read_subdev; + unsigned int status = 0; + unsigned int val; + + val = inl(devpriv->amcc + AMCC_OP_REG_INTCSR); + if (!(val & INTCSR_INTR_ASSERTED)) + return IRQ_NONE; + + if (apci1500_ack_irq(dev, Z8536_PA_CMDSTAT_REG)) + status |= 0x01; /* port a event (inputs 0-7) */ + + if (apci1500_ack_irq(dev, Z8536_PB_CMDSTAT_REG)) { + /* Tests if this is an external error */ + val = inb(dev->iobase + APCI1500_Z8536_PORTB_REG); + val &= 0xc0; + if (val) { + if (val & 0x80) /* voltage error */ + status |= 0x40; + if (val & 0x40) /* short circuit error */ + status |= 0x80; + } else { + status |= 0x02; /* port b event (inputs 8-13) */ + } + } + + /* + * NOTE: The 'status' returned by the sample matches the + * interrupt mask information from the APCI-1500 Users Manual. + * + * Mask Meaning + * ---------- ------------------------------------------ + * 0x00000001 Event 1 has occured + * 0x00000010 Event 2 has occured + * 0x00000100 Counter/timer 1 has run down (not implemented) + * 0x00001000 Counter/timer 2 has run down (not implemented) + * 0x00010000 Counter 3 has run down (not implemented) + * 0x00100000 Watchdog has run down (not implemented) + * 0x01000000 Voltage error + * 0x10000000 Short-circuit error + */ + comedi_buf_write_samples(s, &status, 1); + comedi_handle_events(dev, s); + + return IRQ_HANDLED; +} + +static int apci1500_di_cancel(struct comedi_device *dev, + struct comedi_subdevice *s) +{ + /* Disables the main interrupt on the board */ + z8536_write(dev, 0x00, Z8536_INT_CTRL_REG); + + /* Disable Ports A & B */ + apci1500_port_enable(dev, false); + + /* Ack any pending interrupts */ + apci1500_ack_irq(dev, Z8536_PA_CMDSTAT_REG); + apci1500_ack_irq(dev, Z8536_PB_CMDSTAT_REG); + + /* Disable pattern interrupts */ + z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PA_CMDSTAT_REG); + z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PB_CMDSTAT_REG); + + /* Enable Ports A & B */ + apci1500_port_enable(dev, true); + + return 0; +} + +static int apci1500_di_inttrig_start(struct comedi_device *dev, + struct comedi_subdevice *s, + unsigned int trig_num) +{ + struct apci1500_private *devpriv = dev->private; + struct comedi_cmd *cmd = &s->async->cmd; + unsigned int pa_mode = Z8536_PAB_MODE_PMS_DISABLE; + unsigned int pb_mode = Z8536_PAB_MODE_PMS_DISABLE; + unsigned int pa_trig = trig_num & 0x01; + unsigned int pb_trig = trig_num & 0x02; + bool valid_trig = false; + unsigned int val; + + if (trig_num != cmd->start_arg) + return -EINVAL; + + /* Disable Ports A & B */ + apci1500_port_enable(dev, false); + + /* Set Port A for selected trigger pattern */ + z8536_write(dev, devpriv->pm[pa_trig] & 0xff, Z8536_PA_PM_REG); + z8536_write(dev, devpriv->pt[pa_trig] & 0xff, Z8536_PA_PT_REG); + z8536_write(dev, devpriv->pp[pa_trig] & 0xff, Z8536_PA_PP_REG); + + /* Set Port B for selected trigger pattern */ + z8536_write(dev, (devpriv->pm[pb_trig] >> 8) & 0xff, Z8536_PB_PM_REG); + z8536_write(dev, (devpriv->pt[pb_trig] >> 8) & 0xff, Z8536_PB_PT_REG); + z8536_write(dev, (devpriv->pp[pb_trig] >> 8) & 0xff, Z8536_PB_PP_REG); + + /* Set Port A trigger mode (if enabled) and enable interrupt */ + if (devpriv->pm[pa_trig] & 0xff) { + pa_mode = pa_trig ? Z8536_PAB_MODE_PMS_AND + : Z8536_PAB_MODE_PMS_OR; + + val = z8536_read(dev, Z8536_PA_MODE_REG); + val &= ~Z8536_PAB_MODE_PMS_MASK; + val |= (pa_mode | Z8536_PAB_MODE_IMO); + z8536_write(dev, val, Z8536_PA_MODE_REG); + + z8536_write(dev, Z8536_CMD_SET_IE, Z8536_PA_CMDSTAT_REG); + + valid_trig = true; + + dev_dbg(dev->class_dev, + "Port A configured for %s mode pattern detection\n", + pa_trig ? "AND" : "OR"); + } + + /* Set Port B trigger mode (if enabled) and enable interrupt */ + if (devpriv->pm[pb_trig] & 0xff00) { + pb_mode = pb_trig ? Z8536_PAB_MODE_PMS_AND + : Z8536_PAB_MODE_PMS_OR; + + val = z8536_read(dev, Z8536_PB_MODE_REG); + val &= ~Z8536_PAB_MODE_PMS_MASK; + val |= (pb_mode | Z8536_PAB_MODE_IMO); + z8536_write(dev, val, Z8536_PB_MODE_REG); + + z8536_write(dev, Z8536_CMD_SET_IE, Z8536_PB_CMDSTAT_REG); + + valid_trig = true; + + dev_dbg(dev->class_dev, + "Port B configured for %s mode pattern detection\n", + pb_trig ? "AND" : "OR"); + } + + /* Enable Ports A & B */ + apci1500_port_enable(dev, true); + + if (!valid_trig) { + dev_dbg(dev->class_dev, + "digital trigger %d is not configured\n", trig_num); + return -EINVAL; + } + + /* Authorizes the main interrupt on the board */ + z8536_write(dev, Z8536_INT_CTRL_MIE | Z8536_INT_CTRL_DLC, + Z8536_INT_CTRL_REG); + + return 0; +} + +static int apci1500_di_cmd(struct comedi_device *dev, + struct comedi_subdevice *s) +{ + s->async->inttrig = apci1500_di_inttrig_start; + + return 0; +} + +static int apci1500_di_cmdtest(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_cmd *cmd) +{ + int err = 0; + + /* Step 1 : check if triggers are trivially valid */ + + err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT); + err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); + err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW); + err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); + err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE); + + if (err) + return 1; + + /* Step 2a : make sure trigger sources are unique */ + /* Step 2b : and mutually compatible */ + + /* Step 3: check if arguments are trivially valid */ + + /* + * Internal start source triggers: + * + * 0 AND mode for Port A (digital inputs 0-7) + * AND mode for Port B (digital inputs 8-13 and internal signals) + * + * 1 OR mode for Port A (digital inputs 0-7) + * AND mode for Port B (digital inputs 8-13 and internal signals) + * + * 2 AND mode for Port A (digital inputs 0-7) + * OR mode for Port B (digital inputs 8-13 and internal signals) + * + * 3 OR mode for Port A (digital inputs 0-7) + * OR mode for Port B (digital inputs 8-13 and internal signals) + */ + err |= cfc_check_trigger_arg_max(&cmd->start_arg, 3); + + err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); + err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0); + err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); + err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); + + if (err) + return 3; + + /* Step 4: fix up any arguments */ + + /* Step 5: check channel list if it exists */ + + return 0; +} + +/* + * The pattern-recognition logic must be configured before the digital + * input async command is started. + * + * Digital input channels 0 to 13 can generate interrupts. Channels 14 + * and 15 are connected to internal board status/diagnostic signals. + * + * Channel 14 - Voltage error (the external supply is < 5V) + * Channel 15 - Short-circuit/overtemperature error + * + * data[0] : INSN_CONFIG_DIGITAL_TRIG + * data[1] : trigger number + * 0 = AND mode + * 1 = OR mode + * data[2] : configuration operation: + * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts + * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = edge interrupts + * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = 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 apci1500_di_cfg_trig(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + struct apci1500_private *devpriv = dev->private; + unsigned int trig = data[1]; + unsigned int shift = data[3]; + unsigned int hi_mask = data[4] << shift; + unsigned int lo_mask = data[5] << shift; + unsigned int chan_mask = hi_mask | lo_mask; + unsigned int old_mask = (1 << shift) - 1; + unsigned int pm = devpriv->pm[trig] & old_mask; + unsigned int pt = devpriv->pt[trig] & old_mask; + unsigned int pp = devpriv->pp[trig] & old_mask; + + if (trig > 1) { + dev_dbg(dev->class_dev, + "invalid digital trigger number (0=AND, 1=OR)\n"); + return -EINVAL; + } + + if (chan_mask > 0xffff) { + dev_dbg(dev->class_dev, "invalid digital trigger channel\n"); + return -EINVAL; + } + + switch (data[2]) { + case COMEDI_DIGITAL_TRIG_DISABLE: + /* clear trigger configuration */ + pm = 0; + pt = 0; + pp = 0; + break; + case COMEDI_DIGITAL_TRIG_ENABLE_EDGES: + pm |= chan_mask; /* enable channels */ + pt |= chan_mask; /* enable edge detection */ + pp |= hi_mask; /* rising-edge channels */ + pp &= ~lo_mask; /* falling-edge channels */ + break; + case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS: + pm |= chan_mask; /* enable channels */ + pt &= ~chan_mask; /* enable level detection */ + pp |= hi_mask; /* high level channels */ + pp &= ~lo_mask; /* low level channels */ + break; + default: + return -EINVAL; + } + + /* + * The AND mode trigger can only have one channel (max) enabled + * for edge detection. + */ + if (trig == 0) { + int ret = 0; + unsigned int src; + + src = pt & 0xff; + if (src) + ret |= cfc_check_trigger_is_unique(src); + + src = (pt >> 8) & 0xff; + if (src) + ret |= cfc_check_trigger_is_unique(src); + + if (ret) { + dev_dbg(dev->class_dev, + "invalid AND trigger configuration\n"); + return ret; + } + } + + /* save the trigger configuration */ + devpriv->pm[trig] = pm; + devpriv->pt[trig] = pt; + devpriv->pp[trig] = pp; + + return insn->n; +} + +static int apci1500_di_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + switch (data[0]) { + case INSN_CONFIG_DIGITAL_TRIG: + return apci1500_di_cfg_trig(dev, s, insn, data); + default: + return -EINVAL; + } +} + +static int apci1500_di_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + struct apci1500_private *devpriv = dev->private; + + data[1] = inw(devpriv->addon + APCI1500_DI_REG); + + return insn->n; +} + +static int apci1500_do_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + struct apci1500_private *devpriv = dev->private; + + if (comedi_dio_update_state(s, data)) + outw(s->state, devpriv->addon + APCI1500_DO_REG); + + data[1] = s->state; + + return insn->n; +} + +static int apci1500_timer_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + struct apci1500_private *devpriv = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); + unsigned int val; + + switch (data[0]) { + case INSN_CONFIG_ARM: + val = data[1] & s->maxdata; + z8536_write(dev, val & 0xff, Z8536_CT_RELOAD_LSB_REG(chan)); + z8536_write(dev, (val >> 8) & 0xff, + Z8536_CT_RELOAD_MSB_REG(chan)); + + apci1500_timer_enable(dev, chan, true); + z8536_write(dev, Z8536_CT_CMDSTAT_GCB, + Z8536_CT_CMDSTAT_REG(chan)); + break; + case INSN_CONFIG_DISARM: + apci1500_timer_enable(dev, chan, false); + break; + + case INSN_CONFIG_GET_COUNTER_STATUS: + data[1] = 0; + val = z8536_read(dev, Z8536_CT_CMDSTAT_REG(chan)); + if (val & Z8536_CT_STAT_CIP) + data[1] |= COMEDI_COUNTER_COUNTING; + if (val & Z8536_CT_CMDSTAT_GCB) + data[1] |= COMEDI_COUNTER_ARMED; + if (val & Z8536_STAT_IP) { + data[1] |= COMEDI_COUNTER_TERMINAL_COUNT; + apci1500_ack_irq(dev, Z8536_CT_CMDSTAT_REG(chan)); + } + data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING | + COMEDI_COUNTER_TERMINAL_COUNT; + break; + + case INSN_CONFIG_SET_COUNTER_MODE: + /* Simulate the 8254 timer modes */ + switch (data[1]) { + case I8254_MODE0: + /* Interrupt on Terminal Count */ + val = Z8536_CT_MODE_ECE | + Z8536_CT_MODE_DCS_ONESHOT; + break; + case I8254_MODE1: + /* Hardware Retriggerable One-Shot */ + val = Z8536_CT_MODE_ETE | + Z8536_CT_MODE_DCS_ONESHOT; + break; + case I8254_MODE2: + /* Rate Generator */ + val = Z8536_CT_MODE_CSC | + Z8536_CT_MODE_DCS_PULSE; + break; + case I8254_MODE3: + /* Square Wave Mode */ + val = Z8536_CT_MODE_CSC | + Z8536_CT_MODE_DCS_SQRWAVE; + break; + case I8254_MODE4: + /* Software Triggered Strobe */ + val = Z8536_CT_MODE_REB | + Z8536_CT_MODE_DCS_PULSE; + break; + case I8254_MODE5: + /* Hardware Triggered Strobe (watchdog) */ + val = Z8536_CT_MODE_EOE | + Z8536_CT_MODE_ETE | + Z8536_CT_MODE_REB | + Z8536_CT_MODE_DCS_PULSE; + break; + default: + return -EINVAL; + } + apci1500_timer_enable(dev, chan, false); + z8536_write(dev, val, Z8536_CT_MODE_REG(chan)); + break; + + case INSN_CONFIG_SET_CLOCK_SRC: + if (data[1] > 2) + return -EINVAL; + devpriv->clk_src = data[1]; + if (devpriv->clk_src == 2) + devpriv->clk_src = 3; + outw(devpriv->clk_src, devpriv->addon + APCI1500_CLK_SEL_REG); + break; + case INSN_CONFIG_GET_CLOCK_SRC: + switch (devpriv->clk_src) { + case 0: + data[1] = 0; /* 111.86 kHz / 2 */ + data[2] = 17879; /* 17879 ns (approx) */ + break; + case 1: + data[1] = 1; /* 3.49 kHz / 2 */ + data[2] = 573066; /* 573066 ns (approx) */ + break; + case 3: + data[1] = 2; /* 1.747 kHz / 2 */ + data[2] = 1164822; /* 1164822 ns (approx) */ + break; + default: + return -EINVAL; + } + break; + + case INSN_CONFIG_SET_GATE_SRC: + if (chan == 0) + return -EINVAL; + + val = z8536_read(dev, Z8536_CT_MODE_REG(chan)); + val &= Z8536_CT_MODE_EGE; + if (data[1] == 1) + val |= Z8536_CT_MODE_EGE; + else if (data[1] > 1) + return -EINVAL; + z8536_write(dev, val, Z8536_CT_MODE_REG(chan)); + break; + case INSN_CONFIG_GET_GATE_SRC: + if (chan == 0) + return -EINVAL; + break; + + default: + return -EINVAL; + } + return insn->n; +} + +static int apci1500_timer_insn_write(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + unsigned int chan = CR_CHAN(insn->chanspec); + unsigned int cmd; + + cmd = z8536_read(dev, Z8536_CT_CMDSTAT_REG(chan)); + cmd &= Z8536_CT_CMDSTAT_GCB; /* preserve gate */ + cmd |= Z8536_CT_CMD_TCB; /* set trigger */ + + /* software trigger a timer, it only makes sense to do one write */ + if (insn->n) + z8536_write(dev, cmd, Z8536_CT_CMDSTAT_REG(chan)); + + return insn->n; +} + +static int apci1500_timer_insn_read(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + unsigned int chan = CR_CHAN(insn->chanspec); + unsigned int cmd; + unsigned int val; + int i; + + cmd = z8536_read(dev, Z8536_CT_CMDSTAT_REG(chan)); + cmd &= Z8536_CT_CMDSTAT_GCB; /* preserve gate */ + cmd |= Z8536_CT_CMD_RCC; /* set RCC */ + + for (i = 0; i < insn->n; i++) { + z8536_write(dev, cmd, Z8536_CT_CMDSTAT_REG(chan)); + + val = z8536_read(dev, Z8536_CT_VAL_MSB_REG(chan)) << 8; + val |= z8536_read(dev, Z8536_CT_VAL_LSB_REG(chan)); + + data[i] = val; + } + + return insn->n; +} static int apci1500_auto_attach(struct comedi_device *dev, unsigned long context) @@ -56,6 +771,8 @@ static int apci1500_auto_attach(struct comedi_device *dev, devpriv->amcc = pci_resource_start(pcidev, 0); devpriv->addon = pci_resource_start(pcidev, 2); + z8536_reset(dev); + if (pcidev->irq > 0) { ret = request_irq(pcidev->irq, apci1500_interrupt, IRQF_SHARED, dev->board_name, dev); @@ -67,51 +784,66 @@ static int apci1500_auto_attach(struct comedi_device *dev, if (ret) return ret; - /* Allocate and Initialise DI Subdevice Structures */ + /* Digital Input subdevice */ s = &dev->subdevices[0]; - s->type = COMEDI_SUBD_DI; - s->subdev_flags = SDF_READABLE; - s->n_chan = 16; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_config = apci1500_di_config; - s->insn_read = apci1500_di_read; - s->insn_write = apci1500_di_write; - s->insn_bits = apci1500_di_insn_bits; - - /* Allocate and Initialise DO Subdevice Structures */ + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 16; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_bits = apci1500_di_insn_bits; + if (dev->irq) { + dev->read_subdev = s; + s->subdev_flags |= SDF_CMD_READ; + s->len_chanlist = 1; + s->insn_config = apci1500_di_insn_config; + s->do_cmdtest = apci1500_di_cmdtest; + s->do_cmd = apci1500_di_cmd; + s->cancel = apci1500_di_cancel; + } + + /* Digital Output subdevice */ s = &dev->subdevices[1]; - s->type = COMEDI_SUBD_DO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - s->n_chan = 16; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_config = apci1500_do_config; - s->insn_write = apci1500_do_write; - s->insn_bits = apci1500_do_bits; - - /* Allocate and Initialise Timer Subdevice Structures */ + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITABLE; + s->n_chan = 16; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_bits = apci1500_do_insn_bits; + + /* reset all the digital outputs */ + outw(0x0, devpriv->addon + APCI1500_DO_REG); + + /* Counter/Timer(Watchdog) subdevice */ s = &dev->subdevices[2]; - s->type = COMEDI_SUBD_TIMER; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = 1; - s->maxdata = 0; - s->len_chanlist = 1; - s->range_table = &range_digital; - s->insn_write = apci1500_timer_write; - s->insn_read = apci1500_timer_read; - s->insn_config = apci1500_timer_config; - s->insn_bits = apci1500_timer_bits; - - apci1500_reset(dev); + s->type = COMEDI_SUBD_TIMER; + s->subdev_flags = SDF_WRITABLE | SDF_READABLE; + s->n_chan = 3; + s->maxdata = 0xffff; + s->range_table = &range_unknown; + s->insn_config = apci1500_timer_insn_config; + s->insn_write = apci1500_timer_insn_write; + s->insn_read = apci1500_timer_insn_read; + + /* Enable the PCI interrupt */ + if (dev->irq) { + outl(0x2000 | INTCSR_INBOX_FULL_INT, + devpriv->amcc + AMCC_OP_REG_INTCSR); + inl(devpriv->amcc + AMCC_OP_REG_IMB1); + inl(devpriv->amcc + AMCC_OP_REG_INTCSR); + outl(INTCSR_INBOX_INTR_STATUS | 0x2000 | INTCSR_INBOX_FULL_INT, + devpriv->amcc + AMCC_OP_REG_INTCSR); + } return 0; } static void apci1500_detach(struct comedi_device *dev) { - if (dev->iobase) - apci1500_reset(dev); + struct apci1500_private *devpriv = dev->private; + + if (devpriv->amcc) + outl(0x0, devpriv->amcc + AMCC_OP_REG_INTCSR); comedi_pci_detach(dev); } diff --git a/drivers/staging/comedi/drivers/z8536.h b/drivers/staging/comedi/drivers/z8536.h new file mode 100644 index 000000000000..7be53109cc8d --- /dev/null +++ b/drivers/staging/comedi/drivers/z8536.h @@ -0,0 +1,202 @@ +/* + * Z8536 CIO Internal registers + */ + +#ifndef _Z8536_H +#define _Z8536_H + +/* Master Interrupt Control register */ +#define Z8536_INT_CTRL_REG 0x00 +#define Z8536_INT_CTRL_MIE BIT(7) /* Master Interrupt Enable */ +#define Z8536_INT_CTRL_DLC BIT(6) /* Disable Lower Chain */ +#define Z8536_INT_CTRL_NV BIT(5) /* No Vector */ +#define Z8536_INT_CTRL_PA_VIS BIT(4) /* Port A Vect Inc Status */ +#define Z8536_INT_CTRL_PB_VIS BIT(3) /* Port B Vect Inc Status */ +#define Z8536_INT_CTRL_VT_VIS BIT(2) /* C/T Vect Inc Status */ +#define Z8536_INT_CTRL_RJA BIT(1) /* Right Justified Addresses */ +#define Z8536_INT_CTRL_RESET BIT(0) /* Reset */ + +/* Master Configuration Control register */ +#define Z8536_CFG_CTRL_REG 0x01 +#define Z8536_CFG_CTRL_PBE BIT(7) /* Port B Enable */ +#define Z8536_CFG_CTRL_CT1E BIT(6) /* C/T 1 Enable */ +#define Z8536_CFG_CTRL_CT2E BIT(5) /* C/T 2 Enable */ +#define Z8536_CFG_CTRL_PCE_CT3E BIT(4) /* Port C & C/T 3 Enable */ +#define Z8536_CFG_CTRL_PLC BIT(3) /* Port A/B Link Control */ +#define Z8536_CFG_CTRL_PAE BIT(2) /* Port A Enable */ +#define Z8536_CFG_CTRL_LC_INDEP (0 << 0)/* C/Ts Independent */ +#define Z8536_CFG_CTRL_LC_GATE (1 << 0)/* C/T 1 Out Gates C/T 2 */ +#define Z8536_CFG_CTRL_LC_TRIG (2 << 0)/* C/T 1 Out Triggers C/T 2 */ +#define Z8536_CFG_CTRL_LC_CLK (3 << 0)/* C/T 1 Out Clocks C/T 2 */ +#define Z8536_CFG_CTRL_LC_MASK (3 << 0)/* C/T Link Control mask */ + +/* Interrupt Vector registers */ +#define Z8536_PA_INT_VECT_REG 0x02 +#define Z8536_PB_INT_VECT_REG 0x03 +#define Z8536_CT_INT_VECT_REG 0x04 +#define Z8536_CURR_INT_VECT_REG 0x1f + +/* Port A/B & Counter/Timer 1/2/3 Command and Status registers */ +#define Z8536_PA_CMDSTAT_REG 0x08 +#define Z8536_PB_CMDSTAT_REG 0x09 +#define Z8536_CT1_CMDSTAT_REG 0x0a +#define Z8536_CT2_CMDSTAT_REG 0x0b +#define Z8536_CT3_CMDSTAT_REG 0x0c +#define Z8536_CT_CMDSTAT_REG(x) (0x0a + (x)) +#define Z8536_CMD_NULL (0 << 5)/* Null Code */ +#define Z8536_CMD_CLR_IP_IUS (1 << 5)/* Clear IP & IUS */ +#define Z8536_CMD_SET_IUS (2 << 5)/* Set IUS */ +#define Z8536_CMD_CLR_IUS (3 << 5)/* Clear IUS */ +#define Z8536_CMD_SET_IP (4 << 5)/* Set IP */ +#define Z8536_CMD_CLR_IP (5 << 5)/* Clear IP */ +#define Z8536_CMD_SET_IE (6 << 5)/* Set IE */ +#define Z8536_CMD_CLR_IE (7 << 5)/* Clear IE */ +#define Z8536_CMD_MASK (7 << 5) + +#define Z8536_STAT_IUS BIT(7) /* Interrupt Under Service */ +#define Z8536_STAT_IE BIT(6) /* Interrupt Enable */ +#define Z8536_STAT_IP BIT(5) /* Interrupt Pending */ +#define Z8536_STAT_ERR BIT(4) /* Interrupt Error */ +#define Z8536_STAT_IE_IP (Z8536_STAT_IE | Z8536_STAT_IP) + +#define Z8536_PAB_STAT_ORE BIT(3) /* Output Register Empty */ +#define Z8536_PAB_STAT_IRF BIT(2) /* Input Register Full */ +#define Z8536_PAB_STAT_PMF BIT(1) /* Pattern Match Flag */ +#define Z8536_PAB_CMDSTAT_IOE BIT(0) /* Interrupt On Error */ + +#define Z8536_CT_CMD_RCC BIT(3) /* Read Counter Control */ +#define Z8536_CT_CMDSTAT_GCB BIT(2) /* Gate Command Bit */ +#define Z8536_CT_CMD_TCB BIT(1) /* Trigger Command Bit */ +#define Z8536_CT_STAT_CIP BIT(0) /* Count In Progress */ + +/* Port Data registers */ +#define Z8536_PA_DATA_REG 0x0d +#define Z8536_PB_DATA_REG 0x0e +#define Z8536_PC_DATA_REG 0x0f + +/* Counter/Timer 1/2/3 Current Count registers */ +#define Z8536_CT1_VAL_MSB_REG 0x10 +#define Z8536_CT1_VAL_LSB_REG 0x11 +#define Z8536_CT2_VAL_MSB_REG 0x12 +#define Z8536_CT2_VAL_LSB_REG 0x13 +#define Z8536_CT3_VAL_MSB_REG 0x14 +#define Z8536_CT3_VAL_LSB_REG 0x15 +#define Z8536_CT_VAL_MSB_REG(x) (0x10 + ((x) * 2)) +#define Z8536_CT_VAL_LSB_REG(x) (0x11 + ((x) * 2)) + +/* Counter/Timer 1/2/3 Time Constant registers */ +#define Z8536_CT1_RELOAD_MSB_REG 0x16 +#define Z8536_CT1_RELOAD_LSB_REG 0x17 +#define Z8536_CT2_RELOAD_MSB_REG 0x18 +#define Z8536_CT2_RELOAD_LSB_REG 0x19 +#define Z8536_CT3_RELOAD_MSB_REG 0x1a +#define Z8536_CT3_RELOAD_LSB_REG 0x1b +#define Z8536_CT_RELOAD_MSB_REG(x) (0x16 + ((x) * 2)) +#define Z8536_CT_RELOAD_LSB_REG(x) (0x17 + ((x) * 2)) + +/* Counter/Timer 1/2/3 Mode Specification registers */ +#define Z8536_CT1_MODE_REG 0x1c +#define Z8536_CT2_MODE_REG 0x1d +#define Z8536_CT3_MODE_REG 0x1e +#define Z8536_CT_MODE_REG(x) (0x1c + (x)) +#define Z8536_CT_MODE_CSC BIT(7) /* Continuous/Single Cycle */ +#define Z8536_CT_MODE_EOE BIT(6) /* External Output Enable */ +#define Z8536_CT_MODE_ECE BIT(5) /* External Count Enable */ +#define Z8536_CT_MODE_ETE BIT(4) /* External Trigger Enable */ +#define Z8536_CT_MODE_EGE BIT(3) /* External Gate Enable */ +#define Z8536_CT_MODE_REB BIT(2) /* Retrigger Enable Bit */ +#define Z8536_CT_MODE_DCS_PULSE (0 << 0)/* Duty Cycle - Pulse */ +#define Z8536_CT_MODE_DCS_ONESHOT (1 << 0)/* Duty Cycle - One-Shot */ +#define Z8536_CT_MODE_DCS_SQRWAVE (2 << 0)/* Duty Cycle - Square Wave */ +#define Z8536_CT_MODE_DCS_DO_NOT_USE (3 << 0)/* Duty Cycle - Do Not Use */ +#define Z8536_CT_MODE_DCS_MASK (3 << 0)/* Duty Cycle mask */ + +/* Port A/B Mode Specification registers */ +#define Z8536_PA_MODE_REG 0x20 +#define Z8536_PB_MODE_REG 0x28 +#define Z8536_PAB_MODE_PTS_BIT (0 << 6)/* Bit Port */ +#define Z8536_PAB_MODE_PTS_INPUT (1 << 6)/* Input Port */ +#define Z8536_PAB_MODE_PTS_OUTPUT (2 << 6)/* Output Port */ +#define Z8536_PAB_MODE_PTS_BIDIR (3 << 6)/* Bidirectional Port */ +#define Z8536_PAB_MODE_PTS_MASK (3 << 6)/* Port Type Select mask */ +#define Z8536_PAB_MODE_ITB BIT(5) /* Interrupt on Two Bytes */ +#define Z8536_PAB_MODE_SB BIT(4) /* Single Buffered mode */ +#define Z8536_PAB_MODE_IMO BIT(3) /* Interrupt on Match Only */ +#define Z8536_PAB_MODE_PMS_DISABLE (0 << 1)/* Disable Pattern Match */ +#define Z8536_PAB_MODE_PMS_AND (1 << 1)/* "AND" mode */ +#define Z8536_PAB_MODE_PMS_OR (2 << 1)/* "OR" mode */ +#define Z8536_PAB_MODE_PMS_OR_PEV (3 << 1)/* "OR-Priority" mode */ +#define Z8536_PAB_MODE_PMS_MASK (3 << 1)/* Pattern Mode mask */ +#define Z8536_PAB_MODE_LPM BIT(0) /* Latch on Pattern Match */ +#define Z8536_PAB_MODE_DTE BIT(0) /* Deskew Timer Enabled */ + +/* Port A/B Handshake Specification registers */ +#define Z8536_PA_HANDSHAKE_REG 0x21 +#define Z8536_PB_HANDSHAKE_REG 0x29 +#define Z8536_PAB_HANDSHAKE_HST_INTER (0 << 6)/* Interlocked Handshake */ +#define Z8536_PAB_HANDSHAKE_HST_STROBED (1 << 6)/* Strobed Handshake */ +#define Z8536_PAB_HANDSHAKE_HST_PULSED (2 << 6)/* Pulsed Handshake */ +#define Z8536_PAB_HANDSHAKE_HST_3WIRE (3 << 6)/* Three-Wire Handshake */ +#define Z8536_PAB_HANDSHAKE_HST_MASK (3 << 6)/* Handshake Type mask */ +#define Z8536_PAB_HANDSHAKE_RWS_DISABLE (0 << 3)/* Req/Wait Disabled */ +#define Z8536_PAB_HANDSHAKE_RWS_OUTWAIT (1 << 3)/* Output Wait */ +#define Z8536_PAB_HANDSHAKE_RWS_INWAIT (3 << 3)/* Input Wait */ +#define Z8536_PAB_HANDSHAKE_RWS_SPREQ (4 << 3)/* Special Request */ +#define Z8536_PAB_HANDSHAKE_RWS_OUTREQ (5 << 4)/* Output Request */ +#define Z8536_PAB_HANDSHAKE_RWS_INREQ (7 << 3)/* Input Request */ +#define Z8536_PAB_HANDSHAKE_RWS_MASK (7 << 3)/* Req/Wait mask */ +#define Z8536_PAB_HANDSHAKE_DESKEW(x) ((x) << 0)/* Deskew Time */ +#define Z8536_PAB_HANDSHAKE_DESKEW_MASK (3 << 0)/* Deskew Time mask */ + +/* + * Port A/B/C Data Path Polarity registers + * + * 0 = Non-Inverting + * 1 = Inverting + */ +#define Z8536_PA_DPP_REG 0x22 +#define Z8536_PB_DPP_REG 0x2a +#define Z8536_PC_DPP_REG 0x05 + +/* + * Port A/B/C Data Direction registers + * + * 0 = Output bit + * 1 = Input bit + */ +#define Z8536_PA_DD_REG 0x23 +#define Z8536_PB_DD_REG 0x2b +#define Z8536_PC_DD_REG 0x06 + +/* + * Port A/B/C Special I/O Control registers + * + * 0 = Normal Input or Output + * 1 = Output with open drain or Input with 1's catcher + */ +#define Z8536_PA_SIO_REG 0x24 +#define Z8536_PB_SIO_REG 0x2c +#define Z8536_PC_SIO_REG 0x07 + +/* + * Port A/B Pattern Polarity/Transition/Mask registers + * + * PM PT PP Pattern Specification + * -- -- -- ------------------------------------- + * 0 0 x Bit masked off + * 0 1 x Any transition + * 1 0 0 Zero (low-level) + * 1 0 1 One (high-level) + * 1 1 0 One-to-zero transition (falling-edge) + * 1 1 1 Zero-to-one transition (rising-edge) + */ +#define Z8536_PA_PP_REG 0x25 +#define Z8536_PB_PP_REG 0x2d + +#define Z8536_PA_PT_REG 0x26 +#define Z8536_PB_PT_REG 0x2e + +#define Z8536_PA_PM_REG 0x27 +#define Z8536_PB_PM_REG 0x2f + +#endif /* _Z8536_H */ -- 2.20.1