From patchwork Tue Mar 26 15:47:40 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 15669 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 6658A23E2C for ; Tue, 26 Mar 2013 15:48:08 +0000 (UTC) Received: from mail-ve0-f170.google.com (mail-ve0-f170.google.com [209.85.128.170]) by fiordland.canonical.com (Postfix) with ESMTP id 084EFA199E6 for ; Tue, 26 Mar 2013 15:48:07 +0000 (UTC) Received: by mail-ve0-f170.google.com with SMTP id 15so539925vea.1 for ; Tue, 26 Mar 2013 08:48:07 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:x-forwarded-to:x-forwarded-for:delivered-to:x-received :received-spf:from:to:cc:subject:date:message-id:x-mailer :mime-version:content-type:x-gm-message-state; bh=+Ujgjx1u07FOB4njURYGWuZKItx9RffI4EpGaH2CuQo=; b=PdhmBhhDHoqLw/QvjIAGKiUep8hZwDeMKNbPNEkmU8dwkFGFRoZizCQRtCHkSOjO6w eCDrElHJ0SAQn0/B/cFN2Xs2IyyN+oc0O4I6FhtzzOFPx9awxy+hNMlsAcWKEZHg4QYZ YnHbZfUeyBWy8ar0Bbi3aElDzz14ejQsVRgFZBZ/SJK6xOQz9i5gFHWksJDfuL1c6Ipa 6VWlIB4ikExN7EdPcR3GC4GgaPzqML2c7qn5fMc0lflEVqWqzKCTa2wrHfdJCAtnsCc7 5dAMbBdvDTCe45IIt0AWwIKOVin3Pwi4qvORDVGUael2kYW2xxWSTxIYScUzpP3ROYcX 8X1w== X-Received: by 10.220.150.74 with SMTP id x10mr19851908vcv.68.1364312887489; Tue, 26 Mar 2013 08:48:07 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.59.4.204 with SMTP id cg12csp77315ved; Tue, 26 Mar 2013 08:48:06 -0700 (PDT) X-Received: by 10.14.202.71 with SMTP id c47mr45712456eeo.39.1364312885659; Tue, 26 Mar 2013 08:48:05 -0700 (PDT) Received: from eu1sys200aog104.obsmtp.com (eu1sys200aog104.obsmtp.com [207.126.144.117]) by mx.google.com with SMTP id a1si27068017eez.225.2013.03.26.08.47.56 (version=TLSv1 cipher=RC4-SHA bits=128/128); Tue, 26 Mar 2013 08:48:04 -0700 (PDT) Received-SPF: neutral (google.com: 207.126.144.117 is neither permitted nor denied by best guess record for domain of linus.walleij@stericsson.com) client-ip=207.126.144.117; Authentication-Results: mx.google.com; spf=neutral (google.com: 207.126.144.117 is neither permitted nor denied by best guess record for domain of linus.walleij@stericsson.com) smtp.mail=linus.walleij@stericsson.com Received: from beta.dmz-ap.st.com ([138.198.100.35]) (using TLSv1) by eu1sys200aob104.postini.com ([207.126.147.11]) with SMTP ID DSNKUVHDKr9ArshR78rAOptjox/vwOa6m4lp@postini.com; Tue, 26 Mar 2013 15:48:03 UTC Received: from zeta.dmz-ap.st.com (ns6.st.com [138.198.234.13]) by beta.dmz-ap.st.com (STMicroelectronics) with ESMTP id 6A2D3DB; Tue, 26 Mar 2013 15:39:42 +0000 (GMT) Received: from relay1.stm.gmessaging.net (unknown [10.230.100.17]) by zeta.dmz-ap.st.com (STMicroelectronics) with ESMTP id 802289EB; Tue, 26 Mar 2013 15:47:48 +0000 (GMT) Received: from exdcvycastm022.EQ1STM.local (alteon-source-exch [10.230.100.61]) (using TLSv1 with cipher RC4-MD5 (128/128 bits)) (Client CN "exdcvycastm022", Issuer "exdcvycastm022" (not verified)) by relay1.stm.gmessaging.net (Postfix) with ESMTPS id 0079524C07C; Tue, 26 Mar 2013 16:47:40 +0100 (CET) Received: from steludxu4075.lud.stericsson.com (10.230.100.153) by smtp.stericsson.com (10.230.100.30) with Microsoft SMTP Server (TLS) id 8.3.279.5; Tue, 26 Mar 2013 16:47:47 +0100 From: Linus Walleij To: , Arnd Bergmann Cc: Linus Walleij , Daniel Lezcano , Rickard Andersson , Samuel Ortiz Subject: [PATCH] ARM: ux500: move PM-related PRCMU functions to machine Date: Tue, 26 Mar 2013 16:47:40 +0100 Message-ID: <1364312860-8179-1-git-send-email-linus.walleij@stericsson.com> X-Mailer: git-send-email 1.7.11.3 MIME-Version: 1.0 X-Gm-Message-State: ALoCoQkCRUkPEHuaHfLjjQ7ouCE0nQmZfv9YD/8bt+YjikQ0X/i7YQ7GHqpb584ZGRyF/eigc9us From: Linus Walleij We are trying to decompose and decentralize the code in the DB8500 PRCMU out into subdrivers. The code moved in this patch concerns a group of functions used for decoupling and recoupling the IRQs from the GIC. During sleep and idle the Ux500 system will transfer all IRQ handling to the PRCMU using these functions. Basically we are left with the two alternatives of code placement as: - arch/arm/mach-ux500/pm.c - this because the code is closely related to the GIC, and takes ownership of some of the registers from the PRCMU related to this PM functionality. - drivers/mfd/db8500-prcmu-pm.c - because the code is affecting stuff in the PRCMU register range. But then this code needs to remap and handle GIC registers. This patch implementation is taking the first approach. Currently the cpuidle driver is the only piece of code using this set of functions, but it will later also be used by the suspend/resume code which is currently under review. The header file is moved to: The function prototypes need to be placed in a globally visible header since the CPUidle code is planned to move out to drivers/cpuidle. Cc: Daniel Lezcano Cc: Rickard Andersson Cc: Samuel Ortiz Signed-off-by: Linus Walleij --- As things got silent in the last discussion thread I'm posting one solution to this dilemma. If you have better ideas, tell me. Sam, I'm requesting an ACK for taking this through the ARM SoC tree. --- arch/arm/mach-ux500/Makefile | 2 +- arch/arm/mach-ux500/cpu.c | 4 + arch/arm/mach-ux500/cpuidle.c | 3 +- arch/arm/mach-ux500/pm.c | 167 +++++++++++++++++++++++++++++ drivers/mfd/db8500-prcmu.c | 114 -------------------- include/linux/mfd/db8500-prcmu.h | 6 -- include/linux/mfd/dbx500-prcmu.h | 30 ------ include/linux/platform_data/arm-ux500-pm.h | 21 ++++ 8 files changed, 195 insertions(+), 152 deletions(-) create mode 100644 arch/arm/mach-ux500/pm.c create mode 100644 include/linux/platform_data/arm-ux500-pm.h diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile index f24710d..580a4db 100644 --- a/arch/arm/mach-ux500/Makefile +++ b/arch/arm/mach-ux500/Makefile @@ -3,7 +3,7 @@ # obj-y := cpu.o devices.o devices-common.o \ - id.o usb.o timer.o + id.o usb.o timer.o pm.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o devices-db8500.o diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c index 38459e9..ee69439d 100644 --- a/arch/arm/mach-ux500/cpu.c +++ b/arch/arm/mach-ux500/cpu.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -68,16 +69,19 @@ void __init ux500_init_irq(void) */ if (cpu_is_u8500_family()) { prcmu_early_init(U8500_PRCMU_BASE, SZ_8K - 1); + ux500_pm_init(U8500_PRCMU_BASE, SZ_8K - 1); u8500_clk_init(U8500_CLKRST1_BASE, U8500_CLKRST2_BASE, U8500_CLKRST3_BASE, U8500_CLKRST5_BASE, U8500_CLKRST6_BASE); } else if (cpu_is_u9540()) { prcmu_early_init(U8500_PRCMU_BASE, SZ_8K - 1); + ux500_pm_init(U8500_PRCMU_BASE, SZ_8K - 1); u8500_clk_init(U8500_CLKRST1_BASE, U8500_CLKRST2_BASE, U8500_CLKRST3_BASE, U8500_CLKRST5_BASE, U8500_CLKRST6_BASE); } else if (cpu_is_u8540()) { prcmu_early_init(U8500_PRCMU_BASE, SZ_8K + SZ_4K - 1); + ux500_pm_init(U8500_PRCMU_BASE, SZ_8K + SZ_4K - 1); u8540_clk_init(); } } diff --git a/arch/arm/mach-ux500/cpuidle.c b/arch/arm/mach-ux500/cpuidle.c index ce91493..1e5bb66 100644 --- a/arch/arm/mach-ux500/cpuidle.c +++ b/arch/arm/mach-ux500/cpuidle.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -130,7 +131,7 @@ int __init ux500_idle_init(void) int ret, cpu; struct cpuidle_device *device; - /* Configure wake up reasons */ + /* Configure wake up reasons */ prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) | PRCMU_WAKEUP(ABB)); diff --git a/arch/arm/mach-ux500/pm.c b/arch/arm/mach-ux500/pm.c new file mode 100644 index 0000000..6949a13 --- /dev/null +++ b/arch/arm/mach-ux500/pm.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) ST-Ericsson SA 2010-2013 + * Author: Rickard Andersson for + * ST-Ericsson. + * Author: Daniel Lezcano for Linaro. + * License terms: GNU General Public License (GPL) version 2 + * + */ + +#include +#include +#include +#include +#include + +#include + +/* 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; + +/* 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 */ + void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE); + 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) +{ + return readl(PRCM_ARM_WFI_STANDBY) & cpu ? PRCM_ARM_WFI_STANDBY_WFI1 : + PRCM_ARM_WFI_STANDBY_WFI0; +} + +/* + * 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 */ + void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE); + 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; +} + +void __init ux500_pm_init(u32 phy_base, u32 size) +{ + prcmu_base = ioremap(phy_base, size); + if (!prcmu_base) { + pr_err("could not remap PRCMU for PM functions\n"); + return; + } + /* + * On watchdog reboot the GIC is in some cases decoupled. + * This will make sure that the GIC is correctly configured. + */ + prcmu_gic_recouple(); +} diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index acb8ef7..2d3a5f6 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -794,119 +793,6 @@ u8 db8500_prcmu_get_power_state_result(void) return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS); } -/* This function decouple the gic from the prcmu */ -int db8500_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 db8500_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 db8500_prcmu_gic_pending_irq(void) -{ - u32 pr; /* Pending register */ - u32 er; /* Enable register */ - void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE); - 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 db8500_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 db8500_prcmu_is_cpu_in_wfi(int cpu) -{ - return readl(PRCM_ARM_WFI_STANDBY) & cpu ? PRCM_ARM_WFI_STANDBY_WFI1 : - PRCM_ARM_WFI_STANDBY_WFI0; -} - -/* - * 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 db8500_prcmu_copy_gic_settings(void) -{ - u32 er; /* Enable register */ - void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE); - 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; -} - /* This function should only be called while mb0_transfer.lock is held. */ static void config_wakeups(void) { diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h index ac943df..0bd6944 100644 --- a/include/linux/mfd/db8500-prcmu.h +++ b/include/linux/mfd/db8500-prcmu.h @@ -522,12 +522,6 @@ int db8500_prcmu_load_a9wdog(u8 id, u32 val); void db8500_prcmu_system_reset(u16 reset_code); int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll); u8 db8500_prcmu_get_power_state_result(void); -int db8500_prcmu_gic_decouple(void); -int db8500_prcmu_gic_recouple(void); -int db8500_prcmu_copy_gic_settings(void); -bool db8500_prcmu_gic_pending_irq(void); -bool db8500_prcmu_pending_irq(void); -bool db8500_prcmu_is_cpu_in_wfi(int cpu); void db8500_prcmu_enable_wakeups(u32 wakeups); int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state); int db8500_prcmu_request_clock(u8 clock, bool enable); diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index 8c546cb..fc43cec 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -293,36 +293,6 @@ static inline u8 prcmu_get_power_state_result(void) return db8500_prcmu_get_power_state_result(); } -static inline int prcmu_gic_decouple(void) -{ - return db8500_prcmu_gic_decouple(); -} - -static inline int prcmu_gic_recouple(void) -{ - return db8500_prcmu_gic_recouple(); -} - -static inline bool prcmu_gic_pending_irq(void) -{ - return db8500_prcmu_gic_pending_irq(); -} - -static inline bool prcmu_is_cpu_in_wfi(int cpu) -{ - return db8500_prcmu_is_cpu_in_wfi(cpu); -} - -static inline int prcmu_copy_gic_settings(void) -{ - return db8500_prcmu_copy_gic_settings(); -} - -static inline bool prcmu_pending_irq(void) -{ - return db8500_prcmu_pending_irq(); -} - static inline int prcmu_set_epod(u16 epod_id, u8 epod_state) { return db8500_prcmu_set_epod(epod_id, epod_state); diff --git a/include/linux/platform_data/arm-ux500-pm.h b/include/linux/platform_data/arm-ux500-pm.h new file mode 100644 index 0000000..8dff64b --- /dev/null +++ b/include/linux/platform_data/arm-ux500-pm.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) ST-Ericsson SA 2010-2013 + * Author: Rickard Andersson for + * ST-Ericsson. + * Author: Daniel Lezcano for Linaro. + * License terms: GNU General Public License (GPL) version 2 + * + */ + +#ifndef ARM_UX500_PM_H +#define ARM_UX500_PM_H + +int prcmu_gic_decouple(void); +int prcmu_gic_recouple(void); +bool prcmu_gic_pending_irq(void); +bool prcmu_pending_irq(void); +bool prcmu_is_cpu_in_wfi(int cpu); +int prcmu_copy_gic_settings(void); +void ux500_pm_init(u32 phy_base, u32 size); + +#endif /* ARM_UX500_PM_H */