2012-11-29 12:55:21 +01:00
/*
* Copyright IBM Corp . 2012
*
* Author ( s ) :
* Jan Glauber < jang @ linux . vnet . ibm . com >
*/
2014-07-16 17:21:01 +02:00
# define KMSG_COMPONENT "zpci"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2012-11-29 12:55:21 +01:00
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/delay.h>
# include <linux/pci.h>
2013-04-16 14:11:14 +02:00
# include <asm/pci_debug.h>
2012-11-29 12:55:21 +01:00
# include <asm/pci_clp.h>
2013-10-22 15:17:19 +02:00
static inline void zpci_err_clp ( unsigned int rsp , int rc )
{
struct {
unsigned int rsp ;
int rc ;
} __packed data = { rsp , rc } ;
zpci_err_hex ( & data , sizeof ( data ) ) ;
}
2012-11-29 12:55:21 +01:00
/*
* Call Logical Processor
* Retry logic is handled by the caller .
*/
2013-01-31 19:53:12 +01:00
static inline u8 clp_instr ( void * data )
2012-11-29 12:55:21 +01:00
{
2013-01-31 19:53:12 +01:00
struct { u8 _ [ CLP_BLK_SIZE ] ; } * req = data ;
u64 ignored ;
2012-11-29 12:55:21 +01:00
u8 cc ;
asm volatile (
2013-01-31 19:53:12 +01:00
" .insn rrf,0xb9a00000,%[ign],%[req],0x0,0x2 \n "
2012-11-29 12:55:21 +01:00
" ipm %[cc] \n "
" srl %[cc],28 \n "
2013-01-31 19:53:12 +01:00
: [ cc ] " =d " ( cc ) , [ ign ] " =d " ( ignored ) , " +m " ( * req )
2012-11-29 12:55:21 +01:00
: [ req ] " a " ( req )
2013-01-31 19:53:12 +01:00
: " cc " ) ;
2012-11-29 12:55:21 +01:00
return cc ;
}
2013-08-29 19:37:28 +02:00
static void * clp_alloc_block ( gfp_t gfp_mask )
2012-11-29 12:55:21 +01:00
{
2013-08-29 19:37:28 +02:00
return ( void * ) __get_free_pages ( gfp_mask , get_order ( CLP_BLK_SIZE ) ) ;
2012-11-29 12:55:21 +01:00
}
static void clp_free_block ( void * ptr )
{
free_pages ( ( unsigned long ) ptr , get_order ( CLP_BLK_SIZE ) ) ;
}
static void clp_store_query_pci_fngrp ( struct zpci_dev * zdev ,
struct clp_rsp_query_pci_grp * response )
{
2012-11-29 14:33:30 +01:00
zdev - > tlb_refresh = response - > refresh ;
zdev - > dma_mask = response - > dasm ;
2012-11-29 13:05:05 +01:00
zdev - > msi_addr = response - > msia ;
2012-12-11 14:53:35 +01:00
zdev - > fmb_update = response - > mui ;
2012-11-29 13:05:05 +01:00
2012-11-29 12:55:21 +01:00
switch ( response - > version ) {
case 1 :
zdev - > max_bus_speed = PCIE_SPEED_5_0GT ;
break ;
default :
zdev - > max_bus_speed = PCI_SPEED_UNKNOWN ;
break ;
}
}
static int clp_query_pci_fngrp ( struct zpci_dev * zdev , u8 pfgid )
{
struct clp_req_rsp_query_pci_grp * rrb ;
int rc ;
2013-08-29 19:37:28 +02:00
rrb = clp_alloc_block ( GFP_KERNEL ) ;
2012-11-29 12:55:21 +01:00
if ( ! rrb )
return - ENOMEM ;
memset ( rrb , 0 , sizeof ( * rrb ) ) ;
rrb - > request . hdr . len = sizeof ( rrb - > request ) ;
rrb - > request . hdr . cmd = CLP_QUERY_PCI_FNGRP ;
rrb - > response . hdr . len = sizeof ( rrb - > response ) ;
rrb - > request . pfgid = pfgid ;
rc = clp_instr ( rrb ) ;
if ( ! rc & & rrb - > response . hdr . rsp = = CLP_RC_OK )
clp_store_query_pci_fngrp ( zdev , & rrb - > response ) ;
else {
2013-10-22 15:17:19 +02:00
zpci_err ( " Q PCI FGRP: \n " ) ;
zpci_err_clp ( rrb - > response . hdr . rsp , rc ) ;
2012-11-29 12:55:21 +01:00
rc = - EIO ;
}
clp_free_block ( rrb ) ;
return rc ;
}
static int clp_store_query_pci_fn ( struct zpci_dev * zdev ,
struct clp_rsp_query_pci * response )
{
int i ;
for ( i = 0 ; i < PCI_BAR_COUNT ; i + + ) {
zdev - > bars [ i ] . val = le32_to_cpu ( response - > bar [ i ] ) ;
zdev - > bars [ i ] . size = response - > bar_size [ i ] ;
}
2012-11-29 14:33:30 +01:00
zdev - > start_dma = response - > sdma ;
zdev - > end_dma = response - > edma ;
2012-11-29 12:55:21 +01:00
zdev - > pchid = response - > pchid ;
zdev - > pfgid = response - > pfgid ;
2014-04-16 16:12:05 +02:00
zdev - > pft = response - > pft ;
zdev - > vfn = response - > vfn ;
zdev - > uid = response - > uid ;
memcpy ( zdev - > pfip , response - > pfip , sizeof ( zdev - > pfip ) ) ;
if ( response - > util_str_avail ) {
memcpy ( zdev - > util_str , response - > util_str ,
sizeof ( zdev - > util_str ) ) ;
}
2012-11-29 12:55:21 +01:00
return 0 ;
}
static int clp_query_pci_fn ( struct zpci_dev * zdev , u32 fh )
{
struct clp_req_rsp_query_pci * rrb ;
int rc ;
2013-08-29 19:37:28 +02:00
rrb = clp_alloc_block ( GFP_KERNEL ) ;
2012-11-29 12:55:21 +01:00
if ( ! rrb )
return - ENOMEM ;
memset ( rrb , 0 , sizeof ( * rrb ) ) ;
rrb - > request . hdr . len = sizeof ( rrb - > request ) ;
rrb - > request . hdr . cmd = CLP_QUERY_PCI_FN ;
rrb - > response . hdr . len = sizeof ( rrb - > response ) ;
rrb - > request . fh = fh ;
rc = clp_instr ( rrb ) ;
if ( ! rc & & rrb - > response . hdr . rsp = = CLP_RC_OK ) {
rc = clp_store_query_pci_fn ( zdev , & rrb - > response ) ;
if ( rc )
goto out ;
if ( rrb - > response . pfgid )
rc = clp_query_pci_fngrp ( zdev , rrb - > response . pfgid ) ;
} else {
2013-10-22 15:17:19 +02:00
zpci_err ( " Q PCI FN: \n " ) ;
zpci_err_clp ( rrb - > response . hdr . rsp , rc ) ;
2012-11-29 12:55:21 +01:00
rc = - EIO ;
}
out :
clp_free_block ( rrb ) ;
return rc ;
}
int clp_add_pci_device ( u32 fid , u32 fh , int configured )
{
struct zpci_dev * zdev ;
int rc ;
2013-04-16 14:11:14 +02:00
zpci_dbg ( 3 , " add fid:%x, fh:%x, c:%d \n " , fid , fh , configured ) ;
2013-11-12 19:35:01 +01:00
zdev = kzalloc ( sizeof ( * zdev ) , GFP_KERNEL ) ;
if ( ! zdev )
return - ENOMEM ;
2012-11-29 12:55:21 +01:00
zdev - > fh = fh ;
zdev - > fid = fid ;
/* Query function properties and update zdev */
rc = clp_query_pci_fn ( zdev , fh ) ;
if ( rc )
goto error ;
if ( configured )
zdev - > state = ZPCI_FN_STATE_CONFIGURED ;
else
zdev - > state = ZPCI_FN_STATE_STANDBY ;
rc = zpci_create_device ( zdev ) ;
if ( rc )
goto error ;
return 0 ;
error :
2013-11-12 19:35:01 +01:00
kfree ( zdev ) ;
2012-11-29 12:55:21 +01:00
return rc ;
}
/*
* Enable / Disable a given PCI function defined by its function handle .
*/
static int clp_set_pci_fn ( u32 * fh , u8 nr_dma_as , u8 command )
{
struct clp_req_rsp_set_pci * rrb ;
2013-08-29 19:38:33 +02:00
int rc , retries = 100 ;
2012-11-29 12:55:21 +01:00
2013-08-29 19:37:28 +02:00
rrb = clp_alloc_block ( GFP_KERNEL ) ;
2012-11-29 12:55:21 +01:00
if ( ! rrb )
return - ENOMEM ;
do {
memset ( rrb , 0 , sizeof ( * rrb ) ) ;
rrb - > request . hdr . len = sizeof ( rrb - > request ) ;
rrb - > request . hdr . cmd = CLP_SET_PCI_FN ;
rrb - > response . hdr . len = sizeof ( rrb - > response ) ;
rrb - > request . fh = * fh ;
rrb - > request . oc = command ;
rrb - > request . ndas = nr_dma_as ;
rc = clp_instr ( rrb ) ;
if ( rrb - > response . hdr . rsp = = CLP_RC_SETPCIFN_BUSY ) {
retries - - ;
if ( retries < 0 )
break ;
2013-08-29 19:38:33 +02:00
msleep ( 20 ) ;
2012-11-29 12:55:21 +01:00
}
} while ( rrb - > response . hdr . rsp = = CLP_RC_SETPCIFN_BUSY ) ;
if ( ! rc & & rrb - > response . hdr . rsp = = CLP_RC_OK )
* fh = rrb - > response . fh ;
else {
2013-10-22 15:17:19 +02:00
zpci_err ( " Set PCI FN: \n " ) ;
zpci_err_clp ( rrb - > response . hdr . rsp , rc ) ;
2012-11-29 12:55:21 +01:00
rc = - EIO ;
}
clp_free_block ( rrb ) ;
return rc ;
}
int clp_enable_fh ( struct zpci_dev * zdev , u8 nr_dma_as )
{
u32 fh = zdev - > fh ;
int rc ;
rc = clp_set_pci_fn ( & fh , nr_dma_as , CLP_SET_ENABLE_PCI_FN ) ;
if ( ! rc )
/* Success -> store enabled handle in zdev */
zdev - > fh = fh ;
2013-04-16 14:11:14 +02:00
zpci_dbg ( 3 , " ena fid:%x, fh:%x, rc:%d \n " , zdev - > fid , zdev - > fh , rc ) ;
2012-11-29 12:55:21 +01:00
return rc ;
}
int clp_disable_fh ( struct zpci_dev * zdev )
{
u32 fh = zdev - > fh ;
int rc ;
if ( ! zdev_enabled ( zdev ) )
return 0 ;
rc = clp_set_pci_fn ( & fh , 0 , CLP_SET_DISABLE_PCI_FN ) ;
if ( ! rc )
/* Success -> store disabled handle in zdev */
zdev - > fh = fh ;
2013-04-16 14:11:14 +02:00
zpci_dbg ( 3 , " dis fid:%x, fh:%x, rc:%d \n " , zdev - > fid , zdev - > fh , rc ) ;
2012-11-29 12:55:21 +01:00
return rc ;
}
2013-08-29 19:37:28 +02:00
static int clp_list_pci ( struct clp_req_rsp_list_pci * rrb ,
void ( * cb ) ( struct clp_fh_list_entry * entry ) )
2012-11-29 12:55:21 +01:00
{
u64 resume_token = 0 ;
int entries , i , rc ;
do {
memset ( rrb , 0 , sizeof ( * rrb ) ) ;
rrb - > request . hdr . len = sizeof ( rrb - > request ) ;
rrb - > request . hdr . cmd = CLP_LIST_PCI ;
/* store as many entries as possible */
rrb - > response . hdr . len = CLP_BLK_SIZE - LIST_PCI_HDR_LEN ;
rrb - > request . resume_token = resume_token ;
/* Get PCI function handle list */
rc = clp_instr ( rrb ) ;
if ( rc | | rrb - > response . hdr . rsp ! = CLP_RC_OK ) {
2013-10-22 15:17:19 +02:00
zpci_err ( " List PCI FN: \n " ) ;
zpci_err_clp ( rrb - > response . hdr . rsp , rc ) ;
2012-11-29 12:55:21 +01:00
rc = - EIO ;
goto out ;
}
WARN_ON_ONCE ( rrb - > response . entry_size ! =
sizeof ( struct clp_fh_list_entry ) ) ;
entries = ( rrb - > response . hdr . len - LIST_PCI_HDR_LEN ) /
rrb - > response . entry_size ;
resume_token = rrb - > response . resume_token ;
for ( i = 0 ; i < entries ; i + + )
2013-08-29 19:37:28 +02:00
cb ( & rrb - > response . fh_list [ i ] ) ;
2012-11-29 12:55:21 +01:00
} while ( resume_token ) ;
out :
2013-08-29 19:37:28 +02:00
return rc ;
}
static void __clp_add ( struct clp_fh_list_entry * entry )
{
if ( ! entry - > vendor_id )
return ;
clp_add_pci_device ( entry - > fid , entry - > fh , entry - > config_state ) ;
}
static void __clp_rescan ( struct clp_fh_list_entry * entry )
{
struct zpci_dev * zdev ;
if ( ! entry - > vendor_id )
return ;
zdev = get_zdev_by_fid ( entry - > fid ) ;
if ( ! zdev ) {
clp_add_pci_device ( entry - > fid , entry - > fh , entry - > config_state ) ;
return ;
}
if ( ! entry - > config_state ) {
/*
* The handle is already disabled , that means no iota / irq freeing via
* the firmware interfaces anymore . Need to free resources manually
* ( DMA memory , debug , sysfs ) . . .
*/
zpci_stop_device ( zdev ) ;
}
}
2013-08-29 19:40:01 +02:00
static void __clp_update ( struct clp_fh_list_entry * entry )
{
struct zpci_dev * zdev ;
if ( ! entry - > vendor_id )
return ;
zdev = get_zdev_by_fid ( entry - > fid ) ;
if ( ! zdev )
return ;
zdev - > fh = entry - > fh ;
}
2013-08-29 19:37:28 +02:00
int clp_scan_pci_devices ( void )
{
struct clp_req_rsp_list_pci * rrb ;
int rc ;
rrb = clp_alloc_block ( GFP_KERNEL ) ;
if ( ! rrb )
return - ENOMEM ;
rc = clp_list_pci ( rrb , __clp_add ) ;
clp_free_block ( rrb ) ;
return rc ;
}
int clp_rescan_pci_devices ( void )
{
struct clp_req_rsp_list_pci * rrb ;
int rc ;
rrb = clp_alloc_block ( GFP_KERNEL ) ;
if ( ! rrb )
return - ENOMEM ;
rc = clp_list_pci ( rrb , __clp_rescan ) ;
2012-11-29 12:55:21 +01:00
clp_free_block ( rrb ) ;
return rc ;
}
2013-08-29 19:40:01 +02:00
int clp_rescan_pci_devices_simple ( void )
{
struct clp_req_rsp_list_pci * rrb ;
int rc ;
rrb = clp_alloc_block ( GFP_NOWAIT ) ;
if ( ! rrb )
return - ENOMEM ;
rc = clp_list_pci ( rrb , __clp_update ) ;
clp_free_block ( rrb ) ;
return rc ;
}