Files
openairinterface5g/targets/ARCH/EXMIMO/DRIVER/exmimo3/module_main.c
T
2014-08-14 11:39:48 +00:00

363 lines
14 KiB
C
Executable File

/*******************************************************************************
OpenAirInterface
Copyright(c) 1999 - 2014 Eurecom
OpenAirInterface 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 3 of the License, or
(at your option) any later version.
OpenAirInterface 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 OpenAirInterface.The full GNU General Public License is
included in this distribution in the file called "COPYING". If not,
see <http://www.gnu.org/licenses/>.
Contact Information
OpenAirInterface Admin: openair_admin@eurecom.fr
OpenAirInterface Tech : openair_tech@eurecom.fr
OpenAirInterface Dev : openair4g-devel@eurecom.fr
Address : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.
*******************************************************************************/
/** 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 <matthias.ihmig@mytum.de>, 2012, 2013
* Riadh Ghaddab <riadh.ghaddab@eurecom.fr>
* Raymond Knopp <raymond.knopp@eurecom.fr>
*
* 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 <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/aer.h>
#include <linux/pci_regs.h>
#include <linux/delay.h>
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] )
{
// This print does not work for 64 bit kernels
// 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 (card<MAX_CARDS)
{
pdev[card] = pci_get_device(vid,did, pdev[card-1]);
if(pdev[card])
{
// This print does not work for 64 bit kernels
// printk("[openair][INIT_MODULE][INFO]: openair card %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; // 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; card<number_of_cards; card++)
{
if( pci_enable_device(pdev[card]) )
{
printk("[openair][INIT_MODULE][INFO]: Could not enable PCI card device %d\n",card);
openair_cleanup();
return -ENODEV;
}
else {
printk("[openair][INIT_MODULE][INFO]: *** CARD DEVICE %d (pdev=%p) ENABLED, irq %d\n",card,pdev[card],irq);
printk("[openair][INIT_MODULE][INFO]: *** CARD DEVICE %d (pdev=%p) ENABLED, irq %d\n",card,pdev[card],pdev[card]->irq);
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; i<number_of_cards; i++)
//------------------------------------------------
// Register the device in /dev
//------------------------------------------------
//
major = openair_MAJOR;
if( (res = register_chrdev(major, "openair", &openair_fops )) < 0)
{
printk("[openair][INIT_MODULE][ERROR]: can't register char device driver, major : %d, error: %d\n", major, res);
for (j=0; j<=number_of_cards; j++)
release_mem_region(mmio_start[j],256);
return -EBUSY;
} else {
printk("[openair][INIT_MODULE][INFO]: char device driver registered major : %d\n", major);
openair_chrdev_registered = 1;
}
printk("[openair][MODULE][INFO] Initializing Leon (EXMIMO_PCIE_INIT) on all cards...\n");
for (card=0; card<number_of_cards; card++)
exmimo_firmware_init( card );
printk("[openair][MODULE][INFO] Done module init\n");
return 0;
}
static void __exit openair_cleanup_module(void)
{
//int card;
printk("[openair][CLEANUP MODULE]\n");
// stop any ongoing acquisition
//for (card = 0; card < number_of_cards; card++)
// exmimo_send_pccmd(card, EXMIMO_STOP);
openair_cleanup();
}
static void openair_cleanup(void)
{
int card;
if ( openair_chrdev_registered )
unregister_chrdev(major,"openair");
openair_chrdev_registered = 0;
for (card=0; card<number_of_cards; card++)
{
// unregister interrupt
if ( openair_irq_enabled[card] ) {
printk("[openair][CLEANUP] disabling interrupt card %d\n", card);
if (irq!=0)
free_irq(irq, pdev[card] );
else
free_irq( pdev[card]->irq, 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 <raymond.knopp@eurecom.fr>, Matthias IHMIG <matthias.ihmig@eurecom.fr>, Florian KALTENBERGER <florian.kaltenberger@eurecom.fr>, Riadh GHADDAB <riadh.ghaddab@eurecom.fr>");
MODULE_DESCRIPTION ("openair ExpressMIMO/ExpressMIMO3 driver");
MODULE_LICENSE ("GPL");
module_init (openair_init_module);
module_exit (openair_cleanup_module);