blob: 9e990339a6c4a6f645aba9e34240c9837e328b00 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Stephen Warren11636252016-05-12 12:03:35 -06002/*
3 * Copyright (C) 2015 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
Stephen Warren11636252016-05-12 12:03:35 -06005 */
6
Simon Glasseb517312018-10-01 12:22:45 -06007#define LOG_CATEGORY UCLASS_SYSRESET
8
Stephen Warren11636252016-05-12 12:03:35 -06009#include <common.h>
Simon Glass09140112020-05-10 11:40:03 -060010#include <command.h>
Simon Glass9a3b4ce2019-12-28 10:45:01 -070011#include <cpu_func.h>
Simon Glassdb41d652019-12-28 10:45:07 -070012#include <hang.h>
Stephen Warren11636252016-05-12 12:03:35 -060013#include <sysreset.h>
14#include <dm.h>
15#include <errno.h>
16#include <regmap.h>
17#include <dm/device-internal.h>
18#include <dm/lists.h>
19#include <dm/root.h>
20#include <linux/err.h>
21
22int sysreset_request(struct udevice *dev, enum sysreset_t type)
23{
24 struct sysreset_ops *ops = sysreset_get_ops(dev);
25
26 if (!ops->request)
27 return -ENOSYS;
28
29 return ops->request(dev, type);
30}
31
Mario Six245f5cd2018-08-06 10:23:32 +020032int sysreset_get_status(struct udevice *dev, char *buf, int size)
33{
34 struct sysreset_ops *ops = sysreset_get_ops(dev);
35
36 if (!ops->get_status)
37 return -ENOSYS;
38
39 return ops->get_status(dev, buf, size);
40}
41
Simon Glass751fed42018-10-01 12:22:46 -060042int sysreset_get_last(struct udevice *dev)
43{
44 struct sysreset_ops *ops = sysreset_get_ops(dev);
45
46 if (!ops->get_last)
47 return -ENOSYS;
48
49 return ops->get_last(dev);
50}
51
Stephen Warren11636252016-05-12 12:03:35 -060052int sysreset_walk(enum sysreset_t type)
53{
54 struct udevice *dev;
55 int ret = -ENOSYS;
56
57 while (ret != -EINPROGRESS && type < SYSRESET_COUNT) {
58 for (uclass_first_device(UCLASS_SYSRESET, &dev);
59 dev;
60 uclass_next_device(&dev)) {
61 ret = sysreset_request(dev, type);
62 if (ret == -EINPROGRESS)
63 break;
64 }
65 type++;
66 }
67
68 return ret;
69}
70
Simon Glass751fed42018-10-01 12:22:46 -060071int sysreset_get_last_walk(void)
72{
73 struct udevice *dev;
74 int value = -ENOENT;
75
76 for (uclass_first_device(UCLASS_SYSRESET, &dev);
77 dev;
78 uclass_next_device(&dev)) {
79 int ret;
80
81 ret = sysreset_get_last(dev);
82 if (ret >= 0) {
83 value = ret;
84 break;
85 }
86 }
87
88 return value;
89}
90
Stephen Warren11636252016-05-12 12:03:35 -060091void sysreset_walk_halt(enum sysreset_t type)
92{
93 int ret;
94
95 ret = sysreset_walk(type);
96
97 /* Wait for the reset to take effect */
98 if (ret == -EINPROGRESS)
99 mdelay(100);
100
101 /* Still no reset? Give up */
Simon Glasseb517312018-10-01 12:22:45 -0600102 log_err("System reset not supported on this platform\n");
Stephen Warren11636252016-05-12 12:03:35 -0600103 hang();
104}
105
106/**
107 * reset_cpu() - calls sysreset_walk(SYSRESET_WARM)
108 */
109void reset_cpu(ulong addr)
110{
111 sysreset_walk_halt(SYSRESET_WARM);
112}
113
114
Simon Glass09140112020-05-10 11:40:03 -0600115int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Stephen Warren11636252016-05-12 12:03:35 -0600116{
Bin Meng406be392018-07-19 03:07:31 -0700117 printf("resetting ...\n");
118
Philipp Tomsichb53f69922017-11-24 18:37:58 +0100119 sysreset_walk_halt(SYSRESET_COLD);
Stephen Warren11636252016-05-12 12:03:35 -0600120
121 return 0;
122}
123
Urja Rannikkob8050512019-05-16 21:48:42 +0000124#if IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF)
Simon Glass09140112020-05-10 11:40:03 -0600125int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Urja Rannikkob8050512019-05-16 21:48:42 +0000126{
127 int ret;
128
129 puts("poweroff ...\n");
130 mdelay(100);
131
132 ret = sysreset_walk(SYSRESET_POWER_OFF);
133
134 if (ret == -EINPROGRESS)
135 mdelay(1000);
136
137 /*NOTREACHED when power off*/
138 return CMD_RET_FAILURE;
139}
140#endif
141
Michal Simek758de972018-07-12 10:36:07 +0200142static int sysreset_post_bind(struct udevice *dev)
143{
144#if defined(CONFIG_NEEDS_MANUAL_RELOC)
145 struct sysreset_ops *ops = sysreset_get_ops(dev);
146 static int reloc_done;
147
148 if (!reloc_done) {
149 if (ops->request)
150 ops->request += gd->reloc_off;
151 reloc_done++;
152 }
153#endif
154 return 0;
155}
156
Stephen Warren11636252016-05-12 12:03:35 -0600157UCLASS_DRIVER(sysreset) = {
158 .id = UCLASS_SYSRESET,
159 .name = "sysreset",
Michal Simek758de972018-07-12 10:36:07 +0200160 .post_bind = sysreset_post_bind,
Stephen Warren11636252016-05-12 12:03:35 -0600161};