1 /* 2 * QEMU SiFive E PRCI (Power, Reset, Clock, Interrupt) 3 * 4 * Copyright (c) 2017 SiFive, Inc. 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 "qapi/error.h" 24 #include "qemu/log.h" 25 #include "qemu/module.h" 26 #include "hw/misc/sifive_e_prci.h" 27 28 static uint64_t sifive_e_prci_read(void *opaque, hwaddr addr, unsigned int size) 29 { 30 SiFiveEPRCIState *s = opaque; 31 switch (addr) { 32 case SIFIVE_E_PRCI_HFROSCCFG: 33 return s->hfrosccfg; 34 case SIFIVE_E_PRCI_HFXOSCCFG: 35 return s->hfxosccfg; 36 case SIFIVE_E_PRCI_PLLCFG: 37 return s->pllcfg; 38 case SIFIVE_E_PRCI_PLLOUTDIV: 39 return s->plloutdiv; 40 } 41 qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%x\n", 42 __func__, (int)addr); 43 return 0; 44 } 45 46 static void sifive_e_prci_write(void *opaque, hwaddr addr, 47 uint64_t val64, unsigned int size) 48 { 49 SiFiveEPRCIState *s = opaque; 50 switch (addr) { 51 case SIFIVE_E_PRCI_HFROSCCFG: 52 s->hfrosccfg = (uint32_t) val64; 53 /* OSC stays ready */ 54 s->hfrosccfg |= SIFIVE_E_PRCI_HFROSCCFG_RDY; 55 break; 56 case SIFIVE_E_PRCI_HFXOSCCFG: 57 s->hfxosccfg = (uint32_t) val64; 58 /* OSC stays ready */ 59 s->hfxosccfg |= SIFIVE_E_PRCI_HFXOSCCFG_RDY; 60 break; 61 case SIFIVE_E_PRCI_PLLCFG: 62 s->pllcfg = (uint32_t) val64; 63 /* PLL stays locked */ 64 s->pllcfg |= SIFIVE_E_PRCI_PLLCFG_LOCK; 65 break; 66 case SIFIVE_E_PRCI_PLLOUTDIV: 67 s->plloutdiv = (uint32_t) val64; 68 break; 69 default: 70 qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n", 71 __func__, (int)addr, (int)val64); 72 } 73 } 74 75 static const MemoryRegionOps sifive_e_prci_ops = { 76 .read = sifive_e_prci_read, 77 .write = sifive_e_prci_write, 78 .endianness = DEVICE_NATIVE_ENDIAN, 79 .valid = { 80 .min_access_size = 4, 81 .max_access_size = 4 82 } 83 }; 84 85 static void sifive_e_prci_init(Object *obj) 86 { 87 SiFiveEPRCIState *s = SIFIVE_E_PRCI(obj); 88 89 memory_region_init_io(&s->mmio, obj, &sifive_e_prci_ops, s, 90 TYPE_SIFIVE_E_PRCI, SIFIVE_E_PRCI_REG_SIZE); 91 sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); 92 93 s->hfrosccfg = (SIFIVE_E_PRCI_HFROSCCFG_RDY | SIFIVE_E_PRCI_HFROSCCFG_EN); 94 s->hfxosccfg = (SIFIVE_E_PRCI_HFXOSCCFG_RDY | SIFIVE_E_PRCI_HFXOSCCFG_EN); 95 s->pllcfg = (SIFIVE_E_PRCI_PLLCFG_REFSEL | SIFIVE_E_PRCI_PLLCFG_BYPASS | 96 SIFIVE_E_PRCI_PLLCFG_LOCK); 97 s->plloutdiv = SIFIVE_E_PRCI_PLLOUTDIV_DIV1; 98 } 99 100 static const TypeInfo sifive_e_prci_info = { 101 .name = TYPE_SIFIVE_E_PRCI, 102 .parent = TYPE_SYS_BUS_DEVICE, 103 .instance_size = sizeof(SiFiveEPRCIState), 104 .instance_init = sifive_e_prci_init, 105 }; 106 107 static void sifive_e_prci_register_types(void) 108 { 109 type_register_static(&sifive_e_prci_info); 110 } 111 112 type_init(sifive_e_prci_register_types) 113 114 115 /* 116 * Create PRCI device. 117 */ 118 DeviceState *sifive_e_prci_create(hwaddr addr) 119 { 120 DeviceState *dev = qdev_new(TYPE_SIFIVE_E_PRCI); 121 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 122 sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); 123 return dev; 124 } 125