2011-09-27 17:57:13 +04:00
/*
* drivers / pci / ats . c
*
* Copyright ( C ) 2009 Intel Corporation , Yu Zhao < yu . zhao @ intel . com >
2011-09-27 17:57:15 +04:00
* Copyright ( C ) 2011 Advanced Micro Devices ,
2011-09-27 17:57:13 +04:00
*
* PCI Express I / O Virtualization ( IOV ) support .
* Address Translation Service 1.0
2011-09-27 17:57:15 +04:00
* Page Request Interface added by Joerg Roedel < joerg . roedel @ amd . com >
2011-09-27 17:57:16 +04:00
* PASID support added by Joerg Roedel < joerg . roedel @ amd . com >
2011-09-27 17:57:13 +04:00
*/
2011-05-27 17:37:25 +04:00
# include <linux/export.h>
2011-09-27 17:57:13 +04:00
# include <linux/pci-ats.h>
# include <linux/pci.h>
2011-11-29 23:20:23 +04:00
# include <linux/slab.h>
2011-09-27 17:57:13 +04:00
# include "pci.h"
static int ats_alloc_one ( struct pci_dev * dev , int ps )
{
int pos ;
u16 cap ;
struct pci_ats * ats ;
pos = pci_find_ext_capability ( dev , PCI_EXT_CAP_ID_ATS ) ;
if ( ! pos )
return - ENODEV ;
ats = kzalloc ( sizeof ( * ats ) , GFP_KERNEL ) ;
if ( ! ats )
return - ENOMEM ;
ats - > pos = pos ;
ats - > stu = ps ;
pci_read_config_word ( dev , pos + PCI_ATS_CAP , & cap ) ;
ats - > qdep = PCI_ATS_CAP_QDEP ( cap ) ? PCI_ATS_CAP_QDEP ( cap ) :
PCI_ATS_MAX_QDEP ;
dev - > ats = ats ;
return 0 ;
}
static void ats_free_one ( struct pci_dev * dev )
{
kfree ( dev - > ats ) ;
dev - > ats = NULL ;
}
/**
* pci_enable_ats - enable the ATS capability
* @ dev : the PCI device
* @ ps : the IOMMU page shift
*
* Returns 0 on success , or negative on failure .
*/
int pci_enable_ats ( struct pci_dev * dev , int ps )
{
int rc ;
u16 ctrl ;
BUG_ON ( dev - > ats & & dev - > ats - > is_enabled ) ;
if ( ps < PCI_ATS_MIN_STU )
return - EINVAL ;
if ( dev - > is_physfn | | dev - > is_virtfn ) {
struct pci_dev * pdev = dev - > is_physfn ? dev : dev - > physfn ;
mutex_lock ( & pdev - > sriov - > lock ) ;
if ( pdev - > ats )
rc = pdev - > ats - > stu = = ps ? 0 : - EINVAL ;
else
rc = ats_alloc_one ( pdev , ps ) ;
if ( ! rc )
pdev - > ats - > ref_cnt + + ;
mutex_unlock ( & pdev - > sriov - > lock ) ;
if ( rc )
return rc ;
}
if ( ! dev - > is_physfn ) {
rc = ats_alloc_one ( dev , ps ) ;
if ( rc )
return rc ;
}
ctrl = PCI_ATS_CTRL_ENABLE ;
if ( ! dev - > is_virtfn )
ctrl | = PCI_ATS_CTRL_STU ( ps - PCI_ATS_MIN_STU ) ;
pci_write_config_word ( dev , dev - > ats - > pos + PCI_ATS_CTRL , ctrl ) ;
dev - > ats - > is_enabled = 1 ;
return 0 ;
}
2011-09-27 17:57:14 +04:00
EXPORT_SYMBOL_GPL ( pci_enable_ats ) ;
2011-09-27 17:57:13 +04:00
/**
* pci_disable_ats - disable the ATS capability
* @ dev : the PCI device
*/
void pci_disable_ats ( struct pci_dev * dev )
{
u16 ctrl ;
BUG_ON ( ! dev - > ats | | ! dev - > ats - > is_enabled ) ;
pci_read_config_word ( dev , dev - > ats - > pos + PCI_ATS_CTRL , & ctrl ) ;
ctrl & = ~ PCI_ATS_CTRL_ENABLE ;
pci_write_config_word ( dev , dev - > ats - > pos + PCI_ATS_CTRL , ctrl ) ;
dev - > ats - > is_enabled = 0 ;
if ( dev - > is_physfn | | dev - > is_virtfn ) {
struct pci_dev * pdev = dev - > is_physfn ? dev : dev - > physfn ;
mutex_lock ( & pdev - > sriov - > lock ) ;
pdev - > ats - > ref_cnt - - ;
if ( ! pdev - > ats - > ref_cnt )
ats_free_one ( pdev ) ;
mutex_unlock ( & pdev - > sriov - > lock ) ;
}
if ( ! dev - > is_physfn )
ats_free_one ( dev ) ;
}
2011-09-27 17:57:14 +04:00
EXPORT_SYMBOL_GPL ( pci_disable_ats ) ;
2011-09-27 17:57:13 +04:00
2011-12-17 17:24:40 +04:00
void pci_restore_ats_state ( struct pci_dev * dev )
{
u16 ctrl ;
if ( ! pci_ats_enabled ( dev ) )
return ;
if ( ! pci_find_ext_capability ( dev , PCI_EXT_CAP_ID_ATS ) )
BUG ( ) ;
ctrl = PCI_ATS_CTRL_ENABLE ;
if ( ! dev - > is_virtfn )
ctrl | = PCI_ATS_CTRL_STU ( dev - > ats - > stu - PCI_ATS_MIN_STU ) ;
pci_write_config_word ( dev , dev - > ats - > pos + PCI_ATS_CTRL , ctrl ) ;
}
EXPORT_SYMBOL_GPL ( pci_restore_ats_state ) ;
2011-09-27 17:57:13 +04:00
/**
* pci_ats_queue_depth - query the ATS Invalidate Queue Depth
* @ dev : the PCI device
*
* Returns the queue depth on success , or negative on failure .
*
* The ATS spec uses 0 in the Invalidate Queue Depth field to
* indicate that the function can accept 32 Invalidate Request .
* But here we use the ` real ' values ( i . e . 1 ~ 32 ) for the Queue
* Depth ; and 0 indicates the function shares the Queue with
* other functions ( doesn ' t exclusively own a Queue ) .
*/
int pci_ats_queue_depth ( struct pci_dev * dev )
{
int pos ;
u16 cap ;
if ( dev - > is_virtfn )
return 0 ;
if ( dev - > ats )
return dev - > ats - > qdep ;
pos = pci_find_ext_capability ( dev , PCI_EXT_CAP_ID_ATS ) ;
if ( ! pos )
return - ENODEV ;
pci_read_config_word ( dev , pos + PCI_ATS_CAP , & cap ) ;
return PCI_ATS_CAP_QDEP ( cap ) ? PCI_ATS_CAP_QDEP ( cap ) :
PCI_ATS_MAX_QDEP ;
}
2011-09-27 17:57:14 +04:00
EXPORT_SYMBOL_GPL ( pci_ats_queue_depth ) ;
2011-09-27 17:57:15 +04:00
# ifdef CONFIG_PCI_PRI
/**
* pci_enable_pri - Enable PRI capability
* @ pdev : PCI device structure
*
* Returns 0 on success , negative value on error
*/
int pci_enable_pri ( struct pci_dev * pdev , u32 reqs )
{
u16 control , status ;
u32 max_requests ;
int pos ;
2011-11-03 00:07:15 +04:00
pos = pci_find_ext_capability ( pdev , PCI_EXT_CAP_ID_PRI ) ;
2011-09-27 17:57:15 +04:00
if ( ! pos )
return - EINVAL ;
2011-11-11 21:07:36 +04:00
pci_read_config_word ( pdev , pos + PCI_PRI_CTRL , & control ) ;
pci_read_config_word ( pdev , pos + PCI_PRI_STATUS , & status ) ;
if ( ( control & PCI_PRI_CTRL_ENABLE ) | |
! ( status & PCI_PRI_STATUS_STOPPED ) )
2011-09-27 17:57:15 +04:00
return - EBUSY ;
2011-11-11 21:07:36 +04:00
pci_read_config_dword ( pdev , pos + PCI_PRI_MAX_REQ , & max_requests ) ;
2011-09-27 17:57:15 +04:00
reqs = min ( max_requests , reqs ) ;
2011-11-11 21:07:36 +04:00
pci_write_config_dword ( pdev , pos + PCI_PRI_ALLOC_REQ , reqs ) ;
2011-09-27 17:57:15 +04:00
2011-11-11 21:07:36 +04:00
control | = PCI_PRI_CTRL_ENABLE ;
pci_write_config_word ( pdev , pos + PCI_PRI_CTRL , control ) ;
2011-09-27 17:57:15 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( pci_enable_pri ) ;
/**
* pci_disable_pri - Disable PRI capability
* @ pdev : PCI device structure
*
* Only clears the enabled - bit , regardless of its former value
*/
void pci_disable_pri ( struct pci_dev * pdev )
{
u16 control ;
int pos ;
2011-11-03 00:07:15 +04:00
pos = pci_find_ext_capability ( pdev , PCI_EXT_CAP_ID_PRI ) ;
2011-09-27 17:57:15 +04:00
if ( ! pos )
return ;
2011-11-11 21:07:36 +04:00
pci_read_config_word ( pdev , pos + PCI_PRI_CTRL , & control ) ;
control & = ~ PCI_PRI_CTRL_ENABLE ;
pci_write_config_word ( pdev , pos + PCI_PRI_CTRL , control ) ;
2011-09-27 17:57:15 +04:00
}
EXPORT_SYMBOL_GPL ( pci_disable_pri ) ;
/**
* pci_pri_enabled - Checks if PRI capability is enabled
* @ pdev : PCI device structure
*
* Returns true if PRI is enabled on the device , false otherwise
*/
bool pci_pri_enabled ( struct pci_dev * pdev )
{
u16 control ;
int pos ;
2011-11-03 00:07:15 +04:00
pos = pci_find_ext_capability ( pdev , PCI_EXT_CAP_ID_PRI ) ;
2011-09-27 17:57:15 +04:00
if ( ! pos )
return false ;
2011-11-11 21:07:36 +04:00
pci_read_config_word ( pdev , pos + PCI_PRI_CTRL , & control ) ;
2011-09-27 17:57:15 +04:00
2011-11-11 21:07:36 +04:00
return ( control & PCI_PRI_CTRL_ENABLE ) ? true : false ;
2011-09-27 17:57:15 +04:00
}
EXPORT_SYMBOL_GPL ( pci_pri_enabled ) ;
/**
* pci_reset_pri - Resets device ' s PRI state
* @ pdev : PCI device structure
*
* The PRI capability must be disabled before this function is called .
* Returns 0 on success , negative value on error .
*/
int pci_reset_pri ( struct pci_dev * pdev )
{
u16 control ;
int pos ;
2011-11-03 00:07:15 +04:00
pos = pci_find_ext_capability ( pdev , PCI_EXT_CAP_ID_PRI ) ;
2011-09-27 17:57:15 +04:00
if ( ! pos )
return - EINVAL ;
2011-11-11 21:07:36 +04:00
pci_read_config_word ( pdev , pos + PCI_PRI_CTRL , & control ) ;
if ( control & PCI_PRI_CTRL_ENABLE )
2011-09-27 17:57:15 +04:00
return - EBUSY ;
2011-11-11 21:07:36 +04:00
control | = PCI_PRI_CTRL_RESET ;
2011-09-27 17:57:15 +04:00
2011-11-11 21:07:36 +04:00
pci_write_config_word ( pdev , pos + PCI_PRI_CTRL , control ) ;
2011-09-27 17:57:15 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( pci_reset_pri ) ;
/**
* pci_pri_stopped - Checks whether the PRI capability is stopped
* @ pdev : PCI device structure
*
* Returns true if the PRI capability on the device is disabled and the
* device has no outstanding PRI requests , false otherwise . The device
* indicates this via the STOPPED bit in the status register of the
* capability .
* The device internal state can be cleared by resetting the PRI state
* with pci_reset_pri ( ) . This can force the capability into the STOPPED
* state .
*/
bool pci_pri_stopped ( struct pci_dev * pdev )
{
u16 control , status ;
int pos ;
2011-11-03 00:07:15 +04:00
pos = pci_find_ext_capability ( pdev , PCI_EXT_CAP_ID_PRI ) ;
2011-09-27 17:57:15 +04:00
if ( ! pos )
return true ;
2011-11-11 21:07:36 +04:00
pci_read_config_word ( pdev , pos + PCI_PRI_CTRL , & control ) ;
pci_read_config_word ( pdev , pos + PCI_PRI_STATUS , & status ) ;
2011-09-27 17:57:15 +04:00
2011-11-11 21:07:36 +04:00
if ( control & PCI_PRI_CTRL_ENABLE )
2011-09-27 17:57:15 +04:00
return false ;
return ( status & PCI_PRI_STATUS_STOPPED ) ? true : false ;
}
EXPORT_SYMBOL_GPL ( pci_pri_stopped ) ;
/**
* pci_pri_status - Request PRI status of a device
* @ pdev : PCI device structure
*
* Returns negative value on failure , status on success . The status can
* be checked against status - bits . Supported bits are currently :
* PCI_PRI_STATUS_RF : Response failure
* PCI_PRI_STATUS_UPRGI : Unexpected Page Request Group Index
* PCI_PRI_STATUS_STOPPED : PRI has stopped
*/
int pci_pri_status ( struct pci_dev * pdev )
{
u16 status , control ;
int pos ;
2011-11-03 00:07:15 +04:00
pos = pci_find_ext_capability ( pdev , PCI_EXT_CAP_ID_PRI ) ;
2011-09-27 17:57:15 +04:00
if ( ! pos )
return - EINVAL ;
2011-11-11 21:07:36 +04:00
pci_read_config_word ( pdev , pos + PCI_PRI_CTRL , & control ) ;
pci_read_config_word ( pdev , pos + PCI_PRI_STATUS , & status ) ;
2011-09-27 17:57:15 +04:00
/* Stopped bit is undefined when enable == 1, so clear it */
2011-11-11 21:07:36 +04:00
if ( control & PCI_PRI_CTRL_ENABLE )
2011-09-27 17:57:15 +04:00
status & = ~ PCI_PRI_STATUS_STOPPED ;
return status ;
}
EXPORT_SYMBOL_GPL ( pci_pri_status ) ;
# endif /* CONFIG_PCI_PRI */
2011-09-27 17:57:16 +04:00
# ifdef CONFIG_PCI_PASID
/**
* pci_enable_pasid - Enable the PASID capability
* @ pdev : PCI device structure
* @ features : Features to enable
*
* Returns 0 on success , negative value on error . This function checks
* whether the features are actually supported by the device and returns
* an error if not .
*/
int pci_enable_pasid ( struct pci_dev * pdev , int features )
{
u16 control , supported ;
int pos ;
2011-11-03 00:07:15 +04:00
pos = pci_find_ext_capability ( pdev , PCI_EXT_CAP_ID_PASID ) ;
2011-09-27 17:57:16 +04:00
if ( ! pos )
return - EINVAL ;
2011-11-11 21:07:36 +04:00
pci_read_config_word ( pdev , pos + PCI_PASID_CTRL , & control ) ;
pci_read_config_word ( pdev , pos + PCI_PASID_CAP , & supported ) ;
2011-09-27 17:57:16 +04:00
2011-11-11 21:07:36 +04:00
if ( control & PCI_PASID_CTRL_ENABLE )
2011-09-27 17:57:16 +04:00
return - EINVAL ;
2011-11-11 21:07:36 +04:00
supported & = PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV ;
2011-09-27 17:57:16 +04:00
/* User wants to enable anything unsupported? */
if ( ( supported & features ) ! = features )
return - EINVAL ;
2011-11-11 21:07:36 +04:00
control = PCI_PASID_CTRL_ENABLE | features ;
2011-09-27 17:57:16 +04:00
2011-11-11 21:07:36 +04:00
pci_write_config_word ( pdev , pos + PCI_PASID_CTRL , control ) ;
2011-09-27 17:57:16 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( pci_enable_pasid ) ;
/**
* pci_disable_pasid - Disable the PASID capability
* @ pdev : PCI device structure
*
*/
void pci_disable_pasid ( struct pci_dev * pdev )
{
u16 control = 0 ;
int pos ;
2011-11-03 00:07:15 +04:00
pos = pci_find_ext_capability ( pdev , PCI_EXT_CAP_ID_PASID ) ;
2011-09-27 17:57:16 +04:00
if ( ! pos )
return ;
2011-11-11 21:07:36 +04:00
pci_write_config_word ( pdev , pos + PCI_PASID_CTRL , control ) ;
2011-09-27 17:57:16 +04:00
}
EXPORT_SYMBOL_GPL ( pci_disable_pasid ) ;
/**
* pci_pasid_features - Check which PASID features are supported
* @ pdev : PCI device structure
*
* Returns a negative value when no PASI capability is present .
* Otherwise is returns a bitmask with supported features . Current
* features reported are :
2011-11-11 21:07:36 +04:00
* PCI_PASID_CAP_EXEC - Execute permission supported
* PCI_PASID_CAP_PRIV - Priviledged mode supported
2011-09-27 17:57:16 +04:00
*/
int pci_pasid_features ( struct pci_dev * pdev )
{
u16 supported ;
int pos ;
2011-11-03 00:07:15 +04:00
pos = pci_find_ext_capability ( pdev , PCI_EXT_CAP_ID_PASID ) ;
2011-09-27 17:57:16 +04:00
if ( ! pos )
return - EINVAL ;
2011-11-11 21:07:36 +04:00
pci_read_config_word ( pdev , pos + PCI_PASID_CAP , & supported ) ;
2011-09-27 17:57:16 +04:00
2011-11-11 21:07:36 +04:00
supported & = PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV ;
2011-09-27 17:57:16 +04:00
return supported ;
}
EXPORT_SYMBOL_GPL ( pci_pasid_features ) ;
# define PASID_NUMBER_SHIFT 8
# define PASID_NUMBER_MASK (0x1f << PASID_NUMBER_SHIFT)
/**
* pci_max_pasid - Get maximum number of PASIDs supported by device
* @ pdev : PCI device structure
*
* Returns negative value when PASID capability is not present .
* Otherwise it returns the numer of supported PASIDs .
*/
int pci_max_pasids ( struct pci_dev * pdev )
{
u16 supported ;
int pos ;
2011-11-03 00:07:15 +04:00
pos = pci_find_ext_capability ( pdev , PCI_EXT_CAP_ID_PASID ) ;
2011-09-27 17:57:16 +04:00
if ( ! pos )
return - EINVAL ;
2011-11-11 21:07:36 +04:00
pci_read_config_word ( pdev , pos + PCI_PASID_CAP , & supported ) ;
2011-09-27 17:57:16 +04:00
supported = ( supported & PASID_NUMBER_MASK ) > > PASID_NUMBER_SHIFT ;
return ( 1 < < supported ) ;
}
EXPORT_SYMBOL_GPL ( pci_max_pasids ) ;
# endif /* CONFIG_PCI_PASID */