All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card
@ 2022-12-03 23:18 Strahinja Jankovic
  2022-12-03 23:18 ` [PATCH 1/6] hw/misc: Allwinner-A10 Clock Controller Module Emulation Strahinja Jankovic
                   ` (6 more replies)
  0 siblings, 7 replies; 18+ messages in thread
From: Strahinja Jankovic @ 2022-12-03 23:18 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Beniamino Galvani, Niek Linnenbank, qemu-arm, qemu-devel,
	Strahinja Jankovic

This patch series adds missing Allwinner A10 modules needed for
successful SPL boot:
- Clock controller module
- DRAM controller
- I2C0 controller (added also for Allwinner H3 since it is the same)
- AXP-209 connected to I2C0 bus

It also updates Allwinner A10 emulation so SPL is copied from attached
SD card if `-kernel` parameter is not passed when starting QEMU
(approach adapted from Allwinner H3 implementation).

Boot from SD card has been tested with Cubieboard Armbian SD card image and custom
Yocto image built for Cubieboard.
Example usage for Armbian image:
qemu-system-arm -M cubieboard -nographic -sd ~/Armbian_22.11.0-trunk_Cubieboard_kinetic_edge_6.0.7.img


Strahinja Jankovic (6):
  hw/misc: Allwinner-A10 Clock Controller Module Emulation
  hw/misc: Allwinner A10 DRAM Controller Emulation
  hw/i2c: Allwinner TWI/I2C Emulation
  hw/misc: Allwinner AXP-209 Emulation
  hw/arm: Add AXP-209 to Cubieboard
  hw/arm: Allwinner A10 enable SPL load from MMC

 hw/arm/Kconfig                        |   5 +
 hw/arm/allwinner-a10.c                |  40 +++
 hw/arm/allwinner-h3.c                 |  11 +-
 hw/arm/cubieboard.c                   |  11 +
 hw/i2c/Kconfig                        |   4 +
 hw/i2c/allwinner-i2c.c                | 417 ++++++++++++++++++++++++++
 hw/i2c/meson.build                    |   1 +
 hw/misc/Kconfig                       |  10 +
 hw/misc/allwinner-a10-ccm.c           | 224 ++++++++++++++
 hw/misc/allwinner-a10-dramc.c         | 179 +++++++++++
 hw/misc/allwinner-axp-209.c           | 263 ++++++++++++++++
 hw/misc/meson.build                   |   3 +
 include/hw/arm/allwinner-a10.h        |  27 ++
 include/hw/arm/allwinner-h3.h         |   3 +
 include/hw/i2c/allwinner-i2c.h        | 112 +++++++
 include/hw/misc/allwinner-a10-ccm.h   |  67 +++++
 include/hw/misc/allwinner-a10-dramc.h |  68 +++++
 17 files changed, 1444 insertions(+), 1 deletion(-)
 create mode 100644 hw/i2c/allwinner-i2c.c
 create mode 100644 hw/misc/allwinner-a10-ccm.c
 create mode 100644 hw/misc/allwinner-a10-dramc.c
 create mode 100644 hw/misc/allwinner-axp-209.c
 create mode 100644 include/hw/i2c/allwinner-i2c.h
 create mode 100644 include/hw/misc/allwinner-a10-ccm.h
 create mode 100644 include/hw/misc/allwinner-a10-dramc.h

-- 
2.30.2



^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 1/6] hw/misc: Allwinner-A10 Clock Controller Module Emulation
  2022-12-03 23:18 [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Strahinja Jankovic
@ 2022-12-03 23:18 ` Strahinja Jankovic
  2022-12-07 22:31   ` Niek Linnenbank
  2022-12-03 23:19 ` [PATCH 2/6] hw/misc: Allwinner A10 DRAM Controller Emulation Strahinja Jankovic
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Strahinja Jankovic @ 2022-12-03 23:18 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Beniamino Galvani, Niek Linnenbank, qemu-arm, qemu-devel,
	Strahinja Jankovic

During SPL boot several Clock Controller Module (CCM) registers are
read, most important are PLL and Tuning, as well as divisor registers.

This patch adds these registers and initializes reset values from user's
guide.

Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
---
 hw/arm/Kconfig                      |   1 +
 hw/arm/allwinner-a10.c              |   7 +
 hw/misc/Kconfig                     |   3 +
 hw/misc/allwinner-a10-ccm.c         | 224 ++++++++++++++++++++++++++++
 hw/misc/meson.build                 |   1 +
 include/hw/arm/allwinner-a10.h      |   2 +
 include/hw/misc/allwinner-a10-ccm.h |  67 +++++++++
 7 files changed, 305 insertions(+)
 create mode 100644 hw/misc/allwinner-a10-ccm.c
 create mode 100644 include/hw/misc/allwinner-a10-ccm.h

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 17fcde8e1c..14f52b41af 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -319,6 +319,7 @@ config ALLWINNER_A10
     select AHCI
     select ALLWINNER_A10_PIT
     select ALLWINNER_A10_PIC
+    select ALLWINNER_A10_CCM
     select ALLWINNER_EMAC
     select SERIAL
     select UNIMP
diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
index 79082289ea..86baeeeca2 100644
--- a/hw/arm/allwinner-a10.c
+++ b/hw/arm/allwinner-a10.c
@@ -26,6 +26,7 @@
 #include "hw/usb/hcd-ohci.h"
 
 #define AW_A10_MMC0_BASE        0x01c0f000
+#define AW_A10_CCM_BASE         0x01c20000
 #define AW_A10_PIC_REG_BASE     0x01c20400
 #define AW_A10_PIT_REG_BASE     0x01c20c00
 #define AW_A10_UART0_REG_BASE   0x01c28000
@@ -46,6 +47,8 @@ static void aw_a10_init(Object *obj)
 
     object_initialize_child(obj, "timer", &s->timer, TYPE_AW_A10_PIT);
 
+    object_initialize_child(obj, "ccm", &s->ccm, TYPE_AW_A10_CCM);
+
     object_initialize_child(obj, "emac", &s->emac, TYPE_AW_EMAC);
 
     object_initialize_child(obj, "sata", &s->sata, TYPE_ALLWINNER_AHCI);
@@ -103,6 +106,10 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
     memory_region_add_subregion(get_system_memory(), 0x00000000, &s->sram_a);
     create_unimplemented_device("a10-sram-ctrl", 0x01c00000, 4 * KiB);
 
+    /* Clock Control Module */
+    sysbus_realize(SYS_BUS_DEVICE(&s->ccm), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, AW_A10_CCM_BASE);
+
     /* FIXME use qdev NIC properties instead of nd_table[] */
     if (nd_table[0].used) {
         qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index cbabe9f78c..ed07bf4133 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -174,4 +174,7 @@ config VIRT_CTRL
 config LASI
     bool
 
+config ALLWINNER_A10_CCM
+    bool
+
 source macio/Kconfig
diff --git a/hw/misc/allwinner-a10-ccm.c b/hw/misc/allwinner-a10-ccm.c
new file mode 100644
index 0000000000..68146ee340
--- /dev/null
+++ b/hw/misc/allwinner-a10-ccm.c
@@ -0,0 +1,224 @@
+/*
+ * Allwinner A10 Clock Control Module emulation
+ *
+ * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
+ *
+ *  This file is derived from Allwinner H3 CCU,
+ *  by Niek Linnenbank.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/misc/allwinner-a10-ccm.h"
+
+/* CCM register offsets */
+enum {
+    REG_PLL1_CFG             = 0x0000, /* PLL1 Control */
+    REG_PLL1_TUN             = 0x0004, /* PLL1 Tuning */
+    REG_PLL2_CFG             = 0x0008, /* PLL2 Control */
+    REG_PLL2_TUN             = 0x000C, /* PLL2 Tuning */
+    REG_PLL3_CFG             = 0x0010, /* PLL3 Control */
+    REG_PLL4_CFG             = 0x0018, /* PLL4 Control */
+    REG_PLL5_CFG             = 0x0020, /* PLL5 Control */
+    REG_PLL5_TUN             = 0x0024, /* PLL5 Tuning */
+    REG_PLL6_CFG             = 0x0028, /* PLL6 Control */
+    REG_PLL6_TUN             = 0x002C, /* PLL6 Tuning */
+    REG_PLL7_CFG             = 0x0030, /* PLL7 Control */
+    REG_PLL1_TUN2            = 0x0038, /* PLL1 Tuning2 */
+    REG_PLL5_TUN2            = 0x003C, /* PLL5 Tuning2 */
+    REG_PLL8_CFG             = 0x0040, /* PLL8 Control */
+    REG_OSC24M_CFG           = 0x0050, /* OSC24M Control */
+    REG_CPU_AHB_APB0_CFG     = 0x0054, /* CPU, AHB and APB0 Divide Ratio */
+};
+
+#define REG_INDEX(offset)    (offset / sizeof(uint32_t))
+
+/* CCM register reset values */
+enum {
+    REG_PLL1_CFG_RST         = 0x21005000,
+    REG_PLL1_TUN_RST         = 0x0A101000,
+    REG_PLL2_CFG_RST         = 0x08100010,
+    REG_PLL2_TUN_RST         = 0x00000000,
+    REG_PLL3_CFG_RST         = 0x0010D063,
+    REG_PLL4_CFG_RST         = 0x21009911,
+    REG_PLL5_CFG_RST         = 0x11049280,
+    REG_PLL5_TUN_RST         = 0x14888000,
+    REG_PLL6_CFG_RST         = 0x21009911,
+    REG_PLL6_TUN_RST         = 0x00000000,
+    REG_PLL7_CFG_RST         = 0x0010D063,
+    REG_PLL1_TUN2_RST        = 0x00000000,
+    REG_PLL5_TUN2_RST        = 0x00000000,
+    REG_PLL8_CFG_RST         = 0x21009911,
+    REG_OSC24M_CFG_RST       = 0x00138013,
+    REG_CPU_AHB_APB0_CFG_RST = 0x00010010,
+};
+
+static uint64_t allwinner_a10_ccm_read(void *opaque, hwaddr offset,
+                                       unsigned size)
+{
+    const AwA10ClockCtlState *s = AW_A10_CCM(opaque);
+    const uint32_t idx = REG_INDEX(offset);
+
+    switch (offset) {
+    case REG_PLL1_CFG:
+    case REG_PLL1_TUN:
+    case REG_PLL2_CFG:
+    case REG_PLL2_TUN:
+    case REG_PLL3_CFG:
+    case REG_PLL4_CFG:
+    case REG_PLL5_CFG:
+    case REG_PLL5_TUN:
+    case REG_PLL6_CFG:
+    case REG_PLL6_TUN:
+    case REG_PLL7_CFG:
+    case REG_PLL1_TUN2:
+    case REG_PLL5_TUN2:
+    case REG_PLL8_CFG:
+    case REG_OSC24M_CFG:
+    case REG_CPU_AHB_APB0_CFG:
+        break;
+    case 0x158 ... AW_A10_CCM_IOSIZE:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    default:
+        qemu_log_mask(LOG_UNIMP, "%s: unimplemented read offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    }
+
+    return s->regs[idx];
+}
+
+static void allwinner_a10_ccm_write(void *opaque, hwaddr offset,
+                                   uint64_t val, unsigned size)
+{
+    AwA10ClockCtlState *s = AW_A10_CCM(opaque);
+    const uint32_t idx = REG_INDEX(offset);
+
+    switch (offset) {
+    case REG_PLL1_CFG:
+    case REG_PLL1_TUN:
+    case REG_PLL2_CFG:
+    case REG_PLL2_TUN:
+    case REG_PLL3_CFG:
+    case REG_PLL4_CFG:
+    case REG_PLL5_CFG:
+    case REG_PLL5_TUN:
+    case REG_PLL6_CFG:
+    case REG_PLL6_TUN:
+    case REG_PLL7_CFG:
+    case REG_PLL1_TUN2:
+    case REG_PLL5_TUN2:
+    case REG_PLL8_CFG:
+    case REG_OSC24M_CFG:
+    case REG_CPU_AHB_APB0_CFG:
+        break;
+    case 0x158 ... AW_A10_CCM_IOSIZE:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        break;
+    }
+
+    s->regs[idx] = (uint32_t) val;
+}
+
+static const MemoryRegionOps allwinner_a10_ccm_ops = {
+    .read = allwinner_a10_ccm_read,
+    .write = allwinner_a10_ccm_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .impl.min_access_size = 4,
+};
+
+static void allwinner_a10_ccm_reset_enter(Object *obj, ResetType type)
+{
+    AwA10ClockCtlState *s = AW_A10_CCM(obj);
+
+    /* Set default values for registers */
+    s->regs[REG_INDEX(REG_PLL1_CFG)] = REG_PLL1_CFG_RST;
+    s->regs[REG_INDEX(REG_PLL1_TUN)] = REG_PLL1_TUN_RST;
+    s->regs[REG_INDEX(REG_PLL2_CFG)] = REG_PLL2_CFG_RST;
+    s->regs[REG_INDEX(REG_PLL2_TUN)] = REG_PLL2_TUN_RST;
+    s->regs[REG_INDEX(REG_PLL3_CFG)] = REG_PLL3_CFG_RST;
+    s->regs[REG_INDEX(REG_PLL4_CFG)] = REG_PLL4_CFG_RST;
+    s->regs[REG_INDEX(REG_PLL5_CFG)] = REG_PLL5_CFG_RST;
+    s->regs[REG_INDEX(REG_PLL5_TUN)] = REG_PLL5_TUN_RST;
+    s->regs[REG_INDEX(REG_PLL6_CFG)] = REG_PLL6_CFG_RST;
+    s->regs[REG_INDEX(REG_PLL6_TUN)] = REG_PLL6_TUN_RST;
+    s->regs[REG_INDEX(REG_PLL7_CFG)] = REG_PLL7_CFG_RST;
+    s->regs[REG_INDEX(REG_PLL1_TUN2)] = REG_PLL1_TUN2_RST;
+    s->regs[REG_INDEX(REG_PLL5_TUN2)] = REG_PLL5_TUN2_RST;
+    s->regs[REG_INDEX(REG_PLL8_CFG)] = REG_PLL8_CFG_RST;
+    s->regs[REG_INDEX(REG_OSC24M_CFG)] = REG_OSC24M_CFG_RST;
+    s->regs[REG_INDEX(REG_CPU_AHB_APB0_CFG)] = REG_CPU_AHB_APB0_CFG_RST;
+}
+
+static void allwinner_a10_ccm_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    AwA10ClockCtlState *s = AW_A10_CCM(obj);
+
+    /* Memory mapping */
+    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_ccm_ops, s,
+                          TYPE_AW_A10_CCM, AW_A10_CCM_IOSIZE);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription allwinner_a10_ccm_vmstate = {
+    .name = "allwinner-a10-ccm",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, AwA10ClockCtlState, AW_A10_CCM_REGS_NUM),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void allwinner_a10_ccm_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    rc->phases.enter = allwinner_a10_ccm_reset_enter;
+    dc->vmsd = &allwinner_a10_ccm_vmstate;
+}
+
+static const TypeInfo allwinner_a10_ccm_info = {
+    .name          = TYPE_AW_A10_CCM,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_init = allwinner_a10_ccm_init,
+    .instance_size = sizeof(AwA10ClockCtlState),
+    .class_init    = allwinner_a10_ccm_class_init,
+};
+
+static void allwinner_a10_ccm_register(void)
+{
+    type_register_static(&allwinner_a10_ccm_info);
+}
+
+type_init(allwinner_a10_ccm_register)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 95268eddc0..ebf216edbc 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -38,6 +38,7 @@ subdir('macio')
 
 softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c'))
 
+softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: files('allwinner-a10-ccm.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-ccu.c'))
 specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-cpucfg.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c'))
diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h
index a76dc7b84d..45d0fc2f7e 100644
--- a/include/hw/arm/allwinner-a10.h
+++ b/include/hw/arm/allwinner-a10.h
@@ -12,6 +12,7 @@
 #include "hw/usb/hcd-ohci.h"
 #include "hw/usb/hcd-ehci.h"
 #include "hw/rtc/allwinner-rtc.h"
+#include "hw/misc/allwinner-a10-ccm.h"
 
 #include "target/arm/cpu.h"
 #include "qom/object.h"
@@ -30,6 +31,7 @@ struct AwA10State {
     /*< public >*/
 
     ARMCPU cpu;
+    AwA10ClockCtlState ccm;
     AwA10PITState timer;
     AwA10PICState intc;
     AwEmacState emac;
diff --git a/include/hw/misc/allwinner-a10-ccm.h b/include/hw/misc/allwinner-a10-ccm.h
new file mode 100644
index 0000000000..7f22532efa
--- /dev/null
+++ b/include/hw/misc/allwinner-a10-ccm.h
@@ -0,0 +1,67 @@
+/*
+ * Allwinner A10 Clock Control Module emulation
+ *
+ * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
+ *
+ *  This file is derived from Allwinner H3 CCU,
+ *  by Niek Linnenbank.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_MISC_ALLWINNER_A10_CCM_H
+#define HW_MISC_ALLWINNER_A10_CCM_H
+
+#include "qom/object.h"
+#include "hw/sysbus.h"
+
+/**
+ * @name Constants
+ * @{
+ */
+
+/** Size of register I/O address space used by CCM device */
+#define AW_A10_CCM_IOSIZE        (0x400)
+
+/** Total number of known registers */
+#define AW_A10_CCM_REGS_NUM      (AW_A10_CCM_IOSIZE / sizeof(uint32_t))
+
+/** @} */
+
+/**
+ * @name Object model
+ * @{
+ */
+
+#define TYPE_AW_A10_CCM    "allwinner-a10-ccm"
+OBJECT_DECLARE_SIMPLE_TYPE(AwA10ClockCtlState, AW_A10_CCM)
+
+/** @} */
+
+/**
+ * Allwinner A10 CCM object instance state.
+ */
+struct AwA10ClockCtlState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    /** Maps I/O registers in physical memory */
+    MemoryRegion iomem;
+
+    /** Array of hardware registers */
+    uint32_t regs[AW_A10_CCM_REGS_NUM];
+};
+
+#endif /* HW_MISC_ALLWINNER_H3_CCU_H */
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 2/6] hw/misc: Allwinner A10 DRAM Controller Emulation
  2022-12-03 23:18 [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Strahinja Jankovic
  2022-12-03 23:18 ` [PATCH 1/6] hw/misc: Allwinner-A10 Clock Controller Module Emulation Strahinja Jankovic
@ 2022-12-03 23:19 ` Strahinja Jankovic
  2022-12-07 22:26   ` Niek Linnenbank
  2022-12-03 23:19 ` [PATCH 3/6] hw/i2c: Allwinner TWI/I2C Emulation Strahinja Jankovic
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Strahinja Jankovic @ 2022-12-03 23:19 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Beniamino Galvani, Niek Linnenbank, qemu-arm, qemu-devel,
	Strahinja Jankovic

During SPL boot several DRAM Controller registers are used. Most
important registers are those related to DRAM initialization and
calibration, where SPL initiates process and waits until certain bit is
set/cleared.

This patch adds these registers, initializes reset values from user's
guide and updates state of registers as SPL expects it.

Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
---
 hw/arm/Kconfig                        |   1 +
 hw/arm/allwinner-a10.c                |   7 +
 hw/misc/Kconfig                       |   3 +
 hw/misc/allwinner-a10-dramc.c         | 179 ++++++++++++++++++++++++++
 hw/misc/meson.build                   |   1 +
 include/hw/arm/allwinner-a10.h        |   2 +
 include/hw/misc/allwinner-a10-dramc.h |  68 ++++++++++
 7 files changed, 261 insertions(+)
 create mode 100644 hw/misc/allwinner-a10-dramc.c
 create mode 100644 include/hw/misc/allwinner-a10-dramc.h

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 14f52b41af..140f142ae5 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -320,6 +320,7 @@ config ALLWINNER_A10
     select ALLWINNER_A10_PIT
     select ALLWINNER_A10_PIC
     select ALLWINNER_A10_CCM
+    select ALLWINNER_A10_DRAMC
     select ALLWINNER_EMAC
     select SERIAL
     select UNIMP
diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
index 86baeeeca2..a5f7a36ac9 100644
--- a/hw/arm/allwinner-a10.c
+++ b/hw/arm/allwinner-a10.c
@@ -25,6 +25,7 @@
 #include "hw/boards.h"
 #include "hw/usb/hcd-ohci.h"
 
+#define AW_A10_DRAMC_BASE       0x01c01000
 #define AW_A10_MMC0_BASE        0x01c0f000
 #define AW_A10_CCM_BASE         0x01c20000
 #define AW_A10_PIC_REG_BASE     0x01c20400
@@ -49,6 +50,8 @@ static void aw_a10_init(Object *obj)
 
     object_initialize_child(obj, "ccm", &s->ccm, TYPE_AW_A10_CCM);
 
+    object_initialize_child(obj, "dramc", &s->dramc, TYPE_AW_A10_DRAMC);
+
     object_initialize_child(obj, "emac", &s->emac, TYPE_AW_EMAC);
 
     object_initialize_child(obj, "sata", &s->sata, TYPE_ALLWINNER_AHCI);
@@ -110,6 +113,10 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
     sysbus_realize(SYS_BUS_DEVICE(&s->ccm), &error_fatal);
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, AW_A10_CCM_BASE);
 
+    /* DRAM Control Module */
+    sysbus_realize(SYS_BUS_DEVICE(&s->dramc), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, AW_A10_DRAMC_BASE);
+
     /* FIXME use qdev NIC properties instead of nd_table[] */
     if (nd_table[0].used) {
         qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index ed07bf4133..052fb54310 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -177,4 +177,7 @@ config LASI
 config ALLWINNER_A10_CCM
     bool
 
+config ALLWINNER_A10_DRAMC
+    bool
+
 source macio/Kconfig
diff --git a/hw/misc/allwinner-a10-dramc.c b/hw/misc/allwinner-a10-dramc.c
new file mode 100644
index 0000000000..e118b0c2fd
--- /dev/null
+++ b/hw/misc/allwinner-a10-dramc.c
@@ -0,0 +1,179 @@
+/*
+ * Allwinner A10 DRAM Controller emulation
+ *
+ * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
+ *
+ *  This file is derived from Allwinner H3 DRAMC,
+ *  by Niek Linnenbank.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/misc/allwinner-a10-dramc.h"
+
+/* DRAMC register offsets */
+enum {
+    REG_SDR_CCR = 0x0000,
+    REG_SDR_ZQCR0 = 0x00a8,
+    REG_SDR_ZQSR = 0x00b0
+};
+
+#define REG_INDEX(offset)    (offset / sizeof(uint32_t))
+
+/* DRAMC register flags */
+enum {
+    REG_SDR_CCR_DATA_TRAINING = (1 << 30),
+    REG_SDR_CCR_DRAM_INIT     = (1 << 31),
+};
+enum {
+    REG_SDR_ZQSR_ZCAL         = (1 << 31),
+};
+
+/* DRAMC register reset values */
+enum {
+    REG_SDR_CCR_RESET   = 0x80020000,
+    REG_SDR_ZQCR0_RESET = 0x07b00000,
+    REG_SDR_ZQSR_RESET  = 0x80000000
+};
+
+static uint64_t allwinner_a10_dramc_read(void *opaque, hwaddr offset,
+                                       unsigned size)
+{
+    const AwA10DramControllerState *s = AW_A10_DRAMC(opaque);
+    const uint32_t idx = REG_INDEX(offset);
+
+    switch (offset) {
+    case REG_SDR_CCR:
+    case REG_SDR_ZQCR0:
+    case REG_SDR_ZQSR:
+        break;
+    case 0x2e4 ... AW_A10_DRAMC_IOSIZE:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    default:
+        qemu_log_mask(LOG_UNIMP, "%s: unimplemented read offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    }
+
+    return s->regs[idx];
+}
+
+static void allwinner_a10_dramc_write(void *opaque, hwaddr offset,
+                                   uint64_t val, unsigned size)
+{
+    AwA10DramControllerState *s = AW_A10_DRAMC(opaque);
+    const uint32_t idx = REG_INDEX(offset);
+
+    switch (offset) {
+    case REG_SDR_CCR:
+        if (val & REG_SDR_CCR_DRAM_INIT) {
+            /* Clear DRAM_INIT to indicate process is done. */
+            val &= ~REG_SDR_CCR_DRAM_INIT;
+        }
+        if (val & REG_SDR_CCR_DATA_TRAINING) {
+            /* Clear DATA_TRAINING to indicate process is done. */
+            val &= ~REG_SDR_CCR_DATA_TRAINING;
+        }
+        break;
+    case REG_SDR_ZQCR0:
+        /* Set ZCAL in ZQSR to indicate calibration is done. */
+        s->regs[REG_INDEX(REG_SDR_ZQSR)] |= REG_SDR_ZQSR_ZCAL;
+        break;
+    case 0x2e4 ... AW_A10_DRAMC_IOSIZE:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        break;
+    }
+
+    s->regs[idx] = (uint32_t) val;
+}
+
+static const MemoryRegionOps allwinner_a10_dramc_ops = {
+    .read = allwinner_a10_dramc_read,
+    .write = allwinner_a10_dramc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .impl.min_access_size = 4,
+};
+
+static void allwinner_a10_dramc_reset_enter(Object *obj, ResetType type)
+{
+    AwA10DramControllerState *s = AW_A10_DRAMC(obj);
+
+    /* Set default values for registers */
+    s->regs[REG_INDEX(REG_SDR_CCR)] = REG_SDR_CCR_RESET;
+    s->regs[REG_INDEX(REG_SDR_ZQCR0)] = REG_SDR_ZQCR0_RESET;
+    s->regs[REG_INDEX(REG_SDR_ZQSR)] = REG_SDR_ZQSR_RESET;
+}
+
+static void allwinner_a10_dramc_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    AwA10DramControllerState *s = AW_A10_DRAMC(obj);
+
+    /* Memory mapping */
+    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_dramc_ops, s,
+                          TYPE_AW_A10_DRAMC, AW_A10_DRAMC_IOSIZE);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription allwinner_a10_dramc_vmstate = {
+    .name = "allwinner-a10-dramc",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, AwA10DramControllerState,
+                             AW_A10_DRAMC_REGS_NUM),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void allwinner_a10_dramc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    rc->phases.enter = allwinner_a10_dramc_reset_enter;
+    dc->vmsd = &allwinner_a10_dramc_vmstate;
+}
+
+static const TypeInfo allwinner_a10_dramc_info = {
+    .name          = TYPE_AW_A10_DRAMC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_init = allwinner_a10_dramc_init,
+    .instance_size = sizeof(AwA10DramControllerState),
+    .class_init    = allwinner_a10_dramc_class_init,
+};
+
+static void allwinner_a10_dramc_register(void)
+{
+    type_register_static(&allwinner_a10_dramc_info);
+}
+
+type_init(allwinner_a10_dramc_register)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index ebf216edbc..d7f49f0f81 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -39,6 +39,7 @@ subdir('macio')
 softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c'))
 
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: files('allwinner-a10-ccm.c'))
+softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_DRAMC', if_true: files('allwinner-a10-dramc.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-ccu.c'))
 specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-cpucfg.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c'))
diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h
index 45d0fc2f7e..abe4ff7066 100644
--- a/include/hw/arm/allwinner-a10.h
+++ b/include/hw/arm/allwinner-a10.h
@@ -13,6 +13,7 @@
 #include "hw/usb/hcd-ehci.h"
 #include "hw/rtc/allwinner-rtc.h"
 #include "hw/misc/allwinner-a10-ccm.h"
+#include "hw/misc/allwinner-a10-dramc.h"
 
 #include "target/arm/cpu.h"
 #include "qom/object.h"
@@ -32,6 +33,7 @@ struct AwA10State {
 
     ARMCPU cpu;
     AwA10ClockCtlState ccm;
+    AwA10DramControllerState dramc;
     AwA10PITState timer;
     AwA10PICState intc;
     AwEmacState emac;
diff --git a/include/hw/misc/allwinner-a10-dramc.h b/include/hw/misc/allwinner-a10-dramc.h
new file mode 100644
index 0000000000..b61fbecbe7
--- /dev/null
+++ b/include/hw/misc/allwinner-a10-dramc.h
@@ -0,0 +1,68 @@
+/*
+ * Allwinner A10 DRAM Controller emulation
+ *
+ * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
+ *
+ *  This file is derived from Allwinner H3 DRAMC,
+ *  by Niek Linnenbank.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_MISC_ALLWINNER_A10_DRAMC_H
+#define HW_MISC_ALLWINNER_A10_DRAMC_H
+
+#include "qom/object.h"
+#include "hw/sysbus.h"
+#include "hw/register.h"
+
+/**
+ * @name Constants
+ * @{
+ */
+
+/** Size of register I/O address space used by DRAMC device */
+#define AW_A10_DRAMC_IOSIZE        (0x1000)
+
+/** Total number of known registers */
+#define AW_A10_DRAMC_REGS_NUM      (AW_A10_DRAMC_IOSIZE / sizeof(uint32_t))
+
+/** @} */
+
+/**
+ * @name Object model
+ * @{
+ */
+
+#define TYPE_AW_A10_DRAMC    "allwinner-a10-dramc"
+OBJECT_DECLARE_SIMPLE_TYPE(AwA10DramControllerState, AW_A10_DRAMC)
+
+/** @} */
+
+/**
+ * Allwinner A10 DRAMC object instance state.
+ */
+struct AwA10DramControllerState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    /** Maps I/O registers in physical memory */
+    MemoryRegion iomem;
+
+    /** Array of hardware registers */
+    uint32_t regs[AW_A10_DRAMC_REGS_NUM];
+};
+
+#endif /* HW_MISC_ALLWINNER_A10_DRAMC_H */
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 3/6] hw/i2c: Allwinner TWI/I2C Emulation
  2022-12-03 23:18 [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Strahinja Jankovic
  2022-12-03 23:18 ` [PATCH 1/6] hw/misc: Allwinner-A10 Clock Controller Module Emulation Strahinja Jankovic
  2022-12-03 23:19 ` [PATCH 2/6] hw/misc: Allwinner A10 DRAM Controller Emulation Strahinja Jankovic
@ 2022-12-03 23:19 ` Strahinja Jankovic
  2022-12-07 22:06   ` Niek Linnenbank
  2022-12-03 23:19 ` [PATCH 4/6] hw/misc: Allwinner AXP-209 Emulation Strahinja Jankovic
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Strahinja Jankovic @ 2022-12-03 23:19 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Beniamino Galvani, Niek Linnenbank, qemu-arm, qemu-devel,
	Strahinja Jankovic

This patch implements Allwinner TWI/I2C controller emulation. Only
master-mode functionality is implemented.

The SPL boot for Cubieboard expects AXP209 PMIC on TWI0/I2C0 bus, so this is
first part enabling the TWI/I2C bus operation.

Since both Allwinner A10 and H3 use the same module, it is added for
both boards.

Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
---
 hw/arm/Kconfig                 |   2 +
 hw/arm/allwinner-a10.c         |   8 +
 hw/arm/allwinner-h3.c          |  11 +-
 hw/i2c/Kconfig                 |   4 +
 hw/i2c/allwinner-i2c.c         | 417 +++++++++++++++++++++++++++++++++
 hw/i2c/meson.build             |   1 +
 include/hw/arm/allwinner-a10.h |   2 +
 include/hw/arm/allwinner-h3.h  |   3 +
 include/hw/i2c/allwinner-i2c.h | 112 +++++++++
 9 files changed, 559 insertions(+), 1 deletion(-)
 create mode 100644 hw/i2c/allwinner-i2c.c
 create mode 100644 include/hw/i2c/allwinner-i2c.h

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 140f142ae5..eefe1fd134 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -322,6 +322,7 @@ config ALLWINNER_A10
     select ALLWINNER_A10_CCM
     select ALLWINNER_A10_DRAMC
     select ALLWINNER_EMAC
+    select ALLWINNER_I2C
     select SERIAL
     select UNIMP
 
@@ -329,6 +330,7 @@ config ALLWINNER_H3
     bool
     select ALLWINNER_A10_PIT
     select ALLWINNER_SUN8I_EMAC
+    select ALLWINNER_I2C
     select SERIAL
     select ARM_TIMER
     select ARM_GIC
diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
index a5f7a36ac9..17e439777e 100644
--- a/hw/arm/allwinner-a10.c
+++ b/hw/arm/allwinner-a10.c
@@ -36,6 +36,7 @@
 #define AW_A10_OHCI_BASE        0x01c14400
 #define AW_A10_SATA_BASE        0x01c18000
 #define AW_A10_RTC_BASE         0x01c20d00
+#define AW_A10_I2C0_BASE        0x01c2ac00
 
 static void aw_a10_init(Object *obj)
 {
@@ -56,6 +57,8 @@ static void aw_a10_init(Object *obj)
 
     object_initialize_child(obj, "sata", &s->sata, TYPE_ALLWINNER_AHCI);
 
+    object_initialize_child(obj, "i2c0", &s->i2c0, TYPE_AW_I2C);
+
     if (machine_usb(current_machine)) {
         int i;
 
@@ -176,6 +179,11 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
     /* RTC */
     sysbus_realize(SYS_BUS_DEVICE(&s->rtc), &error_fatal);
     sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->rtc), 0, AW_A10_RTC_BASE, 10);
+
+    /* I2C */
+    sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, AW_A10_I2C0_BASE);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0, qdev_get_gpio_in(dev, 7));
 }
 
 static void aw_a10_class_init(ObjectClass *oc, void *data)
diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
index 308ed15552..bfce3c8d92 100644
--- a/hw/arm/allwinner-h3.c
+++ b/hw/arm/allwinner-h3.c
@@ -53,6 +53,7 @@ const hwaddr allwinner_h3_memmap[] = {
     [AW_H3_DEV_UART1]      = 0x01c28400,
     [AW_H3_DEV_UART2]      = 0x01c28800,
     [AW_H3_DEV_UART3]      = 0x01c28c00,
+    [AW_H3_DEV_TWI0]       = 0x01c2ac00,
     [AW_H3_DEV_EMAC]       = 0x01c30000,
     [AW_H3_DEV_DRAMCOM]    = 0x01c62000,
     [AW_H3_DEV_DRAMCTL]    = 0x01c63000,
@@ -106,7 +107,6 @@ struct AwH3Unimplemented {
     { "uart1",     0x01c28400, 1 * KiB },
     { "uart2",     0x01c28800, 1 * KiB },
     { "uart3",     0x01c28c00, 1 * KiB },
-    { "twi0",      0x01c2ac00, 1 * KiB },
     { "twi1",      0x01c2b000, 1 * KiB },
     { "twi2",      0x01c2b400, 1 * KiB },
     { "scr",       0x01c2c400, 1 * KiB },
@@ -150,6 +150,7 @@ enum {
     AW_H3_GIC_SPI_UART1     =  1,
     AW_H3_GIC_SPI_UART2     =  2,
     AW_H3_GIC_SPI_UART3     =  3,
+    AW_H3_GIC_SPI_TWI0      =  6,
     AW_H3_GIC_SPI_TIMER0    = 18,
     AW_H3_GIC_SPI_TIMER1    = 19,
     AW_H3_GIC_SPI_MMC0      = 60,
@@ -225,6 +226,8 @@ static void allwinner_h3_init(Object *obj)
                               "ram-size");
 
     object_initialize_child(obj, "rtc", &s->rtc, TYPE_AW_RTC_SUN6I);
+
+    object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C);
 }
 
 static void allwinner_h3_realize(DeviceState *dev, Error **errp)
@@ -423,6 +426,12 @@ static void allwinner_h3_realize(DeviceState *dev, Error **errp)
     sysbus_realize(SYS_BUS_DEVICE(&s->rtc), &error_fatal);
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, s->memmap[AW_H3_DEV_RTC]);
 
+    /* I2C */
+    sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, s->memmap[AW_H3_DEV_TWI0]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TWI0));
+
     /* Unimplemented devices */
     for (i = 0; i < ARRAY_SIZE(unimplemented); i++) {
         create_unimplemented_device(unimplemented[i].device_name,
diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig
index 9bb8870517..f8ec461be3 100644
--- a/hw/i2c/Kconfig
+++ b/hw/i2c/Kconfig
@@ -34,6 +34,10 @@ config MPC_I2C
     bool
     select I2C
 
+config ALLWINNER_I2C
+    bool
+    select I2C
+
 config PCA954X
     bool
     select I2C
diff --git a/hw/i2c/allwinner-i2c.c b/hw/i2c/allwinner-i2c.c
new file mode 100644
index 0000000000..4c9080a18b
--- /dev/null
+++ b/hw/i2c/allwinner-i2c.c
@@ -0,0 +1,417 @@
+/*
+ *  Allwinner I2C Bus Serial Interface Emulation
+ *
+ *  Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
+ *
+ *  This file is derived from IMX I2C controller,
+ *  by Jean-Christophe DUBOIS .
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/i2c/allwinner-i2c.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/i2c/i2c.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+
+#ifndef DEBUG_AW_I2C
+#define DEBUG_AW_I2C 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+    do { \
+        if (DEBUG_AW_I2C) { \
+            fprintf(stderr, "[%s]%s: " fmt , TYPE_AW_I2C, \
+                                             __func__, ##args); \
+        } \
+    } while (0)
+
+/* Status value in STAT register is shifted by 3 bits */
+#define TWI_STAT_SHIFT      3
+#define STAT_FROM_STA(x)    ((x) << TWI_STAT_SHIFT)
+#define STAT_TO_STA(x)      ((x) >> TWI_STAT_SHIFT)
+enum {
+    STAT_BUS_ERROR = 0,
+    /* Master mode */
+    STAT_M_STA_TX,
+    STAT_M_RSTA_TX,
+    STAT_M_ADDR_WR_ACK,
+    STAT_M_ADDR_WR_NACK,
+    STAT_M_DATA_TX_ACK,
+    STAT_M_DATA_TX_NACK,
+    STAT_M_ARB_LOST,
+    STAT_M_ADDR_RD_ACK,
+    STAT_M_ADDR_RD_NACK,
+    STAT_M_DATA_RX_ACK,
+    STAT_M_DATA_RX_NACK,
+    /* Slave mode */
+    STAT_S_ADDR_WR_ACK,
+    STAT_S_ARB_LOST_AW_ACK,
+    STAT_S_GCA_ACK,
+    STAT_S_ARB_LOST_GCA_ACK,
+    STAT_S_DATA_RX_SA_ACK,
+    STAT_S_DATA_RX_SA_NACK,
+    STAT_S_DATA_RX_GCA_ACK,
+    STAT_S_DATA_RX_GCA_NACK,
+    STAT_S_STP_RSTA,
+    STAT_S_ADDR_RD_ACK,
+    STAT_S_ARB_LOST_AR_ACK,
+    STAT_S_DATA_TX_ACK,
+    STAT_S_DATA_TX_NACK,
+    STAT_S_LB_TX_ACK,
+    /* Master mode, 10-bit */
+    STAT_M_2ND_ADDR_WR_ACK,
+    STAT_M_2ND_ADDR_WR_NACK,
+    /* Idle */
+    STAT_IDLE = 0x1f
+} TWI_STAT_STA;
+
+static const char *allwinner_i2c_get_regname(unsigned offset)
+{
+    switch (offset) {
+    case TWI_ADDR_REG:
+        return "ADDR";
+    case TWI_XADDR_REG:
+        return "XADDR";
+    case TWI_DATA_REG:
+        return "DATA";
+    case TWI_CNTR_REG:
+        return "CNTR";
+    case TWI_STAT_REG:
+        return "STAT";
+    case TWI_CCR_REG:
+        return "CCR";
+    case TWI_SRST_REG:
+        return "SRST";
+    case TWI_EFR_REG:
+        return "EFR";
+    case TWI_LCR_REG:
+        return "LCR";
+    default:
+        return "[?]";
+    }
+}
+
+static inline bool allwinner_i2c_is_reset(AWI2CState *s)
+{
+    return s->srst & TWI_SRST_MASK;
+}
+
+static inline bool allwinner_i2c_bus_is_enabled(AWI2CState *s)
+{
+    return s->cntr & TWI_CNTR_BUS_EN;
+}
+
+static inline bool allwinner_i2c_interrupt_is_enabled(AWI2CState *s)
+{
+    return s->cntr & TWI_CNTR_INT_EN;
+}
+
+static void allwinner_i2c_reset_hold(Object *obj)
+{
+    AWI2CState *s = AW_I2C(obj);
+
+    if (STAT_TO_STA(s->stat) != STAT_IDLE) {
+        i2c_end_transfer(s->bus);
+    }
+
+    s->addr  = TWI_ADDR_RESET;
+    s->xaddr = TWI_XADDR_RESET;
+    s->data  = TWI_DATA_RESET;
+    s->cntr  = TWI_CNTR_RESET;
+    s->stat  = TWI_STAT_RESET;
+    s->ccr   = TWI_CCR_RESET;
+    s->srst  = TWI_SRST_RESET;
+    s->efr   = TWI_EFR_RESET;
+    s->lcr   = TWI_LCR_RESET;
+}
+
+static inline void allwinner_i2c_raise_interrupt(AWI2CState *s)
+{
+    /*
+     * Raise an interrupt if the device is not reset and it is configured
+     * to generate some interrupts.
+     */
+    if (!allwinner_i2c_is_reset(s) && allwinner_i2c_bus_is_enabled(s)) {
+        if (STAT_TO_STA(s->stat) != STAT_IDLE) {
+            s->cntr |= TWI_CNTR_INT_FLAG;
+            if (allwinner_i2c_interrupt_is_enabled(s)) {
+                qemu_irq_raise(s->irq);
+            }
+        }
+    }
+}
+
+static uint64_t allwinner_i2c_read(void *opaque, hwaddr offset,
+                                   unsigned size)
+{
+    uint16_t value;
+    AWI2CState *s = AW_I2C(opaque);
+
+    switch (offset) {
+    case TWI_ADDR_REG:
+        value = s->addr;
+        break;
+    case TWI_XADDR_REG:
+        value = s->xaddr;
+        break;
+    case TWI_DATA_REG:
+        switch (STAT_TO_STA(s->stat)) {
+        case STAT_M_ADDR_RD_ACK:
+        case STAT_M_DATA_RX_ACK:
+        case STAT_M_DATA_RX_NACK:
+            /* get the next byte */
+            s->data = i2c_recv(s->bus);
+
+            if (s->cntr & TWI_CNTR_A_ACK) {
+                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
+            } else {
+                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
+            }
+            allwinner_i2c_raise_interrupt(s);
+            break;
+        default:
+            break;
+        }
+        value = s->data;
+        break;
+    case TWI_CNTR_REG:
+        value = s->cntr;
+        break;
+    case TWI_STAT_REG:
+        value = s->stat;
+        /*
+         * If polling when reading then change state to indicate data
+         * is available
+         */
+        if (STAT_TO_STA(s->stat) == STAT_M_ADDR_RD_ACK) {
+            if (s->cntr & TWI_CNTR_A_ACK) {
+                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
+            } else {
+                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
+            }
+            allwinner_i2c_raise_interrupt(s);
+        }
+        break;
+    case TWI_CCR_REG:
+        value = s->ccr;
+        break;
+    case TWI_SRST_REG:
+        value = s->srst;
+        break;
+    case TWI_EFR_REG:
+        value = s->efr;
+        break;
+    case TWI_LCR_REG:
+        value = s->lcr;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_AW_I2C, __func__, offset);
+        value = 0;
+        break;
+    }
+
+    DPRINTF("read %s [0x%" HWADDR_PRIx "] -> 0x%02x\n",
+            allwinner_i2c_get_regname(offset), offset, value);
+
+    return (uint64_t)value;
+}
+
+static void allwinner_i2c_write(void *opaque, hwaddr offset,
+                                uint64_t value, unsigned size)
+{
+    AWI2CState *s = AW_I2C(opaque);
+
+    DPRINTF("write %s [0x%" HWADDR_PRIx "] <- 0x%02x\n",
+            allwinner_i2c_get_regname(offset), offset, (int)value);
+
+    value &= 0xff;
+
+    switch (offset) {
+    case TWI_ADDR_REG:
+        s->addr = (uint8_t)value;
+        break;
+    case TWI_XADDR_REG:
+        s->xaddr = (uint8_t)value;
+        break;
+    case TWI_DATA_REG:
+        /* If the device is in reset or not enabled, nothing to do */
+        if (allwinner_i2c_is_reset(s) || (!allwinner_i2c_bus_is_enabled(s))) {
+            break;
+        }
+
+        s->data = value & TWI_DATA_MASK;
+
+        switch (STAT_TO_STA(s->stat)) {
+        case STAT_M_STA_TX:
+        case STAT_M_RSTA_TX:
+            /* Send address */
+            if (i2c_start_transfer(s->bus, extract32(s->data, 1, 7),
+                                extract32(s->data, 0, 1))) {
+                /* If non zero is returned, the address is not valid */
+                s->stat = STAT_FROM_STA(STAT_M_ADDR_WR_NACK);
+            } else {
+                /* Determine if read of write */
+                if (extract32(s->data, 0, 1)) {
+                    s->stat = STAT_FROM_STA(STAT_M_ADDR_RD_ACK);
+                } else {
+                    s->stat = STAT_FROM_STA(STAT_M_ADDR_WR_ACK);
+                }
+                allwinner_i2c_raise_interrupt(s);
+            }
+            break;
+        case STAT_M_ADDR_WR_ACK:
+        case STAT_M_DATA_TX_ACK:
+            if (i2c_send(s->bus, s->data)) {
+                /* If the target return non zero then end the transfer */
+                s->stat = STAT_FROM_STA(STAT_M_DATA_TX_NACK);
+                i2c_end_transfer(s->bus);
+            } else {
+                s->stat = STAT_FROM_STA(STAT_M_DATA_TX_ACK);
+                allwinner_i2c_raise_interrupt(s);
+            }
+            break;
+        default:
+            break;
+        }
+        break;
+    case TWI_CNTR_REG:
+        if (!allwinner_i2c_is_reset(s)) {
+            /* Do something only if not in software reset */
+            s->cntr = value & TWI_CNTR_MASK;
+
+            /* Check if start condition should be sent */
+            if (s->cntr & TWI_CNTR_M_STA) {
+                /* Update status */
+                if (STAT_TO_STA(s->stat) == STAT_IDLE) {
+                    /* Send start condition */
+                    s->stat = STAT_FROM_STA(STAT_M_STA_TX);
+                } else {
+                    /* Send repeated start condition */
+                    s->stat = STAT_FROM_STA(STAT_M_RSTA_TX);
+                }
+                /* Clear start condition */
+                s->cntr &= ~TWI_CNTR_M_STA;
+            }
+            if (s->cntr & TWI_CNTR_M_STP) {
+                /* Update status */
+                i2c_end_transfer(s->bus);
+                s->stat = STAT_FROM_STA(STAT_IDLE);
+                s->cntr &= ~TWI_CNTR_M_STP;
+            }
+            if ((s->cntr & TWI_CNTR_INT_FLAG) == 0) {
+                /* Interrupt flag cleared */
+                qemu_irq_lower(s->irq);
+            }
+            if ((s->cntr & TWI_CNTR_A_ACK) == 0) {
+                if (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_ACK) {
+                    s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
+                }
+            } else {
+                if (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_NACK) {
+                    s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
+                }
+            }
+            allwinner_i2c_raise_interrupt(s);
+
+        }
+        break;
+    case TWI_CCR_REG:
+        s->ccr = value & TWI_CCR_MASK;
+        break;
+    case TWI_SRST_REG:
+        if (((value & TWI_SRST_MASK) == 0) && (s->srst & TWI_SRST_MASK)) {
+            /* Perform reset */
+            allwinner_i2c_reset_hold(OBJECT(s));
+        }
+        s->srst = value & TWI_SRST_MASK;
+        break;
+    case TWI_EFR_REG:
+        s->efr = value & TWI_EFR_MASK;
+        break;
+    case TWI_LCR_REG:
+        s->lcr = value & TWI_LCR_MASK;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_AW_I2C, __func__, offset);
+        break;
+    }
+}
+
+static const MemoryRegionOps allwinner_i2c_ops = {
+    .read = allwinner_i2c_read,
+    .write = allwinner_i2c_write,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription allwinner_i2c_vmstate = {
+    .name = TYPE_AW_I2C,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(addr, AWI2CState),
+        VMSTATE_UINT8(xaddr, AWI2CState),
+        VMSTATE_UINT8(data, AWI2CState),
+        VMSTATE_UINT8(cntr, AWI2CState),
+        VMSTATE_UINT8(ccr, AWI2CState),
+        VMSTATE_UINT8(srst, AWI2CState),
+        VMSTATE_UINT8(efr, AWI2CState),
+        VMSTATE_UINT8(lcr, AWI2CState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void allwinner_i2c_realize(DeviceState *dev, Error **errp)
+{
+    AWI2CState *s = AW_I2C(dev);
+
+    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_i2c_ops, s,
+                          TYPE_AW_I2C, AW_I2C_MEM_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+    s->bus = i2c_init_bus(dev, "i2c");
+}
+
+static void allwinner_i2c_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    rc->phases.hold = allwinner_i2c_reset_hold;
+    dc->vmsd = &allwinner_i2c_vmstate;
+    dc->realize = allwinner_i2c_realize;
+    dc->desc = "Allwinner I2C Controller";
+}
+
+static const TypeInfo allwinner_i2c_type_info = {
+    .name = TYPE_AW_I2C,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(AWI2CState),
+    .class_init = allwinner_i2c_class_init,
+};
+
+static void allwinner_i2c_register_types(void)
+{
+    type_register_static(&allwinner_i2c_type_info);
+}
+
+type_init(allwinner_i2c_register_types)
diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build
index d3df273251..7de7f2f540 100644
--- a/hw/i2c/meson.build
+++ b/hw/i2c/meson.build
@@ -8,6 +8,7 @@ i2c_ss.add(when: 'CONFIG_BITBANG_I2C', if_true: files('bitbang_i2c.c'))
 i2c_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_i2c.c'))
 i2c_ss.add(when: 'CONFIG_IMX_I2C', if_true: files('imx_i2c.c'))
 i2c_ss.add(when: 'CONFIG_MPC_I2C', if_true: files('mpc_i2c.c'))
+i2c_ss.add(when: 'CONFIG_ALLWINNER_I2C', if_true: files('allwinner-i2c.c'))
 i2c_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('microbit_i2c.c'))
 i2c_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_smbus.c'))
 i2c_ss.add(when: 'CONFIG_SMBUS_EEPROM', if_true: files('smbus_eeprom.c'))
diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h
index abe4ff7066..763935fca9 100644
--- a/include/hw/arm/allwinner-a10.h
+++ b/include/hw/arm/allwinner-a10.h
@@ -14,6 +14,7 @@
 #include "hw/rtc/allwinner-rtc.h"
 #include "hw/misc/allwinner-a10-ccm.h"
 #include "hw/misc/allwinner-a10-dramc.h"
+#include "hw/i2c/allwinner-i2c.h"
 
 #include "target/arm/cpu.h"
 #include "qom/object.h"
@@ -39,6 +40,7 @@ struct AwA10State {
     AwEmacState emac;
     AllwinnerAHCIState sata;
     AwSdHostState mmc0;
+    AWI2CState i2c0;
     AwRtcState rtc;
     MemoryRegion sram_a;
     EHCISysBusState ehci[AW_A10_NUM_USB];
diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
index 63025fb27c..1d7ce20589 100644
--- a/include/hw/arm/allwinner-h3.h
+++ b/include/hw/arm/allwinner-h3.h
@@ -47,6 +47,7 @@
 #include "hw/sd/allwinner-sdhost.h"
 #include "hw/net/allwinner-sun8i-emac.h"
 #include "hw/rtc/allwinner-rtc.h"
+#include "hw/i2c/allwinner-i2c.h"
 #include "target/arm/cpu.h"
 #include "sysemu/block-backend.h"
 
@@ -82,6 +83,7 @@ enum {
     AW_H3_DEV_UART2,
     AW_H3_DEV_UART3,
     AW_H3_DEV_EMAC,
+    AW_H3_DEV_TWI0,
     AW_H3_DEV_DRAMCOM,
     AW_H3_DEV_DRAMCTL,
     AW_H3_DEV_DRAMPHY,
@@ -130,6 +132,7 @@ struct AwH3State {
     AwH3SysCtrlState sysctrl;
     AwSidState sid;
     AwSdHostState mmc0;
+    AWI2CState i2c0;
     AwSun8iEmacState emac;
     AwRtcState rtc;
     GICState gic;
diff --git a/include/hw/i2c/allwinner-i2c.h b/include/hw/i2c/allwinner-i2c.h
new file mode 100644
index 0000000000..1e3169a5b9
--- /dev/null
+++ b/include/hw/i2c/allwinner-i2c.h
@@ -0,0 +1,112 @@
+/*
+ *  Allwinner I2C Bus Serial Interface registers definition
+ *
+ *  Copyright (C) 2022 Strahinja Jankovic. <strahinja.p.jankovic@gmail.com>
+ *
+ *  This file is derived from IMX I2C controller,
+ *  by Jean-Christophe DUBOIS .
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef ALLWINNER_I2C_H
+#define ALLWINNER_I2C_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_AW_I2C "allwinner.i2c"
+OBJECT_DECLARE_SIMPLE_TYPE(AWI2CState, AW_I2C)
+
+#define AW_I2C_MEM_SIZE         0x24
+
+/* Allwinner I2C memory map */
+#define TWI_ADDR_REG            0x00  /* slave address register */
+#define TWI_XADDR_REG           0x04  /* extended slave address register */
+#define TWI_DATA_REG            0x08  /* data register */
+#define TWI_CNTR_REG            0x0c  /* control register */
+#define TWI_STAT_REG            0x10  /* status register */
+#define TWI_CCR_REG             0x14  /* clock control register */
+#define TWI_SRST_REG            0x18  /* software reset register */
+#define TWI_EFR_REG             0x1c  /* enhance feature register */
+#define TWI_LCR_REG             0x20  /* line control register */
+
+/* Used only in slave mode, do not set */
+#define TWI_ADDR_RESET          0
+#define TWI_XADDR_RESET         0
+
+/* Data register */
+#define TWI_DATA_MASK           0xFF
+#define TWI_DATA_RESET          0
+
+/* Control register */
+#define TWI_CNTR_INT_EN         (1 << 7)
+#define TWI_CNTR_BUS_EN         (1 << 6)
+#define TWI_CNTR_M_STA          (1 << 5)
+#define TWI_CNTR_M_STP          (1 << 4)
+#define TWI_CNTR_INT_FLAG       (1 << 3)
+#define TWI_CNTR_A_ACK          (1 << 2)
+#define TWI_CNTR_MASK           0xFC
+#define TWI_CNTR_RESET          0
+
+/* Status register */
+#define TWI_STAT_MASK           0xF8
+#define TWI_STAT_RESET          0xF8
+
+/* Clock register */
+#define TWI_CCR_CLK_M_MASK      0x78
+#define TWI_CCR_CLK_N_MASK      0x07
+#define TWI_CCR_MASK            0x7F
+#define TWI_CCR_RESET           0
+
+/* Soft reset */
+#define TWI_SRST_MASK           0x01
+#define TWI_SRST_RESET          0
+
+/* Enhance feature */
+#define TWI_EFR_MASK            0x03
+#define TWI_EFR_RESET           0
+
+/* Line control */
+#define TWI_LCR_SCL_STATE       (1 << 5)
+#define TWI_LCR_SDA_STATE       (1 << 4)
+#define TWI_LCR_SCL_CTL         (1 << 3)
+#define TWI_LCR_SCL_CTL_EN      (1 << 2)
+#define TWI_LCR_SDA_CTL         (1 << 1)
+#define TWI_LCR_SDA_CTL_EN      (1 << 0)
+#define TWI_LCR_MASK            0x3F
+#define TWI_LCR_RESET           0x3A
+
+struct AWI2CState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    I2CBus *bus;
+    qemu_irq irq;
+
+    uint8_t addr;
+    uint8_t xaddr;
+    uint8_t data;
+    uint8_t cntr;
+    uint8_t stat;
+    uint8_t ccr;
+    uint8_t srst;
+    uint8_t efr;
+    uint8_t lcr;
+};
+
+#endif /* ALLWINNER_I2C_H */
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 4/6] hw/misc: Allwinner AXP-209 Emulation
  2022-12-03 23:18 [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Strahinja Jankovic
                   ` (2 preceding siblings ...)
  2022-12-03 23:19 ` [PATCH 3/6] hw/i2c: Allwinner TWI/I2C Emulation Strahinja Jankovic
@ 2022-12-03 23:19 ` Strahinja Jankovic
  2022-12-04 21:39   ` Philippe Mathieu-Daudé
  2022-12-03 23:19 ` [PATCH 5/6] hw/arm: Add AXP-209 to Cubieboard Strahinja Jankovic
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Strahinja Jankovic @ 2022-12-03 23:19 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Beniamino Galvani, Niek Linnenbank, qemu-arm, qemu-devel,
	Strahinja Jankovic

This patch adds minimal support for AXP-209 PMU.
Most important is chip ID since U-Boot SPL expects version 0x1. Besides
the chip ID register, reset values for two more registers used by A10
U-Boot SPL are covered.

Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
---
 hw/arm/Kconfig              |   1 +
 hw/misc/Kconfig             |   4 +
 hw/misc/allwinner-axp-209.c | 263 ++++++++++++++++++++++++++++++++++++
 hw/misc/meson.build         |   1 +
 4 files changed, 269 insertions(+)
 create mode 100644 hw/misc/allwinner-axp-209.c

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index eefe1fd134..67c6e83fe6 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -323,6 +323,7 @@ config ALLWINNER_A10
     select ALLWINNER_A10_DRAMC
     select ALLWINNER_EMAC
     select ALLWINNER_I2C
+    select ALLWINNER_AXP_209
     select SERIAL
     select UNIMP
 
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 052fb54310..3855d937fd 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -180,4 +180,8 @@ config ALLWINNER_A10_CCM
 config ALLWINNER_A10_DRAMC
     bool
 
+config ALLWINNER_AXP_209
+    bool
+    depends on I2C
+
 source macio/Kconfig
diff --git a/hw/misc/allwinner-axp-209.c b/hw/misc/allwinner-axp-209.c
new file mode 100644
index 0000000000..229e3961b6
--- /dev/null
+++ b/hw/misc/allwinner-axp-209.c
@@ -0,0 +1,263 @@
+/*
+ * AXP-209 Emulation
+ *
+ * Written by Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/i2c/i2c.h"
+#include "migration/vmstate.h"
+
+#ifndef AXP_209_ERR_DEBUG
+#define AXP_209_ERR_DEBUG 0
+#endif
+
+#define TYPE_AXP_209 "allwinner.axp209"
+
+#define AXP_209(obj) \
+    OBJECT_CHECK(AXP209I2CState, (obj), TYPE_AXP_209)
+
+#define DB_PRINT(fmt, args...) do { \
+    if (AXP_209_ERR_DEBUG) { \
+        fprintf(stderr, "%s: " fmt, __func__, ## args); \
+    } \
+} while (0)
+
+/* registers */
+enum {
+    REG_POWER_STATUS = 0x0u,
+    REG_OPERATING_MODE,
+    REG_OTG_VBUS_STATUS,
+    REG_CHIP_VERSION,
+    REG_DATA_CACHE_0,
+    REG_DATA_CACHE_1,
+    REG_DATA_CACHE_2,
+    REG_DATA_CACHE_3,
+    REG_DATA_CACHE_4,
+    REG_DATA_CACHE_5,
+    REG_DATA_CACHE_6,
+    REG_DATA_CACHE_7,
+    REG_DATA_CACHE_8,
+    REG_DATA_CACHE_9,
+    REG_DATA_CACHE_A,
+    REG_DATA_CACHE_B,
+    REG_POWER_OUTPUT_CTRL = 0x12u,
+    REG_DC_DC2_OUT_V_CTRL = 0x23u,
+    REG_DC_DC2_DVS_CTRL = 0x25u,
+    REG_DC_DC3_OUT_V_CTRL = 0x27u,
+    REG_LDO2_4_OUT_V_CTRL,
+    REG_LDO3_OUT_V_CTRL,
+    REG_VBUS_CH_MGMT = 0x30u,
+    REG_SHUTDOWN_V_CTRL,
+    REG_SHUTDOWN_CTRL,
+    REG_CHARGE_CTRL_1,
+    REG_CHARGE_CTRL_2,
+    REG_SPARE_CHARGE_CTRL,
+    REG_PEK_KEY_CTRL,
+    REG_DC_DC_FREQ_SET,
+    REG_CHR_TEMP_TH_SET,
+    REG_CHR_HIGH_TEMP_TH_CTRL,
+    REG_IPSOUT_WARN_L1,
+    REG_IPSOUT_WARN_L2,
+    REG_DISCHR_TEMP_TH_SET,
+    REG_DISCHR_HIGH_TEMP_TH_CTRL,
+    REG_IRQ_BANK_1_CTRL = 0x40u,
+    REG_IRQ_BANK_2_CTRL,
+    REG_IRQ_BANK_3_CTRL,
+    REG_IRQ_BANK_4_CTRL,
+    REG_IRQ_BANK_5_CTRL,
+    REG_IRQ_BANK_1_STAT = 0x48u,
+    REG_IRQ_BANK_2_STAT,
+    REG_IRQ_BANK_3_STAT,
+    REG_IRQ_BANK_4_STAT,
+    REG_IRQ_BANK_5_STAT,
+    REG_ADC_ACIN_V_H = 0x56u,
+    REG_ADC_ACIN_V_L,
+    REG_ADC_ACIN_CURR_H,
+    REG_ADC_ACIN_CURR_L,
+    REG_ADC_VBUS_V_H,
+    REG_ADC_VBUS_V_L,
+    REG_ADC_VBUS_CURR_H,
+    REG_ADC_VBUS_CURR_L,
+    REG_ADC_INT_TEMP_H,
+    REG_ADC_INT_TEMP_L,
+    REG_ADC_TEMP_SENS_V_H = 0x62u,
+    REG_ADC_TEMP_SENS_V_L,
+    REG_ADC_BAT_V_H = 0x78u,
+    REG_ADC_BAT_V_L,
+    REG_ADC_BAT_DISCHR_CURR_H,
+    REG_ADC_BAT_DISCHR_CURR_L,
+    REG_ADC_BAT_CHR_CURR_H,
+    REG_ADC_BAT_CHR_CURR_L,
+    REG_ADC_IPSOUT_V_H,
+    REG_ADC_IPSOUT_V_L,
+    REG_DC_DC_MOD_SEL = 0x80u,
+    REG_ADC_EN_1,
+    REG_ADC_EN_2,
+    REG_ADC_SR_CTRL,
+    REG_ADC_IN_RANGE,
+    REG_GPIO1_ADC_IRQ_RISING_TH,
+    REG_GPIO1_ADC_IRQ_FALLING_TH,
+    REG_TIMER_CTRL = 0x8au,
+    REG_VBUS_CTRL_MON_SRP,
+    REG_OVER_TEMP_SHUTDOWN = 0x8fu,
+    REG_GPIO0_FEAT_SET,
+    REG_GPIO_OUT_HIGH_SET,
+    REG_GPIO1_FEAT_SET,
+    REG_GPIO2_FEAT_SET,
+    REG_GPIO_SIG_STATE_SET_MON,
+    REG_GPIO3_SET,
+    REG_COULOMB_CNTR_CTRL = 0xb8u,
+    REG_POWER_MEAS_RES,
+    NR_REGS
+};
+
+#define AXP_209_CHIP_VERSION_ID             (0x01)
+#define AXP_209_DC_DC2_OUT_V_CTRL_RESET     (0x16)
+#define AXP_209_IRQ_BANK_1_CTRL_RESET       (0xd8)
+
+/* A simple I2C slave which returns values of ID or CNT register. */
+typedef struct AXP209I2CState {
+    /*< private >*/
+    I2CSlave i2c;
+    /*< public >*/
+    uint8_t regs[NR_REGS];  /* peripheral registers */
+    uint8_t ptr;            /* current register index */
+    uint8_t count;          /* counter used for tx/rx */
+} AXP209I2CState;
+
+/* Reset all counters and load ID register */
+static void axp_209_reset_enter(Object *obj, ResetType type)
+{
+    AXP209I2CState *s = AXP_209(obj);
+
+    memset(s->regs, 0, NR_REGS);
+    s->ptr = 0;
+    s->count = 0;
+    s->regs[REG_CHIP_VERSION] = AXP_209_CHIP_VERSION_ID;
+    s->regs[REG_DC_DC2_OUT_V_CTRL] = AXP_209_DC_DC2_OUT_V_CTRL_RESET;
+    s->regs[REG_IRQ_BANK_1_CTRL] = AXP_209_IRQ_BANK_1_CTRL_RESET;
+}
+
+/* Handle events from master. */
+static int axp_209_event(I2CSlave *i2c, enum i2c_event event)
+{
+    AXP209I2CState *s = AXP_209(i2c);
+
+    s->count = 0;
+
+    return 0;
+}
+
+/* Called when master requests read */
+static uint8_t axp_209_rx(I2CSlave *i2c)
+{
+    AXP209I2CState *s = AXP_209(i2c);
+    uint8_t ret = 0xff;
+
+    if (s->ptr < NR_REGS) {
+        ret = s->regs[s->ptr++];
+    }
+
+    DB_PRINT("Reading from %d : %d\n", s->ptr - 1, ret);
+
+    return ret;
+}
+
+/*
+ * Called when master sends write.
+ * Update ptr with byte 0, then perform write with second byte.
+ */
+static int axp_209_tx(I2CSlave *i2c, uint8_t data)
+{
+    AXP209I2CState *s = AXP_209(i2c);
+
+    if (s->count == 0) {
+        /* Store register address */
+        s->ptr = data;
+        s->count++;
+        DB_PRINT("Register to access %d\n", data);
+    } else {
+        DB_PRINT("Writing to register %d : %d\n", s->ptr, data);
+        if (s->ptr == REG_DC_DC2_OUT_V_CTRL) {
+            s->regs[s->ptr++] = data;
+        }
+    }
+
+    return 0;
+}
+
+/* Initialization */
+static void axp_209_init(Object *obj)
+{
+    AXP209I2CState *s = AXP_209(obj);
+
+    s->count = 0;
+    s->ptr = 0;
+    memset(s->regs, 0, NR_REGS);
+    s->regs[REG_CHIP_VERSION] = AXP_209_CHIP_VERSION_ID;
+    s->regs[REG_DC_DC2_OUT_V_CTRL] = 0x16;
+    s->regs[REG_IRQ_BANK_1_CTRL] = 0xd8;
+
+    DB_PRINT("INIT AXP209\n");
+
+    return;
+}
+
+static const VMStateDescription vmstate_axp_209 = {
+    .name = TYPE_AXP_209,
+    .version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8_ARRAY(regs, AXP209I2CState, NR_REGS),
+        VMSTATE_UINT8(count, AXP209I2CState),
+        VMSTATE_UINT8(ptr, AXP209I2CState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void axp_209_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc);
+    ResettableClass *rc = RESETTABLE_CLASS(oc);
+
+    rc->phases.enter = axp_209_reset_enter;
+    dc->vmsd = &vmstate_axp_209;
+    isc->event = axp_209_event;
+    isc->recv = axp_209_rx;
+    isc->send = axp_209_tx;
+}
+
+static const TypeInfo axp_209_info = {
+    .name = TYPE_AXP_209,
+    .parent = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(AXP209I2CState),
+    .instance_init = axp_209_init,
+    .class_init = axp_209_class_init
+};
+
+static void axp_209_register_devices(void)
+{
+    type_register_static(&axp_209_info);
+}
+
+type_init(axp_209_register_devices);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index d7f49f0f81..c85a0127fe 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -40,6 +40,7 @@ softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c'))
 
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: files('allwinner-a10-ccm.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_DRAMC', if_true: files('allwinner-a10-dramc.c'))
+softmmu_ss.add(when: 'CONFIG_ALLWINNER_AXP_209', if_true: files('allwinner-axp-209.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-ccu.c'))
 specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-cpucfg.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c'))
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 5/6] hw/arm: Add AXP-209 to Cubieboard
  2022-12-03 23:18 [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Strahinja Jankovic
                   ` (3 preceding siblings ...)
  2022-12-03 23:19 ` [PATCH 4/6] hw/misc: Allwinner AXP-209 Emulation Strahinja Jankovic
@ 2022-12-03 23:19 ` Strahinja Jankovic
  2022-12-03 23:19 ` [PATCH 6/6] hw/arm: Allwinner A10 enable SPL load from MMC Strahinja Jankovic
  2022-12-07 20:25 ` [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Niek Linnenbank
  6 siblings, 0 replies; 18+ messages in thread
From: Strahinja Jankovic @ 2022-12-03 23:19 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Beniamino Galvani, Niek Linnenbank, qemu-arm, qemu-devel,
	Strahinja Jankovic

SPL Boot for Cubieboard expects AXP-209 connected to I2C0 bus.

Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
---
 hw/arm/cubieboard.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c
index 5e3372a3c7..afc7980414 100644
--- a/hw/arm/cubieboard.c
+++ b/hw/arm/cubieboard.c
@@ -20,6 +20,7 @@
 #include "hw/boards.h"
 #include "hw/qdev-properties.h"
 #include "hw/arm/allwinner-a10.h"
+#include "hw/i2c/i2c.h"
 
 static struct arm_boot_info cubieboard_binfo = {
     .loader_start = AW_A10_SDRAM_BASE,
@@ -34,6 +35,7 @@ static void cubieboard_init(MachineState *machine)
     BlockBackend *blk;
     BusState *bus;
     DeviceState *carddev;
+    I2CBus *i2c;
 
     /* BIOS is not supported by this board */
     if (machine->firmware) {
@@ -80,6 +82,10 @@ static void cubieboard_init(MachineState *machine)
         exit(1);
     }
 
+    /* Connect AXP 209 */
+    i2c = (I2CBus *)qdev_get_child_bus(DEVICE(&a10->i2c0), "i2c");
+    i2c_slave_create_simple(i2c, "allwinner.axp209", 0x34);
+
     /* Retrieve SD bus */
     di = drive_get(IF_SD, 0, 0);
     blk = di ? blk_by_legacy_dinfo(di) : NULL;
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 6/6] hw/arm: Allwinner A10 enable SPL load from MMC
  2022-12-03 23:18 [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Strahinja Jankovic
                   ` (4 preceding siblings ...)
  2022-12-03 23:19 ` [PATCH 5/6] hw/arm: Add AXP-209 to Cubieboard Strahinja Jankovic
@ 2022-12-03 23:19 ` Strahinja Jankovic
  2022-12-07 22:39   ` Niek Linnenbank
  2022-12-07 20:25 ` [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Niek Linnenbank
  6 siblings, 1 reply; 18+ messages in thread
From: Strahinja Jankovic @ 2022-12-03 23:19 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Beniamino Galvani, Niek Linnenbank, qemu-arm, qemu-devel,
	Strahinja Jankovic

This patch enables copying of SPL from MMC if `-kernel` parameter is not
passed when starting QEMU. SPL is copied to SRAM_A.

The approach is reused from Allwinner H3 implementation.

Tested with Armbian and custom Yocto image.

Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
---
 hw/arm/allwinner-a10.c         | 18 ++++++++++++++++++
 hw/arm/cubieboard.c            |  5 +++++
 include/hw/arm/allwinner-a10.h | 21 +++++++++++++++++++++
 3 files changed, 44 insertions(+)

diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
index 17e439777e..dc1966ff7a 100644
--- a/hw/arm/allwinner-a10.c
+++ b/hw/arm/allwinner-a10.c
@@ -24,7 +24,9 @@
 #include "sysemu/sysemu.h"
 #include "hw/boards.h"
 #include "hw/usb/hcd-ohci.h"
+#include "hw/loader.h"
 
+#define AW_A10_SRAM_A_BASE      0x00000000
 #define AW_A10_DRAMC_BASE       0x01c01000
 #define AW_A10_MMC0_BASE        0x01c0f000
 #define AW_A10_CCM_BASE         0x01c20000
@@ -38,6 +40,22 @@
 #define AW_A10_RTC_BASE         0x01c20d00
 #define AW_A10_I2C0_BASE        0x01c2ac00
 
+void allwinner_a10_bootrom_setup(AwA10State *s, BlockBackend *blk)
+{
+    const int64_t rom_size = 32 * KiB;
+    g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size);
+
+    if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) {
+        error_setg(&error_fatal, "%s: failed to read BlockBackend data",
+                   __func__);
+        return;
+    }
+
+    rom_add_blob("allwinner-a10.bootrom", buffer, rom_size,
+                  rom_size, AW_A10_SRAM_A_BASE,
+                  NULL, NULL, NULL, NULL, false);
+}
+
 static void aw_a10_init(Object *obj)
 {
     AwA10State *s = AW_A10(obj);
diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c
index afc7980414..37659c35fd 100644
--- a/hw/arm/cubieboard.c
+++ b/hw/arm/cubieboard.c
@@ -99,6 +99,11 @@ static void cubieboard_init(MachineState *machine)
     memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE,
                                 machine->ram);
 
+    /* Load target kernel or start using BootROM */
+    if (!machine->kernel_filename && blk && blk_is_available(blk)) {
+        /* Use Boot ROM to copy data from SD card to SRAM */
+        allwinner_a10_bootrom_setup(a10, blk);
+    }
     /* TODO create and connect IDE devices for ide_drive_get() */
 
     cubieboard_binfo.ram_size = machine->ram_size;
diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h
index 763935fca9..b3c9ed24c7 100644
--- a/include/hw/arm/allwinner-a10.h
+++ b/include/hw/arm/allwinner-a10.h
@@ -15,6 +15,7 @@
 #include "hw/misc/allwinner-a10-ccm.h"
 #include "hw/misc/allwinner-a10-dramc.h"
 #include "hw/i2c/allwinner-i2c.h"
+#include "sysemu/block-backend.h"
 
 #include "target/arm/cpu.h"
 #include "qom/object.h"
@@ -47,4 +48,24 @@ struct AwA10State {
     OHCISysBusState ohci[AW_A10_NUM_USB];
 };
 
+/**
+ * Emulate Boot ROM firmware setup functionality.
+ *
+ * A real Allwinner A10 SoC contains a Boot ROM
+ * which is the first code that runs right after
+ * the SoC is powered on. The Boot ROM is responsible
+ * for loading user code (e.g. a bootloader) from any
+ * of the supported external devices and writing the
+ * downloaded code to internal SRAM. After loading the SoC
+ * begins executing the code written to SRAM.
+ *
+ * This function emulates the Boot ROM by copying 32 KiB
+ * of data from the given block device and writes it to
+ * the start of the first internal SRAM memory.
+ *
+ * @s: Allwinner A10 state object pointer
+ * @blk: Block backend device object pointer
+ */
+void allwinner_a10_bootrom_setup(AwA10State *s, BlockBackend *blk);
+
 #endif
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/6] hw/misc: Allwinner AXP-209 Emulation
  2022-12-03 23:19 ` [PATCH 4/6] hw/misc: Allwinner AXP-209 Emulation Strahinja Jankovic
@ 2022-12-04 21:39   ` Philippe Mathieu-Daudé
  2022-12-05 21:07     ` Strahinja Jankovic
  0 siblings, 1 reply; 18+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-12-04 21:39 UTC (permalink / raw)
  To: Strahinja Jankovic
  Cc: Beniamino Galvani, Peter Maydell, Niek Linnenbank, qemu-arm,
	qemu-devel, Strahinja Jankovic

Hi Strahinja,

On 4/12/22 00:19, Strahinja Jankovic wrote:
> This patch adds minimal support for AXP-209 PMU.
> Most important is chip ID since U-Boot SPL expects version 0x1. Besides
> the chip ID register, reset values for two more registers used by A10
> U-Boot SPL are covered.
> 
> Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> ---
>   hw/arm/Kconfig              |   1 +
>   hw/misc/Kconfig             |   4 +
>   hw/misc/allwinner-axp-209.c | 263 ++++++++++++++++++++++++++++++++++++
>   hw/misc/meson.build         |   1 +
>   4 files changed, 269 insertions(+)
>   create mode 100644 hw/misc/allwinner-axp-209.c


> diff --git a/hw/misc/allwinner-axp-209.c b/hw/misc/allwinner-axp-209.c
> new file mode 100644
> index 0000000000..229e3961b6
> --- /dev/null
> +++ b/hw/misc/allwinner-axp-209.c
> @@ -0,0 +1,263 @@
> +/*
> + * AXP-209 Emulation
> + *
> + * Written by Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> + *

You missed the "Copyright (c) <year> <copyright holders>" line.

> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> + * DEALINGS IN THE SOFTWARE.

If you mind, please also include:

     * SPDX-License-Identifier: MIT

> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "hw/i2c/i2c.h"
> +#include "migration/vmstate.h"
> +
> +#ifndef AXP_209_ERR_DEBUG
> +#define AXP_209_ERR_DEBUG 0
> +#endif
> +
> +#define TYPE_AXP_209 "allwinner.axp209"
> +
> +#define AXP_209(obj) \
> +    OBJECT_CHECK(AXP209I2CState, (obj), TYPE_AXP_209)
> +
> +#define DB_PRINT(fmt, args...) do { \
> +    if (AXP_209_ERR_DEBUG) { \
> +        fprintf(stderr, "%s: " fmt, __func__, ## args); \

Please replace the DB_PRINT() calls by trace events which are more
powerful: when a tracing backend is present, the events are built
in and you can individually enable them at runtime.

> +    } \
> +} while (0)


> +#define AXP_209_CHIP_VERSION_ID             (0x01)
> +#define AXP_209_DC_DC2_OUT_V_CTRL_RESET     (0x16)
> +#define AXP_209_IRQ_BANK_1_CTRL_RESET       (0xd8)


> +/* Reset all counters and load ID register */
> +static void axp_209_reset_enter(Object *obj, ResetType type)
> +{
> +    AXP209I2CState *s = AXP_209(obj);
> +
> +    memset(s->regs, 0, NR_REGS);
> +    s->ptr = 0;
> +    s->count = 0;
> +    s->regs[REG_CHIP_VERSION] = AXP_209_CHIP_VERSION_ID;
> +    s->regs[REG_DC_DC2_OUT_V_CTRL] = AXP_209_DC_DC2_OUT_V_CTRL_RESET;
> +    s->regs[REG_IRQ_BANK_1_CTRL] = AXP_209_IRQ_BANK_1_CTRL_RESET;
> +}


> +/* Initialization */
> +static void axp_209_init(Object *obj)
> +{
> +    AXP209I2CState *s = AXP_209(obj);
> +
> +    s->count = 0;
> +    s->ptr = 0;
> +    memset(s->regs, 0, NR_REGS);
> +    s->regs[REG_CHIP_VERSION] = AXP_209_CHIP_VERSION_ID;
> +    s->regs[REG_DC_DC2_OUT_V_CTRL] = 0x16;
> +    s->regs[REG_IRQ_BANK_1_CTRL] = 0xd8;

The device initialization flow is:

  - init()
  - realize()
  - reset()

So these values are already set in axp_209_reset_enter().

Besides, you should use the definition you added instead of
magic values (AXP_209_DC_DC2_OUT_V_CTRL_RESET and
AXP_209_IRQ_BANK_1_CTRL_RESET).

> +
> +    DB_PRINT("INIT AXP209\n");
> +
> +    return;
> +}

Otherwise LGTM!

Thanks,

Phil.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/6] hw/misc: Allwinner AXP-209 Emulation
  2022-12-04 21:39   ` Philippe Mathieu-Daudé
@ 2022-12-05 21:07     ` Strahinja Jankovic
  0 siblings, 0 replies; 18+ messages in thread
From: Strahinja Jankovic @ 2022-12-05 21:07 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, Niek Linnenbank, qemu-arm, qemu-devel

Hi Philippe,

On Sun, Dec 4, 2022 at 10:39 PM Philippe Mathieu-Daudé
<philmd@linaro.org> wrote:
>
> Hi Strahinja,
>
> On 4/12/22 00:19, Strahinja Jankovic wrote:
> > This patch adds minimal support for AXP-209 PMU.
> > Most important is chip ID since U-Boot SPL expects version 0x1. Besides
> > the chip ID register, reset values for two more registers used by A10
> > U-Boot SPL are covered.
> >
> > Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> > ---
> >   hw/arm/Kconfig              |   1 +
> >   hw/misc/Kconfig             |   4 +
> >   hw/misc/allwinner-axp-209.c | 263 ++++++++++++++++++++++++++++++++++++
> >   hw/misc/meson.build         |   1 +
> >   4 files changed, 269 insertions(+)
> >   create mode 100644 hw/misc/allwinner-axp-209.c
>
>
> > diff --git a/hw/misc/allwinner-axp-209.c b/hw/misc/allwinner-axp-209.c
> > new file mode 100644
> > index 0000000000..229e3961b6
> > --- /dev/null
> > +++ b/hw/misc/allwinner-axp-209.c
> > @@ -0,0 +1,263 @@
> > +/*
> > + * AXP-209 Emulation
> > + *
> > + * Written by Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> > + *
>
> You missed the "Copyright (c) <year> <copyright holders>" line.

Ok, I will add it.

>
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice shall be included in
> > + * all copies or substantial portions of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> > + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> > + * DEALINGS IN THE SOFTWARE.
>
> If you mind, please also include:
>
>      * SPDX-License-Identifier: MIT

Ok, I will add it.

>
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qemu/log.h"
> > +#include "hw/i2c/i2c.h"
> > +#include "migration/vmstate.h"
> > +
> > +#ifndef AXP_209_ERR_DEBUG
> > +#define AXP_209_ERR_DEBUG 0
> > +#endif
> > +
> > +#define TYPE_AXP_209 "allwinner.axp209"
> > +
> > +#define AXP_209(obj) \
> > +    OBJECT_CHECK(AXP209I2CState, (obj), TYPE_AXP_209)
> > +
> > +#define DB_PRINT(fmt, args...) do { \
> > +    if (AXP_209_ERR_DEBUG) { \
> > +        fprintf(stderr, "%s: " fmt, __func__, ## args); \
>
> Please replace the DB_PRINT() calls by trace events which are more
> powerful: when a tracing backend is present, the events are built
> in and you can individually enable them at runtime.

I will do my best to update this to trace events. Have not used them
before, but I will look at other places in code and docs.

>
> > +    } \
> > +} while (0)
>
>
> > +#define AXP_209_CHIP_VERSION_ID             (0x01)
> > +#define AXP_209_DC_DC2_OUT_V_CTRL_RESET     (0x16)
> > +#define AXP_209_IRQ_BANK_1_CTRL_RESET       (0xd8)
>
>
> > +/* Reset all counters and load ID register */
> > +static void axp_209_reset_enter(Object *obj, ResetType type)
> > +{
> > +    AXP209I2CState *s = AXP_209(obj);
> > +
> > +    memset(s->regs, 0, NR_REGS);
> > +    s->ptr = 0;
> > +    s->count = 0;
> > +    s->regs[REG_CHIP_VERSION] = AXP_209_CHIP_VERSION_ID;
> > +    s->regs[REG_DC_DC2_OUT_V_CTRL] = AXP_209_DC_DC2_OUT_V_CTRL_RESET;
> > +    s->regs[REG_IRQ_BANK_1_CTRL] = AXP_209_IRQ_BANK_1_CTRL_RESET;
> > +}
>
>
> > +/* Initialization */
> > +static void axp_209_init(Object *obj)
> > +{
> > +    AXP209I2CState *s = AXP_209(obj);
> > +
> > +    s->count = 0;
> > +    s->ptr = 0;
> > +    memset(s->regs, 0, NR_REGS);
> > +    s->regs[REG_CHIP_VERSION] = AXP_209_CHIP_VERSION_ID;
> > +    s->regs[REG_DC_DC2_OUT_V_CTRL] = 0x16;
> > +    s->regs[REG_IRQ_BANK_1_CTRL] = 0xd8;
>
> The device initialization flow is:
>
>   - init()
>   - realize()
>   - reset()
>
> So these values are already set in axp_209_reset_enter().

Thanks, that makes perfect sense. I will update .init and .reset
functions accordingly in v2 of the patch.

>
> Besides, you should use the definition you added instead of
> magic values (AXP_209_DC_DC2_OUT_V_CTRL_RESET and
> AXP_209_IRQ_BANK_1_CTRL_RESET).

Yes, that was an oversight. I used the macros in .reset, but I forgot
to update them in .init.

>
> > +
> > +    DB_PRINT("INIT AXP209\n");
> > +
> > +    return;
> > +}
>
> Otherwise LGTM!

Thanks!

Best regards,
Strahinja

>
> Thanks,
>
> Phil.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card
  2022-12-03 23:18 [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Strahinja Jankovic
                   ` (5 preceding siblings ...)
  2022-12-03 23:19 ` [PATCH 6/6] hw/arm: Allwinner A10 enable SPL load from MMC Strahinja Jankovic
@ 2022-12-07 20:25 ` Niek Linnenbank
  2022-12-08 19:24   ` Strahinja Jankovic
  6 siblings, 1 reply; 18+ messages in thread
From: Niek Linnenbank @ 2022-12-07 20:25 UTC (permalink / raw)
  To: Strahinja Jankovic
  Cc: Peter Maydell, Beniamino Galvani, qemu-arm, qemu-devel,
	Strahinja Jankovic

[-- Attachment #1: Type: text/plain, Size: 3772 bytes --]

Hello Strahinja,

Thanks for contribution these patches, and also taking the H3 into account
:-)

I've ran the avocado based acceptance tests for both boards and got these
results:

$ ARMBIAN_ARTIFACTS_CACHED=yes AVOCADO_ALLOW_LARGE_STORAGE=yes
./build/tests/venv/bin/avocado --show=app,console run -t
machine:orangepi-pc tests/avocado/boot_linux_console.py
...
RESULTS    : PASS 5 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 |
CANCEL 0
JOB TIME   : 114.24 s

$ ./build/tests/venv/bin/avocado --show=app,console run -t
machine:cubieboard tests/avocado/boot_linux_console.py
...
RESULTS    : PASS 2 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 |
CANCEL 0
JOB TIME   : 22.79 s

So that shows both machines are still running fine. During startup of the
bionic 20.08 image for orangepi-pc it did show this message:
  console: i2c i2c-0: mv64xxx: I2C bus locked, block: 1, time_left: 0
  console: sy8106a: probe of 0-0065 failed with error -110

The SY8106a appears to be an peripheral attached to the I2C bus on the
orangepi-pc, and we don't emulate the SY8106a yet, so that's an error to be
expected:
  https://linux-sunxi.org/SY8106A

So for the series:
Tested-by: Niek Linnenbank <nieklinnenbank@gmail.com>

I'll try to reply to each patch as well.

Kind regards,
Niek

On Sun, Dec 4, 2022 at 12:19 AM Strahinja Jankovic <
strahinjapjankovic@gmail.com> wrote:

> This patch series adds missing Allwinner A10 modules needed for
> successful SPL boot:
> - Clock controller module
> - DRAM controller
> - I2C0 controller (added also for Allwinner H3 since it is the same)
> - AXP-209 connected to I2C0 bus
>
> It also updates Allwinner A10 emulation so SPL is copied from attached
> SD card if `-kernel` parameter is not passed when starting QEMU
> (approach adapted from Allwinner H3 implementation).
>
> Boot from SD card has been tested with Cubieboard Armbian SD card image
> and custom
> Yocto image built for Cubieboard.
> Example usage for Armbian image:
> qemu-system-arm -M cubieboard -nographic -sd
> ~/Armbian_22.11.0-trunk_Cubieboard_kinetic_edge_6.0.7.img
>
>
> Strahinja Jankovic (6):
>   hw/misc: Allwinner-A10 Clock Controller Module Emulation
>   hw/misc: Allwinner A10 DRAM Controller Emulation
>   hw/i2c: Allwinner TWI/I2C Emulation
>   hw/misc: Allwinner AXP-209 Emulation
>   hw/arm: Add AXP-209 to Cubieboard
>   hw/arm: Allwinner A10 enable SPL load from MMC
>
>  hw/arm/Kconfig                        |   5 +
>  hw/arm/allwinner-a10.c                |  40 +++
>  hw/arm/allwinner-h3.c                 |  11 +-
>  hw/arm/cubieboard.c                   |  11 +
>  hw/i2c/Kconfig                        |   4 +
>  hw/i2c/allwinner-i2c.c                | 417 ++++++++++++++++++++++++++
>  hw/i2c/meson.build                    |   1 +
>  hw/misc/Kconfig                       |  10 +
>  hw/misc/allwinner-a10-ccm.c           | 224 ++++++++++++++
>  hw/misc/allwinner-a10-dramc.c         | 179 +++++++++++
>  hw/misc/allwinner-axp-209.c           | 263 ++++++++++++++++
>  hw/misc/meson.build                   |   3 +
>  include/hw/arm/allwinner-a10.h        |  27 ++
>  include/hw/arm/allwinner-h3.h         |   3 +
>  include/hw/i2c/allwinner-i2c.h        | 112 +++++++
>  include/hw/misc/allwinner-a10-ccm.h   |  67 +++++
>  include/hw/misc/allwinner-a10-dramc.h |  68 +++++
>  17 files changed, 1444 insertions(+), 1 deletion(-)
>  create mode 100644 hw/i2c/allwinner-i2c.c
>  create mode 100644 hw/misc/allwinner-a10-ccm.c
>  create mode 100644 hw/misc/allwinner-a10-dramc.c
>  create mode 100644 hw/misc/allwinner-axp-209.c
>  create mode 100644 include/hw/i2c/allwinner-i2c.h
>  create mode 100644 include/hw/misc/allwinner-a10-ccm.h
>  create mode 100644 include/hw/misc/allwinner-a10-dramc.h
>
> --
> 2.30.2
>
>

-- 
Niek Linnenbank

[-- Attachment #2: Type: text/html, Size: 5003 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 3/6] hw/i2c: Allwinner TWI/I2C Emulation
  2022-12-03 23:19 ` [PATCH 3/6] hw/i2c: Allwinner TWI/I2C Emulation Strahinja Jankovic
@ 2022-12-07 22:06   ` Niek Linnenbank
  2022-12-08 19:19     ` Strahinja Jankovic
  0 siblings, 1 reply; 18+ messages in thread
From: Niek Linnenbank @ 2022-12-07 22:06 UTC (permalink / raw)
  To: Strahinja Jankovic
  Cc: Peter Maydell, Beniamino Galvani, qemu-arm, qemu-devel,
	Strahinja Jankovic

[-- Attachment #1: Type: text/plain, Size: 27180 bytes --]

Hi Strahinja,

On Sun, Dec 4, 2022 at 12:19 AM Strahinja Jankovic <
strahinjapjankovic@gmail.com> wrote:

> This patch implements Allwinner TWI/I2C controller emulation. Only
> master-mode functionality is implemented.
>
> The SPL boot for Cubieboard expects AXP209 PMIC on TWI0/I2C0 bus, so this
> is
> first part enabling the TWI/I2C bus operation.
>
> Since both Allwinner A10 and H3 use the same module, it is added for
> both boards.
>

The A10 and H3 datasheets have the same introduction text on the TWI,
suggesting re-use indeed. Unfortunately
the A10 datasheet seems to be missing register documentation, so I can't
compare that with the H3 datasheet.

At least according to what is implemented in the linux kernel, looks like
that indeed both SoCs implement the same I2C module.
The file drivers/i2c/busses/i2c-mv64xxx.c has the following
mv64xxx_i2c_of_match_table:
        { .compatible = "allwinner,sun4i-a10-i2c", .data =
&mv64xxx_i2c_regs_sun4i},
        { .compatible = "allwinner,sun6i-a31-i2c", .data =
&mv64xxx_i2c_regs_sun4i},

And both SoCs define the sun4i-a10-i2c and sun6i-a31-i2c in their device
tree files, respectively.

Could you please also update the documentation files for both boards, so we
can show that they now support TWI/I2C?
  docs/system/arm/cubieboard.rst
  docs/system/arm/orangepi.rst


>
> Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> ---
>  hw/arm/Kconfig                 |   2 +
>  hw/arm/allwinner-a10.c         |   8 +
>  hw/arm/allwinner-h3.c          |  11 +-
>  hw/i2c/Kconfig                 |   4 +
>  hw/i2c/allwinner-i2c.c         | 417 +++++++++++++++++++++++++++++++++
>  hw/i2c/meson.build             |   1 +
>  include/hw/arm/allwinner-a10.h |   2 +
>  include/hw/arm/allwinner-h3.h  |   3 +
>  include/hw/i2c/allwinner-i2c.h | 112 +++++++++
>  9 files changed, 559 insertions(+), 1 deletion(-)
>  create mode 100644 hw/i2c/allwinner-i2c.c
>  create mode 100644 include/hw/i2c/allwinner-i2c.h
>
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 140f142ae5..eefe1fd134 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -322,6 +322,7 @@ config ALLWINNER_A10
>      select ALLWINNER_A10_CCM
>      select ALLWINNER_A10_DRAMC
>      select ALLWINNER_EMAC
> +    select ALLWINNER_I2C
>      select SERIAL
>      select UNIMP
>
> @@ -329,6 +330,7 @@ config ALLWINNER_H3
>      bool
>      select ALLWINNER_A10_PIT
>      select ALLWINNER_SUN8I_EMAC
> +    select ALLWINNER_I2C
>      select SERIAL
>      select ARM_TIMER
>      select ARM_GIC
> diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
> index a5f7a36ac9..17e439777e 100644
> --- a/hw/arm/allwinner-a10.c
> +++ b/hw/arm/allwinner-a10.c
> @@ -36,6 +36,7 @@
>  #define AW_A10_OHCI_BASE        0x01c14400
>  #define AW_A10_SATA_BASE        0x01c18000
>  #define AW_A10_RTC_BASE         0x01c20d00
> +#define AW_A10_I2C0_BASE        0x01c2ac00
>
>  static void aw_a10_init(Object *obj)
>  {
> @@ -56,6 +57,8 @@ static void aw_a10_init(Object *obj)
>
>      object_initialize_child(obj, "sata", &s->sata, TYPE_ALLWINNER_AHCI);
>
> +    object_initialize_child(obj, "i2c0", &s->i2c0, TYPE_AW_I2C);
> +
>      if (machine_usb(current_machine)) {
>          int i;
>
> @@ -176,6 +179,11 @@ static void aw_a10_realize(DeviceState *dev, Error
> **errp)
>      /* RTC */
>      sysbus_realize(SYS_BUS_DEVICE(&s->rtc), &error_fatal);
>      sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->rtc), 0, AW_A10_RTC_BASE,
> 10);
> +
> +    /* I2C */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, AW_A10_I2C0_BASE);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0, qdev_get_gpio_in(dev,
> 7));
>  }
>
>  static void aw_a10_class_init(ObjectClass *oc, void *data)
> diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
> index 308ed15552..bfce3c8d92 100644
> --- a/hw/arm/allwinner-h3.c
> +++ b/hw/arm/allwinner-h3.c
> @@ -53,6 +53,7 @@ const hwaddr allwinner_h3_memmap[] = {
>      [AW_H3_DEV_UART1]      = 0x01c28400,
>      [AW_H3_DEV_UART2]      = 0x01c28800,
>      [AW_H3_DEV_UART3]      = 0x01c28c00,
> +    [AW_H3_DEV_TWI0]       = 0x01c2ac00,
>      [AW_H3_DEV_EMAC]       = 0x01c30000,
>      [AW_H3_DEV_DRAMCOM]    = 0x01c62000,
>      [AW_H3_DEV_DRAMCTL]    = 0x01c63000,
> @@ -106,7 +107,6 @@ struct AwH3Unimplemented {
>      { "uart1",     0x01c28400, 1 * KiB },
>      { "uart2",     0x01c28800, 1 * KiB },
>      { "uart3",     0x01c28c00, 1 * KiB },
> -    { "twi0",      0x01c2ac00, 1 * KiB },
>      { "twi1",      0x01c2b000, 1 * KiB },
>      { "twi2",      0x01c2b400, 1 * KiB },
>      { "scr",       0x01c2c400, 1 * KiB },
> @@ -150,6 +150,7 @@ enum {
>      AW_H3_GIC_SPI_UART1     =  1,
>      AW_H3_GIC_SPI_UART2     =  2,
>      AW_H3_GIC_SPI_UART3     =  3,
> +    AW_H3_GIC_SPI_TWI0      =  6,
>      AW_H3_GIC_SPI_TIMER0    = 18,
>      AW_H3_GIC_SPI_TIMER1    = 19,
>      AW_H3_GIC_SPI_MMC0      = 60,
> @@ -225,6 +226,8 @@ static void allwinner_h3_init(Object *obj)
>                                "ram-size");
>
>      object_initialize_child(obj, "rtc", &s->rtc, TYPE_AW_RTC_SUN6I);
> +
> +    object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C);
>  }
>
>  static void allwinner_h3_realize(DeviceState *dev, Error **errp)
> @@ -423,6 +426,12 @@ static void allwinner_h3_realize(DeviceState *dev,
> Error **errp)
>      sysbus_realize(SYS_BUS_DEVICE(&s->rtc), &error_fatal);
>      sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, s->memmap[AW_H3_DEV_RTC]);
>
> +    /* I2C */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0,
> s->memmap[AW_H3_DEV_TWI0]);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->gic),
> AW_H3_GIC_SPI_TWI0));
> +
>      /* Unimplemented devices */
>      for (i = 0; i < ARRAY_SIZE(unimplemented); i++) {
>          create_unimplemented_device(unimplemented[i].device_name,
> diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig
> index 9bb8870517..f8ec461be3 100644
> --- a/hw/i2c/Kconfig
> +++ b/hw/i2c/Kconfig
> @@ -34,6 +34,10 @@ config MPC_I2C
>      bool
>      select I2C
>
> +config ALLWINNER_I2C
> +    bool
> +    select I2C
> +
>  config PCA954X
>      bool
>      select I2C
> diff --git a/hw/i2c/allwinner-i2c.c b/hw/i2c/allwinner-i2c.c
> new file mode 100644
> index 0000000000..4c9080a18b
> --- /dev/null
> +++ b/hw/i2c/allwinner-i2c.c
> @@ -0,0 +1,417 @@
> +/*
> + *  Allwinner I2C Bus Serial Interface Emulation
> + *
> + *  Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com
> >
> + *
> + *  This file is derived from IMX I2C controller,
> + *  by Jean-Christophe DUBOIS .
> + *
> + *  This program is free software; you can redistribute it and/or modify
> it
> + *  under the terms of the GNU General Public License as published by the
> + *  Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful, but
> WITHOUT
> + *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + *  for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> along
> + *  with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/i2c/allwinner-i2c.h"
> +#include "hw/irq.h"
> +#include "migration/vmstate.h"
> +#include "hw/i2c/i2c.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +
> +#ifndef DEBUG_AW_I2C
> +#define DEBUG_AW_I2C 0
> +#endif
> +
> +#define DPRINTF(fmt, args...) \
> +    do { \
> +        if (DEBUG_AW_I2C) { \
> +            fprintf(stderr, "[%s]%s: " fmt , TYPE_AW_I2C, \
> +                                             __func__, ##args); \
> +        } \
> +    } while (0)
>
As Philipe already suggested earlier, this could be replaced with tracing.
Maybe have a look at some of the other I2C drivers to see how its done, for
example aspeed_i2c.c.


> +
> +/* Status value in STAT register is shifted by 3 bits */
> +#define TWI_STAT_SHIFT      3
> +#define STAT_FROM_STA(x)    ((x) << TWI_STAT_SHIFT)
> +#define STAT_TO_STA(x)      ((x) >> TWI_STAT_SHIFT)
>
Maybe add a newline here for readability.


> +enum {
> +    STAT_BUS_ERROR = 0,
> +    /* Master mode */
> +    STAT_M_STA_TX,
> +    STAT_M_RSTA_TX,
> +    STAT_M_ADDR_WR_ACK,
> +    STAT_M_ADDR_WR_NACK,
> +    STAT_M_DATA_TX_ACK,
> +    STAT_M_DATA_TX_NACK,
> +    STAT_M_ARB_LOST,
> +    STAT_M_ADDR_RD_ACK,
> +    STAT_M_ADDR_RD_NACK,
> +    STAT_M_DATA_RX_ACK,
> +    STAT_M_DATA_RX_NACK,
> +    /* Slave mode */
> +    STAT_S_ADDR_WR_ACK,
> +    STAT_S_ARB_LOST_AW_ACK,
> +    STAT_S_GCA_ACK,
> +    STAT_S_ARB_LOST_GCA_ACK,
> +    STAT_S_DATA_RX_SA_ACK,
> +    STAT_S_DATA_RX_SA_NACK,
> +    STAT_S_DATA_RX_GCA_ACK,
> +    STAT_S_DATA_RX_GCA_NACK,
> +    STAT_S_STP_RSTA,
> +    STAT_S_ADDR_RD_ACK,
> +    STAT_S_ARB_LOST_AR_ACK,
> +    STAT_S_DATA_TX_ACK,
> +    STAT_S_DATA_TX_NACK,
> +    STAT_S_LB_TX_ACK,
> +    /* Master mode, 10-bit */
> +    STAT_M_2ND_ADDR_WR_ACK,
> +    STAT_M_2ND_ADDR_WR_NACK,
> +    /* Idle */
> +    STAT_IDLE = 0x1f
> +} TWI_STAT_STA;
> +
> +static const char *allwinner_i2c_get_regname(unsigned offset)
> +{
> +    switch (offset) {
> +    case TWI_ADDR_REG:
> +        return "ADDR";
> +    case TWI_XADDR_REG:
> +        return "XADDR";
> +    case TWI_DATA_REG:
> +        return "DATA";
> +    case TWI_CNTR_REG:
> +        return "CNTR";
> +    case TWI_STAT_REG:
> +        return "STAT";
> +    case TWI_CCR_REG:
> +        return "CCR";
> +    case TWI_SRST_REG:
> +        return "SRST";
> +    case TWI_EFR_REG:
> +        return "EFR";
> +    case TWI_LCR_REG:
> +        return "LCR";
> +    default:
> +        return "[?]";
> +    }
> +}
> +
> +static inline bool allwinner_i2c_is_reset(AWI2CState *s)
> +{
> +    return s->srst & TWI_SRST_MASK;
> +}
> +
> +static inline bool allwinner_i2c_bus_is_enabled(AWI2CState *s)
> +{
> +    return s->cntr & TWI_CNTR_BUS_EN;
> +}
> +
> +static inline bool allwinner_i2c_interrupt_is_enabled(AWI2CState *s)
> +{
> +    return s->cntr & TWI_CNTR_INT_EN;
> +}
> +
> +static void allwinner_i2c_reset_hold(Object *obj)
> +{
> +    AWI2CState *s = AW_I2C(obj);
> +
> +    if (STAT_TO_STA(s->stat) != STAT_IDLE) {
> +        i2c_end_transfer(s->bus);
> +    }
> +
> +    s->addr  = TWI_ADDR_RESET;
> +    s->xaddr = TWI_XADDR_RESET;
> +    s->data  = TWI_DATA_RESET;
> +    s->cntr  = TWI_CNTR_RESET;
> +    s->stat  = TWI_STAT_RESET;
> +    s->ccr   = TWI_CCR_RESET;
> +    s->srst  = TWI_SRST_RESET;
> +    s->efr   = TWI_EFR_RESET;
> +    s->lcr   = TWI_LCR_RESET;
> +}
> +
> +static inline void allwinner_i2c_raise_interrupt(AWI2CState *s)
> +{
> +    /*
> +     * Raise an interrupt if the device is not reset and it is configured
> +     * to generate some interrupts.
> +     */
> +    if (!allwinner_i2c_is_reset(s) && allwinner_i2c_bus_is_enabled(s)) {
> +        if (STAT_TO_STA(s->stat) != STAT_IDLE) {
> +            s->cntr |= TWI_CNTR_INT_FLAG;
> +            if (allwinner_i2c_interrupt_is_enabled(s)) {
> +                qemu_irq_raise(s->irq);
> +            }
> +        }
> +    }
> +}
> +
> +static uint64_t allwinner_i2c_read(void *opaque, hwaddr offset,
> +                                   unsigned size)
> +{
> +    uint16_t value;
> +    AWI2CState *s = AW_I2C(opaque);
> +
> +    switch (offset) {
> +    case TWI_ADDR_REG:
> +        value = s->addr;
> +        break;
> +    case TWI_XADDR_REG:
> +        value = s->xaddr;
> +        break;
> +    case TWI_DATA_REG:
> +        switch (STAT_TO_STA(s->stat)) {
> +        case STAT_M_ADDR_RD_ACK:
> +        case STAT_M_DATA_RX_ACK:
> +        case STAT_M_DATA_RX_NACK:
> +            /* get the next byte */
> +            s->data = i2c_recv(s->bus);
> +
> +            if (s->cntr & TWI_CNTR_A_ACK) {
> +                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
> +            } else {
> +                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
> +            }
> +            allwinner_i2c_raise_interrupt(s);
> +            break;
>

Looking at this code, here we have a nested switch statement. I just wonder
if it's well defined C programming behavior what really should happen
when calling 'break' in such construction. Should it step out of the first
switch or both? Perhaps it works OK with gcc, but what about other
compilers.
Also for readability, it may be better to avoid nesting switch statements
at all in my opinion.


> +        default:
> +            break;
> +        }
> +        value = s->data;
> +        break;
> +    case TWI_CNTR_REG:
> +        value = s->cntr;
> +        break;
> +    case TWI_STAT_REG:
> +        value = s->stat;
> +        /*
> +         * If polling when reading then change state to indicate data
> +         * is available
> +         */
> +        if (STAT_TO_STA(s->stat) == STAT_M_ADDR_RD_ACK) {
> +            if (s->cntr & TWI_CNTR_A_ACK) {
> +                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
> +            } else {
> +                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
> +            }
> +            allwinner_i2c_raise_interrupt(s);
> +        }
> +        break;
> +    case TWI_CCR_REG:
> +        value = s->ccr;
> +        break;
> +    case TWI_SRST_REG:
> +        value = s->srst;
> +        break;
> +    case TWI_EFR_REG:
> +        value = s->efr;
> +        break;
> +    case TWI_LCR_REG:
> +        value = s->lcr;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
> +                      HWADDR_PRIx "\n", TYPE_AW_I2C, __func__, offset);
> +        value = 0;
> +        break;
> +    }
> +
> +    DPRINTF("read %s [0x%" HWADDR_PRIx "] -> 0x%02x\n",
> +            allwinner_i2c_get_regname(offset), offset, value);
> +
> +    return (uint64_t)value;
> +}
> +
> +static void allwinner_i2c_write(void *opaque, hwaddr offset,
> +                                uint64_t value, unsigned size)
> +{
> +    AWI2CState *s = AW_I2C(opaque);
> +
> +    DPRINTF("write %s [0x%" HWADDR_PRIx "] <- 0x%02x\n",
> +            allwinner_i2c_get_regname(offset), offset, (int)value);
> +
> +    value &= 0xff;
> +
> +    switch (offset) {
> +    case TWI_ADDR_REG:
> +        s->addr = (uint8_t)value;
> +        break;
> +    case TWI_XADDR_REG:
> +        s->xaddr = (uint8_t)value;
> +        break;
> +    case TWI_DATA_REG:
> +        /* If the device is in reset or not enabled, nothing to do */
> +        if (allwinner_i2c_is_reset(s) ||
> (!allwinner_i2c_bus_is_enabled(s))) {
> +            break;
> +        }
> +
> +        s->data = value & TWI_DATA_MASK;
> +
> +        switch (STAT_TO_STA(s->stat)) {
> +        case STAT_M_STA_TX:
> +        case STAT_M_RSTA_TX:
> +            /* Send address */
> +            if (i2c_start_transfer(s->bus, extract32(s->data, 1, 7),
> +                                extract32(s->data, 0, 1))) {
> +                /* If non zero is returned, the address is not valid */
> +                s->stat = STAT_FROM_STA(STAT_M_ADDR_WR_NACK);
> +            } else {
> +                /* Determine if read of write */
> +                if (extract32(s->data, 0, 1)) {
> +                    s->stat = STAT_FROM_STA(STAT_M_ADDR_RD_ACK);
> +                } else {
> +                    s->stat = STAT_FROM_STA(STAT_M_ADDR_WR_ACK);
> +                }
> +                allwinner_i2c_raise_interrupt(s);
> +            }
> +            break;
> +        case STAT_M_ADDR_WR_ACK:
> +        case STAT_M_DATA_TX_ACK:
> +            if (i2c_send(s->bus, s->data)) {
> +                /* If the target return non zero then end the transfer */
> +                s->stat = STAT_FROM_STA(STAT_M_DATA_TX_NACK);
> +                i2c_end_transfer(s->bus);
> +            } else {
> +                s->stat = STAT_FROM_STA(STAT_M_DATA_TX_ACK);
> +                allwinner_i2c_raise_interrupt(s);
> +            }
> +            break;
> +        default:
> +            break;
> +        }
> +        break;
> +    case TWI_CNTR_REG:
> +        if (!allwinner_i2c_is_reset(s)) {
> +            /* Do something only if not in software reset */
> +            s->cntr = value & TWI_CNTR_MASK;
> +
> +            /* Check if start condition should be sent */
> +            if (s->cntr & TWI_CNTR_M_STA) {
> +                /* Update status */
> +                if (STAT_TO_STA(s->stat) == STAT_IDLE) {
> +                    /* Send start condition */
> +                    s->stat = STAT_FROM_STA(STAT_M_STA_TX);
> +                } else {
> +                    /* Send repeated start condition */
> +                    s->stat = STAT_FROM_STA(STAT_M_RSTA_TX);
> +                }
> +                /* Clear start condition */
> +                s->cntr &= ~TWI_CNTR_M_STA;
> +            }
> +            if (s->cntr & TWI_CNTR_M_STP) {
> +                /* Update status */
> +                i2c_end_transfer(s->bus);
> +                s->stat = STAT_FROM_STA(STAT_IDLE);
> +                s->cntr &= ~TWI_CNTR_M_STP;
> +            }
> +            if ((s->cntr & TWI_CNTR_INT_FLAG) == 0) {
> +                /* Interrupt flag cleared */
> +                qemu_irq_lower(s->irq);
> +            }
> +            if ((s->cntr & TWI_CNTR_A_ACK) == 0) {
> +                if (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_ACK) {
> +                    s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
> +                }
> +            } else {
> +                if (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_NACK) {
> +                    s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
> +                }
> +            }
> +            allwinner_i2c_raise_interrupt(s);
> +
> +        }
> +        break;
> +    case TWI_CCR_REG:
> +        s->ccr = value & TWI_CCR_MASK;
> +        break;
> +    case TWI_SRST_REG:
> +        if (((value & TWI_SRST_MASK) == 0) && (s->srst & TWI_SRST_MASK)) {
> +            /* Perform reset */
> +            allwinner_i2c_reset_hold(OBJECT(s));
> +        }
> +        s->srst = value & TWI_SRST_MASK;
> +        break;
> +    case TWI_EFR_REG:
> +        s->efr = value & TWI_EFR_MASK;
> +        break;
> +    case TWI_LCR_REG:
> +        s->lcr = value & TWI_LCR_MASK;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
> +                      HWADDR_PRIx "\n", TYPE_AW_I2C, __func__, offset);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps allwinner_i2c_ops = {
> +    .read = allwinner_i2c_read,
> +    .write = allwinner_i2c_write,
> +    .valid.min_access_size = 1,
> +    .valid.max_access_size = 4,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static const VMStateDescription allwinner_i2c_vmstate = {
> +    .name = TYPE_AW_I2C,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT8(addr, AWI2CState),
> +        VMSTATE_UINT8(xaddr, AWI2CState),
> +        VMSTATE_UINT8(data, AWI2CState),
> +        VMSTATE_UINT8(cntr, AWI2CState),
> +        VMSTATE_UINT8(ccr, AWI2CState),
> +        VMSTATE_UINT8(srst, AWI2CState),
> +        VMSTATE_UINT8(efr, AWI2CState),
> +        VMSTATE_UINT8(lcr, AWI2CState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void allwinner_i2c_realize(DeviceState *dev, Error **errp)
> +{
> +    AWI2CState *s = AW_I2C(dev);
> +
> +    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_i2c_ops, s,
> +                          TYPE_AW_I2C, AW_I2C_MEM_SIZE);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
> +    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
> +    s->bus = i2c_init_bus(dev, "i2c");
> +}
> +
> +static void allwinner_i2c_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> +
> +    rc->phases.hold = allwinner_i2c_reset_hold;
> +    dc->vmsd = &allwinner_i2c_vmstate;
> +    dc->realize = allwinner_i2c_realize;
> +    dc->desc = "Allwinner I2C Controller";
> +}
> +
> +static const TypeInfo allwinner_i2c_type_info = {
> +    .name = TYPE_AW_I2C,
> +    .parent = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(AWI2CState),
> +    .class_init = allwinner_i2c_class_init,
> +};
> +
> +static void allwinner_i2c_register_types(void)
> +{
> +    type_register_static(&allwinner_i2c_type_info);
> +}
> +
> +type_init(allwinner_i2c_register_types)
> diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build
> index d3df273251..7de7f2f540 100644
> --- a/hw/i2c/meson.build
> +++ b/hw/i2c/meson.build
> @@ -8,6 +8,7 @@ i2c_ss.add(when: 'CONFIG_BITBANG_I2C', if_true:
> files('bitbang_i2c.c'))
>  i2c_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_i2c.c'))
>  i2c_ss.add(when: 'CONFIG_IMX_I2C', if_true: files('imx_i2c.c'))
>  i2c_ss.add(when: 'CONFIG_MPC_I2C', if_true: files('mpc_i2c.c'))
> +i2c_ss.add(when: 'CONFIG_ALLWINNER_I2C', if_true:
> files('allwinner-i2c.c'))
>  i2c_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('microbit_i2c.c'))
>  i2c_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_smbus.c'))
>  i2c_ss.add(when: 'CONFIG_SMBUS_EEPROM', if_true: files('smbus_eeprom.c'))
> diff --git a/include/hw/arm/allwinner-a10.h
> b/include/hw/arm/allwinner-a10.h
> index abe4ff7066..763935fca9 100644
> --- a/include/hw/arm/allwinner-a10.h
> +++ b/include/hw/arm/allwinner-a10.h
> @@ -14,6 +14,7 @@
>  #include "hw/rtc/allwinner-rtc.h"
>  #include "hw/misc/allwinner-a10-ccm.h"
>  #include "hw/misc/allwinner-a10-dramc.h"
> +#include "hw/i2c/allwinner-i2c.h"
>
>  #include "target/arm/cpu.h"
>  #include "qom/object.h"
> @@ -39,6 +40,7 @@ struct AwA10State {
>      AwEmacState emac;
>      AllwinnerAHCIState sata;
>      AwSdHostState mmc0;
> +    AWI2CState i2c0;
>      AwRtcState rtc;
>      MemoryRegion sram_a;
>      EHCISysBusState ehci[AW_A10_NUM_USB];
> diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
> index 63025fb27c..1d7ce20589 100644
> --- a/include/hw/arm/allwinner-h3.h
> +++ b/include/hw/arm/allwinner-h3.h
> @@ -47,6 +47,7 @@
>  #include "hw/sd/allwinner-sdhost.h"
>  #include "hw/net/allwinner-sun8i-emac.h"
>  #include "hw/rtc/allwinner-rtc.h"
> +#include "hw/i2c/allwinner-i2c.h"
>  #include "target/arm/cpu.h"
>  #include "sysemu/block-backend.h"
>
> @@ -82,6 +83,7 @@ enum {
>      AW_H3_DEV_UART2,
>      AW_H3_DEV_UART3,
>      AW_H3_DEV_EMAC,
> +    AW_H3_DEV_TWI0,
>      AW_H3_DEV_DRAMCOM,
>      AW_H3_DEV_DRAMCTL,
>      AW_H3_DEV_DRAMPHY,
> @@ -130,6 +132,7 @@ struct AwH3State {
>      AwH3SysCtrlState sysctrl;
>      AwSidState sid;
>      AwSdHostState mmc0;
> +    AWI2CState i2c0;
>      AwSun8iEmacState emac;
>      AwRtcState rtc;
>      GICState gic;
> diff --git a/include/hw/i2c/allwinner-i2c.h
> b/include/hw/i2c/allwinner-i2c.h
> new file mode 100644
> index 0000000000..1e3169a5b9
> --- /dev/null
> +++ b/include/hw/i2c/allwinner-i2c.h
> @@ -0,0 +1,112 @@
> +/*
> + *  Allwinner I2C Bus Serial Interface registers definition
> + *
> + *  Copyright (C) 2022 Strahinja Jankovic. <
> strahinja.p.jankovic@gmail.com>
> + *
> + *  This file is derived from IMX I2C controller,
> + *  by Jean-Christophe DUBOIS .
> + *
> + *  This program is free software; you can redistribute it and/or modify
> it
> + *  under the terms of the GNU General Public License as published by the
> + *  Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful, but
> WITHOUT
> + *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + *  for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> along
> + *  with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#ifndef ALLWINNER_I2C_H
> +#define ALLWINNER_I2C_H
> +
> +#include "hw/sysbus.h"
> +#include "qom/object.h"
> +
> +#define TYPE_AW_I2C "allwinner.i2c"
> +OBJECT_DECLARE_SIMPLE_TYPE(AWI2CState, AW_I2C)
> +
> +#define AW_I2C_MEM_SIZE         0x24
> +
> +/* Allwinner I2C memory map */
> +#define TWI_ADDR_REG            0x00  /* slave address register */
> +#define TWI_XADDR_REG           0x04  /* extended slave address register
> */
> +#define TWI_DATA_REG            0x08  /* data register */
> +#define TWI_CNTR_REG            0x0c  /* control register */
> +#define TWI_STAT_REG            0x10  /* status register */
> +#define TWI_CCR_REG             0x14  /* clock control register */
> +#define TWI_SRST_REG            0x18  /* software reset register */
> +#define TWI_EFR_REG             0x1c  /* enhance feature register */
> +#define TWI_LCR_REG             0x20  /* line control register */
> +
>
If no other modules except for the new driver need these defines, it may be
a good idea to move them to the .c file instead.
Doing so helps avoiding accidental or unintentional use of the defines.
Same for the other flags below.


> +/* Used only in slave mode, do not set */
> +#define TWI_ADDR_RESET          0
> +#define TWI_XADDR_RESET         0
> +
> +/* Data register */
> +#define TWI_DATA_MASK           0xFF
> +#define TWI_DATA_RESET          0
> +
> +/* Control register */
> +#define TWI_CNTR_INT_EN         (1 << 7)
> +#define TWI_CNTR_BUS_EN         (1 << 6)
> +#define TWI_CNTR_M_STA          (1 << 5)
> +#define TWI_CNTR_M_STP          (1 << 4)
> +#define TWI_CNTR_INT_FLAG       (1 << 3)
> +#define TWI_CNTR_A_ACK          (1 << 2)
> +#define TWI_CNTR_MASK           0xFC
> +#define TWI_CNTR_RESET          0
> +
> +/* Status register */
> +#define TWI_STAT_MASK           0xF8
> +#define TWI_STAT_RESET          0xF8
> +
> +/* Clock register */
> +#define TWI_CCR_CLK_M_MASK      0x78
> +#define TWI_CCR_CLK_N_MASK      0x07
> +#define TWI_CCR_MASK            0x7F
> +#define TWI_CCR_RESET           0
> +
> +/* Soft reset */
> +#define TWI_SRST_MASK           0x01
> +#define TWI_SRST_RESET          0
> +
> +/* Enhance feature */
> +#define TWI_EFR_MASK            0x03
> +#define TWI_EFR_RESET           0
> +
> +/* Line control */
> +#define TWI_LCR_SCL_STATE       (1 << 5)
> +#define TWI_LCR_SDA_STATE       (1 << 4)
> +#define TWI_LCR_SCL_CTL         (1 << 3)
> +#define TWI_LCR_SCL_CTL_EN      (1 << 2)
> +#define TWI_LCR_SDA_CTL         (1 << 1)
> +#define TWI_LCR_SDA_CTL_EN      (1 << 0)
> +#define TWI_LCR_MASK            0x3F
> +#define TWI_LCR_RESET           0x3A
> +
> +struct AWI2CState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    MemoryRegion iomem;
> +    I2CBus *bus;
> +    qemu_irq irq;
> +
> +    uint8_t addr;
> +    uint8_t xaddr;
> +    uint8_t data;
> +    uint8_t cntr;
> +    uint8_t stat;
> +    uint8_t ccr;
> +    uint8_t srst;
> +    uint8_t efr;
> +    uint8_t lcr;
> +};
> +
> +#endif /* ALLWINNER_I2C_H */
> --
> 2.30.2
>
>
Regards,
Niek

-- 
Niek Linnenbank

[-- Attachment #2: Type: text/html, Size: 34592 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/6] hw/misc: Allwinner A10 DRAM Controller Emulation
  2022-12-03 23:19 ` [PATCH 2/6] hw/misc: Allwinner A10 DRAM Controller Emulation Strahinja Jankovic
@ 2022-12-07 22:26   ` Niek Linnenbank
  0 siblings, 0 replies; 18+ messages in thread
From: Niek Linnenbank @ 2022-12-07 22:26 UTC (permalink / raw)
  To: Strahinja Jankovic
  Cc: Peter Maydell, Beniamino Galvani, qemu-arm, qemu-devel,
	Strahinja Jankovic

[-- Attachment #1: Type: text/plain, Size: 13254 bytes --]

Hi Strahinja,

On Sun, Dec 4, 2022 at 12:19 AM Strahinja Jankovic <
strahinjapjankovic@gmail.com> wrote:

> During SPL boot several DRAM Controller registers are used. Most
> important registers are those related to DRAM initialization and
> calibration, where SPL initiates process and waits until certain bit is
> set/cleared.
>
> This patch adds these registers, initializes reset values from user's
> guide and updates state of registers as SPL expects it.
>
> Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> ---
>  hw/arm/Kconfig                        |   1 +
>  hw/arm/allwinner-a10.c                |   7 +
>  hw/misc/Kconfig                       |   3 +
>  hw/misc/allwinner-a10-dramc.c         | 179 ++++++++++++++++++++++++++
>  hw/misc/meson.build                   |   1 +
>  include/hw/arm/allwinner-a10.h        |   2 +
>  include/hw/misc/allwinner-a10-dramc.h |  68 ++++++++++
>  7 files changed, 261 insertions(+)
>  create mode 100644 hw/misc/allwinner-a10-dramc.c
>  create mode 100644 include/hw/misc/allwinner-a10-dramc.h
>
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 14f52b41af..140f142ae5 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -320,6 +320,7 @@ config ALLWINNER_A10
>      select ALLWINNER_A10_PIT
>      select ALLWINNER_A10_PIC
>      select ALLWINNER_A10_CCM
> +    select ALLWINNER_A10_DRAMC
>      select ALLWINNER_EMAC
>      select SERIAL
>      select UNIMP
> diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
> index 86baeeeca2..a5f7a36ac9 100644
> --- a/hw/arm/allwinner-a10.c
> +++ b/hw/arm/allwinner-a10.c
> @@ -25,6 +25,7 @@
>  #include "hw/boards.h"
>  #include "hw/usb/hcd-ohci.h"
>
> +#define AW_A10_DRAMC_BASE       0x01c01000
>  #define AW_A10_MMC0_BASE        0x01c0f000
>  #define AW_A10_CCM_BASE         0x01c20000
>  #define AW_A10_PIC_REG_BASE     0x01c20400
> @@ -49,6 +50,8 @@ static void aw_a10_init(Object *obj)
>
>      object_initialize_child(obj, "ccm", &s->ccm, TYPE_AW_A10_CCM);
>
> +    object_initialize_child(obj, "dramc", &s->dramc, TYPE_AW_A10_DRAMC);
> +
>      object_initialize_child(obj, "emac", &s->emac, TYPE_AW_EMAC);
>
>      object_initialize_child(obj, "sata", &s->sata, TYPE_ALLWINNER_AHCI);
> @@ -110,6 +113,10 @@ static void aw_a10_realize(DeviceState *dev, Error
> **errp)
>      sysbus_realize(SYS_BUS_DEVICE(&s->ccm), &error_fatal);
>      sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, AW_A10_CCM_BASE);
>
> +    /* DRAM Control Module */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->dramc), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, AW_A10_DRAMC_BASE);
> +
>      /* FIXME use qdev NIC properties instead of nd_table[] */
>      if (nd_table[0].used) {
>          qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC);
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index ed07bf4133..052fb54310 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -177,4 +177,7 @@ config LASI
>  config ALLWINNER_A10_CCM
>      bool
>
> +config ALLWINNER_A10_DRAMC
> +    bool
> +
>  source macio/Kconfig
> diff --git a/hw/misc/allwinner-a10-dramc.c b/hw/misc/allwinner-a10-dramc.c
> new file mode 100644
> index 0000000000..e118b0c2fd
> --- /dev/null
> +++ b/hw/misc/allwinner-a10-dramc.c
> @@ -0,0 +1,179 @@
> +/*
> + * Allwinner A10 DRAM Controller emulation
> + *
> + * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> + *
> + *  This file is derived from Allwinner H3 DRAMC,
> + *  by Niek Linnenbank.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/units.h"
> +#include "hw/sysbus.h"
> +#include "migration/vmstate.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "hw/misc/allwinner-a10-dramc.h"
> +
> +/* DRAMC register offsets */
> +enum {
> +    REG_SDR_CCR = 0x0000,
> +    REG_SDR_ZQCR0 = 0x00a8,
> +    REG_SDR_ZQSR = 0x00b0
> +};
>

This matches indeed with what is currently implemented in u-boot in the
file arch/arm/include/asm/arch-sunxi/dram_sun4i.h.
And since the datasheets don't document the DRAM controllers, probably the
best we can do is match with u-boot.

Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>

Regards,
Niek


> +
> +#define REG_INDEX(offset)    (offset / sizeof(uint32_t))
> +
> +/* DRAMC register flags */
> +enum {
> +    REG_SDR_CCR_DATA_TRAINING = (1 << 30),
> +    REG_SDR_CCR_DRAM_INIT     = (1 << 31),
> +};
> +enum {
> +    REG_SDR_ZQSR_ZCAL         = (1 << 31),
> +};
> +
> +/* DRAMC register reset values */
> +enum {
> +    REG_SDR_CCR_RESET   = 0x80020000,
> +    REG_SDR_ZQCR0_RESET = 0x07b00000,
> +    REG_SDR_ZQSR_RESET  = 0x80000000
> +};
> +
> +static uint64_t allwinner_a10_dramc_read(void *opaque, hwaddr offset,
> +                                       unsigned size)
> +{
> +    const AwA10DramControllerState *s = AW_A10_DRAMC(opaque);
> +    const uint32_t idx = REG_INDEX(offset);
> +
> +    switch (offset) {
> +    case REG_SDR_CCR:
> +    case REG_SDR_ZQCR0:
> +    case REG_SDR_ZQSR:
> +        break;
> +    case 0x2e4 ... AW_A10_DRAMC_IOSIZE:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset
> 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        return 0;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "%s: unimplemented read offset 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        return 0;
> +    }
> +
> +    return s->regs[idx];
> +}
> +
> +static void allwinner_a10_dramc_write(void *opaque, hwaddr offset,
> +                                   uint64_t val, unsigned size)
> +{
> +    AwA10DramControllerState *s = AW_A10_DRAMC(opaque);
> +    const uint32_t idx = REG_INDEX(offset);
> +
> +    switch (offset) {
> +    case REG_SDR_CCR:
> +        if (val & REG_SDR_CCR_DRAM_INIT) {
> +            /* Clear DRAM_INIT to indicate process is done. */
> +            val &= ~REG_SDR_CCR_DRAM_INIT;
> +        }
> +        if (val & REG_SDR_CCR_DATA_TRAINING) {
> +            /* Clear DATA_TRAINING to indicate process is done. */
> +            val &= ~REG_SDR_CCR_DATA_TRAINING;
> +        }
> +        break;
> +    case REG_SDR_ZQCR0:
> +        /* Set ZCAL in ZQSR to indicate calibration is done. */
> +        s->regs[REG_INDEX(REG_SDR_ZQSR)] |= REG_SDR_ZQSR_ZCAL;
> +        break;
> +    case 0x2e4 ... AW_A10_DRAMC_IOSIZE:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset
> 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset
> 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        break;
> +    }
> +
> +    s->regs[idx] = (uint32_t) val;
> +}
> +
> +static const MemoryRegionOps allwinner_a10_dramc_ops = {
> +    .read = allwinner_a10_dramc_read,
> +    .write = allwinner_a10_dramc_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +    .impl.min_access_size = 4,
> +};
> +
> +static void allwinner_a10_dramc_reset_enter(Object *obj, ResetType type)
> +{
> +    AwA10DramControllerState *s = AW_A10_DRAMC(obj);
> +
> +    /* Set default values for registers */
> +    s->regs[REG_INDEX(REG_SDR_CCR)] = REG_SDR_CCR_RESET;
> +    s->regs[REG_INDEX(REG_SDR_ZQCR0)] = REG_SDR_ZQCR0_RESET;
> +    s->regs[REG_INDEX(REG_SDR_ZQSR)] = REG_SDR_ZQSR_RESET;
> +}
> +
> +static void allwinner_a10_dramc_init(Object *obj)
> +{
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +    AwA10DramControllerState *s = AW_A10_DRAMC(obj);
> +
> +    /* Memory mapping */
> +    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_dramc_ops,
> s,
> +                          TYPE_AW_A10_DRAMC, AW_A10_DRAMC_IOSIZE);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +}
> +
> +static const VMStateDescription allwinner_a10_dramc_vmstate = {
> +    .name = "allwinner-a10-dramc",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, AwA10DramControllerState,
> +                             AW_A10_DRAMC_REGS_NUM),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void allwinner_a10_dramc_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> +
> +    rc->phases.enter = allwinner_a10_dramc_reset_enter;
> +    dc->vmsd = &allwinner_a10_dramc_vmstate;
> +}
> +
> +static const TypeInfo allwinner_a10_dramc_info = {
> +    .name          = TYPE_AW_A10_DRAMC,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_init = allwinner_a10_dramc_init,
> +    .instance_size = sizeof(AwA10DramControllerState),
> +    .class_init    = allwinner_a10_dramc_class_init,
> +};
> +
> +static void allwinner_a10_dramc_register(void)
> +{
> +    type_register_static(&allwinner_a10_dramc_info);
> +}
> +
> +type_init(allwinner_a10_dramc_register)
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index ebf216edbc..d7f49f0f81 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -39,6 +39,7 @@ subdir('macio')
>  softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c'))
>
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true:
> files('allwinner-a10-ccm.c'))
> +softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_DRAMC', if_true:
> files('allwinner-a10-dramc.c'))
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true:
> files('allwinner-h3-ccu.c'))
>  specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true:
> files('allwinner-cpucfg.c'))
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true:
> files('allwinner-h3-dramc.c'))
> diff --git a/include/hw/arm/allwinner-a10.h
> b/include/hw/arm/allwinner-a10.h
> index 45d0fc2f7e..abe4ff7066 100644
> --- a/include/hw/arm/allwinner-a10.h
> +++ b/include/hw/arm/allwinner-a10.h
> @@ -13,6 +13,7 @@
>  #include "hw/usb/hcd-ehci.h"
>  #include "hw/rtc/allwinner-rtc.h"
>  #include "hw/misc/allwinner-a10-ccm.h"
> +#include "hw/misc/allwinner-a10-dramc.h"
>
>  #include "target/arm/cpu.h"
>  #include "qom/object.h"
> @@ -32,6 +33,7 @@ struct AwA10State {
>
>      ARMCPU cpu;
>      AwA10ClockCtlState ccm;
> +    AwA10DramControllerState dramc;
>      AwA10PITState timer;
>      AwA10PICState intc;
>      AwEmacState emac;
> diff --git a/include/hw/misc/allwinner-a10-dramc.h
> b/include/hw/misc/allwinner-a10-dramc.h
> new file mode 100644
> index 0000000000..b61fbecbe7
> --- /dev/null
> +++ b/include/hw/misc/allwinner-a10-dramc.h
> @@ -0,0 +1,68 @@
> +/*
> + * Allwinner A10 DRAM Controller emulation
> + *
> + * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> + *
> + *  This file is derived from Allwinner H3 DRAMC,
> + *  by Niek Linnenbank.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef HW_MISC_ALLWINNER_A10_DRAMC_H
> +#define HW_MISC_ALLWINNER_A10_DRAMC_H
> +
> +#include "qom/object.h"
> +#include "hw/sysbus.h"
> +#include "hw/register.h"
> +
> +/**
> + * @name Constants
> + * @{
> + */
> +
> +/** Size of register I/O address space used by DRAMC device */
> +#define AW_A10_DRAMC_IOSIZE        (0x1000)
> +
> +/** Total number of known registers */
> +#define AW_A10_DRAMC_REGS_NUM      (AW_A10_DRAMC_IOSIZE /
> sizeof(uint32_t))
> +
> +/** @} */
> +
> +/**
> + * @name Object model
> + * @{
> + */
> +
> +#define TYPE_AW_A10_DRAMC    "allwinner-a10-dramc"
> +OBJECT_DECLARE_SIMPLE_TYPE(AwA10DramControllerState, AW_A10_DRAMC)
> +
> +/** @} */
> +
> +/**
> + * Allwinner A10 DRAMC object instance state.
> + */
> +struct AwA10DramControllerState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +    /*< public >*/
> +
> +    /** Maps I/O registers in physical memory */
> +    MemoryRegion iomem;
> +
> +    /** Array of hardware registers */
> +    uint32_t regs[AW_A10_DRAMC_REGS_NUM];
> +};
> +
> +#endif /* HW_MISC_ALLWINNER_A10_DRAMC_H */
> --
> 2.30.2
>
>

-- 
Niek Linnenbank

[-- Attachment #2: Type: text/html, Size: 16580 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/6] hw/misc: Allwinner-A10 Clock Controller Module Emulation
  2022-12-03 23:18 ` [PATCH 1/6] hw/misc: Allwinner-A10 Clock Controller Module Emulation Strahinja Jankovic
@ 2022-12-07 22:31   ` Niek Linnenbank
  0 siblings, 0 replies; 18+ messages in thread
From: Niek Linnenbank @ 2022-12-07 22:31 UTC (permalink / raw)
  To: Strahinja Jankovic
  Cc: Peter Maydell, Beniamino Galvani, qemu-arm, qemu-devel,
	Strahinja Jankovic

[-- Attachment #1: Type: text/plain, Size: 14911 bytes --]

Hi Strahinja,

On Sun, Dec 4, 2022 at 12:19 AM Strahinja Jankovic <
strahinjapjankovic@gmail.com> wrote:

> During SPL boot several Clock Controller Module (CCM) registers are
> read, most important are PLL and Tuning, as well as divisor registers.
>
> This patch adds these registers and initializes reset values from user's
> guide.
>
> Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
>

Looks fine to me:

Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>

Regards,
Niek

> ---
>  hw/arm/Kconfig                      |   1 +
>  hw/arm/allwinner-a10.c              |   7 +
>  hw/misc/Kconfig                     |   3 +
>  hw/misc/allwinner-a10-ccm.c         | 224 ++++++++++++++++++++++++++++
>  hw/misc/meson.build                 |   1 +
>  include/hw/arm/allwinner-a10.h      |   2 +
>  include/hw/misc/allwinner-a10-ccm.h |  67 +++++++++
>  7 files changed, 305 insertions(+)
>  create mode 100644 hw/misc/allwinner-a10-ccm.c
>  create mode 100644 include/hw/misc/allwinner-a10-ccm.h
>
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 17fcde8e1c..14f52b41af 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -319,6 +319,7 @@ config ALLWINNER_A10
>      select AHCI
>      select ALLWINNER_A10_PIT
>      select ALLWINNER_A10_PIC
> +    select ALLWINNER_A10_CCM
>      select ALLWINNER_EMAC
>      select SERIAL
>      select UNIMP
> diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
> index 79082289ea..86baeeeca2 100644
> --- a/hw/arm/allwinner-a10.c
> +++ b/hw/arm/allwinner-a10.c
> @@ -26,6 +26,7 @@
>  #include "hw/usb/hcd-ohci.h"
>
>  #define AW_A10_MMC0_BASE        0x01c0f000
> +#define AW_A10_CCM_BASE         0x01c20000
>  #define AW_A10_PIC_REG_BASE     0x01c20400
>  #define AW_A10_PIT_REG_BASE     0x01c20c00
>  #define AW_A10_UART0_REG_BASE   0x01c28000
> @@ -46,6 +47,8 @@ static void aw_a10_init(Object *obj)
>
>      object_initialize_child(obj, "timer", &s->timer, TYPE_AW_A10_PIT);
>
> +    object_initialize_child(obj, "ccm", &s->ccm, TYPE_AW_A10_CCM);
> +
>      object_initialize_child(obj, "emac", &s->emac, TYPE_AW_EMAC);
>
>      object_initialize_child(obj, "sata", &s->sata, TYPE_ALLWINNER_AHCI);
> @@ -103,6 +106,10 @@ static void aw_a10_realize(DeviceState *dev, Error
> **errp)
>      memory_region_add_subregion(get_system_memory(), 0x00000000,
> &s->sram_a);
>      create_unimplemented_device("a10-sram-ctrl", 0x01c00000, 4 * KiB);
>
> +    /* Clock Control Module */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->ccm), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, AW_A10_CCM_BASE);
> +
>      /* FIXME use qdev NIC properties instead of nd_table[] */
>      if (nd_table[0].used) {
>          qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC);
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index cbabe9f78c..ed07bf4133 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -174,4 +174,7 @@ config VIRT_CTRL
>  config LASI
>      bool
>
> +config ALLWINNER_A10_CCM
> +    bool
> +
>  source macio/Kconfig
> diff --git a/hw/misc/allwinner-a10-ccm.c b/hw/misc/allwinner-a10-ccm.c
> new file mode 100644
> index 0000000000..68146ee340
> --- /dev/null
> +++ b/hw/misc/allwinner-a10-ccm.c
> @@ -0,0 +1,224 @@
> +/*
> + * Allwinner A10 Clock Control Module emulation
> + *
> + * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> + *
> + *  This file is derived from Allwinner H3 CCU,
> + *  by Niek Linnenbank.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/units.h"
> +#include "hw/sysbus.h"
> +#include "migration/vmstate.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "hw/misc/allwinner-a10-ccm.h"
> +
> +/* CCM register offsets */
> +enum {
> +    REG_PLL1_CFG             = 0x0000, /* PLL1 Control */
> +    REG_PLL1_TUN             = 0x0004, /* PLL1 Tuning */
> +    REG_PLL2_CFG             = 0x0008, /* PLL2 Control */
> +    REG_PLL2_TUN             = 0x000C, /* PLL2 Tuning */
> +    REG_PLL3_CFG             = 0x0010, /* PLL3 Control */
> +    REG_PLL4_CFG             = 0x0018, /* PLL4 Control */
> +    REG_PLL5_CFG             = 0x0020, /* PLL5 Control */
> +    REG_PLL5_TUN             = 0x0024, /* PLL5 Tuning */
> +    REG_PLL6_CFG             = 0x0028, /* PLL6 Control */
> +    REG_PLL6_TUN             = 0x002C, /* PLL6 Tuning */
> +    REG_PLL7_CFG             = 0x0030, /* PLL7 Control */
> +    REG_PLL1_TUN2            = 0x0038, /* PLL1 Tuning2 */
> +    REG_PLL5_TUN2            = 0x003C, /* PLL5 Tuning2 */
> +    REG_PLL8_CFG             = 0x0040, /* PLL8 Control */
> +    REG_OSC24M_CFG           = 0x0050, /* OSC24M Control */
> +    REG_CPU_AHB_APB0_CFG     = 0x0054, /* CPU, AHB and APB0 Divide Ratio
> */
> +};
> +
> +#define REG_INDEX(offset)    (offset / sizeof(uint32_t))
> +
> +/* CCM register reset values */
> +enum {
> +    REG_PLL1_CFG_RST         = 0x21005000,
> +    REG_PLL1_TUN_RST         = 0x0A101000,
> +    REG_PLL2_CFG_RST         = 0x08100010,
> +    REG_PLL2_TUN_RST         = 0x00000000,
> +    REG_PLL3_CFG_RST         = 0x0010D063,
> +    REG_PLL4_CFG_RST         = 0x21009911,
> +    REG_PLL5_CFG_RST         = 0x11049280,
> +    REG_PLL5_TUN_RST         = 0x14888000,
> +    REG_PLL6_CFG_RST         = 0x21009911,
> +    REG_PLL6_TUN_RST         = 0x00000000,
> +    REG_PLL7_CFG_RST         = 0x0010D063,
> +    REG_PLL1_TUN2_RST        = 0x00000000,
> +    REG_PLL5_TUN2_RST        = 0x00000000,
> +    REG_PLL8_CFG_RST         = 0x21009911,
> +    REG_OSC24M_CFG_RST       = 0x00138013,
> +    REG_CPU_AHB_APB0_CFG_RST = 0x00010010,
> +};
> +
> +static uint64_t allwinner_a10_ccm_read(void *opaque, hwaddr offset,
> +                                       unsigned size)
> +{
> +    const AwA10ClockCtlState *s = AW_A10_CCM(opaque);
> +    const uint32_t idx = REG_INDEX(offset);
> +
> +    switch (offset) {
> +    case REG_PLL1_CFG:
> +    case REG_PLL1_TUN:
> +    case REG_PLL2_CFG:
> +    case REG_PLL2_TUN:
> +    case REG_PLL3_CFG:
> +    case REG_PLL4_CFG:
> +    case REG_PLL5_CFG:
> +    case REG_PLL5_TUN:
> +    case REG_PLL6_CFG:
> +    case REG_PLL6_TUN:
> +    case REG_PLL7_CFG:
> +    case REG_PLL1_TUN2:
> +    case REG_PLL5_TUN2:
> +    case REG_PLL8_CFG:
> +    case REG_OSC24M_CFG:
> +    case REG_CPU_AHB_APB0_CFG:
> +        break;
> +    case 0x158 ... AW_A10_CCM_IOSIZE:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset
> 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        return 0;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "%s: unimplemented read offset 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        return 0;
> +    }
> +
> +    return s->regs[idx];
> +}
> +
> +static void allwinner_a10_ccm_write(void *opaque, hwaddr offset,
> +                                   uint64_t val, unsigned size)
> +{
> +    AwA10ClockCtlState *s = AW_A10_CCM(opaque);
> +    const uint32_t idx = REG_INDEX(offset);
> +
> +    switch (offset) {
> +    case REG_PLL1_CFG:
> +    case REG_PLL1_TUN:
> +    case REG_PLL2_CFG:
> +    case REG_PLL2_TUN:
> +    case REG_PLL3_CFG:
> +    case REG_PLL4_CFG:
> +    case REG_PLL5_CFG:
> +    case REG_PLL5_TUN:
> +    case REG_PLL6_CFG:
> +    case REG_PLL6_TUN:
> +    case REG_PLL7_CFG:
> +    case REG_PLL1_TUN2:
> +    case REG_PLL5_TUN2:
> +    case REG_PLL8_CFG:
> +    case REG_OSC24M_CFG:
> +    case REG_CPU_AHB_APB0_CFG:
> +        break;
> +    case 0x158 ... AW_A10_CCM_IOSIZE:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset
> 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset
> 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        break;
> +    }
> +
> +    s->regs[idx] = (uint32_t) val;
> +}
> +
> +static const MemoryRegionOps allwinner_a10_ccm_ops = {
> +    .read = allwinner_a10_ccm_read,
> +    .write = allwinner_a10_ccm_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +    .impl.min_access_size = 4,
> +};
> +
> +static void allwinner_a10_ccm_reset_enter(Object *obj, ResetType type)
> +{
> +    AwA10ClockCtlState *s = AW_A10_CCM(obj);
> +
> +    /* Set default values for registers */
> +    s->regs[REG_INDEX(REG_PLL1_CFG)] = REG_PLL1_CFG_RST;
> +    s->regs[REG_INDEX(REG_PLL1_TUN)] = REG_PLL1_TUN_RST;
> +    s->regs[REG_INDEX(REG_PLL2_CFG)] = REG_PLL2_CFG_RST;
> +    s->regs[REG_INDEX(REG_PLL2_TUN)] = REG_PLL2_TUN_RST;
> +    s->regs[REG_INDEX(REG_PLL3_CFG)] = REG_PLL3_CFG_RST;
> +    s->regs[REG_INDEX(REG_PLL4_CFG)] = REG_PLL4_CFG_RST;
> +    s->regs[REG_INDEX(REG_PLL5_CFG)] = REG_PLL5_CFG_RST;
> +    s->regs[REG_INDEX(REG_PLL5_TUN)] = REG_PLL5_TUN_RST;
> +    s->regs[REG_INDEX(REG_PLL6_CFG)] = REG_PLL6_CFG_RST;
> +    s->regs[REG_INDEX(REG_PLL6_TUN)] = REG_PLL6_TUN_RST;
> +    s->regs[REG_INDEX(REG_PLL7_CFG)] = REG_PLL7_CFG_RST;
> +    s->regs[REG_INDEX(REG_PLL1_TUN2)] = REG_PLL1_TUN2_RST;
> +    s->regs[REG_INDEX(REG_PLL5_TUN2)] = REG_PLL5_TUN2_RST;
> +    s->regs[REG_INDEX(REG_PLL8_CFG)] = REG_PLL8_CFG_RST;
> +    s->regs[REG_INDEX(REG_OSC24M_CFG)] = REG_OSC24M_CFG_RST;
> +    s->regs[REG_INDEX(REG_CPU_AHB_APB0_CFG)] = REG_CPU_AHB_APB0_CFG_RST;
> +}
> +
> +static void allwinner_a10_ccm_init(Object *obj)
> +{
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +    AwA10ClockCtlState *s = AW_A10_CCM(obj);
> +
> +    /* Memory mapping */
> +    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_ccm_ops, s,
> +                          TYPE_AW_A10_CCM, AW_A10_CCM_IOSIZE);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +}
> +
> +static const VMStateDescription allwinner_a10_ccm_vmstate = {
> +    .name = "allwinner-a10-ccm",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, AwA10ClockCtlState,
> AW_A10_CCM_REGS_NUM),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void allwinner_a10_ccm_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> +
> +    rc->phases.enter = allwinner_a10_ccm_reset_enter;
> +    dc->vmsd = &allwinner_a10_ccm_vmstate;
> +}
> +
> +static const TypeInfo allwinner_a10_ccm_info = {
> +    .name          = TYPE_AW_A10_CCM,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_init = allwinner_a10_ccm_init,
> +    .instance_size = sizeof(AwA10ClockCtlState),
> +    .class_init    = allwinner_a10_ccm_class_init,
> +};
> +
> +static void allwinner_a10_ccm_register(void)
> +{
> +    type_register_static(&allwinner_a10_ccm_info);
> +}
> +
> +type_init(allwinner_a10_ccm_register)
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 95268eddc0..ebf216edbc 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -38,6 +38,7 @@ subdir('macio')
>
>  softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c'))
>
> +softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true:
> files('allwinner-a10-ccm.c'))
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true:
> files('allwinner-h3-ccu.c'))
>  specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true:
> files('allwinner-cpucfg.c'))
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true:
> files('allwinner-h3-dramc.c'))
> diff --git a/include/hw/arm/allwinner-a10.h
> b/include/hw/arm/allwinner-a10.h
> index a76dc7b84d..45d0fc2f7e 100644
> --- a/include/hw/arm/allwinner-a10.h
> +++ b/include/hw/arm/allwinner-a10.h
> @@ -12,6 +12,7 @@
>  #include "hw/usb/hcd-ohci.h"
>  #include "hw/usb/hcd-ehci.h"
>  #include "hw/rtc/allwinner-rtc.h"
> +#include "hw/misc/allwinner-a10-ccm.h"
>
>  #include "target/arm/cpu.h"
>  #include "qom/object.h"
> @@ -30,6 +31,7 @@ struct AwA10State {
>      /*< public >*/
>
>      ARMCPU cpu;
> +    AwA10ClockCtlState ccm;
>      AwA10PITState timer;
>      AwA10PICState intc;
>      AwEmacState emac;
> diff --git a/include/hw/misc/allwinner-a10-ccm.h
> b/include/hw/misc/allwinner-a10-ccm.h
> new file mode 100644
> index 0000000000..7f22532efa
> --- /dev/null
> +++ b/include/hw/misc/allwinner-a10-ccm.h
> @@ -0,0 +1,67 @@
> +/*
> + * Allwinner A10 Clock Control Module emulation
> + *
> + * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> + *
> + *  This file is derived from Allwinner H3 CCU,
> + *  by Niek Linnenbank.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef HW_MISC_ALLWINNER_A10_CCM_H
> +#define HW_MISC_ALLWINNER_A10_CCM_H
> +
> +#include "qom/object.h"
> +#include "hw/sysbus.h"
> +
> +/**
> + * @name Constants
> + * @{
> + */
> +
> +/** Size of register I/O address space used by CCM device */
> +#define AW_A10_CCM_IOSIZE        (0x400)
> +
> +/** Total number of known registers */
> +#define AW_A10_CCM_REGS_NUM      (AW_A10_CCM_IOSIZE / sizeof(uint32_t))
> +
> +/** @} */
> +
> +/**
> + * @name Object model
> + * @{
> + */
> +
> +#define TYPE_AW_A10_CCM    "allwinner-a10-ccm"
> +OBJECT_DECLARE_SIMPLE_TYPE(AwA10ClockCtlState, AW_A10_CCM)
> +
> +/** @} */
> +
> +/**
> + * Allwinner A10 CCM object instance state.
> + */
> +struct AwA10ClockCtlState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +    /*< public >*/
> +
> +    /** Maps I/O registers in physical memory */
> +    MemoryRegion iomem;
> +
> +    /** Array of hardware registers */
> +    uint32_t regs[AW_A10_CCM_REGS_NUM];
> +};
> +
> +#endif /* HW_MISC_ALLWINNER_H3_CCU_H */
> --
> 2.30.2
>
>

-- 
Niek Linnenbank

[-- Attachment #2: Type: text/html, Size: 18530 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 6/6] hw/arm: Allwinner A10 enable SPL load from MMC
  2022-12-03 23:19 ` [PATCH 6/6] hw/arm: Allwinner A10 enable SPL load from MMC Strahinja Jankovic
@ 2022-12-07 22:39   ` Niek Linnenbank
  2022-12-08 19:22     ` Strahinja Jankovic
  0 siblings, 1 reply; 18+ messages in thread
From: Niek Linnenbank @ 2022-12-07 22:39 UTC (permalink / raw)
  To: Strahinja Jankovic
  Cc: Peter Maydell, Beniamino Galvani, qemu-arm, qemu-devel,
	Strahinja Jankovic

[-- Attachment #1: Type: text/plain, Size: 4253 bytes --]

Hi Strahinja,


On Sun, Dec 4, 2022 at 12:19 AM Strahinja Jankovic <
strahinjapjankovic@gmail.com> wrote:

> This patch enables copying of SPL from MMC if `-kernel` parameter is not
> passed when starting QEMU. SPL is copied to SRAM_A.
>
> The approach is reused from Allwinner H3 implementation.
>
> Tested with Armbian and custom Yocto image.
>
> Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> ---
>  hw/arm/allwinner-a10.c         | 18 ++++++++++++++++++
>  hw/arm/cubieboard.c            |  5 +++++
>  include/hw/arm/allwinner-a10.h | 21 +++++++++++++++++++++
>  3 files changed, 44 insertions(+)
>
> diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
> index 17e439777e..dc1966ff7a 100644
> --- a/hw/arm/allwinner-a10.c
> +++ b/hw/arm/allwinner-a10.c
> @@ -24,7 +24,9 @@
>  #include "sysemu/sysemu.h"
>  #include "hw/boards.h"
>  #include "hw/usb/hcd-ohci.h"
> +#include "hw/loader.h"
>
> +#define AW_A10_SRAM_A_BASE      0x00000000
>  #define AW_A10_DRAMC_BASE       0x01c01000
>  #define AW_A10_MMC0_BASE        0x01c0f000
>  #define AW_A10_CCM_BASE         0x01c20000
> @@ -38,6 +40,22 @@
>  #define AW_A10_RTC_BASE         0x01c20d00
>  #define AW_A10_I2C0_BASE        0x01c2ac00
>
> +void allwinner_a10_bootrom_setup(AwA10State *s, BlockBackend *blk)
> +{
> +    const int64_t rom_size = 32 * KiB;
> +    g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size);
> +
> +    if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) {
> +        error_setg(&error_fatal, "%s: failed to read BlockBackend data",
> +                   __func__);
> +        return;
> +    }
> +
> +    rom_add_blob("allwinner-a10.bootrom", buffer, rom_size,
> +                  rom_size, AW_A10_SRAM_A_BASE,
> +                  NULL, NULL, NULL, NULL, false);
> +}
>

Its probably fine for now to do it in the same way here for the A10 indeed.
Perhaps in the future, we can try
to share some overlapping code between the A10 and H3.

So the patch looks fine to me:
Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>

Regards,
Niek


> +
>  static void aw_a10_init(Object *obj)
>  {
>      AwA10State *s = AW_A10(obj);
> diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c
> index afc7980414..37659c35fd 100644
> --- a/hw/arm/cubieboard.c
> +++ b/hw/arm/cubieboard.c
> @@ -99,6 +99,11 @@ static void cubieboard_init(MachineState *machine)
>      memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE,
>                                  machine->ram);
>
> +    /* Load target kernel or start using BootROM */
> +    if (!machine->kernel_filename && blk && blk_is_available(blk)) {
> +        /* Use Boot ROM to copy data from SD card to SRAM */
> +        allwinner_a10_bootrom_setup(a10, blk);
> +    }
>      /* TODO create and connect IDE devices for ide_drive_get() */
>
>      cubieboard_binfo.ram_size = machine->ram_size;
> diff --git a/include/hw/arm/allwinner-a10.h
> b/include/hw/arm/allwinner-a10.h
> index 763935fca9..b3c9ed24c7 100644
> --- a/include/hw/arm/allwinner-a10.h
> +++ b/include/hw/arm/allwinner-a10.h
> @@ -15,6 +15,7 @@
>  #include "hw/misc/allwinner-a10-ccm.h"
>  #include "hw/misc/allwinner-a10-dramc.h"
>  #include "hw/i2c/allwinner-i2c.h"
> +#include "sysemu/block-backend.h"
>
>  #include "target/arm/cpu.h"
>  #include "qom/object.h"
> @@ -47,4 +48,24 @@ struct AwA10State {
>      OHCISysBusState ohci[AW_A10_NUM_USB];
>  };
>
> +/**
> + * Emulate Boot ROM firmware setup functionality.
> + *
> + * A real Allwinner A10 SoC contains a Boot ROM
> + * which is the first code that runs right after
> + * the SoC is powered on. The Boot ROM is responsible
> + * for loading user code (e.g. a bootloader) from any
> + * of the supported external devices and writing the
> + * downloaded code to internal SRAM. After loading the SoC
> + * begins executing the code written to SRAM.
> + *
> + * This function emulates the Boot ROM by copying 32 KiB
> + * of data from the given block device and writes it to
> + * the start of the first internal SRAM memory.
> + *
> + * @s: Allwinner A10 state object pointer
> + * @blk: Block backend device object pointer
> + */
> +void allwinner_a10_bootrom_setup(AwA10State *s, BlockBackend *blk);
> +
>  #endif
> --
> 2.30.2
>
>

-- 
Niek Linnenbank

[-- Attachment #2: Type: text/html, Size: 5698 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 3/6] hw/i2c: Allwinner TWI/I2C Emulation
  2022-12-07 22:06   ` Niek Linnenbank
@ 2022-12-08 19:19     ` Strahinja Jankovic
  0 siblings, 0 replies; 18+ messages in thread
From: Strahinja Jankovic @ 2022-12-08 19:19 UTC (permalink / raw)
  To: Niek Linnenbank; +Cc: Peter Maydell, Beniamino Galvani, qemu-arm, qemu-devel

Hi Niek,

On Wed, Dec 7, 2022 at 11:06 PM Niek Linnenbank
<nieklinnenbank@gmail.com> wrote:
>
> Hi Strahinja,
>
> On Sun, Dec 4, 2022 at 12:19 AM Strahinja Jankovic <strahinjapjankovic@gmail.com> wrote:
>>
>> This patch implements Allwinner TWI/I2C controller emulation. Only
>> master-mode functionality is implemented.
>>
>> The SPL boot for Cubieboard expects AXP209 PMIC on TWI0/I2C0 bus, so this is
>> first part enabling the TWI/I2C bus operation.
>>
>> Since both Allwinner A10 and H3 use the same module, it is added for
>> both boards.
>
>
> The A10 and H3 datasheets have the same introduction text on the TWI, suggesting re-use indeed. Unfortunately
> the A10 datasheet seems to be missing register documentation, so I can't compare that with the H3 datasheet.

The A10 register documentation for TWI exists in
https://linux-sunxi.org/File:Allwinner_A10_User_manual_V1.5.pdf user
manual (unfortunately, register description for many other modules is
missing). From what I could see, the description matches the H3, so I
thought it would be good to use the same implementation.

>
> At least according to what is implemented in the linux kernel, looks like that indeed both SoCs implement the same I2C module.
> The file drivers/i2c/busses/i2c-mv64xxx.c has the following mv64xxx_i2c_of_match_table:
>         { .compatible = "allwinner,sun4i-a10-i2c", .data = &mv64xxx_i2c_regs_sun4i},
>         { .compatible = "allwinner,sun6i-a31-i2c", .data = &mv64xxx_i2c_regs_sun4i},
>
> And both SoCs define the sun4i-a10-i2c and sun6i-a31-i2c in their device tree files, respectively.
>
> Could you please also update the documentation files for both boards, so we can show that they now support TWI/I2C?
>   docs/system/arm/cubieboard.rst
>   docs/system/arm/orangepi.rst

Yes, I will update these documents in V2. I will also try to update
the description for the cubieboard to have some examples on how to run
the emulation.

>
>>
>>
>> Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
>> ---
>>  hw/arm/Kconfig                 |   2 +
>>  hw/arm/allwinner-a10.c         |   8 +
>>  hw/arm/allwinner-h3.c          |  11 +-
>>  hw/i2c/Kconfig                 |   4 +
>>  hw/i2c/allwinner-i2c.c         | 417 +++++++++++++++++++++++++++++++++
>>  hw/i2c/meson.build             |   1 +
>>  include/hw/arm/allwinner-a10.h |   2 +
>>  include/hw/arm/allwinner-h3.h  |   3 +
>>  include/hw/i2c/allwinner-i2c.h | 112 +++++++++
>>  9 files changed, 559 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/i2c/allwinner-i2c.c
>>  create mode 100644 include/hw/i2c/allwinner-i2c.h
>>
>> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
>> index 140f142ae5..eefe1fd134 100644
>> --- a/hw/arm/Kconfig
>> +++ b/hw/arm/Kconfig
>> @@ -322,6 +322,7 @@ config ALLWINNER_A10
>>      select ALLWINNER_A10_CCM
>>      select ALLWINNER_A10_DRAMC
>>      select ALLWINNER_EMAC
>> +    select ALLWINNER_I2C
>>      select SERIAL
>>      select UNIMP
>>
>> @@ -329,6 +330,7 @@ config ALLWINNER_H3
>>      bool
>>      select ALLWINNER_A10_PIT
>>      select ALLWINNER_SUN8I_EMAC
>> +    select ALLWINNER_I2C
>>      select SERIAL
>>      select ARM_TIMER
>>      select ARM_GIC
>> diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
>> index a5f7a36ac9..17e439777e 100644
>> --- a/hw/arm/allwinner-a10.c
>> +++ b/hw/arm/allwinner-a10.c
>> @@ -36,6 +36,7 @@
>>  #define AW_A10_OHCI_BASE        0x01c14400
>>  #define AW_A10_SATA_BASE        0x01c18000
>>  #define AW_A10_RTC_BASE         0x01c20d00
>> +#define AW_A10_I2C0_BASE        0x01c2ac00
>>
>>  static void aw_a10_init(Object *obj)
>>  {
>> @@ -56,6 +57,8 @@ static void aw_a10_init(Object *obj)
>>
>>      object_initialize_child(obj, "sata", &s->sata, TYPE_ALLWINNER_AHCI);
>>
>> +    object_initialize_child(obj, "i2c0", &s->i2c0, TYPE_AW_I2C);
>> +
>>      if (machine_usb(current_machine)) {
>>          int i;
>>
>> @@ -176,6 +179,11 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
>>      /* RTC */
>>      sysbus_realize(SYS_BUS_DEVICE(&s->rtc), &error_fatal);
>>      sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->rtc), 0, AW_A10_RTC_BASE, 10);
>> +
>> +    /* I2C */
>> +    sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal);
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, AW_A10_I2C0_BASE);
>> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0, qdev_get_gpio_in(dev, 7));
>>  }
>>
>>  static void aw_a10_class_init(ObjectClass *oc, void *data)
>> diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
>> index 308ed15552..bfce3c8d92 100644
>> --- a/hw/arm/allwinner-h3.c
>> +++ b/hw/arm/allwinner-h3.c
>> @@ -53,6 +53,7 @@ const hwaddr allwinner_h3_memmap[] = {
>>      [AW_H3_DEV_UART1]      = 0x01c28400,
>>      [AW_H3_DEV_UART2]      = 0x01c28800,
>>      [AW_H3_DEV_UART3]      = 0x01c28c00,
>> +    [AW_H3_DEV_TWI0]       = 0x01c2ac00,
>>      [AW_H3_DEV_EMAC]       = 0x01c30000,
>>      [AW_H3_DEV_DRAMCOM]    = 0x01c62000,
>>      [AW_H3_DEV_DRAMCTL]    = 0x01c63000,
>> @@ -106,7 +107,6 @@ struct AwH3Unimplemented {
>>      { "uart1",     0x01c28400, 1 * KiB },
>>      { "uart2",     0x01c28800, 1 * KiB },
>>      { "uart3",     0x01c28c00, 1 * KiB },
>> -    { "twi0",      0x01c2ac00, 1 * KiB },
>>      { "twi1",      0x01c2b000, 1 * KiB },
>>      { "twi2",      0x01c2b400, 1 * KiB },
>>      { "scr",       0x01c2c400, 1 * KiB },
>> @@ -150,6 +150,7 @@ enum {
>>      AW_H3_GIC_SPI_UART1     =  1,
>>      AW_H3_GIC_SPI_UART2     =  2,
>>      AW_H3_GIC_SPI_UART3     =  3,
>> +    AW_H3_GIC_SPI_TWI0      =  6,
>>      AW_H3_GIC_SPI_TIMER0    = 18,
>>      AW_H3_GIC_SPI_TIMER1    = 19,
>>      AW_H3_GIC_SPI_MMC0      = 60,
>> @@ -225,6 +226,8 @@ static void allwinner_h3_init(Object *obj)
>>                                "ram-size");
>>
>>      object_initialize_child(obj, "rtc", &s->rtc, TYPE_AW_RTC_SUN6I);
>> +
>> +    object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C);
>>  }
>>
>>  static void allwinner_h3_realize(DeviceState *dev, Error **errp)
>> @@ -423,6 +426,12 @@ static void allwinner_h3_realize(DeviceState *dev, Error **errp)
>>      sysbus_realize(SYS_BUS_DEVICE(&s->rtc), &error_fatal);
>>      sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, s->memmap[AW_H3_DEV_RTC]);
>>
>> +    /* I2C */
>> +    sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal);
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, s->memmap[AW_H3_DEV_TWI0]);
>> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0,
>> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TWI0));
>> +
>>      /* Unimplemented devices */
>>      for (i = 0; i < ARRAY_SIZE(unimplemented); i++) {
>>          create_unimplemented_device(unimplemented[i].device_name,
>> diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig
>> index 9bb8870517..f8ec461be3 100644
>> --- a/hw/i2c/Kconfig
>> +++ b/hw/i2c/Kconfig
>> @@ -34,6 +34,10 @@ config MPC_I2C
>>      bool
>>      select I2C
>>
>> +config ALLWINNER_I2C
>> +    bool
>> +    select I2C
>> +
>>  config PCA954X
>>      bool
>>      select I2C
>> diff --git a/hw/i2c/allwinner-i2c.c b/hw/i2c/allwinner-i2c.c
>> new file mode 100644
>> index 0000000000..4c9080a18b
>> --- /dev/null
>> +++ b/hw/i2c/allwinner-i2c.c
>> @@ -0,0 +1,417 @@
>> +/*
>> + *  Allwinner I2C Bus Serial Interface Emulation
>> + *
>> + *  Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
>> + *
>> + *  This file is derived from IMX I2C controller,
>> + *  by Jean-Christophe DUBOIS .
>> + *
>> + *  This program is free software; you can redistribute it and/or modify it
>> + *  under the terms of the GNU General Public License as published by the
>> + *  Free Software Foundation; either version 2 of the License, or
>> + *  (at your option) any later version.
>> + *
>> + *  This program is distributed in the hope that it will be useful, but WITHOUT
>> + *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
>> + *  for more details.
>> + *
>> + *  You should have received a copy of the GNU General Public License along
>> + *  with this program; if not, see <http://www.gnu.org/licenses/>.
>> + *
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "hw/i2c/allwinner-i2c.h"
>> +#include "hw/irq.h"
>> +#include "migration/vmstate.h"
>> +#include "hw/i2c/i2c.h"
>> +#include "qemu/log.h"
>> +#include "qemu/module.h"
>> +
>> +#ifndef DEBUG_AW_I2C
>> +#define DEBUG_AW_I2C 0
>> +#endif
>> +
>> +#define DPRINTF(fmt, args...) \
>> +    do { \
>> +        if (DEBUG_AW_I2C) { \
>> +            fprintf(stderr, "[%s]%s: " fmt , TYPE_AW_I2C, \
>> +                                             __func__, ##args); \
>> +        } \
>> +    } while (0)
>
> As Philipe already suggested earlier, this could be replaced with tracing. Maybe have a look at some of the other I2C drivers to see how its done, for example aspeed_i2c.c.

I will, thanks.

>
>>
>> +
>> +/* Status value in STAT register is shifted by 3 bits */
>> +#define TWI_STAT_SHIFT      3
>> +#define STAT_FROM_STA(x)    ((x) << TWI_STAT_SHIFT)
>> +#define STAT_TO_STA(x)      ((x) >> TWI_STAT_SHIFT)
>
> Maybe add a newline here for readability.

Ok.

>
>>
>> +enum {
>> +    STAT_BUS_ERROR = 0,
>> +    /* Master mode */
>> +    STAT_M_STA_TX,
>> +    STAT_M_RSTA_TX,
>> +    STAT_M_ADDR_WR_ACK,
>> +    STAT_M_ADDR_WR_NACK,
>> +    STAT_M_DATA_TX_ACK,
>> +    STAT_M_DATA_TX_NACK,
>> +    STAT_M_ARB_LOST,
>> +    STAT_M_ADDR_RD_ACK,
>> +    STAT_M_ADDR_RD_NACK,
>> +    STAT_M_DATA_RX_ACK,
>> +    STAT_M_DATA_RX_NACK,
>> +    /* Slave mode */
>> +    STAT_S_ADDR_WR_ACK,
>> +    STAT_S_ARB_LOST_AW_ACK,
>> +    STAT_S_GCA_ACK,
>> +    STAT_S_ARB_LOST_GCA_ACK,
>> +    STAT_S_DATA_RX_SA_ACK,
>> +    STAT_S_DATA_RX_SA_NACK,
>> +    STAT_S_DATA_RX_GCA_ACK,
>> +    STAT_S_DATA_RX_GCA_NACK,
>> +    STAT_S_STP_RSTA,
>> +    STAT_S_ADDR_RD_ACK,
>> +    STAT_S_ARB_LOST_AR_ACK,
>> +    STAT_S_DATA_TX_ACK,
>> +    STAT_S_DATA_TX_NACK,
>> +    STAT_S_LB_TX_ACK,
>> +    /* Master mode, 10-bit */
>> +    STAT_M_2ND_ADDR_WR_ACK,
>> +    STAT_M_2ND_ADDR_WR_NACK,
>> +    /* Idle */
>> +    STAT_IDLE = 0x1f
>> +} TWI_STAT_STA;
>> +
>> +static const char *allwinner_i2c_get_regname(unsigned offset)
>> +{
>> +    switch (offset) {
>> +    case TWI_ADDR_REG:
>> +        return "ADDR";
>> +    case TWI_XADDR_REG:
>> +        return "XADDR";
>> +    case TWI_DATA_REG:
>> +        return "DATA";
>> +    case TWI_CNTR_REG:
>> +        return "CNTR";
>> +    case TWI_STAT_REG:
>> +        return "STAT";
>> +    case TWI_CCR_REG:
>> +        return "CCR";
>> +    case TWI_SRST_REG:
>> +        return "SRST";
>> +    case TWI_EFR_REG:
>> +        return "EFR";
>> +    case TWI_LCR_REG:
>> +        return "LCR";
>> +    default:
>> +        return "[?]";
>> +    }
>> +}
>> +
>> +static inline bool allwinner_i2c_is_reset(AWI2CState *s)
>> +{
>> +    return s->srst & TWI_SRST_MASK;
>> +}
>> +
>> +static inline bool allwinner_i2c_bus_is_enabled(AWI2CState *s)
>> +{
>> +    return s->cntr & TWI_CNTR_BUS_EN;
>> +}
>> +
>> +static inline bool allwinner_i2c_interrupt_is_enabled(AWI2CState *s)
>> +{
>> +    return s->cntr & TWI_CNTR_INT_EN;
>> +}
>> +
>> +static void allwinner_i2c_reset_hold(Object *obj)
>> +{
>> +    AWI2CState *s = AW_I2C(obj);
>> +
>> +    if (STAT_TO_STA(s->stat) != STAT_IDLE) {
>> +        i2c_end_transfer(s->bus);
>> +    }
>> +
>> +    s->addr  = TWI_ADDR_RESET;
>> +    s->xaddr = TWI_XADDR_RESET;
>> +    s->data  = TWI_DATA_RESET;
>> +    s->cntr  = TWI_CNTR_RESET;
>> +    s->stat  = TWI_STAT_RESET;
>> +    s->ccr   = TWI_CCR_RESET;
>> +    s->srst  = TWI_SRST_RESET;
>> +    s->efr   = TWI_EFR_RESET;
>> +    s->lcr   = TWI_LCR_RESET;
>> +}
>> +
>> +static inline void allwinner_i2c_raise_interrupt(AWI2CState *s)
>> +{
>> +    /*
>> +     * Raise an interrupt if the device is not reset and it is configured
>> +     * to generate some interrupts.
>> +     */
>> +    if (!allwinner_i2c_is_reset(s) && allwinner_i2c_bus_is_enabled(s)) {
>> +        if (STAT_TO_STA(s->stat) != STAT_IDLE) {
>> +            s->cntr |= TWI_CNTR_INT_FLAG;
>> +            if (allwinner_i2c_interrupt_is_enabled(s)) {
>> +                qemu_irq_raise(s->irq);
>> +            }
>> +        }
>> +    }
>> +}
>> +
>> +static uint64_t allwinner_i2c_read(void *opaque, hwaddr offset,
>> +                                   unsigned size)
>> +{
>> +    uint16_t value;
>> +    AWI2CState *s = AW_I2C(opaque);
>> +
>> +    switch (offset) {
>> +    case TWI_ADDR_REG:
>> +        value = s->addr;
>> +        break;
>> +    case TWI_XADDR_REG:
>> +        value = s->xaddr;
>> +        break;
>> +    case TWI_DATA_REG:
>> +        switch (STAT_TO_STA(s->stat)) {
>> +        case STAT_M_ADDR_RD_ACK:
>> +        case STAT_M_DATA_RX_ACK:
>> +        case STAT_M_DATA_RX_NACK:
>> +            /* get the next byte */
>> +            s->data = i2c_recv(s->bus);
>> +
>> +            if (s->cntr & TWI_CNTR_A_ACK) {
>> +                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
>> +            } else {
>> +                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
>> +            }
>> +            allwinner_i2c_raise_interrupt(s);
>> +            break;
>
>
> Looking at this code, here we have a nested switch statement. I just wonder if it's well defined C programming behavior what really should happen
> when calling 'break' in such construction. Should it step out of the first switch or both? Perhaps it works OK with gcc, but what about other compilers.
> Also for readability, it may be better to avoid nesting switch statements at all in my opinion.

No problem, I will convert the nested switch statement to if.

>
>>
>> +        default:
>> +            break;
>> +        }
>> +        value = s->data;
>> +        break;
>> +    case TWI_CNTR_REG:
>> +        value = s->cntr;
>> +        break;
>> +    case TWI_STAT_REG:
>> +        value = s->stat;
>> +        /*
>> +         * If polling when reading then change state to indicate data
>> +         * is available
>> +         */
>> +        if (STAT_TO_STA(s->stat) == STAT_M_ADDR_RD_ACK) {
>> +            if (s->cntr & TWI_CNTR_A_ACK) {
>> +                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
>> +            } else {
>> +                s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
>> +            }
>> +            allwinner_i2c_raise_interrupt(s);
>> +        }
>> +        break;
>> +    case TWI_CCR_REG:
>> +        value = s->ccr;
>> +        break;
>> +    case TWI_SRST_REG:
>> +        value = s->srst;
>> +        break;
>> +    case TWI_EFR_REG:
>> +        value = s->efr;
>> +        break;
>> +    case TWI_LCR_REG:
>> +        value = s->lcr;
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
>> +                      HWADDR_PRIx "\n", TYPE_AW_I2C, __func__, offset);
>> +        value = 0;
>> +        break;
>> +    }
>> +
>> +    DPRINTF("read %s [0x%" HWADDR_PRIx "] -> 0x%02x\n",
>> +            allwinner_i2c_get_regname(offset), offset, value);
>> +
>> +    return (uint64_t)value;
>> +}
>> +
>> +static void allwinner_i2c_write(void *opaque, hwaddr offset,
>> +                                uint64_t value, unsigned size)
>> +{
>> +    AWI2CState *s = AW_I2C(opaque);
>> +
>> +    DPRINTF("write %s [0x%" HWADDR_PRIx "] <- 0x%02x\n",
>> +            allwinner_i2c_get_regname(offset), offset, (int)value);
>> +
>> +    value &= 0xff;
>> +
>> +    switch (offset) {
>> +    case TWI_ADDR_REG:
>> +        s->addr = (uint8_t)value;
>> +        break;
>> +    case TWI_XADDR_REG:
>> +        s->xaddr = (uint8_t)value;
>> +        break;
>> +    case TWI_DATA_REG:
>> +        /* If the device is in reset or not enabled, nothing to do */
>> +        if (allwinner_i2c_is_reset(s) || (!allwinner_i2c_bus_is_enabled(s))) {
>> +            break;
>> +        }
>> +
>> +        s->data = value & TWI_DATA_MASK;
>> +
>> +        switch (STAT_TO_STA(s->stat)) {
>> +        case STAT_M_STA_TX:
>> +        case STAT_M_RSTA_TX:
>> +            /* Send address */
>> +            if (i2c_start_transfer(s->bus, extract32(s->data, 1, 7),
>> +                                extract32(s->data, 0, 1))) {
>> +                /* If non zero is returned, the address is not valid */
>> +                s->stat = STAT_FROM_STA(STAT_M_ADDR_WR_NACK);
>> +            } else {
>> +                /* Determine if read of write */
>> +                if (extract32(s->data, 0, 1)) {
>> +                    s->stat = STAT_FROM_STA(STAT_M_ADDR_RD_ACK);
>> +                } else {
>> +                    s->stat = STAT_FROM_STA(STAT_M_ADDR_WR_ACK);
>> +                }
>> +                allwinner_i2c_raise_interrupt(s);
>> +            }
>> +            break;
>> +        case STAT_M_ADDR_WR_ACK:
>> +        case STAT_M_DATA_TX_ACK:
>> +            if (i2c_send(s->bus, s->data)) {
>> +                /* If the target return non zero then end the transfer */
>> +                s->stat = STAT_FROM_STA(STAT_M_DATA_TX_NACK);
>> +                i2c_end_transfer(s->bus);
>> +            } else {
>> +                s->stat = STAT_FROM_STA(STAT_M_DATA_TX_ACK);
>> +                allwinner_i2c_raise_interrupt(s);
>> +            }
>> +            break;
>> +        default:
>> +            break;
>> +        }
>> +        break;
>> +    case TWI_CNTR_REG:
>> +        if (!allwinner_i2c_is_reset(s)) {
>> +            /* Do something only if not in software reset */
>> +            s->cntr = value & TWI_CNTR_MASK;
>> +
>> +            /* Check if start condition should be sent */
>> +            if (s->cntr & TWI_CNTR_M_STA) {
>> +                /* Update status */
>> +                if (STAT_TO_STA(s->stat) == STAT_IDLE) {
>> +                    /* Send start condition */
>> +                    s->stat = STAT_FROM_STA(STAT_M_STA_TX);
>> +                } else {
>> +                    /* Send repeated start condition */
>> +                    s->stat = STAT_FROM_STA(STAT_M_RSTA_TX);
>> +                }
>> +                /* Clear start condition */
>> +                s->cntr &= ~TWI_CNTR_M_STA;
>> +            }
>> +            if (s->cntr & TWI_CNTR_M_STP) {
>> +                /* Update status */
>> +                i2c_end_transfer(s->bus);
>> +                s->stat = STAT_FROM_STA(STAT_IDLE);
>> +                s->cntr &= ~TWI_CNTR_M_STP;
>> +            }
>> +            if ((s->cntr & TWI_CNTR_INT_FLAG) == 0) {
>> +                /* Interrupt flag cleared */
>> +                qemu_irq_lower(s->irq);
>> +            }
>> +            if ((s->cntr & TWI_CNTR_A_ACK) == 0) {
>> +                if (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_ACK) {
>> +                    s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
>> +                }
>> +            } else {
>> +                if (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_NACK) {
>> +                    s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
>> +                }
>> +            }
>> +            allwinner_i2c_raise_interrupt(s);
>> +
>> +        }
>> +        break;
>> +    case TWI_CCR_REG:
>> +        s->ccr = value & TWI_CCR_MASK;
>> +        break;
>> +    case TWI_SRST_REG:
>> +        if (((value & TWI_SRST_MASK) == 0) && (s->srst & TWI_SRST_MASK)) {
>> +            /* Perform reset */
>> +            allwinner_i2c_reset_hold(OBJECT(s));
>> +        }
>> +        s->srst = value & TWI_SRST_MASK;
>> +        break;
>> +    case TWI_EFR_REG:
>> +        s->efr = value & TWI_EFR_MASK;
>> +        break;
>> +    case TWI_LCR_REG:
>> +        s->lcr = value & TWI_LCR_MASK;
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
>> +                      HWADDR_PRIx "\n", TYPE_AW_I2C, __func__, offset);
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps allwinner_i2c_ops = {
>> +    .read = allwinner_i2c_read,
>> +    .write = allwinner_i2c_write,
>> +    .valid.min_access_size = 1,
>> +    .valid.max_access_size = 4,
>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>> +};
>> +
>> +static const VMStateDescription allwinner_i2c_vmstate = {
>> +    .name = TYPE_AW_I2C,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT8(addr, AWI2CState),
>> +        VMSTATE_UINT8(xaddr, AWI2CState),
>> +        VMSTATE_UINT8(data, AWI2CState),
>> +        VMSTATE_UINT8(cntr, AWI2CState),
>> +        VMSTATE_UINT8(ccr, AWI2CState),
>> +        VMSTATE_UINT8(srst, AWI2CState),
>> +        VMSTATE_UINT8(efr, AWI2CState),
>> +        VMSTATE_UINT8(lcr, AWI2CState),
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static void allwinner_i2c_realize(DeviceState *dev, Error **errp)
>> +{
>> +    AWI2CState *s = AW_I2C(dev);
>> +
>> +    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_i2c_ops, s,
>> +                          TYPE_AW_I2C, AW_I2C_MEM_SIZE);
>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
>> +    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
>> +    s->bus = i2c_init_bus(dev, "i2c");
>> +}
>> +
>> +static void allwinner_i2c_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    ResettableClass *rc = RESETTABLE_CLASS(klass);
>> +
>> +    rc->phases.hold = allwinner_i2c_reset_hold;
>> +    dc->vmsd = &allwinner_i2c_vmstate;
>> +    dc->realize = allwinner_i2c_realize;
>> +    dc->desc = "Allwinner I2C Controller";
>> +}
>> +
>> +static const TypeInfo allwinner_i2c_type_info = {
>> +    .name = TYPE_AW_I2C,
>> +    .parent = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(AWI2CState),
>> +    .class_init = allwinner_i2c_class_init,
>> +};
>> +
>> +static void allwinner_i2c_register_types(void)
>> +{
>> +    type_register_static(&allwinner_i2c_type_info);
>> +}
>> +
>> +type_init(allwinner_i2c_register_types)
>> diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build
>> index d3df273251..7de7f2f540 100644
>> --- a/hw/i2c/meson.build
>> +++ b/hw/i2c/meson.build
>> @@ -8,6 +8,7 @@ i2c_ss.add(when: 'CONFIG_BITBANG_I2C', if_true: files('bitbang_i2c.c'))
>>  i2c_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_i2c.c'))
>>  i2c_ss.add(when: 'CONFIG_IMX_I2C', if_true: files('imx_i2c.c'))
>>  i2c_ss.add(when: 'CONFIG_MPC_I2C', if_true: files('mpc_i2c.c'))
>> +i2c_ss.add(when: 'CONFIG_ALLWINNER_I2C', if_true: files('allwinner-i2c.c'))
>>  i2c_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('microbit_i2c.c'))
>>  i2c_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_smbus.c'))
>>  i2c_ss.add(when: 'CONFIG_SMBUS_EEPROM', if_true: files('smbus_eeprom.c'))
>> diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h
>> index abe4ff7066..763935fca9 100644
>> --- a/include/hw/arm/allwinner-a10.h
>> +++ b/include/hw/arm/allwinner-a10.h
>> @@ -14,6 +14,7 @@
>>  #include "hw/rtc/allwinner-rtc.h"
>>  #include "hw/misc/allwinner-a10-ccm.h"
>>  #include "hw/misc/allwinner-a10-dramc.h"
>> +#include "hw/i2c/allwinner-i2c.h"
>>
>>  #include "target/arm/cpu.h"
>>  #include "qom/object.h"
>> @@ -39,6 +40,7 @@ struct AwA10State {
>>      AwEmacState emac;
>>      AllwinnerAHCIState sata;
>>      AwSdHostState mmc0;
>> +    AWI2CState i2c0;
>>      AwRtcState rtc;
>>      MemoryRegion sram_a;
>>      EHCISysBusState ehci[AW_A10_NUM_USB];
>> diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
>> index 63025fb27c..1d7ce20589 100644
>> --- a/include/hw/arm/allwinner-h3.h
>> +++ b/include/hw/arm/allwinner-h3.h
>> @@ -47,6 +47,7 @@
>>  #include "hw/sd/allwinner-sdhost.h"
>>  #include "hw/net/allwinner-sun8i-emac.h"
>>  #include "hw/rtc/allwinner-rtc.h"
>> +#include "hw/i2c/allwinner-i2c.h"
>>  #include "target/arm/cpu.h"
>>  #include "sysemu/block-backend.h"
>>
>> @@ -82,6 +83,7 @@ enum {
>>      AW_H3_DEV_UART2,
>>      AW_H3_DEV_UART3,
>>      AW_H3_DEV_EMAC,
>> +    AW_H3_DEV_TWI0,
>>      AW_H3_DEV_DRAMCOM,
>>      AW_H3_DEV_DRAMCTL,
>>      AW_H3_DEV_DRAMPHY,
>> @@ -130,6 +132,7 @@ struct AwH3State {
>>      AwH3SysCtrlState sysctrl;
>>      AwSidState sid;
>>      AwSdHostState mmc0;
>> +    AWI2CState i2c0;
>>      AwSun8iEmacState emac;
>>      AwRtcState rtc;
>>      GICState gic;
>> diff --git a/include/hw/i2c/allwinner-i2c.h b/include/hw/i2c/allwinner-i2c.h
>> new file mode 100644
>> index 0000000000..1e3169a5b9
>> --- /dev/null
>> +++ b/include/hw/i2c/allwinner-i2c.h
>> @@ -0,0 +1,112 @@
>> +/*
>> + *  Allwinner I2C Bus Serial Interface registers definition
>> + *
>> + *  Copyright (C) 2022 Strahinja Jankovic. <strahinja.p.jankovic@gmail.com>
>> + *
>> + *  This file is derived from IMX I2C controller,
>> + *  by Jean-Christophe DUBOIS .
>> + *
>> + *  This program is free software; you can redistribute it and/or modify it
>> + *  under the terms of the GNU General Public License as published by the
>> + *  Free Software Foundation; either version 2 of the License, or
>> + *  (at your option) any later version.
>> + *
>> + *  This program is distributed in the hope that it will be useful, but WITHOUT
>> + *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
>> + *  for more details.
>> + *
>> + *  You should have received a copy of the GNU General Public License along
>> + *  with this program; if not, see <http://www.gnu.org/licenses/>.
>> + *
>> + */
>> +
>> +#ifndef ALLWINNER_I2C_H
>> +#define ALLWINNER_I2C_H
>> +
>> +#include "hw/sysbus.h"
>> +#include "qom/object.h"
>> +
>> +#define TYPE_AW_I2C "allwinner.i2c"
>> +OBJECT_DECLARE_SIMPLE_TYPE(AWI2CState, AW_I2C)
>> +
>> +#define AW_I2C_MEM_SIZE         0x24
>> +
>> +/* Allwinner I2C memory map */
>> +#define TWI_ADDR_REG            0x00  /* slave address register */
>> +#define TWI_XADDR_REG           0x04  /* extended slave address register */
>> +#define TWI_DATA_REG            0x08  /* data register */
>> +#define TWI_CNTR_REG            0x0c  /* control register */
>> +#define TWI_STAT_REG            0x10  /* status register */
>> +#define TWI_CCR_REG             0x14  /* clock control register */
>> +#define TWI_SRST_REG            0x18  /* software reset register */
>> +#define TWI_EFR_REG             0x1c  /* enhance feature register */
>> +#define TWI_LCR_REG             0x20  /* line control register */
>> +
>
> If no other modules except for the new driver need these defines, it may be a good idea to move them to the .c file instead.
> Doing so helps avoiding accidental or unintentional use of the defines. Same for the other flags below.

That makes sense, I will move defines that are used only in this .c file to it.

Best regards,
Strahinja

>
>>
>> +/* Used only in slave mode, do not set */
>> +#define TWI_ADDR_RESET          0
>> +#define TWI_XADDR_RESET         0
>> +
>> +/* Data register */
>> +#define TWI_DATA_MASK           0xFF
>> +#define TWI_DATA_RESET          0
>> +
>> +/* Control register */
>> +#define TWI_CNTR_INT_EN         (1 << 7)
>> +#define TWI_CNTR_BUS_EN         (1 << 6)
>> +#define TWI_CNTR_M_STA          (1 << 5)
>> +#define TWI_CNTR_M_STP          (1 << 4)
>> +#define TWI_CNTR_INT_FLAG       (1 << 3)
>> +#define TWI_CNTR_A_ACK          (1 << 2)
>> +#define TWI_CNTR_MASK           0xFC
>> +#define TWI_CNTR_RESET          0
>> +
>> +/* Status register */
>> +#define TWI_STAT_MASK           0xF8
>> +#define TWI_STAT_RESET          0xF8
>> +
>> +/* Clock register */
>> +#define TWI_CCR_CLK_M_MASK      0x78
>> +#define TWI_CCR_CLK_N_MASK      0x07
>> +#define TWI_CCR_MASK            0x7F
>> +#define TWI_CCR_RESET           0
>> +
>> +/* Soft reset */
>> +#define TWI_SRST_MASK           0x01
>> +#define TWI_SRST_RESET          0
>> +
>> +/* Enhance feature */
>> +#define TWI_EFR_MASK            0x03
>> +#define TWI_EFR_RESET           0
>> +
>> +/* Line control */
>> +#define TWI_LCR_SCL_STATE       (1 << 5)
>> +#define TWI_LCR_SDA_STATE       (1 << 4)
>> +#define TWI_LCR_SCL_CTL         (1 << 3)
>> +#define TWI_LCR_SCL_CTL_EN      (1 << 2)
>> +#define TWI_LCR_SDA_CTL         (1 << 1)
>> +#define TWI_LCR_SDA_CTL_EN      (1 << 0)
>> +#define TWI_LCR_MASK            0x3F
>> +#define TWI_LCR_RESET           0x3A
>> +
>> +struct AWI2CState {
>> +    /*< private >*/
>> +    SysBusDevice parent_obj;
>> +
>> +    /*< public >*/
>> +    MemoryRegion iomem;
>> +    I2CBus *bus;
>> +    qemu_irq irq;
>> +
>> +    uint8_t addr;
>> +    uint8_t xaddr;
>> +    uint8_t data;
>> +    uint8_t cntr;
>> +    uint8_t stat;
>> +    uint8_t ccr;
>> +    uint8_t srst;
>> +    uint8_t efr;
>> +    uint8_t lcr;
>> +};
>> +
>> +#endif /* ALLWINNER_I2C_H */
>> --
>> 2.30.2
>>
>
> Regards,
> Niek
>
> --
> Niek Linnenbank
>


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 6/6] hw/arm: Allwinner A10 enable SPL load from MMC
  2022-12-07 22:39   ` Niek Linnenbank
@ 2022-12-08 19:22     ` Strahinja Jankovic
  0 siblings, 0 replies; 18+ messages in thread
From: Strahinja Jankovic @ 2022-12-08 19:22 UTC (permalink / raw)
  To: Niek Linnenbank; +Cc: Peter Maydell, Beniamino Galvani, qemu-arm, qemu-devel

On Wed, Dec 7, 2022 at 11:39 PM Niek Linnenbank
<nieklinnenbank@gmail.com> wrote:
>
> Hi Strahinja,
>
>
> On Sun, Dec 4, 2022 at 12:19 AM Strahinja Jankovic <strahinjapjankovic@gmail.com> wrote:
>>
>> This patch enables copying of SPL from MMC if `-kernel` parameter is not
>> passed when starting QEMU. SPL is copied to SRAM_A.
>>
>> The approach is reused from Allwinner H3 implementation.
>>
>> Tested with Armbian and custom Yocto image.
>>
>> Signed-off-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
>> ---
>>  hw/arm/allwinner-a10.c         | 18 ++++++++++++++++++
>>  hw/arm/cubieboard.c            |  5 +++++
>>  include/hw/arm/allwinner-a10.h | 21 +++++++++++++++++++++
>>  3 files changed, 44 insertions(+)
>>
>> diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
>> index 17e439777e..dc1966ff7a 100644
>> --- a/hw/arm/allwinner-a10.c
>> +++ b/hw/arm/allwinner-a10.c
>> @@ -24,7 +24,9 @@
>>  #include "sysemu/sysemu.h"
>>  #include "hw/boards.h"
>>  #include "hw/usb/hcd-ohci.h"
>> +#include "hw/loader.h"
>>
>> +#define AW_A10_SRAM_A_BASE      0x00000000
>>  #define AW_A10_DRAMC_BASE       0x01c01000
>>  #define AW_A10_MMC0_BASE        0x01c0f000
>>  #define AW_A10_CCM_BASE         0x01c20000
>> @@ -38,6 +40,22 @@
>>  #define AW_A10_RTC_BASE         0x01c20d00
>>  #define AW_A10_I2C0_BASE        0x01c2ac00
>>
>> +void allwinner_a10_bootrom_setup(AwA10State *s, BlockBackend *blk)
>> +{
>> +    const int64_t rom_size = 32 * KiB;
>> +    g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size);
>> +
>> +    if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) {
>> +        error_setg(&error_fatal, "%s: failed to read BlockBackend data",
>> +                   __func__);
>> +        return;
>> +    }
>> +
>> +    rom_add_blob("allwinner-a10.bootrom", buffer, rom_size,
>> +                  rom_size, AW_A10_SRAM_A_BASE,
>> +                  NULL, NULL, NULL, NULL, false);
>> +}
>
>
> Its probably fine for now to do it in the same way here for the A10 indeed. Perhaps in the future, we can try
> to share some overlapping code between the A10 and H3.

That definitely makes sense. I plan on submitting support for A20
after this patch set, so maybe that would be a good opportunity to
refactor the Allwinner support in QEMU.

Best regards,
Strahinja


>
> So the patch looks fine to me:
> Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>
>
> Regards,
> Niek
>
>>
>> +
>>  static void aw_a10_init(Object *obj)
>>  {
>>      AwA10State *s = AW_A10(obj);
>> diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c
>> index afc7980414..37659c35fd 100644
>> --- a/hw/arm/cubieboard.c
>> +++ b/hw/arm/cubieboard.c
>> @@ -99,6 +99,11 @@ static void cubieboard_init(MachineState *machine)
>>      memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE,
>>                                  machine->ram);
>>
>> +    /* Load target kernel or start using BootROM */
>> +    if (!machine->kernel_filename && blk && blk_is_available(blk)) {
>> +        /* Use Boot ROM to copy data from SD card to SRAM */
>> +        allwinner_a10_bootrom_setup(a10, blk);
>> +    }
>>      /* TODO create and connect IDE devices for ide_drive_get() */
>>
>>      cubieboard_binfo.ram_size = machine->ram_size;
>> diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h
>> index 763935fca9..b3c9ed24c7 100644
>> --- a/include/hw/arm/allwinner-a10.h
>> +++ b/include/hw/arm/allwinner-a10.h
>> @@ -15,6 +15,7 @@
>>  #include "hw/misc/allwinner-a10-ccm.h"
>>  #include "hw/misc/allwinner-a10-dramc.h"
>>  #include "hw/i2c/allwinner-i2c.h"
>> +#include "sysemu/block-backend.h"
>>
>>  #include "target/arm/cpu.h"
>>  #include "qom/object.h"
>> @@ -47,4 +48,24 @@ struct AwA10State {
>>      OHCISysBusState ohci[AW_A10_NUM_USB];
>>  };
>>
>> +/**
>> + * Emulate Boot ROM firmware setup functionality.
>> + *
>> + * A real Allwinner A10 SoC contains a Boot ROM
>> + * which is the first code that runs right after
>> + * the SoC is powered on. The Boot ROM is responsible
>> + * for loading user code (e.g. a bootloader) from any
>> + * of the supported external devices and writing the
>> + * downloaded code to internal SRAM. After loading the SoC
>> + * begins executing the code written to SRAM.
>> + *
>> + * This function emulates the Boot ROM by copying 32 KiB
>> + * of data from the given block device and writes it to
>> + * the start of the first internal SRAM memory.
>> + *
>> + * @s: Allwinner A10 state object pointer
>> + * @blk: Block backend device object pointer
>> + */
>> +void allwinner_a10_bootrom_setup(AwA10State *s, BlockBackend *blk);
>> +
>>  #endif
>> --
>> 2.30.2
>>
>
>
> --
> Niek Linnenbank
>


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card
  2022-12-07 20:25 ` [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Niek Linnenbank
@ 2022-12-08 19:24   ` Strahinja Jankovic
  2022-12-08 20:25     ` Niek Linnenbank
  0 siblings, 1 reply; 18+ messages in thread
From: Strahinja Jankovic @ 2022-12-08 19:24 UTC (permalink / raw)
  To: Niek Linnenbank; +Cc: Peter Maydell, Beniamino Galvani, qemu-arm, qemu-devel

Hi Niek,

On Wed, Dec 7, 2022 at 9:25 PM Niek Linnenbank <nieklinnenbank@gmail.com> wrote:
>
> Hello Strahinja,
>
> Thanks for contribution these patches, and also taking the H3 into account :-)

Thank you for looking into these patches and all of the comments. I
will try to submit V2 of this patch set in the following days.

>
> I've ran the avocado based acceptance tests for both boards and got these results:
>
> $ ARMBIAN_ARTIFACTS_CACHED=yes AVOCADO_ALLOW_LARGE_STORAGE=yes ./build/tests/venv/bin/avocado --show=app,console run -t machine:orangepi-pc tests/avocado/boot_linux_console.py
> ...
> RESULTS    : PASS 5 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0
> JOB TIME   : 114.24 s
>
> $ ./build/tests/venv/bin/avocado --show=app,console run -t machine:cubieboard tests/avocado/boot_linux_console.py
> ...
> RESULTS    : PASS 2 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0
> JOB TIME   : 22.79 s

I did not think initially about avocado, but maybe I could also add an
SPL/SD boot test for the cubieboard, similarly to the way it is run
for Orange Pi, for V2 of the patch set?

Best regards,
Strahinja



>
> So that shows both machines are still running fine. During startup of the bionic 20.08 image for orangepi-pc it did show this message:
>   console: i2c i2c-0: mv64xxx: I2C bus locked, block: 1, time_left: 0
>   console: sy8106a: probe of 0-0065 failed with error -110
>
> The SY8106a appears to be an peripheral attached to the I2C bus on the orangepi-pc, and we don't emulate the SY8106a yet, so that's an error to be expected:
>   https://linux-sunxi.org/SY8106A
>
> So for the series:
> Tested-by: Niek Linnenbank <nieklinnenbank@gmail.com>
>
> I'll try to reply to each patch as well.
>
> Kind regards,
> Niek
>
> On Sun, Dec 4, 2022 at 12:19 AM Strahinja Jankovic <strahinjapjankovic@gmail.com> wrote:
>>
>> This patch series adds missing Allwinner A10 modules needed for
>> successful SPL boot:
>> - Clock controller module
>> - DRAM controller
>> - I2C0 controller (added also for Allwinner H3 since it is the same)
>> - AXP-209 connected to I2C0 bus
>>
>> It also updates Allwinner A10 emulation so SPL is copied from attached
>> SD card if `-kernel` parameter is not passed when starting QEMU
>> (approach adapted from Allwinner H3 implementation).
>>
>> Boot from SD card has been tested with Cubieboard Armbian SD card image and custom
>> Yocto image built for Cubieboard.
>> Example usage for Armbian image:
>> qemu-system-arm -M cubieboard -nographic -sd ~/Armbian_22.11.0-trunk_Cubieboard_kinetic_edge_6.0.7.img
>>
>>
>> Strahinja Jankovic (6):
>>   hw/misc: Allwinner-A10 Clock Controller Module Emulation
>>   hw/misc: Allwinner A10 DRAM Controller Emulation
>>   hw/i2c: Allwinner TWI/I2C Emulation
>>   hw/misc: Allwinner AXP-209 Emulation
>>   hw/arm: Add AXP-209 to Cubieboard
>>   hw/arm: Allwinner A10 enable SPL load from MMC
>>
>>  hw/arm/Kconfig                        |   5 +
>>  hw/arm/allwinner-a10.c                |  40 +++
>>  hw/arm/allwinner-h3.c                 |  11 +-
>>  hw/arm/cubieboard.c                   |  11 +
>>  hw/i2c/Kconfig                        |   4 +
>>  hw/i2c/allwinner-i2c.c                | 417 ++++++++++++++++++++++++++
>>  hw/i2c/meson.build                    |   1 +
>>  hw/misc/Kconfig                       |  10 +
>>  hw/misc/allwinner-a10-ccm.c           | 224 ++++++++++++++
>>  hw/misc/allwinner-a10-dramc.c         | 179 +++++++++++
>>  hw/misc/allwinner-axp-209.c           | 263 ++++++++++++++++
>>  hw/misc/meson.build                   |   3 +
>>  include/hw/arm/allwinner-a10.h        |  27 ++
>>  include/hw/arm/allwinner-h3.h         |   3 +
>>  include/hw/i2c/allwinner-i2c.h        | 112 +++++++
>>  include/hw/misc/allwinner-a10-ccm.h   |  67 +++++
>>  include/hw/misc/allwinner-a10-dramc.h |  68 +++++
>>  17 files changed, 1444 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/i2c/allwinner-i2c.c
>>  create mode 100644 hw/misc/allwinner-a10-ccm.c
>>  create mode 100644 hw/misc/allwinner-a10-dramc.c
>>  create mode 100644 hw/misc/allwinner-axp-209.c
>>  create mode 100644 include/hw/i2c/allwinner-i2c.h
>>  create mode 100644 include/hw/misc/allwinner-a10-ccm.h
>>  create mode 100644 include/hw/misc/allwinner-a10-dramc.h
>>
>> --
>> 2.30.2
>>
>
>
> --
> Niek Linnenbank
>


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card
  2022-12-08 19:24   ` Strahinja Jankovic
@ 2022-12-08 20:25     ` Niek Linnenbank
  0 siblings, 0 replies; 18+ messages in thread
From: Niek Linnenbank @ 2022-12-08 20:25 UTC (permalink / raw)
  To: Strahinja Jankovic; +Cc: Peter Maydell, Beniamino Galvani, qemu-arm, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 5219 bytes --]

Hi Strahinja,

On Thu, Dec 8, 2022 at 8:24 PM Strahinja Jankovic <
strahinjapjankovic@gmail.com> wrote:

> Hi Niek,
>
> On Wed, Dec 7, 2022 at 9:25 PM Niek Linnenbank <nieklinnenbank@gmail.com>
> wrote:
> >
> > Hello Strahinja,
> >
> > Thanks for contribution these patches, and also taking the H3 into
> account :-)
>
> Thank you for looking into these patches and all of the comments. I
> will try to submit V2 of this patch set in the following days.
>
> >
> > I've ran the avocado based acceptance tests for both boards and got
> these results:
> >
> > $ ARMBIAN_ARTIFACTS_CACHED=yes AVOCADO_ALLOW_LARGE_STORAGE=yes
> ./build/tests/venv/bin/avocado --show=app,console run -t
> machine:orangepi-pc tests/avocado/boot_linux_console.py
> > ...
> > RESULTS    : PASS 5 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 |
> CANCEL 0
> > JOB TIME   : 114.24 s
> >
> > $ ./build/tests/venv/bin/avocado --show=app,console run -t
> machine:cubieboard tests/avocado/boot_linux_console.py
> > ...
> > RESULTS    : PASS 2 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 |
> CANCEL 0
> > JOB TIME   : 22.79 s
>
> I did not think initially about avocado, but maybe I could also add an
> SPL/SD boot test for the cubieboard, similarly to the way it is run
> for Orange Pi, for V2 of the patch set?
>

Yeah that would be great. It can help to make testing easier when working
on your current code, since its all automated.
And when covering the SPL/SD boot with an acceptance test, it also helps to
ensure it keeps working with future updates to Qemu code too.

One thing to be aware of is to select an image with an URL that is stable.
Once a new test is merged, and the image is subsequently deleted
from the remote server, the test can't run anymore. We've had such a
problem before with the orangepi-pc tests.

Regards,
Niek


>
> Best regards,
> Strahinja
>
>
>
> >
> > So that shows both machines are still running fine. During startup of
> the bionic 20.08 image for orangepi-pc it did show this message:
> >   console: i2c i2c-0: mv64xxx: I2C bus locked, block: 1, time_left: 0
> >   console: sy8106a: probe of 0-0065 failed with error -110
> >
> > The SY8106a appears to be an peripheral attached to the I2C bus on the
> orangepi-pc, and we don't emulate the SY8106a yet, so that's an error to be
> expected:
> >   https://linux-sunxi.org/SY8106A
> >
> > So for the series:
> > Tested-by: Niek Linnenbank <nieklinnenbank@gmail.com>
> >
> > I'll try to reply to each patch as well.
> >
> > Kind regards,
> > Niek
> >
> > On Sun, Dec 4, 2022 at 12:19 AM Strahinja Jankovic <
> strahinjapjankovic@gmail.com> wrote:
> >>
> >> This patch series adds missing Allwinner A10 modules needed for
> >> successful SPL boot:
> >> - Clock controller module
> >> - DRAM controller
> >> - I2C0 controller (added also for Allwinner H3 since it is the same)
> >> - AXP-209 connected to I2C0 bus
> >>
> >> It also updates Allwinner A10 emulation so SPL is copied from attached
> >> SD card if `-kernel` parameter is not passed when starting QEMU
> >> (approach adapted from Allwinner H3 implementation).
> >>
> >> Boot from SD card has been tested with Cubieboard Armbian SD card image
> and custom
> >> Yocto image built for Cubieboard.
> >> Example usage for Armbian image:
> >> qemu-system-arm -M cubieboard -nographic -sd
> ~/Armbian_22.11.0-trunk_Cubieboard_kinetic_edge_6.0.7.img
> >>
> >>
> >> Strahinja Jankovic (6):
> >>   hw/misc: Allwinner-A10 Clock Controller Module Emulation
> >>   hw/misc: Allwinner A10 DRAM Controller Emulation
> >>   hw/i2c: Allwinner TWI/I2C Emulation
> >>   hw/misc: Allwinner AXP-209 Emulation
> >>   hw/arm: Add AXP-209 to Cubieboard
> >>   hw/arm: Allwinner A10 enable SPL load from MMC
> >>
> >>  hw/arm/Kconfig                        |   5 +
> >>  hw/arm/allwinner-a10.c                |  40 +++
> >>  hw/arm/allwinner-h3.c                 |  11 +-
> >>  hw/arm/cubieboard.c                   |  11 +
> >>  hw/i2c/Kconfig                        |   4 +
> >>  hw/i2c/allwinner-i2c.c                | 417 ++++++++++++++++++++++++++
> >>  hw/i2c/meson.build                    |   1 +
> >>  hw/misc/Kconfig                       |  10 +
> >>  hw/misc/allwinner-a10-ccm.c           | 224 ++++++++++++++
> >>  hw/misc/allwinner-a10-dramc.c         | 179 +++++++++++
> >>  hw/misc/allwinner-axp-209.c           | 263 ++++++++++++++++
> >>  hw/misc/meson.build                   |   3 +
> >>  include/hw/arm/allwinner-a10.h        |  27 ++
> >>  include/hw/arm/allwinner-h3.h         |   3 +
> >>  include/hw/i2c/allwinner-i2c.h        | 112 +++++++
> >>  include/hw/misc/allwinner-a10-ccm.h   |  67 +++++
> >>  include/hw/misc/allwinner-a10-dramc.h |  68 +++++
> >>  17 files changed, 1444 insertions(+), 1 deletion(-)
> >>  create mode 100644 hw/i2c/allwinner-i2c.c
> >>  create mode 100644 hw/misc/allwinner-a10-ccm.c
> >>  create mode 100644 hw/misc/allwinner-a10-dramc.c
> >>  create mode 100644 hw/misc/allwinner-axp-209.c
> >>  create mode 100644 include/hw/i2c/allwinner-i2c.h
> >>  create mode 100644 include/hw/misc/allwinner-a10-ccm.h
> >>  create mode 100644 include/hw/misc/allwinner-a10-dramc.h
> >>
> >> --
> >> 2.30.2
> >>
> >
> >
> > --
> > Niek Linnenbank
> >
>


-- 
Niek Linnenbank

[-- Attachment #2: Type: text/html, Size: 7142 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2022-12-08 20:26 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-03 23:18 [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Strahinja Jankovic
2022-12-03 23:18 ` [PATCH 1/6] hw/misc: Allwinner-A10 Clock Controller Module Emulation Strahinja Jankovic
2022-12-07 22:31   ` Niek Linnenbank
2022-12-03 23:19 ` [PATCH 2/6] hw/misc: Allwinner A10 DRAM Controller Emulation Strahinja Jankovic
2022-12-07 22:26   ` Niek Linnenbank
2022-12-03 23:19 ` [PATCH 3/6] hw/i2c: Allwinner TWI/I2C Emulation Strahinja Jankovic
2022-12-07 22:06   ` Niek Linnenbank
2022-12-08 19:19     ` Strahinja Jankovic
2022-12-03 23:19 ` [PATCH 4/6] hw/misc: Allwinner AXP-209 Emulation Strahinja Jankovic
2022-12-04 21:39   ` Philippe Mathieu-Daudé
2022-12-05 21:07     ` Strahinja Jankovic
2022-12-03 23:19 ` [PATCH 5/6] hw/arm: Add AXP-209 to Cubieboard Strahinja Jankovic
2022-12-03 23:19 ` [PATCH 6/6] hw/arm: Allwinner A10 enable SPL load from MMC Strahinja Jankovic
2022-12-07 22:39   ` Niek Linnenbank
2022-12-08 19:22     ` Strahinja Jankovic
2022-12-07 20:25 ` [PATCH 0/6] Enable Cubieboard A10 boot SPL from SD card Niek Linnenbank
2022-12-08 19:24   ` Strahinja Jankovic
2022-12-08 20:25     ` Niek Linnenbank

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.