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