From 0884885c87dfcd66c5734ce59dd1cea884bf7f71 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 19 Nov 2008 14:21:25 -0800 Subject: [PATCH 17/21] Staging: comedi: add rtd520 driver From: Dan Christian This adds the rtd520 comedi driver to the build. From: Dan Christian Cc: David Schleef Cc: Frank Mori Hess Cc: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/Makefile | 1 drivers/staging/comedi/drivers/plx9080.h | 429 +++++ drivers/staging/comedi/drivers/rtd520.c | 2283 +++++++++++++++++++++++++++++++ drivers/staging/comedi/drivers/rtd520.h | 412 +++++ 4 files changed, 3125 insertions(+) create mode 100644 drivers/staging/comedi/drivers/plx9080.h create mode 100644 drivers/staging/comedi/drivers/rtd520.c create mode 100644 drivers/staging/comedi/drivers/rtd520.h --- a/drivers/staging/comedi/drivers/Makefile +++ b/drivers/staging/comedi/drivers/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_COMEDI) += comedi_parport obj-$(CONFIG_COMEDI_PCI_DRIVERS) += mite.o obj-$(CONFIG_COMEDI_PCI_DRIVERS) += icp_multi.o obj-$(CONFIG_COMEDI_PCI_DRIVERS) += me4000.o +obj-$(CONFIG_COMEDI_PCI_DRIVERS) += rtd520.o obj-$(CONFIG_COMEDI_PCI_DRIVERS) += s626.o # Comedi USB drivers --- /dev/null +++ b/drivers/staging/comedi/drivers/plx9080.h @@ -0,0 +1,429 @@ +/* plx9080.h + * + * Copyright (C) 2002,2003 Frank Mori Hess + * + * I modified this file from the plx9060.h header for the + * wanXL device driver in the linux kernel, + * for the register offsets and bit definitions. Made minor modifications, + * added plx9080 registers and + * stripped out stuff that was specifically for the wanXL driver. + * Note: I've only made sure the definitions are correct as far + * as I make use of them. There are still various plx9060-isms + * left in this header file. + * + ******************************************************************** + * + * Copyright (C) 1999 RG Studio s.c., http://www.rgstudio.com.pl/ + * Written by Krzysztof Halasa + * + * Portions (C) SBE Inc., used by permission. + * + * 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. + */ + +#ifndef __COMEDI_PLX9080_H +#define __COMEDI_PLX9080_H + +// descriptor block used for chained dma transfers +struct plx_dma_desc { + volatile uint32_t pci_start_addr; + volatile uint32_t local_start_addr; + /* transfer_size is in bytes, only first 23 bits of register are used */ + volatile uint32_t transfer_size; + /* address of next descriptor (quad word aligned), plus some + * additional bits (see PLX_DMA0_DESCRIPTOR_REG) */ + volatile uint32_t next; +}; + +/********************************************************************** +** Register Offsets and Bit Definitions +** +** Note: All offsets zero relative. IE. Some standard base address +** must be added to the Register Number to properly access the register. +** +**********************************************************************/ + +#define PLX_LAS0RNG_REG 0x0000 /* L, Local Addr Space 0 Range Register */ +#define PLX_LAS1RNG_REG 0x00f0 /* L, Local Addr Space 1 Range Register */ +#define LRNG_IO 0x00000001 /* Map to: 1=I/O, 0=Mem */ +#define LRNG_ANY32 0x00000000 /* Locate anywhere in 32 bit */ +#define LRNG_LT1MB 0x00000002 /* Locate in 1st meg */ +#define LRNG_ANY64 0x00000004 /* Locate anywhere in 64 bit */ +#define LRNG_MEM_MASK 0xfffffff0 // bits that specify range for memory io +#define LRNG_IO_MASK 0xfffffffa // bits that specify range for normal io + +#define PLX_LAS0MAP_REG 0x0004 /* L, Local Addr Space 0 Remap Register */ +#define PLX_LAS1MAP_REG 0x00f4 /* L, Local Addr Space 1 Remap Register */ +#define LMAP_EN 0x00000001 /* Enable slave decode */ +#define LMAP_MEM_MASK 0xfffffff0 // bits that specify decode for memory io +#define LMAP_IO_MASK 0xfffffffa // bits that specify decode bits for normal io + +/* Mode/Arbitration Register. +*/ +#define PLX_MARB_REG 0x8 /* L, Local Arbitration Register */ +#define PLX_DMAARB_REG 0xac +enum marb_bits { + MARB_LLT_MASK = 0x000000ff, /* Local Bus Latency Timer */ + MARB_LPT_MASK = 0x0000ff00, /* Local Bus Pause Timer */ + MARB_LTEN = 0x00010000, /* Latency Timer Enable */ + MARB_LPEN = 0x00020000, /* Pause Timer Enable */ + MARB_BREQ = 0x00040000, /* Local Bus BREQ Enable */ + MARB_DMA_PRIORITY_MASK = 0x00180000, + MARB_LBDS_GIVE_UP_BUS_MODE = 0x00200000, /* local bus direct slave give up bus mode */ + MARB_DS_LLOCK_ENABLE = 0x00400000, /* direct slave LLOCKo# enable */ + MARB_PCI_REQUEST_MODE = 0x00800000, + MARB_PCIv21_MODE = 0x01000000, /* pci specification v2.1 mode */ + MARB_PCI_READ_NO_WRITE_MODE = 0x02000000, + MARB_PCI_READ_WITH_WRITE_FLUSH_MODE = 0x04000000, + MARB_GATE_TIMER_WITH_BREQ = 0x08000000, /* gate local bus latency timer with BREQ */ + MARB_PCI_READ_NO_FLUSH_MODE = 0x10000000, + MARB_USE_SUBSYSTEM_IDS = 0x20000000, +}; + +#define PLX_BIGEND_REG 0xc +enum bigend_bits { + BIGEND_CONFIG = 0x1, /* use big endian ordering for configuration register accesses */ + BIGEND_DIRECT_MASTER = 0x2, + BIGEND_DIRECT_SLAVE_LOCAL0 = 0x4, + BIGEND_ROM = 0x8, + BIGEND_BYTE_LANE = 0x10, /* use byte lane consisting of most significant bits instead of least significant */ + BIGEND_DIRECT_SLAVE_LOCAL1 = 0x20, + BIGEND_DMA1 = 0x40, + BIGEND_DMA0 = 0x80, +}; + +/* Note: The Expansion ROM stuff is only relevant to the PC environment. +** This expansion ROM code is executed by the host CPU at boot time. +** For this reason no bit definitions are provided here. +*/ +#define PLX_ROMRNG_REG 0x0010 /* L, Expn ROM Space Range Register */ +#define PLX_ROMMAP_REG 0x0014 /* L, Local Addr Space Range Register */ + +#define PLX_REGION0_REG 0x0018 /* L, Local Bus Region 0 Descriptor */ +#define RGN_WIDTH 0x00000002 /* Local bus width bits */ +#define RGN_8BITS 0x00000000 /* 08 bit Local Bus */ +#define RGN_16BITS 0x00000001 /* 16 bit Local Bus */ +#define RGN_32BITS 0x00000002 /* 32 bit Local Bus */ +#define RGN_MWS 0x0000003C /* Memory Access Wait States */ +#define RGN_0MWS 0x00000000 +#define RGN_1MWS 0x00000004 +#define RGN_2MWS 0x00000008 +#define RGN_3MWS 0x0000000C +#define RGN_4MWS 0x00000010 +#define RGN_6MWS 0x00000018 +#define RGN_8MWS 0x00000020 +#define RGN_MRE 0x00000040 /* Memory Space Ready Input Enable */ +#define RGN_MBE 0x00000080 /* Memory Space Bterm Input Enable */ +#define RGN_READ_PREFETCH_DISABLE 0x00000100 +#define RGN_ROM_PREFETCH_DISABLE 0x00000200 +#define RGN_READ_PREFETCH_COUNT_ENABLE 0x00000400 +#define RGN_RWS 0x003C0000 /* Expn ROM Wait States */ +#define RGN_RRE 0x00400000 /* ROM Space Ready Input Enable */ +#define RGN_RBE 0x00800000 /* ROM Space Bterm Input Enable */ +#define RGN_MBEN 0x01000000 /* Memory Space Burst Enable */ +#define RGN_RBEN 0x04000000 /* ROM Space Burst Enable */ +#define RGN_THROT 0x08000000 /* De-assert TRDY when FIFO full */ +#define RGN_TRD 0xF0000000 /* Target Ready Delay /8 */ + +#define PLX_REGION1_REG 0x00f8 /* L, Local Bus Region 1 Descriptor */ + +#define PLX_DMRNG_REG 0x001C /* L, Direct Master Range Register */ + +#define PLX_LBAPMEM_REG 0x0020 /* L, Lcl Base Addr for PCI mem space */ + +#define PLX_LBAPIO_REG 0x0024 /* L, Lcl Base Addr for PCI I/O space */ + +#define PLX_DMMAP_REG 0x0028 /* L, Direct Master Remap Register */ +#define DMM_MAE 0x00000001 /* Direct Mstr Memory Acc Enable */ +#define DMM_IAE 0x00000002 /* Direct Mstr I/O Acc Enable */ +#define DMM_LCK 0x00000004 /* LOCK Input Enable */ +#define DMM_PF4 0x00000008 /* Prefetch 4 Mode Enable */ +#define DMM_THROT 0x00000010 /* Assert IRDY when read FIFO full */ +#define DMM_PAF0 0x00000000 /* Programmable Almost fill level */ +#define DMM_PAF1 0x00000020 /* Programmable Almost fill level */ +#define DMM_PAF2 0x00000040 /* Programmable Almost fill level */ +#define DMM_PAF3 0x00000060 /* Programmable Almost fill level */ +#define DMM_PAF4 0x00000080 /* Programmable Almost fill level */ +#define DMM_PAF5 0x000000A0 /* Programmable Almost fill level */ +#define DMM_PAF6 0x000000C0 /* Programmable Almost fill level */ +#define DMM_PAF7 0x000000D0 /* Programmable Almost fill level */ +#define DMM_MAP 0xFFFF0000 /* Remap Address Bits */ + +#define PLX_CAR_REG 0x002C /* L, Configuration Address Register */ +#define CAR_CT0 0x00000000 /* Config Type 0 */ +#define CAR_CT1 0x00000001 /* Config Type 1 */ +#define CAR_REG 0x000000FC /* Register Number Bits */ +#define CAR_FUN 0x00000700 /* Function Number Bits */ +#define CAR_DEV 0x0000F800 /* Device Number Bits */ +#define CAR_BUS 0x00FF0000 /* Bus Number Bits */ +#define CAR_CFG 0x80000000 /* Config Spc Access Enable */ + +#define PLX_DBR_IN_REG 0x0060 /* L, PCI to Local Doorbell Register */ + +#define PLX_DBR_OUT_REG 0x0064 /* L, Local to PCI Doorbell Register */ + +#define PLX_INTRCS_REG 0x0068 /* L, Interrupt Control/Status Reg */ +#define ICS_AERR 0x00000001 /* Assert LSERR on ABORT */ +#define ICS_PERR 0x00000002 /* Assert LSERR on Parity Error */ +#define ICS_SERR 0x00000004 /* Generate PCI SERR# */ +#define ICS_MBIE 0x00000008 // mailbox interrupt enable +#define ICS_PIE 0x00000100 /* PCI Interrupt Enable */ +#define ICS_PDIE 0x00000200 /* PCI Doorbell Interrupt Enable */ +#define ICS_PAIE 0x00000400 /* PCI Abort Interrupt Enable */ +#define ICS_PLIE 0x00000800 /* PCI Local Int Enable */ +#define ICS_RAE 0x00001000 /* Retry Abort Enable */ +#define ICS_PDIA 0x00002000 /* PCI Doorbell Interrupt Active */ +#define ICS_PAIA 0x00004000 /* PCI Abort Interrupt Active */ +#define ICS_LIA 0x00008000 /* Local Interrupt Active */ +#define ICS_LIE 0x00010000 /* Local Interrupt Enable */ +#define ICS_LDIE 0x00020000 /* Local Doorbell Int Enable */ +#define ICS_DMA0_E 0x00040000 /* DMA #0 Interrupt Enable */ +#define ICS_DMA1_E 0x00080000 /* DMA #1 Interrupt Enable */ +#define ICS_LDIA 0x00100000 /* Local Doorbell Int Active */ +#define ICS_DMA0_A 0x00200000 /* DMA #0 Interrupt Active */ +#define ICS_DMA1_A 0x00400000 /* DMA #1 Interrupt Active */ +#define ICS_BIA 0x00800000 /* BIST Interrupt Active */ +#define ICS_TA_DM 0x01000000 /* Target Abort - Direct Master */ +#define ICS_TA_DMA0 0x02000000 /* Target Abort - DMA #0 */ +#define ICS_TA_DMA1 0x04000000 /* Target Abort - DMA #1 */ +#define ICS_TA_RA 0x08000000 /* Target Abort - Retry Timeout */ +#define ICS_MBIA(x) (0x10000000 << ((x) & 0x3)) // mailbox x is active + +#define PLX_CONTROL_REG 0x006C /* L, EEPROM Cntl & PCI Cmd Codes */ +#define CTL_RDMA 0x0000000E /* DMA Read Command */ +#define CTL_WDMA 0x00000070 /* DMA Write Command */ +#define CTL_RMEM 0x00000600 /* Memory Read Command */ +#define CTL_WMEM 0x00007000 /* Memory Write Command */ +#define CTL_USERO 0x00010000 /* USERO output pin control bit */ +#define CTL_USERI 0x00020000 /* USERI input pin bit */ +#define CTL_EE_CLK 0x01000000 /* EEPROM Clock line */ +#define CTL_EE_CS 0x02000000 /* EEPROM Chip Select */ +#define CTL_EE_W 0x04000000 /* EEPROM Write bit */ +#define CTL_EE_R 0x08000000 /* EEPROM Read bit */ +#define CTL_EECHK 0x10000000 /* EEPROM Present bit */ +#define CTL_EERLD 0x20000000 /* EEPROM Reload Register */ +#define CTL_RESET 0x40000000 /* !! Adapter Reset !! */ +#define CTL_READY 0x80000000 /* Local Init Done */ + +#define PLX_ID_REG 0x70 // hard-coded plx vendor and device ids + +#define PLX_REVISION_REG 0x74 // silicon revision + +#define PLX_DMA0_MODE_REG 0x80 // dma channel 0 mode register +#define PLX_DMA1_MODE_REG 0x94 // dma channel 0 mode register +#define PLX_LOCAL_BUS_16_WIDE_BITS 0x1 +#define PLX_LOCAL_BUS_32_WIDE_BITS 0x3 +#define PLX_LOCAL_BUS_WIDTH_MASK 0x3 +#define PLX_DMA_EN_READYIN_BIT 0x40 // enable ready in input +#define PLX_EN_BTERM_BIT 0x80 // enable BTERM# input +#define PLX_DMA_LOCAL_BURST_EN_BIT 0x100 // enable local burst mode +#define PLX_EN_CHAIN_BIT 0x200 // enables chaining +#define PLX_EN_DMA_DONE_INTR_BIT 0x400 // enables interrupt on dma done +#define PLX_LOCAL_ADDR_CONST_BIT 0x800 // hold local address constant (don't increment) +#define PLX_DEMAND_MODE_BIT 0x1000 // enables demand-mode for dma transfer +#define PLX_EOT_ENABLE_BIT 0x4000 +#define PLX_STOP_MODE_BIT 0x8000 +#define PLX_DMA_INTR_PCI_BIT 0x20000 // routes dma interrupt to pci bus (instead of local bus) + +#define PLX_DMA0_PCI_ADDRESS_REG 0x84 // pci address that dma transfers start at +#define PLX_DMA1_PCI_ADDRESS_REG 0x98 + +#define PLX_DMA0_LOCAL_ADDRESS_REG 0x88 // local address that dma transfers start at +#define PLX_DMA1_LOCAL_ADDRESS_REG 0x9c + +#define PLX_DMA0_TRANSFER_SIZE_REG 0x8c // number of bytes to transfer (first 23 bits) +#define PLX_DMA1_TRANSFER_SIZE_REG 0xa0 + +#define PLX_DMA0_DESCRIPTOR_REG 0x90 // descriptor pointer register +#define PLX_DMA1_DESCRIPTOR_REG 0xa4 +#define PLX_DESC_IN_PCI_BIT 0x1 // descriptor is located in pci space (not local space) +#define PLX_END_OF_CHAIN_BIT 0x2 // end of chain bit +#define PLX_INTR_TERM_COUNT 0x4 // interrupt when this descriptor's transfer is finished +#define PLX_XFER_LOCAL_TO_PCI 0x8 // transfer from local to pci bus (not pci to local) + +#define PLX_DMA0_CS_REG 0xa8 // command status register +#define PLX_DMA1_CS_REG 0xa9 +#define PLX_DMA_EN_BIT 0x1 // enable dma channel +#define PLX_DMA_START_BIT 0x2 // start dma transfer +#define PLX_DMA_ABORT_BIT 0x4 // abort dma transfer +#define PLX_CLEAR_DMA_INTR_BIT 0x8 // clear dma interrupt +#define PLX_DMA_DONE_BIT 0x10 // transfer done status bit + +#define PLX_DMA0_THRESHOLD_REG 0xb0 // command status register + +/* + * Accesses near the end of memory can cause the PLX chip + * to pre-fetch data off of end-of-ram. Limit the size of + * memory so host-side accesses cannot occur. + */ + +#define PLX_PREFETCH 32 + +/* + * The PCI Interface, via the PCI-9060 Chip, has up to eight (8) Mailbox + * Registers. The PUTS (Power-Up Test Suite) handles the board-side + * interface/interaction using the first 4 registers. Specifications for + * the use of the full PUTS' command and status interface is contained + * within a separate SBE PUTS Manual. The Host-Side Device Driver only + * uses a subset of the full PUTS interface. + */ + +/*****************************************/ +/*** MAILBOX #(-1) - MEM ACCESS STS ***/ +/*****************************************/ + +#define MBX_STS_VALID 0x57584744 /* 'WXGD' */ +#define MBX_STS_DILAV 0x44475857 /* swapped = 'DGXW' */ + +/*****************************************/ +/*** MAILBOX #0 - PUTS STATUS ***/ +/*****************************************/ + +#define MBX_STS_MASK 0x000000ff /* PUTS Status Register bits */ +#define MBX_STS_TMASK 0x0000000f /* register bits for TEST number */ + +#define MBX_STS_PCIRESET 0x00000100 /* Host issued PCI reset request */ +#define MBX_STS_BUSY 0x00000080 /* PUTS is in progress */ +#define MBX_STS_ERROR 0x00000040 /* PUTS has failed */ +#define MBX_STS_RESERVED 0x000000c0 /* Undefined -> status in transition. + We are in process of changing + bits; we SET Error bit before + RESET of Busy bit */ + +#define MBX_RESERVED_5 0x00000020 /* FYI: reserved/unused bit */ +#define MBX_RESERVED_4 0x00000010 /* FYI: reserved/unused bit */ + +/******************************************/ +/*** MAILBOX #1 - PUTS COMMANDS ***/ +/******************************************/ + +/* + * Any attempt to execute an unimplement command results in the PUTS + * interface executing a NOOP and continuing as if the offending command + * completed normally. Note: this supplies a simple method to interrogate + * mailbox command processing functionality. + */ + +#define MBX_CMD_MASK 0xffff0000 /* PUTS Command Register bits */ + +#define MBX_CMD_ABORTJ 0x85000000 /* abort and jump */ +#define MBX_CMD_RESETP 0x86000000 /* reset and pause at start */ +#define MBX_CMD_PAUSE 0x87000000 /* pause immediately */ +#define MBX_CMD_PAUSEC 0x88000000 /* pause on completion */ +#define MBX_CMD_RESUME 0x89000000 /* resume operation */ +#define MBX_CMD_STEP 0x8a000000 /* single step tests */ + +#define MBX_CMD_BSWAP 0x8c000000 /* identify byte swap scheme */ +#define MBX_CMD_BSWAP_0 0x8c000000 /* use scheme 0 */ +#define MBX_CMD_BSWAP_1 0x8c000001 /* use scheme 1 */ + +#define MBX_CMD_SETHMS 0x8d000000 /* setup host memory access window + size */ +#define MBX_CMD_SETHBA 0x8e000000 /* setup host memory access base + address */ +#define MBX_CMD_MGO 0x8f000000 /* perform memory setup and continue + (IE. Done) */ +#define MBX_CMD_NOOP 0xFF000000 /* dummy, illegal command */ + +/*****************************************/ +/*** MAILBOX #2 - MEMORY SIZE ***/ +/*****************************************/ + +#define MBX_MEMSZ_MASK 0xffff0000 /* PUTS Memory Size Register bits */ + +#define MBX_MEMSZ_128KB 0x00020000 /* 128 kilobyte board */ +#define MBX_MEMSZ_256KB 0x00040000 /* 256 kilobyte board */ +#define MBX_MEMSZ_512KB 0x00080000 /* 512 kilobyte board */ +#define MBX_MEMSZ_1MB 0x00100000 /* 1 megabyte board */ +#define MBX_MEMSZ_2MB 0x00200000 /* 2 megabyte board */ +#define MBX_MEMSZ_4MB 0x00400000 /* 4 megabyte board */ +#define MBX_MEMSZ_8MB 0x00800000 /* 8 megabyte board */ +#define MBX_MEMSZ_16MB 0x01000000 /* 16 megabyte board */ + +/***************************************/ +/*** MAILBOX #2 - BOARD TYPE ***/ +/***************************************/ + +#define MBX_BTYPE_MASK 0x0000ffff /* PUTS Board Type Register */ +#define MBX_BTYPE_FAMILY_MASK 0x0000ff00 /* PUTS Board Family Register */ +#define MBX_BTYPE_SUBTYPE_MASK 0x000000ff /* PUTS Board Subtype */ + +#define MBX_BTYPE_PLX9060 0x00000100 /* PLX family type */ +#define MBX_BTYPE_PLX9080 0x00000300 /* PLX wanXL100s family type */ + +#define MBX_BTYPE_WANXL_4 0x00000104 /* wanXL400, 4-port */ +#define MBX_BTYPE_WANXL_2 0x00000102 /* wanXL200, 2-port */ +#define MBX_BTYPE_WANXL_1s 0x00000301 /* wanXL100s, 1-port */ +#define MBX_BTYPE_WANXL_1t 0x00000401 /* wanXL100T1, 1-port */ + +/*****************************************/ +/*** MAILBOX #3 - SHMQ MAILBOX ***/ +/*****************************************/ + +#define MBX_SMBX_MASK 0x000000ff /* PUTS SHMQ Mailbox bits */ + +/***************************************/ +/*** GENERIC HOST-SIDE DRIVER ***/ +/***************************************/ + +#define MBX_ERR 0 +#define MBX_OK 1 + +/* mailbox check routine - type of testing */ +#define MBXCHK_STS 0x00 /* check for PUTS status */ +#define MBXCHK_NOWAIT 0x01 /* dont care about PUTS status */ + +/* system allocates this many bytes for address mapping mailbox space */ +#define MBX_ADDR_SPACE_360 0x80 /* wanXL100s/200/400 */ +#define MBX_ADDR_MASK_360 (MBX_ADDR_SPACE_360-1) + +static inline int plx9080_abort_dma(void *iobase, unsigned int channel) +{ + void *dma_cs_addr; + uint8_t dma_status; + const int timeout = 10000; + unsigned int i; + + if (channel) + dma_cs_addr = iobase + PLX_DMA1_CS_REG; + else + dma_cs_addr = iobase + PLX_DMA0_CS_REG; + + // abort dma transfer if necessary + dma_status = readb(dma_cs_addr); + if ((dma_status & PLX_DMA_EN_BIT) == 0) { + return 0; + } + // wait to make sure done bit is zero + for (i = 0; (dma_status & PLX_DMA_DONE_BIT) && i < timeout; i++) { + comedi_udelay(1); + dma_status = readb(dma_cs_addr); + } + if (i == timeout) { + rt_printk + ("plx9080: cancel() timed out waiting for dma %i done clear\n", + channel); + return -ETIMEDOUT; + } + // disable and abort channel + writeb(PLX_DMA_ABORT_BIT, dma_cs_addr); + // wait for dma done bit + dma_status = readb(dma_cs_addr); + for (i = 0; (dma_status & PLX_DMA_DONE_BIT) == 0 && i < timeout; i++) { + comedi_udelay(1); + dma_status = readb(dma_cs_addr); + } + if (i == timeout) { + rt_printk + ("plx9080: cancel() timed out waiting for dma %i done set\n", + channel); + return -ETIMEDOUT; + } + + return 0; +} + +#endif /* __COMEDI_PLX9080_H */ --- /dev/null +++ b/drivers/staging/comedi/drivers/rtd520.c @@ -0,0 +1,2283 @@ +/* + comedi/drivers/rtd520.c + Comedi driver for Real Time Devices (RTD) PCI4520/DM7520 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 2001 David A. Schleef + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* +Driver: rtd520 +Description: Real Time Devices PCI4520/DM7520 +Author: Dan Christian +Devices: [Real Time Devices] DM7520HR-1 (rtd520), DM7520HR-8, + PCI4520, PCI4520-8 +Status: Works. Only tested on DM7520-8. Not SMP safe. + +Configuration options: + [0] - PCI bus of device (optional) + If bus/slot is not specified, the first available PCI + device will be used. + [1] - PCI slot of device (optional) +*/ +/* + Created by Dan Christian, NASA Ames Research Center. + + The PCI4520 is a PCI card. The DM7520 is a PC/104-plus card. + Both have: + 8/16 12 bit ADC with FIFO and channel gain table + 8 bits high speed digital out (for external MUX) (or 8 in or 8 out) + 8 bits high speed digital in with FIFO and interrupt on change (or 8 IO) + 2 12 bit DACs with FIFOs + 2 bits output + 2 bits input + bus mastering DMA + timers: ADC sample, pacer, burst, about, delay, DA1, DA2 + sample counter + 3 user timer/counters (8254) + external interrupt + + The DM7520 has slightly fewer features (fewer gain steps). + + These boards can support external multiplexors and multi-board + synchronization, but this driver doesn't support that. + + Board docs: http://www.rtdusa.com/PC104/DM/analog%20IO/dm7520.htm + Data sheet: http://www.rtdusa.com/pdf/dm7520.pdf + Example source: http://www.rtdusa.com/examples/dm/dm7520.zip + Call them and ask for the register level manual. + PCI chip: http://www.plxtech.com/products/toolbox/9080.htm + + Notes: + This board is memory mapped. There is some IO stuff, but it isn't needed. + + I use a pretty loose naming style within the driver (rtd_blah). + All externally visible names should be rtd520_blah. + I use camelCase for structures (and inside them). + I may also use upper CamelCase for function names (old habit). + + This board is somewhat related to the RTD PCI4400 board. + + I borrowed heavily from the ni_mio_common, ni_atmio16d, mite, and + das1800, since they have the best documented code. Driver + cb_pcidas64.c uses the same DMA controller. + + As far as I can tell, the About interrupt doesnt work if Sample is + also enabled. It turns out that About really isn't needed, since + we always count down samples read. + + There was some timer/counter code, but it didn't follow the right API. + +*/ + +/* + driver status: + + Analog-In supports instruction and command mode. + + With DMA, you can sample at 1.15Mhz with 70% idle on a 400Mhz K6-2 + (single channel, 64K read buffer). I get random system lockups when + using DMA with ALI-15xx based systems. I haven't been able to test + any other chipsets. The lockups happen soon after the start of an + acquistion, not in the middle of a long run. + + Without DMA, you can do 620Khz sampling with 20% idle on a 400Mhz K6-2 + (with a 256K read buffer). + + Digital-IO and Analog-Out only support instruction mode. + +*/ + +#include + +#include "../comedidev.h" +#include "comedi_pci.h" + +#define DRV_NAME "rtd520" + +/*====================================================================== + Driver specific stuff (tunable) +======================================================================*/ +/* Enable this to test the new DMA support. You may get hard lock ups */ +/*#define USE_DMA*/ + +/* We really only need 2 buffers. More than that means being much + smarter about knowing which ones are full. */ +#define DMA_CHAIN_COUNT 2 /* max DMA segments/buffers in a ring (min 2) */ + +/* Target period for periodic transfers. This sets the user read latency. */ +/* Note: There are certain rates where we give this up and transfer 1/2 FIFO */ +/* If this is too low, efficiency is poor */ +#define TRANS_TARGET_PERIOD 10000000 /* 10 ms (in nanoseconds) */ + +/* Set a practical limit on how long a list to support (affects memory use) */ +/* The board support a channel list up to the FIFO length (1K or 8K) */ +#define RTD_MAX_CHANLIST 128 /* max channel list that we allow */ + +/* tuning for ai/ao instruction done polling */ +#ifdef FAST_SPIN +#define WAIT_QUIETLY /* as nothing, spin on done bit */ +#define RTD_ADC_TIMEOUT 66000 /* 2 msec at 33mhz bus rate */ +#define RTD_DAC_TIMEOUT 66000 +#define RTD_DMA_TIMEOUT 33000 /* 1 msec */ +#else +/* by delaying, power and electrical noise are reduced somewhat */ +#define WAIT_QUIETLY comedi_udelay (1) +#define RTD_ADC_TIMEOUT 2000 /* in usec */ +#define RTD_DAC_TIMEOUT 2000 /* in usec */ +#define RTD_DMA_TIMEOUT 1000 /* in usec */ +#endif + +/*====================================================================== + Board specific stuff +======================================================================*/ + +/* registers */ +#define PCI_VENDOR_ID_RTD 0x1435 +/* + The board has three memory windows: las0, las1, and lcfg (the PCI chip) + Las1 has the data and can be burst DMAed 32bits at a time. +*/ +#define LCFG_PCIINDEX 0 +/* PCI region 1 is a 256 byte IO space mapping. Use??? */ +#define LAS0_PCIINDEX 2 /* PCI memory resources */ +#define LAS1_PCIINDEX 3 +#define LCFG_PCISIZE 0x100 +#define LAS0_PCISIZE 0x200 +#define LAS1_PCISIZE 0x10 + +#define RTD_CLOCK_RATE 8000000 /* 8Mhz onboard clock */ +#define RTD_CLOCK_BASE 125 /* clock period in ns */ + +/* Note: these speed are slower than the spec, but fit the counter resolution*/ +#define RTD_MAX_SPEED 1625 /* when sampling, in nanoseconds */ +/* max speed if we don't have to wait for settling */ +#define RTD_MAX_SPEED_1 875 /* if single channel, in nanoseconds */ + +#define RTD_MIN_SPEED 2097151875 /* (24bit counter) in nanoseconds */ +/* min speed when only 1 channel (no burst counter) */ +#define RTD_MIN_SPEED_1 5000000 /* 200Hz, in nanoseconds */ + +#include "rtd520.h" +#include "plx9080.h" + +/* Setup continuous ring of 1/2 FIFO transfers. See RTD manual p91 */ +#define DMA_MODE_BITS (\ + PLX_LOCAL_BUS_16_WIDE_BITS \ + | PLX_DMA_EN_READYIN_BIT \ + | PLX_DMA_LOCAL_BURST_EN_BIT \ + | PLX_EN_CHAIN_BIT \ + | PLX_DMA_INTR_PCI_BIT \ + | PLX_LOCAL_ADDR_CONST_BIT \ + | PLX_DEMAND_MODE_BIT) + +#define DMA_TRANSFER_BITS (\ +/* descriptors in PCI memory*/ PLX_DESC_IN_PCI_BIT \ +/* interrupt at end of block */ | PLX_INTR_TERM_COUNT \ +/* from board to PCI */ | PLX_XFER_LOCAL_TO_PCI) + +/*====================================================================== + Comedi specific stuff +======================================================================*/ + +/* + The board has 3 input modes and the gains of 1,2,4,...32 (, 64, 128) +*/ +static const comedi_lrange rtd_ai_7520_range = { 18, { + /* +-5V input range gain steps */ + BIP_RANGE(5.0), + BIP_RANGE(5.0 / 2), + BIP_RANGE(5.0 / 4), + BIP_RANGE(5.0 / 8), + BIP_RANGE(5.0 / 16), + BIP_RANGE(5.0 / 32), + /* +-10V input range gain steps */ + BIP_RANGE(10.0), + BIP_RANGE(10.0 / 2), + BIP_RANGE(10.0 / 4), + BIP_RANGE(10.0 / 8), + BIP_RANGE(10.0 / 16), + BIP_RANGE(10.0 / 32), + /* +10V input range gain steps */ + UNI_RANGE(10.0), + UNI_RANGE(10.0 / 2), + UNI_RANGE(10.0 / 4), + UNI_RANGE(10.0 / 8), + UNI_RANGE(10.0 / 16), + UNI_RANGE(10.0 / 32), + + } +}; + +/* PCI4520 has two more gains (6 more entries) */ +static const comedi_lrange rtd_ai_4520_range = { 24, { + /* +-5V input range gain steps */ + BIP_RANGE(5.0), + BIP_RANGE(5.0 / 2), + BIP_RANGE(5.0 / 4), + BIP_RANGE(5.0 / 8), + BIP_RANGE(5.0 / 16), + BIP_RANGE(5.0 / 32), + BIP_RANGE(5.0 / 64), + BIP_RANGE(5.0 / 128), + /* +-10V input range gain steps */ + BIP_RANGE(10.0), + BIP_RANGE(10.0 / 2), + BIP_RANGE(10.0 / 4), + BIP_RANGE(10.0 / 8), + BIP_RANGE(10.0 / 16), + BIP_RANGE(10.0 / 32), + BIP_RANGE(10.0 / 64), + BIP_RANGE(10.0 / 128), + /* +10V input range gain steps */ + UNI_RANGE(10.0), + UNI_RANGE(10.0 / 2), + UNI_RANGE(10.0 / 4), + UNI_RANGE(10.0 / 8), + UNI_RANGE(10.0 / 16), + UNI_RANGE(10.0 / 32), + UNI_RANGE(10.0 / 64), + UNI_RANGE(10.0 / 128), + } +}; + +/* Table order matches range values */ +static const comedi_lrange rtd_ao_range = { 4, { + RANGE(0, 5), + RANGE(0, 10), + RANGE(-5, 5), + RANGE(-10, 10), + } +}; + +/* + Board descriptions + */ +typedef struct rtdBoard_struct { + const char *name; /* must be first */ + int device_id; + int aiChans; + int aiBits; + int aiMaxGain; + int range10Start; /* start of +-10V range */ + int rangeUniStart; /* start of +10V range */ +} rtdBoard; + +static const rtdBoard rtd520Boards[] = { + { + name: "DM7520", + device_id:0x7520, + aiChans: 16, + aiBits: 12, + aiMaxGain:32, + range10Start:6, + rangeUniStart:12, + }, + { + name: "PCI4520", + device_id:0x4520, + aiChans: 16, + aiBits: 12, + aiMaxGain:128, + range10Start:8, + rangeUniStart:16, + }, +}; + +static DEFINE_PCI_DEVICE_TABLE(rtd520_pci_table) = { + {PCI_VENDOR_ID_RTD, 0x7520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_RTD, 0x4520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0} +}; + +MODULE_DEVICE_TABLE(pci, rtd520_pci_table); + +/* + * Useful for shorthand access to the particular board structure + */ +#define thisboard ((const rtdBoard *)dev->board_ptr) + +/* + This structure is for data unique to this hardware driver. + This is also unique for each board in the system. +*/ +typedef struct { + /* memory mapped board structures */ + void *las0; + void *las1; + void *lcfg; + + unsigned long intCount; /* interrupt count */ + long aiCount; /* total transfer size (samples) */ + int transCount; /* # to tranfer data. 0->1/2FIFO */ + int flags; /* flag event modes */ + + /* PCI device info */ + struct pci_dev *pci_dev; + int got_regions; /* non-zero if PCI regions owned */ + + /* channel list info */ + /* chanBipolar tracks whether a channel is bipolar (and needs +2048) */ + unsigned char chanBipolar[RTD_MAX_CHANLIST / 8]; /* bit array */ + + /* read back data */ + lsampl_t aoValue[2]; /* Used for AO read back */ + + /* timer gate (when enabled) */ + u8 utcGate[4]; /* 1 extra allows simple range check */ + + /* shadow registers affect other registers, but cant be read back */ + /* The macros below update these on writes */ + u16 intMask; /* interrupt mask */ + u16 intClearMask; /* interrupt clear mask */ + u8 utcCtrl[4]; /* crtl mode for 3 utc + read back */ + u8 dioStatus; /* could be read back (dio0Ctrl) */ +#ifdef USE_DMA + /* Always DMA 1/2 FIFO. Buffer (dmaBuff?) is (at least) twice that size. + After transferring, interrupt processes 1/2 FIFO and passes to comedi */ + s16 dma0Offset; /* current processing offset (0, 1/2) */ + uint16_t *dma0Buff[DMA_CHAIN_COUNT]; /* DMA buffers (for ADC) */ + dma_addr_t dma0BuffPhysAddr[DMA_CHAIN_COUNT]; /* physical addresses */ + struct plx_dma_desc *dma0Chain; /* DMA descriptor ring for dmaBuff */ + dma_addr_t dma0ChainPhysAddr; /* physical addresses */ + /* shadow registers */ + u8 dma0Control; + u8 dma1Control; +#endif /* USE_DMA */ + unsigned fifoLen; +} rtdPrivate; + +/* bit defines for "flags" */ +#define SEND_EOS 0x01 /* send End Of Scan events */ +#define DMA0_ACTIVE 0x02 /* DMA0 is active */ +#define DMA1_ACTIVE 0x04 /* DMA1 is active */ + +/* Macros for accessing channel list bit array */ +#define CHAN_ARRAY_TEST(array,index) \ + (((array)[(index)/8] >> ((index) & 0x7)) & 0x1) +#define CHAN_ARRAY_SET(array,index) \ + (((array)[(index)/8] |= 1 << ((index) & 0x7))) +#define CHAN_ARRAY_CLEAR(array,index) \ + (((array)[(index)/8] &= ~(1 << ((index) & 0x7)))) + +/* + * most drivers define the following macro to make it easy to + * access the private structure. + */ +#define devpriv ((rtdPrivate *)dev->private) + +/* Macros to access registers */ + +/* Reset board */ +#define RtdResetBoard(dev) \ + writel (0, devpriv->las0+LAS0_BOARD_RESET) + +/* Reset channel gain table read pointer */ +#define RtdResetCGT(dev) \ + writel (0, devpriv->las0+LAS0_CGT_RESET) + +/* Reset channel gain table read and write pointers */ +#define RtdClearCGT(dev) \ + writel (0, devpriv->las0+LAS0_CGT_CLEAR) + +/* Reset channel gain table read and write pointers */ +#define RtdEnableCGT(dev,v) \ + writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_CGT_ENABLE) + +/* Write channel gain table entry */ +#define RtdWriteCGTable(dev,v) \ + writel (v, devpriv->las0+LAS0_CGT_WRITE) + +/* Write Channel Gain Latch */ +#define RtdWriteCGLatch(dev,v) \ + writel (v, devpriv->las0+LAS0_CGL_WRITE) + +/* Reset ADC FIFO */ +#define RtdAdcClearFifo(dev) \ + writel (0, devpriv->las0+LAS0_ADC_FIFO_CLEAR) + +/* Set ADC start conversion source select (write only) */ +#define RtdAdcConversionSource(dev,v) \ + writel (v, devpriv->las0+LAS0_ADC_CONVERSION) + +/* Set burst start source select (write only) */ +#define RtdBurstStartSource(dev,v) \ + writel (v, devpriv->las0+LAS0_BURST_START) + +/* Set Pacer start source select (write only) */ +#define RtdPacerStartSource(dev,v) \ + writel (v, devpriv->las0+LAS0_PACER_START) + +/* Set Pacer stop source select (write only) */ +#define RtdPacerStopSource(dev,v) \ + writel (v, devpriv->las0+LAS0_PACER_STOP) + +/* Set Pacer clock source select (write only) 0=external 1=internal */ +#define RtdPacerClockSource(dev,v) \ + writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_PACER_SELECT) + +/* Set sample counter source select (write only) */ +#define RtdAdcSampleCounterSource(dev,v) \ + writel (v, devpriv->las0+LAS0_ADC_SCNT_SRC) + +/* Set Pacer trigger mode select (write only) 0=single cycle, 1=repeat */ +#define RtdPacerTriggerMode(dev,v) \ + writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_PACER_REPEAT) + +/* Set About counter stop enable (write only) */ +#define RtdAboutStopEnable(dev,v) \ + writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_ACNT_STOP_ENABLE) + +/* Set external trigger polarity (write only) 0=positive edge, 1=negative */ +#define RtdTriggerPolarity(dev,v) \ + writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_ETRG_POLARITY) + +/* Start single ADC conversion */ +#define RtdAdcStart(dev) \ + writew (0, devpriv->las0+LAS0_ADC) + +/* Read one ADC data value (12bit (with sign extend) as 16bit) */ +/* Note: matches what DMA would get. Actual value >> 3 */ +#define RtdAdcFifoGet(dev) \ + readw (devpriv->las1+LAS1_ADC_FIFO) + +/* Read two ADC data values (DOESNT WORK) */ +#define RtdAdcFifoGet2(dev) \ + readl (devpriv->las1+LAS1_ADC_FIFO) + +/* FIFO status */ +#define RtdFifoStatus(dev) \ + readl (devpriv->las0+LAS0_ADC) + +/* pacer start/stop read=start, write=stop*/ +#define RtdPacerStart(dev) \ + readl (devpriv->las0+LAS0_PACER) +#define RtdPacerStop(dev) \ + writel (0, devpriv->las0+LAS0_PACER) + +/* Interrupt status */ +#define RtdInterruptStatus(dev) \ + readw (devpriv->las0+LAS0_IT) + +/* Interrupt mask */ +#define RtdInterruptMask(dev,v) \ + writew ((devpriv->intMask = (v)),devpriv->las0+LAS0_IT) + +/* Interrupt status clear (only bits set in mask) */ +#define RtdInterruptClear(dev) \ + readw (devpriv->las0+LAS0_CLEAR) + +/* Interrupt clear mask */ +#define RtdInterruptClearMask(dev,v) \ + writew ((devpriv->intClearMask = (v)), devpriv->las0+LAS0_CLEAR) + +/* Interrupt overrun status */ +#define RtdInterruptOverrunStatus(dev) \ + readl (devpriv->las0+LAS0_OVERRUN) + +/* Interrupt overrun clear */ +#define RtdInterruptOverrunClear(dev) \ + writel (0, devpriv->las0+LAS0_OVERRUN) + +/* Pacer counter, 24bit */ +#define RtdPacerCount(dev) \ + readl (devpriv->las0+LAS0_PCLK) +#define RtdPacerCounter(dev,v) \ + writel ((v) & 0xffffff,devpriv->las0+LAS0_PCLK) + +/* Burst counter, 10bit */ +#define RtdBurstCount(dev) \ + readl (devpriv->las0+LAS0_BCLK) +#define RtdBurstCounter(dev,v) \ + writel ((v) & 0x3ff,devpriv->las0+LAS0_BCLK) + +/* Delay counter, 16bit */ +#define RtdDelayCount(dev) \ + readl (devpriv->las0+LAS0_DCLK) +#define RtdDelayCounter(dev,v) \ + writel ((v) & 0xffff, devpriv->las0+LAS0_DCLK) + +/* About counter, 16bit */ +#define RtdAboutCount(dev) \ + readl (devpriv->las0+LAS0_ACNT) +#define RtdAboutCounter(dev,v) \ + writel ((v) & 0xffff, devpriv->las0+LAS0_ACNT) + +/* ADC sample counter, 10bit */ +#define RtdAdcSampleCount(dev) \ + readl (devpriv->las0+LAS0_ADC_SCNT) +#define RtdAdcSampleCounter(dev,v) \ + writel ((v) & 0x3ff, devpriv->las0+LAS0_ADC_SCNT) + +/* User Timer/Counter (8254) */ +#define RtdUtcCounterGet(dev,n) \ + readb (devpriv->las0 \ + + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2))) + +#define RtdUtcCounterPut(dev,n,v) \ + writeb ((v) & 0xff, devpriv->las0 \ + + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2))) + +/* Set UTC (8254) control byte */ +#define RtdUtcCtrlPut(dev,n,v) \ + writeb (devpriv->utcCtrl[(n) & 3] = (((n) & 3) << 6) | ((v) & 0x3f), \ + devpriv->las0 + LAS0_UTC_CTRL) + +/* Set UTCn clock source (write only) */ +#define RtdUtcClockSource(dev,n,v) \ + writew (v, devpriv->las0 \ + + ((n <= 0) ? LAS0_UTC0_CLOCK : \ + ((1 == n) ? LAS0_UTC1_CLOCK : LAS0_UTC2_CLOCK))) + +/* Set UTCn gate source (write only) */ +#define RtdUtcGateSource(dev,n,v) \ + writew (v, devpriv->las0 \ + + ((n <= 0) ? LAS0_UTC0_GATE : \ + ((1 == n) ? LAS0_UTC1_GATE : LAS0_UTC2_GATE))) + +/* User output N source select (write only) */ +#define RtdUsrOutSource(dev,n,v) \ + writel (v,devpriv->las0+((n <= 0) ? LAS0_UOUT0_SELECT : LAS0_UOUT1_SELECT)) + +/* Digital IO */ +#define RtdDio0Read(dev) \ + (readw (devpriv->las0+LAS0_DIO0) & 0xff) +#define RtdDio0Write(dev,v) \ + writew ((v) & 0xff, devpriv->las0+LAS0_DIO0) + +#define RtdDio1Read(dev) \ + (readw (devpriv->las0+LAS0_DIO1) & 0xff) +#define RtdDio1Write(dev,v) \ + writew ((v) & 0xff, devpriv->las0+LAS0_DIO1) + +#define RtdDioStatusRead(dev) \ + (readw (devpriv->las0+LAS0_DIO_STATUS) & 0xff) +#define RtdDioStatusWrite(dev,v) \ + writew ((devpriv->dioStatus = (v)), devpriv->las0+LAS0_DIO_STATUS) + +#define RtdDio0CtrlRead(dev) \ + (readw (devpriv->las0+LAS0_DIO0_CTRL) & 0xff) +#define RtdDio0CtrlWrite(dev,v) \ + writew ((v) & 0xff, devpriv->las0+LAS0_DIO0_CTRL) + +/* Digital to Analog converter */ +/* Write one data value (sign + 12bit + marker bits) */ +/* Note: matches what DMA would put. Actual value << 3 */ +#define RtdDacFifoPut(dev,n,v) \ + writew ((v), devpriv->las1 +(((n) == 0) ? LAS1_DAC1_FIFO : LAS1_DAC2_FIFO)) + +/* Start single DAC conversion */ +#define RtdDacUpdate(dev,n) \ + writew (0, devpriv->las0 +(((n) == 0) ? LAS0_DAC1 : LAS0_DAC2)) + +/* Start single DAC conversion on both DACs */ +#define RtdDacBothUpdate(dev) \ + writew (0, devpriv->las0+LAS0_DAC) + +/* Set DAC output type and range */ +#define RtdDacRange(dev,n,v) \ + writew ((v) & 7, devpriv->las0 \ + +(((n) == 0) ? LAS0_DAC1_CTRL : LAS0_DAC2_CTRL)) + +/* Reset DAC FIFO */ +#define RtdDacClearFifo(dev,n) \ + writel (0, devpriv->las0+(((n) == 0) ? LAS0_DAC1_RESET : LAS0_DAC2_RESET)) + +/* Set source for DMA 0 (write only, shadow?) */ +#define RtdDma0Source(dev,n) \ + writel ((n) & 0xf, devpriv->las0+LAS0_DMA0_SRC) + +/* Set source for DMA 1 (write only, shadow?) */ +#define RtdDma1Source(dev,n) \ + writel ((n) & 0xf, devpriv->las0+LAS0_DMA1_SRC) + +/* Reset board state for DMA 0 */ +#define RtdDma0Reset(dev) \ + writel (0, devpriv->las0+LAS0_DMA0_RESET) + +/* Reset board state for DMA 1 */ +#define RtdDma1Reset(dev) \ + writel (0, devpriv->las0+LAS0_DMA1_SRC) + +/* PLX9080 interrupt mask and status */ +#define RtdPlxInterruptRead(dev) \ + readl (devpriv->lcfg+LCFG_ITCSR) +#define RtdPlxInterruptWrite(dev,v) \ + writel (v, devpriv->lcfg+LCFG_ITCSR) + +/* Set mode for DMA 0 */ +#define RtdDma0Mode(dev,m) \ + writel ((m), devpriv->lcfg+LCFG_DMAMODE0) + +/* Set PCI address for DMA 0 */ +#define RtdDma0PciAddr(dev,a) \ + writel ((a), devpriv->lcfg+LCFG_DMAPADR0) + +/* Set local address for DMA 0 */ +#define RtdDma0LocalAddr(dev,a) \ + writel ((a), devpriv->lcfg+LCFG_DMALADR0) + +/* Set byte count for DMA 0 */ +#define RtdDma0Count(dev,c) \ + writel ((c), devpriv->lcfg+LCFG_DMASIZ0) + +/* Set next descriptor for DMA 0 */ +#define RtdDma0Next(dev,a) \ + writel ((a), devpriv->lcfg+LCFG_DMADPR0) + +/* Set mode for DMA 1 */ +#define RtdDma1Mode(dev,m) \ + writel ((m), devpriv->lcfg+LCFG_DMAMODE1) + +/* Set PCI address for DMA 1 */ +#define RtdDma1PciAddr(dev,a) \ + writel ((a), devpriv->lcfg+LCFG_DMAADR1) + +/* Set local address for DMA 1 */ +#define RtdDma1LocalAddr(dev,a) \ + writel ((a), devpriv->lcfg+LCFG_DMALADR1) + +/* Set byte count for DMA 1 */ +#define RtdDma1Count(dev,c) \ + writel ((c), devpriv->lcfg+LCFG_DMASIZ1) + +/* Set next descriptor for DMA 1 */ +#define RtdDma1Next(dev,a) \ + writel ((a), devpriv->lcfg+LCFG_DMADPR1) + +/* Set control for DMA 0 (write only, shadow?) */ +#define RtdDma0Control(dev,n) \ + writeb (devpriv->dma0Control = (n), devpriv->lcfg+LCFG_DMACSR0) + +/* Get status for DMA 0 */ +#define RtdDma0Status(dev) \ + readb (devpriv->lcfg+LCFG_DMACSR0) + +/* Set control for DMA 1 (write only, shadow?) */ +#define RtdDma1Control(dev,n) \ + writeb (devpriv->dma1Control = (n), devpriv->lcfg+LCFG_DMACSR1) + +/* Get status for DMA 1 */ +#define RtdDma1Status(dev) \ + readb (devpriv->lcfg+LCFG_DMACSR1) + +/* + * The comedi_driver structure tells the Comedi core module + * which functions to call to configure/deconfigure (attac/detach) + * the board, and also about the kernel module that contains + * the device code. + */ +static int rtd_attach(comedi_device * dev, comedi_devconfig * it); +static int rtd_detach(comedi_device * dev); + +static comedi_driver rtd520Driver = { + driver_name: DRV_NAME, + module:THIS_MODULE, + attach:rtd_attach, + detach:rtd_detach, +}; + +static int rtd_ai_rinsn(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data); +static int rtd_ao_winsn(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data); +static int rtd_ao_rinsn(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data); +static int rtd_dio_insn_bits(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data); +static int rtd_dio_insn_config(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data); +static int rtd_ai_cmdtest(comedi_device * dev, comedi_subdevice * s, + comedi_cmd * cmd); +static int rtd_ai_cmd(comedi_device * dev, comedi_subdevice * s); +static int rtd_ai_cancel(comedi_device * dev, comedi_subdevice * s); +//static int rtd_ai_poll (comedi_device *dev,comedi_subdevice *s); +static int rtd_ns_to_timer(unsigned int *ns, int roundMode); +static irqreturn_t rtd_interrupt(int irq, void *d PT_REGS_ARG); +static int rtd520_probe_fifo_depth(comedi_device *dev); + +/* + * Attach is called by the Comedi core to configure the driver + * for a particular board. If you specified a board_name array + * in the driver structure, dev->board_ptr contains that + * address. + */ +static int rtd_attach(comedi_device * dev, comedi_devconfig * it) +{ /* board name and options flags */ + comedi_subdevice *s; + struct pci_dev *pcidev; + int ret; + resource_size_t physLas0; /* configuation */ + resource_size_t physLas1; /* data area */ + resource_size_t physLcfg; /* PLX9080 */ +#ifdef USE_DMA + int index; +#endif + + printk("comedi%d: rtd520 attaching.\n", dev->minor); + +#if defined (CONFIG_COMEDI_DEBUG) && defined (USE_DMA) + /* You can set this a load time: modprobe comedi comedi_debug=1 */ + if (0 == comedi_debug) /* force DMA debug printks */ + comedi_debug = 1; +#endif + + /* + * Allocate the private structure area. alloc_private() is a + * convenient macro defined in comedidev.h. + */ + if (alloc_private(dev, sizeof(rtdPrivate)) < 0) + return -ENOMEM; + + /* + * Probe the device to determine what device in the series it is. + */ + for (pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, NULL); + pcidev != NULL; + pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, pcidev)) { + int i; + + if (it->options[0] || it->options[1]) { + if (pcidev->bus->number != it->options[0] + || PCI_SLOT(pcidev->devfn) != + it->options[1]) { + continue; + } + } + for(i = 0; i < sizeof(rtd520Boards) / sizeof(rtd520Boards[0]); ++i) + { + if(pcidev->device == rtd520Boards[i].device_id) + { + dev->board_ptr = &rtd520Boards[i]; + break; + } + } + if(dev->board_ptr) break; /* found one */ + } + if (!pcidev) { + if (it->options[0] && it->options[1]) { + printk("No RTD card at bus=%d slot=%d.\n", + it->options[0], it->options[1]); + } else { + printk("No RTD card found.\n"); + } + return -EIO; + } + devpriv->pci_dev = pcidev; + dev->board_name = thisboard->name; + + if ((ret = comedi_pci_enable(pcidev, DRV_NAME)) < 0) { + printk("Failed to enable PCI device and request regions.\n"); + return ret; + } + devpriv->got_regions = 1; + + /* + * Initialize base addresses + */ + /* Get the physical address from PCI config */ + physLas0 = pci_resource_start(devpriv->pci_dev, LAS0_PCIINDEX); + physLas1 = pci_resource_start(devpriv->pci_dev, LAS1_PCIINDEX); + physLcfg = pci_resource_start(devpriv->pci_dev, LCFG_PCIINDEX); + /* Now have the kernel map this into memory */ + /* ASSUME page aligned */ + devpriv->las0 = ioremap_nocache(physLas0, LAS0_PCISIZE); + devpriv->las1 = ioremap_nocache(physLas1, LAS1_PCISIZE); + devpriv->lcfg = ioremap_nocache(physLcfg, LCFG_PCISIZE); + + if (!devpriv->las0 || !devpriv->las1 || !devpriv->lcfg) { + return -ENOMEM; + } + + DPRINTK("%s: LAS0=%llx, LAS1=%llx, CFG=%llx.\n", dev->board_name, + (unsigned long long)physLas0, (unsigned long long)physLas1, + (unsigned long long)physLcfg); + { /* The RTD driver does this */ + unsigned char pci_latency; + u16 revision; + /*uint32_t epld_version; */ + + pci_read_config_word(devpriv->pci_dev, PCI_REVISION_ID, + &revision); + DPRINTK("%s: PCI revision %d.\n", dev->board_name, revision); + + pci_read_config_byte(devpriv->pci_dev, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 32) { + printk("%s: PCI latency changed from %d to %d\n", + dev->board_name, pci_latency, 32); + pci_write_config_byte(devpriv->pci_dev, + PCI_LATENCY_TIMER, 32); + } else { + DPRINTK("rtd520: PCI latency = %d\n", pci_latency); + } + + /* Undocumented EPLD version (doesnt match RTD driver results) */ + /*DPRINTK ("rtd520: Reading epld from %p\n", + devpriv->las0+0); + epld_version = readl (devpriv->las0+0); + if ((epld_version & 0xF0) >> 4 == 0x0F) { + DPRINTK("rtd520: pre-v8 EPLD. (%x)\n", epld_version); + } else { + DPRINTK("rtd520: EPLD version %x.\n", epld_version >> 4); + } */ + } + + /* Show board configuration */ + printk("%s:", dev->board_name); + + /* + * Allocate the subdevice structures. alloc_subdevice() is a + * convenient macro defined in comedidev.h. + */ + if (alloc_subdevices(dev, 4) < 0) { + return -ENOMEM; + } + + s = dev->subdevices + 0; + dev->read_subdev = s; + /* analog input subdevice */ + s->type = COMEDI_SUBD_AI; + s->subdev_flags = + SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF | + SDF_CMD_READ; + s->n_chan = thisboard->aiChans; + s->maxdata = (1 << thisboard->aiBits) - 1; + if (thisboard->aiMaxGain <= 32) { + s->range_table = &rtd_ai_7520_range; + } else { + s->range_table = &rtd_ai_4520_range; + } + s->len_chanlist = RTD_MAX_CHANLIST; /* devpriv->fifoLen */ + s->insn_read = rtd_ai_rinsn; + s->do_cmd = rtd_ai_cmd; + s->do_cmdtest = rtd_ai_cmdtest; + s->cancel = rtd_ai_cancel; + /*s->poll = rtd_ai_poll; *//* not ready yet */ + + s = dev->subdevices + 1; + /* analog output subdevice */ + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITABLE; + s->n_chan = 2; + s->maxdata = (1 << thisboard->aiBits) - 1; + s->range_table = &rtd_ao_range; + s->insn_write = rtd_ao_winsn; + s->insn_read = rtd_ao_rinsn; + + s = dev->subdevices + 2; + /* digital i/o subdevice */ + s->type = COMEDI_SUBD_DIO; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE; + /* we only support port 0 right now. Ignoring port 1 and user IO */ + s->n_chan = 8; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_bits = rtd_dio_insn_bits; + s->insn_config = rtd_dio_insn_config; + + /* timer/counter subdevices (not currently supported) */ + s = dev->subdevices + 3; + s->type = COMEDI_SUBD_COUNTER; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE; + s->n_chan = 3; + s->maxdata = 0xffff; + + /* initialize board, per RTD spec */ + /* also, initialize shadow registers */ + RtdResetBoard(dev); + comedi_udelay(100); /* needed? */ + RtdPlxInterruptWrite(dev, 0); + RtdInterruptMask(dev, 0); /* and sets shadow */ + RtdInterruptClearMask(dev, ~0); /* and sets shadow */ + RtdInterruptClear(dev); /* clears bits set by mask */ + RtdInterruptOverrunClear(dev); + RtdClearCGT(dev); + RtdAdcClearFifo(dev); + RtdDacClearFifo(dev, 0); + RtdDacClearFifo(dev, 1); + /* clear digital IO fifo */ + RtdDioStatusWrite(dev, 0); /* safe state, set shadow */ + RtdUtcCtrlPut(dev, 0, 0x30); /* safe state, set shadow */ + RtdUtcCtrlPut(dev, 1, 0x30); /* safe state, set shadow */ + RtdUtcCtrlPut(dev, 2, 0x30); /* safe state, set shadow */ + RtdUtcCtrlPut(dev, 3, 0); /* safe state, set shadow */ + /* TODO: set user out source ??? */ + + /* check if our interrupt is available and get it */ + if ((ret = comedi_request_irq(devpriv->pci_dev->irq, rtd_interrupt, + IRQF_SHARED, DRV_NAME, dev)) < 0) { + printk("Could not get interrupt! (%u)\n", + devpriv->pci_dev->irq); + return ret; + } + dev->irq = devpriv->pci_dev->irq; + printk("( irq=%u )", dev->irq); + + ret = rtd520_probe_fifo_depth(dev); + if(ret < 0) { + return ret; + } + devpriv->fifoLen = ret; + printk("( fifoLen=%d )", devpriv->fifoLen); + +#ifdef USE_DMA + if (dev->irq > 0) { + printk("( DMA buff=%d )\n", DMA_CHAIN_COUNT); + /* The PLX9080 has 2 DMA controllers, but there could be 4 sources: + ADC, digital, DAC1, and DAC2. Since only the ADC supports cmd mode + right now, this isn't an issue (yet) */ + devpriv->dma0Offset = 0; + + for (index = 0; index < DMA_CHAIN_COUNT; index++) { + devpriv->dma0Buff[index] = + pci_alloc_consistent(devpriv->pci_dev, + sizeof(u16) * devpriv->fifoLen / 2, + &devpriv->dma0BuffPhysAddr[index]); + if (devpriv->dma0Buff[index] == NULL) { + ret = -ENOMEM; + goto rtd_attach_die_error; + } + /*DPRINTK ("buff[%d] @ %p virtual, %x PCI\n", + index, + devpriv->dma0Buff[index], devpriv->dma0BuffPhysAddr[index]); */ + } + + /* setup DMA descriptor ring (use cpu_to_le32 for byte ordering?) */ + devpriv->dma0Chain = + pci_alloc_consistent(devpriv->pci_dev, + sizeof(struct plx_dma_desc) * DMA_CHAIN_COUNT, + &devpriv->dma0ChainPhysAddr); + for (index = 0; index < DMA_CHAIN_COUNT; index++) { + devpriv->dma0Chain[index].pci_start_addr = + devpriv->dma0BuffPhysAddr[index]; + devpriv->dma0Chain[index].local_start_addr = + DMALADDR_ADC; + devpriv->dma0Chain[index].transfer_size = + sizeof(u16) * devpriv->fifoLen / 2; + devpriv->dma0Chain[index].next = + (devpriv->dma0ChainPhysAddr + ((index + + 1) % (DMA_CHAIN_COUNT)) + * sizeof(devpriv->dma0Chain[0])) + | DMA_TRANSFER_BITS; + /*DPRINTK ("ring[%d] @%lx PCI: %x, local: %x, N: 0x%x, next: %x\n", + index, + ((long)devpriv->dma0ChainPhysAddr + + (index * sizeof(devpriv->dma0Chain[0]))), + devpriv->dma0Chain[index].pci_start_addr, + devpriv->dma0Chain[index].local_start_addr, + devpriv->dma0Chain[index].transfer_size, + devpriv->dma0Chain[index].next); */ + } + + if (devpriv->dma0Chain == NULL) { + ret = -ENOMEM; + goto rtd_attach_die_error; + } + + RtdDma0Mode(dev, DMA_MODE_BITS); + RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL); /* set DMA trigger source */ + } else { + printk("( no IRQ->no DMA )"); + } +#endif /* USE_DMA */ + + if (dev->irq) { /* enable plx9080 interrupts */ + RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE); + } + + printk("\ncomedi%d: rtd520 driver attached.\n", dev->minor); + + return 1; + +#if 0 + /* hit an error, clean up memory and return ret */ +//rtd_attach_die_error: +#ifdef USE_DMA + for (index = 0; index < DMA_CHAIN_COUNT; index++) { + if (NULL != devpriv->dma0Buff[index]) { /* free buffer memory */ + pci_free_consistent(devpriv->pci_dev, + sizeof(u16) * devpriv->fifoLen / 2, + devpriv->dma0Buff[index], + devpriv->dma0BuffPhysAddr[index]); + devpriv->dma0Buff[index] = NULL; + } + } + if (NULL != devpriv->dma0Chain) { + pci_free_consistent(devpriv->pci_dev, + sizeof(struct plx_dma_desc) + * DMA_CHAIN_COUNT, + devpriv->dma0Chain, devpriv->dma0ChainPhysAddr); + devpriv->dma0Chain = NULL; + } +#endif /* USE_DMA */ + /* subdevices and priv are freed by the core */ + if (dev->irq) { + /* disable interrupt controller */ + RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev) + & ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E)); + comedi_free_irq(dev->irq, dev); + } + + /* release all regions that were allocated */ + if (devpriv->las0) { + iounmap(devpriv->las0); + } + if (devpriv->las1) { + iounmap(devpriv->las1); + } + if (devpriv->lcfg) { + iounmap(devpriv->lcfg); + } + if (devpriv->pci_dev) { + pci_dev_put(devpriv->pci_dev); + } + return ret; +#endif +} + +/* + * _detach is called to deconfigure a device. It should deallocate + * resources. + * This function is also called when _attach() fails, so it should be + * careful not to release resources that were not necessarily + * allocated by _attach(). dev->private and dev->subdevices are + * deallocated automatically by the core. + */ +static int rtd_detach(comedi_device * dev) +{ +#ifdef USE_DMA + int index; +#endif + + DPRINTK("comedi%d: rtd520: removing (%ld ints)\n", + dev->minor, (devpriv ? devpriv->intCount : 0L)); + if (devpriv && devpriv->lcfg) { + DPRINTK("(int status 0x%x, overrun status 0x%x, fifo status 0x%x)...\n", 0xffff & RtdInterruptStatus(dev), 0xffff & RtdInterruptOverrunStatus(dev), (0xffff & RtdFifoStatus(dev)) ^ 0x6666); + } + + if (devpriv) { + /* Shut down any board ops by resetting it */ +#ifdef USE_DMA + if (devpriv->lcfg) { + RtdDma0Control(dev, 0); /* disable DMA */ + RtdDma1Control(dev, 0); /* disable DMA */ + RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE); + } +#endif /* USE_DMA */ + if (devpriv->las0) { + RtdResetBoard(dev); + RtdInterruptMask(dev, 0); + RtdInterruptClearMask(dev, ~0); + RtdInterruptClear(dev); /* clears bits set by mask */ + } +#ifdef USE_DMA + /* release DMA */ + for (index = 0; index < DMA_CHAIN_COUNT; index++) { + if (NULL != devpriv->dma0Buff[index]) { + pci_free_consistent(devpriv->pci_dev, + sizeof(u16) * devpriv->fifoLen / 2, + devpriv->dma0Buff[index], + devpriv->dma0BuffPhysAddr[index]); + devpriv->dma0Buff[index] = NULL; + } + } + if (NULL != devpriv->dma0Chain) { + pci_free_consistent(devpriv->pci_dev, + sizeof(struct plx_dma_desc) * DMA_CHAIN_COUNT, + devpriv->dma0Chain, devpriv->dma0ChainPhysAddr); + devpriv->dma0Chain = NULL; + } +#endif /* USE_DMA */ + + /* release IRQ */ + if (dev->irq) { + /* disable interrupt controller */ + RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev) + & ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E)); + comedi_free_irq(dev->irq, dev); + } + + /* release all regions that were allocated */ + if (devpriv->las0) { + iounmap(devpriv->las0); + } + if (devpriv->las1) { + iounmap(devpriv->las1); + } + if (devpriv->lcfg) { + iounmap(devpriv->lcfg); + } + if (devpriv->pci_dev) { + if (devpriv->got_regions) { + comedi_pci_disable(devpriv->pci_dev); + } + pci_dev_put(devpriv->pci_dev); + } + } + + printk("comedi%d: rtd520: removed.\n", dev->minor); + + return 0; +} + +/* + Convert a single comedi channel-gain entry to a RTD520 table entry +*/ +static unsigned short rtdConvertChanGain(comedi_device * dev, + unsigned int comediChan, int chanIndex) +{ /* index in channel list */ + unsigned int chan, range, aref; + unsigned short r = 0; + + chan = CR_CHAN(comediChan); + range = CR_RANGE(comediChan); + aref = CR_AREF(comediChan); + + r |= chan & 0xf; + + /* Note: we also setup the channel list bipolar flag array */ + if (range < thisboard->range10Start) { /* first batch are +-5 */ + r |= 0x000; /* +-5 range */ + r |= (range & 0x7) << 4; /* gain */ + CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex); + } else if (range < thisboard->rangeUniStart) { /* second batch are +-10 */ + r |= 0x100; /* +-10 range */ + r |= ((range - thisboard->range10Start) & 0x7) << 4; /* gain */ + CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex); + } else { /* last batch is +10 */ + r |= 0x200; /* +10 range */ + r |= ((range - thisboard->rangeUniStart) & 0x7) << 4; /* gain */ + CHAN_ARRAY_CLEAR(devpriv->chanBipolar, chanIndex); + } + + switch (aref) { + case AREF_GROUND: /* on-board ground */ + break; + + case AREF_COMMON: + r |= 0x80; /* ref external analog common */ + break; + + case AREF_DIFF: + r |= 0x400; /* differential inputs */ + break; + + case AREF_OTHER: /* ??? */ + break; + } + /*printk ("chan=%d r=%d a=%d -> 0x%x\n", + chan, range, aref, r); */ + return r; +} + +/* + Setup the channel-gain table from a comedi list +*/ +static void rtd_load_channelgain_list(comedi_device * dev, + unsigned int n_chan, unsigned int *list) +{ + if (n_chan > 1) { /* setup channel gain table */ + int ii; + RtdClearCGT(dev); + RtdEnableCGT(dev, 1); /* enable table */ + for (ii = 0; ii < n_chan; ii++) { + RtdWriteCGTable(dev, rtdConvertChanGain(dev, list[ii], + ii)); + } + } else { /* just use the channel gain latch */ + RtdEnableCGT(dev, 0); /* disable table, enable latch */ + RtdWriteCGLatch(dev, rtdConvertChanGain(dev, list[0], 0)); + } +} + +/* determine fifo size by doing adc conversions until the fifo half +empty status flag clears */ +static int rtd520_probe_fifo_depth(comedi_device *dev) +{ + lsampl_t chanspec = CR_PACK(0, 0, AREF_GROUND); + unsigned i; + static const unsigned limit = 0x2000; + unsigned fifo_size = 0; + + RtdAdcClearFifo(dev); + rtd_load_channelgain_list(dev, 1, &chanspec); + RtdAdcConversionSource(dev, 0); /* software */ + /* convert samples */ + for (i = 0; i < limit; ++i) { + unsigned fifo_status; + /* trigger conversion */ + RtdAdcStart(dev); + comedi_udelay(1); + fifo_status = RtdFifoStatus(dev); + if((fifo_status & FS_ADC_HEMPTY) == 0) { + fifo_size = 2 * i; + break; + } + } + if(i == limit) + { + rt_printk("\ncomedi: %s: failed to probe fifo size.\n", DRV_NAME); + return -EIO; + } + RtdAdcClearFifo(dev); + if(fifo_size != 0x400 || fifo_size != 0x2000) + { + rt_printk("\ncomedi: %s: unexpected fifo size of %i, expected 1024 or 8192.\n", + DRV_NAME, fifo_size); + return -EIO; + } + return fifo_size; +} + +/* + "instructions" read/write data in "one-shot" or "software-triggered" + mode (simplest case). + This doesnt use interrupts. + + Note, we don't do any settling delays. Use a instruction list to + select, delay, then read. + */ +static int rtd_ai_rinsn(comedi_device * dev, + comedi_subdevice * s, comedi_insn * insn, lsampl_t * data) +{ + int n, ii; + int stat; + + /* clear any old fifo data */ + RtdAdcClearFifo(dev); + + /* write channel to multiplexer and clear channel gain table */ + rtd_load_channelgain_list(dev, 1, &insn->chanspec); + + /* set conversion source */ + RtdAdcConversionSource(dev, 0); /* software */ + + /* convert n samples */ + for (n = 0; n < insn->n; n++) { + s16 d; + /* trigger conversion */ + RtdAdcStart(dev); + + for (ii = 0; ii < RTD_ADC_TIMEOUT; ++ii) { + stat = RtdFifoStatus(dev); + if (stat & FS_ADC_NOT_EMPTY) /* 1 -> not empty */ + break; + WAIT_QUIETLY; + } + if (ii >= RTD_ADC_TIMEOUT) { + DPRINTK("rtd520: Error: ADC never finished! FifoStatus=0x%x\n", stat ^ 0x6666); + return -ETIMEDOUT; + } + + /* read data */ + d = RtdAdcFifoGet(dev); /* get 2s comp value */ + /*printk ("rtd520: Got 0x%x after %d usec\n", d, ii+1); */ + d = d >> 3; /* low 3 bits are marker lines */ + if (CHAN_ARRAY_TEST(devpriv->chanBipolar, 0)) { + data[n] = d + 2048; /* convert to comedi unsigned data */ + } else { + data[n] = d; + } + } + + /* return the number of samples read/written */ + return n; +} + +/* + Get what we know is there.... Fast! + This uses 1/2 the bus cycles of read_dregs (below). + + The manual claims that we can do a lword read, but it doesn't work here. +*/ +static int ai_read_n(comedi_device * dev, comedi_subdevice * s, int count) +{ + int ii; + + for (ii = 0; ii < count; ii++) { + sampl_t sample; + s16 d; + + if (0 == devpriv->aiCount) { /* done */ + d = RtdAdcFifoGet(dev); /* Read N and discard */ + continue; + } +#if 0 + if (0 == (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY)) { /* DEBUG */ + DPRINTK("comedi: READ OOPS on %d of %d\n", ii + 1, + count); + break; + } +#endif + d = RtdAdcFifoGet(dev); /* get 2s comp value */ + + d = d >> 3; /* low 3 bits are marker lines */ + if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) { + sample = d + 2048; /* convert to comedi unsigned data */ + } else { + sample = d; + } + if (!comedi_buf_put(s->async, sample)) + return -1; + + if (devpriv->aiCount > 0) /* < 0, means read forever */ + devpriv->aiCount--; + } + return 0; +} + +/* + unknown amout of data is waiting in fifo. +*/ +static int ai_read_dregs(comedi_device * dev, comedi_subdevice * s) +{ + while (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY) { /* 1 -> not empty */ + sampl_t sample; + s16 d = RtdAdcFifoGet(dev); /* get 2s comp value */ + + if (0 == devpriv->aiCount) { /* done */ + continue; /* read rest */ + } + + d = d >> 3; /* low 3 bits are marker lines */ + if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) { + sample = d + 2048; /* convert to comedi unsigned data */ + } else { + sample = d; + } + if (!comedi_buf_put(s->async, sample)) + return -1; + + if (devpriv->aiCount > 0) /* < 0, means read forever */ + devpriv->aiCount--; + } + return 0; +} + +#ifdef USE_DMA +/* + Terminate a DMA transfer and wait for everything to quiet down +*/ +void abort_dma(comedi_device * dev, unsigned int channel) +{ /* DMA channel 0, 1 */ + unsigned long dma_cs_addr; /* the control/status register */ + uint8_t status; + unsigned int ii; + //unsigned long flags; + + dma_cs_addr = (unsigned long)devpriv->lcfg + + ((channel == 0) ? LCFG_DMACSR0 : LCFG_DMACSR1); + + // spinlock for plx dma control/status reg + //comedi_spin_lock_irqsave( &dev->spinlock, flags ); + + // abort dma transfer if necessary + status = readb(dma_cs_addr); + if ((status & PLX_DMA_EN_BIT) == 0) { /* not enabled (Error?) */ + DPRINTK("rtd520: AbortDma on non-active channel %d (0x%x)\n", + channel, status); + goto abortDmaExit; + } + + /* wait to make sure done bit is zero (needed?) */ + for (ii = 0; (status & PLX_DMA_DONE_BIT) && ii < RTD_DMA_TIMEOUT; ii++) { + WAIT_QUIETLY; + status = readb(dma_cs_addr); + } + if (status & PLX_DMA_DONE_BIT) { + printk("rtd520: Timeout waiting for dma %i done clear\n", + channel); + goto abortDmaExit; + } + + /* disable channel (required) */ + writeb(0, dma_cs_addr); + comedi_udelay(1); /* needed?? */ + /* set abort bit for channel */ + writeb(PLX_DMA_ABORT_BIT, dma_cs_addr); + + // wait for dma done bit to be set + status = readb(dma_cs_addr); + for (ii = 0; + (status & PLX_DMA_DONE_BIT) == 0 && ii < RTD_DMA_TIMEOUT; + ii++) { + status = readb(dma_cs_addr); + WAIT_QUIETLY; + } + if ((status & PLX_DMA_DONE_BIT) == 0) { + printk("rtd520: Timeout waiting for dma %i done set\n", + channel); + } + + abortDmaExit: + //comedi_spin_unlock_irqrestore( &dev->spinlock, flags ); +} + +/* + Process what is in the DMA transfer buffer and pass to comedi + Note: this is not re-entrant +*/ +static int ai_process_dma(comedi_device * dev, comedi_subdevice * s) +{ + int ii, n; + s16 *dp; + + if (devpriv->aiCount == 0) /* transfer already complete */ + return 0; + + dp = devpriv->dma0Buff[devpriv->dma0Offset]; + for (ii = 0; ii < devpriv->fifoLen / 2;) { /* convert samples */ + sampl_t sample; + + if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) { + sample = (*dp >> 3) + 2048; /* convert to comedi unsigned data */ + } else { + sample = *dp >> 3; /* low 3 bits are marker lines */ + } + *dp++ = sample; /* put processed value back */ + + if (++s->async->cur_chan >= s->async->cmd.chanlist_len) + s->async->cur_chan = 0; + + ++ii; /* number ready to transfer */ + if (devpriv->aiCount > 0) { /* < 0, means read forever */ + if (--devpriv->aiCount == 0) { /* done */ + /*DPRINTK ("rtd520: Final %d samples\n", ii); */ + break; + } + } + } + + /* now pass the whole array to the comedi buffer */ + dp = devpriv->dma0Buff[devpriv->dma0Offset]; + n = comedi_buf_write_alloc(s->async, ii * sizeof(s16)); + if (n < (ii * sizeof(s16))) { /* any residual is an error */ + DPRINTK("rtd520:ai_process_dma buffer overflow %d samples!\n", + ii - (n / sizeof(s16))); + s->async->events |= COMEDI_CB_ERROR; + return -1; + } + comedi_buf_memcpy_to(s->async, 0, dp, n); + comedi_buf_write_free(s->async, n); + + /* always at least 1 scan -- 1/2 FIFO is larger than our max scan list */ + s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; + + if (++devpriv->dma0Offset >= DMA_CHAIN_COUNT) { /* next buffer */ + devpriv->dma0Offset = 0; + } + return 0; +} +#endif /* USE_DMA */ + +/* + Handle all rtd520 interrupts. + Runs atomically and is never re-entered. + This is a "slow handler"; other interrupts may be active. + The data conversion may someday happen in a "bottom half". +*/ +static irqreturn_t rtd_interrupt(int irq, /* interrupt number (ignored) */ + void *d /* our data */ + PT_REGS_ARG) +{ /* cpu context (ignored) */ + comedi_device *dev = d; /* must be called "dev" for devpriv */ + u16 status; + u16 fifoStatus; + comedi_subdevice *s = dev->subdevices + 0; /* analog in subdevice */ + + if (!dev->attached) { + return IRQ_NONE; + } + + devpriv->intCount++; /* DEBUG statistics */ + + fifoStatus = RtdFifoStatus(dev); + /* check for FIFO full, this automatically halts the ADC! */ + if (!(fifoStatus & FS_ADC_NOT_FULL)) { /* 0 -> full */ + DPRINTK("rtd520: FIFO full! fifo_status=0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); /* should be all 0s */ + goto abortTransfer; + } +#ifdef USE_DMA + if (devpriv->flags & DMA0_ACTIVE) { /* Check DMA */ + u32 istatus = RtdPlxInterruptRead(dev); + + if (istatus & ICS_DMA0_A) { + if (ai_process_dma(dev, s) < 0) { + DPRINTK("rtd520: comedi read buffer overflow (DMA) with %ld to go!\n", devpriv->aiCount); + RtdDma0Control(dev, + (devpriv-> + dma0Control & + ~PLX_DMA_START_BIT) + | PLX_CLEAR_DMA_INTR_BIT); + goto abortTransfer; + } + + /*DPRINTK ("rtd520: DMA transfer: %ld to go, istatus %x\n", + devpriv->aiCount, istatus); */ + RtdDma0Control(dev, + (devpriv->dma0Control & ~PLX_DMA_START_BIT) + | PLX_CLEAR_DMA_INTR_BIT); + if (0 == devpriv->aiCount) { /* counted down */ + DPRINTK("rtd520: Samples Done (DMA).\n"); + goto transferDone; + } + comedi_event(dev, s); + } else { + /*DPRINTK ("rtd520: No DMA ready: istatus %x\n", istatus); */ + } + } + /* Fall through and check for other interrupt sources */ +#endif /* USE_DMA */ + + status = RtdInterruptStatus(dev); + /* if interrupt was not caused by our board, or handled above */ + if (0 == status) { + return IRQ_HANDLED; + } + + if (status & IRQM_ADC_ABOUT_CNT) { /* sample count -> read FIFO */ + /* since the priority interrupt controller may have queued a sample + counter interrupt, even though we have already finished, + we must handle the possibility that there is no data here */ + if (!(fifoStatus & FS_ADC_HEMPTY)) { /* 0 -> 1/2 full */ + /*DPRINTK("rtd520: Sample int, reading 1/2FIFO. fifo_status 0x%x\n", + (fifoStatus ^ 0x6666) & 0x7777); */ + if (ai_read_n(dev, s, devpriv->fifoLen / 2) < 0) { + DPRINTK("rtd520: comedi read buffer overflow (1/2FIFO) with %ld to go!\n", devpriv->aiCount); + goto abortTransfer; + } + if (0 == devpriv->aiCount) { /* counted down */ + DPRINTK("rtd520: Samples Done (1/2). fifo_status was 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); /* should be all 0s */ + goto transferDone; + } + comedi_event(dev, s); + } else if (devpriv->transCount > 0) { /* read often */ + /*DPRINTK("rtd520: Sample int, reading %d fifo_status 0x%x\n", + devpriv->transCount, (fifoStatus ^ 0x6666) & 0x7777); */ + if (fifoStatus & FS_ADC_NOT_EMPTY) { /* 1 -> not empty */ + if (ai_read_n(dev, s, devpriv->transCount) < 0) { + DPRINTK("rtd520: comedi read buffer overflow (N) with %ld to go!\n", devpriv->aiCount); + goto abortTransfer; + } + if (0 == devpriv->aiCount) { /* counted down */ + DPRINTK("rtd520: Samples Done (N). fifo_status was 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); + goto transferDone; + } + comedi_event(dev, s); + } + } else { /* wait for 1/2 FIFO (old) */ + DPRINTK("rtd520: Sample int. Wait for 1/2. fifo_status 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); + } + } else { + DPRINTK("rtd520: unknown interrupt source!\n"); + } + + if (0xffff & RtdInterruptOverrunStatus(dev)) { /* interrupt overrun */ + DPRINTK("rtd520: Interrupt overrun with %ld to go! over_status=0x%x\n", devpriv->aiCount, 0xffff & RtdInterruptOverrunStatus(dev)); + goto abortTransfer; + } + + /* clear the interrupt */ + RtdInterruptClearMask(dev, status); + RtdInterruptClear(dev); + return IRQ_HANDLED; + + abortTransfer: + RtdAdcClearFifo(dev); /* clears full flag */ + s->async->events |= COMEDI_CB_ERROR; + devpriv->aiCount = 0; /* stop and don't transfer any more */ + /* fall into transferDone */ + + transferDone: + RtdPacerStopSource(dev, 0); /* stop on SOFTWARE stop */ + RtdPacerStop(dev); /* Stop PACER */ + RtdAdcConversionSource(dev, 0); /* software trigger only */ + RtdInterruptMask(dev, 0); /* mask out SAMPLE */ +#ifdef USE_DMA + if (devpriv->flags & DMA0_ACTIVE) { + RtdPlxInterruptWrite(dev, /* disable any more interrupts */ + RtdPlxInterruptRead(dev) & ~ICS_DMA0_E); + abort_dma(dev, 0); + devpriv->flags &= ~DMA0_ACTIVE; + /* if Using DMA, then we should have read everything by now */ + if (devpriv->aiCount > 0) { + DPRINTK("rtd520: Lost DMA data! %ld remain\n", + devpriv->aiCount); + } + } +#endif /* USE_DMA */ + + if (devpriv->aiCount > 0) { /* there shouldn't be anything left */ + fifoStatus = RtdFifoStatus(dev); + DPRINTK("rtd520: Finishing up. %ld remain, fifoStat=%x\n", devpriv->aiCount, (fifoStatus ^ 0x6666) & 0x7777); /* should read all 0s */ + ai_read_dregs(dev, s); /* read anything left in FIFO */ + } + + s->async->events |= COMEDI_CB_EOA; /* signal end to comedi */ + comedi_event(dev, s); + + /* clear the interrupt */ + status = RtdInterruptStatus(dev); + RtdInterruptClearMask(dev, status); + RtdInterruptClear(dev); + + fifoStatus = RtdFifoStatus(dev); /* DEBUG */ + DPRINTK("rtd520: Acquisition complete. %ld ints, intStat=%x, overStat=%x\n", devpriv->intCount, status, 0xffff & RtdInterruptOverrunStatus(dev)); + + return IRQ_HANDLED; +} + +#if 0 +/* + return the number of samples available +*/ +static int rtd_ai_poll(comedi_device * dev, comedi_subdevice * s) +{ + /* TODO: This needs to mask interrupts, read_dregs, and then re-enable */ + /* Not sure what to do if DMA is active */ + return s->async->buf_write_count - s->async->buf_read_count; +} +#endif + +/* + cmdtest tests a particular command to see if it is valid. + Using the cmdtest ioctl, a user can create a valid cmd + and then have it executed by the cmd ioctl (asyncronously). + + cmdtest returns 1,2,3,4 or 0, depending on which tests + the command passes. +*/ + +static int rtd_ai_cmdtest(comedi_device * dev, + comedi_subdevice * s, comedi_cmd * cmd) +{ + int err = 0; + int tmp; + + /* step 1: make sure trigger sources are trivially valid */ + + tmp = cmd->start_src; + cmd->start_src &= TRIG_NOW; + if (!cmd->start_src || tmp != cmd->start_src) { + err++; + } + + tmp = cmd->scan_begin_src; + cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT; + if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) { + err++; + } + + tmp = cmd->convert_src; + cmd->convert_src &= TRIG_TIMER | TRIG_EXT; + if (!cmd->convert_src || tmp != cmd->convert_src) { + err++; + } + + tmp = cmd->scan_end_src; + cmd->scan_end_src &= TRIG_COUNT; + if (!cmd->scan_end_src || tmp != cmd->scan_end_src) { + err++; + } + + tmp = cmd->stop_src; + cmd->stop_src &= TRIG_COUNT | TRIG_NONE; + if (!cmd->stop_src || tmp != cmd->stop_src) { + err++; + } + + if (err) + return 1; + + /* step 2: make sure trigger sources are unique + and mutually compatible */ + /* note that mutual compatiblity is not an issue here */ + if (cmd->scan_begin_src != TRIG_TIMER && + cmd->scan_begin_src != TRIG_EXT) { + err++; + } + if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT) { + err++; + } + if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE) { + err++; + } + + if (err) { + return 2; + } + + /* step 3: make sure arguments are trivially compatible */ + + if (cmd->start_arg != 0) { + cmd->start_arg = 0; + err++; + } + + if (cmd->scan_begin_src == TRIG_TIMER) { + /* Note: these are time periods, not actual rates */ + if (1 == cmd->chanlist_len) { /* no scanning */ + if (cmd->scan_begin_arg < RTD_MAX_SPEED_1) { + cmd->scan_begin_arg = RTD_MAX_SPEED_1; + rtd_ns_to_timer(&cmd->scan_begin_arg, + TRIG_ROUND_UP); + err++; + } + if (cmd->scan_begin_arg > RTD_MIN_SPEED_1) { + cmd->scan_begin_arg = RTD_MIN_SPEED_1; + rtd_ns_to_timer(&cmd->scan_begin_arg, + TRIG_ROUND_DOWN); + err++; + } + } else { + if (cmd->scan_begin_arg < RTD_MAX_SPEED) { + cmd->scan_begin_arg = RTD_MAX_SPEED; + rtd_ns_to_timer(&cmd->scan_begin_arg, + TRIG_ROUND_UP); + err++; + } + if (cmd->scan_begin_arg > RTD_MIN_SPEED) { + cmd->scan_begin_arg = RTD_MIN_SPEED; + rtd_ns_to_timer(&cmd->scan_begin_arg, + TRIG_ROUND_DOWN); + err++; + } + } + } else { + /* external trigger */ + /* should be level/edge, hi/lo specification here */ + /* should specify multiple external triggers */ + if (cmd->scan_begin_arg > 9) { + cmd->scan_begin_arg = 9; + err++; + } + } + if (cmd->convert_src == TRIG_TIMER) { + if (1 == cmd->chanlist_len) { /* no scanning */ + if (cmd->convert_arg < RTD_MAX_SPEED_1) { + cmd->convert_arg = RTD_MAX_SPEED_1; + rtd_ns_to_timer(&cmd->convert_arg, + TRIG_ROUND_UP); + err++; + } + if (cmd->convert_arg > RTD_MIN_SPEED_1) { + cmd->convert_arg = RTD_MIN_SPEED_1; + rtd_ns_to_timer(&cmd->convert_arg, + TRIG_ROUND_DOWN); + err++; + } + } else { + if (cmd->convert_arg < RTD_MAX_SPEED) { + cmd->convert_arg = RTD_MAX_SPEED; + rtd_ns_to_timer(&cmd->convert_arg, + TRIG_ROUND_UP); + err++; + } + if (cmd->convert_arg > RTD_MIN_SPEED) { + cmd->convert_arg = RTD_MIN_SPEED; + rtd_ns_to_timer(&cmd->convert_arg, + TRIG_ROUND_DOWN); + err++; + } + } + } else { + /* external trigger */ + /* see above */ + if (cmd->convert_arg > 9) { + cmd->convert_arg = 9; + err++; + } + } + +#if 0 + if (cmd->scan_end_arg != cmd->chanlist_len) { + cmd->scan_end_arg = cmd->chanlist_len; + err++; + } +#endif + if (cmd->stop_src == TRIG_COUNT) { + /* TODO check for rounding error due to counter wrap */ + + } else { + /* TRIG_NONE */ + if (cmd->stop_arg != 0) { + cmd->stop_arg = 0; + err++; + } + } + + if (err) { + return 3; + } + + /* step 4: fix up any arguments */ + + if (cmd->chanlist_len > RTD_MAX_CHANLIST) { + cmd->chanlist_len = RTD_MAX_CHANLIST; + err++; + } + if (cmd->scan_begin_src == TRIG_TIMER) { + tmp = cmd->scan_begin_arg; + rtd_ns_to_timer(&cmd->scan_begin_arg, + cmd->flags & TRIG_ROUND_MASK); + if (tmp != cmd->scan_begin_arg) { + err++; + } + } + if (cmd->convert_src == TRIG_TIMER) { + tmp = cmd->convert_arg; + rtd_ns_to_timer(&cmd->convert_arg, + cmd->flags & TRIG_ROUND_MASK); + if (tmp != cmd->convert_arg) { + err++; + } + if (cmd->scan_begin_src == TRIG_TIMER + && (cmd->scan_begin_arg + < (cmd->convert_arg * cmd->scan_end_arg))) { + cmd->scan_begin_arg = + cmd->convert_arg * cmd->scan_end_arg; + err++; + } + } + + if (err) { + return 4; + } + + return 0; +} + +/* + Execute a analog in command with many possible triggering options. + The data get stored in the async structure of the subdevice. + This is usually done by an interrupt handler. + Userland gets to the data using read calls. +*/ +static int rtd_ai_cmd(comedi_device * dev, comedi_subdevice * s) +{ + comedi_cmd *cmd = &s->async->cmd; + int timer; + + /* stop anything currently running */ + RtdPacerStopSource(dev, 0); /* stop on SOFTWARE stop */ + RtdPacerStop(dev); /* make sure PACER is stopped */ + RtdAdcConversionSource(dev, 0); /* software trigger only */ + RtdInterruptMask(dev, 0); +#ifdef USE_DMA + if (devpriv->flags & DMA0_ACTIVE) { /* cancel anything running */ + RtdPlxInterruptWrite(dev, /* disable any more interrupts */ + RtdPlxInterruptRead(dev) & ~ICS_DMA0_E); + abort_dma(dev, 0); + devpriv->flags &= ~DMA0_ACTIVE; + if (RtdPlxInterruptRead(dev) & ICS_DMA0_A) { /*clear pending int */ + RtdDma0Control(dev, PLX_CLEAR_DMA_INTR_BIT); + } + } + RtdDma0Reset(dev); /* reset onboard state */ +#endif /* USE_DMA */ + RtdAdcClearFifo(dev); /* clear any old data */ + RtdInterruptOverrunClear(dev); + devpriv->intCount = 0; + + if (!dev->irq) { /* we need interrupts for this */ + DPRINTK("rtd520: ERROR! No interrupt available!\n"); + return -ENXIO; + } + + /* start configuration */ + /* load channel list and reset CGT */ + rtd_load_channelgain_list(dev, cmd->chanlist_len, cmd->chanlist); + + /* setup the common case and override if needed */ + if (cmd->chanlist_len > 1) { + /*DPRINTK ("rtd520: Multi channel setup\n"); */ + RtdPacerStartSource(dev, 0); /* software triggers pacer */ + RtdBurstStartSource(dev, 1); /* PACER triggers burst */ + RtdAdcConversionSource(dev, 2); /* BURST triggers ADC */ + } else { /* single channel */ + /*DPRINTK ("rtd520: single channel setup\n"); */ + RtdPacerStartSource(dev, 0); /* software triggers pacer */ + RtdAdcConversionSource(dev, 1); /* PACER triggers ADC */ + } + RtdAboutCounter(dev, devpriv->fifoLen / 2 - 1); /* 1/2 FIFO */ + + if (TRIG_TIMER == cmd->scan_begin_src) { + /* scan_begin_arg is in nanoseconds */ + /* find out how many samples to wait before transferring */ + if (cmd->flags & TRIG_WAKE_EOS) { + /* this may generate un-sustainable interrupt rates */ + /* the application is responsible for doing the right thing */ + devpriv->transCount = cmd->chanlist_len; + devpriv->flags |= SEND_EOS; + } else { + /* arrange to transfer data periodically */ + devpriv->transCount + = + (TRANS_TARGET_PERIOD * cmd->chanlist_len) / + cmd->scan_begin_arg; + if (devpriv->transCount < cmd->chanlist_len) { + /* tranfer after each scan (and avoid 0) */ + devpriv->transCount = cmd->chanlist_len; + } else { /* make a multiple of scan length */ + devpriv->transCount = + (devpriv->transCount + + cmd->chanlist_len - 1) + / cmd->chanlist_len; + devpriv->transCount *= cmd->chanlist_len; + } + devpriv->flags |= SEND_EOS; + } + if (devpriv->transCount >= (devpriv->fifoLen / 2)) { + /* out of counter range, use 1/2 fifo instead */ + devpriv->transCount = 0; + devpriv->flags &= ~SEND_EOS; + } else { + /* interrupt for each tranfer */ + RtdAboutCounter(dev, devpriv->transCount - 1); + } + + DPRINTK("rtd520: scanLen=%d tranferCount=%d fifoLen=%d\n scanTime(ns)=%d flags=0x%x\n", cmd->chanlist_len, devpriv->transCount, devpriv->fifoLen, cmd->scan_begin_arg, devpriv->flags); + } else { /* unknown timing, just use 1/2 FIFO */ + devpriv->transCount = 0; + devpriv->flags &= ~SEND_EOS; + } + RtdPacerClockSource(dev, 1); /* use INTERNAL 8Mhz clock source */ + RtdAboutStopEnable(dev, 1); /* just interrupt, dont stop */ + + /* BUG??? these look like enumerated values, but they are bit fields */ + + /* First, setup when to stop */ + switch (cmd->stop_src) { + case TRIG_COUNT: /* stop after N scans */ + devpriv->aiCount = cmd->stop_arg * cmd->chanlist_len; + if ((devpriv->transCount > 0) + && (devpriv->transCount > devpriv->aiCount)) { + devpriv->transCount = devpriv->aiCount; + } + break; + + case TRIG_NONE: /* stop when cancel is called */ + devpriv->aiCount = -1; /* read forever */ + break; + + default: + DPRINTK("rtd520: Warning! ignoring stop_src mode %d\n", + cmd->stop_src); + } + + /* Scan timing */ + switch (cmd->scan_begin_src) { + case TRIG_TIMER: /* periodic scanning */ + timer = rtd_ns_to_timer(&cmd->scan_begin_arg, + TRIG_ROUND_NEAREST); + /* set PACER clock */ + /*DPRINTK ("rtd520: loading %d into pacer\n", timer); */ + RtdPacerCounter(dev, timer); + + break; + + case TRIG_EXT: + RtdPacerStartSource(dev, 1); /* EXTERNALy trigger pacer */ + break; + + default: + DPRINTK("rtd520: Warning! ignoring scan_begin_src mode %d\n", + cmd->scan_begin_src); + } + + /* Sample timing within a scan */ + switch (cmd->convert_src) { + case TRIG_TIMER: /* periodic */ + if (cmd->chanlist_len > 1) { /* only needed for multi-channel */ + timer = rtd_ns_to_timer(&cmd->convert_arg, + TRIG_ROUND_NEAREST); + /* setup BURST clock */ + /*DPRINTK ("rtd520: loading %d into burst\n", timer); */ + RtdBurstCounter(dev, timer); + } + + break; + + case TRIG_EXT: /* external */ + RtdBurstStartSource(dev, 2); /* EXTERNALy trigger burst */ + break; + + default: + DPRINTK("rtd520: Warning! ignoring convert_src mode %d\n", + cmd->convert_src); + } + /* end configuration */ + + /* This doesn't seem to work. There is no way to clear an interrupt + that the priority controller has queued! */ + RtdInterruptClearMask(dev, ~0); /* clear any existing flags */ + RtdInterruptClear(dev); + + /* TODO: allow multiple interrupt sources */ + if (devpriv->transCount > 0) { /* transfer every N samples */ + RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT); + DPRINTK("rtd520: Transferring every %d\n", devpriv->transCount); + } else { /* 1/2 FIFO transfers */ +#ifdef USE_DMA + devpriv->flags |= DMA0_ACTIVE; + + /* point to first transfer in ring */ + devpriv->dma0Offset = 0; + RtdDma0Mode(dev, DMA_MODE_BITS); + RtdDma0Next(dev, /* point to first block */ + devpriv->dma0Chain[DMA_CHAIN_COUNT - 1].next); + RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL); /* set DMA trigger source */ + + RtdPlxInterruptWrite(dev, /* enable interrupt */ + RtdPlxInterruptRead(dev) | ICS_DMA0_E); + /* Must be 2 steps. See PLX app note about "Starting a DMA transfer" */ + RtdDma0Control(dev, PLX_DMA_EN_BIT); /* enable DMA (clear INTR?) */ + RtdDma0Control(dev, PLX_DMA_EN_BIT | PLX_DMA_START_BIT); /*start DMA */ + DPRINTK("rtd520: Using DMA0 transfers. plxInt %x RtdInt %x\n", + RtdPlxInterruptRead(dev), devpriv->intMask); +#else /* USE_DMA */ + RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT); + DPRINTK("rtd520: Transferring every 1/2 FIFO\n"); +#endif /* USE_DMA */ + } + + /* BUG: start_src is ASSUMED to be TRIG_NOW */ + /* BUG? it seems like things are running before the "start" */ + RtdPacerStart(dev); /* Start PACER */ + return 0; +} + +/* + Stop a running data aquisition. +*/ +static int rtd_ai_cancel(comedi_device * dev, comedi_subdevice * s) +{ + u16 status; + + RtdPacerStopSource(dev, 0); /* stop on SOFTWARE stop */ + RtdPacerStop(dev); /* Stop PACER */ + RtdAdcConversionSource(dev, 0); /* software trigger only */ + RtdInterruptMask(dev, 0); + devpriv->aiCount = 0; /* stop and don't transfer any more */ +#ifdef USE_DMA + if (devpriv->flags & DMA0_ACTIVE) { + RtdPlxInterruptWrite(dev, /* disable any more interrupts */ + RtdPlxInterruptRead(dev) & ~ICS_DMA0_E); + abort_dma(dev, 0); + devpriv->flags &= ~DMA0_ACTIVE; + } +#endif /* USE_DMA */ + status = RtdInterruptStatus(dev); + DPRINTK("rtd520: Acquisition canceled. %ld ints, intStat=%x, overStat=%x\n", devpriv->intCount, status, 0xffff & RtdInterruptOverrunStatus(dev)); + return 0; +} + +/* + Given a desired period and the clock period (both in ns), + return the proper counter value (divider-1). + Sets the original period to be the true value. + Note: you have to check if the value is larger than the counter range! +*/ +static int rtd_ns_to_timer_base(unsigned int *nanosec, /* desired period (in ns) */ + int round_mode, int base) +{ /* clock period (in ns) */ + int divider; + + switch (round_mode) { + case TRIG_ROUND_NEAREST: + default: + divider = (*nanosec + base / 2) / base; + break; + case TRIG_ROUND_DOWN: + divider = (*nanosec) / base; + break; + case TRIG_ROUND_UP: + divider = (*nanosec + base - 1) / base; + break; + } + if (divider < 2) + divider = 2; /* min is divide by 2 */ + + /* Note: we don't check for max, because different timers + have different ranges */ + + *nanosec = base * divider; + return divider - 1; /* countdown is divisor+1 */ +} + +/* + Given a desired period (in ns), + return the proper counter value (divider-1) for the internal clock. + Sets the original period to be the true value. +*/ +static int rtd_ns_to_timer(unsigned int *ns, int round_mode) +{ + return rtd_ns_to_timer_base(ns, round_mode, RTD_CLOCK_BASE); +} + +/* + Output one (or more) analog values to a single port as fast as possible. +*/ +static int rtd_ao_winsn(comedi_device * dev, + comedi_subdevice * s, comedi_insn * insn, lsampl_t * data) +{ + int i; + int chan = CR_CHAN(insn->chanspec); + int range = CR_RANGE(insn->chanspec); + + /* Configure the output range (table index matches the range values) */ + RtdDacRange(dev, chan, range); + + /* Writing a list of values to an AO channel is probably not + * very useful, but that's how the interface is defined. */ + for (i = 0; i < insn->n; ++i) { + int val = data[i] << 3; + int stat = 0; /* initialize to avoid bogus warning */ + int ii; + + /* VERIFY: comedi range and offset conversions */ + + if ((range > 1) /* bipolar */ + &&(data[i] < 2048)) { + /* offset and sign extend */ + val = (((int)data[i]) - 2048) << 3; + } else { /* unipolor */ + val = data[i] << 3; + } + + DPRINTK("comedi: rtd520 DAC chan=%d range=%d writing %d as 0x%x\n", chan, range, data[i], val); + + /* a typical programming sequence */ + RtdDacFifoPut(dev, chan, val); /* put the value in */ + RtdDacUpdate(dev, chan); /* trigger the conversion */ + + devpriv->aoValue[chan] = data[i]; /* save for read back */ + + for (ii = 0; ii < RTD_DAC_TIMEOUT; ++ii) { + stat = RtdFifoStatus(dev); + /* 1 -> not empty */ + if (stat & ((0 == chan) ? FS_DAC1_NOT_EMPTY : + FS_DAC2_NOT_EMPTY)) + break; + WAIT_QUIETLY; + } + if (ii >= RTD_DAC_TIMEOUT) { + DPRINTK("rtd520: Error: DAC never finished! FifoStatus=0x%x\n", stat ^ 0x6666); + return -ETIMEDOUT; + } + } + + /* return the number of samples read/written */ + return i; +} + +/* AO subdevices should have a read insn as well as a write insn. + * Usually this means copying a value stored in devpriv. */ +static int rtd_ao_rinsn(comedi_device * dev, + comedi_subdevice * s, comedi_insn * insn, lsampl_t * data) +{ + int i; + int chan = CR_CHAN(insn->chanspec); + + for (i = 0; i < insn->n; i++) { + data[i] = devpriv->aoValue[chan]; + } + + return i; +} + +/* + Write a masked set of bits and the read back the port. + We track what the bits should be (i.e. we don't read the port first). + + DIO devices are slightly special. Although it is possible to + * implement the insn_read/insn_write interface, it is much more + * useful to applications if you implement the insn_bits interface. + * This allows packed reading/writing of the DIO channels. The + * comedi core can convert between insn_bits and insn_read/write + */ +static int rtd_dio_insn_bits(comedi_device * dev, + comedi_subdevice * s, comedi_insn * insn, lsampl_t * data) +{ + if (insn->n != 2) + return -EINVAL; + + /* The insn data is a mask in data[0] and the new data + * in data[1], each channel cooresponding to a bit. */ + if (data[0]) { + s->state &= ~data[0]; + s->state |= data[0] & data[1]; + + /* Write out the new digital output lines */ + RtdDio0Write(dev, s->state); + } + /* on return, data[1] contains the value of the digital + * input lines. */ + data[1] = RtdDio0Read(dev); + + /*DPRINTK("rtd520:port_0 wrote: 0x%x read: 0x%x\n", s->state, data[1]); */ + + return 2; +} + +/* + Configure one bit on a IO port as Input or Output (hence the name :-). +*/ +static int rtd_dio_insn_config(comedi_device * dev, + comedi_subdevice * s, comedi_insn * insn, lsampl_t * data) +{ + int chan = CR_CHAN(insn->chanspec); + + /* The input or output configuration of each digital line is + * configured by a special insn_config instruction. chanspec + * contains the channel to be changed, and data[0] contains the + * value COMEDI_INPUT or COMEDI_OUTPUT. */ + switch (data[0]) { + case INSN_CONFIG_DIO_OUTPUT: + s->io_bits |= 1 << chan; /* 1 means Out */ + break; + case INSN_CONFIG_DIO_INPUT: + s->io_bits &= ~(1 << chan); + break; + case INSN_CONFIG_DIO_QUERY: + data[1] = + (s-> + io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; + return insn->n; + break; + default: + return -EINVAL; + } + + DPRINTK("rtd520: port_0_direction=0x%x (1 means out)\n", s->io_bits); + /* TODO support digital match interrupts and strobes */ + RtdDioStatusWrite(dev, 0x01); /* make Dio0Ctrl point to direction */ + RtdDio0CtrlWrite(dev, s->io_bits); /* set direction 1 means Out */ + RtdDioStatusWrite(dev, 0); /* make Dio0Ctrl clear interrupts */ + + /* port1 can only be all input or all output */ + + /* there are also 2 user input lines and 2 user output lines */ + + return 1; +} + +/* + * A convenient macro that defines init_module() and cleanup_module(), + * as necessary. + */ +COMEDI_PCI_INITCLEANUP(rtd520Driver, rtd520_pci_table); --- /dev/null +++ b/drivers/staging/comedi/drivers/rtd520.h @@ -0,0 +1,412 @@ +/* + comedi/drivers/rtd520.h + Comedi driver defines for Real Time Devices (RTD) PCI4520/DM7520 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 2001 David A. Schleef + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Created by Dan Christian, NASA Ames Research Center. + See board notes in rtd520.c +*/ + +/* + LAS0 Runtime Area + Local Address Space 0 Offset Read Function Write Function +*/ +#define LAS0_SPARE_00 0x0000 // - - +#define LAS0_SPARE_04 0x0004 // - - +#define LAS0_USER_IO 0x0008 // Read User Inputs Write User Outputs +#define LAS0_SPARE_0C 0x000C // - - +#define LAS0_ADC 0x0010 // Read FIFO Status Software A/D Start +#define LAS0_DAC1 0x0014 // - Software D/A1 Update +#define LAS0_DAC2 0x0018 // - Software D/A2 Update +#define LAS0_SPARE_1C 0x001C // - - +#define LAS0_SPARE_20 0x0020 // - - +#define LAS0_DAC 0x0024 // - Software Simultaneous D/A1 and D/A2 Update +#define LAS0_PACER 0x0028 // Software Pacer Start Software Pacer Stop +#define LAS0_TIMER 0x002C // Read Timer Counters Status HDIN Software Trigger +#define LAS0_IT 0x0030 // Read Interrupt Status Write Interrupt Enable Mask Register +#define LAS0_CLEAR 0x0034 // Clear ITs set by Clear Mask Set Interrupt Clear Mask +#define LAS0_OVERRUN 0x0038 // Read pending interrupts Clear Overrun Register +#define LAS0_SPARE_3C 0x003C // - - + +/* + LAS0 Runtime Area Timer/Counter,Dig.IO + Name Local Address Function +*/ +#define LAS0_PCLK 0x0040 // Pacer Clock value (24bit) Pacer Clock load (24bit) +#define LAS0_BCLK 0x0044 // Burst Clock value (10bit) Burst Clock load (10bit) +#define LAS0_ADC_SCNT 0x0048 // A/D Sample counter value (10bit) A/D Sample counter load (10bit) +#define LAS0_DAC1_UCNT 0x004C // D/A1 Update counter value (10 bit) D/A1 Update counter load (10bit) +#define LAS0_DAC2_UCNT 0x0050 // D/A2 Update counter value (10 bit) D/A2 Update counter load (10bit) +#define LAS0_DCNT 0x0054 // Delay counter value (16 bit) Delay counter load (16bit) +#define LAS0_ACNT 0x0058 // About counter value (16 bit) About counter load (16bit) +#define LAS0_DAC_CLK 0x005C // DAC clock value (16bit) DAC clock load (16bit) +#define LAS0_UTC0 0x0060 // 8254 TC Counter 0 User TC 0 value Load count in TC Counter 0 +#define LAS0_UTC1 0x0064 // 8254 TC Counter 1 User TC 1 value Load count in TC Counter 1 +#define LAS0_UTC2 0x0068 // 8254 TC Counter 2 User TC 2 value Load count in TC Counter 2 +#define LAS0_UTC_CTRL 0x006C // 8254 TC Control Word Program counter mode for TC +#define LAS0_DIO0 0x0070 // Digital I/O Port 0 Read Port Digital I/O Port 0 Write Port +#define LAS0_DIO1 0x0074 // Digital I/O Port 1 Read Port Digital I/O Port 1 Write Port +#define LAS0_DIO0_CTRL 0x0078 // Clear digital IRQ status flag/read Clear digital chip/program Port 0 +#define LAS0_DIO_STATUS 0x007C // Read Digital I/O Status word Program digital control register & + +/* + LAS0 Setup Area + Name Local Address Function +*/ +#define LAS0_BOARD_RESET 0x0100 // Board reset +#define LAS0_DMA0_SRC 0x0104 // DMA 0 Sources select +#define LAS0_DMA1_SRC 0x0108 // DMA 1 Sources select +#define LAS0_ADC_CONVERSION 0x010C // A/D Conversion Signal select +#define LAS0_BURST_START 0x0110 // Burst Clock Start Trigger select +#define LAS0_PACER_START 0x0114 // Pacer Clock Start Trigger select +#define LAS0_PACER_STOP 0x0118 // Pacer Clock Stop Trigger select +#define LAS0_ACNT_STOP_ENABLE 0x011C // About Counter Stop Enable +#define LAS0_PACER_REPEAT 0x0120 // Pacer Start Trigger Mode select +#define LAS0_DIN_START 0x0124 // High Speed Digital Input Sampling Signal select +#define LAS0_DIN_FIFO_CLEAR 0x0128 // Digital Input FIFO Clear +#define LAS0_ADC_FIFO_CLEAR 0x012C // A/D FIFO Clear +#define LAS0_CGT_WRITE 0x0130 // Channel Gain Table Write +#define LAS0_CGL_WRITE 0x0134 // Channel Gain Latch Write +#define LAS0_CG_DATA 0x0138 // Digital Table Write +#define LAS0_CGT_ENABLE 0x013C // Channel Gain Table Enable +#define LAS0_CG_ENABLE 0x0140 // Digital Table Enable +#define LAS0_CGT_PAUSE 0x0144 // Table Pause Enable +#define LAS0_CGT_RESET 0x0148 // Reset Channel Gain Table +#define LAS0_CGT_CLEAR 0x014C // Clear Channel Gain Table +#define LAS0_DAC1_CTRL 0x0150 // D/A1 output type/range +#define LAS0_DAC1_SRC 0x0154 // D/A1 update source +#define LAS0_DAC1_CYCLE 0x0158 // D/A1 cycle mode +#define LAS0_DAC1_RESET 0x015C // D/A1 FIFO reset +#define LAS0_DAC1_FIFO_CLEAR 0x0160 // D/A1 FIFO clear +#define LAS0_DAC2_CTRL 0x0164 // D/A2 output type/range +#define LAS0_DAC2_SRC 0x0168 // D/A2 update source +#define LAS0_DAC2_CYCLE 0x016C // D/A2 cycle mode +#define LAS0_DAC2_RESET 0x0170 // D/A2 FIFO reset +#define LAS0_DAC2_FIFO_CLEAR 0x0174 // D/A2 FIFO clear +#define LAS0_ADC_SCNT_SRC 0x0178 // A/D Sample Counter Source select +#define LAS0_PACER_SELECT 0x0180 // Pacer Clock select +#define LAS0_SBUS0_SRC 0x0184 // SyncBus 0 Source select +#define LAS0_SBUS0_ENABLE 0x0188 // SyncBus 0 enable +#define LAS0_SBUS1_SRC 0x018C // SyncBus 1 Source select +#define LAS0_SBUS1_ENABLE 0x0190 // SyncBus 1 enable +#define LAS0_SBUS2_SRC 0x0198 // SyncBus 2 Source select +#define LAS0_SBUS2_ENABLE 0x019C // SyncBus 2 enable +#define LAS0_ETRG_POLARITY 0x01A4 // External Trigger polarity select +#define LAS0_EINT_POLARITY 0x01A8 // External Interrupt polarity select +#define LAS0_UTC0_CLOCK 0x01AC // UTC0 Clock select +#define LAS0_UTC0_GATE 0x01B0 // UTC0 Gate select +#define LAS0_UTC1_CLOCK 0x01B4 // UTC1 Clock select +#define LAS0_UTC1_GATE 0x01B8 // UTC1 Gate select +#define LAS0_UTC2_CLOCK 0x01BC // UTC2 Clock select +#define LAS0_UTC2_GATE 0x01C0 // UTC2 Gate select +#define LAS0_UOUT0_SELECT 0x01C4 // User Output 0 source select +#define LAS0_UOUT1_SELECT 0x01C8 // User Output 1 source select +#define LAS0_DMA0_RESET 0x01CC // DMA0 Request state machine reset +#define LAS0_DMA1_RESET 0x01D0 // DMA1 Request state machine reset + +/* + LAS1 + Name Local Address Function +*/ +#define LAS1_ADC_FIFO 0x0000 // Read A/D FIFO (16bit) - +#define LAS1_HDIO_FIFO 0x0004 // Read High Speed Digital Input FIFO (16bit) - +#define LAS1_DAC1_FIFO 0x0008 // - Write D/A1 FIFO (16bit) +#define LAS1_DAC2_FIFO 0x000C // - Write D/A2 FIFO (16bit) + +/* + LCFG: PLX 9080 local config & runtime registers + Name Local Address Function +*/ +#define LCFG_ITCSR 0x0068 // INTCSR, Interrupt Control/Status Register +#define LCFG_DMAMODE0 0x0080 // DMA Channel 0 Mode Register +#define LCFG_DMAPADR0 0x0084 // DMA Channel 0 PCI Address Register +#define LCFG_DMALADR0 0x0088 // DMA Channel 0 Local Address Reg +#define LCFG_DMASIZ0 0x008C // DMA Channel 0 Transfer Size (Bytes) Register +#define LCFG_DMADPR0 0x0090 // DMA Channel 0 Descriptor Pointer Register +#define LCFG_DMAMODE1 0x0094 // DMA Channel 1 Mode Register +#define LCFG_DMAPADR1 0x0098 // DMA Channel 1 PCI Address Register +#define LCFG_DMALADR1 0x009C // DMA Channel 1 Local Address Register +#define LCFG_DMASIZ1 0x00A0 // DMA Channel 1 Transfer Size (Bytes) Register +#define LCFG_DMADPR1 0x00A4 // DMA Channel 1 Descriptor Pointer Register +#define LCFG_DMACSR0 0x00A8 // DMA Channel 0 Command/Status Register +#define LCFG_DMACSR1 0x00A9 // DMA Channel 0 Command/Status Register +#define LCFG_DMAARB 0x00AC // DMA Arbitration Register +#define LCFG_DMATHR 0x00B0 // DMA Threshold Register + +/*====================================================================== + Resister bit definitions +======================================================================*/ + +// FIFO Status Word Bits (RtdFifoStatus) +#define FS_DAC1_NOT_EMPTY 0x0001 // D0 - DAC1 FIFO not empty +#define FS_DAC1_HEMPTY 0x0002 // D1 - DAC1 FIFO half empty +#define FS_DAC1_NOT_FULL 0x0004 // D2 - DAC1 FIFO not full +#define FS_DAC2_NOT_EMPTY 0x0010 // D4 - DAC2 FIFO not empty +#define FS_DAC2_HEMPTY 0x0020 // D5 - DAC2 FIFO half empty +#define FS_DAC2_NOT_FULL 0x0040 // D6 - DAC2 FIFO not full +#define FS_ADC_NOT_EMPTY 0x0100 // D8 - ADC FIFO not empty +#define FS_ADC_HEMPTY 0x0200 // D9 - ADC FIFO half empty +#define FS_ADC_NOT_FULL 0x0400 // D10 - ADC FIFO not full +#define FS_DIN_NOT_EMPTY 0x1000 // D12 - DIN FIFO not empty +#define FS_DIN_HEMPTY 0x2000 // D13 - DIN FIFO half empty +#define FS_DIN_NOT_FULL 0x4000 // D14 - DIN FIFO not full + +// Timer Status Word Bits (GetTimerStatus) +#define TS_PCLK_GATE 0x0001 +// D0 - Pacer Clock Gate [0 - gated, 1 - enabled] +#define TS_BCLK_GATE 0x0002 +// D1 - Burst Clock Gate [0 - disabled, 1 - running] +#define TS_DCNT_GATE 0x0004 +// D2 - Pacer Clock Delayed Start Trigger [0 - delay over, 1 - delay in +// progress] +#define TS_ACNT_GATE 0x0008 +// D3 - Pacer Clock About Trigger [0 - completed, 1 - in progress] +#define TS_PCLK_RUN 0x0010 +// D4 - Pacer Clock Shutdown Flag [0 - Pacer Clock cannot be start +// triggered only by Software Pacer Start Command, 1 - Pacer Clock can +// be start triggered] + +// External Trigger polarity select +// External Interrupt polarity select +#define POL_POSITIVE 0x0 // positive edge +#define POL_NEGATIVE 0x1 // negative edge + +// User Output Signal select (SetUout0Source, SetUout1Source) +#define UOUT_ADC 0x0 // A/D Conversion Signal +#define UOUT_DAC1 0x1 // D/A1 Update +#define UOUT_DAC2 0x2 // D/A2 Update +#define UOUT_SOFTWARE 0x3 // Software Programmable + +// Pacer clock select (SetPacerSource) +#define PCLK_INTERNAL 1 // Internal Pacer Clock +#define PCLK_EXTERNAL 0 // External Pacer Clock + +// A/D Sample Counter Sources (SetAdcntSource, SetupSampleCounter) +#define ADC_SCNT_CGT_RESET 0x0 // needs restart with StartPacer +#define ADC_SCNT_FIFO_WRITE 0x1 + +// A/D Conversion Signal Select (for SetConversionSelect) +#define ADC_START_SOFTWARE 0x0 // Software A/D Start +#define ADC_START_PCLK 0x1 // Pacer Clock (Ext. Int. see Func.509) +#define ADC_START_BCLK 0x2 // Burst Clock +#define ADC_START_DIGITAL_IT 0x3 // Digital Interrupt +#define ADC_START_DAC1_MARKER1 0x4 // D/A 1 Data Marker 1 +#define ADC_START_DAC2_MARKER1 0x5 // D/A 2 Data Marker 1 +#define ADC_START_SBUS0 0x6 // SyncBus 0 +#define ADC_START_SBUS1 0x7 // SyncBus 1 +#define ADC_START_SBUS2 0x8 // SyncBus 2 + +// Burst Clock start trigger select (SetBurstStart) +#define BCLK_START_SOFTWARE 0x0 // Software A/D Start (StartBurst) +#define BCLK_START_PCLK 0x1 // Pacer Clock +#define BCLK_START_ETRIG 0x2 // External Trigger +#define BCLK_START_DIGITAL_IT 0x3 // Digital Interrupt +#define BCLK_START_SBUS0 0x4 // SyncBus 0 +#define BCLK_START_SBUS1 0x5 // SyncBus 1 +#define BCLK_START_SBUS2 0x6 // SyncBus 2 + +// Pacer Clock start trigger select (SetPacerStart) +#define PCLK_START_SOFTWARE 0x0 // Software Pacer Start (StartPacer) +#define PCLK_START_ETRIG 0x1 // External trigger +#define PCLK_START_DIGITAL_IT 0x2 // Digital interrupt +#define PCLK_START_UTC2 0x3 // User TC 2 out +#define PCLK_START_SBUS0 0x4 // SyncBus 0 +#define PCLK_START_SBUS1 0x5 // SyncBus 1 +#define PCLK_START_SBUS2 0x6 // SyncBus 2 +#define PCLK_START_D_SOFTWARE 0x8 // Delayed Software Pacer Start +#define PCLK_START_D_ETRIG 0x9 // Delayed external trigger +#define PCLK_START_D_DIGITAL_IT 0xA // Delayed digital interrupt +#define PCLK_START_D_UTC2 0xB // Delayed User TC 2 out +#define PCLK_START_D_SBUS0 0xC // Delayed SyncBus 0 +#define PCLK_START_D_SBUS1 0xD // Delayed SyncBus 1 +#define PCLK_START_D_SBUS2 0xE // Delayed SyncBus 2 +#define PCLK_START_ETRIG_GATED 0xF // External Trigger Gated controlled mode + +// Pacer Clock Stop Trigger select (SetPacerStop) +#define PCLK_STOP_SOFTWARE 0x0 // Software Pacer Stop (StopPacer) +#define PCLK_STOP_ETRIG 0x1 // External Trigger +#define PCLK_STOP_DIGITAL_IT 0x2 // Digital Interrupt +#define PCLK_STOP_ACNT 0x3 // About Counter +#define PCLK_STOP_UTC2 0x4 // User TC2 out +#define PCLK_STOP_SBUS0 0x5 // SyncBus 0 +#define PCLK_STOP_SBUS1 0x6 // SyncBus 1 +#define PCLK_STOP_SBUS2 0x7 // SyncBus 2 +#define PCLK_STOP_A_SOFTWARE 0x8 // About Software Pacer Stop +#define PCLK_STOP_A_ETRIG 0x9 // About External Trigger +#define PCLK_STOP_A_DIGITAL_IT 0xA // About Digital Interrupt +#define PCLK_STOP_A_UTC2 0xC // About User TC2 out +#define PCLK_STOP_A_SBUS0 0xD // About SyncBus 0 +#define PCLK_STOP_A_SBUS1 0xE // About SyncBus 1 +#define PCLK_STOP_A_SBUS2 0xF // About SyncBus 2 + +// About Counter Stop Enable +#define ACNT_STOP 0x0 // stop enable +#define ACNT_NO_STOP 0x1 // stop disabled + +// DAC update source (SetDAC1Start & SetDAC2Start) +#define DAC_START_SOFTWARE 0x0 // Software Update +#define DAC_START_CGT 0x1 // CGT controlled Update +#define DAC_START_DAC_CLK 0x2 // D/A Clock +#define DAC_START_EPCLK 0x3 // External Pacer Clock +#define DAC_START_SBUS0 0x4 // SyncBus 0 +#define DAC_START_SBUS1 0x5 // SyncBus 1 +#define DAC_START_SBUS2 0x6 // SyncBus 2 + +// DAC Cycle Mode (SetDAC1Cycle, SetDAC2Cycle, SetupDAC) +#define DAC_CYCLE_SINGLE 0x0 // not cycle +#define DAC_CYCLE_MULTI 0x1 // cycle + +// 8254 Operation Modes (Set8254Mode, SetupTimerCounter) +#define M8254_EVENT_COUNTER 0 // Event Counter +#define M8254_HW_ONE_SHOT 1 // Hardware-Retriggerable One-Shot +#define M8254_RATE_GENERATOR 2 // Rate Generator +#define M8254_SQUARE_WAVE 3 // Square Wave Mode +#define M8254_SW_STROBE 4 // Software Triggered Strobe +#define M8254_HW_STROBE 5 // Hardware Triggered Strobe (Retriggerable) + +// User Timer/Counter 0 Clock Select (SetUtc0Clock) +#define CUTC0_8MHZ 0x0 // 8MHz +#define CUTC0_EXT_TC_CLOCK1 0x1 // Ext. TC Clock 1 +#define CUTC0_EXT_TC_CLOCK2 0x2 // Ext. TC Clock 2 +#define CUTC0_EXT_PCLK 0x3 // Ext. Pacer Clock + +// User Timer/Counter 1 Clock Select (SetUtc1Clock) +#define CUTC1_8MHZ 0x0 // 8MHz +#define CUTC1_EXT_TC_CLOCK1 0x1 // Ext. TC Clock 1 +#define CUTC1_EXT_TC_CLOCK2 0x2 // Ext. TC Clock 2 +#define CUTC1_EXT_PCLK 0x3 // Ext. Pacer Clock +#define CUTC1_UTC0_OUT 0x4 // User Timer/Counter 0 out +#define CUTC1_DIN_SIGNAL 0x5 // High-Speed Digital Input Sampling signal + +// User Timer/Counter 2 Clock Select (SetUtc2Clock) +#define CUTC2_8MHZ 0x0 // 8MHz +#define CUTC2_EXT_TC_CLOCK1 0x1 // Ext. TC Clock 1 +#define CUTC2_EXT_TC_CLOCK2 0x2 // Ext. TC Clock 2 +#define CUTC2_EXT_PCLK 0x3 // Ext. Pacer Clock +#define CUTC2_UTC1_OUT 0x4 // User Timer/Counter 1 out + +// User Timer/Counter 0 Gate Select (SetUtc0Gate) +#define GUTC0_NOT_GATED 0x0 // Not gated +#define GUTC0_GATED 0x1 // Gated +#define GUTC0_EXT_TC_GATE1 0x2 // Ext. TC Gate 1 +#define GUTC0_EXT_TC_GATE2 0x3 // Ext. TC Gate 2 + +// User Timer/Counter 1 Gate Select (SetUtc1Gate) +#define GUTC1_NOT_GATED 0x0 // Not gated +#define GUTC1_GATED 0x1 // Gated +#define GUTC1_EXT_TC_GATE1 0x2 // Ext. TC Gate 1 +#define GUTC1_EXT_TC_GATE2 0x3 // Ext. TC Gate 2 +#define GUTC1_UTC0_OUT 0x4 // User Timer/Counter 0 out + +// User Timer/Counter 2 Gate Select (SetUtc2Gate) +#define GUTC2_NOT_GATED 0x0 // Not gated +#define GUTC2_GATED 0x1 // Gated +#define GUTC2_EXT_TC_GATE1 0x2 // Ext. TC Gate 1 +#define GUTC2_EXT_TC_GATE2 0x3 // Ext. TC Gate 2 +#define GUTC2_UTC1_OUT 0x4 // User Timer/Counter 1 out + +// Interrupt Source Masks (SetITMask, ClearITMask, GetITStatus) +#define IRQM_ADC_FIFO_WRITE 0x0001 // ADC FIFO Write +#define IRQM_CGT_RESET 0x0002 // Reset CGT +#define IRQM_CGT_PAUSE 0x0008 // Pause CGT +#define IRQM_ADC_ABOUT_CNT 0x0010 // About Counter out +#define IRQM_ADC_DELAY_CNT 0x0020 // Delay Counter out +#define IRQM_ADC_SAMPLE_CNT 0x0040 // ADC Sample Counter +#define IRQM_DAC1_UCNT 0x0080 // DAC1 Update Counter +#define IRQM_DAC2_UCNT 0x0100 // DAC2 Update Counter +#define IRQM_UTC1 0x0200 // User TC1 out +#define IRQM_UTC1_INV 0x0400 // User TC1 out, inverted +#define IRQM_UTC2 0x0800 // User TC2 out +#define IRQM_DIGITAL_IT 0x1000 // Digital Interrupt +#define IRQM_EXTERNAL_IT 0x2000 // External Interrupt +#define IRQM_ETRIG_RISING 0x4000 // External Trigger rising-edge +#define IRQM_ETRIG_FALLING 0x8000 // External Trigger falling-edge + +// DMA Request Sources (LAS0) +#define DMAS_DISABLED 0x0 // DMA Disabled +#define DMAS_ADC_SCNT 0x1 // ADC Sample Counter +#define DMAS_DAC1_UCNT 0x2 // D/A1 Update Counter +#define DMAS_DAC2_UCNT 0x3 // D/A2 Update Counter +#define DMAS_UTC1 0x4 // User TC1 out +#define DMAS_ADFIFO_HALF_FULL 0x8 // A/D FIFO half full +#define DMAS_DAC1_FIFO_HALF_EMPTY 0x9 // D/A1 FIFO half empty +#define DMAS_DAC2_FIFO_HALF_EMPTY 0xA // D/A2 FIFO half empty + +// DMA Local Addresses (0x40000000+LAS1 offset) +#define DMALADDR_ADC 0x40000000 // A/D FIFO +#define DMALADDR_HDIN 0x40000004 // High Speed Digital Input FIFO +#define DMALADDR_DAC1 0x40000008 // D/A1 FIFO +#define DMALADDR_DAC2 0x4000000C // D/A2 FIFO + +// Port 0 compare modes (SetDIO0CompareMode) +#define DIO_MODE_EVENT 0 // Event Mode +#define DIO_MODE_MATCH 1 // Match Mode + +// Digital Table Enable (Port 1 disable) +#define DTBL_DISABLE 0 // Enable Digital Table +#define DTBL_ENABLE 1 // Disable Digital Table + +// Sampling Signal for High Speed Digital Input (SetHdinStart) +#define HDIN_SOFTWARE 0x0 // Software Trigger +#define HDIN_ADC 0x1 // A/D Conversion Signal +#define HDIN_UTC0 0x2 // User TC out 0 +#define HDIN_UTC1 0x3 // User TC out 1 +#define HDIN_UTC2 0x4 // User TC out 2 +#define HDIN_EPCLK 0x5 // External Pacer Clock +#define HDIN_ETRG 0x6 // External Trigger + +// Channel Gain Table / Channel Gain Latch +#define CSC_LATCH 0 // Channel Gain Latch mode +#define CSC_CGT 1 // Channel Gain Table mode + +// Channel Gain Table Pause Enable +#define CGT_PAUSE_DISABLE 0 // Channel Gain Table Pause Disable +#define CGT_PAUSE_ENABLE 1 // Channel Gain Table Pause Enable + +// DAC output type/range (p63) +#define AOUT_UNIP5 0 // 0..+5 Volt +#define AOUT_UNIP10 1 // 0..+10 Volt +#define AOUT_BIP5 2 // -5..+5 Volt +#define AOUT_BIP10 3 // -10..+10 Volt + +// Ghannel Gain Table field definitions (p61) +// Gain +#define GAIN1 0 +#define GAIN2 1 +#define GAIN4 2 +#define GAIN8 3 +#define GAIN16 4 +#define GAIN32 5 +#define GAIN64 6 +#define GAIN128 7 + +// Input range/polarity +#define AIN_BIP5 0 // -5..+5 Volt +#define AIN_BIP10 1 // -10..+10 Volt +#define AIN_UNIP10 2 // 0..+10 Volt + +// non referenced single ended select bit +#define NRSE_AGND 0 // AGND referenced SE input +#define NRSE_AINS 1 // AIN SENSE referenced SE input + +// single ended vs differential +#define GND_SE 0 // Single-Ended +#define GND_DIFF 1 // Differential