2013-12-09 13:30:41 +01:00
/**
* IBM Accelerator Family ' GenWQE '
*
* ( C ) Copyright IBM Corp . 2013
*
* Author : Frank Haverkamp < haver @ linux . vnet . ibm . com >
* Author : Joerg - Stephan Vogt < jsvogt @ de . ibm . com >
* Author : Michael Jung < mijung @ de . ibm . com >
* Author : Michael Ruettger < michael @ ibmra . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License ( version 2 only )
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
/*
* Miscelanous functionality used in the other GenWQE driver parts .
*/
# include <linux/kernel.h>
# include <linux/dma-mapping.h>
# include <linux/sched.h>
# include <linux/vmalloc.h>
# include <linux/page-flags.h>
# include <linux/scatterlist.h>
# include <linux/hugetlb.h>
# include <linux/iommu.h>
# include <linux/delay.h>
# include <linux/pci.h>
# include <linux/dma-mapping.h>
# include <linux/ctype.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/delay.h>
# include <asm/pgtable.h>
# include "genwqe_driver.h"
# include "card_base.h"
# include "card_ddcb.h"
/**
* __genwqe_writeq ( ) - Write 64 - bit register
* @ cd : genwqe device descriptor
* @ byte_offs : byte offset within BAR
* @ val : 64 - bit value
*
* Return : 0 if success ; < 0 if error
*/
int __genwqe_writeq ( struct genwqe_dev * cd , u64 byte_offs , u64 val )
{
2014-06-04 10:57:51 -03:00
struct pci_dev * pci_dev = cd - > pci_dev ;
2013-12-09 13:30:41 +01:00
if ( cd - > err_inject & GENWQE_INJECT_HARDWARE_FAILURE )
return - EIO ;
if ( cd - > mmio = = NULL )
return - EIO ;
2014-06-04 10:57:51 -03:00
if ( pci_channel_offline ( pci_dev ) )
return - EIO ;
2013-12-20 20:27:20 +01:00
__raw_writeq ( ( __force u64 ) cpu_to_be64 ( val ) , cd - > mmio + byte_offs ) ;
2013-12-09 13:30:41 +01:00
return 0 ;
}
/**
* __genwqe_readq ( ) - Read 64 - bit register
* @ cd : genwqe device descriptor
* @ byte_offs : offset within BAR
*
* Return : value from register
*/
u64 __genwqe_readq ( struct genwqe_dev * cd , u64 byte_offs )
{
if ( cd - > err_inject & GENWQE_INJECT_HARDWARE_FAILURE )
return 0xffffffffffffffffull ;
if ( ( cd - > err_inject & GENWQE_INJECT_GFIR_FATAL ) & &
( byte_offs = = IO_SLC_CFGREG_GFIR ) )
return 0x000000000000ffffull ;
if ( ( cd - > err_inject & GENWQE_INJECT_GFIR_INFO ) & &
( byte_offs = = IO_SLC_CFGREG_GFIR ) )
return 0x00000000ffff0000ull ;
if ( cd - > mmio = = NULL )
return 0xffffffffffffffffull ;
2013-12-20 16:26:10 +01:00
return be64_to_cpu ( ( __force __be64 ) __raw_readq ( cd - > mmio + byte_offs ) ) ;
2013-12-09 13:30:41 +01:00
}
/**
* __genwqe_writel ( ) - Write 32 - bit register
* @ cd : genwqe device descriptor
* @ byte_offs : byte offset within BAR
* @ val : 32 - bit value
*
* Return : 0 if success ; < 0 if error
*/
int __genwqe_writel ( struct genwqe_dev * cd , u64 byte_offs , u32 val )
{
2014-06-04 10:57:51 -03:00
struct pci_dev * pci_dev = cd - > pci_dev ;
2013-12-09 13:30:41 +01:00
if ( cd - > err_inject & GENWQE_INJECT_HARDWARE_FAILURE )
return - EIO ;
if ( cd - > mmio = = NULL )
return - EIO ;
2014-06-04 10:57:51 -03:00
if ( pci_channel_offline ( pci_dev ) )
return - EIO ;
2013-12-20 16:26:10 +01:00
__raw_writel ( ( __force u32 ) cpu_to_be32 ( val ) , cd - > mmio + byte_offs ) ;
2013-12-09 13:30:41 +01:00
return 0 ;
}
/**
* __genwqe_readl ( ) - Read 32 - bit register
* @ cd : genwqe device descriptor
* @ byte_offs : offset within BAR
*
* Return : Value from register
*/
u32 __genwqe_readl ( struct genwqe_dev * cd , u64 byte_offs )
{
if ( cd - > err_inject & GENWQE_INJECT_HARDWARE_FAILURE )
return 0xffffffff ;
if ( cd - > mmio = = NULL )
return 0xffffffff ;
2013-12-20 16:26:10 +01:00
return be32_to_cpu ( ( __force __be32 ) __raw_readl ( cd - > mmio + byte_offs ) ) ;
2013-12-09 13:30:41 +01:00
}
/**
* genwqe_read_app_id ( ) - Extract app_id
*
* app_unitcfg need to be filled with valid data first
*/
int genwqe_read_app_id ( struct genwqe_dev * cd , char * app_name , int len )
{
int i , j ;
u32 app_id = ( u32 ) cd - > app_unitcfg ;
memset ( app_name , 0 , len ) ;
for ( i = 0 , j = 0 ; j < min ( len , 4 ) ; j + + ) {
char ch = ( char ) ( ( app_id > > ( 24 - j * 8 ) ) & 0xff ) ;
if ( ch = = ' ' )
continue ;
app_name [ i + + ] = isprint ( ch ) ? ch : ' X ' ;
}
return i ;
}
/**
* genwqe_init_crc32 ( ) - Prepare a lookup table for fast crc32 calculations
*
* Existing kernel functions seem to use a different polynom ,
* therefore we could not use them here .
*
* Genwqe ' s Polynomial = 0x20044009
*/
# define CRC32_POLYNOMIAL 0x20044009
static u32 crc32_tab [ 256 ] ; /* crc32 lookup table */
void genwqe_init_crc32 ( void )
{
int i , j ;
u32 crc ;
for ( i = 0 ; i < 256 ; i + + ) {
crc = i < < 24 ;
for ( j = 0 ; j < 8 ; j + + ) {
if ( crc & 0x80000000 )
crc = ( crc < < 1 ) ^ CRC32_POLYNOMIAL ;
else
crc = ( crc < < 1 ) ;
}
crc32_tab [ i ] = crc ;
}
}
/**
* genwqe_crc32 ( ) - Generate 32 - bit crc as required for DDCBs
* @ buff : pointer to data buffer
* @ len : length of data for calculation
* @ init : initial crc ( 0xffffffff at start )
*
* polynomial = x ^ 32 * + x ^ 29 + x ^ 18 + x ^ 14 + x ^ 3 + 1 ( 0x20044009 )
* Example : 4 bytes 0x01 0x02 0x03 0x04 with init = 0xffffffff should
* result in a crc32 of 0xf33cb7d3 .
*
* The existing kernel crc functions did not cover this polynom yet .
*
* Return : crc32 checksum .
*/
u32 genwqe_crc32 ( u8 * buff , size_t len , u32 init )
{
int i ;
u32 crc ;
crc = init ;
while ( len - - ) {
i = ( ( crc > > 24 ) ^ * buff + + ) & 0xFF ;
crc = ( crc < < 8 ) ^ crc32_tab [ i ] ;
}
return crc ;
}
void * __genwqe_alloc_consistent ( struct genwqe_dev * cd , size_t size ,
dma_addr_t * dma_handle )
{
if ( get_order ( size ) > MAX_ORDER )
return NULL ;
return pci_alloc_consistent ( cd - > pci_dev , size , dma_handle ) ;
}
void __genwqe_free_consistent ( struct genwqe_dev * cd , size_t size ,
void * vaddr , dma_addr_t dma_handle )
{
if ( vaddr = = NULL )
return ;
pci_free_consistent ( cd - > pci_dev , size , vaddr , dma_handle ) ;
}
static void genwqe_unmap_pages ( struct genwqe_dev * cd , dma_addr_t * dma_list ,
int num_pages )
{
int i ;
struct pci_dev * pci_dev = cd - > pci_dev ;
for ( i = 0 ; ( i < num_pages ) & & ( dma_list [ i ] ! = 0x0 ) ; i + + ) {
pci_unmap_page ( pci_dev , dma_list [ i ] ,
PAGE_SIZE , PCI_DMA_BIDIRECTIONAL ) ;
dma_list [ i ] = 0x0 ;
}
}
static int genwqe_map_pages ( struct genwqe_dev * cd ,
struct page * * page_list , int num_pages ,
dma_addr_t * dma_list )
{
int i ;
struct pci_dev * pci_dev = cd - > pci_dev ;
/* establish DMA mapping for requested pages */
for ( i = 0 ; i < num_pages ; i + + ) {
dma_addr_t daddr ;
dma_list [ i ] = 0x0 ;
daddr = pci_map_page ( pci_dev , page_list [ i ] ,
0 , /* map_offs */
PAGE_SIZE ,
PCI_DMA_BIDIRECTIONAL ) ; /* FIXME rd/rw */
if ( pci_dma_mapping_error ( pci_dev , daddr ) ) {
dev_err ( & pci_dev - > dev ,
" [%s] err: no dma addr daddr=%016llx! \n " ,
__func__ , ( long long ) daddr ) ;
goto err ;
}
dma_list [ i ] = daddr ;
}
return 0 ;
err :
genwqe_unmap_pages ( cd , dma_list , num_pages ) ;
return - EIO ;
}
static int genwqe_sgl_size ( int num_pages )
{
int len , num_tlb = num_pages / 7 ;
len = sizeof ( struct sg_entry ) * ( num_pages + num_tlb + 1 ) ;
return roundup ( len , PAGE_SIZE ) ;
}
2014-03-20 15:11:05 +01:00
/**
* genwqe_alloc_sync_sgl ( ) - Allocate memory for sgl and overlapping pages
*
* Allocates memory for sgl and overlapping pages . Pages which might
* overlap other user - space memory blocks are being cached for DMAs ,
* such that we do not run into syncronization issues . Data is copied
* from user - space into the cached pages .
*/
int genwqe_alloc_sync_sgl ( struct genwqe_dev * cd , struct genwqe_sgl * sgl ,
void __user * user_addr , size_t user_size )
2013-12-09 13:30:41 +01:00
{
2014-03-20 15:11:05 +01:00
int rc ;
2013-12-09 13:30:41 +01:00
struct pci_dev * pci_dev = cd - > pci_dev ;
2014-03-20 15:11:05 +01:00
sgl - > fpage_offs = offset_in_page ( ( unsigned long ) user_addr ) ;
sgl - > fpage_size = min_t ( size_t , PAGE_SIZE - sgl - > fpage_offs , user_size ) ;
sgl - > nr_pages = DIV_ROUND_UP ( sgl - > fpage_offs + user_size , PAGE_SIZE ) ;
sgl - > lpage_size = ( user_size - sgl - > fpage_size ) % PAGE_SIZE ;
dev_dbg ( & pci_dev - > dev , " [%s] uaddr=%p usize=%8ld nr_pages=%ld "
" fpage_offs=%lx fpage_size=%ld lpage_size=%ld \n " ,
__func__ , user_addr , user_size , sgl - > nr_pages ,
sgl - > fpage_offs , sgl - > fpage_size , sgl - > lpage_size ) ;
sgl - > user_addr = user_addr ;
sgl - > user_size = user_size ;
sgl - > sgl_size = genwqe_sgl_size ( sgl - > nr_pages ) ;
if ( get_order ( sgl - > sgl_size ) > MAX_ORDER ) {
2013-12-09 13:30:41 +01:00
dev_err ( & pci_dev - > dev ,
" [%s] err: too much memory requested! \n " , __func__ ) ;
2014-03-20 15:11:05 +01:00
return - ENOMEM ;
2013-12-09 13:30:41 +01:00
}
2014-03-20 15:11:05 +01:00
sgl - > sgl = __genwqe_alloc_consistent ( cd , sgl - > sgl_size ,
& sgl - > sgl_dma_addr ) ;
if ( sgl - > sgl = = NULL ) {
2013-12-09 13:30:41 +01:00
dev_err ( & pci_dev - > dev ,
" [%s] err: no memory available! \n " , __func__ ) ;
2014-03-20 15:11:05 +01:00
return - ENOMEM ;
2013-12-09 13:30:41 +01:00
}
2014-03-20 15:11:05 +01:00
/* Only use buffering on incomplete pages */
if ( ( sgl - > fpage_size ! = 0 ) & & ( sgl - > fpage_size ! = PAGE_SIZE ) ) {
sgl - > fpage = __genwqe_alloc_consistent ( cd , PAGE_SIZE ,
& sgl - > fpage_dma_addr ) ;
if ( sgl - > fpage = = NULL )
goto err_out ;
/* Sync with user memory */
if ( copy_from_user ( sgl - > fpage + sgl - > fpage_offs ,
user_addr , sgl - > fpage_size ) ) {
rc = - EFAULT ;
goto err_out ;
}
}
if ( sgl - > lpage_size ! = 0 ) {
sgl - > lpage = __genwqe_alloc_consistent ( cd , PAGE_SIZE ,
& sgl - > lpage_dma_addr ) ;
if ( sgl - > lpage = = NULL )
goto err_out1 ;
/* Sync with user memory */
if ( copy_from_user ( sgl - > lpage , user_addr + user_size -
sgl - > lpage_size , sgl - > lpage_size ) ) {
rc = - EFAULT ;
goto err_out1 ;
}
}
return 0 ;
err_out1 :
__genwqe_free_consistent ( cd , PAGE_SIZE , sgl - > fpage ,
sgl - > fpage_dma_addr ) ;
err_out :
__genwqe_free_consistent ( cd , sgl - > sgl_size , sgl - > sgl ,
sgl - > sgl_dma_addr ) ;
return - ENOMEM ;
2013-12-09 13:30:41 +01:00
}
2014-03-20 15:11:05 +01:00
int genwqe_setup_sgl ( struct genwqe_dev * cd , struct genwqe_sgl * sgl ,
dma_addr_t * dma_list )
2013-12-09 13:30:41 +01:00
{
int i = 0 , j = 0 , p ;
unsigned long dma_offs , map_offs ;
dma_addr_t prev_daddr = 0 ;
struct sg_entry * s , * last_s = NULL ;
2014-03-20 15:11:05 +01:00
size_t size = sgl - > user_size ;
2013-12-09 13:30:41 +01:00
dma_offs = 128 ; /* next block if needed/dma_offset */
2014-03-20 15:11:05 +01:00
map_offs = sgl - > fpage_offs ; /* offset in first page */
2013-12-09 13:30:41 +01:00
2014-03-20 15:11:05 +01:00
s = & sgl - > sgl [ 0 ] ; /* first set of 8 entries */
2013-12-09 13:30:41 +01:00
p = 0 ; /* page */
2014-03-20 15:11:05 +01:00
while ( p < sgl - > nr_pages ) {
2013-12-09 13:30:41 +01:00
dma_addr_t daddr ;
unsigned int size_to_map ;
/* always write the chaining entry, cleanup is done later */
j = 0 ;
2014-03-20 15:11:05 +01:00
s [ j ] . target_addr = cpu_to_be64 ( sgl - > sgl_dma_addr + dma_offs ) ;
2013-12-09 13:30:41 +01:00
s [ j ] . len = cpu_to_be32 ( 128 ) ;
s [ j ] . flags = cpu_to_be32 ( SG_CHAINED ) ;
j + + ;
while ( j < 8 ) {
/* DMA mapping for requested page, offs, size */
size_to_map = min ( size , PAGE_SIZE - map_offs ) ;
2014-03-20 15:11:05 +01:00
if ( ( p = = 0 ) & & ( sgl - > fpage ! = NULL ) ) {
daddr = sgl - > fpage_dma_addr + map_offs ;
} else if ( ( p = = sgl - > nr_pages - 1 ) & &
( sgl - > lpage ! = NULL ) ) {
daddr = sgl - > lpage_dma_addr ;
} else {
daddr = dma_list [ p ] + map_offs ;
}
2013-12-09 13:30:41 +01:00
size - = size_to_map ;
map_offs = 0 ;
if ( prev_daddr = = daddr ) {
u32 prev_len = be32_to_cpu ( last_s - > len ) ;
/* pr_info("daddr combining: "
" %016llx/%08x -> %016llx \n " ,
prev_daddr , prev_len , daddr ) ; */
last_s - > len = cpu_to_be32 ( prev_len +
size_to_map ) ;
p + + ; /* process next page */
2014-03-20 15:11:05 +01:00
if ( p = = sgl - > nr_pages )
2013-12-09 13:30:41 +01:00
goto fixup ; /* nothing to do */
prev_daddr = daddr + size_to_map ;
continue ;
}
/* start new entry */
s [ j ] . target_addr = cpu_to_be64 ( daddr ) ;
s [ j ] . len = cpu_to_be32 ( size_to_map ) ;
s [ j ] . flags = cpu_to_be32 ( SG_DATA ) ;
prev_daddr = daddr + size_to_map ;
last_s = & s [ j ] ;
j + + ;
p + + ; /* process next page */
2014-03-20 15:11:05 +01:00
if ( p = = sgl - > nr_pages )
2013-12-09 13:30:41 +01:00
goto fixup ; /* nothing to do */
}
dma_offs + = 128 ;
s + = 8 ; /* continue 8 elements further */
}
fixup :
if ( j = = 1 ) { /* combining happend on last entry! */
s - = 8 ; /* full shift needed on previous sgl block */
j = 7 ; /* shift all elements */
}
for ( i = 0 ; i < j ; i + + ) /* move elements 1 up */
s [ i ] = s [ i + 1 ] ;
s [ i ] . target_addr = cpu_to_be64 ( 0 ) ;
s [ i ] . len = cpu_to_be32 ( 0 ) ;
s [ i ] . flags = cpu_to_be32 ( SG_END_LIST ) ;
return 0 ;
}
2014-03-20 15:11:05 +01:00
/**
* genwqe_free_sync_sgl ( ) - Free memory for sgl and overlapping pages
*
* After the DMA transfer has been completed we free the memory for
* the sgl and the cached pages . Data is being transfered from cached
* pages into user - space buffers .
*/
int genwqe_free_sync_sgl ( struct genwqe_dev * cd , struct genwqe_sgl * sgl )
2013-12-09 13:30:41 +01:00
{
2014-05-13 22:47:03 +02:00
int rc = 0 ;
2014-03-20 15:11:05 +01:00
struct pci_dev * pci_dev = cd - > pci_dev ;
if ( sgl - > fpage ) {
if ( copy_to_user ( sgl - > user_addr , sgl - > fpage + sgl - > fpage_offs ,
sgl - > fpage_size ) ) {
dev_err ( & pci_dev - > dev , " [%s] err: copying fpage! \n " ,
__func__ ) ;
rc = - EFAULT ;
}
__genwqe_free_consistent ( cd , PAGE_SIZE , sgl - > fpage ,
sgl - > fpage_dma_addr ) ;
sgl - > fpage = NULL ;
sgl - > fpage_dma_addr = 0 ;
}
if ( sgl - > lpage ) {
if ( copy_to_user ( sgl - > user_addr + sgl - > user_size -
sgl - > lpage_size , sgl - > lpage ,
sgl - > lpage_size ) ) {
dev_err ( & pci_dev - > dev , " [%s] err: copying lpage! \n " ,
__func__ ) ;
rc = - EFAULT ;
}
__genwqe_free_consistent ( cd , PAGE_SIZE , sgl - > lpage ,
sgl - > lpage_dma_addr ) ;
sgl - > lpage = NULL ;
sgl - > lpage_dma_addr = 0 ;
}
__genwqe_free_consistent ( cd , sgl - > sgl_size , sgl - > sgl ,
sgl - > sgl_dma_addr ) ;
sgl - > sgl = NULL ;
sgl - > sgl_dma_addr = 0x0 ;
sgl - > sgl_size = 0 ;
return rc ;
2013-12-09 13:30:41 +01:00
}
/**
* free_user_pages ( ) - Give pinned pages back
*
* Documentation of get_user_pages is in mm / memory . c :
*
* If the page is written to , set_page_dirty ( or set_page_dirty_lock ,
* as appropriate ) must be called after the page is finished with , and
* before put_page is called .
*
* FIXME Could be of use to others and might belong in the generic
* code , if others agree . E . g .
* ll_free_user_pages in drivers / staging / lustre / lustre / llite / rw26 . c
* ceph_put_page_vector in net / ceph / pagevec . c
* maybe more ?
*/
static int free_user_pages ( struct page * * page_list , unsigned int nr_pages ,
int dirty )
{
unsigned int i ;
for ( i = 0 ; i < nr_pages ; i + + ) {
if ( page_list [ i ] ! = NULL ) {
if ( dirty )
set_page_dirty_lock ( page_list [ i ] ) ;
put_page ( page_list [ i ] ) ;
}
}
return 0 ;
}
/**
* genwqe_user_vmap ( ) - Map user - space memory to virtual kernel memory
* @ cd : pointer to genwqe device
* @ m : mapping params
* @ uaddr : user virtual address
* @ size : size of memory to be mapped
*
* We need to think about how we could speed this up . Of course it is
* not a good idea to do this over and over again , like we are
* currently doing it . Nevertheless , I am curious where on the path
* the performance is spend . Most probably within the memory
* allocation functions , but maybe also in the DMA mapping code .
*
* Restrictions : The maximum size of the possible mapping currently depends
* on the amount of memory we can get using kzalloc ( ) for the
* page_list and pci_alloc_consistent for the sg_list .
* The sg_list is currently itself not scattered , which could
* be fixed with some effort . The page_list must be split into
* PAGE_SIZE chunks too . All that will make the complicated
* code more complicated .
*
* Return : 0 if success
*/
int genwqe_user_vmap ( struct genwqe_dev * cd , struct dma_mapping * m , void * uaddr ,
unsigned long size , struct ddcb_requ * req )
{
int rc = - EINVAL ;
unsigned long data , offs ;
struct pci_dev * pci_dev = cd - > pci_dev ;
if ( ( uaddr = = NULL ) | | ( size = = 0 ) ) {
m - > size = 0 ; /* mark unused and not added */
return - EINVAL ;
}
m - > u_vaddr = uaddr ;
m - > size = size ;
/* determine space needed for page_list. */
data = ( unsigned long ) uaddr ;
offs = offset_in_page ( data ) ;
m - > nr_pages = DIV_ROUND_UP ( offs + size , PAGE_SIZE ) ;
m - > page_list = kcalloc ( m - > nr_pages ,
sizeof ( struct page * ) + sizeof ( dma_addr_t ) ,
GFP_KERNEL ) ;
if ( ! m - > page_list ) {
dev_err ( & pci_dev - > dev , " err: alloc page_list failed \n " ) ;
m - > nr_pages = 0 ;
m - > u_vaddr = NULL ;
m - > size = 0 ; /* mark unused and not added */
return - ENOMEM ;
}
m - > dma_list = ( dma_addr_t * ) ( m - > page_list + m - > nr_pages ) ;
/* pin user pages in memory */
rc = get_user_pages_fast ( data & PAGE_MASK , /* page aligned addr */
m - > nr_pages ,
1 , /* write by caller */
m - > page_list ) ; /* ptrs to pages */
/* assumption: get_user_pages can be killed by signals. */
if ( rc < m - > nr_pages ) {
free_user_pages ( m - > page_list , rc , 0 ) ;
rc = - EFAULT ;
goto fail_get_user_pages ;
}
rc = genwqe_map_pages ( cd , m - > page_list , m - > nr_pages , m - > dma_list ) ;
if ( rc ! = 0 )
goto fail_free_user_pages ;
return 0 ;
fail_free_user_pages :
free_user_pages ( m - > page_list , m - > nr_pages , 0 ) ;
fail_get_user_pages :
kfree ( m - > page_list ) ;
m - > page_list = NULL ;
m - > dma_list = NULL ;
m - > nr_pages = 0 ;
m - > u_vaddr = NULL ;
m - > size = 0 ; /* mark unused and not added */
return rc ;
}
/**
* genwqe_user_vunmap ( ) - Undo mapping of user - space mem to virtual kernel
* memory
* @ cd : pointer to genwqe device
* @ m : mapping params
*/
int genwqe_user_vunmap ( struct genwqe_dev * cd , struct dma_mapping * m ,
struct ddcb_requ * req )
{
struct pci_dev * pci_dev = cd - > pci_dev ;
if ( ! dma_mapping_used ( m ) ) {
dev_err ( & pci_dev - > dev , " [%s] err: mapping %p not used! \n " ,
__func__ , m ) ;
return - EINVAL ;
}
if ( m - > dma_list )
genwqe_unmap_pages ( cd , m - > dma_list , m - > nr_pages ) ;
if ( m - > page_list ) {
free_user_pages ( m - > page_list , m - > nr_pages , 1 ) ;
kfree ( m - > page_list ) ;
m - > page_list = NULL ;
m - > dma_list = NULL ;
m - > nr_pages = 0 ;
}
m - > u_vaddr = NULL ;
m - > size = 0 ; /* mark as unused and not added */
return 0 ;
}
/**
* genwqe_card_type ( ) - Get chip type SLU Configuration Register
* @ cd : pointer to the genwqe device descriptor
* Return : 0 : Altera Stratix - IV 230
* 1 : Altera Stratix - IV 530
* 2 : Altera Stratix - V A4
* 3 : Altera Stratix - V A7
*/
u8 genwqe_card_type ( struct genwqe_dev * cd )
{
u64 card_type = cd - > slu_unitcfg ;
return ( u8 ) ( ( card_type & IO_SLU_UNITCFG_TYPE_MASK ) > > 20 ) ;
}
/**
* genwqe_card_reset ( ) - Reset the card
* @ cd : pointer to the genwqe device descriptor
*/
int genwqe_card_reset ( struct genwqe_dev * cd )
{
u64 softrst ;
struct pci_dev * pci_dev = cd - > pci_dev ;
if ( ! genwqe_is_privileged ( cd ) )
return - ENODEV ;
/* new SL */
__genwqe_writeq ( cd , IO_SLC_CFGREG_SOFTRESET , 0x1ull ) ;
msleep ( 1000 ) ;
__genwqe_readq ( cd , IO_HSU_FIR_CLR ) ;
__genwqe_readq ( cd , IO_APP_FIR_CLR ) ;
__genwqe_readq ( cd , IO_SLU_FIR_CLR ) ;
/*
* Read - modify - write to preserve the stealth bits
*
* For SL > = 03 9 , Stealth WE bit allows removing
* the read - modify - wrote .
* r - m - w may require a mask 0x3C to avoid hitting hard
* reset again for error reset ( should be 0 , chicken ) .
*/
softrst = __genwqe_readq ( cd , IO_SLC_CFGREG_SOFTRESET ) & 0x3cull ;
__genwqe_writeq ( cd , IO_SLC_CFGREG_SOFTRESET , softrst | 0x2ull ) ;
/* give ERRORRESET some time to finish */
msleep ( 50 ) ;
if ( genwqe_need_err_masking ( cd ) ) {
dev_info ( & pci_dev - > dev ,
" [%s] masking errors for old bitstreams \n " , __func__ ) ;
__genwqe_writeq ( cd , IO_SLC_MISC_DEBUG , 0x0aull ) ;
}
return 0 ;
}
int genwqe_read_softreset ( struct genwqe_dev * cd )
{
u64 bitstream ;
if ( ! genwqe_is_privileged ( cd ) )
return - ENODEV ;
bitstream = __genwqe_readq ( cd , IO_SLU_BITSTREAM ) & 0x1 ;
cd - > softreset = ( bitstream = = 0 ) ? 0x8ull : 0xcull ;
return 0 ;
}
/**
* genwqe_set_interrupt_capability ( ) - Configure MSI capability structure
* @ cd : pointer to the device
* Return : 0 if no error
*/
int genwqe_set_interrupt_capability ( struct genwqe_dev * cd , int count )
{
int rc ;
struct pci_dev * pci_dev = cd - > pci_dev ;
2014-07-09 12:46:39 +02:00
rc = pci_enable_msi_range ( pci_dev , 1 , count ) ;
if ( rc < 0 )
return rc ;
cd - > flags | = GENWQE_FLAG_MSI_ENABLED ;
return 0 ;
2013-12-09 13:30:41 +01:00
}
/**
* genwqe_reset_interrupt_capability ( ) - Undo genwqe_set_interrupt_capability ( )
* @ cd : pointer to the device
*/
void genwqe_reset_interrupt_capability ( struct genwqe_dev * cd )
{
struct pci_dev * pci_dev = cd - > pci_dev ;
if ( cd - > flags & GENWQE_FLAG_MSI_ENABLED ) {
pci_disable_msi ( pci_dev ) ;
cd - > flags & = ~ GENWQE_FLAG_MSI_ENABLED ;
}
}
/**
* set_reg_idx ( ) - Fill array with data . Ignore illegal offsets .
* @ cd : card device
* @ r : debug register array
* @ i : index to desired entry
* @ m : maximum possible entries
* @ addr : addr which is read
* @ index : index in debug array
* @ val : read value
*/
static int set_reg_idx ( struct genwqe_dev * cd , struct genwqe_reg * r ,
unsigned int * i , unsigned int m , u32 addr , u32 idx ,
u64 val )
{
if ( WARN_ON_ONCE ( * i > = m ) )
return - EFAULT ;
r [ * i ] . addr = addr ;
r [ * i ] . idx = idx ;
r [ * i ] . val = val ;
+ + * i ;
return 0 ;
}
static int set_reg ( struct genwqe_dev * cd , struct genwqe_reg * r ,
unsigned int * i , unsigned int m , u32 addr , u64 val )
{
return set_reg_idx ( cd , r , i , m , addr , 0 , val ) ;
}
int genwqe_read_ffdc_regs ( struct genwqe_dev * cd , struct genwqe_reg * regs ,
unsigned int max_regs , int all )
{
unsigned int i , j , idx = 0 ;
u32 ufir_addr , ufec_addr , sfir_addr , sfec_addr ;
u64 gfir , sluid , appid , ufir , ufec , sfir , sfec ;
/* Global FIR */
gfir = __genwqe_readq ( cd , IO_SLC_CFGREG_GFIR ) ;
set_reg ( cd , regs , & idx , max_regs , IO_SLC_CFGREG_GFIR , gfir ) ;
/* UnitCfg for SLU */
sluid = __genwqe_readq ( cd , IO_SLU_UNITCFG ) ; /* 0x00000000 */
set_reg ( cd , regs , & idx , max_regs , IO_SLU_UNITCFG , sluid ) ;
/* UnitCfg for APP */
appid = __genwqe_readq ( cd , IO_APP_UNITCFG ) ; /* 0x02000000 */
set_reg ( cd , regs , & idx , max_regs , IO_APP_UNITCFG , appid ) ;
/* Check all chip Units */
for ( i = 0 ; i < GENWQE_MAX_UNITS ; i + + ) {
/* Unit FIR */
ufir_addr = ( i < < 24 ) | 0x008 ;
ufir = __genwqe_readq ( cd , ufir_addr ) ;
set_reg ( cd , regs , & idx , max_regs , ufir_addr , ufir ) ;
/* Unit FEC */
ufec_addr = ( i < < 24 ) | 0x018 ;
ufec = __genwqe_readq ( cd , ufec_addr ) ;
set_reg ( cd , regs , & idx , max_regs , ufec_addr , ufec ) ;
for ( j = 0 ; j < 64 ; j + + ) {
/* wherever there is a primary 1, read the 2ndary */
if ( ! all & & ( ! ( ufir & ( 1ull < < j ) ) ) )
continue ;
sfir_addr = ( i < < 24 ) | ( 0x100 + 8 * j ) ;
sfir = __genwqe_readq ( cd , sfir_addr ) ;
set_reg ( cd , regs , & idx , max_regs , sfir_addr , sfir ) ;
sfec_addr = ( i < < 24 ) | ( 0x300 + 8 * j ) ;
sfec = __genwqe_readq ( cd , sfec_addr ) ;
set_reg ( cd , regs , & idx , max_regs , sfec_addr , sfec ) ;
}
}
/* fill with invalid data until end */
for ( i = idx ; i < max_regs ; i + + ) {
regs [ i ] . addr = 0xffffffff ;
regs [ i ] . val = 0xffffffffffffffffull ;
}
return idx ;
}
/**
* genwqe_ffdc_buff_size ( ) - Calculates the number of dump registers
*/
int genwqe_ffdc_buff_size ( struct genwqe_dev * cd , int uid )
{
int entries = 0 , ring , traps , traces , trace_entries ;
u32 eevptr_addr , l_addr , d_len , d_type ;
u64 eevptr , val , addr ;
eevptr_addr = GENWQE_UID_OFFS ( uid ) | IO_EXTENDED_ERROR_POINTER ;
eevptr = __genwqe_readq ( cd , eevptr_addr ) ;
if ( ( eevptr ! = 0x0 ) & & ( eevptr ! = - 1ull ) ) {
l_addr = GENWQE_UID_OFFS ( uid ) | eevptr ;
while ( 1 ) {
val = __genwqe_readq ( cd , l_addr ) ;
if ( ( val = = 0x0 ) | | ( val = = - 1ull ) )
break ;
/* 38:24 */
d_len = ( val & 0x0000007fff000000ull ) > > 24 ;
/* 39 */
d_type = ( val & 0x0000008000000000ull ) > > 36 ;
if ( d_type ) { /* repeat */
entries + = d_len ;
} else { /* size in bytes! */
entries + = d_len > > 3 ;
}
l_addr + = 8 ;
}
}
for ( ring = 0 ; ring < 8 ; ring + + ) {
addr = GENWQE_UID_OFFS ( uid ) | IO_EXTENDED_DIAG_MAP ( ring ) ;
val = __genwqe_readq ( cd , addr ) ;
if ( ( val = = 0x0ull ) | | ( val = = - 1ull ) )
continue ;
traps = ( val > > 24 ) & 0xff ;
traces = ( val > > 16 ) & 0xff ;
trace_entries = val & 0xffff ;
entries + = traps + ( traces * trace_entries ) ;
}
return entries ;
}
/**
* genwqe_ffdc_buff_read ( ) - Implements LogoutExtendedErrorRegisters procedure
*/
int genwqe_ffdc_buff_read ( struct genwqe_dev * cd , int uid ,
struct genwqe_reg * regs , unsigned int max_regs )
{
int i , traps , traces , trace , trace_entries , trace_entry , ring ;
unsigned int idx = 0 ;
u32 eevptr_addr , l_addr , d_addr , d_len , d_type ;
u64 eevptr , e , val , addr ;
eevptr_addr = GENWQE_UID_OFFS ( uid ) | IO_EXTENDED_ERROR_POINTER ;
eevptr = __genwqe_readq ( cd , eevptr_addr ) ;
if ( ( eevptr ! = 0x0 ) & & ( eevptr ! = 0xffffffffffffffffull ) ) {
l_addr = GENWQE_UID_OFFS ( uid ) | eevptr ;
while ( 1 ) {
e = __genwqe_readq ( cd , l_addr ) ;
if ( ( e = = 0x0 ) | | ( e = = 0xffffffffffffffffull ) )
break ;
d_addr = ( e & 0x0000000000ffffffull ) ; /* 23:0 */
d_len = ( e & 0x0000007fff000000ull ) > > 24 ; /* 38:24 */
d_type = ( e & 0x0000008000000000ull ) > > 36 ; /* 39 */
d_addr | = GENWQE_UID_OFFS ( uid ) ;
if ( d_type ) {
for ( i = 0 ; i < ( int ) d_len ; i + + ) {
val = __genwqe_readq ( cd , d_addr ) ;
set_reg_idx ( cd , regs , & idx , max_regs ,
d_addr , i , val ) ;
}
} else {
d_len > > = 3 ; /* Size in bytes! */
for ( i = 0 ; i < ( int ) d_len ; i + + , d_addr + = 8 ) {
val = __genwqe_readq ( cd , d_addr ) ;
set_reg_idx ( cd , regs , & idx , max_regs ,
d_addr , 0 , val ) ;
}
}
l_addr + = 8 ;
}
}
/*
* To save time , there are only 6 traces poplulated on Uid = 2 ,
* Ring = 1. each with iters = 512.
*/
for ( ring = 0 ; ring < 8 ; ring + + ) { /* 0 is fls, 1 is fds,
2. . .7 are ASI rings */
addr = GENWQE_UID_OFFS ( uid ) | IO_EXTENDED_DIAG_MAP ( ring ) ;
val = __genwqe_readq ( cd , addr ) ;
if ( ( val = = 0x0ull ) | | ( val = = - 1ull ) )
continue ;
traps = ( val > > 24 ) & 0xff ; /* Number of Traps */
traces = ( val > > 16 ) & 0xff ; /* Number of Traces */
trace_entries = val & 0xffff ; /* Entries per trace */
/* Note: This is a combined loop that dumps both the traps */
/* (for the trace == 0 case) as well as the traces 1 to */
/* 'traces'. */
for ( trace = 0 ; trace < = traces ; trace + + ) {
u32 diag_sel =
GENWQE_EXTENDED_DIAG_SELECTOR ( ring , trace ) ;
addr = ( GENWQE_UID_OFFS ( uid ) |
IO_EXTENDED_DIAG_SELECTOR ) ;
__genwqe_writeq ( cd , addr , diag_sel ) ;
for ( trace_entry = 0 ;
trace_entry < ( trace ? trace_entries : traps ) ;
trace_entry + + ) {
addr = ( GENWQE_UID_OFFS ( uid ) |
IO_EXTENDED_DIAG_READ_MBX ) ;
val = __genwqe_readq ( cd , addr ) ;
set_reg_idx ( cd , regs , & idx , max_regs , addr ,
( diag_sel < < 16 ) | trace_entry , val ) ;
}
}
}
return 0 ;
}
/**
* genwqe_write_vreg ( ) - Write register in virtual window
*
* Note , these registers are only accessible to the PF through the
* VF - window . It is not intended for the VF to access .
*/
int genwqe_write_vreg ( struct genwqe_dev * cd , u32 reg , u64 val , int func )
{
__genwqe_writeq ( cd , IO_PF_SLC_VIRTUAL_WINDOW , func & 0xf ) ;
__genwqe_writeq ( cd , reg , val ) ;
return 0 ;
}
/**
* genwqe_read_vreg ( ) - Read register in virtual window
*
* Note , these registers are only accessible to the PF through the
* VF - window . It is not intended for the VF to access .
*/
u64 genwqe_read_vreg ( struct genwqe_dev * cd , u32 reg , int func )
{
__genwqe_writeq ( cd , IO_PF_SLC_VIRTUAL_WINDOW , func & 0xf ) ;
return __genwqe_readq ( cd , reg ) ;
}
/**
* genwqe_base_clock_frequency ( ) - Deteremine base clock frequency of the card
*
* Note : From a design perspective it turned out to be a bad idea to
* use codes here to specifiy the frequency / speed values . An old
* driver cannot understand new codes and is therefore always a
* problem . Better is to measure out the value or put the
* speed / frequency directly into a register which is always a valid
* value for old as well as for new software .
*
* Return : Card clock in MHz
*/
int genwqe_base_clock_frequency ( struct genwqe_dev * cd )
{
u16 speed ; /* MHz MHz MHz MHz */
static const int speed_grade [ ] = { 250 , 200 , 166 , 175 } ;
speed = ( u16 ) ( ( cd - > slu_unitcfg > > 28 ) & 0x0full ) ;
if ( speed > = ARRAY_SIZE ( speed_grade ) )
return 0 ; /* illegal value */
return speed_grade [ speed ] ;
}
/**
* genwqe_stop_traps ( ) - Stop traps
*
* Before reading out the analysis data , we need to stop the traps .
*/
void genwqe_stop_traps ( struct genwqe_dev * cd )
{
__genwqe_writeq ( cd , IO_SLC_MISC_DEBUG_SET , 0xcull ) ;
}
/**
* genwqe_start_traps ( ) - Start traps
*
* After having read the data , we can / must enable the traps again .
*/
void genwqe_start_traps ( struct genwqe_dev * cd )
{
__genwqe_writeq ( cd , IO_SLC_MISC_DEBUG_CLR , 0xcull ) ;
if ( genwqe_need_err_masking ( cd ) )
__genwqe_writeq ( cd , IO_SLC_MISC_DEBUG , 0x0aull ) ;
}