On 2/19/21 1:52 AM, Asherah Connor wrote:
Adds support for QFW on Arm platforms by checking the device tree for a
"qemu,fw-cfg-mmio"-compatible node in arch_early_init_r and initialising
the driver if found.  Both MMIO and DMA are supported on this
architecture.

The device shows up as:

fw-cfg@9020000 {
    dma-coherent;
    reg = <0x00000000 0x09020000 0x00000000 0x00000018>;
    compatible = "qemu,fw-cfg-mmio";
};

drivers/misc/qfw.c should be converted to the driver model instead of
initializing the driver in arch_early_init_r() on qemu-arm and
qemu_chipset_init() on qemu-x86.

Cf. https://u-boot.readthedocs.io/en/latest/driver-model/index.html

Please, coordinate the change with Simon.

Should cmd/qfw.c be enhanced to show if the ramfb device is present?

Best regards

Heinrich


Signed-off-by: Asherah Connor <a...@kivikakk.ee>
---

  arch/arm/Kconfig            |   1 +
  arch/arm/Makefile           |   1 +
  arch/arm/mach-qemu/Kconfig  |   2 +
  arch/arm/mach-qemu/Makefile |   1 +
  arch/arm/mach-qemu/qemu.c   | 109 ++++++++++++++++++++++++++++++++++++
  5 files changed, 114 insertions(+)
  create mode 100644 arch/arm/mach-qemu/Makefile
  create mode 100644 arch/arm/mach-qemu/qemu.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d51abbeaf0..3841ae3ba2 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -937,6 +937,7 @@ config ARCH_QEMU
        imply DM_RNG
        imply DM_RTC
        imply RTC_PL031
+       imply QFW

  config ARCH_RMOBILE
        bool "Renesas ARM SoCs"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 28b523b37c..90fc3d2d1a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -70,6 +70,7 @@ machine-$(CONFIG_ARCH_NEXELL)         += nexell
  machine-$(CONFIG_ARCH_OMAP2PLUS)      += omap2
  machine-$(CONFIG_ARCH_ORION5X)                += orion5x
  machine-$(CONFIG_ARCH_OWL)            += owl
+machine-$(CONFIG_ARCH_QEMU)            += qemu
  machine-$(CONFIG_ARCH_RMOBILE)                += rmobile
  machine-$(CONFIG_ARCH_ROCKCHIP)               += rockchip
  machine-$(CONFIG_ARCH_S5PC1XX)                += s5pc1xx
diff --git a/arch/arm/mach-qemu/Kconfig b/arch/arm/mach-qemu/Kconfig
index 186c3582eb..50cbfc5efe 100644
--- a/arch/arm/mach-qemu/Kconfig
+++ b/arch/arm/mach-qemu/Kconfig
@@ -19,11 +19,13 @@ config TARGET_QEMU_ARM_32BIT
        select BOARD_LATE_INIT
        select CPU_V7A
        select SYS_ARCH_TIMER
+       select ARCH_EARLY_INIT_R if QFW

  config TARGET_QEMU_ARM_64BIT
        bool "ARMv8, 64bit"
        select ARM64
        select BOARD_LATE_INIT
+       select ARCH_EARLY_INIT_R if QFW

  endchoice

diff --git a/arch/arm/mach-qemu/Makefile b/arch/arm/mach-qemu/Makefile
new file mode 100644
index 0000000000..5f5915fb6e
--- /dev/null
+++ b/arch/arm/mach-qemu/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_QFW) += qemu.o
diff --git a/arch/arm/mach-qemu/qemu.c b/arch/arm/mach-qemu/qemu.c
new file mode 100644
index 0000000000..742cf81d6e
--- /dev/null
+++ b/arch/arm/mach-qemu/qemu.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021, Asherah Connor <a...@kivikakk.ee>
+ */
+#include <dm.h>
+#include <fdt_support.h>
+#include <qfw.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * on Arm, qfw registers are MMIO at offsets; see
+ * https://github.com/qemu/qemu/blob/1af56296/docs/specs/fw_cfg.txt
+ */
+struct qemu_arm_fwcfg_mmio {
+       /*
+        * Each access to the 64-bit data register can be 8/16/32/64 bits wide.
+        */
+       union {
+               u8 data8;
+               u16 data16;
+               u32 data32;
+               u64 data64;
+       };
+       u16 selector;
+       u8 padding[6];
+       u64 dma;
+};
+
+static volatile struct qemu_arm_fwcfg_mmio *mmio;
+
+static void qemu_arm_fwcfg_read_entry_pio(uint16_t entry, uint32_t size,
+                                         void *address)
+{
+       /*
+        * using FW_CFG_INVALID selector will resume a previous read at its last
+        * offset, otherwise read will start at 0 for new selector
+        *
+        * MMIO selector register is big-endian
+        */
+       if (entry != FW_CFG_INVALID)
+               mmio->selector = cpu_to_be16(entry);
+
+       /* data register is string-preserving */
+       while (size >= 8) {
+               *(u64 *)address = mmio->data64;
+               address += 8;
+               size -= 8;
+       }
+       while (size >= 4) {
+               *(u32 *)address = mmio->data32;
+               address += 4;
+               size -= 4;
+       }
+       while (size >= 2) {
+               *(u16 *)address = mmio->data16;
+               address += 2;
+               size -= 2;
+       }
+       while (size >= 1) {
+               *(u8 *)address = mmio->data8;
+               address += 1;
+               size -= 1;
+       }
+}
+
+static void qemu_arm_fwcfg_read_entry_dma(struct fw_cfg_dma_access *dma)
+{
+       /* the DMA address register is big-endian */
+       mmio->dma = cpu_to_be64((uintptr_t)dma);
+
+       while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR)
+               __asm__ __volatile__ ("yield");
+}
+
+static struct fw_cfg_arch_ops fwcfg_arm_ops = {
+       .arch_read_pio = qemu_arm_fwcfg_read_entry_pio,
+       .arch_read_dma = qemu_arm_fwcfg_read_entry_dma
+};
+
+int arch_early_init_r(void)
+{
+       /* Try to init QFW from device tree. */
+       int offset;
+       fdt32_t *reg;
+       u64 mmio_offset, mmio_size;
+       int addr_cells = fdt_address_cells(gd->fdt_blob, 0);
+       int size_cells = fdt_size_cells(gd->fdt_blob, 0);
+
+       offset = fdt_node_offset_by_compatible(gd->fdt_blob, -1,
+                                              "qemu,fw-cfg-mmio");
+       if (offset >= 0) {
+               reg = (fdt32_t *)fdt_getprop(gd->fdt_blob, offset, "reg", 0);
+               if (reg) {
+                       mmio_offset = fdt_read_number(reg, addr_cells);
+                       reg += addr_cells;
+                       mmio_size = fdt_read_number(reg, size_cells);
+
+                       /* Sanity check: at least data+selector wide. */
+                       if (mmio_size >= 10) {
+                               mmio = (struct qemu_arm_fwcfg_mmio
+                                               *)mmio_offset;
+                               qemu_fwcfg_init(&fwcfg_arm_ops);
+                       }
+               }
+       }
+
+       return 0;
+}


Reply via email to