2019-05-27 08:55:21 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-04-04 08:59:27 -07:00
/*
* TI SCI Generic Power Domain Driver
*
2017-04-24 14:17:35 -07:00
* Copyright ( C ) 2015 - 2017 Texas Instruments Incorporated - http : //www.ti.com/
2017-04-04 08:59:27 -07:00
* J Keerthy < j - keerthy @ ti . com >
* Dave Gerlach < d - gerlach @ ti . com >
*/
# include <linux/err.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/pm_domain.h>
# include <linux/slab.h>
# include <linux/soc/ti/ti_sci_protocol.h>
2019-08-26 20:00:41 -07:00
# include <dt-bindings/soc/ti,sci_pm_domain.h>
2017-04-04 08:59:27 -07:00
/**
2020-09-11 21:47:08 -07:00
* struct ti_sci_genpd_provider : holds common TI SCI genpd provider data
* @ ti_sci : handle to TI SCI protocol driver that provides ops to
* communicate with system control processor .
* @ dev : pointer to dev for the driver for devm allocs
* @ pd_list : list of all the power domains on the device
* @ data : onecell data for genpd core
2017-04-04 08:59:27 -07:00
*/
2020-09-11 21:47:08 -07:00
struct ti_sci_genpd_provider {
const struct ti_sci_handle * ti_sci ;
struct device * dev ;
struct list_head pd_list ;
struct genpd_onecell_data data ;
2017-04-04 08:59:27 -07:00
} ;
/**
* struct ti_sci_pm_domain : TI specific data needed for power domain
2020-09-11 21:47:08 -07:00
* @ idx : index of the device that identifies it with the system
* control processor .
* @ exclusive : Permissions for exclusive request or shared request of the
* device .
2017-04-04 08:59:27 -07:00
* @ pd : generic_pm_domain for use with the genpd framework
2020-09-11 21:47:08 -07:00
* @ node : link for the genpd list
* @ parent : link to the parent TI SCI genpd provider
2017-04-04 08:59:27 -07:00
*/
struct ti_sci_pm_domain {
2020-09-11 21:47:08 -07:00
int idx ;
u8 exclusive ;
2017-04-04 08:59:27 -07:00
struct generic_pm_domain pd ;
2020-09-11 21:47:08 -07:00
struct list_head node ;
struct ti_sci_genpd_provider * parent ;
2017-04-04 08:59:27 -07:00
} ;
# define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
2020-09-11 21:47:08 -07:00
/*
* ti_sci_pd_power_off ( ) : genpd power down hook
* @ domain : pointer to the powerdomain to power off
2017-04-04 08:59:27 -07:00
*/
2020-09-11 21:47:08 -07:00
static int ti_sci_pd_power_off ( struct generic_pm_domain * domain )
2017-04-04 08:59:27 -07:00
{
2020-09-11 21:47:08 -07:00
struct ti_sci_pm_domain * pd = genpd_to_ti_sci_pd ( domain ) ;
const struct ti_sci_handle * ti_sci = pd - > parent - > ti_sci ;
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
return ti_sci - > ops . dev_ops . put_device ( ti_sci , pd - > idx ) ;
2017-04-04 08:59:27 -07:00
}
2020-09-11 21:47:08 -07:00
/*
* ti_sci_pd_power_on ( ) : genpd power up hook
* @ domain : pointer to the powerdomain to power on
2017-04-04 08:59:27 -07:00
*/
2020-09-11 21:47:08 -07:00
static int ti_sci_pd_power_on ( struct generic_pm_domain * domain )
2017-04-04 08:59:27 -07:00
{
2020-09-11 21:47:08 -07:00
struct ti_sci_pm_domain * pd = genpd_to_ti_sci_pd ( domain ) ;
const struct ti_sci_handle * ti_sci = pd - > parent - > ti_sci ;
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
if ( pd - > exclusive )
return ti_sci - > ops . dev_ops . get_device_exclusive ( ti_sci ,
pd - > idx ) ;
2019-08-26 20:00:41 -07:00
else
2020-09-11 21:47:08 -07:00
return ti_sci - > ops . dev_ops . get_device ( ti_sci , pd - > idx ) ;
2017-04-04 08:59:27 -07:00
}
2020-09-11 21:47:08 -07:00
/*
* ti_sci_pd_xlate ( ) : translation service for TI SCI genpds
* @ genpdspec : DT identification data for the genpd
* @ data : genpd core data for all the powerdomains on the device
2017-04-04 08:59:27 -07:00
*/
2020-09-11 21:47:08 -07:00
static struct generic_pm_domain * ti_sci_pd_xlate (
struct of_phandle_args * genpdspec ,
void * data )
2017-04-04 08:59:27 -07:00
{
2020-09-11 21:47:08 -07:00
struct genpd_onecell_data * genpd_data = data ;
unsigned int idx = genpdspec - > args [ 0 ] ;
2017-04-04 08:59:27 -07:00
2020-10-29 11:33:37 +02:00
if ( genpdspec - > args_count ! = 1 & & genpdspec - > args_count ! = 2 )
2020-09-11 21:47:08 -07:00
return ERR_PTR ( - EINVAL ) ;
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
if ( idx > = genpd_data - > num_domains ) {
pr_err ( " %s: invalid domain index %u \n " , __func__ , idx ) ;
return ERR_PTR ( - EINVAL ) ;
}
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
if ( ! genpd_data - > domains [ idx ] )
return ERR_PTR ( - ENOENT ) ;
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
genpd_to_ti_sci_pd ( genpd_data - > domains [ idx ] ) - > exclusive =
genpdspec - > args [ 1 ] ;
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
return genpd_data - > domains [ idx ] ;
2017-04-04 08:59:27 -07:00
}
static const struct of_device_id ti_sci_pm_domain_matches [ ] = {
{ . compatible = " ti,sci-pm-domain " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ti_sci_pm_domain_matches ) ;
static int ti_sci_pm_domain_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
2020-09-11 21:47:08 -07:00
struct ti_sci_genpd_provider * pd_provider ;
struct ti_sci_pm_domain * pd ;
struct device_node * np = NULL ;
struct of_phandle_args args ;
2017-04-04 08:59:27 -07:00
int ret ;
2020-09-11 21:47:08 -07:00
u32 max_id = 0 ;
int index ;
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
pd_provider = devm_kzalloc ( dev , sizeof ( * pd_provider ) , GFP_KERNEL ) ;
if ( ! pd_provider )
2017-04-04 08:59:27 -07:00
return - ENOMEM ;
2020-09-11 21:47:08 -07:00
pd_provider - > ti_sci = devm_ti_sci_get_handle ( dev ) ;
if ( IS_ERR ( pd_provider - > ti_sci ) )
return PTR_ERR ( pd_provider - > ti_sci ) ;
pd_provider - > dev = dev ;
INIT_LIST_HEAD ( & pd_provider - > pd_list ) ;
/* Find highest device ID used for power domains */
while ( 1 ) {
np = of_find_node_with_property ( np , " power-domains " ) ;
if ( ! np )
break ;
index = 0 ;
while ( 1 ) {
ret = of_parse_phandle_with_args ( np , " power-domains " ,
" #power-domain-cells " ,
index , & args ) ;
if ( ret )
break ;
if ( args . args_count > = 1 & & args . np = = dev - > of_node ) {
if ( args . args [ 0 ] > max_id )
max_id = args . args [ 0 ] ;
pd = devm_kzalloc ( dev , sizeof ( * pd ) , GFP_KERNEL ) ;
if ( ! pd )
return - ENOMEM ;
pd - > pd . name = devm_kasprintf ( dev , GFP_KERNEL ,
" pd:%d " ,
args . args [ 0 ] ) ;
if ( ! pd - > pd . name )
return - ENOMEM ;
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
pd - > pd . power_off = ti_sci_pd_power_off ;
pd - > pd . power_on = ti_sci_pd_power_on ;
pd - > idx = args . args [ 0 ] ;
pd - > parent = pd_provider ;
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
pm_genpd_init ( & pd - > pd , NULL , true ) ;
2017-07-28 09:51:34 -07:00
2020-09-11 21:47:08 -07:00
list_add ( & pd - > node , & pd_provider - > pd_list ) ;
}
index + + ;
}
}
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
pd_provider - > data . domains =
devm_kcalloc ( dev , max_id + 1 ,
sizeof ( * pd_provider - > data . domains ) ,
GFP_KERNEL ) ;
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
pd_provider - > data . num_domains = max_id + 1 ;
pd_provider - > data . xlate = ti_sci_pd_xlate ;
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
list_for_each_entry ( pd , & pd_provider - > pd_list , node )
pd_provider - > data . domains [ pd - > idx ] = & pd - > pd ;
2017-04-04 08:59:27 -07:00
2020-09-11 21:47:08 -07:00
return of_genpd_add_provider_onecell ( dev - > of_node , & pd_provider - > data ) ;
2017-04-04 08:59:27 -07:00
}
static struct platform_driver ti_sci_pm_domains_driver = {
. probe = ti_sci_pm_domain_probe ,
. driver = {
. name = " ti_sci_pm_domains " ,
. of_match_table = ti_sci_pm_domain_matches ,
} ,
} ;
module_platform_driver ( ti_sci_pm_domains_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " TI System Control Interface (SCI) Power Domain driver " ) ;
MODULE_AUTHOR ( " Dave Gerlach " ) ;