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 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 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 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 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 */ 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 */ 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 */ 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 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 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