162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2010-2013 462306a36Sopenharmony_ci * Author: Rickard Andersson <rickard.andersson@stericsson.com> for 562306a36Sopenharmony_ci * ST-Ericsson. 662306a36Sopenharmony_ci * Author: Daniel Lezcano <daniel.lezcano@linaro.org> for Linaro. 762306a36Sopenharmony_ci * Author: Ulf Hansson <ulf.hansson@linaro.org> for Linaro. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/irqchip/arm-gic.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/suspend.h> 1562306a36Sopenharmony_ci#include <linux/platform_data/arm-ux500-pm.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/of_address.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* ARM WFI Standby signal register */ 2062306a36Sopenharmony_ci#define PRCM_ARM_WFI_STANDBY (prcmu_base + 0x130) 2162306a36Sopenharmony_ci#define PRCM_ARM_WFI_STANDBY_WFI0 0x08 2262306a36Sopenharmony_ci#define PRCM_ARM_WFI_STANDBY_WFI1 0x10 2362306a36Sopenharmony_ci#define PRCM_IOCR (prcmu_base + 0x310) 2462306a36Sopenharmony_ci#define PRCM_IOCR_IOFORCE 0x1 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Dual A9 core interrupt management unit registers */ 2762306a36Sopenharmony_ci#define PRCM_A9_MASK_REQ (prcmu_base + 0x328) 2862306a36Sopenharmony_ci#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define PRCM_A9_MASK_ACK (prcmu_base + 0x32c) 3162306a36Sopenharmony_ci#define PRCM_ARMITMSK31TO0 (prcmu_base + 0x11c) 3262306a36Sopenharmony_ci#define PRCM_ARMITMSK63TO32 (prcmu_base + 0x120) 3362306a36Sopenharmony_ci#define PRCM_ARMITMSK95TO64 (prcmu_base + 0x124) 3462306a36Sopenharmony_ci#define PRCM_ARMITMSK127TO96 (prcmu_base + 0x128) 3562306a36Sopenharmony_ci#define PRCM_POWER_STATE_VAL (prcmu_base + 0x25C) 3662306a36Sopenharmony_ci#define PRCM_ARMITVAL31TO0 (prcmu_base + 0x260) 3762306a36Sopenharmony_ci#define PRCM_ARMITVAL63TO32 (prcmu_base + 0x264) 3862306a36Sopenharmony_ci#define PRCM_ARMITVAL95TO64 (prcmu_base + 0x268) 3962306a36Sopenharmony_ci#define PRCM_ARMITVAL127TO96 (prcmu_base + 0x26C) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void __iomem *prcmu_base; 4262306a36Sopenharmony_cistatic void __iomem *dist_base; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* This function decouple the gic from the prcmu */ 4562306a36Sopenharmony_ciint prcmu_gic_decouple(void) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci u32 val = readl(PRCM_A9_MASK_REQ); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* Set bit 0 register value to 1 */ 5062306a36Sopenharmony_ci writel(val | PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ, 5162306a36Sopenharmony_ci PRCM_A9_MASK_REQ); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* Make sure the register is updated */ 5462306a36Sopenharmony_ci readl(PRCM_A9_MASK_REQ); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* Wait a few cycles for the gic mask completion */ 5762306a36Sopenharmony_ci udelay(1); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* This function recouple the gic with the prcmu */ 6362306a36Sopenharmony_ciint prcmu_gic_recouple(void) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci u32 val = readl(PRCM_A9_MASK_REQ); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* Set bit 0 register value to 0 */ 6862306a36Sopenharmony_ci writel(val & ~PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ, PRCM_A9_MASK_REQ); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return 0; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define PRCMU_GIC_NUMBER_REGS 5 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * This function checks if there are pending irq on the gic. It only 7762306a36Sopenharmony_ci * makes sense if the gic has been decoupled before with the 7862306a36Sopenharmony_ci * db8500_prcmu_gic_decouple function. Disabling an interrupt only 7962306a36Sopenharmony_ci * disables the forwarding of the interrupt to any CPU interface. It 8062306a36Sopenharmony_ci * does not prevent the interrupt from changing state, for example 8162306a36Sopenharmony_ci * becoming pending, or active and pending if it is already 8262306a36Sopenharmony_ci * active. Hence, we have to check the interrupt is pending *and* is 8362306a36Sopenharmony_ci * active. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cibool prcmu_gic_pending_irq(void) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci u32 pr; /* Pending register */ 8862306a36Sopenharmony_ci u32 er; /* Enable register */ 8962306a36Sopenharmony_ci int i; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 5 registers. STI & PPI not skipped */ 9262306a36Sopenharmony_ci for (i = 0; i < PRCMU_GIC_NUMBER_REGS; i++) { 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci pr = readl_relaxed(dist_base + GIC_DIST_PENDING_SET + i * 4); 9562306a36Sopenharmony_ci er = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (pr & er) 9862306a36Sopenharmony_ci return true; /* There is a pending interrupt */ 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return false; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * This function checks if there are pending interrupt on the 10662306a36Sopenharmony_ci * prcmu which has been delegated to monitor the irqs with the 10762306a36Sopenharmony_ci * db8500_prcmu_copy_gic_settings function. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_cibool prcmu_pending_irq(void) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci u32 it, im; 11262306a36Sopenharmony_ci int i; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) { 11562306a36Sopenharmony_ci it = readl(PRCM_ARMITVAL31TO0 + i * 4); 11662306a36Sopenharmony_ci im = readl(PRCM_ARMITMSK31TO0 + i * 4); 11762306a36Sopenharmony_ci if (it & im) 11862306a36Sopenharmony_ci return true; /* There is a pending interrupt */ 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return false; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * This function checks if the specified cpu is in WFI. It's usage 12662306a36Sopenharmony_ci * makes sense only if the gic is decoupled with the db8500_prcmu_gic_decouple 12762306a36Sopenharmony_ci * function. Of course passing smp_processor_id() to this function will 12862306a36Sopenharmony_ci * always return false... 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cibool prcmu_is_cpu_in_wfi(int cpu) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci return readl(PRCM_ARM_WFI_STANDBY) & 13362306a36Sopenharmony_ci (cpu ? PRCM_ARM_WFI_STANDBY_WFI1 : PRCM_ARM_WFI_STANDBY_WFI0); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* 13762306a36Sopenharmony_ci * This function copies the gic SPI settings to the prcmu in order to 13862306a36Sopenharmony_ci * monitor them and abort/finish the retention/off sequence or state. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ciint prcmu_copy_gic_settings(void) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci u32 er; /* Enable register */ 14362306a36Sopenharmony_ci int i; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* We skip the STI and PPI */ 14662306a36Sopenharmony_ci for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) { 14762306a36Sopenharmony_ci er = readl_relaxed(dist_base + 14862306a36Sopenharmony_ci GIC_DIST_ENABLE_SET + (i + 1) * 4); 14962306a36Sopenharmony_ci writel(er, PRCM_ARMITMSK31TO0 + i * 4); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND 15662306a36Sopenharmony_cistatic int ux500_suspend_enter(suspend_state_t state) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci cpu_do_idle(); 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int ux500_suspend_valid(suspend_state_t state) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci return state == PM_SUSPEND_MEM || state == PM_SUSPEND_STANDBY; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic const struct platform_suspend_ops ux500_suspend_ops = { 16862306a36Sopenharmony_ci .enter = ux500_suspend_enter, 16962306a36Sopenharmony_ci .valid = ux500_suspend_valid, 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci#define UX500_SUSPEND_OPS (&ux500_suspend_ops) 17262306a36Sopenharmony_ci#else 17362306a36Sopenharmony_ci#define UX500_SUSPEND_OPS NULL 17462306a36Sopenharmony_ci#endif 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_civoid __init ux500_pm_init(u32 phy_base, u32 size) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct device_node *np; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci prcmu_base = ioremap(phy_base, size); 18162306a36Sopenharmony_ci if (!prcmu_base) { 18262306a36Sopenharmony_ci pr_err("could not remap PRCMU for PM functions\n"); 18362306a36Sopenharmony_ci return; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-gic"); 18662306a36Sopenharmony_ci dist_base = of_iomap(np, 0); 18762306a36Sopenharmony_ci of_node_put(np); 18862306a36Sopenharmony_ci if (!dist_base) { 18962306a36Sopenharmony_ci pr_err("could not remap GIC dist base for PM functions\n"); 19062306a36Sopenharmony_ci return; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* 19462306a36Sopenharmony_ci * On watchdog reboot the GIC is in some cases decoupled. 19562306a36Sopenharmony_ci * This will make sure that the GIC is correctly configured. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci prcmu_gic_recouple(); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Set up ux500 suspend callbacks. */ 20062306a36Sopenharmony_ci suspend_set_ops(UX500_SUSPEND_OPS); 20162306a36Sopenharmony_ci} 202