target-arm queue:

* loader: Fix incorrect parameter name in load_image_mr()
  * Implement MRS (banked) and MSR (banked) instructions
  * virt: Implement versioning for machine model
  * i.MX: some initial patches preparing for i.MX6 support
  * new ASPEED AST2400 SoC and palmetto-bmc machine
  * bcm2835: add some more raspi2 devices
  * sd: fix segfault running "info qtree"
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJW6ZsTAAoJEDwlJe0UNgze8HAQAIqusnaYDgrt6gdmfprVbmIT
 LTWyJvaYYNK9kfIkDcMzF036WGr2vjU2TjP1TeLg/ymMFBlL8J/PwWxeeaMuIpPe
 ywinE0vlm6KvQepyDrS2KIctuWkGGEW8GQZTJW1XSVgYdxrKWbT4Ze47jVHlaqC7
 uF3roMV4dW7CsYkbdmR0UGuIB2uGfH2fiiDYU4iLKy8EfulqcnxVjUHKJaz3Rzzu
 yGrBi5WgV9jUh1gRppvck933rxc5DVwQZeaplO/kxx9J6YkBQArU5Zn1TFTL+5Qm
 VHEYndmtmE0125rmFUAPN6zreNjlFl0j8m+dpaf4uyjxKUuDqalNdxv8sKoczHCc
 kLkx5tM7TEcgABKhp9qX2/BZxLQX22aiL5ujeqoYejJ9KMDCioL58vsJJJKESTGl
 NIjOTErMTP+Yp0fUXZLrbMjkbjszdDN7GLshOvvxr/iWHLvQolpxI9wylPaqqrXP
 A1i8q2J/VuFqFatjSqB9/D9hA0Fx1ThCfWS2d+wJ4+hNsIRhZiAtg2l2YVJurJ5/
 MAzMnYZERQdwINBev8XWNF1j7yUE88Q9qP5ZBU3u3AvGX2R+vTGXPifU5sJaYypL
 SJRoGPCFNjo73VYZcMayIZsNt90mWFacBcEM/qrSu2dN0KrRqlX0OxcbckfiGQOK
 sxDw0qH9jJizs5gKXqsz
 =uS/P
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20160316-1' into staging

target-arm queue:
 * loader: Fix incorrect parameter name in load_image_mr()
 * Implement MRS (banked) and MSR (banked) instructions
 * virt: Implement versioning for machine model
 * i.MX: some initial patches preparing for i.MX6 support
 * new ASPEED AST2400 SoC and palmetto-bmc machine
 * bcm2835: add some more raspi2 devices
 * sd: fix segfault running "info qtree"

# gpg: Signature made Wed 16 Mar 2016 17:42:43 GMT using RSA key ID 14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>"
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"

* remotes/pmaydell/tags/pull-target-arm-20160316-1: (21 commits)
  sd: Fix "info qtree" on boards with SD cards
  bcm2835_dma: add emulation of Raspberry Pi DMA controller
  bcm2835_property: implement framebuffer control/configuration properties
  bcm2835_fb: add framebuffer device for Raspberry Pi
  bcm2835_aux: add emulation of BCM2835 AUX (aka UART1) block
  bcm2835_peripherals: enable sdhci pending-insert quirk for raspberry pi
  hw/arm: Add palmetto-bmc machine
  hw/arm: Add ASPEED AST2400 SoC model
  hw/intc: Add (new) ASPEED VIC device model
  hw/timer: Add ASPEED timer device model
  i.MX: Add missing descriptions in devices.
  i.MX: Add i.MX6 CCM and ANALOG device.
  i.MX: Add the CLK_IPG_HIGH clock
  i.MX: Remove CCM useless clock computation handling.
  i.MX: Rename CCM NOCLK to CLK_NONE for naming consistency.
  i.MX: Allow GPT timer to rollover.
  arm: virt: Move machine class init code to the abstract machine type
  arm: virt: Add an abstract ARM virt machine type
  target-arm: Fix translation level on early translation faults
  target-arm: Implement MRS (banked) and MSR (banked) instructions
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-03-16 17:43:37 +00:00
commit d1f8764099
46 changed files with 4114 additions and 140 deletions

View file

@ -110,3 +110,4 @@ CONFIG_IOH3420=y
CONFIG_I82801B11=y
CONFIG_ACPI=y
CONFIG_SMBIOS=y
CONFIG_ASPEED_SOC=y

View file

@ -16,3 +16,4 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o

137
hw/arm/ast2400.c Normal file
View file

@ -0,0 +1,137 @@
/*
* AST2400 SoC
*
* Andrew Jeffery <andrew@aj.id.au>
* Jeremy Kerr <jk@ozlabs.org>
*
* Copyright 2016 IBM Corp.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "exec/address-spaces.h"
#include "hw/arm/ast2400.h"
#include "hw/char/serial.h"
#define AST2400_UART_5_BASE 0x00184000
#define AST2400_IOMEM_SIZE 0x00200000
#define AST2400_IOMEM_BASE 0x1E600000
#define AST2400_VIC_BASE 0x1E6C0000
#define AST2400_TIMER_BASE 0x1E782000
static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
/*
* IO handlers: simply catch any reads/writes to IO addresses that aren't
* handled by a device mapping.
*/
static uint64_t ast2400_io_read(void *p, hwaddr offset, unsigned size)
{
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n",
__func__, offset, size);
return 0;
}
static void ast2400_io_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n",
__func__, offset, value, size);
}
static const MemoryRegionOps ast2400_io_ops = {
.read = ast2400_io_read,
.write = ast2400_io_write,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void ast2400_init(Object *obj)
{
AST2400State *s = AST2400(obj);
s->cpu = cpu_arm_init("arm926");
object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC);
object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL);
qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default());
object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER);
object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL);
qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default());
}
static void ast2400_realize(DeviceState *dev, Error **errp)
{
int i;
AST2400State *s = AST2400(dev);
Error *err = NULL;
/* IO space */
memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL,
"ast2400.io", AST2400_IOMEM_SIZE);
memory_region_add_subregion_overlap(get_system_memory(), AST2400_IOMEM_BASE,
&s->iomem, -1);
/* VIC */
object_property_set_bool(OBJECT(&s->vic), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, AST2400_VIC_BASE);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0,
qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1,
qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ));
/* Timer */
object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, AST2400_TIMER_BASE);
for (i = 0; i < ARRAY_SIZE(timer_irqs); i++) {
qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->vic), timer_irqs[i]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
}
/* UART - attach an 8250 to the IO space as our UART5 */
if (serial_hds[0]) {
qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]);
serial_mm_init(&s->iomem, AST2400_UART_5_BASE, 2,
uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN);
}
}
static void ast2400_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = ast2400_realize;
/*
* Reason: creates an ARM CPU, thus use after free(), see
* arm_cpu_class_init()
*/
dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo ast2400_type_info = {
.name = TYPE_AST2400,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AST2400State),
.instance_init = ast2400_init,
.class_init = ast2400_class_init,
};
static void ast2400_register_types(void)
{
type_register_static(&ast2400_type_info);
}
type_init(ast2400_register_types)

View file

@ -12,6 +12,7 @@
#include "hw/arm/bcm2835_peripherals.h"
#include "hw/misc/bcm2835_mbox_defs.h"
#include "hw/arm/raspi_platform.h"
#include "sysemu/char.h"
/* Peripheral base address on the VC (GPU) system bus */
#define BCM2835_VC_PERI_BASE 0x7e000000
@ -48,6 +49,11 @@ static void bcm2835_peripherals_init(Object *obj)
object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL);
qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default());
/* AUX / UART1 */
object_initialize(&s->aux, sizeof(s->aux), TYPE_BCM2835_AUX);
object_property_add_child(obj, "aux", OBJECT(&s->aux), NULL);
qdev_set_parent_bus(DEVICE(&s->aux), sysbus_get_default());
/* Mailboxes */
object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX);
object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL);
@ -56,6 +62,16 @@ static void bcm2835_peripherals_init(Object *obj)
object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr",
OBJECT(&s->mbox_mr), &error_abort);
/* Framebuffer */
object_initialize(&s->fb, sizeof(s->fb), TYPE_BCM2835_FB);
object_property_add_child(obj, "fb", OBJECT(&s->fb), NULL);
object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size",
&error_abort);
qdev_set_parent_bus(DEVICE(&s->fb), sysbus_get_default());
object_property_add_const_link(OBJECT(&s->fb), "dma-mr",
OBJECT(&s->gpu_bus_mr), &error_abort);
/* Property channel */
object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY);
object_property_add_child(obj, "property", OBJECT(&s->property), NULL);
@ -63,6 +79,8 @@ static void bcm2835_peripherals_init(Object *obj)
"board-rev", &error_abort);
qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default());
object_property_add_const_link(OBJECT(&s->property), "fb",
OBJECT(&s->fb), &error_abort);
object_property_add_const_link(OBJECT(&s->property), "dma-mr",
OBJECT(&s->gpu_bus_mr), &error_abort);
@ -70,6 +88,14 @@ static void bcm2835_peripherals_init(Object *obj)
object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default());
/* DMA Channels */
object_initialize(&s->dma, sizeof(s->dma), TYPE_BCM2835_DMA);
object_property_add_child(obj, "dma", OBJECT(&s->dma), NULL);
qdev_set_parent_bus(DEVICE(&s->dma), sysbus_get_default());
object_property_add_const_link(OBJECT(&s->dma), "dma-mr",
OBJECT(&s->gpu_bus_mr), &error_abort);
}
static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
@ -78,7 +104,8 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
Object *obj;
MemoryRegion *ram;
Error *err = NULL;
uint32_t ram_size;
uint32_t ram_size, vcram_size;
CharDriverState *chr;
int n;
obj = object_property_get_link(OBJECT(dev), "ram", &err);
@ -131,6 +158,29 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
INTERRUPT_UART));
/* AUX / UART1 */
/* TODO: don't call qemu_char_get_next_serial() here, instead set
* chardev properties for each uart at the board level, once pl011
* (uart0) has been updated to avoid qemu_char_get_next_serial()
*/
chr = qemu_char_get_next_serial();
if (chr == NULL) {
chr = qemu_chr_new("bcm2835.uart1", "null", NULL);
}
qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr);
object_property_set_bool(OBJECT(&s->aux), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->peri_mr, UART1_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0,
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
INTERRUPT_AUX));
/* Mailboxes */
object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err);
if (err) {
@ -144,13 +194,33 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
INTERRUPT_ARM_MAILBOX));
/* Property channel */
object_property_set_int(OBJECT(&s->property), ram_size, "ram-size", &err);
/* Framebuffer */
vcram_size = (uint32_t)object_property_get_int(OBJECT(s), "vcram-size",
&err);
if (err) {
error_propagate(errp, err);
return;
}
object_property_set_int(OBJECT(&s->fb), ram_size - vcram_size,
"vcram-base", &err);
if (err) {
error_propagate(errp, err);
return;
}
object_property_set_bool(OBJECT(&s->fb), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->mbox_mr, MBOX_CHAN_FB << MBOX_AS_CHAN_SHIFT,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->fb), 0));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0,
qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB));
/* Property channel */
object_property_set_bool(OBJECT(&s->property), true, "realized", &err);
if (err) {
error_propagate(errp, err);
@ -171,6 +241,13 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
return;
}
object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk",
&err);
if (err) {
error_propagate(errp, err);
return;
}
object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err);
if (err) {
error_propagate(errp, err);
@ -189,6 +266,24 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
return;
}
/* DMA Channels */
object_property_set_bool(OBJECT(&s->dma), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->peri_mr, DMA_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 0));
memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1));
for (n = 0; n <= 12; n++) {
sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n,
qdev_get_gpio_in_named(DEVICE(&s->ic),
BCM2835_IC_GPU_IRQ,
INTERRUPT_DMA0 + n));
}
}
static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
@ -196,6 +291,8 @@ static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = bcm2835_peripherals_realize;
/* Reason: realize() method uses qemu_char_get_next_serial() */
dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo bcm2835_peripherals_type_info = {

View file

@ -42,6 +42,8 @@ static void bcm2836_init(Object *obj)
&error_abort);
object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals),
"board-rev", &error_abort);
object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals),
"vcram-size", &error_abort);
qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default());
}

View file

@ -291,6 +291,7 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data)
* arm_cpu_class_init()
*/
dc->cannot_destroy_with_object_finalize_yet = true;
dc->desc = "i.MX25 SOC";
}
static const TypeInfo fsl_imx25_type_info = {

View file

@ -265,6 +265,7 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data)
* arm_cpu_class_init()
*/
dc->cannot_destroy_with_object_finalize_yet = true;
dc->desc = "i.MX31 SOC";
}
static const TypeInfo fsl_imx31_type_info = {

65
hw/arm/palmetto-bmc.c Normal file
View file

@ -0,0 +1,65 @@
/*
* OpenPOWER Palmetto BMC
*
* Andrew Jeffery <andrew@aj.id.au>
*
* Copyright 2016 IBM Corp.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "exec/address-spaces.h"
#include "hw/arm/arm.h"
#include "hw/arm/ast2400.h"
#include "hw/boards.h"
static struct arm_boot_info palmetto_bmc_binfo = {
.loader_start = AST2400_SDRAM_BASE,
.board_id = 0,
.nb_cpus = 1,
};
typedef struct PalmettoBMCState {
AST2400State soc;
MemoryRegion ram;
} PalmettoBMCState;
static void palmetto_bmc_init(MachineState *machine)
{
PalmettoBMCState *bmc;
bmc = g_new0(PalmettoBMCState, 1);
object_initialize(&bmc->soc, (sizeof(bmc->soc)), TYPE_AST2400);
object_property_add_child(OBJECT(machine), "soc", OBJECT(&bmc->soc),
&error_abort);
memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size);
memory_region_add_subregion(get_system_memory(), AST2400_SDRAM_BASE,
&bmc->ram);
object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram),
&error_abort);
object_property_set_bool(OBJECT(&bmc->soc), true, "realized",
&error_abort);
palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
palmetto_bmc_binfo.ram_size = ram_size;
arm_load_kernel(ARM_CPU(first_cpu), &palmetto_bmc_binfo);
}
static void palmetto_bmc_machine_init(MachineClass *mc)
{
mc->desc = "OpenPOWER Palmetto BMC";
mc->init = palmetto_bmc_init;
mc->max_cpus = 1;
mc->no_sdcard = 1;
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->no_sdcard = 1;
mc->no_parallel = 1;
}
DEFINE_MACHINE("palmetto-bmc", palmetto_bmc_machine_init);

View file

@ -113,6 +113,7 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size)
static void raspi2_init(MachineState *machine)
{
RasPiState *s = g_new0(RasPiState, 1);
uint32_t vcram_size;
DriveInfo *di;
BlockBackend *blk;
BusState *bus;
@ -149,7 +150,9 @@ static void raspi2_init(MachineState *machine)
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
setup_boot(machine, 2, machine->ram_size);
vcram_size = object_property_get_int(OBJECT(&s->soc), "vcram-size",
&error_abort);
setup_boot(machine, 2, machine->ram_size - vcram_size);
}
static void raspi2_machine_init(MachineClass *mc)
@ -161,11 +164,6 @@ static void raspi2_machine_init(MachineClass *mc)
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->max_cpus = BCM2836_NCPUS;
/* XXX: Temporary restriction in RAM size from the full 1GB. Since
* we do not yet support the framebuffer / GPU, we need to limit
* RAM usable by the OS to sit below the peripherals.
*/
mc->default_ram_size = 0x3F000000; /* BCM2836_PERI_BASE */
mc->default_ram_size = 1024 * 1024 * 1024;
};
DEFINE_MACHINE("raspi2", raspi2_machine_init)

View file

@ -1345,7 +1345,32 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
}
}
static void virt_instance_init(Object *obj)
static void virt_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->init = machvirt_init;
/* Start max_cpus at the maximum QEMU supports. We'll further restrict
* it later in machvirt_init, where we have more information about the
* configuration of the particular instance.
*/
mc->max_cpus = MAX_CPUMASK_BITS;
mc->has_dynamic_sysbus = true;
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
mc->pci_allow_0_address = true;
}
static const TypeInfo virt_machine_info = {
.name = TYPE_VIRT_MACHINE,
.parent = TYPE_MACHINE,
.abstract = true,
.instance_size = sizeof(VirtMachineState),
.class_size = sizeof(VirtMachineClass),
.class_init = virt_machine_class_init,
};
static void virt_2_6_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
@ -1378,34 +1403,28 @@ static void virt_instance_init(Object *obj)
"Valid values are 2, 3 and host", NULL);
}
static void virt_class_init(ObjectClass *oc, void *data)
static void virt_2_6_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
static GlobalProperty compat_props[] = {
{ /* end of list */ }
};
mc->desc = "ARM Virtual Machine",
mc->init = machvirt_init;
/* Start max_cpus at the maximum QEMU supports. We'll further restrict
* it later in machvirt_init, where we have more information about the
* configuration of the particular instance.
*/
mc->max_cpus = MAX_CPUMASK_BITS;
mc->has_dynamic_sysbus = true;
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
mc->pci_allow_0_address = true;
mc->desc = "QEMU 2.6 ARM Virtual Machine";
mc->alias = "virt";
mc->compat_props = compat_props;
}
static const TypeInfo machvirt_info = {
.name = TYPE_VIRT_MACHINE,
.parent = TYPE_MACHINE,
.instance_size = sizeof(VirtMachineState),
.instance_init = virt_instance_init,
.class_size = sizeof(VirtMachineClass),
.class_init = virt_class_init,
.name = MACHINE_TYPE_NAME("virt-2.6"),
.parent = TYPE_VIRT_MACHINE,
.instance_init = virt_2_6_instance_init,
.class_init = virt_2_6_class_init,
};
static void machvirt_machine_init(void)
{
type_register_static(&virt_machine_info);
type_register_static(&machvirt_info);
}

View file

@ -16,6 +16,7 @@ obj-$(CONFIG_SH4) += sh_serial.o
obj-$(CONFIG_PSERIES) += spapr_vty.o
obj-$(CONFIG_DIGIC) += digic-uart.o
obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
obj-$(CONFIG_RASPI) += bcm2835_aux.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o

316
hw/char/bcm2835_aux.c Normal file
View file

@ -0,0 +1,316 @@
/*
* BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
* Copyright (c) 2015, Microsoft
* Written by Andrew Baumann
* Based on pl011.c, copyright terms below:
*
* Arm PrimeCell PL011 UART
*
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the GPL.
*
* At present only the core UART functions (data path for tx/rx) are
* implemented. The following features/registers are unimplemented:
* - Line/modem control
* - Scratch register
* - Extra control
* - Baudrate
* - SPI interfaces
*/
#include "qemu/osdep.h"
#include "hw/char/bcm2835_aux.h"
#define AUX_IRQ 0x0
#define AUX_ENABLES 0x4
#define AUX_MU_IO_REG 0x40
#define AUX_MU_IER_REG 0x44
#define AUX_MU_IIR_REG 0x48
#define AUX_MU_LCR_REG 0x4c
#define AUX_MU_MCR_REG 0x50
#define AUX_MU_LSR_REG 0x54
#define AUX_MU_MSR_REG 0x58
#define AUX_MU_SCRATCH 0x5c
#define AUX_MU_CNTL_REG 0x60
#define AUX_MU_STAT_REG 0x64
#define AUX_MU_BAUD_REG 0x68
/* bits in IER/IIR registers */
#define TX_INT 0x1
#define RX_INT 0x2
static void bcm2835_aux_update(BCM2835AuxState *s)
{
/* signal an interrupt if either:
* 1. rx interrupt is enabled and we have a non-empty rx fifo, or
* 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
*/
s->iir = 0;
if ((s->ier & RX_INT) && s->read_count != 0) {
s->iir |= RX_INT;
}
if (s->ier & TX_INT) {
s->iir |= TX_INT;
}
qemu_set_irq(s->irq, s->iir != 0);
}
static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
{
BCM2835AuxState *s = opaque;
uint32_t c, res;
switch (offset) {
case AUX_IRQ:
return s->iir != 0;
case AUX_ENABLES:
return 1; /* mini UART permanently enabled */
case AUX_MU_IO_REG:
/* "DLAB bit set means access baudrate register" is NYI */
c = s->read_fifo[s->read_pos];
if (s->read_count > 0) {
s->read_count--;
if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
s->read_pos = 0;
}
}
if (s->chr) {
qemu_chr_accept_input(s->chr);
}
bcm2835_aux_update(s);
return c;
case AUX_MU_IER_REG:
/* "DLAB bit set means access baudrate register" is NYI */
return 0xc0 | s->ier; /* FIFO enables always read 1 */
case AUX_MU_IIR_REG:
res = 0xc0; /* FIFO enables */
/* The spec is unclear on what happens when both tx and rx
* interrupts are active, besides that this cannot occur. At
* present, we choose to prioritise the rx interrupt, since
* the tx fifo is always empty. */
if (s->read_count != 0) {
res |= 0x4;
} else {
res |= 0x2;
}
if (s->iir == 0) {
res |= 0x1;
}
return res;
case AUX_MU_LCR_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
return 0;
case AUX_MU_MCR_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
return 0;
case AUX_MU_LSR_REG:
res = 0x60; /* tx idle, empty */
if (s->read_count != 0) {
res |= 0x1;
}
return res;
case AUX_MU_MSR_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
return 0;
case AUX_MU_SCRATCH:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
return 0;
case AUX_MU_CNTL_REG:
return 0x3; /* tx, rx enabled */
case AUX_MU_STAT_REG:
res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
if (s->read_count > 0) {
res |= 0x1; /* data in input buffer */
assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
}
return res;
case AUX_MU_BAUD_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
return 0;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
return 0;
}
}
static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
BCM2835AuxState *s = opaque;
unsigned char ch;
switch (offset) {
case AUX_ENABLES:
if (value != 1) {
qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI "
"or disable UART\n", __func__);
}
break;
case AUX_MU_IO_REG:
/* "DLAB bit set means access baudrate register" is NYI */
ch = value;
if (s->chr) {
qemu_chr_fe_write(s->chr, &ch, 1);
}
break;
case AUX_MU_IER_REG:
/* "DLAB bit set means access baudrate register" is NYI */
s->ier = value & (TX_INT | RX_INT);
bcm2835_aux_update(s);
break;
case AUX_MU_IIR_REG:
if (value & 0x2) {
s->read_count = 0;
}
break;
case AUX_MU_LCR_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
break;
case AUX_MU_MCR_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
break;
case AUX_MU_SCRATCH:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
break;
case AUX_MU_CNTL_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
break;
case AUX_MU_BAUD_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
}
bcm2835_aux_update(s);
}
static int bcm2835_aux_can_receive(void *opaque)
{
BCM2835AuxState *s = opaque;
return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
}
static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
{
BCM2835AuxState *s = opaque;
int slot;
slot = s->read_pos + s->read_count;
if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
slot -= BCM2835_AUX_RX_FIFO_LEN;
}
s->read_fifo[slot] = value;
s->read_count++;
if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
/* buffer full */
}
bcm2835_aux_update(s);
}
static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
{
bcm2835_aux_put_fifo(opaque, *buf);
}
static const MemoryRegionOps bcm2835_aux_ops = {
.read = bcm2835_aux_read,
.write = bcm2835_aux_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static const VMStateDescription vmstate_bcm2835_aux = {
.name = TYPE_BCM2835_AUX,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState,
BCM2835_AUX_RX_FIFO_LEN),
VMSTATE_UINT8(read_pos, BCM2835AuxState),
VMSTATE_UINT8(read_count, BCM2835AuxState),
VMSTATE_UINT8(ier, BCM2835AuxState),
VMSTATE_UINT8(iir, BCM2835AuxState),
VMSTATE_END_OF_LIST()
}
};
static void bcm2835_aux_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
BCM2835AuxState *s = BCM2835_AUX(obj);
memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
TYPE_BCM2835_AUX, 0x100);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
}
static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
{
BCM2835AuxState *s = BCM2835_AUX(dev);
if (s->chr) {
qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive,
bcm2835_aux_receive, NULL, s);
}
}
static Property bcm2835_aux_props[] = {
DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
DEFINE_PROP_END_OF_LIST(),
};
static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = bcm2835_aux_realize;
dc->vmsd = &vmstate_bcm2835_aux;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
dc->props = bcm2835_aux_props;
}
static const TypeInfo bcm2835_aux_info = {
.name = TYPE_BCM2835_AUX,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(BCM2835AuxState),
.instance_init = bcm2835_aux_init,
.class_init = bcm2835_aux_class_init,
};
static void bcm2835_aux_register_types(void)
{
type_register_static(&bcm2835_aux_info);
}
type_init(bcm2835_aux_register_types)

View file

@ -27,6 +27,7 @@ endif
obj-$(CONFIG_OMAP) += omap_dss.o
obj-$(CONFIG_OMAP) += omap_lcdc.o
obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o
obj-$(CONFIG_RASPI) += bcm2835_fb.o
obj-$(CONFIG_SM501) += sm501.o
obj-$(CONFIG_TCX) += tcx.o
obj-$(CONFIG_CG3) += cg3.o

424
hw/display/bcm2835_fb.c Normal file
View file

@ -0,0 +1,424 @@
/*
* Raspberry Pi emulation (c) 2012 Gregory Estrade
* Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann.
* This code is licensed under the GNU GPLv2 and later.
*
* Heavily based on milkymist-vgafb.c, copyright terms below:
* QEMU model of the Milkymist VGA framebuffer.
*
* Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "qemu/osdep.h"
#include "hw/display/bcm2835_fb.h"
#include "hw/display/framebuffer.h"
#include "ui/pixel_ops.h"
#include "hw/misc/bcm2835_mbox_defs.h"
#define DEFAULT_VCRAM_SIZE 0x4000000
#define BCM2835_FB_OFFSET 0x00100000
static void fb_invalidate_display(void *opaque)
{
BCM2835FBState *s = BCM2835_FB(opaque);
s->invalidate = true;
}
static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
int width, int deststep)
{
BCM2835FBState *s = opaque;
uint16_t rgb565;
uint32_t rgb888;
uint8_t r, g, b;
DisplaySurface *surface = qemu_console_surface(s->con);
int bpp = surface_bits_per_pixel(surface);
while (width--) {
switch (s->bpp) {
case 8:
/* lookup palette starting at video ram base
* TODO: cache translation, rather than doing this each time!
*/
rgb888 = ldl_le_phys(&s->dma_as, s->vcram_base + (*src << 2));
r = (rgb888 >> 0) & 0xff;
g = (rgb888 >> 8) & 0xff;
b = (rgb888 >> 16) & 0xff;
src++;
break;
case 16:
rgb565 = lduw_le_p(src);
r = ((rgb565 >> 11) & 0x1f) << 3;
g = ((rgb565 >> 5) & 0x3f) << 2;
b = ((rgb565 >> 0) & 0x1f) << 3;
src += 2;
break;
case 24:
rgb888 = ldl_le_p(src);
r = (rgb888 >> 0) & 0xff;
g = (rgb888 >> 8) & 0xff;
b = (rgb888 >> 16) & 0xff;
src += 3;
break;
case 32:
rgb888 = ldl_le_p(src);
r = (rgb888 >> 0) & 0xff;
g = (rgb888 >> 8) & 0xff;
b = (rgb888 >> 16) & 0xff;
src += 4;
break;
default:
r = 0;
g = 0;
b = 0;
break;
}
if (s->pixo == 0) {
/* swap to BGR pixel format */
uint8_t tmp = r;
r = b;
b = tmp;
}
switch (bpp) {
case 8:
*dst++ = rgb_to_pixel8(r, g, b);
break;
case 15:
*(uint16_t *)dst = rgb_to_pixel15(r, g, b);
dst += 2;
break;
case 16:
*(uint16_t *)dst = rgb_to_pixel16(r, g, b);
dst += 2;
break;
case 24:
rgb888 = rgb_to_pixel24(r, g, b);
*dst++ = rgb888 & 0xff;
*dst++ = (rgb888 >> 8) & 0xff;
*dst++ = (rgb888 >> 16) & 0xff;
break;
case 32:
*(uint32_t *)dst = rgb_to_pixel32(r, g, b);
dst += 4;
break;
default:
return;
}
}
}
static void fb_update_display(void *opaque)
{
BCM2835FBState *s = opaque;
DisplaySurface *surface = qemu_console_surface(s->con);
int first = 0;
int last = 0;
int src_width = 0;
int dest_width = 0;
if (s->lock || !s->xres) {
return;
}
src_width = s->xres * (s->bpp >> 3);
dest_width = s->xres;
switch (surface_bits_per_pixel(surface)) {
case 0:
return;
case 8:
break;
case 15:
dest_width *= 2;
break;
case 16:
dest_width *= 2;
break;
case 24:
dest_width *= 3;
break;
case 32:
dest_width *= 4;
break;
default:
hw_error("bcm2835_fb: bad color depth\n");
break;
}
if (s->invalidate) {
framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base,
s->yres, src_width);
}
framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
src_width, dest_width, 0, s->invalidate,
draw_line_src16, s, &first, &last);
if (first >= 0) {
dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1);
}
s->invalidate = false;
}
static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
{
value &= ~0xf;
s->lock = true;
s->xres = ldl_le_phys(&s->dma_as, value);
s->yres = ldl_le_phys(&s->dma_as, value + 4);
s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8);
s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12);
s->bpp = ldl_le_phys(&s->dma_as, value + 20);
s->xoffset = ldl_le_phys(&s->dma_as, value + 24);
s->yoffset = ldl_le_phys(&s->dma_as, value + 28);
s->base = s->vcram_base | (value & 0xc0000000);
s->base += BCM2835_FB_OFFSET;
/* TODO - Manage properly virtual resolution */
s->pitch = s->xres * (s->bpp >> 3);
s->size = s->yres * s->pitch;
stl_le_phys(&s->dma_as, value + 16, s->pitch);
stl_le_phys(&s->dma_as, value + 32, s->base);
stl_le_phys(&s->dma_as, value + 36, s->size);
s->invalidate = true;
qemu_console_resize(s->con, s->xres, s->yres);
s->lock = false;
}
void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres,
uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp,
uint32_t *pixo, uint32_t *alpha)
{
s->lock = true;
/* TODO: input validation! */
if (xres) {
s->xres = *xres;
}
if (yres) {
s->yres = *yres;
}
if (xoffset) {
s->xoffset = *xoffset;
}
if (yoffset) {
s->yoffset = *yoffset;
}
if (bpp) {
s->bpp = *bpp;
}
if (pixo) {
s->pixo = *pixo;
}
if (alpha) {
s->alpha = *alpha;
}
/* TODO - Manage properly virtual resolution */
s->pitch = s->xres * (s->bpp >> 3);
s->size = s->yres * s->pitch;
s->invalidate = true;
qemu_console_resize(s->con, s->xres, s->yres);
s->lock = false;
}
static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size)
{
BCM2835FBState *s = opaque;
uint32_t res = 0;
switch (offset) {
case MBOX_AS_DATA:
res = MBOX_CHAN_FB;
s->pending = false;
qemu_set_irq(s->mbox_irq, 0);
break;
case MBOX_AS_PENDING:
res = s->pending;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
return 0;
}
return res;
}
static void bcm2835_fb_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
BCM2835FBState *s = opaque;
switch (offset) {
case MBOX_AS_DATA:
/* bcm2835_mbox should check our pending status before pushing */
assert(!s->pending);
s->pending = true;
bcm2835_fb_mbox_push(s, value);
qemu_set_irq(s->mbox_irq, 1);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
return;
}
}
static const MemoryRegionOps bcm2835_fb_ops = {
.read = bcm2835_fb_read,
.write = bcm2835_fb_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static const VMStateDescription vmstate_bcm2835_fb = {
.name = TYPE_BCM2835_FB,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_BOOL(lock, BCM2835FBState),
VMSTATE_BOOL(invalidate, BCM2835FBState),
VMSTATE_BOOL(pending, BCM2835FBState),
VMSTATE_UINT32(xres, BCM2835FBState),
VMSTATE_UINT32(yres, BCM2835FBState),
VMSTATE_UINT32(xres_virtual, BCM2835FBState),
VMSTATE_UINT32(yres_virtual, BCM2835FBState),
VMSTATE_UINT32(xoffset, BCM2835FBState),
VMSTATE_UINT32(yoffset, BCM2835FBState),
VMSTATE_UINT32(bpp, BCM2835FBState),
VMSTATE_UINT32(base, BCM2835FBState),
VMSTATE_UINT32(pitch, BCM2835FBState),
VMSTATE_UINT32(size, BCM2835FBState),
VMSTATE_UINT32(pixo, BCM2835FBState),
VMSTATE_UINT32(alpha, BCM2835FBState),
VMSTATE_END_OF_LIST()
}
};
static const GraphicHwOps vgafb_ops = {
.invalidate = fb_invalidate_display,
.gfx_update = fb_update_display,
};
static void bcm2835_fb_init(Object *obj)
{
BCM2835FBState *s = BCM2835_FB(obj);
memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB,
0x10);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
}
static void bcm2835_fb_reset(DeviceState *dev)
{
BCM2835FBState *s = BCM2835_FB(dev);
s->pending = false;
s->xres_virtual = s->xres;
s->yres_virtual = s->yres;
s->xoffset = 0;
s->yoffset = 0;
s->base = s->vcram_base + BCM2835_FB_OFFSET;
s->pitch = s->xres * (s->bpp >> 3);
s->size = s->yres * s->pitch;
s->invalidate = true;
s->lock = false;
}
static void bcm2835_fb_realize(DeviceState *dev, Error **errp)
{
BCM2835FBState *s = BCM2835_FB(dev);
Error *err = NULL;
Object *obj;
if (s->vcram_base == 0) {
error_setg(errp, "%s: required vcram-base property not set", __func__);
return;
}
obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
if (obj == NULL) {
error_setg(errp, "%s: required dma-mr link not found: %s",
__func__, error_get_pretty(err));
return;
}
s->dma_mr = MEMORY_REGION(obj);
address_space_init(&s->dma_as, s->dma_mr, NULL);
bcm2835_fb_reset(dev);
s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
qemu_console_resize(s->con, s->xres, s->yres);
}
static Property bcm2835_fb_props[] = {
DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/
DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size,
DEFAULT_VCRAM_SIZE),
DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640),
DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480),
DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16),
DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */
DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */
DEFINE_PROP_END_OF_LIST()
};
static void bcm2835_fb_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->props = bcm2835_fb_props;
dc->realize = bcm2835_fb_realize;
dc->reset = bcm2835_fb_reset;
dc->vmsd = &vmstate_bcm2835_fb;
}
static TypeInfo bcm2835_fb_info = {
.name = TYPE_BCM2835_FB,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(BCM2835FBState),
.class_init = bcm2835_fb_class_init,
.instance_init = bcm2835_fb_init,
};
static void bcm2835_fb_register_types(void)
{
type_register_static(&bcm2835_fb_info);
}
type_init(bcm2835_fb_register_types)

View file

@ -11,3 +11,4 @@ common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
obj-$(CONFIG_RASPI) += bcm2835_dma.o

408
hw/dma/bcm2835_dma.c Normal file
View file

@ -0,0 +1,408 @@
/*
* Raspberry Pi emulation (c) 2012 Gregory Estrade
* This code is licensed under the GNU GPLv2 and later.
*/
#include "qemu/osdep.h"
#include "hw/dma/bcm2835_dma.h"
/* DMA CS Control and Status bits */
#define BCM2708_DMA_ACTIVE (1 << 0)
#define BCM2708_DMA_END (1 << 1) /* GE */
#define BCM2708_DMA_INT (1 << 2)
#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */
#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */
#define BCM2708_DMA_ERR (1 << 8)
#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */
#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */
/* DMA control block "info" field bits */
#define BCM2708_DMA_INT_EN (1 << 0)
#define BCM2708_DMA_TDMODE (1 << 1)
#define BCM2708_DMA_WAIT_RESP (1 << 3)
#define BCM2708_DMA_D_INC (1 << 4)
#define BCM2708_DMA_D_WIDTH (1 << 5)
#define BCM2708_DMA_D_DREQ (1 << 6)
#define BCM2708_DMA_D_IGNORE (1 << 7)
#define BCM2708_DMA_S_INC (1 << 8)
#define BCM2708_DMA_S_WIDTH (1 << 9)
#define BCM2708_DMA_S_DREQ (1 << 10)
#define BCM2708_DMA_S_IGNORE (1 << 11)
/* Register offsets */
#define BCM2708_DMA_CS 0x00 /* Control and Status */
#define BCM2708_DMA_ADDR 0x04 /* Control block address */
/* the current control block appears in the following registers - read only */
#define BCM2708_DMA_INFO 0x08
#define BCM2708_DMA_SOURCE_AD 0x0c
#define BCM2708_DMA_DEST_AD 0x10
#define BCM2708_DMA_TXFR_LEN 0x14
#define BCM2708_DMA_STRIDE 0x18
#define BCM2708_DMA_NEXTCB 0x1C
#define BCM2708_DMA_DEBUG 0x20
#define BCM2708_DMA_INT_STATUS 0xfe0 /* Interrupt status of each channel */
#define BCM2708_DMA_ENABLE 0xff0 /* Global enable bits for each channel */
#define BCM2708_DMA_CS_RW_MASK 0x30ff0001 /* All RW bits in DMA_CS */
static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c)
{
BCM2835DMAChan *ch = &s->chan[c];
uint32_t data, xlen, ylen;
int16_t dst_stride, src_stride;
if (!(s->enable & (1 << c))) {
return;
}
while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) {
/* CB fetch */
ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad);
ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4);
ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8);
ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12);
ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16);
ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20);
if (ch->ti & BCM2708_DMA_TDMODE) {
/* 2D transfer mode */
ylen = (ch->txfr_len >> 16) & 0x3fff;
xlen = ch->txfr_len & 0xffff;
dst_stride = ch->stride >> 16;
src_stride = ch->stride & 0xffff;
} else {
ylen = 1;
xlen = ch->txfr_len;
dst_stride = 0;
src_stride = 0;
}
while (ylen != 0) {
/* Normal transfer mode */
while (xlen != 0) {
if (ch->ti & BCM2708_DMA_S_IGNORE) {
/* Ignore reads */
data = 0;
} else {
data = ldl_le_phys(&s->dma_as, ch->source_ad);
}
if (ch->ti & BCM2708_DMA_S_INC) {
ch->source_ad += 4;
}
if (ch->ti & BCM2708_DMA_D_IGNORE) {
/* Ignore writes */
} else {
stl_le_phys(&s->dma_as, ch->dest_ad, data);
}
if (ch->ti & BCM2708_DMA_D_INC) {
ch->dest_ad += 4;
}
/* update remaining transfer length */
xlen -= 4;
if (ch->ti & BCM2708_DMA_TDMODE) {
ch->txfr_len = (ylen << 16) | xlen;
} else {
ch->txfr_len = xlen;
}
}
if (--ylen != 0) {
ch->source_ad += src_stride;
ch->dest_ad += dst_stride;
}
}
ch->cs |= BCM2708_DMA_END;
if (ch->ti & BCM2708_DMA_INT_EN) {
ch->cs |= BCM2708_DMA_INT;
s->int_status |= (1 << c);
qemu_set_irq(ch->irq, 1);
}
/* Process next CB */
ch->conblk_ad = ch->nextconbk;
}
ch->cs &= ~BCM2708_DMA_ACTIVE;
ch->cs |= BCM2708_DMA_ISPAUSED;
}
static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch)
{
ch->cs = 0;
ch->conblk_ad = 0;
}
static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset,
unsigned size, unsigned c)
{
BCM2835DMAChan *ch;
uint32_t res = 0;
assert(size == 4);
assert(c < BCM2835_DMA_NCHANS);
ch = &s->chan[c];
switch (offset) {
case BCM2708_DMA_CS:
res = ch->cs;
break;
case BCM2708_DMA_ADDR:
res = ch->conblk_ad;
break;
case BCM2708_DMA_INFO:
res = ch->ti;
break;
case BCM2708_DMA_SOURCE_AD:
res = ch->source_ad;
break;
case BCM2708_DMA_DEST_AD:
res = ch->dest_ad;
break;
case BCM2708_DMA_TXFR_LEN:
res = ch->txfr_len;
break;
case BCM2708_DMA_STRIDE:
res = ch->stride;
break;
case BCM2708_DMA_NEXTCB:
res = ch->nextconbk;
break;
case BCM2708_DMA_DEBUG:
res = ch->debug;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
break;
}
return res;
}
static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset,
uint64_t value, unsigned size, unsigned c)
{
BCM2835DMAChan *ch;
uint32_t oldcs;
assert(size == 4);
assert(c < BCM2835_DMA_NCHANS);
ch = &s->chan[c];
switch (offset) {
case BCM2708_DMA_CS:
oldcs = ch->cs;
if (value & BCM2708_DMA_RESET) {
bcm2835_dma_chan_reset(ch);
}
if (value & BCM2708_DMA_ABORT) {
/* abort is a no-op, since we always run to completion */
}
if (value & BCM2708_DMA_END) {
ch->cs &= ~BCM2708_DMA_END;
}
if (value & BCM2708_DMA_INT) {
ch->cs &= ~BCM2708_DMA_INT;
s->int_status &= ~(1 << c);
qemu_set_irq(ch->irq, 0);
}
ch->cs &= ~BCM2708_DMA_CS_RW_MASK;
ch->cs |= (value & BCM2708_DMA_CS_RW_MASK);
if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) {
bcm2835_dma_update(s, c);
}
break;
case BCM2708_DMA_ADDR:
ch->conblk_ad = value;
break;
case BCM2708_DMA_DEBUG:
ch->debug = value;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
break;
}
}
static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size)
{
BCM2835DMAState *s = opaque;
if (offset < 0xf00) {
return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf);
} else {
switch (offset) {
case BCM2708_DMA_INT_STATUS:
return s->int_status;
case BCM2708_DMA_ENABLE:
return s->enable;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
return 0;
}
}
}
static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size)
{
return bcm2835_dma_read(opaque, (offset & 0xff), size, 15);
}
static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
BCM2835DMAState *s = opaque;
if (offset < 0xf00) {
bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf);
} else {
switch (offset) {
case BCM2708_DMA_INT_STATUS:
break;
case BCM2708_DMA_ENABLE:
s->enable = (value & 0xffff);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
}
}
}
static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15);
}
static const MemoryRegionOps bcm2835_dma0_ops = {
.read = bcm2835_dma0_read,
.write = bcm2835_dma0_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static const MemoryRegionOps bcm2835_dma15_ops = {
.read = bcm2835_dma15_read,
.write = bcm2835_dma15_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static const VMStateDescription vmstate_bcm2835_dma_chan = {
.name = TYPE_BCM2835_DMA "-chan",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(cs, BCM2835DMAChan),
VMSTATE_UINT32(conblk_ad, BCM2835DMAChan),
VMSTATE_UINT32(ti, BCM2835DMAChan),
VMSTATE_UINT32(source_ad, BCM2835DMAChan),
VMSTATE_UINT32(dest_ad, BCM2835DMAChan),
VMSTATE_UINT32(txfr_len, BCM2835DMAChan),
VMSTATE_UINT32(stride, BCM2835DMAChan),
VMSTATE_UINT32(nextconbk, BCM2835DMAChan),
VMSTATE_UINT32(debug, BCM2835DMAChan),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_bcm2835_dma = {
.name = TYPE_BCM2835_DMA,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1,
vmstate_bcm2835_dma_chan, BCM2835DMAChan),
VMSTATE_UINT32(int_status, BCM2835DMAState),
VMSTATE_UINT32(enable, BCM2835DMAState),
VMSTATE_END_OF_LIST()
}
};
static void bcm2835_dma_init(Object *obj)
{
BCM2835DMAState *s = BCM2835_DMA(obj);
int n;
/* DMA channels 0-14 occupy a contiguous block of IO memory, along
* with the global enable and interrupt status bits. Channel 15
* has the same register map, but is mapped at a discontiguous
* address in a separate IO block.
*/
memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s,
TYPE_BCM2835_DMA, 0x1000);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0);
memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s,
TYPE_BCM2835_DMA "-chan15", 0x100);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15);
for (n = 0; n < 16; n++) {
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq);
}
}
static void bcm2835_dma_reset(DeviceState *dev)
{
BCM2835DMAState *s = BCM2835_DMA(dev);
int n;
s->enable = 0xffff;
s->int_status = 0;
for (n = 0; n < BCM2835_DMA_NCHANS; n++) {
bcm2835_dma_chan_reset(&s->chan[n]);
}
}
static void bcm2835_dma_realize(DeviceState *dev, Error **errp)
{
BCM2835DMAState *s = BCM2835_DMA(dev);
Error *err = NULL;
Object *obj;
obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
if (obj == NULL) {
error_setg(errp, "%s: required dma-mr link not found: %s",
__func__, error_get_pretty(err));
return;
}
s->dma_mr = MEMORY_REGION(obj);
address_space_init(&s->dma_as, s->dma_mr, NULL);
bcm2835_dma_reset(dev);
}
static void bcm2835_dma_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = bcm2835_dma_realize;
dc->reset = bcm2835_dma_reset;
dc->vmsd = &vmstate_bcm2835_dma;
}
static TypeInfo bcm2835_dma_info = {
.name = TYPE_BCM2835_DMA,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(BCM2835DMAState),
.class_init = bcm2835_dma_class_init,
.instance_init = bcm2835_dma_init,
};
static void bcm2835_dma_register_types(void)
{
type_register_static(&bcm2835_dma_info);
}
type_init(bcm2835_dma_register_types)

View file

@ -319,6 +319,7 @@ static void imx_i2c_class_init(ObjectClass *klass, void *data)
dc->vmsd = &imx_i2c_vmstate;
dc->reset = imx_i2c_reset;
dc->realize = imx_i2c_realize;
dc->desc = "i.MX I2C Controller";
}
static const TypeInfo imx_i2c_type_info = {

View file

@ -31,3 +31,4 @@ obj-$(CONFIG_XICS_KVM) += xics_kvm.o
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
obj-$(CONFIG_S390_FLIC) += s390_flic.o
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_vic.o

339
hw/intc/aspeed_vic.c Normal file
View file

@ -0,0 +1,339 @@
/*
* ASPEED Interrupt Controller (New)
*
* Andrew Jeffery <andrew@aj.id.au>
*
* Copyright 2015, 2016 IBM Corp.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
/* The hardware exposes two register sets, a legacy set and a 'new' set. The
* model implements the 'new' register set, and logs warnings on accesses to
* the legacy IO space.
*
* The hardware uses 32bit registers to manage 51 IRQs, with low and high
* registers for each conceptual register. The device model's implementation
* uses 64bit data types to store both low and high register values (in the one
* member), but must cope with access offset values in multiples of 4 passed to
* the callbacks. As such the read() and write() implementations process the
* provided offset to understand whether the access is requesting the lower or
* upper 32 bits of the 64bit member.
*
* Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt"
* fields have separate "enable"/"status" and "clear" registers, where set bits
* are written to one or the other to change state (avoiding a
* read-modify-write sequence).
*/
#include "qemu/osdep.h"
#include <inttypes.h>
#include "hw/intc/aspeed_vic.h"
#include "qemu/bitops.h"
#include "trace.h"
#define AVIC_NEW_BASE_OFFSET 0x80
#define AVIC_L_MASK 0xFFFFFFFFU
#define AVIC_H_MASK 0x0007FFFFU
#define AVIC_EVENT_W_MASK (0x78000ULL << 32)
static void aspeed_vic_update(AspeedVICState *s)
{
uint64_t new = (s->raw & s->enable);
uint64_t flags;
flags = new & s->select;
trace_aspeed_vic_update_fiq(!!flags);
qemu_set_irq(s->fiq, !!flags);
flags = new & ~s->select;
trace_aspeed_vic_update_irq(!!flags);
qemu_set_irq(s->irq, !!flags);
}
static void aspeed_vic_set_irq(void *opaque, int irq, int level)
{
uint64_t irq_mask;
bool raise;
AspeedVICState *s = (AspeedVICState *)opaque;
if (irq > ASPEED_VIC_NR_IRQS) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
__func__, irq);
return;
}
trace_aspeed_vic_set_irq(irq, level);
irq_mask = BIT(irq);
if (s->sense & irq_mask) {
/* level-triggered */
if (s->event & irq_mask) {
/* high-sensitive */
raise = level;
} else {
/* low-sensitive */
raise = !level;
}
s->raw = deposit64(s->raw, irq, 1, raise);
} else {
uint64_t old_level = s->level & irq_mask;
/* edge-triggered */
if (s->dual_edge & irq_mask) {
raise = (!!old_level) != (!!level);
} else {
if (s->event & irq_mask) {
/* rising-sensitive */
raise = !old_level && level;
} else {
/* falling-sensitive */
raise = old_level && !level;
}
}
if (raise) {
s->raw = deposit64(s->raw, irq, 1, raise);
}
}
s->level = deposit64(s->level, irq, 1, level);
aspeed_vic_update(s);
}
static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
{
uint64_t val;
const bool high = !!(offset & 0x4);
hwaddr n_offset = (offset & ~0x4);
AspeedVICState *s = (AspeedVICState *)opaque;
if (offset < AVIC_NEW_BASE_OFFSET) {
qemu_log_mask(LOG_UNIMP, "%s: Ignoring read from legacy registers "
"at 0x%" HWADDR_PRIx "[%u]\n", __func__, offset, size);
return 0;
}
n_offset -= AVIC_NEW_BASE_OFFSET;
switch (n_offset) {
case 0x0: /* IRQ Status */
val = s->raw & ~s->select & s->enable;
break;
case 0x08: /* FIQ Status */
val = s->raw & s->select & s->enable;
break;
case 0x10: /* Raw Interrupt Status */
val = s->raw;
break;
case 0x18: /* Interrupt Selection */
val = s->select;
break;
case 0x20: /* Interrupt Enable */
val = s->enable;
break;
case 0x30: /* Software Interrupt */
val = s->trigger;
break;
case 0x40: /* Interrupt Sensitivity */
val = s->sense;
break;
case 0x48: /* Interrupt Both Edge Trigger Control */
val = s->dual_edge;
break;
case 0x50: /* Interrupt Event */
val = s->event;
break;
case 0x60: /* Edge Triggered Interrupt Status */
val = s->raw & ~s->sense;
break;
/* Illegal */
case 0x28: /* Interrupt Enable Clear */
case 0x38: /* Software Interrupt Clear */
case 0x58: /* Edge Triggered Interrupt Clear */
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Read of write-only register with offset 0x%"
HWADDR_PRIx "\n", __func__, offset);
val = 0;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
val = 0;
break;
}
if (high) {
val = extract64(val, 32, 19);
}
trace_aspeed_vic_read(offset, size, val);
return val;
}
static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
unsigned size)
{
const bool high = !!(offset & 0x4);
hwaddr n_offset = (offset & ~0x4);
AspeedVICState *s = (AspeedVICState *)opaque;
if (offset < AVIC_NEW_BASE_OFFSET) {
qemu_log_mask(LOG_UNIMP,
"%s: Ignoring write to legacy registers at 0x%"
HWADDR_PRIx "[%u] <- 0x%" PRIx64 "\n", __func__, offset,
size, data);
return;
}
n_offset -= AVIC_NEW_BASE_OFFSET;
trace_aspeed_vic_write(offset, size, data);
/* Given we have members using separate enable/clear registers, deposit64()
* isn't quite the tool for the job. Instead, relocate the incoming bits to
* the required bit offset based on the provided access address
*/
if (high) {
data &= AVIC_H_MASK;
data <<= 32;
} else {
data &= AVIC_L_MASK;
}
switch (n_offset) {
case 0x18: /* Interrupt Selection */
/* Register has deposit64() semantics - overwrite requested 32 bits */
if (high) {
s->select &= AVIC_L_MASK;
} else {
s->select &= ((uint64_t) AVIC_H_MASK) << 32;
}
s->select |= data;
break;
case 0x20: /* Interrupt Enable */
s->enable |= data;
break;
case 0x28: /* Interrupt Enable Clear */
s->enable &= ~data;
break;
case 0x30: /* Software Interrupt */
qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
"IRQs requested: 0x%016" PRIx64 "\n", __func__, data);
break;
case 0x38: /* Software Interrupt Clear */
qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
"IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data);
break;
case 0x50: /* Interrupt Event */
/* Register has deposit64() semantics - overwrite the top four valid
* IRQ bits, as only the top four IRQs (GPIOs) can change their event
* type */
if (high) {
s->event &= ~AVIC_EVENT_W_MASK;
s->event |= (data & AVIC_EVENT_W_MASK);
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"Ignoring invalid write to interrupt event register");
}
break;
case 0x58: /* Edge Triggered Interrupt Clear */
s->raw &= ~(data & ~s->sense);
break;
case 0x00: /* IRQ Status */
case 0x08: /* FIQ Status */
case 0x10: /* Raw Interrupt Status */
case 0x40: /* Interrupt Sensitivity */
case 0x48: /* Interrupt Both Edge Trigger Control */
case 0x60: /* Edge Triggered Interrupt Status */
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Write of read-only register with offset 0x%"
HWADDR_PRIx "\n", __func__, offset);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
break;
}
aspeed_vic_update(s);
}
static const MemoryRegionOps aspeed_vic_ops = {
.read = aspeed_vic_read,
.write = aspeed_vic_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
.valid.unaligned = false,
};
static void aspeed_vic_reset(DeviceState *dev)
{
AspeedVICState *s = ASPEED_VIC(dev);
s->level = 0;
s->raw = 0;
s->select = 0;
s->enable = 0;
s->trigger = 0;
s->sense = 0x1F07FFF8FFFFULL;
s->dual_edge = 0xF800070000ULL;
s->event = 0x5F07FFF8FFFFULL;
}
#define AVIC_IO_REGION_SIZE 0x20000
static void aspeed_vic_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
AspeedVICState *s = ASPEED_VIC(dev);
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s,
TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS);
sysbus_init_irq(sbd, &s->irq);
sysbus_init_irq(sbd, &s->fiq);
}
static const VMStateDescription vmstate_aspeed_vic = {
.name = "aspeed.new-vic",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT64(level, AspeedVICState),
VMSTATE_UINT64(raw, AspeedVICState),
VMSTATE_UINT64(select, AspeedVICState),
VMSTATE_UINT64(enable, AspeedVICState),
VMSTATE_UINT64(trigger, AspeedVICState),
VMSTATE_UINT64(sense, AspeedVICState),
VMSTATE_UINT64(dual_edge, AspeedVICState),
VMSTATE_UINT64(event, AspeedVICState),
VMSTATE_END_OF_LIST()
}
};
static void aspeed_vic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = aspeed_vic_realize;
dc->reset = aspeed_vic_reset;
dc->desc = "ASPEED Interrupt Controller (New)";
dc->vmsd = &vmstate_aspeed_vic;
}
static const TypeInfo aspeed_vic_info = {
.name = TYPE_ASPEED_VIC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AspeedVICState),
.class_init = aspeed_vic_class_init,
};
static void aspeed_vic_register_types(void)
{
type_register_static(&aspeed_vic_info);
}
type_init(aspeed_vic_register_types);

View file

@ -28,6 +28,7 @@ obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
obj-$(CONFIG_IMX) += imx_ccm.o
obj-$(CONFIG_IMX) += imx31_ccm.o
obj-$(CONFIG_IMX) += imx25_ccm.o
obj-$(CONFIG_IMX) += imx6_ccm.o
obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
obj-$(CONFIG_MAINSTONE) += mst_fpga.o

View file

@ -17,6 +17,11 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
uint32_t tot_len;
size_t resplen;
uint32_t tmp;
int n;
uint32_t offset, length, color;
uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha;
uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL,
*newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL;
value &= ~0xf;
@ -60,7 +65,14 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
/* base */
stl_le_phys(&s->dma_as, value + 12, 0);
/* size */
stl_le_phys(&s->dma_as, value + 16, s->ram_size);
stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base);
resplen = 8;
break;
case 0x00010006: /* Get VC memory */
/* base */
stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base);
/* size */
stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size);
resplen = 8;
break;
case 0x00028001: /* Set power state */
@ -122,6 +134,114 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
resplen = 8;
break;
/* Frame buffer */
case 0x00040001: /* Allocate buffer */
stl_le_phys(&s->dma_as, value + 12, s->fbdev->base);
stl_le_phys(&s->dma_as, value + 16, s->fbdev->size);
resplen = 8;
break;
case 0x00048001: /* Release buffer */
resplen = 0;
break;
case 0x00040002: /* Blank screen */
resplen = 4;
break;
case 0x00040003: /* Get display width/height */
case 0x00040004:
stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres);
stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres);
resplen = 8;
break;
case 0x00044003: /* Test display width/height */
case 0x00044004:
resplen = 8;
break;
case 0x00048003: /* Set display width/height */
case 0x00048004:
xres = ldl_le_phys(&s->dma_as, value + 12);
newxres = &xres;
yres = ldl_le_phys(&s->dma_as, value + 16);
newyres = &yres;
resplen = 8;
break;
case 0x00040005: /* Get depth */
stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp);
resplen = 4;
break;
case 0x00044005: /* Test depth */
resplen = 4;
break;
case 0x00048005: /* Set depth */
bpp = ldl_le_phys(&s->dma_as, value + 12);
newbpp = &bpp;
resplen = 4;
break;
case 0x00040006: /* Get pixel order */
stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo);
resplen = 4;
break;
case 0x00044006: /* Test pixel order */
resplen = 4;
break;
case 0x00048006: /* Set pixel order */
pixo = ldl_le_phys(&s->dma_as, value + 12);
newpixo = &pixo;
resplen = 4;
break;
case 0x00040007: /* Get alpha */
stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha);
resplen = 4;
break;
case 0x00044007: /* Test pixel alpha */
resplen = 4;
break;
case 0x00048007: /* Set alpha */
alpha = ldl_le_phys(&s->dma_as, value + 12);
newalpha = &alpha;
resplen = 4;
break;
case 0x00040008: /* Get pitch */
stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch);
resplen = 4;
break;
case 0x00040009: /* Get virtual offset */
stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset);
stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset);
resplen = 8;
break;
case 0x00044009: /* Test virtual offset */
resplen = 8;
break;
case 0x00048009: /* Set virtual offset */
xoffset = ldl_le_phys(&s->dma_as, value + 12);
newxoffset = &xoffset;
yoffset = ldl_le_phys(&s->dma_as, value + 16);
newyoffset = &yoffset;
resplen = 8;
break;
case 0x0004000a: /* Get/Test/Set overscan */
case 0x0004400a:
case 0x0004800a:
stl_le_phys(&s->dma_as, value + 12, 0);
stl_le_phys(&s->dma_as, value + 16, 0);
stl_le_phys(&s->dma_as, value + 20, 0);
stl_le_phys(&s->dma_as, value + 24, 0);
resplen = 16;
break;
case 0x0004800b: /* Set palette */
offset = ldl_le_phys(&s->dma_as, value + 12);
length = ldl_le_phys(&s->dma_as, value + 16);
n = 0;
while (n < length - offset) {
color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2));
stl_le_phys(&s->dma_as,
s->fbdev->vcram_base + ((offset + n) << 2), color);
n++;
}
stl_le_phys(&s->dma_as, value + 12, 0);
resplen = 4;
break;
case 0x00060001: /* Get DMA channels */
/* channels 2-5 */
@ -147,6 +267,13 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
value += bufsize + 12;
}
/* Reconfigure framebuffer if required */
if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo
|| newalpha) {
bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset,
newyoffset, newbpp, newpixo, newalpha);
}
/* Buffer response code */
stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31));
}
@ -241,6 +368,15 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp)
Object *obj;
Error *err = NULL;
obj = object_property_get_link(OBJECT(dev), "fb", &err);
if (obj == NULL) {
error_setg(errp, "%s: required fb link not found: %s",
__func__, error_get_pretty(err));
return;
}
s->fbdev = BCM2835_FB(obj);
obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
if (obj == NULL) {
error_setg(errp, "%s: required dma-mr link not found: %s",
@ -259,7 +395,6 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp)
static Property bcm2835_property_props[] = {
DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0),
DEFINE_PROP_UINT32("ram-size", BCM2835PropertyState, ram_size, 0),
DEFINE_PROP_END_OF_LIST()
};

View file

@ -120,20 +120,6 @@ static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev)
return freq;
}
static uint32_t imx25_ccm_get_upll_clk(IMXCCMState *dev)
{
uint32_t freq = 0;
IMX25CCMState *s = IMX25_CCM(dev);
if (!EXTRACT(s->reg[IMX25_CCM_CCTL_REG], UPLL_DIS)) {
freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_UPCTL_REG], CKIH_FREQ);
}
DPRINTF("freq = %d\n", freq);
return freq;
}
static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev)
{
uint32_t freq;
@ -182,21 +168,10 @@ static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
DPRINTF("Clock = %d)\n", clock);
switch (clock) {
case NOCLK:
break;
case CLK_MPLL:
freq = imx25_ccm_get_mpll_clk(dev);
break;
case CLK_UPLL:
freq = imx25_ccm_get_upll_clk(dev);
break;
case CLK_MCU:
freq = imx25_ccm_get_mcu_clk(dev);
break;
case CLK_AHB:
freq = imx25_ccm_get_ahb_clk(dev);
case CLK_NONE:
break;
case CLK_IPG:
case CLK_IPG_HIGH:
freq = imx25_ccm_get_ipg_clk(dev);
break;
case CLK_32k:

View file

@ -152,32 +152,6 @@ static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
return freq;
}
static uint32_t imx31_ccm_get_mcu_clk(IMXCCMState *dev)
{
uint32_t freq;
IMX31CCMState *s = IMX31_CCM(dev);
freq = imx31_ccm_get_mcu_main_clk(dev)
/ (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MCU));
DPRINTF("freq = %d\n", freq);
return freq;
}
static uint32_t imx31_ccm_get_hsp_clk(IMXCCMState *dev)
{
uint32_t freq;
IMX31CCMState *s = IMX31_CCM(dev);
freq = imx31_ccm_get_mcu_main_clk(dev)
/ (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], HSP));
DPRINTF("freq = %d\n", freq);
return freq;
}
static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
{
uint32_t freq;
@ -209,15 +183,10 @@ static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
uint32_t freq = 0;
switch (clock) {
case NOCLK:
break;
case CLK_MCU:
freq = imx31_ccm_get_mcu_clk(dev);
break;
case CLK_HSP:
freq = imx31_ccm_get_hsp_clk(dev);
case CLK_NONE:
break;
case CLK_IPG:
case CLK_IPG_HIGH:
freq = imx31_ccm_get_ipg_clk(dev);
break;
case CLK_32k:

774
hw/misc/imx6_ccm.c Normal file
View file

@ -0,0 +1,774 @@
/*
* IMX6 Clock Control Module
*
* Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
* To get the timer frequencies right, we need to emulate at least part of
* the CCM.
*/
#include "qemu/osdep.h"
#include "hw/misc/imx6_ccm.h"
#ifndef DEBUG_IMX6_CCM
#define DEBUG_IMX6_CCM 0
#endif
#define DPRINTF(fmt, args...) \
do { \
if (DEBUG_IMX6_CCM) { \
fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_CCM, \
__func__, ##args); \
} \
} while (0)
static char const *imx6_ccm_reg_name(uint32_t reg)
{
static char unknown[20];
switch (reg) {
case CCM_CCR:
return "CCR";
case CCM_CCDR:
return "CCDR";
case CCM_CSR:
return "CSR";
case CCM_CCSR:
return "CCSR";
case CCM_CACRR:
return "CACRR";
case CCM_CBCDR:
return "CBCDR";
case CCM_CBCMR:
return "CBCMR";
case CCM_CSCMR1:
return "CSCMR1";
case CCM_CSCMR2:
return "CSCMR2";
case CCM_CSCDR1:
return "CSCDR1";
case CCM_CS1CDR:
return "CS1CDR";
case CCM_CS2CDR:
return "CS2CDR";
case CCM_CDCDR:
return "CDCDR";
case CCM_CHSCCDR:
return "CHSCCDR";
case CCM_CSCDR2:
return "CSCDR2";
case CCM_CSCDR3:
return "CSCDR3";
case CCM_CDHIPR:
return "CDHIPR";
case CCM_CTOR:
return "CTOR";
case CCM_CLPCR:
return "CLPCR";
case CCM_CISR:
return "CISR";
case CCM_CIMR:
return "CIMR";
case CCM_CCOSR:
return "CCOSR";
case CCM_CGPR:
return "CGPR";
case CCM_CCGR0:
return "CCGR0";
case CCM_CCGR1:
return "CCGR1";
case CCM_CCGR2:
return "CCGR2";
case CCM_CCGR3:
return "CCGR3";
case CCM_CCGR4:
return "CCGR4";
case CCM_CCGR5:
return "CCGR5";
case CCM_CCGR6:
return "CCGR6";
case CCM_CMEOR:
return "CMEOR";
default:
sprintf(unknown, "%d ?", reg);
return unknown;
}
}
static char const *imx6_analog_reg_name(uint32_t reg)
{
static char unknown[20];
switch (reg) {
case CCM_ANALOG_PLL_ARM:
return "PLL_ARM";
case CCM_ANALOG_PLL_ARM_SET:
return "PLL_ARM_SET";
case CCM_ANALOG_PLL_ARM_CLR:
return "PLL_ARM_CLR";
case CCM_ANALOG_PLL_ARM_TOG:
return "PLL_ARM_TOG";
case CCM_ANALOG_PLL_USB1:
return "PLL_USB1";
case CCM_ANALOG_PLL_USB1_SET:
return "PLL_USB1_SET";
case CCM_ANALOG_PLL_USB1_CLR:
return "PLL_USB1_CLR";
case CCM_ANALOG_PLL_USB1_TOG:
return "PLL_USB1_TOG";
case CCM_ANALOG_PLL_USB2:
return "PLL_USB2";
case CCM_ANALOG_PLL_USB2_SET:
return "PLL_USB2_SET";
case CCM_ANALOG_PLL_USB2_CLR:
return "PLL_USB2_CLR";
case CCM_ANALOG_PLL_USB2_TOG:
return "PLL_USB2_TOG";
case CCM_ANALOG_PLL_SYS:
return "PLL_SYS";
case CCM_ANALOG_PLL_SYS_SET:
return "PLL_SYS_SET";
case CCM_ANALOG_PLL_SYS_CLR:
return "PLL_SYS_CLR";
case CCM_ANALOG_PLL_SYS_TOG:
return "PLL_SYS_TOG";
case CCM_ANALOG_PLL_SYS_SS:
return "PLL_SYS_SS";
case CCM_ANALOG_PLL_SYS_NUM:
return "PLL_SYS_NUM";
case CCM_ANALOG_PLL_SYS_DENOM:
return "PLL_SYS_DENOM";
case CCM_ANALOG_PLL_AUDIO:
return "PLL_AUDIO";
case CCM_ANALOG_PLL_AUDIO_SET:
return "PLL_AUDIO_SET";
case CCM_ANALOG_PLL_AUDIO_CLR:
return "PLL_AUDIO_CLR";
case CCM_ANALOG_PLL_AUDIO_TOG:
return "PLL_AUDIO_TOG";
case CCM_ANALOG_PLL_AUDIO_NUM:
return "PLL_AUDIO_NUM";
case CCM_ANALOG_PLL_AUDIO_DENOM:
return "PLL_AUDIO_DENOM";
case CCM_ANALOG_PLL_VIDEO:
return "PLL_VIDEO";
case CCM_ANALOG_PLL_VIDEO_SET:
return "PLL_VIDEO_SET";
case CCM_ANALOG_PLL_VIDEO_CLR:
return "PLL_VIDEO_CLR";
case CCM_ANALOG_PLL_VIDEO_TOG:
return "PLL_VIDEO_TOG";
case CCM_ANALOG_PLL_VIDEO_NUM:
return "PLL_VIDEO_NUM";
case CCM_ANALOG_PLL_VIDEO_DENOM:
return "PLL_VIDEO_DENOM";
case CCM_ANALOG_PLL_MLB:
return "PLL_MLB";
case CCM_ANALOG_PLL_MLB_SET:
return "PLL_MLB_SET";
case CCM_ANALOG_PLL_MLB_CLR:
return "PLL_MLB_CLR";
case CCM_ANALOG_PLL_MLB_TOG:
return "PLL_MLB_TOG";
case CCM_ANALOG_PLL_ENET:
return "PLL_ENET";
case CCM_ANALOG_PLL_ENET_SET:
return "PLL_ENET_SET";
case CCM_ANALOG_PLL_ENET_CLR:
return "PLL_ENET_CLR";
case CCM_ANALOG_PLL_ENET_TOG:
return "PLL_ENET_TOG";
case CCM_ANALOG_PFD_480:
return "PFD_480";
case CCM_ANALOG_PFD_480_SET:
return "PFD_480_SET";
case CCM_ANALOG_PFD_480_CLR:
return "PFD_480_CLR";
case CCM_ANALOG_PFD_480_TOG:
return "PFD_480_TOG";
case CCM_ANALOG_PFD_528:
return "PFD_528";
case CCM_ANALOG_PFD_528_SET:
return "PFD_528_SET";
case CCM_ANALOG_PFD_528_CLR:
return "PFD_528_CLR";
case CCM_ANALOG_PFD_528_TOG:
return "PFD_528_TOG";
case CCM_ANALOG_MISC0:
return "MISC0";
case CCM_ANALOG_MISC0_SET:
return "MISC0_SET";
case CCM_ANALOG_MISC0_CLR:
return "MISC0_CLR";
case CCM_ANALOG_MISC0_TOG:
return "MISC0_TOG";
case CCM_ANALOG_MISC2:
return "MISC2";
case CCM_ANALOG_MISC2_SET:
return "MISC2_SET";
case CCM_ANALOG_MISC2_CLR:
return "MISC2_CLR";
case CCM_ANALOG_MISC2_TOG:
return "MISC2_TOG";
case PMU_REG_1P1:
return "PMU_REG_1P1";
case PMU_REG_3P0:
return "PMU_REG_3P0";
case PMU_REG_2P5:
return "PMU_REG_2P5";
case PMU_REG_CORE:
return "PMU_REG_CORE";
case PMU_MISC1:
return "PMU_MISC1";
case PMU_MISC1_SET:
return "PMU_MISC1_SET";
case PMU_MISC1_CLR:
return "PMU_MISC1_CLR";
case PMU_MISC1_TOG:
return "PMU_MISC1_TOG";
case USB_ANALOG_DIGPROG:
return "USB_ANALOG_DIGPROG";
default:
sprintf(unknown, "%d ?", reg);
return unknown;
}
}
#define CKIH_FREQ 24000000 /* 24MHz crystal input */
static const VMStateDescription vmstate_imx6_ccm = {
.name = TYPE_IMX6_CCM,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(ccm, IMX6CCMState, CCM_MAX),
VMSTATE_UINT32_ARRAY(analog, IMX6CCMState, CCM_ANALOG_MAX),
VMSTATE_END_OF_LIST()
},
};
static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev)
{
uint64_t freq = 24000000;
if (EXTRACT(dev->analog[CCM_ANALOG_PLL_SYS], DIV_SELECT)) {
freq *= 22;
} else {
freq *= 20;
}
DPRINTF("freq = %d\n", (uint32_t)freq);
return freq;
}
static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev)
{
uint64_t freq = 0;
freq = imx6_analog_get_pll2_clk(dev) * 18
/ EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC);
DPRINTF("freq = %d\n", (uint32_t)freq);
return freq;
}
static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev)
{
uint64_t freq = 0;
freq = imx6_analog_get_pll2_clk(dev) * 18
/ EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC);
DPRINTF("freq = %d\n", (uint32_t)freq);
return freq;
}
static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev)
{
uint64_t freq = 0;
switch (EXTRACT(dev->ccm[CCM_CBCMR], PRE_PERIPH_CLK_SEL)) {
case 0:
freq = imx6_analog_get_pll2_clk(dev);
break;
case 1:
freq = imx6_analog_get_pll2_pfd2_clk(dev);
break;
case 2:
freq = imx6_analog_get_pll2_pfd0_clk(dev);
break;
case 3:
freq = imx6_analog_get_pll2_pfd2_clk(dev) / 2;
break;
default:
/* We should never get there */
g_assert_not_reached();
break;
}
DPRINTF("freq = %d\n", (uint32_t)freq);
return freq;
}
static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev)
{
uint64_t freq = 0;
freq = imx6_analog_get_periph_clk(dev)
/ (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF));
DPRINTF("freq = %d\n", (uint32_t)freq);
return freq;
}
static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev)
{
uint64_t freq = 0;
freq = imx6_ccm_get_ahb_clk(dev)
/ (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF));;
DPRINTF("freq = %d\n", (uint32_t)freq);
return freq;
}
static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev)
{
uint64_t freq = 0;
freq = imx6_ccm_get_ipg_clk(dev)
/ (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF));
DPRINTF("freq = %d\n", (uint32_t)freq);
return freq;
}
static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
{
uint32_t freq = 0;
IMX6CCMState *s = IMX6_CCM(dev);
switch (clock) {
case CLK_NONE:
break;
case CLK_IPG:
freq = imx6_ccm_get_ipg_clk(s);
break;
case CLK_IPG_HIGH:
freq = imx6_ccm_get_per_clk(s);
break;
case CLK_32k:
freq = CKIL_FREQ;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
TYPE_IMX6_CCM, __func__, clock);
break;
}
DPRINTF("Clock = %d) = %d\n", clock, freq);
return freq;
}
static void imx6_ccm_reset(DeviceState *dev)
{
IMX6CCMState *s = IMX6_CCM(dev);
DPRINTF("\n");
s->ccm[CCM_CCR] = 0x040116FF;
s->ccm[CCM_CCDR] = 0x00000000;
s->ccm[CCM_CSR] = 0x00000010;
s->ccm[CCM_CCSR] = 0x00000100;
s->ccm[CCM_CACRR] = 0x00000000;
s->ccm[CCM_CBCDR] = 0x00018D40;
s->ccm[CCM_CBCMR] = 0x00022324;
s->ccm[CCM_CSCMR1] = 0x00F00000;
s->ccm[CCM_CSCMR2] = 0x02B92F06;
s->ccm[CCM_CSCDR1] = 0x00490B00;
s->ccm[CCM_CS1CDR] = 0x0EC102C1;
s->ccm[CCM_CS2CDR] = 0x000736C1;
s->ccm[CCM_CDCDR] = 0x33F71F92;
s->ccm[CCM_CHSCCDR] = 0x0002A150;
s->ccm[CCM_CSCDR2] = 0x0002A150;
s->ccm[CCM_CSCDR3] = 0x00014841;
s->ccm[CCM_CDHIPR] = 0x00000000;
s->ccm[CCM_CTOR] = 0x00000000;
s->ccm[CCM_CLPCR] = 0x00000079;
s->ccm[CCM_CISR] = 0x00000000;
s->ccm[CCM_CIMR] = 0xFFFFFFFF;
s->ccm[CCM_CCOSR] = 0x000A0001;
s->ccm[CCM_CGPR] = 0x0000FE62;
s->ccm[CCM_CCGR0] = 0xFFFFFFFF;
s->ccm[CCM_CCGR1] = 0xFFFFFFFF;
s->ccm[CCM_CCGR2] = 0xFC3FFFFF;
s->ccm[CCM_CCGR3] = 0xFFFFFFFF;
s->ccm[CCM_CCGR4] = 0xFFFFFFFF;
s->ccm[CCM_CCGR5] = 0xFFFFFFFF;
s->ccm[CCM_CCGR6] = 0xFFFFFFFF;
s->ccm[CCM_CMEOR] = 0xFFFFFFFF;
s->analog[CCM_ANALOG_PLL_ARM] = 0x00013042;
s->analog[CCM_ANALOG_PLL_USB1] = 0x00012000;
s->analog[CCM_ANALOG_PLL_USB2] = 0x00012000;
s->analog[CCM_ANALOG_PLL_SYS] = 0x00013001;
s->analog[CCM_ANALOG_PLL_SYS_SS] = 0x00000000;
s->analog[CCM_ANALOG_PLL_SYS_NUM] = 0x00000000;
s->analog[CCM_ANALOG_PLL_SYS_DENOM] = 0x00000012;
s->analog[CCM_ANALOG_PLL_AUDIO] = 0x00011006;
s->analog[CCM_ANALOG_PLL_AUDIO_NUM] = 0x05F5E100;
s->analog[CCM_ANALOG_PLL_AUDIO_DENOM] = 0x2964619C;
s->analog[CCM_ANALOG_PLL_VIDEO] = 0x0001100C;
s->analog[CCM_ANALOG_PLL_VIDEO_NUM] = 0x05F5E100;
s->analog[CCM_ANALOG_PLL_VIDEO_DENOM] = 0x10A24447;
s->analog[CCM_ANALOG_PLL_MLB] = 0x00010000;
s->analog[CCM_ANALOG_PLL_ENET] = 0x00011001;
s->analog[CCM_ANALOG_PFD_480] = 0x1311100C;
s->analog[CCM_ANALOG_PFD_528] = 0x1018101B;
s->analog[PMU_REG_1P1] = 0x00001073;
s->analog[PMU_REG_3P0] = 0x00000F74;
s->analog[PMU_REG_2P5] = 0x00005071;
s->analog[PMU_REG_CORE] = 0x00402010;
s->analog[PMU_MISC0] = 0x04000000;
s->analog[PMU_MISC1] = 0x00000000;
s->analog[PMU_MISC2] = 0x00272727;
s->analog[USB_ANALOG_USB1_VBUS_DETECT] = 0x00000004;
s->analog[USB_ANALOG_USB1_CHRG_DETECT] = 0x00000000;
s->analog[USB_ANALOG_USB1_VBUS_DETECT_STAT] = 0x00000000;
s->analog[USB_ANALOG_USB1_CHRG_DETECT_STAT] = 0x00000000;
s->analog[USB_ANALOG_USB1_MISC] = 0x00000002;
s->analog[USB_ANALOG_USB2_VBUS_DETECT] = 0x00000004;
s->analog[USB_ANALOG_USB2_CHRG_DETECT] = 0x00000000;
s->analog[USB_ANALOG_USB2_MISC] = 0x00000002;
s->analog[USB_ANALOG_DIGPROG] = 0x00000000;
/* all PLLs need to be locked */
s->analog[CCM_ANALOG_PLL_ARM] |= CCM_ANALOG_PLL_LOCK;
s->analog[CCM_ANALOG_PLL_USB1] |= CCM_ANALOG_PLL_LOCK;
s->analog[CCM_ANALOG_PLL_USB2] |= CCM_ANALOG_PLL_LOCK;
s->analog[CCM_ANALOG_PLL_SYS] |= CCM_ANALOG_PLL_LOCK;
s->analog[CCM_ANALOG_PLL_AUDIO] |= CCM_ANALOG_PLL_LOCK;
s->analog[CCM_ANALOG_PLL_VIDEO] |= CCM_ANALOG_PLL_LOCK;
s->analog[CCM_ANALOG_PLL_MLB] |= CCM_ANALOG_PLL_LOCK;
s->analog[CCM_ANALOG_PLL_ENET] |= CCM_ANALOG_PLL_LOCK;
}
static uint64_t imx6_ccm_read(void *opaque, hwaddr offset, unsigned size)
{
uint32_t value = 0;
uint32_t index = offset >> 2;
IMX6CCMState *s = (IMX6CCMState *)opaque;
value = s->ccm[index];
DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), value);
return (uint64_t)value;
}
static void imx6_ccm_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
uint32_t index = offset >> 2;
IMX6CCMState *s = (IMX6CCMState *)opaque;
DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_ccm_reg_name(index),
(uint32_t)value);
/*
* We will do a better implementation later. In particular some bits
* cannot be written to.
*/
s->ccm[index] = (uint32_t)value;
}
static uint64_t imx6_analog_read(void *opaque, hwaddr offset, unsigned size)
{
uint32_t value;
uint32_t index = offset >> 2;
IMX6CCMState *s = (IMX6CCMState *)opaque;
switch (index) {
case CCM_ANALOG_PLL_ARM_SET:
case CCM_ANALOG_PLL_USB1_SET:
case CCM_ANALOG_PLL_USB2_SET:
case CCM_ANALOG_PLL_SYS_SET:
case CCM_ANALOG_PLL_AUDIO_SET:
case CCM_ANALOG_PLL_VIDEO_SET:
case CCM_ANALOG_PLL_MLB_SET:
case CCM_ANALOG_PLL_ENET_SET:
case CCM_ANALOG_PFD_480_SET:
case CCM_ANALOG_PFD_528_SET:
case CCM_ANALOG_MISC0_SET:
case PMU_MISC1_SET:
case CCM_ANALOG_MISC2_SET:
case USB_ANALOG_USB1_VBUS_DETECT_SET:
case USB_ANALOG_USB1_CHRG_DETECT_SET:
case USB_ANALOG_USB1_MISC_SET:
case USB_ANALOG_USB2_VBUS_DETECT_SET:
case USB_ANALOG_USB2_CHRG_DETECT_SET:
case USB_ANALOG_USB2_MISC_SET:
/*
* All REG_NAME_SET register access are in fact targeting the
* the REG_NAME register.
*/
value = s->analog[index - 1];
break;
case CCM_ANALOG_PLL_ARM_CLR:
case CCM_ANALOG_PLL_USB1_CLR:
case CCM_ANALOG_PLL_USB2_CLR:
case CCM_ANALOG_PLL_SYS_CLR:
case CCM_ANALOG_PLL_AUDIO_CLR:
case CCM_ANALOG_PLL_VIDEO_CLR:
case CCM_ANALOG_PLL_MLB_CLR:
case CCM_ANALOG_PLL_ENET_CLR:
case CCM_ANALOG_PFD_480_CLR:
case CCM_ANALOG_PFD_528_CLR:
case CCM_ANALOG_MISC0_CLR:
case PMU_MISC1_CLR:
case CCM_ANALOG_MISC2_CLR:
case USB_ANALOG_USB1_VBUS_DETECT_CLR:
case USB_ANALOG_USB1_CHRG_DETECT_CLR:
case USB_ANALOG_USB1_MISC_CLR:
case USB_ANALOG_USB2_VBUS_DETECT_CLR:
case USB_ANALOG_USB2_CHRG_DETECT_CLR:
case USB_ANALOG_USB2_MISC_CLR:
/*
* All REG_NAME_CLR register access are in fact targeting the
* the REG_NAME register.
*/
value = s->analog[index - 2];
break;
case CCM_ANALOG_PLL_ARM_TOG:
case CCM_ANALOG_PLL_USB1_TOG:
case CCM_ANALOG_PLL_USB2_TOG:
case CCM_ANALOG_PLL_SYS_TOG:
case CCM_ANALOG_PLL_AUDIO_TOG:
case CCM_ANALOG_PLL_VIDEO_TOG:
case CCM_ANALOG_PLL_MLB_TOG:
case CCM_ANALOG_PLL_ENET_TOG:
case CCM_ANALOG_PFD_480_TOG:
case CCM_ANALOG_PFD_528_TOG:
case CCM_ANALOG_MISC0_TOG:
case PMU_MISC1_TOG:
case CCM_ANALOG_MISC2_TOG:
case USB_ANALOG_USB1_VBUS_DETECT_TOG:
case USB_ANALOG_USB1_CHRG_DETECT_TOG:
case USB_ANALOG_USB1_MISC_TOG:
case USB_ANALOG_USB2_VBUS_DETECT_TOG:
case USB_ANALOG_USB2_CHRG_DETECT_TOG:
case USB_ANALOG_USB2_MISC_TOG:
/*
* All REG_NAME_TOG register access are in fact targeting the
* the REG_NAME register.
*/
value = s->analog[index - 3];
break;
default:
value = s->analog[index];
break;
}
DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_analog_reg_name(index), value);
return (uint64_t)value;
}
static void imx6_analog_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
uint32_t index = offset >> 2;
IMX6CCMState *s = (IMX6CCMState *)opaque;
DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_analog_reg_name(index),
(uint32_t)value);
switch (index) {
case CCM_ANALOG_PLL_ARM_SET:
case CCM_ANALOG_PLL_USB1_SET:
case CCM_ANALOG_PLL_USB2_SET:
case CCM_ANALOG_PLL_SYS_SET:
case CCM_ANALOG_PLL_AUDIO_SET:
case CCM_ANALOG_PLL_VIDEO_SET:
case CCM_ANALOG_PLL_MLB_SET:
case CCM_ANALOG_PLL_ENET_SET:
case CCM_ANALOG_PFD_480_SET:
case CCM_ANALOG_PFD_528_SET:
case CCM_ANALOG_MISC0_SET:
case PMU_MISC1_SET:
case CCM_ANALOG_MISC2_SET:
case USB_ANALOG_USB1_VBUS_DETECT_SET:
case USB_ANALOG_USB1_CHRG_DETECT_SET:
case USB_ANALOG_USB1_MISC_SET:
case USB_ANALOG_USB2_VBUS_DETECT_SET:
case USB_ANALOG_USB2_CHRG_DETECT_SET:
case USB_ANALOG_USB2_MISC_SET:
/*
* All REG_NAME_SET register access are in fact targeting the
* the REG_NAME register. So we change the value of the
* REG_NAME register, setting bits passed in the value.
*/
s->analog[index - 1] |= value;
break;
case CCM_ANALOG_PLL_ARM_CLR:
case CCM_ANALOG_PLL_USB1_CLR:
case CCM_ANALOG_PLL_USB2_CLR:
case CCM_ANALOG_PLL_SYS_CLR:
case CCM_ANALOG_PLL_AUDIO_CLR:
case CCM_ANALOG_PLL_VIDEO_CLR:
case CCM_ANALOG_PLL_MLB_CLR:
case CCM_ANALOG_PLL_ENET_CLR:
case CCM_ANALOG_PFD_480_CLR:
case CCM_ANALOG_PFD_528_CLR:
case CCM_ANALOG_MISC0_CLR:
case PMU_MISC1_CLR:
case CCM_ANALOG_MISC2_CLR:
case USB_ANALOG_USB1_VBUS_DETECT_CLR:
case USB_ANALOG_USB1_CHRG_DETECT_CLR:
case USB_ANALOG_USB1_MISC_CLR:
case USB_ANALOG_USB2_VBUS_DETECT_CLR:
case USB_ANALOG_USB2_CHRG_DETECT_CLR:
case USB_ANALOG_USB2_MISC_CLR:
/*
* All REG_NAME_CLR register access are in fact targeting the
* the REG_NAME register. So we change the value of the
* REG_NAME register, unsetting bits passed in the value.
*/
s->analog[index - 2] &= ~value;
break;
case CCM_ANALOG_PLL_ARM_TOG:
case CCM_ANALOG_PLL_USB1_TOG:
case CCM_ANALOG_PLL_USB2_TOG:
case CCM_ANALOG_PLL_SYS_TOG:
case CCM_ANALOG_PLL_AUDIO_TOG:
case CCM_ANALOG_PLL_VIDEO_TOG:
case CCM_ANALOG_PLL_MLB_TOG:
case CCM_ANALOG_PLL_ENET_TOG:
case CCM_ANALOG_PFD_480_TOG:
case CCM_ANALOG_PFD_528_TOG:
case CCM_ANALOG_MISC0_TOG:
case PMU_MISC1_TOG:
case CCM_ANALOG_MISC2_TOG:
case USB_ANALOG_USB1_VBUS_DETECT_TOG:
case USB_ANALOG_USB1_CHRG_DETECT_TOG:
case USB_ANALOG_USB1_MISC_TOG:
case USB_ANALOG_USB2_VBUS_DETECT_TOG:
case USB_ANALOG_USB2_CHRG_DETECT_TOG:
case USB_ANALOG_USB2_MISC_TOG:
/*
* All REG_NAME_TOG register access are in fact targeting the
* the REG_NAME register. So we change the value of the
* REG_NAME register, toggling bits passed in the value.
*/
s->analog[index - 3] ^= value;
break;
default:
/*
* We will do a better implementation later. In particular some bits
* cannot be written to.
*/
s->analog[index] = value;
break;
}
}
static const struct MemoryRegionOps imx6_ccm_ops = {
.read = imx6_ccm_read,
.write = imx6_ccm_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
/*
* Our device would not work correctly if the guest was doing
* unaligned access. This might not be a limitation on the real
* device but in practice there is no reason for a guest to access
* this device unaligned.
*/
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static const struct MemoryRegionOps imx6_analog_ops = {
.read = imx6_analog_read,
.write = imx6_analog_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
/*
* Our device would not work correctly if the guest was doing
* unaligned access. This might not be a limitation on the real
* device but in practice there is no reason for a guest to access
* this device unaligned.
*/
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static void imx6_ccm_init(Object *obj)
{
DeviceState *dev = DEVICE(obj);
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
IMX6CCMState *s = IMX6_CCM(obj);
/* initialize a container for the all memory range */
memory_region_init(&s->container, OBJECT(dev), TYPE_IMX6_CCM, 0x5000);
/* We initialize an IO memory region for the CCM part */
memory_region_init_io(&s->ioccm, OBJECT(dev), &imx6_ccm_ops, s,
TYPE_IMX6_CCM ".ccm", CCM_MAX * sizeof(uint32_t));
/* Add the CCM as a subregion at offset 0 */
memory_region_add_subregion(&s->container, 0, &s->ioccm);
/* We initialize an IO memory region for the ANALOG part */
memory_region_init_io(&s->ioanalog, OBJECT(dev), &imx6_analog_ops, s,
TYPE_IMX6_CCM ".analog",
CCM_ANALOG_MAX * sizeof(uint32_t));
/* Add the ANALOG as a subregion at offset 0x4000 */
memory_region_add_subregion(&s->container, 0x4000, &s->ioanalog);
sysbus_init_mmio(sd, &s->container);
}
static void imx6_ccm_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
dc->reset = imx6_ccm_reset;
dc->vmsd = &vmstate_imx6_ccm;
dc->desc = "i.MX6 Clock Control Module";
ccm->get_clock_frequency = imx6_ccm_get_clock_frequency;
}
static const TypeInfo imx6_ccm_info = {
.name = TYPE_IMX6_CCM,
.parent = TYPE_IMX_CCM,
.instance_size = sizeof(IMX6CCMState),
.instance_init = imx6_ccm_init,
.class_init = imx6_ccm_class_init,
};
static void imx6_ccm_register_types(void)
{
type_register_static(&imx6_ccm_info);
}
type_init(imx6_ccm_register_types)

View file

@ -693,6 +693,7 @@ static void imx_fec_class_init(ObjectClass *klass, void *data)
dc->reset = imx_fec_reset;
dc->props = imx_fec_properties;
dc->realize = imx_fec_realize;
dc->desc = "i.MX FEC Ethernet Controller";
}
static const TypeInfo imx_fec_info = {

View file

@ -563,17 +563,19 @@ static const VMStateDescription sd_vmstate = {
/* Legacy initialization function for use by non-qdevified callers */
SDState *sd_init(BlockBackend *blk, bool is_spi)
{
Object *obj;
DeviceState *dev;
Error *err = NULL;
dev = qdev_create(NULL, TYPE_SD_CARD);
obj = object_new(TYPE_SD_CARD);
dev = DEVICE(obj);
qdev_prop_set_drive(dev, "drive", blk, &err);
if (err) {
error_report("sd_init failed: %s", error_get_pretty(err));
return NULL;
}
qdev_prop_set_bit(dev, "spi", is_spi);
object_property_set_bool(OBJECT(dev), true, "realized", &err);
object_property_set_bool(obj, true, "realized", &err);
if (err) {
error_report("sd_init failed: %s", error_get_pretty(err));
return NULL;

View file

@ -32,3 +32,4 @@ obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o

449
hw/timer/aspeed_timer.c Normal file
View file

@ -0,0 +1,449 @@
/*
* ASPEED AST2400 Timer
*
* Andrew Jeffery <andrew@aj.id.au>
*
* Copyright (C) 2016 IBM Corp.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "hw/ptimer.h"
#include "hw/sysbus.h"
#include "hw/timer/aspeed_timer.h"
#include "qemu-common.h"
#include "qemu/bitops.h"
#include "qemu/main-loop.h"
#include "qemu/timer.h"
#include "trace.h"
#define TIMER_NR_REGS 4
#define TIMER_CTRL_BITS 4
#define TIMER_CTRL_MASK ((1 << TIMER_CTRL_BITS) - 1)
#define TIMER_CLOCK_USE_EXT true
#define TIMER_CLOCK_EXT_HZ 1000000
#define TIMER_CLOCK_USE_APB false
#define TIMER_CLOCK_APB_HZ 24000000
#define TIMER_REG_STATUS 0
#define TIMER_REG_RELOAD 1
#define TIMER_REG_MATCH_FIRST 2
#define TIMER_REG_MATCH_SECOND 3
#define TIMER_FIRST_CAP_PULSE 4
enum timer_ctrl_op {
op_enable = 0,
op_external_clock,
op_overflow_interrupt,
op_pulse_enable
};
/**
* Avoid mutual references between AspeedTimerCtrlState and AspeedTimer
* structs, as it's a waste of memory. The ptimer BH callback needs to know
* whether a specific AspeedTimer is enabled, but this information is held in
* AspeedTimerCtrlState. So, provide a helper to hoist ourselves from an
* arbitrary AspeedTimer to AspeedTimerCtrlState.
*/
static inline AspeedTimerCtrlState *timer_to_ctrl(AspeedTimer *t)
{
const AspeedTimer (*timers)[] = (void *)t - (t->id * sizeof(*t));
return container_of(timers, AspeedTimerCtrlState, timers);
}
static inline bool timer_ctrl_status(AspeedTimer *t, enum timer_ctrl_op op)
{
return !!(timer_to_ctrl(t)->ctrl & BIT(t->id * TIMER_CTRL_BITS + op));
}
static inline bool timer_enabled(AspeedTimer *t)
{
return timer_ctrl_status(t, op_enable);
}
static inline bool timer_overflow_interrupt(AspeedTimer *t)
{
return timer_ctrl_status(t, op_overflow_interrupt);
}
static inline bool timer_can_pulse(AspeedTimer *t)
{
return t->id >= TIMER_FIRST_CAP_PULSE;
}
static void aspeed_timer_expire(void *opaque)
{
AspeedTimer *t = opaque;
/* Only support interrupts on match values of zero for the moment - this is
* sufficient to boot an aspeed_defconfig Linux kernel.
*
* TODO: matching on arbitrary values (see e.g. hw/timer/a9gtimer.c)
*/
bool match = !(t->match[0] && t->match[1]);
bool interrupt = timer_overflow_interrupt(t) || match;
if (timer_enabled(t) && interrupt) {
t->level = !t->level;
qemu_set_irq(t->irq, t->level);
}
}
static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg)
{
uint64_t value;
switch (reg) {
case TIMER_REG_STATUS:
value = ptimer_get_count(t->timer);
break;
case TIMER_REG_RELOAD:
value = t->reload;
break;
case TIMER_REG_MATCH_FIRST:
case TIMER_REG_MATCH_SECOND:
value = t->match[reg - 2];
break;
default:
qemu_log_mask(LOG_UNIMP, "%s: Programming error: unexpected reg: %d\n",
__func__, reg);
value = 0;
break;
}
return value;
}
static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size)
{
AspeedTimerCtrlState *s = opaque;
const int reg = (offset & 0xf) / 4;
uint64_t value;
switch (offset) {
case 0x30: /* Control Register */
value = s->ctrl;
break;
case 0x34: /* Control Register 2 */
value = s->ctrl2;
break;
case 0x00 ... 0x2c: /* Timers 1 - 4 */
value = aspeed_timer_get_value(&s->timers[(offset >> 4)], reg);
break;
case 0x40 ... 0x8c: /* Timers 5 - 8 */
value = aspeed_timer_get_value(&s->timers[(offset >> 4) - 1], reg);
break;
/* Illegal */
case 0x38:
case 0x3C:
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
value = 0;
break;
}
trace_aspeed_timer_read(offset, size, value);
return value;
}
static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
uint32_t value)
{
AspeedTimer *t;
trace_aspeed_timer_set_value(timer, reg, value);
t = &s->timers[timer];
switch (reg) {
case TIMER_REG_STATUS:
if (timer_enabled(t)) {
ptimer_set_count(t->timer, value);
}
break;
case TIMER_REG_RELOAD:
t->reload = value;
ptimer_set_limit(t->timer, value, 1);
break;
case TIMER_REG_MATCH_FIRST:
case TIMER_REG_MATCH_SECOND:
if (value) {
/* Non-zero match values are unsupported. As such an interrupt will
* always be triggered when the timer reaches zero even if the
* overflow interrupt control bit is clear.
*/
qemu_log_mask(LOG_UNIMP, "%s: Match value unsupported by device: "
"0x%" PRIx32 "\n", __func__, value);
} else {
t->match[reg - 2] = value;
}
break;
default:
qemu_log_mask(LOG_UNIMP, "%s: Programming error: unexpected reg: %d\n",
__func__, reg);
break;
}
}
/* Control register operations are broken out into helpers that can be
* explictly called on aspeed_timer_reset(), but also from
* aspeed_timer_ctrl_op().
*/
static void aspeed_timer_ctrl_enable(AspeedTimer *t, bool enable)
{
trace_aspeed_timer_ctrl_enable(t->id, enable);
if (enable) {
ptimer_run(t->timer, 0);
} else {
ptimer_stop(t->timer);
ptimer_set_limit(t->timer, t->reload, 1);
}
}
static void aspeed_timer_ctrl_external_clock(AspeedTimer *t, bool enable)
{
trace_aspeed_timer_ctrl_external_clock(t->id, enable);
if (enable) {
ptimer_set_freq(t->timer, TIMER_CLOCK_EXT_HZ);
} else {
ptimer_set_freq(t->timer, TIMER_CLOCK_APB_HZ);
}
}
static void aspeed_timer_ctrl_overflow_interrupt(AspeedTimer *t, bool enable)
{
trace_aspeed_timer_ctrl_overflow_interrupt(t->id, enable);
}
static void aspeed_timer_ctrl_pulse_enable(AspeedTimer *t, bool enable)
{
if (timer_can_pulse(t)) {
trace_aspeed_timer_ctrl_pulse_enable(t->id, enable);
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Timer does not support pulse mode\n", __func__);
}
}
/**
* Given the actions are fixed in number and completely described in helper
* functions, dispatch with a lookup table rather than manage control flow with
* a switch statement.
*/
static void (*const ctrl_ops[])(AspeedTimer *, bool) = {
[op_enable] = aspeed_timer_ctrl_enable,
[op_external_clock] = aspeed_timer_ctrl_external_clock,
[op_overflow_interrupt] = aspeed_timer_ctrl_overflow_interrupt,
[op_pulse_enable] = aspeed_timer_ctrl_pulse_enable,
};
/**
* Conditionally affect changes chosen by a timer's control bit.
*
* The aspeed_timer_ctrl_op() interface is convenient for the
* aspeed_timer_set_ctrl() function as the "no change" early exit can be
* calculated for all operations, which cleans up the caller code. However the
* interface isn't convenient for the reset function where we want to enter a
* specific state without artificially constructing old and new values that
* will fall through the change guard (and motivates extracting the actions
* out to helper functions).
*
* @t: The timer to manipulate
* @op: The type of operation to be performed
* @old: The old state of the timer's control bits
* @new: The incoming state for the timer's control bits
*/
static void aspeed_timer_ctrl_op(AspeedTimer *t, enum timer_ctrl_op op,
uint8_t old, uint8_t new)
{
const uint8_t mask = BIT(op);
const bool enable = !!(new & mask);
const bool changed = ((old ^ new) & mask);
if (!changed) {
return;
}
ctrl_ops[op](t, enable);
}
static void aspeed_timer_set_ctrl(AspeedTimerCtrlState *s, uint32_t reg)
{
int i;
int shift;
uint8_t t_old, t_new;
AspeedTimer *t;
const uint8_t enable_mask = BIT(op_enable);
/* Handle a dependency between the 'enable' and remaining three
* configuration bits - i.e. if more than one bit in the control set has
* changed, including the 'enable' bit, then we want either disable the
* timer and perform configuration, or perform configuration and then
* enable the timer
*/
for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
t = &s->timers[i];
shift = (i * TIMER_CTRL_BITS);
t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK;
t_new = (reg >> shift) & TIMER_CTRL_MASK;
/* If we are disabling, do so first */
if ((t_old & enable_mask) && !(t_new & enable_mask)) {
aspeed_timer_ctrl_enable(t, false);
}
aspeed_timer_ctrl_op(t, op_external_clock, t_old, t_new);
aspeed_timer_ctrl_op(t, op_overflow_interrupt, t_old, t_new);
aspeed_timer_ctrl_op(t, op_pulse_enable, t_old, t_new);
/* If we are enabling, do so last */
if (!(t_old & enable_mask) && (t_new & enable_mask)) {
aspeed_timer_ctrl_enable(t, true);
}
}
s->ctrl = reg;
}
static void aspeed_timer_set_ctrl2(AspeedTimerCtrlState *s, uint32_t value)
{
trace_aspeed_timer_set_ctrl2(value);
}
static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF);
const int reg = (offset & 0xf) / 4;
AspeedTimerCtrlState *s = opaque;
switch (offset) {
/* Control Registers */
case 0x30:
aspeed_timer_set_ctrl(s, tv);
break;
case 0x34:
aspeed_timer_set_ctrl2(s, tv);
break;
/* Timer Registers */
case 0x00 ... 0x2c:
aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS), reg, tv);
break;
case 0x40 ... 0x8c:
aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS) - 1, reg, tv);
break;
/* Illegal */
case 0x38:
case 0x3C:
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
break;
}
}
static const MemoryRegionOps aspeed_timer_ops = {
.read = aspeed_timer_read,
.write = aspeed_timer_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
.valid.unaligned = false,
};
static void aspeed_init_one_timer(AspeedTimerCtrlState *s, uint8_t id)
{
QEMUBH *bh;
AspeedTimer *t = &s->timers[id];
t->id = id;
bh = qemu_bh_new(aspeed_timer_expire, t);
t->timer = ptimer_init(bh);
}
static void aspeed_timer_realize(DeviceState *dev, Error **errp)
{
int i;
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
AspeedTimerCtrlState *s = ASPEED_TIMER(dev);
for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
aspeed_init_one_timer(s, i);
sysbus_init_irq(sbd, &s->timers[i].irq);
}
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_timer_ops, s,
TYPE_ASPEED_TIMER, 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
}
static void aspeed_timer_reset(DeviceState *dev)
{
int i;
AspeedTimerCtrlState *s = ASPEED_TIMER(dev);
for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
AspeedTimer *t = &s->timers[i];
/* Explictly call helpers to avoid any conditional behaviour through
* aspeed_timer_set_ctrl().
*/
aspeed_timer_ctrl_enable(t, false);
aspeed_timer_ctrl_external_clock(t, TIMER_CLOCK_USE_APB);
aspeed_timer_ctrl_overflow_interrupt(t, false);
aspeed_timer_ctrl_pulse_enable(t, false);
t->level = 0;
t->reload = 0;
t->match[0] = 0;
t->match[1] = 0;
}
s->ctrl = 0;
s->ctrl2 = 0;
}
static const VMStateDescription vmstate_aspeed_timer = {
.name = "aspeed.timer",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8(id, AspeedTimer),
VMSTATE_INT32(level, AspeedTimer),
VMSTATE_PTIMER(timer, AspeedTimer),
VMSTATE_UINT32(reload, AspeedTimer),
VMSTATE_UINT32_ARRAY(match, AspeedTimer, 2),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_aspeed_timer_state = {
.name = "aspeed.timerctrl",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(ctrl, AspeedTimerCtrlState),
VMSTATE_UINT32(ctrl2, AspeedTimerCtrlState),
VMSTATE_STRUCT_ARRAY(timers, AspeedTimerCtrlState,
ASPEED_TIMER_NR_TIMERS, 1, vmstate_aspeed_timer,
AspeedTimer),
VMSTATE_END_OF_LIST()
}
};
static void timer_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = aspeed_timer_realize;
dc->reset = aspeed_timer_reset;
dc->desc = "ASPEED Timer";
dc->vmsd = &vmstate_aspeed_timer_state;
}
static const TypeInfo aspeed_timer_info = {
.name = TYPE_ASPEED_TIMER,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AspeedTimerCtrlState),
.class_init = timer_class_init,
};
static void aspeed_timer_register_types(void)
{
type_register_static(&aspeed_timer_info);
}
type_init(aspeed_timer_register_types)

View file

@ -52,10 +52,10 @@ static char const *imx_epit_reg_name(uint32_t reg)
* These are typical.
*/
static const IMXClk imx_epit_clocks[] = {
NOCLK, /* 00 disabled */
CLK_IPG, /* 01 ipg_clk, ~532MHz */
CLK_IPG, /* 10 ipg_clk_highfreq */
CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */
CLK_NONE, /* 00 disabled */
CLK_IPG, /* 01 ipg_clk, ~532MHz */
CLK_IPG_HIGH, /* 10 ipg_clk_highfreq */
CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */
};
/*

View file

@ -81,14 +81,14 @@ static const VMStateDescription vmstate_imx_timer_gpt = {
};
static const IMXClk imx_gpt_clocks[] = {
NOCLK, /* 000 No clock source */
CLK_IPG, /* 001 ipg_clk, 532MHz*/
CLK_IPG, /* 010 ipg_clk_highfreq */
NOCLK, /* 011 not defined */
CLK_32k, /* 100 ipg_clk_32k */
NOCLK, /* 101 not defined */
NOCLK, /* 110 not defined */
NOCLK, /* 111 not defined */
CLK_NONE, /* 000 No clock source */
CLK_IPG, /* 001 ipg_clk, 532MHz*/
CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */
CLK_NONE, /* 011 not defined */
CLK_32k, /* 100 ipg_clk_32k */
CLK_NONE, /* 101 not defined */
CLK_NONE, /* 110 not defined */
CLK_NONE, /* 111 not defined */
};
static void imx_gpt_set_freq(IMXGPTState *s)
@ -134,7 +134,7 @@ static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg,
static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
{
uint32_t timeout = GPT_TIMER_MAX;
uint32_t count = 0;
uint32_t count;
long long limit;
if (!(s->cr & GPT_CR_EN)) {
@ -142,20 +142,23 @@ static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
return;
}
/* update the count */
count = imx_gpt_update_count(s);
if (event) {
/* This is a timer event */
if ((s->cr & GPT_CR_FRR) && (s->next_timeout != GPT_TIMER_MAX)) {
/*
* if we are in free running mode and we have not reached
* the GPT_TIMER_MAX limit, then update the count
/*
* This is an event (the ptimer reached 0 and stopped), and the
* timer counter is now equal to s->next_timeout.
*/
if (!(s->cr & GPT_CR_FRR) && (count == s->ocr1)) {
/* We are in restart mode and we crossed the compare channel 1
* value. We need to reset the counter to 0.
*/
count = imx_gpt_update_count(s);
count = s->cnt = s->next_timeout = 0;
} else if (count == GPT_TIMER_MAX) {
/* We reached GPT_TIMER_MAX so we need to rollover */
count = s->cnt = s->next_timeout = 0;
}
} else {
/* not a timer event, then just update the count */
count = imx_gpt_update_count(s);
}
/* now, find the next timeout related to count */

35
include/hw/arm/ast2400.h Normal file
View file

@ -0,0 +1,35 @@
/*
* ASPEED AST2400 SoC
*
* Andrew Jeffery <andrew@aj.id.au>
*
* Copyright 2016 IBM Corp.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#ifndef AST2400_H
#define AST2400_H
#include "hw/arm/arm.h"
#include "hw/intc/aspeed_vic.h"
#include "hw/timer/aspeed_timer.h"
typedef struct AST2400State {
/*< private >*/
DeviceState parent;
/*< public >*/
ARMCPU *cpu;
MemoryRegion iomem;
AspeedVICState vic;
AspeedTimerCtrlState timerctrl;
} AST2400State;
#define TYPE_AST2400 "ast2400"
#define AST2400(obj) OBJECT_CHECK(AST2400State, (obj), TYPE_AST2400)
#define AST2400_SDRAM_BASE 0x40000000
#endif /* AST2400_H */

View file

@ -14,6 +14,9 @@
#include "qemu-common.h"
#include "exec/address-spaces.h"
#include "hw/sysbus.h"
#include "hw/char/bcm2835_aux.h"
#include "hw/display/bcm2835_fb.h"
#include "hw/dma/bcm2835_dma.h"
#include "hw/intc/bcm2835_ic.h"
#include "hw/misc/bcm2835_property.h"
#include "hw/misc/bcm2835_mbox.h"
@ -33,6 +36,9 @@ typedef struct BCM2835PeripheralState {
qemu_irq irq, fiq;
SysBusDevice *uart0;
BCM2835AuxState aux;
BCM2835FBState fb;
BCM2835DMAState dma;
BCM2835ICState ic;
BCM2835PropertyState property;
BCM2835MboxState mboxes;

View file

@ -0,0 +1,33 @@
/*
* Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
* Written by Andrew Baumann
*
* This code is licensed under the GNU GPLv2 and later.
*/
#ifndef BCM2835_AUX_H
#define BCM2835_AUX_H
#include "hw/sysbus.h"
#include "sysemu/char.h"
#define TYPE_BCM2835_AUX "bcm2835-aux"
#define BCM2835_AUX(obj) OBJECT_CHECK(BCM2835AuxState, (obj), TYPE_BCM2835_AUX)
#define BCM2835_AUX_RX_FIFO_LEN 8
typedef struct {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion iomem;
CharDriverState *chr;
qemu_irq irq;
uint8_t read_fifo[BCM2835_AUX_RX_FIFO_LEN];
uint8_t read_pos, read_count;
uint8_t ier, iir;
} BCM2835AuxState;
#endif

View file

@ -0,0 +1,47 @@
/*
* Raspberry Pi emulation (c) 2012 Gregory Estrade
* Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
*
* Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
* Written by Andrew Baumann
*
* This code is licensed under the GNU GPLv2 and later.
*/
#ifndef BCM2835_FB_H
#define BCM2835_FB_H
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "ui/console.h"
#define TYPE_BCM2835_FB "bcm2835-fb"
#define BCM2835_FB(obj) OBJECT_CHECK(BCM2835FBState, (obj), TYPE_BCM2835_FB)
typedef struct {
/*< private >*/
SysBusDevice busdev;
/*< public >*/
uint32_t vcram_base, vcram_size;
MemoryRegion *dma_mr;
AddressSpace dma_as;
MemoryRegion iomem;
MemoryRegionSection fbsection;
QemuConsole *con;
qemu_irq mbox_irq;
bool lock, invalidate, pending;
uint32_t xres, yres;
uint32_t xres_virtual, yres_virtual;
uint32_t xoffset, yoffset;
uint32_t bpp;
uint32_t base, pitch, size;
uint32_t pixo, alpha;
} BCM2835FBState;
void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres,
uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp,
uint32_t *pixo, uint32_t *alpha);
#endif

View file

@ -0,0 +1,47 @@
/*
* Raspberry Pi emulation (c) 2012 Gregory Estrade
* This code is licensed under the GNU GPLv2 and later.
*/
#ifndef BCM2835_DMA_H
#define BCM2835_DMA_H
#include "qemu-common.h"
#include "exec/address-spaces.h"
#include "hw/sysbus.h"
typedef struct {
uint32_t cs;
uint32_t conblk_ad;
uint32_t ti;
uint32_t source_ad;
uint32_t dest_ad;
uint32_t txfr_len;
uint32_t stride;
uint32_t nextconbk;
uint32_t debug;
qemu_irq irq;
} BCM2835DMAChan;
#define TYPE_BCM2835_DMA "bcm2835-dma"
#define BCM2835_DMA(obj) \
OBJECT_CHECK(BCM2835DMAState, (obj), TYPE_BCM2835_DMA)
#define BCM2835_DMA_NCHANS 16
typedef struct {
/*< private >*/
SysBusDevice busdev;
/*< public >*/
MemoryRegion iomem0, iomem15;
MemoryRegion *dma_mr;
AddressSpace dma_as;
BCM2835DMAChan chan[BCM2835_DMA_NCHANS];
uint32_t int_status;
uint32_t enable;
} BCM2835DMAState;
#endif

View file

@ -0,0 +1,48 @@
/*
* ASPEED Interrupt Controller (New)
*
* Andrew Jeffery <andrew@aj.id.au>
*
* Copyright 2016 IBM Corp.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*
* Need to add SVIC and CVIC support
*/
#ifndef ASPEED_VIC_H
#define ASPEED_VIC_H
#include "hw/sysbus.h"
#define TYPE_ASPEED_VIC "aspeed.vic"
#define ASPEED_VIC(obj) OBJECT_CHECK(AspeedVICState, (obj), TYPE_ASPEED_VIC)
#define ASPEED_VIC_NR_IRQS 51
typedef struct AspeedVICState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion iomem;
qemu_irq irq;
qemu_irq fiq;
uint64_t level;
uint64_t raw;
uint64_t select;
uint64_t enable;
uint64_t trigger;
/* 0=edge, 1=level */
uint64_t sense;
/* 0=single-edge, 1=dual-edge */
uint64_t dual_edge;
/* 0=low-sensitive/falling-edge, 1=high-sensitive/rising-edge */
uint64_t event;
} AspeedVICState;
#endif /* ASPEED_VIC_H */

View file

@ -137,7 +137,7 @@ void hmp_info_roms(Monitor *mon, const QDict *qdict);
#define rom_add_blob_fixed(_f, _b, _l, _a) \
rom_add_blob(_f, _b, _l, _l, _a, NULL, NULL, NULL)
#define rom_add_file_mr(_f, _mr, _i) \
rom_add_file(_f, NULL, 0, _i, false, mr)
rom_add_file(_f, NULL, 0, _i, false, _mr)
#define PC_ROM_MIN_VGA 0xc0000
#define PC_ROM_MIN_OPTION 0xc8000

View file

@ -9,6 +9,7 @@
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "net/net.h"
#include "hw/display/bcm2835_fb.h"
#define TYPE_BCM2835_PROPERTY "bcm2835-property"
#define BCM2835_PROPERTY(obj) \
@ -18,13 +19,15 @@ typedef struct {
/*< private >*/
SysBusDevice busdev;
/*< public >*/
MemoryRegion *dma_mr;
AddressSpace dma_as;
MemoryRegion iomem;
qemu_irq mbox_irq;
BCM2835FBState *fbdev;
MACAddr macaddr;
uint32_t board_rev;
uint32_t ram_size;
uint32_t addr;
bool pending;
} BCM2835PropertyState;

197
include/hw/misc/imx6_ccm.h Normal file
View file

@ -0,0 +1,197 @@
/*
* IMX6 Clock Control Module
*
* Copyright (C) 2012 NICTA
* Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef IMX6_CCM_H
#define IMX6_CCM_H
#include "hw/misc/imx_ccm.h"
#include "qemu/bitops.h"
#define CCM_CCR 0
#define CCM_CCDR 1
#define CCM_CSR 2
#define CCM_CCSR 3
#define CCM_CACRR 4
#define CCM_CBCDR 5
#define CCM_CBCMR 6
#define CCM_CSCMR1 7
#define CCM_CSCMR2 8
#define CCM_CSCDR1 9
#define CCM_CS1CDR 10
#define CCM_CS2CDR 11
#define CCM_CDCDR 12
#define CCM_CHSCCDR 13
#define CCM_CSCDR2 14
#define CCM_CSCDR3 15
#define CCM_CDHIPR 18
#define CCM_CTOR 20
#define CCM_CLPCR 21
#define CCM_CISR 22
#define CCM_CIMR 23
#define CCM_CCOSR 24
#define CCM_CGPR 25
#define CCM_CCGR0 26
#define CCM_CCGR1 27
#define CCM_CCGR2 28
#define CCM_CCGR3 29
#define CCM_CCGR4 30
#define CCM_CCGR5 31
#define CCM_CCGR6 32
#define CCM_CMEOR 34
#define CCM_MAX 35
#define CCM_ANALOG_PLL_ARM 0
#define CCM_ANALOG_PLL_ARM_SET 1
#define CCM_ANALOG_PLL_ARM_CLR 2
#define CCM_ANALOG_PLL_ARM_TOG 3
#define CCM_ANALOG_PLL_USB1 4
#define CCM_ANALOG_PLL_USB1_SET 5
#define CCM_ANALOG_PLL_USB1_CLR 6
#define CCM_ANALOG_PLL_USB1_TOG 7
#define CCM_ANALOG_PLL_USB2 8
#define CCM_ANALOG_PLL_USB2_SET 9
#define CCM_ANALOG_PLL_USB2_CLR 10
#define CCM_ANALOG_PLL_USB2_TOG 11
#define CCM_ANALOG_PLL_SYS 12
#define CCM_ANALOG_PLL_SYS_SET 13
#define CCM_ANALOG_PLL_SYS_CLR 14
#define CCM_ANALOG_PLL_SYS_TOG 15
#define CCM_ANALOG_PLL_SYS_SS 16
#define CCM_ANALOG_PLL_SYS_NUM 20
#define CCM_ANALOG_PLL_SYS_DENOM 24
#define CCM_ANALOG_PLL_AUDIO 28
#define CCM_ANALOG_PLL_AUDIO_SET 29
#define CCM_ANALOG_PLL_AUDIO_CLR 30
#define CCM_ANALOG_PLL_AUDIO_TOG 31
#define CCM_ANALOG_PLL_AUDIO_NUM 32
#define CCM_ANALOG_PLL_AUDIO_DENOM 36
#define CCM_ANALOG_PLL_VIDEO 40
#define CCM_ANALOG_PLL_VIDEO_SET 41
#define CCM_ANALOG_PLL_VIDEO_CLR 42
#define CCM_ANALOG_PLL_VIDEO_TOG 44
#define CCM_ANALOG_PLL_VIDEO_NUM 46
#define CCM_ANALOG_PLL_VIDEO_DENOM 48
#define CCM_ANALOG_PLL_MLB 52
#define CCM_ANALOG_PLL_MLB_SET 53
#define CCM_ANALOG_PLL_MLB_CLR 54
#define CCM_ANALOG_PLL_MLB_TOG 55
#define CCM_ANALOG_PLL_ENET 56
#define CCM_ANALOG_PLL_ENET_SET 57
#define CCM_ANALOG_PLL_ENET_CLR 58
#define CCM_ANALOG_PLL_ENET_TOG 59
#define CCM_ANALOG_PFD_480 60
#define CCM_ANALOG_PFD_480_SET 61
#define CCM_ANALOG_PFD_480_CLR 62
#define CCM_ANALOG_PFD_480_TOG 63
#define CCM_ANALOG_PFD_528 64
#define CCM_ANALOG_PFD_528_SET 65
#define CCM_ANALOG_PFD_528_CLR 66
#define CCM_ANALOG_PFD_528_TOG 67
/* PMU registers */
#define PMU_REG_1P1 68
#define PMU_REG_3P0 72
#define PMU_REG_2P5 76
#define PMU_REG_CORE 80
#define CCM_ANALOG_MISC0 84
#define PMU_MISC0 84
#define CCM_ANALOG_MISC0_SET 85
#define CCM_ANALOG_MISC0_CLR 86
#define CCM_ANALOG_MISC0_TOG 87
#define PMU_MISC1 88
#define PMU_MISC1_SET 89
#define PMU_MISC1_CLR 90
#define PMU_MISC1_TOG 91
#define CCM_ANALOG_MISC2 92
#define PMU_MISC2 92
#define CCM_ANALOG_MISC2_SET 93
#define CCM_ANALOG_MISC2_CLR 94
#define CCM_ANALOG_MISC2_TOG 95
#define USB_ANALOG_USB1_VBUS_DETECT 104
#define USB_ANALOG_USB1_VBUS_DETECT_SET 105
#define USB_ANALOG_USB1_VBUS_DETECT_CLR 106
#define USB_ANALOG_USB1_VBUS_DETECT_TOG 107
#define USB_ANALOG_USB1_CHRG_DETECT 108
#define USB_ANALOG_USB1_CHRG_DETECT_SET 109
#define USB_ANALOG_USB1_CHRG_DETECT_CLR 110
#define USB_ANALOG_USB1_CHRG_DETECT_TOG 111
#define USB_ANALOG_USB1_VBUS_DETECT_STAT 112
#define USB_ANALOG_USB1_CHRG_DETECT_STAT 116
#define USB_ANALOG_USB1_MISC 124
#define USB_ANALOG_USB1_MISC_SET 125
#define USB_ANALOG_USB1_MISC_CLR 126
#define USB_ANALOG_USB1_MISC_TOG 127
#define USB_ANALOG_USB2_VBUS_DETECT 128
#define USB_ANALOG_USB2_VBUS_DETECT_SET 129
#define USB_ANALOG_USB2_VBUS_DETECT_CLR 130
#define USB_ANALOG_USB2_VBUS_DETECT_TOG 131
#define USB_ANALOG_USB2_CHRG_DETECT 132
#define USB_ANALOG_USB2_CHRG_DETECT_SET 133
#define USB_ANALOG_USB2_CHRG_DETECT_CLR 134
#define USB_ANALOG_USB2_CHRG_DETECT_TOG 135
#define USB_ANALOG_USB2_VBUS_DETECT_STAT 136
#define USB_ANALOG_USB2_CHRG_DETECT_STAT 140
#define USB_ANALOG_USB2_MISC 148
#define USB_ANALOG_USB2_MISC_SET 149
#define USB_ANALOG_USB2_MISC_CLR 150
#define USB_ANALOG_USB2_MISC_TOG 151
#define USB_ANALOG_DIGPROG 152
#define CCM_ANALOG_MAX 153
/* CCM_CBCMR */
#define PRE_PERIPH_CLK_SEL_SHIFT (18)
#define PRE_PERIPH_CLK_SEL_LENGTH (2)
/* CCM_CBCDR */
#define AHB_PODF_SHIFT (10)
#define AHB_PODF_LENGTH (3)
#define IPG_PODF_SHIFT (8)
#define IPG_PODF_LENGTH (2)
/* CCM_CSCMR1 */
#define PERCLK_PODF_SHIFT (0)
#define PERCLK_PODF_LENGTH (6)
/* CCM_ANALOG_PFD_528 */
#define PFD0_FRAC_SHIFT (0)
#define PFD0_FRAC_LENGTH (6)
#define PFD2_FRAC_SHIFT (16)
#define PFD2_FRAC_LENGTH (6)
/* CCM_ANALOG_PLL_SYS */
#define DIV_SELECT_SHIFT (0)
#define DIV_SELECT_LENGTH (1)
#define CCM_ANALOG_PLL_LOCK (1 << 31);
#define EXTRACT(value, name) extract32(value, name##_SHIFT, name##_LENGTH)
#define TYPE_IMX6_CCM "imx6.ccm"
#define IMX6_CCM(obj) OBJECT_CHECK(IMX6CCMState, (obj), TYPE_IMX6_CCM)
typedef struct IMX6CCMState {
/* <private> */
IMXCCMState parent_obj;
/* <public> */
MemoryRegion container;
MemoryRegion ioccm;
MemoryRegion ioanalog;
uint32_t ccm[CCM_MAX];
uint32_t analog[CCM_ANALOG_MAX];
} IMX6CCMState;
#endif /* IMX6_CCM_H */

View file

@ -43,15 +43,9 @@ typedef struct IMXCCMState {
} IMXCCMState;
typedef enum {
NOCLK,
CLK_MPLL,
CLK_UPLL,
CLK_MCU,
CLK_HSP,
CLK_MAX,
CLK_AHB,
CLK_NONE,
CLK_IPG,
CLK_PER,
CLK_IPG_HIGH,
CLK_32k
} IMXClk;

View file

@ -0,0 +1,59 @@
/*
* ASPEED AST2400 Timer
*
* Andrew Jeffery <andrew@aj.id.au>
*
* Copyright (C) 2016 IBM Corp.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef ASPEED_TIMER_H
#define ASPEED_TIMER_H
#include "hw/ptimer.h"
#define ASPEED_TIMER(obj) \
OBJECT_CHECK(AspeedTimerCtrlState, (obj), TYPE_ASPEED_TIMER);
#define TYPE_ASPEED_TIMER "aspeed.timer"
#define ASPEED_TIMER_NR_TIMERS 8
typedef struct AspeedTimer {
qemu_irq irq;
uint8_t id;
/**
* Track the line level as the ASPEED timers implement edge triggered
* interrupts, signalling with both the rising and falling edge.
*/
int32_t level;
ptimer_state *timer;
uint32_t reload;
uint32_t match[2];
} AspeedTimer;
typedef struct AspeedTimerCtrlState {
/*< private >*/
SysBusDevice parent;
/*< public >*/
MemoryRegion iomem;
uint32_t ctrl;
uint32_t ctrl2;
AspeedTimer timers[ASPEED_TIMER_NR_TIMERS];
} AspeedTimerCtrlState;
#endif /* ASPEED_TIMER_H */

View file

@ -7237,7 +7237,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
CPUState *cs = CPU(cpu);
/* Read an LPAE long-descriptor translation table. */
MMUFaultType fault_type = translation_fault;
uint32_t level = 1;
uint32_t level;
uint32_t epd = 0;
int32_t t0sz, t1sz;
uint32_t tg;
@ -7248,7 +7248,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
target_ulong page_size;
uint32_t attrs;
int32_t stride = 9;
int32_t va_size = 32;
int32_t va_size;
int inputsize;
int32_t tbi = 0;
TCR *tcr = regime_tcr(env, mmu_idx);
@ -7264,6 +7264,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
* support for those page table walks.
*/
if (arm_el_is_aa64(env, el)) {
level = 0;
va_size = 64;
if (el > 1) {
if (mmu_idx != ARMMMUIdx_S2NS) {
@ -7285,6 +7286,8 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
ttbr1_valid = false;
}
} else {
level = 1;
va_size = 32;
/* There is no TTBR1 for EL2 */
if (el == 2) {
ttbr1_valid = false;
@ -7407,27 +7410,26 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
/* For stage 2 translations the starting level is specified by the
* VTCR_EL2.SL0 field (whose interpretation depends on the page size)
*/
int startlevel = extract32(tcr->raw_tcr, 6, 2);
uint32_t sl0 = extract32(tcr->raw_tcr, 6, 2);
uint32_t startlevel;
bool ok;
if (va_size == 32 || stride == 9) {
/* AArch32 or 4KB pages */
level = 2 - startlevel;
startlevel = 2 - sl0;
} else {
/* 16KB or 64KB pages */
level = 3 - startlevel;
startlevel = 3 - sl0;
}
/* Check that the starting level is valid. */
ok = check_s2_mmu_setup(cpu, va_size == 64, level, inputsize, stride);
ok = check_s2_mmu_setup(cpu, va_size == 64, startlevel,
inputsize, stride);
if (!ok) {
/* AArch64 reports these as level 0 faults.
* AArch32 reports these as level 1 faults.
*/
level = va_size == 64 ? 0 : 1;
fault_type = translation_fault;
goto do_fault;
}
level = startlevel;
}
/* Clear the vaddr bits which aren't part of the within-region address,

View file

@ -77,6 +77,9 @@ DEF_HELPER_1(exception_return, void, env)
DEF_HELPER_2(get_r13_banked, i32, env, i32)
DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
DEF_HELPER_3(mrs_banked, i32, env, i32, i32)
DEF_HELPER_4(msr_banked, void, env, i32, i32, i32)
DEF_HELPER_2(get_user_reg, i32, env, i32)
DEF_HELPER_3(set_user_reg, void, env, i32, i32)

View file

@ -494,6 +494,126 @@ uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode)
}
}
static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode,
uint32_t regno)
{
/* Raise an exception if the requested access is one of the UNPREDICTABLE
* cases; otherwise return. This broadly corresponds to the pseudocode
* BankedRegisterAccessValid() and SPSRAccessValid(),
* except that we have already handled some cases at translate time.
*/
int curmode = env->uncached_cpsr & CPSR_M;
if (curmode == tgtmode) {
goto undef;
}
if (tgtmode == ARM_CPU_MODE_USR) {
switch (regno) {
case 8 ... 12:
if (curmode != ARM_CPU_MODE_FIQ) {
goto undef;
}
break;
case 13:
if (curmode == ARM_CPU_MODE_SYS) {
goto undef;
}
break;
case 14:
if (curmode == ARM_CPU_MODE_HYP || curmode == ARM_CPU_MODE_SYS) {
goto undef;
}
break;
default:
break;
}
}
if (tgtmode == ARM_CPU_MODE_HYP) {
switch (regno) {
case 17: /* ELR_Hyp */
if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) {
goto undef;
}
break;
default:
if (curmode != ARM_CPU_MODE_MON) {
goto undef;
}
break;
}
}
return;
undef:
raise_exception(env, EXCP_UDEF, syn_uncategorized(),
exception_target_el(env));
}
void HELPER(msr_banked)(CPUARMState *env, uint32_t value, uint32_t tgtmode,
uint32_t regno)
{
msr_mrs_banked_exc_checks(env, tgtmode, regno);
switch (regno) {
case 16: /* SPSRs */
env->banked_spsr[bank_number(tgtmode)] = value;
break;
case 17: /* ELR_Hyp */
env->elr_el[2] = value;
break;
case 13:
env->banked_r13[bank_number(tgtmode)] = value;
break;
case 14:
env->banked_r14[bank_number(tgtmode)] = value;
break;
case 8 ... 12:
switch (tgtmode) {
case ARM_CPU_MODE_USR:
env->usr_regs[regno - 8] = value;
break;
case ARM_CPU_MODE_FIQ:
env->fiq_regs[regno - 8] = value;
break;
default:
g_assert_not_reached();
}
break;
default:
g_assert_not_reached();
}
}
uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno)
{
msr_mrs_banked_exc_checks(env, tgtmode, regno);
switch (regno) {
case 16: /* SPSRs */
return env->banked_spsr[bank_number(tgtmode)];
case 17: /* ELR_Hyp */
return env->elr_el[2];
case 13:
return env->banked_r13[bank_number(tgtmode)];
case 14:
return env->banked_r14[bank_number(tgtmode)];
case 8 ... 12:
switch (tgtmode) {
case ARM_CPU_MODE_USR:
return env->usr_regs[regno - 8];
case ARM_CPU_MODE_FIQ:
return env->fiq_regs[regno - 8];
default:
g_assert_not_reached();
}
default:
g_assert_not_reached();
}
}
void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome,
uint32_t isread)
{

View file

@ -4160,6 +4160,195 @@ static int gen_set_psr_im(DisasContext *s, uint32_t mask, int spsr, uint32_t val
return gen_set_psr(s, mask, spsr, tmp);
}
static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn,
int *tgtmode, int *regno)
{
/* Decode the r and sysm fields of MSR/MRS banked accesses into
* the target mode and register number, and identify the various
* unpredictable cases.
* MSR (banked) and MRS (banked) are CONSTRAINED UNPREDICTABLE if:
* + executed in user mode
* + using R15 as the src/dest register
* + accessing an unimplemented register
* + accessing a register that's inaccessible at current PL/security state*
* + accessing a register that you could access with a different insn
* We choose to UNDEF in all these cases.
* Since we don't know which of the various AArch32 modes we are in
* we have to defer some checks to runtime.
* Accesses to Monitor mode registers from Secure EL1 (which implies
* that EL3 is AArch64) must trap to EL3.
*
* If the access checks fail this function will emit code to take
* an exception and return false. Otherwise it will return true,
* and set *tgtmode and *regno appropriately.
*/
int exc_target = default_exception_el(s);
/* These instructions are present only in ARMv8, or in ARMv7 with the
* Virtualization Extensions.
*/
if (!arm_dc_feature(s, ARM_FEATURE_V8) &&
!arm_dc_feature(s, ARM_FEATURE_EL2)) {
goto undef;
}
if (IS_USER(s) || rn == 15) {
goto undef;
}
/* The table in the v8 ARM ARM section F5.2.3 describes the encoding
* of registers into (r, sysm).
*/
if (r) {
/* SPSRs for other modes */
switch (sysm) {
case 0xe: /* SPSR_fiq */
*tgtmode = ARM_CPU_MODE_FIQ;
break;
case 0x10: /* SPSR_irq */
*tgtmode = ARM_CPU_MODE_IRQ;
break;
case 0x12: /* SPSR_svc */
*tgtmode = ARM_CPU_MODE_SVC;
break;
case 0x14: /* SPSR_abt */
*tgtmode = ARM_CPU_MODE_ABT;
break;
case 0x16: /* SPSR_und */
*tgtmode = ARM_CPU_MODE_UND;
break;
case 0x1c: /* SPSR_mon */
*tgtmode = ARM_CPU_MODE_MON;
break;
case 0x1e: /* SPSR_hyp */
*tgtmode = ARM_CPU_MODE_HYP;
break;
default: /* unallocated */
goto undef;
}
/* We arbitrarily assign SPSR a register number of 16. */
*regno = 16;
} else {
/* general purpose registers for other modes */
switch (sysm) {
case 0x0 ... 0x6: /* 0b00xxx : r8_usr ... r14_usr */
*tgtmode = ARM_CPU_MODE_USR;
*regno = sysm + 8;
break;
case 0x8 ... 0xe: /* 0b01xxx : r8_fiq ... r14_fiq */
*tgtmode = ARM_CPU_MODE_FIQ;
*regno = sysm;
break;
case 0x10 ... 0x11: /* 0b1000x : r14_irq, r13_irq */
*tgtmode = ARM_CPU_MODE_IRQ;
*regno = sysm & 1 ? 13 : 14;
break;
case 0x12 ... 0x13: /* 0b1001x : r14_svc, r13_svc */
*tgtmode = ARM_CPU_MODE_SVC;
*regno = sysm & 1 ? 13 : 14;
break;
case 0x14 ... 0x15: /* 0b1010x : r14_abt, r13_abt */
*tgtmode = ARM_CPU_MODE_ABT;
*regno = sysm & 1 ? 13 : 14;
break;
case 0x16 ... 0x17: /* 0b1011x : r14_und, r13_und */
*tgtmode = ARM_CPU_MODE_UND;
*regno = sysm & 1 ? 13 : 14;
break;
case 0x1c ... 0x1d: /* 0b1110x : r14_mon, r13_mon */
*tgtmode = ARM_CPU_MODE_MON;
*regno = sysm & 1 ? 13 : 14;
break;
case 0x1e ... 0x1f: /* 0b1111x : elr_hyp, r13_hyp */
*tgtmode = ARM_CPU_MODE_HYP;
/* Arbitrarily pick 17 for ELR_Hyp (which is not a banked LR!) */
*regno = sysm & 1 ? 13 : 17;
break;
default: /* unallocated */
goto undef;
}
}
/* Catch the 'accessing inaccessible register' cases we can detect
* at translate time.
*/
switch (*tgtmode) {
case ARM_CPU_MODE_MON:
if (!arm_dc_feature(s, ARM_FEATURE_EL3) || s->ns) {
goto undef;
}
if (s->current_el == 1) {
/* If we're in Secure EL1 (which implies that EL3 is AArch64)
* then accesses to Mon registers trap to EL3
*/
exc_target = 3;
goto undef;
}
break;
case ARM_CPU_MODE_HYP:
/* Note that we can forbid accesses from EL2 here because they
* must be from Hyp mode itself
*/
if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 3) {
goto undef;
}
break;
default:
break;
}
return true;
undef:
/* If we get here then some access check did not pass */
gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), exc_target);
return false;
}
static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn)
{
TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno;
int tgtmode = 0, regno = 0;
if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, &regno)) {
return;
}
/* Sync state because msr_banked() can raise exceptions */
gen_set_condexec(s);
gen_set_pc_im(s, s->pc - 4);
tcg_reg = load_reg(s, rn);
tcg_tgtmode = tcg_const_i32(tgtmode);
tcg_regno = tcg_const_i32(regno);
gen_helper_msr_banked(cpu_env, tcg_reg, tcg_tgtmode, tcg_regno);
tcg_temp_free_i32(tcg_tgtmode);
tcg_temp_free_i32(tcg_regno);
tcg_temp_free_i32(tcg_reg);
s->is_jmp = DISAS_UPDATE;
}
static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn)
{
TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno;
int tgtmode = 0, regno = 0;
if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, &regno)) {
return;
}
/* Sync state because mrs_banked() can raise exceptions */
gen_set_condexec(s);
gen_set_pc_im(s, s->pc - 4);
tcg_reg = tcg_temp_new_i32();
tcg_tgtmode = tcg_const_i32(tgtmode);
tcg_regno = tcg_const_i32(regno);
gen_helper_mrs_banked(tcg_reg, cpu_env, tcg_tgtmode, tcg_regno);
tcg_temp_free_i32(tcg_tgtmode);
tcg_temp_free_i32(tcg_regno);
store_reg(s, rn, tcg_reg);
s->is_jmp = DISAS_UPDATE;
}
/* Generate an old-style exception return. Marks pc as dead. */
static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
{
@ -8022,7 +8211,26 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
sh = (insn >> 4) & 0xf;
rm = insn & 0xf;
switch (sh) {
case 0x0: /* move program status register */
case 0x0: /* MSR, MRS */
if (insn & (1 << 9)) {
/* MSR (banked) and MRS (banked) */
int sysm = extract32(insn, 16, 4) |
(extract32(insn, 8, 1) << 4);
int r = extract32(insn, 22, 1);
if (op1 & 1) {
/* MSR (banked) */
gen_msr_banked(s, r, sysm, rm);
} else {
/* MRS (banked) */
int rd = extract32(insn, 12, 4);
gen_mrs_banked(s, r, sysm, rd);
}
break;
}
/* MSR, MRS (for PSRs) */
if (op1 & 1) {
/* PSR = reg */
tmp = load_reg(s, rm);
@ -10133,6 +10341,18 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
if (arm_dc_feature(s, ARM_FEATURE_M)) {
goto illegal_op;
}
if (extract32(insn, 5, 1)) {
/* MSR (banked) */
int sysm = extract32(insn, 8, 4) |
(extract32(insn, 4, 1) << 4);
int r = op & 1;
gen_msr_banked(s, r, sysm, rm);
break;
}
/* MSR (for PSRs) */
tmp = load_reg(s, rn);
if (gen_set_psr(s,
msr_mask(s, (insn >> 8) & 0xf, op == 1),
@ -10205,7 +10425,17 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
tcg_gen_subi_i32(tmp, tmp, insn & 0xff);
gen_exception_return(s, tmp);
break;
case 6: /* mrs cpsr. */
case 6: /* MRS */
if (extract32(insn, 5, 1)) {
/* MRS (banked) */
int sysm = extract32(insn, 16, 4) |
(extract32(insn, 4, 1) << 4);
gen_mrs_banked(s, 0, sysm, rd);
break;
}
/* mrs cpsr */
tmp = tcg_temp_new_i32();
if (arm_dc_feature(s, ARM_FEATURE_M)) {
addr = tcg_const_i32(insn & 0xff);
@ -10216,7 +10446,17 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
}
store_reg(s, rd, tmp);
break;
case 7: /* mrs spsr. */
case 7: /* MRS */
if (extract32(insn, 5, 1)) {
/* MRS (banked) */
int sysm = extract32(insn, 16, 4) |
(extract32(insn, 4, 1) << 4);
gen_mrs_banked(s, 1, sysm, rd);
break;
}
/* mrs spsr. */
/* Not accessible in user mode. */
if (IS_USER(s) || arm_dc_feature(s, ARM_FEATURE_M)) {
goto illegal_op;

View file

@ -1892,3 +1892,19 @@ qio_channel_command_new_pid(void *ioc, int writefd, int readfd, int pid) "Comman
qio_channel_command_new_spawn(void *ioc, const char *binary, int flags) "Command new spawn ioc=%p binary=%s flags=%d"
qio_channel_command_abort(void *ioc, int pid) "Command abort ioc=%p pid=%d"
qio_channel_command_wait(void *ioc, int pid, int ret, int status) "Command abort ioc=%p pid=%d ret=%d status=%d"
# hw/timer/aspeed_timer.c
aspeed_timer_ctrl_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d"
aspeed_timer_ctrl_external_clock(uint8_t i, bool enable) "Timer %" PRIu8 ": %d"
aspeed_timer_ctrl_overflow_interrupt(uint8_t i, bool enable) "Timer %" PRIu8 ": %d"
aspeed_timer_ctrl_pulse_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d"
aspeed_timer_set_ctrl2(uint32_t value) "Value: 0x%" PRIx32
aspeed_timer_set_value(int timer, int reg, uint32_t value) "Timer %d register %d: 0x%" PRIx32
aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value) "From 0x%" PRIx64 ": of size %u: 0x%" PRIx64
# hw/intc/aspeed_vic.c
aspeed_vic_set_irq(int irq, int level) "Enabling IRQ %d: %d"
aspeed_vic_update_fiq(int flags) "Raising FIQ: %d"
aspeed_vic_update_irq(int flags) "Raising IRQ: %d"
aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32
aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32