On Sun, 2026-09-27 at 20:13 +0000, 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.
> 
> Signed-off-by: Kuan-Wei Chiu <[email protected]>

Acked-by: Alistair Francis <[email protected]>

Alistair

> ---
>  hw/misc/Kconfig               |  3 ++
>  hw/misc/cv1800b_clk.c         | 98
> +++++++++++++++++++++++++++++++++++
>  hw/misc/meson.build           |  1 +
>  include/hw/misc/cv1800b_clk.h | 26 ++++++++++
>  4 files changed, 128 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 46e3c03cc8..7abd22869a 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -263,4 +263,7 @@ config AXIADO_CLK
>  config K230_DECOMP_GZIP
>      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..38807995ac
> --- /dev/null
> +++ b/hw/misc/cv1800b_clk.c
> @@ -0,0 +1,98 @@
> +/* 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    (sizeof(uint32_t))
> +#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 < CV1800B_CLK_IOSIZE) {
> +        val = s->regs[addr / REG_BYTE_WIDTH];
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Out-of-bounds read at offset 0x%"
> HWADDR_PRIx "\n",
> +                      __func__, addr);
> +    }
> +
> +    return val;
> +}
> +
> +static void cv1800b_clk_write(void *opaque, hwaddr addr, uint64_t
> val, unsigned int size)
> +{
> +    CV1800BClkState *s = opaque;
> +
> +    if (addr < CV1800B_CLK_IOSIZE) {
> +        s->regs[addr / REG_BYTE_WIDTH] = val;
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Out-of-bounds write at offset 0x%"
> HWADDR_PRIx "\n",
> +                      __func__, addr);
> +    }
> +}
> +
> +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, CV1800B_CLK_IOSIZE);
> +    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 9790afa104..244bf278a9 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -39,6 +39,7 @@ 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_K230_DECOMP_GZIP', if_true:
> files('k230_decomp_gzip.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..4d03d887c6
> --- /dev/null
> +++ b/include/hw/misc/cv1800b_clk.h
> @@ -0,0 +1,26 @@
> +/* 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)
> +
> +#define CV1800B_CLK_IOSIZE 0x1000
> +
> +struct CV1800BClkState {
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion iomem;
> +    uint32_t regs[CV1800B_CLK_IOSIZE / sizeof(uint32_t)];
> +};
> +
> +#endif

Reply via email to