From: "Philippe Mathieu-Daudé" <philmd@redhat.com>
To: Laurent Vivier <laurent@vivier.eu>, qemu-devel@nongnu.org
Subject: Re: [PATCH 2/3] q800: add djMEMC memory controller
Date: Thu, 12 Dec 2019 23:07:42 +0100 [thread overview]
Message-ID: <262d4edd-8a91-c6f2-cb99-3ffa20231638@redhat.com> (raw)
In-Reply-To: <20191212200142.15688-3-laurent@vivier.eu>
On 12/12/19 9:01 PM, Laurent Vivier wrote:
> Current implementation is based on GLUE, an early implementation
> of the memory controller found in Macintosh II series.
>
> Quadra 800 uses in fact djMEMC:
>
> The djMEMC is an Apple custom integrated circuit chip that performs a
> variety of functions (RAM management, clock generation, ...).
> It receives interrupt requests from various devices, assign priority to
> each, and asserts one or more interrupt line to the CPU.
>
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
> MAINTAINERS | 2 +
> hw/m68k/Kconfig | 1 +
> hw/m68k/q800.c | 61 ++++----------
> hw/misc/Kconfig | 3 +
> hw/misc/Makefile.objs | 1 +
> hw/misc/djmemc.c | 176 +++++++++++++++++++++++++++++++++++++++
> hw/misc/trace-events | 4 +
> include/hw/misc/djmemc.h | 34 ++++++++
> 8 files changed, 237 insertions(+), 45 deletions(-)
> create mode 100644 hw/misc/djmemc.c
> create mode 100644 include/hw/misc/djmemc.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5e5e3e52d6..07224a2fa2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -925,11 +925,13 @@ F: hw/misc/mac_via.c
> F: hw/nubus/*
> F: hw/display/macfb.c
> F: hw/block/swim.c
> +F: hw/misc/djmemc.c
> F: hw/m68k/bootinfo.h
> F: include/hw/misc/mac_via.h
> F: include/hw/nubus/*
> F: include/hw/display/macfb.h
> F: include/hw/block/swim.h
> +F: include/hw/misc/djmemc.c
>
> MicroBlaze Machines
> -------------------
> diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig
> index c757e7dfa4..bdc43a798a 100644
> --- a/hw/m68k/Kconfig
> +++ b/hw/m68k/Kconfig
> @@ -22,3 +22,4 @@ config Q800
> select ESCC
> select ESP
> select DP8393X
> + select DJMEMC
> diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c
> index ef0014f4c4..9ee0cb1141 100644
> --- a/hw/m68k/q800.c
> +++ b/hw/m68k/q800.c
> @@ -46,6 +46,7 @@
> #include "sysemu/qtest.h"
> #include "sysemu/runstate.h"
> #include "sysemu/reset.h"
> +#include "hw/misc/djmemc.h"
>
> #define MACROM_ADDR 0x40000000
> #define MACROM_SIZE 0x00100000
> @@ -68,6 +69,7 @@
> #define SONIC_PROM_BASE (IO_BASE + 0x08000)
> #define SONIC_BASE (IO_BASE + 0x0a000)
> #define SCC_BASE (IO_BASE + 0x0c000)
> +#define DJMEMC_BASE (IO_BASE + 0x0e000)
> #define ESP_BASE (IO_BASE + 0x10000)
> #define ESP_PDMA (IO_BASE + 0x10100)
> #define ASC_BASE (IO_BASE + 0x14000)
> @@ -85,39 +87,6 @@
>
> #define MAC_CLOCK 3686418
>
> -/*
> - * The GLUE (General Logic Unit) is an Apple custom integrated circuit chip
> - * that performs a variety of functions (RAM management, clock generation, ...).
> - * The GLUE chip receives interrupt requests from various devices,
> - * assign priority to each, and asserts one or more interrupt line to the
> - * CPU.
> - */
> -
> -typedef struct {
> - M68kCPU *cpu;
> - uint8_t ipr;
> -} GLUEState;
> -
> -static void GLUE_set_irq(void *opaque, int irq, int level)
> -{
> - GLUEState *s = opaque;
> - int i;
> -
> - if (level) {
> - s->ipr |= 1 << irq;
> - } else {
> - s->ipr &= ~(1 << irq);
> - }
> -
> - for (i = 7; i >= 0; i--) {
> - if ((s->ipr >> i) & 1) {
> - m68k_set_irq_level(s->cpu, i + 1, i + 25);
> - return;
> - }
> - }
> - m68k_set_irq_level(s->cpu, 0, 0);
> -}
> -
> static void main_cpu_reset(void *opaque)
> {
> M68kCPU *cpu = opaque;
> @@ -149,6 +118,7 @@ static void q800_init(MachineState *machine)
> const char *kernel_cmdline = machine->kernel_cmdline;
> hwaddr parameters_base;
> CPUState *cs;
> + DeviceState *djmemc_dev;
> DeviceState *dev;
> DeviceState *via_dev;
> SysBusESPState *sysbus_esp;
> @@ -156,8 +126,6 @@ static void q800_init(MachineState *machine)
> SysBusDevice *sysbus;
> BusState *adb_bus;
> NubusBus *nubus;
> - GLUEState *irq;
> - qemu_irq *pic;
>
> linux_boot = (kernel_filename != NULL);
>
> @@ -191,11 +159,13 @@ static void q800_init(MachineState *machine)
> g_free(name);
> }
>
> - /* IRQ Glue */
> + /* djMEMC memory and interrupt controller */
>
> - irq = g_new0(GLUEState, 1);
> - irq->cpu = cpu;
> - pic = qemu_allocate_irqs(GLUE_set_irq, irq, 8);
Glad to see you add a QOM INTC and use QDEV API.
> + djmemc_dev = qdev_create(NULL, TYPE_DJMEMC);
> + object_property_set_link(OBJECT(djmemc_dev), OBJECT(cpu), "cpu",
> + &error_abort);
> + qdev_init_nofail(djmemc_dev);
> + sysbus_mmio_map(SYS_BUS_DEVICE(djmemc_dev), 0, DJMEMC_BASE);
>
> /* VIA */
>
> @@ -203,9 +173,10 @@ static void q800_init(MachineState *machine)
> qdev_init_nofail(via_dev);
> sysbus = SYS_BUS_DEVICE(via_dev);
> sysbus_mmio_map(sysbus, 0, VIA_BASE);
> - qdev_connect_gpio_out_named(DEVICE(sysbus), "irq", 0, pic[0]);
> - qdev_connect_gpio_out_named(DEVICE(sysbus), "irq", 1, pic[1]);
> -
> + qdev_connect_gpio_out_named(DEVICE(sysbus), "irq", 0,
> + qdev_get_gpio_in(djmemc_dev, 0));
> + qdev_connect_gpio_out_named(DEVICE(sysbus),
> + "irq", 1, qdev_get_gpio_in(djmemc_dev, 1));
Can you indent the 2 connect_gpio_out() similarly?
>
> adb_bus = qdev_get_child_bus(via_dev, "adb.0");
> dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD);
> @@ -244,7 +215,7 @@ static void q800_init(MachineState *machine)
> sysbus = SYS_BUS_DEVICE(dev);
> sysbus_mmio_map(sysbus, 0, SONIC_BASE);
> sysbus_mmio_map(sysbus, 1, SONIC_PROM_BASE);
> - sysbus_connect_irq(sysbus, 0, pic[2]);
> + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(djmemc_dev, 2));
:)
>
> /* SCC */
>
> @@ -259,8 +230,8 @@ static void q800_init(MachineState *machine)
> qdev_prop_set_uint32(dev, "chnAtype", 0);
> qdev_init_nofail(dev);
> sysbus = SYS_BUS_DEVICE(dev);
> - sysbus_connect_irq(sysbus, 0, pic[3]);
> - sysbus_connect_irq(sysbus, 1, pic[3]);
> + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(djmemc_dev, 3));
> + sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(djmemc_dev, 3));
> sysbus_mmio_map(sysbus, 0, SCC_BASE);
>
> /* SCSI */
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index 2164646553..4c68d20c18 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -125,4 +125,7 @@ config MAC_VIA
> select MOS6522
> select ADB
>
> +config DJMEMC
> + bool
> +
> source macio/Kconfig
> diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
> index ba898a5781..598e46d7db 100644
> --- a/hw/misc/Makefile.objs
> +++ b/hw/misc/Makefile.objs
> @@ -20,6 +20,7 @@ common-obj-$(CONFIG_ARM11SCU) += arm11scu.o
>
> # Mac devices
> common-obj-$(CONFIG_MOS6522) += mos6522.o
> +obj-$(CONFIG_DJMEMC) += djmemc.o
Hmm this is target-specific because you use a pointer to M68kCPU in
DjMEMCState... But then there is nothing m68k specific, we just need to
deliver an IRQ. Maybe we can use a 'CPUState *' and compile as
common-obj? Ah no, we can't because m68k_set_irq_level() is target
specific. OK, not this patch problem.
>
> # PKUnity SoC devices
> common-obj-$(CONFIG_PUV3) += puv3_pm.o
> diff --git a/hw/misc/djmemc.c b/hw/misc/djmemc.c
> new file mode 100644
> index 0000000000..b494e82a60
> --- /dev/null
> +++ b/hw/misc/djmemc.c
> @@ -0,0 +1,176 @@
> +/*
Missing (c).
> + * djMEMC, macintosh memory and interrupt controller
> + * (Quadra 610/650/800 & Centris 610/650)
> + *
> + * https://mac68k.info/wiki/display/mac68k/djMEMC+Information
> + *
> + * The djMEMC is an Apple custom integrated circuit chip that performs a
> + * variety of functions (RAM management, clock generation, ...).
> + * It receives interrupt requests from various devices, assign priority to
> + * each, and asserts one or more interrupt line to the CPU.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "migration/vmstate.h"
> +#include "hw/misc/djmemc.h"
> +#include "hw/qdev-properties.h"
> +#include "trace.h"
> +
> +#define DJMEMC_SIZE 0x2000
> +
> +#define InterleaveConf 0
> +#define Bank0Conf 1
> +#define Bank1Conf 2
> +#define Bank2Conf 3
> +#define Bank3Conf 4
> +#define Bank4Conf 5
> +#define Bank5Conf 6
> +#define Bank6Conf 7
> +#define Bank7Conf 8
> +#define Bank8Conf 9
> +#define Bank9Conf 10
> +#define MemTop 11
> +#define Config 12
> +#define Refresh 13
I agree with Zoltan about using an enum.
> +
> +static const VMStateDescription vmstate_djMEMC = {
> + .name = "djMEMC",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(interleave, DjMEMCState),
> + VMSTATE_UINT32_ARRAY(bank, DjMEMCState, DjMEMCMaxBanks),
> + VMSTATE_UINT32(top, DjMEMCState),
> + VMSTATE_UINT32(config, DjMEMCState),
> + VMSTATE_UINT32(refresh_rate, DjMEMCState),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static uint64_t djMEMC_read(void *opaque, hwaddr addr,
> + unsigned size)
> +{
> + DjMEMCState *s = opaque;
> + uint64_t value = 0;
> +
> + switch (addr >> 2) {
> + case InterleaveConf:
> + value = s->interleave;
break?
> + case Bank0Conf...Bank9Conf:
> + value = s->bank[(addr >> 2) - Bank0Conf];
break? etc...
> + case MemTop:
> + value = s->top;
> + case Config:
> + value = s->config;
> + case Refresh:
> + value = s->refresh_rate;
break + default?
> + }
> + trace_djMEMC_read((int)(addr >> 2), size, value);
I'd use trace funcname in lowercase, but you decide whichever you prefer.
> +
> + return value;
> +}
> +
> +static void djMEMC_write(void *opaque, hwaddr addr, uint64_t value,
> + unsigned size)
> +{
> + DjMEMCState *s = opaque;
> + trace_djMEMC_write((int)(addr >> 2), size, value);
> +
> + switch (addr >> 2) {
> + case InterleaveConf:
> + s->interleave = value;
break? etc...
> + case Bank0Conf...Bank9Conf:
> + s->bank[(addr >> 2) - Bank0Conf] = value;
> + case MemTop:
> + s->top = value;
> + case Config:
> + s->config = value;
> + case Refresh:
> + s->refresh_rate = value;
> + }
> +}
> +
> +static const MemoryRegionOps djMEMC_mmio_ops = {
> + .read = djMEMC_read,
> + .write = djMEMC_write,
> + .impl = {
> + .min_access_size = 4,
> + .max_access_size = 4,
> + },
> + .endianness = DEVICE_BIG_ENDIAN,
> +};
> +
> +static void djMEMC_set_irq(void *opaque, int irq, int level)
> +{
> + DjMEMCState *s = opaque;
> + int i;
> +
> +
> + if (level) {
> + s->ipr |= 1 << irq;
> + } else {
> + s->ipr &= ~(1 << irq);
> + }
> +
> + for (i = 7; i >= 0; i--) {
> + if ((s->ipr >> i) & 1) {
> + m68k_set_irq_level(s->cpu, i + 1, i + 25);
> + return;
> + }
> + }
> + m68k_set_irq_level(s->cpu, 0, 0);
> +}
> +
> +static void djMEMC_init(Object *obj)
> +{
> + DjMEMCState *s = DJMEMC(obj);
> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +
> + memory_region_init_io(&s->mem_regs, NULL, &djMEMC_mmio_ops, s, "djMEMC",
> + DJMEMC_SIZE);
> + sysbus_init_mmio(sbd, &s->mem_regs);
> +
> + qdev_init_gpio_in(DEVICE(obj), djMEMC_set_irq, 8);
> + object_property_add_link(obj, "cpu", TYPE_M68K_CPU,
> + (Object **) &s->cpu,
> + qdev_prop_allow_set_link_before_realize,
> + 0, NULL);
> +}
> +
> +static void djMEMC_reset(DeviceState *d)
> +{
> + DjMEMCState *s = DJMEMC(d);
> + int i;
> +
> + s->interleave = 0;
> + s->top = 0;
> + s->refresh_rate = 0;
> + s->config = 0;
> +
> + for (i = 0; i < DjMEMCMaxBanks; i++) {
> + s->bank[i] = 0;
> + }
> +}
> +
> +static void djMEMC_class_init(ObjectClass *oc, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(oc);
> +
> + dc->reset = djMEMC_reset;
> + dc->vmsd = &vmstate_djMEMC;
> +}
> +
> +static TypeInfo djMEMC_info = {
> + .name = TYPE_DJMEMC,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(DjMEMCState),
> + .instance_init = djMEMC_init,
> + .class_init = djMEMC_class_init,
> +};
> +
> +static void djMEMC_register_types(void)
> +{
> + type_register_static(&djMEMC_info);
> +}
> +
> +type_init(djMEMC_register_types)
> diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> index 1deb1d08c1..c9bcdd4a54 100644
> --- a/hw/misc/trace-events
> +++ b/hw/misc/trace-events
> @@ -149,3 +149,7 @@ bcm2835_mbox_write(unsigned int size, uint64_t addr, uint64_t value) "mbox write
> bcm2835_mbox_read(unsigned int size, uint64_t addr, uint64_t value) "mbox read sz:%u addr:0x%"PRIx64" data:0x%"PRIx64
> bcm2835_mbox_irq(unsigned level) "mbox irq:ARM level:%u"
> bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu"
> +
> +# djmemc.c
> +djMEMC_read(int reg, unsigned size, uint64_t value) "reg=%d size=%u value=0x%"PRIx64
> +djMEMC_write(int reg, unsigned size, uint64_t value) "reg=%d size=%u value=0x%"PRIx64
> diff --git a/include/hw/misc/djmemc.h b/include/hw/misc/djmemc.h
> new file mode 100644
> index 0000000000..0f29ac1cf3
> --- /dev/null
> +++ b/include/hw/misc/djmemc.h
> @@ -0,0 +1,34 @@
> +/*
> + * djMEMC, macintosh memory and interrupt controller
> + * (Quadra 610/650/800 & Centris 610/650)
(C)?
> + */
> +
> +#ifndef HW_MISC_DJMEMC_H
> +#define HW_MISC_DJMEMC_H
> +
> +#include "hw/sysbus.h"
> +#include "cpu.h"
> +
> +#define DjMEMCMaxBanks 10
> +
> +typedef struct DjMEMCState {
> + SysBusDevice parent_obj;
> +
> + MemoryRegion mem_regs;
> +
> + /* Memory controller */
> + uint32_t interleave;
> + uint32_t bank[DjMEMCMaxBanks];
> + uint32_t top;
> + uint32_t config;
> + uint32_t refresh_rate;
> +
> + /* interrupt controller */
> + M68kCPU *cpu;
> + uint8_t ipr;
> +} DjMEMCState;
> +
> +#define TYPE_DJMEMC "djMEMC"
> +#define DJMEMC(obj) OBJECT_CHECK(DjMEMCState, (obj), TYPE_DJMEMC)
> +
> +#endif
>
next prev parent reply other threads:[~2019-12-12 22:08 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-12-12 20:01 [PATCH 0/3] q800: update machine emulation Laurent Vivier
2019-12-12 20:01 ` [PATCH 1/3] q800: fix ESCC base Laurent Vivier
2019-12-14 10:51 ` Mark Cave-Ayland
2019-12-12 20:01 ` [PATCH 2/3] q800: add djMEMC memory controller Laurent Vivier
2019-12-12 21:12 ` BALATON Zoltan
2019-12-12 22:07 ` Philippe Mathieu-Daudé [this message]
2019-12-14 10:54 ` Mark Cave-Ayland
2019-12-12 20:01 ` [PATCH 3/3] q800: add machine id register Laurent Vivier
2019-12-14 10:55 ` Mark Cave-Ayland
2019-12-14 13:15 ` Philippe Mathieu-Daudé
2019-12-14 10:58 ` [PATCH 0/3] q800: update machine emulation Mark Cave-Ayland
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=262d4edd-8a91-c6f2-cb99-3ffa20231638@redhat.com \
--to=philmd@redhat.com \
--cc=laurent@vivier.eu \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).