1 /* 2 * System Register block model of Microsemi SmartFusion2. 3 * 4 * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 * 11 * You should have received a copy of the GNU General Public License along 12 * with this program; if not, see <http://www.gnu.org/licenses/>. 13 */ 14 15 #include "qemu/osdep.h" 16 #include "qapi/error.h" 17 #include "qemu/log.h" 18 #include "qemu/module.h" 19 #include "hw/misc/msf2-sysreg.h" 20 #include "migration/vmstate.h" 21 #include "qemu/error-report.h" 22 #include "trace.h" 23 24 static inline int msf2_divbits(uint32_t div) 25 { 26 int r = ctz32(div); 27 28 return (div < 8) ? r : r + 1; 29 } 30 31 static void msf2_sysreg_reset(DeviceState *d) 32 { 33 MSF2SysregState *s = MSF2_SYSREG(d); 34 35 s->regs[MSSDDR_PLL_STATUS_LOW_CR] = 0x021A2358; 36 s->regs[MSSDDR_PLL_STATUS] = 0x3; 37 s->regs[MSSDDR_FACC1_CR] = msf2_divbits(s->apb0div) << 5 | 38 msf2_divbits(s->apb1div) << 2; 39 } 40 41 static uint64_t msf2_sysreg_read(void *opaque, hwaddr offset, 42 unsigned size) 43 { 44 MSF2SysregState *s = opaque; 45 uint32_t ret = 0; 46 47 offset >>= 2; 48 if (offset < ARRAY_SIZE(s->regs)) { 49 ret = s->regs[offset]; 50 trace_msf2_sysreg_read(offset << 2, ret); 51 } else { 52 qemu_log_mask(LOG_GUEST_ERROR, 53 "%s: Bad offset 0x%08" HWADDR_PRIx "\n", __func__, 54 offset << 2); 55 } 56 57 return ret; 58 } 59 60 static void msf2_sysreg_write(void *opaque, hwaddr offset, 61 uint64_t val, unsigned size) 62 { 63 MSF2SysregState *s = opaque; 64 uint32_t newval = val; 65 66 offset >>= 2; 67 68 switch (offset) { 69 case MSSDDR_PLL_STATUS: 70 trace_msf2_sysreg_write_pll_status(); 71 break; 72 73 case ESRAM_CR: 74 case DDR_CR: 75 case ENVM_REMAP_BASE_CR: 76 if (newval != s->regs[offset]) { 77 qemu_log_mask(LOG_UNIMP, 78 TYPE_MSF2_SYSREG": remapping not supported\n"); 79 } 80 break; 81 82 default: 83 if (offset < ARRAY_SIZE(s->regs)) { 84 trace_msf2_sysreg_write(offset << 2, newval, s->regs[offset]); 85 s->regs[offset] = newval; 86 } else { 87 qemu_log_mask(LOG_GUEST_ERROR, 88 "%s: Bad offset 0x%08" HWADDR_PRIx "\n", __func__, 89 offset << 2); 90 } 91 break; 92 } 93 } 94 95 static const MemoryRegionOps sysreg_ops = { 96 .read = msf2_sysreg_read, 97 .write = msf2_sysreg_write, 98 .endianness = DEVICE_NATIVE_ENDIAN, 99 }; 100 101 static void msf2_sysreg_init(Object *obj) 102 { 103 MSF2SysregState *s = MSF2_SYSREG(obj); 104 105 memory_region_init_io(&s->iomem, obj, &sysreg_ops, s, TYPE_MSF2_SYSREG, 106 MSF2_SYSREG_MMIO_SIZE); 107 sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); 108 } 109 110 static const VMStateDescription vmstate_msf2_sysreg = { 111 .name = TYPE_MSF2_SYSREG, 112 .version_id = 1, 113 .minimum_version_id = 1, 114 .fields = (VMStateField[]) { 115 VMSTATE_UINT32_ARRAY(regs, MSF2SysregState, MSF2_SYSREG_MMIO_SIZE / 4), 116 VMSTATE_END_OF_LIST() 117 } 118 }; 119 120 static Property msf2_sysreg_properties[] = { 121 /* default divisors in Libero GUI */ 122 DEFINE_PROP_UINT8("apb0divisor", MSF2SysregState, apb0div, 2), 123 DEFINE_PROP_UINT8("apb1divisor", MSF2SysregState, apb1div, 2), 124 DEFINE_PROP_END_OF_LIST(), 125 }; 126 127 static void msf2_sysreg_realize(DeviceState *dev, Error **errp) 128 { 129 MSF2SysregState *s = MSF2_SYSREG(dev); 130 131 if ((s->apb0div > 32 || !is_power_of_2(s->apb0div)) 132 || (s->apb1div > 32 || !is_power_of_2(s->apb1div))) { 133 error_setg(errp, "Invalid apb divisor value"); 134 error_append_hint(errp, "apb divisor must be a power of 2" 135 " and maximum value is 32\n"); 136 } 137 } 138 139 static void msf2_sysreg_class_init(ObjectClass *klass, void *data) 140 { 141 DeviceClass *dc = DEVICE_CLASS(klass); 142 143 dc->vmsd = &vmstate_msf2_sysreg; 144 dc->reset = msf2_sysreg_reset; 145 dc->props = msf2_sysreg_properties; 146 dc->realize = msf2_sysreg_realize; 147 } 148 149 static const TypeInfo msf2_sysreg_info = { 150 .name = TYPE_MSF2_SYSREG, 151 .parent = TYPE_SYS_BUS_DEVICE, 152 .class_init = msf2_sysreg_class_init, 153 .instance_size = sizeof(MSF2SysregState), 154 .instance_init = msf2_sysreg_init, 155 }; 156 157 static void msf2_sysreg_register_types(void) 158 { 159 type_register_static(&msf2_sysreg_info); 160 } 161 162 type_init(msf2_sysreg_register_types) 163