2014-11-03 11:07:36 -07:00
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
2016-02-17 17:52:03 -07:00
*
* Description : CoreSight Trace Memory Controller driver
2014-11-03 11:07:36 -07:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/device.h>
# include <linux/io.h>
# include <linux/err.h>
# include <linux/fs.h>
# include <linux/miscdevice.h>
# include <linux/uaccess.h>
# include <linux/slab.h>
# include <linux/dma-mapping.h>
# include <linux/spinlock.h>
2015-05-19 10:55:13 -06:00
# include <linux/pm_runtime.h>
2014-11-03 11:07:36 -07:00
# include <linux/of.h>
# include <linux/coresight.h>
# include <linux/amba/bus.h>
# include "coresight-priv.h"
2016-05-03 11:33:48 -06:00
# include "coresight-tmc.h"
2014-11-03 11:07:36 -07:00
2016-05-03 11:33:50 -06:00
void tmc_wait_for_tmcready ( struct tmc_drvdata * drvdata )
2014-11-03 11:07:36 -07:00
{
/* Ensure formatter, unformatter and hardware fifo are empty */
if ( coresight_timeout ( drvdata - > base ,
2016-05-03 11:33:44 -06:00
TMC_STS , TMC_STS_TMCREADY_BIT , 1 ) ) {
2014-11-03 11:07:36 -07:00
dev_err ( drvdata - > dev ,
" timeout observed when probing at offset %#x \n " ,
TMC_STS ) ;
}
}
2016-05-03 11:33:50 -06:00
void tmc_flush_and_stop ( struct tmc_drvdata * drvdata )
2014-11-03 11:07:36 -07:00
{
u32 ffcr ;
ffcr = readl_relaxed ( drvdata - > base + TMC_FFCR ) ;
ffcr | = TMC_FFCR_STOP_ON_FLUSH ;
writel_relaxed ( ffcr , drvdata - > base + TMC_FFCR ) ;
2016-05-03 11:33:49 -06:00
ffcr | = BIT ( TMC_FFCR_FLUSHMAN_BIT ) ;
2014-11-03 11:07:36 -07:00
writel_relaxed ( ffcr , drvdata - > base + TMC_FFCR ) ;
/* Ensure flush completes */
if ( coresight_timeout ( drvdata - > base ,
TMC_FFCR , TMC_FFCR_FLUSHMAN_BIT , 0 ) ) {
dev_err ( drvdata - > dev ,
" timeout observed when probing at offset %#x \n " ,
TMC_FFCR ) ;
}
2016-05-03 11:33:44 -06:00
tmc_wait_for_tmcready ( drvdata ) ;
2014-11-03 11:07:36 -07:00
}
2016-05-03 11:33:50 -06:00
void tmc_enable_hw ( struct tmc_drvdata * drvdata )
2014-11-03 11:07:36 -07:00
{
writel_relaxed ( TMC_CTL_CAPT_EN , drvdata - > base + TMC_CTL ) ;
}
2016-05-03 11:33:50 -06:00
void tmc_disable_hw ( struct tmc_drvdata * drvdata )
2014-11-03 11:07:36 -07:00
{
writel_relaxed ( 0x0 , drvdata - > base + TMC_CTL ) ;
}
static int tmc_read_prepare ( struct tmc_drvdata * drvdata )
{
2016-05-03 11:33:46 -06:00
int ret = 0 ;
2014-11-03 11:07:36 -07:00
2016-05-03 11:33:46 -06:00
switch ( drvdata - > config_type ) {
case TMC_CONFIG_TYPE_ETB :
case TMC_CONFIG_TYPE_ETF :
2016-05-03 11:33:51 -06:00
ret = tmc_read_prepare_etb ( drvdata ) ;
2016-05-03 11:33:46 -06:00
break ;
case TMC_CONFIG_TYPE_ETR :
2016-05-03 11:33:51 -06:00
ret = tmc_read_prepare_etr ( drvdata ) ;
2016-05-03 11:33:46 -06:00
break ;
default :
ret = - EINVAL ;
2014-11-03 11:07:36 -07:00
}
2016-05-03 11:33:46 -06:00
2016-05-03 11:33:51 -06:00
if ( ! ret )
dev_info ( drvdata - > dev , " TMC read start \n " ) ;
2014-11-03 11:07:36 -07:00
return ret ;
}
2016-05-03 11:33:53 -06:00
static int tmc_read_unprepare ( struct tmc_drvdata * drvdata )
2014-11-03 11:07:36 -07:00
{
2016-05-03 11:33:51 -06:00
int ret = 0 ;
2014-11-03 11:07:36 -07:00
2016-05-03 11:33:46 -06:00
switch ( drvdata - > config_type ) {
case TMC_CONFIG_TYPE_ETB :
case TMC_CONFIG_TYPE_ETF :
2016-05-03 11:33:51 -06:00
ret = tmc_read_unprepare_etb ( drvdata ) ;
2016-05-03 11:33:46 -06:00
break ;
case TMC_CONFIG_TYPE_ETR :
2016-05-03 11:33:51 -06:00
ret = tmc_read_unprepare_etr ( drvdata ) ;
2016-05-03 11:33:46 -06:00
break ;
default :
2016-05-03 11:33:51 -06:00
ret = - EINVAL ;
2014-11-03 11:07:36 -07:00
}
2016-05-03 11:33:46 -06:00
2016-05-03 11:33:51 -06:00
if ( ! ret )
dev_info ( drvdata - > dev , " TMC read end \n " ) ;
2016-05-03 11:33:53 -06:00
return ret ;
2014-11-03 11:07:36 -07:00
}
static int tmc_open ( struct inode * inode , struct file * file )
{
2016-05-03 11:33:53 -06:00
int ret ;
2014-11-03 11:07:36 -07:00
struct tmc_drvdata * drvdata = container_of ( file - > private_data ,
struct tmc_drvdata , miscdev ) ;
ret = tmc_read_prepare ( drvdata ) ;
if ( ret )
return ret ;
2016-05-03 11:33:53 -06:00
2014-11-03 11:07:36 -07:00
nonseekable_open ( inode , file ) ;
dev_dbg ( drvdata - > dev , " %s: successfully opened \n " , __func__ ) ;
return 0 ;
}
static ssize_t tmc_read ( struct file * file , char __user * data , size_t len ,
loff_t * ppos )
{
struct tmc_drvdata * drvdata = container_of ( file - > private_data ,
struct tmc_drvdata , miscdev ) ;
char * bufp = drvdata - > buf + * ppos ;
2016-08-25 15:18:57 -06:00
if ( * ppos + len > drvdata - > len )
len = drvdata - > len - * ppos ;
2014-11-03 11:07:36 -07:00
if ( drvdata - > config_type = = TMC_CONFIG_TYPE_ETR ) {
if ( bufp = = ( char * ) ( drvdata - > vaddr + drvdata - > size ) )
bufp = drvdata - > vaddr ;
else if ( bufp > ( char * ) ( drvdata - > vaddr + drvdata - > size ) )
bufp - = drvdata - > size ;
if ( ( bufp + len ) > ( char * ) ( drvdata - > vaddr + drvdata - > size ) )
len = ( char * ) ( drvdata - > vaddr + drvdata - > size ) - bufp ;
}
if ( copy_to_user ( data , bufp , len ) ) {
dev_dbg ( drvdata - > dev , " %s: copy_to_user failed \n " , __func__ ) ;
return - EFAULT ;
}
* ppos + = len ;
2015-03-30 14:13:35 -06:00
dev_dbg ( drvdata - > dev , " %s: %zu bytes copied, %d bytes left \n " ,
2016-08-25 15:18:57 -06:00
__func__ , len , ( int ) ( drvdata - > len - * ppos ) ) ;
2014-11-03 11:07:36 -07:00
return len ;
}
static int tmc_release ( struct inode * inode , struct file * file )
{
2016-05-03 11:33:53 -06:00
int ret ;
2014-11-03 11:07:36 -07:00
struct tmc_drvdata * drvdata = container_of ( file - > private_data ,
struct tmc_drvdata , miscdev ) ;
2016-05-03 11:33:53 -06:00
ret = tmc_read_unprepare ( drvdata ) ;
if ( ret )
return ret ;
2014-11-03 11:07:36 -07:00
dev_dbg ( drvdata - > dev , " %s: released \n " , __func__ ) ;
return 0 ;
}
static const struct file_operations tmc_fops = {
. owner = THIS_MODULE ,
. open = tmc_open ,
. read = tmc_read ,
. release = tmc_release ,
. llseek = no_llseek ,
} ;
2016-05-03 11:33:57 -06:00
static enum tmc_mem_intf_width tmc_get_memwidth ( u32 devid )
{
enum tmc_mem_intf_width memwidth ;
/*
* Excerpt from the TRM :
*
* DEVID : : MEMWIDTH [ 10 : 8 ]
* 0x2 Memory interface databus is 32 bits wide .
* 0x3 Memory interface databus is 64 bits wide .
* 0x4 Memory interface databus is 128 bits wide .
* 0x5 Memory interface databus is 256 bits wide .
*/
switch ( BMVAL ( devid , 8 , 10 ) ) {
case 0x2 :
memwidth = TMC_MEM_INTF_WIDTH_32BITS ;
break ;
case 0x3 :
memwidth = TMC_MEM_INTF_WIDTH_64BITS ;
break ;
case 0x4 :
memwidth = TMC_MEM_INTF_WIDTH_128BITS ;
break ;
case 0x5 :
memwidth = TMC_MEM_INTF_WIDTH_256BITS ;
break ;
default :
memwidth = 0 ;
}
return memwidth ;
}
2016-05-03 11:33:43 -06:00
# define coresight_tmc_simple_func(name, offset) \
coresight_simple_func ( struct tmc_drvdata , name , offset )
coresight_tmc_simple_func ( rsz , TMC_RSZ ) ;
coresight_tmc_simple_func ( sts , TMC_STS ) ;
coresight_tmc_simple_func ( rrp , TMC_RRP ) ;
coresight_tmc_simple_func ( rwp , TMC_RWP ) ;
coresight_tmc_simple_func ( trg , TMC_TRG ) ;
coresight_tmc_simple_func ( ctl , TMC_CTL ) ;
coresight_tmc_simple_func ( ffsr , TMC_FFSR ) ;
coresight_tmc_simple_func ( ffcr , TMC_FFCR ) ;
coresight_tmc_simple_func ( mode , TMC_MODE ) ;
coresight_tmc_simple_func ( pscr , TMC_PSCR ) ;
coresight_tmc_simple_func ( devid , CORESIGHT_DEVID ) ;
static struct attribute * coresight_tmc_mgmt_attrs [ ] = {
& dev_attr_rsz . attr ,
& dev_attr_sts . attr ,
& dev_attr_rrp . attr ,
& dev_attr_rwp . attr ,
& dev_attr_trg . attr ,
& dev_attr_ctl . attr ,
& dev_attr_ffsr . attr ,
& dev_attr_ffcr . attr ,
& dev_attr_mode . attr ,
& dev_attr_pscr . attr ,
& dev_attr_devid . attr ,
NULL ,
} ;
2015-03-30 14:13:40 -06:00
2016-05-03 11:33:43 -06:00
ssize_t trigger_cntr_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2014-11-03 11:07:36 -07:00
{
struct tmc_drvdata * drvdata = dev_get_drvdata ( dev - > parent ) ;
unsigned long val = drvdata - > trigger_cntr ;
return sprintf ( buf , " %#lx \n " , val ) ;
}
static ssize_t trigger_cntr_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t size )
{
int ret ;
unsigned long val ;
struct tmc_drvdata * drvdata = dev_get_drvdata ( dev - > parent ) ;
ret = kstrtoul ( buf , 16 , & val ) ;
if ( ret )
return ret ;
drvdata - > trigger_cntr = val ;
return size ;
}
static DEVICE_ATTR_RW ( trigger_cntr ) ;
2016-05-03 11:33:43 -06:00
static struct attribute * coresight_tmc_attrs [ ] = {
2014-11-03 11:07:36 -07:00
& dev_attr_trigger_cntr . attr ,
NULL ,
} ;
2016-05-03 11:33:43 -06:00
static const struct attribute_group coresight_tmc_group = {
. attrs = coresight_tmc_attrs ,
2014-11-03 11:07:36 -07:00
} ;
2016-05-03 11:33:43 -06:00
static const struct attribute_group coresight_tmc_mgmt_group = {
. attrs = coresight_tmc_mgmt_attrs ,
. name = " mgmt " ,
} ;
const struct attribute_group * coresight_tmc_groups [ ] = {
& coresight_tmc_group ,
& coresight_tmc_mgmt_group ,
2014-11-03 11:07:36 -07:00
NULL ,
} ;
static int tmc_probe ( struct amba_device * adev , const struct amba_id * id )
{
int ret = 0 ;
u32 devid ;
void __iomem * base ;
struct device * dev = & adev - > dev ;
struct coresight_platform_data * pdata = NULL ;
struct tmc_drvdata * drvdata ;
struct resource * res = & adev - > res ;
struct coresight_desc * desc ;
struct device_node * np = adev - > dev . of_node ;
if ( np ) {
pdata = of_get_coresight_platform_data ( dev , np ) ;
2016-08-25 15:18:55 -06:00
if ( IS_ERR ( pdata ) ) {
ret = PTR_ERR ( pdata ) ;
goto out ;
}
2014-11-03 11:07:36 -07:00
adev - > dev . platform_data = pdata ;
}
2016-08-25 15:18:55 -06:00
ret = - ENOMEM ;
2014-11-03 11:07:36 -07:00
drvdata = devm_kzalloc ( dev , sizeof ( * drvdata ) , GFP_KERNEL ) ;
if ( ! drvdata )
2016-08-25 15:18:55 -06:00
goto out ;
desc = devm_kzalloc ( dev , sizeof ( * desc ) , GFP_KERNEL ) ;
if ( ! desc )
goto out ;
2014-11-03 11:07:36 -07:00
drvdata - > dev = & adev - > dev ;
dev_set_drvdata ( dev , drvdata ) ;
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource ( dev , res ) ;
2016-08-25 15:18:55 -06:00
if ( IS_ERR ( base ) ) {
ret = PTR_ERR ( base ) ;
goto out ;
}
2014-11-03 11:07:36 -07:00
drvdata - > base = base ;
spin_lock_init ( & drvdata - > spinlock ) ;
devid = readl_relaxed ( drvdata - > base + CORESIGHT_DEVID ) ;
drvdata - > config_type = BMVAL ( devid , 6 , 7 ) ;
2016-05-03 11:33:57 -06:00
drvdata - > memwidth = tmc_get_memwidth ( devid ) ;
2014-11-03 11:07:36 -07:00
if ( drvdata - > config_type = = TMC_CONFIG_TYPE_ETR ) {
if ( np )
ret = of_property_read_u32 ( np ,
" arm,buffer-size " ,
& drvdata - > size ) ;
if ( ret )
drvdata - > size = SZ_1M ;
} else {
drvdata - > size = readl_relaxed ( drvdata - > base + TMC_RSZ ) * 4 ;
}
2015-05-19 10:55:13 -06:00
pm_runtime_put ( & adev - > dev ) ;
2014-11-03 11:07:36 -07:00
desc - > pdata = pdata ;
desc - > dev = dev ;
desc - > subtype . sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER ;
2016-05-03 11:33:43 -06:00
desc - > groups = coresight_tmc_groups ;
2014-11-03 11:07:36 -07:00
if ( drvdata - > config_type = = TMC_CONFIG_TYPE_ETB ) {
desc - > type = CORESIGHT_DEV_TYPE_SINK ;
desc - > ops = & tmc_etb_cs_ops ;
} else if ( drvdata - > config_type = = TMC_CONFIG_TYPE_ETR ) {
desc - > type = CORESIGHT_DEV_TYPE_SINK ;
desc - > ops = & tmc_etr_cs_ops ;
} else {
desc - > type = CORESIGHT_DEV_TYPE_LINKSINK ;
desc - > subtype . link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO ;
desc - > ops = & tmc_etf_cs_ops ;
}
drvdata - > csdev = coresight_register ( desc ) ;
if ( IS_ERR ( drvdata - > csdev ) ) {
ret = PTR_ERR ( drvdata - > csdev ) ;
2016-08-25 15:18:55 -06:00
goto out ;
2014-11-03 11:07:36 -07:00
}
drvdata - > miscdev . name = pdata - > name ;
drvdata - > miscdev . minor = MISC_DYNAMIC_MINOR ;
drvdata - > miscdev . fops = & tmc_fops ;
ret = misc_register ( & drvdata - > miscdev ) ;
if ( ret )
2016-08-25 15:18:55 -06:00
coresight_unregister ( drvdata - > csdev ) ;
out :
2014-11-03 11:07:36 -07:00
return ret ;
}
static struct amba_id tmc_ids [ ] = {
{
. id = 0x0003b961 ,
. mask = 0x0003ffff ,
} ,
{ 0 , 0 } ,
} ;
static struct amba_driver tmc_driver = {
. drv = {
. name = " coresight-tmc " ,
. owner = THIS_MODULE ,
2016-02-02 14:14:00 -07:00
. suppress_bind_attrs = true ,
2014-11-03 11:07:36 -07:00
} ,
. probe = tmc_probe ,
. id_table = tmc_ids ,
} ;
2016-02-17 17:52:03 -07:00
builtin_amba_driver ( tmc_driver ) ;