2019-05-27 08:55:05 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) 2001 Dave Engebretsen , IBM Corporation
* Copyright ( C ) 2003 Anton Blanchard < anton @ au . ibm . com > , IBM
*
* pSeries specific routines for PCI .
*/
2005-06-23 09:43:23 +10:00
# include <linux/init.h>
# include <linux/ioport.h>
2005-04-16 15:20:36 -07:00
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/string.h>
2007-03-22 23:14:07 +01:00
# include <asm/eeh.h>
2005-04-16 15:20:36 -07:00
# include <asm/pci-bridge.h>
2005-06-23 09:43:23 +10:00
# include <asm/prom.h>
2005-09-28 02:50:25 +10:00
# include <asm/ppc-pci.h>
2018-12-19 19:52:19 +11:00
# include <asm/pci.h>
2014-08-20 08:55:19 +10:00
# include "pseries.h"
2005-04-16 15:20:36 -07:00
#if 0
void pcibios_name_device ( struct pci_dev * dev )
{
struct device_node * dn ;
/*
* Add IBM loc code ( slot ) as a prefix to the device names for service
*/
dn = pci_device_to_OF_node ( dev ) ;
if ( dn ) {
2012-11-19 18:31:52 +00:00
const char * loc_code = of_get_property ( dn , " ibm,loc-code " ,
NULL ) ;
2005-04-16 15:20:36 -07:00
if ( loc_code ) {
int loc_len = strlen ( loc_code ) ;
if ( loc_len < sizeof ( dev - > dev . name ) ) {
memmove ( dev - > dev . name + loc_len + 1 , dev - > dev . name ,
sizeof ( dev - > dev . name ) - loc_len - 1 ) ;
memcpy ( dev - > dev . name , loc_code , loc_len ) ;
dev - > dev . name [ loc_len ] = ' ' ;
dev - > dev . name [ sizeof ( dev - > dev . name ) - 1 ] = ' \0 ' ;
}
}
}
2017-11-09 08:00:34 -06:00
}
2005-04-16 15:20:36 -07:00
DECLARE_PCI_FIXUP_HEADER ( PCI_ANY_ID , PCI_ANY_ID , pcibios_name_device ) ;
# endif
2017-11-09 08:00:34 -06:00
# ifdef CONFIG_PCI_IOV
2018-01-05 10:45:51 -06:00
# define MAX_VFS_FOR_MAP_PE 256
struct pe_map_bar_entry {
__be64 bar ; /* Input: Virtual Function BAR */
__be16 rid ; /* Input: Virtual Function Router ID */
__be16 pe_num ; /* Output: Virtual Function PE Number */
__be32 reserved ; /* Reserved Space */
} ;
int pseries_send_map_pe ( struct pci_dev * pdev ,
u16 num_vfs ,
struct pe_map_bar_entry * vf_pe_array )
{
struct pci_dn * pdn ;
int rc ;
unsigned long buid , addr ;
int ibm_map_pes = rtas_token ( " ibm,open-sriov-map-pe-number " ) ;
if ( ibm_map_pes = = RTAS_UNKNOWN_SERVICE )
return - EINVAL ;
pdn = pci_get_pdn ( pdev ) ;
addr = rtas_config_addr ( pdn - > busno , pdn - > devfn , 0 ) ;
buid = pdn - > phb - > buid ;
spin_lock ( & rtas_data_buf_lock ) ;
memcpy ( rtas_data_buf , vf_pe_array ,
RTAS_DATA_BUF_SIZE ) ;
rc = rtas_call ( ibm_map_pes , 5 , 1 , NULL , addr ,
BUID_HI ( buid ) , BUID_LO ( buid ) ,
rtas_data_buf ,
num_vfs * sizeof ( struct pe_map_bar_entry ) ) ;
memcpy ( vf_pe_array , rtas_data_buf , RTAS_DATA_BUF_SIZE ) ;
spin_unlock ( & rtas_data_buf_lock ) ;
if ( rc )
dev_err ( & pdev - > dev ,
" %s: Failed to associate pes PE#%lx, rc=%x \n " ,
__func__ , addr , rc ) ;
return rc ;
}
void pseries_set_pe_num ( struct pci_dev * pdev , u16 vf_index , __be16 pe_num )
{
struct pci_dn * pdn ;
pdn = pci_get_pdn ( pdev ) ;
pdn - > pe_num_map [ vf_index ] = be16_to_cpu ( pe_num ) ;
dev_dbg ( & pdev - > dev , " VF %04x:%02x:%02x.%x associated with PE#%x \n " ,
pci_domain_nr ( pdev - > bus ) ,
pdev - > bus - > number ,
PCI_SLOT ( pci_iov_virtfn_devfn ( pdev , vf_index ) ) ,
PCI_FUNC ( pci_iov_virtfn_devfn ( pdev , vf_index ) ) ,
pdn - > pe_num_map [ vf_index ] ) ;
}
int pseries_associate_pes ( struct pci_dev * pdev , u16 num_vfs )
{
struct pci_dn * pdn ;
int i , rc , vf_index ;
struct pe_map_bar_entry * vf_pe_array ;
struct resource * res ;
u64 size ;
vf_pe_array = kzalloc ( RTAS_DATA_BUF_SIZE , GFP_KERNEL ) ;
if ( ! vf_pe_array )
return - ENOMEM ;
pdn = pci_get_pdn ( pdev ) ;
/* create firmware structure to associate pes */
for ( vf_index = 0 ; vf_index < num_vfs ; vf_index + + ) {
pdn - > pe_num_map [ vf_index ] = IODA_INVALID_PE ;
for ( i = 0 ; i < PCI_SRIOV_NUM_BARS ; i + + ) {
res = & pdev - > resource [ i + PCI_IOV_RESOURCES ] ;
if ( ! res - > parent )
continue ;
size = pcibios_iov_resource_alignment ( pdev , i +
PCI_IOV_RESOURCES ) ;
vf_pe_array [ vf_index ] . bar =
cpu_to_be64 ( res - > start + size * vf_index ) ;
vf_pe_array [ vf_index ] . rid =
cpu_to_be16 ( ( pci_iov_virtfn_bus ( pdev , vf_index )
< < 8 ) | pci_iov_virtfn_devfn ( pdev ,
vf_index ) ) ;
vf_pe_array [ vf_index ] . pe_num =
cpu_to_be16 ( IODA_INVALID_PE ) ;
}
}
rc = pseries_send_map_pe ( pdev , num_vfs , vf_pe_array ) ;
/* Only zero is success */
if ( ! rc )
for ( vf_index = 0 ; vf_index < num_vfs ; vf_index + + )
pseries_set_pe_num ( pdev , vf_index ,
vf_pe_array [ vf_index ] . pe_num ) ;
kfree ( vf_pe_array ) ;
return rc ;
}
int pseries_pci_sriov_enable ( struct pci_dev * pdev , u16 num_vfs )
{
struct pci_dn * pdn ;
int rc ;
const int * max_vfs ;
int max_config_vfs ;
struct device_node * dn = pci_device_to_OF_node ( pdev ) ;
max_vfs = of_get_property ( dn , " ibm,number-of-configurable-vfs " , NULL ) ;
if ( ! max_vfs )
return - EINVAL ;
/* First integer stores max config */
max_config_vfs = of_read_number ( & max_vfs [ 0 ] , 1 ) ;
if ( max_config_vfs < num_vfs & & num_vfs > MAX_VFS_FOR_MAP_PE ) {
dev_err ( & pdev - > dev ,
" Num VFs %x > %x Configurable VFs \n " ,
num_vfs , ( num_vfs > MAX_VFS_FOR_MAP_PE ) ?
MAX_VFS_FOR_MAP_PE : max_config_vfs ) ;
return - EINVAL ;
}
pdn = pci_get_pdn ( pdev ) ;
pdn - > pe_num_map = kmalloc_array ( num_vfs ,
sizeof ( * pdn - > pe_num_map ) ,
GFP_KERNEL ) ;
if ( ! pdn - > pe_num_map )
return - ENOMEM ;
rc = pseries_associate_pes ( pdev , num_vfs ) ;
/* Anything other than zero is failure */
if ( rc ) {
dev_err ( & pdev - > dev , " Failure to enable sriov: %x \n " , rc ) ;
kfree ( pdn - > pe_num_map ) ;
} else {
pci_vf_drivers_autoprobe ( pdev , false ) ;
}
return rc ;
}
2017-11-09 08:00:34 -06:00
int pseries_pcibios_sriov_enable ( struct pci_dev * pdev , u16 num_vfs )
{
/* Allocate PCI data */
2019-08-21 16:26:54 +10:00
add_sriov_vf_pdns ( pdev ) ;
2018-01-05 10:45:51 -06:00
return pseries_pci_sriov_enable ( pdev , num_vfs ) ;
2017-11-09 08:00:34 -06:00
}
int pseries_pcibios_sriov_disable ( struct pci_dev * pdev )
{
2018-01-05 10:45:51 -06:00
struct pci_dn * pdn ;
pdn = pci_get_pdn ( pdev ) ;
/* Releasing pe_num_map */
kfree ( pdn - > pe_num_map ) ;
2017-11-09 08:00:34 -06:00
/* Release PCI data */
2019-08-21 16:26:54 +10:00
remove_sriov_vf_pdns ( pdev ) ;
2017-11-09 08:00:35 -06:00
pci_vf_drivers_autoprobe ( pdev , true ) ;
2017-11-09 08:00:34 -06:00
return 0 ;
}
# endif
2005-04-16 15:20:36 -07:00
static void __init pSeries_request_regions ( void )
{
if ( ! isa_io_base )
return ;
request_region ( 0x20 , 0x20 , " pic1 " ) ;
request_region ( 0xa0 , 0x20 , " pic2 " ) ;
request_region ( 0x00 , 0x20 , " dma1 " ) ;
request_region ( 0x40 , 0x20 , " timer " ) ;
request_region ( 0x80 , 0x10 , " dma page reg " ) ;
request_region ( 0xc0 , 0x20 , " dma2 " ) ;
}
void __init pSeries_final_fixup ( void )
{
2018-12-19 19:52:19 +11:00
struct pci_controller * hose ;
2005-04-16 15:20:36 -07:00
pSeries_request_regions ( ) ;
2019-08-16 14:48:10 +10:00
eeh_show_enabled ( ) ;
2017-11-09 08:00:34 -06:00
# ifdef CONFIG_PCI_IOV
ppc_md . pcibios_sriov_enable = pseries_pcibios_sriov_enable ;
ppc_md . pcibios_sriov_disable = pseries_pcibios_sriov_disable ;
# endif
2018-12-19 19:52:19 +11:00
list_for_each_entry ( hose , & hose_list , list_node ) {
struct device_node * dn = hose - > dn , * nvdn ;
while ( 1 ) {
dn = of_find_all_nodes ( dn ) ;
if ( ! dn )
break ;
nvdn = of_parse_phandle ( dn , " ibm,nvlink " , 0 ) ;
if ( ! nvdn )
continue ;
if ( ! of_device_is_compatible ( nvdn , " ibm,npu-link " ) )
continue ;
if ( ! of_device_is_compatible ( nvdn - > parent ,
" ibm,power9-npu " ) )
continue ;
2019-01-14 16:47:45 -08:00
# ifdef CONFIG_PPC_POWERNV
2018-12-19 19:52:19 +11:00
WARN_ON_ONCE ( pnv_npu2_init ( hose ) ) ;
2019-01-14 16:47:45 -08:00
# endif
2018-12-19 19:52:19 +11:00
break ;
}
}
2005-04-16 15:20:36 -07:00
}
/*
* Assume the winbond 82 c105 is the IDE controller on a
2007-01-04 18:31:55 +01:00
* p610 / p615 / p630 . We should probably be more careful in case
2005-04-16 15:20:36 -07:00
* someone tries to plug in a similar adapter .
*/
static void fixup_winbond_82c105 ( struct pci_dev * dev )
{
int i ;
unsigned int reg ;
2006-03-28 23:15:54 +11:00
if ( ! machine_is ( pseries ) )
2005-04-16 15:20:36 -07:00
return ;
printk ( " Using INTC for W82c105 IDE controller. \n " ) ;
pci_read_config_dword ( dev , 0x40 , & reg ) ;
/* Enable LEGIRQ to use INTC instead of ISA interrupts */
pci_write_config_dword ( dev , 0x40 , reg | ( 1 < < 11 ) ) ;
for ( i = 0 ; i < DEVICE_COUNT_RESOURCE ; + + i ) {
/* zap the 2nd function of the winbond chip */
if ( dev - > resource [ i ] . flags & IORESOURCE_IO
& & dev - > bus - > number = = 0 & & dev - > devfn = = 0x81 )
dev - > resource [ i ] . flags & = ~ IORESOURCE_IO ;
2007-02-10 21:38:37 +01:00
if ( dev - > resource [ i ] . start = = 0 & & dev - > resource [ i ] . end ) {
dev - > resource [ i ] . flags = 0 ;
dev - > resource [ i ] . end = 0 ;
}
2005-04-16 15:20:36 -07:00
}
}
DECLARE_PCI_FIXUP_HEADER ( PCI_VENDOR_ID_WINBOND , PCI_DEVICE_ID_WINBOND_82C105 ,
fixup_winbond_82c105 ) ;
2013-05-03 12:43:12 +00:00
int pseries_root_bridge_prepare ( struct pci_host_bridge * bridge )
{
struct device_node * dn , * pdn ;
struct pci_bus * bus ;
2014-01-17 11:56:51 -02:00
u32 pcie_link_speed_stats [ 2 ] ;
int rc ;
2013-05-03 12:43:12 +00:00
bus = bridge - > bus ;
powerpc/pseries: use pci_host_bridge.release_fn() to kfree(phb)
This patch leverages 'struct pci_host_bridge' from the PCI subsystem
in order to free the pci_controller only after the last reference to
its devices is dropped (avoiding an oops in pcibios_release_device()
if the last reference is dropped after pcibios_free_controller()).
The patch relies on pci_host_bridge.release_fn() (and .release_data),
which is called automatically by the PCI subsystem when the root bus
is released (i.e., the last reference is dropped). Those fields are
set via pci_set_host_bridge_release() (e.g. in the platform-specific
implementation of pcibios_root_bridge_prepare()).
It introduces the 'pcibios_free_controller_deferred()' .release_fn()
and it expects .release_data to hold a pointer to the pci_controller.
The function implictly calls 'pcibios_free_controller()', so an user
must *NOT* explicitly call it if using the new _deferred() callback.
The functionality is enabled for pseries (although it isn't platform
specific, and may be used by cxl).
Details on not-so-elegant design choices:
- Use 'pci_host_bridge.release_data' field as pointer to associated
'struct pci_controller' so *not* to 'pci_bus_to_host(bridge->bus)'
in pcibios_free_controller_deferred().
That's because pci_remove_root_bus() sets 'host_bridge->bus = NULL'
(so, if the last reference is released after pci_remove_root_bus()
runs, which eventually reaches pcibios_free_controller_deferred(),
that would hit a null pointer dereference).
The cxl/vphb.c code calls pci_remove_root_bus(), and the cxl folks
are interested in this fix.
Test-case #1 (hold references)
# ls -ld /sys/block/sd* | grep -m1 0021:01:00.0
<...> /sys/block/sdaa -> ../devices/pci0021:01/0021:01:00.0/<...>
# ls -ld /sys/block/sd* | grep -m1 0021:01:00.1
<...> /sys/block/sdab -> ../devices/pci0021:01/0021:01:00.1/<...>
# cat >/dev/sdaa & pid1=$!
# cat >/dev/sdab & pid2=$!
# drmgr -w 5 -d 1 -c phb -s 'PHB 33' -r
Validating PHB DLPAR capability...yes.
[ 594.306719] pci_hp_remove_devices: PCI: Removing devices on bus 0021:01
[ 594.306738] pci_hp_remove_devices: Removing 0021:01:00.0...
...
[ 598.236381] pci_hp_remove_devices: Removing 0021:01:00.1...
...
[ 611.972077] pci_bus 0021:01: busn_res: [bus 01-ff] is released
[ 611.972140] rpadlpar_io: slot PHB 33 removed
# kill -9 $pid1
# kill -9 $pid2
[ 632.918088] pcibios_free_controller_deferred: domain 33, dynamic 1
Test-case #2 (don't hold references)
# drmgr -w 5 -d 1 -c phb -s 'PHB 33' -r
Validating PHB DLPAR capability...yes.
[ 916.357363] pci_hp_remove_devices: PCI: Removing devices on bus 0021:01
[ 916.357386] pci_hp_remove_devices: Removing 0021:01:00.0...
...
[ 920.566527] pci_hp_remove_devices: Removing 0021:01:00.1...
...
[ 933.955873] pci_bus 0021:01: busn_res: [bus 01-ff] is released
[ 933.955977] pcibios_free_controller_deferred: domain 33, dynamic 1
[ 933.955999] rpadlpar_io: slot PHB 33 removed
Suggested-By: Gavin Shan <gwshan@linux.vnet.ibm.com>
Signed-off-by: Mauricio Faria de Oliveira <mauricfo@linux.vnet.ibm.com>
Reviewed-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Reviewed-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com>
Tested-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com> # cxl
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2016-08-11 17:25:40 -03:00
/* Rely on the pcibios_free_controller_deferred() callback. */
pci_set_host_bridge_release ( bridge , pcibios_free_controller_deferred ,
( void * ) pci_bus_to_host ( bus ) ) ;
2013-05-03 12:43:12 +00:00
dn = pcibios_get_phb_of_node ( bus ) ;
if ( ! dn )
return 0 ;
for ( pdn = dn ; pdn ! = NULL ; pdn = of_get_next_parent ( pdn ) ) {
2014-01-17 11:56:51 -02:00
rc = of_property_read_u32_array ( pdn ,
" ibm,pcie-link-speed-stats " ,
& pcie_link_speed_stats [ 0 ] , 2 ) ;
if ( ! rc )
2013-05-03 12:43:12 +00:00
break ;
}
of_node_put ( pdn ) ;
2014-01-17 11:56:51 -02:00
if ( rc ) {
2014-11-03 08:18:06 +11:00
pr_debug ( " no ibm,pcie-link-speed-stats property \n " ) ;
2013-05-03 12:43:12 +00:00
return 0 ;
}
2014-01-17 11:56:51 -02:00
switch ( pcie_link_speed_stats [ 0 ] ) {
2013-05-03 12:43:12 +00:00
case 0x01 :
bus - > max_bus_speed = PCIE_SPEED_2_5GT ;
break ;
case 0x02 :
bus - > max_bus_speed = PCIE_SPEED_5_0GT ;
break ;
2014-01-17 11:56:52 -02:00
case 0x04 :
bus - > max_bus_speed = PCIE_SPEED_8_0GT ;
break ;
2013-05-03 12:43:12 +00:00
default :
bus - > max_bus_speed = PCI_SPEED_UNKNOWN ;
break ;
}
2014-01-17 11:56:51 -02:00
switch ( pcie_link_speed_stats [ 1 ] ) {
2013-05-03 12:43:12 +00:00
case 0x01 :
bus - > cur_bus_speed = PCIE_SPEED_2_5GT ;
break ;
case 0x02 :
bus - > cur_bus_speed = PCIE_SPEED_5_0GT ;
break ;
2014-01-17 11:56:52 -02:00
case 0x04 :
bus - > cur_bus_speed = PCIE_SPEED_8_0GT ;
break ;
2013-05-03 12:43:12 +00:00
default :
bus - > cur_bus_speed = PCI_SPEED_UNKNOWN ;
break ;
}
return 0 ;
}