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 <command.h>
10 #include <cpu_func.h>
11 #include <dm.h>
12 #include <errno.h>
13 #include <hang.h>
14 #include <log.h>
15 #include <regmap.h>
16 #include <spl.h>
17 #include <sysreset.h>
18 #include <dm/device-internal.h>
19 #include <dm/lists.h>
20 #include <dm/root.h>
21 #include <linux/delay.h>
22 #include <linux/err.h>
23 #include <asm/global_data.h>
24 
sysreset_request(struct udevice * dev,enum sysreset_t type)25 int sysreset_request(struct udevice *dev, enum sysreset_t type)
26 {
27 	struct sysreset_ops *ops = sysreset_get_ops(dev);
28 
29 	if (!ops->request)
30 		return -ENOSYS;
31 
32 	return ops->request(dev, type);
33 }
34 
sysreset_get_status(struct udevice * dev,char * buf,int size)35 int sysreset_get_status(struct udevice *dev, char *buf, int size)
36 {
37 	struct sysreset_ops *ops = sysreset_get_ops(dev);
38 
39 	if (!ops->get_status)
40 		return -ENOSYS;
41 
42 	return ops->get_status(dev, buf, size);
43 }
44 
sysreset_get_last(struct udevice * dev)45 int sysreset_get_last(struct udevice *dev)
46 {
47 	struct sysreset_ops *ops = sysreset_get_ops(dev);
48 
49 	if (!ops->get_last)
50 		return -ENOSYS;
51 
52 	return ops->get_last(dev);
53 }
54 
sysreset_walk(enum sysreset_t type)55 int sysreset_walk(enum sysreset_t type)
56 {
57 	struct udevice *dev;
58 	int ret = -ENOSYS;
59 
60 	while (ret != -EINPROGRESS && type < SYSRESET_COUNT) {
61 		for (uclass_first_device(UCLASS_SYSRESET, &dev);
62 		     dev;
63 		     uclass_next_device(&dev)) {
64 			ret = sysreset_request(dev, type);
65 			if (ret == -EINPROGRESS)
66 				break;
67 		}
68 		type++;
69 	}
70 
71 	return ret;
72 }
73 
sysreset_get_last_walk(void)74 int sysreset_get_last_walk(void)
75 {
76 	struct udevice *dev;
77 	int value = -ENOENT;
78 
79 	for (uclass_first_device(UCLASS_SYSRESET, &dev);
80 	     dev;
81 	     uclass_next_device(&dev)) {
82 		int ret;
83 
84 		ret = sysreset_get_last(dev);
85 		if (ret >= 0) {
86 			value = ret;
87 			break;
88 		}
89 	}
90 
91 	return value;
92 }
93 
sysreset_walk_halt(enum sysreset_t type)94 void sysreset_walk_halt(enum sysreset_t type)
95 {
96 	int ret;
97 
98 	ret = sysreset_walk(type);
99 
100 	/* Wait for the reset to take effect */
101 	if (ret == -EINPROGRESS)
102 		mdelay(100);
103 
104 	/* Still no reset? Give up */
105 	if (xpl_phase() <= PHASE_SPL)
106 		log_err("no sysreset\n");
107 	else
108 		log_err("System reset not supported on this platform\n");
109 	hang();
110 }
111 
112 /**
113  * reset_cpu() - calls sysreset_walk(SYSRESET_WARM)
114  */
reset_cpu(void)115 void reset_cpu(void)
116 {
117 	sysreset_walk_halt(SYSRESET_WARM);
118 }
119 
120 #if IS_ENABLED(CONFIG_SYSRESET_CMD_RESET)
do_reset(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])121 int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
122 {
123 	enum sysreset_t reset_type = SYSRESET_COLD;
124 
125 	if (argc > 2)
126 		return CMD_RET_USAGE;
127 
128 	if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'w') {
129 		reset_type = SYSRESET_WARM;
130 	}
131 
132 	printf("resetting ...\n");
133 	mdelay(100);
134 
135 	sysreset_walk_halt(reset_type);
136 
137 	return 0;
138 }
139 #endif
140 
141 #if IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF)
do_poweroff(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])142 int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
143 {
144 	int ret;
145 
146 	puts("poweroff ...\n");
147 	mdelay(100);
148 
149 	ret = sysreset_walk(SYSRESET_POWER_OFF);
150 
151 	if (ret == -EINPROGRESS)
152 		mdelay(1000);
153 
154 	/*NOTREACHED when power off*/
155 	return CMD_RET_FAILURE;
156 }
157 #endif
158 
159 UCLASS_DRIVER(sysreset) = {
160 	.id		= UCLASS_SYSRESET,
161 	.name		= "sysreset",
162 };
163