2 * Allwinner R40 Clock Control Unit emulation
4 * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "qemu/osdep.h"
21 #include "qemu/units.h"
22 #include "hw/sysbus.h"
23 #include "migration/vmstate.h"
25 #include "qemu/module.h"
26 #include "hw/misc/allwinner-r40-ccu.h"
28 /* CCU register offsets */
30 REG_PLL_CPUX_CTRL
= 0x0000,
31 REG_PLL_AUDIO_CTRL
= 0x0008,
32 REG_PLL_VIDEO0_CTRL
= 0x0010,
33 REG_PLL_VE_CTRL
= 0x0018,
34 REG_PLL_DDR0_CTRL
= 0x0020,
35 REG_PLL_PERIPH0_CTRL
= 0x0028,
36 REG_PLL_PERIPH1_CTRL
= 0x002c,
37 REG_PLL_VIDEO1_CTRL
= 0x0030,
38 REG_PLL_SATA_CTRL
= 0x0034,
39 REG_PLL_GPU_CTRL
= 0x0038,
40 REG_PLL_MIPI_CTRL
= 0x0040,
41 REG_PLL_DE_CTRL
= 0x0048,
42 REG_PLL_DDR1_CTRL
= 0x004c,
43 REG_AHB1_APB1_CFG
= 0x0054,
44 REG_APB2_CFG
= 0x0058,
45 REG_MMC0_CLK
= 0x0088,
46 REG_MMC1_CLK
= 0x008c,
47 REG_MMC2_CLK
= 0x0090,
48 REG_MMC3_CLK
= 0x0094,
49 REG_USBPHY_CFG
= 0x00cc,
50 REG_PLL_DDR_AUX
= 0x00f0,
51 REG_DRAM_CFG
= 0x00f4,
52 REG_PLL_DDR1_CFG
= 0x00f8,
53 REG_DRAM_CLK_GATING
= 0x0100,
54 REG_GMAC_CLK
= 0x0164,
55 REG_SYS_32K_CLK
= 0x0310,
56 REG_PLL_LOCK_CTRL
= 0x0320,
59 #define REG_INDEX(offset) (offset / sizeof(uint32_t))
61 /* CCU register flags */
63 REG_PLL_ENABLE
= (1 << 31),
64 REG_PLL_LOCK
= (1 << 28),
67 static uint64_t allwinner_r40_ccu_read(void *opaque
, hwaddr offset
,
70 const AwR40ClockCtlState
*s
= AW_R40_CCU(opaque
);
71 const uint32_t idx
= REG_INDEX(offset
);
74 case 0x324 ... AW_R40_CCU_IOSIZE
:
75 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
76 __func__
, (uint32_t)offset
);
83 static void allwinner_r40_ccu_write(void *opaque
, hwaddr offset
,
84 uint64_t val
, unsigned size
)
86 AwR40ClockCtlState
*s
= AW_R40_CCU(opaque
);
89 case REG_DRAM_CFG
: /* DRAM Configuration(for DDR0) */
90 /* bit16: SDRCLK_UPD (SDRCLK configuration 0 update) */
93 case REG_PLL_DDR1_CTRL
: /* DDR1 Control register */
94 /* bit30: SDRPLL_UPD */
96 if (val
& REG_PLL_ENABLE
) {
100 case REG_PLL_CPUX_CTRL
:
101 case REG_PLL_AUDIO_CTRL
:
102 case REG_PLL_VE_CTRL
:
103 case REG_PLL_VIDEO0_CTRL
:
104 case REG_PLL_DDR0_CTRL
:
105 case REG_PLL_PERIPH0_CTRL
:
106 case REG_PLL_PERIPH1_CTRL
:
107 case REG_PLL_VIDEO1_CTRL
:
108 case REG_PLL_SATA_CTRL
:
109 case REG_PLL_GPU_CTRL
:
110 case REG_PLL_MIPI_CTRL
:
111 case REG_PLL_DE_CTRL
:
112 if (val
& REG_PLL_ENABLE
) {
116 case 0x324 ... AW_R40_CCU_IOSIZE
:
117 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
118 __func__
, (uint32_t)offset
);
121 qemu_log_mask(LOG_UNIMP
, "%s: unimplemented write offset 0x%04x\n",
122 __func__
, (uint32_t)offset
);
126 s
->regs
[REG_INDEX(offset
)] = (uint32_t) val
;
129 static const MemoryRegionOps allwinner_r40_ccu_ops
= {
130 .read
= allwinner_r40_ccu_read
,
131 .write
= allwinner_r40_ccu_write
,
132 .endianness
= DEVICE_NATIVE_ENDIAN
,
134 .min_access_size
= 4,
135 .max_access_size
= 4,
137 .impl
.min_access_size
= 4,
140 static void allwinner_r40_ccu_reset(DeviceState
*dev
)
142 AwR40ClockCtlState
*s
= AW_R40_CCU(dev
);
144 memset(s
->regs
, 0, sizeof(s
->regs
));
146 /* Set default values for registers */
147 s
->regs
[REG_INDEX(REG_PLL_CPUX_CTRL
)] = 0x00001000;
148 s
->regs
[REG_INDEX(REG_PLL_AUDIO_CTRL
)] = 0x00035514;
149 s
->regs
[REG_INDEX(REG_PLL_VIDEO0_CTRL
)] = 0x03006207;
150 s
->regs
[REG_INDEX(REG_PLL_VE_CTRL
)] = 0x03006207;
151 s
->regs
[REG_INDEX(REG_PLL_DDR0_CTRL
)] = 0x00001000,
152 s
->regs
[REG_INDEX(REG_PLL_PERIPH0_CTRL
)] = 0x00041811;
153 s
->regs
[REG_INDEX(REG_PLL_PERIPH1_CTRL
)] = 0x00041811;
154 s
->regs
[REG_INDEX(REG_PLL_VIDEO1_CTRL
)] = 0x03006207;
155 s
->regs
[REG_INDEX(REG_PLL_SATA_CTRL
)] = 0x00001811;
156 s
->regs
[REG_INDEX(REG_PLL_GPU_CTRL
)] = 0x03006207;
157 s
->regs
[REG_INDEX(REG_PLL_MIPI_CTRL
)] = 0x00000515;
158 s
->regs
[REG_INDEX(REG_PLL_DE_CTRL
)] = 0x03006207;
159 s
->regs
[REG_INDEX(REG_PLL_DDR1_CTRL
)] = 0x00001800;
160 s
->regs
[REG_INDEX(REG_AHB1_APB1_CFG
)] = 0x00001010;
161 s
->regs
[REG_INDEX(REG_APB2_CFG
)] = 0x01000000;
162 s
->regs
[REG_INDEX(REG_PLL_DDR_AUX
)] = 0x00000001;
163 s
->regs
[REG_INDEX(REG_PLL_DDR1_CFG
)] = 0x0ccca000;
164 s
->regs
[REG_INDEX(REG_SYS_32K_CLK
)] = 0x0000000f;
167 static void allwinner_r40_ccu_init(Object
*obj
)
169 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
170 AwR40ClockCtlState
*s
= AW_R40_CCU(obj
);
173 memory_region_init_io(&s
->iomem
, OBJECT(s
), &allwinner_r40_ccu_ops
, s
,
174 TYPE_AW_R40_CCU
, AW_R40_CCU_IOSIZE
);
175 sysbus_init_mmio(sbd
, &s
->iomem
);
178 static const VMStateDescription allwinner_r40_ccu_vmstate
= {
179 .name
= "allwinner-r40-ccu",
181 .minimum_version_id
= 1,
182 .fields
= (const VMStateField
[]) {
183 VMSTATE_UINT32_ARRAY(regs
, AwR40ClockCtlState
, AW_R40_CCU_REGS_NUM
),
184 VMSTATE_END_OF_LIST()
188 static void allwinner_r40_ccu_class_init(ObjectClass
*klass
, void *data
)
190 DeviceClass
*dc
= DEVICE_CLASS(klass
);
192 dc
->reset
= allwinner_r40_ccu_reset
;
193 dc
->vmsd
= &allwinner_r40_ccu_vmstate
;
196 static const TypeInfo allwinner_r40_ccu_info
= {
197 .name
= TYPE_AW_R40_CCU
,
198 .parent
= TYPE_SYS_BUS_DEVICE
,
199 .instance_init
= allwinner_r40_ccu_init
,
200 .instance_size
= sizeof(AwR40ClockCtlState
),
201 .class_init
= allwinner_r40_ccu_class_init
,
204 static void allwinner_r40_ccu_register(void)
206 type_register_static(&allwinner_r40_ccu_info
);
209 type_init(allwinner_r40_ccu_register
)