On Wed, Jun 17, 2026 at 02:46:38PM +0800, Chao Liu wrote:
> On Tue, Jun 16, 2026 at 07:01:44PM +0800, Kuan-Wei Chiu wrote:
> > Add a stub for the CV1800B clock controller. This is specifically
> > required for the SDHCI controller to function correctly under Linux.
> >
> > The Linux 'sophgo,cv1800-clk' driver probes this device to determine
> > the clock tree configuration. This implementation sets the bypass
> > registers (CLK_BYP_0 and CLK_BYP_1) to 0xFFFFFFFF during reset,
> > matching the POR default state. This bypasses the PLLs and allows the
> > SDHCI and other peripherals to operate using the 25MHz reference clock.
> >
> > Without this device, the SD card driver fails to initialize, preventing
> > the system from mounting the root filesystem from the SD card:
> >
> > [ 0.888739] Waiting for root device /dev/mmcblk0...
> > [ 10.727739] mmc0: Timeout waiting for hardware cmd interrupt.
> > [ 10.728042] mmc0: sdhci: ============ SDHCI REGISTER DUMP ===========
> > [ 10.728356] mmc0: sdhci: Sys addr: 0x00000002 | Version: 0x00002402
> > [ 10.728618] mmc0: sdhci: Blk size: 0x00000000 | Blk cnt: 0x00000000
> > [ 10.728919] mmc0: sdhci: Argument: 0x00000000 | Trn mode: 0x00000000
> > [ 10.729271] mmc0: sdhci: Present: 0x01ff0000 | Host ctl: 0x00000001
> > [ 10.729591] mmc0: sdhci: Power: 0x0000000f | Blk gap: 0x00000000
> > [ 10.729903] mmc0: sdhci: Wake-up: 0x00000000 | Clock: 0x00000000
> > [ 10.730223] mmc0: sdhci: Timeout: 0x00000000 | Int stat: 0x00000000
> > [ 10.730537] mmc0: sdhci: Int enab: 0x00ff0083 | Sig enab: 0x00ff0083
> > [ 10.730795] mmc0: sdhci: ACmd stat: 0x00000000 | Slot int: 0x00000000
> > [ 10.731005] mmc0: sdhci: Caps: 0x056900b9 | Caps_1: 0x00000000
> > [ 10.731211] mmc0: sdhci: Cmd: 0x00000000 | Max curr: 0x00000000
> > [ 10.731415] mmc0: sdhci: Resp[0]: 0x00000000 | Resp[1]: 0x00000000
> > [ 10.731636] mmc0: sdhci: Resp[2]: 0x00000000 | Resp[3]: 0x00000000
> > [ 10.731851] mmc0: sdhci: Host ctl2: 0x00000000
> > [ 10.732018] mmc0: sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0x00000000
> > [ 10.732229] mmc0: sdhci: ============================================
> >
> > Signed-off-by: Kuan-Wei Chiu <[email protected]>
> > ---
> > hw/misc/Kconfig | 3 ++
> > hw/misc/cv1800b_clk.c | 90 +++++++++++++++++++++++++++++++++++
> > hw/misc/meson.build | 1 +
> > include/hw/misc/cv1800b_clk.h | 24 ++++++++++
> > 4 files changed, 118 insertions(+)
> > create mode 100644 hw/misc/cv1800b_clk.c
> > create mode 100644 include/hw/misc/cv1800b_clk.h
> >
> > diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> > index 1543ee6653..fd56f0a4c5 100644
> > --- a/hw/misc/Kconfig
> > +++ b/hw/misc/Kconfig
> > @@ -257,4 +257,7 @@ config XLNX_VERSAL_TRNG
> > config XLNX_ZYNQ_DDRC
> > bool
> >
> > +config SOPHGO_CV1800B_CLK
> > + bool
> > +
> > source macio/Kconfig
> > diff --git a/hw/misc/cv1800b_clk.c b/hw/misc/cv1800b_clk.c
> > new file mode 100644
> > index 0000000000..db7e626158
> > --- /dev/null
> > +++ b/hw/misc/cv1800b_clk.c
> > @@ -0,0 +1,90 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * Sophgo CV1800B Clock Controller
> > + *
> > + * Copyright (c) 2026 Kuan-Wei Chiu <[email protected]>
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qemu/log.h"
> > +#include "hw/misc/cv1800b_clk.h"
> > +
> > +#define REG_BYTE_WIDTH (4)
> > +#define REG_CLK_BYP_0 (0x030 / REG_BYTE_WIDTH)
> > +#define REG_CLK_BYP_1 (0x034 / REG_BYTE_WIDTH)
> > +
> > +static uint64_t cv1800b_clk_read(void *opaque, hwaddr addr, unsigned int
> > size)
> > +{
> > + CV1800BClkState *s = opaque;
> > + uint32_t val = 0;
> > +
> > + if ((addr / 4) < ARRAY_SIZE(s->regs)) {
> > + val = s->regs[addr / 4];
> We can use REG_BYTE_WIDTH to replace the const number 4.
>
> like:
>
> if ((addr / REG_BYTE_WIDTH) < ARRAY_SIZE(s->regs)) {
> val = s->regs[addr / REG_BYTE_WIDTH];
> }
Sorry about that, I should have addressed this as well.
I'll change it in the next version.
Regards,
Kuan-Wei
> > + }
> > +
> > + return val;
> > +}
> > +
> > +static void cv1800b_clk_write(void *opaque, hwaddr addr, uint64_t val,
> > unsigned int size)
> > +{
> > + CV1800BClkState *s = opaque;
> > +
> > + if ((addr / 4) < ARRAY_SIZE(s->regs)) {
> > + s->regs[addr / 4] = val;
> This needs to be modified here as well.
>
> > + }
> > +}
> > +
> > +static const MemoryRegionOps cv1800b_clk_ops = {
> > + .read = cv1800b_clk_read,
> > + .write = cv1800b_clk_write,
> > + .endianness = DEVICE_LITTLE_ENDIAN,
> > + .valid = {
> > + .min_access_size = 4,
> > + .max_access_size = 4,
> > + },
> > +};
> > +
> > +static void cv1800b_clk_reset_hold(Object *obj, ResetType type)
> > +{
> > + CV1800BClkState *s = CV1800B_CLK(obj);
> > +
> > + memset(s->regs, 0, sizeof(s->regs));
> > +
> > + /*
> > + * TODO: Implement proper PLL state machines.
> > + * For now, use POR default to bypass PLLs and boot via 25MHz XTAL.
> > + */
> > + s->regs[REG_CLK_BYP_0] = 0xFFFFFFFF;
> > + s->regs[REG_CLK_BYP_1] = 0xFFFFFFFF;
> > +}
> > +
> > +static void cv1800b_clk_init(Object *obj)
> > +{
> > + CV1800BClkState *s = CV1800B_CLK(obj);
> > +
> > + memory_region_init_io(&s->iomem, obj, &cv1800b_clk_ops, s,
> > + TYPE_CV1800B_CLK, 0x1000);
> > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
> > +}
> > +
> > +static void cv1800b_clk_class_init(ObjectClass *klass, const void *data)
> > +{
> > + ResettableClass *rc = RESETTABLE_CLASS(klass);
> > +
> > + rc->phases.hold = cv1800b_clk_reset_hold;
> > +}
> > +
> > +static const TypeInfo cv1800b_clk_info = {
> > + .name = TYPE_CV1800B_CLK,
> > + .parent = TYPE_SYS_BUS_DEVICE,
> > + .instance_size = sizeof(CV1800BClkState),
> > + .instance_init = cv1800b_clk_init,
> > + .class_init = cv1800b_clk_class_init,
> > +};
> > +
> > +static void cv1800b_clk_register_types(void)
> > +{
> > + type_register_static(&cv1800b_clk_info);
> > +}
> > +
> > +type_init(cv1800b_clk_register_types)
> > diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> > index 23265f6035..692f290a87 100644
> > --- a/hw/misc/meson.build
> > +++ b/hw/misc/meson.build
> > @@ -36,6 +36,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true:
> > files('sifive_e_prci.c'))
> > system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true:
> > files('sifive_e_aon.c'))
> > system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true:
> > files('sifive_u_otp.c'))
> > system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true:
> > files('sifive_u_prci.c'))
> > +system_ss.add(when: 'CONFIG_SOPHGO_CV1800B_CLK', if_true:
> > files('cv1800b_clk.c'))
> >
> > subdir('macio')
> >
> > diff --git a/include/hw/misc/cv1800b_clk.h b/include/hw/misc/cv1800b_clk.h
> > new file mode 100644
> > index 0000000000..05c0d1ca1b
> > --- /dev/null
> > +++ b/include/hw/misc/cv1800b_clk.h
> > @@ -0,0 +1,24 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * Sophgo CV1800B Clock Controller
> > + *
> > + * Copyright (c) 2026 Kuan-Wei Chiu <[email protected]>
> > + */
> > +
> > +#ifndef HW_MISC_CV1800B_CLK_H
> > +#define HW_MISC_CV1800B_CLK_H
> > +
> > +#include "hw/core/sysbus.h"
> > +#include "qom/object.h"
> > +
> > +#define TYPE_CV1800B_CLK "cv1800b-clk"
> > +OBJECT_DECLARE_SIMPLE_TYPE(CV1800BClkState, CV1800B_CLK)
> > +
> > +struct CV1800BClkState {
> > + SysBusDevice parent_obj;
> > +
> > + MemoryRegion iomem;
> > + uint32_t regs[0x1000 / 4];
> Maybe we can move the struct types to the C source file.
>
> It's easier to use macros to replace the constant values there.
>
> Thanks,
> Chao
> > +};
> > +
> > +#endif
> > --
> > 2.54.0.1136.gdb2ca164c4-goog
> >