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