2011-09-19 17:45:02 +00:00
/*
* ICS backend for OPAL managed interrupts .
*
* Copyright 2011 IBM Corp .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# undef DEBUG
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/irq.h>
# include <linux/smp.h>
# include <linux/interrupt.h>
# include <linux/init.h>
# include <linux/cpu.h>
# include <linux/of.h>
# include <linux/spinlock.h>
# include <linux/msi.h>
# include <asm/prom.h>
# include <asm/smp.h>
# include <asm/machdep.h>
# include <asm/irq.h>
# include <asm/errno.h>
# include <asm/xics.h>
# include <asm/opal.h>
# include <asm/firmware.h>
static int ics_opal_mangle_server ( int server )
{
/* No link for now */
return server < < 2 ;
}
static int ics_opal_unmangle_server ( int server )
{
/* No link for now */
return server > > 2 ;
}
static void ics_opal_unmask_irq ( struct irq_data * d )
{
unsigned int hw_irq = ( unsigned int ) irqd_to_hwirq ( d ) ;
int64_t rc ;
int server ;
pr_devel ( " ics-hal: unmask virq %d [hw 0x%x] \n " , d - > irq , hw_irq ) ;
if ( hw_irq = = XICS_IPI | | hw_irq = = XICS_IRQ_SPURIOUS )
return ;
server = xics_get_irq_server ( d - > irq , d - > affinity , 0 ) ;
server = ics_opal_mangle_server ( server ) ;
rc = opal_set_xive ( hw_irq , server , DEFAULT_PRIORITY ) ;
if ( rc ! = OPAL_SUCCESS )
pr_err ( " %s: opal_set_xive(irq=%d [hw 0x%x] server=%x) "
" error %lld \n " ,
__func__ , d - > irq , hw_irq , server , rc ) ;
}
static unsigned int ics_opal_startup ( struct irq_data * d )
{
# ifdef CONFIG_PCI_MSI
/*
* The generic MSI code returns with the interrupt disabled on the
* card , using the MSI mask bits . Firmware doesn ' t appear to unmask
* at that level , so we do it here by hand .
*/
if ( d - > msi_desc )
unmask_msi_irq ( d ) ;
# endif
/* unmask it */
ics_opal_unmask_irq ( d ) ;
return 0 ;
}
static void ics_opal_mask_real_irq ( unsigned int hw_irq )
{
int server = ics_opal_mangle_server ( xics_default_server ) ;
int64_t rc ;
if ( hw_irq = = XICS_IPI )
return ;
/* Have to set XIVE to 0xff to be able to remove a slot */
rc = opal_set_xive ( hw_irq , server , 0xff ) ;
if ( rc ! = OPAL_SUCCESS )
pr_err ( " %s: opal_set_xive(0xff) irq=%u returned %lld \n " ,
__func__ , hw_irq , rc ) ;
}
static void ics_opal_mask_irq ( struct irq_data * d )
{
unsigned int hw_irq = ( unsigned int ) irqd_to_hwirq ( d ) ;
pr_devel ( " ics-hal: mask virq %d [hw 0x%x] \n " , d - > irq , hw_irq ) ;
if ( hw_irq = = XICS_IPI | | hw_irq = = XICS_IRQ_SPURIOUS )
return ;
ics_opal_mask_real_irq ( hw_irq ) ;
}
static int ics_opal_set_affinity ( struct irq_data * d ,
const struct cpumask * cpumask ,
bool force )
{
unsigned int hw_irq = ( unsigned int ) irqd_to_hwirq ( d ) ;
2013-09-23 12:04:59 +10:00
__be16 oserver ;
2011-09-19 17:45:02 +00:00
int16_t server ;
int8_t priority ;
int64_t rc ;
int wanted_server ;
if ( hw_irq = = XICS_IPI | | hw_irq = = XICS_IRQ_SPURIOUS )
return - 1 ;
2013-09-23 12:04:59 +10:00
rc = opal_get_xive ( hw_irq , & oserver , & priority ) ;
2011-09-19 17:45:02 +00:00
if ( rc ! = OPAL_SUCCESS ) {
2013-09-23 12:04:59 +10:00
pr_err ( " %s: opal_get_xive(irq=%d [hw 0x%x]) error %lld \n " ,
__func__ , d - > irq , hw_irq , rc ) ;
2011-09-19 17:45:02 +00:00
return - 1 ;
}
2013-09-23 12:04:59 +10:00
server = be16_to_cpu ( oserver ) ;
2011-09-19 17:45:02 +00:00
wanted_server = xics_get_irq_server ( d - > irq , cpumask , 1 ) ;
if ( wanted_server < 0 ) {
char cpulist [ 128 ] ;
cpumask_scnprintf ( cpulist , sizeof ( cpulist ) , cpumask ) ;
pr_warning ( " %s: No online cpus in the mask %s for irq %d \n " ,
__func__ , cpulist , d - > irq ) ;
return - 1 ;
}
server = ics_opal_mangle_server ( wanted_server ) ;
pr_devel ( " ics-hal: set-affinity irq %d [hw 0x%x] server: 0x%x/0x%x \n " ,
d - > irq , hw_irq , wanted_server , server ) ;
rc = opal_set_xive ( hw_irq , server , priority ) ;
if ( rc ! = OPAL_SUCCESS ) {
pr_err ( " %s: opal_set_xive(irq=%d [hw 0x%x] server=%x) "
" error %lld \n " ,
__func__ , d - > irq , hw_irq , server , rc ) ;
return - 1 ;
}
2013-05-13 00:57:49 +00:00
return IRQ_SET_MASK_OK ;
2011-09-19 17:45:02 +00:00
}
static struct irq_chip ics_opal_irq_chip = {
. name = " OPAL ICS " ,
. irq_startup = ics_opal_startup ,
. irq_mask = ics_opal_mask_irq ,
. irq_unmask = ics_opal_unmask_irq ,
. irq_eoi = NULL , /* Patched at init time */
. irq_set_affinity = ics_opal_set_affinity
} ;
static int ics_opal_map ( struct ics * ics , unsigned int virq ) ;
static void ics_opal_mask_unknown ( struct ics * ics , unsigned long vec ) ;
static long ics_opal_get_server ( struct ics * ics , unsigned long vec ) ;
static int ics_opal_host_match ( struct ics * ics , struct device_node * node )
{
return 1 ;
}
/* Only one global & state struct ics */
static struct ics ics_hal = {
. map = ics_opal_map ,
. mask_unknown = ics_opal_mask_unknown ,
. get_server = ics_opal_get_server ,
. host_match = ics_opal_host_match ,
} ;
static int ics_opal_map ( struct ics * ics , unsigned int virq )
{
unsigned int hw_irq = ( unsigned int ) virq_to_hw ( virq ) ;
int64_t rc ;
2013-09-23 12:04:59 +10:00
__be16 server ;
2011-09-19 17:45:02 +00:00
int8_t priority ;
if ( WARN_ON ( hw_irq = = XICS_IPI | | hw_irq = = XICS_IRQ_SPURIOUS ) )
return - EINVAL ;
/* Check if HAL knows about this interrupt */
rc = opal_get_xive ( hw_irq , & server , & priority ) ;
if ( rc ! = OPAL_SUCCESS )
return - ENXIO ;
irq_set_chip_and_handler ( virq , & ics_opal_irq_chip , handle_fasteoi_irq ) ;
irq_set_chip_data ( virq , & ics_hal ) ;
return 0 ;
}
static void ics_opal_mask_unknown ( struct ics * ics , unsigned long vec )
{
int64_t rc ;
2013-09-23 12:04:59 +10:00
__be16 server ;
2011-09-19 17:45:02 +00:00
int8_t priority ;
/* Check if HAL knows about this interrupt */
rc = opal_get_xive ( vec , & server , & priority ) ;
if ( rc ! = OPAL_SUCCESS )
return ;
ics_opal_mask_real_irq ( vec ) ;
}
static long ics_opal_get_server ( struct ics * ics , unsigned long vec )
{
int64_t rc ;
2013-09-23 12:04:59 +10:00
__be16 server ;
2011-09-19 17:45:02 +00:00
int8_t priority ;
/* Check if HAL knows about this interrupt */
rc = opal_get_xive ( vec , & server , & priority ) ;
if ( rc ! = OPAL_SUCCESS )
return - 1 ;
2013-09-23 12:04:59 +10:00
return ics_opal_unmangle_server ( be16_to_cpu ( server ) ) ;
2011-09-19 17:45:02 +00:00
}
int __init ics_opal_init ( void )
{
if ( ! firmware_has_feature ( FW_FEATURE_OPAL ) )
return - ENODEV ;
/* We need to patch our irq chip's EOI to point to the
* right ICP
*/
ics_opal_irq_chip . irq_eoi = icp_ops - > eoi ;
/* Register ourselves */
xics_register_ics ( & ics_hal ) ;
pr_info ( " ICS OPAL backend registered \n " ) ;
return 0 ;
}