2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-10-19 14:05:17 +03:00
/*
* HiSilicon SoC Hardware event counters support
*
2021-05-22 13:23:57 +03:00
* Copyright ( C ) 2017 HiSilicon Limited
2017-10-19 14:05:17 +03:00
* Author : Anurup M < anurup . m @ huawei . com >
* Shaokun Zhang < zhangshaokun @ hisilicon . com >
*
* This code is based on the uncore PMUs like arm - cci and arm - ccn .
*/
# include <linux/bitmap.h>
# include <linux/bitops.h>
# include <linux/bug.h>
# include <linux/err.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
2019-11-07 10:56:04 +03:00
# include <asm/cputype.h>
2017-10-19 14:05:17 +03:00
# include <asm/local64.h>
# include "hisi_uncore_pmu.h"
# define HISI_GET_EVENTID(ev) (ev->hw.config_base & 0xff)
drivers/perf: hisi: Add new functions for L3C PMU
On HiSilicon Hip09 platform, some new functions are enhanced on L3C PMU:
* tt_req: it is the abbreviation of tracetag request and allows user to
count only read/write/atomic operations. tt_req is 3-bit and details are
listed in the hisi-pmu document.
$# perf stat -a -e hisi_sccl3_l3c0/config=0x02,tt_req=0x4/ sleep 5
* tt_core: it is the abbreviation of tracetag core and allows user to
filter by core/thread within the cluster, it is a 8-bit bitmap that each
bit represents the corresponding core/thread in this L3C.
$# perf stat -a -e hisi_sccl3_l3c0/config=0x02,tt_core=0xf/ sleep 5
* datasrc_cfg: it is the abbreviation of data source configuration and
allows user to check where the data comes from, such as: from local DDR,
cross-die DDR or cross-socket DDR. Its is 5-bit and represents different
data source in the SoC.
$# perf stat -a -e hisi_sccl3_l3c0/dat_access,datasrc_cfg=0xe/ sleep 5
* datasrc_skt: it is the abbreviation of data source from another socket
and is used in the multi-chips, if user wants to check the cross-socket
datat source, it shall be added in perf command. Only one bit is used to
control this.
$# perf stat -a -e hisi_sccl3_l3c0/dat_access,datasrc_cfg=0x10,datasrc_skt=1/ sleep 5
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: John Garry <john.garry@huawei.com>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: John Garry <john.garry@huawei.com>
Co-developed-by: Qi Liu <liuqi115@huawei.com>
Signed-off-by: Qi Liu <liuqi115@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Link: https://lore.kernel.org/r/1615186237-22263-5-git-send-email-zhangshaokun@hisilicon.com
Signed-off-by: Will Deacon <will@kernel.org>
2021-03-08 09:50:32 +03:00
# define HISI_MAX_PERIOD(nr) (GENMASK_ULL((nr) - 1, 0))
2017-10-19 14:05:17 +03:00
/*
* PMU format attributes
*/
ssize_t hisi_format_sysfs_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct dev_ext_attribute * eattr ;
eattr = container_of ( attr , struct dev_ext_attribute , attr ) ;
2021-03-19 13:04:33 +03:00
return sysfs_emit ( buf , " %s \n " , ( char * ) eattr - > var ) ;
2017-10-19 14:05:17 +03:00
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_format_sysfs_show ) ;
2017-10-19 14:05:17 +03:00
/*
* PMU event attributes
*/
ssize_t hisi_event_sysfs_show ( struct device * dev ,
struct device_attribute * attr , char * page )
{
struct dev_ext_attribute * eattr ;
eattr = container_of ( attr , struct dev_ext_attribute , attr ) ;
2021-03-19 13:04:33 +03:00
return sysfs_emit ( page , " config=0x%lx \n " , ( unsigned long ) eattr - > var ) ;
2017-10-19 14:05:17 +03:00
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_event_sysfs_show ) ;
2017-10-19 14:05:17 +03:00
/*
* sysfs cpumask attributes . For uncore PMU , we only have a single CPU to show
*/
ssize_t hisi_cpumask_sysfs_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( dev_get_drvdata ( dev ) ) ;
2021-03-19 13:04:33 +03:00
return sysfs_emit ( buf , " %d \n " , hisi_pmu - > on_cpu ) ;
2017-10-19 14:05:17 +03:00
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_cpumask_sysfs_show ) ;
2017-10-19 14:05:17 +03:00
static bool hisi_validate_event_group ( struct perf_event * event )
{
struct perf_event * sibling , * leader = event - > group_leader ;
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( event - > pmu ) ;
/* Include count for the event */
int counters = 1 ;
if ( ! is_software_event ( leader ) ) {
/*
* We must NOT create groups containing mixed PMUs , although
* software events are acceptable
*/
if ( leader - > pmu ! = event - > pmu )
return false ;
/* Increment counter for the leader */
if ( leader ! = event )
counters + + ;
}
2018-03-15 19:36:56 +03:00
for_each_sibling_event ( sibling , event - > group_leader ) {
2017-10-19 14:05:17 +03:00
if ( is_software_event ( sibling ) )
continue ;
if ( sibling - > pmu ! = event - > pmu )
return false ;
/* Increment counter for each sibling */
counters + + ;
}
/* The group can not count events more than the counters in the HW */
return counters < = hisi_pmu - > num_counters ;
}
int hisi_uncore_pmu_get_event_idx ( struct perf_event * event )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( event - > pmu ) ;
unsigned long * used_mask = hisi_pmu - > pmu_events . used_mask ;
u32 num_counters = hisi_pmu - > num_counters ;
int idx ;
idx = find_first_zero_bit ( used_mask , num_counters ) ;
if ( idx = = num_counters )
return - EAGAIN ;
set_bit ( idx , used_mask ) ;
return idx ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_get_event_idx ) ;
2017-10-19 14:05:17 +03:00
2020-10-08 12:26:18 +03:00
ssize_t hisi_uncore_pmu_identifier_attr_show ( struct device * dev ,
struct device_attribute * attr ,
char * page )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( dev_get_drvdata ( dev ) ) ;
2021-03-19 13:04:31 +03:00
return sysfs_emit ( page , " 0x%08x \n " , hisi_pmu - > identifier ) ;
2020-10-08 12:26:18 +03:00
}
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_identifier_attr_show ) ;
2017-10-19 14:05:17 +03:00
static void hisi_uncore_pmu_clear_event_idx ( struct hisi_pmu * hisi_pmu , int idx )
{
clear_bit ( idx , hisi_pmu - > pmu_events . used_mask ) ;
}
2021-03-08 09:50:30 +03:00
static irqreturn_t hisi_uncore_pmu_isr ( int irq , void * data )
{
struct hisi_pmu * hisi_pmu = data ;
struct perf_event * event ;
unsigned long overflown ;
int idx ;
overflown = hisi_pmu - > ops - > get_int_status ( hisi_pmu ) ;
if ( ! overflown )
return IRQ_NONE ;
/*
* Find the counter index which overflowed if the bit was set
* and handle it .
*/
for_each_set_bit ( idx , & overflown , hisi_pmu - > num_counters ) {
/* Write 1 to clear the IRQ status flag */
hisi_pmu - > ops - > clear_int_status ( hisi_pmu , idx ) ;
/* Get the corresponding event struct */
event = hisi_pmu - > pmu_events . hw_events [ idx ] ;
if ( ! event )
continue ;
hisi_uncore_pmu_event_update ( event ) ;
hisi_uncore_pmu_set_event_period ( event ) ;
}
return IRQ_HANDLED ;
}
int hisi_uncore_pmu_init_irq ( struct hisi_pmu * hisi_pmu ,
struct platform_device * pdev )
{
int irq , ret ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return irq ;
ret = devm_request_irq ( & pdev - > dev , irq , hisi_uncore_pmu_isr ,
IRQF_NOBALANCING | IRQF_NO_THREAD ,
dev_name ( & pdev - > dev ) , hisi_pmu ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev ,
" Fail to request IRQ: %d ret: %d. \n " , irq , ret ) ;
return ret ;
}
hisi_pmu - > irq = irq ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_init_irq ) ;
2017-10-19 14:05:17 +03:00
int hisi_uncore_pmu_event_init ( struct perf_event * event )
{
struct hw_perf_event * hwc = & event - > hw ;
struct hisi_pmu * hisi_pmu ;
if ( event - > attr . type ! = event - > pmu - > type )
return - ENOENT ;
/*
* We do not support sampling as the counters are all
* shared by all CPU cores in a CPU die ( SCCL ) . Also we
* do not support attach to a task ( per - process mode )
*/
if ( is_sampling_event ( event ) | | event - > attach_state & PERF_ATTACH_TASK )
return - EOPNOTSUPP ;
/*
* The uncore counters not specific to any CPU , so cannot
* support per - task
*/
if ( event - > cpu < 0 )
return - EINVAL ;
/*
* Validate if the events in group does not exceed the
* available counters in hardware .
*/
if ( ! hisi_validate_event_group ( event ) )
return - EINVAL ;
hisi_pmu = to_hisi_pmu ( event - > pmu ) ;
if ( event - > attr . config > hisi_pmu - > check_event )
return - EINVAL ;
if ( hisi_pmu - > on_cpu = = - 1 )
return - EINVAL ;
/*
* We don ' t assign an index until we actually place the event onto
* hardware . Use - 1 to signify that we haven ' t decided where to put it
* yet .
*/
hwc - > idx = - 1 ;
hwc - > config_base = event - > attr . config ;
/* Enforce to use the same CPU for all events in this PMU */
event - > cpu = hisi_pmu - > on_cpu ;
return 0 ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_event_init ) ;
2017-10-19 14:05:17 +03:00
/*
* Set the counter to count the event that we ' re interested in ,
* and enable interrupt and counter .
*/
static void hisi_uncore_pmu_enable_event ( struct perf_event * event )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( event - > pmu ) ;
struct hw_perf_event * hwc = & event - > hw ;
hisi_pmu - > ops - > write_evtype ( hisi_pmu , hwc - > idx ,
HISI_GET_EVENTID ( event ) ) ;
drivers/perf: hisi: Add new functions for L3C PMU
On HiSilicon Hip09 platform, some new functions are enhanced on L3C PMU:
* tt_req: it is the abbreviation of tracetag request and allows user to
count only read/write/atomic operations. tt_req is 3-bit and details are
listed in the hisi-pmu document.
$# perf stat -a -e hisi_sccl3_l3c0/config=0x02,tt_req=0x4/ sleep 5
* tt_core: it is the abbreviation of tracetag core and allows user to
filter by core/thread within the cluster, it is a 8-bit bitmap that each
bit represents the corresponding core/thread in this L3C.
$# perf stat -a -e hisi_sccl3_l3c0/config=0x02,tt_core=0xf/ sleep 5
* datasrc_cfg: it is the abbreviation of data source configuration and
allows user to check where the data comes from, such as: from local DDR,
cross-die DDR or cross-socket DDR. Its is 5-bit and represents different
data source in the SoC.
$# perf stat -a -e hisi_sccl3_l3c0/dat_access,datasrc_cfg=0xe/ sleep 5
* datasrc_skt: it is the abbreviation of data source from another socket
and is used in the multi-chips, if user wants to check the cross-socket
datat source, it shall be added in perf command. Only one bit is used to
control this.
$# perf stat -a -e hisi_sccl3_l3c0/dat_access,datasrc_cfg=0x10,datasrc_skt=1/ sleep 5
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: John Garry <john.garry@huawei.com>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: John Garry <john.garry@huawei.com>
Co-developed-by: Qi Liu <liuqi115@huawei.com>
Signed-off-by: Qi Liu <liuqi115@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Link: https://lore.kernel.org/r/1615186237-22263-5-git-send-email-zhangshaokun@hisilicon.com
Signed-off-by: Will Deacon <will@kernel.org>
2021-03-08 09:50:32 +03:00
if ( hisi_pmu - > ops - > enable_filter )
hisi_pmu - > ops - > enable_filter ( event ) ;
2017-10-19 14:05:17 +03:00
hisi_pmu - > ops - > enable_counter_int ( hisi_pmu , hwc ) ;
hisi_pmu - > ops - > enable_counter ( hisi_pmu , hwc ) ;
}
/*
* Disable counter and interrupt .
*/
static void hisi_uncore_pmu_disable_event ( struct perf_event * event )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( event - > pmu ) ;
struct hw_perf_event * hwc = & event - > hw ;
hisi_pmu - > ops - > disable_counter ( hisi_pmu , hwc ) ;
hisi_pmu - > ops - > disable_counter_int ( hisi_pmu , hwc ) ;
drivers/perf: hisi: Add new functions for L3C PMU
On HiSilicon Hip09 platform, some new functions are enhanced on L3C PMU:
* tt_req: it is the abbreviation of tracetag request and allows user to
count only read/write/atomic operations. tt_req is 3-bit and details are
listed in the hisi-pmu document.
$# perf stat -a -e hisi_sccl3_l3c0/config=0x02,tt_req=0x4/ sleep 5
* tt_core: it is the abbreviation of tracetag core and allows user to
filter by core/thread within the cluster, it is a 8-bit bitmap that each
bit represents the corresponding core/thread in this L3C.
$# perf stat -a -e hisi_sccl3_l3c0/config=0x02,tt_core=0xf/ sleep 5
* datasrc_cfg: it is the abbreviation of data source configuration and
allows user to check where the data comes from, such as: from local DDR,
cross-die DDR or cross-socket DDR. Its is 5-bit and represents different
data source in the SoC.
$# perf stat -a -e hisi_sccl3_l3c0/dat_access,datasrc_cfg=0xe/ sleep 5
* datasrc_skt: it is the abbreviation of data source from another socket
and is used in the multi-chips, if user wants to check the cross-socket
datat source, it shall be added in perf command. Only one bit is used to
control this.
$# perf stat -a -e hisi_sccl3_l3c0/dat_access,datasrc_cfg=0x10,datasrc_skt=1/ sleep 5
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: John Garry <john.garry@huawei.com>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: John Garry <john.garry@huawei.com>
Co-developed-by: Qi Liu <liuqi115@huawei.com>
Signed-off-by: Qi Liu <liuqi115@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Link: https://lore.kernel.org/r/1615186237-22263-5-git-send-email-zhangshaokun@hisilicon.com
Signed-off-by: Will Deacon <will@kernel.org>
2021-03-08 09:50:32 +03:00
if ( hisi_pmu - > ops - > disable_filter )
hisi_pmu - > ops - > disable_filter ( event ) ;
2017-10-19 14:05:17 +03:00
}
void hisi_uncore_pmu_set_event_period ( struct perf_event * event )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( event - > pmu ) ;
struct hw_perf_event * hwc = & event - > hw ;
/*
* The HiSilicon PMU counters support 32 bits or 48 bits , depending on
* the PMU . We reduce it to 2 ^ ( counter_bits - 1 ) to account for the
* extreme interrupt latency . So we could hopefully handle the overflow
* interrupt before another 2 ^ ( counter_bits - 1 ) events occur and the
* counter overtakes its previous value .
*/
u64 val = BIT_ULL ( hisi_pmu - > counter_bits - 1 ) ;
local64_set ( & hwc - > prev_count , val ) ;
/* Write start value to the hardware event counter */
hisi_pmu - > ops - > write_counter ( hisi_pmu , hwc , val ) ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_set_event_period ) ;
2017-10-19 14:05:17 +03:00
void hisi_uncore_pmu_event_update ( struct perf_event * event )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( event - > pmu ) ;
struct hw_perf_event * hwc = & event - > hw ;
u64 delta , prev_raw_count , new_raw_count ;
do {
/* Read the count from the counter register */
new_raw_count = hisi_pmu - > ops - > read_counter ( hisi_pmu , hwc ) ;
prev_raw_count = local64_read ( & hwc - > prev_count ) ;
} while ( local64_cmpxchg ( & hwc - > prev_count , prev_raw_count ,
new_raw_count ) ! = prev_raw_count ) ;
/*
* compute the delta
*/
delta = ( new_raw_count - prev_raw_count ) &
HISI_MAX_PERIOD ( hisi_pmu - > counter_bits ) ;
local64_add ( delta , & event - > count ) ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_event_update ) ;
2017-10-19 14:05:17 +03:00
void hisi_uncore_pmu_start ( struct perf_event * event , int flags )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( event - > pmu ) ;
struct hw_perf_event * hwc = & event - > hw ;
if ( WARN_ON_ONCE ( ! ( hwc - > state & PERF_HES_STOPPED ) ) )
return ;
WARN_ON_ONCE ( ! ( hwc - > state & PERF_HES_UPTODATE ) ) ;
hwc - > state = 0 ;
hisi_uncore_pmu_set_event_period ( event ) ;
if ( flags & PERF_EF_RELOAD ) {
u64 prev_raw_count = local64_read ( & hwc - > prev_count ) ;
hisi_pmu - > ops - > write_counter ( hisi_pmu , hwc , prev_raw_count ) ;
}
hisi_uncore_pmu_enable_event ( event ) ;
perf_event_update_userpage ( event ) ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_start ) ;
2017-10-19 14:05:17 +03:00
void hisi_uncore_pmu_stop ( struct perf_event * event , int flags )
{
struct hw_perf_event * hwc = & event - > hw ;
hisi_uncore_pmu_disable_event ( event ) ;
WARN_ON_ONCE ( hwc - > state & PERF_HES_STOPPED ) ;
hwc - > state | = PERF_HES_STOPPED ;
if ( hwc - > state & PERF_HES_UPTODATE )
return ;
/* Read hardware counter and update the perf counter statistics */
hisi_uncore_pmu_event_update ( event ) ;
hwc - > state | = PERF_HES_UPTODATE ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_stop ) ;
2017-10-19 14:05:17 +03:00
int hisi_uncore_pmu_add ( struct perf_event * event , int flags )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( event - > pmu ) ;
struct hw_perf_event * hwc = & event - > hw ;
int idx ;
hwc - > state = PERF_HES_STOPPED | PERF_HES_UPTODATE ;
/* Get an available counter index for counting */
idx = hisi_pmu - > ops - > get_event_idx ( event ) ;
if ( idx < 0 )
return idx ;
event - > hw . idx = idx ;
hisi_pmu - > pmu_events . hw_events [ idx ] = event ;
if ( flags & PERF_EF_START )
hisi_uncore_pmu_start ( event , PERF_EF_RELOAD ) ;
return 0 ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_add ) ;
2017-10-19 14:05:17 +03:00
void hisi_uncore_pmu_del ( struct perf_event * event , int flags )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( event - > pmu ) ;
struct hw_perf_event * hwc = & event - > hw ;
hisi_uncore_pmu_stop ( event , PERF_EF_UPDATE ) ;
hisi_uncore_pmu_clear_event_idx ( hisi_pmu , hwc - > idx ) ;
perf_event_update_userpage ( event ) ;
hisi_pmu - > pmu_events . hw_events [ hwc - > idx ] = NULL ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_del ) ;
2017-10-19 14:05:17 +03:00
void hisi_uncore_pmu_read ( struct perf_event * event )
{
/* Read hardware counter and update the perf counter statistics */
hisi_uncore_pmu_event_update ( event ) ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_read ) ;
2017-10-19 14:05:17 +03:00
void hisi_uncore_pmu_enable ( struct pmu * pmu )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( pmu ) ;
2022-02-11 01:48:56 +03:00
bool enabled = ! bitmap_empty ( hisi_pmu - > pmu_events . used_mask ,
2017-10-19 14:05:17 +03:00
hisi_pmu - > num_counters ) ;
if ( ! enabled )
return ;
hisi_pmu - > ops - > start_counters ( hisi_pmu ) ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_enable ) ;
2017-10-19 14:05:17 +03:00
void hisi_uncore_pmu_disable ( struct pmu * pmu )
{
struct hisi_pmu * hisi_pmu = to_hisi_pmu ( pmu ) ;
hisi_pmu - > ops - > stop_counters ( hisi_pmu ) ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_disable ) ;
2017-10-19 14:05:17 +03:00
2019-11-14 03:57:03 +03:00
2017-10-19 14:05:17 +03:00
/*
2019-11-14 03:57:03 +03:00
* The Super CPU Cluster ( SCCL ) and CPU Cluster ( CCL ) IDs can be
* determined from the MPIDR_EL1 , but the encoding varies by CPU :
*
* - For MT variants of TSV110 :
* SCCL is Aff2 [ 7 : 3 ] , CCL is Aff2 [ 2 : 0 ]
*
* - For other MT parts :
* SCCL is Aff3 [ 7 : 0 ] , CCL is Aff2 [ 7 : 0 ]
*
* - For non - MT parts :
* SCCL is Aff2 [ 7 : 0 ] , CCL is Aff1 [ 7 : 0 ]
2017-10-19 14:05:17 +03:00
*/
2019-11-14 03:57:03 +03:00
static void hisi_read_sccl_and_ccl_id ( int * scclp , int * cclp )
2017-10-19 14:05:17 +03:00
{
u64 mpidr = read_cpuid_mpidr ( ) ;
2019-11-14 03:57:03 +03:00
int aff3 = MPIDR_AFFINITY_LEVEL ( mpidr , 3 ) ;
int aff2 = MPIDR_AFFINITY_LEVEL ( mpidr , 2 ) ;
int aff1 = MPIDR_AFFINITY_LEVEL ( mpidr , 1 ) ;
bool mt = mpidr & MPIDR_MT_BITMASK ;
int sccl , ccl ;
if ( mt & & read_cpuid_part_number ( ) = = HISI_CPU_PART_TSV110 ) {
sccl = aff2 > > 3 ;
ccl = aff2 & 0x7 ;
} else if ( mt ) {
sccl = aff3 ;
ccl = aff2 ;
2017-10-19 14:05:17 +03:00
} else {
2019-11-14 03:57:03 +03:00
sccl = aff2 ;
ccl = aff1 ;
2017-10-19 14:05:17 +03:00
}
2019-11-14 03:57:03 +03:00
if ( scclp )
* scclp = sccl ;
if ( cclp )
* cclp = ccl ;
2017-10-19 14:05:17 +03:00
}
/*
* Check whether the CPU is associated with this uncore PMU
*/
static bool hisi_pmu_cpu_is_associated_pmu ( struct hisi_pmu * hisi_pmu )
{
int sccl_id , ccl_id ;
2022-04-15 13:23:51 +03:00
/* If SCCL_ID is -1, the PMU is in a SICL and has no CPU affinity */
if ( hisi_pmu - > sccl_id = = - 1 )
return true ;
2017-10-19 14:05:17 +03:00
if ( hisi_pmu - > ccl_id = = - 1 ) {
/* If CCL_ID is -1, the PMU only shares the same SCCL */
hisi_read_sccl_and_ccl_id ( & sccl_id , NULL ) ;
return sccl_id = = hisi_pmu - > sccl_id ;
}
hisi_read_sccl_and_ccl_id ( & sccl_id , & ccl_id ) ;
return sccl_id = = hisi_pmu - > sccl_id & & ccl_id = = hisi_pmu - > ccl_id ;
}
int hisi_uncore_pmu_online_cpu ( unsigned int cpu , struct hlist_node * node )
{
struct hisi_pmu * hisi_pmu = hlist_entry_safe ( node , struct hisi_pmu ,
node ) ;
if ( ! hisi_pmu_cpu_is_associated_pmu ( hisi_pmu ) )
return 0 ;
cpumask_set_cpu ( cpu , & hisi_pmu - > associated_cpus ) ;
/* If another CPU is already managing this PMU, simply return. */
if ( hisi_pmu - > on_cpu ! = - 1 )
return 0 ;
/* Use this CPU in cpumask for event counting */
hisi_pmu - > on_cpu = cpu ;
/* Overflow interrupt also should use the same CPU */
2021-05-18 12:17:33 +03:00
WARN_ON ( irq_set_affinity ( hisi_pmu - > irq , cpumask_of ( cpu ) ) ) ;
2017-10-19 14:05:17 +03:00
return 0 ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_online_cpu ) ;
2017-10-19 14:05:17 +03:00
int hisi_uncore_pmu_offline_cpu ( unsigned int cpu , struct hlist_node * node )
{
struct hisi_pmu * hisi_pmu = hlist_entry_safe ( node , struct hisi_pmu ,
node ) ;
cpumask_t pmu_online_cpus ;
unsigned int target ;
if ( ! cpumask_test_and_clear_cpu ( cpu , & hisi_pmu - > associated_cpus ) )
return 0 ;
/* Nothing to do if this CPU doesn't own the PMU */
if ( hisi_pmu - > on_cpu ! = cpu )
return 0 ;
/* Give up ownership of the PMU */
hisi_pmu - > on_cpu = - 1 ;
/* Choose a new CPU to migrate ownership of the PMU to */
cpumask_and ( & pmu_online_cpus , & hisi_pmu - > associated_cpus ,
cpu_online_mask ) ;
target = cpumask_any_but ( & pmu_online_cpus , cpu ) ;
if ( target > = nr_cpu_ids )
return 0 ;
perf_pmu_migrate_context ( & hisi_pmu - > pmu , cpu , target ) ;
/* Use this CPU for event counting */
hisi_pmu - > on_cpu = target ;
2021-05-18 12:17:33 +03:00
WARN_ON ( irq_set_affinity ( hisi_pmu - > irq , cpumask_of ( target ) ) ) ;
2017-10-19 14:05:17 +03:00
return 0 ;
}
2020-05-07 05:58:25 +03:00
EXPORT_SYMBOL_GPL ( hisi_uncore_pmu_offline_cpu ) ;
2022-05-16 16:16:01 +03:00
void hisi_pmu_init ( struct pmu * pmu , const char * name ,
const struct attribute_group * * attr_groups , struct module * module )
{
pmu - > name = name ;
pmu - > module = module ;
pmu - > task_ctx_nr = perf_invalid_context ;
pmu - > event_init = hisi_uncore_pmu_event_init ;
pmu - > pmu_enable = hisi_uncore_pmu_enable ;
pmu - > pmu_disable = hisi_uncore_pmu_disable ;
pmu - > add = hisi_uncore_pmu_add ;
pmu - > del = hisi_uncore_pmu_del ;
pmu - > start = hisi_uncore_pmu_start ;
pmu - > stop = hisi_uncore_pmu_stop ;
pmu - > read = hisi_uncore_pmu_read ;
pmu - > attr_groups = attr_groups ;
}
EXPORT_SYMBOL_GPL ( hisi_pmu_init ) ;
2020-05-07 05:58:25 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;