You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC platform updates from Olof Johansson:
"Our SoC branch usually contains expanded support for new SoCs and
other core platform code. In this case, that includes:
- support for the new Annapurna Labs "Alpine" platform
- a rework greatly simplifying adding new platform support to the
MCPM subsystem (Multi-cluster power management)
- cpuidle and PM improvements for Exynos3250
- misc updates for Renesas, OMAP, Meson, i.MX. Some of these could
have gone in other branches but ended up here for various reasons"
* tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (53 commits)
ARM: alpine: add support for generic pci
ARM: Exynos: migrate DCSCB to the new MCPM backend abstraction
ARM: vexpress: migrate DCSCB to the new MCPM backend abstraction
ARM: vexpress: DCSCB: tighten CPU validity assertion
ARM: vexpress: migrate TC2 to the new MCPM backend abstraction
ARM: MCPM: move the algorithmic complexity to the core code
ARM: EXYNOS: allow cpuidle driver usage on Exynos3250 SoC
ARM: EXYNOS: add AFTR mode support for Exynos3250
ARM: EXYNOS: add code for setting/clearing boot flag
ARM: EXYNOS: fix CPU1 hotplug on Exynos3250
ARM: S3C64XX: Use fixed IRQ bases to avoid conflicts on Cragganmore
ARM: cygnus: fix const declaration bcm_cygnus_dt_compat
ARM: DRA7: hwmod: Fix the hwmod class for GPTimer4
ARM: DRA7: hwmod: Add data for GPTimers 13 through 16
ARM: EXYNOS: Remove left over 'extra_save'
ARM: EXYNOS: Constify exynos_pm_data array
ARM: EXYNOS: use static in suspend.c
ARM: EXYNOS: Use platform device name as power domain name
ARM: EXYNOS: add support for async-bridge clocks for pm_domains
ARM: omap-device: add missed callback for suspend-to-disk
...
This commit is contained in:
@@ -96,6 +96,11 @@ EBU Armada family
|
||||
88F6820
|
||||
88F6828
|
||||
|
||||
Armada 390/398 Flavors:
|
||||
88F6920
|
||||
88F6928
|
||||
Product infos: http://www.marvell.com/embedded-processors/armada-39x/
|
||||
|
||||
Armada XP Flavors:
|
||||
MV78230
|
||||
MV78260
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
Annapurna Labs Alpine Platform Device Tree Bindings
|
||||
---------------------------------------------------------------
|
||||
|
||||
Boards in the Alpine family shall have the following properties:
|
||||
|
||||
* Required root node properties:
|
||||
compatible: must contain "al,alpine"
|
||||
|
||||
* Example:
|
||||
|
||||
/ {
|
||||
model = "Annapurna Labs Alpine Dev Board";
|
||||
compatible = "al,alpine";
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
* CPU node:
|
||||
|
||||
The Alpine platform includes cortex-a15 cores.
|
||||
enable-method: must be "al,alpine-smp" to allow smp [1]
|
||||
|
||||
Example:
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
enable-method = "al,alpine-smp";
|
||||
|
||||
cpu@0 {
|
||||
compatible = "arm,cortex-a15";
|
||||
device_type = "cpu";
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
compatible = "arm,cortex-a15";
|
||||
device_type = "cpu";
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
cpu@2 {
|
||||
compatible = "arm,cortex-a15";
|
||||
device_type = "cpu";
|
||||
reg = <2>;
|
||||
};
|
||||
|
||||
cpu@3 {
|
||||
compatible = "arm,cortex-a15";
|
||||
device_type = "cpu";
|
||||
reg = <3>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
* Alpine CPU resume registers
|
||||
|
||||
The CPU resume register are used to define required resume address after
|
||||
reset.
|
||||
|
||||
Properties:
|
||||
- compatible : Should contain "al,alpine-cpu-resume".
|
||||
- reg : Offset and length of the register set for the device
|
||||
|
||||
Example:
|
||||
|
||||
cpu_resume {
|
||||
compatible = "al,alpine-cpu-resume";
|
||||
reg = <0xfbff5ed0 0x30>;
|
||||
};
|
||||
|
||||
* Alpine System-Fabric Service Registers
|
||||
|
||||
The System-Fabric Service Registers allow various operation on CPU and
|
||||
system fabric, like powering CPUs off.
|
||||
|
||||
Properties:
|
||||
- compatible : Should contain "al,alpine-sysfabric-service" and "syscon".
|
||||
- reg : Offset and length of the register set for the device
|
||||
|
||||
Example:
|
||||
|
||||
nb_service {
|
||||
compatible = "al,alpine-sysfabric-service", "syscon";
|
||||
reg = <0xfb070000 0x10000>;
|
||||
};
|
||||
|
||||
[1] arm/cpu-enable-method/al,alpine-smp
|
||||
@@ -8,3 +8,7 @@ Boards with the Amlogic Meson6 SoC shall have the following properties:
|
||||
Boards with the Amlogic Meson8 SoC shall have the following properties:
|
||||
Required root node property:
|
||||
compatible: "amlogic,meson8";
|
||||
|
||||
Board compatible values:
|
||||
- "geniatech,atv1200"
|
||||
- "minix,neo-x8"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
Marvell Armada 39x Platforms Device Tree Bindings
|
||||
-------------------------------------------------
|
||||
|
||||
Boards with a SoC of the Marvell Armada 39x family shall have the
|
||||
following property:
|
||||
|
||||
Required root node property:
|
||||
|
||||
- compatible: must contain "marvell,armada390"
|
||||
|
||||
In addition, boards using the Marvell Armada 398 SoC shall have the
|
||||
following property before the previous one:
|
||||
|
||||
Required root node property:
|
||||
|
||||
compatible: must contain "marvell,armada398"
|
||||
|
||||
Example:
|
||||
|
||||
compatible = "marvell,a398-db", "marvell,armada398", "marvell,armada390";
|
||||
@@ -0,0 +1,52 @@
|
||||
========================================================
|
||||
Secondary CPU enable-method "al,alpine-smp" binding
|
||||
========================================================
|
||||
|
||||
This document describes the "al,alpine-smp" method for
|
||||
enabling secondary CPUs. To apply to all CPUs, a single
|
||||
"al,alpine-smp" enable method should be defined in the
|
||||
"cpus" node.
|
||||
|
||||
Enable method name: "al,alpine-smp"
|
||||
Compatible machines: "al,alpine"
|
||||
Compatible CPUs: "arm,cortex-a15"
|
||||
Related properties: (none)
|
||||
|
||||
Note:
|
||||
This enable method requires valid nodes compatible with
|
||||
"al,alpine-cpu-resume" and "al,alpine-nb-service"[1].
|
||||
|
||||
Example:
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
enable-method = "al,alpine-smp";
|
||||
|
||||
cpu@0 {
|
||||
compatible = "arm,cortex-a15";
|
||||
device_type = "cpu";
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
compatible = "arm,cortex-a15";
|
||||
device_type = "cpu";
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
cpu@2 {
|
||||
compatible = "arm,cortex-a15";
|
||||
device_type = "cpu";
|
||||
reg = <2>;
|
||||
};
|
||||
|
||||
cpu@3 {
|
||||
compatible = "arm,cortex-a15";
|
||||
device_type = "cpu";
|
||||
reg = <3>;
|
||||
};
|
||||
};
|
||||
|
||||
--
|
||||
[1] arm/al,alpine.txt
|
||||
@@ -192,6 +192,7 @@ nodes to be present and contain the properties described below.
|
||||
"brcm,brahma-b15"
|
||||
"marvell,armada-375-smp"
|
||||
"marvell,armada-380-smp"
|
||||
"marvell,armada-390-smp"
|
||||
"marvell,armada-xp-smp"
|
||||
"qcom,gcc-msm8660"
|
||||
"qcom,kpss-acc-v1"
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
Geniatech platforms device tree bindings
|
||||
-------------------------------------------
|
||||
|
||||
Geniatech ATV1200
|
||||
- compatible = "geniatech,atv1200"
|
||||
@@ -0,0 +1,59 @@
|
||||
Freescale i.MX General Power Controller
|
||||
=======================================
|
||||
|
||||
The i.MX6Q General Power Control (GPC) block contains DVFS load tracking
|
||||
counters and Power Gating Control (PGC) for the CPU and PU (GPU/VPU) power
|
||||
domains.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx6q-gpc" or "fsl,imx6sl-gpc"
|
||||
- reg: should be register base and length as documented in the
|
||||
datasheet
|
||||
- interrupts: Should contain GPC interrupt request 1
|
||||
- pu-supply: Link to the LDO regulator powering the PU power domain
|
||||
- clocks: Clock phandles to devices in the PU power domain that need
|
||||
to be enabled during domain power-up for reset propagation.
|
||||
- #power-domain-cells: Should be 1, see below:
|
||||
|
||||
The gpc node is a power-controller as documented by the generic power domain
|
||||
bindings in Documentation/devicetree/bindings/power/power_domain.txt.
|
||||
|
||||
Example:
|
||||
|
||||
gpc: gpc@020dc000 {
|
||||
compatible = "fsl,imx6q-gpc";
|
||||
reg = <0x020dc000 0x4000>;
|
||||
interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 90 IRQ_TYPE_LEVEL_HIGH>;
|
||||
pu-supply = <®_pu>;
|
||||
clocks = <&clks IMX6QDL_CLK_GPU3D_CORE>,
|
||||
<&clks IMX6QDL_CLK_GPU3D_SHADER>,
|
||||
<&clks IMX6QDL_CLK_GPU2D_CORE>,
|
||||
<&clks IMX6QDL_CLK_GPU2D_AXI>,
|
||||
<&clks IMX6QDL_CLK_OPENVG_AXI>,
|
||||
<&clks IMX6QDL_CLK_VPU_AXI>;
|
||||
#power-domain-cells = <1>;
|
||||
};
|
||||
|
||||
|
||||
Specifying power domain for IP modules
|
||||
======================================
|
||||
|
||||
IP cores belonging to a power domain should contain a 'power-domains' property
|
||||
that is a phandle pointing to the gpc device node and a DOMAIN_INDEX specifying
|
||||
the power domain the device belongs to.
|
||||
|
||||
Example of a device that is part of the PU power domain:
|
||||
|
||||
vpu: vpu@02040000 {
|
||||
reg = <0x02040000 0x3c000>;
|
||||
/* ... */
|
||||
power-domains = <&gpc 1>;
|
||||
/* ... */
|
||||
};
|
||||
|
||||
The following DOMAIN_INDEX values are valid for i.MX6Q:
|
||||
ARM_DOMAIN 0
|
||||
PU_DOMAIN 1
|
||||
The following additional DOMAIN_INDEX value is valid for i.MX6SL:
|
||||
DISPLAY_DOMAIN 2
|
||||
@@ -11,6 +11,7 @@ adapteva Adapteva, Inc.
|
||||
adh AD Holdings Plc.
|
||||
adi Analog Devices, Inc.
|
||||
aeroflexgaisler Aeroflex Gaisler AB
|
||||
al Annapurna Labs
|
||||
allwinner Allwinner Technology Co., Ltd.
|
||||
alphascale AlphaScale Integrated Circuits Systems, Inc.
|
||||
altr Altera Corp.
|
||||
@@ -118,6 +119,7 @@ merrii Merrii Technology Co., Ltd.
|
||||
micrel Micrel Inc.
|
||||
microchip Microchip Technology Inc.
|
||||
micron Micron Technology Inc.
|
||||
minix MINIX Technology Ltd.
|
||||
mitsubishi Mitsubishi Electric Corporation
|
||||
mosaixtech Mosaix Technologies, Inc.
|
||||
moxa Moxa
|
||||
|
||||
@@ -886,6 +886,11 @@ S: Maintained
|
||||
F: drivers/media/rc/meson-ir.c
|
||||
N: meson[x68]
|
||||
|
||||
ARM/Annapurna Labs ALPINE ARCHITECTURE
|
||||
M: Tsahee Zidenberg <tsahee@annapurnalabs.com>
|
||||
S: Maintained
|
||||
F: arch/arm/mach-alpine/
|
||||
|
||||
ARM/ATMEL AT91RM9200 AND AT91SAM ARM ARCHITECTURES
|
||||
M: Andrew Victor <linux@maxim.org.za>
|
||||
M: Nicolas Ferre <nicolas.ferre@atmel.com>
|
||||
|
||||
@@ -839,6 +839,8 @@ config ARCH_VIRT
|
||||
#
|
||||
source "arch/arm/mach-mvebu/Kconfig"
|
||||
|
||||
source "arch/arm/mach-alpine/Kconfig"
|
||||
|
||||
source "arch/arm/mach-asm9260/Kconfig"
|
||||
|
||||
source "arch/arm/mach-at91/Kconfig"
|
||||
|
||||
+11
-1
@@ -93,6 +93,14 @@ choice
|
||||
prompt "Kernel low-level debugging port"
|
||||
depends on DEBUG_LL
|
||||
|
||||
config DEBUG_ALPINE_UART0
|
||||
bool "Kernel low-level debugging messages via Alpine UART0"
|
||||
depends on ARCH_ALPINE
|
||||
select DEBUG_UART_8250
|
||||
help
|
||||
Say Y here if you want kernel low-level debugging support
|
||||
on Alpine based platforms.
|
||||
|
||||
config DEBUG_ASM9260_UART
|
||||
bool "Kernel low-level debugging via asm9260 UART"
|
||||
depends on MACH_ASM9260
|
||||
@@ -1397,6 +1405,7 @@ config DEBUG_UART_PHYS
|
||||
default 0xf8b00000 if DEBUG_HIX5HD2_UART
|
||||
default 0xf991e000 if DEBUG_QCOM_UARTDM
|
||||
default 0xfcb00000 if DEBUG_HI3620_UART
|
||||
default 0xfd883000 if DEBUG_ALPINE_UART0
|
||||
default 0xfe800000 if ARCH_IOP32X
|
||||
default 0xff690000 if DEBUG_RK32_UART2
|
||||
default 0xffc02000 if DEBUG_SOCFPGA_UART
|
||||
@@ -1462,6 +1471,7 @@ config DEBUG_UART_VIRT
|
||||
default 0xfd000000 if ARCH_SPEAR3XX || ARCH_SPEAR6XX
|
||||
default 0xfd000000 if ARCH_SPEAR13XX
|
||||
default 0xfd012000 if ARCH_MV78XX0
|
||||
default 0xfd883000 if DEBUG_ALPINE_UART0
|
||||
default 0xfde12000 if ARCH_DOVE
|
||||
default 0xfe012000 if ARCH_ORION5X
|
||||
default 0xf31004c0 if DEBUG_MESON_UARTAO
|
||||
@@ -1522,7 +1532,7 @@ config DEBUG_UART_8250_WORD
|
||||
depends on DEBUG_LL_UART_8250 || DEBUG_UART_8250
|
||||
depends on DEBUG_UART_8250_SHIFT >= 2
|
||||
default y if DEBUG_PICOXCELL_UART || DEBUG_SOCFPGA_UART || \
|
||||
ARCH_KEYSTONE || \
|
||||
ARCH_KEYSTONE || DEBUG_ALPINE_UART0 || \
|
||||
DEBUG_DAVINCI_DMx_UART0 || DEBUG_DAVINCI_DA8XX_UART1 || \
|
||||
DEBUG_DAVINCI_DA8XX_UART2 || \
|
||||
DEBUG_BCM_KONA_UART || DEBUG_RK32_UART2 || \
|
||||
|
||||
@@ -142,6 +142,7 @@ textofs-$(CONFIG_ARCH_AXXIA) := 0x00308000
|
||||
|
||||
# Machine directory name. This list is sorted alphanumerically
|
||||
# by CONFIG_* macro name.
|
||||
machine-$(CONFIG_ARCH_ALPINE) += alpine
|
||||
machine-$(CONFIG_ARCH_AT91) += at91
|
||||
machine-$(CONFIG_ARCH_AXXIA) += axxia
|
||||
machine-$(CONFIG_ARCH_BCM) += bcm
|
||||
|
||||
@@ -513,9 +513,27 @@
|
||||
pinctrl-0 = <&iic3_pins>;
|
||||
status = "okay";
|
||||
|
||||
pmic@58 {
|
||||
compatible = "dlg,da9063";
|
||||
reg = <0x58>;
|
||||
interrupt-parent = <&irqc0>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-controller;
|
||||
|
||||
rtc {
|
||||
compatible = "dlg,da9063-rtc";
|
||||
};
|
||||
|
||||
wdt {
|
||||
compatible = "dlg,da9063-watchdog";
|
||||
};
|
||||
};
|
||||
|
||||
vdd_dvfs: regulator@68 {
|
||||
compatible = "dlg,da9210";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&irqc0>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
regulator-min-microvolt = <1000000>;
|
||||
regulator-max-microvolt = <1000000>;
|
||||
|
||||
@@ -517,9 +517,27 @@
|
||||
status = "okay";
|
||||
clock-frequency = <100000>;
|
||||
|
||||
pmic@58 {
|
||||
compatible = "dlg,da9063";
|
||||
reg = <0x58>;
|
||||
interrupt-parent = <&irqc0>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-controller;
|
||||
|
||||
rtc {
|
||||
compatible = "dlg,da9063-rtc";
|
||||
};
|
||||
|
||||
wdt {
|
||||
compatible = "dlg,da9063-watchdog";
|
||||
};
|
||||
};
|
||||
|
||||
vdd_dvfs: regulator@68 {
|
||||
compatible = "dlg,da9210";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&irqc0>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
regulator-min-microvolt = <1000000>;
|
||||
regulator-max-microvolt = <1000000>;
|
||||
|
||||
+170
-32
@@ -55,22 +55,81 @@ bool mcpm_is_available(void)
|
||||
return (platform_ops) ? true : false;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't use regular spinlocks. In the switcher case, it is possible
|
||||
* for an outbound CPU to call power_down() after its inbound counterpart
|
||||
* is already live using the same logical CPU number which trips lockdep
|
||||
* debugging.
|
||||
*/
|
||||
static arch_spinlock_t mcpm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
|
||||
|
||||
static int mcpm_cpu_use_count[MAX_NR_CLUSTERS][MAX_CPUS_PER_CLUSTER];
|
||||
|
||||
static inline bool mcpm_cluster_unused(unsigned int cluster)
|
||||
{
|
||||
int i, cnt;
|
||||
for (i = 0, cnt = 0; i < MAX_CPUS_PER_CLUSTER; i++)
|
||||
cnt |= mcpm_cpu_use_count[cluster][i];
|
||||
return !cnt;
|
||||
}
|
||||
|
||||
int mcpm_cpu_power_up(unsigned int cpu, unsigned int cluster)
|
||||
{
|
||||
bool cpu_is_down, cluster_is_down;
|
||||
int ret = 0;
|
||||
|
||||
if (!platform_ops)
|
||||
return -EUNATCH; /* try not to shadow power_up errors */
|
||||
might_sleep();
|
||||
return platform_ops->power_up(cpu, cluster);
|
||||
|
||||
/* backward compatibility callback */
|
||||
if (platform_ops->power_up)
|
||||
return platform_ops->power_up(cpu, cluster);
|
||||
|
||||
pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
|
||||
|
||||
/*
|
||||
* Since this is called with IRQs enabled, and no arch_spin_lock_irq
|
||||
* variant exists, we need to disable IRQs manually here.
|
||||
*/
|
||||
local_irq_disable();
|
||||
arch_spin_lock(&mcpm_lock);
|
||||
|
||||
cpu_is_down = !mcpm_cpu_use_count[cluster][cpu];
|
||||
cluster_is_down = mcpm_cluster_unused(cluster);
|
||||
|
||||
mcpm_cpu_use_count[cluster][cpu]++;
|
||||
/*
|
||||
* The only possible values are:
|
||||
* 0 = CPU down
|
||||
* 1 = CPU (still) up
|
||||
* 2 = CPU requested to be up before it had a chance
|
||||
* to actually make itself down.
|
||||
* Any other value is a bug.
|
||||
*/
|
||||
BUG_ON(mcpm_cpu_use_count[cluster][cpu] != 1 &&
|
||||
mcpm_cpu_use_count[cluster][cpu] != 2);
|
||||
|
||||
if (cluster_is_down)
|
||||
ret = platform_ops->cluster_powerup(cluster);
|
||||
if (cpu_is_down && !ret)
|
||||
ret = platform_ops->cpu_powerup(cpu, cluster);
|
||||
|
||||
arch_spin_unlock(&mcpm_lock);
|
||||
local_irq_enable();
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef void (*phys_reset_t)(unsigned long);
|
||||
|
||||
void mcpm_cpu_power_down(void)
|
||||
{
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
bool cpu_going_down, last_man;
|
||||
phys_reset_t phys_reset;
|
||||
|
||||
if (WARN_ON_ONCE(!platform_ops || !platform_ops->power_down))
|
||||
return;
|
||||
if (WARN_ON_ONCE(!platform_ops))
|
||||
return;
|
||||
BUG_ON(!irqs_disabled());
|
||||
|
||||
/*
|
||||
@@ -79,28 +138,65 @@ void mcpm_cpu_power_down(void)
|
||||
*/
|
||||
setup_mm_for_reboot();
|
||||
|
||||
platform_ops->power_down();
|
||||
/* backward compatibility callback */
|
||||
if (platform_ops->power_down) {
|
||||
platform_ops->power_down();
|
||||
goto not_dead;
|
||||
}
|
||||
|
||||
mpidr = read_cpuid_mpidr();
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
|
||||
|
||||
__mcpm_cpu_going_down(cpu, cluster);
|
||||
|
||||
arch_spin_lock(&mcpm_lock);
|
||||
BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
|
||||
|
||||
mcpm_cpu_use_count[cluster][cpu]--;
|
||||
BUG_ON(mcpm_cpu_use_count[cluster][cpu] != 0 &&
|
||||
mcpm_cpu_use_count[cluster][cpu] != 1);
|
||||
cpu_going_down = !mcpm_cpu_use_count[cluster][cpu];
|
||||
last_man = mcpm_cluster_unused(cluster);
|
||||
|
||||
if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
|
||||
platform_ops->cpu_powerdown_prepare(cpu, cluster);
|
||||
platform_ops->cluster_powerdown_prepare(cluster);
|
||||
arch_spin_unlock(&mcpm_lock);
|
||||
platform_ops->cluster_cache_disable();
|
||||
__mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
|
||||
} else {
|
||||
if (cpu_going_down)
|
||||
platform_ops->cpu_powerdown_prepare(cpu, cluster);
|
||||
arch_spin_unlock(&mcpm_lock);
|
||||
/*
|
||||
* If cpu_going_down is false here, that means a power_up
|
||||
* request raced ahead of us. Even if we do not want to
|
||||
* shut this CPU down, the caller still expects execution
|
||||
* to return through the system resume entry path, like
|
||||
* when the WFI is aborted due to a new IRQ or the like..
|
||||
* So let's continue with cache cleaning in all cases.
|
||||
*/
|
||||
platform_ops->cpu_cache_disable();
|
||||
}
|
||||
|
||||
__mcpm_cpu_down(cpu, cluster);
|
||||
|
||||
/* Now we are prepared for power-down, do it: */
|
||||
if (cpu_going_down)
|
||||
wfi();
|
||||
|
||||
not_dead:
|
||||
/*
|
||||
* It is possible for a power_up request to happen concurrently
|
||||
* with a power_down request for the same CPU. In this case the
|
||||
* power_down method might not be able to actually enter a
|
||||
* powered down state with the WFI instruction if the power_up
|
||||
* method has removed the required reset condition. The
|
||||
* power_down method is then allowed to return. We must perform
|
||||
* a re-entry in the kernel as if the power_up method just had
|
||||
* deasserted reset on the CPU.
|
||||
*
|
||||
* To simplify race issues, the platform specific implementation
|
||||
* must accommodate for the possibility of unordered calls to
|
||||
* power_down and power_up with a usage count. Therefore, if a
|
||||
* call to power_up is issued for a CPU that is not down, then
|
||||
* the next call to power_down must not attempt a full shutdown
|
||||
* but only do the minimum (normally disabling L1 cache and CPU
|
||||
* coherency) and return just as if a concurrent power_up request
|
||||
* had happened as described above.
|
||||
* CPU might not be able to actually enter a powered down state
|
||||
* with the WFI instruction if the power_up request has removed
|
||||
* the required reset condition. We must perform a re-entry in
|
||||
* the kernel as if the power_up method just had deasserted reset
|
||||
* on the CPU.
|
||||
*/
|
||||
|
||||
phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset);
|
||||
phys_reset(virt_to_phys(mcpm_entry_point));
|
||||
|
||||
@@ -125,26 +221,66 @@ int mcpm_wait_for_cpu_powerdown(unsigned int cpu, unsigned int cluster)
|
||||
|
||||
void mcpm_cpu_suspend(u64 expected_residency)
|
||||
{
|
||||
phys_reset_t phys_reset;
|
||||
|
||||
if (WARN_ON_ONCE(!platform_ops || !platform_ops->suspend))
|
||||
if (WARN_ON_ONCE(!platform_ops))
|
||||
return;
|
||||
BUG_ON(!irqs_disabled());
|
||||
|
||||
/* Very similar to mcpm_cpu_power_down() */
|
||||
setup_mm_for_reboot();
|
||||
platform_ops->suspend(expected_residency);
|
||||
phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset);
|
||||
phys_reset(virt_to_phys(mcpm_entry_point));
|
||||
BUG();
|
||||
/* backward compatibility callback */
|
||||
if (platform_ops->suspend) {
|
||||
phys_reset_t phys_reset;
|
||||
BUG_ON(!irqs_disabled());
|
||||
setup_mm_for_reboot();
|
||||
platform_ops->suspend(expected_residency);
|
||||
phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset);
|
||||
phys_reset(virt_to_phys(mcpm_entry_point));
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Some platforms might have to enable special resume modes, etc. */
|
||||
if (platform_ops->cpu_suspend_prepare) {
|
||||
unsigned int mpidr = read_cpuid_mpidr();
|
||||
unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
unsigned int cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
arch_spin_lock(&mcpm_lock);
|
||||
platform_ops->cpu_suspend_prepare(cpu, cluster);
|
||||
arch_spin_unlock(&mcpm_lock);
|
||||
}
|
||||
mcpm_cpu_power_down();
|
||||
}
|
||||
|
||||
int mcpm_cpu_powered_up(void)
|
||||
{
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
bool cpu_was_down, first_man;
|
||||
unsigned long flags;
|
||||
|
||||
if (!platform_ops)
|
||||
return -EUNATCH;
|
||||
if (platform_ops->powered_up)
|
||||
|
||||
/* backward compatibility callback */
|
||||
if (platform_ops->powered_up) {
|
||||
platform_ops->powered_up();
|
||||
return 0;
|
||||
}
|
||||
|
||||
mpidr = read_cpuid_mpidr();
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
local_irq_save(flags);
|
||||
arch_spin_lock(&mcpm_lock);
|
||||
|
||||
cpu_was_down = !mcpm_cpu_use_count[cluster][cpu];
|
||||
first_man = mcpm_cluster_unused(cluster);
|
||||
|
||||
if (first_man && platform_ops->cluster_is_up)
|
||||
platform_ops->cluster_is_up(cluster);
|
||||
if (cpu_was_down)
|
||||
mcpm_cpu_use_count[cluster][cpu] = 1;
|
||||
if (platform_ops->cpu_is_up)
|
||||
platform_ops->cpu_is_up(cpu, cluster);
|
||||
|
||||
arch_spin_unlock(&mcpm_lock);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -334,8 +470,10 @@ int __init mcpm_sync_init(
|
||||
}
|
||||
mpidr = read_cpuid_mpidr();
|
||||
this_cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
for_each_online_cpu(i)
|
||||
for_each_online_cpu(i) {
|
||||
mcpm_cpu_use_count[this_cluster][i] = 1;
|
||||
mcpm_sync.clusters[this_cluster].cpus[i].cpu = CPU_UP;
|
||||
}
|
||||
mcpm_sync.clusters[this_cluster].cluster = CLUSTER_UP;
|
||||
sync_cache_w(&mcpm_sync);
|
||||
|
||||
|
||||
@@ -171,12 +171,73 @@ void mcpm_cpu_suspend(u64 expected_residency);
|
||||
int mcpm_cpu_powered_up(void);
|
||||
|
||||
/*
|
||||
* Platform specific methods used in the implementation of the above API.
|
||||
* Platform specific callbacks used in the implementation of the above API.
|
||||
*
|
||||
* cpu_powerup:
|
||||
* Make given CPU runable. Called with MCPM lock held and IRQs disabled.
|
||||
* The given cluster is assumed to be set up (cluster_powerup would have
|
||||
* been called beforehand). Must return 0 for success or negative error code.
|
||||
*
|
||||
* cluster_powerup:
|
||||
* Set up power for given cluster. Called with MCPM lock held and IRQs
|
||||
* disabled. Called before first cpu_powerup when cluster is down. Must
|
||||
* return 0 for success or negative error code.
|
||||
*
|
||||
* cpu_suspend_prepare:
|
||||
* Special suspend configuration. Called on target CPU with MCPM lock held
|
||||
* and IRQs disabled. This callback is optional. If provided, it is called
|
||||
* before cpu_powerdown_prepare.
|
||||
*
|
||||
* cpu_powerdown_prepare:
|
||||
* Configure given CPU for power down. Called on target CPU with MCPM lock
|
||||
* held and IRQs disabled. Power down must be effective only at the next WFI instruction.
|
||||
*
|
||||
* cluster_powerdown_prepare:
|
||||
* Configure given cluster for power down. Called on one CPU from target
|
||||
* cluster with MCPM lock held and IRQs disabled. A cpu_powerdown_prepare
|
||||
* for each CPU in the cluster has happened when this occurs.
|
||||
*
|
||||
* cpu_cache_disable:
|
||||
* Clean and disable CPU level cache for the calling CPU. Called on with IRQs
|
||||
* disabled only. The CPU is no longer cache coherent with the rest of the
|
||||
* system when this returns.
|
||||
*
|
||||
* cluster_cache_disable:
|
||||
* Clean and disable the cluster wide cache as well as the CPU level cache
|
||||
* for the calling CPU. No call to cpu_cache_disable will happen for this
|
||||
* CPU. Called with IRQs disabled and only when all the other CPUs are done
|
||||
* with their own cpu_cache_disable. The cluster is no longer cache coherent
|
||||
* with the rest of the system when this returns.
|
||||
*
|
||||
* cpu_is_up:
|
||||
* Called on given CPU after it has been powered up or resumed. The MCPM lock
|
||||
* is held and IRQs disabled. This callback is optional.
|
||||
*
|
||||
* cluster_is_up:
|
||||
* Called by the first CPU to be powered up or resumed in given cluster.
|
||||
* The MCPM lock is held and IRQs disabled. This callback is optional. If
|
||||
* provided, it is called before cpu_is_up for that CPU.
|
||||
*
|
||||
* wait_for_powerdown:
|
||||
* Wait until given CPU is powered down. This is called in sleeping context.
|
||||
* Some reasonable timeout must be considered. Must return 0 for success or
|
||||
* negative error code.
|
||||
*/
|
||||
struct mcpm_platform_ops {
|
||||
int (*cpu_powerup)(unsigned int cpu, unsigned int cluster);
|
||||
int (*cluster_powerup)(unsigned int cluster);
|
||||
void (*cpu_suspend_prepare)(unsigned int cpu, unsigned int cluster);
|
||||
void (*cpu_powerdown_prepare)(unsigned int cpu, unsigned int cluster);
|
||||
void (*cluster_powerdown_prepare)(unsigned int cluster);
|
||||
void (*cpu_cache_disable)(void);
|
||||
void (*cluster_cache_disable)(void);
|
||||
void (*cpu_is_up)(unsigned int cpu, unsigned int cluster);
|
||||
void (*cluster_is_up)(unsigned int cluster);
|
||||
int (*wait_for_powerdown)(unsigned int cpu, unsigned int cluster);
|
||||
|
||||
/* deprecated callbacks */
|
||||
int (*power_up)(unsigned int cpu, unsigned int cluster);
|
||||
void (*power_down)(void);
|
||||
int (*wait_for_powerdown)(unsigned int cpu, unsigned int cluster);
|
||||
void (*suspend)(u64);
|
||||
void (*powered_up)(void);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
config ARCH_ALPINE
|
||||
bool "Annapurna Labs Alpine platform" if ARCH_MULTI_V7
|
||||
select ARM_AMBA
|
||||
select ARM_GIC
|
||||
select GENERIC_IRQ_CHIP
|
||||
select HAVE_ARM_ARCH_TIMER
|
||||
select HAVE_SMP
|
||||
select MFD_SYSCON
|
||||
select PCI
|
||||
select PCI_HOST_GENERIC
|
||||
help
|
||||
This enables support for the Annapurna Labs Alpine V1 boards.
|
||||
@@ -0,0 +1,2 @@
|
||||
obj-y += alpine_machine.o
|
||||
obj-$(CONFIG_SMP) += platsmp.o alpine_cpu_pm.o
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Low-level power-management support for Alpine platform.
|
||||
*
|
||||
* Copyright (C) 2015 Annapurna Labs Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#include "alpine_cpu_pm.h"
|
||||
#include "alpine_cpu_resume.h"
|
||||
|
||||
/* NB registers */
|
||||
#define AL_SYSFAB_POWER_CONTROL(cpu) (0x2000 + (cpu)*0x100 + 0x20)
|
||||
|
||||
static struct regmap *al_sysfabric;
|
||||
static struct al_cpu_resume_regs __iomem *al_cpu_resume_regs;
|
||||
static int wakeup_supported;
|
||||
|
||||
int alpine_cpu_wakeup(unsigned int phys_cpu, uint32_t phys_resume_addr)
|
||||
{
|
||||
if (!wakeup_supported)
|
||||
return -ENOSYS;
|
||||
|
||||
/*
|
||||
* Set CPU resume address -
|
||||
* secure firmware running on boot will jump to this address
|
||||
* after setting proper CPU mode, and initialiing e.g. secure
|
||||
* regs (the same mode all CPUs are booted to - usually HYP)
|
||||
*/
|
||||
writel(phys_resume_addr,
|
||||
&al_cpu_resume_regs->per_cpu[phys_cpu].resume_addr);
|
||||
|
||||
/* Power-up the CPU */
|
||||
regmap_write(al_sysfabric, AL_SYSFAB_POWER_CONTROL(phys_cpu), 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init alpine_cpu_pm_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
uint32_t watermark;
|
||||
|
||||
al_sysfabric = syscon_regmap_lookup_by_compatible("al,alpine-sysfabric-service");
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "al,alpine-cpu-resume");
|
||||
al_cpu_resume_regs = of_iomap(np, 0);
|
||||
|
||||
wakeup_supported = !IS_ERR(al_sysfabric) && al_cpu_resume_regs;
|
||||
|
||||
if (wakeup_supported) {
|
||||
watermark = readl(&al_cpu_resume_regs->watermark);
|
||||
wakeup_supported = (watermark & AL_CPU_RESUME_MAGIC_NUM_MASK)
|
||||
== AL_CPU_RESUME_MAGIC_NUM;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user