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