diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/Makefile b/targets/ARCH/EXMIMO/DRIVER/exmimo3/Makefile new file mode 100755 index 0000000000000000000000000000000000000000..30dba76a2f31f1a8c76233bd2c6d9b143272c3f6 --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/Makefile @@ -0,0 +1,22 @@ +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 + diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/Module.symvers b/targets/ARCH/EXMIMO/DRIVER/exmimo3/Module.symvers new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/compile.sh b/targets/ARCH/EXMIMO/DRIVER/exmimo3/compile.sh new file mode 100644 index 0000000000000000000000000000000000000000..dc912a6ebaa0783f1ebb85a0b756307397269e12 --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/compile.sh @@ -0,0 +1,4 @@ +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` diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/defs.h b/targets/ARCH/EXMIMO/DRIVER/exmimo3/defs.h new file mode 100755 index 0000000000000000000000000000000000000000..5da6b679430a01afe04713aabe3102a62f38adc7 --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/defs.h @@ -0,0 +1,52 @@ +#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 diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reenumerate_expressmimo.sh b/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reenumerate_expressmimo.sh new file mode 100755 index 0000000000000000000000000000000000000000..09ee7050fbe99b8779a89ad1c5beade0f702612f --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reenumerate_expressmimo.sh @@ -0,0 +1,20 @@ +#!/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 diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reinsert_driver.sh b/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reinsert_driver.sh new file mode 100755 index 0000000000000000000000000000000000000000..75198b35d1164a72545217791b203bfd749c7d2b --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/do_reinsert_driver.sh @@ -0,0 +1 @@ +rmmod openair_rf && sleep 1 ; insmod openair_rf.ko diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/exmimo_fw.c b/targets/ARCH/EXMIMO/DRIVER/exmimo3/exmimo_fw.c new file mode 100644 index 0000000000000000000000000000000000000000..ab48233443080147cf253a51a5a574fb4a1757ea --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/exmimo_fw.c @@ -0,0 +1,337 @@ +/** 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); +} + diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/extern.h b/targets/ARCH/EXMIMO/DRIVER/exmimo3/extern.h new file mode 100755 index 0000000000000000000000000000000000000000..751574fe76658439f3355160e653bf3f0c7dce25 --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/extern.h @@ -0,0 +1,30 @@ +#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__ diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/fileops.c b/targets/ARCH/EXMIMO/DRIVER/exmimo3/fileops.c new file mode 100755 index 0000000000000000000000000000000000000000..fc7e4e3d637f1dec50d1e34f1c34df9ff6b4b553 --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/fileops.c @@ -0,0 +1,435 @@ +/** 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; +} + + diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/irq.c b/targets/ARCH/EXMIMO/DRIVER/exmimo3/irq.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab9bbb9610b437169ab42fcdcfe9b4e58052cd1 --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/irq.c @@ -0,0 +1,171 @@ +/** 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]); + } +} + + diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/module_main.c b/targets/ARCH/EXMIMO/DRIVER/exmimo3/module_main.c new file mode 100755 index 0000000000000000000000000000000000000000..1124ee53baaee19230e24624fed98f7713785a0c --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/module_main.c @@ -0,0 +1,333 @@ +/** 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); diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/modules.order b/targets/ARCH/EXMIMO/DRIVER/exmimo3/modules.order new file mode 100644 index 0000000000000000000000000000000000000000..4c907820b7b23e69731312243784c8b4f9f6bb39 --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/modules.order @@ -0,0 +1 @@ +kernel//homes/ghaddab/riadh/openair4G/trunk/targets/ARCH/EXMIMO/DRIVER/eurecom/openair_rf.ko diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.ko b/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.ko new file mode 100644 index 0000000000000000000000000000000000000000..2945847c2b6e074ad67dbf0f133abf8b8d8bfb46 Binary files /dev/null and b/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.ko differ diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.mod.c b/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.mod.c new file mode 100644 index 0000000000000000000000000000000000000000..1981fc51b30863b7f3e118a36e9ab3a4133b3a84 --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/openair_rf.mod.c @@ -0,0 +1,68 @@ +#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"); diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/test_script.sh b/targets/ARCH/EXMIMO/DRIVER/exmimo3/test_script.sh new file mode 100755 index 0000000000000000000000000000000000000000..0e2ae5968dc68637c7ddb1371d868406cc64b902 --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/test_script.sh @@ -0,0 +1,12 @@ +#!/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 + diff --git a/targets/ARCH/EXMIMO/DRIVER/exmimo3/vars.h b/targets/ARCH/EXMIMO/DRIVER/exmimo3/vars.h new file mode 100755 index 0000000000000000000000000000000000000000..5a284fbe9e099b594ae33e18b10c6e59370af235 --- /dev/null +++ b/targets/ARCH/EXMIMO/DRIVER/exmimo3/vars.h @@ -0,0 +1,41 @@ +#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