1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2015 Google, Inc
4  * Written by Simon Glass <sjg@chromium.org>
5  */
6 
7 #define LOG_CATEGORY UCLASS_SYSRESET
8 
9 #include <common.h>
10 #include <sysreset.h>
11 #include <dm.h>
12 #include <errno.h>
13 #include <regmap.h>
14 #include <dm/device-internal.h>
15 #include <dm/lists.h>
16 #include <dm/root.h>
17 #include <linux/err.h>
18 
19 int sysreset_request(struct udevice *dev, enum sysreset_t type)
20 {
21 	struct sysreset_ops *ops = sysreset_get_ops(dev);
22 
23 	if (!ops->request)
24 		return -ENOSYS;
25 
26 	return ops->request(dev, type);
27 }
28 
29 int sysreset_get_status(struct udevice *dev, char *buf, int size)
30 {
31 	struct sysreset_ops *ops = sysreset_get_ops(dev);
32 
33 	if (!ops->get_status)
34 		return -ENOSYS;
35 
36 	return ops->get_status(dev, buf, size);
37 }
38 
39 int sysreset_walk(enum sysreset_t type)
40 {
41 	struct udevice *dev;
42 	int ret = -ENOSYS;
43 
44 	while (ret != -EINPROGRESS && type < SYSRESET_COUNT) {
45 		for (uclass_first_device(UCLASS_SYSRESET, &dev);
46 		     dev;
47 		     uclass_next_device(&dev)) {
48 			ret = sysreset_request(dev, type);
49 			if (ret == -EINPROGRESS)
50 				break;
51 		}
52 		type++;
53 	}
54 
55 	return ret;
56 }
57 
58 void sysreset_walk_halt(enum sysreset_t type)
59 {
60 	int ret;
61 
62 	ret = sysreset_walk(type);
63 
64 	/* Wait for the reset to take effect */
65 	if (ret == -EINPROGRESS)
66 		mdelay(100);
67 
68 	/* Still no reset? Give up */
69 	log_err("System reset not supported on this platform\n");
70 	hang();
71 }
72 
73 /**
74  * reset_cpu() - calls sysreset_walk(SYSRESET_WARM)
75  */
76 void reset_cpu(ulong addr)
77 {
78 	sysreset_walk_halt(SYSRESET_WARM);
79 }
80 
81 
82 int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
83 {
84 	printf("resetting ...\n");
85 
86 	sysreset_walk_halt(SYSRESET_COLD);
87 
88 	return 0;
89 }
90 
91 static int sysreset_post_bind(struct udevice *dev)
92 {
93 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
94 	struct sysreset_ops *ops = sysreset_get_ops(dev);
95 	static int reloc_done;
96 
97 	if (!reloc_done) {
98 		if (ops->request)
99 			ops->request += gd->reloc_off;
100 		reloc_done++;
101 	}
102 #endif
103 	return 0;
104 }
105 
106 UCLASS_DRIVER(sysreset) = {
107 	.id		= UCLASS_SYSRESET,
108 	.name		= "sysreset",
109 	.post_bind	= sysreset_post_bind,
110 };
111