blob: 279b087d16defe9107c0510877db5653d2dc0582 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Stephen Warren859f2562016-05-12 12:03:35 -06002/*
3 * Copyright (C) 2015 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
Stephen Warren859f2562016-05-12 12:03:35 -06005 */
6
Simon Glassb8b4d2a2018-10-01 12:22:45 -06007#define LOG_CATEGORY UCLASS_SYSRESET
8
Stephen Warren859f2562016-05-12 12:03:35 -06009#include <common.h>
Simon Glassed38aef2020-05-10 11:40:03 -060010#include <command.h>
Simon Glassafb02152019-12-28 10:45:01 -070011#include <cpu_func.h>
Stephen Warren859f2562016-05-12 12:03:35 -060012#include <dm.h>
13#include <errno.h>
Simon Glass12905d62020-12-23 08:11:14 -070014#include <hang.h>
15#include <log.h>
Stephen Warren859f2562016-05-12 12:03:35 -060016#include <regmap.h>
Simon Glass12905d62020-12-23 08:11:14 -070017#include <spl.h>
18#include <sysreset.h>
Stephen Warren859f2562016-05-12 12:03:35 -060019#include <dm/device-internal.h>
20#include <dm/lists.h>
21#include <dm/root.h>
Simon Glassdbd79542020-05-10 11:40:11 -060022#include <linux/delay.h>
Stephen Warren859f2562016-05-12 12:03:35 -060023#include <linux/err.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060024#include <asm/global_data.h>
Stephen Warren859f2562016-05-12 12:03:35 -060025
26int sysreset_request(struct udevice *dev, enum sysreset_t type)
27{
28 struct sysreset_ops *ops = sysreset_get_ops(dev);
29
30 if (!ops->request)
31 return -ENOSYS;
32
33 return ops->request(dev, type);
34}
35
Mario Six265f82a2018-08-06 10:23:32 +020036int 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
Simon Glass07fbba52018-10-01 12:22:46 -060046int 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
Stephen Warren859f2562016-05-12 12:03:35 -060056int sysreset_walk(enum sysreset_t type)
57{
58 struct udevice *dev;
59 int ret = -ENOSYS;
60
61 while (ret != -EINPROGRESS && type < SYSRESET_COUNT) {
62 for (uclass_first_device(UCLASS_SYSRESET, &dev);
63 dev;
64 uclass_next_device(&dev)) {
65 ret = sysreset_request(dev, type);
66 if (ret == -EINPROGRESS)
67 break;
68 }
69 type++;
70 }
71
72 return ret;
73}
74
Simon Glass07fbba52018-10-01 12:22:46 -060075int 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
Stephen Warren859f2562016-05-12 12:03:35 -060095void sysreset_walk_halt(enum sysreset_t type)
96{
97 int ret;
98
99 ret = sysreset_walk(type);
100
101 /* Wait for the reset to take effect */
102 if (ret == -EINPROGRESS)
103 mdelay(100);
104
105 /* Still no reset? Give up */
Simon Glass12905d62020-12-23 08:11:14 -0700106 if (spl_phase() <= PHASE_SPL)
107 log_err("no sysreset\n");
108 else
109 log_err("System reset not supported on this platform\n");
Stephen Warren859f2562016-05-12 12:03:35 -0600110 hang();
111}
112
113/**
114 * reset_cpu() - calls sysreset_walk(SYSRESET_WARM)
115 */
Harald Seiler6f14d5f2020-12-15 16:47:52 +0100116void reset_cpu(void)
Stephen Warren859f2562016-05-12 12:03:35 -0600117{
118 sysreset_walk_halt(SYSRESET_WARM);
119}
120
121
Bin Menga0aba782021-02-25 17:22:52 +0800122#if IS_ENABLED(CONFIG_SYSRESET_CMD_RESET)
Simon Glassed38aef2020-05-10 11:40:03 -0600123int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Stephen Warren859f2562016-05-12 12:03:35 -0600124{
Igor Opaniuk814f3032021-04-01 02:01:55 +0300125 enum sysreset_t reset_type = SYSRESET_COLD;
126
127 if (argc > 2)
128 return CMD_RET_USAGE;
129
130 if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'w') {
131 reset_type = SYSRESET_WARM;
132 }
133
Bin Meng5588e8c2018-07-19 03:07:31 -0700134 printf("resetting ...\n");
Heinrich Schuchardt22242602020-07-29 12:13:41 +0200135 mdelay(100);
Bin Meng5588e8c2018-07-19 03:07:31 -0700136
Igor Opaniuk814f3032021-04-01 02:01:55 +0300137 sysreset_walk_halt(reset_type);
Stephen Warren859f2562016-05-12 12:03:35 -0600138
139 return 0;
140}
Bin Menga0aba782021-02-25 17:22:52 +0800141#endif
Stephen Warren859f2562016-05-12 12:03:35 -0600142
Urja Rannikkoe008ebf2019-05-16 21:48:42 +0000143#if IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF)
Simon Glassed38aef2020-05-10 11:40:03 -0600144int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Urja Rannikkoe008ebf2019-05-16 21:48:42 +0000145{
146 int ret;
147
148 puts("poweroff ...\n");
149 mdelay(100);
150
151 ret = sysreset_walk(SYSRESET_POWER_OFF);
152
153 if (ret == -EINPROGRESS)
154 mdelay(1000);
155
156 /*NOTREACHED when power off*/
157 return CMD_RET_FAILURE;
158}
159#endif
160
Michal Simek28d3deb2018-07-12 10:36:07 +0200161static int sysreset_post_bind(struct udevice *dev)
162{
163#if defined(CONFIG_NEEDS_MANUAL_RELOC)
164 struct sysreset_ops *ops = sysreset_get_ops(dev);
165 static int reloc_done;
166
167 if (!reloc_done) {
168 if (ops->request)
169 ops->request += gd->reloc_off;
170 reloc_done++;
171 }
172#endif
173 return 0;
174}
175
Stephen Warren859f2562016-05-12 12:03:35 -0600176UCLASS_DRIVER(sysreset) = {
177 .id = UCLASS_SYSRESET,
178 .name = "sysreset",
Michal Simek28d3deb2018-07-12 10:36:07 +0200179 .post_bind = sysreset_post_bind,
Stephen Warren859f2562016-05-12 12:03:35 -0600180};