⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gsc_hpdi.c

📁 rtlinux-3.2-pre3.tar.bz2 rtlinux3.2-pre3的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*    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 + -