]>
Commit | Line | Data |
---|---|---|
22929e12 PD |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
c1ef736e | 3 | * Copyright (C) 2018-2019 Intel Corporation <www.intel.com> |
62030004 | 4 | * |
62030004 TFC |
5 | */ |
6 | #include <common.h> | |
7 | #include <dm.h> | |
7b51b576 | 8 | #include <env.h> |
62030004 TFC |
9 | #include <errno.h> |
10 | #include <blk.h> | |
11 | #include <fs.h> | |
12 | #include <fs_loader.h> | |
f7ae49fc | 13 | #include <log.h> |
401d1c4f | 14 | #include <asm/global_data.h> |
62030004 TFC |
15 | #include <linux/string.h> |
16 | #include <mapmem.h> | |
17 | #include <malloc.h> | |
18 | #include <spl.h> | |
19 | ||
20 | DECLARE_GLOBAL_DATA_PTR; | |
21 | ||
31a2cf1c TFC |
22 | /** |
23 | * struct firmware - A place for storing firmware and its attribute data. | |
24 | * | |
25 | * This holds information about a firmware and its content. | |
26 | * | |
27 | * @size: Size of a file | |
28 | * @data: Buffer for file | |
29 | * @priv: Firmware loader private fields | |
30 | * @name: Filename | |
31 | * @offset: Offset of reading a file | |
32 | */ | |
33 | struct firmware { | |
34 | size_t size; | |
35 | const u8 *data; | |
36 | const char *name; | |
37 | u32 offset; | |
62030004 TFC |
38 | }; |
39 | ||
40 | #ifdef CONFIG_CMD_UBIFS | |
41 | static int mount_ubifs(char *mtdpart, char *ubivol) | |
42 | { | |
43 | int ret = ubi_part(mtdpart, NULL); | |
44 | ||
45 | if (ret) { | |
46 | debug("Cannot find mtd partition %s\n", mtdpart); | |
47 | return ret; | |
48 | } | |
49 | ||
50 | return cmd_ubifs_mount(ubivol); | |
51 | } | |
52 | ||
53 | static int umount_ubifs(void) | |
54 | { | |
55 | return cmd_ubifs_umount(); | |
56 | } | |
57 | #else | |
58 | static int mount_ubifs(char *mtdpart, char *ubivol) | |
59 | { | |
60 | debug("Error: Cannot load image: no UBIFS support\n"); | |
61 | return -ENOSYS; | |
62 | } | |
63 | #endif | |
64 | ||
8a8d24bd | 65 | static int select_fs_dev(struct device_plat *plat) |
62030004 TFC |
66 | { |
67 | int ret; | |
68 | ||
69 | if (plat->phandlepart.phandle) { | |
70 | ofnode node; | |
71 | ||
72 | node = ofnode_get_by_phandle(plat->phandlepart.phandle); | |
73 | ||
62030004 TFC |
74 | struct udevice *dev; |
75 | ||
7c096ea4 | 76 | ret = device_get_global_by_ofnode(node, &dev); |
62030004 TFC |
77 | if (!ret) { |
78 | struct blk_desc *desc = blk_get_by_device(dev); | |
79 | if (desc) { | |
80 | ret = fs_set_blk_dev_with_part(desc, | |
81 | plat->phandlepart.partition); | |
82 | } else { | |
83 | debug("%s: No device found\n", __func__); | |
84 | return -ENODEV; | |
85 | } | |
86 | } | |
87 | } else if (plat->mtdpart && plat->ubivol) { | |
88 | ret = mount_ubifs(plat->mtdpart, plat->ubivol); | |
89 | if (ret) | |
90 | return ret; | |
91 | ||
92 | ret = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS); | |
93 | } else { | |
94 | debug("Error: unsupported storage device.\n"); | |
95 | return -ENODEV; | |
96 | } | |
97 | ||
98 | if (ret) | |
99 | debug("Error: could not access storage.\n"); | |
100 | ||
101 | return ret; | |
102 | } | |
103 | ||
104 | /** | |
105 | * _request_firmware_prepare - Prepare firmware struct. | |
106 | * | |
31a2cf1c | 107 | * @dev: An instance of a driver. |
62030004 TFC |
108 | * @name: Name of firmware file. |
109 | * @dbuf: Address of buffer to load firmware into. | |
110 | * @size: Size of buffer. | |
111 | * @offset: Offset of a file for start reading into buffer. | |
62030004 TFC |
112 | * |
113 | * Return: Negative value if fail, 0 for successful. | |
114 | */ | |
31a2cf1c TFC |
115 | static int _request_firmware_prepare(struct udevice *dev, |
116 | const char *name, void *dbuf, | |
117 | size_t size, u32 offset) | |
62030004 TFC |
118 | { |
119 | if (!name || name[0] == '\0') | |
120 | return -EINVAL; | |
121 | ||
31a2cf1c | 122 | struct firmware *firmwarep = dev_get_priv(dev); |
62030004 | 123 | |
31a2cf1c TFC |
124 | if (!firmwarep) |
125 | return -ENOMEM; | |
62030004 | 126 | |
31a2cf1c TFC |
127 | firmwarep->name = name; |
128 | firmwarep->offset = offset; | |
129 | firmwarep->data = dbuf; | |
130 | firmwarep->size = size; | |
62030004 TFC |
131 | |
132 | return 0; | |
133 | } | |
134 | ||
62030004 TFC |
135 | /** |
136 | * fw_get_filesystem_firmware - load firmware into an allocated buffer. | |
31a2cf1c | 137 | * @dev: An instance of a driver. |
62030004 TFC |
138 | * |
139 | * Return: Size of total read, negative value when error. | |
140 | */ | |
31a2cf1c | 141 | static int fw_get_filesystem_firmware(struct udevice *dev) |
62030004 | 142 | { |
62030004 TFC |
143 | loff_t actread; |
144 | char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume; | |
145 | int ret; | |
146 | ||
147 | storage_interface = env_get("storage_interface"); | |
148 | dev_part = env_get("fw_dev_part"); | |
149 | ubi_mtdpart = env_get("fw_ubi_mtdpart"); | |
150 | ubi_volume = env_get("fw_ubi_volume"); | |
151 | ||
152 | if (storage_interface && dev_part) { | |
153 | ret = fs_set_blk_dev(storage_interface, dev_part, FS_TYPE_ANY); | |
154 | } else if (storage_interface && ubi_mtdpart && ubi_volume) { | |
155 | ret = mount_ubifs(ubi_mtdpart, ubi_volume); | |
156 | if (ret) | |
157 | return ret; | |
158 | ||
159 | if (!strcmp("ubi", storage_interface)) | |
160 | ret = fs_set_blk_dev(storage_interface, NULL, | |
161 | FS_TYPE_UBIFS); | |
162 | else | |
163 | ret = -ENODEV; | |
164 | } else { | |
0fd3d911 | 165 | ret = select_fs_dev(dev_get_plat(dev)); |
62030004 TFC |
166 | } |
167 | ||
168 | if (ret) | |
169 | goto out; | |
170 | ||
31a2cf1c TFC |
171 | struct firmware *firmwarep = dev_get_priv(dev); |
172 | ||
173 | if (!firmwarep) | |
174 | return -ENOMEM; | |
62030004 | 175 | |
31a2cf1c TFC |
176 | ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data), |
177 | firmwarep->offset, firmwarep->size, &actread); | |
7c096ea4 | 178 | |
62030004 | 179 | if (ret) { |
907837d6 | 180 | debug("Error: %d Failed to read %s from flash %lld != %zu.\n", |
31a2cf1c | 181 | ret, firmwarep->name, actread, firmwarep->size); |
62030004 TFC |
182 | } else { |
183 | ret = actread; | |
184 | } | |
185 | ||
186 | out: | |
187 | #ifdef CONFIG_CMD_UBIFS | |
188 | umount_ubifs(); | |
189 | #endif | |
190 | return ret; | |
191 | } | |
192 | ||
193 | /** | |
194 | * request_firmware_into_buf - Load firmware into a previously allocated buffer. | |
31a2cf1c | 195 | * @dev: An instance of a driver. |
62030004 TFC |
196 | * @name: Name of firmware file. |
197 | * @buf: Address of buffer to load firmware into. | |
198 | * @size: Size of buffer. | |
199 | * @offset: Offset of a file for start reading into buffer. | |
62030004 | 200 | * |
31a2cf1c | 201 | * The firmware is loaded directly into the buffer pointed to by @buf. |
62030004 TFC |
202 | * |
203 | * Return: Size of total read, negative value when error. | |
204 | */ | |
31a2cf1c | 205 | int request_firmware_into_buf(struct udevice *dev, |
62030004 | 206 | const char *name, |
31a2cf1c | 207 | void *buf, size_t size, u32 offset) |
62030004 TFC |
208 | { |
209 | int ret; | |
210 | ||
31a2cf1c | 211 | if (!dev) |
62030004 TFC |
212 | return -EINVAL; |
213 | ||
31a2cf1c | 214 | ret = _request_firmware_prepare(dev, name, buf, size, offset); |
62030004 TFC |
215 | if (ret < 0) /* error */ |
216 | return ret; | |
217 | ||
31a2cf1c | 218 | ret = fw_get_filesystem_firmware(dev); |
62030004 TFC |
219 | |
220 | return ret; | |
221 | } | |
222 | ||
d1998a9f | 223 | static int fs_loader_of_to_plat(struct udevice *dev) |
62030004 | 224 | { |
62030004 TFC |
225 | u32 phandlepart[2]; |
226 | ||
c1ef736e | 227 | ofnode fs_loader_node = dev_ofnode(dev); |
62030004 | 228 | |
c1ef736e | 229 | if (ofnode_valid(fs_loader_node)) { |
8a8d24bd | 230 | struct device_plat *plat; |
62030004 | 231 | |
0fd3d911 | 232 | plat = dev_get_plat(dev); |
c1ef736e TFC |
233 | if (!ofnode_read_u32_array(fs_loader_node, |
234 | "phandlepart", | |
235 | phandlepart, 2)) { | |
236 | plat->phandlepart.phandle = phandlepart[0]; | |
237 | plat->phandlepart.partition = phandlepart[1]; | |
238 | } | |
62030004 | 239 | |
c1ef736e TFC |
240 | plat->mtdpart = (char *)ofnode_read_string( |
241 | fs_loader_node, "mtdpart"); | |
62030004 | 242 | |
c1ef736e TFC |
243 | plat->ubivol = (char *)ofnode_read_string( |
244 | fs_loader_node, "ubivol"); | |
62030004 TFC |
245 | } |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
250 | static int fs_loader_probe(struct udevice *dev) | |
251 | { | |
db32a446 TFC |
252 | #if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(BLK) |
253 | int ret; | |
0fd3d911 | 254 | struct device_plat *plat = dev_get_plat(dev); |
db32a446 TFC |
255 | |
256 | if (plat->phandlepart.phandle) { | |
257 | ofnode node = ofnode_get_by_phandle(plat->phandlepart.phandle); | |
258 | struct udevice *parent_dev = NULL; | |
259 | ||
260 | ret = device_get_global_by_ofnode(node, &parent_dev); | |
261 | if (!ret) { | |
262 | struct udevice *dev; | |
263 | ||
264 | ret = blk_get_from_parent(parent_dev, &dev); | |
265 | if (ret) { | |
266 | debug("fs_loader: No block device: %d\n", | |
267 | ret); | |
268 | ||
269 | return ret; | |
270 | } | |
271 | } | |
272 | } | |
273 | #endif | |
274 | ||
62030004 TFC |
275 | return 0; |
276 | }; | |
277 | ||
278 | static const struct udevice_id fs_loader_ids[] = { | |
279 | { .compatible = "u-boot,fs-loader"}, | |
280 | { } | |
281 | }; | |
282 | ||
283 | U_BOOT_DRIVER(fs_loader) = { | |
284 | .name = "fs-loader", | |
285 | .id = UCLASS_FS_FIRMWARE_LOADER, | |
286 | .of_match = fs_loader_ids, | |
287 | .probe = fs_loader_probe, | |
d1998a9f | 288 | .of_to_plat = fs_loader_of_to_plat, |
8a8d24bd | 289 | .plat_auto = sizeof(struct device_plat), |
41575d8e | 290 | .priv_auto = sizeof(struct firmware), |
62030004 TFC |
291 | }; |
292 | ||
293 | UCLASS_DRIVER(fs_loader) = { | |
294 | .id = UCLASS_FS_FIRMWARE_LOADER, | |
295 | .name = "fs-loader", | |
296 | }; |