2015-11-11 05:29:01 +03:00
/*
* Generic OPP debugfs interface
*
* Copyright ( C ) 2015 - 2016 Viresh Kumar < viresh . kumar @ linaro . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/debugfs.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/init.h>
# include <linux/limits.h>
2016-12-01 13:58:19 +03:00
# include <linux/slab.h>
2015-11-11 05:29:01 +03:00
# include "opp.h"
static struct dentry * rootdir ;
static void opp_set_dev_name ( const struct device * dev , char * name )
{
if ( dev - > parent )
snprintf ( name , NAME_MAX , " %s-%s " , dev_name ( dev - > parent ) ,
dev_name ( dev ) ) ;
else
snprintf ( name , NAME_MAX , " %s " , dev_name ( dev ) ) ;
}
void opp_debug_remove_one ( struct dev_pm_opp * opp )
{
debugfs_remove_recursive ( opp - > dentry ) ;
}
2016-12-01 13:58:19 +03:00
static bool opp_debug_create_supplies ( struct dev_pm_opp * opp ,
struct opp_table * opp_table ,
struct dentry * pdentry )
{
struct dentry * d ;
2017-05-23 07:02:13 +03:00
int i ;
2016-12-01 13:58:19 +03:00
2017-05-23 07:02:13 +03:00
for ( i = 0 ; i < opp_table - > regulator_count ; i + + ) {
2017-09-21 08:45:36 +03:00
char name [ 15 ] ;
snprintf ( name , sizeof ( name ) , " supply-%d " , i ) ;
2016-12-01 13:58:19 +03:00
/* Create per-opp directory */
d = debugfs_create_dir ( name , pdentry ) ;
if ( ! d )
return false ;
if ( ! debugfs_create_ulong ( " u_volt_target " , S_IRUGO , d ,
& opp - > supplies [ i ] . u_volt ) )
return false ;
if ( ! debugfs_create_ulong ( " u_volt_min " , S_IRUGO , d ,
& opp - > supplies [ i ] . u_volt_min ) )
return false ;
if ( ! debugfs_create_ulong ( " u_volt_max " , S_IRUGO , d ,
& opp - > supplies [ i ] . u_volt_max ) )
return false ;
if ( ! debugfs_create_ulong ( " u_amp " , S_IRUGO , d ,
& opp - > supplies [ i ] . u_amp ) )
return false ;
2017-05-23 07:02:13 +03:00
}
2016-12-01 13:58:19 +03:00
return true ;
}
2016-02-16 11:47:53 +03:00
int opp_debug_create_one ( struct dev_pm_opp * opp , struct opp_table * opp_table )
2015-11-11 05:29:01 +03:00
{
2016-02-16 11:47:53 +03:00
struct dentry * pdentry = opp_table - > dentry ;
2015-11-11 05:29:01 +03:00
struct dentry * d ;
2018-04-06 12:05:45 +03:00
unsigned long id ;
2015-11-11 05:29:01 +03:00
char name [ 25 ] ; /* 20 chars for 64 bit value + 5 (opp:\0) */
2018-04-06 12:05:45 +03:00
/*
* Get directory name for OPP .
*
* - Normally rate is unique to each OPP , use it to get unique opp - name .
* - For some devices rate isn ' t available , use index instead .
*/
if ( likely ( opp - > rate ) )
id = opp - > rate ;
else
id = _get_opp_count ( opp_table ) ;
snprintf ( name , sizeof ( name ) , " opp:%lu " , id ) ;
2015-11-11 05:29:01 +03:00
/* Create per-opp directory */
d = debugfs_create_dir ( name , pdentry ) ;
if ( ! d )
return - ENOMEM ;
if ( ! debugfs_create_bool ( " available " , S_IRUGO , d , & opp - > available ) )
return - ENOMEM ;
if ( ! debugfs_create_bool ( " dynamic " , S_IRUGO , d , & opp - > dynamic ) )
return - ENOMEM ;
if ( ! debugfs_create_bool ( " turbo " , S_IRUGO , d , & opp - > turbo ) )
return - ENOMEM ;
if ( ! debugfs_create_bool ( " suspend " , S_IRUGO , d , & opp - > suspend ) )
return - ENOMEM ;
2017-10-11 10:24:14 +03:00
if ( ! debugfs_create_u32 ( " performance_state " , S_IRUGO , d , & opp - > pstate ) )
return - ENOMEM ;
2015-11-11 05:29:01 +03:00
if ( ! debugfs_create_ulong ( " rate_hz " , S_IRUGO , d , & opp - > rate ) )
return - ENOMEM ;
2016-12-01 13:58:19 +03:00
if ( ! opp_debug_create_supplies ( opp , opp_table , d ) )
2015-11-11 05:29:01 +03:00
return - ENOMEM ;
if ( ! debugfs_create_ulong ( " clock_latency_ns " , S_IRUGO , d ,
& opp - > clock_latency_ns ) )
return - ENOMEM ;
opp - > dentry = d ;
return 0 ;
}
2016-02-16 11:47:53 +03:00
static int opp_list_debug_create_dir ( struct opp_device * opp_dev ,
struct opp_table * opp_table )
2015-11-11 05:29:01 +03:00
{
2016-02-16 11:47:53 +03:00
const struct device * dev = opp_dev - > dev ;
2015-11-11 05:29:01 +03:00
struct dentry * d ;
2016-02-16 11:47:53 +03:00
opp_set_dev_name ( dev , opp_table - > dentry_name ) ;
2015-11-11 05:29:01 +03:00
/* Create device specific directory */
2016-02-16 11:47:53 +03:00
d = debugfs_create_dir ( opp_table - > dentry_name , rootdir ) ;
2015-11-11 05:29:01 +03:00
if ( ! d ) {
dev_err ( dev , " %s: Failed to create debugfs dir \n " , __func__ ) ;
return - ENOMEM ;
}
2016-02-16 11:47:53 +03:00
opp_dev - > dentry = d ;
opp_table - > dentry = d ;
2015-11-11 05:29:01 +03:00
return 0 ;
}
2016-02-16 11:47:53 +03:00
static int opp_list_debug_create_link ( struct opp_device * opp_dev ,
struct opp_table * opp_table )
2015-11-11 05:29:01 +03:00
{
2016-02-16 11:47:53 +03:00
const struct device * dev = opp_dev - > dev ;
2015-11-11 05:29:01 +03:00
char name [ NAME_MAX ] ;
struct dentry * d ;
2016-02-16 11:47:53 +03:00
opp_set_dev_name ( opp_dev - > dev , name ) ;
2015-11-11 05:29:01 +03:00
/* Create device specific directory link */
2016-02-16 11:47:53 +03:00
d = debugfs_create_symlink ( name , rootdir , opp_table - > dentry_name ) ;
2015-11-11 05:29:01 +03:00
if ( ! d ) {
dev_err ( dev , " %s: Failed to create link \n " , __func__ ) ;
return - ENOMEM ;
}
2016-02-16 11:47:53 +03:00
opp_dev - > dentry = d ;
2015-11-11 05:29:01 +03:00
return 0 ;
}
/**
* opp_debug_register - add a device opp node to the debugfs ' opp ' directory
2016-02-16 11:47:53 +03:00
* @ opp_dev : opp - dev pointer for device
* @ opp_table : the device - opp being added
2015-11-11 05:29:01 +03:00
*
* Dynamically adds device specific directory in debugfs ' opp ' directory . If the
* device - opp is shared with other devices , then links will be created for all
* devices except the first .
*
* Return : 0 on success , otherwise negative error .
*/
2016-02-16 11:47:53 +03:00
int opp_debug_register ( struct opp_device * opp_dev , struct opp_table * opp_table )
2015-11-11 05:29:01 +03:00
{
if ( ! rootdir ) {
pr_debug ( " %s: Uninitialized rootdir \n " , __func__ ) ;
return - EINVAL ;
}
2016-02-16 11:47:53 +03:00
if ( opp_table - > dentry )
return opp_list_debug_create_link ( opp_dev , opp_table ) ;
2015-11-11 05:29:01 +03:00
2016-02-16 11:47:53 +03:00
return opp_list_debug_create_dir ( opp_dev , opp_table ) ;
2015-11-11 05:29:01 +03:00
}
2016-02-16 11:47:53 +03:00
static void opp_migrate_dentry ( struct opp_device * opp_dev ,
struct opp_table * opp_table )
2015-11-11 05:29:01 +03:00
{
2016-02-16 11:47:53 +03:00
struct opp_device * new_dev ;
2015-11-11 05:29:01 +03:00
const struct device * dev ;
struct dentry * dentry ;
2016-02-16 11:47:53 +03:00
/* Look for next opp-dev */
list_for_each_entry ( new_dev , & opp_table - > dev_list , node )
if ( new_dev ! = opp_dev )
2015-11-11 05:29:01 +03:00
break ;
/* new_dev is guaranteed to be valid here */
dev = new_dev - > dev ;
debugfs_remove_recursive ( new_dev - > dentry ) ;
2016-02-16 11:47:53 +03:00
opp_set_dev_name ( dev , opp_table - > dentry_name ) ;
2015-11-11 05:29:01 +03:00
2016-02-16 11:47:53 +03:00
dentry = debugfs_rename ( rootdir , opp_dev - > dentry , rootdir ,
opp_table - > dentry_name ) ;
2015-11-11 05:29:01 +03:00
if ( ! dentry ) {
dev_err ( dev , " %s: Failed to rename link from: %s to %s \n " ,
2016-02-16 11:47:53 +03:00
__func__ , dev_name ( opp_dev - > dev ) , dev_name ( dev ) ) ;
2015-11-11 05:29:01 +03:00
return ;
}
new_dev - > dentry = dentry ;
2016-02-16 11:47:53 +03:00
opp_table - > dentry = dentry ;
2015-11-11 05:29:01 +03:00
}
/**
* opp_debug_unregister - remove a device opp node from debugfs opp directory
2016-02-16 11:47:53 +03:00
* @ opp_dev : opp - dev pointer for device
* @ opp_table : the device - opp being removed
2015-11-11 05:29:01 +03:00
*
* Dynamically removes device specific directory from debugfs ' opp ' directory .
*/
2016-02-16 11:47:53 +03:00
void opp_debug_unregister ( struct opp_device * opp_dev ,
struct opp_table * opp_table )
2015-11-11 05:29:01 +03:00
{
2016-02-16 11:47:53 +03:00
if ( opp_dev - > dentry = = opp_table - > dentry ) {
2015-11-11 05:29:01 +03:00
/* Move the real dentry object under another device */
2016-02-16 11:47:53 +03:00
if ( ! list_is_singular ( & opp_table - > dev_list ) ) {
opp_migrate_dentry ( opp_dev , opp_table ) ;
2015-11-11 05:29:01 +03:00
goto out ;
}
2016-02-16 11:47:53 +03:00
opp_table - > dentry = NULL ;
2015-11-11 05:29:01 +03:00
}
2016-02-16 11:47:53 +03:00
debugfs_remove_recursive ( opp_dev - > dentry ) ;
2015-11-11 05:29:01 +03:00
out :
2016-02-16 11:47:53 +03:00
opp_dev - > dentry = NULL ;
2015-11-11 05:29:01 +03:00
}
static int __init opp_debug_init ( void )
{
/* Create /sys/kernel/debug/opp directory */
rootdir = debugfs_create_dir ( " opp " , NULL ) ;
if ( ! rootdir ) {
pr_err ( " %s: Failed to create root directory \n " , __func__ ) ;
return - ENOMEM ;
}
return 0 ;
}
core_initcall ( opp_debug_init ) ;