2018-01-12 10:09:26 +01:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2018 Broadcom
*/
/**
* DOC : VC4 V3D performance monitor module
*
* The V3D block provides 16 hardware counters which can count various events .
*/
# include "vc4_drv.h"
# include "vc4_regs.h"
# define VC4_PERFMONID_MIN 1
# define VC4_PERFMONID_MAX U32_MAX
void vc4_perfmon_get ( struct vc4_perfmon * perfmon )
{
if ( perfmon )
refcount_inc ( & perfmon - > refcnt ) ;
}
void vc4_perfmon_put ( struct vc4_perfmon * perfmon )
{
if ( perfmon & & refcount_dec_and_test ( & perfmon - > refcnt ) )
kfree ( perfmon ) ;
}
void vc4_perfmon_start ( struct vc4_dev * vc4 , struct vc4_perfmon * perfmon )
{
unsigned int i ;
u32 mask ;
if ( WARN_ON_ONCE ( ! perfmon | | vc4 - > active_perfmon ) )
return ;
for ( i = 0 ; i < perfmon - > ncounters ; i + + )
V3D_WRITE ( V3D_PCTRS ( i ) , perfmon - > events [ i ] ) ;
mask = GENMASK ( perfmon - > ncounters - 1 , 0 ) ;
V3D_WRITE ( V3D_PCTRC , mask ) ;
V3D_WRITE ( V3D_PCTRE , V3D_PCTRE_EN | mask ) ;
vc4 - > active_perfmon = perfmon ;
}
void vc4_perfmon_stop ( struct vc4_dev * vc4 , struct vc4_perfmon * perfmon ,
bool capture )
{
unsigned int i ;
if ( WARN_ON_ONCE ( ! vc4 - > active_perfmon | |
perfmon ! = vc4 - > active_perfmon ) )
return ;
if ( capture ) {
for ( i = 0 ; i < perfmon - > ncounters ; i + + )
perfmon - > counters [ i ] + = V3D_READ ( V3D_PCTR ( i ) ) ;
}
V3D_WRITE ( V3D_PCTRE , 0 ) ;
vc4 - > active_perfmon = NULL ;
}
struct vc4_perfmon * vc4_perfmon_find ( struct vc4_file * vc4file , int id )
{
struct vc4_perfmon * perfmon ;
mutex_lock ( & vc4file - > perfmon . lock ) ;
perfmon = idr_find ( & vc4file - > perfmon . idr , id ) ;
vc4_perfmon_get ( perfmon ) ;
mutex_unlock ( & vc4file - > perfmon . lock ) ;
return perfmon ;
}
void vc4_perfmon_open_file ( struct vc4_file * vc4file )
{
mutex_init ( & vc4file - > perfmon . lock ) ;
idr_init ( & vc4file - > perfmon . idr ) ;
}
static int vc4_perfmon_idr_del ( int id , void * elem , void * data )
{
struct vc4_perfmon * perfmon = elem ;
vc4_perfmon_put ( perfmon ) ;
return 0 ;
}
void vc4_perfmon_close_file ( struct vc4_file * vc4file )
{
mutex_lock ( & vc4file - > perfmon . lock ) ;
idr_for_each ( & vc4file - > perfmon . idr , vc4_perfmon_idr_del , NULL ) ;
idr_destroy ( & vc4file - > perfmon . idr ) ;
mutex_unlock ( & vc4file - > perfmon . lock ) ;
}
int vc4_perfmon_create_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
2019-04-01 11:35:59 -07:00
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
2018-01-12 10:09:26 +01:00
struct vc4_file * vc4file = file_priv - > driver_priv ;
struct drm_vc4_perfmon_create * req = data ;
struct vc4_perfmon * perfmon ;
unsigned int i ;
int ret ;
2019-04-01 11:35:59 -07:00
if ( ! vc4 - > v3d ) {
DRM_DEBUG ( " Creating perfmon no VC4 V3D probed \n " ) ;
return - ENODEV ;
}
2018-01-12 10:09:26 +01:00
/* Number of monitored counters cannot exceed HW limits. */
if ( req - > ncounters > DRM_VC4_MAX_PERF_COUNTERS | |
! req - > ncounters )
return - EINVAL ;
/* Make sure all events are valid. */
for ( i = 0 ; i < req - > ncounters ; i + + ) {
if ( req - > events [ i ] > = VC4_PERFCNT_NUM_EVENTS )
return - EINVAL ;
}
2019-01-30 19:00:15 -06:00
perfmon = kzalloc ( struct_size ( perfmon , counters , req - > ncounters ) ,
2018-01-12 10:09:26 +01:00
GFP_KERNEL ) ;
if ( ! perfmon )
return - ENOMEM ;
for ( i = 0 ; i < req - > ncounters ; i + + )
perfmon - > events [ i ] = req - > events [ i ] ;
perfmon - > ncounters = req - > ncounters ;
refcount_set ( & perfmon - > refcnt , 1 ) ;
mutex_lock ( & vc4file - > perfmon . lock ) ;
ret = idr_alloc ( & vc4file - > perfmon . idr , perfmon , VC4_PERFMONID_MIN ,
VC4_PERFMONID_MAX , GFP_KERNEL ) ;
mutex_unlock ( & vc4file - > perfmon . lock ) ;
if ( ret < 0 ) {
kfree ( perfmon ) ;
return ret ;
}
req - > id = ret ;
return 0 ;
}
int vc4_perfmon_destroy_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
2019-04-01 11:35:59 -07:00
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
2018-01-12 10:09:26 +01:00
struct vc4_file * vc4file = file_priv - > driver_priv ;
struct drm_vc4_perfmon_destroy * req = data ;
struct vc4_perfmon * perfmon ;
2019-04-01 11:35:59 -07:00
if ( ! vc4 - > v3d ) {
DRM_DEBUG ( " Destroying perfmon no VC4 V3D probed \n " ) ;
return - ENODEV ;
}
2018-01-12 10:09:26 +01:00
mutex_lock ( & vc4file - > perfmon . lock ) ;
perfmon = idr_remove ( & vc4file - > perfmon . idr , req - > id ) ;
mutex_unlock ( & vc4file - > perfmon . lock ) ;
if ( ! perfmon )
return - EINVAL ;
vc4_perfmon_put ( perfmon ) ;
return 0 ;
}
int vc4_perfmon_get_values_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
2019-04-01 11:35:59 -07:00
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
2018-01-12 10:09:26 +01:00
struct vc4_file * vc4file = file_priv - > driver_priv ;
struct drm_vc4_perfmon_get_values * req = data ;
struct vc4_perfmon * perfmon ;
int ret ;
2019-04-01 11:35:59 -07:00
if ( ! vc4 - > v3d ) {
DRM_DEBUG ( " Getting perfmon no VC4 V3D probed \n " ) ;
return - ENODEV ;
}
2018-01-12 10:09:26 +01:00
mutex_lock ( & vc4file - > perfmon . lock ) ;
perfmon = idr_find ( & vc4file - > perfmon . idr , req - > id ) ;
vc4_perfmon_get ( perfmon ) ;
mutex_unlock ( & vc4file - > perfmon . lock ) ;
if ( ! perfmon )
return - EINVAL ;
if ( copy_to_user ( u64_to_user_ptr ( req - > values_ptr ) , perfmon - > counters ,
perfmon - > ncounters * sizeof ( u64 ) ) )
ret = - EFAULT ;
else
ret = 0 ;
vc4_perfmon_put ( perfmon ) ;
return ret ;
}