| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Sandbox interface for QFW |
| * |
| * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> |
| * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee> |
| */ |
| |
| #define LOG_CATEGORY UCLASS_QFW |
| |
| #include <asm/types.h> |
| #include <asm/io.h> |
| #include <dm.h> |
| #include <dm/device.h> |
| #include <qfw.h> |
| |
| struct qfw_sandbox_plat { |
| u8 file_dir_offset; |
| }; |
| |
| static void qfw_sandbox_read_entry_io(struct udevice *dev, u16 entry, u32 size, |
| void *address) |
| { |
| debug("%s: entry 0x%x size %u address %p\n", __func__, entry, size, |
| address); |
| |
| switch (entry) { |
| case FW_CFG_SIGNATURE: |
| if (size == 4) |
| *((u32 *)address) = cpu_to_be32(QEMU_FW_CFG_SIGNATURE); |
| break; |
| case FW_CFG_ID: |
| /* Advertise DMA support */ |
| if (size == 1) |
| *((u8 *)address) = FW_CFG_DMA_ENABLED; |
| break; |
| default: |
| debug("%s got unsupported entry 0x%x\n", __func__, entry); |
| /* |
| * Sandbox driver doesn't support other entries here, assume we use DMA |
| * to read them -- the uclass driver will exclusively use it when |
| * advertised. |
| */ |
| } |
| } |
| |
| static void qfw_sandbox_read_entry_dma(struct udevice *dev, struct qfw_dma *dma) |
| { |
| u16 entry; |
| u32 control = be32_to_cpu(dma->control); |
| void *address = (void *)(uintptr_t)be64_to_cpu(dma->address); |
| u32 length = be32_to_cpu(dma->length); |
| struct qfw_sandbox_plat *plat = dev_get_plat(dev); |
| struct fw_cfg_file *file; |
| |
| debug("%s\n", __func__); |
| |
| if (!(control & FW_CFG_DMA_READ)) |
| return; |
| |
| if (control & FW_CFG_DMA_SELECT) { |
| /* Start new read. */ |
| entry = control >> 16; |
| |
| /* Arbitrary values to be used by tests. */ |
| switch (entry) { |
| case FW_CFG_NB_CPUS: |
| if (length == 2) |
| *((u16 *)address) = cpu_to_le16(5); |
| break; |
| case FW_CFG_FILE_DIR: |
| if (length == 4) { |
| *((u32 *)address) = cpu_to_be32(2); |
| plat->file_dir_offset = 1; |
| } |
| break; |
| default: |
| debug("%s got unsupported entry 0x%x\n", __func__, |
| entry); |
| } |
| } else if (plat->file_dir_offset && length == 64) { |
| file = address; |
| switch (plat->file_dir_offset) { |
| case 1: |
| file->size = cpu_to_be32(8); |
| file->select = cpu_to_be16(FW_CFG_FILE_FIRST); |
| strcpy(file->name, "test-one"); |
| plat->file_dir_offset++; |
| break; |
| case 2: |
| file->size = cpu_to_be32(8); |
| file->select = cpu_to_be16(FW_CFG_FILE_FIRST + 1); |
| strcpy(file->name, "test-two"); |
| plat->file_dir_offset++; |
| break; |
| } |
| } |
| |
| /* |
| * Signal that we are finished. No-one checks this in sandbox -- |
| * normally the platform-specific driver looks for it -- but let's |
| * replicate the behaviour in case someone relies on it later. |
| */ |
| dma->control = 0; |
| } |
| |
| static int qfw_sandbox_probe(struct udevice *dev) |
| { |
| return qfw_register(dev); |
| } |
| |
| static struct dm_qfw_ops qfw_sandbox_ops = { |
| .read_entry_io = qfw_sandbox_read_entry_io, |
| .read_entry_dma = qfw_sandbox_read_entry_dma, |
| }; |
| |
| U_BOOT_DRIVER(qfw_sandbox) = { |
| .name = "qfw_sandbox", |
| .id = UCLASS_QFW, |
| .plat_auto = sizeof(struct qfw_sandbox_plat), |
| .probe = qfw_sandbox_probe, |
| .ops = &qfw_sandbox_ops, |
| }; |
| |
| U_BOOT_DRVINFO(qfw_sandbox) = { |
| .name = "qfw_sandbox", |
| }; |