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 
sysreset_request(struct udevice * dev,enum sysreset_t type)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 
sysreset_get_status(struct udevice * dev,char * buf,int size)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 
sysreset_get_last(struct udevice * dev)39 int sysreset_get_last(struct udevice *dev)
40 {
41 	struct sysreset_ops *ops = sysreset_get_ops(dev);
42 
43 	if (!ops->get_last)
44 		return -ENOSYS;
45 
46 	return ops->get_last(dev);
47 }
48 
sysreset_walk(enum sysreset_t type)49 int sysreset_walk(enum sysreset_t type)
50 {
51 	struct udevice *dev;
52 	int ret = -ENOSYS;
53 
54 	while (ret != -EINPROGRESS && type < SYSRESET_COUNT) {
55 		for (uclass_first_device(UCLASS_SYSRESET, &dev);
56 		     dev;
57 		     uclass_next_device(&dev)) {
58 			ret = sysreset_request(dev, type);
59 			if (ret == -EINPROGRESS)
60 				break;
61 		}
62 		type++;
63 	}
64 
65 	return ret;
66 }
67 
sysreset_get_last_walk(void)68 int sysreset_get_last_walk(void)
69 {
70 	struct udevice *dev;
71 	int value = -ENOENT;
72 
73 	for (uclass_first_device(UCLASS_SYSRESET, &dev);
74 	     dev;
75 	     uclass_next_device(&dev)) {
76 		int ret;
77 
78 		ret = sysreset_get_last(dev);
79 		if (ret >= 0) {
80 			value = ret;
81 			break;
82 		}
83 	}
84 
85 	return value;
86 }
87 
sysreset_walk_halt(enum sysreset_t type)88 void sysreset_walk_halt(enum sysreset_t type)
89 {
90 	int ret;
91 
92 	ret = sysreset_walk(type);
93 
94 	/* Wait for the reset to take effect */
95 	if (ret == -EINPROGRESS)
96 		mdelay(100);
97 
98 	/* Still no reset? Give up */
99 	log_err("System reset not supported on this platform\n");
100 	hang();
101 }
102 
103 /**
104  * reset_cpu() - calls sysreset_walk(SYSRESET_WARM)
105  */
reset_cpu(ulong addr)106 void reset_cpu(ulong addr)
107 {
108 	sysreset_walk_halt(SYSRESET_WARM);
109 }
110 
111 
do_reset(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])112 int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
113 {
114 	printf("resetting ...\n");
115 
116 	sysreset_walk_halt(SYSRESET_COLD);
117 
118 	return 0;
119 }
120 
sysreset_post_bind(struct udevice * dev)121 static int sysreset_post_bind(struct udevice *dev)
122 {
123 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
124 	struct sysreset_ops *ops = sysreset_get_ops(dev);
125 	static int reloc_done;
126 
127 	if (!reloc_done) {
128 		if (ops->request)
129 			ops->request += gd->reloc_off;
130 		reloc_done++;
131 	}
132 #endif
133 	return 0;
134 }
135 
136 UCLASS_DRIVER(sysreset) = {
137 	.id		= UCLASS_SYSRESET,
138 	.name		= "sysreset",
139 	.post_bind	= sysreset_post_bind,
140 };
141