/** module_main.c (was: device.c) * * Main Kernel module functions for load/init and cleanup of the kernel driver * * Supports multiple ExpressMIMO cards * * Authors: Matthias Ihmig , 2012, 2013 * Riadh Ghaddab * Raymond Knopp * * Changelog: * 14.01.2013: removed remaining of BIGPHYS stuff and replaced with pci_alloc_consistent * 20.01.2013: added support for multiple cards * 24.01.2013: restructured interfaces & structures of how physical and virtual pointers are handled */ #include "openair_device.h" #include "defs.h" #include "vars.h" #include #include #include #include #include static void openair_cleanup(void); extern irqreturn_t openair_irq_handler(int irq, void *cookie); static int irq = 0; module_param(irq,int,S_IRUSR|S_IWUSR); static struct file_operations openair_fops = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) unlocked_ioctl:openair_device_ioctl, #else ioctl: openair_device_ioctl, #endif open: openair_device_open, release:openair_device_release, mmap: openair_device_mmap }; static int __init openair_init_module( void ) { int res = 0; unsigned int readback; unsigned int card, j; unsigned int vid,did; unsigned short vendor, subid; exmimo_id_t exmimo_id_tmp[MAX_CARDS]; //------------------------------------------------ // Find and enable ExpressMIMO PCI cards //------------------------------------------------ // card = 0; pdev[card] = pci_get_device(XILINX_VENDOR, XILINX_ID, NULL); if( pdev[card] ) { printk("[openair][INIT_MODULE][INFO]: openair card (ExpressMIMO) %d found, bus 0x%x, primary 0x%x, secondary 0x%x\n",card, pdev[card]->bus->number, pdev[card]->bus->primary,pdev[card]->bus->secondary); pci_read_config_word(pdev[card], PCI_SUBSYSTEM_ID, &subid); pci_read_config_word(pdev[card], PCI_SUBSYSTEM_VENDOR_ID, &vendor); exmimo_id_tmp[card].board_vendor = vendor; if ( exmimo_id_tmp[card].board_vendor == XILINX_VENDOR ) exmimo_id_tmp[card].board_vendor = EURECOM_VENDOR; // set default to EURECOM exmimo_id_tmp[card].board_exmimoversion = (subid >> 12) & 0x0F; if (exmimo_id_tmp[card].board_exmimoversion == 0) exmimo_id_tmp[card].board_exmimoversion = 1; // default (for old bitstreams) is ExpressMIMO-1 exmimo_id_tmp[card].board_hwrev = (subid >> 8) & 0x0F; exmimo_id_tmp[card].board_swrev = (subid ) & 0xFF; printk("[openair][INIT_MODULE][INFO]: card %d: ExpressMIMO-%i (HW Rev %i), Bitstream: %s, SW/Protocol Revision: 0x%02X\n", card, exmimo_id_tmp[card].board_exmimoversion, exmimo_id_tmp[card].board_hwrev, ( (exmimo_id_tmp[card].board_vendor == EURECOM_VENDOR) ? "Eurecom" : "Telecom Paristech"), exmimo_id_tmp[card].board_swrev ); card++; vid = XILINX_VENDOR; did = XILINX_ID; } else { printk("[openair][INIT_MODULE][INFO]: no card found, stopping.\n"); return -ENODEV; } // Now look for more cards on the same bus while (cardbus->number,pdev[card]->bus->primary,pdev[card]->bus->secondary); pci_read_config_word(pdev[card], PCI_SUBSYSTEM_ID, &subid); pci_read_config_word(pdev[card], PCI_SUBSYSTEM_VENDOR_ID, &vendor); exmimo_id_tmp[card].board_vendor = vendor; if ( exmimo_id_tmp[card].board_vendor == XILINX_VENDOR ) exmimo_id_tmp[card].board_vendor = EURECOM_VENDOR; // default (for old bitstreams) is EURECOM_VENDOR exmimo_id_tmp[card].board_exmimoversion = (subid >> 12) & 0x0F; if (exmimo_id_tmp[card].board_exmimoversion == 0) exmimo_id_tmp[card].board_exmimoversion = 1; // default (for old bitstreams) is ExpressMIMO-1 exmimo_id_tmp[card].board_hwrev = (subid >> 8) & 0x0F; exmimo_id_tmp[card].board_swrev = (subid ) & 0xFF; printk("[openair][INIT_MODULE][INFO]: card %d: ExpressMIMO-%i (HW Rev %i), Bitstream: %s, SW/Protocol Revision: 0x%02X\n", card, exmimo_id_tmp[card].board_exmimoversion, exmimo_id_tmp[card].board_hwrev, ( (exmimo_id_tmp[card].board_vendor == EURECOM_VENDOR) ? "Eurecom" : "Telecom Paristech"), exmimo_id_tmp[card].board_swrev ); card++; } else break; } // at least one device found, enable it number_of_cards = card; for (card=0; cardirq); openair_pci_device_enabled[card] = 1; } // Make the FPGA to a PCI master pci_set_master(pdev[card]); // set DMA mask if (!pci_set_dma_mask(pdev[card], DMA_BIT_MASK(32))) { if (pci_set_consistent_dma_mask(pdev[card], DMA_BIT_MASK(32))) { printk(KERN_INFO "[openair][INIT_MODULE]: Unable to obtain 32bit DMA for consistent allocations\n"); openair_cleanup(); return -EIO; } } if (pci_enable_pcie_error_reporting(pdev[card]) > 0) printk("[openair][INIT_MODULE][INFO]: Enabled PCIe error reporting\n"); else printk("[openair][INIT_MODULE][INFO]: Failed to enable PCIe error reporting\n"); pci_cleanup_aer_uncorrect_error_status(pdev[card]); mmio_start[card] = pci_resource_start(pdev[card], 0); // get start of BAR0 mmio_length[card] = pci_resource_len (pdev[card], 0); mmio_flags[card] = pci_resource_flags(pdev[card], 0); if (check_mem_region(mmio_start[card],256) < 0) { printk("[openair][INIT_MODULE][FATAL] : Cannot get memory region 0, aborting\n"); mmio_start[card] = 0; openair_cleanup(); return -EBUSY; } else printk("[openair][INIT_MODULE][INFO] : Reserving memory region 0 : mmio_start = 0x%x\n",(unsigned int)mmio_start[card]); request_mem_region(mmio_start[card], 256, "openair_rf"); bar[card] = pci_iomap( pdev[card], 0, mmio_length[card] ); // get virtual kernel address for BAR0 printk("[openair][INIT_MODULE][INFO]: BAR0 card %d = 0x%p\n", card, bar[card]); printk("[openair][INIT_MODULE][INFO]: Writing 0x%x to BAR0+0x1c (PCIBASEL)\n", 0x12345678); iowrite32( 0x12345678, (bar[card]+PCIE_PCIBASEL) ); udelay(100); readback = ioread32( bar[card]+PCIE_PCIBASEL ); if (readback != 0x12345678) { printk("[openair][INIT_MODULE][INFO]: Readback of FPGA register failed (%x)\n",readback); openair_cleanup(); return -EIO; } iowrite32((1<<8), bar[card] +PCIE_CONTROL0 ); // bit8=AHBPCIE_CTL0_SOFTRESET, but what is bit9 and bit10? udelay(1000); readback = ioread32( bar[card] +PCIE_CONTROL0); printk("CONTROL0 readback %x\n",readback); // allocating buffers if ( exmimo_memory_alloc( card ) ) { printk("[openair][MODULE][ERROR] exmimo_memory_alloc() failed!\n"); openair_cleanup(); return -ENOMEM; } exmimo_pci_kvirt[card].exmimo_id_ptr->board_vendor = exmimo_id_tmp[card].board_vendor; exmimo_pci_kvirt[card].exmimo_id_ptr->board_exmimoversion = exmimo_id_tmp[card].board_exmimoversion; exmimo_pci_kvirt[card].exmimo_id_ptr->board_hwrev = exmimo_id_tmp[card].board_hwrev; exmimo_pci_kvirt[card].exmimo_id_ptr->board_swrev = exmimo_id_tmp[card].board_swrev; if (irq!=0) printk("[OPENAIR][SCHED][INIT] card %d: Trying to get IRQ %d\n", card,irq); else printk("[OPENAIR][SCHED][INIT] card %d: Trying to get IRQ %d\n", card, pdev[card]->irq); openair_irq_enabled[card] = 0; // #ifdef CONFIG_PREEMPT_RT doesn't work -> fix misconfigured header files? #if 1 if (irq!=0){ if ( (res = request_irq(irq, openair_irq_handler, IRQF_SHARED , "openair_rf", pdev[card] )) == 0) { openair_irq_enabled[card] = 1; } else { printk("[EXMIMO][SCHED][INIT] Cannot get IRQ %d for HW, error: %d\n", irq, res); openair_cleanup(); return -EBUSY; }} else { if ( (res = request_irq(pdev[card]->irq, openair_irq_handler, IRQF_SHARED , "openair_rf", pdev[card] )) == 0) { openair_irq_enabled[card] = 1; } else { printk("[EXMIMO][SCHED][INIT] Cannot get IRQ %d for HW, error: %d\n", pdev[card]->irq, res); openair_cleanup(); return -EBUSY; }} #else printk("Warning: didn't request IRQ for PREEMPT_REALTIME\n"); #endif } // for (i=0; iirq, pdev[card] ); openair_irq_enabled[card] = 0; } exmimo_firmware_cleanup( card ); if ( bar[card] ) { printk("unmap bar[%d] %p\n", card, bar[card]); iounmap((void *)bar[card]); } if ( mmio_start[card] ) { printk("release mem[%d] %x\n", card, (unsigned int)mmio_start[card]); release_mem_region(mmio_start[card],256); } if ( openair_pci_device_enabled[card] ) { printk("pci_disable_device %i\n", card); pci_disable_device( pdev[card] ); } } } MODULE_AUTHOR ("Raymond KNOPP , Matthias IHMIG , Florian KALTENBERGER "); MODULE_DESCRIPTION ("openair ExpressMIMO/ExpressMIMO2 driver"); MODULE_LICENSE ("GPL"); module_init (openair_init_module); module_exit (openair_cleanup_module);