xref: /qemu/hw/misc/allwinner-a10-ccm.c (revision 29b62a10)
1 /*
2  * Allwinner A10 Clock Control Module emulation
3  *
4  * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
5  *
6  *  This file is derived from Allwinner H3 CCU,
7  *  by Niek Linnenbank.
8  *
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.
13  *
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.
18  *
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/>.
21  */
22 
23 #include "qemu/osdep.h"
24 #include "qemu/units.h"
25 #include "hw/sysbus.h"
26 #include "migration/vmstate.h"
27 #include "qemu/log.h"
28 #include "qemu/module.h"
29 #include "hw/misc/allwinner-a10-ccm.h"
30 
31 /* CCM register offsets */
32 enum {
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 */
49 };
50 
51 #define REG_INDEX(offset)    (offset / sizeof(uint32_t))
52 
53 /* CCM register reset values */
54 enum {
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,
71 };
72 
73 static uint64_t allwinner_a10_ccm_read(void *opaque, hwaddr offset,
74                                        unsigned size)
75 {
76     const AwA10ClockCtlState *s = AW_A10_CCM(opaque);
77     const uint32_t idx = REG_INDEX(offset);
78 
79     switch (offset) {
80     case REG_PLL1_CFG:
81     case REG_PLL1_TUN:
82     case REG_PLL2_CFG:
83     case REG_PLL2_TUN:
84     case REG_PLL3_CFG:
85     case REG_PLL4_CFG:
86     case REG_PLL5_CFG:
87     case REG_PLL5_TUN:
88     case REG_PLL6_CFG:
89     case REG_PLL6_TUN:
90     case REG_PLL7_CFG:
91     case REG_PLL1_TUN2:
92     case REG_PLL5_TUN2:
93     case REG_PLL8_CFG:
94     case REG_OSC24M_CFG:
95     case REG_CPU_AHB_APB0_CFG:
96         break;
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);
100         return 0;
101     default:
102         qemu_log_mask(LOG_UNIMP, "%s: unimplemented read offset 0x%04x\n",
103                       __func__, (uint32_t)offset);
104         return 0;
105     }
106 
107     return s->regs[idx];
108 }
109 
110 static void allwinner_a10_ccm_write(void *opaque, hwaddr offset,
111                                    uint64_t val, unsigned size)
112 {
113     AwA10ClockCtlState *s = AW_A10_CCM(opaque);
114     const uint32_t idx = REG_INDEX(offset);
115 
116     switch (offset) {
117     case REG_PLL1_CFG:
118     case REG_PLL1_TUN:
119     case REG_PLL2_CFG:
120     case REG_PLL2_TUN:
121     case REG_PLL3_CFG:
122     case REG_PLL4_CFG:
123     case REG_PLL5_CFG:
124     case REG_PLL5_TUN:
125     case REG_PLL6_CFG:
126     case REG_PLL6_TUN:
127     case REG_PLL7_CFG:
128     case REG_PLL1_TUN2:
129     case REG_PLL5_TUN2:
130     case REG_PLL8_CFG:
131     case REG_OSC24M_CFG:
132     case REG_CPU_AHB_APB0_CFG:
133         break;
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);
137         break;
138     default:
139         qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n",
140                       __func__, (uint32_t)offset);
141         break;
142     }
143 
144     s->regs[idx] = (uint32_t) val;
145 }
146 
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,
151     .valid = {
152         .min_access_size = 4,
153         .max_access_size = 4,
154     },
155     .impl.min_access_size = 4,
156 };
157 
158 static void allwinner_a10_ccm_reset_enter(Object *obj, ResetType type)
159 {
160     AwA10ClockCtlState *s = AW_A10_CCM(obj);
161 
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;
179 }
180 
181 static void allwinner_a10_ccm_init(Object *obj)
182 {
183     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
184     AwA10ClockCtlState *s = AW_A10_CCM(obj);
185 
186     /* Memory mapping */
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);
190 }
191 
192 static const VMStateDescription allwinner_a10_ccm_vmstate = {
193     .name = "allwinner-a10-ccm",
194     .version_id = 1,
195     .minimum_version_id = 1,
196     .fields = (VMStateField[]) {
197         VMSTATE_UINT32_ARRAY(regs, AwA10ClockCtlState, AW_A10_CCM_REGS_NUM),
198         VMSTATE_END_OF_LIST()
199     }
200 };
201 
202 static void allwinner_a10_ccm_class_init(ObjectClass *klass, void *data)
203 {
204     DeviceClass *dc = DEVICE_CLASS(klass);
205     ResettableClass *rc = RESETTABLE_CLASS(klass);
206 
207     rc->phases.enter = allwinner_a10_ccm_reset_enter;
208     dc->vmsd = &allwinner_a10_ccm_vmstate;
209 }
210 
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,
217 };
218 
219 static void allwinner_a10_ccm_register(void)
220 {
221     type_register_static(&allwinner_a10_ccm_info);
222 }
223 
224 type_init(allwinner_a10_ccm_register)
225