2015-05-27 16:07:18 +10:00
/*
* Copyright 2014 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/pci.h>
# include <misc/cxl.h>
# include "cxl.h"
static int cxl_pci_probe_mode ( struct pci_bus * bus )
{
return PCI_PROBE_NORMAL ;
}
static int cxl_setup_msi_irqs ( struct pci_dev * pdev , int nvec , int type )
{
return - ENODEV ;
}
static void cxl_teardown_msi_irqs ( struct pci_dev * pdev )
{
/*
* MSI should never be set but need still need to provide this call
* back .
*/
}
static bool cxl_pci_enable_device_hook ( struct pci_dev * dev )
{
struct pci_controller * phb ;
struct cxl_afu * afu ;
2018-06-28 12:05:08 +02:00
struct cxl_context * ctx ;
2015-05-27 16:07:18 +10:00
phb = pci_bus_to_host ( dev - > bus ) ;
afu = ( struct cxl_afu * ) phb - > private_data ;
2015-09-07 10:52:58 +10:00
2016-03-04 12:26:41 +01:00
if ( ! cxl_ops - > link_ok ( afu - > adapter , afu ) ) {
2015-09-07 10:52:58 +10:00
dev_warn ( & dev - > dev , " %s: Device link is down, refusing to enable AFU \n " , __func__ ) ;
return false ;
}
2015-05-27 16:07:18 +10:00
set_dma_offset ( & dev - > dev , PAGE_OFFSET ) ;
2018-06-28 12:05:08 +02:00
/*
* Allocate a context to do cxl things too . If we eventually do real
* DMA ops , we ' ll need a default context to attach them to
*/
ctx = cxl_dev_context_init ( dev ) ;
if ( IS_ERR ( ctx ) )
return false ;
dev - > dev . archdata . cxl_ctx = ctx ;
return ( cxl_ops - > afu_check_and_enable ( afu ) = = 0 ) ;
}
static void cxl_pci_disable_device ( struct pci_dev * dev )
{
struct cxl_context * ctx = cxl_get_context ( dev ) ;
if ( ctx ) {
if ( ctx - > status = = STARTED ) {
dev_err ( & dev - > dev , " Default context started \n " ) ;
return ;
}
dev - > dev . archdata . cxl_ctx = NULL ;
cxl_release_context ( ctx ) ;
}
2015-05-27 16:07:18 +10:00
}
static resource_size_t cxl_pci_window_alignment ( struct pci_bus * bus ,
unsigned long type )
{
return 1 ;
}
static void cxl_pci_reset_secondary_bus ( struct pci_dev * dev )
{
/* Should we do an AFU reset here ? */
}
static int cxl_pcie_cfg_record ( u8 bus , u8 devfn )
{
return ( bus < < 8 ) + devfn ;
}
2016-12-09 17:18:50 +11:00
static inline struct cxl_afu * pci_bus_to_afu ( struct pci_bus * bus )
2015-05-27 16:07:18 +10:00
{
2016-12-09 17:18:50 +11:00
struct pci_controller * phb = bus ? pci_bus_to_host ( bus ) : NULL ;
2015-05-27 16:07:18 +10:00
2016-12-09 17:18:50 +11:00
return phb ? phb - > private_data : NULL ;
}
2017-02-06 12:07:17 +11:00
static void cxl_afu_configured_put ( struct cxl_afu * afu )
{
atomic_dec_if_positive ( & afu - > configured_state ) ;
}
static bool cxl_afu_configured_get ( struct cxl_afu * afu )
{
return atomic_inc_unless_negative ( & afu - > configured_state ) ;
}
2016-12-09 17:18:50 +11:00
static inline int cxl_pcie_config_info ( struct pci_bus * bus , unsigned int devfn ,
struct cxl_afu * afu , int * _record )
{
int record ;
2015-06-29 16:05:11 +05:30
2016-03-04 12:26:40 +01:00
record = cxl_pcie_cfg_record ( bus - > number , devfn ) ;
if ( record > afu - > crs_num )
2015-05-27 16:07:18 +10:00
return PCIBIOS_DEVICE_NOT_FOUND ;
2016-03-04 12:26:40 +01:00
* _record = record ;
2015-05-27 16:07:18 +10:00
return 0 ;
}
static int cxl_pcie_read_config ( struct pci_bus * bus , unsigned int devfn ,
int offset , int len , u32 * val )
{
2016-03-04 12:26:40 +01:00
int rc , record ;
struct cxl_afu * afu ;
u8 val8 ;
u16 val16 ;
u32 val32 ;
2015-05-27 16:07:18 +10:00
2016-12-09 17:18:50 +11:00
afu = pci_bus_to_afu ( bus ) ;
/* Grab a reader lock on afu. */
2017-02-06 12:07:17 +11:00
if ( afu = = NULL | | ! cxl_afu_configured_get ( afu ) )
2016-12-09 17:18:50 +11:00
return PCIBIOS_DEVICE_NOT_FOUND ;
rc = cxl_pcie_config_info ( bus , devfn , afu , & record ) ;
2015-05-27 16:07:18 +10:00
if ( rc )
2016-12-09 17:18:50 +11:00
goto out ;
2015-05-27 16:07:18 +10:00
2016-03-04 12:26:40 +01:00
switch ( len ) {
case 1 :
rc = cxl_ops - > afu_cr_read8 ( afu , record , offset , & val8 ) ;
* val = val8 ;
break ;
case 2 :
rc = cxl_ops - > afu_cr_read16 ( afu , record , offset , & val16 ) ;
* val = val16 ;
break ;
case 4 :
rc = cxl_ops - > afu_cr_read32 ( afu , record , offset , & val32 ) ;
* val = val32 ;
break ;
default :
WARN_ON ( 1 ) ;
}
2016-12-09 17:18:50 +11:00
out :
2017-02-06 12:07:17 +11:00
cxl_afu_configured_put ( afu ) ;
2016-12-09 17:18:50 +11:00
return rc ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL ;
2015-05-27 16:07:18 +10:00
}
static int cxl_pcie_write_config ( struct pci_bus * bus , unsigned int devfn ,
int offset , int len , u32 val )
{
2016-03-04 12:26:40 +01:00
int rc , record ;
struct cxl_afu * afu ;
2015-05-27 16:07:18 +10:00
2016-12-09 17:18:50 +11:00
afu = pci_bus_to_afu ( bus ) ;
/* Grab a reader lock on afu. */
2017-02-06 12:07:17 +11:00
if ( afu = = NULL | | ! cxl_afu_configured_get ( afu ) )
2016-12-09 17:18:50 +11:00
return PCIBIOS_DEVICE_NOT_FOUND ;
rc = cxl_pcie_config_info ( bus , devfn , afu , & record ) ;
2015-05-27 16:07:18 +10:00
if ( rc )
2016-12-09 17:18:50 +11:00
goto out ;
2015-05-27 16:07:18 +10:00
2016-03-04 12:26:40 +01:00
switch ( len ) {
case 1 :
rc = cxl_ops - > afu_cr_write8 ( afu , record , offset , val & 0xff ) ;
break ;
case 2 :
rc = cxl_ops - > afu_cr_write16 ( afu , record , offset , val & 0xffff ) ;
break ;
case 4 :
rc = cxl_ops - > afu_cr_write32 ( afu , record , offset , val ) ;
break ;
default :
WARN_ON ( 1 ) ;
}
2015-05-27 16:07:18 +10:00
2016-12-09 17:18:50 +11:00
out :
2017-02-06 12:07:17 +11:00
cxl_afu_configured_put ( afu ) ;
2016-12-09 17:18:50 +11:00
return rc ? PCIBIOS_SET_FAILED : PCIBIOS_SUCCESSFUL ;
2015-05-27 16:07:18 +10:00
}
static struct pci_ops cxl_pcie_pci_ops =
{
. read = cxl_pcie_read_config ,
. write = cxl_pcie_write_config ,
} ;
static struct pci_controller_ops cxl_pci_controller_ops =
{
. probe_mode = cxl_pci_probe_mode ,
. enable_device_hook = cxl_pci_enable_device_hook ,
2018-06-28 12:05:08 +02:00
. disable_device = cxl_pci_disable_device ,
. release_device = cxl_pci_disable_device ,
2015-05-27 16:07:18 +10:00
. window_alignment = cxl_pci_window_alignment ,
. reset_secondary_bus = cxl_pci_reset_secondary_bus ,
. setup_msi_irqs = cxl_setup_msi_irqs ,
. teardown_msi_irqs = cxl_teardown_msi_irqs ,
} ;
int cxl_pci_vphb_add ( struct cxl_afu * afu )
{
2016-06-15 16:42:16 +02:00
struct pci_controller * phb ;
2016-03-04 12:26:40 +01:00
struct device_node * vphb_dn ;
struct device * parent ;
2016-07-14 07:17:05 +10:00
/*
* If there are no AFU configuration records we won ' t have anything to
* expose under the vPHB , so skip creating one , returning success since
* this is still a valid case . This will also opt us out of EEH
* handling since we won ' t have anything special to do if there are no
* kernel drivers attached to the vPHB , and EEH handling is not yet
* supported in the peer model .
*/
if ( ! afu - > crs_num )
return 0 ;
2016-06-15 16:42:16 +02:00
/* The parent device is the adapter. Reuse the device node of
* the adapter .
* We don ' t seem to care what device node is used for the vPHB ,
* but tools such as lsvpd walk up the device parents looking
* for a valid location code , so we might as well show devices
* attached to the adapter as being located on that adapter .
*/
parent = afu - > adapter - > dev . parent ;
vphb_dn = parent - > of_node ;
2015-05-27 16:07:18 +10:00
/* Alloc and setup PHB data structure */
2016-03-04 12:26:40 +01:00
phb = pcibios_alloc_controller ( vphb_dn ) ;
2015-05-27 16:07:18 +10:00
if ( ! phb )
return - ENODEV ;
/* Setup parent in sysfs */
2016-03-04 12:26:40 +01:00
phb - > parent = parent ;
2015-05-27 16:07:18 +10:00
/* Setup the PHB using arch provided callback */
phb - > ops = & cxl_pcie_pci_ops ;
2016-03-04 12:26:40 +01:00
phb - > cfg_addr = NULL ;
2016-07-22 19:01:36 +10:00
phb - > cfg_data = NULL ;
2015-05-27 16:07:18 +10:00
phb - > private_data = afu ;
phb - > controller_ops = cxl_pci_controller_ops ;
/* Scan the bus */
pcibios_scan_phb ( phb ) ;
if ( phb - > bus = = NULL )
return - ENXIO ;
2016-08-18 17:35:14 +10:00
/* Set release hook on root bus */
pci_set_host_bridge_release ( to_pci_host_bridge ( phb - > bus - > bridge ) ,
pcibios_free_controller_deferred ,
( void * ) phb ) ;
2015-05-27 16:07:18 +10:00
/* Claim resources. This might need some rework as well depending
* whether we are doing probe - only or not , like assigning unassigned
* resources etc . . .
*/
pcibios_claim_one_bus ( phb - > bus ) ;
/* Add probed PCI devices to the device model */
pci_bus_add_devices ( phb - > bus ) ;
afu - > phb = phb ;
return 0 ;
}
void cxl_pci_vphb_remove ( struct cxl_afu * afu )
{
struct pci_controller * phb ;
/* If there is no configuration record we won't have one of these */
if ( ! afu | | ! afu - > phb )
return ;
phb = afu - > phb ;
2015-10-13 15:09:44 +11:00
afu - > phb = NULL ;
2015-05-27 16:07:18 +10:00
pci_remove_root_bus ( phb - > bus ) ;
2016-08-18 17:35:14 +10:00
/*
* We don ' t free phb here - that ' s handled by
* pcibios_free_controller_deferred ( )
*/
2015-05-27 16:07:18 +10:00
}
2016-02-29 11:10:53 +05:30
bool cxl_pci_is_vphb_device ( struct pci_dev * dev )
{
struct pci_controller * phb ;
phb = pci_bus_to_host ( dev - > bus ) ;
2018-06-28 12:05:05 +02:00
return ( phb - > ops = = & cxl_pcie_pci_ops ) ;
2016-02-29 11:10:53 +05:30
}
2015-05-27 16:07:18 +10:00
struct cxl_afu * cxl_pci_to_afu ( struct pci_dev * dev )
{
struct pci_controller * phb ;
phb = pci_bus_to_host ( dev - > bus ) ;
2018-06-28 12:05:05 +02:00
return ( struct cxl_afu * ) phb - > private_data ;
2015-05-27 16:07:18 +10:00
}
EXPORT_SYMBOL_GPL ( cxl_pci_to_afu ) ;
unsigned int cxl_pci_to_cfg_record ( struct pci_dev * dev )
{
return cxl_pcie_cfg_record ( dev - > bus - > number , dev - > devfn ) ;
}
EXPORT_SYMBOL_GPL ( cxl_pci_to_cfg_record ) ;