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

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

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

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

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2016-03-16 17:43:37 +00:00
46 changed files with 4114 additions and 140 deletions
+1
View File
@@ -110,3 +110,4 @@ CONFIG_IOH3420=y
CONFIG_I82801B11=y
CONFIG_ACPI=y
CONFIG_SMBIOS=y
CONFIG_ASPEED_SOC=y
+1
View File
@@ -16,3 +16,4 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o
+137
View File
@@ -0,0 +1,137 @@
/*
* AST2400 SoC
*
* Andrew Jeffery <andrew@aj.id.au>
* Jeremy Kerr <jk@ozlabs.org>
*
* Copyright 2016 IBM Corp.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "exec/address-spaces.h"
#include "hw/arm/ast2400.h"
#include "hw/char/serial.h"
#define AST2400_UART_5_BASE 0x00184000
#define AST2400_IOMEM_SIZE 0x00200000
#define AST2400_IOMEM_BASE 0x1E600000
#define AST2400_VIC_BASE 0x1E6C0000
#define AST2400_TIMER_BASE 0x1E782000
static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
/*
* IO handlers: simply catch any reads/writes to IO addresses that aren't
* handled by a device mapping.
*/
static uint64_t ast2400_io_read(void *p, hwaddr offset, unsigned size)
{
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n",
__func__, offset, size);
return 0;
}
static void ast2400_io_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n",
__func__, offset, value, size);
}
static const MemoryRegionOps ast2400_io_ops = {
.read = ast2400_io_read,
.write = ast2400_io_write,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void ast2400_init(Object *obj)
{
AST2400State *s = AST2400(obj);
s->cpu = cpu_arm_init("arm926");
object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC);
object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL);
qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default());
object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER);
object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL);
qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default());
}
static void ast2400_realize(DeviceState *dev, Error **errp)
{
int i;
AST2400State *s = AST2400(dev);
Error *err = NULL;
/* IO space */
memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL,
"ast2400.io", AST2400_IOMEM_SIZE);
memory_region_add_subregion_overlap(get_system_memory(), AST2400_IOMEM_BASE,
&s->iomem, -1);
/* VIC */
object_property_set_bool(OBJECT(&s->vic), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, AST2400_VIC_BASE);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0,
qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1,
qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ));
/* Timer */
object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, AST2400_TIMER_BASE);
for (i = 0; i < ARRAY_SIZE(timer_irqs); i++) {
qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->vic), timer_irqs[i]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
}
/* UART - attach an 8250 to the IO space as our UART5 */
if (serial_hds[0]) {
qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]);
serial_mm_init(&s->iomem, AST2400_UART_5_BASE, 2,
uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN);
}
}
static void ast2400_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = ast2400_realize;
/*
* Reason: creates an ARM CPU, thus use after free(), see
* arm_cpu_class_init()
*/
dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo ast2400_type_info = {
.name = TYPE_AST2400,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AST2400State),
.instance_init = ast2400_init,
.class_init = ast2400_class_init,
};
static void ast2400_register_types(void)
{
type_register_static(&ast2400_type_info);
}
type_init(ast2400_register_types)
+100 -3
View File
@@ -12,6 +12,7 @@
#include "hw/arm/bcm2835_peripherals.h"
#include "hw/misc/bcm2835_mbox_defs.h"
#include "hw/arm/raspi_platform.h"
#include "sysemu/char.h"
/* Peripheral base address on the VC (GPU) system bus */
#define BCM2835_VC_PERI_BASE 0x7e000000
@@ -48,6 +49,11 @@ static void bcm2835_peripherals_init(Object *obj)
object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL);
qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default());
/* AUX / UART1 */
object_initialize(&s->aux, sizeof(s->aux), TYPE_BCM2835_AUX);
object_property_add_child(obj, "aux", OBJECT(&s->aux), NULL);
qdev_set_parent_bus(DEVICE(&s->aux), sysbus_get_default());
/* Mailboxes */
object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX);
object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL);
@@ -56,6 +62,16 @@ static void bcm2835_peripherals_init(Object *obj)
object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr",
OBJECT(&s->mbox_mr), &error_abort);
/* Framebuffer */
object_initialize(&s->fb, sizeof(s->fb), TYPE_BCM2835_FB);
object_property_add_child(obj, "fb", OBJECT(&s->fb), NULL);
object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size",
&error_abort);
qdev_set_parent_bus(DEVICE(&s->fb), sysbus_get_default());
object_property_add_const_link(OBJECT(&s->fb), "dma-mr",
OBJECT(&s->gpu_bus_mr), &error_abort);
/* Property channel */
object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY);
object_property_add_child(obj, "property", OBJECT(&s->property), NULL);
@@ -63,6 +79,8 @@ static void bcm2835_peripherals_init(Object *obj)
"board-rev", &error_abort);
qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default());
object_property_add_const_link(OBJECT(&s->property), "fb",
OBJECT(&s->fb), &error_abort);
object_property_add_const_link(OBJECT(&s->property), "dma-mr",
OBJECT(&s->gpu_bus_mr), &error_abort);
@@ -70,6 +88,14 @@ static void bcm2835_peripherals_init(Object *obj)
object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default());
/* DMA Channels */
object_initialize(&s->dma, sizeof(s->dma), TYPE_BCM2835_DMA);
object_property_add_child(obj, "dma", OBJECT(&s->dma), NULL);
qdev_set_parent_bus(DEVICE(&s->dma), sysbus_get_default());
object_property_add_const_link(OBJECT(&s->dma), "dma-mr",
OBJECT(&s->gpu_bus_mr), &error_abort);
}
static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
@@ -78,7 +104,8 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
Object *obj;
MemoryRegion *ram;
Error *err = NULL;
uint32_t ram_size;
uint32_t ram_size, vcram_size;
CharDriverState *chr;
int n;
obj = object_property_get_link(OBJECT(dev), "ram", &err);
@@ -131,6 +158,29 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
INTERRUPT_UART));
/* AUX / UART1 */
/* TODO: don't call qemu_char_get_next_serial() here, instead set
* chardev properties for each uart at the board level, once pl011
* (uart0) has been updated to avoid qemu_char_get_next_serial()
*/
chr = qemu_char_get_next_serial();
if (chr == NULL) {
chr = qemu_chr_new("bcm2835.uart1", "null", NULL);
}
qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr);
object_property_set_bool(OBJECT(&s->aux), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->peri_mr, UART1_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0,
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
INTERRUPT_AUX));
/* Mailboxes */
object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err);
if (err) {
@@ -144,13 +194,33 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
INTERRUPT_ARM_MAILBOX));
/* Property channel */
object_property_set_int(OBJECT(&s->property), ram_size, "ram-size", &err);
/* Framebuffer */
vcram_size = (uint32_t)object_property_get_int(OBJECT(s), "vcram-size",
&err);
if (err) {
error_propagate(errp, err);
return;
}
object_property_set_int(OBJECT(&s->fb), ram_size - vcram_size,
"vcram-base", &err);
if (err) {
error_propagate(errp, err);
return;
}
object_property_set_bool(OBJECT(&s->fb), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->mbox_mr, MBOX_CHAN_FB << MBOX_AS_CHAN_SHIFT,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->fb), 0));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0,
qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB));
/* Property channel */
object_property_set_bool(OBJECT(&s->property), true, "realized", &err);
if (err) {
error_propagate(errp, err);
@@ -171,6 +241,13 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
return;
}
object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk",
&err);
if (err) {
error_propagate(errp, err);
return;
}
object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err);
if (err) {
error_propagate(errp, err);
@@ -189,6 +266,24 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
return;
}
/* DMA Channels */
object_property_set_bool(OBJECT(&s->dma), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->peri_mr, DMA_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 0));
memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1));
for (n = 0; n <= 12; n++) {
sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n,
qdev_get_gpio_in_named(DEVICE(&s->ic),
BCM2835_IC_GPU_IRQ,
INTERRUPT_DMA0 + n));
}
}
static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
@@ -196,6 +291,8 @@ static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = bcm2835_peripherals_realize;
/* Reason: realize() method uses qemu_char_get_next_serial() */
dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo bcm2835_peripherals_type_info = {
+2
View File
@@ -42,6 +42,8 @@ static void bcm2836_init(Object *obj)
&error_abort);
object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals),
"board-rev", &error_abort);
object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals),
"vcram-size", &error_abort);
qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default());
}
+1
View File
@@ -291,6 +291,7 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data)
* arm_cpu_class_init()
*/
dc->cannot_destroy_with_object_finalize_yet = true;
dc->desc = "i.MX25 SOC";
}
static const TypeInfo fsl_imx25_type_info = {
+1
View File
@@ -265,6 +265,7 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data)
* arm_cpu_class_init()
*/
dc->cannot_destroy_with_object_finalize_yet = true;
dc->desc = "i.MX31 SOC";
}
static const TypeInfo fsl_imx31_type_info = {
+65
View File
@@ -0,0 +1,65 @@
/*
* OpenPOWER Palmetto BMC
*
* Andrew Jeffery <andrew@aj.id.au>
*
* Copyright 2016 IBM Corp.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "exec/address-spaces.h"
#include "hw/arm/arm.h"
#include "hw/arm/ast2400.h"
#include "hw/boards.h"
static struct arm_boot_info palmetto_bmc_binfo = {
.loader_start = AST2400_SDRAM_BASE,
.board_id = 0,
.nb_cpus = 1,
};
typedef struct PalmettoBMCState {
AST2400State soc;
MemoryRegion ram;
} PalmettoBMCState;
static void palmetto_bmc_init(MachineState *machine)
{
PalmettoBMCState *bmc;
bmc = g_new0(PalmettoBMCState, 1);
object_initialize(&bmc->soc, (sizeof(bmc->soc)), TYPE_AST2400);
object_property_add_child(OBJECT(machine), "soc", OBJECT(&bmc->soc),
&error_abort);
memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size);
memory_region_add_subregion(get_system_memory(), AST2400_SDRAM_BASE,
&bmc->ram);
object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram),
&error_abort);
object_property_set_bool(OBJECT(&bmc->soc), true, "realized",
&error_abort);
palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
palmetto_bmc_binfo.ram_size = ram_size;
arm_load_kernel(ARM_CPU(first_cpu), &palmetto_bmc_binfo);
}
static void palmetto_bmc_machine_init(MachineClass *mc)
{
mc->desc = "OpenPOWER Palmetto BMC";
mc->init = palmetto_bmc_init;
mc->max_cpus = 1;
mc->no_sdcard = 1;
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->no_sdcard = 1;
mc->no_parallel = 1;
}
DEFINE_MACHINE("palmetto-bmc", palmetto_bmc_machine_init);
+5 -7
View File
@@ -113,6 +113,7 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size)
static void raspi2_init(MachineState *machine)
{
RasPiState *s = g_new0(RasPiState, 1);
uint32_t vcram_size;
DriveInfo *di;
BlockBackend *blk;
BusState *bus;
@@ -149,7 +150,9 @@ static void raspi2_init(MachineState *machine)
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
setup_boot(machine, 2, machine->ram_size);
vcram_size = object_property_get_int(OBJECT(&s->soc), "vcram-size",
&error_abort);
setup_boot(machine, 2, machine->ram_size - vcram_size);
}
static void raspi2_machine_init(MachineClass *mc)
@@ -161,11 +164,6 @@ static void raspi2_machine_init(MachineClass *mc)
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->max_cpus = BCM2836_NCPUS;
/* XXX: Temporary restriction in RAM size from the full 1GB. Since
* we do not yet support the framebuffer / GPU, we need to limit
* RAM usable by the OS to sit below the peripherals.
*/
mc->default_ram_size = 0x3F000000; /* BCM2836_PERI_BASE */
mc->default_ram_size = 1024 * 1024 * 1024;
};
DEFINE_MACHINE("raspi2", raspi2_machine_init)
+38 -19
View File
@@ -1345,7 +1345,32 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
}
}
static void virt_instance_init(Object *obj)
static void virt_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->init = machvirt_init;
/* Start max_cpus at the maximum QEMU supports. We'll further restrict
* it later in machvirt_init, where we have more information about the
* configuration of the particular instance.
*/
mc->max_cpus = MAX_CPUMASK_BITS;
mc->has_dynamic_sysbus = true;
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
mc->pci_allow_0_address = true;
}
static const TypeInfo virt_machine_info = {
.name = TYPE_VIRT_MACHINE,
.parent = TYPE_MACHINE,
.abstract = true,
.instance_size = sizeof(VirtMachineState),
.class_size = sizeof(VirtMachineClass),
.class_init = virt_machine_class_init,
};
static void virt_2_6_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
@@ -1378,34 +1403,28 @@ static void virt_instance_init(Object *obj)
"Valid values are 2, 3 and host", NULL);
}
static void virt_class_init(ObjectClass *oc, void *data)
static void virt_2_6_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
static GlobalProperty compat_props[] = {
{ /* end of list */ }
};
mc->desc = "ARM Virtual Machine",
mc->init = machvirt_init;
/* Start max_cpus at the maximum QEMU supports. We'll further restrict
* it later in machvirt_init, where we have more information about the
* configuration of the particular instance.
*/
mc->max_cpus = MAX_CPUMASK_BITS;
mc->has_dynamic_sysbus = true;
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
mc->pci_allow_0_address = true;
mc->desc = "QEMU 2.6 ARM Virtual Machine";
mc->alias = "virt";
mc->compat_props = compat_props;
}
static const TypeInfo machvirt_info = {
.name = TYPE_VIRT_MACHINE,
.parent = TYPE_MACHINE,
.instance_size = sizeof(VirtMachineState),
.instance_init = virt_instance_init,
.class_size = sizeof(VirtMachineClass),
.class_init = virt_class_init,
.name = MACHINE_TYPE_NAME("virt-2.6"),
.parent = TYPE_VIRT_MACHINE,
.instance_init = virt_2_6_instance_init,
.class_init = virt_2_6_class_init,
};
static void machvirt_machine_init(void)
{
type_register_static(&virt_machine_info);
type_register_static(&machvirt_info);
}
+1
View File
@@ -16,6 +16,7 @@ obj-$(CONFIG_SH4) += sh_serial.o
obj-$(CONFIG_PSERIES) += spapr_vty.o
obj-$(CONFIG_DIGIC) += digic-uart.o
obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
obj-$(CONFIG_RASPI) += bcm2835_aux.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
+316
View File
@@ -0,0 +1,316 @@
/*
* BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
* Copyright (c) 2015, Microsoft
* Written by Andrew Baumann
* Based on pl011.c, copyright terms below:
*
* Arm PrimeCell PL011 UART
*
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the GPL.
*
* At present only the core UART functions (data path for tx/rx) are
* implemented. The following features/registers are unimplemented:
* - Line/modem control
* - Scratch register
* - Extra control
* - Baudrate
* - SPI interfaces
*/
#include "qemu/osdep.h"
#include "hw/char/bcm2835_aux.h"
#define AUX_IRQ 0x0
#define AUX_ENABLES 0x4
#define AUX_MU_IO_REG 0x40
#define AUX_MU_IER_REG 0x44
#define AUX_MU_IIR_REG 0x48
#define AUX_MU_LCR_REG 0x4c
#define AUX_MU_MCR_REG 0x50
#define AUX_MU_LSR_REG 0x54
#define AUX_MU_MSR_REG 0x58
#define AUX_MU_SCRATCH 0x5c
#define AUX_MU_CNTL_REG 0x60
#define AUX_MU_STAT_REG 0x64
#define AUX_MU_BAUD_REG 0x68
/* bits in IER/IIR registers */
#define TX_INT 0x1
#define RX_INT 0x2
static void bcm2835_aux_update(BCM2835AuxState *s)
{
/* signal an interrupt if either:
* 1. rx interrupt is enabled and we have a non-empty rx fifo, or
* 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
*/
s->iir = 0;
if ((s->ier & RX_INT) && s->read_count != 0) {
s->iir |= RX_INT;
}
if (s->ier & TX_INT) {
s->iir |= TX_INT;
}
qemu_set_irq(s->irq, s->iir != 0);
}
static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
{
BCM2835AuxState *s = opaque;
uint32_t c, res;
switch (offset) {
case AUX_IRQ:
return s->iir != 0;
case AUX_ENABLES:
return 1; /* mini UART permanently enabled */
case AUX_MU_IO_REG:
/* "DLAB bit set means access baudrate register" is NYI */
c = s->read_fifo[s->read_pos];
if (s->read_count > 0) {
s->read_count--;
if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
s->read_pos = 0;
}
}
if (s->chr) {
qemu_chr_accept_input(s->chr);
}
bcm2835_aux_update(s);
return c;
case AUX_MU_IER_REG:
/* "DLAB bit set means access baudrate register" is NYI */
return 0xc0 | s->ier; /* FIFO enables always read 1 */
case AUX_MU_IIR_REG:
res = 0xc0; /* FIFO enables */
/* The spec is unclear on what happens when both tx and rx
* interrupts are active, besides that this cannot occur. At
* present, we choose to prioritise the rx interrupt, since
* the tx fifo is always empty. */
if (s->read_count != 0) {
res |= 0x4;
} else {
res |= 0x2;
}
if (s->iir == 0) {
res |= 0x1;
}
return res;
case AUX_MU_LCR_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
return 0;
case AUX_MU_MCR_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
return 0;
case AUX_MU_LSR_REG:
res = 0x60; /* tx idle, empty */
if (s->read_count != 0) {
res |= 0x1;
}
return res;
case AUX_MU_MSR_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
return 0;
case AUX_MU_SCRATCH:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
return 0;
case AUX_MU_CNTL_REG:
return 0x3; /* tx, rx enabled */
case AUX_MU_STAT_REG:
res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
if (s->read_count > 0) {
res |= 0x1; /* data in input buffer */
assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
}
return res;
case AUX_MU_BAUD_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
return 0;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
return 0;
}
}
static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
BCM2835AuxState *s = opaque;
unsigned char ch;
switch (offset) {
case AUX_ENABLES:
if (value != 1) {
qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI "
"or disable UART\n", __func__);
}
break;
case AUX_MU_IO_REG:
/* "DLAB bit set means access baudrate register" is NYI */
ch = value;
if (s->chr) {
qemu_chr_fe_write(s->chr, &ch, 1);
}
break;
case AUX_MU_IER_REG:
/* "DLAB bit set means access baudrate register" is NYI */
s->ier = value & (TX_INT | RX_INT);
bcm2835_aux_update(s);
break;
case AUX_MU_IIR_REG:
if (value & 0x2) {
s->read_count = 0;
}
break;
case AUX_MU_LCR_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
break;
case AUX_MU_MCR_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
break;
case AUX_MU_SCRATCH:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
break;
case AUX_MU_CNTL_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
break;
case AUX_MU_BAUD_REG:
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
}
bcm2835_aux_update(s);
}
static int bcm2835_aux_can_receive(void *opaque)
{
BCM2835AuxState *s = opaque;
return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
}
static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
{
BCM2835AuxState *s = opaque;
int slot;
slot = s->read_pos + s->read_count;
if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
slot -= BCM2835_AUX_RX_FIFO_LEN;
}
s->read_fifo[slot] = value;
s->read_count++;
if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
/* buffer full */
}
bcm2835_aux_update(s);
}
static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
{
bcm2835_aux_put_fifo(opaque, *buf);
}
static const MemoryRegionOps bcm2835_aux_ops = {
.read = bcm2835_aux_read,
.write = bcm2835_aux_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static const VMStateDescription vmstate_bcm2835_aux = {
.name = TYPE_BCM2835_AUX,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState,
BCM2835_AUX_RX_FIFO_LEN),
VMSTATE_UINT8(read_pos, BCM2835AuxState),
VMSTATE_UINT8(read_count, BCM2835AuxState),
VMSTATE_UINT8(ier, BCM2835AuxState),
VMSTATE_UINT8(iir, BCM2835AuxState),
VMSTATE_END_OF_LIST()
}
};
static void bcm2835_aux_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
BCM2835AuxState *s = BCM2835_AUX(obj);
memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
TYPE_BCM2835_AUX, 0x100);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
}
static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
{
BCM2835AuxState *s = BCM2835_AUX(dev);
if (s->chr) {
qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive,
bcm2835_aux_receive, NULL, s);
}
}
static Property bcm2835_aux_props[] = {
DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
DEFINE_PROP_END_OF_LIST(),
};
static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = bcm2835_aux_realize;
dc->vmsd = &vmstate_bcm2835_aux;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
dc->props = bcm2835_aux_props;
}
static const TypeInfo bcm2835_aux_info = {
.name = TYPE_BCM2835_AUX,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(BCM2835AuxState),
.instance_init = bcm2835_aux_init,
.class_init = bcm2835_aux_class_init,
};
static void bcm2835_aux_register_types(void)
{
type_register_static(&bcm2835_aux_info);
}
type_init(bcm2835_aux_register_types)
+1
View File
@@ -27,6 +27,7 @@ endif
obj-$(CONFIG_OMAP) += omap_dss.o
obj-$(CONFIG_OMAP) += omap_lcdc.o
obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o
obj-$(CONFIG_RASPI) += bcm2835_fb.o
obj-$(CONFIG_SM501) += sm501.o
obj-$(CONFIG_TCX) += tcx.o
obj-$(CONFIG_CG3) += cg3.o
+424
View File
@@ -0,0 +1,424 @@
/*
* Raspberry Pi emulation (c) 2012 Gregory Estrade
* Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann.
* This code is licensed under the GNU GPLv2 and later.
*
* Heavily based on milkymist-vgafb.c, copyright terms below:
* QEMU model of the Milkymist VGA framebuffer.
*
* Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "qemu/osdep.h"
#include "hw/display/bcm2835_fb.h"
#include "hw/display/framebuffer.h"
#include "ui/pixel_ops.h"
#include "hw/misc/bcm2835_mbox_defs.h"
#define DEFAULT_VCRAM_SIZE 0x4000000
#define BCM2835_FB_OFFSET 0x00100000
static void fb_invalidate_display(void *opaque)
{
BCM2835FBState *s = BCM2835_FB(opaque);
s->invalidate = true;
}
static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
int width, int deststep)
{
BCM2835FBState *s = opaque;
uint16_t rgb565;
uint32_t rgb888;
uint8_t r, g, b;
DisplaySurface *surface = qemu_console_surface(s->con);
int bpp = surface_bits_per_pixel(surface);
while (width--) {
switch (s->bpp) {
case 8:
/* lookup palette starting at video ram base
* TODO: cache translation, rather than doing this each time!
*/
rgb888 = ldl_le_phys(&s->dma_as, s->vcram_base + (*src << 2));
r = (rgb888 >> 0) & 0xff;
g = (rgb888 >> 8) & 0xff;
b = (rgb888 >> 16) & 0xff;
src++;
break;
case 16:
rgb565 = lduw_le_p(src);
r = ((rgb565 >> 11) & 0x1f) << 3;
g = ((rgb565 >> 5) & 0x3f) << 2;
b = ((rgb565 >> 0) & 0x1f) << 3;
src += 2;
break;
case 24:
rgb888 = ldl_le_p(src);
r = (rgb888 >> 0) & 0xff;
g = (rgb888 >> 8) & 0xff;
b = (rgb888 >> 16) & 0xff;
src += 3;
break;
case 32:
rgb888 = ldl_le_p(src);
r = (rgb888 >> 0) & 0xff;
g = (rgb888 >> 8) & 0xff;
b = (rgb888 >> 16) & 0xff;
src += 4;
break;
default:
r = 0;
g = 0;
b = 0;
break;
}
if (s->pixo == 0) {
/* swap to BGR pixel format */
uint8_t tmp = r;
r = b;
b = tmp;
}
switch (bpp) {
case 8:
*dst++ = rgb_to_pixel8(r, g, b);
break;
case 15:
*(uint16_t *)dst = rgb_to_pixel15(r, g, b);
dst += 2;
break;
case 16:
*(uint16_t *)dst = rgb_to_pixel16(r, g, b);
dst += 2;
break;
case 24:
rgb888 = rgb_to_pixel24(r, g, b);
*dst++ = rgb888 & 0xff;
*dst++ = (rgb888 >> 8) & 0xff;
*dst++ = (rgb888 >> 16) & 0xff;
break;
case 32:
*(uint32_t *)dst = rgb_to_pixel32(r, g, b);
dst += 4;
break;
default:
return;
}
}
}
static void fb_update_display(void *opaque)
{
BCM2835FBState *s = opaque;
DisplaySurface *surface = qemu_console_surface(s->con);
int first = 0;
int last = 0;
int src_width = 0;
int dest_width = 0;
if (s->lock || !s->xres) {
return;
}
src_width = s->xres * (s->bpp >> 3);
dest_width = s->xres;
switch (surface_bits_per_pixel(surface)) {
case 0:
return;
case 8:
break;
case 15:
dest_width *= 2;
break;
case 16:
dest_width *= 2;
break;
case 24:
dest_width *= 3;
break;
case 32:
dest_width *= 4;
break;
default:
hw_error("bcm2835_fb: bad color depth\n");
break;
}
if (s->invalidate) {
framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base,
s->yres, src_width);
}
framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
src_width, dest_width, 0, s->invalidate,
draw_line_src16, s, &first, &last);
if (first >= 0) {
dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1);
}
s->invalidate = false;
}
static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
{
value &= ~0xf;
s->lock = true;
s->xres = ldl_le_phys(&s->dma_as, value);
s->yres = ldl_le_phys(&s->dma_as, value + 4);
s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8);
s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12);
s->bpp = ldl_le_phys(&s->dma_as, value + 20);
s->xoffset = ldl_le_phys(&s->dma_as, value + 24);
s->yoffset = ldl_le_phys(&s->dma_as, value + 28);
s->base = s->vcram_base | (value & 0xc0000000);
s->base += BCM2835_FB_OFFSET;
/* TODO - Manage properly virtual resolution */
s->pitch = s->xres * (s->bpp >> 3);
s->size = s->yres * s->pitch;
stl_le_phys(&s->dma_as, value + 16, s->pitch);
stl_le_phys(&s->dma_as, value + 32, s->base);
stl_le_phys(&s->dma_as, value + 36, s->size);
s->invalidate = true;
qemu_console_resize(s->con, s->xres, s->yres);
s->lock = false;
}
void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres,
uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp,
uint32_t *pixo, uint32_t *alpha)
{
s->lock = true;
/* TODO: input validation! */
if (xres) {
s->xres = *xres;
}
if (yres) {
s->yres = *yres;
}
if (xoffset) {
s->xoffset = *xoffset;
}
if (yoffset) {
s->yoffset = *yoffset;
}
if (bpp) {
s->bpp = *bpp;
}
if (pixo) {
s->pixo = *pixo;
}
if (alpha) {
s->alpha = *alpha;
}
/* TODO - Manage properly virtual resolution */
s->pitch = s->xres * (s->bpp >> 3);
s->size = s->yres * s->pitch;
s->invalidate = true;
qemu_console_resize(s->con, s->xres, s->yres);
s->lock = false;
}
static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size)
{
BCM2835FBState *s = opaque;
uint32_t res = 0;
switch (offset) {
case MBOX_AS_DATA:
res = MBOX_CHAN_FB;
s->pending = false;
qemu_set_irq(s->mbox_irq, 0);
break;
case MBOX_AS_PENDING:
res = s->pending;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
return 0;
}
return res;
}
static void bcm2835_fb_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
BCM2835FBState *s = opaque;
switch (offset) {
case MBOX_AS_DATA:
/* bcm2835_mbox should check our pending status before pushing */
assert(!s->pending);
s->pending = true;
bcm2835_fb_mbox_push(s, value);
qemu_set_irq(s->mbox_irq, 1);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
return;
}
}
static const MemoryRegionOps bcm2835_fb_ops = {
.read = bcm2835_fb_read,
.write = bcm2835_fb_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static const VMStateDescription vmstate_bcm2835_fb = {
.name = TYPE_BCM2835_FB,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_BOOL(lock, BCM2835FBState),
VMSTATE_BOOL(invalidate, BCM2835FBState),
VMSTATE_BOOL(pending, BCM2835FBState),
VMSTATE_UINT32(xres, BCM2835FBState),
VMSTATE_UINT32(yres, BCM2835FBState),
VMSTATE_UINT32(xres_virtual, BCM2835FBState),
VMSTATE_UINT32(yres_virtual, BCM2835FBState),
VMSTATE_UINT32(xoffset, BCM2835FBState),
VMSTATE_UINT32(yoffset, BCM2835FBState),
VMSTATE_UINT32(bpp, BCM2835FBState),
VMSTATE_UINT32(base, BCM2835FBState),
VMSTATE_UINT32(pitch, BCM2835FBState),
VMSTATE_UINT32(size, BCM2835FBState),
VMSTATE_UINT32(pixo, BCM2835FBState),
VMSTATE_UINT32(alpha, BCM2835FBState),
VMSTATE_END_OF_LIST()
}
};
static const GraphicHwOps vgafb_ops = {
.invalidate = fb_invalidate_display,
.gfx_update = fb_update_display,
};
static void bcm2835_fb_init(Object *obj)
{
BCM2835FBState *s = BCM2835_FB(obj);
memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB,
0x10);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
}
static void bcm2835_fb_reset(DeviceState *dev)
{
BCM2835FBState *s = BCM2835_FB(dev);
s->pending = false;
s->xres_virtual = s->xres;
s->yres_virtual = s->yres;
s->xoffset = 0;
s->yoffset = 0;
s->base = s->vcram_base + BCM2835_FB_OFFSET;
s->pitch = s->xres * (s->bpp >> 3);
s->size = s->yres * s->pitch;
s->invalidate = true;
s->lock = false;
}
static void bcm2835_fb_realize(DeviceState *dev, Error **errp)
{
BCM2835FBState *s = BCM2835_FB(dev);
Error *err = NULL;
Object *obj;
if (s->vcram_base == 0) {
error_setg(errp, "%s: required vcram-base property not set", __func__);
return;
}
obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
if (obj == NULL) {
error_setg(errp, "%s: required dma-mr link not found: %s",
__func__, error_get_pretty(err));
return;
}
s->dma_mr = MEMORY_REGION(obj);
address_space_init(&s->dma_as, s->dma_mr, NULL);
bcm2835_fb_reset(dev);
s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
qemu_console_resize(s->con, s->xres, s->yres);
}
static Property bcm2835_fb_props[] = {
DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/
DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size,
DEFAULT_VCRAM_SIZE),
DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640),
DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480),
DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16),
DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */
DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */
DEFINE_PROP_END_OF_LIST()
};
static void bcm2835_fb_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->props = bcm2835_fb_props;
dc->realize = bcm2835_fb_realize;
dc->reset = bcm2835_fb_reset;
dc->vmsd = &vmstate_bcm2835_fb;
}
static TypeInfo bcm2835_fb_info = {
.name = TYPE_BCM2835_FB,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(BCM2835FBState),
.class_init = bcm2835_fb_class_init,
.instance_init = bcm2835_fb_init,
};
static void bcm2835_fb_register_types(void)
{
type_register_static(&bcm2835_fb_info);
}
type_init(bcm2835_fb_register_types)
+1
View File
@@ -11,3 +11,4 @@ common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
obj-$(CONFIG_RASPI) += bcm2835_dma.o
+408
View File
@@ -0,0 +1,408 @@
/*
* Raspberry Pi emulation (c) 2012 Gregory Estrade
* This code is licensed under the GNU GPLv2 and later.
*/
#include "qemu/osdep.h"
#include "hw/dma/bcm2835_dma.h"
/* DMA CS Control and Status bits */
#define BCM2708_DMA_ACTIVE (1 << 0)
#define BCM2708_DMA_END (1 << 1) /* GE */
#define BCM2708_DMA_INT (1 << 2)
#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */
#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */
#define BCM2708_DMA_ERR (1 << 8)
#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */
#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */
/* DMA control block "info" field bits */
#define BCM2708_DMA_INT_EN (1 << 0)
#define BCM2708_DMA_TDMODE (1 << 1)
#define BCM2708_DMA_WAIT_RESP (1 << 3)
#define BCM2708_DMA_D_INC (1 << 4)
#define BCM2708_DMA_D_WIDTH (1 << 5)
#define BCM2708_DMA_D_DREQ (1 << 6)
#define BCM2708_DMA_D_IGNORE (1 << 7)
#define BCM2708_DMA_S_INC (1 << 8)
#define BCM2708_DMA_S_WIDTH (1 << 9)
#define BCM2708_DMA_S_DREQ (1 << 10)
#define BCM2708_DMA_S_IGNORE (1 << 11)
/* Register offsets */
#define BCM2708_DMA_CS 0x00 /* Control and Status */
#define BCM2708_DMA_ADDR 0x04 /* Control block address */
/* the current control block appears in the following registers - read only */
#define BCM2708_DMA_INFO 0x08
#define BCM2708_DMA_SOURCE_AD 0x0c
#define BCM2708_DMA_DEST_AD 0x10
#define BCM2708_DMA_TXFR_LEN 0x14
#define BCM2708_DMA_STRIDE 0x18
#define BCM2708_DMA_NEXTCB 0x1C
#define BCM2708_DMA_DEBUG 0x20
#define BCM2708_DMA_INT_STATUS 0xfe0 /* Interrupt status of each channel */
#define BCM2708_DMA_ENABLE 0xff0 /* Global enable bits for each channel */
#define BCM2708_DMA_CS_RW_MASK 0x30ff0001 /* All RW bits in DMA_CS */
static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c)
{
BCM2835DMAChan *ch = &s->chan[c];
uint32_t data, xlen, ylen;
int16_t dst_stride, src_stride;
if (!(s->enable & (1 << c))) {
return;
}
while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) {
/* CB fetch */
ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad);
ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4);
ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8);
ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12);
ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16);
ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20);
if (ch->ti & BCM2708_DMA_TDMODE) {
/* 2D transfer mode */
ylen = (ch->txfr_len >> 16) & 0x3fff;
xlen = ch->txfr_len & 0xffff;
dst_stride = ch->stride >> 16;
src_stride = ch->stride & 0xffff;
} else {
ylen = 1;
xlen = ch->txfr_len;
dst_stride = 0;
src_stride = 0;
}
while (ylen != 0) {
/* Normal transfer mode */
while (xlen != 0) {
if (ch->ti & BCM2708_DMA_S_IGNORE) {
/* Ignore reads */
data = 0;
} else {
data = ldl_le_phys(&s->dma_as, ch->source_ad);
}
if (ch->ti & BCM2708_DMA_S_INC) {
ch->source_ad += 4;
}
if (ch->ti & BCM2708_DMA_D_IGNORE) {
/* Ignore writes */
} else {
stl_le_phys(&s->dma_as, ch->dest_ad, data);
}
if (ch->ti & BCM2708_DMA_D_INC) {
ch->dest_ad += 4;
}
/* update remaining transfer length */
xlen -= 4;
if (ch->ti & BCM2708_DMA_TDMODE) {
ch->txfr_len = (ylen << 16) | xlen;
} else {
ch->txfr_len = xlen;
}
}
if (--ylen != 0) {
ch->source_ad += src_stride;
ch->dest_ad += dst_stride;
}
}
ch->cs |= BCM2708_DMA_END;
if (ch->ti & BCM2708_DMA_INT_EN) {
ch->cs |= BCM2708_DMA_INT;
s->int_status |= (1 << c);
qemu_set_irq(ch->irq, 1);
}
/* Process next CB */
ch->conblk_ad = ch->nextconbk;
}
ch->cs &= ~BCM2708_DMA_ACTIVE;
ch->cs |= BCM2708_DMA_ISPAUSED;
}
static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch)
{
ch->cs = 0;
ch->conblk_ad = 0;
}
static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset,
unsigned size, unsigned c)
{
BCM2835DMAChan *ch;
uint32_t res = 0;
assert(size == 4);
assert(c < BCM2835_DMA_NCHANS);
ch = &s->chan[c];
switch (offset) {
case BCM2708_DMA_CS:
res = ch->cs;
break;
case BCM2708_DMA_ADDR:
res = ch->conblk_ad;
break;
case BCM2708_DMA_INFO:
res = ch->ti;
break;
case BCM2708_DMA_SOURCE_AD:
res = ch->source_ad;
break;
case BCM2708_DMA_DEST_AD:
res = ch->dest_ad;
break;
case BCM2708_DMA_TXFR_LEN:
res = ch->txfr_len;
break;
case BCM2708_DMA_STRIDE:
res = ch->stride;
break;
case BCM2708_DMA_NEXTCB:
res = ch->nextconbk;
break;
case BCM2708_DMA_DEBUG:
res = ch->debug;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
break;
}
return res;
}
static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset,
uint64_t value, unsigned size, unsigned c)
{
BCM2835DMAChan *ch;
uint32_t oldcs;
assert(size == 4);
assert(c < BCM2835_DMA_NCHANS);
ch = &s->chan[c];
switch (offset) {
case BCM2708_DMA_CS:
oldcs = ch->cs;
if (value & BCM2708_DMA_RESET) {
bcm2835_dma_chan_reset(ch);
}
if (value & BCM2708_DMA_ABORT) {
/* abort is a no-op, since we always run to completion */
}
if (value & BCM2708_DMA_END) {
ch->cs &= ~BCM2708_DMA_END;
}
if (value & BCM2708_DMA_INT) {
ch->cs &= ~BCM2708_DMA_INT;
s->int_status &= ~(1 << c);
qemu_set_irq(ch->irq, 0);
}
ch->cs &= ~BCM2708_DMA_CS_RW_MASK;
ch->cs |= (value & BCM2708_DMA_CS_RW_MASK);
if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) {
bcm2835_dma_update(s, c);
}
break;
case BCM2708_DMA_ADDR:
ch->conblk_ad = value;
break;
case BCM2708_DMA_DEBUG:
ch->debug = value;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
break;
}
}
static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size)
{
BCM2835DMAState *s = opaque;
if (offset < 0xf00) {
return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf);
} else {
switch (offset) {
case BCM2708_DMA_INT_STATUS:
return s->int_status;
case BCM2708_DMA_ENABLE:
return s->enable;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
return 0;
}
}
}
static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size)
{
return bcm2835_dma_read(opaque, (offset & 0xff), size, 15);
}
static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
BCM2835DMAState *s = opaque;
if (offset < 0xf00) {
bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf);
} else {
switch (offset) {
case BCM2708_DMA_INT_STATUS:
break;
case BCM2708_DMA_ENABLE:
s->enable = (value & 0xffff);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
}
}
}
static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15);
}
static const MemoryRegionOps bcm2835_dma0_ops = {
.read = bcm2835_dma0_read,
.write = bcm2835_dma0_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static const MemoryRegionOps bcm2835_dma15_ops = {
.read = bcm2835_dma15_read,
.write = bcm2835_dma15_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
};
static const VMStateDescription vmstate_bcm2835_dma_chan = {
.name = TYPE_BCM2835_DMA "-chan",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(cs, BCM2835DMAChan),
VMSTATE_UINT32(conblk_ad, BCM2835DMAChan),
VMSTATE_UINT32(ti, BCM2835DMAChan),
VMSTATE_UINT32(source_ad, BCM2835DMAChan),
VMSTATE_UINT32(dest_ad, BCM2835DMAChan),
VMSTATE_UINT32(txfr_len, BCM2835DMAChan),
VMSTATE_UINT32(stride, BCM2835DMAChan),
VMSTATE_UINT32(nextconbk, BCM2835DMAChan),
VMSTATE_UINT32(debug, BCM2835DMAChan),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_bcm2835_dma = {
.name = TYPE_BCM2835_DMA,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1,
vmstate_bcm2835_dma_chan, BCM2835DMAChan),
VMSTATE_UINT32(int_status, BCM2835DMAState),
VMSTATE_UINT32(enable, BCM2835DMAState),
VMSTATE_END_OF_LIST()
}
};
static void bcm2835_dma_init(Object *obj)
{
BCM2835DMAState *s = BCM2835_DMA(obj);
int n;
/* DMA channels 0-14 occupy a contiguous block of IO memory, along
* with the global enable and interrupt status bits. Channel 15
* has the same register map, but is mapped at a discontiguous
* address in a separate IO block.
*/
memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s,
TYPE_BCM2835_DMA, 0x1000);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0);
memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s,
TYPE_BCM2835_DMA "-chan15", 0x100);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15);
for (n = 0; n < 16; n++) {
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq);
}
}
static void bcm2835_dma_reset(DeviceState *dev)
{
BCM2835DMAState *s = BCM2835_DMA(dev);
int n;
s->enable = 0xffff;
s->int_status = 0;
for (n = 0; n < BCM2835_DMA_NCHANS; n++) {
bcm2835_dma_chan_reset(&s->chan[n]);
}
}
static void bcm2835_dma_realize(DeviceState *dev, Error **errp)
{
BCM2835DMAState *s = BCM2835_DMA(dev);
Error *err = NULL;
Object *obj;
obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
if (obj == NULL) {
error_setg(errp, "%s: required dma-mr link not found: %s",
__func__, error_get_pretty(err));
return;
}
s->dma_mr = MEMORY_REGION(obj);
address_space_init(&s->dma_as, s->dma_mr, NULL);
bcm2835_dma_reset(dev);
}
static void bcm2835_dma_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = bcm2835_dma_realize;
dc->reset = bcm2835_dma_reset;
dc->vmsd = &vmstate_bcm2835_dma;
}
static TypeInfo bcm2835_dma_info = {
.name = TYPE_BCM2835_DMA,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(BCM2835DMAState),
.class_init = bcm2835_dma_class_init,
.instance_init = bcm2835_dma_init,
};
static void bcm2835_dma_register_types(void)
{
type_register_static(&bcm2835_dma_info);
}
type_init(bcm2835_dma_register_types)
+1
View File
@@ -319,6 +319,7 @@ static void imx_i2c_class_init(ObjectClass *klass, void *data)
dc->vmsd = &imx_i2c_vmstate;
dc->reset = imx_i2c_reset;
dc->realize = imx_i2c_realize;
dc->desc = "i.MX I2C Controller";
}
static const TypeInfo imx_i2c_type_info = {
+1
View File
@@ -31,3 +31,4 @@ obj-$(CONFIG_XICS_KVM) += xics_kvm.o
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
obj-$(CONFIG_S390_FLIC) += s390_flic.o
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_vic.o
+339
View File
@@ -0,0 +1,339 @@
/*
* ASPEED Interrupt Controller (New)
*
* Andrew Jeffery <andrew@aj.id.au>
*
* Copyright 2015, 2016 IBM Corp.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
/* The hardware exposes two register sets, a legacy set and a 'new' set. The
* model implements the 'new' register set, and logs warnings on accesses to
* the legacy IO space.
*
* The hardware uses 32bit registers to manage 51 IRQs, with low and high
* registers for each conceptual register. The device model's implementation
* uses 64bit data types to store both low and high register values (in the one
* member), but must cope with access offset values in multiples of 4 passed to
* the callbacks. As such the read() and write() implementations process the
* provided offset to understand whether the access is requesting the lower or
* upper 32 bits of the 64bit member.
*
* Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt"
* fields have separate "enable"/"status" and "clear" registers, where set bits
* are written to one or the other to change state (avoiding a
* read-modify-write sequence).
*/
#include "qemu/osdep.h"
#include <inttypes.h>
#include "hw/intc/aspeed_vic.h"
#include "qemu/bitops.h"
#include "trace.h"
#define AVIC_NEW_BASE_OFFSET 0x80
#define AVIC_L_MASK 0xFFFFFFFFU
#define AVIC_H_MASK 0x0007FFFFU
#define AVIC_EVENT_W_MASK (0x78000ULL << 32)
static void aspeed_vic_update(AspeedVICState *s)
{
uint64_t new = (s->raw & s->enable);
uint64_t flags;
flags = new & s->select;
trace_aspeed_vic_update_fiq(!!flags);
qemu_set_irq(s->fiq, !!flags);
flags = new & ~s->select;
trace_aspeed_vic_update_irq(!!flags);
qemu_set_irq(s->irq, !!flags);
}
static void aspeed_vic_set_irq(void *opaque, int irq, int level)
{
uint64_t irq_mask;
bool raise;
AspeedVICState *s = (AspeedVICState *)opaque;
if (irq > ASPEED_VIC_NR_IRQS) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
__func__, irq);
return;
}
trace_aspeed_vic_set_irq(irq, level);
irq_mask = BIT(irq);
if (s->sense & irq_mask) {
/* level-triggered */
if (s->event & irq_mask) {
/* high-sensitive */
raise = level;
} else {
/* low-sensitive */
raise = !level;
}
s->raw = deposit64(s->raw, irq, 1, raise);
} else {
uint64_t old_level = s->level & irq_mask;
/* edge-triggered */
if (s->dual_edge & irq_mask) {
raise = (!!old_level) != (!!level);
} else {
if (s->event & irq_mask) {
/* rising-sensitive */
raise = !old_level && level;
} else {
/* falling-sensitive */
raise = old_level && !level;
}
}
if (raise) {
s->raw = deposit64(s->raw, irq, 1, raise);
}
}
s->level = deposit64(s->level, irq, 1, level);
aspeed_vic_update(s);
}
static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
{
uint64_t val;
const bool high = !!(offset & 0x4);
hwaddr n_offset = (offset & ~0x4);
AspeedVICState *s = (AspeedVICState *)opaque;
if (offset < AVIC_NEW_BASE_OFFSET) {
qemu_log_mask(LOG_UNIMP, "%s: Ignoring read from legacy registers "
"at 0x%" HWADDR_PRIx "[%u]\n", __func__, offset, size);
return 0;
}
n_offset -= AVIC_NEW_BASE_OFFSET;
switch (n_offset) {
case 0x0: /* IRQ Status */
val = s->raw & ~s->select & s->enable;
break;
case 0x08: /* FIQ Status */
val = s->raw & s->select & s->enable;
break;
case 0x10: /* Raw Interrupt Status */
val = s->raw;
break;
case 0x18: /* Interrupt Selection */
val = s->select;
break;
case 0x20: /* Interrupt Enable */
val = s->enable;
break;
case 0x30: /* Software Interrupt */
val = s->trigger;
break;
case 0x40: /* Interrupt Sensitivity */
val = s->sense;
break;
case 0x48: /* Interrupt Both Edge Trigger Control */
val = s->dual_edge;
break;
case 0x50: /* Interrupt Event */
val = s->event;
break;
case 0x60: /* Edge Triggered Interrupt Status */
val = s->raw & ~s->sense;
break;
/* Illegal */
case 0x28: /* Interrupt Enable Clear */
case 0x38: /* Software Interrupt Clear */
case 0x58: /* Edge Triggered Interrupt Clear */
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Read of write-only register with offset 0x%"
HWADDR_PRIx "\n", __func__, offset);
val = 0;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
val = 0;
break;
}
if (high) {
val = extract64(val, 32, 19);
}
trace_aspeed_vic_read(offset, size, val);
return val;
}
static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
unsigned size)
{
const bool high = !!(offset & 0x4);
hwaddr n_offset = (offset & ~0x4);
AspeedVICState *s = (AspeedVICState *)opaque;
if (offset < AVIC_NEW_BASE_OFFSET) {
qemu_log_mask(LOG_UNIMP,
"%s: Ignoring write to legacy registers at 0x%"
HWADDR_PRIx "[%u] <- 0x%" PRIx64 "\n", __func__, offset,
size, data);
return;
}
n_offset -= AVIC_NEW_BASE_OFFSET;
trace_aspeed_vic_write(offset, size, data);
/* Given we have members using separate enable/clear registers, deposit64()
* isn't quite the tool for the job. Instead, relocate the incoming bits to
* the required bit offset based on the provided access address
*/
if (high) {
data &= AVIC_H_MASK;
data <<= 32;
} else {
data &= AVIC_L_MASK;
}
switch (n_offset) {
case 0x18: /* Interrupt Selection */
/* Register has deposit64() semantics - overwrite requested 32 bits */
if (high) {
s->select &= AVIC_L_MASK;
} else {
s->select &= ((uint64_t) AVIC_H_MASK) << 32;
}
s->select |= data;
break;
case 0x20: /* Interrupt Enable */
s->enable |= data;
break;
case 0x28: /* Interrupt Enable Clear */
s->enable &= ~data;
break;
case 0x30: /* Software Interrupt */
qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
"IRQs requested: 0x%016" PRIx64 "\n", __func__, data);
break;
case 0x38: /* Software Interrupt Clear */
qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
"IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data);
break;
case 0x50: /* Interrupt Event */
/* Register has deposit64() semantics - overwrite the top four valid
* IRQ bits, as only the top four IRQs (GPIOs) can change their event
* type */
if (high) {
s->event &= ~AVIC_EVENT_W_MASK;
s->event |= (data & AVIC_EVENT_W_MASK);
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"Ignoring invalid write to interrupt event register");
}
break;
case 0x58: /* Edge Triggered Interrupt Clear */
s->raw &= ~(data & ~s->sense);
break;
case 0x00: /* IRQ Status */
case 0x08: /* FIQ Status */
case 0x10: /* Raw Interrupt Status */
case 0x40: /* Interrupt Sensitivity */
case 0x48: /* Interrupt Both Edge Trigger Control */
case 0x60: /* Edge Triggered Interrupt Status */
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Write of read-only register with offset 0x%"
HWADDR_PRIx "\n", __func__, offset);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
break;
}
aspeed_vic_update(s);
}
static const MemoryRegionOps aspeed_vic_ops = {
.read = aspeed_vic_read,
.write = aspeed_vic_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
.valid.unaligned = false,
};
static void aspeed_vic_reset(DeviceState *dev)
{
AspeedVICState *s = ASPEED_VIC(dev);
s->level = 0;
s->raw = 0;
s->select = 0;
s->enable = 0;
s->trigger = 0;
s->sense = 0x1F07FFF8FFFFULL;
s->dual_edge = 0xF800070000ULL;
s->event = 0x5F07FFF8FFFFULL;
}
#define AVIC_IO_REGION_SIZE 0x20000
static void aspeed_vic_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
AspeedVICState *s = ASPEED_VIC(dev);
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s,
TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS);
sysbus_init_irq(sbd, &s->irq);
sysbus_init_irq(sbd, &s->fiq);
}
static const VMStateDescription vmstate_aspeed_vic = {
.name = "aspeed.new-vic",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT64(level, AspeedVICState),
VMSTATE_UINT64(raw, AspeedVICState),
VMSTATE_UINT64(select, AspeedVICState),
VMSTATE_UINT64(enable, AspeedVICState),
VMSTATE_UINT64(trigger, AspeedVICState),
VMSTATE_UINT64(sense, AspeedVICState),
VMSTATE_UINT64(dual_edge, AspeedVICState),
VMSTATE_UINT64(event, AspeedVICState),
VMSTATE_END_OF_LIST()
}
};
static void aspeed_vic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = aspeed_vic_realize;
dc->reset = aspeed_vic_reset;
dc->desc = "ASPEED Interrupt Controller (New)";
dc->vmsd = &vmstate_aspeed_vic;
}
static const TypeInfo aspeed_vic_info = {
.name = TYPE_ASPEED_VIC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AspeedVICState),
.class_init = aspeed_vic_class_init,
};
static void aspeed_vic_register_types(void)
{
type_register_static(&aspeed_vic_info);
}
type_init(aspeed_vic_register_types);
+1
View File
@@ -28,6 +28,7 @@ obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
obj-$(CONFIG_IMX) += imx_ccm.o
obj-$(CONFIG_IMX) += imx31_ccm.o
obj-$(CONFIG_IMX) += imx25_ccm.o
obj-$(CONFIG_IMX) += imx6_ccm.o
obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
obj-$(CONFIG_MAINSTONE) += mst_fpga.o

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