@@ -560,6 +560,16 @@ config DISPLAY_BOARDINFO
when U-Boot starts up. The board function checkboard() is called
to do this.
+config GENERIC_FIRMWARE_LOADER
+ bool "Enable generic firmware loader driver for file system"
+ depends on SPL
+ help
+ This is file system generic loader which can be used to load
+ the file image from the storage into target such as memory.
+
+ The consumer driver would then use this loader to program whatever,
+ ie. the FPGA device.
+
menu "Start-up hooks"
config ARCH_EARLY_INIT_R
@@ -134,3 +134,4 @@ obj-$(CONFIG_$(SPL_)LOG) += log.o
obj-$(CONFIG_$(SPL_)LOG_CONSOLE) += log_console.o
obj-y += s_record.o
obj-y += xyzModem.o
+obj-$(CONFIG_GENERIC_FIRMWARE_LOADER) += fs_loader.o
new file mode 100644
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2018 Intel Corporation <www.intel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <fs.h>
+#include <fs_loader.h>
+#include <nand.h>
+#include <sata.h>
+#include <spi.h>
+#include <spi_flash.h>
+#include <spl.h>
+#include <linux/string.h>
+#ifdef CONFIG_CMD_UBIFS
+#include <ubi_uboot.h>
+#include <ubifs_uboot.h>
+#endif
+#include <usb.h>
+
+struct firmware_priv {
+ const char *name; /* Filename */
+ u32 offset; /* Offset of reading a file */
+};
+
+static struct device_location default_locations[] = {
+ {
+ .name = "mmc",
+ .devpart = "0:1",
+ },
+ {
+ .name = "usb",
+ .devpart = "0:1",
+ },
+ {
+ .name = "sata",
+ .devpart = "0:1",
+ },
+ {
+ .name = "ubi",
+ .mtdpart = "UBI",
+ .ubivol = "ubi0",
+ },
+};
+
+/* USB build is not supported yet in SPL */
+#ifndef CONFIG_SPL_BUILD
+#ifdef CONFIG_USB_STORAGE
+static int init_usb(void)
+{
+ int err;
+
+ err = usb_init();
+ if (err)
+ return err;
+
+#ifndef CONFIG_DM_USB
+ err = usb_stor_scan(0);
+ if (err)
+ return err;
+#endif
+
+ return err;
+}
+#else
+static int init_usb(void)
+{
+ debug("Error: Cannot load flash image: no USB support\n");
+ return -ENOSYS;
+}
+#endif
+#endif
+
+#ifdef CONFIG_SATA
+static int init_storage_sata(struct device_location *location)
+{
+ char *dup_str = NULL;
+ const char *dev_str, *part_str;
+ int dev;
+
+ /* Get device ID */
+ part_str = strchr(location->devpart, ':');
+ if (part_str) {
+ dup_str = strdup(location->devpart);
+ dup_str[part_str - location->devpart] = 0;
+ dev_str = dup_str;
+ part_str++;
+ } else {
+ dev_str = location->devpart;
+ }
+
+ dev = (int)simple_strtoul(dev_str, NULL, 10);
+
+ return sata_probe(dev);
+}
+#else
+static int init_storage_sata(struct device_location *location)
+{
+ debug("Error: Cannot load image: no SATA support\n");
+ return -ENOSYS;
+}
+#endif
+
+#ifdef CONFIG_CMD_UBIFS
+static int mount_ubifs(struct device_location *location)
+{
+ int ret = ubi_part(location->mtdpart, NULL);
+
+ if (ret) {
+ debug("Cannot find mtd partition %s\n", location->mtdpart);
+ return ret;
+ }
+
+ return cmd_ubifs_mount(location->ubivol);
+}
+
+static int umount_ubifs(void)
+{
+ return cmd_ubifs_umount();
+}
+#else
+static int mount_ubifs(struct device_location *location)
+{
+ debug("Error: Cannot load image: no UBIFS support\n");
+ return -ENOSYS;
+}
+#endif
+
+#if defined(CONFIG_SPL_MMC_SUPPORT) && defined(CONFIG_SPL_BUILD)
+__weak int init_mmc(void)
+{
+ /* Just for the case MMC is not yet initialized */
+ struct mmc *mmc = NULL;
+ int err;
+
+#ifdef CONFIG_SPL
+ spl_mmc_find_device(&mmc, spl_boot_device());
+#else
+ return -ENOENT;
+#endif
+
+ err = mmc_init(mmc);
+ if (err) {
+ debug("spl: mmc init failed with error: %d\n", err);
+ return err;
+ }
+
+ return err;
+}
+#else
+__weak int init_mmc(void)
+{
+ /* Expect somewhere already initialize MMC */
+ return 0;
+}
+#endif
+
+static int select_fs_dev(struct device_location *location)
+{
+ int ret;
+
+ if (!strcmp("mmc", location->name)) {
+ ret = fs_set_blk_dev("mmc", location->devpart, FS_TYPE_ANY);
+ } else if (!strcmp("usb", location->name)) {
+ ret = fs_set_blk_dev("usb", location->devpart, FS_TYPE_ANY);
+ } else if (!strcmp("sata", location->name)) {
+ ret = fs_set_blk_dev("sata", location->devpart, FS_TYPE_ANY);
+ } else if (!strcmp("ubi", location->name)) {
+ if (location->ubivol)
+ ret = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS);
+ else
+ ret = -ENODEV;
+ } else {
+ debug("Error: unsupported location storage.\n");
+ return -ENODEV;
+ }
+
+ if (ret)
+ debug("Error: could not access storage.\n");
+
+ return ret;
+}
+
+static int init_storage_device(struct device_location *location)
+{
+ int ret;
+
+ if (!strcmp("mmc", location->name)) {
+ ret = init_mmc();
+ } else if (!strcmp("sata", location->name)) {
+ ret = init_storage_sata(location);
+ } else if (location->ubivol) {
+ ret = mount_ubifs(location);
+#ifndef CONFIG_SPL_BUILD
+ /* USB build is not supported yet in SPL */
+ } else if (!strcmp("usb", location->name)) {
+ ret = init_usb();
+#endif
+ } else {
+ debug("Error: no supported storage device is available.\n");
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+static void set_storage_devpart(char *name, char *devpart)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(default_locations); i++) {
+ if (!strcmp(default_locations[i].name, name))
+ default_locations[i].devpart = devpart;
+ }
+}
+
+static void set_storage_mtdpart(char *name, char *mtdpart)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(default_locations); i++) {
+ if (!strcmp(default_locations[i].name, name))
+ default_locations[i].mtdpart = mtdpart;
+ }
+}
+
+static void set_storage_ubivol(char *name, char *ubivol)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(default_locations); i++) {
+ if (!strcmp(default_locations[i].name, name))
+ default_locations[i].ubivol = ubivol;
+ }
+}
+
+/**
+ * _request_firmware_prepare - Prepare firmware struct.
+ *
+ * @firmware_p: Pointer to pointer to firmware image.
+ * @name: Name of firmware file.
+ * @dbuf: Address of buffer to load firmware into.
+ * @size: Size of buffer.
+ * @offset: Offset of a file for start reading into buffer.
+ *
+ * Return: Negative value if fail, 0 for successful.
+ */
+static int _request_firmware_prepare(struct firmware **firmware_p,
+ const char *name, void *dbuf,
+ size_t size, u32 offset)
+{
+ struct firmware *firmware;
+ struct firmware_priv *fw_priv;
+
+ *firmware_p = NULL;
+
+ if (!name || name[0] == '\0')
+ return -EINVAL;
+
+ firmware = calloc(1, sizeof(*firmware));
+ if (!firmware)
+ return -ENOMEM;
+
+ fw_priv = calloc(1, sizeof(*fw_priv));
+ if (!fw_priv) {
+ free(firmware);
+ return -ENOMEM;
+ }
+
+ fw_priv->name = name;
+ fw_priv->offset = offset;
+ firmware->data = dbuf;
+ firmware->size = size;
+ firmware->priv = fw_priv;
+ *firmware_p = firmware;
+
+ return 0;
+}
+
+/**
+ * fw_get_filesystem_firmware - load firmware into an allocated buffer.
+ * @location: An array of supported firmware location.
+ * @firmware_p: pointer to firmware image.
+ *
+ * Return: Size of total read, negative value when error.
+ */
+static int fw_get_filesystem_firmware(struct device_location *location,
+ struct firmware *firmware_p)
+{
+ struct firmware_priv *fw_priv = NULL;
+ loff_t actread;
+ char *dev_part, *ubi_mtdpart, *ubi_volume;
+ int ret;
+
+ dev_part = env_get("fw_dev_part");
+ if (dev_part)
+ set_storage_devpart(location->name, dev_part);
+
+ ubi_mtdpart = env_get("fw_ubi_mtdpart");
+ if (ubi_mtdpart)
+ set_storage_mtdpart(location->name, ubi_mtdpart);
+
+ ubi_volume = env_get("fw_ubi_volume");
+ if (ubi_volume)
+ set_storage_ubivol(location->name, ubi_volume);
+
+ ret = init_storage_device(location);
+ if (ret)
+ goto out;
+
+ ret = select_fs_dev(location);
+ if (ret)
+ goto out;
+
+ fw_priv = firmware_p->priv;
+
+ ret = fs_read(fw_priv->name, (ulong)firmware_p->data, fw_priv->offset,
+ firmware_p->size, &actread);
+ if (ret) {
+ debug("Error: %d Failed to read %s from flash %lld != %d.\n",
+ ret, fw_priv->name, actread, firmware_p->size);
+ } else {
+ ret = actread;
+ }
+
+out:
+#ifdef CONFIG_CMD_UBIFS
+ if (location->ubivol)
+ umount_ubifs();
+#endif
+
+ return ret;
+}
+
+/**
+ * request_firmware_into_buf - Load firmware into a previously allocated buffer.
+ * @firmware_p: Pointer to firmware image.
+ * @name: Name of firmware file.
+ * @location: An array of supported firmware location.
+ * @buf: Address of buffer to load firmware into.
+ * @size: Size of buffer.
+ * @offset: Offset of a file for start reading into buffer.
+ *
+ * The firmware is loaded directly into the buffer pointed to by @buf and
+ * the @firmware_p data member is pointed at @buf.
+ *
+ * Return: Size of total read, negative value when error.
+ */
+int request_firmware_into_buf(struct firmware **firmware_p,
+ const char *name,
+ struct device_location *location,
+ void *buf, size_t size, u32 offset)
+{
+ int ret;
+
+ ret = _request_firmware_prepare(firmware_p, name, buf, size, offset);
+ if (ret < 0) /* error */
+ return ret;
+
+ ret = fw_get_filesystem_firmware(location, *firmware_p);
+
+ return ret;
+}
new file mode 100644
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 Intel Corporation <www.intel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+Introduction
+------------
+This is firmware loader for U-Boot framework, which has very close to some Linux
+Firmware API. For the details of Linux Firmware API, you can refer to
+https://01.org/linuxgraphics/gfx-docs/drm/driver-api/firmware/index.html.
+
+Firmware loader can be used to load whatever(firmware, image, and binary) from
+the flash in file system format into target location such as memory, then
+consumer driver such as FPGA driver can program FPGA image from the target
+location into FPGA.
+
+Firmware Loader API core features
+---------------------------------
+=> Firmware storage device partition search
+ ----------------------------------------
+ => Default storage device partition search set for mmc, usb, sata and ubi
+ as shown in below:
+ static struct device_location default_locations[] = {
+ {
+ .name = "mmc",
+ .devpart = "0:1",
+ },
+ {
+ .name = "usb",
+ .devpart = "0:1",
+ },
+ {
+ .name = "sata",
+ .devpart = "0:1",
+ },
+ {
+ .name = "ubi",
+ .mtdpart = "UBI",
+ .ubivol= "ubi0",
+ },
+ };
+
+ However, the default storage device .devpart, .mtdpart, and .ubivol
+ values can be overwritten with value which is defined in the environment
+ variable "fw_dev_part", "fw_ubi_mtdpart", and "fw_ubi_volume"
+ respectively.
+ For example: env_set("fw_dev_part", "0:2");
+
+Firmware Loader API
+-------------------
+=> int request_firmware_into_buf(struct firmware **firmware_p,
+ const char *name,
+ struct device_location *location,
+ void *buf, size_t size, u32 offset)
+ --------------------------------------------------------------
+ => Load firmware into a previously allocated buffer
+
+ Parameters:
+ struct firmware **firmware_p
+ pointer to firmware image
+
+ const char *name
+ name of firmware file
+
+ struct device_location *location
+ an array of supported firmware location
+
+ void *buf
+ address of buffer to load firmware into
+
+ size_t size
+ size of buffer
+
+ u32 offset
+ offset of a file for start reading into buffer
+
+ return: size of total read
+ -ve when error
+
+ Description:
+ The firmware is loaded directly into the buffer pointed to by buf and
+ the @firmware_p data member is pointed at buf.
new file mode 100644
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 Intel Corporation <www.intel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+#ifndef _FS_LOADER_H_
+#define _FS_LOADER_H_
+
+#include <linux/types.h>
+
+struct firmware {
+ size_t size; /* Size of a file */
+ const u8 *data; /* Buffer for file */
+ void *priv; /* Firmware loader private fields */
+};
+
+struct device_location {
+ char *name; /* Such as mmc, usb,and sata. */
+ char *devpart; /* Use the load command dev:part conventions */
+ char *mtdpart; /* MTD partition for ubi part */
+ char *ubivol; /* UBI volume-name for ubifsmount */
+};
+
+int request_firmware_into_buf(struct firmware **firmware_p,
+ const char *name,
+ struct device_location *location,
+ void *buf, size_t size, u32 offset);
+#endif