qemu/hw/riscv/sifive_u_prci.c
<<
>>
Prefs
   1/*
   2 * QEMU SiFive U PRCI (Power, Reset, Clock, Interrupt)
   3 *
   4 * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com>
   5 *
   6 * Simple model of the PRCI to emulate register reads made by the SDK BSP
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms and conditions of the GNU General Public License,
  10 * version 2 or later, as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope it will be useful, but WITHOUT
  13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15 * more details.
  16 *
  17 * You should have received a copy of the GNU General Public License along with
  18 * this program.  If not, see <http://www.gnu.org/licenses/>.
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "hw/sysbus.h"
  23#include "qemu/log.h"
  24#include "qemu/module.h"
  25#include "hw/riscv/sifive_u_prci.h"
  26
  27static uint64_t sifive_u_prci_read(void *opaque, hwaddr addr, unsigned int size)
  28{
  29    SiFiveUPRCIState *s = opaque;
  30
  31    switch (addr) {
  32    case SIFIVE_U_PRCI_HFXOSCCFG:
  33        return s->hfxosccfg;
  34    case SIFIVE_U_PRCI_COREPLLCFG0:
  35        return s->corepllcfg0;
  36    case SIFIVE_U_PRCI_DDRPLLCFG0:
  37        return s->ddrpllcfg0;
  38    case SIFIVE_U_PRCI_DDRPLLCFG1:
  39        return s->ddrpllcfg1;
  40    case SIFIVE_U_PRCI_GEMGXLPLLCFG0:
  41        return s->gemgxlpllcfg0;
  42    case SIFIVE_U_PRCI_GEMGXLPLLCFG1:
  43        return s->gemgxlpllcfg1;
  44    case SIFIVE_U_PRCI_CORECLKSEL:
  45        return s->coreclksel;
  46    case SIFIVE_U_PRCI_DEVICESRESET:
  47        return s->devicesreset;
  48    case SIFIVE_U_PRCI_CLKMUXSTATUS:
  49        return s->clkmuxstatus;
  50    }
  51
  52    qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n",
  53                  __func__, addr);
  54
  55    return 0;
  56}
  57
  58static void sifive_u_prci_write(void *opaque, hwaddr addr,
  59                                uint64_t val64, unsigned int size)
  60{
  61    SiFiveUPRCIState *s = opaque;
  62    uint32_t val32 = (uint32_t)val64;
  63
  64    switch (addr) {
  65    case SIFIVE_U_PRCI_HFXOSCCFG:
  66        s->hfxosccfg = val32;
  67        /* OSC stays ready */
  68        s->hfxosccfg |= SIFIVE_U_PRCI_HFXOSCCFG_RDY;
  69        break;
  70    case SIFIVE_U_PRCI_COREPLLCFG0:
  71        s->corepllcfg0 = val32;
  72        /* internal feedback */
  73        s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE;
  74        /* PLL stays locked */
  75        s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK;
  76        break;
  77    case SIFIVE_U_PRCI_DDRPLLCFG0:
  78        s->ddrpllcfg0 = val32;
  79        /* internal feedback */
  80        s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE;
  81        /* PLL stays locked */
  82        s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK;
  83        break;
  84    case SIFIVE_U_PRCI_DDRPLLCFG1:
  85        s->ddrpllcfg1 = val32;
  86        break;
  87    case SIFIVE_U_PRCI_GEMGXLPLLCFG0:
  88        s->gemgxlpllcfg0 = val32;
  89        /* internal feedback */
  90        s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE;
  91        /* PLL stays locked */
  92        s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK;
  93        break;
  94    case SIFIVE_U_PRCI_GEMGXLPLLCFG1:
  95        s->gemgxlpllcfg1 = val32;
  96        break;
  97    case SIFIVE_U_PRCI_CORECLKSEL:
  98        s->coreclksel = val32;
  99        break;
 100    case SIFIVE_U_PRCI_DEVICESRESET:
 101        s->devicesreset = val32;
 102        break;
 103    case SIFIVE_U_PRCI_CLKMUXSTATUS:
 104        s->clkmuxstatus = val32;
 105        break;
 106    default:
 107        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx
 108                      " v=0x%x\n", __func__, addr, val32);
 109    }
 110}
 111
 112static const MemoryRegionOps sifive_u_prci_ops = {
 113    .read = sifive_u_prci_read,
 114    .write = sifive_u_prci_write,
 115    .endianness = DEVICE_NATIVE_ENDIAN,
 116    .valid = {
 117        .min_access_size = 4,
 118        .max_access_size = 4
 119    }
 120};
 121
 122static void sifive_u_prci_realize(DeviceState *dev, Error **errp)
 123{
 124    SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev);
 125
 126    memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_prci_ops, s,
 127                          TYPE_SIFIVE_U_PRCI, SIFIVE_U_PRCI_REG_SIZE);
 128    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
 129}
 130
 131static void sifive_u_prci_reset(DeviceState *dev)
 132{
 133    SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev);
 134
 135    /* Initialize register to power-on-reset values */
 136    s->hfxosccfg = SIFIVE_U_PRCI_HFXOSCCFG_RDY | SIFIVE_U_PRCI_HFXOSCCFG_EN;
 137    s->corepllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF |
 138                     SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE |
 139                     SIFIVE_U_PRCI_PLLCFG0_LOCK;
 140    s->ddrpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF |
 141                    SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE |
 142                    SIFIVE_U_PRCI_PLLCFG0_LOCK;
 143    s->gemgxlpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF |
 144                       SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE |
 145                       SIFIVE_U_PRCI_PLLCFG0_LOCK;
 146    s->coreclksel = SIFIVE_U_PRCI_CORECLKSEL_HFCLK;
 147}
 148
 149static void sifive_u_prci_class_init(ObjectClass *klass, void *data)
 150{
 151    DeviceClass *dc = DEVICE_CLASS(klass);
 152
 153    dc->realize = sifive_u_prci_realize;
 154    dc->reset = sifive_u_prci_reset;
 155}
 156
 157static const TypeInfo sifive_u_prci_info = {
 158    .name          = TYPE_SIFIVE_U_PRCI,
 159    .parent        = TYPE_SYS_BUS_DEVICE,
 160    .instance_size = sizeof(SiFiveUPRCIState),
 161    .class_init    = sifive_u_prci_class_init,
 162};
 163
 164static void sifive_u_prci_register_types(void)
 165{
 166    type_register_static(&sifive_u_prci_info);
 167}
 168
 169type_init(sifive_u_prci_register_types)
 170