mirror of https://github.com/xemu-project/xemu.git
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:
commit
d1f8764099
|
@ -110,3 +110,4 @@ CONFIG_IOH3420=y
|
|||
CONFIG_I82801B11=y
|
||||
CONFIG_ACPI=y
|
||||
CONFIG_SMBIOS=y
|
||||
CONFIG_ASPEED_SOC=y
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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 = {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
|
|
|
@ -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 = α
|
||||
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()
|
||||
};
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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 */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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, ®no)) {
|
||||
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, ®no)) {
|
||||
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;
|
||||
|
|
16
trace-events
16
trace-events
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue