1 /* 2 * QEMU model of the CRF - Clock Reset FPD. 3 * 4 * Copyright (c) 2022 Xilinx Inc. 5 * SPDX-License-Identifier: GPL-2.0-or-later 6 * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com> 7 */ 8 9 #include "qemu/osdep.h" 10 #include "hw/sysbus.h" 11 #include "hw/register.h" 12 #include "qemu/bitops.h" 13 #include "qemu/log.h" 14 #include "migration/vmstate.h" 15 #include "hw/irq.h" 16 #include "hw/misc/xlnx-zynqmp-crf.h" 17 #include "target/arm/arm-powerctl.h" 18 19 #ifndef XLNX_ZYNQMP_CRF_ERR_DEBUG 20 #define XLNX_ZYNQMP_CRF_ERR_DEBUG 0 21 #endif 22 23 #define CRF_MAX_CPU 4 24 25 static void ir_update_irq(XlnxZynqMPCRF *s) 26 { 27 bool pending = s->regs[R_IR_STATUS] & ~s->regs[R_IR_MASK]; 28 qemu_set_irq(s->irq_ir, pending); 29 } 30 31 static void ir_status_postw(RegisterInfo *reg, uint64_t val64) 32 { 33 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque); 34 ir_update_irq(s); 35 } 36 37 static uint64_t ir_enable_prew(RegisterInfo *reg, uint64_t val64) 38 { 39 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque); 40 uint32_t val = val64; 41 42 s->regs[R_IR_MASK] &= ~val; 43 ir_update_irq(s); 44 return 0; 45 } 46 47 static uint64_t ir_disable_prew(RegisterInfo *reg, uint64_t val64) 48 { 49 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque); 50 uint32_t val = val64; 51 52 s->regs[R_IR_MASK] |= val; 53 ir_update_irq(s); 54 return 0; 55 } 56 57 static uint64_t rst_fpd_apu_prew(RegisterInfo *reg, uint64_t val64) 58 { 59 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(reg->opaque); 60 uint32_t val = val64; 61 uint32_t val_old = s->regs[R_RST_FPD_APU]; 62 unsigned int i; 63 64 for (i = 0; i < CRF_MAX_CPU; i++) { 65 uint32_t mask = (1 << (R_RST_FPD_APU_ACPU0_RESET_SHIFT + i)); 66 67 if ((val ^ val_old) & mask) { 68 if (val & mask) { 69 arm_set_cpu_off(i); 70 } else { 71 arm_set_cpu_on_and_reset(i); 72 } 73 } 74 } 75 return val64; 76 } 77 78 static const RegisterAccessInfo crf_regs_info[] = { 79 { .name = "ERR_CTRL", .addr = A_ERR_CTRL, 80 },{ .name = "IR_STATUS", .addr = A_IR_STATUS, 81 .w1c = 0x1, 82 .post_write = ir_status_postw, 83 },{ .name = "IR_MASK", .addr = A_IR_MASK, 84 .reset = 0x1, 85 .ro = 0x1, 86 },{ .name = "IR_ENABLE", .addr = A_IR_ENABLE, 87 .pre_write = ir_enable_prew, 88 },{ .name = "IR_DISABLE", .addr = A_IR_DISABLE, 89 .pre_write = ir_disable_prew, 90 },{ .name = "CRF_WPROT", .addr = A_CRF_WPROT, 91 },{ .name = "APLL_CTRL", .addr = A_APLL_CTRL, 92 .reset = 0x12c09, 93 .rsvd = 0xf88c80f6, 94 },{ .name = "APLL_CFG", .addr = A_APLL_CFG, 95 .rsvd = 0x1801210, 96 },{ .name = "APLL_FRAC_CFG", .addr = A_APLL_FRAC_CFG, 97 .rsvd = 0x7e330000, 98 },{ .name = "DPLL_CTRL", .addr = A_DPLL_CTRL, 99 .reset = 0x2c09, 100 .rsvd = 0xf88c80f6, 101 },{ .name = "DPLL_CFG", .addr = A_DPLL_CFG, 102 .rsvd = 0x1801210, 103 },{ .name = "DPLL_FRAC_CFG", .addr = A_DPLL_FRAC_CFG, 104 .rsvd = 0x7e330000, 105 },{ .name = "VPLL_CTRL", .addr = A_VPLL_CTRL, 106 .reset = 0x12809, 107 .rsvd = 0xf88c80f6, 108 },{ .name = "VPLL_CFG", .addr = A_VPLL_CFG, 109 .rsvd = 0x1801210, 110 },{ .name = "VPLL_FRAC_CFG", .addr = A_VPLL_FRAC_CFG, 111 .rsvd = 0x7e330000, 112 },{ .name = "PLL_STATUS", .addr = A_PLL_STATUS, 113 .reset = 0x3f, 114 .rsvd = 0xc0, 115 .ro = 0x3f, 116 },{ .name = "APLL_TO_LPD_CTRL", .addr = A_APLL_TO_LPD_CTRL, 117 .reset = 0x400, 118 .rsvd = 0xc0ff, 119 },{ .name = "DPLL_TO_LPD_CTRL", .addr = A_DPLL_TO_LPD_CTRL, 120 .reset = 0x400, 121 .rsvd = 0xc0ff, 122 },{ .name = "VPLL_TO_LPD_CTRL", .addr = A_VPLL_TO_LPD_CTRL, 123 .reset = 0x400, 124 .rsvd = 0xc0ff, 125 },{ .name = "ACPU_CTRL", .addr = A_ACPU_CTRL, 126 .reset = 0x3000400, 127 .rsvd = 0xfcffc0f8, 128 },{ .name = "DBG_TRACE_CTRL", .addr = A_DBG_TRACE_CTRL, 129 .reset = 0x2500, 130 .rsvd = 0xfeffc0f8, 131 },{ .name = "DBG_FPD_CTRL", .addr = A_DBG_FPD_CTRL, 132 .reset = 0x1002500, 133 .rsvd = 0xfeffc0f8, 134 },{ .name = "DP_VIDEO_REF_CTRL", .addr = A_DP_VIDEO_REF_CTRL, 135 .reset = 0x1002300, 136 .rsvd = 0xfec0c0f8, 137 },{ .name = "DP_AUDIO_REF_CTRL", .addr = A_DP_AUDIO_REF_CTRL, 138 .reset = 0x1032300, 139 .rsvd = 0xfec0c0f8, 140 },{ .name = "DP_STC_REF_CTRL", .addr = A_DP_STC_REF_CTRL, 141 .reset = 0x1203200, 142 .rsvd = 0xfec0c0f8, 143 },{ .name = "DDR_CTRL", .addr = A_DDR_CTRL, 144 .reset = 0x1000500, 145 .rsvd = 0xfeffc0f8, 146 },{ .name = "GPU_REF_CTRL", .addr = A_GPU_REF_CTRL, 147 .reset = 0x1500, 148 .rsvd = 0xf8ffc0f8, 149 },{ .name = "SATA_REF_CTRL", .addr = A_SATA_REF_CTRL, 150 .reset = 0x1001600, 151 .rsvd = 0xfeffc0f8, 152 },{ .name = "PCIE_REF_CTRL", .addr = A_PCIE_REF_CTRL, 153 .reset = 0x1500, 154 .rsvd = 0xfeffc0f8, 155 },{ .name = "GDMA_REF_CTRL", .addr = A_GDMA_REF_CTRL, 156 .reset = 0x1000500, 157 .rsvd = 0xfeffc0f8, 158 },{ .name = "DPDMA_REF_CTRL", .addr = A_DPDMA_REF_CTRL, 159 .reset = 0x1000500, 160 .rsvd = 0xfeffc0f8, 161 },{ .name = "TOPSW_MAIN_CTRL", .addr = A_TOPSW_MAIN_CTRL, 162 .reset = 0x1000400, 163 .rsvd = 0xfeffc0f8, 164 },{ .name = "TOPSW_LSBUS_CTRL", .addr = A_TOPSW_LSBUS_CTRL, 165 .reset = 0x1000800, 166 .rsvd = 0xfeffc0f8, 167 },{ .name = "DBG_TSTMP_CTRL", .addr = A_DBG_TSTMP_CTRL, 168 .reset = 0xa00, 169 .rsvd = 0xffffc0f8, 170 }, 171 { .name = "RST_FPD_TOP", .addr = A_RST_FPD_TOP, 172 .reset = 0xf9ffe, 173 .rsvd = 0xf06001, 174 },{ .name = "RST_FPD_APU", .addr = A_RST_FPD_APU, 175 .reset = 0x3d0f, 176 .rsvd = 0xc2f0, 177 .pre_write = rst_fpd_apu_prew, 178 },{ .name = "RST_DDR_SS", .addr = A_RST_DDR_SS, 179 .reset = 0xf, 180 .rsvd = 0xf3, 181 } 182 }; 183 184 static void crf_reset_enter(Object *obj, ResetType type) 185 { 186 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); 187 unsigned int i; 188 189 for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { 190 register_reset(&s->regs_info[i]); 191 } 192 } 193 194 static void crf_reset_hold(Object *obj, ResetType type) 195 { 196 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); 197 ir_update_irq(s); 198 } 199 200 static const MemoryRegionOps crf_ops = { 201 .read = register_read_memory, 202 .write = register_write_memory, 203 .endianness = DEVICE_LITTLE_ENDIAN, 204 .valid = { 205 .min_access_size = 4, 206 .max_access_size = 4, 207 }, 208 }; 209 210 static void crf_init(Object *obj) 211 { 212 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); 213 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 214 215 s->reg_array = 216 register_init_block32(DEVICE(obj), crf_regs_info, 217 ARRAY_SIZE(crf_regs_info), 218 s->regs_info, s->regs, 219 &crf_ops, 220 XLNX_ZYNQMP_CRF_ERR_DEBUG, 221 CRF_R_MAX * 4); 222 sysbus_init_mmio(sbd, &s->reg_array->mem); 223 sysbus_init_irq(sbd, &s->irq_ir); 224 } 225 226 static void crf_finalize(Object *obj) 227 { 228 XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); 229 register_finalize_block(s->reg_array); 230 } 231 232 static const VMStateDescription vmstate_crf = { 233 .name = TYPE_XLNX_ZYNQMP_CRF, 234 .version_id = 1, 235 .minimum_version_id = 1, 236 .fields = (const VMStateField[]) { 237 VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCRF, CRF_R_MAX), 238 VMSTATE_END_OF_LIST(), 239 } 240 }; 241 242 static void crf_class_init(ObjectClass *klass, void *data) 243 { 244 ResettableClass *rc = RESETTABLE_CLASS(klass); 245 DeviceClass *dc = DEVICE_CLASS(klass); 246 247 dc->vmsd = &vmstate_crf; 248 rc->phases.enter = crf_reset_enter; 249 rc->phases.hold = crf_reset_hold; 250 } 251 252 static const TypeInfo crf_info = { 253 .name = TYPE_XLNX_ZYNQMP_CRF, 254 .parent = TYPE_SYS_BUS_DEVICE, 255 .instance_size = sizeof(XlnxZynqMPCRF), 256 .class_init = crf_class_init, 257 .instance_init = crf_init, 258 .instance_finalize = crf_finalize, 259 }; 260 261 static void crf_register_types(void) 262 { 263 type_register_static(&crf_info); 264 } 265 266 type_init(crf_register_types) 267