]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
f9917454 SG |
2 | /* |
3 | * Copyright (C) 2015 Google, Inc | |
4 | * Written by Simon Glass <[email protected]> | |
f9917454 SG |
5 | */ |
6 | ||
eb517315 SG |
7 | #define LOG_CATEGORY UCLASS_SYSRESET |
8 | ||
f9917454 | 9 | #include <common.h> |
09140113 | 10 | #include <command.h> |
9a3b4ceb | 11 | #include <cpu_func.h> |
f9917454 SG |
12 | #include <dm.h> |
13 | #include <errno.h> | |
4c66cb4a SG |
14 | #include <hang.h> |
15 | #include <log.h> | |
f9917454 | 16 | #include <regmap.h> |
4c66cb4a SG |
17 | #include <spl.h> |
18 | #include <sysreset.h> | |
f9917454 SG |
19 | #include <dm/device-internal.h> |
20 | #include <dm/lists.h> | |
21 | #include <dm/root.h> | |
c05ed00a | 22 | #include <linux/delay.h> |
f9917454 | 23 | #include <linux/err.h> |
401d1c4f | 24 | #include <asm/global_data.h> |
f9917454 | 25 | |
11636258 | 26 | int sysreset_request(struct udevice *dev, enum sysreset_t type) |
f9917454 | 27 | { |
11636258 | 28 | struct sysreset_ops *ops = sysreset_get_ops(dev); |
f9917454 SG |
29 | |
30 | if (!ops->request) | |
31 | return -ENOSYS; | |
32 | ||
33 | return ops->request(dev, type); | |
34 | } | |
35 | ||
245f5cda MS |
36 | int sysreset_get_status(struct udevice *dev, char *buf, int size) |
37 | { | |
38 | struct sysreset_ops *ops = sysreset_get_ops(dev); | |
39 | ||
40 | if (!ops->get_status) | |
41 | return -ENOSYS; | |
42 | ||
43 | return ops->get_status(dev, buf, size); | |
44 | } | |
45 | ||
751fed42 SG |
46 | int sysreset_get_last(struct udevice *dev) |
47 | { | |
48 | struct sysreset_ops *ops = sysreset_get_ops(dev); | |
49 | ||
50 | if (!ops->get_last) | |
51 | return -ENOSYS; | |
52 | ||
53 | return ops->get_last(dev); | |
54 | } | |
55 | ||
11636258 | 56 | int sysreset_walk(enum sysreset_t type) |
f9917454 SG |
57 | { |
58 | struct udevice *dev; | |
1704d083 | 59 | int ret = -ENOSYS; |
f9917454 | 60 | |
11636258 SW |
61 | while (ret != -EINPROGRESS && type < SYSRESET_COUNT) { |
62 | for (uclass_first_device(UCLASS_SYSRESET, &dev); | |
1704d083 SG |
63 | dev; |
64 | uclass_next_device(&dev)) { | |
11636258 | 65 | ret = sysreset_request(dev, type); |
f9917454 SG |
66 | if (ret == -EINPROGRESS) |
67 | break; | |
68 | } | |
1704d083 | 69 | type++; |
f9917454 SG |
70 | } |
71 | ||
1704d083 SG |
72 | return ret; |
73 | } | |
74 | ||
751fed42 SG |
75 | int sysreset_get_last_walk(void) |
76 | { | |
77 | struct udevice *dev; | |
78 | int value = -ENOENT; | |
79 | ||
80 | for (uclass_first_device(UCLASS_SYSRESET, &dev); | |
81 | dev; | |
82 | uclass_next_device(&dev)) { | |
83 | int ret; | |
84 | ||
85 | ret = sysreset_get_last(dev); | |
86 | if (ret >= 0) { | |
87 | value = ret; | |
88 | break; | |
89 | } | |
90 | } | |
91 | ||
92 | return value; | |
93 | } | |
94 | ||
11636258 | 95 | void sysreset_walk_halt(enum sysreset_t type) |
1704d083 SG |
96 | { |
97 | int ret; | |
98 | ||
11636258 | 99 | ret = sysreset_walk(type); |
1704d083 | 100 | |
f9917454 | 101 | /* Wait for the reset to take effect */ |
1704d083 SG |
102 | if (ret == -EINPROGRESS) |
103 | mdelay(100); | |
f9917454 SG |
104 | |
105 | /* Still no reset? Give up */ | |
4c66cb4a SG |
106 | if (spl_phase() <= PHASE_SPL) |
107 | log_err("no sysreset\n"); | |
108 | else | |
109 | log_err("System reset not supported on this platform\n"); | |
f9917454 SG |
110 | hang(); |
111 | } | |
112 | ||
113 | /** | |
11636258 | 114 | * reset_cpu() - calls sysreset_walk(SYSRESET_WARM) |
f9917454 SG |
115 | */ |
116 | void reset_cpu(ulong addr) | |
117 | { | |
11636258 | 118 | sysreset_walk_halt(SYSRESET_WARM); |
1704d083 SG |
119 | } |
120 | ||
121 | ||
09140113 | 122 | int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) |
1704d083 | 123 | { |
406be398 | 124 | printf("resetting ...\n"); |
e20a6e44 | 125 | mdelay(100); |
406be398 | 126 | |
b53f6992 | 127 | sysreset_walk_halt(SYSRESET_COLD); |
1704d083 SG |
128 | |
129 | return 0; | |
f9917454 SG |
130 | } |
131 | ||
b8050511 | 132 | #if IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) |
09140113 | 133 | int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) |
b8050511 UR |
134 | { |
135 | int ret; | |
136 | ||
137 | puts("poweroff ...\n"); | |
138 | mdelay(100); | |
139 | ||
140 | ret = sysreset_walk(SYSRESET_POWER_OFF); | |
141 | ||
142 | if (ret == -EINPROGRESS) | |
143 | mdelay(1000); | |
144 | ||
145 | /*NOTREACHED when power off*/ | |
146 | return CMD_RET_FAILURE; | |
147 | } | |
148 | #endif | |
149 | ||
758de97b MS |
150 | static int sysreset_post_bind(struct udevice *dev) |
151 | { | |
152 | #if defined(CONFIG_NEEDS_MANUAL_RELOC) | |
153 | struct sysreset_ops *ops = sysreset_get_ops(dev); | |
154 | static int reloc_done; | |
155 | ||
156 | if (!reloc_done) { | |
157 | if (ops->request) | |
158 | ops->request += gd->reloc_off; | |
159 | reloc_done++; | |
160 | } | |
161 | #endif | |
162 | return 0; | |
163 | } | |
164 | ||
11636258 SW |
165 | UCLASS_DRIVER(sysreset) = { |
166 | .id = UCLASS_SYSRESET, | |
167 | .name = "sysreset", | |
758de97b | 168 | .post_bind = sysreset_post_bind, |
f9917454 | 169 | }; |