@ -41,6 +41,8 @@
# include <asm/virt.h>
# include <linux/syscore_ops.h>
# include <linux/suspend.h>
# include <linux/notifier.h>
# include "irq-gic-common.h"
@ -60,6 +62,14 @@ struct gic_chip_data {
u32 nr_redist_regions ;
unsigned int irq_nr ;
struct partition_desc * ppi_descs [ 16 ] ;
# ifdef CONFIG_HIBERNATION
unsigned int enabled_irqs [ 32 ] ;
unsigned int active_irqs [ 32 ] ;
unsigned int irq_edg_lvl [ 64 ] ;
unsigned int ppi_edg_lvl ;
unsigned int enabled_sgis ;
unsigned int pending_sgis ;
# endif
} ;
static struct gic_chip_data gic_data __read_mostly ;
@ -74,6 +84,9 @@ static struct gic_kvm_info gic_v3_kvm_info;
/* Our default, arbitrary priority value. Linux only uses one anyway. */
# define DEFAULT_PMR_VALUE 0xf0
static void gic_dist_init ( void ) ;
static void gic_cpu_init ( void ) ;
static inline unsigned int gic_irq ( struct irq_data * d )
{
return d - > hwirq ;
@ -334,9 +347,54 @@ static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
}
# ifdef CONFIG_PM
# ifdef CONFIG_HIBERNATION
extern int in_suspend ;
static bool hibernation ;
static int gic_suspend_notifier ( struct notifier_block * nb ,
unsigned long event ,
void * dummy )
{
if ( event = = PM_HIBERNATION_PREPARE )
hibernation = true ;
else if ( event = = PM_POST_HIBERNATION )
hibernation = false ;
return NOTIFY_OK ;
}
static struct notifier_block gic_notif_block = {
. notifier_call = gic_suspend_notifier ,
} ;
static void gic_hibernation_suspend ( void )
{
int i ;
void __iomem * base = gic_data . dist_base ;
void __iomem * rdist_base = gic_data_rdist_sgi_base ( ) ;
gic_data . enabled_sgis = readl_relaxed ( rdist_base + GICD_ISENABLER ) ;
gic_data . pending_sgis = readl_relaxed ( rdist_base + GICD_ISPENDR ) ;
/* Store edge level for PPIs by reading GICR_ICFGR1 */
gic_data . ppi_edg_lvl = readl_relaxed ( rdist_base + GICR_ICFGR0 + 4 ) ;
for ( i = 0 ; i * 32 < gic_data . irq_nr ; i + + ) {
gic_data . enabled_irqs [ i ] = readl_relaxed ( base +
GICD_ISENABLER + i * 4 ) ;
gic_data . active_irqs [ i ] = readl_relaxed ( base +
GICD_ISPENDR + i * 4 ) ;
}
for ( i = 2 ; i < gic_data . irq_nr / 16 ; i + + )
gic_data . irq_edg_lvl [ i ] = readl_relaxed ( base +
GICD_ICFGR + i * 4 ) ;
}
# endif
static int gic_suspend ( void )
{
# ifdef CONFIG_HIBERNATION
if ( unlikely ( hibernation ) )
gic_hibernation_suspend ( ) ;
# endif
return 0 ;
}
@ -379,6 +437,48 @@ static void gic_resume_one(struct gic_chip_data *gic)
static void gic_resume ( void )
{
# ifdef CONFIG_HIBERNATION
int i ;
void __iomem * base = gic_data . dist_base ;
void __iomem * rdist_base = gic_data_rdist_sgi_base ( ) ;
/*
* in_suspend is defined in hibernate . c and will be 0 during
* hibernation restore case . Also it willl be 0 for suspend to ram case
* and similar cases . Underlying code will not get executed in regular
* cases and will be executed only for hibernation restore .
*/
if ( unlikely ( ( in_suspend = = 0 & & hibernation ) ) ) {
pr_info ( " Re-initializing gic in hibernation restore \n " ) ;
gic_dist_init ( ) ;
gic_cpu_init ( ) ;
/* Activate and enable SGIs and PPIs */
writel_relaxed ( gic_data . enabled_sgis ,
rdist_base + GICD_ISENABLER ) ;
writel_relaxed ( gic_data . pending_sgis ,
rdist_base + GICD_ISPENDR ) ;
/* Restore edge and level triggers for PPIs from GICR_ICFGR1 */
writel_relaxed ( gic_data . ppi_edg_lvl ,
rdist_base + GICR_ICFGR0 + 4 ) ;
/* Restore edge and level triggers */
for ( i = 2 ; i < gic_data . irq_nr / 16 ; i + + )
writel_relaxed ( gic_data . irq_edg_lvl [ i ] ,
base + GICD_ICFGR + i * 4 ) ;
gic_dist_wait_for_rwp ( ) ;
/* Activate and enable interupts from backup */
for ( i = 0 ; i * 32 < gic_data . irq_nr ; i + + ) {
writel_relaxed ( gic_data . active_irqs [ i ] ,
base + GICD_ISPENDR + i * 4 ) ;
writel_relaxed ( gic_data . enabled_irqs [ i ] ,
base + GICD_ISENABLER + i * 4 ) ;
}
gic_dist_wait_for_rwp ( ) ;
}
# endif
gic_resume_one ( & gic_data ) ;
}
@ -458,7 +558,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
} while ( irqnr ! = ICC_IAR1_EL1_SPURIOUS ) ;
}
static void __init gic_dist_init ( void )
static void gic_dist_init ( void )
{
unsigned int i ;
u64 affinity ;
@ -1278,7 +1378,11 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
redist_stride , & node - > fwnode ) ;
if ( err )
goto out_unmap_rdist ;
# ifdef CONFIG_HIBERNATION
err = register_pm_notifier ( & gic_notif_block ) ;
if ( err )
goto out_unmap_rdist ;
# endif
gic_populate_ppi_partitions ( node ) ;
gic_of_setup_kvm_info ( node ) ;
return 0 ;