xref: /openbmc/qemu/hw/misc/sifive_u_prci.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
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 
sifive_u_prci_read(void * opaque,hwaddr addr,unsigned int size)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 
sifive_u_prci_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)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 
sifive_u_prci_realize(DeviceState * dev,Error ** errp)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 
sifive_u_prci_reset(DeviceState * dev)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 
sifive_u_prci_class_init(ObjectClass * klass,void * data)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 
sifive_u_prci_register_types(void)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