2019-05-28 19:57:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-03-19 18:36:12 +04:00
/*
* Copyright ( C ) ST - Ericsson SA 2010 - 2013
* Author : Rickard Andersson < rickard . andersson @ stericsson . com > for
* ST - Ericsson .
* Author : Daniel Lezcano < daniel . lezcano @ linaro . org > for Linaro .
2013-12-18 18:59:37 +04:00
* Author : Ulf Hansson < ulf . hansson @ linaro . org > for Linaro .
2013-03-19 18:36:12 +04:00
*/
# include <linux/kernel.h>
# include <linux/irqchip/arm-gic.h>
# include <linux/delay.h>
# include <linux/io.h>
2013-12-18 18:59:37 +04:00
# include <linux/suspend.h>
2013-03-19 18:36:12 +04:00
# include <linux/platform_data/arm-ux500-pm.h>
2015-05-14 10:48:37 +03:00
# include <linux/of.h>
# include <linux/of_address.h>
2013-03-19 18:36:12 +04:00
2013-03-19 18:41:55 +04:00
# include "db8500-regs.h"
2013-03-19 18:36:12 +04:00
/* ARM WFI Standby signal register */
# define PRCM_ARM_WFI_STANDBY (prcmu_base + 0x130)
# define PRCM_ARM_WFI_STANDBY_WFI0 0x08
# define PRCM_ARM_WFI_STANDBY_WFI1 0x10
# define PRCM_IOCR (prcmu_base + 0x310)
# define PRCM_IOCR_IOFORCE 0x1
/* Dual A9 core interrupt management unit registers */
# define PRCM_A9_MASK_REQ (prcmu_base + 0x328)
# define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1
# define PRCM_A9_MASK_ACK (prcmu_base + 0x32c)
# define PRCM_ARMITMSK31TO0 (prcmu_base + 0x11c)
# define PRCM_ARMITMSK63TO32 (prcmu_base + 0x120)
# define PRCM_ARMITMSK95TO64 (prcmu_base + 0x124)
# define PRCM_ARMITMSK127TO96 (prcmu_base + 0x128)
# define PRCM_POWER_STATE_VAL (prcmu_base + 0x25C)
# define PRCM_ARMITVAL31TO0 (prcmu_base + 0x260)
# define PRCM_ARMITVAL63TO32 (prcmu_base + 0x264)
# define PRCM_ARMITVAL95TO64 (prcmu_base + 0x268)
# define PRCM_ARMITVAL127TO96 (prcmu_base + 0x26C)
static void __iomem * prcmu_base ;
2015-05-14 10:48:37 +03:00
static void __iomem * dist_base ;
2013-03-19 18:36:12 +04:00
/* This function decouple the gic from the prcmu */
int prcmu_gic_decouple ( void )
{
u32 val = readl ( PRCM_A9_MASK_REQ ) ;
/* Set bit 0 register value to 1 */
writel ( val | PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ ,
PRCM_A9_MASK_REQ ) ;
/* Make sure the register is updated */
readl ( PRCM_A9_MASK_REQ ) ;
/* Wait a few cycles for the gic mask completion */
udelay ( 1 ) ;
return 0 ;
}
/* This function recouple the gic with the prcmu */
int prcmu_gic_recouple ( void )
{
u32 val = readl ( PRCM_A9_MASK_REQ ) ;
/* Set bit 0 register value to 0 */
writel ( val & ~ PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ , PRCM_A9_MASK_REQ ) ;
return 0 ;
}
# define PRCMU_GIC_NUMBER_REGS 5
/*
* This function checks if there are pending irq on the gic . It only
* makes sense if the gic has been decoupled before with the
* db8500_prcmu_gic_decouple function . Disabling an interrupt only
* disables the forwarding of the interrupt to any CPU interface . It
* does not prevent the interrupt from changing state , for example
* becoming pending , or active and pending if it is already
* active . Hence , we have to check the interrupt is pending * and * is
* active .
*/
bool prcmu_gic_pending_irq ( void )
{
u32 pr ; /* Pending register */
u32 er ; /* Enable register */
int i ;
/* 5 registers. STI & PPI not skipped */
for ( i = 0 ; i < PRCMU_GIC_NUMBER_REGS ; i + + ) {
pr = readl_relaxed ( dist_base + GIC_DIST_PENDING_SET + i * 4 ) ;
er = readl_relaxed ( dist_base + GIC_DIST_ENABLE_SET + i * 4 ) ;
if ( pr & er )
return true ; /* There is a pending interrupt */
}
return false ;
}
/*
* This function checks if there are pending interrupt on the
* prcmu which has been delegated to monitor the irqs with the
* db8500_prcmu_copy_gic_settings function .
*/
bool prcmu_pending_irq ( void )
{
u32 it , im ;
int i ;
for ( i = 0 ; i < PRCMU_GIC_NUMBER_REGS - 1 ; i + + ) {
it = readl ( PRCM_ARMITVAL31TO0 + i * 4 ) ;
im = readl ( PRCM_ARMITMSK31TO0 + i * 4 ) ;
if ( it & im )
return true ; /* There is a pending interrupt */
}
return false ;
}
/*
* This function checks if the specified cpu is in in WFI . It ' s usage
* makes sense only if the gic is decoupled with the db8500_prcmu_gic_decouple
* function . Of course passing smp_processor_id ( ) to this function will
* always return false . . .
*/
bool prcmu_is_cpu_in_wfi ( int cpu )
{
2016-11-16 18:20:37 +03:00
return readl ( PRCM_ARM_WFI_STANDBY ) &
( cpu ? PRCM_ARM_WFI_STANDBY_WFI1 : PRCM_ARM_WFI_STANDBY_WFI0 ) ;
2013-03-19 18:36:12 +04:00
}
/*
* This function copies the gic SPI settings to the prcmu in order to
* monitor them and abort / finish the retention / off sequence or state .
*/
int prcmu_copy_gic_settings ( void )
{
u32 er ; /* Enable register */
int i ;
/* We skip the STI and PPI */
for ( i = 0 ; i < PRCMU_GIC_NUMBER_REGS - 1 ; i + + ) {
er = readl_relaxed ( dist_base +
GIC_DIST_ENABLE_SET + ( i + 1 ) * 4 ) ;
writel ( er , PRCM_ARMITMSK31TO0 + i * 4 ) ;
}
return 0 ;
}
2013-12-18 18:59:37 +04:00
# ifdef CONFIG_SUSPEND
static int ux500_suspend_enter ( suspend_state_t state )
{
cpu_do_idle ( ) ;
return 0 ;
}
static int ux500_suspend_valid ( suspend_state_t state )
{
return state = = PM_SUSPEND_MEM | | state = = PM_SUSPEND_STANDBY ;
}
static const struct platform_suspend_ops ux500_suspend_ops = {
. enter = ux500_suspend_enter ,
. valid = ux500_suspend_valid ,
} ;
# define UX500_SUSPEND_OPS (&ux500_suspend_ops)
# else
# define UX500_SUSPEND_OPS NULL
# endif
2013-03-19 18:36:12 +04:00
void __init ux500_pm_init ( u32 phy_base , u32 size )
{
2015-05-14 10:48:37 +03:00
struct device_node * np ;
2013-03-19 18:36:12 +04:00
prcmu_base = ioremap ( phy_base , size ) ;
if ( ! prcmu_base ) {
pr_err ( " could not remap PRCMU for PM functions \n " ) ;
return ;
}
2015-05-14 10:48:37 +03:00
np = of_find_compatible_node ( NULL , NULL , " arm,cortex-a9-gic " ) ;
dist_base = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
if ( ! dist_base ) {
pr_err ( " could not remap GIC dist base for PM functions \n " ) ;
return ;
}
2013-03-19 18:36:12 +04:00
/*
* On watchdog reboot the GIC is in some cases decoupled .
* This will make sure that the GIC is correctly configured .
*/
prcmu_gic_recouple ( ) ;
2013-12-18 18:59:37 +04:00
/* Set up ux500 suspend callbacks. */
suspend_set_ops ( UX500_SUSPEND_OPS ) ;
2013-03-19 18:36:12 +04:00
}