From patchwork Tue Jul 16 16:05:43 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Pieralisi X-Patchwork-Id: 2828186 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 25E439F967 for ; Tue, 16 Jul 2013 16:07:38 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 60FAB2022D for ; Tue, 16 Jul 2013 16:07:36 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 08C752022A for ; Tue, 16 Jul 2013 16:07:34 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Uz7lz-0004I5-Lk; Tue, 16 Jul 2013 16:06:44 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Uz7ll-0000Yf-PV; Tue, 16 Jul 2013 16:06:29 +0000 Received: from service87.mimecast.com ([91.220.42.44]) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Uz7lY-0000Wo-Ui for linux-arm-kernel@lists.infradead.org; Tue, 16 Jul 2013 16:06:19 +0000 Received: from cam-owa2.Emea.Arm.com (fw-tnat.cambridge.arm.com [217.140.96.21]) by service87.mimecast.com; Tue, 16 Jul 2013 17:05:52 +0100 Received: from e102568-lin.cambridge.arm.com ([10.1.255.212]) by cam-owa2.Emea.Arm.com with Microsoft SMTPSVC(6.0.3790.3959); Tue, 16 Jul 2013 17:05:51 +0100 From: Lorenzo Pieralisi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree-discuss@lists.ozlabs.org Subject: [RFC PATCH v5 1/1] drivers: mfd: vexpress: add Serial Power Controller (SPC) support Date: Tue, 16 Jul 2013 17:05:43 +0100 Message-Id: <1373990743-23106-2-git-send-email-lorenzo.pieralisi@arm.com> X-Mailer: git-send-email 1.8.2.2 In-Reply-To: <1373990743-23106-1-git-send-email-lorenzo.pieralisi@arm.com> References: <1373990743-23106-1-git-send-email-lorenzo.pieralisi@arm.com> X-OriginalArrivalTime: 16 Jul 2013 16:05:51.0676 (UTC) FILETIME=[5806FFC0:01CE823E] X-MC-Unique: 113071617055207201 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130716_120617_376507_30AEB350 X-CRM114-Status: GOOD ( 25.38 ) X-Spam-Score: -2.6 (--) Cc: Jon Medhurst , Nicolas Pitre , Lorenzo Pieralisi , Samuel Ortiz , Pawel Moll , Sudeep KarkadaNagesha , Achin Gupta , Amit Kucheria , Olof Johansson X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The TC2 versatile express core tile integrates a logic block that provides the interface between the dual cluster test-chip and the M3 microcontroller that carries out power management. The logic block, called Serial Power Controller (SPC), contains several memory mapped registers to control among other things low-power states, wake-up irqs and per-CPU jump addresses registers. This patch provides a driver that enables run-time control of features implemented by the SPC power management control logic. The SPC control logic is required to be programmed very early in the boot process to reset secondary CPUs on the TC2 testchip, set-up jump addresses and wake-up IRQs for power management. Hence, waiting for core changes to be made in the device core code to enable early registration of platform devices, the driver puts in place an early init scheme that allows kernel drivers to initialize the SPC driver directly from the components requiring it, if their initialization routine is called before the driver init function by the boot process. Device tree bindings documentation for the SPC component is provided with the patchset. Cc: Samuel Ortiz Cc: Olof Johansson Cc: Pawel Moll Cc: Amit Kucheria Cc: Jon Medhurst Signed-off-by: Achin Gupta Signed-off-by: Lorenzo Pieralisi Signed-off-by: Sudeep KarkadaNagesha --- Documentation/devicetree/bindings/mfd/vexpress-spc.txt | 36 ++ drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/vexpress-spc.c | 253 ++++++++++ include/linux/vexpress.h | 17 + 5 files changed, 317 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/vexpress-spc.txt b/Documentation/devicetree/bindings/mfd/vexpress-spc.txt new file mode 100644 index 0000000..1614725 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/vexpress-spc.txt @@ -0,0 +1,36 @@ +* ARM Versatile Express Serial Power Controller device tree bindings + +Latest ARM development boards implement a power management interface (serial +power controller - SPC) that is capable of managing power states transitions, +wake-up IRQs and resume addresses for ARM multiprocessor testchips. +The serial controller can be programmed through a memory mapped interface +that enables communication between firmware running on the microcontroller +managing power states and the application processors. + +The SPC DT bindings are defined as follows: + +- spc node + + - compatible: + Usage: required + Value type: + Definition: must be + "arm,vexpress-spc,v2p-ca15_a7", "arm,vexpress-spc" + - reg: + Usage: required + Value type: + Definition: A standard property that specifies the base address + and the size of the SPC address space + - interrupts: + Usage: required + Value type: + Definition: SPC interrupt configuration. A standard property + that follows ePAPR interrupts specifications + +Example: + +spc: spc@7fff0000 { + compatible = "arm,vexpress-spc,v2p-ca15_a7", "arm,vexpress-spc"; + reg = <0x7fff0000 0x1000>; + interrupts = <0 95 4>; +}; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6959b8d..ebd23f4 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1149,3 +1149,13 @@ config VEXPRESS_CONFIG help Platform configuration infrastructure for the ARM Ltd. Versatile Express. + +config VEXPRESS_SPC + bool "Versatile Express SPC driver support" + depends on ARM + help + The Serial Power Controller (SPC) for ARM Ltd. test chips, is + an IP that provides a memory mapped interface to power controller + HW. The driver provides an API abstraction allowing to program + registers controlling low-level power management features like power + down flags, global and per-cpu wake-up IRQs. diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 718e94a..3a01203 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -153,5 +153,6 @@ obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o +obj-$(CONFIG_VEXPRESS_SPC) += vexpress-spc.o obj-$(CONFIG_MFD_RETU) += retu-mfd.o obj-$(CONFIG_MFD_AS3711) += as3711.o diff --git a/drivers/mfd/vexpress-spc.c b/drivers/mfd/vexpress-spc.c new file mode 100644 index 0000000..aa8c2a4 --- /dev/null +++ b/drivers/mfd/vexpress-spc.c @@ -0,0 +1,253 @@ +/* + * Versatile Express Serial Power Controller (SPC) support + * + * Copyright (C) 2013 ARM Ltd. + * + * Authors: Sudeep KarkadaNagesha + * Achin Gupta + * Lorenzo Pieralisi + * + * 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define SPCLOG "vexpress-spc: " + +/* SCC conf registers */ +#define A15_CONF 0x400 +#define SYS_INFO 0x700 + +/* SPC registers base */ +#define SPC_BASE 0xB00 + +/* SPC wake-up IRQs status and mask */ +#define WAKE_INT_MASK (SPC_BASE + 0x24) +#define WAKE_INT_RAW (SPC_BASE + 0x28) +#define WAKE_INT_STAT (SPC_BASE + 0x2c) +/* SPC power down registers */ +#define A15_PWRDN_EN (SPC_BASE + 0x30) +#define A7_PWRDN_EN (SPC_BASE + 0x34) +/* SPC per-CPU mailboxes */ +#define A15_BX_ADDR0 (SPC_BASE + 0x68) +#define A7_BX_ADDR0 (SPC_BASE + 0x78) + +/* wake-up interrupt masks */ +#define GBL_WAKEUP_INT_MSK (0x3 << 10) + +/* TC2 static dual-cluster configuration */ +#define MAX_CLUSTERS 2 + +struct ve_spc_drvdata { + void __iomem *baseaddr; + /* + * A15s cluster identifier + * It corresponds to A15 processors MPIDR[15:8] bitfield + */ + u32 a15_clusid; +}; + +static struct ve_spc_drvdata *info; + +static inline bool cluster_is_a15(u32 cluster) +{ + return cluster == info->a15_clusid; +} + +/** + * ve_spc_global_wakeup_irq() + * + * Function to set/clear global wakeup IRQs. Not protected by locking since + * it might be used in code paths where normal cacheable locks are not + * working. Locking must be provided by the caller to ensure atomicity. + * + * @set: if true, global wake-up IRQs are set, if false they are cleared + */ +void ve_spc_global_wakeup_irq(bool set) +{ + u32 reg; + + reg = readl_relaxed(info->baseaddr + WAKE_INT_MASK); + + if (set) + reg |= GBL_WAKEUP_INT_MSK; + else + reg &= ~GBL_WAKEUP_INT_MSK; + + writel_relaxed(reg, info->baseaddr + WAKE_INT_MASK); +} + +/** + * ve_spc_cpu_wakeup_irq() + * + * Function to set/clear per-CPU wake-up IRQs. Not protected by locking since + * it might be used in code paths where normal cacheable locks are not + * working. Locking must be provided by the caller to ensure atomicity. + * + * @cluster: mpidr[15:8] bitfield describing cluster affinity level + * @cpu: mpidr[7:0] bitfield describing cpu affinity level + * @set: if true, wake-up IRQs are set, if false they are cleared + */ +void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set) +{ + u32 mask, reg; + + if (cluster >= MAX_CLUSTERS) + return; + + mask = 1 << cpu; + + if (!cluster_is_a15(cluster)) + mask <<= 4; + + reg = readl_relaxed(info->baseaddr + WAKE_INT_MASK); + + if (set) + reg |= mask; + else + reg &= ~mask; + + writel_relaxed(reg, info->baseaddr + WAKE_INT_MASK); +} + +/** + * ve_spc_set_resume_addr() - set the jump address used for warm boot + * + * @cluster: mpidr[15:8] bitfield describing cluster affinity level + * @cpu: mpidr[7:0] bitfield describing cpu affinity level + * @addr: physical resume address + */ +void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr) +{ + void __iomem *baseaddr; + + if (cluster >= MAX_CLUSTERS) + return; + + if (cluster_is_a15(cluster)) + baseaddr = info->baseaddr + A15_BX_ADDR0 + (cpu << 2); + else + baseaddr = info->baseaddr + A7_BX_ADDR0 + (cpu << 2); + + writel_relaxed(addr, baseaddr); +} + +/** + * ve_spc_get_nr_cpus() - get number of cpus in a cluster + * + * @cluster: mpidr[15:8] bitfield describing cluster affinity level + * + * Return: > 0 number of cpus in the cluster + * or 0 if cluster number invalid + */ +u32 ve_spc_get_nr_cpus(u32 cluster) +{ + u32 val; + + if (cluster >= MAX_CLUSTERS) + return 0; + + val = readl_relaxed(info->baseaddr + SYS_INFO); + val = cluster_is_a15(cluster) ? (val >> 16) : (val >> 20); + return val & 0xf; +} + +/** + * ve_spc_powerdown() + * + * Function to enable/disable cluster powerdown. Not protected by locking + * since it might be used in code paths where normal cacheable locks are not + * working. Locking must be provided by the caller to ensure atomicity. + * + * @cluster: mpidr[15:8] bitfield describing cluster affinity level + * @enable: if true enables powerdown, if false disables it + */ +void ve_spc_powerdown(u32 cluster, bool enable) +{ + u32 pwdrn_reg; + + if (cluster >= MAX_CLUSTERS) + return; + + pwdrn_reg = cluster_is_a15(cluster) ? A15_PWRDN_EN : A7_PWRDN_EN; + writel_relaxed(enable, info->baseaddr + pwdrn_reg); +} + +static const struct of_device_id ve_spc_ids[] __initconst = { + { .compatible = "arm,vexpress-spc,v2p-ca15_a7" }, + { .compatible = "arm,vexpress-spc" }, + {}, +}; + +static int __init ve_spc_probe(void) +{ + int ret; + struct device_node *node = of_find_matching_node(NULL, ve_spc_ids); + + if (!node) + return -ENODEV; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + pr_err(SPCLOG "unable to allocate mem\n"); + return -ENOMEM; + } + + info->baseaddr = of_iomap(node, 0); + if (!info->baseaddr) { + pr_err(SPCLOG "unable to ioremap memory\n"); + ret = -ENXIO; + goto mem_free; + } + + info->a15_clusid = readl_relaxed(info->baseaddr + A15_CONF) & 0xf; + + /* + * Multi-cluster systems may need this data when non-coherent, during + * cluster power-up/power-down. Make sure driver info reaches main + * memory. + */ + sync_cache_w(info); + sync_cache_w(&info); + pr_info("vexpress-spc loaded at %p\n", info->baseaddr); + return 0; + +mem_free: + kfree(info); + return ret; +} + +/** + * ve_spc_init() + * + * Function exported to manage pre early_initcall initialization. + * SPC code is needed very early in the boot process to bring CPUs out of + * reset and initialize power management back-end so an init interface is + * provided to platform code to allow early initialization. The init + * interface can be removed as soon as the DT layer and platform bus allow + * platform device creation and probing before SMP boot. + */ +int __init ve_spc_init(void) +{ + static int ve_spc_init_status __initdata = -EAGAIN; + + if (ve_spc_init_status == -EAGAIN) + ve_spc_init_status = ve_spc_probe(); + + return ve_spc_init_status; +} +early_initcall(ve_spc_init); diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h index 50368e0..d0106ef 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -132,4 +132,21 @@ void vexpress_clk_of_register_spc(void); void vexpress_clk_init(void __iomem *sp810_base); void vexpress_clk_of_init(void); +/* SPC */ + +#ifdef CONFIG_VEXPRESS_SPC +int __init ve_spc_init(void); +void ve_spc_global_wakeup_irq(bool set); +void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set); +void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr); +u32 ve_spc_get_nr_cpus(u32 cluster); +void ve_spc_powerdown(u32 cluster, bool enable); +#else +static inline int ve_spc_init(void) { return -ENODEV; } +static inline void ve_spc_global_wakeup_irq(bool set) { } +static inline void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set) { } +static inline void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr) { } +static inline u32 ve_spc_get_nr_cpus(u32 cluster) { return 0; } +static inline void ve_spc_powerdown(u32 cluster, bool enable) { } +#endif #endif