blob: 565c22a36e78a5cc986df44efc0de297af6eb412 [file] [log] [blame]
Simon Glassef5e3892022-04-24 23:31:06 -06001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Uclass implementation for standard boot
4 *
5 * Copyright 2021 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
7 */
8
9#include <common.h>
10#include <bootflow.h>
11#include <bootstd.h>
12#include <dm.h>
13#include <log.h>
14#include <malloc.h>
15#include <dm/device-internal.h>
16#include <dm/lists.h>
17#include <dm/read.h>
18#include <dm/uclass-internal.h>
19
20DECLARE_GLOBAL_DATA_PTR;
21
22/* These are used if filename-prefixes is not present */
23const char *const default_prefixes[] = {"/", "/boot/", NULL};
24
25static int bootstd_of_to_plat(struct udevice *dev)
26{
27 struct bootstd_priv *priv = dev_get_priv(dev);
28 int ret;
29
30 if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
31 /* Don't check errors since livetree and flattree are different */
32 ret = dev_read_string_list(dev, "filename-prefixes",
33 &priv->prefixes);
34 dev_read_string_list(dev, "bootdev-order",
35 &priv->bootdev_order);
36 }
37
38 return 0;
39}
40
41static void bootstd_clear_glob_(struct bootstd_priv *priv)
42{
43 while (!list_empty(&priv->glob_head)) {
44 struct bootflow *bflow;
45
46 bflow = list_first_entry(&priv->glob_head, struct bootflow,
47 glob_node);
Simon Glassa8f5be12022-04-24 23:31:09 -060048 bootflow_remove(bflow);
Simon Glassef5e3892022-04-24 23:31:06 -060049 }
50}
51
52void bootstd_clear_glob(void)
53{
54 struct bootstd_priv *std;
55
56 if (bootstd_get_priv(&std))
57 return;
58
59 bootstd_clear_glob_(std);
60}
61
62static int bootstd_remove(struct udevice *dev)
63{
64 struct bootstd_priv *priv = dev_get_priv(dev);
65
66 free(priv->prefixes);
67 free(priv->bootdev_order);
68 bootstd_clear_glob_(priv);
69
70 return 0;
71}
72
73const char *const *const bootstd_get_bootdev_order(struct udevice *dev)
74{
75 struct bootstd_priv *std = dev_get_priv(dev);
76
77 return std->bootdev_order;
78}
79
80const char *const *const bootstd_get_prefixes(struct udevice *dev)
81{
82 struct bootstd_priv *std = dev_get_priv(dev);
83
84 return std->prefixes ? std->prefixes : default_prefixes;
85}
86
87int bootstd_get_priv(struct bootstd_priv **stdp)
88{
89 struct udevice *dev;
90 int ret;
91
92 ret = uclass_first_device_err(UCLASS_BOOTSTD, &dev);
93 if (ret)
94 return ret;
95 *stdp = dev_get_priv(dev);
96
97 return 0;
98}
99
100static int bootstd_probe(struct udevice *dev)
101{
102 struct bootstd_priv *std = dev_get_priv(dev);
103
104 INIT_LIST_HEAD(&std->glob_head);
105
106 return 0;
107}
108
109/* For now, bind the boormethod device if none are found in the devicetree */
110int dm_scan_other(bool pre_reloc_only)
111{
Simon Glassa950d312022-04-24 23:31:08 -0600112 struct driver *drv = ll_entry_start(struct driver, driver);
113 const int n_ents = ll_entry_count(struct driver, driver);
114 struct udevice *dev, *bootstd;
115 int i, ret;
Simon Glassef5e3892022-04-24 23:31:06 -0600116
117 /* These are not needed before relocation */
118 if (!(gd->flags & GD_FLG_RELOC))
119 return 0;
120
121 /* Create a bootstd device if needed */
122 uclass_find_first_device(UCLASS_BOOTSTD, &bootstd);
123 if (!bootstd) {
124 ret = device_bind_driver(gd->dm_root, "bootstd_drv", "bootstd",
125 &bootstd);
126 if (ret)
127 return log_msg_ret("bootstd", ret);
128 }
129
Simon Glassa950d312022-04-24 23:31:08 -0600130 /* If there are no bootmeth devices, create them */
131 uclass_find_first_device(UCLASS_BOOTMETH, &dev);
132 if (dev)
133 return 0;
134
135 for (i = 0; i < n_ents; i++, drv++) {
Simon Glassbd18b692022-07-30 15:52:28 -0600136 if (drv->id == UCLASS_BOOTMETH) {
Simon Glassa950d312022-04-24 23:31:08 -0600137 const char *name = drv->name;
138
139 if (!strncmp("bootmeth_", name, 9))
140 name += 9;
141 ret = device_bind(bootstd, drv, name, 0, ofnode_null(),
142 &dev);
143 if (ret)
144 return log_msg_ret("meth", ret);
145 }
146 }
147
Simon Glassef5e3892022-04-24 23:31:06 -0600148 return 0;
149}
150
151static const struct udevice_id bootstd_ids[] = {
152 { .compatible = "u-boot,boot-std" },
153 { }
154};
155
156U_BOOT_DRIVER(bootstd_drv) = {
157 .id = UCLASS_BOOTSTD,
158 .name = "bootstd_drv",
159 .of_to_plat = bootstd_of_to_plat,
160 .probe = bootstd_probe,
161 .remove = bootstd_remove,
162 .of_match = bootstd_ids,
163 .priv_auto = sizeof(struct bootstd_priv),
164};
165
166UCLASS_DRIVER(bootstd) = {
167 .id = UCLASS_BOOTSTD,
168 .name = "bootstd",
169#if CONFIG_IS_ENABLED(OF_REAL)
170 .post_bind = dm_scan_fdt_dev,
171#endif
172};