On Thu, Jul 9, 2020 at 9:23 AM Philippe Mathieu-Daudé <f4...@amsat.org> wrote: > > On 7/9/20 8:43 AM, Havard Skinnemoen wrote: > > On Wed, Jul 8, 2020 at 11:04 PM Philippe Mathieu-Daudé <f4...@amsat.org> > > wrote: > >> On 7/9/20 2:35 AM, Havard Skinnemoen wrote: > >>> Implement a device model for the System Global Control Registers in the > >>> NPCM730 and NPCM750 BMC SoCs. > >>> > >>> This is primarily used to enable SMP boot (the boot ROM spins reading > >>> the SCRPAD register) and DDR memory initialization; other registers are > >>> best effort for now. > >>> > >>> The reset values of the MDLR and PWRON registers are determined by the > >>> SoC variant (730 vs 750) and board straps respectively. > >>> > >>> Reviewed-by: Joel Stanley <j...@jms.id.au> > >>> Reviewed-by: Cédric Le Goater <c...@kaod.org> > >>> Signed-off-by: Havard Skinnemoen <hskinnem...@google.com> > >>> --- > [...] > >>> diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c > >>> new file mode 100644 > >>> index 0000000000..9934cd238d > >>> --- /dev/null > >>> +++ b/hw/misc/npcm7xx_gcr.c > >>> @@ -0,0 +1,226 @@ > >>> +/* > >>> + * Nuvoton NPCM7xx System Global Control Registers. > >>> + * > >>> + * Copyright 2020 Google LLC > >>> + * > >>> + * 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. > >>> + */ > >>> + > >>> +#include "qemu/osdep.h" > >>> + > >>> +#include "hw/misc/npcm7xx_gcr.h" > >>> +#include "hw/qdev-properties.h" > >>> +#include "migration/vmstate.h" > >>> +#include "qapi/error.h" > >>> +#include "qemu/log.h" > >>> +#include "qemu/module.h" > >>> +#include "qemu/units.h" > >>> + > >>> +#include "trace.h" > >>> + > >>> +static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = { > >>> + [NPCM7XX_GCR_PDID] = 0x04a92750, /* Poleg A1 */ > >>> + [NPCM7XX_GCR_MISCPE] = 0x0000ffff, > >>> + [NPCM7XX_GCR_SPSWC] = 0x00000003, > >>> + [NPCM7XX_GCR_INTCR] = 0x0000035e, > >>> + [NPCM7XX_GCR_HIFCR] = 0x0000004e, > >>> + [NPCM7XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */ > >>> + [NPCM7XX_GCR_RESSR] = 0x80000000, > >>> + [NPCM7XX_GCR_DSCNT] = 0x000000c0, > >>> + [NPCM7XX_GCR_DAVCLVLR] = 0x5a00f3cf, > >>> + [NPCM7XX_GCR_SCRPAD] = 0x00000008, > >>> + [NPCM7XX_GCR_USB1PHYCTL] = 0x034730e4, > >>> + [NPCM7XX_GCR_USB2PHYCTL] = 0x034730e4, > >>> +}; > >>> + > >>> +static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned > >>> size) > >>> +{ > >>> + uint32_t reg = offset / sizeof(uint32_t); > >>> + NPCM7xxGCRState *s = opaque; > >>> + > >>> + if (reg >= NPCM7XX_GCR_NR_REGS) { > >>> + qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04x out of > >>> range\n", > >>> + __func__, (unsigned int)offset); > >> > >> Maybe use HWADDR_PRIx instead of casting to int? > > > > Will do, thanks! > > Glad you are not annoyed by my review comments. > > FYI your series quality is in my top-4 "adding new machine to QEMU". > I'd like to use it as example to follow. > > I am slowly reviewing because this is not part of my work duties, > so I do that in my free time before/after work, which is why I can > barely do more that 2 per day, as I have to learn the SoC and > cross check documentation (or in this case, existing firmware code > since there is no public doc).
Your feedback is super valuable, thanks a lot. I really appreciate it. > > [...] > >>> +static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp) > >>> +{ > >>> + NPCM7xxGCRState *s = NPCM7XX_GCR(dev); > >>> + uint64_t dram_size; > >>> + Error *err = NULL; > >>> + Object *obj; > >>> + > >>> + obj = object_property_get_link(OBJECT(dev), "dram-mr", &err); > >>> + if (!obj) { > >>> + error_setg(errp, "%s: required dram-mr link not found: %s", > >>> + __func__, error_get_pretty(err)); > >>> + return; > >>> + } > >>> + dram_size = memory_region_size(MEMORY_REGION(obj)); > >>> + > >>> + /* Power-on reset value */ > >>> + s->reset_intcr3 = 0x00001002; > >>> + > >>> + /* > >>> + * The GMMAP (Graphics Memory Map) field is used by u-boot to detect > >>> the > >>> + * DRAM size, and is normally initialized by the boot block as part > >>> of DRAM > >>> + * training. However, since we don't have a complete emulation of the > >>> + * memory controller and try to make it look like it has already been > >>> + * initialized, the boot block will skip this initialization, and we > >>> need > >>> + * to make sure this field is set correctly up front. > >>> + * > >>> + * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 > >>> GiB or > >>> + * more of DRAM will be interpreted as 128 MiB. > >> > >> I'd say u-boot is right in wrapping at 2GB, as more DRAM is > >> un-addressable. > > > > Ah, maybe I shouldn't have said "or more". The bug is that if you > > specify _exactly_ 2GiB, u-boot will see 128 MiB. > > > > But I agree more than 2GiB doesn't make sense, so I'll add a check for that. > > > > Not sure if I agree that the check should be here. > 2 GiB is an > > addressing limitation, and the GCR module shouldn't really know what > > the SoC's address space looks like. The lower limit is because the GCR > > module can't encode anything less than 128 MiB. > > > >>> + * > >>> + * > >>> https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244 > >>> + */ > >>> + if (dram_size >= 2 * GiB) { > >> > >> See my comment in this series on the machine patch. > >> > >>> + s->reset_intcr3 |= 4 << 8; > >>> + } else if (dram_size >= 1 * GiB) { > >>> + s->reset_intcr3 |= 3 << 8; > >>> + } else if (dram_size >= 512 * MiB) { > >>> + s->reset_intcr3 |= 2 << 8; > >>> + } else if (dram_size >= 256 * MiB) { > >>> + s->reset_intcr3 |= 1 << 8; > >>> + } else if (dram_size >= 128 * MiB) { > >>> + s->reset_intcr3 |= 0 << 8; > > I think this can be simplified as: > > s->reset_intcr3 = (4 - clz32(dram_size)) << 8; > > Which implicitly truncate to power of 2 (which is what this > block does, as you can have only 1 bank managed per this SGC). Good idea. I find this a little easier to understand though: #define NPCM7XX_GCR_MIN_DRAM_SIZE (128 * MiB) s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8; > But checking is_power_of_2(dram_size) and reporting an error > else, is probably even better. OK, I'll add a check for this, and also explicit checks for too large and too small. The former is currently redundant with the DRAM size check I'm adding to npcm7xx_realize(), but I kind of like the idea of checking for encoding limitations and addressing limitations separately, as they may not necessarily stay the same forever. > >>> + } else { > >>> + error_setg(errp, > >>> + "npcm7xx_gcr: DRAM size %" PRIu64 > >>> + " is too small (need 128 MiB minimum)", > >>> + dram_size); > >> > >> Ah, so you could add the same error for >2GB. Easier. > >> > >> Preferably using HWADDR_PRIx, and similar error for >2GB: > >> Reviewed-by: Philippe Mathieu-Daudé <f4...@amsat.org>