122929e12SPatrick Delaunay // SPDX-License-Identifier: GPL-2.0
262030004STien Fong Chee /*
362030004STien Fong Chee * Copyright (C) 2018 Intel Corporation <www.intel.com>
462030004STien Fong Chee *
562030004STien Fong Chee */
662030004STien Fong Chee #include <common.h>
762030004STien Fong Chee #include <dm.h>
862030004STien Fong Chee #include <errno.h>
962030004STien Fong Chee #include <blk.h>
1062030004STien Fong Chee #include <fs.h>
1162030004STien Fong Chee #include <fs_loader.h>
1262030004STien Fong Chee #include <linux/string.h>
1362030004STien Fong Chee #include <mapmem.h>
1462030004STien Fong Chee #include <malloc.h>
1562030004STien Fong Chee #include <spl.h>
1662030004STien Fong Chee
1762030004STien Fong Chee DECLARE_GLOBAL_DATA_PTR;
1862030004STien Fong Chee
19*31a2cf1cSTien Fong Chee /**
20*31a2cf1cSTien Fong Chee * struct firmware - A place for storing firmware and its attribute data.
21*31a2cf1cSTien Fong Chee *
22*31a2cf1cSTien Fong Chee * This holds information about a firmware and its content.
23*31a2cf1cSTien Fong Chee *
24*31a2cf1cSTien Fong Chee * @size: Size of a file
25*31a2cf1cSTien Fong Chee * @data: Buffer for file
26*31a2cf1cSTien Fong Chee * @priv: Firmware loader private fields
27*31a2cf1cSTien Fong Chee * @name: Filename
28*31a2cf1cSTien Fong Chee * @offset: Offset of reading a file
29*31a2cf1cSTien Fong Chee */
30*31a2cf1cSTien Fong Chee struct firmware {
31*31a2cf1cSTien Fong Chee size_t size;
32*31a2cf1cSTien Fong Chee const u8 *data;
33*31a2cf1cSTien Fong Chee const char *name;
34*31a2cf1cSTien Fong Chee u32 offset;
3562030004STien Fong Chee };
3662030004STien Fong Chee
3762030004STien Fong Chee #ifdef CONFIG_CMD_UBIFS
mount_ubifs(char * mtdpart,char * ubivol)3862030004STien Fong Chee static int mount_ubifs(char *mtdpart, char *ubivol)
3962030004STien Fong Chee {
4062030004STien Fong Chee int ret = ubi_part(mtdpart, NULL);
4162030004STien Fong Chee
4262030004STien Fong Chee if (ret) {
4362030004STien Fong Chee debug("Cannot find mtd partition %s\n", mtdpart);
4462030004STien Fong Chee return ret;
4562030004STien Fong Chee }
4662030004STien Fong Chee
4762030004STien Fong Chee return cmd_ubifs_mount(ubivol);
4862030004STien Fong Chee }
4962030004STien Fong Chee
umount_ubifs(void)5062030004STien Fong Chee static int umount_ubifs(void)
5162030004STien Fong Chee {
5262030004STien Fong Chee return cmd_ubifs_umount();
5362030004STien Fong Chee }
5462030004STien Fong Chee #else
mount_ubifs(char * mtdpart,char * ubivol)5562030004STien Fong Chee static int mount_ubifs(char *mtdpart, char *ubivol)
5662030004STien Fong Chee {
5762030004STien Fong Chee debug("Error: Cannot load image: no UBIFS support\n");
5862030004STien Fong Chee return -ENOSYS;
5962030004STien Fong Chee }
6062030004STien Fong Chee #endif
6162030004STien Fong Chee
select_fs_dev(struct device_platdata * plat)6262030004STien Fong Chee static int select_fs_dev(struct device_platdata *plat)
6362030004STien Fong Chee {
6462030004STien Fong Chee int ret;
6562030004STien Fong Chee
6662030004STien Fong Chee if (plat->phandlepart.phandle) {
6762030004STien Fong Chee ofnode node;
6862030004STien Fong Chee
6962030004STien Fong Chee node = ofnode_get_by_phandle(plat->phandlepart.phandle);
7062030004STien Fong Chee
7162030004STien Fong Chee struct udevice *dev;
7262030004STien Fong Chee
737c096ea4SKeerthy ret = device_get_global_by_ofnode(node, &dev);
7462030004STien Fong Chee if (!ret) {
7562030004STien Fong Chee struct blk_desc *desc = blk_get_by_device(dev);
7662030004STien Fong Chee if (desc) {
7762030004STien Fong Chee ret = fs_set_blk_dev_with_part(desc,
7862030004STien Fong Chee plat->phandlepart.partition);
7962030004STien Fong Chee } else {
8062030004STien Fong Chee debug("%s: No device found\n", __func__);
8162030004STien Fong Chee return -ENODEV;
8262030004STien Fong Chee }
8362030004STien Fong Chee }
8462030004STien Fong Chee } else if (plat->mtdpart && plat->ubivol) {
8562030004STien Fong Chee ret = mount_ubifs(plat->mtdpart, plat->ubivol);
8662030004STien Fong Chee if (ret)
8762030004STien Fong Chee return ret;
8862030004STien Fong Chee
8962030004STien Fong Chee ret = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS);
9062030004STien Fong Chee } else {
9162030004STien Fong Chee debug("Error: unsupported storage device.\n");
9262030004STien Fong Chee return -ENODEV;
9362030004STien Fong Chee }
9462030004STien Fong Chee
9562030004STien Fong Chee if (ret)
9662030004STien Fong Chee debug("Error: could not access storage.\n");
9762030004STien Fong Chee
9862030004STien Fong Chee return ret;
9962030004STien Fong Chee }
10062030004STien Fong Chee
10162030004STien Fong Chee /**
10262030004STien Fong Chee * _request_firmware_prepare - Prepare firmware struct.
10362030004STien Fong Chee *
104*31a2cf1cSTien Fong Chee * @dev: An instance of a driver.
10562030004STien Fong Chee * @name: Name of firmware file.
10662030004STien Fong Chee * @dbuf: Address of buffer to load firmware into.
10762030004STien Fong Chee * @size: Size of buffer.
10862030004STien Fong Chee * @offset: Offset of a file for start reading into buffer.
10962030004STien Fong Chee *
11062030004STien Fong Chee * Return: Negative value if fail, 0 for successful.
11162030004STien Fong Chee */
_request_firmware_prepare(struct udevice * dev,const char * name,void * dbuf,size_t size,u32 offset)112*31a2cf1cSTien Fong Chee static int _request_firmware_prepare(struct udevice *dev,
113*31a2cf1cSTien Fong Chee const char *name, void *dbuf,
114*31a2cf1cSTien Fong Chee size_t size, u32 offset)
11562030004STien Fong Chee {
11662030004STien Fong Chee if (!name || name[0] == '\0')
11762030004STien Fong Chee return -EINVAL;
11862030004STien Fong Chee
119*31a2cf1cSTien Fong Chee struct firmware *firmwarep = dev_get_priv(dev);
120*31a2cf1cSTien Fong Chee
121*31a2cf1cSTien Fong Chee if (!firmwarep)
12262030004STien Fong Chee return -ENOMEM;
12362030004STien Fong Chee
124*31a2cf1cSTien Fong Chee firmwarep->name = name;
125*31a2cf1cSTien Fong Chee firmwarep->offset = offset;
126*31a2cf1cSTien Fong Chee firmwarep->data = dbuf;
127*31a2cf1cSTien Fong Chee firmwarep->size = size;
12862030004STien Fong Chee
12962030004STien Fong Chee return 0;
13062030004STien Fong Chee }
13162030004STien Fong Chee
13262030004STien Fong Chee /**
13362030004STien Fong Chee * fw_get_filesystem_firmware - load firmware into an allocated buffer.
134*31a2cf1cSTien Fong Chee * @dev: An instance of a driver.
13562030004STien Fong Chee *
13662030004STien Fong Chee * Return: Size of total read, negative value when error.
13762030004STien Fong Chee */
fw_get_filesystem_firmware(struct udevice * dev)138*31a2cf1cSTien Fong Chee static int fw_get_filesystem_firmware(struct udevice *dev)
13962030004STien Fong Chee {
14062030004STien Fong Chee loff_t actread;
14162030004STien Fong Chee char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume;
14262030004STien Fong Chee int ret;
14362030004STien Fong Chee
14462030004STien Fong Chee storage_interface = env_get("storage_interface");
14562030004STien Fong Chee dev_part = env_get("fw_dev_part");
14662030004STien Fong Chee ubi_mtdpart = env_get("fw_ubi_mtdpart");
14762030004STien Fong Chee ubi_volume = env_get("fw_ubi_volume");
14862030004STien Fong Chee
14962030004STien Fong Chee if (storage_interface && dev_part) {
15062030004STien Fong Chee ret = fs_set_blk_dev(storage_interface, dev_part, FS_TYPE_ANY);
15162030004STien Fong Chee } else if (storage_interface && ubi_mtdpart && ubi_volume) {
15262030004STien Fong Chee ret = mount_ubifs(ubi_mtdpart, ubi_volume);
15362030004STien Fong Chee if (ret)
15462030004STien Fong Chee return ret;
15562030004STien Fong Chee
15662030004STien Fong Chee if (!strcmp("ubi", storage_interface))
15762030004STien Fong Chee ret = fs_set_blk_dev(storage_interface, NULL,
15862030004STien Fong Chee FS_TYPE_UBIFS);
15962030004STien Fong Chee else
16062030004STien Fong Chee ret = -ENODEV;
16162030004STien Fong Chee } else {
162*31a2cf1cSTien Fong Chee ret = select_fs_dev(dev->platdata);
16362030004STien Fong Chee }
16462030004STien Fong Chee
16562030004STien Fong Chee if (ret)
16662030004STien Fong Chee goto out;
16762030004STien Fong Chee
168*31a2cf1cSTien Fong Chee struct firmware *firmwarep = dev_get_priv(dev);
16962030004STien Fong Chee
170*31a2cf1cSTien Fong Chee if (!firmwarep)
171*31a2cf1cSTien Fong Chee return -ENOMEM;
172*31a2cf1cSTien Fong Chee
173*31a2cf1cSTien Fong Chee ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data),
174*31a2cf1cSTien Fong Chee firmwarep->offset, firmwarep->size, &actread);
1757c096ea4SKeerthy
17662030004STien Fong Chee if (ret) {
177907837d6SKeerthy debug("Error: %d Failed to read %s from flash %lld != %zu.\n",
178*31a2cf1cSTien Fong Chee ret, firmwarep->name, actread, firmwarep->size);
17962030004STien Fong Chee } else {
18062030004STien Fong Chee ret = actread;
18162030004STien Fong Chee }
18262030004STien Fong Chee
18362030004STien Fong Chee out:
18462030004STien Fong Chee #ifdef CONFIG_CMD_UBIFS
18562030004STien Fong Chee umount_ubifs();
18662030004STien Fong Chee #endif
18762030004STien Fong Chee return ret;
18862030004STien Fong Chee }
18962030004STien Fong Chee
19062030004STien Fong Chee /**
19162030004STien Fong Chee * request_firmware_into_buf - Load firmware into a previously allocated buffer.
192*31a2cf1cSTien Fong Chee * @dev: An instance of a driver.
19362030004STien Fong Chee * @name: Name of firmware file.
19462030004STien Fong Chee * @buf: Address of buffer to load firmware into.
19562030004STien Fong Chee * @size: Size of buffer.
19662030004STien Fong Chee * @offset: Offset of a file for start reading into buffer.
19762030004STien Fong Chee *
198*31a2cf1cSTien Fong Chee * The firmware is loaded directly into the buffer pointed to by @buf.
19962030004STien Fong Chee *
20062030004STien Fong Chee * Return: Size of total read, negative value when error.
20162030004STien Fong Chee */
request_firmware_into_buf(struct udevice * dev,const char * name,void * buf,size_t size,u32 offset)202*31a2cf1cSTien Fong Chee int request_firmware_into_buf(struct udevice *dev,
20362030004STien Fong Chee const char *name,
204*31a2cf1cSTien Fong Chee void *buf, size_t size, u32 offset)
20562030004STien Fong Chee {
20662030004STien Fong Chee int ret;
20762030004STien Fong Chee
208*31a2cf1cSTien Fong Chee if (!dev)
20962030004STien Fong Chee return -EINVAL;
21062030004STien Fong Chee
211*31a2cf1cSTien Fong Chee ret = _request_firmware_prepare(dev, name, buf, size, offset);
21262030004STien Fong Chee if (ret < 0) /* error */
21362030004STien Fong Chee return ret;
21462030004STien Fong Chee
215*31a2cf1cSTien Fong Chee ret = fw_get_filesystem_firmware(dev);
21662030004STien Fong Chee
21762030004STien Fong Chee return ret;
21862030004STien Fong Chee }
21962030004STien Fong Chee
fs_loader_ofdata_to_platdata(struct udevice * dev)22062030004STien Fong Chee static int fs_loader_ofdata_to_platdata(struct udevice *dev)
22162030004STien Fong Chee {
22262030004STien Fong Chee const char *fs_loader_path;
22362030004STien Fong Chee u32 phandlepart[2];
22462030004STien Fong Chee
22562030004STien Fong Chee fs_loader_path = ofnode_get_chosen_prop("firmware-loader");
22662030004STien Fong Chee
22762030004STien Fong Chee if (fs_loader_path) {
22862030004STien Fong Chee ofnode fs_loader_node;
22962030004STien Fong Chee
23062030004STien Fong Chee fs_loader_node = ofnode_path(fs_loader_path);
23162030004STien Fong Chee if (ofnode_valid(fs_loader_node)) {
23262030004STien Fong Chee struct device_platdata *plat;
23362030004STien Fong Chee plat = dev->platdata;
23462030004STien Fong Chee
23562030004STien Fong Chee if (!ofnode_read_u32_array(fs_loader_node,
23662030004STien Fong Chee "phandlepart",
23762030004STien Fong Chee phandlepart, 2)) {
23862030004STien Fong Chee plat->phandlepart.phandle = phandlepart[0];
23962030004STien Fong Chee plat->phandlepart.partition = phandlepart[1];
24062030004STien Fong Chee }
24162030004STien Fong Chee
24262030004STien Fong Chee plat->mtdpart = (char *)ofnode_read_string(
24362030004STien Fong Chee fs_loader_node, "mtdpart");
24462030004STien Fong Chee
24562030004STien Fong Chee plat->ubivol = (char *)ofnode_read_string(
24662030004STien Fong Chee fs_loader_node, "ubivol");
24762030004STien Fong Chee }
24862030004STien Fong Chee }
24962030004STien Fong Chee
25062030004STien Fong Chee return 0;
25162030004STien Fong Chee }
25262030004STien Fong Chee
fs_loader_probe(struct udevice * dev)25362030004STien Fong Chee static int fs_loader_probe(struct udevice *dev)
25462030004STien Fong Chee {
25562030004STien Fong Chee return 0;
25662030004STien Fong Chee };
25762030004STien Fong Chee
25862030004STien Fong Chee static const struct udevice_id fs_loader_ids[] = {
25962030004STien Fong Chee { .compatible = "u-boot,fs-loader"},
26062030004STien Fong Chee { }
26162030004STien Fong Chee };
26262030004STien Fong Chee
26362030004STien Fong Chee U_BOOT_DRIVER(fs_loader) = {
26462030004STien Fong Chee .name = "fs-loader",
26562030004STien Fong Chee .id = UCLASS_FS_FIRMWARE_LOADER,
26662030004STien Fong Chee .of_match = fs_loader_ids,
26762030004STien Fong Chee .probe = fs_loader_probe,
26862030004STien Fong Chee .ofdata_to_platdata = fs_loader_ofdata_to_platdata,
26962030004STien Fong Chee .platdata_auto_alloc_size = sizeof(struct device_platdata),
270*31a2cf1cSTien Fong Chee .priv_auto_alloc_size = sizeof(struct firmware),
27162030004STien Fong Chee };
27262030004STien Fong Chee
27362030004STien Fong Chee UCLASS_DRIVER(fs_loader) = {
27462030004STien Fong Chee .id = UCLASS_FS_FIRMWARE_LOADER,
27562030004STien Fong Chee .name = "fs-loader",
27662030004STien Fong Chee };
277