mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-06-12 22:49:43 +03:00
The subclass_code member of the pci_ep_header structure is a 1-byte
field. The macro PCI_CLASS_MEMORY_RAM is a concetation of baseclass_code
and subclass_code as follows:
PCI_BASE_CLASS_MEMORY: 0x05
Subclass Code for RAM: 0x00
PCI_CLASS_MEMORY_RAM: 0x0500
Hence, instead of extracting it via an implicity type conversion from int
to u8 which throws a warning, explicitly mask the bits to extract the
subclass_code.
Fixes: cde77583cf ("spl: Add support for Device Firmware Upgrade (DFU) over PCIe")
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Tested-by: Anshul Dalal <anshuld@ti.com>
Reviewed-by: Mattijs Korpershoek <mkorpershoek@kernel.org>
Tested-by: Mattijs Korpershoek <mkorpershoek@kernel.org> # am62x_evm_a53
Link: https://lore.kernel.org/r/20260305103815.999886-1-s-vadapalli@ti.com
Signed-off-by: Mattijs Korpershoek <mkorpershoek@kernel.org>
148 lines
3.2 KiB
C
148 lines
3.2 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2016
|
|
* Texas Instruments, <www.ti.com>
|
|
*
|
|
* Ravi B <ravibabu@ti.com>
|
|
*/
|
|
#include <env.h>
|
|
#include <spl.h>
|
|
#include <linux/compiler.h>
|
|
#include <errno.h>
|
|
#include <watchdog.h>
|
|
#include <console.h>
|
|
#include <g_dnl.h>
|
|
#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)
|
|
{
|
|
int ret;
|
|
|
|
ret = dfu_init_env_entities(interface, devstring);
|
|
if (ret) {
|
|
dfu_free_entities();
|
|
goto exit;
|
|
}
|
|
|
|
run_usb_dnl_gadget(usb_index, "usb_dnl_dfu");
|
|
exit:
|
|
dfu_free_entities();
|
|
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 & 0xff;
|
|
|
|
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);
|
|
if (!str_env) {
|
|
pr_err("\"%s\" env variable not defined!\n", dfu_alt_info);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = env_set("dfu_alt_info", str_env);
|
|
if (ret) {
|
|
pr_err("unable to set env variable \"dfu_alt_info\"!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* invoke dfu command */
|
|
return run_dfu(usbctrl, interface, devstr);
|
|
}
|