xref: /qemu/hw/misc/sifive_u_prci.c (revision 12d1a768bdfea6e27a3a829228840d72507613a1)
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