2022-07-20 13:51:31 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2020 - 2022 Loongson Technology Corporation Limited
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/irqchip.h>
# include <linux/irqdomain.h>
# include <asm/loongarch.h>
# include <asm/setup.h>
static struct irq_domain * irq_domain ;
struct fwnode_handle * cpuintc_handle ;
2022-07-20 13:51:32 +03:00
static u32 lpic_gsi_to_irq ( u32 gsi )
{
/* Only pch irqdomain transferring is required for LoongArch. */
if ( gsi > = GSI_MIN_PCH_IRQ & & gsi < = GSI_MAX_PCH_IRQ )
return acpi_register_gsi ( NULL , gsi , ACPI_LEVEL_SENSITIVE , ACPI_ACTIVE_HIGH ) ;
return 0 ;
}
static struct fwnode_handle * lpic_get_gsi_domain_id ( u32 gsi )
{
int id ;
struct fwnode_handle * domain_handle = NULL ;
switch ( gsi ) {
case GSI_MIN_CPU_IRQ . . . GSI_MAX_CPU_IRQ :
if ( liointc_handle )
domain_handle = liointc_handle ;
break ;
case GSI_MIN_LPC_IRQ . . . GSI_MAX_LPC_IRQ :
if ( pch_lpc_handle )
domain_handle = pch_lpc_handle ;
break ;
case GSI_MIN_PCH_IRQ . . . GSI_MAX_PCH_IRQ :
id = find_pch_pic ( gsi ) ;
if ( id > = 0 & & pch_pic_handle [ id ] )
domain_handle = pch_pic_handle [ id ] ;
break ;
}
return domain_handle ;
}
2022-07-20 13:51:31 +03:00
static void mask_loongarch_irq ( struct irq_data * d )
{
clear_csr_ecfg ( ECFGF ( d - > hwirq ) ) ;
}
static void unmask_loongarch_irq ( struct irq_data * d )
{
set_csr_ecfg ( ECFGF ( d - > hwirq ) ) ;
}
static struct irq_chip cpu_irq_controller = {
. name = " CPUINTC " ,
. irq_mask = mask_loongarch_irq ,
. irq_unmask = unmask_loongarch_irq ,
} ;
static void handle_cpu_irq ( struct pt_regs * regs )
{
int hwirq ;
unsigned int estat = read_csr_estat ( ) & CSR_ESTAT_IS ;
while ( ( hwirq = ffs ( estat ) ) ) {
estat & = ~ BIT ( hwirq - 1 ) ;
generic_handle_domain_irq ( irq_domain , hwirq - 1 ) ;
}
}
static int loongarch_cpu_intc_map ( struct irq_domain * d , unsigned int irq ,
irq_hw_number_t hwirq )
{
irq_set_noprobe ( irq ) ;
irq_set_chip_and_handler ( irq , & cpu_irq_controller , handle_percpu_irq ) ;
return 0 ;
}
static const struct irq_domain_ops loongarch_cpu_intc_irq_domain_ops = {
. map = loongarch_cpu_intc_map ,
. xlate = irq_domain_xlate_onecell ,
} ;
2022-11-14 14:38:23 +03:00
# ifdef CONFIG_OF
2022-12-05 07:47:08 +03:00
static int __init cpuintc_of_init ( struct device_node * of_node ,
2022-11-14 14:38:23 +03:00
struct device_node * parent )
{
cpuintc_handle = of_node_to_fwnode ( of_node ) ;
irq_domain = irq_domain_create_linear ( cpuintc_handle , EXCCODE_INT_NUM ,
& loongarch_cpu_intc_irq_domain_ops , NULL ) ;
if ( ! irq_domain )
panic ( " Failed to add irqdomain for loongarch CPU " ) ;
set_handle_irq ( & handle_cpu_irq ) ;
return 0 ;
}
2022-12-05 07:47:08 +03:00
IRQCHIP_DECLARE ( cpu_intc , " loongson,cpu-interrupt-controller " , cpuintc_of_init ) ;
2022-11-14 14:38:23 +03:00
# endif
2022-10-20 17:25:14 +03:00
static int __init liointc_parse_madt ( union acpi_subtable_headers * header ,
const unsigned long end )
2022-07-20 13:51:31 +03:00
{
struct acpi_madt_lio_pic * liointc_entry = ( struct acpi_madt_lio_pic * ) header ;
return liointc_acpi_init ( irq_domain , liointc_entry ) ;
}
2022-10-20 17:25:14 +03:00
static int __init eiointc_parse_madt ( union acpi_subtable_headers * header ,
const unsigned long end )
2022-07-20 13:51:31 +03:00
{
struct acpi_madt_eio_pic * eiointc_entry = ( struct acpi_madt_eio_pic * ) header ;
return eiointc_acpi_init ( irq_domain , eiointc_entry ) ;
}
static int __init acpi_cascade_irqdomain_init ( void )
{
2022-10-20 17:25:14 +03:00
int r ;
r = acpi_table_parse_madt ( ACPI_MADT_TYPE_LIO_PIC , liointc_parse_madt , 0 ) ;
if ( r < 0 )
return r ;
r = acpi_table_parse_madt ( ACPI_MADT_TYPE_EIO_PIC , eiointc_parse_madt , 0 ) ;
if ( r < 0 )
return r ;
2022-07-20 13:51:31 +03:00
return 0 ;
}
static int __init cpuintc_acpi_init ( union acpi_subtable_headers * header ,
const unsigned long end )
{
2022-10-20 17:25:14 +03:00
int ret ;
2022-07-20 13:51:31 +03:00
if ( irq_domain )
return 0 ;
/* Mask interrupts. */
clear_csr_ecfg ( ECFG0_IM ) ;
clear_csr_estat ( ESTATF_IP ) ;
2022-08-08 13:50:20 +03:00
cpuintc_handle = irq_domain_alloc_named_fwnode ( " CPUINTC " ) ;
2022-07-20 13:51:31 +03:00
irq_domain = irq_domain_create_linear ( cpuintc_handle , EXCCODE_INT_NUM ,
& loongarch_cpu_intc_irq_domain_ops , NULL ) ;
if ( ! irq_domain )
panic ( " Failed to add irqdomain for LoongArch CPU " ) ;
set_handle_irq ( & handle_cpu_irq ) ;
2022-07-20 13:51:32 +03:00
acpi_set_irq_model ( ACPI_IRQ_MODEL_LPIC , lpic_get_gsi_domain_id ) ;
acpi_set_gsi_to_irq_fallback ( lpic_gsi_to_irq ) ;
2022-10-20 17:25:14 +03:00
ret = acpi_cascade_irqdomain_init ( ) ;
2022-07-20 13:51:31 +03:00
2022-10-20 17:25:14 +03:00
return ret ;
2022-07-20 13:51:31 +03:00
}
IRQCHIP_ACPI_DECLARE ( cpuintc_v1 , ACPI_MADT_TYPE_CORE_PIC ,
NULL , ACPI_MADT_CORE_PIC_VERSION_V1 , cpuintc_acpi_init ) ;