diff options
Diffstat (limited to 'common/spl/spl_dfu.c')
| -rw-r--r-- | common/spl/spl_dfu.c | 91 |
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); |
