2018-05-09 21:06:04 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2012 , The Linux Foundation . All rights reserved .
2014-11-03 21:07:35 +03:00
*/
# include <linux/types.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/clk.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_graph.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/amba/bus.h>
# include <linux/coresight.h>
2015-03-30 23:13:34 +03:00
# include <linux/cpumask.h>
2014-11-03 21:07:35 +03:00
# include <asm/smp_plat.h>
static int of_dev_node_match ( struct device * dev , void * data )
{
return dev - > of_node = = data ;
}
static struct device *
of_coresight_get_endpoint_device ( struct device_node * endpoint )
{
struct device * dev = NULL ;
/*
2015-05-19 19:55:20 +03:00
* If we have a non - configurable replicator , it will be found on the
2014-11-03 21:07:35 +03:00
* platform bus .
*/
dev = bus_find_device ( & platform_bus_type , NULL ,
endpoint , of_dev_node_match ) ;
if ( dev )
return dev ;
/*
* We have a configurable component - circle through the AMBA bus
* looking for the device that matches the endpoint node .
*/
return bus_find_device ( & amba_bustype , NULL ,
endpoint , of_dev_node_match ) ;
}
2018-09-20 22:17:43 +03:00
static inline bool of_coresight_legacy_ep_is_input ( struct device_node * ep )
2018-09-20 22:17:41 +03:00
{
return of_property_read_bool ( ep , " slave-mode " ) ;
}
2018-09-20 22:17:43 +03:00
static void of_coresight_get_ports_legacy ( const struct device_node * node ,
int * nr_inport , int * nr_outport )
2014-11-03 21:07:35 +03:00
{
struct device_node * ep = NULL ;
int in = 0 , out = 0 ;
do {
2014-12-01 15:32:32 +03:00
ep = of_graph_get_next_endpoint ( node , ep ) ;
2014-11-03 21:07:35 +03:00
if ( ! ep )
break ;
2018-09-20 22:17:43 +03:00
if ( of_coresight_legacy_ep_is_input ( ep ) )
2014-11-03 21:07:35 +03:00
in + + ;
else
out + + ;
} while ( ep ) ;
* nr_inport = in ;
* nr_outport = out ;
}
2018-09-20 22:17:43 +03:00
static struct device_node * of_coresight_get_port_parent ( struct device_node * ep )
{
struct device_node * parent = of_graph_get_port_parent ( ep ) ;
/*
* Skip one - level up to the real device node , if we
* are using the new bindings .
*/
2019-02-06 02:24:55 +03:00
if ( of_node_name_eq ( parent , " in-ports " ) | |
of_node_name_eq ( parent , " out-ports " ) )
2018-09-20 22:17:43 +03:00
parent = of_get_next_parent ( parent ) ;
return parent ;
}
static inline struct device_node *
of_coresight_get_input_ports_node ( const struct device_node * node )
{
return of_get_child_by_name ( node , " in-ports " ) ;
}
static inline struct device_node *
of_coresight_get_output_ports_node ( const struct device_node * node )
{
return of_get_child_by_name ( node , " out-ports " ) ;
}
static inline int
of_coresight_count_ports ( struct device_node * port_parent )
{
int i = 0 ;
struct device_node * ep = NULL ;
while ( ( ep = of_graph_get_next_endpoint ( port_parent , ep ) ) )
i + + ;
return i ;
}
static void of_coresight_get_ports ( const struct device_node * node ,
int * nr_inport , int * nr_outport )
{
struct device_node * input_ports = NULL , * output_ports = NULL ;
input_ports = of_coresight_get_input_ports_node ( node ) ;
output_ports = of_coresight_get_output_ports_node ( node ) ;
if ( input_ports | | output_ports ) {
if ( input_ports ) {
* nr_inport = of_coresight_count_ports ( input_ports ) ;
of_node_put ( input_ports ) ;
}
if ( output_ports ) {
* nr_outport = of_coresight_count_ports ( output_ports ) ;
of_node_put ( output_ports ) ;
}
} else {
/* Fall back to legacy DT bindings parsing */
of_coresight_get_ports_legacy ( node , nr_inport , nr_outport ) ;
}
}
2014-11-03 21:07:35 +03:00
static int of_coresight_alloc_memory ( struct device * dev ,
struct coresight_platform_data * pdata )
{
2018-09-20 22:17:42 +03:00
if ( pdata - > nr_outport ) {
pdata - > conns = devm_kzalloc ( dev , pdata - > nr_outport *
sizeof ( * pdata - > conns ) ,
GFP_KERNEL ) ;
if ( ! pdata - > conns )
return - ENOMEM ;
}
2014-11-03 21:07:35 +03:00
return 0 ;
}
2017-06-05 23:15:15 +03:00
int of_coresight_get_cpu ( const struct device_node * node )
{
int cpu ;
2018-01-02 14:25:28 +03:00
struct device_node * dn ;
2017-06-05 23:15:15 +03:00
dn = of_parse_phandle ( node , " cpu " , 0 ) ;
/* Affinity defaults to CPU0 */
if ( ! dn )
return 0 ;
2018-01-02 14:25:28 +03:00
cpu = of_cpu_node_to_id ( dn ) ;
2017-06-05 23:15:15 +03:00
of_node_put ( dn ) ;
/* Affinity to CPU0 if no cpu nodes are found */
2018-01-02 14:25:28 +03:00
return ( cpu < 0 ) ? 0 : cpu ;
2017-06-05 23:15:15 +03:00
}
EXPORT_SYMBOL_GPL ( of_coresight_get_cpu ) ;
2018-09-20 22:17:37 +03:00
/*
* of_coresight_parse_endpoint : Parse the given output endpoint @ ep
2018-09-20 22:17:42 +03:00
* and fill the connection information in @ conn
2018-09-20 22:17:37 +03:00
*
* Parses the local port , remote device name and the remote port .
*
* Returns :
* 1 - If the parsing is successful and a connection record
* was created for an output connection .
* 0 - If the parsing completed without any fatal errors .
* - Errno - Fatal error , abort the scanning .
*/
static int of_coresight_parse_endpoint ( struct device * dev ,
struct device_node * ep ,
2018-09-20 22:17:42 +03:00
struct coresight_connection * conn )
2018-09-20 22:17:37 +03:00
{
int ret = 0 ;
struct of_endpoint endpoint , rendpoint ;
struct device_node * rparent = NULL ;
2018-09-20 22:17:40 +03:00
struct device_node * rep = NULL ;
2018-09-20 22:17:37 +03:00
struct device * rdev = NULL ;
do {
/* Parse the local port details */
if ( of_graph_parse_endpoint ( ep , & endpoint ) )
break ;
/*
2018-09-20 22:17:40 +03:00
* Get a handle on the remote endpoint and the device it is
* attached to .
2018-09-20 22:17:37 +03:00
*/
2018-09-20 22:17:40 +03:00
rep = of_graph_get_remote_endpoint ( ep ) ;
if ( ! rep )
2018-09-20 22:17:37 +03:00
break ;
2018-09-20 22:17:43 +03:00
rparent = of_coresight_get_port_parent ( rep ) ;
2018-09-20 22:17:40 +03:00
if ( ! rparent )
2018-09-20 22:17:37 +03:00
break ;
2018-09-20 22:17:40 +03:00
if ( of_graph_parse_endpoint ( rep , & rendpoint ) )
2018-09-20 22:17:37 +03:00
break ;
/* If the remote device is not available, defer probing */
rdev = of_coresight_get_endpoint_device ( rparent ) ;
if ( ! rdev ) {
ret = - EPROBE_DEFER ;
break ;
}
2018-09-20 22:17:42 +03:00
conn - > outport = endpoint . port ;
conn - > child_name = devm_kstrdup ( dev ,
dev_name ( rdev ) ,
GFP_KERNEL ) ;
conn - > child_port = rendpoint . port ;
2018-09-20 22:17:37 +03:00
/* Connection record updated */
ret = 1 ;
} while ( 0 ) ;
2018-09-20 22:18:19 +03:00
of_node_put ( rparent ) ;
of_node_put ( rep ) ;
put_device ( rdev ) ;
2018-09-20 22:17:38 +03:00
2018-09-20 22:17:37 +03:00
return ret ;
}
2017-06-05 23:15:06 +03:00
struct coresight_platform_data *
of_get_coresight_platform_data ( struct device * dev ,
const struct device_node * node )
2014-11-03 21:07:35 +03:00
{
2018-09-20 22:17:42 +03:00
int ret = 0 ;
2014-11-03 21:07:35 +03:00
struct coresight_platform_data * pdata ;
2018-09-20 22:17:42 +03:00
struct coresight_connection * conn ;
2014-11-03 21:07:35 +03:00
struct device_node * ep = NULL ;
2018-09-20 22:17:43 +03:00
const struct device_node * parent = NULL ;
bool legacy_binding = false ;
2014-11-03 21:07:35 +03:00
pdata = devm_kzalloc ( dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return ERR_PTR ( - ENOMEM ) ;
2015-01-10 02:57:19 +03:00
/* Use device name as sysfs handle */
2014-11-03 21:07:35 +03:00
pdata - > name = dev_name ( dev ) ;
2018-09-20 22:17:37 +03:00
pdata - > cpu = of_coresight_get_cpu ( node ) ;
2014-11-03 21:07:35 +03:00
/* Get the number of input and output port for this component */
of_coresight_get_ports ( node , & pdata - > nr_inport , & pdata - > nr_outport ) ;
2018-09-20 22:17:37 +03:00
/* If there are no output connections, we are done */
if ( ! pdata - > nr_outport )
return pdata ;
ret = of_coresight_alloc_memory ( dev , pdata ) ;
if ( ret )
return ERR_PTR ( ret ) ;
2018-09-20 22:17:43 +03:00
parent = of_coresight_get_output_ports_node ( node ) ;
/*
* If the DT uses obsoleted bindings , the ports are listed
* under the device and we need to filter out the input
* ports .
*/
if ( ! parent ) {
legacy_binding = true ;
parent = node ;
dev_warn_once ( dev , " Uses obsolete Coresight DT bindings \n " ) ;
}
2018-09-20 22:17:42 +03:00
conn = pdata - > conns ;
2018-09-20 22:17:43 +03:00
/* Iterate through each output port to discover topology */
while ( ( ep = of_graph_get_next_endpoint ( parent , ep ) ) ) {
2018-09-20 22:17:37 +03:00
/*
2018-09-20 22:17:43 +03:00
* Legacy binding mixes input / output ports under the
* same parent . So , skip the input ports if we are dealing
* with legacy binding , as they processed with their
* connected output ports .
2018-09-20 22:17:37 +03:00
*/
2018-09-20 22:17:43 +03:00
if ( legacy_binding & & of_coresight_legacy_ep_is_input ( ep ) )
2018-09-20 22:17:37 +03:00
continue ;
2018-09-20 22:17:42 +03:00
ret = of_coresight_parse_endpoint ( dev , ep , conn ) ;
2018-09-20 22:17:37 +03:00
switch ( ret ) {
case 1 :
2018-09-20 22:17:42 +03:00
conn + + ; /* Fall through */
2018-09-20 22:17:37 +03:00
case 0 :
break ;
default :
2014-11-03 21:07:35 +03:00
return ERR_PTR ( ret ) ;
2018-09-20 22:17:37 +03:00
}
2014-11-03 21:07:35 +03:00
}
return pdata ;
}
EXPORT_SYMBOL_GPL ( of_get_coresight_platform_data ) ;