aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-10-15 15:18:22 +0100
committerPeter Maydell <peter.maydell@linaro.org>2024-10-15 15:18:22 +0100
commitf774a677507966222624a9b2859f06ede7608100 (patch)
tree0d6a7f482982a20ef8609b798e15d5974cd2db85 /hw
parentc155d13167c6ace099e351e28125f9eb3694ae27 (diff)
parentf160a4f8d0ef322377db3519c0aa088ccd99edf1 (diff)
downloadqemu-f774a677507966222624a9b2859f06ede7608100.zip
qemu-f774a677507966222624a9b2859f06ede7608100.tar.gz
qemu-f774a677507966222624a9b2859f06ede7608100.tar.bz2
Merge tag 'pull-target-arm-20241015-1' of https://git.linaro.org/people/pmaydell/qemu-arm into staging
target-arm queue: * hw/arm/omap1: Remove unused omap_uwire_attach() method * stm32f405: Add RCC device to stm32f405 SoC * arm/gicv3: add missing casts * hw/misc: Create STM32L4x5 SYSCFG clock * hw/arm: Add SPI to Allwinner A10 * hw/intc/omap_intc: Remove now-unnecessary abstract base class * hw/char/pl011: Use correct masks for IBRD and FBRD * docs/devel: Convert txt files to rST * Remove MAX111X, MAX7310, DSCM-1XXXX, pcmcia devices (used only by now-removed omap/pxa2xx boards) * vl.c: Remove pxa2xx-specific -portrait and -rotate options * dma: Fix function names in documentation * hw/arm/xilinx_zynq: Add various missing unimplemented devices # -----BEGIN PGP SIGNATURE----- # # iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmcOeWEZHHBldGVyLm1h # eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3jCMD/482mpT1s+mrEJFWSJJXU4G # 8kr4Zj6+NafbayJ0vHTkpSbkEbPxuvDiUqmlnbI+3o11i+Z3IyiaGZbba7dyNnKl # 02MdQavL0dB+eMrcFNofRRvwvsposuj2ixgwTQe6L32HSFdHerVVwuhHM/wfwyCh # DKt7gPRovD/7CtwDOSpyW7cK64WK1IUlE8VEsbFdQbCPkopm55LQ2sLT4TshadpG # A6xcxyLN0x/lHgCmvijB1T09LSc1nQpUEQNIokC4f1Rmy6HNgGDYY1G7GAJf99mT # nWhATuuhZThiYfRbN5KQoS9tGEUduxtkGhHiOgpdXpgc3cS7RusCHoqAnibpsVh3 # TgAkaRAX1d/jQ2KYR2h2jI3nh66ObhrFRT3dkzRZrIvmK9zeWUKmS9lzZ94aVfPH # +MtBPwsO5OhzEABs8WpMY9V1nYaYDsFATMc1akUSaSLn1Er9Uz66NIk+J4Lob4P0 # 78IPvTmwvAIITiqQvkISsc37n5a2/toeaffU2hPKtQLlhyilWynEZA5YItrXSTuk # gYIBxyZSbzGj/ofZ9T9C0GDLbhJp9ksNIpIqRUiHOH3z9b85r7HVZORp+COw/ZXR # UGak6rpJ+XVOxVL/cPRTvZB0RbUHIZh7WLNH2G7Tfv4E4llqL81iuImHXVh/2CXO # 9GWr9qbDLDYQ+BI7ipLAYg== # =n2CA # -----END PGP SIGNATURE----- # gpg: Signature made Tue 15 Oct 2024 15:17:05 BST # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # gpg: aka "Peter Maydell <peter@archaic.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * tag 'pull-target-arm-20241015-1' of https://git.linaro.org/people/pmaydell/qemu-arm: (28 commits) hw/arm/xilinx_zynq: Add various missing unimplemented devices dma: Fix function names in documentation vl.c: Remove pxa2xx-specific -portrait and -rotate options hw/block: Remove ecc hw: Remove PCMCIA subsystem hw/ide: Remove DSCM-1XXXX microdrive device model hw/gpio: Remove MAX7310 device hw/adc: Remove MAX111X device docs/devel/lockcnt: Include kernel-doc API documentation include: Move QemuLockCnt APIs to their own header docs/devel/rcu: Convert to rST format docs/devel/multiple-iothreads: Convert to rST format docs/devel/lockcnt: Convert to rST format docs/devel/blkverify: Convert to rST format docs/devel/blkdebug: Convert to rST format hw/char/pl011: Use correct masks for IBRD and FBRD hw/intc/omap_intc: Remove now-unnecessary abstract base class hw/arm: Add SPI to Allwinner A10 hw/ssi: Allwinner A10 SPI emulation tests/qtest: Check STM32L4x5 clock connections ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/Kconfig1
-rw-r--r--hw/adc/Kconfig3
-rw-r--r--hw/adc/max111x.c236
-rw-r--r--hw/adc/meson.build1
-rw-r--r--hw/arm/Kconfig3
-rw-r--r--hw/arm/allwinner-a10.c8
-rw-r--r--hw/arm/omap1.c29
-rw-r--r--hw/arm/stm32f405_soc.c12
-rw-r--r--hw/arm/stm32l4x5_soc.c2
-rw-r--r--hw/arm/xilinx_zynq.c70
-rw-r--r--hw/block/Kconfig3
-rw-r--r--hw/block/ecc.c91
-rw-r--r--hw/block/meson.build1
-rw-r--r--hw/char/pl011.c4
-rw-r--r--hw/core/clock.c16
-rw-r--r--hw/core/cpu-common.c1
-rw-r--r--hw/gpio/Kconfig4
-rw-r--r--hw/gpio/max7310.c217
-rw-r--r--hw/gpio/meson.build1
-rw-r--r--hw/ide/Kconfig6
-rw-r--r--hw/ide/meson.build1
-rw-r--r--hw/ide/microdrive.c644
-rw-r--r--hw/intc/arm_gicv3_cpuif.c6
-rw-r--r--hw/intc/omap_intc.c11
-rw-r--r--hw/meson.build1
-rw-r--r--hw/misc/Kconfig4
-rw-r--r--hw/misc/meson.build1
-rw-r--r--hw/misc/stm32_rcc.c162
-rw-r--r--hw/misc/stm32l4x5_syscfg.c19
-rw-r--r--hw/misc/trace-events6
-rw-r--r--hw/pcmcia/Kconfig2
-rw-r--r--hw/pcmcia/meson.build1
-rw-r--r--hw/pcmcia/pcmcia.c24
-rw-r--r--hw/ssi/Kconfig4
-rw-r--r--hw/ssi/allwinner-a10-spi.c561
-rw-r--r--hw/ssi/meson.build1
-rw-r--r--hw/ssi/trace-events10
37 files changed, 890 insertions, 1277 deletions
diff --git a/hw/Kconfig b/hw/Kconfig
index 6fdaff1..1b4e9bb 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -27,7 +27,6 @@ source nvme/Kconfig
source nvram/Kconfig
source pci-bridge/Kconfig
source pci-host/Kconfig
-source pcmcia/Kconfig
source pci/Kconfig
source remote/Kconfig
source rtc/Kconfig
diff --git a/hw/adc/Kconfig b/hw/adc/Kconfig
index a825bd3..25d2229 100644
--- a/hw/adc/Kconfig
+++ b/hw/adc/Kconfig
@@ -1,5 +1,2 @@
config STM32F2XX_ADC
bool
-
-config MAX111X
- bool
diff --git a/hw/adc/max111x.c b/hw/adc/max111x.c
deleted file mode 100644
index aa51e47..0000000
--- a/hw/adc/max111x.c
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Maxim MAX1110/1111 ADC chip emulation.
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Written by Andrzej Zaborowski <balrog@zabor.org>
- *
- * This code is licensed under the GNU GPLv2.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "qemu/osdep.h"
-#include "hw/adc/max111x.h"
-#include "hw/irq.h"
-#include "migration/vmstate.h"
-#include "qemu/module.h"
-#include "hw/qdev-properties.h"
-
-/* Control-byte bitfields */
-#define CB_PD0 (1 << 0)
-#define CB_PD1 (1 << 1)
-#define CB_SGL (1 << 2)
-#define CB_UNI (1 << 3)
-#define CB_SEL0 (1 << 4)
-#define CB_SEL1 (1 << 5)
-#define CB_SEL2 (1 << 6)
-#define CB_START (1 << 7)
-
-#define CHANNEL_NUM(v, b0, b1, b2) \
- ((((v) >> (2 + (b0))) & 4) | \
- (((v) >> (3 + (b1))) & 2) | \
- (((v) >> (4 + (b2))) & 1))
-
-static uint32_t max111x_read(MAX111xState *s)
-{
- if (!s->tb1)
- return 0;
-
- switch (s->cycle ++) {
- case 1:
- return s->rb2;
- case 2:
- return s->rb3;
- }
-
- return 0;
-}
-
-/* Interpret a control-byte */
-static void max111x_write(MAX111xState *s, uint32_t value)
-{
- int measure, chan;
-
- /* Ignore the value if START bit is zero */
- if (!(value & CB_START))
- return;
-
- s->cycle = 0;
-
- if (!(value & CB_PD1)) {
- s->tb1 = 0;
- return;
- }
-
- s->tb1 = value;
-
- if (s->inputs == 8)
- chan = CHANNEL_NUM(value, 1, 0, 2);
- else
- chan = CHANNEL_NUM(value & ~CB_SEL0, 0, 1, 2);
-
- if (value & CB_SGL)
- measure = s->input[chan] - s->com;
- else
- measure = s->input[chan] - s->input[chan ^ 1];
-
- if (!(value & CB_UNI))
- measure ^= 0x80;
-
- s->rb2 = (measure >> 2) & 0x3f;
- s->rb3 = (measure << 6) & 0xc0;
-
- /* FIXME: When should the IRQ be lowered? */
- qemu_irq_raise(s->interrupt);
-}
-
-static uint32_t max111x_transfer(SSIPeripheral *dev, uint32_t value)
-{
- MAX111xState *s = MAX_111X(dev);
- max111x_write(s, value);
- return max111x_read(s);
-}
-
-static const VMStateDescription vmstate_max111x = {
- .name = "max111x",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (const VMStateField[]) {
- VMSTATE_SSI_PERIPHERAL(parent_obj, MAX111xState),
- VMSTATE_UINT8(tb1, MAX111xState),
- VMSTATE_UINT8(rb2, MAX111xState),
- VMSTATE_UINT8(rb3, MAX111xState),
- VMSTATE_INT32_EQUAL(inputs, MAX111xState, NULL),
- VMSTATE_INT32(com, MAX111xState),
- VMSTATE_ARRAY_INT32_UNSAFE(input, MAX111xState, inputs,
- vmstate_info_uint8, uint8_t),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static void max111x_input_set(void *opaque, int line, int value)
-{
- MAX111xState *s = MAX_111X(opaque);
-
- assert(line >= 0 && line < s->inputs);
- s->input[line] = value;
-}
-
-static int max111x_init(SSIPeripheral *d, int inputs)
-{
- DeviceState *dev = DEVICE(d);
- MAX111xState *s = MAX_111X(dev);
-
- qdev_init_gpio_out(dev, &s->interrupt, 1);
- qdev_init_gpio_in(dev, max111x_input_set, inputs);
-
- s->inputs = inputs;
-
- return 0;
-}
-
-static void max1110_realize(SSIPeripheral *dev, Error **errp)
-{
- max111x_init(dev, 8);
-}
-
-static void max1111_realize(SSIPeripheral *dev, Error **errp)
-{
- max111x_init(dev, 4);
-}
-
-static void max111x_reset(DeviceState *dev)
-{
- MAX111xState *s = MAX_111X(dev);
- int i;
-
- for (i = 0; i < s->inputs; i++) {
- s->input[i] = s->reset_input[i];
- }
- s->com = 0;
- s->tb1 = 0;
- s->rb2 = 0;
- s->rb3 = 0;
- s->cycle = 0;
-}
-
-static Property max1110_properties[] = {
- /* Reset values for ADC inputs */
- DEFINE_PROP_UINT8("input0", MAX111xState, reset_input[0], 0xf0),
- DEFINE_PROP_UINT8("input1", MAX111xState, reset_input[1], 0xe0),
- DEFINE_PROP_UINT8("input2", MAX111xState, reset_input[2], 0xd0),
- DEFINE_PROP_UINT8("input3", MAX111xState, reset_input[3], 0xc0),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static Property max1111_properties[] = {
- /* Reset values for ADC inputs */
- DEFINE_PROP_UINT8("input0", MAX111xState, reset_input[0], 0xf0),
- DEFINE_PROP_UINT8("input1", MAX111xState, reset_input[1], 0xe0),
- DEFINE_PROP_UINT8("input2", MAX111xState, reset_input[2], 0xd0),
- DEFINE_PROP_UINT8("input3", MAX111xState, reset_input[3], 0xc0),
- DEFINE_PROP_UINT8("input4", MAX111xState, reset_input[4], 0xb0),
- DEFINE_PROP_UINT8("input5", MAX111xState, reset_input[5], 0xa0),
- DEFINE_PROP_UINT8("input6", MAX111xState, reset_input[6], 0x90),
- DEFINE_PROP_UINT8("input7", MAX111xState, reset_input[7], 0x80),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void max111x_class_init(ObjectClass *klass, void *data)
-{
- SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- k->transfer = max111x_transfer;
- device_class_set_legacy_reset(dc, max111x_reset);
- dc->vmsd = &vmstate_max111x;
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
-}
-
-static const TypeInfo max111x_info = {
- .name = TYPE_MAX_111X,
- .parent = TYPE_SSI_PERIPHERAL,
- .instance_size = sizeof(MAX111xState),
- .class_init = max111x_class_init,
- .abstract = true,
-};
-
-static void max1110_class_init(ObjectClass *klass, void *data)
-{
- SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- k->realize = max1110_realize;
- device_class_set_props(dc, max1110_properties);
-}
-
-static const TypeInfo max1110_info = {
- .name = TYPE_MAX_1110,
- .parent = TYPE_MAX_111X,
- .class_init = max1110_class_init,
-};
-
-static void max1111_class_init(ObjectClass *klass, void *data)
-{
- SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- k->realize = max1111_realize;
- device_class_set_props(dc, max1111_properties);
-}
-
-static const TypeInfo max1111_info = {
- .name = TYPE_MAX_1111,
- .parent = TYPE_MAX_111X,
- .class_init = max1111_class_init,
-};
-
-static void max111x_register_types(void)
-{
- type_register_static(&max111x_info);
- type_register_static(&max1110_info);
- type_register_static(&max1111_info);
-}
-
-type_init(max111x_register_types)
diff --git a/hw/adc/meson.build b/hw/adc/meson.build
index a4f85b7..7f7acc1 100644
--- a/hw/adc/meson.build
+++ b/hw/adc/meson.build
@@ -2,4 +2,3 @@ system_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c'))
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_adc.c'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c'))
system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c'))
-system_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c'))
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index e7b9cfb..e7fd933 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -150,7 +150,6 @@ config OMAP
bool
select FRAMEBUFFER
select I2C
- select ECC
select NAND
select PFLASH_CFI01
select SD
@@ -328,6 +327,7 @@ config ALLWINNER_A10
select ALLWINNER_WDT
select ALLWINNER_EMAC
select ALLWINNER_I2C
+ select ALLWINNER_A10_SPI
select AXP2XX_PMU
select SERIAL_MM
select UNIMP
@@ -397,6 +397,7 @@ config STM32F405_SOC
bool
select ARM_V7M
select OR_IRQ
+ select STM32_RCC
select STM32F4XX_SYSCFG
select STM32F4XX_EXTI
diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
index 08cdff6..9eb1aa7 100644
--- a/hw/arm/allwinner-a10.c
+++ b/hw/arm/allwinner-a10.c
@@ -35,6 +35,7 @@
#define AW_A10_PIC_REG_BASE 0x01c20400
#define AW_A10_PIT_REG_BASE 0x01c20c00
#define AW_A10_UART0_REG_BASE 0x01c28000
+#define AW_A10_SPI0_BASE 0x01c05000
#define AW_A10_EMAC_BASE 0x01c0b000
#define AW_A10_EHCI_BASE 0x01c14000
#define AW_A10_OHCI_BASE 0x01c14400
@@ -80,6 +81,8 @@ static void aw_a10_init(Object *obj)
object_initialize_child(obj, "i2c0", &s->i2c0, TYPE_AW_I2C);
+ object_initialize_child(obj, "spi0", &s->spi0, TYPE_AW_A10_SPI);
+
for (size_t i = 0; i < AW_A10_NUM_USB; i++) {
object_initialize_child(obj, "ehci[*]", &s->ehci[i],
TYPE_PLATFORM_EHCI);
@@ -195,6 +198,11 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, AW_A10_I2C0_BASE);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0, qdev_get_gpio_in(dev, 7));
+ /* SPI */
+ sysbus_realize(SYS_BUS_DEVICE(&s->spi0), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi0), 0, AW_A10_SPI0_BASE);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi0), 0, qdev_get_gpio_in(dev, 10));
+
/* WDT */
sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_fatal);
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->wdt), 0, AW_A10_WDT_BASE, 1);
diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c
index 86ee336..25030c7 100644
--- a/hw/arm/omap1.c
+++ b/hw/arm/omap1.c
@@ -2170,29 +2170,27 @@ struct omap_uwire_s {
uint16_t rxbuf;
uint16_t control;
uint16_t setup[5];
-
- uWireSlave *chip[4];
};
static void omap_uwire_transfer_start(struct omap_uwire_s *s)
{
int chipselect = (s->control >> 10) & 3; /* INDEX */
- uWireSlave *slave = s->chip[chipselect];
if ((s->control >> 5) & 0x1f) { /* NB_BITS_WR */
- if (s->control & (1 << 12)) /* CS_CMD */
- if (slave && slave->send)
- slave->send(slave->opaque,
- s->txbuf >> (16 - ((s->control >> 5) & 0x1f)));
+ if (s->control & (1 << 12)) { /* CS_CMD */
+ qemu_log_mask(LOG_UNIMP, "uWireSlave TX CS:%d data:0x%04x\n",
+ chipselect,
+ s->txbuf >> (16 - ((s->control >> 5) & 0x1f)));
+ }
s->control &= ~(1 << 14); /* CSRB */
/* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or
* a DRQ. When is the level IRQ supposed to be reset? */
}
if ((s->control >> 0) & 0x1f) { /* NB_BITS_RD */
- if (s->control & (1 << 12)) /* CS_CMD */
- if (slave && slave->receive)
- s->rxbuf = slave->receive(slave->opaque);
+ if (s->control & (1 << 12)) { /* CS_CMD */
+ qemu_log_mask(LOG_UNIMP, "uWireSlave RX CS:%d\n", chipselect);
+ }
s->control |= 1 << 15; /* RDRB */
/* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or
* a DRQ. When is the level IRQ supposed to be reset? */
@@ -2321,17 +2319,6 @@ static struct omap_uwire_s *omap_uwire_init(MemoryRegion *system_memory,
return s;
}
-void omap_uwire_attach(struct omap_uwire_s *s,
- uWireSlave *slave, int chipselect)
-{
- if (chipselect < 0 || chipselect > 3) {
- error_report("%s: Bad chipselect %i", __func__, chipselect);
- exit(-1);
- }
-
- s->chip[chipselect] = slave;
-}
-
/* Pseudonoise Pulse-Width Light Modulator */
struct omap_pwl_s {
MemoryRegion iomem;
diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c
index 2ad5b79..72ae621 100644
--- a/hw/arm/stm32f405_soc.c
+++ b/hw/arm/stm32f405_soc.c
@@ -30,6 +30,7 @@
#include "hw/qdev-clock.h"
#include "hw/misc/unimp.h"
+#define RCC_ADDR 0x40023800
#define SYSCFG_ADD 0x40013800
static const uint32_t usart_addr[] = { 0x40011000, 0x40004400, 0x40004800,
0x40004C00, 0x40005000, 0x40011400,
@@ -59,6 +60,8 @@ static void stm32f405_soc_initfn(Object *obj)
object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
+ object_initialize_child(obj, "rcc", &s->rcc, TYPE_STM32_RCC);
+
object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32F4XX_SYSCFG);
for (i = 0; i < STM_NUM_USARTS; i++) {
@@ -160,6 +163,14 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
return;
}
+ /* Reset and clock controller */
+ dev = DEVICE(&s->rcc);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->rcc), errp)) {
+ return;
+ }
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, RCC_ADDR);
+
/* System configuration controller */
dev = DEVICE(&s->syscfg);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->syscfg), errp)) {
@@ -276,7 +287,6 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
create_unimplemented_device("GPIOH", 0x40021C00, 0x400);
create_unimplemented_device("GPIOI", 0x40022000, 0x400);
create_unimplemented_device("CRC", 0x40023000, 0x400);
- create_unimplemented_device("RCC", 0x40023800, 0x400);
create_unimplemented_device("Flash Int", 0x40023C00, 0x400);
create_unimplemented_device("BKPSRAM", 0x40024000, 0x400);
create_unimplemented_device("DMA1", 0x40026000, 0x400);
diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c
index fac83d3..16e3505 100644
--- a/hw/arm/stm32l4x5_soc.c
+++ b/hw/arm/stm32l4x5_soc.c
@@ -236,6 +236,8 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
/* System configuration controller */
busdev = SYS_BUS_DEVICE(&s->syscfg);
+ qdev_connect_clock_in(DEVICE(&s->syscfg), "clk",
+ qdev_get_clock_out(DEVICE(&(s->rcc)), "syscfg-out"));
if (!sysbus_realize(busdev, errp)) {
return;
}
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 37c234f..fde4d94 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -34,6 +34,7 @@
#include "hw/net/cadence_gem.h"
#include "hw/cpu/a9mpcore.h"
#include "hw/qdev-clock.h"
+#include "hw/misc/unimp.h"
#include "sysemu/reset.h"
#include "qom/object.h"
#include "exec/tswap.h"
@@ -373,6 +374,75 @@ static void zynq_init(MachineState *machine)
sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]);
sysbus_mmio_map(busdev, 0, 0xF8007000);
+ /*
+ * Refer to the ug585-Zynq-7000-TRM manual B.3 (Module Summary) and
+ * the zynq-7000.dtsi. Add placeholders for unimplemented devices.
+ */
+ create_unimplemented_device("zynq.i2c0", 0xE0004000, 4 * KiB);
+ create_unimplemented_device("zynq.i2c1", 0xE0005000, 4 * KiB);
+ create_unimplemented_device("zynq.can0", 0xE0008000, 4 * KiB);
+ create_unimplemented_device("zynq.can1", 0xE0009000, 4 * KiB);
+ create_unimplemented_device("zynq.gpio", 0xE000A000, 4 * KiB);
+ create_unimplemented_device("zynq.smcc", 0xE000E000, 4 * KiB);
+
+ /* Direct Memory Access Controller, PL330, Non-Secure Mode */
+ create_unimplemented_device("zynq.dma_ns", 0xF8004000, 4 * KiB);
+
+ /* System Watchdog Timer Registers */
+ create_unimplemented_device("zynq.swdt", 0xF8005000, 4 * KiB);
+
+ /* DDR memory controller */
+ create_unimplemented_device("zynq.ddrc", 0xF8006000, 4 * KiB);
+
+ /* AXI_HP Interface (AFI) */
+ create_unimplemented_device("zynq.axi_hp0", 0xF8008000, 0x28);
+ create_unimplemented_device("zynq.axi_hp1", 0xF8009000, 0x28);
+ create_unimplemented_device("zynq.axi_hp2", 0xF800A000, 0x28);
+ create_unimplemented_device("zynq.axi_hp3", 0xF800B000, 0x28);
+
+ create_unimplemented_device("zynq.efuse", 0xF800d000, 0x20);
+
+ /* Embedded Trace Buffer */
+ create_unimplemented_device("zynq.etb", 0xF8801000, 4 * KiB);
+
+ /* Cross Trigger Interface, ETB and TPIU */
+ create_unimplemented_device("zynq.cti_etb_tpiu", 0xF8802000, 4 * KiB);
+
+ /* Trace Port Interface Unit */
+ create_unimplemented_device("zynq.tpiu", 0xF8803000, 4 * KiB);
+
+ /* CoreSight Trace Funnel */
+ create_unimplemented_device("zynq.funnel", 0xF8804000, 4 * KiB);
+
+ /* Instrumentation Trace Macrocell */
+ create_unimplemented_device("zynq.itm", 0xF8805000, 4 * KiB);
+
+ /* Cross Trigger Interface, FTM */
+ create_unimplemented_device("zynq.cti_ftm", 0xF8809000, 4 * KiB);
+
+ /* Fabric Trace Macrocell */
+ create_unimplemented_device("zynq.ftm", 0xF880B000, 4 * KiB);
+
+ /* Cortex A9 Performance Monitoring Unit, CPU */
+ create_unimplemented_device("cortex-a9.pmu0", 0xF8891000, 4 * KiB);
+ create_unimplemented_device("cortex-a9.pmu1", 0xF8893000, 4 * KiB);
+
+ /* Cross Trigger Interface, CPU */
+ create_unimplemented_device("zynq.cpu_cti0", 0xF8898000, 4 * KiB);
+ create_unimplemented_device("zynq.cpu_cti1", 0xF8899000, 4 * KiB);
+
+ /* CoreSight PTM-A9, CPU */
+ create_unimplemented_device("cortex-a9.ptm0", 0xF889c000, 4 * KiB);
+ create_unimplemented_device("cortex-a9.ptm1", 0xF889d000, 4 * KiB);
+
+ /* AMBA NIC301 TrustZone */
+ create_unimplemented_device("zynq.trustZone", 0xF8900000, 0x20);
+
+ /* AMBA Network Interconnect Advanced Quality of Service (QoS-301) */
+ create_unimplemented_device("zynq.qos301_cpu", 0xF8946000, 0x130);
+ create_unimplemented_device("zynq.qos301_dmac", 0xF8947000, 0x130);
+ create_unimplemented_device("zynq.qos301_iou", 0xF8948000, 0x130);
+
zynq_binfo.ram_size = machine->ram_size;
zynq_binfo.board_id = 0xd32;
zynq_binfo.loader_start = 0;
diff --git a/hw/block/Kconfig b/hw/block/Kconfig
index e67a6fd..a898e04 100644
--- a/hw/block/Kconfig
+++ b/hw/block/Kconfig
@@ -22,9 +22,6 @@ config PFLASH_CFI01
config PFLASH_CFI02
bool
-config ECC
- bool
-
config VIRTIO_BLK
bool
default y
diff --git a/hw/block/ecc.c b/hw/block/ecc.c
deleted file mode 100644
index ed889a4..0000000
--- a/hw/block/ecc.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Calculate Error-correcting Codes. Used by NAND Flash controllers
- * (not by NAND chips).
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Written by Andrzej Zaborowski <balrog@zabor.org>
- *
- * This code is licensed under the GNU GPL v2.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "qemu/osdep.h"
-#include "migration/vmstate.h"
-#include "hw/block/flash.h"
-
-/*
- * Pre-calculated 256-way 1 byte column parity. Table borrowed from Linux.
- */
-static const uint8_t nand_ecc_precalc_table[] = {
- 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
- 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
- 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
- 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
- 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
- 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
- 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
- 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
- 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
- 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
- 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
- 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
- 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
- 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
- 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
- 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
- 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
- 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
- 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
- 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
- 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
- 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
- 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
- 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
- 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
- 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
- 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
- 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
- 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
- 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
- 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
- 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
-};
-
-/* Update ECC parity count. */
-uint8_t ecc_digest(ECCState *s, uint8_t sample)
-{
- uint8_t idx = nand_ecc_precalc_table[sample];
-
- s->cp ^= idx & 0x3f;
- if (idx & 0x40) {
- s->lp[0] ^= ~s->count;
- s->lp[1] ^= s->count;
- }
- s->count ++;
-
- return sample;
-}
-
-/* Reinitialise the counters. */
-void ecc_reset(ECCState *s)
-{
- s->lp[0] = 0x0000;
- s->lp[1] = 0x0000;
- s->cp = 0x00;
- s->count = 0;
-}
-
-/* Save/restore */
-const VMStateDescription vmstate_ecc_state = {
- .name = "ecc-state",
- .version_id = 0,
- .minimum_version_id = 0,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT8(cp, ECCState),
- VMSTATE_UINT16_ARRAY(lp, ECCState, 2),
- VMSTATE_UINT16(count, ECCState),
- VMSTATE_END_OF_LIST(),
- },
-};
diff --git a/hw/block/meson.build b/hw/block/meson.build
index 999a93d..16a51bf 100644
--- a/hw/block/meson.build
+++ b/hw/block/meson.build
@@ -3,7 +3,6 @@ system_ss.add(files(
'cdrom.c',
'hd-geometry.c'
))
-system_ss.add(when: 'CONFIG_ECC', if_true: files('ecc.c'))
system_ss.add(when: 'CONFIG_FDC', if_true: files('fdc.c'))
system_ss.add(when: 'CONFIG_FDC_ISA', if_true: files('fdc-isa.c'))
system_ss.add(when: 'CONFIG_FDC_SYSBUS', if_true: files('fdc-sysbus.c'))
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 15df7c1..0fd1334 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -90,10 +90,10 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr)
#define CR_UARTEN (1 << 0)
/* Integer Baud Rate Divider, UARTIBRD */
-#define IBRD_MASK 0x3f
+#define IBRD_MASK 0xffff
/* Fractional Baud Rate Divider, UARTFBRD */
-#define FBRD_MASK 0xffff
+#define FBRD_MASK 0x3f
static const unsigned char pl011_id_arm[8] =
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
diff --git a/hw/core/clock.c b/hw/core/clock.c
index e212865..cbe7b1b 100644
--- a/hw/core/clock.c
+++ b/hw/core/clock.c
@@ -13,6 +13,8 @@
#include "qemu/osdep.h"
#include "qemu/cutils.h"
+#include "qapi/visitor.h"
+#include "sysemu/qtest.h"
#include "hw/clock.h"
#include "trace.h"
@@ -158,6 +160,15 @@ bool clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider)
return true;
}
+static void clock_period_prop_get(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ Clock *clk = CLOCK(obj);
+ uint64_t period = clock_get(clk);
+ visit_type_uint64(v, name, &period, errp);
+}
+
+
static void clock_initfn(Object *obj)
{
Clock *clk = CLOCK(obj);
@@ -166,6 +177,11 @@ static void clock_initfn(Object *obj)
clk->divider = 1;
QLIST_INIT(&clk->children);
+
+ if (qtest_enabled()) {
+ object_property_add(obj, "qtest-clock-period", "uint64",
+ clock_period_prop_get, NULL, NULL, NULL);
+ }
}
static void clock_finalizefn(Object *obj)
diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index 7982ecd..09c7903 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -24,6 +24,7 @@
#include "sysemu/hw_accel.h"
#include "qemu/log.h"
#include "qemu/main-loop.h"
+#include "qemu/lockcnt.h"
#include "exec/log.h"
#include "exec/gdbstub.h"
#include "sysemu/tcg.h"
diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig
index 843630d..c423e10 100644
--- a/hw/gpio/Kconfig
+++ b/hw/gpio/Kconfig
@@ -1,7 +1,3 @@
-config MAX7310
- bool
- depends on I2C
-
config PL061
bool
diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c
deleted file mode 100644
index 43a92b8..0000000
--- a/hw/gpio/max7310.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * MAX7310 8-port GPIO expansion chip.
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Written by Andrzej Zaborowski <balrog@zabor.org>
- *
- * This file is licensed under GNU GPL.
- */
-
-#include "qemu/osdep.h"
-#include "hw/i2c/i2c.h"
-#include "hw/irq.h"
-#include "migration/vmstate.h"
-#include "qemu/log.h"
-#include "qemu/module.h"
-#include "qom/object.h"
-
-#define TYPE_MAX7310 "max7310"
-OBJECT_DECLARE_SIMPLE_TYPE(MAX7310State, MAX7310)
-
-struct MAX7310State {
- I2CSlave parent_obj;
-
- int i2c_command_byte;
- int len;
-
- uint8_t level;
- uint8_t direction;
- uint8_t polarity;
- uint8_t status;
- uint8_t command;
- qemu_irq handler[8];
- qemu_irq *gpio_in;
-};
-
-static void max7310_reset(DeviceState *dev)
-{
- MAX7310State *s = MAX7310(dev);
-
- s->level &= s->direction;
- s->direction = 0xff;
- s->polarity = 0xf0;
- s->status = 0x01;
- s->command = 0x00;
-}
-
-static uint8_t max7310_rx(I2CSlave *i2c)
-{
- MAX7310State *s = MAX7310(i2c);
-
- switch (s->command) {
- case 0x00: /* Input port */
- return s->level ^ s->polarity;
-
- case 0x01: /* Output port */
- return s->level & ~s->direction;
-
- case 0x02: /* Polarity inversion */
- return s->polarity;
-
- case 0x03: /* Configuration */
- return s->direction;
-
- case 0x04: /* Timeout */
- return s->status;
-
- case 0xff: /* Reserved */
- return 0xff;
-
- default:
- qemu_log_mask(LOG_UNIMP, "%s: Unsupported register 0x02%" PRIx8 "\n",
- __func__, s->command);
- break;
- }
- return 0xff;
-}
-
-static int max7310_tx(I2CSlave *i2c, uint8_t data)
-{
- MAX7310State *s = MAX7310(i2c);
- uint8_t diff;
- int line;
-
- if (s->len ++ > 1) {
-#ifdef VERBOSE
- printf("%s: message too long (%i bytes)\n", __func__, s->len);
-#endif
- return 1;
- }
-
- if (s->i2c_command_byte) {
- s->command = data;
- s->i2c_command_byte = 0;
- return 0;
- }
-
- switch (s->command) {
- case 0x01: /* Output port */
- for (diff = (data ^ s->level) & ~s->direction; diff;
- diff &= ~(1 << line)) {
- line = ctz32(diff);
- if (s->handler[line])
- qemu_set_irq(s->handler[line], (data >> line) & 1);
- }
- s->level = (s->level & s->direction) | (data & ~s->direction);
- break;
-
- case 0x02: /* Polarity inversion */
- s->polarity = data;
- break;
-
- case 0x03: /* Configuration */
- s->level &= ~(s->direction ^ data);
- s->direction = data;
- break;
-
- case 0x04: /* Timeout */
- s->status = data;
- break;
-
- case 0x00: /* Input port - ignore writes */
- break;
- default:
- qemu_log_mask(LOG_UNIMP, "%s: Unsupported register 0x02%" PRIx8 "\n",
- __func__, s->command);
- return 1;
- }
-
- return 0;
-}
-
-static int max7310_event(I2CSlave *i2c, enum i2c_event event)
-{
- MAX7310State *s = MAX7310(i2c);
- s->len = 0;
-
- switch (event) {
- case I2C_START_SEND:
- s->i2c_command_byte = 1;
- break;
- case I2C_FINISH:
-#ifdef VERBOSE
- if (s->len == 1)
- printf("%s: message too short (%i bytes)\n", __func__, s->len);
-#endif
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static const VMStateDescription vmstate_max7310 = {
- .name = "max7310",
- .version_id = 0,
- .minimum_version_id = 0,
- .fields = (const VMStateField[]) {
- VMSTATE_INT32(i2c_command_byte, MAX7310State),
- VMSTATE_INT32(len, MAX7310State),
- VMSTATE_UINT8(level, MAX7310State),
- VMSTATE_UINT8(direction, MAX7310State),
- VMSTATE_UINT8(polarity, MAX7310State),
- VMSTATE_UINT8(status, MAX7310State),
- VMSTATE_UINT8(command, MAX7310State),
- VMSTATE_I2C_SLAVE(parent_obj, MAX7310State),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static void max7310_gpio_set(void *opaque, int line, int level)
-{
- MAX7310State *s = (MAX7310State *) opaque;
- assert(line >= 0 && line < ARRAY_SIZE(s->handler));
-
- if (level)
- s->level |= s->direction & (1 << line);
- else
- s->level &= ~(s->direction & (1 << line));
-}
-
-/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols),
- * but also accepts sequences that are not SMBus so return an I2C device. */
-static void max7310_realize(DeviceState *dev, Error **errp)
-{
- MAX7310State *s = MAX7310(dev);
-
- qdev_init_gpio_in(dev, max7310_gpio_set, ARRAY_SIZE(s->handler));
- qdev_init_gpio_out(dev, s->handler, ARRAY_SIZE(s->handler));
-}
-
-static void max7310_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
-
- dc->realize = max7310_realize;
- k->event = max7310_event;
- k->recv = max7310_rx;
- k->send = max7310_tx;
- device_class_set_legacy_reset(dc, max7310_reset);
- dc->vmsd = &vmstate_max7310;
-}
-
-static const TypeInfo max7310_info = {
- .name = TYPE_MAX7310,
- .parent = TYPE_I2C_SLAVE,
- .instance_size = sizeof(MAX7310State),
- .class_init = max7310_class_init,
-};
-
-static void max7310_register_types(void)
-{
- type_register_static(&max7310_info);
-}
-
-type_init(max7310_register_types)
diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build
index 089b248..7484061 100644
--- a/hw/gpio/meson.build
+++ b/hw/gpio/meson.build
@@ -1,7 +1,6 @@
system_ss.add(when: 'CONFIG_GPIO_KEY', if_true: files('gpio_key.c'))
system_ss.add(when: 'CONFIG_GPIO_MPC8XXX', if_true: files('mpc8xxx.c'))
system_ss.add(when: 'CONFIG_GPIO_PWR', if_true: files('gpio_pwr.c'))
-system_ss.add(when: 'CONFIG_MAX7310', if_true: files('max7310.c'))
system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
system_ss.add(when: 'CONFIG_PL061', if_true: files('pl061.c'))
diff --git a/hw/ide/Kconfig b/hw/ide/Kconfig
index 6dfc5a2..2e22b67 100644
--- a/hw/ide/Kconfig
+++ b/hw/ide/Kconfig
@@ -43,12 +43,6 @@ config IDE_VIA
bool
select IDE_PCI
-config MICRODRIVE
- bool
- select IDE_BUS
- select IDE_DEV
- depends on PCMCIA
-
config AHCI
bool
select IDE_BUS
diff --git a/hw/ide/meson.build b/hw/ide/meson.build
index d09705c..90ea861 100644
--- a/hw/ide/meson.build
+++ b/hw/ide/meson.build
@@ -13,4 +13,3 @@ system_ss.add(when: 'CONFIG_IDE_PCI', if_true: files('pci.c'))
system_ss.add(when: 'CONFIG_IDE_PIIX', if_true: files('piix.c', 'ioport.c'))
system_ss.add(when: 'CONFIG_IDE_SII3112', if_true: files('sii3112.c'))
system_ss.add(when: 'CONFIG_IDE_VIA', if_true: files('via.c'))
-system_ss.add(when: 'CONFIG_MICRODRIVE', if_true: files('microdrive.c'))
diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c
deleted file mode 100644
index 5475d59..0000000
--- a/hw/ide/microdrive.c
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- * QEMU IDE Emulation: microdrive (CF / PCMCIA)
- *
- * Copyright (c) 2003 Fabrice Bellard
- * Copyright (c) 2006 Openedhand Ltd.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qemu/osdep.h"
-#include "hw/pcmcia.h"
-#include "migration/vmstate.h"
-#include "qapi/error.h"
-#include "qemu/module.h"
-#include "sysemu/dma.h"
-#include "hw/irq.h"
-
-#include "qom/object.h"
-#include "ide-internal.h"
-
-#define TYPE_MICRODRIVE "microdrive"
-OBJECT_DECLARE_SIMPLE_TYPE(MicroDriveState, MICRODRIVE)
-
-/***********************************************************/
-/* CF-ATA Microdrive */
-
-#define METADATA_SIZE 0x20
-
-/* DSCM-1XXXX Microdrive hard disk with CF+ II / PCMCIA interface. */
-
-struct MicroDriveState {
- /*< private >*/
- PCMCIACardState parent_obj;
- /*< public >*/
-
- IDEBus bus;
- uint32_t attr_base;
- uint32_t io_base;
-
- /* Card state */
- uint8_t opt;
- uint8_t stat;
- uint8_t pins;
-
- uint8_t ctrl;
- uint16_t io;
- uint8_t cycle;
-};
-
-/* Register bitfields */
-enum md_opt {
- OPT_MODE_MMAP = 0,
- OPT_MODE_IOMAP16 = 1,
- OPT_MODE_IOMAP1 = 2,
- OPT_MODE_IOMAP2 = 3,
- OPT_MODE = 0x3f,
- OPT_LEVIREQ = 0x40,
- OPT_SRESET = 0x80,
-};
-enum md_cstat {
- STAT_INT = 0x02,
- STAT_PWRDWN = 0x04,
- STAT_XE = 0x10,
- STAT_IOIS8 = 0x20,
- STAT_SIGCHG = 0x40,
- STAT_CHANGED = 0x80,
-};
-enum md_pins {
- PINS_MRDY = 0x02,
- PINS_CRDY = 0x20,
-};
-enum md_ctrl {
- CTRL_IEN = 0x02,
- CTRL_SRST = 0x04,
-};
-
-static inline void md_interrupt_update(MicroDriveState *s)
-{
- PCMCIACardState *card = PCMCIA_CARD(s);
-
- if (card->slot == NULL) {
- return;
- }
-
- qemu_set_irq(card->slot->irq,
- !(s->stat & STAT_INT) && /* Inverted */
- !(s->ctrl & (CTRL_IEN | CTRL_SRST)) &&
- !(s->opt & OPT_SRESET));
-}
-
-static void md_set_irq(void *opaque, int irq, int level)
-{
- MicroDriveState *s = opaque;
-
- if (level) {
- s->stat |= STAT_INT;
- } else {
- s->stat &= ~STAT_INT;
- }
-
- md_interrupt_update(s);
-}
-
-static void md_reset(DeviceState *dev)
-{
- MicroDriveState *s = MICRODRIVE(dev);
-
- s->opt = OPT_MODE_MMAP;
- s->stat = 0;
- s->pins = 0;
- s->cycle = 0;
- s->ctrl = 0;
- ide_bus_reset(&s->bus);
-}
-
-static uint8_t md_attr_read(PCMCIACardState *card, uint32_t at)
-{
- MicroDriveState *s = MICRODRIVE(card);
- PCMCIACardClass *pcc = PCMCIA_CARD_GET_CLASS(card);
-
- if (at < s->attr_base) {
- if (at < pcc->cis_len) {
- return pcc->cis[at];
- } else {
- return 0x00;
- }
- }
-
- at -= s->attr_base;
-
- switch (at) {
- case 0x00: /* Configuration Option Register */
- return s->opt;
- case 0x02: /* Card Configuration Status Register */
- if (s->ctrl & CTRL_IEN) {
- return s->stat & ~STAT_INT;
- } else {
- return s->stat;
- }
- case 0x04: /* Pin Replacement Register */
- return (s->pins & PINS_CRDY) | 0x0c;
- case 0x06: /* Socket and Copy Register */
- return 0x00;
-#ifdef VERBOSE
- default:
- printf("%s: Bad attribute space register %02x\n", __func__, at);
-#endif
- }
-
- return 0;
-}
-
-static void md_attr_write(PCMCIACardState *card, uint32_t at, uint8_t value)
-{
- MicroDriveState *s = MICRODRIVE(card);
-
- at -= s->attr_base;
-
- switch (at) {
- case 0x00: /* Configuration Option Register */
- s->opt = value & 0xcf;
- if (value & OPT_SRESET) {
- device_cold_reset(DEVICE(s));
- }
- md_interrupt_update(s);
- break;
- case 0x02: /* Card Configuration Status Register */
- if ((s->stat ^ value) & STAT_PWRDWN) {
- s->pins |= PINS_CRDY;
- }
- s->stat &= 0x82;
- s->stat |= value & 0x74;
- md_interrupt_update(s);
- /* Word 170 in Identify Device must be equal to STAT_XE */
- break;
- case 0x04: /* Pin Replacement Register */
- s->pins &= PINS_CRDY;
- s->pins |= value & PINS_MRDY;
- break;
- case 0x06: /* Socket and Copy Register */
- break;
- default:
- printf("%s: Bad attribute space register %02x\n", __func__, at);
- }
-}
-
-static uint16_t md_common_read(PCMCIACardState *card, uint32_t at)
-{
- MicroDriveState *s = MICRODRIVE(card);
- IDEState *ifs;
- uint16_t ret;
- at -= s->io_base;
-
- switch (s->opt & OPT_MODE) {
- case OPT_MODE_MMAP:
- if ((at & ~0x3ff) == 0x400) {
- at = 0;
- }
- break;
- case OPT_MODE_IOMAP16:
- at &= 0xf;
- break;
- case OPT_MODE_IOMAP1:
- if ((at & ~0xf) == 0x3f0) {
- at -= 0x3e8;
- } else if ((at & ~0xf) == 0x1f0) {
- at -= 0x1f0;
- }
- break;
- case OPT_MODE_IOMAP2:
- if ((at & ~0xf) == 0x370) {
- at -= 0x368;
- } else if ((at & ~0xf) == 0x170) {
- at -= 0x170;
- }
- }
-
- switch (at) {
- case 0x0: /* Even RD Data */
- case 0x8:
- return ide_data_readw(&s->bus, 0);
-
- /* TODO: 8-bit accesses */
- if (s->cycle) {
- ret = s->io >> 8;
- } else {
- s->io = ide_data_readw(&s->bus, 0);
- ret = s->io & 0xff;
- }
- s->cycle = !s->cycle;
- return ret;
- case 0x9: /* Odd RD Data */
- return s->io >> 8;
- case 0xd: /* Error */
- return ide_ioport_read(&s->bus, 0x1);
- case 0xe: /* Alternate Status */
- ifs = ide_bus_active_if(&s->bus);
- if (ifs->blk) {
- return ifs->status;
- } else {
- return 0;
- }
- case 0xf: /* Device Address */
- ifs = ide_bus_active_if(&s->bus);
- return 0xc2 | ((~ifs->select << 2) & 0x3c);
- default:
- return ide_ioport_read(&s->bus, at);
- }
-
- return 0;
-}
-
-static void md_common_write(PCMCIACardState *card, uint32_t at, uint16_t value)
-{
- MicroDriveState *s = MICRODRIVE(card);
- at -= s->io_base;
-
- switch (s->opt & OPT_MODE) {
- case OPT_MODE_MMAP:
- if ((at & ~0x3ff) == 0x400) {
- at = 0;
- }
- break;
- case OPT_MODE_IOMAP16:
- at &= 0xf;
- break;
- case OPT_MODE_IOMAP1:
- if ((at & ~0xf) == 0x3f0) {
- at -= 0x3e8;
- } else if ((at & ~0xf) == 0x1f0) {
- at -= 0x1f0;
- }
- break;
- case OPT_MODE_IOMAP2:
- if ((at & ~0xf) == 0x370) {
- at -= 0x368;
- } else if ((at & ~0xf) == 0x170) {
- at -= 0x170;
- }
- }
-
- switch (at) {
- case 0x0: /* Even WR Data */
- case 0x8:
- ide_data_writew(&s->bus, 0, value);
- break;
-
- /* TODO: 8-bit accesses */
- if (s->cycle) {
- ide_data_writew(&s->bus, 0, s->io | (value << 8));
- } else {
- s->io = value & 0xff;
- }
- s->cycle = !s->cycle;
- break;
- case 0x9:
- s->io = value & 0xff;
- s->cycle = !s->cycle;
- break;
- case 0xd: /* Features */
- ide_ioport_write(&s->bus, 0x1, value);
- break;
- case 0xe: /* Device Control */
- s->ctrl = value;
- if (value & CTRL_SRST) {
- device_cold_reset(DEVICE(s));
- }
- md_interrupt_update(s);
- break;
- default:
- if (s->stat & STAT_PWRDWN) {
- s->pins |= PINS_CRDY;
- s->stat &= ~STAT_PWRDWN;
- }
- ide_ioport_write(&s->bus, at, value);
- }
-}
-
-static const VMStateDescription vmstate_microdrive = {
- .name = "microdrive",
- .version_id = 3,
- .minimum_version_id = 0,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT8(opt, MicroDriveState),
- VMSTATE_UINT8(stat, MicroDriveState),
- VMSTATE_UINT8(pins, MicroDriveState),
- VMSTATE_UINT8(ctrl, MicroDriveState),
- VMSTATE_UINT16(io, MicroDriveState),
- VMSTATE_UINT8(cycle, MicroDriveState),
- VMSTATE_IDE_BUS(bus, MicroDriveState),
- VMSTATE_IDE_DRIVES(bus.ifs, MicroDriveState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const uint8_t dscm1xxxx_cis[0x14a] = {
- [0x000] = CISTPL_DEVICE, /* 5V Device Information */
- [0x002] = 0x03, /* Tuple length = 4 bytes */
- [0x004] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */
- [0x006] = 0x01, /* Size = 2K bytes */
- [0x008] = CISTPL_ENDMARK,
-
- [0x00a] = CISTPL_DEVICE_OC, /* Additional Device Information */
- [0x00c] = 0x04, /* Tuple length = 4 byest */
- [0x00e] = 0x03, /* Conditions: Ext = 0, Vcc 3.3V, MWAIT = 1 */
- [0x010] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */
- [0x012] = 0x01, /* Size = 2K bytes */
- [0x014] = CISTPL_ENDMARK,
-
- [0x016] = CISTPL_JEDEC_C, /* JEDEC ID */
- [0x018] = 0x02, /* Tuple length = 2 bytes */
- [0x01a] = 0xdf, /* PC Card ATA with no Vpp required */
- [0x01c] = 0x01,
-
- [0x01e] = CISTPL_MANFID, /* Manufacture ID */
- [0x020] = 0x04, /* Tuple length = 4 bytes */
- [0x022] = 0xa4, /* TPLMID_MANF = 00a4 (IBM) */
- [0x024] = 0x00,
- [0x026] = 0x00, /* PLMID_CARD = 0000 */
- [0x028] = 0x00,
-
- [0x02a] = CISTPL_VERS_1, /* Level 1 Version */
- [0x02c] = 0x12, /* Tuple length = 23 bytes */
- [0x02e] = 0x04, /* Major Version = JEIDA 4.2 / PCMCIA 2.1 */
- [0x030] = 0x01, /* Minor Version = 1 */
- [0x032] = 'I',
- [0x034] = 'B',
- [0x036] = 'M',
- [0x038] = 0x00,
- [0x03a] = 'm',
- [0x03c] = 'i',
- [0x03e] = 'c',
- [0x040] = 'r',
- [0x042] = 'o',
- [0x044] = 'd',
- [0x046] = 'r',
- [0x048] = 'i',
- [0x04a] = 'v',
- [0x04c] = 'e',
- [0x04e] = 0x00,
- [0x050] = CISTPL_ENDMARK,
-
- [0x052] = CISTPL_FUNCID, /* Function ID */
- [0x054] = 0x02, /* Tuple length = 2 bytes */
- [0x056] = 0x04, /* TPLFID_FUNCTION = Fixed Disk */
- [0x058] = 0x01, /* TPLFID_SYSINIT: POST = 1, ROM = 0 */
-
- [0x05a] = CISTPL_FUNCE, /* Function Extension */
- [0x05c] = 0x02, /* Tuple length = 2 bytes */
- [0x05e] = 0x01, /* TPLFE_TYPE = Disk Device Interface */
- [0x060] = 0x01, /* TPLFE_DATA = PC Card ATA Interface */
-
- [0x062] = CISTPL_FUNCE, /* Function Extension */
- [0x064] = 0x03, /* Tuple length = 3 bytes */
- [0x066] = 0x02, /* TPLFE_TYPE = Basic PC Card ATA Interface */
- [0x068] = 0x08, /* TPLFE_DATA: Rotating, Unique, Single */
- [0x06a] = 0x0f, /* TPLFE_DATA: Sleep, Standby, Idle, Auto */
-
- [0x06c] = CISTPL_CONFIG, /* Configuration */
- [0x06e] = 0x05, /* Tuple length = 5 bytes */
- [0x070] = 0x01, /* TPCC_RASZ = 2 bytes, TPCC_RMSZ = 1 byte */
- [0x072] = 0x07, /* TPCC_LAST = 7 */
- [0x074] = 0x00, /* TPCC_RADR = 0200 */
- [0x076] = 0x02,
- [0x078] = 0x0f, /* TPCC_RMSK = 200, 202, 204, 206 */
-
- [0x07a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
- [0x07c] = 0x0b, /* Tuple length = 11 bytes */
- [0x07e] = 0xc0, /* TPCE_INDX = Memory Mode, Default, Iface */
- [0x080] = 0xc0, /* TPCE_IF = Memory, no BVDs, no WP, READY */
- [0x082] = 0xa1, /* TPCE_FS = Vcc only, no I/O, Memory, Misc */
- [0x084] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
- [0x086] = 0x55, /* NomV: 5.0 V */
- [0x088] = 0x4d, /* MinV: 4.5 V */
- [0x08a] = 0x5d, /* MaxV: 5.5 V */
- [0x08c] = 0x4e, /* Peakl: 450 mA */
- [0x08e] = 0x08, /* TPCE_MS = 1 window, 1 byte, Host address */
- [0x090] = 0x00, /* Window descriptor: Window length = 0 */
- [0x092] = 0x20, /* TPCE_MI: support power down mode, RW */
-
- [0x094] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
- [0x096] = 0x06, /* Tuple length = 6 bytes */
- [0x098] = 0x00, /* TPCE_INDX = Memory Mode, no Default */
- [0x09a] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
- [0x09c] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
- [0x09e] = 0xb5, /* NomV: 3.3 V */
- [0x0a0] = 0x1e,
- [0x0a2] = 0x3e, /* Peakl: 350 mA */
-
- [0x0a4] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
- [0x0a6] = 0x0d, /* Tuple length = 13 bytes */
- [0x0a8] = 0xc1, /* TPCE_INDX = I/O and Memory Mode, Default */
- [0x0aa] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */
- [0x0ac] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */
- [0x0ae] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
- [0x0b0] = 0x55, /* NomV: 5.0 V */
- [0x0b2] = 0x4d, /* MinV: 4.5 V */
- [0x0b4] = 0x5d, /* MaxV: 5.5 V */
- [0x0b6] = 0x4e, /* Peakl: 450 mA */
- [0x0b8] = 0x64, /* TPCE_IO = 16-byte boundary, 16/8 accesses */
- [0x0ba] = 0xf0, /* TPCE_IR = MASK, Level, Pulse, Share */
- [0x0bc] = 0xff, /* IRQ0..IRQ7 supported */
- [0x0be] = 0xff, /* IRQ8..IRQ15 supported */
- [0x0c0] = 0x20, /* TPCE_MI = support power down mode */
-
- [0x0c2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
- [0x0c4] = 0x06, /* Tuple length = 6 bytes */
- [0x0c6] = 0x01, /* TPCE_INDX = I/O and Memory Mode */
- [0x0c8] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
- [0x0ca] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
- [0x0cc] = 0xb5, /* NomV: 3.3 V */
- [0x0ce] = 0x1e,
- [0x0d0] = 0x3e, /* Peakl: 350 mA */
-
- [0x0d2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
- [0x0d4] = 0x12, /* Tuple length = 18 bytes */
- [0x0d6] = 0xc2, /* TPCE_INDX = I/O Primary Mode */
- [0x0d8] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */
- [0x0da] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */
- [0x0dc] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
- [0x0de] = 0x55, /* NomV: 5.0 V */
- [0x0e0] = 0x4d, /* MinV: 4.5 V */
- [0x0e2] = 0x5d, /* MaxV: 5.5 V */
- [0x0e4] = 0x4e, /* Peakl: 450 mA */
- [0x0e6] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */
- [0x0e8] = 0x61, /* Range: 2 fields, 2 bytes addr, 1 byte len */
- [0x0ea] = 0xf0, /* Field 1 address = 0x01f0 */
- [0x0ec] = 0x01,
- [0x0ee] = 0x07, /* Address block length = 8 */
- [0x0f0] = 0xf6, /* Field 2 address = 0x03f6 */
- [0x0f2] = 0x03,
- [0x0f4] = 0x01, /* Address block length = 2 */
- [0x0f6] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */
- [0x0f8] = 0x20, /* TPCE_MI = support power down mode */
-
- [0x0fa] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
- [0x0fc] = 0x06, /* Tuple length = 6 bytes */
- [0x0fe] = 0x02, /* TPCE_INDX = I/O Primary Mode, no Default */
- [0x100] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
- [0x102] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
- [0x104] = 0xb5, /* NomV: 3.3 V */
- [0x106] = 0x1e,
- [0x108] = 0x3e, /* Peakl: 350 mA */
-
- [0x10a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
- [0x10c] = 0x12, /* Tuple length = 18 bytes */
- [0x10e] = 0xc3, /* TPCE_INDX = I/O Secondary Mode, Default */
- [0x110] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */
- [0x112] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */
- [0x114] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
- [0x116] = 0x55, /* NomV: 5.0 V */
- [0x118] = 0x4d, /* MinV: 4.5 V */
- [0x11a] = 0x5d, /* MaxV: 5.5 V */
- [0x11c] = 0x4e, /* Peakl: 450 mA */
- [0x11e] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */
- [0x120] = 0x61, /* Range: 2 fields, 2 byte addr, 1 byte len */
- [0x122] = 0x70, /* Field 1 address = 0x0170 */
- [0x124] = 0x01,
- [0x126] = 0x07, /* Address block length = 8 */
- [0x128] = 0x76, /* Field 2 address = 0x0376 */
- [0x12a] = 0x03,
- [0x12c] = 0x01, /* Address block length = 2 */
- [0x12e] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */
- [0x130] = 0x20, /* TPCE_MI = support power down mode */
-
- [0x132] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
- [0x134] = 0x06, /* Tuple length = 6 bytes */
- [0x136] = 0x03, /* TPCE_INDX = I/O Secondary Mode */
- [0x138] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
- [0x13a] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
- [0x13c] = 0xb5, /* NomV: 3.3 V */
- [0x13e] = 0x1e,
- [0x140] = 0x3e, /* Peakl: 350 mA */
-
- [0x142] = CISTPL_NO_LINK, /* No Link */
- [0x144] = 0x00, /* Tuple length = 0 bytes */
-
- [0x146] = CISTPL_END, /* Tuple End */
-};
-
-#define TYPE_DSCM1XXXX "dscm1xxxx"
-
-static int dscm1xxxx_attach(PCMCIACardState *card)
-{
- MicroDriveState *md = MICRODRIVE(card);
- PCMCIACardClass *pcc = PCMCIA_CARD_GET_CLASS(card);
-
- md->attr_base = pcc->cis[0x74] | (pcc->cis[0x76] << 8);
- md->io_base = 0x0;
-
- device_cold_reset(DEVICE(md));
- md_interrupt_update(md);
-
- return 0;
-}
-
-static int dscm1xxxx_detach(PCMCIACardState *card)
-{
- MicroDriveState *md = MICRODRIVE(card);
-
- device_cold_reset(DEVICE(md));
- return 0;
-}
-
-PCMCIACardState *dscm1xxxx_init(DriveInfo *dinfo)
-{
- MicroDriveState *md;
-
- md = MICRODRIVE(object_new(TYPE_DSCM1XXXX));
- qdev_realize(DEVICE(md), NULL, &error_fatal);
-
- if (dinfo != NULL) {
- ide_bus_create_drive(&md->bus, 0, dinfo);
- }
- md->bus.ifs[0].drive_kind = IDE_CFATA;
- md->bus.ifs[0].mdata_size = METADATA_SIZE;
- md->bus.ifs[0].mdata_storage = g_malloc0(METADATA_SIZE);
-
- return PCMCIA_CARD(md);
-}
-
-static void dscm1xxxx_class_init(ObjectClass *oc, void *data)
-{
- PCMCIACardClass *pcc = PCMCIA_CARD_CLASS(oc);
- DeviceClass *dc = DEVICE_CLASS(oc);
-
- pcc->cis = dscm1xxxx_cis;
- pcc->cis_len = sizeof(dscm1xxxx_cis);
-
- pcc->attach = dscm1xxxx_attach;
- pcc->detach = dscm1xxxx_detach;
- /* Reason: Needs to be wired-up in code, see dscm1xxxx_init() */
- dc->user_creatable = false;
-}
-
-static const TypeInfo dscm1xxxx_type_info = {
- .name = TYPE_DSCM1XXXX,
- .parent = TYPE_MICRODRIVE,
- .class_init = dscm1xxxx_class_init,
-};
-
-static void microdrive_realize(DeviceState *dev, Error **errp)
-{
- MicroDriveState *md = MICRODRIVE(dev);
-
- ide_bus_init_output_irq(&md->bus, qemu_allocate_irq(md_set_irq, md, 0));
-}
-
-static void microdrive_init(Object *obj)
-{
- MicroDriveState *md = MICRODRIVE(obj);
-
- ide_bus_init(&md->bus, sizeof(md->bus), DEVICE(obj), 0, 1);
-}
-
-static void microdrive_class_init(ObjectClass *oc, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(oc);
- PCMCIACardClass *pcc = PCMCIA_CARD_CLASS(oc);
-
- pcc->attr_read = md_attr_read;
- pcc->attr_write = md_attr_write;
- pcc->common_read = md_common_read;
- pcc->common_write = md_common_write;
- pcc->io_read = md_common_read;
- pcc->io_write = md_common_write;
-
- dc->realize = microdrive_realize;
- device_class_set_legacy_reset(dc, md_reset);
- dc->vmsd = &vmstate_microdrive;
-}
-
-static const TypeInfo microdrive_type_info = {
- .name = TYPE_MICRODRIVE,
- .parent = TYPE_PCMCIA_CARD,
- .instance_size = sizeof(MicroDriveState),
- .instance_init = microdrive_init,
- .abstract = true,
- .class_init = microdrive_class_init,
-};
-
-static void microdrive_register_types(void)
-{
- type_register_static(&microdrive_type_info);
- type_register_static(&dscm1xxxx_type_info);
-}
-
-type_init(microdrive_register_types)
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index bdb13b0..ea1d1b3 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -781,7 +781,7 @@ static void icv_activate_irq(GICv3CPUState *cs, int idx, int grp)
if (nmi) {
cs->ich_apr[grp][regno] |= ICV_AP1R_EL1_NMI;
} else {
- cs->ich_apr[grp][regno] |= (1 << regbit);
+ cs->ich_apr[grp][regno] |= (1U << regbit);
}
}
@@ -793,7 +793,7 @@ static void icv_activate_vlpi(GICv3CPUState *cs)
int regno = aprbit / 32;
int regbit = aprbit % 32;
- cs->ich_apr[cs->hppvlpi.grp][regno] |= (1 << regbit);
+ cs->ich_apr[cs->hppvlpi.grp][regno] |= (1U << regbit);
gicv3_redist_vlpi_pending(cs, cs->hppvlpi.irq, 0);
}
@@ -1170,7 +1170,7 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq)
if (nmi) {
cs->icc_apr[cs->hppi.grp][regno] |= ICC_AP1R_EL1_NMI;
} else {
- cs->icc_apr[cs->hppi.grp][regno] |= (1 << regbit);
+ cs->icc_apr[cs->hppi.grp][regno] |= (1U << regbit);
}
if (irq < GIC_INTERNAL) {
diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c
index a48e6fc..a98358d 100644
--- a/hw/intc/omap_intc.c
+++ b/hw/intc/omap_intc.c
@@ -392,22 +392,15 @@ static void omap_intc_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo omap_intc_info = {
- .name = "omap-intc",
- .parent = TYPE_OMAP_INTC,
- .instance_init = omap_intc_init,
- .class_init = omap_intc_class_init,
-};
-
-static const TypeInfo omap_intc_type_info = {
.name = TYPE_OMAP_INTC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(OMAPIntcState),
- .abstract = true,
+ .instance_init = omap_intc_init,
+ .class_init = omap_intc_class_init,
};
static void omap_intc_register_types(void)
{
- type_register_static(&omap_intc_type_info);
type_register_static(&omap_intc_info);
}
diff --git a/hw/meson.build b/hw/meson.build
index e86badc..b827c82 100644
--- a/hw/meson.build
+++ b/hw/meson.build
@@ -27,7 +27,6 @@ subdir('nvram')
subdir('pci')
subdir('pci-bridge')
subdir('pci-host')
-subdir('pcmcia')
subdir('rtc')
subdir('scsi')
subdir('sd')
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 1e08785..1f1baa5 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -74,7 +74,6 @@ config IVSHMEM_DEVICE
config ECCMEMCTL
bool
- select ECC
config IMX
bool
@@ -82,6 +81,9 @@ config IMX
select SSI
select USB_EHCI_SYSBUS
+config STM32_RCC
+ bool
+
config STM32F2XX_SYSCFG
bool
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index a295195..d02d96e 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -100,6 +100,7 @@ system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files(
system_ss.add(when: 'CONFIG_XLNX_VERSAL_TRNG', if_true: files(
'xlnx-versal-trng.c',
))
+system_ss.add(when: 'CONFIG_STM32_RCC', if_true: files('stm32_rcc.c'))
system_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
system_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c'))
diff --git a/hw/misc/stm32_rcc.c b/hw/misc/stm32_rcc.c
new file mode 100644
index 0000000..26672b5
--- /dev/null
+++ b/hw/misc/stm32_rcc.c
@@ -0,0 +1,162 @@
+/*
+ * STM32 RCC (only reset and enable registers are implemented)
+ *
+ * Copyright (c) 2024 Román Cárdenas <rcardenas.rod@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/stm32_rcc.h"
+
+static void stm32_rcc_reset(DeviceState *dev)
+{
+ STM32RccState *s = STM32_RCC(dev);
+
+ for (int i = 0; i < STM32_RCC_NREGS; i++) {
+ s->regs[i] = 0;
+ }
+}
+
+static uint64_t stm32_rcc_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ STM32RccState *s = STM32_RCC(opaque);
+
+ uint32_t value = 0;
+ if (addr > STM32_RCC_DCKCFGR2) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
+ __func__, addr);
+ } else {
+ value = s->regs[addr >> 2];
+ }
+ trace_stm32_rcc_read(addr, value);
+ return value;
+}
+
+static void stm32_rcc_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ STM32RccState *s = STM32_RCC(opaque);
+ uint32_t value = val64;
+ uint32_t prev_value, new_value, irq_offset;
+
+ trace_stm32_rcc_write(value, addr);
+
+ if (addr > STM32_RCC_DCKCFGR2) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
+ __func__, addr);
+ return;
+ }
+
+ switch (addr) {
+ case STM32_RCC_AHB1_RSTR...STM32_RCC_APB2_RSTR:
+ prev_value = s->regs[addr / 4];
+ s->regs[addr / 4] = value;
+
+ irq_offset = ((addr - STM32_RCC_AHB1_RSTR) / 4) * 32;
+ for (int i = 0; i < 32; i++) {
+ new_value = extract32(value, i, 1);
+ if (extract32(prev_value, i, 1) && !new_value) {
+ trace_stm32_rcc_pulse_reset(irq_offset + i, new_value);
+ qemu_set_irq(s->reset_irq[irq_offset + i], new_value);
+ }
+ }
+ return;
+ case STM32_RCC_AHB1_ENR...STM32_RCC_APB2_ENR:
+ prev_value = s->regs[addr / 4];
+ s->regs[addr / 4] = value;
+
+ irq_offset = ((addr - STM32_RCC_AHB1_ENR) / 4) * 32;
+ for (int i = 0; i < 32; i++) {
+ new_value = extract32(value, i, 1);
+ if (!extract32(prev_value, i, 1) && new_value) {
+ trace_stm32_rcc_pulse_enable(irq_offset + i, new_value);
+ qemu_set_irq(s->enable_irq[irq_offset + i], new_value);
+ }
+ }
+ return;
+ default:
+ qemu_log_mask(
+ LOG_UNIMP,
+ "%s: The RCC peripheral only supports enable and reset in QEMU\n",
+ __func__
+ );
+ s->regs[addr >> 2] = value;
+ }
+}
+
+static const MemoryRegionOps stm32_rcc_ops = {
+ .read = stm32_rcc_read,
+ .write = stm32_rcc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void stm32_rcc_init(Object *obj)
+{
+ STM32RccState *s = STM32_RCC(obj);
+
+ memory_region_init_io(&s->mmio, obj, &stm32_rcc_ops, s,
+ TYPE_STM32_RCC, STM32_RCC_PERIPHERAL_SIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+ qdev_init_gpio_out(DEVICE(obj), s->reset_irq, STM32_RCC_NIRQS);
+ qdev_init_gpio_out(DEVICE(obj), s->enable_irq, STM32_RCC_NIRQS);
+
+ for (int i = 0; i < STM32_RCC_NIRQS; i++) {
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->reset_irq[i]);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->enable_irq[i]);
+ }
+}
+
+static const VMStateDescription vmstate_stm32_rcc = {
+ .name = TYPE_STM32_RCC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, STM32RccState, STM32_RCC_NREGS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void stm32_rcc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_stm32_rcc;
+ device_class_set_legacy_reset(dc, stm32_rcc_reset);
+}
+
+static const TypeInfo stm32_rcc_info = {
+ .name = TYPE_STM32_RCC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(STM32RccState),
+ .instance_init = stm32_rcc_init,
+ .class_init = stm32_rcc_class_init,
+};
+
+static void stm32_rcc_register_types(void)
+{
+ type_register_static(&stm32_rcc_info);
+}
+
+type_init(stm32_rcc_register_types)
diff --git a/hw/misc/stm32l4x5_syscfg.c b/hw/misc/stm32l4x5_syscfg.c
index a5a1ce2..a947a9e 100644
--- a/hw/misc/stm32l4x5_syscfg.c
+++ b/hw/misc/stm32l4x5_syscfg.c
@@ -26,6 +26,9 @@
#include "trace.h"
#include "hw/irq.h"
#include "migration/vmstate.h"
+#include "hw/clock.h"
+#include "hw/qdev-clock.h"
+#include "qapi/error.h"
#include "hw/misc/stm32l4x5_syscfg.h"
#include "hw/gpio/stm32l4x5_gpio.h"
@@ -225,12 +228,22 @@ static void stm32l4x5_syscfg_init(Object *obj)
qdev_init_gpio_in(DEVICE(obj), stm32l4x5_syscfg_set_irq,
GPIO_NUM_PINS * NUM_GPIOS);
qdev_init_gpio_out(DEVICE(obj), s->gpio_out, GPIO_NUM_PINS);
+ s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0);
+}
+
+static void stm32l4x5_syscfg_realize(DeviceState *dev, Error **errp)
+{
+ Stm32l4x5SyscfgState *s = STM32L4X5_SYSCFG(dev);
+ if (!clock_has_source(s->clk)) {
+ error_setg(errp, "SYSCFG: clk input must be connected");
+ return;
+ }
}
static const VMStateDescription vmstate_stm32l4x5_syscfg = {
.name = TYPE_STM32L4X5_SYSCFG,
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_UINT32(memrmp, Stm32l4x5SyscfgState),
VMSTATE_UINT32(cfgr1, Stm32l4x5SyscfgState),
@@ -241,6 +254,7 @@ static const VMStateDescription vmstate_stm32l4x5_syscfg = {
VMSTATE_UINT32(swpr, Stm32l4x5SyscfgState),
VMSTATE_UINT32(skr, Stm32l4x5SyscfgState),
VMSTATE_UINT32(swpr2, Stm32l4x5SyscfgState),
+ VMSTATE_CLOCK(clk, Stm32l4x5SyscfgState),
VMSTATE_END_OF_LIST()
}
};
@@ -251,6 +265,7 @@ static void stm32l4x5_syscfg_class_init(ObjectClass *klass, void *data)
ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->vmsd = &vmstate_stm32l4x5_syscfg;
+ dc->realize = stm32l4x5_syscfg_realize;
rc->phases.hold = stm32l4x5_syscfg_hold_reset;
}
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 1be0717..b9fbcb0 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -156,6 +156,12 @@ npcm7xx_pwm_write(const char *id, uint64_t offset, uint32_t value) "%s offset: 0
npcm7xx_pwm_update_freq(const char *id, uint8_t index, uint32_t old_value, uint32_t new_value) "%s pwm[%u] Update Freq: old_freq: %u, new_freq: %u"
npcm7xx_pwm_update_duty(const char *id, uint8_t index, uint32_t old_value, uint32_t new_value) "%s pwm[%u] Update Duty: old_duty: %u, new_duty: %u"
+# stm32_rcc.c
+stm32_rcc_read(uint64_t addr, uint64_t data) "reg read: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
+stm32_rcc_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
+stm32_rcc_pulse_enable(int line, int level) "Enable: %d to %d"
+stm32_rcc_pulse_reset(int line, int level) "Reset: %d to %d"
+
# stm32f4xx_syscfg.c
stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interrupt: GPIO: %d, Line: %d; Level: %d"
stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"
diff --git a/hw/pcmcia/Kconfig b/hw/pcmcia/Kconfig
deleted file mode 100644
index 41f2df9..0000000
--- a/hw/pcmcia/Kconfig
+++ /dev/null
@@ -1,2 +0,0 @@
-config PCMCIA
- bool
diff --git a/hw/pcmcia/meson.build b/hw/pcmcia/meson.build
deleted file mode 100644
index edcb7f5..0000000
--- a/hw/pcmcia/meson.build
+++ /dev/null
@@ -1 +0,0 @@
-system_ss.add(when: 'CONFIG_PCMCIA', if_true: files('pcmcia.c'))
diff --git a/hw/pcmcia/pcmcia.c b/hw/pcmcia/pcmcia.c
deleted file mode 100644
index 03d13e7..0000000
--- a/hw/pcmcia/pcmcia.c
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * PCMCIA emulation
- *
- * Copyright 2013 SUSE LINUX Products GmbH
- */
-
-#include "qemu/osdep.h"
-#include "qemu/module.h"
-#include "hw/pcmcia.h"
-
-static const TypeInfo pcmcia_card_type_info = {
- .name = TYPE_PCMCIA_CARD,
- .parent = TYPE_DEVICE,
- .instance_size = sizeof(PCMCIACardState),
- .abstract = true,
- .class_size = sizeof(PCMCIACardClass),
-};
-
-static void pcmcia_register_types(void)
-{
- type_register_static(&pcmcia_card_type_info);
-}
-
-type_init(pcmcia_register_types)
diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig
index 8d180de..1bd5646 100644
--- a/hw/ssi/Kconfig
+++ b/hw/ssi/Kconfig
@@ -28,3 +28,7 @@ config BCM2835_SPI
config PNV_SPI
bool
select SSI
+
+config ALLWINNER_A10_SPI
+ bool
+ select SSI
diff --git a/hw/ssi/allwinner-a10-spi.c b/hw/ssi/allwinner-a10-spi.c
new file mode 100644
index 0000000..3eb50b4
--- /dev/null
+++ b/hw/ssi/allwinner-a10-spi.c
@@ -0,0 +1,561 @@
+/*
+ * Allwinner SPI Bus Serial Interface Emulation
+ *
+ * Copyright (C) 2024 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/ssi/allwinner-a10-spi.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "trace.h"
+
+/* Allwinner SPI memory map */
+#define SPI_RXDATA_REG 0x00 /* receive data register */
+#define SPI_TXDATA_REG 0x04 /* transmit data register */
+#define SPI_CTL_REG 0x08 /* control register */
+#define SPI_INTCTL_REG 0x0c /* interrupt control register */
+#define SPI_INT_STA_REG 0x10 /* interrupt status register */
+#define SPI_DMACTL_REG 0x14 /* DMA control register */
+#define SPI_WAIT_REG 0x18 /* wait clock counter register */
+#define SPI_CCTL_REG 0x1c /* clock rate control register */
+#define SPI_BC_REG 0x20 /* burst control register */
+#define SPI_TC_REG 0x24 /* transmit counter register */
+#define SPI_FIFO_STA_REG 0x28 /* FIFO status register */
+
+/* Data register */
+#define SPI_DATA_RESET 0
+
+/* Control register */
+#define SPI_CTL_SDC (1 << 19)
+#define SPI_CTL_TP_EN (1 << 18)
+#define SPI_CTL_SS_LEVEL (1 << 17)
+#define SPI_CTL_SS_CTRL (1 << 16)
+#define SPI_CTL_DHB (1 << 15)
+#define SPI_CTL_DDB (1 << 14)
+#define SPI_CTL_SS (3 << 12)
+#define SPI_CTL_SS_SHIFT 12
+#define SPI_CTL_RPSM (1 << 11)
+#define SPI_CTL_XCH (1 << 10)
+#define SPI_CTL_RF_RST (1 << 9)
+#define SPI_CTL_TF_RST (1 << 8)
+#define SPI_CTL_SSCTL (1 << 7)
+#define SPI_CTL_LMTF (1 << 6)
+#define SPI_CTL_DMAMC (1 << 5)
+#define SPI_CTL_SSPOL (1 << 4)
+#define SPI_CTL_POL (1 << 3)
+#define SPI_CTL_PHA (1 << 2)
+#define SPI_CTL_MODE (1 << 1)
+#define SPI_CTL_EN (1 << 0)
+#define SPI_CTL_MASK 0xFFFFFu
+#define SPI_CTL_RESET 0x0002001Cu
+
+/* Interrupt control register */
+#define SPI_INTCTL_SS_INT_EN (1 << 17)
+#define SPI_INTCTL_TX_INT_EN (1 << 16)
+#define SPI_INTCTL_TF_UR_INT_EN (1 << 14)
+#define SPI_INTCTL_TF_OF_INT_EN (1 << 13)
+#define SPI_INTCTL_TF_E34_INT_EN (1 << 12)
+#define SPI_INTCTL_TF_E14_INT_EN (1 << 11)
+#define SPI_INTCTL_TF_FL_INT_EN (1 << 10)
+#define SPI_INTCTL_TF_HALF_EMP_INT_EN (1 << 9)
+#define SPI_INTCTL_TF_EMP_INT_EN (1 << 8)
+#define SPI_INTCTL_RF_UR_INT_EN (1 << 6)
+#define SPI_INTCTL_RF_OF_INT_EN (1 << 5)
+#define SPI_INTCTL_RF_E34_INT_EN (1 << 4)
+#define SPI_INTCTL_RF_E14_INT_EN (1 << 3)
+#define SPI_INTCTL_RF_FU_INT_EN (1 << 2)
+#define SPI_INTCTL_RF_HALF_FU_INT_EN (1 << 1)
+#define SPI_INTCTL_RF_RDY_INT_EN (1 << 0)
+#define SPI_INTCTL_MASK 0x37F7Fu
+#define SPI_INTCTL_RESET 0
+
+/* Interrupt status register */
+#define SPI_INT_STA_INT_CBF (1 << 31)
+#define SPI_INT_STA_SSI (1 << 17)
+#define SPI_INT_STA_TC (1 << 16)
+#define SPI_INT_STA_TU (1 << 14)
+#define SPI_INT_STA_TO (1 << 13)
+#define SPI_INT_STA_TE34 (1 << 12)
+#define SPI_INT_STA_TE14 (1 << 11)
+#define SPI_INT_STA_TF (1 << 10)
+#define SPI_INT_STA_THE (1 << 9)
+#define SPI_INT_STA_TE (1 << 8)
+#define SPI_INT_STA_RU (1 << 6)
+#define SPI_INT_STA_RO (1 << 5)
+#define SPI_INT_STA_RF34 (1 << 4)
+#define SPI_INT_STA_RF14 (1 << 3)
+#define SPI_INT_STA_RF (1 << 2)
+#define SPI_INT_STA_RHF (1 << 1)
+#define SPI_INT_STA_RR (1 << 0)
+#define SPI_INT_STA_MASK 0x80037F7Fu
+#define SPI_INT_STA_RESET 0x00001B00u
+
+/* DMA control register - not implemented */
+#define SPI_DMACTL_RESET 0
+
+/* Wait clock register */
+#define SPI_WAIT_REG_WCC_MASK 0xFFFFu
+#define SPI_WAIT_RESET 0
+
+/* Clock control register - not implemented */
+#define SPI_CCTL_RESET 2
+
+/* Burst count register */
+#define SPI_BC_BC_MASK 0xFFFFFFu
+#define SPI_BC_RESET 0
+
+/* Transmi counter register */
+#define SPI_TC_WTC_MASK 0xFFFFFFu
+#define SPI_TC_RESET 0
+
+/* FIFO status register */
+#define SPI_FIFO_STA_CNT_MASK 0x7F
+#define SPI_FIFO_STA_TF_CNT_SHIFT 16
+#define SPI_FIFO_STA_RF_CNT_SHIFT 0
+#define SPI_FIFO_STA_RESET 0
+
+#define REG_INDEX(offset) (offset / sizeof(uint32_t))
+
+
+static const char *allwinner_a10_spi_get_regname(unsigned offset)
+{
+ switch (offset) {
+ case SPI_RXDATA_REG:
+ return "RXDATA";
+ case SPI_TXDATA_REG:
+ return "TXDATA";
+ case SPI_CTL_REG:
+ return "CTL";
+ case SPI_INTCTL_REG:
+ return "INTCTL";
+ case SPI_INT_STA_REG:
+ return "INT_STA";
+ case SPI_DMACTL_REG:
+ return "DMACTL";
+ case SPI_WAIT_REG:
+ return "WAIT";
+ case SPI_CCTL_REG:
+ return "CCTL";
+ case SPI_BC_REG:
+ return "BC";
+ case SPI_TC_REG:
+ return "TC";
+ case SPI_FIFO_STA_REG:
+ return "FIFO_STA";
+ default:
+ return "[?]";
+ }
+}
+
+static bool allwinner_a10_spi_is_enabled(AWA10SPIState *s)
+{
+ return s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_EN;
+}
+
+static void allwinner_a10_spi_txfifo_reset(AWA10SPIState *s)
+{
+ fifo8_reset(&s->tx_fifo);
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= (SPI_INT_STA_TE | SPI_INT_STA_TE14 |
+ SPI_INT_STA_THE | SPI_INT_STA_TE34);
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~(SPI_INT_STA_TU | SPI_INT_STA_TO);
+}
+
+static void allwinner_a10_spi_rxfifo_reset(AWA10SPIState *s)
+{
+ fifo8_reset(&s->rx_fifo);
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &=
+ ~(SPI_INT_STA_RU | SPI_INT_STA_RO | SPI_INT_STA_RF | SPI_INT_STA_RR |
+ SPI_INT_STA_RHF | SPI_INT_STA_RF14 | SPI_INT_STA_RF34);
+}
+
+static uint8_t allwinner_a10_spi_selected_channel(AWA10SPIState *s)
+{
+ return (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_SS) >> SPI_CTL_SS_SHIFT;
+}
+
+static void allwinner_a10_spi_reset_hold(Object *obj, ResetType type)
+{
+ AWA10SPIState *s = AW_A10_SPI(obj);
+
+ s->regs[REG_INDEX(SPI_RXDATA_REG)] = SPI_DATA_RESET;
+ s->regs[REG_INDEX(SPI_TXDATA_REG)] = SPI_DATA_RESET;
+ s->regs[REG_INDEX(SPI_CTL_REG)] = SPI_CTL_RESET;
+ s->regs[REG_INDEX(SPI_INTCTL_REG)] = SPI_INTCTL_RESET;
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] = SPI_INT_STA_RESET;
+ s->regs[REG_INDEX(SPI_DMACTL_REG)] = SPI_DMACTL_RESET;
+ s->regs[REG_INDEX(SPI_WAIT_REG)] = SPI_WAIT_RESET;
+ s->regs[REG_INDEX(SPI_CCTL_REG)] = SPI_CCTL_RESET;
+ s->regs[REG_INDEX(SPI_BC_REG)] = SPI_BC_RESET;
+ s->regs[REG_INDEX(SPI_TC_REG)] = SPI_TC_RESET;
+ s->regs[REG_INDEX(SPI_FIFO_STA_REG)] = SPI_FIFO_STA_RESET;
+
+ allwinner_a10_spi_txfifo_reset(s);
+ allwinner_a10_spi_rxfifo_reset(s);
+}
+
+static void allwinner_a10_spi_update_irq(AWA10SPIState *s)
+{
+ bool level;
+
+ if (fifo8_is_empty(&s->rx_fifo)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RR;
+ } else {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RR;
+ }
+
+ if (fifo8_num_used(&s->rx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 2)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF14;
+ } else {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RF14;
+ }
+
+ if (fifo8_num_used(&s->rx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 1)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RHF;
+ } else {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RHF;
+ }
+
+ if (fifo8_num_free(&s->rx_fifo) <= (AW_A10_SPI_FIFO_SIZE >> 2)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF34;
+ } else {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RF34;
+ }
+
+ if (fifo8_is_full(&s->rx_fifo)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF;
+ } else {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RF;
+ }
+
+ if (fifo8_is_empty(&s->tx_fifo)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TE;
+ } else {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TE;
+ }
+
+ if (fifo8_num_free(&s->tx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 2)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TE14;
+ } else {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TE14;
+ }
+
+ if (fifo8_num_free(&s->tx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 1)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_THE;
+ } else {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_THE;
+ }
+
+ if (fifo8_num_used(&s->tx_fifo) <= (AW_A10_SPI_FIFO_SIZE >> 2)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TE34;
+ } else {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TE34;
+ }
+
+ if (fifo8_is_full(&s->rx_fifo)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TF;
+ } else {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TF;
+ }
+
+ level = (s->regs[REG_INDEX(SPI_INT_STA_REG)] &
+ s->regs[REG_INDEX(SPI_INTCTL_REG)]) != 0;
+
+ qemu_set_irq(s->irq, level);
+
+ trace_allwinner_a10_spi_update_irq(level);
+}
+
+static void allwinner_a10_spi_flush_txfifo(AWA10SPIState *s)
+{
+ uint32_t burst_count = s->regs[REG_INDEX(SPI_BC_REG)];
+ uint32_t tx_burst = s->regs[REG_INDEX(SPI_TC_REG)];
+ trace_allwinner_a10_spi_burst_length(tx_burst);
+
+ trace_allwinner_a10_spi_flush_txfifo_begin(fifo8_num_used(&s->tx_fifo),
+ fifo8_num_used(&s->rx_fifo));
+
+ while (!fifo8_is_empty(&s->tx_fifo)) {
+ uint8_t tx = fifo8_pop(&s->tx_fifo);
+ uint8_t rx = 0;
+ bool fill_rx = true;
+
+ trace_allwinner_a10_spi_tx(tx);
+
+ /* Write one byte at a time */
+ rx = ssi_transfer(s->bus, tx);
+
+ trace_allwinner_a10_spi_rx(rx);
+
+ /* Check DHB here to determine if RX bytes should be stored */
+ if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_DHB) {
+ /* Store rx bytes only after WTC transfers */
+ if (tx_burst > 0u) {
+ fill_rx = false;
+ tx_burst--;
+ }
+ }
+
+ if (fill_rx) {
+ if (fifo8_is_full(&s->rx_fifo)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF;
+ } else {
+ fifo8_push(&s->rx_fifo, rx);
+ }
+ }
+
+ allwinner_a10_spi_update_irq(s);
+
+ burst_count--;
+
+ if (burst_count == 0) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TC;
+ s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_XCH;
+ break;
+ }
+ }
+
+ if (fifo8_is_empty(&s->tx_fifo)) {
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TC;
+ s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_XCH;
+ }
+
+ trace_allwinner_a10_spi_flush_txfifo_end(fifo8_num_used(&s->tx_fifo),
+ fifo8_num_used(&s->rx_fifo));
+}
+
+static uint64_t allwinner_a10_spi_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ uint32_t value = 0;
+ AWA10SPIState *s = opaque;
+ uint32_t index = offset >> 2;
+
+ if (offset > SPI_FIFO_STA_REG) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "[%s]%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
+ TYPE_AW_A10_SPI, __func__, offset);
+ return 0;
+ }
+
+ value = s->regs[index];
+
+ if (allwinner_a10_spi_is_enabled(s)) {
+ switch (offset) {
+ case SPI_RXDATA_REG:
+ if (fifo8_is_empty(&s->rx_fifo)) {
+ /* value is undefined */
+ value = 0xdeadbeef;
+ } else {
+ /* read from the RX FIFO */
+ value = fifo8_pop(&s->rx_fifo);
+ }
+ break;
+ case SPI_TXDATA_REG:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "[%s]%s: Trying to read from TX FIFO\n",
+ TYPE_AW_A10_SPI, __func__);
+
+ /* Reading from TXDATA gives 0 */
+ break;
+ case SPI_FIFO_STA_REG:
+ /* Read current tx/rx fifo data count */
+ value = fifo8_num_used(&s->tx_fifo) << SPI_FIFO_STA_TF_CNT_SHIFT |
+ fifo8_num_used(&s->rx_fifo) << SPI_FIFO_STA_RF_CNT_SHIFT;
+ break;
+ case SPI_CTL_REG:
+ case SPI_INTCTL_REG:
+ case SPI_INT_STA_REG:
+ case SPI_DMACTL_REG:
+ case SPI_WAIT_REG:
+ case SPI_CCTL_REG:
+ case SPI_BC_REG:
+ case SPI_TC_REG:
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__,
+ (uint32_t)offset);
+ break;
+ }
+
+ allwinner_a10_spi_update_irq(s);
+ }
+ trace_allwinner_a10_spi_read(allwinner_a10_spi_get_regname(offset), value);
+
+ return value;
+}
+
+static bool allwinner_a10_spi_update_cs_level(AWA10SPIState *s, int cs_line_nr)
+{
+ if (cs_line_nr == allwinner_a10_spi_selected_channel(s)) {
+ return (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_SS_LEVEL) != 0;
+ } else {
+ return (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_SSPOL) != 0;
+ }
+}
+
+static void allwinner_a10_spi_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ AWA10SPIState *s = opaque;
+ uint32_t index = offset >> 2;
+ int i = 0;
+
+ if (offset > SPI_FIFO_STA_REG) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "[%s]%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
+ TYPE_AW_A10_SPI, __func__, offset);
+ return;
+ }
+
+ trace_allwinner_a10_spi_write(allwinner_a10_spi_get_regname(offset),
+ (uint32_t)value);
+
+ if (!allwinner_a10_spi_is_enabled(s)) {
+ /* Block is disabled */
+ if (offset != SPI_CTL_REG) {
+ /* Ignore access */
+ return;
+ }
+ }
+
+ switch (offset) {
+ case SPI_RXDATA_REG:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to write to RX FIFO\n",
+ TYPE_AW_A10_SPI, __func__);
+ break;
+ case SPI_TXDATA_REG:
+ if (fifo8_is_full(&s->tx_fifo)) {
+ /* Ignore writes if queue is full */
+ break;
+ }
+
+ fifo8_push(&s->tx_fifo, (uint8_t)value);
+
+ break;
+ case SPI_INT_STA_REG:
+ /* Handle W1C bits - everything except SPI_INT_STA_INT_CBF. */
+ value &= ~SPI_INT_STA_INT_CBF;
+ s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~(value & SPI_INT_STA_MASK);
+ break;
+ case SPI_CTL_REG:
+ s->regs[REG_INDEX(SPI_CTL_REG)] = value;
+
+ for (i = 0; i < AW_A10_SPI_CS_LINES_NR; i++) {
+ qemu_set_irq(
+ s->cs_lines[i],
+ allwinner_a10_spi_update_cs_level(s, i));
+ }
+
+ if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_XCH) {
+ /* Request to start emitting */
+ allwinner_a10_spi_flush_txfifo(s);
+ }
+ if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_TF_RST) {
+ allwinner_a10_spi_txfifo_reset(s);
+ s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_TF_RST;
+ }
+ if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_RF_RST) {
+ allwinner_a10_spi_rxfifo_reset(s);
+ s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_RF_RST;
+ }
+ break;
+ case SPI_INTCTL_REG:
+ case SPI_DMACTL_REG:
+ case SPI_WAIT_REG:
+ case SPI_CCTL_REG:
+ case SPI_BC_REG:
+ case SPI_TC_REG:
+ case SPI_FIFO_STA_REG:
+ s->regs[index] = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__,
+ (uint32_t)offset);
+ break;
+ }
+
+ allwinner_a10_spi_update_irq(s);
+}
+
+static const MemoryRegionOps allwinner_a10_spi_ops = {
+ .read = allwinner_a10_spi_read,
+ .write = allwinner_a10_spi_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription allwinner_a10_spi_vmstate = {
+ .name = TYPE_AW_A10_SPI,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_FIFO8(tx_fifo, AWA10SPIState),
+ VMSTATE_FIFO8(rx_fifo, AWA10SPIState),
+ VMSTATE_UINT32_ARRAY(regs, AWA10SPIState, AW_A10_SPI_REGS_NUM),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void allwinner_a10_spi_realize(DeviceState *dev, Error **errp)
+{
+ AWA10SPIState *s = AW_A10_SPI(dev);
+ int i = 0;
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_spi_ops, s,
+ TYPE_AW_A10_SPI, AW_A10_SPI_IOSIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+
+ s->bus = ssi_create_bus(dev, "spi");
+ for (i = 0; i < AW_A10_SPI_CS_LINES_NR; i++) {
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]);
+ }
+ fifo8_create(&s->tx_fifo, AW_A10_SPI_FIFO_SIZE);
+ fifo8_create(&s->rx_fifo, AW_A10_SPI_FIFO_SIZE);
+}
+
+static void allwinner_a10_spi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->phases.hold = allwinner_a10_spi_reset_hold;
+ dc->vmsd = &allwinner_a10_spi_vmstate;
+ dc->realize = allwinner_a10_spi_realize;
+ dc->desc = "Allwinner A10 SPI Controller";
+}
+
+static const TypeInfo allwinner_a10_spi_type_info = {
+ .name = TYPE_AW_A10_SPI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AWA10SPIState),
+ .class_init = allwinner_a10_spi_class_init,
+};
+
+static void allwinner_a10_spi_register_types(void)
+{
+ type_register_static(&allwinner_a10_spi_type_info);
+}
+
+type_init(allwinner_a10_spi_register_types)
diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
index 23cd425..6afb1ea 100644
--- a/hw/ssi/meson.build
+++ b/hw/ssi/meson.build
@@ -1,3 +1,4 @@
+system_ss.add(when: 'CONFIG_ALLWINNER_A10_SPI', if_true: files('allwinner-a10-spi.c'))
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c'))
system_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c', 'npcm_pspi.c'))
diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
index 089d269..2f36cf9 100644
--- a/hw/ssi/trace-events
+++ b/hw/ssi/trace-events
@@ -53,3 +53,13 @@ pnv_spi_rx_read_N2frame(void) ""
pnv_spi_shift_rx(uint8_t byte, uint32_t index) "byte = 0x%2.2x into RDR from payload index %d"
pnv_spi_sequencer_stop_requested(const char* reason) "due to %s"
pnv_spi_RDR_match(const char* result) "%s"
+
+# allwinner_a10_spi.c
+allwinner_a10_spi_update_irq(uint32_t level) "IRQ level is %d"
+allwinner_a10_spi_flush_txfifo_begin(uint32_t tx, uint32_t rx) "Begin: TX Fifo Size = %d, RX Fifo Size = %d"
+allwinner_a10_spi_flush_txfifo_end(uint32_t tx, uint32_t rx) "End: TX Fifo Size = %d, RX Fifo Size = %d"
+allwinner_a10_spi_burst_length(uint32_t len) "Burst length = %d"
+allwinner_a10_spi_tx(uint8_t byte) "write 0x%02x"
+allwinner_a10_spi_rx(uint8_t byte) "read 0x%02x"
+allwinner_a10_spi_read(const char* regname, uint32_t value) "reg[%s] => 0x%08x"
+allwinner_a10_spi_write(const char* regname, uint32_t value) "reg[%s] <= 0x%08x"