2018-03-28 18:46:15 +03:00
// SPDX-License-Identifier: GPL-2.0
2015-09-22 15:47:17 +03:00
/*
* Intel ( R ) Trace Hub Software Trace Hub support
*
* Copyright ( C ) 2014 - 2015 Intel Corporation .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/types.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/io.h>
# include <linux/mm.h>
# include <linux/slab.h>
# include <linux/stm.h>
# include "intel_th.h"
# include "sth.h"
struct sth_device {
void __iomem * base ;
void __iomem * channels ;
phys_addr_t channels_phys ;
struct device * dev ;
struct stm_data stm ;
unsigned int sw_nmasters ;
} ;
static struct intel_th_channel __iomem *
sth_channel ( struct sth_device * sth , unsigned int master , unsigned int channel )
{
struct intel_th_channel __iomem * sw_map = sth - > channels ;
return & sw_map [ ( master - sth - > stm . sw_start ) * sth - > stm . sw_nchannels +
channel ] ;
}
static void sth_iowrite ( void __iomem * dest , const unsigned char * payload ,
unsigned int size )
{
switch ( size ) {
# ifdef CONFIG_64BIT
case 8 :
writeq_relaxed ( * ( u64 * ) payload , dest ) ;
break ;
# endif
case 4 :
writel_relaxed ( * ( u32 * ) payload , dest ) ;
break ;
case 2 :
writew_relaxed ( * ( u16 * ) payload , dest ) ;
break ;
case 1 :
writeb_relaxed ( * ( u8 * ) payload , dest ) ;
break ;
default :
break ;
}
}
2016-11-21 15:57:21 +08:00
static ssize_t notrace sth_stm_packet ( struct stm_data * stm_data ,
unsigned int master ,
unsigned int channel ,
unsigned int packet ,
unsigned int flags ,
unsigned int size ,
const unsigned char * payload )
2015-09-22 15:47:17 +03:00
{
struct sth_device * sth = container_of ( stm_data , struct sth_device , stm ) ;
struct intel_th_channel __iomem * out =
sth_channel ( sth , master , channel ) ;
u64 __iomem * outp = & out - > Dn ;
unsigned long reg = REG_STH_TRIG ;
# ifndef CONFIG_64BIT
if ( size > 4 )
size = 4 ;
# endif
size = rounddown_pow_of_two ( size ) ;
switch ( packet ) {
/* Global packets (GERR, XSYNC, TRIG) are sent with register writes */
case STP_PACKET_GERR :
reg + = 4 ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2019-02-12 15:43:02 -06:00
2015-09-22 15:47:17 +03:00
case STP_PACKET_XSYNC :
reg + = 8 ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2019-02-12 15:43:02 -06:00
2015-09-22 15:47:17 +03:00
case STP_PACKET_TRIG :
if ( flags & STP_PACKET_TIMESTAMPED )
reg + = 4 ;
2016-02-15 19:11:58 +02:00
writeb_relaxed ( * payload , sth - > base + reg ) ;
2015-09-22 15:47:17 +03:00
break ;
case STP_PACKET_MERR :
2016-02-15 19:11:58 +02:00
if ( size > 4 )
size = 4 ;
2015-09-22 15:47:17 +03:00
sth_iowrite ( & out - > MERR , payload , size ) ;
break ;
case STP_PACKET_FLAG :
if ( flags & STP_PACKET_TIMESTAMPED )
outp = ( u64 __iomem * ) & out - > FLAG_TS ;
else
outp = ( u64 __iomem * ) & out - > FLAG ;
2016-02-15 19:11:58 +02:00
size = 0 ;
writeb_relaxed ( 0 , outp ) ;
2015-09-22 15:47:17 +03:00
break ;
case STP_PACKET_USER :
if ( flags & STP_PACKET_TIMESTAMPED )
outp = & out - > USER_TS ;
else
outp = & out - > USER ;
sth_iowrite ( outp , payload , size ) ;
break ;
case STP_PACKET_DATA :
outp = & out - > Dn ;
if ( flags & STP_PACKET_TIMESTAMPED )
outp + = 2 ;
if ( flags & STP_PACKET_MARKED )
outp + + ;
sth_iowrite ( outp , payload , size ) ;
break ;
2016-02-15 19:11:58 +02:00
default :
return - ENOTSUPP ;
2015-09-22 15:47:17 +03:00
}
return size ;
}
static phys_addr_t
sth_stm_mmio_addr ( struct stm_data * stm_data , unsigned int master ,
unsigned int channel , unsigned int nr_chans )
{
struct sth_device * sth = container_of ( stm_data , struct sth_device , stm ) ;
phys_addr_t addr ;
master - = sth - > stm . sw_start ;
addr = sth - > channels_phys + ( master * sth - > stm . sw_nchannels + channel ) *
sizeof ( struct intel_th_channel ) ;
if ( offset_in_page ( addr ) | |
offset_in_page ( nr_chans * sizeof ( struct intel_th_channel ) ) )
return 0 ;
return addr ;
}
static int sth_stm_link ( struct stm_data * stm_data , unsigned int master ,
unsigned int channel )
{
struct sth_device * sth = container_of ( stm_data , struct sth_device , stm ) ;
2020-07-06 19:13:39 +03:00
return intel_th_set_output ( to_intel_th_device ( sth - > dev ) , master ) ;
2015-09-22 15:47:17 +03:00
}
static int intel_th_sw_init ( struct sth_device * sth )
{
u32 reg ;
reg = ioread32 ( sth - > base + REG_STH_STHCAP1 ) ;
sth - > stm . sw_nchannels = reg & 0xff ;
reg = ioread32 ( sth - > base + REG_STH_STHCAP0 ) ;
sth - > stm . sw_start = reg & 0xffff ;
sth - > stm . sw_end = reg > > 16 ;
sth - > sw_nmasters = sth - > stm . sw_end - sth - > stm . sw_start ;
dev_dbg ( sth - > dev , " sw_start: %x sw_end: %x masters: %x nchannels: %x \n " ,
sth - > stm . sw_start , sth - > stm . sw_end , sth - > sw_nmasters ,
sth - > stm . sw_nchannels ) ;
return 0 ;
}
static int intel_th_sth_probe ( struct intel_th_device * thdev )
{
struct device * dev = & thdev - > dev ;
struct sth_device * sth ;
struct resource * res ;
void __iomem * base , * channels ;
int err ;
res = intel_th_device_get_resource ( thdev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENODEV ;
base = devm_ioremap ( dev , res - > start , resource_size ( res ) ) ;
2015-10-16 17:09:13 +03:00
if ( ! base )
return - ENOMEM ;
2015-09-22 15:47:17 +03:00
res = intel_th_device_get_resource ( thdev , IORESOURCE_MEM , 1 ) ;
if ( ! res )
return - ENODEV ;
channels = devm_ioremap ( dev , res - > start , resource_size ( res ) ) ;
2015-10-16 17:09:13 +03:00
if ( ! channels )
return - ENOMEM ;
2015-09-22 15:47:17 +03:00
sth = devm_kzalloc ( dev , sizeof ( * sth ) , GFP_KERNEL ) ;
if ( ! sth )
return - ENOMEM ;
sth - > dev = dev ;
sth - > base = base ;
sth - > channels = channels ;
sth - > channels_phys = res - > start ;
sth - > stm . name = dev_name ( dev ) ;
sth - > stm . packet = sth_stm_packet ;
sth - > stm . mmio_addr = sth_stm_mmio_addr ;
sth - > stm . sw_mmiosz = sizeof ( struct intel_th_channel ) ;
sth - > stm . link = sth_stm_link ;
err = intel_th_sw_init ( sth ) ;
if ( err )
return err ;
err = stm_register_device ( dev , & sth - > stm , THIS_MODULE ) ;
if ( err ) {
dev_err ( dev , " stm_register_device failed \n " ) ;
return err ;
}
dev_set_drvdata ( dev , sth ) ;
return 0 ;
}
static void intel_th_sth_remove ( struct intel_th_device * thdev )
{
struct sth_device * sth = dev_get_drvdata ( & thdev - > dev ) ;
stm_unregister_device ( & sth - > stm ) ;
}
static struct intel_th_driver intel_th_sth_driver = {
. probe = intel_th_sth_probe ,
. remove = intel_th_sth_remove ,
. driver = {
. name = " sth " ,
. owner = THIS_MODULE ,
} ,
} ;
module_driver ( intel_th_sth_driver ,
intel_th_driver_register ,
intel_th_driver_unregister ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Intel(R) Trace Hub Software Trace Hub driver " ) ;
MODULE_AUTHOR ( " Alexander Shishkin <alexander.shishkin@intel.com> " ) ;