2 * Allwinner A10 Clock Control Module emulation
4 * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
6 * This file is derived from Allwinner H3 CCU,
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "qemu/osdep.h"
24 #include "qemu/units.h"
25 #include "hw/sysbus.h"
26 #include "migration/vmstate.h"
28 #include "qemu/module.h"
29 #include "hw/misc/allwinner-a10-ccm.h"
31 /* CCM register offsets */
33 REG_PLL1_CFG
= 0x0000, /* PLL1 Control */
34 REG_PLL1_TUN
= 0x0004, /* PLL1 Tuning */
35 REG_PLL2_CFG
= 0x0008, /* PLL2 Control */
36 REG_PLL2_TUN
= 0x000C, /* PLL2 Tuning */
37 REG_PLL3_CFG
= 0x0010, /* PLL3 Control */
38 REG_PLL4_CFG
= 0x0018, /* PLL4 Control */
39 REG_PLL5_CFG
= 0x0020, /* PLL5 Control */
40 REG_PLL5_TUN
= 0x0024, /* PLL5 Tuning */
41 REG_PLL6_CFG
= 0x0028, /* PLL6 Control */
42 REG_PLL6_TUN
= 0x002C, /* PLL6 Tuning */
43 REG_PLL7_CFG
= 0x0030, /* PLL7 Control */
44 REG_PLL1_TUN2
= 0x0038, /* PLL1 Tuning2 */
45 REG_PLL5_TUN2
= 0x003C, /* PLL5 Tuning2 */
46 REG_PLL8_CFG
= 0x0040, /* PLL8 Control */
47 REG_OSC24M_CFG
= 0x0050, /* OSC24M Control */
48 REG_CPU_AHB_APB0_CFG
= 0x0054, /* CPU, AHB and APB0 Divide Ratio */
51 #define REG_INDEX(offset) (offset / sizeof(uint32_t))
53 /* CCM register reset values */
55 REG_PLL1_CFG_RST
= 0x21005000,
56 REG_PLL1_TUN_RST
= 0x0A101000,
57 REG_PLL2_CFG_RST
= 0x08100010,
58 REG_PLL2_TUN_RST
= 0x00000000,
59 REG_PLL3_CFG_RST
= 0x0010D063,
60 REG_PLL4_CFG_RST
= 0x21009911,
61 REG_PLL5_CFG_RST
= 0x11049280,
62 REG_PLL5_TUN_RST
= 0x14888000,
63 REG_PLL6_CFG_RST
= 0x21009911,
64 REG_PLL6_TUN_RST
= 0x00000000,
65 REG_PLL7_CFG_RST
= 0x0010D063,
66 REG_PLL1_TUN2_RST
= 0x00000000,
67 REG_PLL5_TUN2_RST
= 0x00000000,
68 REG_PLL8_CFG_RST
= 0x21009911,
69 REG_OSC24M_CFG_RST
= 0x00138013,
70 REG_CPU_AHB_APB0_CFG_RST
= 0x00010010,
73 static uint64_t allwinner_a10_ccm_read(void *opaque
, hwaddr offset
,
76 const AwA10ClockCtlState
*s
= AW_A10_CCM(opaque
);
77 const uint32_t idx
= REG_INDEX(offset
);
95 case REG_CPU_AHB_APB0_CFG
:
97 case 0x158 ... AW_A10_CCM_IOSIZE
:
98 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
99 __func__
, (uint32_t)offset
);
102 qemu_log_mask(LOG_UNIMP
, "%s: unimplemented read offset 0x%04x\n",
103 __func__
, (uint32_t)offset
);
110 static void allwinner_a10_ccm_write(void *opaque
, hwaddr offset
,
111 uint64_t val
, unsigned size
)
113 AwA10ClockCtlState
*s
= AW_A10_CCM(opaque
);
114 const uint32_t idx
= REG_INDEX(offset
);
132 case REG_CPU_AHB_APB0_CFG
:
134 case 0x158 ... AW_A10_CCM_IOSIZE
:
135 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
136 __func__
, (uint32_t)offset
);
139 qemu_log_mask(LOG_UNIMP
, "%s: unimplemented write offset 0x%04x\n",
140 __func__
, (uint32_t)offset
);
144 s
->regs
[idx
] = (uint32_t) val
;
147 static const MemoryRegionOps allwinner_a10_ccm_ops
= {
148 .read
= allwinner_a10_ccm_read
,
149 .write
= allwinner_a10_ccm_write
,
150 .endianness
= DEVICE_NATIVE_ENDIAN
,
152 .min_access_size
= 4,
153 .max_access_size
= 4,
155 .impl
.min_access_size
= 4,
158 static void allwinner_a10_ccm_reset_enter(Object
*obj
, ResetType type
)
160 AwA10ClockCtlState
*s
= AW_A10_CCM(obj
);
162 /* Set default values for registers */
163 s
->regs
[REG_INDEX(REG_PLL1_CFG
)] = REG_PLL1_CFG_RST
;
164 s
->regs
[REG_INDEX(REG_PLL1_TUN
)] = REG_PLL1_TUN_RST
;
165 s
->regs
[REG_INDEX(REG_PLL2_CFG
)] = REG_PLL2_CFG_RST
;
166 s
->regs
[REG_INDEX(REG_PLL2_TUN
)] = REG_PLL2_TUN_RST
;
167 s
->regs
[REG_INDEX(REG_PLL3_CFG
)] = REG_PLL3_CFG_RST
;
168 s
->regs
[REG_INDEX(REG_PLL4_CFG
)] = REG_PLL4_CFG_RST
;
169 s
->regs
[REG_INDEX(REG_PLL5_CFG
)] = REG_PLL5_CFG_RST
;
170 s
->regs
[REG_INDEX(REG_PLL5_TUN
)] = REG_PLL5_TUN_RST
;
171 s
->regs
[REG_INDEX(REG_PLL6_CFG
)] = REG_PLL6_CFG_RST
;
172 s
->regs
[REG_INDEX(REG_PLL6_TUN
)] = REG_PLL6_TUN_RST
;
173 s
->regs
[REG_INDEX(REG_PLL7_CFG
)] = REG_PLL7_CFG_RST
;
174 s
->regs
[REG_INDEX(REG_PLL1_TUN2
)] = REG_PLL1_TUN2_RST
;
175 s
->regs
[REG_INDEX(REG_PLL5_TUN2
)] = REG_PLL5_TUN2_RST
;
176 s
->regs
[REG_INDEX(REG_PLL8_CFG
)] = REG_PLL8_CFG_RST
;
177 s
->regs
[REG_INDEX(REG_OSC24M_CFG
)] = REG_OSC24M_CFG_RST
;
178 s
->regs
[REG_INDEX(REG_CPU_AHB_APB0_CFG
)] = REG_CPU_AHB_APB0_CFG_RST
;
181 static void allwinner_a10_ccm_init(Object
*obj
)
183 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
184 AwA10ClockCtlState
*s
= AW_A10_CCM(obj
);
187 memory_region_init_io(&s
->iomem
, OBJECT(s
), &allwinner_a10_ccm_ops
, s
,
188 TYPE_AW_A10_CCM
, AW_A10_CCM_IOSIZE
);
189 sysbus_init_mmio(sbd
, &s
->iomem
);
192 static const VMStateDescription allwinner_a10_ccm_vmstate
= {
193 .name
= "allwinner-a10-ccm",
195 .minimum_version_id
= 1,
196 .fields
= (const VMStateField
[]) {
197 VMSTATE_UINT32_ARRAY(regs
, AwA10ClockCtlState
, AW_A10_CCM_REGS_NUM
),
198 VMSTATE_END_OF_LIST()
202 static void allwinner_a10_ccm_class_init(ObjectClass
*klass
, void *data
)
204 DeviceClass
*dc
= DEVICE_CLASS(klass
);
205 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
207 rc
->phases
.enter
= allwinner_a10_ccm_reset_enter
;
208 dc
->vmsd
= &allwinner_a10_ccm_vmstate
;
211 static const TypeInfo allwinner_a10_ccm_info
= {
212 .name
= TYPE_AW_A10_CCM
,
213 .parent
= TYPE_SYS_BUS_DEVICE
,
214 .instance_init
= allwinner_a10_ccm_init
,
215 .instance_size
= sizeof(AwA10ClockCtlState
),
216 .class_init
= allwinner_a10_ccm_class_init
,
219 static void allwinner_a10_ccm_register(void)
221 type_register_static(&allwinner_a10_ccm_info
);
224 type_init(allwinner_a10_ccm_register
)