2011-09-06 10:39:44 +04:00
/*
2013-03-21 18:58:06 +04:00
* Copyright 2011 - 2013 Freescale Semiconductor , Inc .
2011-09-06 10:39:44 +04:00
* Copyright 2011 Linaro Ltd .
*
* The code contained herein is licensed under the GNU General Public
* License . You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations :
*
* http : //www.opensource.org/licenses/gpl-license.html
* http : //www.gnu.org/copyleft/gpl.html
*/
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
2012-12-27 23:10:24 +04:00
# include <linux/irqchip/arm-gic.h>
2013-03-25 16:20:42 +04:00
# include "common.h"
2011-09-06 10:39:44 +04:00
# define GPC_IMR1 0x008
# define GPC_PGC_CPU_PDN 0x2a0
# define IMR_NUM 4
static void __iomem * gpc_base ;
static u32 gpc_wake_irqs [ IMR_NUM ] ;
static u32 gpc_saved_imrs [ IMR_NUM ] ;
void imx_gpc_pre_suspend ( void )
{
void __iomem * reg_imr1 = gpc_base + GPC_IMR1 ;
int i ;
/* Tell GPC to power off ARM core when suspend */
writel_relaxed ( 0x1 , gpc_base + GPC_PGC_CPU_PDN ) ;
for ( i = 0 ; i < IMR_NUM ; i + + ) {
gpc_saved_imrs [ i ] = readl_relaxed ( reg_imr1 + i * 4 ) ;
writel_relaxed ( ~ gpc_wake_irqs [ i ] , reg_imr1 + i * 4 ) ;
}
}
void imx_gpc_post_resume ( void )
{
void __iomem * reg_imr1 = gpc_base + GPC_IMR1 ;
int i ;
/* Keep ARM core powered on for other low-power modes */
writel_relaxed ( 0x0 , gpc_base + GPC_PGC_CPU_PDN ) ;
for ( i = 0 ; i < IMR_NUM ; i + + )
writel_relaxed ( gpc_saved_imrs [ i ] , reg_imr1 + i * 4 ) ;
}
static int imx_gpc_irq_set_wake ( struct irq_data * d , unsigned int on )
{
unsigned int idx = d - > irq / 32 - 1 ;
u32 mask ;
/* Sanity check for SPI irq */
if ( d - > irq < 32 )
return - EINVAL ;
mask = 1 < < d - > irq % 32 ;
gpc_wake_irqs [ idx ] = on ? gpc_wake_irqs [ idx ] | mask :
gpc_wake_irqs [ idx ] & ~ mask ;
return 0 ;
}
2013-03-21 18:58:06 +04:00
void imx_gpc_mask_all ( void )
{
void __iomem * reg_imr1 = gpc_base + GPC_IMR1 ;
int i ;
for ( i = 0 ; i < IMR_NUM ; i + + ) {
gpc_saved_imrs [ i ] = readl_relaxed ( reg_imr1 + i * 4 ) ;
writel_relaxed ( ~ 0 , reg_imr1 + i * 4 ) ;
}
}
void imx_gpc_restore_all ( void )
{
void __iomem * reg_imr1 = gpc_base + GPC_IMR1 ;
int i ;
for ( i = 0 ; i < IMR_NUM ; i + + )
writel_relaxed ( gpc_saved_imrs [ i ] , reg_imr1 + i * 4 ) ;
}
2013-10-16 15:52:00 +04:00
void imx_gpc_irq_unmask ( struct irq_data * d )
2011-09-06 10:39:44 +04:00
{
void __iomem * reg ;
u32 val ;
/* Sanity check for SPI irq */
if ( d - > irq < 32 )
return ;
reg = gpc_base + GPC_IMR1 + ( d - > irq / 32 - 1 ) * 4 ;
val = readl_relaxed ( reg ) ;
val & = ~ ( 1 < < d - > irq % 32 ) ;
writel_relaxed ( val , reg ) ;
}
2013-10-16 15:52:00 +04:00
void imx_gpc_irq_mask ( struct irq_data * d )
2011-09-06 10:39:44 +04:00
{
void __iomem * reg ;
u32 val ;
/* Sanity check for SPI irq */
if ( d - > irq < 32 )
return ;
reg = gpc_base + GPC_IMR1 + ( d - > irq / 32 - 1 ) * 4 ;
val = readl_relaxed ( reg ) ;
val | = 1 < < ( d - > irq % 32 ) ;
writel_relaxed ( val , reg ) ;
}
void __init imx_gpc_init ( void )
{
struct device_node * np ;
2012-12-04 18:55:13 +04:00
int i ;
2011-09-06 10:39:44 +04:00
np = of_find_compatible_node ( NULL , NULL , " fsl,imx6q-gpc " ) ;
gpc_base = of_iomap ( np , 0 ) ;
WARN_ON ( ! gpc_base ) ;
2012-12-04 18:55:13 +04:00
/* Initially mask all interrupts */
for ( i = 0 ; i < IMR_NUM ; i + + )
writel_relaxed ( ~ 0 , gpc_base + GPC_IMR1 + i * 4 ) ;
2011-09-06 10:39:44 +04:00
/* Register GPC as the secondary interrupt controller behind GIC */
gic_arch_extn . irq_mask = imx_gpc_irq_mask ;
gic_arch_extn . irq_unmask = imx_gpc_irq_unmask ;
gic_arch_extn . irq_set_wake = imx_gpc_irq_set_wake ;
}