blob: 3f5414ed1f8a5660c76daabd77bb776d6ce1fff8 [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>
Simon Glassf7ae49f2020-05-10 11:40:05 -060013#include <log.h>
Stephen Warren11636252016-05-12 12:03:35 -060014#include <sysreset.h>
15#include <dm.h>
16#include <errno.h>
17#include <regmap.h>
18#include <dm/device-internal.h>
19#include <dm/lists.h>
20#include <dm/root.h>
Simon Glassc05ed002020-05-10 11:40:11 -060021#include <linux/delay.h>
Stephen Warren11636252016-05-12 12:03:35 -060022#include <linux/err.h>
23
24int sysreset_request(struct udevice *dev, enum sysreset_t type)
25{
26 struct sysreset_ops *ops = sysreset_get_ops(dev);
27
28 if (!ops->request)
29 return -ENOSYS;
30
31 return ops->request(dev, type);
32}
33
Mario Six245f5cd2018-08-06 10:23:32 +020034int sysreset_get_status(struct udevice *dev, char *buf, int size)
35{
36 struct sysreset_ops *ops = sysreset_get_ops(dev);
37
38 if (!ops->get_status)
39 return -ENOSYS;
40
41 return ops->get_status(dev, buf, size);
42}
43
Simon Glass751fed42018-10-01 12:22:46 -060044int sysreset_get_last(struct udevice *dev)
45{
46 struct sysreset_ops *ops = sysreset_get_ops(dev);
47
48 if (!ops->get_last)
49 return -ENOSYS;
50
51 return ops->get_last(dev);
52}
53
Stephen Warren11636252016-05-12 12:03:35 -060054int sysreset_walk(enum sysreset_t type)
55{
56 struct udevice *dev;
57 int ret = -ENOSYS;
58
59 while (ret != -EINPROGRESS && type < SYSRESET_COUNT) {
60 for (uclass_first_device(UCLASS_SYSRESET, &dev);
61 dev;
62 uclass_next_device(&dev)) {
63 ret = sysreset_request(dev, type);
64 if (ret == -EINPROGRESS)
65 break;
66 }
67 type++;
68 }
69
70 return ret;
71}
72
Simon Glass751fed42018-10-01 12:22:46 -060073int sysreset_get_last_walk(void)
74{
75 struct udevice *dev;
76 int value = -ENOENT;
77
78 for (uclass_first_device(UCLASS_SYSRESET, &dev);
79 dev;
80 uclass_next_device(&dev)) {
81 int ret;
82
83 ret = sysreset_get_last(dev);
84 if (ret >= 0) {
85 value = ret;
86 break;
87 }
88 }
89
90 return value;
91}
92
Stephen Warren11636252016-05-12 12:03:35 -060093void sysreset_walk_halt(enum sysreset_t type)
94{
95 int ret;
96
97 ret = sysreset_walk(type);
98
99 /* Wait for the reset to take effect */
100 if (ret == -EINPROGRESS)
101 mdelay(100);
102
103 /* Still no reset? Give up */
Simon Glasseb517312018-10-01 12:22:45 -0600104 log_err("System reset not supported on this platform\n");
Stephen Warren11636252016-05-12 12:03:35 -0600105 hang();
106}
107
108/**
109 * reset_cpu() - calls sysreset_walk(SYSRESET_WARM)
110 */
111void reset_cpu(ulong addr)
112{
113 sysreset_walk_halt(SYSRESET_WARM);
114}
115
116
Simon Glass09140112020-05-10 11:40:03 -0600117int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Stephen Warren11636252016-05-12 12:03:35 -0600118{
Bin Meng406be392018-07-19 03:07:31 -0700119 printf("resetting ...\n");
Heinrich Schuchardte20a6e42020-07-29 12:13:41 +0200120 mdelay(100);
Bin Meng406be392018-07-19 03:07:31 -0700121
Philipp Tomsichb53f69922017-11-24 18:37:58 +0100122 sysreset_walk_halt(SYSRESET_COLD);
Stephen Warren11636252016-05-12 12:03:35 -0600123
124 return 0;
125}
126
Urja Rannikkob8050512019-05-16 21:48:42 +0000127#if IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF)
Simon Glass09140112020-05-10 11:40:03 -0600128int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Urja Rannikkob8050512019-05-16 21:48:42 +0000129{
130 int ret;
131
132 puts("poweroff ...\n");
133 mdelay(100);
134
135 ret = sysreset_walk(SYSRESET_POWER_OFF);
136
137 if (ret == -EINPROGRESS)
138 mdelay(1000);
139
140 /*NOTREACHED when power off*/
141 return CMD_RET_FAILURE;
142}
143#endif
144
Michal Simek758de972018-07-12 10:36:07 +0200145static int sysreset_post_bind(struct udevice *dev)
146{
147#if defined(CONFIG_NEEDS_MANUAL_RELOC)
148 struct sysreset_ops *ops = sysreset_get_ops(dev);
149 static int reloc_done;
150
151 if (!reloc_done) {
152 if (ops->request)
153 ops->request += gd->reloc_off;
154 reloc_done++;
155 }
156#endif
157 return 0;
158}
159
Stephen Warren11636252016-05-12 12:03:35 -0600160UCLASS_DRIVER(sysreset) = {
161 .id = UCLASS_SYSRESET,
162 .name = "sysreset",
Michal Simek758de972018-07-12 10:36:07 +0200163 .post_bind = sysreset_post_bind,
Stephen Warren11636252016-05-12 12:03:35 -0600164};