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