summaryrefslogtreecommitdiff
path: root/common/spl/spl_dfu.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/spl/spl_dfu.c')
-rw-r--r--common/spl/spl_dfu.c91
1 files changed, 91 insertions, 0 deletions
diff --git a/common/spl/spl_dfu.c b/common/spl/spl_dfu.c
index e9f381c392c..b09f82790c9 100644
--- a/common/spl/spl_dfu.c
+++ b/common/spl/spl_dfu.c
@@ -15,6 +15,17 @@
#include <usb.h>
#include <dfu.h>
#include <linux/printk.h>
+#include <pci_ep.h>
+#include <dm/uclass.h>
+#include <cpu_func.h>
+#include <linux/io.h>
+
+/*
+ * Macros define size of magic word and boot phase string
+ * in bytes.
+ */
+#define MAGIC_WORD_SIZE 4
+#define BOOT_PHASE_STRING_SIZE 63
static int run_dfu(int usb_index, char *interface, char *devstring)
{
@@ -32,11 +43,91 @@ exit:
return ret;
}
+#ifdef CONFIG_SPL_PCI_DFU
+static int dfu_over_pcie(void)
+{
+ u32 offset, magic_word;
+ volatile void *addr;
+ struct udevice *dev;
+ struct pci_bar bar;
+ struct pci_ep_header hdr;
+ uint fn = 0;
+ int ret;
+ char *bootphase;
+
+ uclass_get_device_by_seq(UCLASS_PCI_EP, 0, &dev);
+ if (!dev) {
+ pr_err("Failed to get pci ep device\n");
+ return -ENODEV;
+ }
+
+ hdr.deviceid = CONFIG_SPL_PCI_DFU_DEVICE_ID;
+ hdr.vendorid = CONFIG_SPL_PCI_DFU_VENDOR_ID;
+ hdr.baseclass_code = PCI_BASE_CLASS_MEMORY;
+ hdr.subclass_code = PCI_CLASS_MEMORY_RAM;
+
+ ret = pci_ep_write_header(dev, fn, &hdr);
+ if (ret) {
+ pr_err("Failed to write header: %d\n", ret);
+ return ret;
+ }
+
+ bar.barno = BAR_0;
+ bar.phys_addr = (dma_addr_t)CONFIG_SPL_PCI_DFU_SPL_LOAD_FIT_ADDRESS;
+ bar.flags = PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_TYPE_32 |
+ PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+ bar.size = CONFIG_SPL_PCI_DFU_BAR_SIZE;
+
+ ret = pci_ep_set_bar(dev, fn, &bar);
+ if (ret) {
+ pr_err("Failed to set bar: %d\n", ret);
+ return ret;
+ }
+
+ ret = pci_ep_start(dev);
+ if (ret) {
+ pr_err("Failed to start ep: %d\n", ret);
+ return ret;
+ }
+
+ addr = (void *)CONFIG_SPL_PCI_DFU_SPL_LOAD_FIT_ADDRESS;
+ offset = CONFIG_SPL_PCI_DFU_BAR_SIZE - MAGIC_WORD_SIZE;
+
+ if (sizeof(CONFIG_SPL_PCI_DFU_BOOT_PHASE) > BOOT_PHASE_STRING_SIZE) {
+ pr_err("Not copying boot phase. String too long\n");
+ } else {
+ bootphase = (char *)(addr + CONFIG_SPL_PCI_DFU_BAR_SIZE -
+ (BOOT_PHASE_STRING_SIZE + MAGIC_WORD_SIZE + 1));
+ strlcpy(bootphase, CONFIG_SPL_PCI_DFU_BOOT_PHASE,
+ sizeof(CONFIG_SPL_PCI_DFU_BOOT_PHASE) + 1);
+ }
+
+ addr = addr + offset;
+ magic_word = CONFIG_SPL_PCI_DFU_MAGIC_WORD;
+ (*(int *)addr) = 0;
+ flush_dcache_all();
+ for (;;) {
+ if (*(int *)addr == magic_word)
+ break;
+ invalidate_dcache_all();
+ }
+
+ return 0;
+}
+#endif
+
int spl_dfu_cmd(int usbctrl, char *dfu_alt_info, char *interface, char *devstr)
{
char *str_env;
int ret;
+#ifdef CONFIG_SPL_PCI_DFU
+ if (spl_boot_device() == BOOT_DEVICE_PCIE)
+ return dfu_over_pcie();
+#endif
+
/* set default environment */
env_set_default(NULL, 0);
str_env = env_get(dfu_alt_info);