From patchwork Fri Dec 29 10:55:02 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Mylene Josserand X-Patchwork-Id: 853864 Return-Path: X-Original-To: incoming-imx@patchwork.ozlabs.org Delivered-To: patchwork-incoming-imx@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=65.50.211.133; helo=bombadil.infradead.org; envelope-from=linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="R0QJ27wA"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3z7Nxm6QkQz9s74 for ; Fri, 29 Dec 2017 22:03:56 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=oCv/oyPKvlewYdX4ukBe8SYT+r6wouN15okd4qQgrOo=; b=R0QJ27wA1EpymR N7enwk/f6aRaeSSw5pSw4fp+M0B+xGt0kg20GT4dglJgBmjFqmC578TufIaKlI5wTqtpNLITsS/jU ieWx/fbKkDfXiMLaByWrQg0z8PXKtHIAruoKdHuf7OC7EewgbTQVePL3Uwpr+94Qlcu7ZRYOCtI1I Xj7/eg6dvmBTdRlxWyB7C3QnF2rJVUHwhcVPQnFjxiBdWX2cXF6nCtaSHQBd8yHNIzxRcpPhmnuZh qXTR1LiATfCVc7dltoMSGWeog3mWqWZ5NffPCMQX3ebcgd4bDOs0b8E+dj2jGzec1qqsKwmqFY495 ObVPB3nvGAmbm6I3yTuQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1eUsS8-0003Xl-Kp; Fri, 29 Dec 2017 11:03:52 +0000 Received: from mail.free-electrons.com ([62.4.15.54]) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1eUsKH-0006w8-63 for linux-arm-kernel@lists.infradead.org; Fri, 29 Dec 2017 10:55:50 +0000 Received: by mail.free-electrons.com (Postfix, from userid 110) id 88CC3207A3; Fri, 29 Dec 2017 11:55:31 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT, URIBL_BLOCKED shortcircuit=ham autolearn=disabled version=3.4.0 Received: from dell-desktop.lan (LStLambert-657-1-97-87.w90-63.abo.wanadoo.fr [90.63.216.87]) by mail.free-electrons.com (Postfix) with ESMTPSA id 2C91520731; Fri, 29 Dec 2017 11:55:21 +0100 (CET) From: =?utf-8?q?Myl=C3=A8ne_Josserand?= To: maxime.ripard@free-electrons.com, wens@csie.org, linux@armlinux.org.uk, robh+dt@kernel.org, mark.rutland@arm.com Subject: [PATCH v2 1/5] ARM: sun9i: Support SMP on A80 with Multi-Cluster Power Management (MCPM) Date: Fri, 29 Dec 2017 11:55:02 +0100 Message-Id: <20171229105506.24851-2-mylene.josserand@free-electrons.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171229105506.24851-1-mylene.josserand@free-electrons.com> References: <20171229105506.24851-1-mylene.josserand@free-electrons.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20171229_025545_613192_2A32D0AD X-CRM114-Status: GOOD ( 23.97 ) X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: thomas.petazzoni@free-electrons.com, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, quentin.schulz@free-electrons.com, clabbe.montjoie@gmail.com, mylene.josserand@free-electrons.com, linux-arm-kernel@lists.infradead.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org List-Id: linux-imx-kernel.lists.patchwork.ozlabs.org From: Chen-Yu Tsai The A80 is a big.LITTLE SoC with 1 cluster of 4 Cortex-A7s and 1 cluster of 4 Cortex-A15s. This patch adds support to bring up the second cluster and thus all cores using the common MCPM code. Core/cluster power down has not been implemented, thus CPU hotplugging and big.LITTLE switcher is not supported. Signed-off-by: Chen-Yu Tsai Signed-off-by: Mylène Josserand --- arch/arm/mach-sunxi/Kconfig | 10 ++ arch/arm/mach-sunxi/Makefile | 1 + arch/arm/mach-sunxi/mcpm.c | 391 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 402 insertions(+) create mode 100644 arch/arm/mach-sunxi/mcpm.c diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 58153cdf025b..177380548d99 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -47,5 +47,15 @@ config MACH_SUN9I bool "Allwinner (sun9i) SoCs support" default ARCH_SUNXI select ARM_GIC + imply MCPM + +config SUN9I_A80_MCPM + bool "Allwinner A80 Multi-Cluster PM support" + depends on MCPM && MACH_SUN9I + default MACH_SUN9I + select ARM_CCI400_PORT_CTRL + help + This is needed to provide CPU and cluster power management + on Allwinner A80 implementing big.LITTLE. endif diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile index 27b168f121a1..e8558912c714 100644 --- a/arch/arm/mach-sunxi/Makefile +++ b/arch/arm/mach-sunxi/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_ARCH_SUNXI) += sunxi.o obj-$(CONFIG_SMP) += platsmp.o +obj-$(CONFIG_SUN9I_A80_MCPM) += mcpm.o diff --git a/arch/arm/mach-sunxi/mcpm.c b/arch/arm/mach-sunxi/mcpm.c new file mode 100644 index 000000000000..4b6e1d6ae379 --- /dev/null +++ b/arch/arm/mach-sunxi/mcpm.c @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2015 Chen-Yu Tsai + * + * Chen-Yu Tsai + * + * arch/arm/mach-sunxi/mcpm.c + * + * Based on arch/arm/mach-exynos/mcpm-exynos.c and Allwinner code + * + * 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 +#include +#include +#include + +#include +#include +#include + +#define SUNXI_CPUS_PER_CLUSTER 4 +#define SUNXI_NR_CLUSTERS 2 + +#define SUN9I_A80_A15_CLUSTER 1 + +#define CPUCFG_CX_CTRL_REG0(c) (0x10 * (c)) +#define CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE(n) BIT(n) +#define CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE_ALL 0xf +#define CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A7 BIT(4) +#define CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A15 BIT(0) +#define CPUCFG_CX_CTRL_REG1(c) (0x10 * (c) + 0x4) +#define CPUCFG_CX_CTRL_REG1_ACINACTM BIT(0) +#define CPUCFG_CX_RST_CTRL(c) (0x80 + 0x4 * (c)) +#define CPUCFG_CX_RST_CTRL_DBG_SOC_RST BIT(24) +#define CPUCFG_CX_RST_CTRL_ETM_RST(n) BIT(20 + (n)) +#define CPUCFG_CX_RST_CTRL_ETM_RST_ALL (0xf << 20) +#define CPUCFG_CX_RST_CTRL_DBG_RST(n) BIT(16 + (n)) +#define CPUCFG_CX_RST_CTRL_DBG_RST_ALL (0xf << 16) +#define CPUCFG_CX_RST_CTRL_H_RST BIT(12) +#define CPUCFG_CX_RST_CTRL_L2_RST BIT(8) +#define CPUCFG_CX_RST_CTRL_CX_RST(n) BIT(4 + (n)) +#define CPUCFG_CX_RST_CTRL_CORE_RST(n) BIT(n) + +#define PRCM_CPU_PO_RST_CTRL(c) (0x4 + 0x4 * (c)) +#define PRCM_CPU_PO_RST_CTRL_CORE(n) BIT(n) +#define PRCM_CPU_PO_RST_CTRL_CORE_ALL 0xf +#define PRCM_PWROFF_GATING_REG(c) (0x100 + 0x4 * (c)) +#define PRCM_PWROFF_GATING_REG_CLUSTER BIT(4) +#define PRCM_PWROFF_GATING_REG_CORE(n) BIT(n) +#define PRCM_PWR_SWITCH_REG(c, cpu) (0x140 + 0x10 * (c) + 0x4 * (cpu)) +#define PRCM_CPU_SOFT_ENTRY_REG 0x164 + +static void __iomem *cpucfg_base; +static void __iomem *prcm_base; + +static int sunxi_cpu_power_switch_set(unsigned int cpu, unsigned int cluster, + bool enable) +{ + u32 reg; + + /* control sequence from Allwinner A80 user manual v1.2 PRCM section */ + reg = readl(prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu)); + if (enable) { + if (reg == 0x00) { + pr_debug("power clamp for cluster %u cpu %u already open\n", + cluster, cpu); + return 0; + } + + writel(0xff, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu)); + udelay(10); + writel(0xfe, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu)); + udelay(10); + writel(0xf8, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu)); + udelay(10); + writel(0xf0, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu)); + udelay(10); + writel(0x00, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu)); + udelay(10); + } else { + writel(0xff, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu)); + udelay(10); + } + + return 0; +} + +static int sunxi_cpu_powerup(unsigned int cpu, unsigned int cluster) +{ + u32 reg; + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + if (cpu >= SUNXI_CPUS_PER_CLUSTER || cluster >= SUNXI_NR_CLUSTERS) + return -EINVAL; + + /* assert processor power-on reset */ + reg = readl(prcm_base + PRCM_CPU_PO_RST_CTRL(cluster)); + reg &= ~PRCM_CPU_PO_RST_CTRL_CORE(cpu); + writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster)); + + /* Cortex-A7: hold L1 reset disable signal low */ + if (!(of_machine_is_compatible("allwinner,sun9i-a80") && + cluster == SUN9I_A80_A15_CLUSTER)) { + reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster)); + reg &= ~CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE(cpu); + writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster)); + } + + /* assert processor related resets */ + reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster)); + reg &= ~CPUCFG_CX_RST_CTRL_DBG_RST(cpu); + + /* + * Allwinner code also asserts resets for NEON on A15. According + * to ARM manuals, asserting power-on reset is sufficient. + */ + if (!(of_machine_is_compatible("allwinner,sun9i-a80") && + cluster == SUN9I_A80_A15_CLUSTER)) { + reg &= ~CPUCFG_CX_RST_CTRL_ETM_RST(cpu); + } + writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster)); + + /* open power switch */ + sunxi_cpu_power_switch_set(cpu, cluster, true); + + /* clear processor power gate */ + reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster)); + reg &= ~PRCM_PWROFF_GATING_REG_CORE(cpu); + writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster)); + udelay(20); + + /* de-assert processor power-on reset */ + reg = readl(prcm_base + PRCM_CPU_PO_RST_CTRL(cluster)); + reg |= PRCM_CPU_PO_RST_CTRL_CORE(cpu); + writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster)); + + /* de-assert all processor resets */ + reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster)); + reg |= CPUCFG_CX_RST_CTRL_DBG_RST(cpu); + reg |= CPUCFG_CX_RST_CTRL_CORE_RST(cpu); + if (!(of_machine_is_compatible("allwinner,sun9i-a80") && + cluster == SUN9I_A80_A15_CLUSTER)) { + reg |= CPUCFG_CX_RST_CTRL_ETM_RST(cpu); + } else { + reg |= CPUCFG_CX_RST_CTRL_CX_RST(cpu); /* NEON */ + } + writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster)); + + return 0; +} + +static int sunxi_cluster_powerup(unsigned int cluster) +{ + u32 reg; + + pr_debug("%s: cluster %u\n", __func__, cluster); + if (cluster >= SUNXI_NR_CLUSTERS) + return -EINVAL; + + /* assert ACINACTM */ + reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster)); + reg |= CPUCFG_CX_CTRL_REG1_ACINACTM; + writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster)); + + /* assert cluster processor power-on resets */ + reg = readl(prcm_base + PRCM_CPU_PO_RST_CTRL(cluster)); + reg &= ~PRCM_CPU_PO_RST_CTRL_CORE_ALL; + writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster)); + + /* assert cluster resets */ + reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster)); + reg &= ~CPUCFG_CX_RST_CTRL_DBG_SOC_RST; + reg &= ~CPUCFG_CX_RST_CTRL_DBG_RST_ALL; + reg &= ~CPUCFG_CX_RST_CTRL_H_RST; + reg &= ~CPUCFG_CX_RST_CTRL_L2_RST; + + /* + * Allwinner code also asserts resets for NEON on A15. According + * to ARM manuals, asserting power-on reset is sufficient. + */ + if (!(of_machine_is_compatible("allwinner,sun9i-a80") && + cluster == SUN9I_A80_A15_CLUSTER)) { + reg &= ~CPUCFG_CX_RST_CTRL_ETM_RST_ALL; + } + writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster)); + + /* hold L1/L2 reset disable signals low */ + reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster)); + if (of_machine_is_compatible("allwinner,sun9i-a80") && + cluster == SUN9I_A80_A15_CLUSTER) { + /* Cortex-A15: hold L2RSTDISABLE low */ + reg &= ~CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A15; + } else { + /* Cortex-A7: hold L1RSTDISABLE and L2RSTDISABLE low */ + reg &= ~CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE_ALL; + reg &= ~CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A7; + } + writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster)); + + /* clear cluster power gate */ + reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster)); + reg &= ~PRCM_PWROFF_GATING_REG_CLUSTER; + writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster)); + udelay(20); + + /* de-assert cluster resets */ + reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster)); + reg |= CPUCFG_CX_RST_CTRL_DBG_SOC_RST; + reg |= CPUCFG_CX_RST_CTRL_H_RST; + reg |= CPUCFG_CX_RST_CTRL_L2_RST; + writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster)); + + /* de-assert ACINACTM */ + reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster)); + reg &= ~CPUCFG_CX_CTRL_REG1_ACINACTM; + writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster)); + + return 0; +} + +static void sunxi_cpu_cache_disable(void) +{ + /* Disable and flush the local CPU cache. */ + v7_exit_coherency_flush(louis); +} + +/* + * This bit is shared between the initial mcpm_sync_init call to enable + * CCI-400 and proper cluster cache disable before power down. + */ +static void sunxi_cluster_cache_disable_without_axi(void) +{ + if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A15) { + /* + * On the Cortex-A15 we need to disable + * L2 prefetching before flushing the cache. + */ + asm volatile( + "mcr p15, 1, %0, c15, c0, 3\n" + "isb\n" + "dsb" + : : "r" (0x400)); + } + + /* Flush all cache levels for this cluster. */ + v7_exit_coherency_flush(all); + + /* + * Disable cluster-level coherency by masking + * incoming snoops and DVM messages: + */ + cci_disable_port_by_cpu(read_cpuid_mpidr()); +} + +static void sunxi_cluster_cache_disable(void) +{ + unsigned int cluster = MPIDR_AFFINITY_LEVEL(read_cpuid_mpidr(), 1); + u32 reg; + + pr_info("%s: cluster %u\n", __func__, cluster); + + sunxi_cluster_cache_disable_without_axi(); + + /* last man standing, assert ACINACTM */ + reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster)); + reg |= CPUCFG_CX_CTRL_REG1_ACINACTM; + writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster)); +} + +static const struct mcpm_platform_ops sunxi_power_ops = { + .cpu_powerup = sunxi_cpu_powerup, + .cluster_powerup = sunxi_cluster_powerup, + .cpu_cache_disable = sunxi_cpu_cache_disable, + .cluster_cache_disable = sunxi_cluster_cache_disable, +}; + +/* + * Enable cluster-level coherency, in preparation for turning on the MMU. + * + * Also enable regional clock gating and L2 data latency settings for + * Cortex-A15. + */ +static void __naked sunxi_power_up_setup(unsigned int affinity_level) +{ + asm volatile ( + "mrc p15, 0, r1, c0, c0, 0\n" + "movw r2, #" __stringify(ARM_CPU_PART_MASK & 0xffff) "\n" + "movt r2, #" __stringify(ARM_CPU_PART_MASK >> 16) "\n" + "and r1, r1, r2\n" + "movw r2, #" __stringify(ARM_CPU_PART_CORTEX_A15 & 0xffff) "\n" + "movt r2, #" __stringify(ARM_CPU_PART_CORTEX_A15 >> 16) "\n" + "cmp r1, r2\n" + "bne not_a15\n" + + /* The following is Cortex-A15 specific */ + + /* L2CTRL: Enable CPU regional clock gates */ + "mrc p15, 1, r1, c15, c0, 4\n" + "orr r1, r1, #(0x1<<31)\n" + "mcr p15, 1, r1, c15, c0, 4\n" + + /* L2ACTLR */ + "mrc p15, 1, r1, c15, c0, 0\n" + /* Enable L2, GIC, and Timer regional clock gates */ + "orr r1, r1, #(0x1<<26)\n" + /* Disable clean/evict from being pushed to external */ + "orr r1, r1, #(0x1<<3)\n" + "mcr p15, 1, r1, c15, c0, 0\n" + + /* L2 data RAM latency */ + "mrc p15, 1, r1, c9, c0, 2\n" + "bic r1, r1, #(0x7<<0)\n" + "orr r1, r1, #(0x3<<0)\n" + "mcr p15, 1, r1, c9, c0, 2\n" + + /* End of Cortex-A15 specific setup */ + "not_a15:\n" + + "cmp r0, #1\n" + "bxne lr\n" + "b cci_enable_port_for_self" + ); +} + +static void sunxi_mcpm_setup_entry_point(void) +{ + __raw_writel(virt_to_phys(mcpm_entry_point), + prcm_base + PRCM_CPU_SOFT_ENTRY_REG); +} + +static int __init sunxi_mcpm_init(void) +{ + struct device_node *node; + int ret; + + if (!of_machine_is_compatible("allwinner,sun9i-a80")) + return -ENODEV; + + if (!cci_probed()) + return -ENODEV; + + node = of_find_compatible_node(NULL, NULL, + "allwinner,sun9i-a80-cpucfg"); + if (!node) + return -ENODEV; + + cpucfg_base = of_iomap(node, 0); + of_node_put(node); + if (!cpucfg_base) { + pr_err("%s: failed to map CPUCFG registers\n", __func__); + return -ENOMEM; + } + + node = of_find_compatible_node(NULL, NULL, + "allwinner,sun9i-a80-prcm"); + if (!node) + return -ENODEV; + + prcm_base = of_iomap(node, 0); + of_node_put(node); + if (!prcm_base) { + pr_err("%s: failed to map PRCM registers\n", __func__); + iounmap(prcm_base); + return -ENOMEM; + } + + ret = mcpm_platform_register(&sunxi_power_ops); + if (!ret) + ret = mcpm_sync_init(sunxi_power_up_setup); + if (!ret) + /* do not disable AXI master as no one will re-enable it */ + ret = mcpm_loopback(sunxi_cluster_cache_disable_without_axi); + if (ret) { + iounmap(cpucfg_base); + iounmap(prcm_base); + return ret; + } + + mcpm_smp_set_ops(); + + pr_info("sunxi MCPM support installed\n"); + + sunxi_mcpm_setup_entry_point(); + + return ret; +} + +early_initcall(sunxi_mcpm_init);