2020-06-01 14:45:40 +05:30
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2012 Regents of the University of California
* Copyright ( C ) 2017 - 2018 SiFive
* Copyright ( C ) 2020 Western Digital Corporation or its affiliates .
*/
# define pr_fmt(fmt) "riscv-intc: " fmt
2023-05-15 11:19:23 +05:30
# include <linux/acpi.h>
2020-06-01 14:45:40 +05:30
# include <linux/atomic.h>
# include <linux/bits.h>
# include <linux/cpu.h>
# include <linux/irq.h>
# include <linux/irqchip.h>
# include <linux/irqdomain.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/smp.h>
2024-02-22 16:39:39 +08:00
# include <linux/soc/andes/irq.h>
2020-06-01 14:45:40 +05:30
2024-02-22 15:09:56 +05:30
# include <asm/hwcap.h>
2020-06-01 14:45:40 +05:30
static struct irq_domain * intc_domain ;
2024-02-22 16:39:38 +08:00
static unsigned int riscv_intc_nr_irqs __ro_after_init = BITS_PER_LONG ;
static unsigned int riscv_intc_custom_base __ro_after_init = BITS_PER_LONG ;
static unsigned int riscv_intc_custom_nr_irqs __ro_after_init ;
2020-06-01 14:45:40 +05:30
static asmlinkage void riscv_intc_irq ( struct pt_regs * regs )
{
unsigned long cause = regs - > cause & ~ CAUSE_IRQ_FLAG ;
2024-02-22 16:39:38 +08:00
if ( generic_handle_domain_irq ( intc_domain , cause ) )
pr_warn_ratelimited ( " Failed to handle interrupt (cause: %ld) \n " , cause ) ;
2020-06-01 14:45:40 +05:30
}
2024-02-22 15:09:56 +05:30
static asmlinkage void riscv_intc_aia_irq ( struct pt_regs * regs )
{
unsigned long topi ;
while ( ( topi = csr_read ( CSR_TOPI ) ) )
generic_handle_domain_irq ( intc_domain , topi > > TOPI_IID_SHIFT ) ;
}
2020-06-01 14:45:40 +05:30
/*
* On RISC - V systems local interrupts are masked or unmasked by writing
* the SIE ( Supervisor Interrupt Enable ) CSR . As CSRs can only be written
* on the local hart , these functions can only be called on the hart that
* corresponds to the IRQ chip .
*/
static void riscv_intc_irq_mask ( struct irq_data * d )
{
2024-02-22 15:09:56 +05:30
if ( IS_ENABLED ( CONFIG_32BIT ) & & d - > hwirq > = BITS_PER_LONG )
csr_clear ( CSR_IEH , BIT ( d - > hwirq - BITS_PER_LONG ) ) ;
else
csr_clear ( CSR_IE , BIT ( d - > hwirq ) ) ;
2020-06-01 14:45:40 +05:30
}
static void riscv_intc_irq_unmask ( struct irq_data * d )
{
2024-02-22 15:09:56 +05:30
if ( IS_ENABLED ( CONFIG_32BIT ) & & d - > hwirq > = BITS_PER_LONG )
csr_set ( CSR_IEH , BIT ( d - > hwirq - BITS_PER_LONG ) ) ;
else
csr_set ( CSR_IE , BIT ( d - > hwirq ) ) ;
2020-06-01 14:45:40 +05:30
}
2024-02-22 16:39:39 +08:00
static void andes_intc_irq_mask ( struct irq_data * d )
{
/*
* Andes specific S - mode local interrupt causes ( hwirq )
* are defined as ( 256 + n ) and controlled by n - th bit
* of SLIE .
*/
unsigned int mask = BIT ( d - > hwirq % BITS_PER_LONG ) ;
if ( d - > hwirq < ANDES_SLI_CAUSE_BASE )
csr_clear ( CSR_IE , mask ) ;
else
csr_clear ( ANDES_CSR_SLIE , mask ) ;
}
static void andes_intc_irq_unmask ( struct irq_data * d )
{
unsigned int mask = BIT ( d - > hwirq % BITS_PER_LONG ) ;
if ( d - > hwirq < ANDES_SLI_CAUSE_BASE )
csr_set ( CSR_IE , mask ) ;
else
csr_set ( ANDES_CSR_SLIE , mask ) ;
}
2023-03-28 09:22:23 +05:30
static void riscv_intc_irq_eoi ( struct irq_data * d )
{
/*
* The RISC - V INTC driver uses handle_percpu_devid_irq ( ) flow
* for the per - HART local interrupts and child irqchip drivers
* ( such as PLIC , SBI IPI , CLINT , APLIC , IMSIC , etc ) implement
* chained handlers for the per - HART local interrupts .
*
* In the absence of irq_eoi ( ) , the chained_irq_enter ( ) and
* chained_irq_exit ( ) functions ( used by child irqchip drivers )
* will do unnecessary mask / unmask of per - HART local interrupts
* at the time of handling interrupts . To avoid this , we provide
* an empty irq_eoi ( ) callback for RISC - V INTC irqchip .
*/
}
2020-06-01 14:45:40 +05:30
static struct irq_chip riscv_intc_chip = {
. name = " RISC-V INTC " ,
. irq_mask = riscv_intc_irq_mask ,
. irq_unmask = riscv_intc_irq_unmask ,
2023-03-28 09:22:23 +05:30
. irq_eoi = riscv_intc_irq_eoi ,
2020-06-01 14:45:40 +05:30
} ;
2024-02-22 16:39:39 +08:00
static struct irq_chip andes_intc_chip = {
. name = " RISC-V INTC " ,
. irq_mask = andes_intc_irq_mask ,
. irq_unmask = andes_intc_irq_unmask ,
. irq_eoi = riscv_intc_irq_eoi ,
} ;
2020-06-01 14:45:40 +05:30
static int riscv_intc_domain_map ( struct irq_domain * d , unsigned int irq ,
irq_hw_number_t hwirq )
{
2024-02-22 16:39:39 +08:00
struct irq_chip * chip = d - > host_data ;
2020-06-01 14:45:40 +05:30
irq_set_percpu_devid ( irq ) ;
2024-02-22 16:39:39 +08:00
irq_domain_set_info ( d , irq , hwirq , chip , NULL , handle_percpu_devid_irq ,
NULL , NULL ) ;
2020-06-01 14:45:40 +05:30
return 0 ;
}
2023-03-28 09:22:19 +05:30
static int riscv_intc_domain_alloc ( struct irq_domain * domain ,
unsigned int virq , unsigned int nr_irqs ,
void * arg )
{
int i , ret ;
irq_hw_number_t hwirq ;
unsigned int type = IRQ_TYPE_NONE ;
struct irq_fwspec * fwspec = arg ;
ret = irq_domain_translate_onecell ( domain , fwspec , & hwirq , & type ) ;
if ( ret )
return ret ;
2024-02-22 16:39:38 +08:00
/*
* Only allow hwirq for which we have corresponding standard or
* custom interrupt enable register .
*/
2024-03-12 14:28:08 -07:00
if ( hwirq > = riscv_intc_nr_irqs & &
( hwirq < riscv_intc_custom_base | |
hwirq > = riscv_intc_custom_base + riscv_intc_custom_nr_irqs ) )
2024-02-22 16:39:38 +08:00
return - EINVAL ;
2023-03-28 09:22:19 +05:30
for ( i = 0 ; i < nr_irqs ; i + + ) {
ret = riscv_intc_domain_map ( domain , virq + i , hwirq + i ) ;
if ( ret )
return ret ;
}
return 0 ;
}
2020-06-01 14:45:40 +05:30
static const struct irq_domain_ops riscv_intc_domain_ops = {
. map = riscv_intc_domain_map ,
. xlate = irq_domain_xlate_onecell ,
2023-03-28 09:22:19 +05:30
. alloc = riscv_intc_domain_alloc
2020-06-01 14:45:40 +05:30
} ;
2023-03-28 09:22:18 +05:30
static struct fwnode_handle * riscv_intc_hwnode ( void )
{
return intc_domain - > fwnode ;
}
2024-02-22 15:09:56 +05:30
static int __init riscv_intc_init_common ( struct fwnode_handle * fn , struct irq_chip * chip )
2023-05-15 11:19:23 +05:30
{
int rc ;
2024-02-22 16:39:39 +08:00
intc_domain = irq_domain_create_tree ( fn , & riscv_intc_domain_ops , chip ) ;
2023-05-15 11:19:23 +05:30
if ( ! intc_domain ) {
pr_err ( " unable to add IRQ domain \n " ) ;
return - ENXIO ;
}
2024-03-12 14:28:08 -07:00
if ( riscv_isa_extension_available ( NULL , SxAIA ) ) {
riscv_intc_nr_irqs = 64 ;
2024-02-26 09:37:37 +05:30
rc = set_handle_irq ( & riscv_intc_aia_irq ) ;
2024-03-12 14:28:08 -07:00
} else {
2024-02-26 09:37:37 +05:30
rc = set_handle_irq ( & riscv_intc_irq ) ;
2024-03-12 14:28:08 -07:00
}
2023-05-15 11:19:23 +05:30
if ( rc ) {
pr_err ( " failed to set irq handler \n " ) ;
return rc ;
}
riscv_set_intc_hwnode_fn ( riscv_intc_hwnode ) ;
2024-02-26 09:37:37 +05:30
pr_info ( " %d local interrupts mapped%s \n " ,
2024-03-12 14:28:08 -07:00
riscv_intc_nr_irqs ,
2024-02-26 09:37:37 +05:30
riscv_isa_extension_available ( NULL , SxAIA ) ? " using AIA " : " " ) ;
2024-02-22 15:09:56 +05:30
if ( riscv_intc_custom_nr_irqs )
pr_info ( " %d custom local interrupts mapped \n " , riscv_intc_custom_nr_irqs ) ;
2023-05-15 11:19:23 +05:30
return 0 ;
}
2020-06-01 14:45:40 +05:30
static int __init riscv_intc_init ( struct device_node * node ,
struct device_node * parent )
{
2024-02-22 16:39:39 +08:00
struct irq_chip * chip = & riscv_intc_chip ;
2022-05-27 10:47:42 +05:30
unsigned long hartid ;
2024-02-22 16:39:39 +08:00
int rc ;
2020-06-01 14:45:40 +05:30
2022-05-27 10:47:42 +05:30
rc = riscv_of_parent_hartid ( node , & hartid ) ;
if ( rc < 0 ) {
2020-06-11 10:53:02 -07:00
pr_warn ( " unable to find hart id for %pOF \n " , node ) ;
2020-06-01 14:45:40 +05:30
return 0 ;
}
/*
* The DT will have one INTC DT node under each CPU ( or HART )
* DT node so riscv_intc_init ( ) function will be called once
* for each INTC DT node . We only need to do INTC initialization
* for the INTC DT node belonging to boot CPU ( or boot HART ) .
*/
2023-10-03 10:13:51 +05:30
if ( riscv_hartid_to_cpuid ( hartid ) ! = smp_processor_id ( ) ) {
/*
* The INTC nodes of each CPU are suppliers for downstream
* interrupt controllers ( such as PLIC , IMSIC and APLIC
* direct - mode ) so we should mark an INTC node as initialized
* if we are not creating IRQ domain for it .
*/
fwnode_dev_initialized ( of_fwnode_handle ( node ) , true ) ;
2020-06-01 14:45:40 +05:30
return 0 ;
2023-10-03 10:13:51 +05:30
}
2020-06-01 14:45:40 +05:30
2024-02-22 16:39:39 +08:00
if ( of_device_is_compatible ( node , " andestech,cpu-intc " ) ) {
riscv_intc_custom_base = ANDES_SLI_CAUSE_BASE ;
riscv_intc_custom_nr_irqs = ANDES_RV_IRQ_LAST ;
chip = & andes_intc_chip ;
}
return riscv_intc_init_common ( of_node_to_fwnode ( node ) , chip ) ;
2023-05-15 11:19:23 +05:30
}
2020-06-01 14:45:40 +05:30
2023-05-15 11:19:23 +05:30
IRQCHIP_DECLARE ( riscv , " riscv,cpu-intc " , riscv_intc_init ) ;
2024-02-22 16:39:39 +08:00
IRQCHIP_DECLARE ( andes , " andestech,cpu-intc " , riscv_intc_init ) ;
2020-06-01 14:45:40 +05:30
2023-05-15 11:19:23 +05:30
# ifdef CONFIG_ACPI
2023-03-28 09:22:18 +05:30
2023-05-15 11:19:23 +05:30
static int __init riscv_intc_acpi_init ( union acpi_subtable_headers * header ,
const unsigned long end )
{
struct acpi_madt_rintc * rintc ;
2024-05-27 13:41:13 +05:30
struct fwnode_handle * fn ;
int rc ;
2020-06-01 14:45:40 +05:30
2023-05-15 11:19:23 +05:30
rintc = ( struct acpi_madt_rintc * ) header ;
/*
* The ACPI MADT will have one INTC for each CPU ( or HART )
* so riscv_intc_acpi_init ( ) function will be called once
* for each INTC . We only do INTC initialization
* for the INTC belonging to the boot CPU ( or boot HART ) .
*/
if ( riscv_hartid_to_cpuid ( rintc - > hart_id ) ! = smp_processor_id ( ) )
return 0 ;
fn = irq_domain_alloc_named_fwnode ( " RISCV-INTC " ) ;
if ( ! fn ) {
pr_err ( " unable to allocate INTC FW node \n " ) ;
return - ENOMEM ;
}
2024-05-27 13:41:13 +05:30
rc = riscv_intc_init_common ( fn , & riscv_intc_chip ) ;
if ( rc )
irq_domain_free_fwnode ( fn ) ;
return rc ;
2020-06-01 14:45:40 +05:30
}
2023-05-15 11:19:23 +05:30
IRQCHIP_ACPI_DECLARE ( riscv_intc , ACPI_MADT_TYPE_RINTC , NULL ,
ACPI_MADT_RINTC_VERSION_V1 , riscv_intc_acpi_init ) ;
# endif