target-arm queue:

* Fix some incorrect syndrome values in various sysreg traps
  * Clean up sysreg trap code to avoid similar future bugs
  * Make boards/SoCs using a9mpcore and a15mpcore objects specify
    number of GIC interrupts explicitly
  * Kconfig: Extract CONFIG_USB_CHIPIDEA from CONFIG_IMX
  * target/arm: Use uint32_t in t32_expandimm_imm()
  * New board model: NPCM845 Evaluation board "npcm845-evb"
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAme3Vk8ZHHBldGVyLm1h
 eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3t8QD/48yOUtFwUMFrwbbszK5rss
 Ghhtk0ylKIxPQirgzjLUNq4hV2MZEtdmyiqaEllvA0aS839oWhFW0hbsGmoL/TVF
 tqXVP3Y1dMubmNHCGcgiFqaxaInnNSC9S1ALiEm5a37g519706WLXPVLqJJ9t31b
 uWHKT1hqxstzWSExhGrEkSEghcgN3u1KyCz0zyq9bk/F3OFWZfHNH6JqutQX18Ua
 5HtcD1Pum6WjayBc3y4AYVYH4xyQclY7LPR+zKNf2d5GuZ+J6MlXMyfCuE2/J//m
 wHAtAoeuFhi/HFHR4vQP4L7HrhFrECbjfWha85F/rmiOAo6LnbICyPt4tAPe5So3
 FCtSHfht9ToulBqULE+F/AWVCdt8UeDRgOANSHFsMkxYiUK8QpMv8A2AtwJUqiMp
 WbAzw31f6SgANgFQObhoRNE3QyX8V53ZJAsPhDooTxwMiglqVTM3Xux8W2zz9FdU
 BTwCy23efBqKf4RWfeHjAXctGshePI1mTBJmvEKG5G5ligMNeg7ZiQqqfRVBagc/
 gpsQKNjpN9MVVds3thUvMCYO/9NOeeAtcVA2vW7qf7HrYaM72UngCPWjhNfAj/9I
 9hxgqEnKC6qoD/zMyFv+XwqNlL1PuD79rbvN8TWFd/f8iBIYOY6WVEYmsi7WGugI
 WzYI93RqFaQhrpyHDcRVGw==
 =djUd
 -----END PGP SIGNATURE-----

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

target-arm queue:
 * Fix some incorrect syndrome values in various sysreg traps
 * Clean up sysreg trap code to avoid similar future bugs
 * Make boards/SoCs using a9mpcore and a15mpcore objects specify
   number of GIC interrupts explicitly
 * Kconfig: Extract CONFIG_USB_CHIPIDEA from CONFIG_IMX
 * target/arm: Use uint32_t in t32_expandimm_imm()
 * New board model: NPCM845 Evaluation board "npcm845-evb"

# -----BEGIN PGP SIGNATURE-----
#
# iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAme3Vk8ZHHBldGVyLm1h
# eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3t8QD/48yOUtFwUMFrwbbszK5rss
# Ghhtk0ylKIxPQirgzjLUNq4hV2MZEtdmyiqaEllvA0aS839oWhFW0hbsGmoL/TVF
# tqXVP3Y1dMubmNHCGcgiFqaxaInnNSC9S1ALiEm5a37g519706WLXPVLqJJ9t31b
# uWHKT1hqxstzWSExhGrEkSEghcgN3u1KyCz0zyq9bk/F3OFWZfHNH6JqutQX18Ua
# 5HtcD1Pum6WjayBc3y4AYVYH4xyQclY7LPR+zKNf2d5GuZ+J6MlXMyfCuE2/J//m
# wHAtAoeuFhi/HFHR4vQP4L7HrhFrECbjfWha85F/rmiOAo6LnbICyPt4tAPe5So3
# FCtSHfht9ToulBqULE+F/AWVCdt8UeDRgOANSHFsMkxYiUK8QpMv8A2AtwJUqiMp
# WbAzw31f6SgANgFQObhoRNE3QyX8V53ZJAsPhDooTxwMiglqVTM3Xux8W2zz9FdU
# BTwCy23efBqKf4RWfeHjAXctGshePI1mTBJmvEKG5G5ligMNeg7ZiQqqfRVBagc/
# gpsQKNjpN9MVVds3thUvMCYO/9NOeeAtcVA2vW7qf7HrYaM72UngCPWjhNfAj/9I
# 9hxgqEnKC6qoD/zMyFv+XwqNlL1PuD79rbvN8TWFd/f8iBIYOY6WVEYmsi7WGugI
# WzYI93RqFaQhrpyHDcRVGw==
# =djUd
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 21 Feb 2025 00:20:31 HKT
# 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]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* tag 'pull-target-arm-20250220' of https://git.linaro.org/people/pmaydell/qemu-arm: (41 commits)
  docs/system/arm: Add Description for NPCM8XX SoC
  hw/arm: Add NPCM845 Evaluation board
  hw/arm: Add NPCM8XX SoC
  hw/net: Add NPCM8XX PCS Module
  hw/misc: Support NPCM8XX CLK Module Registers
  hw/misc: Add nr_regs and cold_reset_values to NPCM CLK
  hw/misc: Move NPCM7XX CLK to NPCM CLK
  hw/misc: Rename npcm7xx_clk to npcm_clk
  hw/misc: Support 8-bytes memop in NPCM GCR module
  hw/misc: Store DRAM size in NPCM8XX GCR Module
  hw/misc: Add support for NPCM8XX GCR
  hw/misc: Add nr_regs and cold_reset_values to NPCM GCR
  hw/misc: Move NPCM7XX GCR to NPCM GCR
  hw/misc: Rename npcm7xx_gcr to npcm_gcr
  hw/ssi: Make flash size a property in NPCM7XX FIU
  pc-bios: Add NPCM8XX vBootrom
  roms: Update vbootrom to 1287b6e
  target/arm: Use uint32_t in t32_expandimm_imm()
  Kconfig: Extract CONFIG_USB_CHIPIDEA from CONFIG_IMX
  hw/cpu/arm_mpcore: Remove default values for GIC external IRQs
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2025-02-21 07:14:03 +08:00
commit f41af4c585
45 changed files with 2626 additions and 497 deletions

View File

@ -878,6 +878,7 @@ F: include/hw/*/npcm*
F: tests/qtest/npcm*
F: tests/qtest/adm1266-test.c
F: pc-bios/npcm7xx_bootrom.bin
F: pc-bios/npcm8xx_bootrom.bin
F: roms/vbootrom
F: docs/system/arm/nuvoton.rst
F: tests/functional/test_arm_quanta_gsj.py

View File

@ -8,3 +8,4 @@ include ../arm-softmmu/default.mak
# CONFIG_XLNX_ZYNQMP_ARM=n
# CONFIG_XLNX_VERSAL=n
# CONFIG_SBSA_REF=n
# CONFIG_NPCM8XX=n

View File

@ -1,12 +1,13 @@
Nuvoton iBMC boards (``kudo-bmc``, ``mori-bmc``, ``npcm750-evb``, ``quanta-gbs-bmc``, ``quanta-gsj``)
=====================================================================================================
Nuvoton iBMC boards (``kudo-bmc``, ``mori-bmc``, ``npcm750-evb``, ``quanta-gbs-bmc``, ``quanta-gsj``, ``npcm845-evb``)
======================================================================================================================
The `Nuvoton iBMC`_ chips (NPCM7xx) are a family of ARM-based SoCs that are
The `Nuvoton iBMC`_ chips are a family of Arm-based SoCs that are
designed to be used as Baseboard Management Controllers (BMCs) in various
servers. They all feature one or two ARM Cortex-A9 CPU cores, as well as an
assortment of peripherals targeted for either Enterprise or Data Center /
Hyperscale applications. The former is a superset of the latter, so NPCM750 has
all the peripherals of NPCM730 and more.
servers. Currently there are two families: NPCM7XX series and
NPCM8XX series. NPCM7XX series feature one or two Arm Cortex-A9 CPU cores,
while NPCM8XX feature 4 Arm Cortex-A35 CPU cores. Both series contain a
different assortment of peripherals targeted for either Enterprise or Data
Center / Hyperscale applications.
.. _Nuvoton iBMC: https://www.nuvoton.com/products/cloud-computing/ibmc/
@ -27,6 +28,11 @@ There are also two more SoCs, NPCM710 and NPCM705, which are single-core
variants of NPCM750 and NPCM730, respectively. These are currently not
supported by QEMU.
The NPCM8xx SoC is the successor of the NPCM7xx SoC. It has 4 Cortex-A35 cores.
The following machines are based on this chip :
- ``npcm845-evb`` Nuvoton NPCM845 Evaluation board
Supported devices
-----------------
@ -62,6 +68,8 @@ Missing devices
* System Wake-up Control (SWC)
* Shared memory (SHM)
* eSPI slave interface
* Block-transfer interface (8XX only)
* Virtual UART (8XX only)
* Ethernet controller (GMAC)
* USB device (USBD)
@ -76,6 +84,11 @@ Missing devices
* Video capture
* Encoding compression engine
* Security features
* I3C buses (8XX only)
* Temperature sensor interface (8XX only)
* Virtual UART (8XX only)
* Flash monitor (8XX only)
* JTAG master (8XX only)
Boot options
------------

View File

@ -303,7 +303,7 @@ config ZYNQ
select PL330
select SDHCI
select SSI_M25P80
select USB_EHCI_SYSBUS
select USB_CHIPIDEA
select XILINX # UART
select XILINX_AXI
select XILINX_SPI
@ -481,6 +481,19 @@ config NPCM7XX
select PCA954X
select USB_OHCI_SYSBUS
config NPCM8XX
bool
default y
depends on TCG && AARCH64
select ARM_GIC
select SMBUS
select PL310 # cache controller
select NPCM7XX
select SERIAL
select SSI
select UNIMP
config FSL_IMX25
bool
default y
@ -489,6 +502,7 @@ config FSL_IMX25
select IMX
select IMX_FEC
select IMX_I2C
select USB_CHIPIDEA
select WDT_IMX2
select SDHCI
@ -516,6 +530,7 @@ config FSL_IMX6
select PL310 # cache controller
select PCI_EXPRESS_DESIGNWARE
select SDHCI
select USB_CHIPIDEA
select OR_IRQ
config ASPEED_SOC
@ -576,6 +591,7 @@ config FSL_IMX7
select SDHCI
select OR_IRQ
select UNIMP
select USB_CHIPIDEA
config ARM_SMMUV3
bool
@ -591,6 +607,7 @@ config FSL_IMX6UL
select IMX_I2C
select WDT_IMX2
select SDHCI
select USB_CHIPIDEA
select UNIMP
config MICROBIT

View File

@ -103,6 +103,8 @@
#define EXYNOS4210_PL330_BASE1_ADDR 0x12690000
#define EXYNOS4210_PL330_BASE2_ADDR 0x12850000
#define GIC_EXT_IRQS 64 /* FIXME: verify for this SoC */
enum ExtGicId {
EXT_GIC_ID_MDMA_LCD0 = 66,
EXT_GIC_ID_PDMA0,
@ -394,7 +396,8 @@ static void exynos4210_init_board_irqs(Exynos4210State *s)
}
if (irq_id) {
qdev_connect_gpio_out(splitter, splitin,
qdev_get_gpio_in(extgicdev, irq_id - 32));
qdev_get_gpio_in(extgicdev,
irq_id - GIC_INTERNAL));
}
}
for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) {
@ -421,7 +424,8 @@ static void exynos4210_init_board_irqs(Exynos4210State *s)
s->irq_table[n] = qdev_get_gpio_in(splitter, 0);
qdev_connect_gpio_out(splitter, 0, qdev_get_gpio_in(intcdev, n));
qdev_connect_gpio_out(splitter, 1,
qdev_get_gpio_in(extgicdev, irq_id - 32));
qdev_get_gpio_in(extgicdev,
irq_id - GIC_INTERNAL));
} else {
s->irq_table[n] = qdev_get_gpio_in(intcdev, n);
}
@ -586,6 +590,8 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp)
/* Private memory region and Internal GIC */
qdev_prop_set_uint32(DEVICE(&s->a9mpcore), "num-cpu", EXYNOS4210_NCPUS);
qdev_prop_set_uint32(DEVICE(&s->a9mpcore), "num-irq",
GIC_EXT_IRQS + GIC_INTERNAL);
busdev = SYS_BUS_DEVICE(&s->a9mpcore);
sysbus_realize(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR);

View File

@ -45,7 +45,7 @@
#define MVBAR_ADDR 0x200
#define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t))
#define NIRQ_GIC 160
#define GIC_EXT_IRQS 128 /* EnergyCore ECX-1000 & ECX-2000 */
/* Board init. */
@ -180,7 +180,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
{
DeviceState *dev = NULL;
SysBusDevice *busdev;
qemu_irq pic[128];
qemu_irq pic[GIC_EXT_IRQS];
int n;
unsigned int smp_cpus = machine->smp.cpus;
qemu_irq cpu_irq[4];
@ -260,7 +260,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
break;
}
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC);
qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
@ -271,7 +271,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
sysbus_connect_irq(busdev, n + 3 * smp_cpus, cpu_vfiq[n]);
}
for (n = 0; n < 128; n++) {
for (n = 0; n < GIC_EXT_IRQS; n++) {
pic[n] = qdev_get_gpio_in(dev, n);
}

View File

@ -12,6 +12,7 @@ arm_ss.add(when: 'CONFIG_MUSICPAL', if_true: files('musicpal.c'))
arm_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c'))
arm_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: files('olimex-stm32-h405.c'))
arm_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c'))
arm_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm8xx.c', 'npcm8xx_boards.c'))
arm_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c'))
arm_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa-ref.c'))
arm_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c'))

View File

@ -292,17 +292,21 @@ static const struct {
hwaddr regs_addr;
int cs_count;
const hwaddr *flash_addr;
size_t flash_size;
} npcm7xx_fiu[] = {
{
.name = "fiu0",
.regs_addr = 0xfb000000,
.cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr),
.flash_addr = npcm7xx_fiu0_flash_addr,
.flash_size = 128 * MiB,
}, {
.name = "fiu3",
.regs_addr = 0xc0000000,
.cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr),
.flash_addr = npcm7xx_fiu3_flash_addr,
.flash_size = 128 * MiB,
},
};
@ -735,6 +739,8 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
object_property_set_int(OBJECT(sbd), "cs-count",
npcm7xx_fiu[i].cs_count, &error_abort);
object_property_set_int(OBJECT(sbd), "flash-size",
npcm7xx_fiu[i].flash_size, &error_abort);
sysbus_realize(sbd, &error_abort);
sysbus_mmio_map(sbd, 0, npcm7xx_fiu[i].regs_addr);

805
hw/arm/npcm8xx.c Normal file
View File

@ -0,0 +1,805 @@
/*
* Nuvoton NPCM8xx SoC family.
*
* Copyright 2022 Google LLC
*
* 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.
*/
#include "qemu/osdep.h"
#include "hw/boards.h"
#include "hw/arm/boot.h"
#include "hw/arm/bsa.h"
#include "hw/arm/npcm8xx.h"
#include "hw/char/serial-mm.h"
#include "hw/intc/arm_gic.h"
#include "hw/loader.h"
#include "hw/misc/unimp.h"
#include "hw/qdev-clock.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "qemu/units.h"
#include "system/system.h"
/*
* This covers the whole MMIO space. We'll use this to catch any MMIO accesses
* that aren't handled by a device.
*/
#define NPCM8XX_MMIO_BA 0x80000000
#define NPCM8XX_MMIO_SZ 0x7ffd0000
/* OTP fuse array */
#define NPCM8XX_OTP_BA 0xf0189000
/* GIC Distributor */
#define NPCM8XX_GICD_BA 0xdfff9000
#define NPCM8XX_GICC_BA 0xdfffa000
/* Core system modules. */
#define NPCM8XX_CPUP_BA 0xf03fe000
#define NPCM8XX_GCR_BA 0xf0800000
#define NPCM8XX_CLK_BA 0xf0801000
#define NPCM8XX_MC_BA 0xf0824000
#define NPCM8XX_RNG_BA 0xf000b000
/* ADC Module */
#define NPCM8XX_ADC_BA 0xf000c000
/* Internal AHB SRAM */
#define NPCM8XX_RAM3_BA 0xc0008000
#define NPCM8XX_RAM3_SZ (4 * KiB)
/* Memory blocks at the end of the address space */
#define NPCM8XX_RAM2_BA 0xfffb0000
#define NPCM8XX_RAM2_SZ (256 * KiB)
#define NPCM8XX_ROM_BA 0xffff0100
#define NPCM8XX_ROM_SZ (64 * KiB)
/* SDHCI Modules */
#define NPCM8XX_MMC_BA 0xf0842000
/* Run PLL1 at 1600 MHz */
#define NPCM8XX_PLLCON1_FIXUP_VAL 0x00402101
/* Run the CPU from PLL1 and UART from PLL2 */
#define NPCM8XX_CLKSEL_FIXUP_VAL 0x004aaba9
/* Clock configuration values to be fixed up when bypassing bootloader */
/*
* Interrupt lines going into the GIC. This does not include internal Cortex-A35
* interrupts.
*/
enum NPCM8xxInterrupt {
NPCM8XX_ADC_IRQ = 0,
NPCM8XX_PECI_IRQ = 6,
NPCM8XX_KCS_HIB_IRQ = 9,
NPCM8XX_MMC_IRQ = 26,
NPCM8XX_TIMER0_IRQ = 32, /* Timer Module 0 */
NPCM8XX_TIMER1_IRQ,
NPCM8XX_TIMER2_IRQ,
NPCM8XX_TIMER3_IRQ,
NPCM8XX_TIMER4_IRQ,
NPCM8XX_TIMER5_IRQ, /* Timer Module 1 */
NPCM8XX_TIMER6_IRQ,
NPCM8XX_TIMER7_IRQ,
NPCM8XX_TIMER8_IRQ,
NPCM8XX_TIMER9_IRQ,
NPCM8XX_TIMER10_IRQ, /* Timer Module 2 */
NPCM8XX_TIMER11_IRQ,
NPCM8XX_TIMER12_IRQ,
NPCM8XX_TIMER13_IRQ,
NPCM8XX_TIMER14_IRQ,
NPCM8XX_WDG0_IRQ = 47, /* Timer Module 0 Watchdog */
NPCM8XX_WDG1_IRQ, /* Timer Module 1 Watchdog */
NPCM8XX_WDG2_IRQ, /* Timer Module 2 Watchdog */
NPCM8XX_EHCI1_IRQ = 61,
NPCM8XX_OHCI1_IRQ,
NPCM8XX_EHCI2_IRQ,
NPCM8XX_OHCI2_IRQ,
NPCM8XX_PWM0_IRQ = 93, /* PWM module 0 */
NPCM8XX_PWM1_IRQ, /* PWM module 1 */
NPCM8XX_MFT0_IRQ = 96, /* MFT module 0 */
NPCM8XX_MFT1_IRQ, /* MFT module 1 */
NPCM8XX_MFT2_IRQ, /* MFT module 2 */
NPCM8XX_MFT3_IRQ, /* MFT module 3 */
NPCM8XX_MFT4_IRQ, /* MFT module 4 */
NPCM8XX_MFT5_IRQ, /* MFT module 5 */
NPCM8XX_MFT6_IRQ, /* MFT module 6 */
NPCM8XX_MFT7_IRQ, /* MFT module 7 */
NPCM8XX_PCI_MBOX1_IRQ = 105,
NPCM8XX_PCI_MBOX2_IRQ,
NPCM8XX_GPIO0_IRQ = 116,
NPCM8XX_GPIO1_IRQ,
NPCM8XX_GPIO2_IRQ,
NPCM8XX_GPIO3_IRQ,
NPCM8XX_GPIO4_IRQ,
NPCM8XX_GPIO5_IRQ,
NPCM8XX_GPIO6_IRQ,
NPCM8XX_GPIO7_IRQ,
NPCM8XX_SMBUS0_IRQ = 128,
NPCM8XX_SMBUS1_IRQ,
NPCM8XX_SMBUS2_IRQ,
NPCM8XX_SMBUS3_IRQ,
NPCM8XX_SMBUS4_IRQ,
NPCM8XX_SMBUS5_IRQ,
NPCM8XX_SMBUS6_IRQ,
NPCM8XX_SMBUS7_IRQ,
NPCM8XX_SMBUS8_IRQ,
NPCM8XX_SMBUS9_IRQ,
NPCM8XX_SMBUS10_IRQ,
NPCM8XX_SMBUS11_IRQ,
NPCM8XX_SMBUS12_IRQ,
NPCM8XX_SMBUS13_IRQ,
NPCM8XX_SMBUS14_IRQ,
NPCM8XX_SMBUS15_IRQ,
NPCM8XX_SMBUS16_IRQ,
NPCM8XX_SMBUS17_IRQ,
NPCM8XX_SMBUS18_IRQ,
NPCM8XX_SMBUS19_IRQ,
NPCM8XX_SMBUS20_IRQ,
NPCM8XX_SMBUS21_IRQ,
NPCM8XX_SMBUS22_IRQ,
NPCM8XX_SMBUS23_IRQ,
NPCM8XX_SMBUS24_IRQ,
NPCM8XX_SMBUS25_IRQ,
NPCM8XX_SMBUS26_IRQ,
NPCM8XX_UART0_IRQ = 192,
NPCM8XX_UART1_IRQ,
NPCM8XX_UART2_IRQ,
NPCM8XX_UART3_IRQ,
NPCM8XX_UART4_IRQ,
NPCM8XX_UART5_IRQ,
NPCM8XX_UART6_IRQ,
};
/* Total number of GIC interrupts, including internal Cortex-A35 interrupts. */
#define NPCM8XX_NUM_IRQ (288)
#define NPCM8XX_PPI_BASE(cpu) \
((NPCM8XX_NUM_IRQ - GIC_INTERNAL) + (cpu) * GIC_INTERNAL)
/* Register base address for each Timer Module */
static const hwaddr npcm8xx_tim_addr[] = {
0xf0008000,
0xf0009000,
0xf000a000,
};
/* Register base address for each 16550 UART */
static const hwaddr npcm8xx_uart_addr[] = {
0xf0000000,
0xf0001000,
0xf0002000,
0xf0003000,
0xf0004000,
0xf0005000,
0xf0006000,
};
/* Direct memory-mapped access to SPI0 CS0-1. */
static const hwaddr npcm8xx_fiu0_flash_addr[] = {
0x80000000, /* CS0 */
0x88000000, /* CS1 */
};
/* Direct memory-mapped access to SPI1 CS0-3. */
static const hwaddr npcm8xx_fiu1_flash_addr[] = {
0x90000000, /* CS0 */
0x91000000, /* CS1 */
0x92000000, /* CS2 */
0x93000000, /* CS3 */
};
/* Direct memory-mapped access to SPI3 CS0-3. */
static const hwaddr npcm8xx_fiu3_flash_addr[] = {
0xa0000000, /* CS0 */
0xa8000000, /* CS1 */
0xb0000000, /* CS2 */
0xb8000000, /* CS3 */
};
/* Register base address for each PWM Module */
static const hwaddr npcm8xx_pwm_addr[] = {
0xf0103000,
0xf0104000,
0xf0105000,
};
/* Register base address for each MFT Module */
static const hwaddr npcm8xx_mft_addr[] = {
0xf0180000,
0xf0181000,
0xf0182000,
0xf0183000,
0xf0184000,
0xf0185000,
0xf0186000,
0xf0187000,
};
/* Direct memory-mapped access to each SMBus Module. */
static const hwaddr npcm8xx_smbus_addr[] = {
0xf0080000,
0xf0081000,
0xf0082000,
0xf0083000,
0xf0084000,
0xf0085000,
0xf0086000,
0xf0087000,
0xf0088000,
0xf0089000,
0xf008a000,
0xf008b000,
0xf008c000,
0xf008d000,
0xf008e000,
0xf008f000,
0xfff00000,
0xfff01000,
0xfff02000,
0xfff03000,
0xfff04000,
0xfff05000,
0xfff06000,
0xfff07000,
0xfff08000,
0xfff09000,
0xfff0a000,
};
/* Register base address for each USB host EHCI registers */
static const hwaddr npcm8xx_ehci_addr[] = {
0xf0828100,
0xf082a100,
};
/* Register base address for each USB host OHCI registers */
static const hwaddr npcm8xx_ohci_addr[] = {
0xf0829000,
0xf082b000,
};
static const struct {
hwaddr regs_addr;
uint32_t reset_pu;
uint32_t reset_pd;
uint32_t reset_osrc;
uint32_t reset_odsc;
} npcm8xx_gpio[] = {
{
.regs_addr = 0xf0010000,
.reset_pu = 0x00000300,
.reset_pd = 0x000f0000,
}, {
.regs_addr = 0xf0011000,
.reset_pu = 0xe0fefe01,
.reset_pd = 0x07000000,
}, {
.regs_addr = 0xf0012000,
.reset_pu = 0xc00fffff,
.reset_pd = 0x3ff00000,
}, {
.regs_addr = 0xf0013000,
.reset_pd = 0x00003000,
}, {
.regs_addr = 0xf0014000,
.reset_pu = 0xffff0000,
}, {
.regs_addr = 0xf0015000,
.reset_pu = 0xff8387fe,
.reset_pd = 0x007c0001,
.reset_osrc = 0x08000000,
}, {
.regs_addr = 0xf0016000,
.reset_pu = 0x00000801,
.reset_pd = 0x00000302,
}, {
.regs_addr = 0xf0017000,
.reset_pu = 0x000002ff,
.reset_pd = 0x00000c00,
},
};
static const struct {
const char *name;
hwaddr regs_addr;
int cs_count;
const hwaddr *flash_addr;
size_t flash_size;
} npcm8xx_fiu[] = {
{
.name = "fiu0",
.regs_addr = 0xfb000000,
.cs_count = ARRAY_SIZE(npcm8xx_fiu0_flash_addr),
.flash_addr = npcm8xx_fiu0_flash_addr,
.flash_size = 128 * MiB,
},
{
.name = "fiu1",
.regs_addr = 0xfb002000,
.cs_count = ARRAY_SIZE(npcm8xx_fiu1_flash_addr),
.flash_addr = npcm8xx_fiu1_flash_addr,
.flash_size = 16 * MiB,
}, {
.name = "fiu3",
.regs_addr = 0xc0000000,
.cs_count = ARRAY_SIZE(npcm8xx_fiu3_flash_addr),
.flash_addr = npcm8xx_fiu3_flash_addr,
.flash_size = 128 * MiB,
},
};
static struct arm_boot_info npcm8xx_binfo = {
.loader_start = NPCM8XX_LOADER_START,
.smp_loader_start = NPCM8XX_SMP_LOADER_START,
.smp_bootreg_addr = NPCM8XX_SMP_BOOTREG_ADDR,
.gic_cpu_if_addr = NPCM8XX_GICC_BA,
.secure_boot = false,
.board_id = -1,
.board_setup_addr = NPCM8XX_BOARD_SETUP_ADDR,
};
void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc)
{
npcm8xx_binfo.ram_size = machine->ram_size;
arm_load_kernel(&soc->cpu[0], machine, &npcm8xx_binfo);
}
static void npcm8xx_init_fuses(NPCM8xxState *s)
{
NPCM8xxClass *nc = NPCM8XX_GET_CLASS(s);
uint32_t value;
/*
* The initial mask of disabled modules indicates the chip derivative (e.g.
* NPCM750 or NPCM730).
*/
value = cpu_to_le32(nc->disabled_modules);
npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE,
sizeof(value));
}
static void npcm8xx_write_adc_calibration(NPCM8xxState *s)
{
/* Both ADC and the fuse array must have realized. */
QEMU_BUILD_BUG_ON(sizeof(s->adc.calibration_r_values) != 4);
npcm7xx_otp_array_write(&s->fuse_array, s->adc.calibration_r_values,
NPCM7XX_FUSE_ADC_CALIB, sizeof(s->adc.calibration_r_values));
}
static qemu_irq npcm8xx_irq(NPCM8xxState *s, int n)
{
return qdev_get_gpio_in(DEVICE(&s->gic), n);
}
static void npcm8xx_init(Object *obj)
{
NPCM8xxState *s = NPCM8XX(obj);
int i;
object_initialize_child(obj, "cpu-cluster", &s->cpu_cluster,
TYPE_CPU_CLUSTER);
for (i = 0; i < NPCM8XX_MAX_NUM_CPUS; i++) {
object_initialize_child(OBJECT(&s->cpu_cluster), "cpu[*]", &s->cpu[i],
ARM_CPU_TYPE_NAME("cortex-a35"));
}
object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GIC);
object_initialize_child(obj, "gcr", &s->gcr, TYPE_NPCM8XX_GCR);
object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
"power-on-straps");
object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM8XX_CLK);
object_initialize_child(obj, "otp", &s->fuse_array,
TYPE_NPCM7XX_FUSE_ARRAY);
object_initialize_child(obj, "mc", &s->mc, TYPE_NPCM7XX_MC);
object_initialize_child(obj, "rng", &s->rng, TYPE_NPCM7XX_RNG);
object_initialize_child(obj, "adc", &s->adc, TYPE_NPCM7XX_ADC);
for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
}
for (i = 0; i < ARRAY_SIZE(s->gpio); i++) {
object_initialize_child(obj, "gpio[*]", &s->gpio[i], TYPE_NPCM7XX_GPIO);
}
for (i = 0; i < ARRAY_SIZE(s->smbus); i++) {
object_initialize_child(obj, "smbus[*]", &s->smbus[i],
TYPE_NPCM7XX_SMBUS);
DEVICE(&s->smbus[i])->id = g_strdup_printf("smbus[%d]", i);
}
for (i = 0; i < ARRAY_SIZE(s->ehci); i++) {
object_initialize_child(obj, "ehci[*]", &s->ehci[i], TYPE_NPCM7XX_EHCI);
}
for (i = 0; i < ARRAY_SIZE(s->ohci); i++) {
object_initialize_child(obj, "ohci[*]", &s->ohci[i], TYPE_SYSBUS_OHCI);
}
QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_fiu) != ARRAY_SIZE(s->fiu));
for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
object_initialize_child(obj, npcm8xx_fiu[i].name, &s->fiu[i],
TYPE_NPCM7XX_FIU);
}
for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
object_initialize_child(obj, "pwm[*]", &s->pwm[i], TYPE_NPCM7XX_PWM);
}
for (i = 0; i < ARRAY_SIZE(s->mft); i++) {
object_initialize_child(obj, "mft[*]", &s->mft[i], TYPE_NPCM7XX_MFT);
}
object_initialize_child(obj, "mmc", &s->mmc, TYPE_NPCM7XX_SDHCI);
}
static void npcm8xx_realize(DeviceState *dev, Error **errp)
{
NPCM8xxState *s = NPCM8XX(dev);
NPCM8xxClass *nc = NPCM8XX_GET_CLASS(s);
int i;
if (memory_region_size(s->dram) > NPCM8XX_DRAM_SZ) {
error_setg(errp, "%s: NPCM8xx cannot address more than %" PRIu64
" MiB of DRAM", __func__, NPCM8XX_DRAM_SZ / MiB);
return;
}
/* CPUs */
for (i = 0; i < nc->num_cpus; i++) {
object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity",
arm_build_mp_affinity(i, NPCM8XX_MAX_NUM_CPUS),
&error_abort);
object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true,
&error_abort);
object_property_set_int(OBJECT(&s->cpu[i]), "core-count",
nc->num_cpus, &error_abort);
/* Disable security extensions. */
object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3", false,
&error_abort);
if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
return;
}
}
/* ARM GIC for Cortex A35. Can only fail if we pass bad parameters here. */
object_property_set_uint(OBJECT(&s->gic), "num-cpu", nc->num_cpus, errp);
object_property_set_uint(OBJECT(&s->gic), "num-irq", NPCM8XX_NUM_IRQ, errp);
object_property_set_uint(OBJECT(&s->gic), "revision", 2, errp);
object_property_set_bool(OBJECT(&s->gic), "has-security-extensions", true,
errp);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->gic), errp)) {
return;
}
for (i = 0; i < nc->num_cpus; i++) {
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i,
qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus,
qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus * 2,
qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_VIRQ));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus * 3,
qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_VFIQ));
qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_PHYS,
qdev_get_gpio_in(DEVICE(&s->gic),
NPCM8XX_PPI_BASE(i) + ARCH_TIMER_NS_EL1_IRQ));
qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_VIRT,
qdev_get_gpio_in(DEVICE(&s->gic),
NPCM8XX_PPI_BASE(i) + ARCH_TIMER_VIRT_IRQ));
qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_HYP,
qdev_get_gpio_in(DEVICE(&s->gic),
NPCM8XX_PPI_BASE(i) + ARCH_TIMER_NS_EL2_IRQ));
qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_SEC,
qdev_get_gpio_in(DEVICE(&s->gic),
NPCM8XX_PPI_BASE(i) + ARCH_TIMER_S_EL1_IRQ));
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, NPCM8XX_GICD_BA);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, NPCM8XX_GICC_BA);
/* CPU cluster */
qdev_prop_set_uint32(DEVICE(&s->cpu_cluster), "cluster-id", 0);
qdev_realize(DEVICE(&s->cpu_cluster), NULL, &error_fatal);
/* System Global Control Registers (GCR). Can fail due to user input. */
object_property_set_int(OBJECT(&s->gcr), "disabled-modules",
nc->disabled_modules, &error_abort);
object_property_add_const_link(OBJECT(&s->gcr), "dram-mr", OBJECT(s->dram));
if (!sysbus_realize(SYS_BUS_DEVICE(&s->gcr), errp)) {
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gcr), 0, NPCM8XX_GCR_BA);
/* Clock Control Registers (CLK). Cannot fail. */
sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM8XX_CLK_BA);
/* OTP fuse strap array. Cannot fail. */
sysbus_realize(SYS_BUS_DEVICE(&s->fuse_array), &error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM8XX_OTP_BA);
npcm8xx_init_fuses(s);
/* Fake Memory Controller (MC). Cannot fail. */
sysbus_realize(SYS_BUS_DEVICE(&s->mc), &error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->mc), 0, NPCM8XX_MC_BA);
/* ADC Modules. Cannot fail. */
qdev_connect_clock_in(DEVICE(&s->adc), "clock", qdev_get_clock_out(
DEVICE(&s->clk), "adc-clock"));
sysbus_realize(SYS_BUS_DEVICE(&s->adc), &error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, NPCM8XX_ADC_BA);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0,
npcm8xx_irq(s, NPCM8XX_ADC_IRQ));
npcm8xx_write_adc_calibration(s);
/* Timer Modules (TIM). Cannot fail. */
QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_tim_addr) != ARRAY_SIZE(s->tim));
for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
SysBusDevice *sbd = SYS_BUS_DEVICE(&s->tim[i]);
int first_irq;
int j;
/* Connect the timer clock. */
qdev_connect_clock_in(DEVICE(&s->tim[i]), "clock", qdev_get_clock_out(
DEVICE(&s->clk), "timer-clock"));
sysbus_realize(sbd, &error_abort);
sysbus_mmio_map(sbd, 0, npcm8xx_tim_addr[i]);
first_irq = NPCM8XX_TIMER0_IRQ + i * NPCM7XX_TIMERS_PER_CTRL;
for (j = 0; j < NPCM7XX_TIMERS_PER_CTRL; j++) {
qemu_irq irq = npcm8xx_irq(s, first_irq + j);
sysbus_connect_irq(sbd, j, irq);
}
/* IRQ for watchdogs */
sysbus_connect_irq(sbd, NPCM7XX_TIMERS_PER_CTRL,
npcm8xx_irq(s, NPCM8XX_WDG0_IRQ + i));
/* GPIO that connects clk module with watchdog */
qdev_connect_gpio_out_named(DEVICE(&s->tim[i]),
NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 0,
qdev_get_gpio_in_named(DEVICE(&s->clk),
NPCM7XX_WATCHDOG_RESET_GPIO_IN, i));
}
/* UART0..6 (16550 compatible) */
for (i = 0; i < ARRAY_SIZE(npcm8xx_uart_addr); i++) {
serial_mm_init(get_system_memory(), npcm8xx_uart_addr[i], 2,
npcm8xx_irq(s, NPCM8XX_UART0_IRQ + i), 115200,
serial_hd(i), DEVICE_LITTLE_ENDIAN);
}
/* Random Number Generator. Cannot fail. */
sysbus_realize(SYS_BUS_DEVICE(&s->rng), &error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->rng), 0, NPCM8XX_RNG_BA);
/* GPIO modules. Cannot fail. */
QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_gpio) != ARRAY_SIZE(s->gpio));
for (i = 0; i < ARRAY_SIZE(s->gpio); i++) {
Object *obj = OBJECT(&s->gpio[i]);
object_property_set_uint(obj, "reset-pullup",
npcm8xx_gpio[i].reset_pu, &error_abort);
object_property_set_uint(obj, "reset-pulldown",
npcm8xx_gpio[i].reset_pd, &error_abort);
object_property_set_uint(obj, "reset-osrc",
npcm8xx_gpio[i].reset_osrc, &error_abort);
object_property_set_uint(obj, "reset-odsc",
npcm8xx_gpio[i].reset_odsc, &error_abort);
sysbus_realize(SYS_BUS_DEVICE(obj), &error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm8xx_gpio[i].regs_addr);
sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0,
npcm8xx_irq(s, NPCM8XX_GPIO0_IRQ + i));
}
/* SMBus modules. Cannot fail. */
QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_smbus_addr) != ARRAY_SIZE(s->smbus));
for (i = 0; i < ARRAY_SIZE(s->smbus); i++) {
Object *obj = OBJECT(&s->smbus[i]);
sysbus_realize(SYS_BUS_DEVICE(obj), &error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm8xx_smbus_addr[i]);
sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0,
npcm8xx_irq(s, NPCM8XX_SMBUS0_IRQ + i));
}
/* USB Host */
QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->ohci) != ARRAY_SIZE(s->ehci));
for (i = 0; i < ARRAY_SIZE(s->ehci); i++) {
object_property_set_bool(OBJECT(&s->ehci[i]), "companion-enable", true,
&error_abort);
sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), &error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, npcm8xx_ehci_addr[i]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0,
npcm8xx_irq(s, NPCM8XX_EHCI1_IRQ + 2 * i));
}
for (i = 0; i < ARRAY_SIZE(s->ohci); i++) {
object_property_set_str(OBJECT(&s->ohci[i]), "masterbus", "usb-bus.0",
&error_abort);
object_property_set_uint(OBJECT(&s->ohci[i]), "num-ports", 1,
&error_abort);
object_property_set_uint(OBJECT(&s->ohci[i]), "firstport", i,
&error_abort);
sysbus_realize(SYS_BUS_DEVICE(&s->ohci[i]), &error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->ohci[i]), 0, npcm8xx_ohci_addr[i]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->ohci[i]), 0,
npcm8xx_irq(s, NPCM8XX_OHCI1_IRQ + 2 * i));
}
/* PWM Modules. Cannot fail. */
QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_pwm_addr) != ARRAY_SIZE(s->pwm));
for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
SysBusDevice *sbd = SYS_BUS_DEVICE(&s->pwm[i]);
qdev_connect_clock_in(DEVICE(&s->pwm[i]), "clock", qdev_get_clock_out(
DEVICE(&s->clk), "apb3-clock"));
sysbus_realize(sbd, &error_abort);
sysbus_mmio_map(sbd, 0, npcm8xx_pwm_addr[i]);
sysbus_connect_irq(sbd, i, npcm8xx_irq(s, NPCM8XX_PWM0_IRQ + i));
}
/* MFT Modules. Cannot fail. */
QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_mft_addr) != ARRAY_SIZE(s->mft));
for (i = 0; i < ARRAY_SIZE(s->mft); i++) {
SysBusDevice *sbd = SYS_BUS_DEVICE(&s->mft[i]);
qdev_connect_clock_in(DEVICE(&s->mft[i]), "clock-in",
qdev_get_clock_out(DEVICE(&s->clk),
"apb4-clock"));
sysbus_realize(sbd, &error_abort);
sysbus_mmio_map(sbd, 0, npcm8xx_mft_addr[i]);
sysbus_connect_irq(sbd, 0, npcm8xx_irq(s, NPCM8XX_MFT0_IRQ + i));
}
/*
* Flash Interface Unit (FIU). Can fail if incorrect number of chip selects
* specified, but this is a programming error.
*/
QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_fiu) != ARRAY_SIZE(s->fiu));
for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
SysBusDevice *sbd = SYS_BUS_DEVICE(&s->fiu[i]);
int j;
object_property_set_int(OBJECT(sbd), "cs-count",
npcm8xx_fiu[i].cs_count, &error_abort);
object_property_set_int(OBJECT(sbd), "flash-size",
npcm8xx_fiu[i].flash_size, &error_abort);
sysbus_realize(sbd, &error_abort);
sysbus_mmio_map(sbd, 0, npcm8xx_fiu[i].regs_addr);
for (j = 0; j < npcm8xx_fiu[i].cs_count; j++) {
sysbus_mmio_map(sbd, j + 1, npcm8xx_fiu[i].flash_addr[j]);
}
}
/* RAM2 (SRAM) */
memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
NPCM8XX_RAM2_SZ, &error_abort);
memory_region_add_subregion(get_system_memory(), NPCM8XX_RAM2_BA, &s->sram);
/* RAM3 (SRAM) */
memory_region_init_ram(&s->ram3, OBJECT(dev), "ram3",
NPCM8XX_RAM3_SZ, &error_abort);
memory_region_add_subregion(get_system_memory(), NPCM8XX_RAM3_BA, &s->ram3);
/* Internal ROM */
memory_region_init_rom(&s->irom, OBJECT(dev), "irom", NPCM8XX_ROM_SZ,
&error_abort);
memory_region_add_subregion(get_system_memory(), NPCM8XX_ROM_BA, &s->irom);
/* SDHCI */
sysbus_realize(SYS_BUS_DEVICE(&s->mmc), &error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc), 0, NPCM8XX_MMC_BA);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc), 0,
npcm8xx_irq(s, NPCM8XX_MMC_IRQ));
create_unimplemented_device("npcm8xx.shm", 0xc0001000, 4 * KiB);
create_unimplemented_device("npcm8xx.gicextra", 0xdfffa000, 24 * KiB);
create_unimplemented_device("npcm8xx.vdmx", 0xe0800000, 4 * KiB);
create_unimplemented_device("npcm8xx.pcierc", 0xe1000000, 64 * KiB);
create_unimplemented_device("npcm8xx.rootc", 0xe8000000, 128 * MiB);
create_unimplemented_device("npcm8xx.kcs", 0xf0007000, 4 * KiB);
create_unimplemented_device("npcm8xx.gfxi", 0xf000e000, 4 * KiB);
create_unimplemented_device("npcm8xx.fsw", 0xf000f000, 4 * KiB);
create_unimplemented_device("npcm8xx.bt", 0xf0030000, 4 * KiB);
create_unimplemented_device("npcm8xx.espi", 0xf009f000, 4 * KiB);
create_unimplemented_device("npcm8xx.peci", 0xf0100000, 4 * KiB);
create_unimplemented_device("npcm8xx.siox[1]", 0xf0101000, 4 * KiB);
create_unimplemented_device("npcm8xx.siox[2]", 0xf0102000, 4 * KiB);
create_unimplemented_device("npcm8xx.tmps", 0xf0188000, 4 * KiB);
create_unimplemented_device("npcm8xx.pspi", 0xf0201000, 4 * KiB);
create_unimplemented_device("npcm8xx.viru1", 0xf0204000, 4 * KiB);
create_unimplemented_device("npcm8xx.viru2", 0xf0205000, 4 * KiB);
create_unimplemented_device("npcm8xx.jtm1", 0xf0208000, 4 * KiB);
create_unimplemented_device("npcm8xx.jtm2", 0xf0209000, 4 * KiB);
create_unimplemented_device("npcm8xx.flm0", 0xf0210000, 4 * KiB);
create_unimplemented_device("npcm8xx.flm1", 0xf0211000, 4 * KiB);
create_unimplemented_device("npcm8xx.flm2", 0xf0212000, 4 * KiB);
create_unimplemented_device("npcm8xx.flm3", 0xf0213000, 4 * KiB);
create_unimplemented_device("npcm8xx.ahbpci", 0xf0400000, 1 * MiB);
create_unimplemented_device("npcm8xx.dap", 0xf0500000, 960 * KiB);
create_unimplemented_device("npcm8xx.mcphy", 0xf05f0000, 64 * KiB);
create_unimplemented_device("npcm8xx.pcs", 0xf0780000, 256 * KiB);
create_unimplemented_device("npcm8xx.tsgen", 0xf07fc000, 8 * KiB);
create_unimplemented_device("npcm8xx.gmac1", 0xf0802000, 8 * KiB);
create_unimplemented_device("npcm8xx.gmac2", 0xf0804000, 8 * KiB);
create_unimplemented_device("npcm8xx.gmac3", 0xf0806000, 8 * KiB);
create_unimplemented_device("npcm8xx.gmac4", 0xf0808000, 8 * KiB);
create_unimplemented_device("npcm8xx.copctl", 0xf080c000, 4 * KiB);
create_unimplemented_device("npcm8xx.tipctl", 0xf080d000, 4 * KiB);
create_unimplemented_device("npcm8xx.rst", 0xf080e000, 4 * KiB);
create_unimplemented_device("npcm8xx.vcd", 0xf0810000, 64 * KiB);
create_unimplemented_device("npcm8xx.ece", 0xf0820000, 8 * KiB);
create_unimplemented_device("npcm8xx.vdma", 0xf0822000, 8 * KiB);
create_unimplemented_device("npcm8xx.usbd[0]", 0xf0830000, 4 * KiB);
create_unimplemented_device("npcm8xx.usbd[1]", 0xf0831000, 4 * KiB);
create_unimplemented_device("npcm8xx.usbd[2]", 0xf0832000, 4 * KiB);
create_unimplemented_device("npcm8xx.usbd[3]", 0xf0833000, 4 * KiB);
create_unimplemented_device("npcm8xx.usbd[4]", 0xf0834000, 4 * KiB);
create_unimplemented_device("npcm8xx.usbd[5]", 0xf0835000, 4 * KiB);
create_unimplemented_device("npcm8xx.usbd[6]", 0xf0836000, 4 * KiB);
create_unimplemented_device("npcm8xx.usbd[7]", 0xf0837000, 4 * KiB);
create_unimplemented_device("npcm8xx.usbd[8]", 0xf0838000, 4 * KiB);
create_unimplemented_device("npcm8xx.usbd[9]", 0xf0839000, 4 * KiB);
create_unimplemented_device("npcm8xx.pci_mbox1", 0xf0848000, 64 * KiB);
create_unimplemented_device("npcm8xx.gdma0", 0xf0850000, 4 * KiB);
create_unimplemented_device("npcm8xx.gdma1", 0xf0851000, 4 * KiB);
create_unimplemented_device("npcm8xx.gdma2", 0xf0852000, 4 * KiB);
create_unimplemented_device("npcm8xx.aes", 0xf0858000, 4 * KiB);
create_unimplemented_device("npcm8xx.des", 0xf0859000, 4 * KiB);
create_unimplemented_device("npcm8xx.sha", 0xf085a000, 4 * KiB);
create_unimplemented_device("npcm8xx.pci_mbox2", 0xf0868000, 64 * KiB);
create_unimplemented_device("npcm8xx.i3c0", 0xfff10000, 4 * KiB);
create_unimplemented_device("npcm8xx.i3c1", 0xfff11000, 4 * KiB);
create_unimplemented_device("npcm8xx.i3c2", 0xfff12000, 4 * KiB);
create_unimplemented_device("npcm8xx.i3c3", 0xfff13000, 4 * KiB);
create_unimplemented_device("npcm8xx.i3c4", 0xfff14000, 4 * KiB);
create_unimplemented_device("npcm8xx.i3c5", 0xfff15000, 4 * KiB);
create_unimplemented_device("npcm8xx.spixcs0", 0xf8000000, 16 * MiB);
create_unimplemented_device("npcm8xx.spixcs1", 0xf9000000, 16 * MiB);
create_unimplemented_device("npcm8xx.spix", 0xfb001000, 4 * KiB);
create_unimplemented_device("npcm8xx.vect", 0xffff0000, 256);
}
static const Property npcm8xx_properties[] = {
DEFINE_PROP_LINK("dram-mr", NPCM8xxState, dram, TYPE_MEMORY_REGION,
MemoryRegion *),
};
static void npcm8xx_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
NPCM8xxClass *nc = NPCM8XX_CLASS(oc);
dc->realize = npcm8xx_realize;
dc->user_creatable = false;
nc->disabled_modules = 0x00000000;
nc->num_cpus = NPCM8XX_MAX_NUM_CPUS;
device_class_set_props(dc, npcm8xx_properties);
}
static const TypeInfo npcm8xx_soc_types[] = {
{
.name = TYPE_NPCM8XX,
.parent = TYPE_DEVICE,
.instance_size = sizeof(NPCM8xxState),
.instance_init = npcm8xx_init,
.class_size = sizeof(NPCM8xxClass),
.class_init = npcm8xx_class_init,
},
};
DEFINE_TYPES(npcm8xx_soc_types);

254
hw/arm/npcm8xx_boards.c Normal file
View File

@ -0,0 +1,254 @@
/*
* Machine definitions for boards featuring an NPCM8xx SoC.
*
* Copyright 2021 Google LLC
*
* 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.
*/
#include "qemu/osdep.h"
#include "chardev/char.h"
#include "hw/boards.h"
#include "hw/arm/npcm8xx.h"
#include "hw/core/cpu.h"
#include "hw/loader.h"
#include "hw/qdev-core.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/datadir.h"
#include "qemu/units.h"
#define NPCM845_EVB_POWER_ON_STRAPS 0x000017ff
static const char npcm8xx_default_bootrom[] = "npcm8xx_bootrom.bin";
static void npcm8xx_load_bootrom(MachineState *machine, NPCM8xxState *soc)
{
const char *bios_name = machine->firmware ?: npcm8xx_default_bootrom;
g_autofree char *filename = NULL;
int ret;
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (!filename) {
error_report("Could not find ROM image '%s'", bios_name);
if (!machine->kernel_filename) {
/* We can't boot without a bootrom or a kernel image. */
exit(1);
}
return;
}
ret = load_image_mr(filename, machine->ram);
if (ret < 0) {
error_report("Failed to load ROM image '%s'", filename);
exit(1);
}
}
static void npcm8xx_connect_flash(NPCM7xxFIUState *fiu, int cs_no,
const char *flash_type, DriveInfo *dinfo)
{
DeviceState *flash;
qemu_irq flash_cs;
flash = qdev_new(flash_type);
if (dinfo) {
qdev_prop_set_drive(flash, "drive", blk_by_legacy_dinfo(dinfo));
}
qdev_realize_and_unref(flash, BUS(fiu->spi), &error_fatal);
flash_cs = qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0);
qdev_connect_gpio_out_named(DEVICE(fiu), "cs", cs_no, flash_cs);
}
static void npcm8xx_connect_dram(NPCM8xxState *soc, MemoryRegion *dram)
{
memory_region_add_subregion(get_system_memory(), NPCM8XX_DRAM_BA, dram);
object_property_set_link(OBJECT(soc), "dram-mr", OBJECT(dram),
&error_abort);
}
static NPCM8xxState *npcm8xx_create_soc(MachineState *machine,
uint32_t hw_straps)
{
NPCM8xxMachineClass *nmc = NPCM8XX_MACHINE_GET_CLASS(machine);
Object *obj;
obj = object_new_with_props(nmc->soc_type, OBJECT(machine), "soc",
&error_abort, NULL);
object_property_set_uint(obj, "power-on-straps", hw_straps, &error_abort);
return NPCM8XX(obj);
}
static I2CBus *npcm8xx_i2c_get_bus(NPCM8xxState *soc, uint32_t num)
{
g_assert(num < ARRAY_SIZE(soc->smbus));
return I2C_BUS(qdev_get_child_bus(DEVICE(&soc->smbus[num]), "i2c-bus"));
}
static void npcm8xx_init_pwm_splitter(NPCM8xxMachine *machine,
NPCM8xxState *soc, const int *fan_counts)
{
SplitIRQ *splitters = machine->fan_splitter;
/*
* PWM 0~3 belong to module 0 output 0~3.
* PWM 4~7 belong to module 1 output 0~3.
*/
for (int i = 0; i < NPCM8XX_NR_PWM_MODULES; ++i) {
for (int j = 0; j < NPCM7XX_PWM_PER_MODULE; ++j) {
int splitter_no = i * NPCM7XX_PWM_PER_MODULE + j;
DeviceState *splitter;
if (fan_counts[splitter_no] < 1) {
continue;
}
object_initialize_child(OBJECT(machine), "fan-splitter[*]",
&splitters[splitter_no], TYPE_SPLIT_IRQ);
splitter = DEVICE(&splitters[splitter_no]);
qdev_prop_set_uint16(splitter, "num-lines",
fan_counts[splitter_no]);
qdev_realize(splitter, NULL, &error_abort);
qdev_connect_gpio_out_named(DEVICE(&soc->pwm[i]), "duty-gpio-out",
j, qdev_get_gpio_in(splitter, 0));
}
}
}
static void npcm8xx_connect_pwm_fan(NPCM8xxState *soc, SplitIRQ *splitter,
int fan_no, int output_no)
{
DeviceState *fan;
int fan_input;
qemu_irq fan_duty_gpio;
g_assert(fan_no >= 0 && fan_no <= NPCM7XX_MFT_MAX_FAN_INPUT);
/*
* Fan 0~1 belong to module 0 input 0~1.
* Fan 2~3 belong to module 1 input 0~1.
* ...
* Fan 14~15 belong to module 7 input 0~1.
* Fan 16~17 belong to module 0 input 2~3.
* Fan 18~19 belong to module 1 input 2~3.
*/
if (fan_no < 16) {
fan = DEVICE(&soc->mft[fan_no / 2]);
fan_input = fan_no % 2;
} else {
fan = DEVICE(&soc->mft[(fan_no - 16) / 2]);
fan_input = fan_no % 2 + 2;
}
/* Connect the Fan to PWM module */
fan_duty_gpio = qdev_get_gpio_in_named(fan, "duty", fan_input);
qdev_connect_gpio_out(DEVICE(splitter), output_no, fan_duty_gpio);
}
static void npcm845_evb_i2c_init(NPCM8xxState *soc)
{
/* tmp100 temperature sensor on SVB, tmp105 is compatible */
i2c_slave_create_simple(npcm8xx_i2c_get_bus(soc, 6), "tmp105", 0x48);
}
static void npcm845_evb_fan_init(NPCM8xxMachine *machine, NPCM8xxState *soc)
{
SplitIRQ *splitter = machine->fan_splitter;
static const int fan_counts[] = {2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0};
npcm8xx_init_pwm_splitter(machine, soc, fan_counts);
npcm8xx_connect_pwm_fan(soc, &splitter[0], 0x00, 0);
npcm8xx_connect_pwm_fan(soc, &splitter[0], 0x01, 1);
npcm8xx_connect_pwm_fan(soc, &splitter[1], 0x02, 0);
npcm8xx_connect_pwm_fan(soc, &splitter[1], 0x03, 1);
npcm8xx_connect_pwm_fan(soc, &splitter[2], 0x04, 0);
npcm8xx_connect_pwm_fan(soc, &splitter[2], 0x05, 1);
npcm8xx_connect_pwm_fan(soc, &splitter[3], 0x06, 0);
npcm8xx_connect_pwm_fan(soc, &splitter[3], 0x07, 1);
npcm8xx_connect_pwm_fan(soc, &splitter[4], 0x08, 0);
npcm8xx_connect_pwm_fan(soc, &splitter[4], 0x09, 1);
npcm8xx_connect_pwm_fan(soc, &splitter[5], 0x0a, 0);
npcm8xx_connect_pwm_fan(soc, &splitter[5], 0x0b, 1);
npcm8xx_connect_pwm_fan(soc, &splitter[6], 0x0c, 0);
npcm8xx_connect_pwm_fan(soc, &splitter[6], 0x0d, 1);
npcm8xx_connect_pwm_fan(soc, &splitter[7], 0x0e, 0);
npcm8xx_connect_pwm_fan(soc, &splitter[7], 0x0f, 1);
}
static void npcm845_evb_init(MachineState *machine)
{
NPCM8xxState *soc;
soc = npcm8xx_create_soc(machine, NPCM845_EVB_POWER_ON_STRAPS);
npcm8xx_connect_dram(soc, machine->ram);
qdev_realize(DEVICE(soc), NULL, &error_fatal);
npcm8xx_load_bootrom(machine, soc);
npcm8xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0));
npcm845_evb_i2c_init(soc);
npcm845_evb_fan_init(NPCM8XX_MACHINE(machine), soc);
npcm8xx_load_kernel(machine, soc);
}
static void npcm8xx_set_soc_type(NPCM8xxMachineClass *nmc, const char *type)
{
NPCM8xxClass *sc = NPCM8XX_CLASS(object_class_by_name(type));
MachineClass *mc = MACHINE_CLASS(nmc);
nmc->soc_type = type;
mc->default_cpus = mc->min_cpus = mc->max_cpus = sc->num_cpus;
}
static void npcm8xx_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
static const char * const valid_cpu_types[] = {
ARM_CPU_TYPE_NAME("cortex-a9"),
NULL
};
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->no_parallel = 1;
mc->default_ram_id = "ram";
mc->valid_cpu_types = valid_cpu_types;
}
static void npcm845_evb_machine_class_init(ObjectClass *oc, void *data)
{
NPCM8xxMachineClass *nmc = NPCM8XX_MACHINE_CLASS(oc);
MachineClass *mc = MACHINE_CLASS(oc);
npcm8xx_set_soc_type(nmc, TYPE_NPCM8XX);
mc->desc = "Nuvoton NPCM845 Evaluation Board (Cortex-A35)";
mc->init = npcm845_evb_init;
mc->default_ram_size = 1 * GiB;
};
static const TypeInfo npcm8xx_machine_types[] = {
{
.name = TYPE_NPCM8XX_MACHINE,
.parent = TYPE_MACHINE,
.instance_size = sizeof(NPCM8xxMachine),
.class_size = sizeof(NPCM8xxMachineClass),
.class_init = npcm8xx_machine_class_init,
.abstract = true,
}, {
.name = MACHINE_TYPE_NAME("npcm845-evb"),
.parent = TYPE_NPCM8XX_MACHINE,
.class_init = npcm845_evb_machine_class_init,
},
};
DEFINE_TYPES(npcm8xx_machine_types)

View File

@ -35,6 +35,8 @@
#define SMP_BOOT_ADDR 0xe0000000
#define SMP_BOOTREG_ADDR 0x10000030
#define GIC_EXT_IRQS 64 /* Realview PBX-A9 development board */
/* Board init. */
static struct arm_boot_info realview_binfo = {
@ -185,7 +187,12 @@ static void realview_init(MachineState *machine,
sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, 0x10000000);
if (is_mpcore) {
dev = qdev_new(is_pb ? TYPE_A9MPCORE_PRIV : "realview_mpcore");
if (is_pb) {
dev = qdev_new(TYPE_A9MPCORE_PRIV);
qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL);
} else {
dev = qdev_new("realview_mpcore");
}
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
@ -201,7 +208,7 @@ static void realview_init(MachineState *machine,
/* For now just create the nIRQ GIC, and ignore the others. */
dev = sysbus_create_simple(TYPE_REALVIEW_GIC, gic_addr, cpu_irq[0]);
}
for (n = 0; n < 64; n++) {
for (n = 0; n < GIC_EXT_IRQS; n++) {
pic[n] = qdev_get_gpio_in(dev, n);
}

View File

@ -51,6 +51,8 @@
#define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024)
#define VEXPRESS_FLASH_SECT_SIZE (256 * 1024)
#define GIC_EXT_IRQS 64 /* Versatile Express A9 development board */
/* Number of virtio transports to create (0..8; limited by
* number of available IRQ lines).
*/
@ -241,6 +243,7 @@ static void init_cpus(MachineState *ms, const char *cpu_type,
*/
dev = qdev_new(privdev);
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, periphbase);
@ -251,7 +254,7 @@ static void init_cpus(MachineState *ms, const char *cpu_type,
* external interrupts starting from 32 (because there
* are internal interrupts 0..31).
*/
for (n = 0; n < 64; n++) {
for (n = 0; n < GIC_EXT_IRQS; n++) {
pic[n] = qdev_get_gpio_in(dev, n);
}
@ -543,7 +546,7 @@ static void vexpress_common_init(MachineState *machine)
VexpressMachineClass *vmc = VEXPRESS_MACHINE_GET_CLASS(machine);
VEDBoardInfo *daughterboard = vmc->daughterboard;
DeviceState *dev, *sysctl, *pl041;
qemu_irq pic[64];
qemu_irq pic[GIC_EXT_IRQS];
uint32_t sys_id;
DriveInfo *dinfo;
PFlashCFI01 *pflash0;

View File

@ -54,11 +54,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(ZynqMachineState, ZYNQ_MACHINE)
#define FLASH_SIZE (64 * 1024 * 1024)
#define FLASH_SECTOR_SIZE (128 * 1024)
#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */
#define MPCORE_PERIPHBASE 0xF8F00000
#define ZYNQ_BOARD_MIDR 0x413FC090
#define GIC_EXT_IRQS 64 /* Zynq 7000 SoC */
static const int dma_irqs[8] = {
46, 47, 48, 49, 72, 73, 74, 75
};
@ -207,7 +207,7 @@ static void zynq_init(MachineState *machine)
MemoryRegion *ocm_ram = g_new(MemoryRegion, 1);
DeviceState *dev, *slcr;
SysBusDevice *busdev;
qemu_irq pic[64];
qemu_irq pic[GIC_EXT_IRQS];
int n;
unsigned int smp_cpus = machine->smp.cpus;
@ -263,6 +263,7 @@ static void zynq_init(MachineState *machine)
dev = qdev_new(TYPE_A9MPCORE_PRIV);
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
@ -277,16 +278,16 @@ static void zynq_init(MachineState *machine)
qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
}
for (n = 0; n < 64; n++) {
for (n = 0; n < GIC_EXT_IRQS; n++) {
pic[n] = qdev_get_gpio_in(dev, n);
}
n = zynq_init_spi_flashes(0xE0006000, pic[58 - IRQ_OFFSET], false, 0);
n = zynq_init_spi_flashes(0xE0007000, pic[81 - IRQ_OFFSET], false, n);
n = zynq_init_spi_flashes(0xE000D000, pic[51 - IRQ_OFFSET], true, n);
n = zynq_init_spi_flashes(0xE0006000, pic[58 - GIC_INTERNAL], false, 0);
n = zynq_init_spi_flashes(0xE0007000, pic[81 - GIC_INTERNAL], false, n);
n = zynq_init_spi_flashes(0xE000D000, pic[51 - GIC_INTERNAL], true, n);
sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - IRQ_OFFSET]);
sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - IRQ_OFFSET]);
sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - GIC_INTERNAL]);
sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - GIC_INTERNAL]);
dev = qdev_new(TYPE_CADENCE_UART);
busdev = SYS_BUS_DEVICE(dev);
@ -295,7 +296,7 @@ static void zynq_init(MachineState *machine)
qdev_get_clock_out(slcr, "uart0_ref_clk"));
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, 0xE0000000);
sysbus_connect_irq(busdev, 0, pic[59 - IRQ_OFFSET]);
sysbus_connect_irq(busdev, 0, pic[59 - GIC_INTERNAL]);
dev = qdev_new(TYPE_CADENCE_UART);
busdev = SYS_BUS_DEVICE(dev);
qdev_prop_set_chr(dev, "chardev", serial_hd(1));
@ -303,15 +304,15 @@ static void zynq_init(MachineState *machine)
qdev_get_clock_out(slcr, "uart1_ref_clk"));
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, 0xE0001000);
sysbus_connect_irq(busdev, 0, pic[82 - IRQ_OFFSET]);
sysbus_connect_irq(busdev, 0, pic[82 - GIC_INTERNAL]);
sysbus_create_varargs("cadence_ttc", 0xF8001000,
pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL);
pic[42-GIC_INTERNAL], pic[43-GIC_INTERNAL], pic[44-GIC_INTERNAL], NULL);
sysbus_create_varargs("cadence_ttc", 0xF8002000,
pic[69-IRQ_OFFSET], pic[70-IRQ_OFFSET], pic[71-IRQ_OFFSET], NULL);
pic[69-GIC_INTERNAL], pic[70-GIC_INTERNAL], pic[71-GIC_INTERNAL], NULL);
gem_init(0xE000B000, pic[54 - IRQ_OFFSET]);
gem_init(0xE000C000, pic[77 - IRQ_OFFSET]);
gem_init(0xE000B000, pic[54 - GIC_INTERNAL]);
gem_init(0xE000C000, pic[77 - GIC_INTERNAL]);
for (n = 0; n < 2; n++) {
int hci_irq = n ? 79 : 56;
@ -330,7 +331,7 @@ static void zynq_init(MachineState *machine)
qdev_prop_set_uint64(dev, "capareg", ZYNQ_SDHCI_CAPABILITIES);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, hci_addr);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - IRQ_OFFSET]);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - GIC_INTERNAL]);
di = drive_get(IF_SD, 0, n);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
@ -343,7 +344,7 @@ static void zynq_init(MachineState *machine)
dev = qdev_new(TYPE_ZYNQ_XADC);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8007100);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-IRQ_OFFSET]);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-GIC_INTERNAL]);
dev = qdev_new("pl330");
object_property_set_link(OBJECT(dev), "memory",
@ -363,15 +364,15 @@ static void zynq_init(MachineState *machine)
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, 0xF8003000);
sysbus_connect_irq(busdev, 0, pic[45-IRQ_OFFSET]); /* abort irq line */
sysbus_connect_irq(busdev, 0, pic[45-GIC_INTERNAL]); /* abort irq line */
for (n = 0; n < ARRAY_SIZE(dma_irqs); ++n) { /* event irqs */
sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - IRQ_OFFSET]);
sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - GIC_INTERNAL]);
}
dev = qdev_new("xlnx.ps7-dev-cfg");
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]);
sysbus_connect_irq(busdev, 0, pic[40 - GIC_INTERNAL]);
sysbus_mmio_map(busdev, 0, 0xF8007000);
/*
@ -460,7 +461,7 @@ static void zynq_machine_class_init(ObjectClass *oc, void *data)
};
MachineClass *mc = MACHINE_CLASS(oc);
ObjectProperty *prop;
mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9";
mc->desc = "Xilinx Zynq 7000 Platform Baseboard for Cortex-A9";
mc->init = zynq_init;
mc->max_cpus = ZYNQ_MAX_CPUS;
mc->ignore_memory_transaction_failures = true;

View File

@ -58,6 +58,11 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
bool has_el2 = false;
Object *cpuobj;
if (s->num_irq < 32 || s->num_irq > 256) {
error_setg(errp, "Property 'num-irq' must be between 32 and 256");
return;
}
gicdev = DEVICE(&s->gic);
qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu);
qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq);
@ -146,13 +151,14 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
static const Property a15mp_priv_properties[] = {
DEFINE_PROP_UINT32("num-cpu", A15MPPrivState, num_cpu, 1),
/* The Cortex-A15MP may have anything from 0 to 224 external interrupt
* IRQ lines (with another 32 internal). We default to 128+32, which
* is the number provided by the Cortex-A15MP test chip in the
* Versatile Express A15 development board.
* Other boards may differ and should set this property appropriately.
/*
* The Cortex-A15MP may have anything from 0 to 224 external interrupt
* lines, plus always 32 internal IRQs. This property sets the total
* of internal + external, so the valid range is from 32 to 256.
* The board model must set this to whatever the configuration
* used for the CPU on that board or SoC is.
*/
DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 160),
DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 0),
};
static void a15mp_priv_class_init(ObjectClass *klass, void *data)

View File

@ -56,6 +56,11 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
CPUState *cpu0;
Object *cpuobj;
if (s->num_irq < 32 || s->num_irq > 256) {
error_setg(errp, "Property 'num-irq' must be between 32 and 256");
return;
}
cpu0 = qemu_get_cpu(0);
cpuobj = OBJECT(cpu0);
if (strcmp(object_get_typename(cpuobj), ARM_CPU_TYPE_NAME("cortex-a9"))) {
@ -160,13 +165,14 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
static const Property a9mp_priv_properties[] = {
DEFINE_PROP_UINT32("num-cpu", A9MPPrivState, num_cpu, 1),
/* The Cortex-A9MP may have anything from 0 to 224 external interrupt
* IRQ lines (with another 32 internal). We default to 64+32, which
* is the number provided by the Cortex-A9MP test chip in the
* Realview PBX-A9 and Versatile Express A9 development boards.
* Other boards may differ and should set this property appropriately.
/*
* The Cortex-A9MP may have anything from 0 to 224 external interrupt
* lines, plus always 32 internal IRQs. This property sets the total
* of internal + external, so the valid range is from 32 to 256.
* The board model must set this to whatever the configuration
* used for the CPU on that board or SoC is.
*/
DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 96),
DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 0),
};
static void a9mp_priv_class_init(ObjectClass *klass, void *data)

View File

@ -2291,7 +2291,7 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env,
r = CP_ACCESS_TRAP_EL3;
break;
case 3:
if (!is_a64(env) && !arm_is_el3_or_mon(env)) {
if (!arm_is_el3_or_mon(env)) {
r = CP_ACCESS_TRAP_EL3;
}
break;
@ -2300,9 +2300,6 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env,
}
}
if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) {
r = CP_ACCESS_TRAP;
}
return r;
}
@ -2356,7 +2353,7 @@ static CPAccessResult gicv3_fiq_access(CPUARMState *env,
r = CP_ACCESS_TRAP_EL3;
break;
case 3:
if (!is_a64(env) && !arm_is_el3_or_mon(env)) {
if (!arm_is_el3_or_mon(env)) {
r = CP_ACCESS_TRAP_EL3;
}
break;
@ -2365,9 +2362,6 @@ static CPAccessResult gicv3_fiq_access(CPUARMState *env,
}
}
if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) {
r = CP_ACCESS_TRAP;
}
return r;
}
@ -2395,7 +2389,7 @@ static CPAccessResult gicv3_irq_access(CPUARMState *env,
r = CP_ACCESS_TRAP_EL3;
break;
case 3:
if (!is_a64(env) && !arm_is_el3_or_mon(env)) {
if (!arm_is_el3_or_mon(env)) {
r = CP_ACCESS_TRAP_EL3;
}
break;
@ -2404,9 +2398,6 @@ static CPAccessResult gicv3_irq_access(CPUARMState *env,
}
}
if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) {
r = CP_ACCESS_TRAP;
}
return r;
}

View File

@ -69,8 +69,8 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files(
'imx_rngc.c',
))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
'npcm7xx_clk.c',
'npcm7xx_gcr.c',
'npcm_clk.c',
'npcm_gcr.c',
'npcm7xx_mft.c',
'npcm7xx_pwm.c',
'npcm7xx_rng.c',

View File

@ -1,264 +0,0 @@
/*
* Nuvoton NPCM7xx System Global Control Registers.
*
* Copyright 2020 Google LLC
*
* 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.
*/
#include "qemu/osdep.h"
#include "hw/misc/npcm7xx_gcr.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qemu/units.h"
#include "trace.h"
#define NPCM7XX_GCR_MIN_DRAM_SIZE (128 * MiB)
#define NPCM7XX_GCR_MAX_DRAM_SIZE (2 * GiB)
enum NPCM7xxGCRRegisters {
NPCM7XX_GCR_PDID,
NPCM7XX_GCR_PWRON,
NPCM7XX_GCR_MFSEL1 = 0x0c / sizeof(uint32_t),
NPCM7XX_GCR_MFSEL2,
NPCM7XX_GCR_MISCPE,
NPCM7XX_GCR_SPSWC = 0x038 / sizeof(uint32_t),
NPCM7XX_GCR_INTCR,
NPCM7XX_GCR_INTSR,
NPCM7XX_GCR_HIFCR = 0x050 / sizeof(uint32_t),
NPCM7XX_GCR_INTCR2 = 0x060 / sizeof(uint32_t),
NPCM7XX_GCR_MFSEL3,
NPCM7XX_GCR_SRCNT,
NPCM7XX_GCR_RESSR,
NPCM7XX_GCR_RLOCKR1,
NPCM7XX_GCR_FLOCKR1,
NPCM7XX_GCR_DSCNT,
NPCM7XX_GCR_MDLR,
NPCM7XX_GCR_SCRPAD3,
NPCM7XX_GCR_SCRPAD2,
NPCM7XX_GCR_DAVCLVLR = 0x098 / sizeof(uint32_t),
NPCM7XX_GCR_INTCR3,
NPCM7XX_GCR_VSINTR = 0x0ac / sizeof(uint32_t),
NPCM7XX_GCR_MFSEL4,
NPCM7XX_GCR_CPBPNTR = 0x0c4 / sizeof(uint32_t),
NPCM7XX_GCR_CPCTL = 0x0d0 / sizeof(uint32_t),
NPCM7XX_GCR_CP2BST,
NPCM7XX_GCR_B2CPNT,
NPCM7XX_GCR_CPPCTL,
NPCM7XX_GCR_I2CSEGSEL,
NPCM7XX_GCR_I2CSEGCTL,
NPCM7XX_GCR_VSRCR,
NPCM7XX_GCR_MLOCKR,
NPCM7XX_GCR_SCRPAD = 0x013c / sizeof(uint32_t),
NPCM7XX_GCR_USB1PHYCTL,
NPCM7XX_GCR_USB2PHYCTL,
NPCM7XX_GCR_REGS_END,
};
static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = {
[NPCM7XX_GCR_PDID] = 0x04a92750, /* Poleg A1 */
[NPCM7XX_GCR_MISCPE] = 0x0000ffff,
[NPCM7XX_GCR_SPSWC] = 0x00000003,
[NPCM7XX_GCR_INTCR] = 0x0000035e,
[NPCM7XX_GCR_HIFCR] = 0x0000004e,
[NPCM7XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */
[NPCM7XX_GCR_RESSR] = 0x80000000,
[NPCM7XX_GCR_DSCNT] = 0x000000c0,
[NPCM7XX_GCR_DAVCLVLR] = 0x5a00f3cf,
[NPCM7XX_GCR_SCRPAD] = 0x00000008,
[NPCM7XX_GCR_USB1PHYCTL] = 0x034730e4,
[NPCM7XX_GCR_USB2PHYCTL] = 0x034730e4,
};
static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size)
{
uint32_t reg = offset / sizeof(uint32_t);
NPCM7xxGCRState *s = opaque;
if (reg >= NPCM7XX_GCR_NR_REGS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
__func__, offset);
return 0;
}
trace_npcm7xx_gcr_read(offset, s->regs[reg]);
return s->regs[reg];
}
static void npcm7xx_gcr_write(void *opaque, hwaddr offset,
uint64_t v, unsigned size)
{
uint32_t reg = offset / sizeof(uint32_t);
NPCM7xxGCRState *s = opaque;
uint32_t value = v;
trace_npcm7xx_gcr_write(offset, value);
if (reg >= NPCM7XX_GCR_NR_REGS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
__func__, offset);
return;
}
switch (reg) {
case NPCM7XX_GCR_PDID:
case NPCM7XX_GCR_PWRON:
case NPCM7XX_GCR_INTSR:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
__func__, offset);
return;
case NPCM7XX_GCR_RESSR:
case NPCM7XX_GCR_CP2BST:
/* Write 1 to clear */
value = s->regs[reg] & ~value;
break;
case NPCM7XX_GCR_RLOCKR1:
case NPCM7XX_GCR_MDLR:
/* Write 1 to set */
value |= s->regs[reg];
break;
};
s->regs[reg] = value;
}
static const struct MemoryRegionOps npcm7xx_gcr_ops = {
.read = npcm7xx_gcr_read,
.write = npcm7xx_gcr_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type)
{
NPCM7xxGCRState *s = NPCM7XX_GCR(obj);
QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
memcpy(s->regs, cold_reset_values, sizeof(s->regs));
s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron;
s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr;
s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3;
}
static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp)
{
ERRP_GUARD();
NPCM7xxGCRState *s = NPCM7XX_GCR(dev);
uint64_t dram_size;
Object *obj;
obj = object_property_get_link(OBJECT(dev), "dram-mr", errp);
if (!obj) {
error_prepend(errp, "%s: required dram-mr link not found: ", __func__);
return;
}
dram_size = memory_region_size(MEMORY_REGION(obj));
if (!is_power_of_2(dram_size) ||
dram_size < NPCM7XX_GCR_MIN_DRAM_SIZE ||
dram_size > NPCM7XX_GCR_MAX_DRAM_SIZE) {
g_autofree char *sz = size_to_str(dram_size);
g_autofree char *min_sz = size_to_str(NPCM7XX_GCR_MIN_DRAM_SIZE);
g_autofree char *max_sz = size_to_str(NPCM7XX_GCR_MAX_DRAM_SIZE);
error_setg(errp, "%s: unsupported DRAM size %s", __func__, sz);
error_append_hint(errp,
"DRAM size must be a power of two between %s and %s,"
" inclusive.\n", min_sz, max_sz);
return;
}
/* Power-on reset value */
s->reset_intcr3 = 0x00001002;
/*
* The GMMAP (Graphics Memory Map) field is used by u-boot to detect the
* DRAM size, and is normally initialized by the boot block as part of DRAM
* training. However, since we don't have a complete emulation of the
* memory controller and try to make it look like it has already been
* initialized, the boot block will skip this initialization, and we need
* to make sure this field is set correctly up front.
*
* WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB of
* DRAM will be interpreted as 128 MiB.
*
* https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244
*/
s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8;
}
static void npcm7xx_gcr_init(Object *obj)
{
NPCM7xxGCRState *s = NPCM7XX_GCR(obj);
memory_region_init_io(&s->iomem, obj, &npcm7xx_gcr_ops, s,
TYPE_NPCM7XX_GCR, 4 * KiB);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
}
static const VMStateDescription vmstate_npcm7xx_gcr = {
.name = "npcm7xx-gcr",
.version_id = 0,
.minimum_version_id = 0,
.fields = (const VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, NPCM7xxGCRState, NPCM7XX_GCR_NR_REGS),
VMSTATE_END_OF_LIST(),
},
};
static const Property npcm7xx_gcr_properties[] = {
DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0),
DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0),
};
static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data)
{
ResettableClass *rc = RESETTABLE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
QEMU_BUILD_BUG_ON(NPCM7XX_GCR_REGS_END > NPCM7XX_GCR_NR_REGS);
dc->desc = "NPCM7xx System Global Control Registers";
dc->realize = npcm7xx_gcr_realize;
dc->vmsd = &vmstate_npcm7xx_gcr;
rc->phases.enter = npcm7xx_gcr_enter_reset;
device_class_set_props(dc, npcm7xx_gcr_properties);
}
static const TypeInfo npcm7xx_gcr_info = {
.name = TYPE_NPCM7XX_GCR,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(NPCM7xxGCRState),
.instance_init = npcm7xx_gcr_init,
.class_init = npcm7xx_gcr_class_init,
};
static void npcm7xx_gcr_register_type(void)
{
type_register_static(&npcm7xx_gcr_info);
}
type_init(npcm7xx_gcr_register_type);

View File

@ -1,5 +1,5 @@
/*
* Nuvoton NPCM7xx Clock Control Registers.
* Nuvoton NPCM7xx/8xx Clock Control Registers.
*
* Copyright 2020 Google LLC
*
@ -16,7 +16,7 @@
#include "qemu/osdep.h"
#include "hw/misc/npcm7xx_clk.h"
#include "hw/misc/npcm_clk.h"
#include "hw/timer/npcm7xx_timer.h"
#include "hw/qdev-clock.h"
#include "migration/vmstate.h"
@ -72,7 +72,57 @@ enum NPCM7xxCLKRegisters {
NPCM7XX_CLK_AHBCKFI,
NPCM7XX_CLK_SECCNT,
NPCM7XX_CLK_CNTR25M,
NPCM7XX_CLK_REGS_END,
};
enum NPCM8xxCLKRegisters {
NPCM8XX_CLK_CLKEN1,
NPCM8XX_CLK_CLKSEL,
NPCM8XX_CLK_CLKDIV1,
NPCM8XX_CLK_PLLCON0,
NPCM8XX_CLK_PLLCON1,
NPCM8XX_CLK_SWRSTR,
NPCM8XX_CLK_IPSRST1 = 0x20 / sizeof(uint32_t),
NPCM8XX_CLK_IPSRST2,
NPCM8XX_CLK_CLKEN2,
NPCM8XX_CLK_CLKDIV2,
NPCM8XX_CLK_CLKEN3,
NPCM8XX_CLK_IPSRST3,
NPCM8XX_CLK_WD0RCR,
NPCM8XX_CLK_WD1RCR,
NPCM8XX_CLK_WD2RCR,
NPCM8XX_CLK_SWRSTC1,
NPCM8XX_CLK_SWRSTC2,
NPCM8XX_CLK_SWRSTC3,
NPCM8XX_CLK_TIPRSTC,
NPCM8XX_CLK_PLLCON2,
NPCM8XX_CLK_CLKDIV3,
NPCM8XX_CLK_CORSTC,
NPCM8XX_CLK_PLLCONG,
NPCM8XX_CLK_AHBCKFI,
NPCM8XX_CLK_SECCNT,
NPCM8XX_CLK_CNTR25M,
/* Registers unique to NPCM8XX SoC */
NPCM8XX_CLK_CLKEN4,
NPCM8XX_CLK_IPSRST4,
NPCM8XX_CLK_BUSTO,
NPCM8XX_CLK_CLKDIV4,
NPCM8XX_CLK_WD0RCRB,
NPCM8XX_CLK_WD1RCRB,
NPCM8XX_CLK_WD2RCRB,
NPCM8XX_CLK_SWRSTC1B,
NPCM8XX_CLK_SWRSTC2B,
NPCM8XX_CLK_SWRSTC3B,
NPCM8XX_CLK_TIPRSTCB,
NPCM8XX_CLK_CORSTCB,
NPCM8XX_CLK_IPSRSTDIS1,
NPCM8XX_CLK_IPSRSTDIS2,
NPCM8XX_CLK_IPSRSTDIS3,
NPCM8XX_CLK_IPSRSTDIS4,
NPCM8XX_CLK_CLKENDIS1,
NPCM8XX_CLK_CLKENDIS2,
NPCM8XX_CLK_CLKENDIS3,
NPCM8XX_CLK_CLKENDIS4,
NPCM8XX_CLK_THRTL_CNT,
};
/*
@ -81,7 +131,7 @@ enum NPCM7xxCLKRegisters {
* All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on
* core domain reset, but this reset type is not yet supported by QEMU.
*/
static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
static const uint32_t npcm7xx_cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
[NPCM7XX_CLK_CLKEN1] = 0xffffffff,
[NPCM7XX_CLK_CLKSEL] = 0x004aaaaa,
[NPCM7XX_CLK_CLKDIV1] = 0x5413f855,
@ -103,6 +153,46 @@ static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
[NPCM7XX_CLK_AHBCKFI] = 0x000000c8,
};
/*
* These reset values were taken from version 0.92 of the NPCM8xx data sheet.
*/
static const uint32_t npcm8xx_cold_reset_values[NPCM8XX_CLK_NR_REGS] = {
[NPCM8XX_CLK_CLKEN1] = 0xffffffff,
[NPCM8XX_CLK_CLKSEL] = 0x154aaaaa,
[NPCM8XX_CLK_CLKDIV1] = 0x5413f855,
[NPCM8XX_CLK_PLLCON0] = 0x00222101 | PLLCON_LOKI,
[NPCM8XX_CLK_PLLCON1] = 0x00202101 | PLLCON_LOKI,
[NPCM8XX_CLK_IPSRST1] = 0x00001000,
[NPCM8XX_CLK_IPSRST2] = 0x80000000,
[NPCM8XX_CLK_CLKEN2] = 0xffffffff,
[NPCM8XX_CLK_CLKDIV2] = 0xaa4f8f9f,
[NPCM8XX_CLK_CLKEN3] = 0xffffffff,
[NPCM8XX_CLK_IPSRST3] = 0x03000000,
[NPCM8XX_CLK_WD0RCR] = 0xffffffff,
[NPCM8XX_CLK_WD1RCR] = 0xffffffff,
[NPCM8XX_CLK_WD2RCR] = 0xffffffff,
[NPCM8XX_CLK_SWRSTC1] = 0x00000003,
[NPCM8XX_CLK_SWRSTC2] = 0x00000001,
[NPCM8XX_CLK_SWRSTC3] = 0x00000001,
[NPCM8XX_CLK_TIPRSTC] = 0x00000001,
[NPCM8XX_CLK_PLLCON2] = 0x00c02105 | PLLCON_LOKI,
[NPCM8XX_CLK_CLKDIV3] = 0x00009100,
[NPCM8XX_CLK_CORSTC] = 0x04000003,
[NPCM8XX_CLK_PLLCONG] = 0x01228606 | PLLCON_LOKI,
[NPCM8XX_CLK_AHBCKFI] = 0x000000c8,
[NPCM8XX_CLK_CLKEN4] = 0xffffffff,
[NPCM8XX_CLK_CLKDIV4] = 0x70009000,
[NPCM8XX_CLK_IPSRST4] = 0x02000000,
[NPCM8XX_CLK_WD0RCRB] = 0xfffffe71,
[NPCM8XX_CLK_WD1RCRB] = 0xfffffe71,
[NPCM8XX_CLK_WD2RCRB] = 0xfffffe71,
[NPCM8XX_CLK_SWRSTC1B] = 0xfffffe71,
[NPCM8XX_CLK_SWRSTC2B] = 0xfffffe71,
[NPCM8XX_CLK_SWRSTC3B] = 0xfffffe71,
[NPCM8XX_CLK_TIPRSTCB] = 0xfffffe71,
[NPCM8XX_CLK_CORSTCB] = 0xfffffe71,
};
/* The number of watchdogs that can trigger a reset. */
#define NPCM7XX_NR_WATCHDOGS (3)
@ -198,7 +288,7 @@ static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg)
}
}
static void npcm7xx_clk_update_all_plls(NPCM7xxCLKState *clk)
static void npcm7xx_clk_update_all_plls(NPCMCLKState *clk)
{
int i;
@ -207,7 +297,7 @@ static void npcm7xx_clk_update_all_plls(NPCM7xxCLKState *clk)
}
}
static void npcm7xx_clk_update_all_sels(NPCM7xxCLKState *clk)
static void npcm7xx_clk_update_all_sels(NPCMCLKState *clk)
{
int i;
@ -216,7 +306,7 @@ static void npcm7xx_clk_update_all_sels(NPCM7xxCLKState *clk)
}
}
static void npcm7xx_clk_update_all_dividers(NPCM7xxCLKState *clk)
static void npcm7xx_clk_update_all_dividers(NPCMCLKState *clk)
{
int i;
@ -225,7 +315,7 @@ static void npcm7xx_clk_update_all_dividers(NPCM7xxCLKState *clk)
}
}
static void npcm7xx_clk_update_all_clocks(NPCM7xxCLKState *clk)
static void npcm7xx_clk_update_all_clocks(NPCMCLKState *clk)
{
clock_update_hz(clk->clkref, NPCM7XX_CLOCK_REF_HZ);
npcm7xx_clk_update_all_plls(clk);
@ -635,7 +725,7 @@ static void npcm7xx_clk_divider_init(Object *obj)
}
static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll,
NPCM7xxCLKState *clk, const PLLInitInfo *init_info)
NPCMCLKState *clk, const PLLInitInfo *init_info)
{
pll->name = init_info->name;
pll->clk = clk;
@ -647,7 +737,7 @@ static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll,
}
static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel,
NPCM7xxCLKState *clk, const SELInitInfo *init_info)
NPCMCLKState *clk, const SELInitInfo *init_info)
{
int input_size = init_info->input_size;
@ -664,7 +754,7 @@ static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel,
}
static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div,
NPCM7xxCLKState *clk, const DividerInitInfo *init_info)
NPCMCLKState *clk, const DividerInitInfo *init_info)
{
div->name = init_info->name;
div->clk = clk;
@ -683,7 +773,7 @@ static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div,
}
}
static Clock *npcm7xx_get_clock(NPCM7xxCLKState *clk, ClockSrcType type,
static Clock *npcm7xx_get_clock(NPCMCLKState *clk, ClockSrcType type,
int index)
{
switch (type) {
@ -700,7 +790,7 @@ static Clock *npcm7xx_get_clock(NPCM7xxCLKState *clk, ClockSrcType type,
}
}
static void npcm7xx_connect_clocks(NPCM7xxCLKState *clk)
static void npcm7xx_connect_clocks(NPCMCLKState *clk)
{
int i, j;
Clock *src;
@ -724,14 +814,15 @@ static void npcm7xx_connect_clocks(NPCM7xxCLKState *clk)
}
}
static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
static uint64_t npcm_clk_read(void *opaque, hwaddr offset, unsigned size)
{
uint32_t reg = offset / sizeof(uint32_t);
NPCM7xxCLKState *s = opaque;
NPCMCLKState *s = opaque;
NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s);
int64_t now_ns;
uint32_t value = 0;
if (reg >= NPCM7XX_CLK_NR_REGS) {
if (reg >= c->nr_regs) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
__func__, offset);
@ -766,21 +857,22 @@ static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
break;
};
trace_npcm7xx_clk_read(offset, value);
trace_npcm_clk_read(offset, value);
return value;
}
static void npcm7xx_clk_write(void *opaque, hwaddr offset,
static void npcm_clk_write(void *opaque, hwaddr offset,
uint64_t v, unsigned size)
{
uint32_t reg = offset / sizeof(uint32_t);
NPCM7xxCLKState *s = opaque;
NPCMCLKState *s = opaque;
NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s);
uint32_t value = v;
trace_npcm7xx_clk_write(offset, value);
trace_npcm_clk_write(offset, value);
if (reg >= NPCM7XX_CLK_NR_REGS) {
if (reg >= c->nr_regs) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
__func__, offset);
@ -842,7 +934,7 @@ static void npcm7xx_clk_write(void *opaque, hwaddr offset,
static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n,
int level)
{
NPCM7xxCLKState *clk = NPCM7XX_CLK(opaque);
NPCMCLKState *clk = NPCM_CLK(opaque);
uint32_t rcr;
g_assert(n >= 0 && n <= NPCM7XX_NR_WATCHDOGS);
@ -856,9 +948,9 @@ static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n,
}
}
static const struct MemoryRegionOps npcm7xx_clk_ops = {
.read = npcm7xx_clk_read,
.write = npcm7xx_clk_write,
static const struct MemoryRegionOps npcm_clk_ops = {
.read = npcm_clk_read,
.write = npcm_clk_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
@ -867,13 +959,13 @@ static const struct MemoryRegionOps npcm7xx_clk_ops = {
},
};
static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
static void npcm_clk_enter_reset(Object *obj, ResetType type)
{
NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
NPCMCLKState *s = NPCM_CLK(obj);
NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s);
QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values));
g_assert(sizeof(s->regs) >= c->nr_regs * sizeof(uint32_t));
memcpy(s->regs, c->cold_reset_values, sizeof(s->regs));
s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
npcm7xx_clk_update_all_clocks(s);
/*
@ -882,7 +974,7 @@ static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
*/
}
static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s)
static void npcm7xx_clk_init_clock_hierarchy(NPCMCLKState *s)
{
int i;
@ -918,19 +1010,19 @@ static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s)
clock_update_hz(s->clkref, NPCM7XX_CLOCK_REF_HZ);
}
static void npcm7xx_clk_init(Object *obj)
static void npcm_clk_init(Object *obj)
{
NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
NPCMCLKState *s = NPCM_CLK(obj);
memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s,
TYPE_NPCM7XX_CLK, 4 * KiB);
memory_region_init_io(&s->iomem, obj, &npcm_clk_ops, s,
TYPE_NPCM_CLK, 4 * KiB);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
}
static int npcm7xx_clk_post_load(void *opaque, int version_id)
static int npcm_clk_post_load(void *opaque, int version_id)
{
if (version_id >= 1) {
NPCM7xxCLKState *clk = opaque;
NPCMCLKState *clk = opaque;
npcm7xx_clk_update_all_clocks(clk);
}
@ -938,10 +1030,10 @@ static int npcm7xx_clk_post_load(void *opaque, int version_id)
return 0;
}
static void npcm7xx_clk_realize(DeviceState *dev, Error **errp)
static void npcm_clk_realize(DeviceState *dev, Error **errp)
{
int i;
NPCM7xxCLKState *s = NPCM7XX_CLK(dev);
NPCMCLKState *s = NPCM_CLK(dev);
qdev_init_gpio_in_named(DEVICE(s), npcm7xx_clk_perform_watchdog_reset,
NPCM7XX_WATCHDOG_RESET_GPIO_IN, NPCM7XX_NR_WATCHDOGS);
@ -996,15 +1088,15 @@ static const VMStateDescription vmstate_npcm7xx_clk_divider = {
},
};
static const VMStateDescription vmstate_npcm7xx_clk = {
.name = "npcm7xx-clk",
.version_id = 1,
.minimum_version_id = 1,
.post_load = npcm7xx_clk_post_load,
static const VMStateDescription vmstate_npcm_clk = {
.name = "npcm-clk",
.version_id = 3,
.minimum_version_id = 3,
.post_load = npcm_clk_post_load,
.fields = (const VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS),
VMSTATE_INT64(ref_ns, NPCM7xxCLKState),
VMSTATE_CLOCK(clkref, NPCM7xxCLKState),
VMSTATE_UINT32_ARRAY(regs, NPCMCLKState, NPCM_CLK_MAX_NR_REGS),
VMSTATE_INT64(ref_ns, NPCMCLKState),
VMSTATE_CLOCK(clkref, NPCMCLKState),
VMSTATE_END_OF_LIST(),
},
};
@ -1033,17 +1125,34 @@ static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_npcm7xx_clk_divider;
}
static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
static void npcm_clk_class_init(ObjectClass *klass, void *data)
{
ResettableClass *rc = RESETTABLE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM7XX_CLK_NR_REGS);
dc->vmsd = &vmstate_npcm_clk;
dc->realize = npcm_clk_realize;
rc->phases.enter = npcm_clk_enter_reset;
}
static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
{
NPCMCLKClass *c = NPCM_CLK_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
dc->desc = "NPCM7xx Clock Control Registers";
dc->vmsd = &vmstate_npcm7xx_clk;
dc->realize = npcm7xx_clk_realize;
rc->phases.enter = npcm7xx_clk_enter_reset;
c->nr_regs = NPCM7XX_CLK_NR_REGS;
c->cold_reset_values = npcm7xx_cold_reset_values;
}
static void npcm8xx_clk_class_init(ObjectClass *klass, void *data)
{
NPCMCLKClass *c = NPCM_CLK_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
dc->desc = "NPCM8xx Clock Control Registers";
c->nr_regs = NPCM8XX_CLK_NR_REGS;
c->cold_reset_values = npcm8xx_cold_reset_values;
}
static const TypeInfo npcm7xx_clk_pll_info = {
@ -1070,19 +1179,35 @@ static const TypeInfo npcm7xx_clk_divider_info = {
.class_init = npcm7xx_clk_divider_class_init,
};
static const TypeInfo npcm_clk_info = {
.name = TYPE_NPCM_CLK,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(NPCMCLKState),
.instance_init = npcm_clk_init,
.class_size = sizeof(NPCMCLKClass),
.class_init = npcm_clk_class_init,
.abstract = true,
};
static const TypeInfo npcm7xx_clk_info = {
.name = TYPE_NPCM7XX_CLK,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(NPCM7xxCLKState),
.instance_init = npcm7xx_clk_init,
.parent = TYPE_NPCM_CLK,
.class_init = npcm7xx_clk_class_init,
};
static const TypeInfo npcm8xx_clk_info = {
.name = TYPE_NPCM8XX_CLK,
.parent = TYPE_NPCM_CLK,
.class_init = npcm8xx_clk_class_init,
};
static void npcm7xx_clk_register_type(void)
{
type_register_static(&npcm7xx_clk_pll_info);
type_register_static(&npcm7xx_clk_sel_info);
type_register_static(&npcm7xx_clk_divider_info);
type_register_static(&npcm_clk_info);
type_register_static(&npcm7xx_clk_info);
type_register_static(&npcm8xx_clk_info);
}
type_init(npcm7xx_clk_register_type);

482
hw/misc/npcm_gcr.c Normal file
View File

@ -0,0 +1,482 @@
/*
* Nuvoton NPCM7xx/8xx System Global Control Registers.
*
* Copyright 2020 Google LLC
*
* 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.
*/
#include "qemu/osdep.h"
#include "hw/misc/npcm_gcr.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qemu/units.h"
#include "trace.h"
#define NPCM7XX_GCR_MIN_DRAM_SIZE (128 * MiB)
#define NPCM7XX_GCR_MAX_DRAM_SIZE (2 * GiB)
enum NPCM7xxGCRRegisters {
NPCM7XX_GCR_PDID,
NPCM7XX_GCR_PWRON,
NPCM7XX_GCR_MFSEL1 = 0x0c / sizeof(uint32_t),
NPCM7XX_GCR_MFSEL2,
NPCM7XX_GCR_MISCPE,
NPCM7XX_GCR_SPSWC = 0x038 / sizeof(uint32_t),
NPCM7XX_GCR_INTCR,
NPCM7XX_GCR_INTSR,
NPCM7XX_GCR_HIFCR = 0x050 / sizeof(uint32_t),
NPCM7XX_GCR_INTCR2 = 0x060 / sizeof(uint32_t),
NPCM7XX_GCR_MFSEL3,
NPCM7XX_GCR_SRCNT,
NPCM7XX_GCR_RESSR,
NPCM7XX_GCR_RLOCKR1,
NPCM7XX_GCR_FLOCKR1,
NPCM7XX_GCR_DSCNT,
NPCM7XX_GCR_MDLR,
NPCM7XX_GCR_SCRPAD3,
NPCM7XX_GCR_SCRPAD2,
NPCM7XX_GCR_DAVCLVLR = 0x098 / sizeof(uint32_t),
NPCM7XX_GCR_INTCR3,
NPCM7XX_GCR_VSINTR = 0x0ac / sizeof(uint32_t),
NPCM7XX_GCR_MFSEL4,
NPCM7XX_GCR_CPBPNTR = 0x0c4 / sizeof(uint32_t),
NPCM7XX_GCR_CPCTL = 0x0d0 / sizeof(uint32_t),
NPCM7XX_GCR_CP2BST,
NPCM7XX_GCR_B2CPNT,
NPCM7XX_GCR_CPPCTL,
NPCM7XX_GCR_I2CSEGSEL,
NPCM7XX_GCR_I2CSEGCTL,
NPCM7XX_GCR_VSRCR,
NPCM7XX_GCR_MLOCKR,
NPCM7XX_GCR_SCRPAD = 0x013c / sizeof(uint32_t),
NPCM7XX_GCR_USB1PHYCTL,
NPCM7XX_GCR_USB2PHYCTL,
};
static const uint32_t npcm7xx_cold_reset_values[NPCM7XX_GCR_NR_REGS] = {
[NPCM7XX_GCR_PDID] = 0x04a92750, /* Poleg A1 */
[NPCM7XX_GCR_MISCPE] = 0x0000ffff,
[NPCM7XX_GCR_SPSWC] = 0x00000003,
[NPCM7XX_GCR_INTCR] = 0x0000035e,
[NPCM7XX_GCR_HIFCR] = 0x0000004e,
[NPCM7XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */
[NPCM7XX_GCR_RESSR] = 0x80000000,
[NPCM7XX_GCR_DSCNT] = 0x000000c0,
[NPCM7XX_GCR_DAVCLVLR] = 0x5a00f3cf,
[NPCM7XX_GCR_SCRPAD] = 0x00000008,
[NPCM7XX_GCR_USB1PHYCTL] = 0x034730e4,
[NPCM7XX_GCR_USB2PHYCTL] = 0x034730e4,
};
enum NPCM8xxGCRRegisters {
NPCM8XX_GCR_PDID,
NPCM8XX_GCR_PWRON,
NPCM8XX_GCR_MISCPE = 0x014 / sizeof(uint32_t),
NPCM8XX_GCR_FLOCKR2 = 0x020 / sizeof(uint32_t),
NPCM8XX_GCR_FLOCKR3,
NPCM8XX_GCR_A35_MODE = 0x034 / sizeof(uint32_t),
NPCM8XX_GCR_SPSWC,
NPCM8XX_GCR_INTCR,
NPCM8XX_GCR_INTSR,
NPCM8XX_GCR_HIFCR = 0x050 / sizeof(uint32_t),
NPCM8XX_GCR_INTCR2 = 0x060 / sizeof(uint32_t),
NPCM8XX_GCR_SRCNT = 0x068 / sizeof(uint32_t),
NPCM8XX_GCR_RESSR,
NPCM8XX_GCR_RLOCKR1,
NPCM8XX_GCR_FLOCKR1,
NPCM8XX_GCR_DSCNT,
NPCM8XX_GCR_MDLR,
NPCM8XX_GCR_SCRPAD_C = 0x080 / sizeof(uint32_t),
NPCM8XX_GCR_SCRPAD_B,
NPCM8XX_GCR_DAVCLVLR = 0x098 / sizeof(uint32_t),
NPCM8XX_GCR_INTCR3,
NPCM8XX_GCR_PCIRCTL = 0x0a0 / sizeof(uint32_t),
NPCM8XX_GCR_VSINTR,
NPCM8XX_GCR_SD2SUR1 = 0x0b4 / sizeof(uint32_t),
NPCM8XX_GCR_SD2SUR2,
NPCM8XX_GCR_INTCR4 = 0x0c0 / sizeof(uint32_t),
NPCM8XX_GCR_CPCTL = 0x0d0 / sizeof(uint32_t),
NPCM8XX_GCR_CP2BST,
NPCM8XX_GCR_B2CPNT,
NPCM8XX_GCR_CPPCTL,
NPCM8XX_GCR_I2CSEGSEL = 0x0e0 / sizeof(uint32_t),
NPCM8XX_GCR_I2CSEGCTL,
NPCM8XX_GCR_VSRCR,
NPCM8XX_GCR_MLOCKR,
NPCM8XX_GCR_SCRPAD = 0x13c / sizeof(uint32_t),
NPCM8XX_GCR_USB1PHYCTL,
NPCM8XX_GCR_USB2PHYCTL,
NPCM8XX_GCR_USB3PHYCTL,
NPCM8XX_GCR_MFSEL1 = 0x260 / sizeof(uint32_t),
NPCM8XX_GCR_MFSEL2,
NPCM8XX_GCR_MFSEL3,
NPCM8XX_GCR_MFSEL4,
NPCM8XX_GCR_MFSEL5,
NPCM8XX_GCR_MFSEL6,
NPCM8XX_GCR_MFSEL7,
NPCM8XX_GCR_MFSEL_LK1 = 0x280 / sizeof(uint32_t),
NPCM8XX_GCR_MFSEL_LK2,
NPCM8XX_GCR_MFSEL_LK3,
NPCM8XX_GCR_MFSEL_LK4,
NPCM8XX_GCR_MFSEL_LK5,
NPCM8XX_GCR_MFSEL_LK6,
NPCM8XX_GCR_MFSEL_LK7,
NPCM8XX_GCR_MFSEL_SET1 = 0x2a0 / sizeof(uint32_t),
NPCM8XX_GCR_MFSEL_SET2,
NPCM8XX_GCR_MFSEL_SET3,
NPCM8XX_GCR_MFSEL_SET4,
NPCM8XX_GCR_MFSEL_SET5,
NPCM8XX_GCR_MFSEL_SET6,
NPCM8XX_GCR_MFSEL_SET7,
NPCM8XX_GCR_MFSEL_CLR1 = 0x2c0 / sizeof(uint32_t),
NPCM8XX_GCR_MFSEL_CLR2,
NPCM8XX_GCR_MFSEL_CLR3,
NPCM8XX_GCR_MFSEL_CLR4,
NPCM8XX_GCR_MFSEL_CLR5,
NPCM8XX_GCR_MFSEL_CLR6,
NPCM8XX_GCR_MFSEL_CLR7,
NPCM8XX_GCR_WD0RCRLK = 0x400 / sizeof(uint32_t),
NPCM8XX_GCR_WD1RCRLK,
NPCM8XX_GCR_WD2RCRLK,
NPCM8XX_GCR_SWRSTC1LK,
NPCM8XX_GCR_SWRSTC2LK,
NPCM8XX_GCR_SWRSTC3LK,
NPCM8XX_GCR_TIPRSTCLK,
NPCM8XX_GCR_CORSTCLK,
NPCM8XX_GCR_WD0RCRBLK,
NPCM8XX_GCR_WD1RCRBLK,
NPCM8XX_GCR_WD2RCRBLK,
NPCM8XX_GCR_SWRSTC1BLK,
NPCM8XX_GCR_SWRSTC2BLK,
NPCM8XX_GCR_SWRSTC3BLK,
NPCM8XX_GCR_TIPRSTCBLK,
NPCM8XX_GCR_CORSTCBLK,
/* 64 scratch pad registers start here. 0xe00 ~ 0xefc */
NPCM8XX_GCR_SCRPAD_00 = 0xe00 / sizeof(uint32_t),
/* 32 semaphore registers start here. 0xf00 ~ 0xf7c */
NPCM8XX_GCR_GP_SEMFR_00 = 0xf00 / sizeof(uint32_t),
NPCM8XX_GCR_GP_SEMFR_31 = 0xf7c / sizeof(uint32_t),
};
static const uint32_t npcm8xx_cold_reset_values[NPCM8XX_GCR_NR_REGS] = {
[NPCM8XX_GCR_PDID] = 0x04a35850, /* Arbel A1 */
[NPCM8XX_GCR_MISCPE] = 0x0000ffff,
[NPCM8XX_GCR_A35_MODE] = 0xfff4ff30,
[NPCM8XX_GCR_SPSWC] = 0x00000003,
[NPCM8XX_GCR_INTCR] = 0x0010035e,
[NPCM8XX_GCR_HIFCR] = 0x0000004e,
[NPCM8XX_GCR_SD2SUR1] = 0xfdc80000,
[NPCM8XX_GCR_SD2SUR2] = 0x5200b130,
[NPCM8XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */
[NPCM8XX_GCR_RESSR] = 0x80000000,
[NPCM8XX_GCR_DAVCLVLR] = 0x5a00f3cf,
[NPCM8XX_GCR_INTCR3] = 0x5e001002,
[NPCM8XX_GCR_VSRCR] = 0x00004800,
[NPCM8XX_GCR_SCRPAD] = 0x00000008,
[NPCM8XX_GCR_USB1PHYCTL] = 0x034730e4,
[NPCM8XX_GCR_USB2PHYCTL] = 0x034730e4,
[NPCM8XX_GCR_USB3PHYCTL] = 0x034730e4,
/* All 32 semaphores should be initialized to 1. */
[NPCM8XX_GCR_GP_SEMFR_00...NPCM8XX_GCR_GP_SEMFR_31] = 0x00000001,
};
static uint64_t npcm_gcr_read(void *opaque, hwaddr offset, unsigned size)
{
uint32_t reg = offset / sizeof(uint32_t);
NPCMGCRState *s = opaque;
NPCMGCRClass *c = NPCM_GCR_GET_CLASS(s);
uint64_t value;
if (reg >= c->nr_regs) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
__func__, offset);
return 0;
}
switch (size) {
case 4:
value = s->regs[reg];
break;
case 8:
g_assert(!(reg & 1));
value = deposit64(s->regs[reg], 32, 32, s->regs[reg + 1]);
break;
default:
g_assert_not_reached();
}
trace_npcm_gcr_read(offset, value);
return value;
}
static void npcm_gcr_write(void *opaque, hwaddr offset,
uint64_t v, unsigned size)
{
uint32_t reg = offset / sizeof(uint32_t);
NPCMGCRState *s = opaque;
NPCMGCRClass *c = NPCM_GCR_GET_CLASS(s);
uint32_t value = v;
trace_npcm_gcr_write(offset, v);
if (reg >= c->nr_regs) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
__func__, offset);
return;
}
switch (size) {
case 4:
switch (reg) {
case NPCM7XX_GCR_PDID:
case NPCM7XX_GCR_PWRON:
case NPCM7XX_GCR_INTSR:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
__func__, offset);
return;
case NPCM7XX_GCR_RESSR:
case NPCM7XX_GCR_CP2BST:
/* Write 1 to clear */
value = s->regs[reg] & ~value;
break;
case NPCM7XX_GCR_RLOCKR1:
case NPCM7XX_GCR_MDLR:
/* Write 1 to set */
value |= s->regs[reg];
break;
};
s->regs[reg] = value;
break;
case 8:
g_assert(!(reg & 1));
s->regs[reg] = value;
s->regs[reg + 1] = extract64(v, 32, 32);
break;
default:
g_assert_not_reached();
}
}
static bool npcm_gcr_check_mem_op(void *opaque, hwaddr offset,
unsigned size, bool is_write,
MemTxAttrs attrs)
{
NPCMGCRClass *c = NPCM_GCR_GET_CLASS(opaque);
if (offset >= c->nr_regs * sizeof(uint32_t)) {
return false;
}
switch (size) {
case 4:
return true;
case 8:
if (offset >= NPCM8XX_GCR_SCRPAD_00 * sizeof(uint32_t) &&
offset < (NPCM8XX_GCR_NR_REGS - 1) * sizeof(uint32_t)) {
return true;
} else {
return false;
}
default:
return false;
}
}
static const struct MemoryRegionOps npcm_gcr_ops = {
.read = npcm_gcr_read,
.write = npcm_gcr_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 8,
.accepts = npcm_gcr_check_mem_op,
.unaligned = false,
},
};
static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type)
{
NPCMGCRState *s = NPCM_GCR(obj);
NPCMGCRClass *c = NPCM_GCR_GET_CLASS(obj);
g_assert(sizeof(s->regs) >= sizeof(c->cold_reset_values));
g_assert(sizeof(s->regs) >= c->nr_regs * sizeof(uint32_t));
memcpy(s->regs, c->cold_reset_values, c->nr_regs * sizeof(uint32_t));
/* These 3 registers are at the same location in both 7xx and 8xx. */
s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron;
s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr;
s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3;
}
static void npcm8xx_gcr_enter_reset(Object *obj, ResetType type)
{
NPCMGCRState *s = NPCM_GCR(obj);
NPCMGCRClass *c = NPCM_GCR_GET_CLASS(obj);
memcpy(s->regs, c->cold_reset_values, c->nr_regs * sizeof(uint32_t));
/* These 3 registers are at the same location in both 7xx and 8xx. */
s->regs[NPCM8XX_GCR_PWRON] = s->reset_pwron;
s->regs[NPCM8XX_GCR_MDLR] = s->reset_mdlr;
s->regs[NPCM8XX_GCR_INTCR3] = s->reset_intcr3;
s->regs[NPCM8XX_GCR_SCRPAD_B] = s->reset_scrpad_b;
}
static void npcm_gcr_realize(DeviceState *dev, Error **errp)
{
ERRP_GUARD();
NPCMGCRState *s = NPCM_GCR(dev);
uint64_t dram_size;
Object *obj;
obj = object_property_get_link(OBJECT(dev), "dram-mr", errp);
if (!obj) {
error_prepend(errp, "%s: required dram-mr link not found: ", __func__);
return;
}
dram_size = memory_region_size(MEMORY_REGION(obj));
if (!is_power_of_2(dram_size) ||
dram_size < NPCM7XX_GCR_MIN_DRAM_SIZE ||
dram_size > NPCM7XX_GCR_MAX_DRAM_SIZE) {
g_autofree char *sz = size_to_str(dram_size);
g_autofree char *min_sz = size_to_str(NPCM7XX_GCR_MIN_DRAM_SIZE);
g_autofree char *max_sz = size_to_str(NPCM7XX_GCR_MAX_DRAM_SIZE);
error_setg(errp, "%s: unsupported DRAM size %s", __func__, sz);
error_append_hint(errp,
"DRAM size must be a power of two between %s and %s,"
" inclusive.\n", min_sz, max_sz);
return;
}
/* Power-on reset value */
s->reset_intcr3 = 0x00001002;
/*
* The GMMAP (Graphics Memory Map) field is used by u-boot to detect the
* DRAM size, and is normally initialized by the boot block as part of DRAM
* training. However, since we don't have a complete emulation of the
* memory controller and try to make it look like it has already been
* initialized, the boot block will skip this initialization, and we need
* to make sure this field is set correctly up front.
*
* WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB of
* DRAM will be interpreted as 128 MiB.
*
* https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244
*/
s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8;
/*
* The boot block starting from 0.0.6 for NPCM8xx SoCs stores the DRAM size
* in the SCRPAD2 registers. We need to set this field correctly since
* the initialization is skipped as we mentioned above.
* https://github.com/Nuvoton-Israel/u-boot/blob/npcm8mnx-v2019.01_tmp/board/nuvoton/arbel/arbel.c#L737
*/
s->reset_scrpad_b = dram_size;
}
static void npcm_gcr_init(Object *obj)
{
NPCMGCRState *s = NPCM_GCR(obj);
memory_region_init_io(&s->iomem, obj, &npcm_gcr_ops, s,
TYPE_NPCM_GCR, 4 * KiB);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
}
static const VMStateDescription vmstate_npcm_gcr = {
.name = "npcm-gcr",
.version_id = 2,
.minimum_version_id = 2,
.fields = (const VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, NPCMGCRState, NPCM_GCR_MAX_NR_REGS),
VMSTATE_END_OF_LIST(),
},
};
static const Property npcm_gcr_properties[] = {
DEFINE_PROP_UINT32("disabled-modules", NPCMGCRState, reset_mdlr, 0),
DEFINE_PROP_UINT32("power-on-straps", NPCMGCRState, reset_pwron, 0),
};
static void npcm_gcr_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = npcm_gcr_realize;
dc->vmsd = &vmstate_npcm_gcr;
device_class_set_props(dc, npcm_gcr_properties);
}
static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data)
{
NPCMGCRClass *c = NPCM_GCR_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->desc = "NPCM7xx System Global Control Registers";
rc->phases.enter = npcm7xx_gcr_enter_reset;
c->nr_regs = NPCM7XX_GCR_NR_REGS;
c->cold_reset_values = npcm7xx_cold_reset_values;
rc->phases.enter = npcm7xx_gcr_enter_reset;
}
static void npcm8xx_gcr_class_init(ObjectClass *klass, void *data)
{
NPCMGCRClass *c = NPCM_GCR_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->desc = "NPCM8xx System Global Control Registers";
c->nr_regs = NPCM8XX_GCR_NR_REGS;
c->cold_reset_values = npcm8xx_cold_reset_values;
rc->phases.enter = npcm8xx_gcr_enter_reset;
}
static const TypeInfo npcm_gcr_info[] = {
{
.name = TYPE_NPCM_GCR,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(NPCMGCRState),
.instance_init = npcm_gcr_init,
.class_size = sizeof(NPCMGCRClass),
.class_init = npcm_gcr_class_init,
.abstract = true,
},
{
.name = TYPE_NPCM7XX_GCR,
.parent = TYPE_NPCM_GCR,
.class_init = npcm7xx_gcr_class_init,
},
{
.name = TYPE_NPCM8XX_GCR,
.parent = TYPE_NPCM_GCR,
.class_init = npcm8xx_gcr_class_init,
},
};
DEFINE_TYPES(npcm_gcr_info)

View File

@ -130,13 +130,13 @@ mos6522_set_sr_int(void) "set sr_int"
mos6522_write(uint64_t addr, const char *name, uint64_t val) "reg=0x%"PRIx64 " [%s] val=0x%"PRIx64
mos6522_read(uint64_t addr, const char *name, unsigned val) "reg=0x%"PRIx64 " [%s] val=0x%x"
# npcm7xx_clk.c
npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
# npcm_clk.c
npcm_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
npcm_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
# npcm7xx_gcr.c
npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
# npcm_gcr.c
npcm_gcr_read(uint64_t offset, uint64_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx64
npcm_gcr_write(uint64_t offset, uint64_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx64
# npcm7xx_mft.c
npcm7xx_mft_read(const char *name, uint64_t offset, uint16_t value) "%s: offset: 0x%04" PRIx64 " value: 0x%04" PRIx16

View File

@ -40,6 +40,7 @@ system_ss.add(when: 'CONFIG_SUNHME', if_true: files('sunhme.c'))
system_ss.add(when: 'CONFIG_FTGMAC100', if_true: files('ftgmac100.c'))
system_ss.add(when: 'CONFIG_SUNGEM', if_true: files('sungem.c'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_emc.c', 'npcm_gmac.c'))
system_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm_pcs.c'))
system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_fec.c'))
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_llan.c'))

410
hw/net/npcm_pcs.c Normal file
View File

@ -0,0 +1,410 @@
/*
* Nuvoton NPCM8xx PCS Module
*
* Copyright 2022 Google LLC
*
* 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.
*/
/*
* Disclaimer:
* Currently we only implemented the default values of the registers and
* the soft reset feature. These are required to boot up the GMAC module
* in Linux kernel for NPCM845 boards. Other functionalities are not modeled.
*/
#include "qemu/osdep.h"
#include "exec/hwaddr.h"
#include "hw/registerfields.h"
#include "hw/net/npcm_pcs.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/units.h"
#include "trace.h"
#define NPCM_PCS_IND_AC_BA 0x1fe
#define NPCM_PCS_IND_SR_CTL 0x1e00
#define NPCM_PCS_IND_SR_MII 0x1f00
#define NPCM_PCS_IND_SR_TIM 0x1f07
#define NPCM_PCS_IND_VR_MII 0x1f80
REG16(NPCM_PCS_SR_CTL_ID1, 0x08)
REG16(NPCM_PCS_SR_CTL_ID2, 0x0a)
REG16(NPCM_PCS_SR_CTL_STS, 0x10)
REG16(NPCM_PCS_SR_MII_CTRL, 0x00)
REG16(NPCM_PCS_SR_MII_STS, 0x02)
REG16(NPCM_PCS_SR_MII_DEV_ID1, 0x04)
REG16(NPCM_PCS_SR_MII_DEV_ID2, 0x06)
REG16(NPCM_PCS_SR_MII_AN_ADV, 0x08)
REG16(NPCM_PCS_SR_MII_LP_BABL, 0x0a)
REG16(NPCM_PCS_SR_MII_AN_EXPN, 0x0c)
REG16(NPCM_PCS_SR_MII_EXT_STS, 0x1e)
REG16(NPCM_PCS_SR_TIM_SYNC_ABL, 0x10)
REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR, 0x12)
REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_UPR, 0x14)
REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR, 0x16)
REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_UPR, 0x18)
REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR, 0x1a)
REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_UPR, 0x1c)
REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR, 0x1e)
REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_UPR, 0x20)
REG16(NPCM_PCS_VR_MII_MMD_DIG_CTRL1, 0x000)
REG16(NPCM_PCS_VR_MII_AN_CTRL, 0x002)
REG16(NPCM_PCS_VR_MII_AN_INTR_STS, 0x004)
REG16(NPCM_PCS_VR_MII_TC, 0x006)
REG16(NPCM_PCS_VR_MII_DBG_CTRL, 0x00a)
REG16(NPCM_PCS_VR_MII_EEE_MCTRL0, 0x00c)
REG16(NPCM_PCS_VR_MII_EEE_TXTIMER, 0x010)
REG16(NPCM_PCS_VR_MII_EEE_RXTIMER, 0x012)
REG16(NPCM_PCS_VR_MII_LINK_TIMER_CTRL, 0x014)
REG16(NPCM_PCS_VR_MII_EEE_MCTRL1, 0x016)
REG16(NPCM_PCS_VR_MII_DIG_STS, 0x020)
REG16(NPCM_PCS_VR_MII_ICG_ERRCNT1, 0x022)
REG16(NPCM_PCS_VR_MII_MISC_STS, 0x030)
REG16(NPCM_PCS_VR_MII_RX_LSTS, 0x040)
REG16(NPCM_PCS_VR_MII_MP_TX_BSTCTRL0, 0x070)
REG16(NPCM_PCS_VR_MII_MP_TX_LVLCTRL0, 0x074)
REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL0, 0x07a)
REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL1, 0x07c)
REG16(NPCM_PCS_VR_MII_MP_TX_STS, 0x090)
REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL0, 0x0b0)
REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL1, 0x0b2)
REG16(NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0, 0x0ba)
REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL0, 0x0f0)
REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL1, 0x0f2)
REG16(NPCM_PCS_VR_MII_MP_MPLL_STS, 0x110)
REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL2, 0x126)
REG16(NPCM_PCS_VR_MII_MP_LVL_CTRL, 0x130)
REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL0, 0x132)
REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL1, 0x134)
REG16(NPCM_PCS_VR_MII_DIG_CTRL2, 0x1c2)
REG16(NPCM_PCS_VR_MII_DIG_ERRCNT_SEL, 0x1c4)
/* Register Fields */
#define NPCM_PCS_SR_MII_CTRL_RST BIT(15)
static const uint16_t npcm_pcs_sr_ctl_cold_reset_values[NPCM_PCS_NR_SR_CTLS] = {
[R_NPCM_PCS_SR_CTL_ID1] = 0x699e,
[R_NPCM_PCS_SR_CTL_STS] = 0x8000,
};
static const uint16_t npcm_pcs_sr_mii_cold_reset_values[NPCM_PCS_NR_SR_MIIS] = {
[R_NPCM_PCS_SR_MII_CTRL] = 0x1140,
[R_NPCM_PCS_SR_MII_STS] = 0x0109,
[R_NPCM_PCS_SR_MII_DEV_ID1] = 0x699e,
[R_NPCM_PCS_SR_MII_DEV_ID2] = 0xced0,
[R_NPCM_PCS_SR_MII_AN_ADV] = 0x0020,
[R_NPCM_PCS_SR_MII_EXT_STS] = 0xc000,
};
static const uint16_t npcm_pcs_sr_tim_cold_reset_values[NPCM_PCS_NR_SR_TIMS] = {
[R_NPCM_PCS_SR_TIM_SYNC_ABL] = 0x0003,
[R_NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR] = 0x0038,
[R_NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR] = 0x0038,
[R_NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR] = 0x0058,
[R_NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR] = 0x0048,
};
static const uint16_t npcm_pcs_vr_mii_cold_reset_values[NPCM_PCS_NR_VR_MIIS] = {
[R_NPCM_PCS_VR_MII_MMD_DIG_CTRL1] = 0x2400,
[R_NPCM_PCS_VR_MII_AN_INTR_STS] = 0x000a,
[R_NPCM_PCS_VR_MII_EEE_MCTRL0] = 0x899c,
[R_NPCM_PCS_VR_MII_DIG_STS] = 0x0010,
[R_NPCM_PCS_VR_MII_MP_TX_BSTCTRL0] = 0x000a,
[R_NPCM_PCS_VR_MII_MP_TX_LVLCTRL0] = 0x007f,
[R_NPCM_PCS_VR_MII_MP_TX_GENCTRL0] = 0x0001,
[R_NPCM_PCS_VR_MII_MP_RX_GENCTRL0] = 0x0100,
[R_NPCM_PCS_VR_MII_MP_RX_GENCTRL1] = 0x1100,
[R_NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0] = 0x000e,
[R_NPCM_PCS_VR_MII_MP_MPLL_CTRL0] = 0x0100,
[R_NPCM_PCS_VR_MII_MP_MPLL_CTRL1] = 0x0032,
[R_NPCM_PCS_VR_MII_MP_MPLL_STS] = 0x0001,
[R_NPCM_PCS_VR_MII_MP_LVL_CTRL] = 0x0019,
};
static void npcm_pcs_soft_reset(NPCMPCSState *s)
{
memcpy(s->sr_ctl, npcm_pcs_sr_ctl_cold_reset_values,
NPCM_PCS_NR_SR_CTLS * sizeof(uint16_t));
memcpy(s->sr_mii, npcm_pcs_sr_mii_cold_reset_values,
NPCM_PCS_NR_SR_MIIS * sizeof(uint16_t));
memcpy(s->sr_tim, npcm_pcs_sr_tim_cold_reset_values,
NPCM_PCS_NR_SR_TIMS * sizeof(uint16_t));
memcpy(s->vr_mii, npcm_pcs_vr_mii_cold_reset_values,
NPCM_PCS_NR_VR_MIIS * sizeof(uint16_t));
}
static uint16_t npcm_pcs_read_sr_ctl(NPCMPCSState *s, hwaddr offset)
{
hwaddr regno = offset / sizeof(uint16_t);
if (regno >= NPCM_PCS_NR_SR_CTLS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: SR_CTL read offset 0x%04" HWADDR_PRIx
" is out of range.\n",
DEVICE(s)->canonical_path, offset);
return 0;
}
return s->sr_ctl[regno];
}
static uint16_t npcm_pcs_read_sr_mii(NPCMPCSState *s, hwaddr offset)
{
hwaddr regno = offset / sizeof(uint16_t);
if (regno >= NPCM_PCS_NR_SR_MIIS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: SR_MII read offset 0x%04" HWADDR_PRIx
" is out of range.\n",
DEVICE(s)->canonical_path, offset);
return 0;
}
return s->sr_mii[regno];
}
static uint16_t npcm_pcs_read_sr_tim(NPCMPCSState *s, hwaddr offset)
{
hwaddr regno = offset / sizeof(uint16_t);
if (regno >= NPCM_PCS_NR_SR_TIMS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: SR_TIM read offset 0x%04" HWADDR_PRIx
" is out of range.\n",
DEVICE(s)->canonical_path, offset);
return 0;
}
return s->sr_tim[regno];
}
static uint16_t npcm_pcs_read_vr_mii(NPCMPCSState *s, hwaddr offset)
{
hwaddr regno = offset / sizeof(uint16_t);
if (regno >= NPCM_PCS_NR_VR_MIIS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: VR_MII read offset 0x%04" HWADDR_PRIx
" is out of range.\n",
DEVICE(s)->canonical_path, offset);
return 0;
}
return s->vr_mii[regno];
}
static void npcm_pcs_write_sr_ctl(NPCMPCSState *s, hwaddr offset, uint16_t v)
{
hwaddr regno = offset / sizeof(uint16_t);
if (regno >= NPCM_PCS_NR_SR_CTLS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: SR_CTL write offset 0x%04" HWADDR_PRIx
" is out of range.\n",
DEVICE(s)->canonical_path, offset);
return;
}
s->sr_ctl[regno] = v;
}
static void npcm_pcs_write_sr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v)
{
hwaddr regno = offset / sizeof(uint16_t);
if (regno >= NPCM_PCS_NR_SR_MIIS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: SR_MII write offset 0x%04" HWADDR_PRIx
" is out of range.\n",
DEVICE(s)->canonical_path, offset);
return;
}
s->sr_mii[regno] = v;
if ((offset == A_NPCM_PCS_SR_MII_CTRL) && (v & NPCM_PCS_SR_MII_CTRL_RST)) {
/* Trigger a soft reset */
npcm_pcs_soft_reset(s);
}
}
static void npcm_pcs_write_sr_tim(NPCMPCSState *s, hwaddr offset, uint16_t v)
{
hwaddr regno = offset / sizeof(uint16_t);
if (regno >= NPCM_PCS_NR_SR_TIMS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: SR_TIM write offset 0x%04" HWADDR_PRIx
" is out of range.\n",
DEVICE(s)->canonical_path, offset);
return;
}
s->sr_tim[regno] = v;
}
static void npcm_pcs_write_vr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v)
{
hwaddr regno = offset / sizeof(uint16_t);
if (regno >= NPCM_PCS_NR_VR_MIIS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: VR_MII write offset 0x%04" HWADDR_PRIx
" is out of range.\n",
DEVICE(s)->canonical_path, offset);
return;
}
s->vr_mii[regno] = v;
}
static uint64_t npcm_pcs_read(void *opaque, hwaddr offset, unsigned size)
{
NPCMPCSState *s = opaque;
uint16_t v = 0;
if (offset == NPCM_PCS_IND_AC_BA) {
v = s->indirect_access_base;
} else {
switch (s->indirect_access_base) {
case NPCM_PCS_IND_SR_CTL:
v = npcm_pcs_read_sr_ctl(s, offset);
break;
case NPCM_PCS_IND_SR_MII:
v = npcm_pcs_read_sr_mii(s, offset);
break;
case NPCM_PCS_IND_SR_TIM:
v = npcm_pcs_read_sr_tim(s, offset);
break;
case NPCM_PCS_IND_VR_MII:
v = npcm_pcs_read_vr_mii(s, offset);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Read with invalid indirect address base: 0x%"
PRIx16 "\n", DEVICE(s)->canonical_path,
s->indirect_access_base);
}
}
trace_npcm_pcs_reg_read(DEVICE(s)->canonical_path, s->indirect_access_base,
offset, v);
return v;
}
static void npcm_pcs_write(void *opaque, hwaddr offset,
uint64_t v, unsigned size)
{
NPCMPCSState *s = opaque;
trace_npcm_pcs_reg_write(DEVICE(s)->canonical_path, s->indirect_access_base,
offset, v);
if (offset == NPCM_PCS_IND_AC_BA) {
s->indirect_access_base = v;
} else {
switch (s->indirect_access_base) {
case NPCM_PCS_IND_SR_CTL:
npcm_pcs_write_sr_ctl(s, offset, v);
break;
case NPCM_PCS_IND_SR_MII:
npcm_pcs_write_sr_mii(s, offset, v);
break;
case NPCM_PCS_IND_SR_TIM:
npcm_pcs_write_sr_tim(s, offset, v);
break;
case NPCM_PCS_IND_VR_MII:
npcm_pcs_write_vr_mii(s, offset, v);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Write with invalid indirect address base: 0x%02"
PRIx16 "\n", DEVICE(s)->canonical_path,
s->indirect_access_base);
}
}
}
static void npcm_pcs_enter_reset(Object *obj, ResetType type)
{
NPCMPCSState *s = NPCM_PCS(obj);
npcm_pcs_soft_reset(s);
}
static const struct MemoryRegionOps npcm_pcs_ops = {
.read = npcm_pcs_read,
.write = npcm_pcs_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 2,
.max_access_size = 2,
.unaligned = false,
},
};
static void npcm_pcs_realize(DeviceState *dev, Error **errp)
{
NPCMPCSState *pcs = NPCM_PCS(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
memory_region_init_io(&pcs->iomem, OBJECT(pcs), &npcm_pcs_ops, pcs,
TYPE_NPCM_PCS, 8 * KiB);
sysbus_init_mmio(sbd, &pcs->iomem);
}
static const VMStateDescription vmstate_npcm_pcs = {
.name = TYPE_NPCM_PCS,
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
VMSTATE_UINT16(indirect_access_base, NPCMPCSState),
VMSTATE_UINT16_ARRAY(sr_ctl, NPCMPCSState, NPCM_PCS_NR_SR_CTLS),
VMSTATE_UINT16_ARRAY(sr_mii, NPCMPCSState, NPCM_PCS_NR_SR_MIIS),
VMSTATE_UINT16_ARRAY(sr_tim, NPCMPCSState, NPCM_PCS_NR_SR_TIMS),
VMSTATE_UINT16_ARRAY(vr_mii, NPCMPCSState, NPCM_PCS_NR_VR_MIIS),
VMSTATE_END_OF_LIST(),
},
};
static void npcm_pcs_class_init(ObjectClass *klass, void *data)
{
ResettableClass *rc = RESETTABLE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->desc = "NPCM PCS Controller";
dc->realize = npcm_pcs_realize;
dc->vmsd = &vmstate_npcm_pcs;
rc->phases.enter = npcm_pcs_enter_reset;
}
static const TypeInfo npcm_pcs_types[] = {
{
.name = TYPE_NPCM_PCS,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(NPCMPCSState),
.class_init = npcm_pcs_class_init,
},
};
DEFINE_TYPES(npcm_pcs_types)

View File

@ -483,8 +483,8 @@ npcm_gmac_packet_tx_desc_data(const char* name, uint32_t tdes0, uint32_t tdes1)
npcm_gmac_tx_desc_owner(const char* name, uint32_t desc_addr) "%s: TX Descriptor @0x%04" PRIX32 " is owned by software"
# npcm_pcs.c
npcm_pcs_reg_read(const char *name, uint16_t indirect_access_baes, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16
npcm_pcs_reg_write(const char *name, uint16_t indirect_access_baes, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16
npcm_pcs_reg_read(const char *name, uint16_t indirect_access_base, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16
npcm_pcs_reg_write(const char *name, uint16_t indirect_access_base, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16
# dp8398x.c
dp8393x_raise_irq(int isr) "raise irq, isr is 0x%04x"

View File

@ -29,7 +29,7 @@
#include "trace.h"
/* Up to 128 MiB of flash may be accessed directly as memory. */
#define NPCM7XX_FIU_FLASH_WINDOW_SIZE (128 * MiB)
#define NPCM7XX_FIU_MAX_FLASH_WINDOW_SIZE (128 * MiB)
/* Each module has 4 KiB of register space. Only a fraction of it is used. */
#define NPCM7XX_FIU_CTRL_REGS_SIZE (4 * KiB)
@ -507,6 +507,17 @@ static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp)
return;
}
if (s->flash_size == 0) {
error_setg(errp, "%s: flash size must be set", dev->canonical_path);
return;
}
if (s->flash_size > NPCM7XX_FIU_MAX_FLASH_WINDOW_SIZE) {
error_setg(errp, "%s: flash size should not exceed 128 MiB",
dev->canonical_path);
return;
}
s->spi = ssi_create_bus(dev, "spi");
s->cs_lines = g_new0(qemu_irq, s->cs_count);
qdev_init_gpio_out_named(DEVICE(s), s->cs_lines, "cs", s->cs_count);
@ -525,7 +536,7 @@ static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp)
flash->fiu = s;
memory_region_init_io(&flash->direct_access, OBJECT(s),
&npcm7xx_fiu_flash_ops, &s->flash[i], "flash",
NPCM7XX_FIU_FLASH_WINDOW_SIZE);
s->flash_size);
sysbus_init_mmio(sbd, &flash->direct_access);
}
}
@ -543,6 +554,7 @@ static const VMStateDescription vmstate_npcm7xx_fiu = {
static const Property npcm7xx_fiu_properties[] = {
DEFINE_PROP_INT32("cs-count", NPCM7xxFIUState, cs_count, 0),
DEFINE_PROP_SIZE("flash-size", NPCM7xxFIUState, flash_size, 0),
};
static void npcm7xx_fiu_class_init(ObjectClass *klass, void *data)

View File

@ -143,3 +143,7 @@ config USB_DWC3
config XLNX_USB_SUBSYS
bool
select USB_DWC3
config USB_CHIPIDEA
bool
select USB_EHCI_SYSBUS

View File

@ -25,8 +25,8 @@ system_ss.add(when: 'CONFIG_USB_XHCI_SYSBUS', if_true: files('hcd-xhci-sysbus.c'
system_ss.add(when: 'CONFIG_USB_XHCI_NEC', if_true: files('hcd-xhci-nec.c'))
system_ss.add(when: 'CONFIG_USB_DWC2', if_true: files('hcd-dwc2.c'))
system_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c'))
system_ss.add(when: 'CONFIG_USB_CHIPIDEA', if_true: files('chipidea.c'))
system_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c'))
system_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c'))
system_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c'))
system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c'))

View File

@ -23,8 +23,8 @@
#include "hw/gpio/npcm7xx_gpio.h"
#include "hw/i2c/npcm7xx_smbus.h"
#include "hw/mem/npcm7xx_mc.h"
#include "hw/misc/npcm7xx_clk.h"
#include "hw/misc/npcm7xx_gcr.h"
#include "hw/misc/npcm_clk.h"
#include "hw/misc/npcm_gcr.h"
#include "hw/misc/npcm7xx_mft.h"
#include "hw/misc/npcm7xx_pwm.h"
#include "hw/misc/npcm7xx_rng.h"
@ -89,8 +89,8 @@ struct NPCM7xxState {
MemoryRegion ram3;
MemoryRegion *dram;
NPCM7xxGCRState gcr;
NPCM7xxCLKState clk;
NPCMGCRState gcr;
NPCMCLKState clk;
NPCM7xxTimerCtrlState tim[3];
NPCM7xxADCState adc;
NPCM7xxPWMState pwm[NPCM7XX_NR_PWM_MODULES];

127
include/hw/arm/npcm8xx.h Normal file
View File

@ -0,0 +1,127 @@
/*
* Nuvoton NPCM8xx SoC family.
*
* Copyright 2022 Google LLC
*
* 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.
*/
#ifndef NPCM8XX_H
#define NPCM8XX_H
#include "hw/adc/npcm7xx_adc.h"
#include "hw/core/split-irq.h"
#include "hw/cpu/cluster.h"
#include "hw/gpio/npcm7xx_gpio.h"
#include "hw/i2c/npcm7xx_smbus.h"
#include "hw/intc/arm_gic_common.h"
#include "hw/mem/npcm7xx_mc.h"
#include "hw/misc/npcm_clk.h"
#include "hw/misc/npcm_gcr.h"
#include "hw/misc/npcm7xx_mft.h"
#include "hw/misc/npcm7xx_pwm.h"
#include "hw/misc/npcm7xx_rng.h"
#include "hw/net/npcm7xx_emc.h"
#include "hw/nvram/npcm7xx_otp.h"
#include "hw/sd/npcm7xx_sdhci.h"
#include "hw/timer/npcm7xx_timer.h"
#include "hw/ssi/npcm7xx_fiu.h"
#include "hw/usb/hcd-ehci.h"
#include "hw/usb/hcd-ohci.h"
#include "target/arm/cpu.h"
#define NPCM8XX_MAX_NUM_CPUS (4)
/* The first half of the address space is reserved for DDR4 DRAM. */
#define NPCM8XX_DRAM_BA (0x00000000)
#define NPCM8XX_DRAM_SZ (2 * GiB)
/* Magic addresses for setting up direct kernel booting and SMP boot stubs. */
#define NPCM8XX_LOADER_START (0x00000000) /* Start of SDRAM */
#define NPCM8XX_SMP_LOADER_START (0xffff0000) /* Boot ROM */
#define NPCM8XX_SMP_BOOTREG_ADDR (0xf080013c) /* GCR.SCRPAD */
#define NPCM8XX_BOARD_SETUP_ADDR (0xffff1000) /* Boot ROM */
#define NPCM8XX_NR_PWM_MODULES 3
struct NPCM8xxMachine {
MachineState parent_obj;
/*
* PWM fan splitter. each splitter connects to one PWM output and
* multiple MFT inputs.
*/
SplitIRQ fan_splitter[NPCM8XX_NR_PWM_MODULES *
NPCM7XX_PWM_PER_MODULE];
};
struct NPCM8xxMachineClass {
MachineClass parent_class;
const char *soc_type;
};
#define TYPE_NPCM8XX_MACHINE MACHINE_TYPE_NAME("npcm8xx")
OBJECT_DECLARE_TYPE(NPCM8xxMachine, NPCM8xxMachineClass, NPCM8XX_MACHINE)
struct NPCM8xxState {
DeviceState parent_obj;
ARMCPU cpu[NPCM8XX_MAX_NUM_CPUS];
CPUClusterState cpu_cluster;
GICState gic;
MemoryRegion sram;
MemoryRegion irom;
MemoryRegion ram3;
MemoryRegion *dram;
NPCMGCRState gcr;
NPCMCLKState clk;
NPCM7xxTimerCtrlState tim[3];
NPCM7xxADCState adc;
NPCM7xxPWMState pwm[NPCM8XX_NR_PWM_MODULES];
NPCM7xxMFTState mft[8];
NPCM7xxOTPState fuse_array;
NPCM7xxMCState mc;
NPCM7xxRNGState rng;
NPCM7xxGPIOState gpio[8];
NPCM7xxSMBusState smbus[27];
EHCISysBusState ehci[2];
OHCISysBusState ohci[2];
NPCM7xxFIUState fiu[3];
NPCM7xxSDHCIState mmc;
};
struct NPCM8xxClass {
DeviceClass parent_class;
/* Bitmask of modules that are permanently disabled on this chip. */
uint32_t disabled_modules;
/* Number of CPU cores enabled in this SoC class. */
uint32_t num_cpus;
};
#define TYPE_NPCM8XX "npcm8xx"
OBJECT_DECLARE_TYPE(NPCM8xxState, NPCM8xxClass, NPCM8XX)
/**
* npcm8xx_load_kernel - Loads memory with everything needed to boot
* @machine - The machine containing the SoC to be booted.
* @soc - The SoC containing the CPU to be booted.
*
* This will set up the ARM boot info structure for the specific NPCM8xx
* derivative and call arm_load_kernel() to set up loading of the kernel, etc.
* into memory, if requested by the user.
*/
void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc);
#endif /* NPCM8XX_H */

View File

@ -1,5 +1,5 @@
/*
* Nuvoton NPCM7xx Clock Control Registers.
* Nuvoton NPCM7xx/8xx Clock Control Registers.
*
* Copyright 2020 Google LLC
*
@ -13,18 +13,20 @@
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef NPCM7XX_CLK_H
#define NPCM7XX_CLK_H
#ifndef NPCM_CLK_H
#define NPCM_CLK_H
#include "exec/memory.h"
#include "hw/clock.h"
#include "hw/sysbus.h"
/*
* Number of registers in our device state structure. Don't change this without
* incrementing the version_id in the vmstate.
*/
#define NPCM7XX_CLK_NR_REGS (0x70 / sizeof(uint32_t))
#define NPCM8XX_CLK_NR_REGS (0xc4 / sizeof(uint32_t))
/*
* Number of maximum registers in NPCM device state structure. Don't change
* this without incrementing the version_id in the vmstate.
*/
#define NPCM_CLK_MAX_NR_REGS NPCM8XX_CLK_NR_REGS
#define NPCM7XX_WATCHDOG_RESET_GPIO_IN "npcm7xx-clk-watchdog-reset-gpio-in"
@ -80,7 +82,7 @@ typedef enum NPCM7xxClockDivider {
NPCM7XX_CLOCK_NR_DIVIDERS,
} NPCM7xxClockConverter;
typedef struct NPCM7xxCLKState NPCM7xxCLKState;
typedef struct NPCMCLKState NPCMCLKState;
/**
* struct NPCM7xxClockPLLState - A PLL module in CLK module.
@ -94,7 +96,7 @@ typedef struct NPCM7xxClockPLLState {
DeviceState parent;
const char *name;
NPCM7xxCLKState *clk;
NPCMCLKState *clk;
Clock *clock_in;
Clock *clock_out;
@ -115,7 +117,7 @@ typedef struct NPCM7xxClockSELState {
DeviceState parent;
const char *name;
NPCM7xxCLKState *clk;
NPCMCLKState *clk;
uint8_t input_size;
Clock *clock_in[NPCM7XX_CLK_SEL_MAX_INPUT];
Clock *clock_out;
@ -140,7 +142,7 @@ typedef struct NPCM7xxClockDividerState {
DeviceState parent;
const char *name;
NPCM7xxCLKState *clk;
NPCMCLKState *clk;
Clock *clock_in;
Clock *clock_out;
@ -155,17 +157,21 @@ typedef struct NPCM7xxClockDividerState {
};
} NPCM7xxClockDividerState;
struct NPCM7xxCLKState {
struct NPCMCLKState {
SysBusDevice parent;
MemoryRegion iomem;
/* Clock converters */
/*
* TODO: Implement unique clock converters for NPCM8xx.
* NPCM8xx adds a few more clock outputs.
*/
NPCM7xxClockPLLState plls[NPCM7XX_CLOCK_NR_PLLS];
NPCM7xxClockSELState sels[NPCM7XX_CLOCK_NR_SELS];
NPCM7xxClockDividerState dividers[NPCM7XX_CLOCK_NR_DIVIDERS];
uint32_t regs[NPCM7XX_CLK_NR_REGS];
uint32_t regs[NPCM_CLK_MAX_NR_REGS];
/* Time reference for SECCNT and CNTR25M, initialized by power on reset */
int64_t ref_ns;
@ -174,7 +180,16 @@ struct NPCM7xxCLKState {
Clock *clkref;
};
#define TYPE_NPCM7XX_CLK "npcm7xx-clk"
OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxCLKState, NPCM7XX_CLK)
typedef struct NPCMCLKClass {
SysBusDeviceClass parent;
#endif /* NPCM7XX_CLK_H */
size_t nr_regs;
const uint32_t *cold_reset_values;
} NPCMCLKClass;
#define TYPE_NPCM_CLK "npcm-clk"
OBJECT_DECLARE_TYPE(NPCMCLKState, NPCMCLKClass, NPCM_CLK)
#define TYPE_NPCM7XX_CLK "npcm7xx-clk"
#define TYPE_NPCM8XX_CLK "npcm8xx-clk"
#endif /* NPCM_CLK_H */

View File

@ -1,5 +1,5 @@
/*
* Nuvoton NPCM7xx System Global Control Registers.
* Nuvoton NPCM7xx/8xx System Global Control Registers.
*
* Copyright 2020 Google LLC
*
@ -13,11 +13,12 @@
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef NPCM7XX_GCR_H
#define NPCM7XX_GCR_H
#ifndef NPCM_GCR_H
#define NPCM_GCR_H
#include "exec/memory.h"
#include "hw/sysbus.h"
#include "qom/object.h"
/*
* NPCM7XX PWRON STRAP bit fields
@ -53,21 +54,33 @@
* Number of registers in our device state structure. Don't change this without
* incrementing the version_id in the vmstate.
*/
#define NPCM_GCR_MAX_NR_REGS NPCM8XX_GCR_NR_REGS
#define NPCM7XX_GCR_NR_REGS (0x148 / sizeof(uint32_t))
#define NPCM8XX_GCR_NR_REGS (0xf80 / sizeof(uint32_t))
struct NPCM7xxGCRState {
typedef struct NPCMGCRState {
SysBusDevice parent;
MemoryRegion iomem;
uint32_t regs[NPCM7XX_GCR_NR_REGS];
uint32_t regs[NPCM_GCR_MAX_NR_REGS];
uint32_t reset_pwron;
uint32_t reset_mdlr;
uint32_t reset_intcr3;
};
uint32_t reset_scrpad_b;
} NPCMGCRState;
typedef struct NPCMGCRClass {
SysBusDeviceClass parent;
size_t nr_regs;
const uint32_t *cold_reset_values;
} NPCMGCRClass;
#define TYPE_NPCM_GCR "npcm-gcr"
#define TYPE_NPCM7XX_GCR "npcm7xx-gcr"
OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxGCRState, NPCM7XX_GCR)
#define TYPE_NPCM8XX_GCR "npcm8xx-gcr"
OBJECT_DECLARE_TYPE(NPCMGCRState, NPCMGCRClass, NPCM_GCR)
#endif /* NPCM7XX_GCR_H */
#endif /* NPCM_GCR_H */

42
include/hw/net/npcm_pcs.h Normal file
View File

@ -0,0 +1,42 @@
/*
* Nuvoton NPCM8xx PCS Module
*
* Copyright 2022 Google LLC
*
* 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.
*/
#ifndef NPCM_PCS_H
#define NPCM_PCS_H
#include "hw/sysbus.h"
#define NPCM_PCS_NR_SR_CTLS (0x12 / sizeof(uint16_t))
#define NPCM_PCS_NR_SR_MIIS (0x20 / sizeof(uint16_t))
#define NPCM_PCS_NR_SR_TIMS (0x22 / sizeof(uint16_t))
#define NPCM_PCS_NR_VR_MIIS (0x1c6 / sizeof(uint16_t))
struct NPCMPCSState {
SysBusDevice parent;
MemoryRegion iomem;
uint16_t indirect_access_base;
uint16_t sr_ctl[NPCM_PCS_NR_SR_CTLS];
uint16_t sr_mii[NPCM_PCS_NR_SR_MIIS];
uint16_t sr_tim[NPCM_PCS_NR_SR_TIMS];
uint16_t vr_mii[NPCM_PCS_NR_VR_MIIS];
};
#define TYPE_NPCM_PCS "npcm-pcs"
OBJECT_DECLARE_SIMPLE_TYPE(NPCMPCSState, NPCM_PCS)
#endif /* NPCM_PCS_H */

View File

@ -60,6 +60,7 @@ struct NPCM7xxFIUState {
int32_t cs_count;
int32_t active_cs;
qemu_irq *cs_lines;
uint64_t flash_size;
NPCM7xxFIUFlash *flash;
SSIBus *spi;

View File

@ -70,10 +70,10 @@
source code also contains code reused from other projects described here:
https://github.com/riscv/opensbi/blob/master/ThirdPartyNotices.md.
- npcm7xx_bootrom.bin is a simplified, free (Apache 2.0) boot ROM for Nuvoton
NPCM7xx BMC devices. It currently implements the bare minimum to load, parse,
initialize and run boot images stored in SPI flash, but may grow more
features over time as needed. The source code is available at:
- npcm{7xx,8xx}_bootrom.bin is a simplified, free (Apache 2.0) boot ROM for
Nuvoton NPCM7xx/8xx BMC devices. It currently implements the bare minimum to
load, parse, initialize and run boot images stored in SPI flash, but may grow
more features over time as needed. The source code is available at:
https://github.com/google/vbootrom
- hppa-firmware.img (32-bit) and hppa-firmware64.img (64-bit) are firmware

View File

@ -80,6 +80,7 @@ blobs = [
'opensbi-riscv32-generic-fw_dynamic.bin',
'opensbi-riscv64-generic-fw_dynamic.bin',
'npcm7xx_bootrom.bin',
'npcm8xx_bootrom.bin',
'vof.bin',
'vof-nvram.bin',
]

Binary file not shown.

BIN
pc-bios/npcm8xx_bootrom.bin Normal file

Binary file not shown.

View File

@ -34,6 +34,7 @@ find-cross-gcc = $(firstword $(wildcard $(patsubst %ld,%gcc,$(call find-cross-ld
# finally strip off path + toolname so we get the prefix
find-cross-prefix = $(subst gcc,,$(notdir $(call find-cross-gcc,$(1))))
aarch64_cross_prefix := $(call find-cross-prefix,aarch64)
arm_cross_prefix := $(call find-cross-prefix,arm)
powerpc64_cross_prefix := $(call find-cross-prefix,powerpc64)
powerpc_cross_prefix := $(call find-cross-prefix,powerpc)
@ -66,6 +67,7 @@ default help:
@echo " u-boot.e500 -- update u-boot.e500"
@echo " u-boot.sam460 -- update u-boot.sam460"
@echo " npcm7xx_bootrom -- update vbootrom for npcm7xx"
@echo " npcm8xx_bootrom -- update vbootrom for npcm8xx"
@echo " efi -- update UEFI (edk2) platform firmware"
@echo " opensbi32-generic -- update OpenSBI for 32-bit generic machine"
@echo " opensbi64-generic -- update OpenSBI for 64-bit generic machine"
@ -194,6 +196,10 @@ npcm7xx_bootrom:
$(MAKE) -C vbootrom CROSS_COMPILE=$(arm_cross_prefix)
cp vbootrom/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin
npcm8xx_bootrom:
$(MAKE) -C vbootrom CROSS_COMPILE=$(aarch64_cross_prefix)
cp vbootrom/npcm8xx_bootrom.bin ../pc-bios/npcm8xx_bootrom.bin
hppa-firmware:
$(MAKE) -C seabios-hppa parisc
cp seabios-hppa/out/hppa-firmware.img ../pc-bios/

@ -1 +1 @@
Subproject commit 0c37a43527f0ee2b9584e7fb2fdc805e902635ac
Subproject commit 1287b6e42e839ba2ab0f06268c5b53ae60df3537

View File

@ -328,20 +328,23 @@ typedef enum CPAccessResult {
* Access fails due to a configurable trap or enable which would
* result in a categorized exception syndrome giving information about
* the failing instruction (ie syndrome category 0x3, 0x4, 0x5, 0x6,
* 0xc or 0x18).
* 0xc or 0x18). These traps are always to a specified target EL,
* never to the usual target EL.
*/
CP_ACCESS_TRAP = (1 << 2),
CP_ACCESS_TRAP_EL2 = CP_ACCESS_TRAP | 2,
CP_ACCESS_TRAP_EL3 = CP_ACCESS_TRAP | 3,
CP_ACCESS_TRAP_BIT = (1 << 2),
CP_ACCESS_TRAP_EL1 = CP_ACCESS_TRAP_BIT | 1,
CP_ACCESS_TRAP_EL2 = CP_ACCESS_TRAP_BIT | 2,
CP_ACCESS_TRAP_EL3 = CP_ACCESS_TRAP_BIT | 3,
/*
* Access fails and results in an exception syndrome 0x0 ("uncategorized").
* Access fails with UNDEFINED, i.e. an exception syndrome 0x0
* ("uncategorized"), which is what an undefined insn produces.
* Note that this is not a catch-all case -- the set of cases which may
* result in this failure is specifically defined by the architecture.
* This trap is always to the usual target EL, never directly to a
* specified target EL.
*/
CP_ACCESS_TRAP_UNCATEGORIZED = (2 << 2),
CP_ACCESS_UNDEFINED = (2 << 2),
} CPAccessResult;
/* Indexes into fgt_read[] */

View File

@ -62,6 +62,7 @@
#define EXCP_NMI 26
#define EXCP_VINMI 27
#define EXCP_VFNMI 28
#define EXCP_MON_TRAP 29 /* AArch32 trap to Monitor mode */
/* NB: add new EXCP_ defines to the array in arm_log_exception() too */
#define ARMV7M_EXCP_RESET 1
@ -2595,6 +2596,11 @@ static inline bool arm_is_secure_below_el3(CPUARMState *env)
return false;
}
static inline bool arm_is_el3_or_mon(CPUARMState *env)
{
return false;
}
static inline ARMSecuritySpace arm_security_space(CPUARMState *env)
{
return ARMSS_NonSecure;

View File

@ -875,12 +875,13 @@ static CPAccessResult access_tdcc(CPUARMState *env, const ARMCPRegInfo *ri,
(env->cp15.mdcr_el3 & MDCR_TDCC);
if (el < 1 && mdscr_el1_tdcc) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
if (el < 2 && (mdcr_el2_tda || mdcr_el2_tdcc)) {
return CP_ACCESS_TRAP_EL2;
}
if (el < 3 && ((env->cp15.mdcr_el3 & MDCR_TDA) || mdcr_el3_tdcc)) {
if (!arm_is_el3_or_mon(env) &&
((env->cp15.mdcr_el3 & MDCR_TDA) || mdcr_el3_tdcc)) {
return CP_ACCESS_TRAP_EL3;
}
return CP_ACCESS_OK;

View File

@ -285,7 +285,7 @@ static CPAccessResult access_el3_aa32ns(CPUARMState *env,
{
if (!is_a64(env) && arm_current_el(env) == 3 &&
arm_is_secure_below_el3(env)) {
return CP_ACCESS_TRAP_UNCATEGORIZED;
return CP_ACCESS_UNDEFINED;
}
return CP_ACCESS_OK;
}
@ -310,7 +310,7 @@ static CPAccessResult access_trap_aa32s_el1(CPUARMState *env,
return CP_ACCESS_TRAP_EL3;
}
/* This will be EL1 NS and EL2 NS, which just UNDEF */
return CP_ACCESS_TRAP_UNCATEGORIZED;
return CP_ACCESS_UNDEFINED;
}
/*
@ -881,7 +881,7 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t mdcr_el2 = arm_mdcr_el2_eff(env);
if (el == 0 && !(env->cp15.c9_pmuserenr & 1)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
if (el < 2 && (mdcr_el2 & MDCR_TPM)) {
return CP_ACCESS_TRAP_EL2;
@ -2159,7 +2159,7 @@ static CPAccessResult teehbr_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
if (arm_current_el(env) == 0 && (env->teecr & 1)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
return teecr_access(env, ri, isread);
}
@ -2239,14 +2239,14 @@ static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri,
cntkctl = env->cp15.c14_cntkctl;
}
if (!extract32(cntkctl, 0, 2)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
break;
case 1:
if (!isread && ri->state == ARM_CP_STATE_AA32 &&
arm_is_secure_below_el3(env)) {
/* Accesses from 32-bit Secure EL1 UNDEF (*not* trap to EL3!) */
return CP_ACCESS_TRAP_UNCATEGORIZED;
return CP_ACCESS_UNDEFINED;
}
break;
case 2:
@ -2255,7 +2255,7 @@ static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri,
}
if (!isread && el < arm_highest_el(env)) {
return CP_ACCESS_TRAP_UNCATEGORIZED;
return CP_ACCESS_UNDEFINED;
}
return CP_ACCESS_OK;
@ -2278,7 +2278,7 @@ static CPAccessResult gt_counter_access(CPUARMState *env, int timeridx,
/* CNT[PV]CT: not visible from PL0 if EL0[PV]CTEN is zero */
if (!extract32(env->cp15.c14_cntkctl, timeridx, 1)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
/* fall through */
case 1:
@ -2319,7 +2319,7 @@ static CPAccessResult gt_timer_access(CPUARMState *env, int timeridx,
* EL0 if EL0[PV]TEN is zero.
*/
if (!extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
/* fall through */
@ -2385,7 +2385,7 @@ static CPAccessResult gt_stimer_access(CPUARMState *env,
switch (arm_current_el(env)) {
case 1:
if (!arm_is_secure(env)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_UNDEFINED;
}
if (!(env->cp15.scr_el3 & SCR_ST)) {
return CP_ACCESS_TRAP_EL3;
@ -2393,7 +2393,7 @@ static CPAccessResult gt_stimer_access(CPUARMState *env,
return CP_ACCESS_OK;
case 0:
case 2:
return CP_ACCESS_TRAP;
return CP_ACCESS_UNDEFINED;
case 3:
return CP_ACCESS_OK;
default:
@ -3304,7 +3304,7 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri,
}
return CP_ACCESS_TRAP_EL3;
}
return CP_ACCESS_TRAP_UNCATEGORIZED;
return CP_ACCESS_UNDEFINED;
}
}
return CP_ACCESS_OK;
@ -3601,7 +3601,7 @@ static CPAccessResult at_e012_access(CPUARMState *env, const ARMCPRegInfo *ri,
* scr_write() ensures that the NSE bit is not set otherwise.
*/
if ((env->cp15.scr_el3 & (SCR_NSE | SCR_NS)) == SCR_NSE) {
return CP_ACCESS_TRAP;
return CP_ACCESS_UNDEFINED;
}
return CP_ACCESS_OK;
}
@ -3611,7 +3611,7 @@ static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri,
{
if (arm_current_el(env) == 3 &&
!(env->cp15.scr_el3 & (SCR_NS | SCR_EEL2))) {
return CP_ACCESS_TRAP;
return CP_ACCESS_UNDEFINED;
}
return at_e012_access(env, ri, isread);
}
@ -4499,7 +4499,7 @@ static CPAccessResult aa64_daif_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
if (arm_current_el(env) == 0 && !(arm_sctlr(env, 0) & SCTLR_UMA)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
return CP_ACCESS_OK;
}
@ -4589,9 +4589,9 @@ static CPAccessResult aa64_cacheop_poc_access(CPUARMState *env,
/* Cache invalidate/clean to Point of Coherency or Persistence... */
switch (arm_current_el(env)) {
case 0:
/* ... EL0 must UNDEF unless SCTLR_EL1.UCI is set. */
/* ... EL0 must trap to EL1 unless SCTLR_EL1.UCI is set. */
if (!(arm_sctlr(env, 0) & SCTLR_UCI)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
/* fall through */
case 1:
@ -4609,9 +4609,9 @@ static CPAccessResult do_cacheop_pou_access(CPUARMState *env, uint64_t hcrflags)
/* Cache invalidate/clean to Point of Unification... */
switch (arm_current_el(env)) {
case 0:
/* ... EL0 must UNDEF unless SCTLR_EL1.UCI is set. */
/* ... EL0 must trap to EL1 unless SCTLR_EL1.UCI is set. */
if (!(arm_sctlr(env, 0) & SCTLR_UCI)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
/* fall through */
case 1:
@ -4651,7 +4651,7 @@ static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri,
}
} else {
if (!(env->cp15.sctlr_el[1] & SCTLR_DZE)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
if (hcr & HCR_TDZ) {
return CP_ACCESS_TRAP_EL2;
@ -4684,7 +4684,7 @@ static CPAccessResult sp_el0_access(CPUARMState *env, const ARMCPRegInfo *ri,
* Access to SP_EL0 is undefined if it's being used as
* the stack pointer.
*/
return CP_ACCESS_TRAP_UNCATEGORIZED;
return CP_ACCESS_UNDEFINED;
}
return CP_ACCESS_OK;
}
@ -5674,7 +5674,7 @@ static CPAccessResult sel2_access(CPUARMState *env, const ARMCPRegInfo *ri,
if (arm_current_el(env) == 3 || arm_is_secure_below_el3(env)) {
return CP_ACCESS_OK;
}
return CP_ACCESS_TRAP_UNCATEGORIZED;
return CP_ACCESS_UNDEFINED;
}
static const ARMCPRegInfo el2_sec_cp_reginfo[] = {
@ -5710,7 +5710,7 @@ static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri,
if (isread) {
return CP_ACCESS_OK;
}
return CP_ACCESS_TRAP_UNCATEGORIZED;
return CP_ACCESS_UNDEFINED;
}
static const ARMCPRegInfo el3_cp_reginfo[] = {
@ -5798,7 +5798,7 @@ static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK;
}
if (!(arm_hcr_el2_eff(env) & HCR_E2H)) {
return CP_ACCESS_TRAP_UNCATEGORIZED;
return CP_ACCESS_UNDEFINED;
}
return CP_ACCESS_OK;
}
@ -5896,7 +5896,7 @@ static CPAccessResult el2_e2h_e12_access(CPUARMState *env,
}
/* FOO_EL12 aliases only exist when E2H is 1; otherwise they UNDEF */
if (!(arm_hcr_el2_eff(env) & HCR_E2H)) {
return CP_ACCESS_TRAP_UNCATEGORIZED;
return CP_ACCESS_UNDEFINED;
}
if (ri->orig_accessfn) {
return ri->orig_accessfn(env, ri->opaque, isread);
@ -6073,7 +6073,7 @@ static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri,
}
} else {
if (!(env->cp15.sctlr_el[1] & SCTLR_UCT)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
if (hcr & HCR_TID2) {
return CP_ACCESS_TRAP_EL2;
@ -6103,7 +6103,7 @@ static CPAccessResult access_terr(CPUARMState *env, const ARMCPRegInfo *ri,
if (el < 2 && (arm_hcr_el2_eff(env) & HCR_TERR)) {
return CP_ACCESS_TRAP_EL2;
}
if (el < 3 && (env->cp15.scr_el3 & SCR_TERR)) {
if (!arm_is_el3_or_mon(env) && (env->cp15.scr_el3 & SCR_TERR)) {
return CP_ACCESS_TRAP_EL3;
}
return CP_ACCESS_OK;
@ -6372,7 +6372,7 @@ static CPAccessResult access_tpidr2(CPUARMState *env, const ARMCPRegInfo *ri,
if (el == 0) {
uint64_t sctlr = arm_sctlr(env, el);
if (!(sctlr & SCTLR_EnTP2)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
}
/* TODO: FEAT_FGT */
@ -6750,8 +6750,8 @@ static CPAccessResult access_lor_other(CPUARMState *env,
const ARMCPRegInfo *ri, bool isread)
{
if (arm_is_secure_below_el3(env)) {
/* Access denied in secure mode. */
return CP_ACCESS_TRAP;
/* UNDEF if SCR_EL3.NS == 0 */
return CP_ACCESS_UNDEFINED;
}
return access_lor_ns(env, ri, isread);
}
@ -7172,7 +7172,7 @@ static CPAccessResult access_scxtnum(CPUARMState *env, const ARMCPRegInfo *ri,
if (hcr & HCR_TGE) {
return CP_ACCESS_TRAP_EL2;
}
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
} else if (el < 2 && (env->cp15.sctlr_el[2] & SCTLR_TSCXT)) {
return CP_ACCESS_TRAP_EL2;
@ -7292,7 +7292,7 @@ static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri,
if (el == 0) {
uint64_t sctlr = arm_sctlr(env, el);
if (!(sctlr & SCTLR_EnRCTX)) {
return CP_ACCESS_TRAP;
return CP_ACCESS_TRAP_EL1;
}
} else if (el == 1) {
uint64_t hcr = arm_hcr_el2_eff(env);
@ -9684,6 +9684,7 @@ void arm_log_exception(CPUState *cs)
[EXCP_NMI] = "NMI",
[EXCP_VINMI] = "Virtual IRQ NMI",
[EXCP_VFNMI] = "Virtual FIQ NMI",
[EXCP_MON_TRAP] = "Monitor Trap",
};
if (idx >= 0 && idx < ARRAY_SIZE(excnames)) {
@ -10250,6 +10251,16 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs)
mask = CPSR_A | CPSR_I | CPSR_F;
offset = 0;
break;
case EXCP_MON_TRAP:
new_mode = ARM_CPU_MODE_MON;
addr = 0x04;
mask = CPSR_A | CPSR_I | CPSR_F;
if (env->thumb) {
offset = 2;
} else {
offset = 4;
}
break;
default:
cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
return; /* Never happens. Keep compiler happy. */

View File

@ -313,15 +313,19 @@ void HELPER(check_bxj_trap)(CPUARMState *env, uint32_t rm)
}
#ifndef CONFIG_USER_ONLY
/* Function checks whether WFx (WFI/WFE) instructions are set up to be trapped.
/*
* Function checks whether WFx (WFI/WFE) instructions are set up to be trapped.
* The function returns the target EL (1-3) if the instruction is to be trapped;
* otherwise it returns 0 indicating it is not trapped.
* For a trap, *excp is updated with the EXCP_* trap type to use.
*/
static inline int check_wfx_trap(CPUARMState *env, bool is_wfe)
static inline int check_wfx_trap(CPUARMState *env, bool is_wfe, uint32_t *excp)
{
int cur_el = arm_current_el(env);
uint64_t mask;
*excp = EXCP_UDEF;
if (arm_feature(env, ARM_FEATURE_M)) {
/* M profile cores can never trap WFI/WFE. */
return 0;
@ -331,18 +335,9 @@ static inline int check_wfx_trap(CPUARMState *env, bool is_wfe)
* WFx instructions being trapped to EL1. These trap bits don't exist in v7.
*/
if (cur_el < 1 && arm_feature(env, ARM_FEATURE_V8)) {
int target_el;
mask = is_wfe ? SCTLR_nTWE : SCTLR_nTWI;
if (arm_is_secure_below_el3(env) && !arm_el_is_aa64(env, 3)) {
/* Secure EL0 and Secure PL1 is at EL3 */
target_el = 3;
} else {
target_el = 1;
}
if (!(env->cp15.sctlr_el[target_el] & mask)) {
return target_el;
if (!(arm_sctlr(env, cur_el) & mask)) {
return exception_target_el(env);
}
}
@ -358,9 +353,12 @@ static inline int check_wfx_trap(CPUARMState *env, bool is_wfe)
}
/* We are not trapping to EL1 or EL2; trap to EL3 if SCR_EL3 requires it */
if (cur_el < 3) {
if (arm_feature(env, ARM_FEATURE_V8) && !arm_is_el3_or_mon(env)) {
mask = (is_wfe) ? SCR_TWE : SCR_TWI;
if (env->cp15.scr_el3 & mask) {
if (!arm_el_is_aa64(env, 3)) {
*excp = EXCP_MON_TRAP;
}
return 3;
}
}
@ -383,7 +381,8 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len)
return;
#else
CPUState *cs = env_cpu(env);
int target_el = check_wfx_trap(env, false);
uint32_t excp;
int target_el = check_wfx_trap(env, false, &excp);
if (cpu_has_work(cs)) {
/* Don't bother to go into our "low power state" if
@ -399,7 +398,7 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len)
env->regs[15] -= insn_len;
}
raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, insn_len == 2),
raise_exception(env, excp, syn_wfx(1, 0xe, 0, insn_len == 2),
target_el);
}
@ -424,7 +423,8 @@ void HELPER(wfit)(CPUARMState *env, uint64_t timeout)
#else
ARMCPU *cpu = env_archcpu(env);
CPUState *cs = env_cpu(env);
int target_el = check_wfx_trap(env, false);
uint32_t excp;
int target_el = check_wfx_trap(env, false, &excp);
/* The WFIT should time out when CNTVCT_EL0 >= the specified value. */
uint64_t cntval = gt_get_countervalue(env);
uint64_t offset = gt_virt_cnt_offset(env);
@ -441,8 +441,7 @@ void HELPER(wfit)(CPUARMState *env, uint64_t timeout)
if (target_el) {
env->pc -= 4;
raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, false),
target_el);
raise_exception(env, excp, syn_wfx(1, 0xe, 0, false), target_el);
}
if (uadd64_overflow(timeout, offset, &nexttick)) {
@ -758,12 +757,13 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key,
const ARMCPRegInfo *ri = get_arm_cp_reginfo(cpu->cp_regs, key);
CPAccessResult res = CP_ACCESS_OK;
int target_el;
uint32_t excp;
assert(ri != NULL);
if (arm_feature(env, ARM_FEATURE_XSCALE) && ri->cp < 14
&& extract32(env->cp15.c15_cpar, ri->cp, 1) == 0) {
res = CP_ACCESS_TRAP;
res = CP_ACCESS_UNDEFINED;
goto fail;
}
@ -780,7 +780,7 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key,
* the other trap takes priority. So we take the "check HSTR_EL2" path
* for all of those cases.)
*/
if (res != CP_ACCESS_OK && ((res & CP_ACCESS_EL_MASK) == 0) &&
if (res != CP_ACCESS_OK && ((res & CP_ACCESS_EL_MASK) < 2) &&
arm_current_el(env) == 0) {
goto fail;
}
@ -851,12 +851,25 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key,
}
fail:
switch (res & ~CP_ACCESS_EL_MASK) {
case CP_ACCESS_TRAP:
excp = EXCP_UDEF;
switch (res) {
/* CP_ACCESS_TRAP* traps are always direct to a specified EL */
case CP_ACCESS_TRAP_EL3:
/*
* If EL3 is AArch32 then there's no syndrome register; the cases
* where we would raise a SystemAccessTrap to AArch64 EL3 all become
* raising a Monitor trap exception. (Because there's no visible
* syndrome it doesn't matter what we pass to raise_exception().)
*/
if (!arm_el_is_aa64(env, 3)) {
excp = EXCP_MON_TRAP;
}
break;
case CP_ACCESS_TRAP_UNCATEGORIZED:
/* Only CP_ACCESS_TRAP traps are direct to a specified EL */
assert((res & CP_ACCESS_EL_MASK) == 0);
case CP_ACCESS_TRAP_EL2:
case CP_ACCESS_TRAP_EL1:
break;
case CP_ACCESS_UNDEFINED:
/* CP_ACCESS_UNDEFINED is never direct to a specified EL */
if (cpu_isar_feature(aa64_ids, cpu) && isread &&
arm_cpreg_in_idspace(ri)) {
/*
@ -876,6 +889,9 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key,
case 0:
target_el = exception_target_el(env);
break;
case 1:
assert(arm_current_el(env) < 2);
break;
case 2:
assert(arm_current_el(env) != 3);
assert(arm_is_el2_enabled(env));
@ -884,11 +900,10 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key,
assert(arm_feature(env, ARM_FEATURE_EL3));
break;
default:
/* No "direct" traps to EL1 */
g_assert_not_reached();
}
raise_exception(env, EXCP_UDEF, syndrome, target_el);
raise_exception(env, excp, syndrome, target_el);
}
const void *HELPER(lookup_cp_reg)(CPUARMState *env, uint32_t key)

View File

@ -3510,7 +3510,7 @@ static int t32_expandimm_rot(DisasContext *s, int x)
/* Return the unrotated immediate from T32ExpandImm. */
static int t32_expandimm_imm(DisasContext *s, int x)
{
int imm = extract32(x, 0, 8);
uint32_t imm = extract32(x, 0, 8);
switch (extract32(x, 8, 4)) {
case 0: /* XY */