📄 gsc_hpdi.c
字号:
/* gsc_hpdi.c This is a driver for the General Standards Corporation High Speed Parallel Digital Interface rs485 boards. Author: Frank Mori Hess <fmhess@users.sourceforge.net> Copyright (C) 2003 Coherent Imaging Systems COMEDI - Linux Control and Measurement Device Interface Copyright (C) 1997-8 David A. Schleef <ds@schleef.org> 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: gsc_hpdi.oDescription: Driver for the General Standards Corporation High Speed Parallel Digital Interface rs485 boards.Author: Frank Mori Hess <fmhess@users.sourceforge.net>Status: only receive mode works, transmit not supportedUpdated: 2003-02-20Devices: [General Standards Corporation] PCI-HPDI32 (gsc_hpdi), PMC-HPDI32Configuration options: [0] - PCI bus of device (optional) [1] - PCI slot of device (optional)There are some additional hpdi models available from GSC for whichsupport could be added to this driver.*/#include <linux/comedidev.h>#include <linux/delay.h>#include <linux/pci.h>#include "plx9080.h"#include "comedi_fc.h"static int hpdi_attach( comedi_device *dev, comedi_devconfig *it );static int hpdi_detach( comedi_device *dev );void abort_dma( comedi_device *dev, unsigned int channel );static int hpdi_cmd( comedi_device *dev, comedi_subdevice *s );static int hpdi_cmd_test( comedi_device *dev, comedi_subdevice *s, comedi_cmd *cmd );static int hpdi_cancel( comedi_device *dev, comedi_subdevice *s );static void handle_interrupt(int irq, void *d, struct pt_regs *regs);static int dio_config_block_size( comedi_device *dev, lsampl_t *data );#undef HPDI_DEBUG // disable debugging messages//#define HPDI_DEBUG // enable debugging code#ifdef HPDI_DEBUG#define DEBUG_PRINT(format, args...) rt_printk(format , ## args )#else#define DEBUG_PRINT(format, args...)#endif#define TIMER_BASE 50 // 20MHz master clock#define DMA_BUFFER_SIZE 0x10000#define NUM_DMA_BUFFERS 4#define NUM_DMA_DESCRIPTORS 256// indices of base address regionsenum base_address_regions{ PLX9080_BADDRINDEX = 0, HPDI_BADDRINDEX = 2,};enum hpdi_registers{ FIRMWARE_REV_REG = 0x0, BOARD_CONTROL_REG = 0x4, BOARD_STATUS_REG = 0x8, TX_PROG_ALMOST_REG = 0xc, RX_PROG_ALMOST_REG = 0x10, FEATURES_REG = 0x14, FIFO_REG = 0x18, TX_STATUS_COUNT_REG = 0x1c, TX_LINE_VALID_COUNT_REG = 0x20, TX_LINE_INVALID_COUNT_REG = 0x24, RX_STATUS_COUNT_REG = 0x28, RX_LINE_COUNT_REG = 0x2c, INTERRUPT_CONTROL_REG = 0x30, INTERRUPT_STATUS_REG = 0x34, TX_CLOCK_DIVIDER_REG = 0x38, TX_FIFO_SIZE_REG = 0x40, RX_FIFO_SIZE_REG = 0x44, TX_FIFO_WORDS_REG = 0x48, RX_FIFO_WORDS_REG = 0x4c, INTERRUPT_EDGE_LEVEL_REG = 0x50, INTERRUPT_POLARITY_REG = 0x54,};int command_channel_valid( unsigned int channel ){ if( channel == 0 || channel > 6 ) { rt_printk( "gsc_hpdi: bug! invalid cable command channel\n"); return 0; } return 1;}// bit definitionsenum firmware_revision_bits{ FEATURES_REG_PRESENT_BIT = 0x8000,};int firmware_revision( uint32_t fwr_bits ){ return fwr_bits & 0xff;}int pcb_revision( uint32_t fwr_bits ){ return ( fwr_bits >> 8 ) & 0xff;}int hpdi_subid( uint32_t fwr_bits ){ return ( fwr_bits >> 16 ) & 0xff;}enum board_control_bits{ BOARD_RESET_BIT = 0x1, /* wait 10usec before accessing fifos */ TX_FIFO_RESET_BIT = 0x2, RX_FIFO_RESET_BIT = 0x4, TX_ENABLE_BIT = 0x10, RX_ENABLE_BIT = 0x20, DEMAND_DMA_DIRECTION_TX_BIT = 0x40, /* for channel 0, channel 1 can only transmit (when present) */ LINE_VALID_ON_STATUS_VALID_BIT = 0x80, START_TX_BIT = 0x10, CABLE_THROTTLE_ENABLE_BIT = 0x20, TEST_MODE_ENABLE_BIT = 0x80000000,};uint32_t command_discrete_output_bits( unsigned int channel, int output, int output_value ){ uint32_t bits = 0; if( command_channel_valid( channel ) == 0 ) return 0; if( output ) { bits |= 0x1 << ( 16 + channel ); if( output_value ) bits |= 0x1 << ( 24 + channel ); }else bits |= 0x1 << ( 24 + channel ); return bits;}enum board_status_bits{ COMMAND_LINE_STATUS_MASK = 0x7f, TX_IN_PROGRESS_BIT = 0x80, TX_NOT_EMPTY_BIT = 0x100, TX_NOT_ALMOST_EMPTY_BIT = 0x200, TX_NOT_ALMOST_FULL_BIT = 0x400, TX_NOT_FULL_BIT = 0x800, RX_NOT_EMPTY_BIT = 0x1000, RX_NOT_ALMOST_EMPTY_BIT = 0x2000, RX_NOT_ALMOST_FULL_BIT = 0x4000, RX_NOT_FULL_BIT = 0x8000, BOARD_JUMPER0_INSTALLED_BIT = 0x10000, BOARD_JUMPER1_INSTALLED_BIT = 0x20000, TX_OVERRUN_BIT = 0x200000, RX_UNDERRUN_BIT = 0x400000, RX_OVERRUN_BIT = 0x800000,};uint32_t almost_full_bits( unsigned int num_words ){// XXX need to add or subtract one? return ( num_words << 16 ) & 0xff0000;}uint32_t almost_empty_bits( unsigned int num_words ){ return num_words & 0xffff;}unsigned int almost_full_num_words( uint32_t bits ){// XXX need to add or subtract one? return ( bits >> 16 ) & 0xffff;}unsigned int almost_empty_num_words( uint32_t bits ){ return bits & 0xffff;}enum features_bits{ FIFO_SIZE_PRESENT_BIT = 0x1, FIFO_WORDS_PRESENT_BIT = 0x2, LEVEL_EDGE_INTERRUPTS_PRESENT_BIT = 0x4, GPIO_SUPPORTED_BIT = 0x8, PLX_DMA_CH1_SUPPORTED_BIT = 0x10, OVERRUN_UNDERRUN_SUPPORTED_BIT = 0x20,};enum interrupt_sources{ FRAME_VALID_START_INTR = 0, FRAME_VALID_END_INTR = 1, TX_FIFO_EMPTY_INTR = 8, TX_FIFO_ALMOST_EMPTY_INTR = 9, TX_FIFO_ALMOST_FULL_INTR = 10, TX_FIFO_FULL_INTR = 11, RX_EMPTY_INTR = 12, RX_ALMOST_EMPTY_INTR = 13, RX_ALMOST_FULL_INTR = 14, RX_FULL_INTR = 15,};int command_intr_source( unsigned int channel ){ if( command_channel_valid( channel ) == 0 ) channel = 1; return channel + 1;}uint32_t intr_bit( int interrupt_source ){ return 0x1 << interrupt_source;}uint32_t tx_clock_divisor_bits( unsigned int divisor ){ return divisor & 0xff;}unsigned int fifo_size( uint32_t fifo_size_bits ){ return fifo_size_bits & 0xfffff;}unsigned int fifo_words( uint32_t fifo_words_bits ){ return fifo_words_bits & 0xfffff;}uint32_t intr_edge_bit( int interrupt_source ){ return 0x1 << interrupt_source;}uint32_t intr_active_high_bit( int interrupt_source ){ return 0x1 << interrupt_source;}typedef struct{ char *name; int device_id; // pci device id int subdevice_id; // pci subdevice id} hpdi_board;static const hpdi_board hpdi_boards[] ={ { name: "pci-hpdi32", device_id: PCI_DEVICE_ID_PLX_9080, subdevice_id: 0x2400, },#if 0 { name: "pxi-hpdi32", device_id: 0x9656, subdevice_id: 0x2705, },#endif};static inline unsigned int num_boards( void ){ return sizeof( hpdi_boards ) / sizeof( hpdi_board );}static struct pci_device_id hpdi_pci_table[] __devinitdata = { { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9080, PCI_VENDOR_ID_PLX, 0x2400, 0, 0, 0 }, { 0 }};MODULE_DEVICE_TABLE(pci, hpdi_pci_table);static inline hpdi_board* board( const comedi_device *dev ){ return ( hpdi_board * ) dev->board_ptr;}typedef struct{ struct pci_dev *hw_dev; // pointer to board's pci_dev struct // base addresses (physical) unsigned long plx9080_phys_iobase; unsigned long hpdi_phys_iobase; // base addresses (ioremapped) unsigned long plx9080_iobase; unsigned long hpdi_iobase; uint32_t *dio_buffer[ NUM_DMA_BUFFERS ]; // dma buffers dma_addr_t dio_buffer_phys_addr[ NUM_DMA_BUFFERS ]; // physical addresses of dma buffers struct plx_dma_desc *dma_desc; // array of dma descriptors read by plx9080, allocated to get proper alignment dma_addr_t dma_desc_phys_addr; // physical address of dma descriptor array unsigned int num_dma_descriptors; uint32_t *desc_dio_buffer[ NUM_DMA_DESCRIPTORS ]; // pointer to start of buffers indexed by descriptor volatile unsigned int dma_desc_index; // index of the dma descriptor that is currently being used unsigned int tx_fifo_size; unsigned int rx_fifo_size; volatile unsigned long dio_count; volatile uint32_t bits[ 24 ]; // software copies of values written to hpdi registers volatile unsigned int block_size; // number of bytes at which to generate COMEDI_CB_BLOCK events unsigned dio_config_output : 1;} hpdi_private;static inline hpdi_private* priv( comedi_device *dev ){ return dev->private;}static comedi_driver driver_hpdi ={ driver_name: "gsc_hpdi", module: THIS_MODULE, attach: hpdi_attach, detach: hpdi_detach,};COMEDI_INITCLEANUP( driver_hpdi );static int dio_config_insn( comedi_device *dev,comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){ switch( data[ 0 ] ) { case COMEDI_OUTPUT: if( insn->n != 1 ) return -EINVAL; priv(dev)->dio_config_output = 1; return 1; break; case COMEDI_INPUT: if( insn->n != 1 ) return -EINVAL; priv(dev)->dio_config_output = 0; return 1; break; case INSN_CONFIG_BLOCK_SIZE: if( insn->n != 2 ) return -EINVAL; return dio_config_block_size( dev, data ); break; default: break; } return -EINVAL;}static void disable_plx_interrupts( comedi_device *dev ){ writel( 0, priv(dev)->plx9080_iobase + PLX_INTRCS_REG );}// initialize plx9080 chipstatic void init_plx9080(comedi_device *dev){ uint32_t bits; unsigned long plx_iobase = priv(dev)->plx9080_iobase; // plx9080 dump DEBUG_PRINT(" plx interrupt status 0x%x\n", readl(plx_iobase + PLX_INTRCS_REG)); DEBUG_PRINT(" plx id bits 0x%x\n", readl(plx_iobase + PLX_ID_REG)); DEBUG_PRINT(" plx control reg 0x%x\n", readl(priv(dev)->plx9080_iobase + PLX_CONTROL_REG)); DEBUG_PRINT(" plx revision 0x%x\n", readl(plx_iobase + PLX_REVISION_REG)); DEBUG_PRINT(" plx dma channel 0 mode 0x%x\n", readl(plx_iobase + PLX_DMA0_MODE_REG)); DEBUG_PRINT(" plx dma channel 1 mode 0x%x\n", readl(plx_iobase + PLX_DMA1_MODE_REG)); DEBUG_PRINT(" plx dma channel 0 pci address 0x%x\n", readl(plx_iobase + PLX_DMA0_PCI_ADDRESS_REG)); DEBUG_PRINT(" plx dma channel 0 local address 0x%x\n", readl(plx_iobase + PLX_DMA0_LOCAL_ADDRESS_REG)); DEBUG_PRINT(" plx dma channel 0 transfer size 0x%x\n", readl(plx_iobase + PLX_DMA0_TRANSFER_SIZE_REG)); DEBUG_PRINT(" plx dma channel 0 descriptor 0x%x\n", readl(plx_iobase + PLX_DMA0_DESCRIPTOR_REG)); DEBUG_PRINT(" plx dma channel 0 command status 0x%x\n", readb(plx_iobase + PLX_DMA0_CS_REG)); DEBUG_PRINT(" plx dma channel 0 threshold 0x%x\n", readl(plx_iobase + PLX_DMA0_THRESHOLD_REG)); disable_plx_interrupts( dev ); abort_dma(dev, 0); abort_dma(dev, 1); // configure dma0 mode bits = 0; // enable ready input bits |= PLX_DMA_EN_READYIN_BIT; // enable dma chaining bits |= PLX_EN_CHAIN_BIT; // enable interrupt on dma done (probably don't need this, since chain never finishes) bits |= PLX_EN_DMA_DONE_INTR_BIT; // don't increment local address during transfers (we are transferring from a fixed fifo register) bits |= PLX_LOCAL_ADDR_CONST_BIT; // route dma interrupt to pci bus bits |= PLX_DMA_INTR_PCI_BIT; // enable demand mode bits |= PLX_DEMAND_MODE_BIT; // enable local burst mode bits |= PLX_DMA_LOCAL_BURST_EN_BIT; bits |= PLX_LOCAL_BUS_32_WIDE_BITS; writel(bits, plx_iobase + PLX_DMA0_MODE_REG);}/* Allocate and initialize the subdevice structures. */static int setup_subdevices(comedi_device *dev){ comedi_subdevice *s; if( alloc_subdevices( dev, 1 ) < 0 ) return -ENOMEM; s = dev->subdevices + 0; /* analog input subdevice */ dev->read_subdev = s;/* dev->write_subdev = s; */ s->type = COMEDI_SUBD_DIO; s->subdev_flags = SDF_READABLE | SDF_WRITEABLE | SDF_LSAMPL; s->n_chan = 32; s->len_chanlist = 32; s->maxdata = 1; s->range_table = &range_digital; s->insn_config = dio_config_insn; s->do_cmd = hpdi_cmd; s->do_cmdtest = hpdi_cmd_test; s->cancel = hpdi_cancel; return 0;}static int init_hpdi( comedi_device *dev ){ uint32_t plx_intcsr_bits; writel( BOARD_RESET_BIT, priv(dev)->hpdi_iobase + BOARD_CONTROL_REG ); comedi_udelay( 10 ); writel( almost_empty_bits( 32 ) | almost_full_bits( 32 ), priv(dev)->hpdi_iobase + RX_PROG_ALMOST_REG ); writel( almost_empty_bits( 32 ) | almost_full_bits( 32 ), priv(dev)->hpdi_iobase + TX_PROG_ALMOST_REG ); priv(dev)->tx_fifo_size = fifo_size( readl( priv(dev)->hpdi_iobase + TX_FIFO_SIZE_REG ) ); priv(dev)->rx_fifo_size = fifo_size( readl( priv(dev)->hpdi_iobase + RX_FIFO_SIZE_REG ) ); // enable interrupts plx_intcsr_bits = ICS_AERR | ICS_PERR | ICS_PIE | ICS_PLIE | ICS_PAIE | ICS_LIE | ICS_DMA0_E; writel( plx_intcsr_bits, priv(dev)->plx9080_iobase + PLX_INTRCS_REG ); writel( 0, priv(dev)->hpdi_iobase + INTERRUPT_CONTROL_REG ); return 0;}// setup dma descriptors so a link completes every 'transfer_size' bytesstatic int setup_dma_descriptors( comedi_device *dev, unsigned int transfer_size ){ unsigned int buffer_index, buffer_offset; uint32_t next_bits = PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI; unsigned int i; if( transfer_size > DMA_BUFFER_SIZE ) transfer_size = DMA_BUFFER_SIZE; transfer_size -= transfer_size % sizeof( uint32_t ); if( transfer_size == 0 ) return -1; DEBUG_PRINT(" transfer_size %i\n", transfer_size ); DEBUG_PRINT(" descriptors at 0x%lx\n", (unsigned long) priv(dev)->dma_desc_phys_addr ); buffer_offset = 0; buffer_index = 0; for( i = 0; i < NUM_DMA_DESCRIPTORS && buffer_index < NUM_DMA_BUFFERS; i++ ) { priv(dev)->dma_desc[ i ].pci_start_addr = priv(dev)->dio_buffer_phys_addr[ buffer_index ] + buffer_offset; priv(dev)->dma_desc[ i ].local_start_addr = FIFO_REG; priv(dev)->dma_desc[ i ].transfer_size = transfer_size; priv(dev)->dma_desc[ i ].next = ( priv(dev)->dma_desc_phys_addr + ( i + 1 ) * sizeof( priv(dev)->dma_desc[ 0 ] ) ) | next_bits; priv(dev)->desc_dio_buffer[ i ] = priv(dev)->dio_buffer[ buffer_index ] + ( buffer_offset / sizeof( uint32_t ) ); buffer_offset += transfer_size; if( transfer_size + buffer_offset > DMA_BUFFER_SIZE )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -