1 /* 2 * QEMU SiFive U PRCI (Power, Reset, Clock, Interrupt) 3 * 4 * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com> 5 * 6 * Simple model of the PRCI to emulate register reads made by the SDK BSP 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms and conditions of the GNU General Public License, 10 * version 2 or later, as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "qemu/osdep.h" 22 #include "hw/sysbus.h" 23 #include "qemu/log.h" 24 #include "qemu/module.h" 25 #include "hw/misc/sifive_u_prci.h" 26 27 static uint64_t sifive_u_prci_read(void *opaque, hwaddr addr, unsigned int size) 28 { 29 SiFiveUPRCIState *s = opaque; 30 31 switch (addr) { 32 case SIFIVE_U_PRCI_HFXOSCCFG: 33 return s->hfxosccfg; 34 case SIFIVE_U_PRCI_COREPLLCFG0: 35 return s->corepllcfg0; 36 case SIFIVE_U_PRCI_DDRPLLCFG0: 37 return s->ddrpllcfg0; 38 case SIFIVE_U_PRCI_DDRPLLCFG1: 39 return s->ddrpllcfg1; 40 case SIFIVE_U_PRCI_GEMGXLPLLCFG0: 41 return s->gemgxlpllcfg0; 42 case SIFIVE_U_PRCI_GEMGXLPLLCFG1: 43 return s->gemgxlpllcfg1; 44 case SIFIVE_U_PRCI_CORECLKSEL: 45 return s->coreclksel; 46 case SIFIVE_U_PRCI_DEVICESRESET: 47 return s->devicesreset; 48 case SIFIVE_U_PRCI_CLKMUXSTATUS: 49 return s->clkmuxstatus; 50 } 51 52 qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n", 53 __func__, addr); 54 55 return 0; 56 } 57 58 static void sifive_u_prci_write(void *opaque, hwaddr addr, 59 uint64_t val64, unsigned int size) 60 { 61 SiFiveUPRCIState *s = opaque; 62 uint32_t val32 = (uint32_t)val64; 63 64 switch (addr) { 65 case SIFIVE_U_PRCI_HFXOSCCFG: 66 s->hfxosccfg = val32; 67 /* OSC stays ready */ 68 s->hfxosccfg |= SIFIVE_U_PRCI_HFXOSCCFG_RDY; 69 break; 70 case SIFIVE_U_PRCI_COREPLLCFG0: 71 s->corepllcfg0 = val32; 72 /* internal feedback */ 73 s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; 74 /* PLL stays locked */ 75 s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; 76 break; 77 case SIFIVE_U_PRCI_DDRPLLCFG0: 78 s->ddrpllcfg0 = val32; 79 /* internal feedback */ 80 s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; 81 /* PLL stays locked */ 82 s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; 83 break; 84 case SIFIVE_U_PRCI_DDRPLLCFG1: 85 s->ddrpllcfg1 = val32; 86 break; 87 case SIFIVE_U_PRCI_GEMGXLPLLCFG0: 88 s->gemgxlpllcfg0 = val32; 89 /* internal feedback */ 90 s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; 91 /* PLL stays locked */ 92 s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; 93 break; 94 case SIFIVE_U_PRCI_GEMGXLPLLCFG1: 95 s->gemgxlpllcfg1 = val32; 96 break; 97 case SIFIVE_U_PRCI_CORECLKSEL: 98 s->coreclksel = val32; 99 break; 100 case SIFIVE_U_PRCI_DEVICESRESET: 101 s->devicesreset = val32; 102 break; 103 case SIFIVE_U_PRCI_CLKMUXSTATUS: 104 s->clkmuxstatus = val32; 105 break; 106 default: 107 qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx 108 " v=0x%x\n", __func__, addr, val32); 109 } 110 } 111 112 static const MemoryRegionOps sifive_u_prci_ops = { 113 .read = sifive_u_prci_read, 114 .write = sifive_u_prci_write, 115 .endianness = DEVICE_NATIVE_ENDIAN, 116 .valid = { 117 .min_access_size = 4, 118 .max_access_size = 4 119 } 120 }; 121 122 static void sifive_u_prci_realize(DeviceState *dev, Error **errp) 123 { 124 SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev); 125 126 memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_prci_ops, s, 127 TYPE_SIFIVE_U_PRCI, SIFIVE_U_PRCI_REG_SIZE); 128 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); 129 } 130 131 static void sifive_u_prci_reset(DeviceState *dev) 132 { 133 SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev); 134 135 /* Initialize register to power-on-reset values */ 136 s->hfxosccfg = SIFIVE_U_PRCI_HFXOSCCFG_RDY | SIFIVE_U_PRCI_HFXOSCCFG_EN; 137 s->corepllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | 138 SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | 139 SIFIVE_U_PRCI_PLLCFG0_LOCK; 140 s->ddrpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | 141 SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | 142 SIFIVE_U_PRCI_PLLCFG0_LOCK; 143 s->gemgxlpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | 144 SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | 145 SIFIVE_U_PRCI_PLLCFG0_LOCK; 146 s->coreclksel = SIFIVE_U_PRCI_CORECLKSEL_HFCLK; 147 } 148 149 static void sifive_u_prci_class_init(ObjectClass *klass, void *data) 150 { 151 DeviceClass *dc = DEVICE_CLASS(klass); 152 153 dc->realize = sifive_u_prci_realize; 154 device_class_set_legacy_reset(dc, sifive_u_prci_reset); 155 } 156 157 static const TypeInfo sifive_u_prci_info = { 158 .name = TYPE_SIFIVE_U_PRCI, 159 .parent = TYPE_SYS_BUS_DEVICE, 160 .instance_size = sizeof(SiFiveUPRCIState), 161 .class_init = sifive_u_prci_class_init, 162 }; 163 164 static void sifive_u_prci_register_types(void) 165 { 166 type_register_static(&sifive_u_prci_info); 167 } 168 169 type_init(sifive_u_prci_register_types) 170