2016-03-04 12:26:36 +01:00
/*
* Copyright 2015 IBM Corp .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# include "cxl.h"
static const __be32 * read_prop_string ( const struct device_node * np ,
const char * prop_name )
{
const __be32 * prop ;
prop = of_get_property ( np , prop_name , NULL ) ;
if ( cxl_verbose & & prop )
pr_info ( " %s: %s \n " , prop_name , ( char * ) prop ) ;
return prop ;
}
static const __be32 * read_prop_dword ( const struct device_node * np ,
const char * prop_name , u32 * val )
{
const __be32 * prop ;
prop = of_get_property ( np , prop_name , NULL ) ;
if ( prop )
* val = be32_to_cpu ( prop [ 0 ] ) ;
if ( cxl_verbose & & prop )
pr_info ( " %s: %#x (%u) \n " , prop_name , * val , * val ) ;
return prop ;
}
static const __be64 * read_prop64_dword ( const struct device_node * np ,
const char * prop_name , u64 * val )
{
const __be64 * prop ;
prop = of_get_property ( np , prop_name , NULL ) ;
if ( prop )
* val = be64_to_cpu ( prop [ 0 ] ) ;
if ( cxl_verbose & & prop )
pr_info ( " %s: %#llx (%llu) \n " , prop_name , * val , * val ) ;
return prop ;
}
static int read_handle ( struct device_node * np , u64 * handle )
{
const __be32 * prop ;
u64 size ;
/* Get address and size of the node */
prop = of_get_address ( np , 0 , & size , NULL ) ;
if ( size )
return - EINVAL ;
/* Helper to read a big number; size is in cells (not bytes) */
* handle = of_read_number ( prop , of_n_addr_cells ( np ) ) ;
return 0 ;
}
static int read_phys_addr ( struct device_node * np , char * prop_name ,
struct cxl_afu * afu )
{
int i , len , entry_size , naddr , nsize , type ;
u64 addr , size ;
const __be32 * prop ;
naddr = of_n_addr_cells ( np ) ;
nsize = of_n_size_cells ( np ) ;
prop = of_get_property ( np , prop_name , & len ) ;
if ( prop ) {
entry_size = naddr + nsize ;
for ( i = 0 ; i < ( len / 4 ) ; i + = entry_size , prop + = entry_size ) {
type = be32_to_cpu ( prop [ 0 ] ) ;
addr = of_read_number ( prop , naddr ) ;
size = of_read_number ( & prop [ naddr ] , nsize ) ;
switch ( type ) {
case 0 : /* unit address */
afu - > guest - > handle = addr ;
break ;
case 1 : /* p2 area */
afu - > guest - > p2n_phys + = addr ;
afu - > guest - > p2n_size = size ;
break ;
case 2 : /* problem state area */
afu - > psn_phys + = addr ;
afu - > adapter - > ps_size = size ;
break ;
default :
pr_err ( " Invalid address type %d found in %s property of AFU \n " ,
type , prop_name ) ;
return - EINVAL ;
}
if ( cxl_verbose )
pr_info ( " %s: %#x %#llx (size %#llx) \n " ,
prop_name , type , addr , size ) ;
}
}
return 0 ;
}
static int read_vpd ( struct cxl * adapter , struct cxl_afu * afu )
{
char vpd [ 256 ] ;
int rc ;
size_t len = sizeof ( vpd ) ;
memset ( vpd , 0 , len ) ;
if ( adapter )
rc = cxl_guest_read_adapter_vpd ( adapter , vpd , len ) ;
else
rc = cxl_guest_read_afu_vpd ( afu , vpd , len ) ;
if ( rc > 0 ) {
cxl_dump_debug_buffer ( vpd , rc ) ;
rc = 0 ;
}
return rc ;
}
int cxl_of_read_afu_handle ( struct cxl_afu * afu , struct device_node * afu_np )
{
if ( read_handle ( afu_np , & afu - > guest - > handle ) )
return - EINVAL ;
pr_devel ( " AFU handle: 0x%.16llx \n " , afu - > guest - > handle ) ;
return 0 ;
}
int cxl_of_read_afu_properties ( struct cxl_afu * afu , struct device_node * np )
{
int i , len , rc ;
char * p ;
const __be32 * prop ;
u16 device_id , vendor_id ;
u32 val = 0 , class_code ;
/* Properties are read in the same order as listed in PAPR */
if ( cxl_verbose ) {
pr_info ( " Dump of the 'ibm,coherent-platform-function' node properties: \n " ) ;
prop = of_get_property ( np , " compatible " , & len ) ;
i = 0 ;
while ( i < len ) {
p = ( char * ) prop + i ;
pr_info ( " compatible: %s \n " , p ) ;
i + = strlen ( p ) + 1 ;
}
read_prop_string ( np , " name " ) ;
}
rc = read_phys_addr ( np , " reg " , afu ) ;
if ( rc )
return rc ;
rc = read_phys_addr ( np , " assigned-addresses " , afu ) ;
if ( rc )
return rc ;
if ( afu - > psn_phys = = 0 )
afu - > psa = false ;
else
afu - > psa = true ;
if ( cxl_verbose ) {
read_prop_string ( np , " ibm,loc-code " ) ;
read_prop_string ( np , " device_type " ) ;
}
read_prop_dword ( np , " ibm,#processes " , & afu - > max_procs_virtualised ) ;
if ( cxl_verbose ) {
read_prop_dword ( np , " ibm,scratchpad-size " , & val ) ;
read_prop_dword ( np , " ibm,programmable " , & val ) ;
read_prop_string ( np , " ibm,phandle " ) ;
read_vpd ( NULL , afu ) ;
}
read_prop_dword ( np , " ibm,max-ints-per-process " , & afu - > guest - > max_ints ) ;
afu - > irqs_max = afu - > guest - > max_ints ;
prop = read_prop_dword ( np , " ibm,min-ints-per-process " , & afu - > pp_irqs ) ;
if ( prop ) {
/* One extra interrupt for the PSL interrupt is already
* included . Remove it now to keep only AFU interrupts and
* match the native case .
*/
afu - > pp_irqs - - ;
}
if ( cxl_verbose ) {
read_prop_dword ( np , " ibm,max-ints " , & val ) ;
read_prop_dword ( np , " ibm,vpd-size " , & val ) ;
}
read_prop64_dword ( np , " ibm,error-buffer-size " , & afu - > eb_len ) ;
afu - > eb_offset = 0 ;
if ( cxl_verbose )
read_prop_dword ( np , " ibm,config-record-type " , & val ) ;
read_prop64_dword ( np , " ibm,config-record-size " , & afu - > crs_len ) ;
afu - > crs_offset = 0 ;
read_prop_dword ( np , " ibm,#config-records " , & afu - > crs_num ) ;
if ( cxl_verbose ) {
for ( i = 0 ; i < afu - > crs_num ; i + + ) {
rc = cxl_ops - > afu_cr_read16 ( afu , i , PCI_DEVICE_ID ,
& device_id ) ;
if ( ! rc )
pr_info ( " record %d - device-id: %#x \n " ,
i , device_id ) ;
rc = cxl_ops - > afu_cr_read16 ( afu , i , PCI_VENDOR_ID ,
& vendor_id ) ;
if ( ! rc )
pr_info ( " record %d - vendor-id: %#x \n " ,
i , vendor_id ) ;
rc = cxl_ops - > afu_cr_read32 ( afu , i , PCI_CLASS_REVISION ,
& class_code ) ;
if ( ! rc ) {
class_code > > = 8 ;
pr_info ( " record %d - class-code: %#x \n " ,
i , class_code ) ;
}
}
read_prop_dword ( np , " ibm,function-number " , & val ) ;
read_prop_dword ( np , " ibm,privileged-function " , & val ) ;
read_prop_dword ( np , " vendor-id " , & val ) ;
read_prop_dword ( np , " device-id " , & val ) ;
read_prop_dword ( np , " revision-id " , & val ) ;
read_prop_dword ( np , " class-code " , & val ) ;
read_prop_dword ( np , " subsystem-vendor-id " , & val ) ;
read_prop_dword ( np , " subsystem-id " , & val ) ;
}
/*
* if " ibm,process-mmio " doesn ' t exist then per - process mmio is
* not supported
*/
val = 0 ;
prop = read_prop_dword ( np , " ibm,process-mmio " , & val ) ;
if ( prop & & val = = 1 )
afu - > pp_psa = true ;
else
afu - > pp_psa = false ;
if ( cxl_verbose ) {
read_prop_dword ( np , " ibm,supports-aur " , & val ) ;
read_prop_dword ( np , " ibm,supports-csrp " , & val ) ;
read_prop_dword ( np , " ibm,supports-prr " , & val ) ;
}
prop = read_prop_dword ( np , " ibm,function-error-interrupt " , & val ) ;
if ( prop )
afu - > serr_hwirq = val ;
pr_devel ( " AFU handle: %#llx \n " , afu - > guest - > handle ) ;
pr_devel ( " p2n_phys: %#llx (size %#llx) \n " ,
afu - > guest - > p2n_phys , afu - > guest - > p2n_size ) ;
pr_devel ( " psn_phys: %#llx (size %#llx) \n " ,
afu - > psn_phys , afu - > adapter - > ps_size ) ;
pr_devel ( " Max number of processes virtualised=%i \n " ,
afu - > max_procs_virtualised ) ;
pr_devel ( " Per-process irqs min=%i, max=%i \n " , afu - > pp_irqs ,
afu - > irqs_max ) ;
pr_devel ( " Slice error interrupt=%#lx \n " , afu - > serr_hwirq ) ;
return 0 ;
}
static int read_adapter_irq_config ( struct cxl * adapter , struct device_node * np )
{
const __be32 * ranges ;
int len , nranges , i ;
struct irq_avail * cur ;
ranges = of_get_property ( np , " interrupt-ranges " , & len ) ;
if ( ranges = = NULL | | len < ( 2 * sizeof ( int ) ) )
return - EINVAL ;
/*
* encoded array of two cells per entry , each cell encoded as
* with encode - int
*/
nranges = len / ( 2 * sizeof ( int ) ) ;
if ( nranges = = 0 | | ( nranges * 2 * sizeof ( int ) ) ! = len )
return - EINVAL ;
adapter - > guest - > irq_avail = kzalloc ( nranges * sizeof ( struct irq_avail ) ,
GFP_KERNEL ) ;
if ( adapter - > guest - > irq_avail = = NULL )
return - ENOMEM ;
adapter - > guest - > irq_base_offset = be32_to_cpu ( ranges [ 0 ] ) ;
for ( i = 0 ; i < nranges ; i + + ) {
cur = & adapter - > guest - > irq_avail [ i ] ;
cur - > offset = be32_to_cpu ( ranges [ i * 2 ] ) ;
cur - > range = be32_to_cpu ( ranges [ i * 2 + 1 ] ) ;
cur - > bitmap = kcalloc ( BITS_TO_LONGS ( cur - > range ) ,
sizeof ( * cur - > bitmap ) , GFP_KERNEL ) ;
if ( cur - > bitmap = = NULL )
goto err ;
if ( cur - > offset < adapter - > guest - > irq_base_offset )
adapter - > guest - > irq_base_offset = cur - > offset ;
if ( cxl_verbose )
pr_info ( " available IRQ range: %#lx-%#lx (%lu) \n " ,
cur - > offset , cur - > offset + cur - > range - 1 ,
cur - > range ) ;
}
adapter - > guest - > irq_nranges = nranges ;
spin_lock_init ( & adapter - > guest - > irq_alloc_lock ) ;
return 0 ;
err :
for ( i - - ; i > = 0 ; i - - ) {
cur = & adapter - > guest - > irq_avail [ i ] ;
kfree ( cur - > bitmap ) ;
}
kfree ( adapter - > guest - > irq_avail ) ;
adapter - > guest - > irq_avail = NULL ;
return - ENOMEM ;
}
int cxl_of_read_adapter_handle ( struct cxl * adapter , struct device_node * np )
{
if ( read_handle ( np , & adapter - > guest - > handle ) )
return - EINVAL ;
pr_devel ( " Adapter handle: 0x%.16llx \n " , adapter - > guest - > handle ) ;
return 0 ;
}
int cxl_of_read_adapter_properties ( struct cxl * adapter , struct device_node * np )
{
int rc , len , naddr , i ;
char * p ;
const __be32 * prop ;
u32 val = 0 ;
/* Properties are read in the same order as listed in PAPR */
naddr = of_n_addr_cells ( np ) ;
if ( cxl_verbose ) {
pr_info ( " Dump of the 'ibm,coherent-platform-facility' node properties: \n " ) ;
read_prop_dword ( np , " #address-cells " , & val ) ;
read_prop_dword ( np , " #size-cells " , & val ) ;
prop = of_get_property ( np , " compatible " , & len ) ;
i = 0 ;
while ( i < len ) {
p = ( char * ) prop + i ;
pr_info ( " compatible: %s \n " , p ) ;
i + = strlen ( p ) + 1 ;
}
read_prop_string ( np , " name " ) ;
read_prop_string ( np , " model " ) ;
prop = of_get_property ( np , " reg " , NULL ) ;
if ( prop ) {
pr_info ( " reg: addr:%#llx size:%#x \n " ,
of_read_number ( prop , naddr ) ,
be32_to_cpu ( prop [ naddr ] ) ) ;
}
read_prop_string ( np , " ibm,loc-code " ) ;
}
if ( ( rc = read_adapter_irq_config ( adapter , np ) ) )
return rc ;
if ( cxl_verbose ) {
read_prop_string ( np , " device_type " ) ;
read_prop_string ( np , " ibm,phandle " ) ;
}
prop = read_prop_dword ( np , " ibm,caia-version " , & val ) ;
if ( prop ) {
adapter - > caia_major = ( val & 0xFF00 ) > > 8 ;
adapter - > caia_minor = val & 0xFF ;
}
prop = read_prop_dword ( np , " ibm,psl-revision " , & val ) ;
if ( prop )
adapter - > psl_rev = val ;
prop = read_prop_string ( np , " status " ) ;
if ( prop ) {
adapter - > guest - > status = kasprintf ( GFP_KERNEL , " %s " , ( char * ) prop ) ;
if ( adapter - > guest - > status = = NULL )
return - ENOMEM ;
}
prop = read_prop_dword ( np , " vendor-id " , & val ) ;
if ( prop )
adapter - > guest - > vendor = val ;
prop = read_prop_dword ( np , " device-id " , & val ) ;
if ( prop )
adapter - > guest - > device = val ;
if ( cxl_verbose ) {
read_prop_dword ( np , " ibm,privileged-facility " , & val ) ;
read_prop_dword ( np , " revision-id " , & val ) ;
read_prop_dword ( np , " class-code " , & val ) ;
}
prop = read_prop_dword ( np , " subsystem-vendor-id " , & val ) ;
if ( prop )
adapter - > guest - > subsystem_vendor = val ;
prop = read_prop_dword ( np , " subsystem-id " , & val ) ;
if ( prop )
adapter - > guest - > subsystem = val ;
if ( cxl_verbose )
read_vpd ( adapter , NULL ) ;
return 0 ;
}
static int cxl_of_remove ( struct platform_device * pdev )
{
struct cxl * adapter ;
int afu ;
adapter = dev_get_drvdata ( & pdev - > dev ) ;
for ( afu = 0 ; afu < adapter - > slices ; afu + + )
cxl_guest_remove_afu ( adapter - > afu [ afu ] ) ;
cxl_guest_remove_adapter ( adapter ) ;
return 0 ;
}
static void cxl_of_shutdown ( struct platform_device * pdev )
{
cxl_of_remove ( pdev ) ;
}
int cxl_of_probe ( struct platform_device * pdev )
{
struct device_node * np = NULL ;
struct device_node * afu_np = NULL ;
struct cxl * adapter = NULL ;
int ret ;
2016-07-29 13:55:34 +10:00
int slice = 0 , slice_ok = 0 ;
2016-03-04 12:26:36 +01:00
pr_devel ( " in %s \n " , __func__ ) ;
np = pdev - > dev . of_node ;
if ( np = = NULL )
return - ENODEV ;
/* init adapter */
adapter = cxl_guest_init_adapter ( np , pdev ) ;
if ( IS_ERR ( adapter ) ) {
dev_err ( & pdev - > dev , " guest_init_adapter failed: %li \n " , PTR_ERR ( adapter ) ) ;
return PTR_ERR ( adapter ) ;
}
/* init afu */
2016-07-29 13:55:34 +10:00
for_each_child_of_node ( np , afu_np ) {
2016-03-04 12:26:36 +01:00
if ( ( ret = cxl_guest_init_afu ( adapter , slice , afu_np ) ) )
dev_err ( & pdev - > dev , " AFU %i failed to initialise: %i \n " ,
slice , ret ) ;
else
slice_ok + + ;
2016-07-29 13:55:34 +10:00
slice + + ;
2016-03-04 12:26:36 +01:00
}
if ( slice_ok = = 0 ) {
dev_info ( & pdev - > dev , " No active AFU " ) ;
adapter - > slices = 0 ;
}
return 0 ;
}
static const struct of_device_id cxl_of_match [ ] = {
{ . compatible = " ibm,coherent-platform-facility " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , cxl_of_match ) ;
struct platform_driver cxl_of_driver = {
. driver = {
. name = " cxl_of " ,
. of_match_table = cxl_of_match ,
. owner = THIS_MODULE
} ,
. probe = cxl_of_probe ,
. remove = cxl_of_remove ,
. shutdown = cxl_of_shutdown ,
} ;