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