2018-05-09 12:06:04 -06:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2011 - 2012 , The Linux Foundation . All rights reserved .
2016-02-17 17:52:03 -07:00
*
* Description : CoreSight Funnel driver
2014-11-03 11:07:39 -07:00
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/fs.h>
# include <linux/slab.h>
2019-05-02 10:54:05 -06:00
# include <linux/of.h>
# include <linux/platform_device.h>
2015-05-19 10:55:12 -06:00
# include <linux/pm_runtime.h>
2014-11-03 11:07:39 -07:00
# include <linux/coresight.h>
# include <linux/amba/bus.h>
2015-05-19 10:55:17 -06:00
# include <linux/clk.h>
2014-11-03 11:07:39 -07:00
# include "coresight-priv.h"
# define FUNNEL_FUNCTL 0x000
# define FUNNEL_PRICTL 0x004
# define FUNNEL_HOLDTIME_MASK 0xf00
# define FUNNEL_HOLDTIME_SHFT 0x8
# define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT)
2018-09-20 13:18:13 -06:00
# define FUNNEL_ENSx_MASK 0xff
2014-11-03 11:07:39 -07:00
/**
* struct funnel_drvdata - specifics associated to a funnel component
* @ base : memory mapped base address for this component .
2015-05-19 10:55:17 -06:00
* @ atclk : optional clock for the core parts of the funnel .
2014-11-03 11:07:39 -07:00
* @ csdev : component vitals needed by the framework .
* @ priority : port selection order .
*/
struct funnel_drvdata {
void __iomem * base ;
2015-05-19 10:55:17 -06:00
struct clk * atclk ;
2014-11-03 11:07:39 -07:00
struct coresight_device * csdev ;
unsigned long priority ;
} ;
2019-05-02 10:54:05 -06:00
static int dynamic_funnel_enable_hw ( struct funnel_drvdata * drvdata , int port )
2014-11-03 11:07:39 -07:00
{
u32 functl ;
2018-09-20 13:18:13 -06:00
int rc = 0 ;
2014-11-03 11:07:39 -07:00
CS_UNLOCK ( drvdata - > base ) ;
functl = readl_relaxed ( drvdata - > base + FUNNEL_FUNCTL ) ;
2018-09-20 13:18:13 -06:00
/* Claim the device only when we enable the first slave */
if ( ! ( functl & FUNNEL_ENSx_MASK ) ) {
rc = coresight_claim_device_unlocked ( drvdata - > base ) ;
if ( rc )
goto done ;
}
2014-11-03 11:07:39 -07:00
functl & = ~ FUNNEL_HOLDTIME_MASK ;
functl | = FUNNEL_HOLDTIME ;
functl | = ( 1 < < port ) ;
writel_relaxed ( functl , drvdata - > base + FUNNEL_FUNCTL ) ;
writel_relaxed ( drvdata - > priority , drvdata - > base + FUNNEL_PRICTL ) ;
2018-09-20 13:18:13 -06:00
done :
2014-11-03 11:07:39 -07:00
CS_LOCK ( drvdata - > base ) ;
2018-09-20 13:18:13 -06:00
return rc ;
2014-11-03 11:07:39 -07:00
}
static int funnel_enable ( struct coresight_device * csdev , int inport ,
int outport )
{
2019-05-02 10:54:05 -06:00
int rc = 0 ;
2014-11-03 11:07:39 -07:00
struct funnel_drvdata * drvdata = dev_get_drvdata ( csdev - > dev . parent ) ;
2019-05-02 10:54:05 -06:00
if ( drvdata - > base )
rc = dynamic_funnel_enable_hw ( drvdata , inport ) ;
2014-11-03 11:07:39 -07:00
2018-09-20 13:18:13 -06:00
if ( ! rc )
2019-06-19 11:29:10 -06:00
dev_dbg ( & csdev - > dev , " FUNNEL inport %d enabled \n " , inport ) ;
2018-09-20 13:18:13 -06:00
return rc ;
2014-11-03 11:07:39 -07:00
}
2019-05-02 10:54:05 -06:00
static void dynamic_funnel_disable_hw ( struct funnel_drvdata * drvdata ,
int inport )
2014-11-03 11:07:39 -07:00
{
u32 functl ;
CS_UNLOCK ( drvdata - > base ) ;
functl = readl_relaxed ( drvdata - > base + FUNNEL_FUNCTL ) ;
functl & = ~ ( 1 < < inport ) ;
writel_relaxed ( functl , drvdata - > base + FUNNEL_FUNCTL ) ;
2018-09-20 13:18:13 -06:00
/* Disclaim the device if none of the slaves are now active */
if ( ! ( functl & FUNNEL_ENSx_MASK ) )
coresight_disclaim_device_unlocked ( drvdata - > base ) ;
2014-11-03 11:07:39 -07:00
CS_LOCK ( drvdata - > base ) ;
}
static void funnel_disable ( struct coresight_device * csdev , int inport ,
int outport )
{
struct funnel_drvdata * drvdata = dev_get_drvdata ( csdev - > dev . parent ) ;
2019-05-02 10:54:05 -06:00
if ( drvdata - > base )
dynamic_funnel_disable_hw ( drvdata , inport ) ;
2014-11-03 11:07:39 -07:00
2019-06-19 11:29:10 -06:00
dev_dbg ( & csdev - > dev , " FUNNEL inport %d disabled \n " , inport ) ;
2014-11-03 11:07:39 -07:00
}
static const struct coresight_ops_link funnel_link_ops = {
. enable = funnel_enable ,
. disable = funnel_disable ,
} ;
static const struct coresight_ops funnel_cs_ops = {
. link_ops = & funnel_link_ops ,
} ;
static ssize_t priority_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct funnel_drvdata * drvdata = dev_get_drvdata ( dev - > parent ) ;
unsigned long val = drvdata - > priority ;
return sprintf ( buf , " %#lx \n " , val ) ;
}
static ssize_t priority_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t size )
{
int ret ;
unsigned long val ;
struct funnel_drvdata * drvdata = dev_get_drvdata ( dev - > parent ) ;
ret = kstrtoul ( buf , 16 , & val ) ;
if ( ret )
return ret ;
drvdata - > priority = val ;
return size ;
}
static DEVICE_ATTR_RW ( priority ) ;
static u32 get_funnel_ctrl_hw ( struct funnel_drvdata * drvdata )
{
u32 functl ;
CS_UNLOCK ( drvdata - > base ) ;
functl = readl_relaxed ( drvdata - > base + FUNNEL_FUNCTL ) ;
CS_LOCK ( drvdata - > base ) ;
return functl ;
}
static ssize_t funnel_ctrl_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
u32 val ;
struct funnel_drvdata * drvdata = dev_get_drvdata ( dev - > parent ) ;
2019-06-19 11:29:10 -06:00
pm_runtime_get_sync ( dev - > parent ) ;
2014-11-03 11:07:39 -07:00
val = get_funnel_ctrl_hw ( drvdata ) ;
2015-05-19 10:55:12 -06:00
2019-06-19 11:29:10 -06:00
pm_runtime_put ( dev - > parent ) ;
2014-11-03 11:07:39 -07:00
return sprintf ( buf , " %#x \n " , val ) ;
}
static DEVICE_ATTR_RO ( funnel_ctrl ) ;
static struct attribute * coresight_funnel_attrs [ ] = {
& dev_attr_funnel_ctrl . attr ,
& dev_attr_priority . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( coresight_funnel ) ;
2019-05-02 10:54:05 -06:00
static int funnel_probe ( struct device * dev , struct resource * res )
2014-11-03 11:07:39 -07:00
{
2015-05-19 10:55:17 -06:00
int ret ;
2014-11-03 11:07:39 -07:00
void __iomem * base ;
struct coresight_platform_data * pdata = NULL ;
struct funnel_drvdata * drvdata ;
2016-08-25 15:19:05 -06:00
struct coresight_desc desc = { 0 } ;
2014-11-03 11:07:39 -07:00
2019-06-19 13:52:53 -06:00
if ( is_of_node ( dev_fwnode ( dev ) ) & &
of_device_is_compatible ( dev - > of_node , " arm,coresight-funnel " ) )
2019-05-02 10:54:05 -06:00
pr_warn_once ( " Uses OBSOLETE CoreSight funnel binding \n " ) ;
2014-11-03 11:07:39 -07:00
drvdata = devm_kzalloc ( dev , sizeof ( * drvdata ) , GFP_KERNEL ) ;
if ( ! drvdata )
return - ENOMEM ;
2019-05-02 10:54:05 -06:00
drvdata - > atclk = devm_clk_get ( dev , " atclk " ) ; /* optional */
2015-05-19 10:55:17 -06:00
if ( ! IS_ERR ( drvdata - > atclk ) ) {
ret = clk_prepare_enable ( drvdata - > atclk ) ;
if ( ret )
return ret ;
}
2014-11-03 11:07:39 -07:00
2019-05-02 10:54:05 -06:00
/*
* Map the device base for dynamic - funnel , which has been
* validated by AMBA core .
*/
if ( res ) {
base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( base ) ) {
ret = PTR_ERR ( base ) ;
goto out_disable_clk ;
}
drvdata - > base = base ;
desc . groups = coresight_funnel_groups ;
}
2014-11-03 11:07:39 -07:00
2019-05-02 10:54:05 -06:00
dev_set_drvdata ( dev , drvdata ) ;
2014-11-03 11:07:39 -07:00
2019-06-19 13:53:00 -06:00
pdata = coresight_get_platform_data ( dev ) ;
if ( IS_ERR ( pdata ) ) {
ret = PTR_ERR ( pdata ) ;
goto out_disable_clk ;
}
dev - > platform_data = pdata ;
2016-08-25 15:19:05 -06:00
desc . type = CORESIGHT_DEV_TYPE_LINK ;
desc . subtype . link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG ;
desc . ops = & funnel_cs_ops ;
desc . pdata = pdata ;
desc . dev = dev ;
2019-06-19 13:52:57 -06:00
desc . name = dev_name ( dev ) ;
2016-08-25 15:19:05 -06:00
drvdata - > csdev = coresight_register ( & desc ) ;
2019-05-02 10:54:05 -06:00
if ( IS_ERR ( drvdata - > csdev ) ) {
ret = PTR_ERR ( drvdata - > csdev ) ;
goto out_disable_clk ;
}
pm_runtime_put ( dev ) ;
2014-11-03 11:07:39 -07:00
2019-05-02 10:54:05 -06:00
out_disable_clk :
if ( ret & & ! IS_ERR_OR_NULL ( drvdata - > atclk ) )
clk_disable_unprepare ( drvdata - > atclk ) ;
return ret ;
2014-11-03 11:07:39 -07:00
}
2015-05-19 10:55:17 -06:00
# ifdef CONFIG_PM
static int funnel_runtime_suspend ( struct device * dev )
{
struct funnel_drvdata * drvdata = dev_get_drvdata ( dev ) ;
if ( drvdata & & ! IS_ERR ( drvdata - > atclk ) )
clk_disable_unprepare ( drvdata - > atclk ) ;
return 0 ;
}
static int funnel_runtime_resume ( struct device * dev )
{
struct funnel_drvdata * drvdata = dev_get_drvdata ( dev ) ;
if ( drvdata & & ! IS_ERR ( drvdata - > atclk ) )
clk_prepare_enable ( drvdata - > atclk ) ;
return 0 ;
}
# endif
static const struct dev_pm_ops funnel_dev_pm_ops = {
SET_RUNTIME_PM_OPS ( funnel_runtime_suspend , funnel_runtime_resume , NULL )
} ;
2019-05-02 10:54:05 -06:00
static int static_funnel_probe ( struct platform_device * pdev )
{
int ret ;
pm_runtime_get_noresume ( & pdev - > dev ) ;
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
/* Static funnel do not have programming base */
ret = funnel_probe ( & pdev - > dev , NULL ) ;
if ( ret ) {
pm_runtime_put_noidle ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
}
return ret ;
}
static const struct of_device_id static_funnel_match [ ] = {
{ . compatible = " arm,coresight-static-funnel " } ,
{ }
} ;
static struct platform_driver static_funnel_driver = {
. probe = static_funnel_probe ,
. driver = {
. name = " coresight-static-funnel " ,
. of_match_table = static_funnel_match ,
. pm = & funnel_dev_pm_ops ,
. suppress_bind_attrs = true ,
} ,
} ;
builtin_platform_driver ( static_funnel_driver ) ;
static int dynamic_funnel_probe ( struct amba_device * adev ,
const struct amba_id * id )
{
return funnel_probe ( & adev - > dev , & adev - > res ) ;
}
static const struct amba_id dynamic_funnel_ids [ ] = {
2014-11-03 11:07:39 -07:00
{
2017-10-10 14:32:12 -06:00
. id = 0x000bb908 ,
. mask = 0x000fffff ,
2014-11-03 11:07:39 -07:00
} ,
2017-08-02 10:22:18 -06:00
{
/* Coresight SoC-600 */
. id = 0x000bb9eb ,
. mask = 0x000fffff ,
} ,
2014-11-03 11:07:39 -07:00
{ 0 , 0 } ,
} ;
2019-05-02 10:54:05 -06:00
static struct amba_driver dynamic_funnel_driver = {
2014-11-03 11:07:39 -07:00
. drv = {
2019-05-02 10:54:05 -06:00
. name = " coresight-dynamic-funnel " ,
2014-11-03 11:07:39 -07:00
. owner = THIS_MODULE ,
2015-05-19 10:55:17 -06:00
. pm = & funnel_dev_pm_ops ,
2016-02-02 14:14:00 -07:00
. suppress_bind_attrs = true ,
2014-11-03 11:07:39 -07:00
} ,
2019-05-02 10:54:05 -06:00
. probe = dynamic_funnel_probe ,
. id_table = dynamic_funnel_ids ,
2014-11-03 11:07:39 -07:00
} ;
2019-05-02 10:54:05 -06:00
builtin_amba_driver ( dynamic_funnel_driver ) ;