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
*/
2019-08-29 14:28:33 -06:00
# include <linux/acpi.h>
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
2019-06-19 13:53:04 -06:00
DEFINE_CORESIGHT_DEVLIST ( funnel_devs , " funnel " ) ;
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 .
2019-11-04 11:12:50 -07:00
* @ spinlock : serialize enable / disable operations .
2014-11-03 11:07:39 -07:00
*/
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-11-04 11:12:50 -07:00
spinlock_t spinlock ;
2014-11-03 11:07:39 -07:00
} ;
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 ;
2021-02-01 11:13:28 -07:00
struct coresight_device * csdev = drvdata - > csdev ;
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 ) ) {
2021-02-01 11:13:28 -07:00
rc = coresight_claim_device_unlocked ( csdev ) ;
2018-09-20 13:18:13 -06:00
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-11-04 11:12:50 -07:00
unsigned long flags ;
bool first_enable = false ;
spin_lock_irqsave ( & drvdata - > spinlock , flags ) ;
if ( atomic_read ( & csdev - > refcnt [ inport ] ) = = 0 ) {
if ( drvdata - > base )
rc = dynamic_funnel_enable_hw ( drvdata , inport ) ;
if ( ! rc )
first_enable = true ;
}
2018-09-20 13:18:13 -06:00
if ( ! rc )
2019-11-04 11:12:50 -07:00
atomic_inc ( & csdev - > refcnt [ inport ] ) ;
spin_unlock_irqrestore ( & drvdata - > spinlock , flags ) ;
if ( first_enable )
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 ;
2021-02-01 11:13:28 -07:00
struct coresight_device * csdev = drvdata - > csdev ;
2014-11-03 11:07:39 -07:00
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 ) )
2021-02-01 11:13:28 -07:00
coresight_disclaim_device_unlocked ( csdev ) ;
2018-09-20 13:18:13 -06:00
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-11-04 11:12:50 -07:00
unsigned long flags ;
bool last_disable = false ;
spin_lock_irqsave ( & drvdata - > spinlock , flags ) ;
if ( atomic_dec_return ( & csdev - > refcnt [ inport ] ) = = 0 ) {
if ( drvdata - > base )
dynamic_funnel_disable_hw ( drvdata , inport ) ;
last_disable = true ;
}
spin_unlock_irqrestore ( & drvdata - > spinlock , flags ) ;
2014-11-03 11:07:39 -07:00
2019-11-04 11:12:50 -07:00
if ( last_disable )
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-08-29 14:28:32 -06:00
dev_warn_once ( dev , " Uses OBSOLETE CoreSight funnel binding \n " ) ;
2019-05-02 10:54:05 -06:00
2019-06-19 13:53:04 -06:00
desc . name = coresight_alloc_device_name ( & funnel_devs , dev ) ;
if ( ! desc . name )
return - ENOMEM ;
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 ;
2021-02-01 11:13:25 -07:00
desc . access = CSDEV_ACCESS_IOMEM ( base ) ;
2019-05-02 10:54:05 -06:00
}
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 ;
2019-11-18 11:52:06 -07:00
spin_lock_init ( & drvdata - > spinlock ) ;
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 ;
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 ) ;
2019-06-20 16:12:37 -06:00
ret = 0 ;
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
}
2020-12-08 11:26:50 -07:00
static int funnel_remove ( struct device * dev )
2020-09-28 10:35:02 -06:00
{
struct funnel_drvdata * drvdata = dev_get_drvdata ( dev ) ;
coresight_unregister ( drvdata - > csdev ) ;
return 0 ;
}
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 ;
}
2020-12-08 11:26:50 -07:00
static int static_funnel_remove ( struct platform_device * pdev )
2020-09-28 10:35:02 -06:00
{
funnel_remove ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
return 0 ;
}
2019-05-02 10:54:05 -06:00
static const struct of_device_id static_funnel_match [ ] = {
{ . compatible = " arm,coresight-static-funnel " } ,
{ }
} ;
2020-09-28 10:35:02 -06:00
MODULE_DEVICE_TABLE ( of , static_funnel_match ) ;
2019-08-29 14:28:33 -06:00
# ifdef CONFIG_ACPI
static const struct acpi_device_id static_funnel_ids [ ] = {
{ " ARMHC9FE " , 0 } ,
{ } ,
} ;
2020-09-28 10:35:02 -06:00
MODULE_DEVICE_TABLE ( acpi , static_funnel_ids ) ;
2019-08-29 14:28:33 -06:00
# endif
2019-05-02 10:54:05 -06:00
static struct platform_driver static_funnel_driver = {
. probe = static_funnel_probe ,
2020-09-28 10:35:02 -06:00
. remove = static_funnel_remove ,
2019-05-02 10:54:05 -06:00
. driver = {
. name = " coresight-static-funnel " ,
2020-11-27 10:52:49 -07:00
/* THIS_MODULE is taken care of by platform_driver_register() */
2019-05-02 10:54:05 -06:00
. of_match_table = static_funnel_match ,
2019-08-29 14:28:33 -06:00
. acpi_match_table = ACPI_PTR ( static_funnel_ids ) ,
2019-05-02 10:54:05 -06:00
. pm = & funnel_dev_pm_ops ,
. suppress_bind_attrs = true ,
} ,
} ;
static int dynamic_funnel_probe ( struct amba_device * adev ,
const struct amba_id * id )
{
return funnel_probe ( & adev - > dev , & adev - > res ) ;
}
2021-01-26 17:58:34 +01:00
static void dynamic_funnel_remove ( struct amba_device * adev )
2020-09-28 10:35:02 -06:00
{
2021-01-26 17:58:34 +01:00
funnel_remove ( & adev - > dev ) ;
2020-09-28 10:35:02 -06:00
}
2019-05-02 10:54:05 -06:00
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 } ,
} ;
2020-09-28 10:35:02 -06:00
MODULE_DEVICE_TABLE ( amba , dynamic_funnel_ids ) ;
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 ,
2020-09-28 10:35:02 -06:00
. remove = dynamic_funnel_remove ,
2019-05-02 10:54:05 -06:00
. id_table = dynamic_funnel_ids ,
2014-11-03 11:07:39 -07:00
} ;
2020-09-28 10:35:02 -06:00
static int __init funnel_init ( void )
{
int ret ;
ret = platform_driver_register ( & static_funnel_driver ) ;
if ( ret ) {
pr_info ( " Error registering platform driver \n " ) ;
return ret ;
}
ret = amba_driver_register ( & dynamic_funnel_driver ) ;
if ( ret ) {
pr_info ( " Error registering amba driver \n " ) ;
platform_driver_unregister ( & static_funnel_driver ) ;
}
return ret ;
}
static void __exit funnel_exit ( void )
{
platform_driver_unregister ( & static_funnel_driver ) ;
amba_driver_unregister ( & dynamic_funnel_driver ) ;
}
module_init ( funnel_init ) ;
module_exit ( funnel_exit ) ;
MODULE_AUTHOR ( " Pratik Patel <pratikp@codeaurora.org> " ) ;
MODULE_AUTHOR ( " Mathieu Poirier <mathieu.poirier@linaro.org> " ) ;
MODULE_DESCRIPTION ( " Arm CoreSight Funnel Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;