2021-06-08 13:15:41 +02:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2021 Raspberry Pi
*/
# include "v3d_drv.h"
# include "v3d_regs.h"
# define V3D_PERFMONID_MIN 1
# define V3D_PERFMONID_MAX U32_MAX
void v3d_perfmon_get ( struct v3d_perfmon * perfmon )
{
if ( perfmon )
refcount_inc ( & perfmon - > refcnt ) ;
}
void v3d_perfmon_put ( struct v3d_perfmon * perfmon )
{
2022-11-08 14:54:25 -03:00
if ( perfmon & & refcount_dec_and_test ( & perfmon - > refcnt ) ) {
mutex_destroy ( & perfmon - > lock ) ;
2021-06-08 13:15:41 +02:00
kfree ( perfmon ) ;
2022-11-08 14:54:25 -03:00
}
2021-06-08 13:15:41 +02:00
}
void v3d_perfmon_start ( struct v3d_dev * v3d , struct v3d_perfmon * perfmon )
{
unsigned int i ;
u32 mask ;
2022-04-24 19:35:12 +01:00
u8 ncounters ;
2021-06-08 13:15:41 +02:00
if ( WARN_ON_ONCE ( ! perfmon | | v3d - > active_perfmon ) )
return ;
2022-04-24 19:35:12 +01:00
ncounters = perfmon - > ncounters ;
2021-06-08 13:15:41 +02:00
mask = GENMASK ( ncounters - 1 , 0 ) ;
for ( i = 0 ; i < ncounters ; i + + ) {
u32 source = i / 4 ;
u32 channel = V3D_SET_FIELD ( perfmon - > counters [ i ] , V3D_PCTR_S0 ) ;
i + + ;
channel | = V3D_SET_FIELD ( i < ncounters ? perfmon - > counters [ i ] : 0 ,
V3D_PCTR_S1 ) ;
i + + ;
channel | = V3D_SET_FIELD ( i < ncounters ? perfmon - > counters [ i ] : 0 ,
V3D_PCTR_S2 ) ;
i + + ;
channel | = V3D_SET_FIELD ( i < ncounters ? perfmon - > counters [ i ] : 0 ,
V3D_PCTR_S3 ) ;
V3D_CORE_WRITE ( 0 , V3D_V4_PCTR_0_SRC_X ( source ) , channel ) ;
}
V3D_CORE_WRITE ( 0 , V3D_V4_PCTR_0_CLR , mask ) ;
V3D_CORE_WRITE ( 0 , V3D_PCTR_0_OVERFLOW , mask ) ;
V3D_CORE_WRITE ( 0 , V3D_V4_PCTR_0_EN , mask ) ;
v3d - > active_perfmon = perfmon ;
}
void v3d_perfmon_stop ( struct v3d_dev * v3d , struct v3d_perfmon * perfmon ,
bool capture )
{
unsigned int i ;
if ( ! perfmon | | ! v3d - > active_perfmon )
return ;
mutex_lock ( & perfmon - > lock ) ;
if ( perfmon ! = v3d - > active_perfmon ) {
mutex_unlock ( & perfmon - > lock ) ;
return ;
}
if ( capture )
for ( i = 0 ; i < perfmon - > ncounters ; i + + )
perfmon - > values [ i ] + = V3D_CORE_READ ( 0 , V3D_PCTR_0_PCTRX ( i ) ) ;
V3D_CORE_WRITE ( 0 , V3D_V4_PCTR_0_EN , 0 ) ;
v3d - > active_perfmon = NULL ;
mutex_unlock ( & perfmon - > lock ) ;
}
struct v3d_perfmon * v3d_perfmon_find ( struct v3d_file_priv * v3d_priv , int id )
{
struct v3d_perfmon * perfmon ;
mutex_lock ( & v3d_priv - > perfmon . lock ) ;
perfmon = idr_find ( & v3d_priv - > perfmon . idr , id ) ;
v3d_perfmon_get ( perfmon ) ;
mutex_unlock ( & v3d_priv - > perfmon . lock ) ;
return perfmon ;
}
void v3d_perfmon_open_file ( struct v3d_file_priv * v3d_priv )
{
mutex_init ( & v3d_priv - > perfmon . lock ) ;
2022-07-01 21:02:25 +02:00
idr_init_base ( & v3d_priv - > perfmon . idr , 1 ) ;
2021-06-08 13:15:41 +02:00
}
static int v3d_perfmon_idr_del ( int id , void * elem , void * data )
{
struct v3d_perfmon * perfmon = elem ;
v3d_perfmon_put ( perfmon ) ;
return 0 ;
}
void v3d_perfmon_close_file ( struct v3d_file_priv * v3d_priv )
{
mutex_lock ( & v3d_priv - > perfmon . lock ) ;
idr_for_each ( & v3d_priv - > perfmon . idr , v3d_perfmon_idr_del , NULL ) ;
idr_destroy ( & v3d_priv - > perfmon . idr ) ;
mutex_unlock ( & v3d_priv - > perfmon . lock ) ;
2022-11-08 14:54:25 -03:00
mutex_destroy ( & v3d_priv - > perfmon . lock ) ;
2021-06-08 13:15:41 +02:00
}
int v3d_perfmon_create_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct v3d_file_priv * v3d_priv = file_priv - > driver_priv ;
struct drm_v3d_perfmon_create * req = data ;
struct v3d_perfmon * perfmon ;
unsigned int i ;
int ret ;
/* Number of monitored counters cannot exceed HW limits. */
if ( req - > ncounters > DRM_V3D_MAX_PERF_COUNTERS | |
! req - > ncounters )
return - EINVAL ;
/* Make sure all counters are valid. */
for ( i = 0 ; i < req - > ncounters ; i + + ) {
if ( req - > counters [ i ] > = V3D_PERFCNT_NUM )
return - EINVAL ;
}
perfmon = kzalloc ( struct_size ( perfmon , values , req - > ncounters ) ,
GFP_KERNEL ) ;
if ( ! perfmon )
return - ENOMEM ;
for ( i = 0 ; i < req - > ncounters ; i + + )
perfmon - > counters [ i ] = req - > counters [ i ] ;
perfmon - > ncounters = req - > ncounters ;
refcount_set ( & perfmon - > refcnt , 1 ) ;
mutex_init ( & perfmon - > lock ) ;
mutex_lock ( & v3d_priv - > perfmon . lock ) ;
ret = idr_alloc ( & v3d_priv - > perfmon . idr , perfmon , V3D_PERFMONID_MIN ,
V3D_PERFMONID_MAX , GFP_KERNEL ) ;
mutex_unlock ( & v3d_priv - > perfmon . lock ) ;
if ( ret < 0 ) {
2022-11-08 14:54:25 -03:00
mutex_destroy ( & perfmon - > lock ) ;
2021-06-08 13:15:41 +02:00
kfree ( perfmon ) ;
return ret ;
}
req - > id = ret ;
return 0 ;
}
int v3d_perfmon_destroy_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct v3d_file_priv * v3d_priv = file_priv - > driver_priv ;
struct drm_v3d_perfmon_destroy * req = data ;
struct v3d_perfmon * perfmon ;
mutex_lock ( & v3d_priv - > perfmon . lock ) ;
perfmon = idr_remove ( & v3d_priv - > perfmon . idr , req - > id ) ;
mutex_unlock ( & v3d_priv - > perfmon . lock ) ;
if ( ! perfmon )
return - EINVAL ;
v3d_perfmon_put ( perfmon ) ;
return 0 ;
}
int v3d_perfmon_get_values_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct v3d_dev * v3d = to_v3d_dev ( dev ) ;
struct v3d_file_priv * v3d_priv = file_priv - > driver_priv ;
struct drm_v3d_perfmon_get_values * req = data ;
struct v3d_perfmon * perfmon ;
int ret = 0 ;
if ( req - > pad ! = 0 )
return - EINVAL ;
mutex_lock ( & v3d_priv - > perfmon . lock ) ;
perfmon = idr_find ( & v3d_priv - > perfmon . idr , req - > id ) ;
v3d_perfmon_get ( perfmon ) ;
mutex_unlock ( & v3d_priv - > perfmon . lock ) ;
if ( ! perfmon )
return - EINVAL ;
v3d_perfmon_stop ( v3d , perfmon , true ) ;
if ( copy_to_user ( u64_to_user_ptr ( req - > values_ptr ) , perfmon - > values ,
perfmon - > ncounters * sizeof ( u64 ) ) )
ret = - EFAULT ;
v3d_perfmon_put ( perfmon ) ;
return ret ;
}