186c2dff9SBernhard Beschow /*
286c2dff9SBernhard Beschow * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com>
386c2dff9SBernhard Beschow *
486c2dff9SBernhard Beschow * i.MX 8M Plus ANALOG IP block emulation code
586c2dff9SBernhard Beschow *
686c2dff9SBernhard Beschow * Based on hw/misc/imx7_ccm.c
786c2dff9SBernhard Beschow *
886c2dff9SBernhard Beschow * SPDX-License-Identifier: GPL-2.0-or-later
986c2dff9SBernhard Beschow */
1086c2dff9SBernhard Beschow
1186c2dff9SBernhard Beschow #include "qemu/osdep.h"
1286c2dff9SBernhard Beschow #include "qemu/log.h"
1386c2dff9SBernhard Beschow
1486c2dff9SBernhard Beschow #include "hw/misc/imx8mp_analog.h"
1586c2dff9SBernhard Beschow #include "migration/vmstate.h"
1686c2dff9SBernhard Beschow
1786c2dff9SBernhard Beschow #define ANALOG_PLL_LOCK BIT(31)
1886c2dff9SBernhard Beschow
imx8mp_analog_reset(DeviceState * dev)1986c2dff9SBernhard Beschow static void imx8mp_analog_reset(DeviceState *dev)
2086c2dff9SBernhard Beschow {
2186c2dff9SBernhard Beschow IMX8MPAnalogState *s = IMX8MP_ANALOG(dev);
2286c2dff9SBernhard Beschow
2386c2dff9SBernhard Beschow memset(s->analog, 0, sizeof(s->analog));
2486c2dff9SBernhard Beschow
2586c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL1_GEN_CTRL] = 0x00002010;
2686c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL1_FDIV_CTL0] = 0x00145032;
2786c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL1_FDIV_CTL1] = 0x00000000;
2886c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL1_SSCG_CTRL] = 0x00000000;
2986c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL1_MNIT_CTRL] = 0x00100103;
3086c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL2_GEN_CTRL] = 0x00002010;
3186c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL2_FDIV_CTL0] = 0x00145032;
3286c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL2_FDIV_CTL1] = 0x00000000;
3386c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL2_SSCG_CTRL] = 0x00000000;
3486c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL2_MNIT_CTRL] = 0x00100103;
3586c2dff9SBernhard Beschow s->analog[ANALOG_VIDEO_PLL1_GEN_CTRL] = 0x00002010;
3686c2dff9SBernhard Beschow s->analog[ANALOG_VIDEO_PLL1_FDIV_CTL0] = 0x00145032;
3786c2dff9SBernhard Beschow s->analog[ANALOG_VIDEO_PLL1_FDIV_CTL1] = 0x00000000;
3886c2dff9SBernhard Beschow s->analog[ANALOG_VIDEO_PLL1_SSCG_CTRL] = 0x00000000;
3986c2dff9SBernhard Beschow s->analog[ANALOG_VIDEO_PLL1_MNIT_CTRL] = 0x00100103;
4086c2dff9SBernhard Beschow s->analog[ANALOG_DRAM_PLL_GEN_CTRL] = 0x00002010;
4186c2dff9SBernhard Beschow s->analog[ANALOG_DRAM_PLL_FDIV_CTL0] = 0x0012c032;
4286c2dff9SBernhard Beschow s->analog[ANALOG_DRAM_PLL_FDIV_CTL1] = 0x00000000;
4386c2dff9SBernhard Beschow s->analog[ANALOG_DRAM_PLL_SSCG_CTRL] = 0x00000000;
4486c2dff9SBernhard Beschow s->analog[ANALOG_DRAM_PLL_MNIT_CTRL] = 0x00100103;
4586c2dff9SBernhard Beschow s->analog[ANALOG_GPU_PLL_GEN_CTRL] = 0x00000810;
4686c2dff9SBernhard Beschow s->analog[ANALOG_GPU_PLL_FDIV_CTL0] = 0x000c8031;
4786c2dff9SBernhard Beschow s->analog[ANALOG_GPU_PLL_LOCKD_CTRL] = 0x0010003f;
4886c2dff9SBernhard Beschow s->analog[ANALOG_GPU_PLL_MNIT_CTRL] = 0x00280081;
4986c2dff9SBernhard Beschow s->analog[ANALOG_VPU_PLL_GEN_CTRL] = 0x00000810;
5086c2dff9SBernhard Beschow s->analog[ANALOG_VPU_PLL_FDIV_CTL0] = 0x0012c032;
5186c2dff9SBernhard Beschow s->analog[ANALOG_VPU_PLL_LOCKD_CTRL] = 0x0010003f;
5286c2dff9SBernhard Beschow s->analog[ANALOG_VPU_PLL_MNIT_CTRL] = 0x00280081;
5386c2dff9SBernhard Beschow s->analog[ANALOG_ARM_PLL_GEN_CTRL] = 0x00000810;
5486c2dff9SBernhard Beschow s->analog[ANALOG_ARM_PLL_FDIV_CTL0] = 0x000fa031;
5586c2dff9SBernhard Beschow s->analog[ANALOG_ARM_PLL_LOCKD_CTRL] = 0x0010003f;
5686c2dff9SBernhard Beschow s->analog[ANALOG_ARM_PLL_MNIT_CTRL] = 0x00280081;
5786c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL1_GEN_CTRL] = 0x0aaaa810;
5886c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL1_FDIV_CTL0] = 0x00190032;
5986c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL1_LOCKD_CTRL] = 0x0010003f;
6086c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL1_MNIT_CTRL] = 0x00280081;
6186c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL2_GEN_CTRL] = 0x0aaaa810;
6286c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL2_FDIV_CTL0] = 0x000fa031;
6386c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL2_LOCKD_CTRL] = 0x0010003f;
6486c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL2_MNIT_CTRL] = 0x00280081;
6586c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL3_GEN_CTRL] = 0x00000810;
6686c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL3_FDIV_CTL0] = 0x000fa031;
6786c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL3_LOCKD_CTRL] = 0x0010003f;
6886c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL3_MNIT_CTRL] = 0x00280081;
6986c2dff9SBernhard Beschow s->analog[ANALOG_OSC_MISC_CFG] = 0x00000000;
7086c2dff9SBernhard Beschow s->analog[ANALOG_ANAMIX_PLL_MNIT_CTL] = 0x00000000;
7186c2dff9SBernhard Beschow s->analog[ANALOG_DIGPROG] = 0x00824010;
7286c2dff9SBernhard Beschow
7386c2dff9SBernhard Beschow /* all PLLs need to be locked */
7486c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK;
7586c2dff9SBernhard Beschow s->analog[ANALOG_AUDIO_PLL2_GEN_CTRL] |= ANALOG_PLL_LOCK;
7686c2dff9SBernhard Beschow s->analog[ANALOG_VIDEO_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK;
7786c2dff9SBernhard Beschow s->analog[ANALOG_DRAM_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK;
7886c2dff9SBernhard Beschow s->analog[ANALOG_GPU_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK;
7986c2dff9SBernhard Beschow s->analog[ANALOG_VPU_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK;
8086c2dff9SBernhard Beschow s->analog[ANALOG_ARM_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK;
8186c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK;
8286c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL2_GEN_CTRL] |= ANALOG_PLL_LOCK;
8386c2dff9SBernhard Beschow s->analog[ANALOG_SYS_PLL3_GEN_CTRL] |= ANALOG_PLL_LOCK;
8486c2dff9SBernhard Beschow }
8586c2dff9SBernhard Beschow
imx8mp_analog_read(void * opaque,hwaddr offset,unsigned size)8686c2dff9SBernhard Beschow static uint64_t imx8mp_analog_read(void *opaque, hwaddr offset, unsigned size)
8786c2dff9SBernhard Beschow {
8886c2dff9SBernhard Beschow IMX8MPAnalogState *s = opaque;
8986c2dff9SBernhard Beschow
9086c2dff9SBernhard Beschow return s->analog[offset >> 2];
9186c2dff9SBernhard Beschow }
9286c2dff9SBernhard Beschow
imx8mp_analog_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)9386c2dff9SBernhard Beschow static void imx8mp_analog_write(void *opaque, hwaddr offset,
9486c2dff9SBernhard Beschow uint64_t value, unsigned size)
9586c2dff9SBernhard Beschow {
9686c2dff9SBernhard Beschow IMX8MPAnalogState *s = opaque;
9786c2dff9SBernhard Beschow
9886c2dff9SBernhard Beschow if (offset >> 2 == ANALOG_DIGPROG) {
9986c2dff9SBernhard Beschow qemu_log_mask(LOG_GUEST_ERROR,
10086c2dff9SBernhard Beschow "Guest write to read-only ANALOG_DIGPROG register\n");
10186c2dff9SBernhard Beschow } else {
10286c2dff9SBernhard Beschow s->analog[offset >> 2] = value;
10386c2dff9SBernhard Beschow }
10486c2dff9SBernhard Beschow }
10586c2dff9SBernhard Beschow
10686c2dff9SBernhard Beschow static const struct MemoryRegionOps imx8mp_analog_ops = {
10786c2dff9SBernhard Beschow .read = imx8mp_analog_read,
10886c2dff9SBernhard Beschow .write = imx8mp_analog_write,
10986c2dff9SBernhard Beschow .endianness = DEVICE_NATIVE_ENDIAN,
11086c2dff9SBernhard Beschow .impl = {
11186c2dff9SBernhard Beschow .min_access_size = 4,
11286c2dff9SBernhard Beschow .max_access_size = 4,
11386c2dff9SBernhard Beschow .unaligned = false,
11486c2dff9SBernhard Beschow },
11586c2dff9SBernhard Beschow };
11686c2dff9SBernhard Beschow
imx8mp_analog_init(Object * obj)11786c2dff9SBernhard Beschow static void imx8mp_analog_init(Object *obj)
11886c2dff9SBernhard Beschow {
11986c2dff9SBernhard Beschow IMX8MPAnalogState *s = IMX8MP_ANALOG(obj);
12086c2dff9SBernhard Beschow SysBusDevice *sd = SYS_BUS_DEVICE(obj);
12186c2dff9SBernhard Beschow
12286c2dff9SBernhard Beschow memory_region_init(&s->mmio.container, obj, TYPE_IMX8MP_ANALOG, 0x10000);
12386c2dff9SBernhard Beschow
12486c2dff9SBernhard Beschow memory_region_init_io(&s->mmio.analog, obj, &imx8mp_analog_ops, s,
12586c2dff9SBernhard Beschow TYPE_IMX8MP_ANALOG, sizeof(s->analog));
12686c2dff9SBernhard Beschow memory_region_add_subregion(&s->mmio.container, 0, &s->mmio.analog);
12786c2dff9SBernhard Beschow
12886c2dff9SBernhard Beschow sysbus_init_mmio(sd, &s->mmio.container);
12986c2dff9SBernhard Beschow }
13086c2dff9SBernhard Beschow
13186c2dff9SBernhard Beschow static const VMStateDescription imx8mp_analog_vmstate = {
13286c2dff9SBernhard Beschow .name = TYPE_IMX8MP_ANALOG,
13386c2dff9SBernhard Beschow .version_id = 1,
13486c2dff9SBernhard Beschow .minimum_version_id = 1,
13586c2dff9SBernhard Beschow .fields = (const VMStateField[]) {
13686c2dff9SBernhard Beschow VMSTATE_UINT32_ARRAY(analog, IMX8MPAnalogState, ANALOG_MAX),
13786c2dff9SBernhard Beschow VMSTATE_END_OF_LIST()
13886c2dff9SBernhard Beschow },
13986c2dff9SBernhard Beschow };
14086c2dff9SBernhard Beschow
imx8mp_analog_class_init(ObjectClass * klass,const void * data)141*12d1a768SPhilippe Mathieu-Daudé static void imx8mp_analog_class_init(ObjectClass *klass, const void *data)
14286c2dff9SBernhard Beschow {
14386c2dff9SBernhard Beschow DeviceClass *dc = DEVICE_CLASS(klass);
14486c2dff9SBernhard Beschow
14586c2dff9SBernhard Beschow device_class_set_legacy_reset(dc, imx8mp_analog_reset);
14686c2dff9SBernhard Beschow dc->vmsd = &imx8mp_analog_vmstate;
14786c2dff9SBernhard Beschow dc->desc = "i.MX 8M Plus Analog Module";
14886c2dff9SBernhard Beschow }
14986c2dff9SBernhard Beschow
15086c2dff9SBernhard Beschow static const TypeInfo imx8mp_analog_types[] = {
15186c2dff9SBernhard Beschow {
15286c2dff9SBernhard Beschow .name = TYPE_IMX8MP_ANALOG,
15386c2dff9SBernhard Beschow .parent = TYPE_SYS_BUS_DEVICE,
15486c2dff9SBernhard Beschow .instance_size = sizeof(IMX8MPAnalogState),
15586c2dff9SBernhard Beschow .instance_init = imx8mp_analog_init,
15686c2dff9SBernhard Beschow .class_init = imx8mp_analog_class_init,
15786c2dff9SBernhard Beschow }
15886c2dff9SBernhard Beschow };
15986c2dff9SBernhard Beschow
16086c2dff9SBernhard Beschow DEFINE_TYPES(imx8mp_analog_types);
161