168 lines
3.7 KiB
C
168 lines
3.7 KiB
C
/*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2023 Ventana Micro Systems Inc.
|
|
*
|
|
* Authors:
|
|
* Anup Patel <apatel@ventanamicro.com>
|
|
*/
|
|
|
|
#include <libfdt.h>
|
|
#include <sbi/sbi_ecall_interface.h>
|
|
#include <sbi/sbi_error.h>
|
|
#include <sbi/sbi_hart.h>
|
|
#include <sbi/sbi_system.h>
|
|
#include <sbi_utils/fdt/fdt_helper.h>
|
|
#include <sbi_utils/regmap/fdt_regmap.h>
|
|
#include <sbi_utils/reset/fdt_reset.h>
|
|
|
|
struct syscon_reset {
|
|
struct regmap *rmap;
|
|
u32 priority;
|
|
u32 offset;
|
|
u32 value;
|
|
u32 mask;
|
|
};
|
|
|
|
static struct syscon_reset poweroff;
|
|
static struct syscon_reset reboot;
|
|
|
|
static struct syscon_reset *syscon_reset_get(bool is_reboot, u32 type)
|
|
{
|
|
struct syscon_reset *reset = NULL;
|
|
|
|
switch (type) {
|
|
case SBI_SRST_RESET_TYPE_SHUTDOWN:
|
|
if (!is_reboot)
|
|
reset = &poweroff;
|
|
break;
|
|
case SBI_SRST_RESET_TYPE_COLD_REBOOT:
|
|
case SBI_SRST_RESET_TYPE_WARM_REBOOT:
|
|
if (is_reboot)
|
|
reset = &reboot;
|
|
break;
|
|
}
|
|
|
|
if (reset && !reset->rmap)
|
|
reset = NULL;
|
|
|
|
return reset;
|
|
}
|
|
|
|
static void syscon_reset_exec(struct syscon_reset *reset)
|
|
{
|
|
/* Issue the reset through regmap */
|
|
if (reset)
|
|
regmap_update_bits(reset->rmap, reset->offset,
|
|
reset->mask, reset->value);
|
|
|
|
/* hang !!! */
|
|
sbi_hart_hang();
|
|
}
|
|
|
|
static int syscon_poweroff_check(u32 type, u32 reason)
|
|
{
|
|
struct syscon_reset *reset = syscon_reset_get(false, type);
|
|
|
|
return (reset) ? reset->priority : 0;
|
|
}
|
|
|
|
static void syscon_do_poweroff(u32 type, u32 reason)
|
|
{
|
|
syscon_reset_exec(syscon_reset_get(false, type));
|
|
}
|
|
|
|
static struct sbi_system_reset_device syscon_poweroff = {
|
|
.name = "syscon-poweroff",
|
|
.system_reset_check = syscon_poweroff_check,
|
|
.system_reset = syscon_do_poweroff
|
|
};
|
|
|
|
static int syscon_reboot_check(u32 type, u32 reason)
|
|
{
|
|
struct syscon_reset *reset = syscon_reset_get(true, type);
|
|
|
|
return (reset) ? reset->priority : 0;
|
|
}
|
|
|
|
static void syscon_do_reboot(u32 type, u32 reason)
|
|
{
|
|
syscon_reset_exec(syscon_reset_get(true, type));
|
|
}
|
|
|
|
static struct sbi_system_reset_device syscon_reboot = {
|
|
.name = "syscon-reboot",
|
|
.system_reset_check = syscon_reboot_check,
|
|
.system_reset = syscon_do_reboot
|
|
};
|
|
|
|
static int syscon_reset_init(void *fdt, int nodeoff,
|
|
const struct fdt_match *match)
|
|
{
|
|
int rc, len;
|
|
const fdt32_t *val, *mask;
|
|
bool is_reboot = (ulong)match->data;
|
|
struct syscon_reset *reset = (is_reboot) ? &reboot : &poweroff;
|
|
|
|
if (reset->rmap)
|
|
return SBI_EALREADY;
|
|
|
|
rc = fdt_regmap_get(fdt, nodeoff, &reset->rmap);
|
|
if (rc)
|
|
return rc;
|
|
|
|
val = fdt_getprop(fdt, nodeoff, "priority", &len);
|
|
reset->priority = (val && len > 0) ? fdt32_to_cpu(*val) : 192;
|
|
|
|
val = fdt_getprop(fdt, nodeoff, "offset", &len);
|
|
if (val && len > 0)
|
|
reset->offset = fdt32_to_cpu(*val);
|
|
else
|
|
return SBI_EINVAL;
|
|
|
|
val = fdt_getprop(fdt, nodeoff, "value", &len);
|
|
mask = fdt_getprop(fdt, nodeoff, "mask", &len);
|
|
if (!val && !mask)
|
|
return SBI_EINVAL;
|
|
|
|
if (!val) {
|
|
/* support old binding */
|
|
reset->value = fdt32_to_cpu(*mask);
|
|
reset->mask = 0xFFFFFFFF;
|
|
} else if (!mask) {
|
|
/* support value without mask */
|
|
reset->value = fdt32_to_cpu(*val);
|
|
reset->mask = 0xFFFFFFFF;
|
|
} else {
|
|
reset->value = fdt32_to_cpu(*val);
|
|
reset->mask = fdt32_to_cpu(*mask);
|
|
}
|
|
|
|
if (is_reboot)
|
|
sbi_system_reset_add_device(&syscon_reboot);
|
|
else
|
|
sbi_system_reset_add_device(&syscon_poweroff);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct fdt_match syscon_poweroff_match[] = {
|
|
{ .compatible = "syscon-poweroff", .data = (const void *)false },
|
|
{ },
|
|
};
|
|
|
|
struct fdt_reset fdt_syscon_poweroff = {
|
|
.match_table = syscon_poweroff_match,
|
|
.init = syscon_reset_init,
|
|
};
|
|
|
|
static const struct fdt_match syscon_reboot_match[] = {
|
|
{ .compatible = "syscon-reboot", .data = (const void *)true },
|
|
{ },
|
|
};
|
|
|
|
struct fdt_reset fdt_syscon_reboot = {
|
|
.match_table = syscon_reboot_match,
|
|
.init = syscon_reset_init,
|
|
};
|