If the system is running PSCI firmware, the System Reset function (func ID: 0x80000009) is supposed to be handled by PSCI, that is, the SoC/board specific reset implementation should be moved to PSCI. U-Boot should call the PSCI service according to the arm-smccc manner. The arm-smccc is supported on ARMv7 or later. Especially, ARMv8 generation SoCs are likely to run ARM Trusted Firmware BL31. In this case, U-Boot is a non-secure world boot loader, so it should not be able to reset the system directly. Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>master
parent
c54bcf6805
commit
573a3811ed
@ -0,0 +1,6 @@ |
||||
config FIRMWARE |
||||
bool |
||||
|
||||
config ARM_PSCI_FW |
||||
bool |
||||
select FIRMWARE |
@ -0,0 +1,2 @@ |
||||
obj-$(CONFIG_FIRMWARE) += firmware-uclass.o
|
||||
obj-$(CONFIG_ARM_PSCI_FW) += psci.o
|
@ -0,0 +1,11 @@ |
||||
/*
|
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <dm/uclass.h> |
||||
|
||||
/* Firmware access is platform-dependent. No generic code in uclass */ |
||||
UCLASS_DRIVER(firmware) = { |
||||
.id = UCLASS_FIRMWARE, |
||||
.name = "firmware", |
||||
}; |
@ -0,0 +1,94 @@ |
||||
/*
|
||||
* Copyright (C) 2017 Masahiro Yamada <yamada.masahiro@socionext.com> |
||||
* |
||||
* Based on drivers/firmware/psci.c from Linux: |
||||
* Copyright (C) 2015 ARM Limited |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm/device.h> |
||||
#include <dm/lists.h> |
||||
#include <libfdt.h> |
||||
#include <linux/arm-smccc.h> |
||||
#include <linux/errno.h> |
||||
#include <linux/psci.h> |
||||
|
||||
psci_fn *invoke_psci_fn; |
||||
|
||||
static unsigned long __invoke_psci_fn_hvc(unsigned long function_id, |
||||
unsigned long arg0, unsigned long arg1, |
||||
unsigned long arg2) |
||||
{ |
||||
struct arm_smccc_res res; |
||||
|
||||
arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); |
||||
return res.a0; |
||||
} |
||||
|
||||
static unsigned long __invoke_psci_fn_smc(unsigned long function_id, |
||||
unsigned long arg0, unsigned long arg1, |
||||
unsigned long arg2) |
||||
{ |
||||
struct arm_smccc_res res; |
||||
|
||||
arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); |
||||
return res.a0; |
||||
} |
||||
|
||||
static int psci_bind(struct udevice *dev) |
||||
{ |
||||
/* No SYSTEM_RESET support for PSCI 0.1 */ |
||||
if (of_device_is_compatible(dev, "arm,psci-0.2") || |
||||
of_device_is_compatible(dev, "arm,psci-1.0")) { |
||||
int ret; |
||||
|
||||
/* bind psci-sysreset optionally */ |
||||
ret = device_bind_driver(dev, "psci-sysreset", "psci-sysreset", |
||||
NULL); |
||||
if (ret) |
||||
debug("PSCI System Reset was not bound.\n"); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int psci_probe(struct udevice *dev) |
||||
{ |
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
const char *method; |
||||
|
||||
method = fdt_stringlist_get(gd->fdt_blob, dev->of_offset, "method", 0, |
||||
NULL); |
||||
if (!method) { |
||||
printf("missing \"method\" property\n"); |
||||
return -ENXIO; |
||||
} |
||||
|
||||
if (!strcmp("hvc", method)) { |
||||
invoke_psci_fn = __invoke_psci_fn_hvc; |
||||
} else if (!strcmp("smc", method)) { |
||||
invoke_psci_fn = __invoke_psci_fn_smc; |
||||
} else { |
||||
printf("invalid \"method\" property: %s\n", method); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct udevice_id psci_of_match[] = { |
||||
{ .compatible = "arm,psci" }, |
||||
{ .compatible = "arm,psci-0.2" }, |
||||
{ .compatible = "arm,psci-1.0" }, |
||||
{}, |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(psci) = { |
||||
.name = "psci", |
||||
.id = UCLASS_FIRMWARE, |
||||
.of_match = psci_of_match, |
||||
.bind = psci_bind, |
||||
.probe = psci_probe, |
||||
}; |
@ -0,0 +1,41 @@ |
||||
/*
|
||||
* Copyright (C) 2017 Masahiro Yamada <yamada.masahiro@socionext.com> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <dm/device.h> |
||||
#include <sysreset.h> |
||||
#include <linux/errno.h> |
||||
#include <linux/psci.h> |
||||
|
||||
static int psci_sysreset_request(struct udevice *dev, enum sysreset_t type) |
||||
{ |
||||
unsigned long function_id; |
||||
|
||||
switch (type) { |
||||
case SYSRESET_WARM: |
||||
case SYSRESET_COLD: |
||||
function_id = PSCI_0_2_FN_SYSTEM_RESET; |
||||
break; |
||||
case SYSRESET_POWER: |
||||
function_id = PSCI_0_2_FN_SYSTEM_OFF; |
||||
break; |
||||
default: |
||||
return -ENOSYS; |
||||
} |
||||
|
||||
invoke_psci_fn(function_id, 0, 0, 0); |
||||
|
||||
return -EINPROGRESS; |
||||
} |
||||
|
||||
static struct sysreset_ops psci_sysreset_ops = { |
||||
.request = psci_sysreset_request, |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(psci_sysreset) = { |
||||
.name = "psci-sysreset", |
||||
.id = UCLASS_SYSRESET, |
||||
.ops = &psci_sysreset_ops, |
||||
}; |
Loading…
Reference in new issue