2015-03-24 20:58:51 +03:00
/*
* ACPI GSI IRQ layer
*
* Copyright ( C ) 2015 ARM Ltd .
* Author : Lorenzo Pieralisi < lorenzo . pieralisi @ arm . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/acpi.h>
# include <linux/irq.h>
# include <linux/irqdomain.h>
2015-10-13 14:51:37 +03:00
# include <linux/of.h>
2015-03-24 20:58:51 +03:00
enum acpi_irq_model_id acpi_irq_model ;
2015-10-13 14:51:37 +03:00
static struct fwnode_handle * acpi_gsi_domain_id ;
2015-03-24 20:58:51 +03:00
static unsigned int acpi_gsi_get_irq_type ( int trigger , int polarity )
{
switch ( polarity ) {
case ACPI_ACTIVE_LOW :
return trigger = = ACPI_EDGE_SENSITIVE ?
IRQ_TYPE_EDGE_FALLING :
IRQ_TYPE_LEVEL_LOW ;
case ACPI_ACTIVE_HIGH :
return trigger = = ACPI_EDGE_SENSITIVE ?
IRQ_TYPE_EDGE_RISING :
IRQ_TYPE_LEVEL_HIGH ;
case ACPI_ACTIVE_BOTH :
if ( trigger = = ACPI_EDGE_SENSITIVE )
return IRQ_TYPE_EDGE_BOTH ;
default :
return IRQ_TYPE_NONE ;
}
}
/**
* acpi_gsi_to_irq ( ) - Retrieve the linux irq number for a given GSI
* @ gsi : GSI IRQ number to map
* @ irq : pointer where linux IRQ number is stored
*
* irq location updated with irq value [ > 0 on success , 0 on failure ]
*
* Returns : linux IRQ number on success ( > 0 )
* - EINVAL on failure
*/
int acpi_gsi_to_irq ( u32 gsi , unsigned int * irq )
{
2015-10-13 14:51:37 +03:00
struct irq_domain * d = irq_find_matching_fwnode ( acpi_gsi_domain_id ,
DOMAIN_BUS_ANY ) ;
* irq = irq_find_mapping ( d , gsi ) ;
2015-03-24 20:58:51 +03:00
/*
* * irq = = 0 means no mapping , that should
* be reported as a failure
*/
return ( * irq > 0 ) ? * irq : - EINVAL ;
}
EXPORT_SYMBOL_GPL ( acpi_gsi_to_irq ) ;
/**
* acpi_register_gsi ( ) - Map a GSI to a linux IRQ number
* @ dev : device for which IRQ has to be mapped
* @ gsi : GSI IRQ number
* @ trigger : trigger type of the GSI number to be mapped
* @ polarity : polarity of the GSI to be mapped
*
* Returns : a valid linux IRQ number on success
* - EINVAL on failure
*/
int acpi_register_gsi ( struct device * dev , u32 gsi , int trigger ,
int polarity )
{
unsigned int irq ;
unsigned int irq_type = acpi_gsi_get_irq_type ( trigger , polarity ) ;
2015-10-13 14:51:38 +03:00
if ( acpi_gsi_domain_id ) {
struct irq_fwspec fwspec ;
fwspec . fwnode = acpi_gsi_domain_id ;
fwspec . param [ 0 ] = gsi ;
fwspec . param [ 1 ] = irq_type ;
fwspec . param_count = 2 ;
return irq_create_fwspec_mapping ( & fwspec ) ;
} else {
irq = irq_create_mapping ( NULL , gsi ) ;
if ( ! irq )
return - EINVAL ;
}
2015-03-24 20:58:51 +03:00
/* Set irq type if specified and different than the current one */
if ( irq_type ! = IRQ_TYPE_NONE & &
irq_type ! = irq_get_trigger_type ( irq ) )
irq_set_irq_type ( irq , irq_type ) ;
return irq ;
}
EXPORT_SYMBOL_GPL ( acpi_register_gsi ) ;
/**
* acpi_unregister_gsi ( ) - Free a GSI < - > linux IRQ number mapping
* @ gsi : GSI IRQ number
*/
void acpi_unregister_gsi ( u32 gsi )
{
2015-10-13 14:51:37 +03:00
struct irq_domain * d = irq_find_matching_fwnode ( acpi_gsi_domain_id ,
DOMAIN_BUS_ANY ) ;
int irq = irq_find_mapping ( d , gsi ) ;
2015-03-24 20:58:51 +03:00
irq_dispose_mapping ( irq ) ;
}
EXPORT_SYMBOL_GPL ( acpi_unregister_gsi ) ;
2015-10-13 14:51:38 +03:00
/**
* acpi_set_irq_model - Setup the GSI irqdomain information
* @ model : the value assigned to acpi_irq_model
* @ fwnode : the irq_domain identifier for mapping and looking up
* GSI interrupts
*/
void __init acpi_set_irq_model ( enum acpi_irq_model_id model ,
struct fwnode_handle * fwnode )
{
acpi_irq_model = model ;
acpi_gsi_domain_id = fwnode ;
}