1a8fb0a50SBin Meng /* 2a8fb0a50SBin Meng * Microchip PolarFire SoC MMUART emulation 3a8fb0a50SBin Meng * 4a8fb0a50SBin Meng * Copyright (c) 2020 Wind River Systems, Inc. 5a8fb0a50SBin Meng * 6a8fb0a50SBin Meng * Author: 7a8fb0a50SBin Meng * Bin Meng <bin.meng@windriver.com> 8a8fb0a50SBin Meng * 9a8fb0a50SBin Meng * This program is free software; you can redistribute it and/or 10a8fb0a50SBin Meng * modify it under the terms of the GNU General Public License as 11a8fb0a50SBin Meng * published by the Free Software Foundation; either version 2 or 12a8fb0a50SBin Meng * (at your option) version 3 of the License. 13a8fb0a50SBin Meng * 14a8fb0a50SBin Meng * This program is distributed in the hope that it will be useful, 15a8fb0a50SBin Meng * but WITHOUT ANY WARRANTY; without even the implied warranty of 16a8fb0a50SBin Meng * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17a8fb0a50SBin Meng * GNU General Public License for more details. 18a8fb0a50SBin Meng * 19a8fb0a50SBin Meng * You should have received a copy of the GNU General Public License along 20a8fb0a50SBin Meng * with this program; if not, see <http://www.gnu.org/licenses/>. 21a8fb0a50SBin Meng */ 22a8fb0a50SBin Meng 23a8fb0a50SBin Meng #include "qemu/osdep.h" 24a8fb0a50SBin Meng #include "qemu/log.h" 2531ca70b5SPhilippe Mathieu-Daudé #include "qapi/error.h" 2631ca70b5SPhilippe Mathieu-Daudé #include "migration/vmstate.h" 27a8fb0a50SBin Meng #include "hw/char/mchp_pfsoc_mmuart.h" 2831ca70b5SPhilippe Mathieu-Daudé #include "hw/qdev-properties.h" 29a8fb0a50SBin Meng 3024ce762dSPhilippe Mathieu-Daudé #define REGS_OFFSET 0x20 3124ce762dSPhilippe Mathieu-Daudé 32a8fb0a50SBin Meng static uint64_t mchp_pfsoc_mmuart_read(void *opaque, hwaddr addr, unsigned size) 33a8fb0a50SBin Meng { 34a8fb0a50SBin Meng MchpPfSoCMMUartState *s = opaque; 35a8fb0a50SBin Meng 36284a66a8SPhilippe Mathieu-Daudé addr >>= 2; 37284a66a8SPhilippe Mathieu-Daudé if (addr >= MCHP_PFSOC_MMUART_REG_COUNT) { 38a8fb0a50SBin Meng qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n", 39284a66a8SPhilippe Mathieu-Daudé __func__, addr << 2); 40a8fb0a50SBin Meng return 0; 41a8fb0a50SBin Meng } 42a8fb0a50SBin Meng 43284a66a8SPhilippe Mathieu-Daudé return s->reg[addr]; 44a8fb0a50SBin Meng } 45a8fb0a50SBin Meng 46a8fb0a50SBin Meng static void mchp_pfsoc_mmuart_write(void *opaque, hwaddr addr, 47a8fb0a50SBin Meng uint64_t value, unsigned size) 48a8fb0a50SBin Meng { 49a8fb0a50SBin Meng MchpPfSoCMMUartState *s = opaque; 50a8fb0a50SBin Meng uint32_t val32 = (uint32_t)value; 51a8fb0a50SBin Meng 52284a66a8SPhilippe Mathieu-Daudé addr >>= 2; 53284a66a8SPhilippe Mathieu-Daudé if (addr >= MCHP_PFSOC_MMUART_REG_COUNT) { 54a8fb0a50SBin Meng qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx 55284a66a8SPhilippe Mathieu-Daudé " v=0x%x\n", __func__, addr << 2, val32); 56a8fb0a50SBin Meng return; 57a8fb0a50SBin Meng } 58a8fb0a50SBin Meng 59284a66a8SPhilippe Mathieu-Daudé s->reg[addr] = val32; 60a8fb0a50SBin Meng } 61a8fb0a50SBin Meng 62a8fb0a50SBin Meng static const MemoryRegionOps mchp_pfsoc_mmuart_ops = { 63a8fb0a50SBin Meng .read = mchp_pfsoc_mmuart_read, 64a8fb0a50SBin Meng .write = mchp_pfsoc_mmuart_write, 65a8fb0a50SBin Meng .endianness = DEVICE_LITTLE_ENDIAN, 66a8fb0a50SBin Meng .impl = { 67a8fb0a50SBin Meng .min_access_size = 4, 68a8fb0a50SBin Meng .max_access_size = 4, 69a8fb0a50SBin Meng }, 70a8fb0a50SBin Meng }; 71a8fb0a50SBin Meng 7231ca70b5SPhilippe Mathieu-Daudé static void mchp_pfsoc_mmuart_reset(DeviceState *dev) 73a8fb0a50SBin Meng { 7431ca70b5SPhilippe Mathieu-Daudé MchpPfSoCMMUartState *s = MCHP_PFSOC_UART(dev); 75a8fb0a50SBin Meng 7631ca70b5SPhilippe Mathieu-Daudé memset(s->reg, 0, sizeof(s->reg)); 7731ca70b5SPhilippe Mathieu-Daudé device_cold_reset(DEVICE(&s->serial_mm)); 7831ca70b5SPhilippe Mathieu-Daudé } 79a8fb0a50SBin Meng 8031ca70b5SPhilippe Mathieu-Daudé static void mchp_pfsoc_mmuart_init(Object *obj) 8131ca70b5SPhilippe Mathieu-Daudé { 8231ca70b5SPhilippe Mathieu-Daudé MchpPfSoCMMUartState *s = MCHP_PFSOC_UART(obj); 8324ce762dSPhilippe Mathieu-Daudé 8431ca70b5SPhilippe Mathieu-Daudé object_initialize_child(obj, "serial-mm", &s->serial_mm, TYPE_SERIAL_MM); 8531ca70b5SPhilippe Mathieu-Daudé object_property_add_alias(obj, "chardev", OBJECT(&s->serial_mm), "chardev"); 8631ca70b5SPhilippe Mathieu-Daudé } 8731ca70b5SPhilippe Mathieu-Daudé 8831ca70b5SPhilippe Mathieu-Daudé static void mchp_pfsoc_mmuart_realize(DeviceState *dev, Error **errp) 8931ca70b5SPhilippe Mathieu-Daudé { 9031ca70b5SPhilippe Mathieu-Daudé MchpPfSoCMMUartState *s = MCHP_PFSOC_UART(dev); 9131ca70b5SPhilippe Mathieu-Daudé 9231ca70b5SPhilippe Mathieu-Daudé qdev_prop_set_uint8(DEVICE(&s->serial_mm), "regshift", 2); 9331ca70b5SPhilippe Mathieu-Daudé qdev_prop_set_uint32(DEVICE(&s->serial_mm), "baudbase", 399193); 9431ca70b5SPhilippe Mathieu-Daudé qdev_prop_set_uint8(DEVICE(&s->serial_mm), "endianness", 9531ca70b5SPhilippe Mathieu-Daudé DEVICE_LITTLE_ENDIAN); 9631ca70b5SPhilippe Mathieu-Daudé if (!sysbus_realize(SYS_BUS_DEVICE(&s->serial_mm), errp)) { 9731ca70b5SPhilippe Mathieu-Daudé return; 9831ca70b5SPhilippe Mathieu-Daudé } 9931ca70b5SPhilippe Mathieu-Daudé 10031ca70b5SPhilippe Mathieu-Daudé sysbus_pass_irq(SYS_BUS_DEVICE(dev), SYS_BUS_DEVICE(&s->serial_mm)); 10131ca70b5SPhilippe Mathieu-Daudé 10231ca70b5SPhilippe Mathieu-Daudé memory_region_init(&s->container, OBJECT(s), "mchp.pfsoc.mmuart", 0x1000); 10331ca70b5SPhilippe Mathieu-Daudé sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->container); 10431ca70b5SPhilippe Mathieu-Daudé 10531ca70b5SPhilippe Mathieu-Daudé memory_region_add_subregion(&s->container, 0, 10631ca70b5SPhilippe Mathieu-Daudé sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->serial_mm), 0)); 10731ca70b5SPhilippe Mathieu-Daudé 10831ca70b5SPhilippe Mathieu-Daudé memory_region_init_io(&s->iomem, OBJECT(s), &mchp_pfsoc_mmuart_ops, s, 10924ce762dSPhilippe Mathieu-Daudé "mchp.pfsoc.mmuart.regs", 0x1000 - REGS_OFFSET); 11024ce762dSPhilippe Mathieu-Daudé memory_region_add_subregion(&s->container, REGS_OFFSET, &s->iomem); 11131ca70b5SPhilippe Mathieu-Daudé } 112a8fb0a50SBin Meng 11331ca70b5SPhilippe Mathieu-Daudé static const VMStateDescription mchp_pfsoc_mmuart_vmstate = { 11431ca70b5SPhilippe Mathieu-Daudé .name = "mchp.pfsoc.uart", 11531ca70b5SPhilippe Mathieu-Daudé .version_id = 0, 11631ca70b5SPhilippe Mathieu-Daudé .minimum_version_id = 0, 1172f6cab05SRichard Henderson .fields = (const VMStateField[]) { 11831ca70b5SPhilippe Mathieu-Daudé VMSTATE_UINT32_ARRAY(reg, MchpPfSoCMMUartState, 11931ca70b5SPhilippe Mathieu-Daudé MCHP_PFSOC_MMUART_REG_COUNT), 12031ca70b5SPhilippe Mathieu-Daudé VMSTATE_END_OF_LIST() 12131ca70b5SPhilippe Mathieu-Daudé } 12231ca70b5SPhilippe Mathieu-Daudé }; 123a8fb0a50SBin Meng 12431ca70b5SPhilippe Mathieu-Daudé static void mchp_pfsoc_mmuart_class_init(ObjectClass *oc, void *data) 12531ca70b5SPhilippe Mathieu-Daudé { 12631ca70b5SPhilippe Mathieu-Daudé DeviceClass *dc = DEVICE_CLASS(oc); 127a8fb0a50SBin Meng 12831ca70b5SPhilippe Mathieu-Daudé dc->realize = mchp_pfsoc_mmuart_realize; 129*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, mchp_pfsoc_mmuart_reset); 13031ca70b5SPhilippe Mathieu-Daudé dc->vmsd = &mchp_pfsoc_mmuart_vmstate; 13131ca70b5SPhilippe Mathieu-Daudé set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 13231ca70b5SPhilippe Mathieu-Daudé } 133a8fb0a50SBin Meng 13431ca70b5SPhilippe Mathieu-Daudé static const TypeInfo mchp_pfsoc_mmuart_info = { 13531ca70b5SPhilippe Mathieu-Daudé .name = TYPE_MCHP_PFSOC_UART, 13631ca70b5SPhilippe Mathieu-Daudé .parent = TYPE_SYS_BUS_DEVICE, 13731ca70b5SPhilippe Mathieu-Daudé .instance_size = sizeof(MchpPfSoCMMUartState), 13831ca70b5SPhilippe Mathieu-Daudé .instance_init = mchp_pfsoc_mmuart_init, 13931ca70b5SPhilippe Mathieu-Daudé .class_init = mchp_pfsoc_mmuart_class_init, 14031ca70b5SPhilippe Mathieu-Daudé }; 14131ca70b5SPhilippe Mathieu-Daudé 14231ca70b5SPhilippe Mathieu-Daudé static void mchp_pfsoc_mmuart_register_types(void) 14331ca70b5SPhilippe Mathieu-Daudé { 14431ca70b5SPhilippe Mathieu-Daudé type_register_static(&mchp_pfsoc_mmuart_info); 14531ca70b5SPhilippe Mathieu-Daudé } 14631ca70b5SPhilippe Mathieu-Daudé 14731ca70b5SPhilippe Mathieu-Daudé type_init(mchp_pfsoc_mmuart_register_types) 14831ca70b5SPhilippe Mathieu-Daudé 14931ca70b5SPhilippe Mathieu-Daudé MchpPfSoCMMUartState *mchp_pfsoc_mmuart_create(MemoryRegion *sysmem, 15031ca70b5SPhilippe Mathieu-Daudé hwaddr base, 15131ca70b5SPhilippe Mathieu-Daudé qemu_irq irq, Chardev *chr) 15231ca70b5SPhilippe Mathieu-Daudé { 15331ca70b5SPhilippe Mathieu-Daudé DeviceState *dev = qdev_new(TYPE_MCHP_PFSOC_UART); 15431ca70b5SPhilippe Mathieu-Daudé SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 15531ca70b5SPhilippe Mathieu-Daudé 15631ca70b5SPhilippe Mathieu-Daudé qdev_prop_set_chr(dev, "chardev", chr); 15731ca70b5SPhilippe Mathieu-Daudé sysbus_realize(sbd, &error_fatal); 15831ca70b5SPhilippe Mathieu-Daudé 15931ca70b5SPhilippe Mathieu-Daudé memory_region_add_subregion(sysmem, base, sysbus_mmio_get_region(sbd, 0)); 16031ca70b5SPhilippe Mathieu-Daudé sysbus_connect_irq(sbd, 0, irq); 16131ca70b5SPhilippe Mathieu-Daudé 16231ca70b5SPhilippe Mathieu-Daudé return MCHP_PFSOC_UART(dev); 163a8fb0a50SBin Meng } 164