On Thu, May 14, 2026 at 01:15:25AM +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         | 89 +++++++++++++++++++++++++++++++++++
>  hw/misc/meson.build           |  1 +
>  include/hw/misc/cv1800b_clk.h | 24 ++++++++++
>  4 files changed, 117 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 99bdf09219..42cdf771a4 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -254,4 +254,7 @@ config IOSB
>  config XLNX_VERSAL_TRNG
>      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..738f0910db
> --- /dev/null
> +++ b/hw/misc/cv1800b_clk.c
> @@ -0,0 +1,89 @@
> +/* 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_CLK_BYP_0  (0x030 / 4)
> +#define REG_CLK_BYP_1  (0x034 / 4)
I suggest using the REG_BYTE_WIDTH macro instead of the constant value 4
here and elsewhere.

#define REG_BYTE_WIDTH    (4)
#define REG_CLK_BYP_0     (0x030 / REG_BYTE_WIDTH)
#define REG_CLK_BYP_1     (0x034 / REG_BYTE_WIDTH)

You could also use `RegisterInfo` to define these register operations, so
we don’t have to manually define these helper macros. Since this hardware
is relatively simple, the current implementation is acceptable as well.

Thanks,
Chao
> +
> +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];
> +    }
> +
> +    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;
> +    }
> +}
> +
> +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 245ab9b98c..a4c5f90cd8 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -35,6 +35,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];
> +};
> +
> +#endif
> -- 
> 2.54.0.563.g4f69b47b94-goog
> 

Reply via email to