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
sifive_u_prci_read(void * opaque,hwaddr addr,unsigned int size)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
sifive_u_prci_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)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
sifive_u_prci_realize(DeviceState * dev,Error ** errp)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
sifive_u_prci_reset(DeviceState * dev)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
sifive_u_prci_class_init(ObjectClass * klass,const void * data)149*12d1a768SPhilippe Mathieu-Daudé static void sifive_u_prci_class_init(ObjectClass *klass, const void *data)
1500d952994SBin Meng {
1510d952994SBin Meng DeviceClass *dc = DEVICE_CLASS(klass);
1520d952994SBin Meng
1530d952994SBin Meng dc->realize = sifive_u_prci_realize;
154e3d08143SPeter 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
sifive_u_prci_register_types(void)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