Coverage Report

Created: 2026-10-13 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/barebox/drivers/power/reset/reboot-mode.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
4
 * Copyright (c) 2019, Ahmad Fatoum, Pengutronix
5
 */
6
7
#include <common.h>
8
#include <driver.h>
9
#include <init.h>
10
#include <of.h>
11
#include <linux/reboot-mode.h>
12
#include <globalvar.h>
13
#include <magicvar.h>
14
15
static int __priority;
16
static struct reboot_mode_driver *__boot_mode;
17
18
static int reboot_mode_param_set(struct param_d *p, void *priv)
19
0
{
20
0
  struct reboot_mode_driver *reboot = priv;
21
0
  size_t i = reboot->reboot_mode_next * reboot->nelems;
22
23
0
  return reboot->write(reboot, &reboot->magics[i]);
24
0
}
25
26
static int reboot_mode_add_param(struct device *dev,
27
         const char *prefix,
28
         struct reboot_mode_driver *reboot)
29
52
{
30
52
  char name[sizeof "system.reboot_mode.when"];
31
52
  struct param_d *param;
32
33
52
  scnprintf(name, sizeof(name), "%sprev", prefix);
34
35
52
  param = dev_add_param_enum_ro(dev, name,
36
52
              &reboot->reboot_mode_prev, reboot->modes,
37
52
              reboot->nmodes);
38
52
  if (IS_ERR(param))
39
0
    return PTR_ERR(param);
40
41
52
  scnprintf(name, sizeof(name), "%snext", prefix);
42
43
52
  param = dev_add_param_enum(dev, name,
44
52
           reboot_mode_param_set, NULL,
45
52
           &reboot->reboot_mode_next, reboot->modes,
46
52
           reboot->nmodes, reboot);
47
48
52
  return PTR_ERR_OR_ZERO(param);
49
52
}
50
51
static int of_reboot_mode_fixup(struct device_node *root, void *ctx)
52
0
{
53
0
  struct reboot_mode_driver *reboot = ctx;
54
0
  struct device_node *dstnp, *srcnp, *dstparent;
55
56
0
  srcnp = reboot->dev->of_node;
57
0
  dstnp = of_get_node_by_reproducible_name(root, srcnp);
58
59
0
  if (dstnp) {
60
0
    dstparent = dstnp->parent;
61
0
    of_delete_node(dstnp);
62
0
  } else {
63
0
    dstparent = of_get_node_by_reproducible_name(root, srcnp->parent);
64
0
  }
65
66
0
  if (!dstparent)
67
0
    return -EINVAL;
68
69
0
  of_copy_node(dstparent, srcnp);
70
71
0
  return 0;
72
0
}
73
74
static __maybe_unused int reboot_mode_add_globalvar(void)
75
26
{
76
26
  struct reboot_mode_driver *reboot = __boot_mode;
77
78
26
  if (!reboot)
79
0
    return 0;
80
81
26
  if (!reboot->no_fixup)
82
0
    of_register_fixup(of_reboot_mode_fixup, reboot);
83
84
26
  return reboot_mode_add_param(&global_device, "system.reboot_mode.", reboot);
85
26
}
86
#ifdef CONFIG_GLOBALVAR
87
late_initcall(reboot_mode_add_globalvar);
88
#endif
89
90
static void reboot_mode_print(struct reboot_mode_driver *reboot,
91
            const char *prefix, const u32 *arr)
92
78
{
93
78
  size_t i;
94
78
  dev_dbg(reboot->dev, "%s: ", prefix);
95
156
  for (i = 0; i < reboot->nelems; i++)
96
78
    __pr_printk(7, "%08x ", arr[i]);
97
78
  __pr_printk(7, "\n");
98
78
}
99
100
static const char *get_mode_name(const struct property *prop)
101
312
{
102
312
  unsigned prefix_len;
103
104
312
  prefix_len = str_has_prefix(prop->name, "mode-");
105
312
  if (!prefix_len)
106
208
    prefix_len = str_has_prefix(prop->name, "barebox,mode-");
107
312
  if (!prefix_len)
108
208
    return NULL;
109
110
104
  return prop->name + prefix_len;
111
312
}
112
113
/**
114
 * reboot_mode_register - register a reboot mode driver
115
 * @reboot: reboot mode driver
116
 * @reboot_mode: reboot mode read from hardware
117
 *
118
 * Returns: 0 on success or a negative error code on failure.
119
 */
120
int reboot_mode_register(struct reboot_mode_driver *reboot,
121
       const u32 *reboot_mode, size_t nelems)
122
26
{
123
26
  struct property *prop;
124
26
  struct device_node *np = reboot->dev->of_node;
125
26
  const char *alias;
126
26
  size_t nmodes = 0;
127
26
  int i = 0;
128
26
  int ret, normal = -1;
129
130
156
  for_each_property_of_node(np, prop) {
131
156
    u32 magic;
132
133
156
    if (!get_mode_name(prop))
134
104
      continue;
135
52
    if (of_property_read_u32(np, prop->name, &magic))
136
0
      continue;
137
138
52
    nmodes++;
139
52
  }
140
141
26
  reboot->nmodes = nmodes;
142
26
  reboot->nelems = nelems;
143
144
  /*
145
   * Allocate one entry more than necessary, because in the loop below
146
   * we use an entry before we realize that the property is not valid.
147
   */
148
26
  reboot->magics = xzalloc((nmodes + 1) * nelems * sizeof(u32));
149
26
  reboot->modes = xzalloc((nmodes + 1) * sizeof(const char *));
150
151
26
  reboot_mode_print(reboot, "registering magic", reboot_mode);
152
153
156
  for_each_property_of_node(np, prop) {
154
156
    const char **mode;
155
156
    u32 *magic;
156
157
156
    magic = &reboot->magics[i * nelems];
158
156
    mode = &reboot->modes[i];
159
160
156
    *mode = get_mode_name(prop);
161
156
    if (!*mode)
162
104
      continue;
163
52
    if (*mode[0] == '\0') {
164
0
      ret = -EINVAL;
165
0
      dev_err(reboot->dev, "invalid mode name(%s): too short!\n",
166
0
        prop->name);
167
0
      goto error;
168
0
    }
169
170
52
    if (!strcmp(*mode, "normal"))
171
26
      normal = i;
172
173
52
    if (of_property_read_u32_array(np, prop->name, magic, nelems)) {
174
0
      dev_err(reboot->dev, "reboot mode %s without magic number\n",
175
0
        *mode);
176
0
      continue;
177
0
    }
178
179
52
    reboot_mode_print(reboot, *mode, magic);
180
181
52
    i++;
182
52
  }
183
184
26
  for (i = 0; i < reboot->nmodes; i++) {
185
26
    if (memcmp(&reboot->magics[i * nelems], reboot_mode, nelems * sizeof(u32)))
186
0
      continue;
187
188
26
    reboot->reboot_mode_prev = i;
189
26
    break;
190
26
  }
191
192
26
  reboot_mode_add_param(reboot->dev, "", reboot);
193
194
  /* clear mode for next reboot */
195
26
  if (normal >= 0)
196
26
    reboot->write(reboot, &reboot->magics[normal]);
197
198
26
  if (!reboot->priority)
199
0
    reboot->priority = REBOOT_MODE_DEFAULT_PRIORITY;
200
201
26
  if (reboot->priority >= __priority) {
202
26
    __priority = reboot->priority;
203
26
    __boot_mode = reboot;
204
26
  }
205
206
207
26
  alias = of_alias_get(np);
208
26
  if (alias)
209
26
    dev_set_name(reboot->dev, "%s", alias);
210
211
26
  return 0;
212
213
0
error:
214
0
  free(reboot->magics);
215
0
  free(reboot->modes);
216
217
0
  return ret;
218
26
}
219
EXPORT_SYMBOL_GPL(reboot_mode_register);
220
221
const char *reboot_mode_get(void)
222
0
{
223
0
  if (!__boot_mode)
224
0
    return NULL;
225
226
0
  return __boot_mode->modes[__boot_mode->reboot_mode_prev];
227
0
}
228
EXPORT_SYMBOL_GPL(reboot_mode_get);
229
230
BAREBOX_MAGICVAR(global.system.reboot_mode.prev,
231
     "reboot-mode: Mode set previously, before barebox start");
232
BAREBOX_MAGICVAR(global.system.reboot_mode.next,
233
     "reboot-mode: Mode to set next, to be evaluated after reset");