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