@ -99,7 +99,7 @@ struct ivhd_header {
u64 mmio_phys ;
u16 pci_seg ;
u16 info ;
u32 reserved ;
u32 efr ;
} __attribute__ ( ( packed ) ) ;
/*
@ -154,6 +154,7 @@ bool amd_iommu_iotlb_sup __read_mostly = true;
u32 amd_iommu_max_pasids __read_mostly = ~ 0 ;
bool amd_iommu_v2_present __read_mostly ;
bool amd_iommu_pc_present __read_mostly ;
bool amd_iommu_force_isolation __read_mostly ;
@ -369,23 +370,23 @@ static void iommu_disable(struct amd_iommu *iommu)
* mapping and unmapping functions for the IOMMU MMIO space . Each AMD IOMMU in
* the system has one .
*/
static u8 __iomem * __init iommu_map_mmio_space ( u64 address )
static u8 __iomem * __init iommu_map_mmio_space ( u64 address , u64 end )
{
if ( ! request_mem_region ( address , MMIO_REGION_LENGTH , " amd_iommu " ) ) {
pr_err ( " AMD-Vi: Can not reserve memory region %llx for mmio \n " ,
address ) ;
if ( ! request_mem_region ( address , end , " amd_iommu " ) ) {
pr_err ( " AMD-Vi: Can not reserve memory region %llx-%llx for mmio \n " ,
address , end ) ;
pr_err ( " AMD-Vi: This is a BIOS bug. Please contact your hardware vendor \n " ) ;
return NULL ;
}
return ( u8 __iomem * ) ioremap_nocache ( address , MMIO_REGION_LENGTH ) ;
return ( u8 __iomem * ) ioremap_nocache ( address , end ) ;
}
static void __init iommu_unmap_mmio_space ( struct amd_iommu * iommu )
{
if ( iommu - > mmio_base )
iounmap ( iommu - > mmio_base ) ;
release_mem_region ( iommu - > mmio_phys , MMIO_REGION_LENGTH ) ;
release_mem_region ( iommu - > mmio_phys , iommu - > mmio_phys_end ) ;
}
/****************************************************************************
@ -1085,7 +1086,18 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
iommu - > cap_ptr = h - > cap_ptr ;
iommu - > pci_seg = h - > pci_seg ;
iommu - > mmio_phys = h - > mmio_phys ;
iommu - > mmio_base = iommu_map_mmio_space ( h - > mmio_phys ) ;
/* Check if IVHD EFR contains proper max banks/counters */
if ( ( h - > efr ! = 0 ) & &
( ( h - > efr & ( 0xF < < 13 ) ) ! = 0 ) & &
( ( h - > efr & ( 0x3F < < 17 ) ) ! = 0 ) ) {
iommu - > mmio_phys_end = MMIO_REG_END_OFFSET ;
} else {
iommu - > mmio_phys_end = MMIO_CNTR_CONF_OFFSET ;
}
iommu - > mmio_base = iommu_map_mmio_space ( iommu - > mmio_phys ,
iommu - > mmio_phys_end ) ;
if ( ! iommu - > mmio_base )
return - ENOMEM ;
@ -1160,6 +1172,33 @@ static int __init init_iommu_all(struct acpi_table_header *table)
return 0 ;
}
static void init_iommu_perf_ctr ( struct amd_iommu * iommu )
{
u64 val = 0xabcd , val2 = 0 ;
if ( ! iommu_feature ( iommu , FEATURE_PC ) )
return ;
amd_iommu_pc_present = true ;
/* Check if the performance counters can be written to */
if ( ( 0 ! = amd_iommu_pc_get_set_reg_val ( 0 , 0 , 0 , 0 , & val , true ) ) | |
( 0 ! = amd_iommu_pc_get_set_reg_val ( 0 , 0 , 0 , 0 , & val2 , false ) ) | |
( val ! = val2 ) ) {
pr_err ( " AMD-Vi: Unable to write to IOMMU perf counter. \n " ) ;
amd_iommu_pc_present = false ;
return ;
}
pr_info ( " AMD-Vi: IOMMU performance counters supported \n " ) ;
val = readl ( iommu - > mmio_base + MMIO_CNTR_CONF_OFFSET ) ;
iommu - > max_banks = ( u8 ) ( ( val > > 12 ) & 0x3f ) ;
iommu - > max_counters = ( u8 ) ( ( val > > 7 ) & 0xf ) ;
}
static int iommu_init_pci ( struct amd_iommu * iommu )
{
int cap_ptr = iommu - > cap_ptr ;
@ -1226,6 +1265,8 @@ static int iommu_init_pci(struct amd_iommu *iommu)
if ( iommu - > cap & ( 1UL < < IOMMU_CAP_NPCACHE ) )
amd_iommu_np_cache = true ;
init_iommu_perf_ctr ( iommu ) ;
if ( is_rd890_iommu ( iommu - > dev ) ) {
int i , j ;
@ -1278,7 +1319,7 @@ static void print_iommu_info(void)
if ( iommu_feature ( iommu , ( 1ULL < < i ) ) )
pr_cont ( " %s " , feat_str [ i ] ) ;
}
pr_cont ( " \n " ) ;
pr_cont ( " \n " ) ;
}
}
if ( irq_remapping_enabled )
@ -2232,3 +2273,84 @@ bool amd_iommu_v2_supported(void)
return amd_iommu_v2_present ;
}
EXPORT_SYMBOL ( amd_iommu_v2_supported ) ;
/****************************************************************************
*
* IOMMU EFR Performance Counter support functionality . This code allows
* access to the IOMMU PC functionality .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
u8 amd_iommu_pc_get_max_banks ( u16 devid )
{
struct amd_iommu * iommu ;
u8 ret = 0 ;
/* locate the iommu governing the devid */
iommu = amd_iommu_rlookup_table [ devid ] ;
if ( iommu )
ret = iommu - > max_banks ;
return ret ;
}
EXPORT_SYMBOL ( amd_iommu_pc_get_max_banks ) ;
bool amd_iommu_pc_supported ( void )
{
return amd_iommu_pc_present ;
}
EXPORT_SYMBOL ( amd_iommu_pc_supported ) ;
u8 amd_iommu_pc_get_max_counters ( u16 devid )
{
struct amd_iommu * iommu ;
u8 ret = 0 ;
/* locate the iommu governing the devid */
iommu = amd_iommu_rlookup_table [ devid ] ;
if ( iommu )
ret = iommu - > max_counters ;
return ret ;
}
EXPORT_SYMBOL ( amd_iommu_pc_get_max_counters ) ;
int amd_iommu_pc_get_set_reg_val ( u16 devid , u8 bank , u8 cntr , u8 fxn ,
u64 * value , bool is_write )
{
struct amd_iommu * iommu ;
u32 offset ;
u32 max_offset_lim ;
/* Make sure the IOMMU PC resource is available */
if ( ! amd_iommu_pc_present )
return - ENODEV ;
/* Locate the iommu associated with the device ID */
iommu = amd_iommu_rlookup_table [ devid ] ;
/* Check for valid iommu and pc register indexing */
if ( WARN_ON ( ( iommu = = NULL ) | | ( fxn > 0x28 ) | | ( fxn & 7 ) ) )
return - ENODEV ;
offset = ( u32 ) ( ( ( 0x40 | bank ) < < 12 ) | ( cntr < < 8 ) | fxn ) ;
/* Limit the offset to the hw defined mmio region aperture */
max_offset_lim = ( u32 ) ( ( ( 0x40 | iommu - > max_banks ) < < 12 ) |
( iommu - > max_counters < < 8 ) | 0x28 ) ;
if ( ( offset < MMIO_CNTR_REG_OFFSET ) | |
( offset > max_offset_lim ) )
return - EINVAL ;
if ( is_write ) {
writel ( ( u32 ) * value , iommu - > mmio_base + offset ) ;
writel ( ( * value > > 32 ) , iommu - > mmio_base + offset + 4 ) ;
} else {
* value = readl ( iommu - > mmio_base + offset + 4 ) ;
* value < < = 32 ;
* value = readl ( iommu - > mmio_base + offset ) ;
}
return 0 ;
}
EXPORT_SYMBOL ( amd_iommu_pc_get_set_reg_val ) ;