xref: /qemu/hw/misc/npcm7xx_clk.c (revision 92eecfff)
1 /*
2  * Nuvoton NPCM7xx Clock Control Registers.
3  *
4  * Copyright 2020 Google LLC
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14  * for more details.
15  */
16 
17 #include "qemu/osdep.h"
18 
19 #include "hw/misc/npcm7xx_clk.h"
20 #include "hw/timer/npcm7xx_timer.h"
21 #include "migration/vmstate.h"
22 #include "qemu/error-report.h"
23 #include "qemu/log.h"
24 #include "qemu/module.h"
25 #include "qemu/timer.h"
26 #include "qemu/units.h"
27 #include "trace.h"
28 #include "sysemu/watchdog.h"
29 
30 #define PLLCON_LOKI     BIT(31)
31 #define PLLCON_LOKS     BIT(30)
32 #define PLLCON_PWDEN    BIT(12)
33 
34 enum NPCM7xxCLKRegisters {
35     NPCM7XX_CLK_CLKEN1,
36     NPCM7XX_CLK_CLKSEL,
37     NPCM7XX_CLK_CLKDIV1,
38     NPCM7XX_CLK_PLLCON0,
39     NPCM7XX_CLK_PLLCON1,
40     NPCM7XX_CLK_SWRSTR,
41     NPCM7XX_CLK_IPSRST1         = 0x20 / sizeof(uint32_t),
42     NPCM7XX_CLK_IPSRST2,
43     NPCM7XX_CLK_CLKEN2,
44     NPCM7XX_CLK_CLKDIV2,
45     NPCM7XX_CLK_CLKEN3,
46     NPCM7XX_CLK_IPSRST3,
47     NPCM7XX_CLK_WD0RCR,
48     NPCM7XX_CLK_WD1RCR,
49     NPCM7XX_CLK_WD2RCR,
50     NPCM7XX_CLK_SWRSTC1,
51     NPCM7XX_CLK_SWRSTC2,
52     NPCM7XX_CLK_SWRSTC3,
53     NPCM7XX_CLK_SWRSTC4,
54     NPCM7XX_CLK_PLLCON2,
55     NPCM7XX_CLK_CLKDIV3,
56     NPCM7XX_CLK_CORSTC,
57     NPCM7XX_CLK_PLLCONG,
58     NPCM7XX_CLK_AHBCKFI,
59     NPCM7XX_CLK_SECCNT,
60     NPCM7XX_CLK_CNTR25M,
61     NPCM7XX_CLK_REGS_END,
62 };
63 
64 /*
65  * These reset values were taken from version 0.91 of the NPCM750R data sheet.
66  *
67  * All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on
68  * core domain reset, but this reset type is not yet supported by QEMU.
69  */
70 static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
71     [NPCM7XX_CLK_CLKEN1]        = 0xffffffff,
72     [NPCM7XX_CLK_CLKSEL]        = 0x004aaaaa,
73     [NPCM7XX_CLK_CLKDIV1]       = 0x5413f855,
74     [NPCM7XX_CLK_PLLCON0]       = 0x00222101 | PLLCON_LOKI,
75     [NPCM7XX_CLK_PLLCON1]       = 0x00202101 | PLLCON_LOKI,
76     [NPCM7XX_CLK_IPSRST1]       = 0x00001000,
77     [NPCM7XX_CLK_IPSRST2]       = 0x80000000,
78     [NPCM7XX_CLK_CLKEN2]        = 0xffffffff,
79     [NPCM7XX_CLK_CLKDIV2]       = 0xaa4f8f9f,
80     [NPCM7XX_CLK_CLKEN3]        = 0xffffffff,
81     [NPCM7XX_CLK_IPSRST3]       = 0x03000000,
82     [NPCM7XX_CLK_WD0RCR]        = 0xffffffff,
83     [NPCM7XX_CLK_WD1RCR]        = 0xffffffff,
84     [NPCM7XX_CLK_WD2RCR]        = 0xffffffff,
85     [NPCM7XX_CLK_SWRSTC1]       = 0x00000003,
86     [NPCM7XX_CLK_PLLCON2]       = 0x00c02105 | PLLCON_LOKI,
87     [NPCM7XX_CLK_CORSTC]        = 0x04000003,
88     [NPCM7XX_CLK_PLLCONG]       = 0x01228606 | PLLCON_LOKI,
89     [NPCM7XX_CLK_AHBCKFI]       = 0x000000c8,
90 };
91 
92 /* Register Field Definitions */
93 #define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
94 
95 /* The number of watchdogs that can trigger a reset. */
96 #define NPCM7XX_NR_WATCHDOGS    (3)
97 
98 static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
99 {
100     uint32_t reg = offset / sizeof(uint32_t);
101     NPCM7xxCLKState *s = opaque;
102     int64_t now_ns;
103     uint32_t value = 0;
104 
105     if (reg >= NPCM7XX_CLK_NR_REGS) {
106         qemu_log_mask(LOG_GUEST_ERROR,
107                       "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
108                       __func__, offset);
109         return 0;
110     }
111 
112     switch (reg) {
113     case NPCM7XX_CLK_SWRSTR:
114         qemu_log_mask(LOG_GUEST_ERROR,
115                       "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n",
116                       __func__, offset);
117         break;
118 
119     case NPCM7XX_CLK_SECCNT:
120         now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
121         value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND;
122         break;
123 
124     case NPCM7XX_CLK_CNTR25M:
125         now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
126         /*
127          * This register counts 25 MHz cycles, updating every 640 ns. It rolls
128          * over to zero every second.
129          *
130          * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000.
131          */
132         value = (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_TIMER_REF_HZ;
133         break;
134 
135     default:
136         value = s->regs[reg];
137         break;
138     };
139 
140     trace_npcm7xx_clk_read(offset, value);
141 
142     return value;
143 }
144 
145 static void npcm7xx_clk_write(void *opaque, hwaddr offset,
146                               uint64_t v, unsigned size)
147 {
148     uint32_t reg = offset / sizeof(uint32_t);
149     NPCM7xxCLKState *s = opaque;
150     uint32_t value = v;
151 
152     trace_npcm7xx_clk_write(offset, value);
153 
154     if (reg >= NPCM7XX_CLK_NR_REGS) {
155         qemu_log_mask(LOG_GUEST_ERROR,
156                       "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
157                       __func__, offset);
158         return;
159     }
160 
161     switch (reg) {
162     case NPCM7XX_CLK_SWRSTR:
163         qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n",
164                       __func__, value);
165         value = 0;
166         break;
167 
168     case NPCM7XX_CLK_PLLCON0:
169     case NPCM7XX_CLK_PLLCON1:
170     case NPCM7XX_CLK_PLLCON2:
171     case NPCM7XX_CLK_PLLCONG:
172         if (value & PLLCON_PWDEN) {
173             /* Power down -- clear lock and indicate loss of lock */
174             value &= ~PLLCON_LOKI;
175             value |= PLLCON_LOKS;
176         } else {
177             /* Normal mode -- assume always locked */
178             value |= PLLCON_LOKI;
179             /* Keep LOKS unchanged unless cleared by writing 1 */
180             if (value & PLLCON_LOKS) {
181                 value &= ~PLLCON_LOKS;
182             } else {
183                 value |= (value & PLLCON_LOKS);
184             }
185         }
186         break;
187 
188     case NPCM7XX_CLK_CNTR25M:
189         qemu_log_mask(LOG_GUEST_ERROR,
190                       "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
191                       __func__, offset);
192         return;
193     }
194 
195     s->regs[reg] = value;
196 }
197 
198 /* Perform reset action triggered by a watchdog */
199 static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n,
200         int level)
201 {
202     NPCM7xxCLKState *clk = NPCM7XX_CLK(opaque);
203     uint32_t rcr;
204 
205     g_assert(n >= 0 && n <= NPCM7XX_NR_WATCHDOGS);
206     rcr = clk->regs[NPCM7XX_CLK_WD0RCR + n];
207     if (rcr & NPCM7XX_CLK_WDRCR_CA9C) {
208         watchdog_perform_action();
209     } else {
210         qemu_log_mask(LOG_UNIMP,
211                 "%s: only CPU reset is implemented. (requested 0x%" PRIx32")\n",
212                 __func__, rcr);
213     }
214 }
215 
216 static const struct MemoryRegionOps npcm7xx_clk_ops = {
217     .read       = npcm7xx_clk_read,
218     .write      = npcm7xx_clk_write,
219     .endianness = DEVICE_LITTLE_ENDIAN,
220     .valid      = {
221         .min_access_size        = 4,
222         .max_access_size        = 4,
223         .unaligned              = false,
224     },
225 };
226 
227 static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
228 {
229     NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
230 
231     QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
232 
233     switch (type) {
234     case RESET_TYPE_COLD:
235         memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values));
236         s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
237         return;
238     }
239 
240     /*
241      * A small number of registers need to be reset on a core domain reset,
242      * but no such reset type exists yet.
243      */
244     qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.",
245                   __func__, type);
246 }
247 
248 static void npcm7xx_clk_init(Object *obj)
249 {
250     NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
251 
252     memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s,
253                           TYPE_NPCM7XX_CLK, 4 * KiB);
254     sysbus_init_mmio(&s->parent, &s->iomem);
255     qdev_init_gpio_in_named(DEVICE(s), npcm7xx_clk_perform_watchdog_reset,
256             NPCM7XX_WATCHDOG_RESET_GPIO_IN, NPCM7XX_NR_WATCHDOGS);
257 }
258 
259 static const VMStateDescription vmstate_npcm7xx_clk = {
260     .name = "npcm7xx-clk",
261     .version_id = 0,
262     .minimum_version_id = 0,
263     .fields = (VMStateField[]) {
264         VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS),
265         VMSTATE_INT64(ref_ns, NPCM7xxCLKState),
266         VMSTATE_END_OF_LIST(),
267     },
268 };
269 
270 static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
271 {
272     ResettableClass *rc = RESETTABLE_CLASS(klass);
273     DeviceClass *dc = DEVICE_CLASS(klass);
274 
275     QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM7XX_CLK_NR_REGS);
276 
277     dc->desc = "NPCM7xx Clock Control Registers";
278     dc->vmsd = &vmstate_npcm7xx_clk;
279     rc->phases.enter = npcm7xx_clk_enter_reset;
280 }
281 
282 static const TypeInfo npcm7xx_clk_info = {
283     .name               = TYPE_NPCM7XX_CLK,
284     .parent             = TYPE_SYS_BUS_DEVICE,
285     .instance_size      = sizeof(NPCM7xxCLKState),
286     .instance_init      = npcm7xx_clk_init,
287     .class_init         = npcm7xx_clk_class_init,
288 };
289 
290 static void npcm7xx_clk_register_type(void)
291 {
292     type_register_static(&npcm7xx_clk_info);
293 }
294 type_init(npcm7xx_clk_register_type);
295