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