mirror of
				https://github.com/riscv-software-src/opensbi
				synced 2025-11-04 14:00:31 +00:00 
			
		
		
		
	The reset driver subsystem does not need any extra data, so it can use `struct fdt_driver` directly. The generic fdt_reset_init() performs a best-effort initialization of all matching DT nodes. Platform-specific logic expects exactly one DT node to match a single driver. This is accomplished by using fdt_driver_init_one() with a local list containing that one driver. Signed-off-by: Samuel Holland <samuel.holland@sifive.com> Reviewed-by: Anup Patel <anup@brainfault.org>
		
			
				
	
	
		
			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(const 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 },
 | 
						|
	{ },
 | 
						|
};
 | 
						|
 | 
						|
const struct fdt_driver 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 },
 | 
						|
	{ },
 | 
						|
};
 | 
						|
 | 
						|
const struct fdt_driver fdt_syscon_reboot = {
 | 
						|
	.match_table = syscon_reboot_match,
 | 
						|
	.init = syscon_reset_init,
 | 
						|
};
 |