2010-06-16 22:19:47 +05:30
/*
* OMAP WakeupGen Source file
*
* OMAP WakeupGen is the interrupt controller extension used along
* with ARM GIC to wake the CPU out from low power states on
* external interrupts . It is responsible for generating wakeup
* event from the incoming interrupts and enable bits . It is
* implemented in MPU always ON power domain . During normal operation ,
* WakeupGen delivers external interrupts directly to the GIC .
*
* Copyright ( C ) 2011 Texas Instruments , Inc .
* Santosh Shilimkar < santosh . shilimkar @ ti . 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/kernel.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/platform_device.h>
# include <linux/cpu.h>
2010-06-16 23:29:31 +05:30
# include <linux/notifier.h>
# include <linux/cpu_pm.h>
2012-12-27 13:10:24 -06:00
# include <linux/irqchip/arm-gic.h>
2010-06-16 22:19:47 +05:30
2012-09-20 11:41:16 -07:00
# include "omap-wakeupgen.h"
2012-09-20 11:41:14 -07:00
# include "omap-secure.h"
2010-06-16 23:29:31 +05:30
2012-08-31 10:59:07 -07:00
# include "soc.h"
2010-06-16 23:29:31 +05:30
# include "omap4-sar-layout.h"
# include "common.h"
2010-06-16 22:19:47 +05:30
2013-10-09 12:42:33 +05:30
# define AM43XX_NR_REG_BANKS 7
# define AM43XX_IRQS 224
# define MAX_NR_REG_BANKS AM43XX_NR_REG_BANKS
# define MAX_IRQS AM43XX_IRQS
# define DEFAULT_NR_REG_BANKS 5
# define DEFAULT_IRQS 160
2010-06-16 22:19:47 +05:30
# define WKG_MASK_ALL 0x00000000
# define WKG_UNMASK_ALL 0xffffffff
# define CPU_ENA_OFFSET 0x400
# define CPU0_ID 0x0
# define CPU1_ID 0x1
2012-05-09 20:38:35 +05:30
# define OMAP4_NR_BANKS 4
# define OMAP4_NR_IRQS 128
2010-06-16 22:19:47 +05:30
static void __iomem * wakeupgen_base ;
2010-06-16 23:29:31 +05:30
static void __iomem * sar_base ;
2012-12-07 16:49:47 +02:00
static DEFINE_RAW_SPINLOCK ( wakeupgen_lock ) ;
2012-09-04 17:22:45 -07:00
static unsigned int irq_target_cpu [ MAX_IRQS ] ;
2013-10-09 12:42:33 +05:30
static unsigned int irq_banks = DEFAULT_NR_REG_BANKS ;
static unsigned int max_irqs = DEFAULT_IRQS ;
2012-05-09 20:38:35 +05:30
static unsigned int omap_secure_apis ;
2010-06-16 22:19:47 +05:30
/*
* Static helper functions .
*/
static inline u32 wakeupgen_readl ( u8 idx , u32 cpu )
{
2014-04-15 20:37:46 +03:00
return readl_relaxed ( wakeupgen_base + OMAP_WKG_ENB_A_0 +
2010-06-16 22:19:47 +05:30
( cpu * CPU_ENA_OFFSET ) + ( idx * 4 ) ) ;
}
static inline void wakeupgen_writel ( u32 val , u8 idx , u32 cpu )
{
2014-04-15 20:37:46 +03:00
writel_relaxed ( val , wakeupgen_base + OMAP_WKG_ENB_A_0 +
2010-06-16 22:19:47 +05:30
( cpu * CPU_ENA_OFFSET ) + ( idx * 4 ) ) ;
}
2010-06-16 23:29:31 +05:30
static inline void sar_writel ( u32 val , u32 offset , u8 idx )
{
2014-04-15 20:37:46 +03:00
writel_relaxed ( val , sar_base + offset + ( idx * 4 ) ) ;
2010-06-16 23:29:31 +05:30
}
2010-06-16 22:19:47 +05:30
static inline int _wakeupgen_get_irq_info ( u32 irq , u32 * bit_posn , u8 * reg_index )
{
unsigned int spi_irq ;
/*
* PPIs and SGIs are not supported .
*/
if ( irq < OMAP44XX_IRQ_GIC_START )
return - EINVAL ;
/*
* Subtract the GIC offset .
*/
spi_irq = irq - OMAP44XX_IRQ_GIC_START ;
if ( spi_irq > MAX_IRQS ) {
pr_err ( " omap wakeupGen: Invalid IRQ%d \n " , irq ) ;
return - EINVAL ;
}
/*
* Each WakeupGen register controls 32 interrupt .
* i . e . 1 bit per SPI IRQ
*/
* reg_index = spi_irq > > 5 ;
* bit_posn = spi_irq % = 32 ;
return 0 ;
}
static void _wakeupgen_clear ( unsigned int irq , unsigned int cpu )
{
u32 val , bit_number ;
u8 i ;
if ( _wakeupgen_get_irq_info ( irq , & bit_number , & i ) )
return ;
val = wakeupgen_readl ( i , cpu ) ;
val & = ~ BIT ( bit_number ) ;
wakeupgen_writel ( val , i , cpu ) ;
}
static void _wakeupgen_set ( unsigned int irq , unsigned int cpu )
{
u32 val , bit_number ;
u8 i ;
if ( _wakeupgen_get_irq_info ( irq , & bit_number , & i ) )
return ;
val = wakeupgen_readl ( i , cpu ) ;
val | = BIT ( bit_number ) ;
wakeupgen_writel ( val , i , cpu ) ;
}
/*
* Architecture specific Mask extension
*/
static void wakeupgen_mask ( struct irq_data * d )
{
unsigned long flags ;
2012-12-07 16:49:47 +02:00
raw_spin_lock_irqsave ( & wakeupgen_lock , flags ) ;
2013-12-03 15:57:24 +05:30
_wakeupgen_clear ( d - > hwirq , irq_target_cpu [ d - > hwirq ] ) ;
2012-12-07 16:49:47 +02:00
raw_spin_unlock_irqrestore ( & wakeupgen_lock , flags ) ;
2010-06-16 22:19:47 +05:30
}
/*
* Architecture specific Unmask extension
*/
static void wakeupgen_unmask ( struct irq_data * d )
{
unsigned long flags ;
2012-12-07 16:49:47 +02:00
raw_spin_lock_irqsave ( & wakeupgen_lock , flags ) ;
2013-12-03 15:57:24 +05:30
_wakeupgen_set ( d - > hwirq , irq_target_cpu [ d - > hwirq ] ) ;
2012-12-07 16:49:47 +02:00
raw_spin_unlock_irqrestore ( & wakeupgen_lock , flags ) ;
2010-06-16 22:19:47 +05:30
}
2012-03-06 12:00:25 -08:00
# ifdef CONFIG_HOTPLUG_CPU
2012-05-09 20:38:35 +05:30
static DEFINE_PER_CPU ( u32 [ MAX_NR_REG_BANKS ] , irqmasks ) ;
2012-03-06 12:00:25 -08:00
static void _wakeupgen_save_masks ( unsigned int cpu )
{
u8 i ;
2012-05-09 20:38:35 +05:30
for ( i = 0 ; i < irq_banks ; i + + )
2012-03-06 12:00:25 -08:00
per_cpu ( irqmasks , cpu ) [ i ] = wakeupgen_readl ( i , cpu ) ;
}
static void _wakeupgen_restore_masks ( unsigned int cpu )
{
u8 i ;
2012-05-09 20:38:35 +05:30
for ( i = 0 ; i < irq_banks ; i + + )
2012-03-06 12:00:25 -08:00
wakeupgen_writel ( per_cpu ( irqmasks , cpu ) [ i ] , i , cpu ) ;
}
static void _wakeupgen_set_all ( unsigned int cpu , unsigned int reg )
{
u8 i ;
2012-05-09 20:38:35 +05:30
for ( i = 0 ; i < irq_banks ; i + + )
2012-03-06 12:00:25 -08:00
wakeupgen_writel ( reg , i , cpu ) ;
}
2010-06-16 22:19:47 +05:30
/*
* Mask or unmask all interrupts on given CPU .
* 0 = Mask all interrupts on the ' cpu '
* 1 = Unmask all interrupts on the ' cpu '
* Ensure that the initial mask is maintained . This is faster than
* iterating through GIC registers to arrive at the correct masks .
*/
static void wakeupgen_irqmask_all ( unsigned int cpu , unsigned int set )
{
unsigned long flags ;
2012-12-07 16:49:47 +02:00
raw_spin_lock_irqsave ( & wakeupgen_lock , flags ) ;
2010-06-16 22:19:47 +05:30
if ( set ) {
_wakeupgen_save_masks ( cpu ) ;
_wakeupgen_set_all ( cpu , WKG_MASK_ALL ) ;
} else {
_wakeupgen_set_all ( cpu , WKG_UNMASK_ALL ) ;
_wakeupgen_restore_masks ( cpu ) ;
}
2012-12-07 16:49:47 +02:00
raw_spin_unlock_irqrestore ( & wakeupgen_lock , flags ) ;
2010-06-16 22:19:47 +05:30
}
2012-03-06 12:00:25 -08:00
# endif
2010-06-16 22:19:47 +05:30
2010-06-16 23:29:31 +05:30
# ifdef CONFIG_CPU_PM
2012-05-09 20:38:35 +05:30
static inline void omap4_irq_save_context ( void )
2010-06-16 23:29:31 +05:30
{
u32 i , val ;
if ( omap_rev ( ) = = OMAP4430_REV_ES1_0 )
return ;
2012-05-09 20:38:35 +05:30
for ( i = 0 ; i < irq_banks ; i + + ) {
2010-06-16 23:29:31 +05:30
/* Save the CPUx interrupt mask for IRQ 0 to 127 */
val = wakeupgen_readl ( i , 0 ) ;
sar_writel ( val , WAKEUPGENENB_OFFSET_CPU0 , i ) ;
val = wakeupgen_readl ( i , 1 ) ;
sar_writel ( val , WAKEUPGENENB_OFFSET_CPU1 , i ) ;
/*
* Disable the secure interrupts for CPUx . The restore
* code blindly restores secure and non - secure interrupt
* masks from SAR RAM . Secure interrupts are not suppose
* to be enabled from HLOS . So overwrite the SAR location
* so that the secure interrupt remains disabled .
*/
sar_writel ( 0x0 , WAKEUPGENENB_SECURE_OFFSET_CPU0 , i ) ;
sar_writel ( 0x0 , WAKEUPGENENB_SECURE_OFFSET_CPU1 , i ) ;
}
/* Save AuxBoot* registers */
2014-04-15 20:37:46 +03:00
val = readl_relaxed ( wakeupgen_base + OMAP_AUX_CORE_BOOT_0 ) ;
writel_relaxed ( val , sar_base + AUXCOREBOOT0_OFFSET ) ;
val = readl_relaxed ( wakeupgen_base + OMAP_AUX_CORE_BOOT_1 ) ;
writel_relaxed ( val , sar_base + AUXCOREBOOT1_OFFSET ) ;
2010-06-16 23:29:31 +05:30
/* Save SyncReq generation logic */
2014-04-15 20:37:46 +03:00
val = readl_relaxed ( wakeupgen_base + OMAP_PTMSYNCREQ_MASK ) ;
writel_relaxed ( val , sar_base + PTMSYNCREQ_MASK_OFFSET ) ;
val = readl_relaxed ( wakeupgen_base + OMAP_PTMSYNCREQ_EN ) ;
writel_relaxed ( val , sar_base + PTMSYNCREQ_EN_OFFSET ) ;
2010-06-16 23:29:31 +05:30
/* Set the Backup Bit Mask status */
2014-04-15 20:37:46 +03:00
val = readl_relaxed ( sar_base + SAR_BACKUP_STATUS_OFFSET ) ;
2010-06-16 23:29:31 +05:30
val | = SAR_BACKUP_STATUS_WAKEUPGEN ;
2014-04-15 20:37:46 +03:00
writel_relaxed ( val , sar_base + SAR_BACKUP_STATUS_OFFSET ) ;
2012-05-09 20:38:35 +05:30
}
static inline void omap5_irq_save_context ( void )
{
u32 i , val ;
for ( i = 0 ; i < irq_banks ; i + + ) {
/* Save the CPUx interrupt mask for IRQ 0 to 159 */
val = wakeupgen_readl ( i , 0 ) ;
sar_writel ( val , OMAP5_WAKEUPGENENB_OFFSET_CPU0 , i ) ;
val = wakeupgen_readl ( i , 1 ) ;
sar_writel ( val , OMAP5_WAKEUPGENENB_OFFSET_CPU1 , i ) ;
sar_writel ( 0x0 , OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU0 , i ) ;
sar_writel ( 0x0 , OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU1 , i ) ;
}
/* Save AuxBoot* registers */
2014-04-15 20:37:46 +03:00
val = readl_relaxed ( wakeupgen_base + OMAP_AUX_CORE_BOOT_0 ) ;
writel_relaxed ( val , sar_base + OMAP5_AUXCOREBOOT0_OFFSET ) ;
val = readl_relaxed ( wakeupgen_base + OMAP_AUX_CORE_BOOT_0 ) ;
writel_relaxed ( val , sar_base + OMAP5_AUXCOREBOOT1_OFFSET ) ;
2012-05-09 20:38:35 +05:30
/* Set the Backup Bit Mask status */
2014-04-15 20:37:46 +03:00
val = readl_relaxed ( sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET ) ;
2012-05-09 20:38:35 +05:30
val | = SAR_BACKUP_STATUS_WAKEUPGEN ;
2014-04-15 20:37:46 +03:00
writel_relaxed ( val , sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET ) ;
2012-05-09 20:38:35 +05:30
}
/*
* Save WakeupGen interrupt context in SAR BANK3 . Restore is done by
* ROM code . WakeupGen IP is integrated along with GIC to manage the
* interrupt wakeups from CPU low power states . It manages
* masking / unmasking of Shared peripheral interrupts ( SPI ) . So the
* interrupt enable / disable control should be in sync and consistent
* at WakeupGen and GIC so that interrupts are not lost .
*/
static void irq_save_context ( void )
{
if ( ! sar_base )
sar_base = omap4_get_sar_ram_base ( ) ;
if ( soc_is_omap54xx ( ) )
omap5_irq_save_context ( ) ;
else
omap4_irq_save_context ( ) ;
2010-06-16 23:29:31 +05:30
}
/*
* Clear WakeupGen SAR backup status .
*/
2012-04-13 06:34:26 -06:00
static void irq_sar_clear ( void )
2010-06-16 23:29:31 +05:30
{
u32 val ;
2012-05-09 20:38:35 +05:30
u32 offset = SAR_BACKUP_STATUS_OFFSET ;
if ( soc_is_omap54xx ( ) )
offset = OMAP5_SAR_BACKUP_STATUS_OFFSET ;
2014-04-15 20:37:46 +03:00
val = readl_relaxed ( sar_base + offset ) ;
2010-06-16 23:29:31 +05:30
val & = ~ SAR_BACKUP_STATUS_WAKEUPGEN ;
2014-04-15 20:37:46 +03:00
writel_relaxed ( val , sar_base + offset ) ;
2010-06-16 23:29:31 +05:30
}
/*
* Save GIC and Wakeupgen interrupt context using secure API
* for HS / EMU devices .
*/
static void irq_save_secure_context ( void )
{
u32 ret ;
ret = omap_secure_dispatcher ( OMAP4_HAL_SAVEGIC_INDEX ,
FLAG_START_CRITICAL ,
0 , 0 , 0 , 0 , 0 ) ;
if ( ret ! = API_HAL_RET_VALUE_OK )
pr_err ( " GIC and Wakeupgen context save failed \n " ) ;
}
# endif
2010-06-16 22:19:48 +05:30
# ifdef CONFIG_HOTPLUG_CPU
2013-06-17 15:43:14 -04:00
static int irq_cpu_hotplug_notify ( struct notifier_block * self ,
unsigned long action , void * hcpu )
2010-06-16 22:19:48 +05:30
{
unsigned int cpu = ( unsigned int ) hcpu ;
switch ( action ) {
case CPU_ONLINE :
wakeupgen_irqmask_all ( cpu , 0 ) ;
break ;
case CPU_DEAD :
wakeupgen_irqmask_all ( cpu , 1 ) ;
break ;
}
return NOTIFY_OK ;
}
static struct notifier_block __refdata irq_hotplug_notifier = {
. notifier_call = irq_cpu_hotplug_notify ,
} ;
static void __init irq_hotplug_init ( void )
{
register_hotcpu_notifier ( & irq_hotplug_notifier ) ;
}
# else
static void __init irq_hotplug_init ( void )
{ }
# endif
2010-06-16 23:29:31 +05:30
# ifdef CONFIG_CPU_PM
static int irq_notifier ( struct notifier_block * self , unsigned long cmd , void * v )
{
switch ( cmd ) {
case CPU_CLUSTER_PM_ENTER :
if ( omap_type ( ) = = OMAP2_DEVICE_TYPE_GP )
irq_save_context ( ) ;
else
irq_save_secure_context ( ) ;
break ;
case CPU_CLUSTER_PM_EXIT :
if ( omap_type ( ) = = OMAP2_DEVICE_TYPE_GP )
irq_sar_clear ( ) ;
break ;
}
return NOTIFY_OK ;
}
static struct notifier_block irq_notifier_block = {
. notifier_call = irq_notifier ,
} ;
static void __init irq_pm_init ( void )
{
2012-05-09 20:38:35 +05:30
/* FIXME: Remove this when MPU OSWR support is added */
if ( ! soc_is_omap54xx ( ) )
cpu_pm_register_notifier ( & irq_notifier_block ) ;
2010-06-16 23:29:31 +05:30
}
# else
static void __init irq_pm_init ( void )
{ }
# endif
2012-05-09 20:38:35 +05:30
void __iomem * omap_get_wakeupgen_base ( void )
{
return wakeupgen_base ;
}
int omap_secure_apis_support ( void )
{
return omap_secure_apis ;
}
2010-06-16 22:19:47 +05:30
/*
* Initialise the wakeupgen module .
*/
int __init omap_wakeupgen_init ( void )
{
int i ;
unsigned int boot_cpu = smp_processor_id ( ) ;
/* Not supported on OMAP4 ES1.0 silicon */
if ( omap_rev ( ) = = OMAP4430_REV_ES1_0 ) {
WARN ( 1 , " WakeupGen: Not supported on OMAP4430 ES1.0 \n " ) ;
return - EPERM ;
}
/* Static mapping, never released */
2012-05-09 20:38:35 +05:30
wakeupgen_base = ioremap ( OMAP_WKUPGEN_BASE , SZ_4K ) ;
2010-06-16 22:19:47 +05:30
if ( WARN_ON ( ! wakeupgen_base ) )
return - ENOMEM ;
2012-05-09 20:38:35 +05:30
if ( cpu_is_omap44xx ( ) ) {
irq_banks = OMAP4_NR_BANKS ;
max_irqs = OMAP4_NR_IRQS ;
omap_secure_apis = 1 ;
2013-10-09 12:42:33 +05:30
} else if ( soc_is_am43xx ( ) ) {
irq_banks = AM43XX_NR_REG_BANKS ;
max_irqs = AM43XX_IRQS ;
2012-05-09 20:38:35 +05:30
}
2010-06-16 22:19:47 +05:30
/* Clear all IRQ bitmasks at wakeupGen level */
2012-05-09 20:38:35 +05:30
for ( i = 0 ; i < irq_banks ; i + + ) {
2010-06-16 22:19:47 +05:30
wakeupgen_writel ( 0 , i , CPU0_ID ) ;
2013-10-09 12:42:33 +05:30
if ( ! soc_is_am43xx ( ) )
wakeupgen_writel ( 0 , i , CPU1_ID ) ;
2010-06-16 22:19:47 +05:30
}
/*
* Override GIC architecture specific functions to add
* OMAP WakeupGen interrupt controller along with GIC
*/
gic_arch_extn . irq_mask = wakeupgen_mask ;
gic_arch_extn . irq_unmask = wakeupgen_unmask ;
gic_arch_extn . flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE ;
/*
* FIXME : Add support to set_smp_affinity ( ) once the core
* GIC code has necessary hooks in place .
*/
/* Associate all the IRQs to boot CPU like GIC init does. */
2012-05-09 20:38:35 +05:30
for ( i = 0 ; i < max_irqs ; i + + )
2010-06-16 22:19:47 +05:30
irq_target_cpu [ i ] = boot_cpu ;
2010-06-16 22:19:48 +05:30
irq_hotplug_init ( ) ;
2010-06-16 23:29:31 +05:30
irq_pm_init ( ) ;
2010-06-16 22:19:48 +05:30
2010-06-16 22:19:47 +05:30
return 0 ;
}