Merge tag 'pull-target-arm-20230606' of https://git.linaro.org/people/pmaydell/qemu-arm into staging

target-arm queue:
 * Support gdbstub (guest debug) in HVF
 * xnlx-versal: Support CANFD controller
 * bpim2u: New board model: Banana Pi BPI-M2 Ultra
 * Emulate FEAT_LSE2
 * allow DC CVA[D]P in user mode emulation
 * trap DCC access in user mode emulation

# -----BEGIN PGP SIGNATURE-----
#
# iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmR/AKUZHHBldGVyLm1h
# eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3jzIEACNepQGY44yPhrEG+wD4WAB
# fH670KI33HcsFd2rGsC369gcssQbRIW/29reOzNhRMuol+kHI6OFaONpuKSdO0Rz
# TLVIsnT2Uq8KwbYfLtDQt5knj027amPy75d4re8wIK1eZB4dOIHysqAvQrJYeync
# 9obKku8xXGLwZh/mYHoVgHcZU0cPJO9nri39n1tV3JUBsgmqEURjzbZrMcF+yMX7
# bUzOYQvC1Iedmo+aWfx43u82AlNQFz1lsqmnQj7Z5rvv0HT+BRF5WzVMP0qRh5+Z
# njkqmBH9xb9kkgeHmeMvHpWox+J+obeSmVg/4gDNlJpThmpuU0Vr7EXUN3MBQlV9
# lhyy6zrTwC/BToiQqdT2dnpao9FzXy5exfnqi/py5IuqfjAzSO+p61LlPPZ4cJri
# pCK4yq2gzQXYfrlZkUJipvRMH8Xa4IdQx+w7lXrQoJdduF4/+6aJW/GAWSu0e7eC
# zgBwaJjI7ENce8ixJnuEFUxUnaBo8dl72a0PGA1UU8PL+cJNOIpyhPk4goWQprdn
# iFF4ZnjhBRZ2gk/4HGD9u5Vo2lNqP93YS5QhkGkF+HJsBmcOZgidIUpfHhPQvvHO
# Np196T2cAETCWGV1xG4CaTpxN2ndRReq3C0/mzfhIbwhXEACtvAiSlO4KB8t6pJj
# MzinCABXHcovJbGbxZ9j6w==
# =8SdN
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 06 Jun 2023 02:47:17 AM PDT
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [full]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [full]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [full]
# gpg:                 aka "Peter Maydell <peter@archaic.org.uk>" [unknown]

* tag 'pull-target-arm-20230606' of https://git.linaro.org/people/pmaydell/qemu-arm: (42 commits)
  target/arm: trap DCC access in user mode emulation
  tests/tcg/aarch64: add DC CVA[D]P tests
  target/arm: allow DC CVA[D]P in user mode emulation
  target/arm: Enable FEAT_LSE2 for -cpu max
  tests/tcg/multiarch: Adjust sigbus.c
  tests/tcg/aarch64: Use stz2g in mte-7.c
  target/arm: Move mte check for store-exclusive
  target/arm: Relax ordered/atomic alignment checks for LSE2
  target/arm: Add SCTLR.nAA to TBFLAG_A64
  target/arm: Check alignment in helper_mte_check
  target/arm: Pass single_memop to gen_mte_checkN
  target/arm: Pass memop to gen_mte_check1*
  target/arm: Hoist finalize_memop out of do_fp_{ld, st}
  target/arm: Hoist finalize_memop out of do_gpr_{ld, st}
  target/arm: Load/store integer pair with one tcg operation
  target/arm: Sink gen_mte_check1 into load/store_exclusive
  target/arm: Use tcg_gen_qemu_{ld, st}_i128 in gen_sve_{ld, st}r
  target/arm: Use tcg_gen_qemu_st_i128 for STZG, STZ2G
  target/arm: Use tcg_gen_qemu_{st, ld}_i128 for do_fp_{st, ld}
  target/arm: Use tcg_gen_qemu_ld_i128 for LDXP
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson
2023-06-06 12:11:34 -07:00
63 changed files with 7386 additions and 733 deletions
+1 -1
View File
@@ -1819,7 +1819,7 @@ M: Francisco Iglesias <francisco.iglesias@amd.com>
S: Maintained
F: hw/net/can/xlnx-*
F: include/hw/net/xlnx-*
F: tests/qtest/xlnx-can-test*
F: tests/qtest/xlnx-can*-test*
EDU
M: Jiri Slaby <jslaby@suse.cz>
+119
View File
@@ -52,6 +52,7 @@
#include "qemu/main-loop.h"
#include "exec/address-spaces.h"
#include "exec/exec-all.h"
#include "exec/gdbstub.h"
#include "sysemu/cpus.h"
#include "sysemu/hvf.h"
#include "sysemu/hvf_int.h"
@@ -334,18 +335,26 @@ static int hvf_accel_init(MachineState *ms)
s->slots[x].slot_id = x;
}
QTAILQ_INIT(&s->hvf_sw_breakpoints);
hvf_state = s;
memory_listener_register(&hvf_memory_listener, &address_space_memory);
return hvf_arch_init();
}
static inline int hvf_gdbstub_sstep_flags(void)
{
return SSTEP_ENABLE | SSTEP_NOIRQ;
}
static void hvf_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "HVF";
ac->init_machine = hvf_accel_init;
ac->allowed = &hvf_allowed;
ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags;
}
static const TypeInfo hvf_accel_type = {
@@ -395,6 +404,8 @@ static int hvf_init_vcpu(CPUState *cpu)
cpu->vcpu_dirty = 1;
assert_hvf_ok(r);
cpu->hvf->guest_debug_enabled = false;
return hvf_arch_init_vcpu(cpu);
}
@@ -462,6 +473,108 @@ static void hvf_start_vcpu_thread(CPUState *cpu)
cpu, QEMU_THREAD_JOINABLE);
}
static int hvf_insert_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len)
{
struct hvf_sw_breakpoint *bp;
int err;
if (type == GDB_BREAKPOINT_SW) {
bp = hvf_find_sw_breakpoint(cpu, addr);
if (bp) {
bp->use_count++;
return 0;
}
bp = g_new(struct hvf_sw_breakpoint, 1);
bp->pc = addr;
bp->use_count = 1;
err = hvf_arch_insert_sw_breakpoint(cpu, bp);
if (err) {
g_free(bp);
return err;
}
QTAILQ_INSERT_HEAD(&hvf_state->hvf_sw_breakpoints, bp, entry);
} else {
err = hvf_arch_insert_hw_breakpoint(addr, len, type);
if (err) {
return err;
}
}
CPU_FOREACH(cpu) {
err = hvf_update_guest_debug(cpu);
if (err) {
return err;
}
}
return 0;
}
static int hvf_remove_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len)
{
struct hvf_sw_breakpoint *bp;
int err;
if (type == GDB_BREAKPOINT_SW) {
bp = hvf_find_sw_breakpoint(cpu, addr);
if (!bp) {
return -ENOENT;
}
if (bp->use_count > 1) {
bp->use_count--;
return 0;
}
err = hvf_arch_remove_sw_breakpoint(cpu, bp);
if (err) {
return err;
}
QTAILQ_REMOVE(&hvf_state->hvf_sw_breakpoints, bp, entry);
g_free(bp);
} else {
err = hvf_arch_remove_hw_breakpoint(addr, len, type);
if (err) {
return err;
}
}
CPU_FOREACH(cpu) {
err = hvf_update_guest_debug(cpu);
if (err) {
return err;
}
}
return 0;
}
static void hvf_remove_all_breakpoints(CPUState *cpu)
{
struct hvf_sw_breakpoint *bp, *next;
CPUState *tmpcpu;
QTAILQ_FOREACH_SAFE(bp, &hvf_state->hvf_sw_breakpoints, entry, next) {
if (hvf_arch_remove_sw_breakpoint(cpu, bp) != 0) {
/* Try harder to find a CPU that currently sees the breakpoint. */
CPU_FOREACH(tmpcpu)
{
if (hvf_arch_remove_sw_breakpoint(tmpcpu, bp) == 0) {
break;
}
}
}
QTAILQ_REMOVE(&hvf_state->hvf_sw_breakpoints, bp, entry);
g_free(bp);
}
hvf_arch_remove_all_hw_breakpoints();
CPU_FOREACH(cpu) {
hvf_update_guest_debug(cpu);
}
}
static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
{
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
@@ -473,6 +586,12 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
ops->synchronize_post_init = hvf_cpu_synchronize_post_init;
ops->synchronize_state = hvf_cpu_synchronize_state;
ops->synchronize_pre_loadvm = hvf_cpu_synchronize_pre_loadvm;
ops->insert_breakpoint = hvf_insert_breakpoint;
ops->remove_breakpoint = hvf_remove_breakpoint;
ops->remove_all_breakpoints = hvf_remove_all_breakpoints;
ops->update_guest_debug = hvf_update_guest_debug;
ops->supports_guest_debug = hvf_arch_supports_guest_debug;
};
static const TypeInfo hvf_accel_ops_type = {
.name = ACCEL_OPS_NAME("hvf"),
+23
View File
@@ -44,3 +44,26 @@ void assert_hvf_ok(hv_return_t ret)
abort();
}
struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, target_ulong pc)
{
struct hvf_sw_breakpoint *bp;
QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) {
if (bp->pc == pc) {
return bp;
}
}
return NULL;
}
int hvf_sw_breakpoints_active(CPUState *cpu)
{
return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints);
}
int hvf_update_guest_debug(CPUState *cpu)
{
hvf_arch_update_guest_debug(cpu);
return 0;
}
+139
View File
@@ -0,0 +1,139 @@
Banana Pi BPI-M2U (``bpim2u``)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Banana Pi BPI-M2 Ultra is a quad-core mini single board computer built with
Allwinner A40i/R40/V40 SoC. It features 2GB of RAM and 8GB eMMC. It also
has onboard WiFi and BT. On the ports side, the BPI-M2 Ultra has 2 USB A
2.0 ports, 1 USB OTG port, 1 HDMI port, 1 audio jack, a DC power port,
and last but not least, a SATA port.
Supported devices
"""""""""""""""""
The Banana Pi M2U machine supports the following devices:
* SMP (Quad Core Cortex-A7)
* Generic Interrupt Controller configuration
* SRAM mappings
* SDRAM controller
* Timer device (re-used from Allwinner A10)
* UART
* SD/MMC storage controller
* EMAC ethernet
* GMAC ethernet
* Clock Control Unit
* TWI (I2C)
Limitations
"""""""""""
Currently, Banana Pi M2U does *not* support the following features:
- Graphical output via HDMI, GPU and/or the Display Engine
- Audio output
- Hardware Watchdog
- Real Time Clock
- USB 2.0 interfaces
Also see the 'unimplemented' array in the Allwinner R40 SoC module
for a complete list of unimplemented I/O devices: ``./hw/arm/allwinner-r40.c``
Boot options
""""""""""""
The Banana Pi M2U machine can start using the standard -kernel functionality
for loading a Linux kernel or ELF executable. Additionally, the Banana Pi M2U
machine can also emulate the BootROM which is present on an actual Allwinner R40
based SoC, which loads the bootloader from a SD card, specified via the -sd
argument to qemu-system-arm.
Running mainline Linux
""""""""""""""""""""""
To build a Linux mainline kernel that can be booted by the Banana Pi M2U machine,
simply configure the kernel using the sunxi_defconfig configuration:
.. code-block:: bash
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make mrproper
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make sunxi_defconfig
To boot the newly build linux kernel in QEMU with the Banana Pi M2U machine, use:
.. code-block:: bash
$ qemu-system-arm -M bpim2u -nographic \
-kernel /path/to/linux/arch/arm/boot/zImage \
-append 'console=ttyS0,115200' \
-dtb /path/to/linux/arch/arm/boot/dts/sun8i-r40-bananapi-m2-ultra.dtb
Banana Pi M2U images
""""""""""""""""""""
Note that the mainline kernel does not have a root filesystem. You can choose
to build you own image with buildroot using the bananapi_m2_ultra_defconfig.
Also see https://buildroot.org for more information.
Another possibility is to run an OpenWrt image for Banana Pi M2U which
can be downloaded from:
https://downloads.openwrt.org/releases/22.03.3/targets/sunxi/cortexa7/
When using an image as an SD card, it must be resized to a power of two. This can be
done with the ``qemu-img`` command. It is recommended to only increase the image size
instead of shrinking it to a power of two, to avoid loss of data. For example,
to prepare a downloaded Armbian image, first extract it and then increase
its size to one gigabyte as follows:
.. code-block:: bash
$ qemu-img resize \
openwrt-22.03.3-sunxi-cortexa7-sinovoip_bananapi-m2-ultra-ext4-sdcard.img \
1G
Instead of providing a custom Linux kernel via the -kernel command you may also
choose to let the Banana Pi M2U machine load the bootloader from SD card, just like
a real board would do using the BootROM. Simply pass the selected image via the -sd
argument and remove the -kernel, -append, -dbt and -initrd arguments:
.. code-block:: bash
$ qemu-system-arm -M bpim2u -nic user -nographic \
-sd openwrt-22.03.3-sunxi-cortexa7-sinovoip_bananapi-m2-ultra-ext4-sdcard.img
Running U-Boot
""""""""""""""
U-Boot mainline can be build and configured using the Bananapi_M2_Ultra_defconfig
using similar commands as describe above for Linux. Note that it is recommended
for development/testing to select the following configuration setting in U-Boot:
Device Tree Control > Provider for DTB for DT Control > Embedded DTB
The BootROM of allwinner R40 loading u-boot from the 8KiB offset of sdcard.
Let's create an bootable disk image:
.. code-block:: bash
$ dd if=/dev/zero of=sd.img bs=32M count=1
$ dd if=u-boot-sunxi-with-spl.bin of=sd.img bs=1k seek=8 conv=notrunc
And then boot it.
.. code-block:: bash
$ qemu-system-arm -M bpim2u -nographic -sd sd.img
Banana Pi M2U integration tests
"""""""""""""""""""""""""""""""
The Banana Pi M2U machine has several integration tests included.
To run the whole set of tests, build QEMU from source and simply
provide the following command:
.. code-block:: bash
$ cd qemu-build-dir
$ AVOCADO_ALLOW_LARGE_STORAGE=yes tests/venv/bin/avocado \
--verbose --show=app,console run -t machine:bpim2u \
../tests/avocado/boot_linux_console.py
+1
View File
@@ -50,6 +50,7 @@ the following architecture extensions:
- FEAT_LRCPC (Load-acquire RCpc instructions)
- FEAT_LRCPC2 (Load-acquire RCpc instructions v2)
- FEAT_LSE (Large System Extensions)
- FEAT_LSE2 (Large System Extensions v2)
- FEAT_LVA (Large Virtual Address space)
- FEAT_MTE (Memory Tagging Extension)
- FEAT_MTE2 (Memory Tagging Extension)
+31
View File
@@ -34,6 +34,7 @@ Implemented devices:
- DDR memory
- BBRAM (36 bytes of Battery-backed RAM)
- eFUSE (3072 bytes of one-time field-programmable bit array)
- 2 CANFDs
QEMU does not yet model any other devices, including the PL and the AI Engine.
@@ -224,3 +225,33 @@ To use a different index value, N, from default of 1, add:
Better yet, do not use actual product data when running guest image
on this Xilinx Versal Virt board.
Using CANFDs for Versal Virt
""""""""""""""""""""""""""""
Versal CANFD controller is developed based on SocketCAN and QEMU CAN bus
implementation. Bus connection and socketCAN connection for each CAN module
can be set through command lines.
To connect both CANFD0 and CANFD1 on the same bus:
.. code-block:: bash
-object can-bus,id=canbus -machine canbus0=canbus -machine canbus1=canbus
To connect CANFD0 and CANFD1 to separate buses:
.. code-block:: bash
-object can-bus,id=canbus0 -object can-bus,id=canbus1 \
-machine canbus0=canbus0 -machine canbus1=canbus1
The SocketCAN interface can connect to a Physical or a Virtual CAN interfaces on
the host machine. Please check this document to learn about CAN interface on
Linux: docs/system/devices/can.rst
To connect CANFD0 and CANFD1 to host machine's CAN interface can0:
.. code-block:: bash
-object can-bus,id=canbus -machine canbus0=canbus -machine canbus1=canbus
-object can-host-socketcan,id=canhost0,if=can0,canbus=canbus
+1
View File
@@ -83,6 +83,7 @@ undocumented; you can get a complete list by running
arm/versatile
arm/vexpress
arm/aspeed
arm/bananapi_m2u.rst
arm/sabrelite
arm/digic
arm/cubieboard
+13 -1
View File
@@ -383,7 +383,7 @@ config ALLWINNER_A10
select ALLWINNER_WDT
select ALLWINNER_EMAC
select ALLWINNER_I2C
select AXP209_PMU
select AXP2XX_PMU
select SERIAL
select UNIMP
@@ -403,6 +403,18 @@ config ALLWINNER_H3
select USB_EHCI_SYSBUS
select SD
config ALLWINNER_R40
bool
default y if TCG && ARM
select ALLWINNER_SRAMC
select ALLWINNER_A10_PIT
select AXP2XX_PMU
select SERIAL
select ARM_TIMER
select ARM_GIC
select UNIMP
select SD
config RASPI
bool
default y
File diff suppressed because it is too large Load Diff
+145
View File
@@ -0,0 +1,145 @@
/*
* Bananapi M2U emulation
*
* Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
*
* 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 "exec/address-spaces.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "hw/boards.h"
#include "hw/i2c/i2c.h"
#include "hw/qdev-properties.h"
#include "hw/arm/allwinner-r40.h"
static struct arm_boot_info bpim2u_binfo;
/*
* R40 can boot from mmc0 and mmc2, and bpim2u has two mmc interface, one is
* connected to sdcard and another mount an emmc media.
* Attach the mmc driver and try loading bootloader.
*/
static void mmc_attach_drive(AwR40State *s, AwSdHostState *mmc, int unit,
bool load_bootroom, bool *bootroom_loaded)
{
DriveInfo *di = drive_get(IF_SD, 0, unit);
BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL;
BusState *bus;
DeviceState *carddev;
bus = qdev_get_child_bus(DEVICE(mmc), "sd-bus");
if (bus == NULL) {
error_report("No SD bus found in SOC object");
exit(1);
}
carddev = qdev_new(TYPE_SD_CARD);
qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal);
qdev_realize_and_unref(carddev, bus, &error_fatal);
if (load_bootroom && blk && blk_is_available(blk)) {
/* Use Boot ROM to copy data from SD card to SRAM */
*bootroom_loaded = allwinner_r40_bootrom_setup(s, blk, unit);
}
}
static void bpim2u_init(MachineState *machine)
{
bool bootroom_loaded = false;
AwR40State *r40;
I2CBus *i2c;
/* BIOS is not supported by this board */
if (machine->firmware) {
error_report("BIOS not supported for this machine");
exit(1);
}
/* Only allow Cortex-A7 for this board */
if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a7")) != 0) {
error_report("This board can only be used with cortex-a7 CPU");
exit(1);
}
r40 = AW_R40(object_new(TYPE_AW_R40));
object_property_add_child(OBJECT(machine), "soc", OBJECT(r40));
object_unref(OBJECT(r40));
/* Setup timer properties */
object_property_set_int(OBJECT(r40), "clk0-freq", 32768, &error_abort);
object_property_set_int(OBJECT(r40), "clk1-freq", 24 * 1000 * 1000,
&error_abort);
/* DRAMC */
r40->ram_size = machine->ram_size / MiB;
object_property_set_uint(OBJECT(r40), "ram-addr",
r40->memmap[AW_R40_DEV_SDRAM], &error_abort);
object_property_set_int(OBJECT(r40), "ram-size",
r40->ram_size, &error_abort);
/* GMAC PHY */
object_property_set_uint(OBJECT(r40), "gmac-phy-addr", 1, &error_abort);
/* Mark R40 object realized */
qdev_realize(DEVICE(r40), NULL, &error_abort);
/*
* Plug in SD card and try load bootrom, R40 has 4 mmc controllers but can
* only booting from mmc0 and mmc2.
*/
for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
switch (i) {
case 0:
case 2:
mmc_attach_drive(r40, &r40->mmc[i], i,
!machine->kernel_filename && !bootroom_loaded,
&bootroom_loaded);
break;
default:
mmc_attach_drive(r40, &r40->mmc[i], i, false, NULL);
break;
}
}
/* Connect AXP221 */
i2c = I2C_BUS(qdev_get_child_bus(DEVICE(&r40->i2c0), "i2c"));
i2c_slave_create_simple(i2c, "axp221_pmu", 0x34);
/* SDRAM */
memory_region_add_subregion(get_system_memory(),
r40->memmap[AW_R40_DEV_SDRAM], machine->ram);
bpim2u_binfo.loader_start = r40->memmap[AW_R40_DEV_SDRAM];
bpim2u_binfo.ram_size = machine->ram_size;
bpim2u_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC;
arm_load_kernel(ARM_CPU(first_cpu), machine, &bpim2u_binfo);
}
static void bpim2u_machine_init(MachineClass *mc)
{
mc->desc = "Bananapi M2U (Cortex-A7)";
mc->init = bpim2u_init;
mc->min_cpus = AW_R40_NUM_CPUS;
mc->max_cpus = AW_R40_NUM_CPUS;
mc->default_cpus = AW_R40_NUM_CPUS;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a7");
mc->default_ram_size = 1 * GiB;
mc->default_ram_id = "bpim2u.ram";
}
DEFINE_MACHINE("bpim2u", bpim2u_machine_init)
+1
View File
@@ -37,6 +37,7 @@ arm_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c', 'omap2.c'))
arm_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c'))
arm_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c'))
arm_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c'))
arm_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c'))
arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.c', 'raspi.c'))
arm_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c'))
arm_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: files('stm32f205_soc.c'))
+53
View File
@@ -40,9 +40,11 @@ struct VersalVirt {
uint32_t clk_25Mhz;
uint32_t usb;
uint32_t dwc;
uint32_t canfd[2];
} phandle;
struct arm_boot_info binfo;
CanBusState *canbus[XLNX_VERSAL_NR_CANFD];
struct {
bool secure;
} cfg;
@@ -235,6 +237,38 @@ static void fdt_add_uart_nodes(VersalVirt *s)
}
}
static void fdt_add_canfd_nodes(VersalVirt *s)
{
uint64_t addrs[] = { MM_CANFD1, MM_CANFD0 };
uint32_t size[] = { MM_CANFD1_SIZE, MM_CANFD0_SIZE };
unsigned int irqs[] = { VERSAL_CANFD1_IRQ_0, VERSAL_CANFD0_IRQ_0 };
const char clocknames[] = "can_clk\0s_axi_aclk";
int i;
/* Create and connect CANFD0 and CANFD1 nodes to canbus0. */
for (i = 0; i < ARRAY_SIZE(addrs); i++) {
char *name = g_strdup_printf("/canfd@%" PRIx64, addrs[i]);
qemu_fdt_add_subnode(s->fdt, name);
qemu_fdt_setprop_cell(s->fdt, name, "rx-fifo-depth", 0x40);
qemu_fdt_setprop_cell(s->fdt, name, "tx-mailbox-count", 0x20);
qemu_fdt_setprop_cells(s->fdt, name, "clocks",
s->phandle.clk_25Mhz, s->phandle.clk_25Mhz);
qemu_fdt_setprop(s->fdt, name, "clock-names",
clocknames, sizeof(clocknames));
qemu_fdt_setprop_cells(s->fdt, name, "interrupts",
GIC_FDT_IRQ_TYPE_SPI, irqs[i],
GIC_FDT_IRQ_FLAGS_LEVEL_HI);
qemu_fdt_setprop_sized_cells(s->fdt, name, "reg",
2, addrs[i], 2, size[i]);
qemu_fdt_setprop_string(s->fdt, name, "compatible",
"xlnx,canfd-2.0");
g_free(name);
}
}
static void fdt_add_fixed_link_nodes(VersalVirt *s, char *gemname,
uint32_t phandle)
{
@@ -639,12 +673,17 @@ static void versal_virt_init(MachineState *machine)
TYPE_XLNX_VERSAL);
object_property_set_link(OBJECT(&s->soc), "ddr", OBJECT(machine->ram),
&error_abort);
object_property_set_link(OBJECT(&s->soc), "canbus0", OBJECT(s->canbus[0]),
&error_abort);
object_property_set_link(OBJECT(&s->soc), "canbus1", OBJECT(s->canbus[1]),
&error_abort);
sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal);
fdt_create(s);
create_virtio_regions(s);
fdt_add_gem_nodes(s);
fdt_add_uart_nodes(s);
fdt_add_canfd_nodes(s);
fdt_add_gic_nodes(s);
fdt_add_timer_nodes(s);
fdt_add_zdma_nodes(s);
@@ -712,6 +751,20 @@ static void versal_virt_init(MachineState *machine)
static void versal_virt_machine_instance_init(Object *obj)
{
VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj);
/*
* User can set canbus0 and canbus1 properties to can-bus object and connect
* to socketcan(optional) interface via command line.
*/
object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
(Object **)&s->canbus[0],
object_property_allow_set_link,
0);
object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
(Object **)&s->canbus[1],
object_property_allow_set_link,
0);
}
static void versal_virt_machine_class_init(ObjectClass *oc, void *data)
+37
View File
@@ -184,6 +184,38 @@ static void versal_create_uarts(Versal *s, qemu_irq *pic)
}
}
static void versal_create_canfds(Versal *s, qemu_irq *pic)
{
int i;
uint32_t irqs[] = { VERSAL_CANFD0_IRQ_0, VERSAL_CANFD1_IRQ_0};
uint64_t addrs[] = { MM_CANFD0, MM_CANFD1 };
for (i = 0; i < ARRAY_SIZE(s->lpd.iou.canfd); i++) {
char *name = g_strdup_printf("canfd%d", i);
SysBusDevice *sbd;
MemoryRegion *mr;
object_initialize_child(OBJECT(s), name, &s->lpd.iou.canfd[i],
TYPE_XILINX_CANFD);
sbd = SYS_BUS_DEVICE(&s->lpd.iou.canfd[i]);
object_property_set_int(OBJECT(&s->lpd.iou.canfd[i]), "ext_clk_freq",
XLNX_VERSAL_CANFD_REF_CLK , &error_abort);
object_property_set_link(OBJECT(&s->lpd.iou.canfd[i]), "canfdbus",
OBJECT(s->lpd.iou.canbus[i]),
&error_abort);
sysbus_realize(sbd, &error_fatal);
mr = sysbus_mmio_get_region(sbd, 0);
memory_region_add_subregion(&s->mr_ps, addrs[i], mr);
sysbus_connect_irq(sbd, 0, pic[irqs[i]]);
g_free(name);
}
}
static void versal_create_usbs(Versal *s, qemu_irq *pic)
{
DeviceState *dev;
@@ -718,6 +750,7 @@ static void versal_realize(DeviceState *dev, Error **errp)
versal_create_apu_gic(s, pic);
versal_create_rpu_cpus(s);
versal_create_uarts(s, pic);
versal_create_canfds(s, pic);
versal_create_usbs(s, pic);
versal_create_gems(s, pic);
versal_create_admas(s, pic);
@@ -757,6 +790,10 @@ static void versal_init(Object *obj)
static Property versal_properties[] = {
DEFINE_PROP_LINK("ddr", Versal, cfg.mr_ddr, TYPE_MEMORY_REGION,
MemoryRegion *),
DEFINE_PROP_LINK("canbus0", Versal, lpd.iou.canbus[0],
TYPE_CAN_BUS, CanBusState *),
DEFINE_PROP_LINK("canbus1", Versal, lpd.iou.canbus[1],
TYPE_CAN_BUS, CanBusState *),
DEFINE_PROP_END_OF_LIST()
};
+4 -1
View File
@@ -170,13 +170,16 @@ config VIRT_CTRL
config LASI
bool
config ALLWINNER_SRAMC
bool
config ALLWINNER_A10_CCM
bool
config ALLWINNER_A10_DRAMC
bool
config AXP209_PMU
config AXP2XX_PMU
bool
depends on I2C
+209
View File
@@ -0,0 +1,209 @@
/*
* Allwinner R40 Clock Control Unit emulation
*
* Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
*
* 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-r40-ccu.h"
/* CCU register offsets */
enum {
REG_PLL_CPUX_CTRL = 0x0000,
REG_PLL_AUDIO_CTRL = 0x0008,
REG_PLL_VIDEO0_CTRL = 0x0010,
REG_PLL_VE_CTRL = 0x0018,
REG_PLL_DDR0_CTRL = 0x0020,
REG_PLL_PERIPH0_CTRL = 0x0028,
REG_PLL_PERIPH1_CTRL = 0x002c,
REG_PLL_VIDEO1_CTRL = 0x0030,
REG_PLL_SATA_CTRL = 0x0034,
REG_PLL_GPU_CTRL = 0x0038,
REG_PLL_MIPI_CTRL = 0x0040,
REG_PLL_DE_CTRL = 0x0048,
REG_PLL_DDR1_CTRL = 0x004c,
REG_AHB1_APB1_CFG = 0x0054,
REG_APB2_CFG = 0x0058,
REG_MMC0_CLK = 0x0088,
REG_MMC1_CLK = 0x008c,
REG_MMC2_CLK = 0x0090,
REG_MMC3_CLK = 0x0094,
REG_USBPHY_CFG = 0x00cc,
REG_PLL_DDR_AUX = 0x00f0,
REG_DRAM_CFG = 0x00f4,
REG_PLL_DDR1_CFG = 0x00f8,
REG_DRAM_CLK_GATING = 0x0100,
REG_GMAC_CLK = 0x0164,
REG_SYS_32K_CLK = 0x0310,
REG_PLL_LOCK_CTRL = 0x0320,
};
#define REG_INDEX(offset) (offset / sizeof(uint32_t))
/* CCU register flags */
enum {
REG_PLL_ENABLE = (1 << 31),
REG_PLL_LOCK = (1 << 28),
};
static uint64_t allwinner_r40_ccu_read(void *opaque, hwaddr offset,
unsigned size)
{
const AwR40ClockCtlState *s = AW_R40_CCU(opaque);
const uint32_t idx = REG_INDEX(offset);
switch (offset) {
case 0x324 ... AW_R40_CCU_IOSIZE:
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
return 0;
}
return s->regs[idx];
}
static void allwinner_r40_ccu_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
AwR40ClockCtlState *s = AW_R40_CCU(opaque);
switch (offset) {
case REG_DRAM_CFG: /* DRAM Configuration(for DDR0) */
/* bit16: SDRCLK_UPD (SDRCLK configuration 0 update) */
val &= ~(1 << 16);
break;
case REG_PLL_DDR1_CTRL: /* DDR1 Control register */
/* bit30: SDRPLL_UPD */
val &= ~(1 << 30);
if (val & REG_PLL_ENABLE) {
val |= REG_PLL_LOCK;
}
break;
case REG_PLL_CPUX_CTRL:
case REG_PLL_AUDIO_CTRL:
case REG_PLL_VE_CTRL:
case REG_PLL_VIDEO0_CTRL:
case REG_PLL_DDR0_CTRL:
case REG_PLL_PERIPH0_CTRL:
case REG_PLL_PERIPH1_CTRL:
case REG_PLL_VIDEO1_CTRL:
case REG_PLL_SATA_CTRL:
case REG_PLL_GPU_CTRL:
case REG_PLL_MIPI_CTRL:
case REG_PLL_DE_CTRL:
if (val & REG_PLL_ENABLE) {
val |= REG_PLL_LOCK;
}
break;
case 0x324 ... AW_R40_CCU_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[REG_INDEX(offset)] = (uint32_t) val;
}
static const MemoryRegionOps allwinner_r40_ccu_ops = {
.read = allwinner_r40_ccu_read,
.write = allwinner_r40_ccu_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
.impl.min_access_size = 4,
};
static void allwinner_r40_ccu_reset(DeviceState *dev)
{
AwR40ClockCtlState *s = AW_R40_CCU(dev);
memset(s->regs, 0, sizeof(s->regs));
/* Set default values for registers */
s->regs[REG_INDEX(REG_PLL_CPUX_CTRL)] = 0x00001000;
s->regs[REG_INDEX(REG_PLL_AUDIO_CTRL)] = 0x00035514;
s->regs[REG_INDEX(REG_PLL_VIDEO0_CTRL)] = 0x03006207;
s->regs[REG_INDEX(REG_PLL_VE_CTRL)] = 0x03006207;
s->regs[REG_INDEX(REG_PLL_DDR0_CTRL)] = 0x00001000,
s->regs[REG_INDEX(REG_PLL_PERIPH0_CTRL)] = 0x00041811;
s->regs[REG_INDEX(REG_PLL_PERIPH1_CTRL)] = 0x00041811;
s->regs[REG_INDEX(REG_PLL_VIDEO1_CTRL)] = 0x03006207;
s->regs[REG_INDEX(REG_PLL_SATA_CTRL)] = 0x00001811;
s->regs[REG_INDEX(REG_PLL_GPU_CTRL)] = 0x03006207;
s->regs[REG_INDEX(REG_PLL_MIPI_CTRL)] = 0x00000515;
s->regs[REG_INDEX(REG_PLL_DE_CTRL)] = 0x03006207;
s->regs[REG_INDEX(REG_PLL_DDR1_CTRL)] = 0x00001800;
s->regs[REG_INDEX(REG_AHB1_APB1_CFG)] = 0x00001010;
s->regs[REG_INDEX(REG_APB2_CFG)] = 0x01000000;
s->regs[REG_INDEX(REG_PLL_DDR_AUX)] = 0x00000001;
s->regs[REG_INDEX(REG_PLL_DDR1_CFG)] = 0x0ccca000;
s->regs[REG_INDEX(REG_SYS_32K_CLK)] = 0x0000000f;
}
static void allwinner_r40_ccu_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
AwR40ClockCtlState *s = AW_R40_CCU(obj);
/* Memory mapping */
memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_r40_ccu_ops, s,
TYPE_AW_R40_CCU, AW_R40_CCU_IOSIZE);
sysbus_init_mmio(sbd, &s->iomem);
}
static const VMStateDescription allwinner_r40_ccu_vmstate = {
.name = "allwinner-r40-ccu",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, AwR40ClockCtlState, AW_R40_CCU_REGS_NUM),
VMSTATE_END_OF_LIST()
}
};
static void allwinner_r40_ccu_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = allwinner_r40_ccu_reset;
dc->vmsd = &allwinner_r40_ccu_vmstate;
}
static const TypeInfo allwinner_r40_ccu_info = {
.name = TYPE_AW_R40_CCU,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_init = allwinner_r40_ccu_init,
.instance_size = sizeof(AwR40ClockCtlState),
.class_init = allwinner_r40_ccu_class_init,
};
static void allwinner_r40_ccu_register(void)
{
type_register_static(&allwinner_r40_ccu_info);
}
type_init(allwinner_r40_ccu_register)
File diff suppressed because it is too large Load Diff
+184
View File
@@ -0,0 +1,184 @@
/*
* Allwinner R40 SRAM controller emulation
*
* Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
*
* 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 "qapi/error.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/misc/allwinner-sramc.h"
#include "trace.h"
/*
* register offsets
* https://linux-sunxi.org/SRAM_Controller_Register_Guide
*/
enum {
REG_SRAM_CTL1_CFG = 0x04, /* SRAM Control register 1 */
REG_SRAM_VER = 0x24, /* SRAM Version register */
REG_SRAM_R40_SOFT_ENTRY_REG0 = 0xbc,
};
/* REG_SRAMC_VERSION bit defines */
#define SRAM_VER_READ_ENABLE (1 << 15)
#define SRAM_VER_VERSION_SHIFT 16
#define SRAM_VERSION_SUN8I_R40 0x1701
static uint64_t allwinner_sramc_read(void *opaque, hwaddr offset,
unsigned size)
{
AwSRAMCState *s = AW_SRAMC(opaque);
AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s);
uint64_t val = 0;
switch (offset) {
case REG_SRAM_CTL1_CFG:
val = s->sram_ctl1;
break;
case REG_SRAM_VER:
/* bit15: lock bit, set this bit before reading this register */
if (s->sram_ver & SRAM_VER_READ_ENABLE) {
val = SRAM_VER_READ_ENABLE |
(sc->sram_version_code << SRAM_VER_VERSION_SHIFT);
}
break;
case REG_SRAM_R40_SOFT_ENTRY_REG0:
val = s->sram_soft_entry_reg0;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
return 0;
}
trace_allwinner_sramc_read(offset, val);
return val;
}
static void allwinner_sramc_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
AwSRAMCState *s = AW_SRAMC(opaque);
trace_allwinner_sramc_write(offset, val);
switch (offset) {
case REG_SRAM_CTL1_CFG:
s->sram_ctl1 = val;
break;
case REG_SRAM_VER:
/* Only the READ_ENABLE bit is writeable */
s->sram_ver = val & SRAM_VER_READ_ENABLE;
break;
case REG_SRAM_R40_SOFT_ENTRY_REG0:
s->sram_soft_entry_reg0 = val;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
break;
}
}
static const MemoryRegionOps allwinner_sramc_ops = {
.read = allwinner_sramc_read,
.write = allwinner_sramc_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
.impl.min_access_size = 4,
};
static const VMStateDescription allwinner_sramc_vmstate = {
.name = "allwinner-sramc",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(sram_ver, AwSRAMCState),
VMSTATE_UINT32(sram_soft_entry_reg0, AwSRAMCState),
VMSTATE_END_OF_LIST()
}
};
static void allwinner_sramc_reset(DeviceState *dev)
{
AwSRAMCState *s = AW_SRAMC(dev);
AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s);
switch (sc->sram_version_code) {
case SRAM_VERSION_SUN8I_R40:
s->sram_ctl1 = 0x1300;
break;
}
}
static void allwinner_sramc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = allwinner_sramc_reset;
dc->vmsd = &allwinner_sramc_vmstate;
}
static void allwinner_sramc_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
AwSRAMCState *s = AW_SRAMC(obj);
/* Memory mapping */
memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sramc_ops, s,
TYPE_AW_SRAMC, 1 * KiB);
sysbus_init_mmio(sbd, &s->iomem);
}
static const TypeInfo allwinner_sramc_info = {
.name = TYPE_AW_SRAMC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_init = allwinner_sramc_init,
.instance_size = sizeof(AwSRAMCState),
.class_init = allwinner_sramc_class_init,
};
static void allwinner_r40_sramc_class_init(ObjectClass *klass, void *data)
{
AwSRAMCClass *sc = AW_SRAMC_CLASS(klass);
sc->sram_version_code = SRAM_VERSION_SUN8I_R40;
}
static const TypeInfo allwinner_r40_sramc_info = {
.name = TYPE_AW_SRAMC_SUN8I_R40,
.parent = TYPE_AW_SRAMC,
.class_init = allwinner_r40_sramc_class_init,
};
static void allwinner_sramc_register(void)
{
type_register_static(&allwinner_sramc_info);
type_register_static(&allwinner_r40_sramc_info);
}
type_init(allwinner_sramc_register)
-238
View File
@@ -1,238 +0,0 @@
/*
* AXP-209 PMU Emulation
*
* Copyright (C) 2022 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.
*
* SPDX-License-Identifier: MIT
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "trace.h"
#include "hw/i2c/i2c.h"
#include "migration/vmstate.h"
#define TYPE_AXP209_PMU "axp209_pmu"
#define AXP209(obj) \
OBJECT_CHECK(AXP209I2CState, (obj), TYPE_AXP209_PMU)
/* 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 AXP209_CHIP_VERSION_ID (0x01)
#define AXP209_DC_DC2_OUT_V_CTRL_RESET (0x16)
#define AXP209_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 axp209_reset_enter(Object *obj, ResetType type)
{
AXP209I2CState *s = AXP209(obj);
memset(s->regs, 0, NR_REGS);
s->ptr = 0;
s->count = 0;
s->regs[REG_CHIP_VERSION] = AXP209_CHIP_VERSION_ID;
s->regs[REG_DC_DC2_OUT_V_CTRL] = AXP209_DC_DC2_OUT_V_CTRL_RESET;
s->regs[REG_IRQ_BANK_1_CTRL] = AXP209_IRQ_BANK_1_CTRL_RESET;
}
/* Handle events from master. */
static int axp209_event(I2CSlave *i2c, enum i2c_event event)
{
AXP209I2CState *s = AXP209(i2c);
s->count = 0;
return 0;
}
/* Called when master requests read */
static uint8_t axp209_rx(I2CSlave *i2c)
{
AXP209I2CState *s = AXP209(i2c);
uint8_t ret = 0xff;
if (s->ptr < NR_REGS) {
ret = s->regs[s->ptr++];
}
trace_axp209_rx(s->ptr - 1, ret);
return ret;
}
/*
* Called when master sends write.
* Update ptr with byte 0, then perform write with second byte.
*/
static int axp209_tx(I2CSlave *i2c, uint8_t data)
{
AXP209I2CState *s = AXP209(i2c);
if (s->count == 0) {
/* Store register address */
s->ptr = data;
s->count++;
trace_axp209_select(data);
} else {
trace_axp209_tx(s->ptr, data);
if (s->ptr == REG_DC_DC2_OUT_V_CTRL) {
s->regs[s->ptr++] = data;
}
}
return 0;
}
static const VMStateDescription vmstate_axp209 = {
.name = TYPE_AXP209_PMU,
.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 axp209_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 = axp209_reset_enter;
dc->vmsd = &vmstate_axp209;
isc->event = axp209_event;
isc->recv = axp209_rx;
isc->send = axp209_tx;
}
static const TypeInfo axp209_info = {
.name = TYPE_AXP209_PMU,
.parent = TYPE_I2C_SLAVE,
.instance_size = sizeof(AXP209I2CState),
.class_init = axp209_class_init
};
static void axp209_register_devices(void)
{
type_register_static(&axp209_info);
}
type_init(axp209_register_devices);
+283
View File
@@ -0,0 +1,283 @@
/*
* AXP-2XX PMU Emulation, supported lists:
* AXP209
* AXP221
*
* Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
* Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.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.
*
* SPDX-License-Identifier: MIT
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qom/object.h"
#include "trace.h"
#include "hw/i2c/i2c.h"
#include "migration/vmstate.h"
#define TYPE_AXP2XX "axp2xx_pmu"
#define TYPE_AXP209_PMU "axp209_pmu"
#define TYPE_AXP221_PMU "axp221_pmu"
OBJECT_DECLARE_TYPE(AXP2xxI2CState, AXP2xxClass, AXP2XX)
#define NR_REGS (0xff)
/* A simple I2C slave which returns values of ID or CNT register. */
typedef struct AXP2xxI2CState {
/*< 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 */
} AXP2xxI2CState;
typedef struct AXP2xxClass {
/*< private >*/
I2CSlaveClass parent_class;
/*< public >*/
void (*reset_enter)(AXP2xxI2CState *s, ResetType type);
} AXP2xxClass;
#define AXP209_CHIP_VERSION_ID (0x01)
#define AXP209_DC_DC2_OUT_V_CTRL_RESET (0x16)
/* Reset all counters and load ID register */
static void axp209_reset_enter(AXP2xxI2CState *s, ResetType type)
{
memset(s->regs, 0, NR_REGS);
s->ptr = 0;
s->count = 0;
s->regs[0x03] = AXP209_CHIP_VERSION_ID;
s->regs[0x23] = AXP209_DC_DC2_OUT_V_CTRL_RESET;
s->regs[0x30] = 0x60;
s->regs[0x32] = 0x46;
s->regs[0x34] = 0x41;
s->regs[0x35] = 0x22;
s->regs[0x36] = 0x5d;
s->regs[0x37] = 0x08;
s->regs[0x38] = 0xa5;
s->regs[0x39] = 0x1f;
s->regs[0x3a] = 0x68;
s->regs[0x3b] = 0x5f;
s->regs[0x3c] = 0xfc;
s->regs[0x3d] = 0x16;
s->regs[0x40] = 0xd8;
s->regs[0x42] = 0xff;
s->regs[0x43] = 0x3b;
s->regs[0x80] = 0xe0;
s->regs[0x82] = 0x83;
s->regs[0x83] = 0x80;
s->regs[0x84] = 0x32;
s->regs[0x86] = 0xff;
s->regs[0x90] = 0x07;
s->regs[0x91] = 0xa0;
s->regs[0x92] = 0x07;
s->regs[0x93] = 0x07;
}
#define AXP221_PWR_STATUS_ACIN_PRESENT BIT(7)
#define AXP221_PWR_STATUS_ACIN_AVAIL BIT(6)
#define AXP221_PWR_STATUS_VBUS_PRESENT BIT(5)
#define AXP221_PWR_STATUS_VBUS_USED BIT(4)
#define AXP221_PWR_STATUS_BAT_CHARGING BIT(2)
#define AXP221_PWR_STATUS_ACIN_VBUS_POWERED BIT(1)
/* Reset all counters and load ID register */
static void axp221_reset_enter(AXP2xxI2CState *s, ResetType type)
{
memset(s->regs, 0, NR_REGS);
s->ptr = 0;
s->count = 0;
/* input power status register */
s->regs[0x00] = AXP221_PWR_STATUS_ACIN_PRESENT
| AXP221_PWR_STATUS_ACIN_AVAIL
| AXP221_PWR_STATUS_ACIN_VBUS_POWERED;
s->regs[0x01] = 0x00; /* no battery is connected */
/*
* CHIPID register, no documented on datasheet, but it is checked in
* u-boot spl. I had read it from AXP221s and got 0x06 value.
* So leave 06h here.
*/
s->regs[0x03] = 0x06;
s->regs[0x10] = 0xbf;
s->regs[0x13] = 0x01;
s->regs[0x30] = 0x60;
s->regs[0x31] = 0x03;
s->regs[0x32] = 0x43;
s->regs[0x33] = 0xc6;
s->regs[0x34] = 0x45;
s->regs[0x35] = 0x0e;
s->regs[0x36] = 0x5d;
s->regs[0x37] = 0x08;
s->regs[0x38] = 0xa5;
s->regs[0x39] = 0x1f;
s->regs[0x3c] = 0xfc;
s->regs[0x3d] = 0x16;
s->regs[0x80] = 0x80;
s->regs[0x82] = 0xe0;
s->regs[0x84] = 0x32;
s->regs[0x8f] = 0x01;
s->regs[0x90] = 0x07;
s->regs[0x91] = 0x1f;
s->regs[0x92] = 0x07;
s->regs[0x93] = 0x1f;
s->regs[0x40] = 0xd8;
s->regs[0x41] = 0xff;
s->regs[0x42] = 0x03;
s->regs[0x43] = 0x03;
s->regs[0xb8] = 0xc0;
s->regs[0xb9] = 0x64;
s->regs[0xe6] = 0xa0;
}
static void axp2xx_reset_enter(Object *obj, ResetType type)
{
AXP2xxI2CState *s = AXP2XX(obj);
AXP2xxClass *sc = AXP2XX_GET_CLASS(s);
sc->reset_enter(s, type);
}
/* Handle events from master. */
static int axp2xx_event(I2CSlave *i2c, enum i2c_event event)
{
AXP2xxI2CState *s = AXP2XX(i2c);
s->count = 0;
return 0;
}
/* Called when master requests read */
static uint8_t axp2xx_rx(I2CSlave *i2c)
{
AXP2xxI2CState *s = AXP2XX(i2c);
uint8_t ret = 0xff;
if (s->ptr < NR_REGS) {
ret = s->regs[s->ptr++];
}
trace_axp2xx_rx(s->ptr - 1, ret);
return ret;
}
/*
* Called when master sends write.
* Update ptr with byte 0, then perform write with second byte.
*/
static int axp2xx_tx(I2CSlave *i2c, uint8_t data)
{
AXP2xxI2CState *s = AXP2XX(i2c);
if (s->count == 0) {
/* Store register address */
s->ptr = data;
s->count++;
trace_axp2xx_select(data);
} else {
trace_axp2xx_tx(s->ptr, data);
s->regs[s->ptr++] = data;
}
return 0;
}
static const VMStateDescription vmstate_axp2xx = {
.name = TYPE_AXP2XX,
.version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8_ARRAY(regs, AXP2xxI2CState, NR_REGS),
VMSTATE_UINT8(ptr, AXP2xxI2CState),
VMSTATE_UINT8(count, AXP2xxI2CState),
VMSTATE_END_OF_LIST()
}
};
static void axp2xx_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 = axp2xx_reset_enter;
dc->vmsd = &vmstate_axp2xx;
isc->event = axp2xx_event;
isc->recv = axp2xx_rx;
isc->send = axp2xx_tx;
}
static const TypeInfo axp2xx_info = {
.name = TYPE_AXP2XX,
.parent = TYPE_I2C_SLAVE,
.instance_size = sizeof(AXP2xxI2CState),
.class_size = sizeof(AXP2xxClass),
.class_init = axp2xx_class_init,
.abstract = true,
};
static void axp209_class_init(ObjectClass *oc, void *data)
{
AXP2xxClass *sc = AXP2XX_CLASS(oc);
sc->reset_enter = axp209_reset_enter;
}
static const TypeInfo axp209_info = {
.name = TYPE_AXP209_PMU,
.parent = TYPE_AXP2XX,
.class_init = axp209_class_init
};
static void axp221_class_init(ObjectClass *oc, void *data)
{
AXP2xxClass *sc = AXP2XX_CLASS(oc);
sc->reset_enter = axp221_reset_enter;
}
static const TypeInfo axp221_info = {
.name = TYPE_AXP221_PMU,
.parent = TYPE_AXP2XX,
.class_init = axp221_class_init,
};
static void axp2xx_register_devices(void)
{
type_register_static(&axp2xx_info);
type_register_static(&axp209_info);
type_register_static(&axp221_info);
}
type_init(axp2xx_register_devices);
+4 -1
View File
@@ -37,6 +37,7 @@ subdir('macio')
softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c'))
softmmu_ss.add(when: 'CONFIG_ALLWINNER_SRAMC', if_true: files('allwinner-sramc.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'))
@@ -44,7 +45,9 @@ 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'))
softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl.c'))
softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c'))
softmmu_ss.add(when: 'CONFIG_AXP209_PMU', if_true: files('axp209.c'))
softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c'))
softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-dramc.c'))
softmmu_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.c'))
softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c'))
softmmu_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c'))
softmmu_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c'))

Some files were not shown because too many files have changed in this diff Show More