sifive_u_prci.c (5553B)
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 27static 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 58static 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 112static 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 122static 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 131static 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 149static 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 dc->reset = sifive_u_prci_reset; 155} 156 157static 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 164static void sifive_u_prci_register_types(void) 165{ 166 type_register_static(&sifive_u_prci_info); 167} 168 169type_init(sifive_u_prci_register_types)