19fe640a5SBin Meng /* 29fe640a5SBin Meng * QEMU SiFive U PRCI (Power, Reset, Clock, Interrupt) 39fe640a5SBin Meng * 49fe640a5SBin Meng * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com> 59fe640a5SBin Meng * 69fe640a5SBin Meng * Simple model of the PRCI to emulate register reads made by the SDK BSP 79fe640a5SBin Meng * 89fe640a5SBin Meng * This program is free software; you can redistribute it and/or modify it 99fe640a5SBin Meng * under the terms and conditions of the GNU General Public License, 109fe640a5SBin Meng * version 2 or later, as published by the Free Software Foundation. 119fe640a5SBin Meng * 129fe640a5SBin Meng * This program is distributed in the hope it will be useful, but WITHOUT 139fe640a5SBin Meng * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 149fe640a5SBin Meng * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 159fe640a5SBin Meng * more details. 169fe640a5SBin Meng * 179fe640a5SBin Meng * You should have received a copy of the GNU General Public License along with 189fe640a5SBin Meng * this program. If not, see <http://www.gnu.org/licenses/>. 199fe640a5SBin Meng */ 209fe640a5SBin Meng 219fe640a5SBin Meng #include "qemu/osdep.h" 229fe640a5SBin Meng #include "hw/sysbus.h" 239fe640a5SBin Meng #include "qemu/log.h" 249fe640a5SBin Meng #include "qemu/module.h" 259fe640a5SBin Meng #include "hw/misc/sifive_u_prci.h" 269fe640a5SBin Meng 279fe640a5SBin Meng static uint64_t sifive_u_prci_read(void *opaque, hwaddr addr, unsigned int size) 289fe640a5SBin Meng { 299fe640a5SBin Meng SiFiveUPRCIState *s = opaque; 309fe640a5SBin Meng 319fe640a5SBin Meng switch (addr) { 329fe640a5SBin Meng case SIFIVE_U_PRCI_HFXOSCCFG: 339fe640a5SBin Meng return s->hfxosccfg; 349fe640a5SBin Meng case SIFIVE_U_PRCI_COREPLLCFG0: 359fe640a5SBin Meng return s->corepllcfg0; 369fe640a5SBin Meng case SIFIVE_U_PRCI_DDRPLLCFG0: 379fe640a5SBin Meng return s->ddrpllcfg0; 389fe640a5SBin Meng case SIFIVE_U_PRCI_DDRPLLCFG1: 399fe640a5SBin Meng return s->ddrpllcfg1; 409fe640a5SBin Meng case SIFIVE_U_PRCI_GEMGXLPLLCFG0: 419fe640a5SBin Meng return s->gemgxlpllcfg0; 429fe640a5SBin Meng case SIFIVE_U_PRCI_GEMGXLPLLCFG1: 439fe640a5SBin Meng return s->gemgxlpllcfg1; 449fe640a5SBin Meng case SIFIVE_U_PRCI_CORECLKSEL: 459fe640a5SBin Meng return s->coreclksel; 469fe640a5SBin Meng case SIFIVE_U_PRCI_DEVICESRESET: 479fe640a5SBin Meng return s->devicesreset; 489fe640a5SBin Meng case SIFIVE_U_PRCI_CLKMUXSTATUS: 499fe640a5SBin Meng return s->clkmuxstatus; 509fe640a5SBin Meng } 519fe640a5SBin Meng 529fe640a5SBin Meng qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n", 539fe640a5SBin Meng __func__, addr); 549fe640a5SBin Meng 559fe640a5SBin Meng return 0; 569fe640a5SBin Meng } 579fe640a5SBin Meng 589fe640a5SBin Meng static void sifive_u_prci_write(void *opaque, hwaddr addr, 599fe640a5SBin Meng uint64_t val64, unsigned int size) 609fe640a5SBin Meng { 619fe640a5SBin Meng SiFiveUPRCIState *s = opaque; 629fe640a5SBin Meng uint32_t val32 = (uint32_t)val64; 639fe640a5SBin Meng 649fe640a5SBin Meng switch (addr) { 659fe640a5SBin Meng case SIFIVE_U_PRCI_HFXOSCCFG: 669fe640a5SBin Meng s->hfxosccfg = val32; 679fe640a5SBin Meng /* OSC stays ready */ 689fe640a5SBin Meng s->hfxosccfg |= SIFIVE_U_PRCI_HFXOSCCFG_RDY; 699fe640a5SBin Meng break; 709fe640a5SBin Meng case SIFIVE_U_PRCI_COREPLLCFG0: 719fe640a5SBin Meng s->corepllcfg0 = val32; 729fe640a5SBin Meng /* internal feedback */ 739fe640a5SBin Meng s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; 749fe640a5SBin Meng /* PLL stays locked */ 759fe640a5SBin Meng s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; 769fe640a5SBin Meng break; 779fe640a5SBin Meng case SIFIVE_U_PRCI_DDRPLLCFG0: 789fe640a5SBin Meng s->ddrpllcfg0 = val32; 799fe640a5SBin Meng /* internal feedback */ 809fe640a5SBin Meng s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; 819fe640a5SBin Meng /* PLL stays locked */ 829fe640a5SBin Meng s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; 839fe640a5SBin Meng break; 849fe640a5SBin Meng case SIFIVE_U_PRCI_DDRPLLCFG1: 859fe640a5SBin Meng s->ddrpllcfg1 = val32; 869fe640a5SBin Meng break; 879fe640a5SBin Meng case SIFIVE_U_PRCI_GEMGXLPLLCFG0: 889fe640a5SBin Meng s->gemgxlpllcfg0 = val32; 899fe640a5SBin Meng /* internal feedback */ 909fe640a5SBin Meng s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; 919fe640a5SBin Meng /* PLL stays locked */ 929fe640a5SBin Meng s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; 939fe640a5SBin Meng break; 949fe640a5SBin Meng case SIFIVE_U_PRCI_GEMGXLPLLCFG1: 959fe640a5SBin Meng s->gemgxlpllcfg1 = val32; 969fe640a5SBin Meng break; 979fe640a5SBin Meng case SIFIVE_U_PRCI_CORECLKSEL: 989fe640a5SBin Meng s->coreclksel = val32; 999fe640a5SBin Meng break; 1009fe640a5SBin Meng case SIFIVE_U_PRCI_DEVICESRESET: 1019fe640a5SBin Meng s->devicesreset = val32; 1029fe640a5SBin Meng break; 1039fe640a5SBin Meng case SIFIVE_U_PRCI_CLKMUXSTATUS: 1049fe640a5SBin Meng s->clkmuxstatus = val32; 1059fe640a5SBin Meng break; 1069fe640a5SBin Meng default: 1079fe640a5SBin Meng qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx 1089fe640a5SBin Meng " v=0x%x\n", __func__, addr, val32); 1099fe640a5SBin Meng } 1109fe640a5SBin Meng } 1119fe640a5SBin Meng 1129fe640a5SBin Meng static const MemoryRegionOps sifive_u_prci_ops = { 1139fe640a5SBin Meng .read = sifive_u_prci_read, 1149fe640a5SBin Meng .write = sifive_u_prci_write, 1159fe640a5SBin Meng .endianness = DEVICE_NATIVE_ENDIAN, 1169fe640a5SBin Meng .valid = { 1179fe640a5SBin Meng .min_access_size = 4, 1189fe640a5SBin Meng .max_access_size = 4 1199fe640a5SBin Meng } 1209fe640a5SBin Meng }; 1219fe640a5SBin Meng 1229fe640a5SBin Meng static void sifive_u_prci_realize(DeviceState *dev, Error **errp) 1239fe640a5SBin Meng { 1249fe640a5SBin Meng SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev); 1259fe640a5SBin Meng 1269fe640a5SBin Meng memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_prci_ops, s, 1279fe640a5SBin Meng TYPE_SIFIVE_U_PRCI, SIFIVE_U_PRCI_REG_SIZE); 1289fe640a5SBin Meng sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); 1299fe640a5SBin Meng } 1309fe640a5SBin Meng 1319fe640a5SBin Meng static void sifive_u_prci_reset(DeviceState *dev) 1329fe640a5SBin Meng { 1339fe640a5SBin Meng SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev); 1349fe640a5SBin Meng 1359fe640a5SBin Meng /* Initialize register to power-on-reset values */ 1369fe640a5SBin Meng s->hfxosccfg = SIFIVE_U_PRCI_HFXOSCCFG_RDY | SIFIVE_U_PRCI_HFXOSCCFG_EN; 1379fe640a5SBin Meng s->corepllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | 1389fe640a5SBin Meng SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | 1399fe640a5SBin Meng SIFIVE_U_PRCI_PLLCFG0_LOCK; 1409fe640a5SBin Meng s->ddrpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | 1419fe640a5SBin Meng SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | 1429fe640a5SBin Meng SIFIVE_U_PRCI_PLLCFG0_LOCK; 1439fe640a5SBin Meng s->gemgxlpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | 1449fe640a5SBin Meng SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | 1459fe640a5SBin Meng SIFIVE_U_PRCI_PLLCFG0_LOCK; 1469fe640a5SBin Meng s->coreclksel = SIFIVE_U_PRCI_CORECLKSEL_HFCLK; 1479fe640a5SBin Meng } 1489fe640a5SBin Meng 1499fe640a5SBin Meng static void sifive_u_prci_class_init(ObjectClass *klass, void *data) 1509fe640a5SBin Meng { 1519fe640a5SBin Meng DeviceClass *dc = DEVICE_CLASS(klass); 1529fe640a5SBin Meng 1539fe640a5SBin Meng dc->realize = sifive_u_prci_realize; 154*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, sifive_u_prci_reset); 1559fe640a5SBin Meng } 1569fe640a5SBin Meng 1579fe640a5SBin Meng static const TypeInfo sifive_u_prci_info = { 1589fe640a5SBin Meng .name = TYPE_SIFIVE_U_PRCI, 1599fe640a5SBin Meng .parent = TYPE_SYS_BUS_DEVICE, 1609fe640a5SBin Meng .instance_size = sizeof(SiFiveUPRCIState), 1619fe640a5SBin Meng .class_init = sifive_u_prci_class_init, 1629fe640a5SBin Meng }; 1639fe640a5SBin Meng 1649fe640a5SBin Meng static void sifive_u_prci_register_types(void) 1659fe640a5SBin Meng { 1669fe640a5SBin Meng type_register_static(&sifive_u_prci_info); 1679fe640a5SBin Meng } 1689fe640a5SBin Meng 1699fe640a5SBin Meng type_init(sifive_u_prci_register_types) 170