Commit 51bcb55c authored by ghaddab's avatar ghaddab

new driver for exmimo3

git-svn-id: http://svn.eurecom.fr/openair4G/trunk@5596 818b1a75-f10b-46b9-bf7c-635c3b92a50f
parent 03f1e6fd
CCC = gcc
KERNEL_MAIN_TYPE=$(shell echo `uname -r | cut -d. -f-2 | tr "." "_"`)
export KERNEL_MAIN_TYPE
SUBVERSION=$(shell echo `grep '^SUBLEVEL =' /usr/src/linux/Makefile | sed -e 's, ,,g' | sed -e 's/SUBLEVEL=//'`)
IS_KERNEL_SUBVERSION_GREATER_THAN_20=$(shell if [ $(SUBVERSION) -ge 20 ] ; then echo true ; fi)
EXTRA_CFLAGS = -ggdb -D__KERNEL__ -DMODULE -D_LOOSE_KERNEL_NAMES -I/lib/modules/$(shell uname -r)/build/include -I/lib/modules/$(shell uname -r)/build/include/asm/mach-default -include /lib/modules/$(shell uname -r)/build/include/linux/autoconf.h
EXTRA_CFLAGS += -I$(PWD)/../../DEFS
obj-m += openair_rf.o
openair_rf-objs += module_main.o irq.o fileops.o exmimo_fw.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
rm -rf *.o
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
echo "Hint: this may not work if /usr/src/linux is not set to currently booted kernel."
echo "Try 'make' instead."
make -C /usr/src/linux V=1 M=`pwd`
#ifndef __DEFS_H__
#define __DEFS_H__
#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>
#include <asm/segment.h>
#include <asm/page.h>
#include <asm/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/slab.h>
//#include <linux/config.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#ifdef KERNEL2_6
#include <linux/slab.h>
#endif
#include "openair_device.h"
#include "linux/moduleparam.h"
/*------------------------------------------------*/
/* Prototypes */
/*------------------------------------------------*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
long openair_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
#else
int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd, unsigned long arg);
#endif
int openair_device_open (struct inode *inode,struct file *filp);
int openair_device_release (struct inode *inode,struct file *filp);
int openair_device_mmap (struct file *filp, struct vm_area_struct *vma);
int exmimo_memory_alloc(int card);
int exmimo_firmware_init(int card);
int exmimo_firmware_cleanup(int card_id);
int exmimo_send_pccmd(int card_id, unsigned int cmd);
#endif
#!/bin/sh
# Re-Enumerate FPGA card
# After resetting or powering up the card or after reloading the FPGA bitstream,
# run this script to re-enumerate the PCIe device in Linux.
# You may need to change the device path. Check lspci output for this.
# You need to run this as root:
# sudo ./do_reenumerate_expressmimo.sh
# Matthias <ihmig@eurecom.fr>, 2013
rmmod openair_rf
echo 1 > /sys/bus/pci/devices/0000\:01\:00.0/remove
echo 1 > /sys/bus/pci/devices/0000\:05\:00.0/remove
echo 1 > /sys/bus/pci/rescan
insmod openair_rf.ko
rmmod openair_rf && sleep 1 ; insmod openair_rf.ko
/** exmimo_fw.c
*
* Initialization routines for
* Shared Memory management for ExMIMO and PC(kernel) data exchange
* (memory allocation, pointer management)
*
* There is now one pci_alloc_consistent for each RX and TX buffer
* and one for all structures, including pointers, printk, firmware_dump and config
*
* Authors: Matthias Ihmig <matthias.ihmig@mytum.de>, 2012, 2013
* Riadh Ghaddab <riadh.ghaddab@eurecom.fr>
* Raymond Knopp <raymond.knopp@eurecom.fr>
*
* Changelog:
* 24.01.2013: added memory management functions for bigshm, lots of cleanups
*/
#include <linux/delay.h>
#include "extern.h"
#include "defs.h"
#include "pcie_interface.h"
/*****************************************************
* Private functions within this file
*/
void mem_SetPageReserved(void *kvirt_addr, unsigned int size_pages)
{
size_t temp_size = size_pages << PAGE_SHIFT;
void *adr = kvirt_addr;
while (temp_size > 0)
{
SetPageReserved( virt_to_page(adr) );
adr += PAGE_SIZE;
temp_size -= PAGE_SIZE;
}
}
void mem_ClearPageReserved(void *kvirt_addr, unsigned int size_pages)
{
size_t temp_size = size_pages << PAGE_SHIFT;
void *adr = kvirt_addr;
while (temp_size > 0) {
ClearPageReserved( virt_to_page(adr) );
adr += PAGE_SIZE;
temp_size -= PAGE_SIZE;
}
}
// allocates big shared memory, to be used for structures, pointers, etc.
// bigshm allocs a single larger memory block for shared structures and pointers, one per card, except: ADC+DAC buffers!
// returns -1 on error, 0 on success
int bigshm_init(int card)
{
printk("[openair][module] calling pci_alloc_consistent for card %d, bigshm (size: %lu*%lu bytes)...\n", card, BIGSHM_SIZE_PAGES, PAGE_SIZE);
if ( sizeof(dma_addr_t) != 4)
printk("!!! WARNING: sizeof (dma_addr_t) = %lu! Only 32bit mode (= 4) (also: no PAE) is supported at this time!\n", sizeof(dma_addr_t));
if ( bigshm_head[card] == NULL )
bigshm_head[card] = pci_alloc_consistent( pdev[card], BIGSHM_SIZE_PAGES<<PAGE_SHIFT, &bigshm_head_phys[card] );
if (bigshm_head[card] == NULL) {
printk("[openair][MODULE][ERROR] Cannot Allocate Memory (%lu bytes) for shared data (bigshm)\n", BIGSHM_SIZE_PAGES<<PAGE_SHIFT);
return -ENOMEM;
}
else {
printk("[openair][MODULE][INFO] Bigshm at %p (phys %x)\n", bigshm_head[card], (unsigned int) bigshm_head_phys[card]);
bigshm_currentptr[card] = bigshm_head[card];
mem_SetPageReserved( bigshm_head[card], BIGSHM_SIZE_PAGES);
memset(bigshm_head[card], 0, BIGSHM_SIZE_PAGES << PAGE_SHIFT);
}
return 0;
}
// use this instead of pci_alloc_consistent to assign memory from the bigshm block
// return kernel virtual pointer and sets physical DMA address in dma_handle
void *bigshm_assign( int card, size_t size_bytes, dma_addr_t *dma_handle_ptr )
{
void *ret;
size_t size = size_bytes;
//size = (size-1) + 4 - ( (size-1) % 4); // round up to keep addresses aligned to DWs
//size = (size-1) + 16 - ( (size-1) % 16); // round up to keep addresses aligned to 4 DWs
// round up to the next 64 DW (workaround for current bug in DMA on ExMIMO2)
size = (size-1) + 256 - ( (size-1) % 256);
if ( (bigshm_currentptr[card] - bigshm_head[card]) > (BIGSHM_SIZE_PAGES<<PAGE_SHIFT) -size ) {
printk("Not enough memory in bigshm! Make BIGSHM_SIZE_PAGES bigger!\n");
return NULL;
}
*dma_handle_ptr = bigshm_head_phys[card] + ( (dma_addr_t)bigshm_currentptr[card] - (dma_addr_t)bigshm_head[card] );
ret = bigshm_currentptr[card];
bigshm_currentptr[card] = (char *)bigshm_currentptr[card] + size;
//printk("bigshm_assign: size %d, virt = %p, dma = %x, bigshm_phys=%x, _current=%p, _head=%p\n",
// size, ret, *dma_handle_ptr, bigshm_head_phys[card], bigshm_currentptr[card], bigshm_head[card]);
return ret;
}
// assign shared memory between card an PC for data exchange: interface structure with pointers,
// firmware- and printk buffers, configuration structure
// returns -1 on error, 0 on success
int exmimo_assign_shm_vars(int card_id)
{
int j;
if (p_exmimo_pci_phys[card_id] == NULL)
{
p_exmimo_pci_phys[card_id] = (exmimo_pci_interface_bot_t *) bigshm_assign( card_id,
sizeof(exmimo_pci_interface_bot_t),
&pphys_exmimo_pci_phys[card_id]);
printk("Intializing EXMIMO interface support (exmimo_pci_bot at %p, phys %x, size %lu bytes)\n",p_exmimo_pci_phys[card_id],(unsigned int)pphys_exmimo_pci_phys[card_id], sizeof(exmimo_pci_interface_bot_t));
exmimo_pci_kvirt[card_id].firmware_block_ptr = (char *) bigshm_assign( card_id,
MAX_FIRMWARE_BLOCK_SIZE_B,
(dma_addr_t*)&(p_exmimo_pci_phys[card_id]->firmware_block_ptr));
printk("firmware_code_block_ptr : %p (phys = %08x)\n", exmimo_pci_kvirt[card_id].firmware_block_ptr, p_exmimo_pci_phys[card_id]->firmware_block_ptr);
exmimo_pci_kvirt[card_id].printk_buffer_ptr = (char *) bigshm_assign( card_id,
MAX_PRINTK_BUFFER_B,
(dma_addr_t*)&(p_exmimo_pci_phys[card_id]->printk_buffer_ptr));
printk("printk_buffer_ptr : %p (phys = %08x)\n", exmimo_pci_kvirt[card_id].printk_buffer_ptr, p_exmimo_pci_phys[card_id]->printk_buffer_ptr);
exmimo_pci_kvirt[card_id].exmimo_config_ptr = (exmimo_config_t *) bigshm_assign( card_id,
sizeof(exmimo_config_t),
(dma_addr_t*)&(p_exmimo_pci_phys[card_id]->exmimo_config_ptr));
printk("exmimo_config_ptr : %p (phys = %08x)\n", exmimo_pci_kvirt[card_id].exmimo_config_ptr, p_exmimo_pci_phys[card_id]->exmimo_config_ptr);
exmimo_pci_kvirt[card_id].exmimo_id_ptr = (exmimo_id_t *) bigshm_assign( card_id,
sizeof(exmimo_id_t),
(dma_addr_t*)&(p_exmimo_pci_phys[card_id]->exmimo_id_ptr));
printk("exmimo_id_ptr : %p (phys = %08x)\n", exmimo_pci_kvirt[card_id].exmimo_id_ptr, p_exmimo_pci_phys[card_id]->exmimo_id_ptr);
if ( p_exmimo_pci_phys[card_id] == NULL || exmimo_pci_kvirt[card_id].firmware_block_ptr == NULL
|| exmimo_pci_kvirt[card_id].printk_buffer_ptr == NULL || exmimo_pci_kvirt[card_id].exmimo_config_ptr == NULL
|| exmimo_pci_kvirt[card_id].exmimo_id_ptr == NULL )
return -1;
for (j=0; j<MAX_ANTENNAS; j++)
{
// size 4*1 should be sufficient, but just to make sure we can also use DMA of size 4DW as fallback
exmimo_pci_kvirt[card_id].rxcnt_ptr[j] = (uint32_t *) bigshm_assign( card_id, 4*4,
(dma_addr_t*)&(p_exmimo_pci_phys[card_id]->rxcnt_ptr[j]) );
printk("exmimo_pci_kvirt[%d].rxcnt_ptr[%d] = %p (phys = %08x)\n", card_id, j, exmimo_pci_kvirt[card_id].rxcnt_ptr[j], p_exmimo_pci_phys[card_id]->rxcnt_ptr[j]);
exmimo_pci_kvirt[card_id].txcnt_ptr[j] = (uint32_t *) bigshm_assign( card_id, 4*4,
(dma_addr_t*)&(p_exmimo_pci_phys[card_id]->txcnt_ptr[j]) );
printk("exmimo_pci_kvirt[%d].txcnt_ptr[%d] = %p (phys = %08x)\n", card_id, j, exmimo_pci_kvirt[card_id].txcnt_ptr[j], p_exmimo_pci_phys[card_id]->txcnt_ptr[j]);
if ( exmimo_pci_kvirt[card_id].rxcnt_ptr[j] == NULL || exmimo_pci_kvirt[card_id].txcnt_ptr[j] == NULL)
return -1;
}
}
return 0;
}
// allocate memory for RX and TX chains for all antennas
// returns -1 on error, 0 on success
int exmimo_allocate_rx_tx_buffers(int card_id)
{
size_t size;
int j,i;
dma_addr_t dma_addr_dummy;
// Round up to the next PAGE_SIZE (typ. 4096 bytes)
size = (ADAC_BUFFERSZ_PERCHAN_B >> PAGE_SHIFT) + 1;
size <<= PAGE_SHIFT;
for (j=0; j<MAX_ANTENNAS; j++)
{
exmimo_pci_kvirt[card_id].adc_head[j] = (uint32_t *)pci_alloc_consistent( pdev[card_id], size, &dma_addr_dummy);
p_exmimo_pci_phys[card_id]->adc_head[j]=((uint32_t*)&dma_addr_dummy)[0];
printk("exmimo_pci_kvirt[%d].adc_head[%d] = %p (phys = %08x)\n", card_id, j, exmimo_pci_kvirt[card_id].adc_head[j], p_exmimo_pci_phys[card_id]->adc_head[j]);
if ( exmimo_pci_kvirt[card_id].adc_head[j] == NULL)
return -1;
// printk("[MODULE MAIN0]Phys address TX[0] : (%8lx)\n",p_exmimo_pci_phys[0]->dac_head[ openair_mmap_getAntTX(2) ]);
mem_SetPageReserved( exmimo_pci_kvirt[card_id].adc_head[j], size >> PAGE_SHIFT );
memset( exmimo_pci_kvirt[card_id].adc_head[j], 0x10+j, size);
// printk("[MODULE MAIN1]Phys address TX[0] : (%8lx)\n",p_exmimo_pci_phys[0]->dac_head[ openair_mmap_getAntTX(2) ]);
exmimo_pci_kvirt[card_id].dac_head[j] = (uint32_t *)pci_alloc_consistent( pdev[card_id], size,&dma_addr_dummy);
p_exmimo_pci_phys[card_id]->dac_head[j]=((uint32_t*)&dma_addr_dummy)[0];
// printk("exmimo_pci_kvirt[%d].dac_head[%d] = %p (phys = %08x)\n", card_id, j, exmimo_pci_kvirt[card_id].dac_head[j], p_exmimo_pci_phys[card_id]->dac_head[j]);
if ( exmimo_pci_kvirt[card_id].dac_head[j] == NULL)
return -ENOMEM;
// printk("[MODULE MAIN2]Phys address TX[0] : (%8lx)\n",p_exmimo_pci_phys[0]->dac_head[ openair_mmap_getAntTX(2) ]);
mem_SetPageReserved( exmimo_pci_kvirt[card_id].dac_head[j], size >> PAGE_SHIFT );
memset( exmimo_pci_kvirt[card_id].dac_head[j], 0x20+j, size);
// printk("[MODULE MAIN3]Phys address TX[0] : (%8lx)\n",p_exmimo_pci_phys[0]->dac_head[ openair_mmap_getAntTX(2) ]);
}
return 0;
}
/*********************************************
* Public functions ExpressMIMO Interface
*/
/* Allocates buffer and assigns pointers
*
* return 0 on success
*/
int exmimo_memory_alloc(int card)
{
int i;
if ( bigshm_init( card ) ) {
printk("exmimo_memory_alloc(): bigshm_init failed for card %d.\n", card);
return -ENOMEM;
}
if ( exmimo_assign_shm_vars( card ) ) {
printk("exmimo_memory_alloc(): exmimo_assign_shm_vars failed to assign enough shared memory for all variables and structures for card %i!\n", card);
return -ENOMEM;
}
if ( exmimo_allocate_rx_tx_buffers( card ) ) {
printk("exmimo_memory_alloc(): exmimo_allocate_rx_tx_buffers() failed to allocate enough memory for RX and TX buffers for card %i!\n", card);
return -ENOMEM;
}
return 0;
}
/*
* Copies pointer to Leon
*/
int exmimo_firmware_init(int card)
{
// put DMA pointer to exmimo_pci_interface_bot into LEON register
iowrite32( pphys_exmimo_pci_phys[card], (bar[card]+PCIE_PCIBASEL) ); // lower 32bit of address
iowrite32( 0, (bar[card]+PCIE_PCIBASEH) ); // higher 32bit of address
iowrite32(pphys_exmimo_pci_phys[card]-> p_exmimo_pci_phys[card_id]->adc_head[j]);
if (exmimo_pci_kvirt[card].exmimo_id_ptr->board_swrev == BOARD_SWREV_CMDREGISTERS)
iowrite32( EXMIMO_CONTROL2_COOKIE, bar[card]+ PCIE_CONTROL2);
//printk("exmimo_firmware_init(): initializing Leon (EXMIMO_PCIE_INIT)...\n");
exmimo_send_pccmd(card, EXMIMO_PCIE_INIT);
return 0;
}
/*
* Free memory on unloading the kernel driver
*/
int exmimo_firmware_cleanup(int card)
{
size_t size;
int j;
// free exmimo_allocate_rx_tx_buffers
size = (ADAC_BUFFERSZ_PERCHAN_B >> PAGE_SHIFT) + 1;
size <<= PAGE_SHIFT;
for (j=0; j<MAX_ANTENNAS; j++)
{
if ( exmimo_pci_kvirt[card].adc_head[j] ) {
mem_ClearPageReserved( exmimo_pci_kvirt[card].adc_head[j], size >> PAGE_SHIFT );
pci_free_consistent( pdev[card], size, exmimo_pci_kvirt[card].adc_head[j], p_exmimo_pci_phys[card]->adc_head[j] );
}
if ( exmimo_pci_kvirt[card].dac_head[j] ) {
mem_ClearPageReserved( exmimo_pci_kvirt[card].dac_head[j], size >> PAGE_SHIFT );
pci_free_consistent( pdev[card], size, exmimo_pci_kvirt[card].dac_head[j], p_exmimo_pci_phys[card]->dac_head[j] );
}
}
if ( bigshm_head[card] ) {
printk("free bigshm_head[%d] pdev %p, size %lu, head %p, phys %x\n", card, pdev[card], BIGSHM_SIZE_PAGES<<PAGE_SHIFT, bigshm_head[card], (unsigned int)bigshm_head_phys[card]);
mem_ClearPageReserved( bigshm_head[card], BIGSHM_SIZE_PAGES );
pci_free_consistent( pdev[card], BIGSHM_SIZE_PAGES<<PAGE_SHIFT, bigshm_head[card], bigshm_head_phys[card]);
}
return 0;
}
/*
* Send command to Leon and wait until command is completed
*/
int exmimo_send_pccmd(int card_id, unsigned int cmd)
{
unsigned int val;
unsigned int cnt=0;
//printk("Sending command to ExpressMIMO (card %d) : %x\n",card_id, cmd);
iowrite32(cmd,(bar[card_id]+PCIE_CONTROL1));
// printk("Readback of control1 %x\n",ioread32(bar[0]+PCIE_CONTROL1));
switch(cmd)
{
case EXMIMO_CONFIG :
break;
case EXMIMO_PCIE_INIT :
break;
}
// workaround for ExMIMO1&2: no command ack -> sleep
while (cnt<100 && ( ioread32(bar[card_id]+PCIE_CONTROL1) != EXMIMO_NOP )) {
//printk("ctrl0: %08x, ctrl1: %08x, ctrl2: %08x, status: %08x\n", ioread32(bar[card_id]+PCIE_CONTROL0), ioread32(bar[card_id]+PCIE_CONTROL1), ioread32(bar[card_id]+PCIE_CONTROL2), ioread32(bar[card_id]+PCIE_STATUS));
msleep(100);
cnt++;
}
if (cnt==100)
printk("exmimo_send_pccmd error: Timeout: no EXMIMO_NOP received within 10sec for card%d, pccmd=%x\n", card_id, cmd);
//printk("Ctrl0: %08x, ctrl1: %08x, ctrl2: %08x, status: %08x\n", ioread32(bar[card_id]+PCIE_CONTROL0), ioread32(bar[card_id]+PCIE_CONTROL1), ioread32(bar[card_id]+PCIE_CONTROL2), ioread32(bar[card_id]+PCIE_STATUS));
return(0);
}
#ifndef __EXTERN_H__
#define __EXTERN_H__
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#ifdef KERNEL2_6
#include <linux/slab.h>
#endif
#include "defs.h"
#include "pcie_interface.h"
extern char number_of_cards;
extern int major;
extern struct pci_dev *pdev[MAX_CARDS];
extern void __iomem *bar[MAX_CARDS];
extern void *bigshm_head[MAX_CARDS];
extern void *bigshm_currentptr[MAX_CARDS];
extern dma_addr_t bigshm_head_phys[MAX_CARDS];
extern dma_addr_t pphys_exmimo_pci_phys[MAX_CARDS];
extern exmimo_pci_interface_bot_t *p_exmimo_pci_phys[MAX_CARDS];
extern exmimo_pci_interface_bot_virtual_t exmimo_pci_kvirt[MAX_CARDS];
#endif // __EXTERN_H__
/** fileops.c
*
* Device IOCTL File Operations on character device /dev/openair0
*
* 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
*/
#include <linux/delay.h>
#include "openair_device.h"
#include "defs.h"
#include "extern.h"
#include "pcie_interface.h"
#define invert4(x) { \
unsigned int ltmp; \
ltmp=x; x=((ltmp & 0xff)<<24) | ((ltmp & 0xff00)<<8) | \
((ltmp & 0xff0000)>>8) | ((ltmp & 0xff000000)>>24); \
}
extern int get_frame_done;
int is_card_num_invalid(int card)
{
if (card<0 || card>=number_of_cards)
{
printk("[openair][IOCTL]: ERROR: received invalid card number (%d)!\n", card);
return -1;
}
else
return 0;
}
//-----------------------------------------------------------------------------
int openair_device_open (struct inode *inode,struct file *filp)
{
//printk("[openair][MODULE] openair_open()\n");
return 0;
}
//-----------------------------------------------------------------------------
int openair_device_release (struct inode *inode,struct file *filp)
{
// printk("[openair][MODULE] openair_release(), MODE = %d\n",openair_daq_vars.mode);
return 0;
}
//-----------------------------------------------------------------------------
int openair_device_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long phys;
unsigned long start = (unsigned long)vma->vm_start;
unsigned long size = (unsigned long)(vma->vm_end-vma->vm_start);
unsigned long maxsize;
unsigned int memblock_ind;
unsigned int card;
memblock_ind = openair_mmap_getMemBlock( vma->vm_pgoff );
card = openair_mmap_getCard( vma->vm_pgoff );
/*printk("[openair][MMAP] called (start %lx, end %lx, pg_off %lx, size %lx)\n",
vma->vm_start,
vma->vm_end,
vma->vm_pgoff,
size);
*/
vma->vm_pgoff = 0;
// not supported by 64 bit kernels
//vma->vm_flags |= VM_RESERVED;
vma->vm_flags |= VM_IO;
if ( is_card_num_invalid(card) )
return -EINVAL;
if (memblock_ind == openair_mmap_BIGSHM)
{
// map a buffer from bigshm
maxsize = BIGSHM_SIZE_PAGES<<PAGE_SHIFT;
if ( size > maxsize) {
printk("[openair][MMAP][ERROR] Trying to map more than %d bytes (req size=%d)\n",
(unsigned int)(BIGSHM_SIZE_PAGES<<PAGE_SHIFT), (unsigned int)size);
return -EINVAL;
}
phys = bigshm_head_phys[card];
}
else if ( (memblock_ind & 1) == 1)
{
// mmap a RX buffer
maxsize = ADAC_BUFFERSZ_PERCHAN_B;
if ( size > maxsize) {
printk("[openair][MMAP][ERROR] Trying to map more than %d bytes (req size=%d)\n",
(unsigned int)(ADAC_BUFFERSZ_PERCHAN_B), (unsigned int)size);
return -EINVAL;
}
phys = p_exmimo_pci_phys[card]->adc_head[ openair_mmap_getAntRX(memblock_ind) ];
}
else
{
// mmap a TX buffer
maxsize = ADAC_BUFFERSZ_PERCHAN_B;
if ( size > maxsize) {
printk("[openair][MMAP][ERROR] Trying to map more than %d bytes (%d)\n",
(unsigned int)(ADAC_BUFFERSZ_PERCHAN_B), (unsigned int)size);
return -EINVAL;
}
phys = p_exmimo_pci_phys[card]->dac_head[ openair_mmap_getAntTX(memblock_ind) ];
}
if (0)
printk("[openair][MMAP] card%d: map phys (%08lx) at start %lx, end %lx, pg_off %lx, size %lx\n",
card,
phys,
vma->vm_start,
vma->vm_end,
vma->vm_pgoff,
size);
/* loop through all the physical pages in the buffer */
/* Remember this won't work for vmalloc()d memory ! */
if (remap_pfn_range(vma,
start,
phys>>PAGE_SHIFT,
vma->vm_end-vma->vm_start,
vma->vm_page_prot))
{
printk("[openair][MMAP] ERROR EAGAIN\n");
return -EAGAIN;
}
return 0;
}
//-----------------------------------------------------------------------------
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
long openair_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
#else
int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd, unsigned long arg)
#endif
{
/* arg is not meaningful if no arg is passed in user space */
//-----------------------------------------------------------------------------
int i, c;
int tmp;
static unsigned int update_firmware_command;
static unsigned int update_firmware_address;
static unsigned int update_firmware_length;
static unsigned int* update_firmware_kbuffer;
static unsigned int* __user update_firmware_ubuffer;
static unsigned int update_firmware_start_address;
static unsigned int update_firmware_stack_pointer;
static unsigned int update_firmware_bss_address;
static unsigned int update_firmware_bss_size;
unsigned int *fw_block;
unsigned int sparc_tmp_0;
unsigned int sparc_tmp_1;
static unsigned int lendian_length;
unsigned int get_frame_cnt=0;
switch(cmd)
{
case openair_STOP:
printk("[openair][IOCTL] openair_STOP(card%d)\n", (int)arg);
if ( is_card_num_invalid((int)arg) )
return -EINVAL;
exmimo_send_pccmd((int)arg, EXMIMO_STOP);
break;
case openair_STOP_WITHOUT_RESET:
printk("[openair][IOCTL] openair_STOP_WITHOUT_RESET(card%d)\n", (int)arg);
if ( is_card_num_invalid((int)arg) )
return -EINVAL;
exmimo_send_pccmd((int)arg, EXMIMO_STOP_WITHOUT_RESET);
break;
case openair_GET_FRAME:
get_frame_cnt=0;
get_frame_done = 0;
printk("[openair][IOCTL] : openair_GET_FRAME: calling exmimo_send_pccmd(%d, EXMIMO_GET_FRAME)\n", (int)arg);
if ( is_card_num_invalid((int)arg) )
return -EINVAL;
exmimo_send_pccmd((int)arg, EXMIMO_GET_FRAME);
while (get_frame_cnt<10 && !get_frame_done) {
msleep(10);
get_frame_cnt++;
}
if (get_frame_cnt==200)
printk("[openair][IOCTL] : Get frame error: no IRQ received within 2000ms.\n");
get_frame_done = 0;
break;
case openair_GET_BIGSHMTOPS_KVIRT:
//printk("[openair][IOCTL] : openair_GET_BIGSHMTOPS_KVIRT (0x%p)[0] = %p[0] (bigshm_head) for 0..3 (sizeof %d) \n", (void *)arg, bigshm_head[0], sizeof(bigshm_head));
copy_to_user((void *)arg, bigshm_head, sizeof(bigshm_head));
break;
case openair_GET_PCI_INTERFACE_BOTS_KVIRT:
//printk("[openair][IOCTL] : openair_GET_PCI_INTERFACE_BOTS_KVIRT: copying exmimo_pci_kvirt(@%8p) to %lx (sizeof %d)\n", &exmimo_pci_kvirt[0], arg, sizeof(exmimo_pci_kvirt));
copy_to_user((void *)arg, exmimo_pci_kvirt, sizeof(exmimo_pci_kvirt));
break;
case openair_GET_NUM_DETECTED_CARDS:
//printk("[openair][IOCTL] : openair_GET_NUM_DETECTED_CARDS: *(0x%p) = %d\n", (void *)arg, number_of_cards);
copy_to_user((void *)arg, &number_of_cards, sizeof(number_of_cards));
break;
case openair_DUMP_CONFIG:
//printk("[openair][IOCTL] openair_DUMP_CONFIG(%d)\n", (int)arg);
if ( is_card_num_invalid((int)arg) ) {
printk("[openair][IOCTL] openair_DUMP_CONFIG: Invalid card number %d.\n", (int)arg);
return -EINVAL;
}
printk("[openair][IOCTL] : openair_DUMP_CONFIG(%d): exmimo_pci_kvirt[%d].exmimo_config_ptr = %p (phys %08x)\n",
(int)arg, (int)arg, exmimo_pci_kvirt[(int)arg].exmimo_config_ptr, p_exmimo_pci_phys[(int)arg]->exmimo_config_ptr);
/*printk("EXMIMO_CONFIG: freq0 %d Hz, freq1 %d Hz, freqtx0 %d Hz, freqtx1 %d Hz, \nRX gain0 %d dB, RX Gain1 %d dB\n",
exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rf_freq_rx[0],
exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rf_freq_rx[1],
exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rf_freq_tx[0],
exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rf_freq_tx[1],
exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rx_gain[0][0],
exmimo_pci_kvirt[(int)arg].exmimo_config_ptr->rf.rx_gain[1][0]);
*/
exmimo_send_pccmd((int)arg, EXMIMO_CONFIG);
break;
case openair_START_RT_ACQUISITION:
printk("[openair][IOCTL] openair_START_TX_SIG(%d): send_pccmd(EXMIMO_START_RT_ACQUISITION).\n", (int) arg);
if ( is_card_num_invalid((int)arg) )
return -EINVAL;
exmimo_send_pccmd((int)arg, EXMIMO_START_RT_ACQUISITION);
break;
case openair_UPDATE_FIRMWARE:
printk("[openair][IOCTL] openair_UPDATE_FIRMWARE\n");
/***************************************************
* Updating the firmware of Cardbus-MIMO-1 or ExpressMIMO SoC *
***************************************************/
/* 1st argument of this ioctl indicates the action to perform among these:
- Transfer a block of data at a specified address (given as the 2nd argument)
and for a specified length (given as the 3rd argument, in number of 32-bit words).
The USER-SPACE address where to find the block of data is given as the 4th
argument.
- Ask the Leon processor to clear the .bss section. In this case, the base
address of section .bss is given as the 2nd argument, and its size is
given as the 3rd one.
- Ask the Leon processor to jump at a specified address (given as the 2nd
argument, most oftenly expected to be the top address of Ins, Scratch Pad
Ram), after having set the stack pointer (given as the 3rd argument).
For the openair_UPDATE_FIRMWARE ioctl, we perform a partial infinite loop
while acknowledging the PCI irq from Leon software: the max number of loop
is yielded by preprocessor constant MAX_IOCTL_ACK_CNT. This avoids handing
the kernel with an infinite polling loop. An exception is the case of clearing
the bss: it takes time to Leon3 to perform this operation, so we poll te
acknowledge with no limit */
#define MAX_IOCTL_ACK_CNT 500
update_firmware_command = *((unsigned int*)arg);
switch (update_firmware_command)
{
case UPDATE_FIRMWARE_TRANSFER_BLOCK:
update_firmware_address = ((unsigned int*)arg)[1];
update_firmware_length = ((unsigned int*)arg)[2];
update_firmware_ubuffer = (unsigned int*)((unsigned int*)arg)[3];
update_firmware_kbuffer = (unsigned int*)kmalloc(update_firmware_length * 4 /* 4 because kmalloc expects bytes */,
GFP_KERNEL);
if (!update_firmware_kbuffer) {
printk("[openair][IOCTL] Could NOT allocate %u bytes from kernel memory (kmalloc failed).\n", lendian_length * 4);
return -1;
break;
}
// update all cards at the same time
for (c=0; c<number_of_cards; c++)
{
fw_block = (unsigned int *)exmimo_pci_kvirt[c].firmware_block_ptr;
/* Copy the data block from user space */
fw_block[0] = update_firmware_address;
fw_block[1] = update_firmware_length;
//printk("copy_from_user %p => %p (pci) => fw[0]=fw_addr=%08x (ahb), fw[1]=fw_length=%d DW\n",update_firmware_ubuffer,&fw_block[16],update_firmware_address,update_firmware_length);
tmp = copy_from_user(update_firmware_kbuffer,
update_firmware_ubuffer, /* from */
update_firmware_length * 4 /* in bytes */
);
if (tmp) {
printk("[openair][IOCTL] Could NOT copy all data from user-space to kernel-space (%d bytes remained uncopied).\n", tmp);
return -1;
}
// pci_map_single(pdev[0],(void*)fw_block, update_firmware_length*4,PCI_DMA_BIDIRECTIONAL);
for (i=0;i<update_firmware_length;i++)
{
fw_block[32+i] = ((unsigned int *)update_firmware_kbuffer)[i];
// Endian flipping is done in user-space so undo it
invert4(fw_block[32+i]);
}
exmimo_send_pccmd(c, EXMIMO_FW_INIT);
printk("[openair][IOCTL] card%d: ok %u DW copied to address 0x%08x (fw_block_ptr %p)\n",
c, fw_block[1], fw_block[0],fw_block);
}
kfree(update_firmware_kbuffer);
/*
for (i=0; i<update_firmware_length;i++) {
printk("%08x ", fw_block[32+i]);
if ((i % 8) == 7)
printk("\n");
}*/
break;
case UPDATE_FIRMWARE_CLEAR_BSS:
update_firmware_bss_address = ((unsigned int*)arg)[1];
update_firmware_bss_size = ((unsigned int*)arg)[2];
sparc_tmp_0 = update_firmware_bss_address;
sparc_tmp_1 = update_firmware_bss_size;
printk("[openair][IOCTL] ok asked Leon to clear .bss (addr 0x%08x, size %d bytes)\n", sparc_tmp_0, sparc_tmp_1);
// update all cards at the same time
for (c=0; c<number_of_cards; c++)
{
fw_block = (unsigned int *)exmimo_pci_kvirt[c].firmware_block_ptr;
fw_block[0] = update_firmware_bss_address;
fw_block[1] = update_firmware_bss_size;
exmimo_send_pccmd(c, EXMIMO_FW_CLEAR_BSS);
}
break;
case UPDATE_FIRMWARE_START_EXECUTION:
update_firmware_start_address = ((unsigned int*)arg)[1];
update_firmware_stack_pointer = ((unsigned int*)arg)[2];
sparc_tmp_0 = update_firmware_start_address;
sparc_tmp_1 = update_firmware_stack_pointer;
printk("[openair][IOCTL] ok asked Leon to set stack and start execution (addr 0x%08x, stackptr %08x)\n", sparc_tmp_0, sparc_tmp_1);
for (c=0; c<number_of_cards; c++)
{
fw_block = (unsigned int *)exmimo_pci_kvirt[c].firmware_block_ptr;
fw_block[0] = update_firmware_start_address;
fw_block[1] = update_firmware_stack_pointer;
exmimo_send_pccmd(c, EXMIMO_FW_START_EXEC);
msleep(10);
exmimo_firmware_init(c);
}
break;
case UPDATE_FIRMWARE_FORCE_REBOOT:
printk("[openair][IOCTL] ok asked Leon to reboot.\n");
for (c=0; c<number_of_cards; c++)
{
exmimo_send_pccmd(c, EXMIMO_REBOOT);
exmimo_firmware_init(c);
}
break;
case UPDATE_FIRMWARE_TEST_GOK:
printk("[openair][IOCTL] TEST_GOK command doesn't work with ExpressMIMO. Ignored.\n");
break;
default:
return -1;
break;
}
break;
default:
//----------------------
printk("[IOCTL] openair_IOCTL unknown: basecmd = %i (cmd=%X)\n", _IOC_NR(cmd), cmd );
return -EPERM;
break;
}
return 0;
}
/** irq.c
- IRQ Handler: IRQ from Leon to PCIe/Kernel: exmimo_irq_handler
- sends received packets to userspace
- send command from PC to Leon and trigger IRQ on Leon using CONTROL1 register
- commands are defined in $OPENAIR0/express-mimo/software/pcie_interface.h
- added: pass card_id as parameter to tasklet and irq handler
Authors:
Raymond Knopp <raymond.knopp@eurecom.fr>
Matthias Ihmig <matthias.ihmig@mytum.de>, 2011, 2013
*/
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/swab.h>
#include "openair_device.h"
#include "extern.h"
unsigned int openair_bh_cnt;
int get_frame_done = 0;
void pcie_printk(int card_id);
void openair_do_tasklet (unsigned long card_id);
DECLARE_TASKLET (openair_tasklet, openair_do_tasklet, 0);
irqreturn_t openair_irq_handler(int irq, void *cookie)
{
unsigned int irqval;
unsigned int irqcmd = EXMIMO_NOP;
unsigned long card_id; // = (unsigned long) cookie;
unsigned int pcie_control = PCIE_CONTROL2;
// check interrupt status register
//pci_read_config_word(pdev[0],6 , &irqval);
// find card_id. cookie is set by request_irq and static, so we will always find it
for (card_id=0; card_id<MAX_CARDS; card_id++)
if ( pdev[card_id] == cookie )
break;
if (exmimo_pci_kvirt[card_id].exmimo_id_ptr->board_swrev == BOARD_SWREV_LEGACY)
pcie_control = PCIE_CONTROL1;
else
pcie_control = PCIE_CONTROL2;
//printk("irq hndl called: card_id=%i, irqval=%i\n", card_id, irqval);
// get AHBPCIE interrupt line (bit 7) to determine if IRQ was for us from ExMIMO card, or from a different device
// reading CONTROL0 will also clear this bit and the LEON-to-PC IRQ line
irqval = ioread32(bar[card_id]+PCIE_CONTROL0);
irqcmd = ioread32(bar[card_id]+pcie_control);
//printk("IRQ handler: ctrl0: %08x, ctrl1: %08x, ctrl2: %08x, status: %08x\n", irqval, ioread32(bar[card_id]+PCIE_CONTROL1), ioread32(bar[card_id]+PCIE_CONTROL2), ioread32(bar[card_id]+PCIE_STATUS));
if ( (irqval & 0x80) == 0 ) { // CTRL0.bit7 is no set -> IRQ is not from ExMIMO i.e. not for us
if (exmimo_pci_kvirt[card_id].exmimo_id_ptr->board_swrev == BOARD_SWREV_CMDREGISTERS){
if (irqcmd != EXMIMO_NOP && irqcmd != EXMIMO_CONTROL2_COOKIE) {
if (irqcmd == GET_FRAME_DONE)
{
get_frame_done = 1;
}
openair_tasklet.data = card_id;
tasklet_schedule(&openair_tasklet);
openair_bh_cnt++;
return IRQ_HANDLED;
}
else
{
return IRQ_NONE;
}
}
else
return IRQ_NONE;
}
else
{
if (exmimo_pci_kvirt[card_id].exmimo_id_ptr->board_swrev == BOARD_SWREV_LEGACY){
// clear PCIE interrupt (bit 7 of register 0x0)
iowrite32(irqval&0xffffff7f,bar[card_id]+PCIE_CONTROL0);
}
if (irqcmd == GET_FRAME_DONE)
{
get_frame_done = 1;
}
openair_tasklet.data = card_id;
tasklet_schedule(&openair_tasklet);
openair_bh_cnt++;
return IRQ_HANDLED;
}
}
void openair_do_tasklet (unsigned long card_id)
{
int save_irq_cnt = openair_bh_cnt;
unsigned int irqcmd;
unsigned int pcie_control = PCIE_CONTROL2;
openair_bh_cnt = 0;
if (exmimo_pci_kvirt[card_id].exmimo_id_ptr->board_swrev == BOARD_SWREV_LEGACY)
pcie_control = PCIE_CONTROL1;
else
pcie_control = PCIE_CONTROL2;
irqcmd = ioread32(bar[card_id]+pcie_control);
if (save_irq_cnt > 1)
printk("[openair][IRQ tasklet(%ld)]: Warning: received more than 1 PCIE IRQ (openair_bh_cnt=%i), only the last one is acted upon(irqcmd= %i (0x%X)\n", card_id, openair_bh_cnt, irqcmd, irqcmd);
switch( irqcmd )
{
case PCI_PRINTK:
// printk("Got PCIe interrupt for printk ...\n");
pcie_printk((int) card_id);
break;
case GET_FRAME_DONE:
printk("[openair][IRQ tasklet] : Got PCIe interrupt for GET_FRAME_DONE ...\n");
break;
case EXMIMO_NOP:
printk("[openair][IRQ tasklet] : Received IRQ, with CONTROL0.bit7(IRQ Leon2PC) set, but irqcmd = EXMIMO_NOP. This should not happen with bitstreams built after June 3rd 2013.\n");
break;
default:
printk("[openair][IRQ tasklet] : Got unknown PCIe cmd: card_id = %li, irqcmd(CONTROL1) = %i (0x%X)\n", card_id, irqcmd, irqcmd);
}
iowrite32(EXMIMO_NOP, bar[card_id]+pcie_control);
}
void pcie_printk(int card_id)
{
char *buffer = exmimo_pci_kvirt[card_id].printk_buffer_ptr;
unsigned int len = ((unsigned int *)buffer)[0];
unsigned int off=0,i;
unsigned char *dword;
unsigned char tmp;
//printk("In pci_fifo_printk : buffer %p, len %d: \n",buffer,len);
printk("[LEON card%d]: ", card_id);
if (len<1024)
{
if ( (len&3) >0 )
off=1;
for (i=0; i<(off+(len>>2)); i++)
{
dword = &((unsigned char *)buffer)[(1+i)<<2];
tmp = dword[3];
dword[3] = dword[0];
dword[0] = tmp;
tmp = dword[2];
dword[2] = dword[1];
dword[1] = tmp;
}
for (i=0; i<len; i++)
printk("%c",((char*)&buffer[4])[i]);
}
}
/** 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);
kernel//homes/ghaddab/riadh/openair4G/trunk/targets/ARCH/EXMIMO/DRIVER/eurecom/openair_rf.ko
#include <linux/module.h>
#include <linux/vermagic.h>
#include <linux/compiler.h>
MODULE_INFO(vermagic, VERMAGIC_STRING);
struct module __this_module
__attribute__((section(".gnu.linkonce.this_module"))) = {
.name = KBUILD_MODNAME,
.init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
.exit = cleanup_module,
#endif
.arch = MODULE_ARCH_INIT,
};
static const struct modversion_info ____versions[]
__used
__attribute__((section("__versions"))) = {
{ 0x4d6582e4, "module_layout" },
{ 0x6bc3fbc0, "__unregister_chrdev" },
{ 0x1fedf0f4, "__request_region" },
{ 0x12da5bb2, "__kmalloc" },
{ 0xf9a482f9, "msleep" },
{ 0xb8957094, "mem_map" },
{ 0x15692c87, "param_ops_int" },
{ 0x69a358a6, "iomem_resource" },
{ 0x763c5db, "dma_set_mask" },
{ 0x184a8a78, "pci_disable_device" },
{ 0x38620fb, "__register_chrdev" },
{ 0x3573f04f, "x86_dma_fallback_dev" },
{ 0xeae3dfd6, "__const_udelay" },
{ 0xb74d03f5, "pci_set_master" },
{ 0x2bc95bd4, "memset" },
{ 0x50eedeb8, "printk" },
{ 0xae88b44d, "ipipe_percpu" },
{ 0xfaef0ed, "__tasklet_schedule" },
{ 0x2f287f0d, "copy_to_user" },
{ 0xb4390f9a, "mcount" },
{ 0x16305289, "warn_slowpath_null" },
{ 0x7e61808f, "dma_release_from_coherent" },
{ 0x2072ee9b, "request_threaded_irq" },
{ 0x98fb1f75, "dma_alloc_from_coherent" },
{ 0xa8a6f639, "__check_region" },
{ 0x19c39034, "pci_bus_read_config_word" },
{ 0x7c61340c, "__release_region" },
{ 0x37a0cba, "kfree" },
{ 0xf85828f1, "remap_pfn_range" },
{ 0xec591975, "dma_supported" },
{ 0xedc03953, "iounmap" },
{ 0x7628f3c7, "this_cpu_off" },
{ 0x94828082, "pci_get_device" },
{ 0x471d2cd9, "pci_iomap" },
{ 0x436c2179, "iowrite32" },
{ 0xc1a6619, "pci_enable_device" },
{ 0x362ef408, "_copy_from_user" },
{ 0xf358d5ac, "dma_ops" },
{ 0xe484e35f, "ioread32" },
{ 0xf20dabd8, "free_irq" },
};
static const char __module_depends[]
__used
__attribute__((section(".modinfo"))) =
"depends=";
MODULE_INFO(srcversion, "BAA63F2436D299D87A90400");
#!/bin/bash
PCI=`lspci -m | grep Xilinx`
if [ -z "$PCI" ]; then
echo "No card found. Stopping!"
return
fi
SLOT_NUMBER=`echo $PCI | awk -F\" '{print $1}'`
setpci -s $SLOT_NUMBER 60.b=10
#ifndef __VARS_H__
#define __VARS_H__
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#ifdef KERNEL2_6
#include <linux/slab.h>
#endif
#include "defs.h"
#include "pcie_interface.h"
unsigned int openair_irq_enabled[MAX_CARDS] = INIT_ZEROS;
unsigned int openair_chrdev_registered = 0;
unsigned int openair_pci_device_enabled[MAX_CARDS] = INIT_ZEROS;
struct pci_dev *pdev[MAX_CARDS] = INIT_ZEROS;
void __iomem *bar[MAX_CARDS] = INIT_ZEROS;
resource_size_t mmio_start[MAX_CARDS] = INIT_ZEROS;
resource_size_t mmio_length[MAX_CARDS];
unsigned int mmio_flags[MAX_CARDS];
int major;
char number_of_cards;
// bigshm allocs a single larger block, used for shared structures and pointers
dma_addr_t bigshm_head_phys[MAX_CARDS] = INIT_ZEROS;
void *bigshm_head[MAX_CARDS] = INIT_ZEROS;
void *bigshm_currentptr[MAX_CARDS];
dma_addr_t pphys_exmimo_pci_phys[MAX_CARDS]; // phys pointer to pci_bot structure in shared mem
exmimo_pci_interface_bot_t *p_exmimo_pci_phys[MAX_CARDS] = INIT_ZEROS; // inside struct has physical (DMA) pointers to pci_bot memory blocks
exmimo_pci_interface_bot_virtual_t exmimo_pci_kvirt[MAX_CARDS]; // has virtual pointers to pci_bot memory blocks
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment