2 * Allwinner H3 Clock Control Unit emulation
4 * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.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-h3-ccu.h"
28 /* CCU register offsets */
30 REG_PLL_CPUX
= 0x0000, /* PLL CPUX Control */
31 REG_PLL_AUDIO
= 0x0008, /* PLL Audio Control */
32 REG_PLL_VIDEO
= 0x0010, /* PLL Video Control */
33 REG_PLL_VE
= 0x0018, /* PLL VE Control */
34 REG_PLL_DDR
= 0x0020, /* PLL DDR Control */
35 REG_PLL_PERIPH0
= 0x0028, /* PLL Peripherals 0 Control */
36 REG_PLL_GPU
= 0x0038, /* PLL GPU Control */
37 REG_PLL_PERIPH1
= 0x0044, /* PLL Peripherals 1 Control */
38 REG_PLL_DE
= 0x0048, /* PLL Display Engine Control */
39 REG_CPUX_AXI
= 0x0050, /* CPUX/AXI Configuration */
40 REG_APB1
= 0x0054, /* ARM Peripheral Bus 1 Config */
41 REG_APB2
= 0x0058, /* ARM Peripheral Bus 2 Config */
42 REG_DRAM_CFG
= 0x00F4, /* DRAM Configuration */
43 REG_MBUS
= 0x00FC, /* MBUS Reset */
44 REG_PLL_TIME0
= 0x0200, /* PLL Stable Time 0 */
45 REG_PLL_TIME1
= 0x0204, /* PLL Stable Time 1 */
46 REG_PLL_CPUX_BIAS
= 0x0220, /* PLL CPUX Bias */
47 REG_PLL_AUDIO_BIAS
= 0x0224, /* PLL Audio Bias */
48 REG_PLL_VIDEO_BIAS
= 0x0228, /* PLL Video Bias */
49 REG_PLL_VE_BIAS
= 0x022C, /* PLL VE Bias */
50 REG_PLL_DDR_BIAS
= 0x0230, /* PLL DDR Bias */
51 REG_PLL_PERIPH0_BIAS
= 0x0234, /* PLL Peripherals 0 Bias */
52 REG_PLL_GPU_BIAS
= 0x023C, /* PLL GPU Bias */
53 REG_PLL_PERIPH1_BIAS
= 0x0244, /* PLL Peripherals 1 Bias */
54 REG_PLL_DE_BIAS
= 0x0248, /* PLL Display Engine Bias */
55 REG_PLL_CPUX_TUNING
= 0x0250, /* PLL CPUX Tuning */
56 REG_PLL_DDR_TUNING
= 0x0260, /* PLL DDR Tuning */
59 #define REG_INDEX(offset) (offset / sizeof(uint32_t))
61 /* CCU register flags */
63 REG_DRAM_CFG_UPDATE
= (1 << 16),
67 REG_PLL_ENABLE
= (1 << 31),
68 REG_PLL_LOCK
= (1 << 28),
72 /* CCU register reset values */
74 REG_PLL_CPUX_RST
= 0x00001000,
75 REG_PLL_AUDIO_RST
= 0x00035514,
76 REG_PLL_VIDEO_RST
= 0x03006207,
77 REG_PLL_VE_RST
= 0x03006207,
78 REG_PLL_DDR_RST
= 0x00001000,
79 REG_PLL_PERIPH0_RST
= 0x00041811,
80 REG_PLL_GPU_RST
= 0x03006207,
81 REG_PLL_PERIPH1_RST
= 0x00041811,
82 REG_PLL_DE_RST
= 0x03006207,
83 REG_CPUX_AXI_RST
= 0x00010000,
84 REG_APB1_RST
= 0x00001010,
85 REG_APB2_RST
= 0x01000000,
86 REG_DRAM_CFG_RST
= 0x00000000,
87 REG_MBUS_RST
= 0x80000000,
88 REG_PLL_TIME0_RST
= 0x000000FF,
89 REG_PLL_TIME1_RST
= 0x000000FF,
90 REG_PLL_CPUX_BIAS_RST
= 0x08100200,
91 REG_PLL_AUDIO_BIAS_RST
= 0x10100000,
92 REG_PLL_VIDEO_BIAS_RST
= 0x10100000,
93 REG_PLL_VE_BIAS_RST
= 0x10100000,
94 REG_PLL_DDR_BIAS_RST
= 0x81104000,
95 REG_PLL_PERIPH0_BIAS_RST
= 0x10100010,
96 REG_PLL_GPU_BIAS_RST
= 0x10100000,
97 REG_PLL_PERIPH1_BIAS_RST
= 0x10100010,
98 REG_PLL_DE_BIAS_RST
= 0x10100000,
99 REG_PLL_CPUX_TUNING_RST
= 0x0A101000,
100 REG_PLL_DDR_TUNING_RST
= 0x14880000,
103 static uint64_t allwinner_h3_ccu_read(void *opaque
, hwaddr offset
,
106 const AwH3ClockCtlState
*s
= AW_H3_CCU(opaque
);
107 const uint32_t idx
= REG_INDEX(offset
);
110 case 0x308 ... AW_H3_CCU_IOSIZE
:
111 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
112 __func__
, (uint32_t)offset
);
119 static void allwinner_h3_ccu_write(void *opaque
, hwaddr offset
,
120 uint64_t val
, unsigned size
)
122 AwH3ClockCtlState
*s
= AW_H3_CCU(opaque
);
123 const uint32_t idx
= REG_INDEX(offset
);
126 case REG_DRAM_CFG
: /* DRAM Configuration */
127 val
&= ~REG_DRAM_CFG_UPDATE
;
129 case REG_PLL_CPUX
: /* PLL CPUX Control */
130 case REG_PLL_AUDIO
: /* PLL Audio Control */
131 case REG_PLL_VIDEO
: /* PLL Video Control */
132 case REG_PLL_VE
: /* PLL VE Control */
133 case REG_PLL_DDR
: /* PLL DDR Control */
134 case REG_PLL_PERIPH0
: /* PLL Peripherals 0 Control */
135 case REG_PLL_GPU
: /* PLL GPU Control */
136 case REG_PLL_PERIPH1
: /* PLL Peripherals 1 Control */
137 case REG_PLL_DE
: /* PLL Display Engine Control */
138 if (val
& REG_PLL_ENABLE
) {
142 case 0x308 ... AW_H3_CCU_IOSIZE
:
143 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
144 __func__
, (uint32_t)offset
);
147 qemu_log_mask(LOG_UNIMP
, "%s: unimplemented write offset 0x%04x\n",
148 __func__
, (uint32_t)offset
);
152 s
->regs
[idx
] = (uint32_t) val
;
155 static const MemoryRegionOps allwinner_h3_ccu_ops
= {
156 .read
= allwinner_h3_ccu_read
,
157 .write
= allwinner_h3_ccu_write
,
158 .endianness
= DEVICE_NATIVE_ENDIAN
,
160 .min_access_size
= 4,
161 .max_access_size
= 4,
163 .impl
.min_access_size
= 4,
166 static void allwinner_h3_ccu_reset(DeviceState
*dev
)
168 AwH3ClockCtlState
*s
= AW_H3_CCU(dev
);
170 /* Set default values for registers */
171 s
->regs
[REG_INDEX(REG_PLL_CPUX
)] = REG_PLL_CPUX_RST
;
172 s
->regs
[REG_INDEX(REG_PLL_AUDIO
)] = REG_PLL_AUDIO_RST
;
173 s
->regs
[REG_INDEX(REG_PLL_VIDEO
)] = REG_PLL_VIDEO_RST
;
174 s
->regs
[REG_INDEX(REG_PLL_VE
)] = REG_PLL_VE_RST
;
175 s
->regs
[REG_INDEX(REG_PLL_DDR
)] = REG_PLL_DDR_RST
;
176 s
->regs
[REG_INDEX(REG_PLL_PERIPH0
)] = REG_PLL_PERIPH0_RST
;
177 s
->regs
[REG_INDEX(REG_PLL_GPU
)] = REG_PLL_GPU_RST
;
178 s
->regs
[REG_INDEX(REG_PLL_PERIPH1
)] = REG_PLL_PERIPH1_RST
;
179 s
->regs
[REG_INDEX(REG_PLL_DE
)] = REG_PLL_DE_RST
;
180 s
->regs
[REG_INDEX(REG_CPUX_AXI
)] = REG_CPUX_AXI_RST
;
181 s
->regs
[REG_INDEX(REG_APB1
)] = REG_APB1_RST
;
182 s
->regs
[REG_INDEX(REG_APB2
)] = REG_APB2_RST
;
183 s
->regs
[REG_INDEX(REG_DRAM_CFG
)] = REG_DRAM_CFG_RST
;
184 s
->regs
[REG_INDEX(REG_MBUS
)] = REG_MBUS_RST
;
185 s
->regs
[REG_INDEX(REG_PLL_TIME0
)] = REG_PLL_TIME0_RST
;
186 s
->regs
[REG_INDEX(REG_PLL_TIME1
)] = REG_PLL_TIME1_RST
;
187 s
->regs
[REG_INDEX(REG_PLL_CPUX_BIAS
)] = REG_PLL_CPUX_BIAS_RST
;
188 s
->regs
[REG_INDEX(REG_PLL_AUDIO_BIAS
)] = REG_PLL_AUDIO_BIAS_RST
;
189 s
->regs
[REG_INDEX(REG_PLL_VIDEO_BIAS
)] = REG_PLL_VIDEO_BIAS_RST
;
190 s
->regs
[REG_INDEX(REG_PLL_VE_BIAS
)] = REG_PLL_VE_BIAS_RST
;
191 s
->regs
[REG_INDEX(REG_PLL_DDR_BIAS
)] = REG_PLL_DDR_BIAS_RST
;
192 s
->regs
[REG_INDEX(REG_PLL_PERIPH0_BIAS
)] = REG_PLL_PERIPH0_BIAS_RST
;
193 s
->regs
[REG_INDEX(REG_PLL_GPU_BIAS
)] = REG_PLL_GPU_BIAS_RST
;
194 s
->regs
[REG_INDEX(REG_PLL_PERIPH1_BIAS
)] = REG_PLL_PERIPH1_BIAS_RST
;
195 s
->regs
[REG_INDEX(REG_PLL_DE_BIAS
)] = REG_PLL_DE_BIAS_RST
;
196 s
->regs
[REG_INDEX(REG_PLL_CPUX_TUNING
)] = REG_PLL_CPUX_TUNING_RST
;
197 s
->regs
[REG_INDEX(REG_PLL_DDR_TUNING
)] = REG_PLL_DDR_TUNING_RST
;
200 static void allwinner_h3_ccu_init(Object
*obj
)
202 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
203 AwH3ClockCtlState
*s
= AW_H3_CCU(obj
);
206 memory_region_init_io(&s
->iomem
, OBJECT(s
), &allwinner_h3_ccu_ops
, s
,
207 TYPE_AW_H3_CCU
, AW_H3_CCU_IOSIZE
);
208 sysbus_init_mmio(sbd
, &s
->iomem
);
211 static const VMStateDescription allwinner_h3_ccu_vmstate
= {
212 .name
= "allwinner-h3-ccu",
214 .minimum_version_id
= 1,
215 .fields
= (const VMStateField
[]) {
216 VMSTATE_UINT32_ARRAY(regs
, AwH3ClockCtlState
, AW_H3_CCU_REGS_NUM
),
217 VMSTATE_END_OF_LIST()
221 static void allwinner_h3_ccu_class_init(ObjectClass
*klass
, void *data
)
223 DeviceClass
*dc
= DEVICE_CLASS(klass
);
225 dc
->reset
= allwinner_h3_ccu_reset
;
226 dc
->vmsd
= &allwinner_h3_ccu_vmstate
;
229 static const TypeInfo allwinner_h3_ccu_info
= {
230 .name
= TYPE_AW_H3_CCU
,
231 .parent
= TYPE_SYS_BUS_DEVICE
,
232 .instance_init
= allwinner_h3_ccu_init
,
233 .instance_size
= sizeof(AwH3ClockCtlState
),
234 .class_init
= allwinner_h3_ccu_class_init
,
237 static void allwinner_h3_ccu_register(void)
239 type_register_static(&allwinner_h3_ccu_info
);
242 type_init(allwinner_h3_ccu_register
)