10d952994SBin Meng /* 20d952994SBin Meng * QEMU SiFive U PRCI (Power, Reset, Clock, Interrupt) 30d952994SBin Meng * 40d952994SBin Meng * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com> 50d952994SBin Meng * 60d952994SBin Meng * Simple model of the PRCI to emulate register reads made by the SDK BSP 70d952994SBin Meng * 80d952994SBin Meng * This program is free software; you can redistribute it and/or modify it 90d952994SBin Meng * under the terms and conditions of the GNU General Public License, 100d952994SBin Meng * version 2 or later, as published by the Free Software Foundation. 110d952994SBin Meng * 120d952994SBin Meng * This program is distributed in the hope it will be useful, but WITHOUT 130d952994SBin Meng * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 140d952994SBin Meng * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 150d952994SBin Meng * more details. 160d952994SBin Meng * 170d952994SBin Meng * You should have received a copy of the GNU General Public License along with 180d952994SBin Meng * this program. If not, see <http://www.gnu.org/licenses/>. 190d952994SBin Meng */ 200d952994SBin Meng 210d952994SBin Meng #include "qemu/osdep.h" 220d952994SBin Meng #include "hw/sysbus.h" 230d952994SBin Meng #include "qemu/log.h" 240d952994SBin Meng #include "qemu/module.h" 259fe640a5SBin Meng #include "hw/misc/sifive_u_prci.h" 260d952994SBin Meng 270d952994SBin Meng static uint64_t sifive_u_prci_read(void *opaque, hwaddr addr, unsigned int size) 280d952994SBin Meng { 290d952994SBin Meng SiFiveUPRCIState *s = opaque; 300d952994SBin Meng 310d952994SBin Meng switch (addr) { 320d952994SBin Meng case SIFIVE_U_PRCI_HFXOSCCFG: 330d952994SBin Meng return s->hfxosccfg; 340d952994SBin Meng case SIFIVE_U_PRCI_COREPLLCFG0: 350d952994SBin Meng return s->corepllcfg0; 360d952994SBin Meng case SIFIVE_U_PRCI_DDRPLLCFG0: 370d952994SBin Meng return s->ddrpllcfg0; 380d952994SBin Meng case SIFIVE_U_PRCI_DDRPLLCFG1: 390d952994SBin Meng return s->ddrpllcfg1; 400d952994SBin Meng case SIFIVE_U_PRCI_GEMGXLPLLCFG0: 410d952994SBin Meng return s->gemgxlpllcfg0; 420d952994SBin Meng case SIFIVE_U_PRCI_GEMGXLPLLCFG1: 430d952994SBin Meng return s->gemgxlpllcfg1; 440d952994SBin Meng case SIFIVE_U_PRCI_CORECLKSEL: 450d952994SBin Meng return s->coreclksel; 460d952994SBin Meng case SIFIVE_U_PRCI_DEVICESRESET: 470d952994SBin Meng return s->devicesreset; 480d952994SBin Meng case SIFIVE_U_PRCI_CLKMUXSTATUS: 490d952994SBin Meng return s->clkmuxstatus; 500d952994SBin Meng } 510d952994SBin Meng 520d952994SBin Meng qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n", 530d952994SBin Meng __func__, addr); 540d952994SBin Meng 550d952994SBin Meng return 0; 560d952994SBin Meng } 570d952994SBin Meng 580d952994SBin Meng static void sifive_u_prci_write(void *opaque, hwaddr addr, 590d952994SBin Meng uint64_t val64, unsigned int size) 600d952994SBin Meng { 610d952994SBin Meng SiFiveUPRCIState *s = opaque; 620d952994SBin Meng uint32_t val32 = (uint32_t)val64; 630d952994SBin Meng 640d952994SBin Meng switch (addr) { 650d952994SBin Meng case SIFIVE_U_PRCI_HFXOSCCFG: 660d952994SBin Meng s->hfxosccfg = val32; 670d952994SBin Meng /* OSC stays ready */ 680d952994SBin Meng s->hfxosccfg |= SIFIVE_U_PRCI_HFXOSCCFG_RDY; 690d952994SBin Meng break; 700d952994SBin Meng case SIFIVE_U_PRCI_COREPLLCFG0: 710d952994SBin Meng s->corepllcfg0 = val32; 720d952994SBin Meng /* internal feedback */ 730d952994SBin Meng s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; 740d952994SBin Meng /* PLL stays locked */ 750d952994SBin Meng s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; 760d952994SBin Meng break; 770d952994SBin Meng case SIFIVE_U_PRCI_DDRPLLCFG0: 780d952994SBin Meng s->ddrpllcfg0 = val32; 790d952994SBin Meng /* internal feedback */ 800d952994SBin Meng s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; 810d952994SBin Meng /* PLL stays locked */ 820d952994SBin Meng s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; 830d952994SBin Meng break; 840d952994SBin Meng case SIFIVE_U_PRCI_DDRPLLCFG1: 850d952994SBin Meng s->ddrpllcfg1 = val32; 860d952994SBin Meng break; 870d952994SBin Meng case SIFIVE_U_PRCI_GEMGXLPLLCFG0: 880d952994SBin Meng s->gemgxlpllcfg0 = val32; 890d952994SBin Meng /* internal feedback */ 900d952994SBin Meng s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; 910d952994SBin Meng /* PLL stays locked */ 920d952994SBin Meng s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; 930d952994SBin Meng break; 940d952994SBin Meng case SIFIVE_U_PRCI_GEMGXLPLLCFG1: 950d952994SBin Meng s->gemgxlpllcfg1 = val32; 960d952994SBin Meng break; 970d952994SBin Meng case SIFIVE_U_PRCI_CORECLKSEL: 980d952994SBin Meng s->coreclksel = val32; 990d952994SBin Meng break; 1000d952994SBin Meng case SIFIVE_U_PRCI_DEVICESRESET: 1010d952994SBin Meng s->devicesreset = val32; 1020d952994SBin Meng break; 1030d952994SBin Meng case SIFIVE_U_PRCI_CLKMUXSTATUS: 1040d952994SBin Meng s->clkmuxstatus = val32; 1050d952994SBin Meng break; 1060d952994SBin Meng default: 1070d952994SBin Meng qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx 1080d952994SBin Meng " v=0x%x\n", __func__, addr, val32); 1090d952994SBin Meng } 1100d952994SBin Meng } 1110d952994SBin Meng 1120d952994SBin Meng static const MemoryRegionOps sifive_u_prci_ops = { 1130d952994SBin Meng .read = sifive_u_prci_read, 1140d952994SBin Meng .write = sifive_u_prci_write, 1150d952994SBin Meng .endianness = DEVICE_NATIVE_ENDIAN, 1160d952994SBin Meng .valid = { 1170d952994SBin Meng .min_access_size = 4, 1180d952994SBin Meng .max_access_size = 4 1190d952994SBin Meng } 1200d952994SBin Meng }; 1210d952994SBin Meng 1220d952994SBin Meng static void sifive_u_prci_realize(DeviceState *dev, Error **errp) 1230d952994SBin Meng { 1240d952994SBin Meng SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev); 1250d952994SBin Meng 1260d952994SBin Meng memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_prci_ops, s, 1270d952994SBin Meng TYPE_SIFIVE_U_PRCI, SIFIVE_U_PRCI_REG_SIZE); 1280d952994SBin Meng sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); 1290d952994SBin Meng } 1300d952994SBin Meng 1310d952994SBin Meng static void sifive_u_prci_reset(DeviceState *dev) 1320d952994SBin Meng { 1330d952994SBin Meng SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev); 1340d952994SBin Meng 1350d952994SBin Meng /* Initialize register to power-on-reset values */ 1360d952994SBin Meng s->hfxosccfg = SIFIVE_U_PRCI_HFXOSCCFG_RDY | SIFIVE_U_PRCI_HFXOSCCFG_EN; 1370d952994SBin Meng s->corepllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | 1380d952994SBin Meng SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | 1390d952994SBin Meng SIFIVE_U_PRCI_PLLCFG0_LOCK; 1400d952994SBin Meng s->ddrpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | 1410d952994SBin Meng SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | 1420d952994SBin Meng SIFIVE_U_PRCI_PLLCFG0_LOCK; 1430d952994SBin Meng s->gemgxlpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | 1440d952994SBin Meng SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | 1450d952994SBin Meng SIFIVE_U_PRCI_PLLCFG0_LOCK; 1460d952994SBin Meng s->coreclksel = SIFIVE_U_PRCI_CORECLKSEL_HFCLK; 1470d952994SBin Meng } 1480d952994SBin Meng 1490d952994SBin Meng static void sifive_u_prci_class_init(ObjectClass *klass, void *data) 1500d952994SBin Meng { 1510d952994SBin Meng DeviceClass *dc = DEVICE_CLASS(klass); 1520d952994SBin Meng 1530d952994SBin Meng dc->realize = sifive_u_prci_realize; 154*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, sifive_u_prci_reset); 1550d952994SBin Meng } 1560d952994SBin Meng 1570d952994SBin Meng static const TypeInfo sifive_u_prci_info = { 1580d952994SBin Meng .name = TYPE_SIFIVE_U_PRCI, 1590d952994SBin Meng .parent = TYPE_SYS_BUS_DEVICE, 1600d952994SBin Meng .instance_size = sizeof(SiFiveUPRCIState), 1610d952994SBin Meng .class_init = sifive_u_prci_class_init, 1620d952994SBin Meng }; 1630d952994SBin Meng 1640d952994SBin Meng static void sifive_u_prci_register_types(void) 1650d952994SBin Meng { 1660d952994SBin Meng type_register_static(&sifive_u_prci_info); 1670d952994SBin Meng } 1680d952994SBin Meng 1690d952994SBin Meng type_init(sifive_u_prci_register_types) 170