qemu/hw/char/mchp_pfsoc_mmuart.c
<<
>>
Prefs
   1/*
   2 * Microchip PolarFire SoC MMUART emulation
   3 *
   4 * Copyright (c) 2020 Wind River Systems, Inc.
   5 *
   6 * Author:
   7 *   Bin Meng <bin.meng@windriver.com>
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License as
  11 * published by the Free Software Foundation; either version 2 or
  12 * (at your option) version 3 of the License.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License along
  20 * with this program; if not, see <http://www.gnu.org/licenses/>.
  21 */
  22
  23#include "qemu/osdep.h"
  24#include "qemu/log.h"
  25#include "qapi/error.h"
  26#include "migration/vmstate.h"
  27#include "hw/char/mchp_pfsoc_mmuart.h"
  28#include "hw/qdev-properties.h"
  29
  30#define REGS_OFFSET 0x20
  31
  32static uint64_t mchp_pfsoc_mmuart_read(void *opaque, hwaddr addr, unsigned size)
  33{
  34    MchpPfSoCMMUartState *s = opaque;
  35
  36    addr >>= 2;
  37    if (addr >= MCHP_PFSOC_MMUART_REG_COUNT) {
  38        qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n",
  39                      __func__, addr << 2);
  40        return 0;
  41    }
  42
  43    return s->reg[addr];
  44}
  45
  46static void mchp_pfsoc_mmuart_write(void *opaque, hwaddr addr,
  47                                    uint64_t value, unsigned size)
  48{
  49    MchpPfSoCMMUartState *s = opaque;
  50    uint32_t val32 = (uint32_t)value;
  51
  52    addr >>= 2;
  53    if (addr >= MCHP_PFSOC_MMUART_REG_COUNT) {
  54        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx
  55                      " v=0x%x\n", __func__, addr << 2, val32);
  56        return;
  57    }
  58
  59    s->reg[addr] = val32;
  60}
  61
  62static const MemoryRegionOps mchp_pfsoc_mmuart_ops = {
  63    .read = mchp_pfsoc_mmuart_read,
  64    .write = mchp_pfsoc_mmuart_write,
  65    .endianness = DEVICE_LITTLE_ENDIAN,
  66    .impl = {
  67        .min_access_size = 4,
  68        .max_access_size = 4,
  69    },
  70};
  71
  72static void mchp_pfsoc_mmuart_reset(DeviceState *dev)
  73{
  74    MchpPfSoCMMUartState *s = MCHP_PFSOC_UART(dev);
  75
  76    memset(s->reg, 0, sizeof(s->reg));
  77    device_cold_reset(DEVICE(&s->serial_mm));
  78}
  79
  80static void mchp_pfsoc_mmuart_init(Object *obj)
  81{
  82    MchpPfSoCMMUartState *s = MCHP_PFSOC_UART(obj);
  83
  84    object_initialize_child(obj, "serial-mm", &s->serial_mm, TYPE_SERIAL_MM);
  85    object_property_add_alias(obj, "chardev", OBJECT(&s->serial_mm), "chardev");
  86}
  87
  88static void mchp_pfsoc_mmuart_realize(DeviceState *dev, Error **errp)
  89{
  90    MchpPfSoCMMUartState *s = MCHP_PFSOC_UART(dev);
  91
  92    qdev_prop_set_uint8(DEVICE(&s->serial_mm), "regshift", 2);
  93    qdev_prop_set_uint32(DEVICE(&s->serial_mm), "baudbase", 399193);
  94    qdev_prop_set_uint8(DEVICE(&s->serial_mm), "endianness",
  95                        DEVICE_LITTLE_ENDIAN);
  96    if (!sysbus_realize(SYS_BUS_DEVICE(&s->serial_mm), errp)) {
  97        return;
  98    }
  99
 100    sysbus_pass_irq(SYS_BUS_DEVICE(dev), SYS_BUS_DEVICE(&s->serial_mm));
 101
 102    memory_region_init(&s->container, OBJECT(s), "mchp.pfsoc.mmuart", 0x1000);
 103    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->container);
 104
 105    memory_region_add_subregion(&s->container, 0,
 106                    sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->serial_mm), 0));
 107
 108    memory_region_init_io(&s->iomem, OBJECT(s), &mchp_pfsoc_mmuart_ops, s,
 109                          "mchp.pfsoc.mmuart.regs", 0x1000 - REGS_OFFSET);
 110    memory_region_add_subregion(&s->container, REGS_OFFSET, &s->iomem);
 111}
 112
 113static const VMStateDescription mchp_pfsoc_mmuart_vmstate = {
 114    .name = "mchp.pfsoc.uart",
 115    .version_id = 0,
 116    .minimum_version_id = 0,
 117    .fields = (VMStateField[]) {
 118        VMSTATE_UINT32_ARRAY(reg, MchpPfSoCMMUartState,
 119                             MCHP_PFSOC_MMUART_REG_COUNT),
 120        VMSTATE_END_OF_LIST()
 121    }
 122};
 123
 124static void mchp_pfsoc_mmuart_class_init(ObjectClass *oc, void *data)
 125{
 126    DeviceClass *dc = DEVICE_CLASS(oc);
 127
 128    dc->realize = mchp_pfsoc_mmuart_realize;
 129    dc->reset = mchp_pfsoc_mmuart_reset;
 130    dc->vmsd = &mchp_pfsoc_mmuart_vmstate;
 131    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 132}
 133
 134static const TypeInfo mchp_pfsoc_mmuart_info = {
 135    .name          = TYPE_MCHP_PFSOC_UART,
 136    .parent        = TYPE_SYS_BUS_DEVICE,
 137    .instance_size = sizeof(MchpPfSoCMMUartState),
 138    .instance_init = mchp_pfsoc_mmuart_init,
 139    .class_init    = mchp_pfsoc_mmuart_class_init,
 140};
 141
 142static void mchp_pfsoc_mmuart_register_types(void)
 143{
 144    type_register_static(&mchp_pfsoc_mmuart_info);
 145}
 146
 147type_init(mchp_pfsoc_mmuart_register_types)
 148
 149MchpPfSoCMMUartState *mchp_pfsoc_mmuart_create(MemoryRegion *sysmem,
 150                                               hwaddr base,
 151                                               qemu_irq irq, Chardev *chr)
 152{
 153    DeviceState *dev = qdev_new(TYPE_MCHP_PFSOC_UART);
 154    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 155
 156    qdev_prop_set_chr(dev, "chardev", chr);
 157    sysbus_realize(sbd, &error_fatal);
 158
 159    memory_region_add_subregion(sysmem, base, sysbus_mmio_get_region(sbd, 0));
 160    sysbus_connect_irq(sbd, 0, irq);
 161
 162    return MCHP_PFSOC_UART(dev);
 163}
 164