📄 gsc_hpdi.c
字号:
{ buffer_offset = 0; buffer_index++; } DEBUG_PRINT(" desc %i\n", i ); DEBUG_PRINT(" start addr virt 0x%p, phys 0x%lx\n", priv(dev)->desc_dio_buffer[ i ], (unsigned long)priv(dev)->dma_desc[ i ].pci_start_addr ); DEBUG_PRINT(" next 0x%lx\n", (unsigned long)priv(dev)->dma_desc[ i ].next ); } priv(dev)->num_dma_descriptors = i; // fix last descriptor to point back to first priv(dev)->dma_desc[ i - 1 ].next = priv(dev)->dma_desc_phys_addr | next_bits; DEBUG_PRINT(" desc %i next fixup 0x%lx\n", i - 1, (unsigned long) priv(dev)->dma_desc[ i - 1 ].next ); priv(dev)->block_size = transfer_size; return transfer_size;}static int hpdi_attach(comedi_device *dev, comedi_devconfig *it){ struct pci_dev* pcidev; int i; int retval; printk( "comedi%d: gsc_hpdi\n", dev->minor ); if( alloc_private( dev, sizeof( hpdi_private ) ) < 0 ) return -ENOMEM; pcidev = NULL; for( i = 0; i < num_boards() && dev->board_ptr == NULL; i++ ) { do { pcidev = pci_find_subsys( PCI_VENDOR_ID_PLX, hpdi_boards[ i ].device_id, PCI_VENDOR_ID_PLX, hpdi_boards[ i ].subdevice_id, pcidev ); // was a particular bus/slot requested? if( it->options[0] || it->options[1] ) { // are we on the wrong bus/slot? if( pcidev->bus->number != it->options[0] || PCI_SLOT( pcidev->devfn ) != it->options[1] ) continue; } if( pcidev ) { dev->board_ptr = hpdi_boards + i; break; } }while( pcidev != NULL ); } if( dev->board_ptr == NULL ) { printk("gsc_hpdi: no hpdi card found\n"); return -EIO; } printk("gsc_hpdi: found %s on bus %i, slot %i\n", board( dev )->name, pcidev->bus->number, PCI_SLOT(pcidev->devfn)); if( pci_enable_device( pcidev ) ) return -EIO; pci_set_master( pcidev ); priv(dev)->hw_dev = pcidev; //Initialize dev->board_name dev->board_name = board(dev)->name; if( pci_request_regions( pcidev, driver_hpdi.driver_name ) ) { /* Couldn't allocate io space */ printk(KERN_WARNING " failed to allocate io memory\n"); return -EIO; } priv(dev)->plx9080_phys_iobase = pci_resource_start(pcidev, PLX9080_BADDRINDEX); priv(dev)->hpdi_phys_iobase = pci_resource_start(pcidev, HPDI_BADDRINDEX); // remap, won't work with 2.0 kernels but who cares priv(dev)->plx9080_iobase = (unsigned long) ioremap( priv(dev)->plx9080_phys_iobase, pci_resource_len( pcidev, PLX9080_BADDRINDEX ) ); priv(dev)->hpdi_iobase = (unsigned long) ioremap( priv(dev)->hpdi_phys_iobase, pci_resource_len( pcidev, HPDI_BADDRINDEX ) ); DEBUG_PRINT(" plx9080 remapped to 0x%lx\n", priv(dev)->plx9080_iobase); DEBUG_PRINT(" hpdi remapped to 0x%lx\n", priv(dev)->hpdi_iobase); // get irq if( comedi_request_irq( pcidev->irq, handle_interrupt, SA_SHIRQ, driver_hpdi.driver_name, dev ) ) { printk( " unable to allocate irq %d\n", pcidev->irq ); return -EINVAL; } dev->irq = pcidev->irq; printk(" irq %i\n", dev->irq); init_plx9080(dev); // alocate pci dma buffers for( i = 0; i < NUM_DMA_BUFFERS; i++ ) { priv(dev)->dio_buffer[ i ] = pci_alloc_consistent( priv(dev)->hw_dev, DMA_BUFFER_SIZE, &priv(dev)->dio_buffer_phys_addr[ i ] ); DEBUG_PRINT( "dio_buffer at virt 0x%p, phys 0x%lx\n", priv(dev)->dio_buffer[ i ], (unsigned long) priv(dev)->dio_buffer_phys_addr[ i ]); } // allocate dma descriptors priv(dev)->dma_desc = pci_alloc_consistent( priv(dev)->hw_dev, sizeof( struct plx_dma_desc ) * NUM_DMA_DESCRIPTORS, &priv(dev)->dma_desc_phys_addr); if( priv(dev)->dma_desc_phys_addr & 0xf ) { printk(" dma descriptors not quad-word aligned (bug)\n"); return -EIO; } retval = setup_dma_descriptors( dev, 0x1000 ); if( retval < 0 ) return retval; retval = setup_subdevices( dev ); if( retval < 0 ) return retval; return init_hpdi( dev );}static int hpdi_detach(comedi_device *dev){ unsigned int i; printk( "comedi%d: gsc_hpdi: remove\n", dev->minor ); if( dev->irq ) comedi_free_irq( dev->irq, dev ); if( priv(dev) ) { if( priv(dev)->hw_dev ) { if(priv(dev)->plx9080_iobase) { disable_plx_interrupts( dev ); iounmap( (void*) priv(dev)->plx9080_iobase ); } if( priv(dev)->hpdi_iobase ) iounmap((void*)priv(dev)->hpdi_iobase); if( priv(dev)->plx9080_phys_iobase || priv(dev)->hpdi_phys_iobase ) pci_release_regions( priv(dev)->hw_dev ); // free pci dma buffers for( i = 0; i < NUM_DMA_BUFFERS; i++ ) { if( priv(dev)->dio_buffer[ i ] ) pci_free_consistent( priv(dev)->hw_dev, DMA_BUFFER_SIZE, priv(dev)->dio_buffer[ i ], priv(dev)->dio_buffer_phys_addr[ i ] ); } // free dma descriptors if( priv(dev)->dma_desc ) pci_free_consistent( priv(dev)->hw_dev, sizeof( struct plx_dma_desc ) * NUM_DMA_DESCRIPTORS, priv(dev)->dma_desc, priv(dev)->dma_desc_phys_addr ); pci_disable_device( priv(dev)->hw_dev ); } } return 0;}static int dio_config_block_size( comedi_device *dev, lsampl_t *data ){ unsigned int requested_block_size; int retval; requested_block_size = data[ 1 ]; retval = setup_dma_descriptors( dev, requested_block_size ); if( retval < 0 ) return retval; data[ 1 ] = retval; return 2;}static int di_cmd_test(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd){ int err = 0; int tmp; int i; /* 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_EXT; if( !cmd->scan_begin_src || tmp != cmd->scan_begin_src ) err++; tmp = cmd->convert_src; cmd->convert_src &= TRIG_NOW; 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 */ // uniqueness check 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->chanlist_len ) { cmd->chanlist_len = 32; err++; } if( cmd->scan_end_arg != cmd->chanlist_len ) { cmd->scan_end_arg = cmd->chanlist_len; err++; } switch(cmd->stop_src) { case TRIG_COUNT: if(!cmd->stop_arg) { cmd->stop_arg = 1; err++; } break; case TRIG_NONE: if(cmd->stop_arg != 0) { cmd->stop_arg = 0; err++; } break; default: break; } if(err) return 3; /* step 4: fix up any arguments */ if(err) return 4; if( cmd->chanlist ) { for( i = 1; i < cmd->chanlist_len; i++ ) { if( CR_CHAN( cmd->chanlist[ i ] ) != i ) { // XXX could support 8 channels or 16 channels comedi_error( dev, "chanlist must be channels 0 to 31 in order" ); err++; break; } } } if(err) return 5; return 0;}static int hpdi_cmd_test( comedi_device *dev, comedi_subdevice *s, comedi_cmd *cmd ){ if( priv(dev)->dio_config_output ) { return -EINVAL; }else return di_cmd_test( dev, s, cmd );}static inline void hpdi_writel( comedi_device *dev, uint32_t bits, unsigned int offset ){ writel( bits | priv(dev)->bits[ offset / sizeof( uint32_t ) ], priv(dev)->hpdi_iobase + offset );}static int di_cmd(comedi_device *dev,comedi_subdevice *s){ uint32_t bits; unsigned long flags; comedi_async *async = s->async; comedi_cmd *cmd = &async->cmd; hpdi_writel( dev, RX_FIFO_RESET_BIT, BOARD_CONTROL_REG ); DEBUG_PRINT( "hpdi: in di_cmd\n"); abort_dma(dev, 0); priv(dev)->dma_desc_index = 0; // give location of first dma descriptor bits = priv(dev)->dma_desc_phys_addr | PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI; writel( bits, priv(dev)->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG ); // spinlock for plx dma control/status reg comedi_spin_lock_irqsave( &dev->spinlock, flags ); // enable dma transfer writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT | PLX_CLEAR_DMA_INTR_BIT, priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG); comedi_spin_unlock_irqrestore( &dev->spinlock, flags ); if( cmd->stop_src == TRIG_COUNT ) priv(dev)->dio_count = cmd->stop_arg; else priv(dev)->dio_count = 1; // clear over/under run status flags writel( RX_UNDERRUN_BIT | RX_OVERRUN_BIT, priv(dev)->hpdi_iobase + BOARD_STATUS_REG ); // enable interrupts writel( intr_bit( RX_FULL_INTR ), priv(dev)->hpdi_iobase + INTERRUPT_CONTROL_REG ); DEBUG_PRINT( "hpdi: starting rx\n"); hpdi_writel( dev, RX_ENABLE_BIT, BOARD_CONTROL_REG ); return 0;}static int hpdi_cmd( comedi_device *dev, comedi_subdevice *s ){ if( priv(dev)->dio_config_output ) { return -EINVAL; }else return di_cmd( dev, s );}static void drain_dma_buffers(comedi_device *dev, unsigned int channel){ comedi_async *async = dev->read_subdev->async; uint32_t next_transfer_addr; int j; int num_samples = 0; unsigned long pci_addr_reg; if(channel) pci_addr_reg = priv(dev)->plx9080_iobase + PLX_DMA1_PCI_ADDRESS_REG; else pci_addr_reg = priv(dev)->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG; // loop until we have read all the full buffers j = 0; for(next_transfer_addr = readl(pci_addr_reg); (next_transfer_addr < priv(dev)->dma_desc[ priv(dev)->dma_desc_index ].pci_start_addr || next_transfer_addr >= priv(dev)->dma_desc[ priv(dev)->dma_desc_index ].pci_start_addr + priv(dev)->block_size ) && j < priv(dev)->num_dma_descriptors; j++ ) { // transfer data from dma buffer to comedi buffer num_samples = priv(dev)->block_size / sizeof( uint32_t ); if( async->cmd.stop_src == TRIG_COUNT ) { if(num_samples > priv(dev)->dio_count) num_samples = priv(dev)->dio_count; priv(dev)->dio_count -= num_samples; } cfc_write_array_to_buffer( dev->read_subdev, priv(dev)->desc_dio_buffer[ priv(dev)->dma_desc_index ], num_samples * sizeof( uint32_t ) ); priv(dev)->dma_desc_index++; priv(dev)->dma_desc_index %= priv(dev)->num_dma_descriptors; DEBUG_PRINT("next desc addr 0x%lx\n", (unsigned long) priv(dev)->dma_desc[ priv(dev)->dma_desc_index ].next ); DEBUG_PRINT("pci addr reg 0x%x\n", next_transfer_addr); } // XXX check for buffer overrun somehow}static void handle_interrupt(int irq, void *d, struct pt_regs *regs){ comedi_device *dev = d; comedi_subdevice *s = dev->read_subdev; comedi_async *async = s->async; uint32_t hpdi_intr_status, hpdi_board_status; uint32_t plx_status; uint32_t plx_bits; uint8_t dma0_status, dma1_status; unsigned long flags; plx_status = readl( priv(dev)->plx9080_iobase + PLX_INTRCS_REG ); hpdi_intr_status = readl( priv(dev)->hpdi_iobase + INTERRUPT_STATUS_REG ); hpdi_board_status = readl( priv(dev)->hpdi_iobase + BOARD_STATUS_REG ); async->events = 0; if( hpdi_intr_status ) { DEBUG_PRINT("hpdi: intr status 0x%x, ", hpdi_intr_status); writel( hpdi_intr_status, priv(dev)->hpdi_iobase + INTERRUPT_STATUS_REG ); }else if( ( plx_status & ( ICS_DMA0_A | ICS_DMA1_A | ICS_LIA ) ) == 0 ) { return; } // spin lock makes sure noone else changes plx dma control reg comedi_spin_lock_irqsave( &dev->spinlock, flags ); dma0_status = readb(priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG); if(plx_status & ICS_DMA0_A) { // dma chan 0 interrupt writeb((dma0_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT, priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG); DEBUG_PRINT("dma0 status 0x%x\n", dma0_status); if(dma0_status & PLX_DMA_EN_BIT) { drain_dma_buffers(dev, 0); } DEBUG_PRINT(" cleared dma ch0 interrupt\n"); } comedi_spin_unlock_irqrestore( &dev->spinlock, flags ); // spin lock makes sure noone else changes plx dma control reg comedi_spin_lock_irqsave( &dev->spinlock, flags ); dma1_status = readb(priv(dev)->plx9080_iobase + PLX_DMA1_CS_REG); if(plx_status & ICS_DMA1_A) // XXX { // dma chan 1 interrupt writeb((dma1_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT, priv(dev)->plx9080_iobase + PLX_DMA1_CS_REG); DEBUG_PRINT("dma1 status 0x%x\n", dma1_status); DEBUG_PRINT(" cleared dma ch1 interrupt\n"); } comedi_spin_unlock_irqrestore( &dev->spinlock, flags ); // clear possible plx9080 interrupt sources if(plx_status & ICS_LDIA) { // clear local doorbell interrupt plx_bits = readl(priv(dev)->plx9080_iobase + PLX_DBR_OUT_REG); writel(plx_bits, priv(dev)->plx9080_iobase + PLX_DBR_OUT_REG); DEBUG_PRINT(" cleared local doorbell bits 0x%x\n", plx_bits); } if( hpdi_board_status & RX_OVERRUN_BIT ) { comedi_error(dev, "rx fifo overrun"); async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; DEBUG_PRINT( "dma0_status 0x%x\n", (int)readb(priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG)); } if( hpdi_board_status & RX_UNDERRUN_BIT ) { comedi_error(dev, "rx fifo underrun"); async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; } if( priv(dev)->dio_count == 0 ) async->events |= COMEDI_CB_EOA; DEBUG_PRINT("board status 0x%x, ", hpdi_board_status); DEBUG_PRINT("plx status 0x%x\n", plx_status); if( async->events ) DEBUG_PRINT( " events 0x%x\n", async->events ); cfc_handle_events( dev, s ); return;}void abort_dma( comedi_device *dev, unsigned int channel ){ unsigned long flags; // spinlock for plx dma control/status reg comedi_spin_lock_irqsave( &dev->spinlock, flags ); plx9080_abort_dma( priv( dev )->plx9080_iobase, channel ); comedi_spin_unlock_irqrestore( &dev->spinlock, flags );}static int hpdi_cancel( comedi_device *dev, comedi_subdevice *s ){ hpdi_writel( dev, 0, BOARD_CONTROL_REG ); writel( 0, priv(dev)->hpdi_iobase + INTERRUPT_CONTROL_REG ); abort_dma(dev, 0); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -