2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-02-18 18:13:58 +03:00
/*
* Copyright ( C ) 2014 STMicroelectronics – All Rights Reserved
*
* Author : Lee Jones < lee . jones @ linaro . org >
*
* This is a re - write of Christophe Kerello ' s PMU driver .
*/
# include <dt-bindings/interrupt-controller/irq-st.h>
# include <linux/err.h>
# include <linux/mfd/syscon.h>
2023-07-14 20:46:43 +03:00
# include <linux/of.h>
2015-02-18 18:13:58 +03:00
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# define STIH407_SYSCFG_5102 0x198
# define ST_A9_IRQ_MASK 0x001FFFFF
# define ST_A9_IRQ_MAX_CHANS 2
# define ST_A9_IRQ_EN_CTI_0 BIT(0)
# define ST_A9_IRQ_EN_CTI_1 BIT(1)
# define ST_A9_IRQ_EN_PMU_0 BIT(2)
# define ST_A9_IRQ_EN_PMU_1 BIT(3)
# define ST_A9_IRQ_EN_PL310_L2 BIT(4)
# define ST_A9_IRQ_EN_EXT_0 BIT(5)
# define ST_A9_IRQ_EN_EXT_1 BIT(6)
# define ST_A9_IRQ_EN_EXT_2 BIT(7)
# define ST_A9_FIQ_N_SEL(dev, chan) (dev << (8 + (chan * 3)))
# define ST_A9_IRQ_N_SEL(dev, chan) (dev << (14 + (chan * 3)))
# define ST_A9_EXTIRQ_INV_SEL(dev) (dev << 20)
struct st_irq_syscfg {
struct regmap * regmap ;
unsigned int syscfg ;
unsigned int config ;
bool ext_inverted ;
} ;
static const struct of_device_id st_irq_syscfg_match [ ] = {
{
. compatible = " st,stih407-irq-syscfg " ,
. data = ( void * ) STIH407_SYSCFG_5102 ,
} ,
{ }
} ;
static int st_irq_xlate ( struct platform_device * pdev ,
int device , int channel , bool irq )
{
struct st_irq_syscfg * ddata = dev_get_drvdata ( & pdev - > dev ) ;
/* Set the device enable bit. */
switch ( device ) {
case ST_IRQ_SYSCFG_EXT_0 :
ddata - > config | = ST_A9_IRQ_EN_EXT_0 ;
break ;
case ST_IRQ_SYSCFG_EXT_1 :
ddata - > config | = ST_A9_IRQ_EN_EXT_1 ;
break ;
case ST_IRQ_SYSCFG_EXT_2 :
ddata - > config | = ST_A9_IRQ_EN_EXT_2 ;
break ;
case ST_IRQ_SYSCFG_CTI_0 :
ddata - > config | = ST_A9_IRQ_EN_CTI_0 ;
break ;
case ST_IRQ_SYSCFG_CTI_1 :
ddata - > config | = ST_A9_IRQ_EN_CTI_1 ;
break ;
case ST_IRQ_SYSCFG_PMU_0 :
ddata - > config | = ST_A9_IRQ_EN_PMU_0 ;
break ;
case ST_IRQ_SYSCFG_PMU_1 :
ddata - > config | = ST_A9_IRQ_EN_PMU_1 ;
break ;
case ST_IRQ_SYSCFG_pl310_L2 :
ddata - > config | = ST_A9_IRQ_EN_PL310_L2 ;
break ;
case ST_IRQ_SYSCFG_DISABLED :
return 0 ;
default :
dev_err ( & pdev - > dev , " Unrecognised device %d \n " , device ) ;
return - EINVAL ;
}
/* Select IRQ/FIQ channel for device. */
ddata - > config | = irq ?
ST_A9_IRQ_N_SEL ( device , channel ) :
ST_A9_FIQ_N_SEL ( device , channel ) ;
return 0 ;
}
static int st_irq_syscfg_enable ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct st_irq_syscfg * ddata = dev_get_drvdata ( & pdev - > dev ) ;
int channels , ret , i ;
u32 device , invert ;
channels = of_property_count_u32_elems ( np , " st,irq-device " ) ;
if ( channels ! = ST_A9_IRQ_MAX_CHANS ) {
dev_err ( & pdev - > dev , " st,enable-irq-device must have 2 elems \n " ) ;
return - EINVAL ;
}
channels = of_property_count_u32_elems ( np , " st,fiq-device " ) ;
if ( channels ! = ST_A9_IRQ_MAX_CHANS ) {
dev_err ( & pdev - > dev , " st,enable-fiq-device must have 2 elems \n " ) ;
return - EINVAL ;
}
for ( i = 0 ; i < ST_A9_IRQ_MAX_CHANS ; i + + ) {
of_property_read_u32_index ( np , " st,irq-device " , i , & device ) ;
ret = st_irq_xlate ( pdev , device , i , true ) ;
if ( ret )
return ret ;
of_property_read_u32_index ( np , " st,fiq-device " , i , & device ) ;
ret = st_irq_xlate ( pdev , device , i , false ) ;
if ( ret )
return ret ;
}
/* External IRQs may be inverted. */
of_property_read_u32 ( np , " st,invert-ext " , & invert ) ;
ddata - > config | = ST_A9_EXTIRQ_INV_SEL ( invert ) ;
return regmap_update_bits ( ddata - > regmap , ddata - > syscfg ,
ST_A9_IRQ_MASK , ddata - > config ) ;
}
static int st_irq_syscfg_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct st_irq_syscfg * ddata ;
ddata = devm_kzalloc ( & pdev - > dev , sizeof ( * ddata ) , GFP_KERNEL ) ;
if ( ! ddata )
return - ENOMEM ;
2022-11-17 14:16:50 +03:00
ddata - > syscfg = ( unsigned int ) device_get_match_data ( & pdev - > dev ) ;
2015-02-18 18:13:58 +03:00
ddata - > regmap = syscon_regmap_lookup_by_phandle ( np , " st,syscfg " ) ;
if ( IS_ERR ( ddata - > regmap ) ) {
dev_err ( & pdev - > dev , " syscfg phandle missing \n " ) ;
return PTR_ERR ( ddata - > regmap ) ;
}
dev_set_drvdata ( & pdev - > dev , ddata ) ;
return st_irq_syscfg_enable ( pdev ) ;
}
2016-12-17 03:29:27 +03:00
static int __maybe_unused st_irq_syscfg_resume ( struct device * dev )
2015-02-18 18:13:58 +03:00
{
struct st_irq_syscfg * ddata = dev_get_drvdata ( dev ) ;
return regmap_update_bits ( ddata - > regmap , ddata - > syscfg ,
ST_A9_IRQ_MASK , ddata - > config ) ;
}
static SIMPLE_DEV_PM_OPS ( st_irq_syscfg_pm_ops , NULL , st_irq_syscfg_resume ) ;
static struct platform_driver st_irq_syscfg_driver = {
. driver = {
. name = " st_irq_syscfg " ,
. pm = & st_irq_syscfg_pm_ops ,
. of_match_table = st_irq_syscfg_match ,
} ,
. probe = st_irq_syscfg_probe ,
} ;
static int __init st_irq_syscfg_init ( void )
{
return platform_driver_register ( & st_irq_syscfg_driver ) ;
}
core_initcall ( st_irq_syscfg_init ) ;