2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-05-20 00:05:59 +04:00
/*
2017-03-30 03:29:09 +03:00
* Copyright ( C ) 2014 - 2017 Broadcom
2014-05-20 00:05:59 +04:00
*/
# include <linux/init.h>
# include <linux/types.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/sysfs.h>
# include <linux/io.h>
# include <linux/string.h>
# include <linux/device.h>
# include <linux/list.h>
# include <linux/of.h>
# include <linux/bitops.h>
2014-09-18 23:37:14 +04:00
# include <linux/pm.h>
2017-03-30 03:29:11 +03:00
# include <linux/kernel.h>
# include <linux/kdebug.h>
# include <linux/notifier.h>
2014-05-20 00:05:59 +04:00
2016-01-31 23:29:27 +03:00
# ifdef CONFIG_MIPS
# include <asm/traps.h>
# endif
2014-05-20 00:05:59 +04:00
# define ARB_ERR_CAP_CLEAR (1 << 0)
# define ARB_ERR_CAP_STATUS_TIMEOUT (1 << 12)
# define ARB_ERR_CAP_STATUS_TEA (1 << 11)
# define ARB_ERR_CAP_STATUS_WRITE (1 << 1)
# define ARB_ERR_CAP_STATUS_VALID (1 << 0)
2014-11-26 03:49:51 +03:00
enum {
ARB_TIMER ,
ARB_ERR_CAP_CLR ,
ARB_ERR_CAP_HI_ADDR ,
ARB_ERR_CAP_ADDR ,
ARB_ERR_CAP_STATUS ,
ARB_ERR_CAP_MASTER ,
} ;
2014-11-26 03:49:52 +03:00
static const int gisb_offsets_bcm7038 [ ] = {
[ ARB_TIMER ] = 0x00c ,
[ ARB_ERR_CAP_CLR ] = 0x0c4 ,
[ ARB_ERR_CAP_HI_ADDR ] = - 1 ,
[ ARB_ERR_CAP_ADDR ] = 0x0c8 ,
[ ARB_ERR_CAP_STATUS ] = 0x0d0 ,
[ ARB_ERR_CAP_MASTER ] = - 1 ,
} ;
2017-03-30 03:29:14 +03:00
static const int gisb_offsets_bcm7278 [ ] = {
[ ARB_TIMER ] = 0x008 ,
[ ARB_ERR_CAP_CLR ] = 0x7f8 ,
[ ARB_ERR_CAP_HI_ADDR ] = - 1 ,
[ ARB_ERR_CAP_ADDR ] = 0x7e0 ,
[ ARB_ERR_CAP_STATUS ] = 0x7f0 ,
[ ARB_ERR_CAP_MASTER ] = 0x7f4 ,
} ;
2014-11-26 03:49:52 +03:00
static const int gisb_offsets_bcm7400 [ ] = {
[ ARB_TIMER ] = 0x00c ,
[ ARB_ERR_CAP_CLR ] = 0x0c8 ,
[ ARB_ERR_CAP_HI_ADDR ] = - 1 ,
[ ARB_ERR_CAP_ADDR ] = 0x0cc ,
[ ARB_ERR_CAP_STATUS ] = 0x0d4 ,
[ ARB_ERR_CAP_MASTER ] = 0x0d8 ,
} ;
static const int gisb_offsets_bcm7435 [ ] = {
[ ARB_TIMER ] = 0x00c ,
[ ARB_ERR_CAP_CLR ] = 0x168 ,
[ ARB_ERR_CAP_HI_ADDR ] = - 1 ,
[ ARB_ERR_CAP_ADDR ] = 0x16c ,
[ ARB_ERR_CAP_STATUS ] = 0x174 ,
[ ARB_ERR_CAP_MASTER ] = 0x178 ,
} ;
2014-11-26 03:49:51 +03:00
static const int gisb_offsets_bcm7445 [ ] = {
[ ARB_TIMER ] = 0x008 ,
[ ARB_ERR_CAP_CLR ] = 0x7e4 ,
[ ARB_ERR_CAP_HI_ADDR ] = 0x7e8 ,
[ ARB_ERR_CAP_ADDR ] = 0x7ec ,
[ ARB_ERR_CAP_STATUS ] = 0x7f4 ,
[ ARB_ERR_CAP_MASTER ] = 0x7f8 ,
} ;
2014-05-20 00:05:59 +04:00
struct brcmstb_gisb_arb_device {
void __iomem * base ;
2014-11-26 03:49:51 +03:00
const int * gisb_offsets ;
2014-11-26 03:49:53 +03:00
bool big_endian ;
2014-05-20 00:05:59 +04:00
struct mutex lock ;
struct list_head next ;
u32 valid_mask ;
const char * master_names [ sizeof ( u32 ) * BITS_PER_BYTE ] ;
2014-09-18 23:37:14 +04:00
u32 saved_timeout ;
2014-05-20 00:05:59 +04:00
} ;
static LIST_HEAD ( brcmstb_gisb_arb_device_list ) ;
2014-11-26 03:49:50 +03:00
static u32 gisb_read ( struct brcmstb_gisb_arb_device * gdev , int reg )
{
2014-11-26 03:49:51 +03:00
int offset = gdev - > gisb_offsets [ reg ] ;
2017-03-30 03:29:10 +03:00
if ( offset < 0 ) {
/* return 1 if the hardware doesn't have ARB_ERR_CAP_MASTER */
if ( reg = = ARB_ERR_CAP_MASTER )
return 1 ;
else
return 0 ;
}
2014-11-26 03:49:51 +03:00
2014-11-26 03:49:53 +03:00
if ( gdev - > big_endian )
return ioread32be ( gdev - > base + offset ) ;
else
return ioread32 ( gdev - > base + offset ) ;
2014-11-26 03:49:50 +03:00
}
2017-03-30 03:29:10 +03:00
static u64 gisb_read_address ( struct brcmstb_gisb_arb_device * gdev )
{
u64 value ;
value = gisb_read ( gdev , ARB_ERR_CAP_ADDR ) ;
value | = ( u64 ) gisb_read ( gdev , ARB_ERR_CAP_HI_ADDR ) < < 32 ;
return value ;
}
2014-11-26 03:49:50 +03:00
static void gisb_write ( struct brcmstb_gisb_arb_device * gdev , u32 val , int reg )
{
2014-11-26 03:49:51 +03:00
int offset = gdev - > gisb_offsets [ reg ] ;
if ( offset = = - 1 )
return ;
2014-11-26 03:49:53 +03:00
if ( gdev - > big_endian )
2017-03-30 03:29:09 +03:00
iowrite32be ( val , gdev - > base + offset ) ;
2014-11-26 03:49:53 +03:00
else
2017-03-30 03:29:09 +03:00
iowrite32 ( val , gdev - > base + offset ) ;
2014-11-26 03:49:50 +03:00
}
2014-05-20 00:05:59 +04:00
static ssize_t gisb_arb_get_timeout ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
2018-10-21 22:59:58 +03:00
struct brcmstb_gisb_arb_device * gdev = dev_get_drvdata ( dev ) ;
2014-05-20 00:05:59 +04:00
u32 timeout ;
mutex_lock ( & gdev - > lock ) ;
2014-11-26 03:49:50 +03:00
timeout = gisb_read ( gdev , ARB_TIMER ) ;
2014-05-20 00:05:59 +04:00
mutex_unlock ( & gdev - > lock ) ;
return sprintf ( buf , " %d " , timeout ) ;
}
static ssize_t gisb_arb_set_timeout ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
2018-10-21 22:59:58 +03:00
struct brcmstb_gisb_arb_device * gdev = dev_get_drvdata ( dev ) ;
2014-05-20 00:05:59 +04:00
int val , ret ;
ret = kstrtoint ( buf , 10 , & val ) ;
if ( ret < 0 )
return ret ;
if ( val = = 0 | | val > = 0xffffffff )
return - EINVAL ;
mutex_lock ( & gdev - > lock ) ;
2014-11-26 03:49:50 +03:00
gisb_write ( gdev , val , ARB_TIMER ) ;
2014-05-20 00:05:59 +04:00
mutex_unlock ( & gdev - > lock ) ;
return count ;
}
static const char *
brcmstb_gisb_master_to_str ( struct brcmstb_gisb_arb_device * gdev ,
u32 masters )
{
u32 mask = gdev - > valid_mask & masters ;
if ( hweight_long ( mask ) ! = 1 )
return NULL ;
return gdev - > master_names [ ffs ( mask ) - 1 ] ;
}
static int brcmstb_gisb_arb_decode_addr ( struct brcmstb_gisb_arb_device * gdev ,
const char * reason )
{
u32 cap_status ;
2017-03-30 03:29:10 +03:00
u64 arb_addr ;
2014-05-20 00:05:59 +04:00
u32 master ;
const char * m_name ;
char m_fmt [ 11 ] ;
2014-11-26 03:49:50 +03:00
cap_status = gisb_read ( gdev , ARB_ERR_CAP_STATUS ) ;
2014-05-20 00:05:59 +04:00
/* Invalid captured address, bail out */
if ( ! ( cap_status & ARB_ERR_CAP_STATUS_VALID ) )
return 1 ;
/* Read the address and master */
2017-03-30 03:29:10 +03:00
arb_addr = gisb_read_address ( gdev ) ;
2014-11-26 03:49:50 +03:00
master = gisb_read ( gdev , ARB_ERR_CAP_MASTER ) ;
2014-05-20 00:05:59 +04:00
m_name = brcmstb_gisb_master_to_str ( gdev , master ) ;
if ( ! m_name ) {
snprintf ( m_fmt , sizeof ( m_fmt ) , " 0x%08x " , master ) ;
m_name = m_fmt ;
}
2017-03-30 03:29:10 +03:00
pr_crit ( " %s: %s at 0x%llx [%c %s], core: %s \n " ,
2014-05-20 00:05:59 +04:00
__func__ , reason , arb_addr ,
cap_status & ARB_ERR_CAP_STATUS_WRITE ? ' W ' : ' R ' ,
cap_status & ARB_ERR_CAP_STATUS_TIMEOUT ? " timeout " : " " ,
m_name ) ;
/* clear the GISB error */
2014-11-26 03:49:50 +03:00
gisb_write ( gdev , ARB_ERR_CAP_CLEAR , ARB_ERR_CAP_CLR ) ;
2014-05-20 00:05:59 +04:00
return 0 ;
}
2016-01-31 23:29:27 +03:00
# ifdef CONFIG_MIPS
static int brcmstb_bus_error_handler ( struct pt_regs * regs , int is_fixup )
{
int ret = 0 ;
struct brcmstb_gisb_arb_device * gdev ;
u32 cap_status ;
list_for_each_entry ( gdev , & brcmstb_gisb_arb_device_list , next ) {
cap_status = gisb_read ( gdev , ARB_ERR_CAP_STATUS ) ;
/* Invalid captured address, bail out */
if ( ! ( cap_status & ARB_ERR_CAP_STATUS_VALID ) ) {
is_fixup = 1 ;
goto out ;
}
ret | = brcmstb_gisb_arb_decode_addr ( gdev , " bus error " ) ;
}
out :
return is_fixup ? MIPS_BE_FIXUP : MIPS_BE_FATAL ;
}
# endif
2014-05-20 00:05:59 +04:00
static irqreturn_t brcmstb_gisb_timeout_handler ( int irq , void * dev_id )
{
brcmstb_gisb_arb_decode_addr ( dev_id , " timeout " ) ;
return IRQ_HANDLED ;
}
static irqreturn_t brcmstb_gisb_tea_handler ( int irq , void * dev_id )
{
brcmstb_gisb_arb_decode_addr ( dev_id , " target abort " ) ;
return IRQ_HANDLED ;
}
2017-03-30 03:29:11 +03:00
/*
* Dump out gisb errors on die or panic .
*/
static int dump_gisb_error ( struct notifier_block * self , unsigned long v ,
void * p ) ;
static struct notifier_block gisb_die_notifier = {
. notifier_call = dump_gisb_error ,
} ;
static struct notifier_block gisb_panic_notifier = {
. notifier_call = dump_gisb_error ,
} ;
static int dump_gisb_error ( struct notifier_block * self , unsigned long v ,
void * p )
{
struct brcmstb_gisb_arb_device * gdev ;
const char * reason = " panic " ;
if ( self = = & gisb_die_notifier )
reason = " die " ;
/* iterate over each GISB arb registered handlers */
list_for_each_entry ( gdev , & brcmstb_gisb_arb_device_list , next )
brcmstb_gisb_arb_decode_addr ( gdev , reason ) ;
return NOTIFY_DONE ;
}
2014-05-20 00:05:59 +04:00
static DEVICE_ATTR ( gisb_arb_timeout , S_IWUSR | S_IRUGO ,
gisb_arb_get_timeout , gisb_arb_set_timeout ) ;
static struct attribute * gisb_arb_sysfs_attrs [ ] = {
& dev_attr_gisb_arb_timeout . attr ,
NULL ,
} ;
static struct attribute_group gisb_arb_sysfs_attr_group = {
. attrs = gisb_arb_sysfs_attrs ,
} ;
2014-11-26 03:49:52 +03:00
static const struct of_device_id brcmstb_gisb_arb_of_match [ ] = {
{ . compatible = " brcm,gisb-arb " , . data = gisb_offsets_bcm7445 } ,
{ . compatible = " brcm,bcm7445-gisb-arb " , . data = gisb_offsets_bcm7445 } ,
{ . compatible = " brcm,bcm7435-gisb-arb " , . data = gisb_offsets_bcm7435 } ,
{ . compatible = " brcm,bcm7400-gisb-arb " , . data = gisb_offsets_bcm7400 } ,
2017-03-30 03:29:14 +03:00
{ . compatible = " brcm,bcm7278-gisb-arb " , . data = gisb_offsets_bcm7278 } ,
2014-11-26 03:49:52 +03:00
{ . compatible = " brcm,bcm7038-gisb-arb " , . data = gisb_offsets_bcm7038 } ,
{ } ,
} ;
2014-11-20 21:14:46 +03:00
static int __init brcmstb_gisb_arb_probe ( struct platform_device * pdev )
2014-05-20 00:05:59 +04:00
{
struct device_node * dn = pdev - > dev . of_node ;
struct brcmstb_gisb_arb_device * gdev ;
2014-11-26 03:49:52 +03:00
const struct of_device_id * of_id ;
2014-05-20 00:05:59 +04:00
struct resource * r ;
int err , timeout_irq , tea_irq ;
unsigned int num_masters , j = 0 ;
int i , first , last ;
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
timeout_irq = platform_get_irq ( pdev , 0 ) ;
tea_irq = platform_get_irq ( pdev , 1 ) ;
gdev = devm_kzalloc ( & pdev - > dev , sizeof ( * gdev ) , GFP_KERNEL ) ;
if ( ! gdev )
return - ENOMEM ;
mutex_init ( & gdev - > lock ) ;
INIT_LIST_HEAD ( & gdev - > next ) ;
2014-06-11 09:00:05 +04:00
gdev - > base = devm_ioremap_resource ( & pdev - > dev , r ) ;
if ( IS_ERR ( gdev - > base ) )
return PTR_ERR ( gdev - > base ) ;
2014-05-20 00:05:59 +04:00
2014-11-26 03:49:52 +03:00
of_id = of_match_node ( brcmstb_gisb_arb_of_match , dn ) ;
if ( ! of_id ) {
pr_err ( " failed to look up compatible string \n " ) ;
return - EINVAL ;
}
gdev - > gisb_offsets = of_id - > data ;
2014-11-26 03:49:53 +03:00
gdev - > big_endian = of_device_is_big_endian ( dn ) ;
2014-11-26 03:49:51 +03:00
2014-05-20 00:05:59 +04:00
err = devm_request_irq ( & pdev - > dev , timeout_irq ,
brcmstb_gisb_timeout_handler , 0 , pdev - > name ,
gdev ) ;
if ( err < 0 )
return err ;
err = devm_request_irq ( & pdev - > dev , tea_irq ,
brcmstb_gisb_tea_handler , 0 , pdev - > name ,
gdev ) ;
if ( err < 0 )
return err ;
/* If we do not have a valid mask, assume all masters are enabled */
if ( of_property_read_u32 ( dn , " brcm,gisb-arb-master-mask " ,
& gdev - > valid_mask ) )
gdev - > valid_mask = 0xffffffff ;
/* Proceed with reading the litteral names if we agree on the
* number of masters
*/
num_masters = of_property_count_strings ( dn ,
" brcm,gisb-arb-master-names " ) ;
if ( hweight_long ( gdev - > valid_mask ) = = num_masters ) {
first = ffs ( gdev - > valid_mask ) - 1 ;
last = fls ( gdev - > valid_mask ) - 1 ;
for ( i = first ; i < last ; i + + ) {
if ( ! ( gdev - > valid_mask & BIT ( i ) ) )
continue ;
of_property_read_string_index ( dn ,
" brcm,gisb-arb-master-names " , j ,
& gdev - > master_names [ i ] ) ;
j + + ;
}
}
err = sysfs_create_group ( & pdev - > dev . kobj , & gisb_arb_sysfs_attr_group ) ;
if ( err )
return err ;
platform_set_drvdata ( pdev , gdev ) ;
list_add_tail ( & gdev - > next , & brcmstb_gisb_arb_device_list ) ;
2016-01-31 23:29:27 +03:00
# ifdef CONFIG_MIPS
board_be_handler = brcmstb_bus_error_handler ;
# endif
2014-09-18 23:32:10 +04:00
2017-03-30 03:29:11 +03:00
if ( list_is_singular ( & brcmstb_gisb_arb_device_list ) ) {
register_die_notifier ( & gisb_die_notifier ) ;
atomic_notifier_chain_register ( & panic_notifier_list ,
& gisb_panic_notifier ) ;
}
2019-03-20 22:40:56 +03:00
dev_info ( & pdev - > dev , " registered irqs: %d, %d \n " ,
timeout_irq , tea_irq ) ;
2014-05-20 00:05:59 +04:00
return 0 ;
}
2014-09-18 23:37:14 +04:00
# ifdef CONFIG_PM_SLEEP
static int brcmstb_gisb_arb_suspend ( struct device * dev )
{
2018-10-21 22:59:58 +03:00
struct brcmstb_gisb_arb_device * gdev = dev_get_drvdata ( dev ) ;
2014-09-18 23:37:14 +04:00
2014-12-08 19:16:03 +03:00
gdev - > saved_timeout = gisb_read ( gdev , ARB_TIMER ) ;
2014-09-18 23:37:14 +04:00
return 0 ;
}
/* Make sure we provide the same timeout value that was configured before, and
* do this before the GISB timeout interrupt handler has any chance to run .
*/
static int brcmstb_gisb_arb_resume_noirq ( struct device * dev )
{
2018-10-21 22:59:58 +03:00
struct brcmstb_gisb_arb_device * gdev = dev_get_drvdata ( dev ) ;
2014-09-18 23:37:14 +04:00
2014-12-08 19:16:03 +03:00
gisb_write ( gdev , gdev - > saved_timeout , ARB_TIMER ) ;
2014-09-18 23:37:14 +04:00
return 0 ;
}
# else
# define brcmstb_gisb_arb_suspend NULL
# define brcmstb_gisb_arb_resume_noirq NULL
# endif
static const struct dev_pm_ops brcmstb_gisb_arb_pm_ops = {
. suspend = brcmstb_gisb_arb_suspend ,
. resume_noirq = brcmstb_gisb_arb_resume_noirq ,
} ;
2014-05-20 00:05:59 +04:00
static struct platform_driver brcmstb_gisb_arb_driver = {
. driver = {
. name = " brcm-gisb-arb " ,
. of_match_table = brcmstb_gisb_arb_of_match ,
2014-09-18 23:37:14 +04:00
. pm = & brcmstb_gisb_arb_pm_ops ,
2014-05-20 00:05:59 +04:00
} ,
} ;
static int __init brcm_gisb_driver_init ( void )
{
2014-11-20 21:14:46 +03:00
return platform_driver_probe ( & brcmstb_gisb_arb_driver ,
brcmstb_gisb_arb_probe ) ;
2014-05-20 00:05:59 +04:00
}
module_init ( brcm_gisb_driver_init ) ;