2 * System Register block model of Microsemi SmartFusion2.
4 * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>
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.
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/>.
15 #include "qemu/osdep.h"
16 #include "qapi/error.h"
18 #include "qemu/module.h"
19 #include "hw/misc/msf2-sysreg.h"
20 #include "hw/qdev-properties.h"
21 #include "migration/vmstate.h"
22 #include "qemu/error-report.h"
25 static inline int msf2_divbits(uint32_t div
)
29 return (div
< 8) ? r
: r
+ 1;
32 static void msf2_sysreg_reset(DeviceState
*d
)
34 MSF2SysregState
*s
= MSF2_SYSREG(d
);
36 s
->regs
[MSSDDR_PLL_STATUS_LOW_CR
] = 0x021A2358;
37 s
->regs
[MSSDDR_PLL_STATUS
] = 0x3;
38 s
->regs
[MSSDDR_FACC1_CR
] = msf2_divbits(s
->apb0div
) << 5 |
39 msf2_divbits(s
->apb1div
) << 2;
42 static uint64_t msf2_sysreg_read(void *opaque
, hwaddr offset
,
45 MSF2SysregState
*s
= opaque
;
49 if (offset
< ARRAY_SIZE(s
->regs
)) {
50 ret
= s
->regs
[offset
];
51 trace_msf2_sysreg_read(offset
<< 2, ret
);
53 qemu_log_mask(LOG_GUEST_ERROR
,
54 "%s: Bad offset 0x%08" HWADDR_PRIx
"\n", __func__
,
61 static void msf2_sysreg_write(void *opaque
, hwaddr offset
,
62 uint64_t val
, unsigned size
)
64 MSF2SysregState
*s
= opaque
;
65 uint32_t newval
= val
;
70 case MSSDDR_PLL_STATUS
:
71 trace_msf2_sysreg_write_pll_status();
76 case ENVM_REMAP_BASE_CR
:
77 if (newval
!= s
->regs
[offset
]) {
78 qemu_log_mask(LOG_UNIMP
,
79 TYPE_MSF2_SYSREG
": remapping not supported\n");
84 if (offset
< ARRAY_SIZE(s
->regs
)) {
85 trace_msf2_sysreg_write(offset
<< 2, newval
, s
->regs
[offset
]);
86 s
->regs
[offset
] = newval
;
88 qemu_log_mask(LOG_GUEST_ERROR
,
89 "%s: Bad offset 0x%08" HWADDR_PRIx
"\n", __func__
,
96 static const MemoryRegionOps sysreg_ops
= {
97 .read
= msf2_sysreg_read
,
98 .write
= msf2_sysreg_write
,
99 .endianness
= DEVICE_NATIVE_ENDIAN
,
102 static void msf2_sysreg_init(Object
*obj
)
104 MSF2SysregState
*s
= MSF2_SYSREG(obj
);
106 memory_region_init_io(&s
->iomem
, obj
, &sysreg_ops
, s
, TYPE_MSF2_SYSREG
,
107 MSF2_SYSREG_MMIO_SIZE
);
108 sysbus_init_mmio(SYS_BUS_DEVICE(obj
), &s
->iomem
);
111 static const VMStateDescription vmstate_msf2_sysreg
= {
112 .name
= TYPE_MSF2_SYSREG
,
114 .minimum_version_id
= 1,
115 .fields
= (const VMStateField
[]) {
116 VMSTATE_UINT32_ARRAY(regs
, MSF2SysregState
, MSF2_SYSREG_MMIO_SIZE
/ 4),
117 VMSTATE_END_OF_LIST()
121 static Property msf2_sysreg_properties
[] = {
122 /* default divisors in Libero GUI */
123 DEFINE_PROP_UINT8("apb0divisor", MSF2SysregState
, apb0div
, 2),
124 DEFINE_PROP_UINT8("apb1divisor", MSF2SysregState
, apb1div
, 2),
125 DEFINE_PROP_END_OF_LIST(),
128 static void msf2_sysreg_realize(DeviceState
*dev
, Error
**errp
)
130 MSF2SysregState
*s
= MSF2_SYSREG(dev
);
132 if ((s
->apb0div
> 32 || !is_power_of_2(s
->apb0div
))
133 || (s
->apb1div
> 32 || !is_power_of_2(s
->apb1div
))) {
134 error_setg(errp
, "Invalid apb divisor value");
135 error_append_hint(errp
, "apb divisor must be a power of 2"
136 " and maximum value is 32\n");
140 static void msf2_sysreg_class_init(ObjectClass
*klass
, void *data
)
142 DeviceClass
*dc
= DEVICE_CLASS(klass
);
144 dc
->vmsd
= &vmstate_msf2_sysreg
;
145 device_class_set_legacy_reset(dc
, msf2_sysreg_reset
);
146 device_class_set_props(dc
, msf2_sysreg_properties
);
147 dc
->realize
= msf2_sysreg_realize
;
150 static const TypeInfo msf2_sysreg_info
= {
151 .name
= TYPE_MSF2_SYSREG
,
152 .parent
= TYPE_SYS_BUS_DEVICE
,
153 .class_init
= msf2_sysreg_class_init
,
154 .instance_size
= sizeof(MSF2SysregState
),
155 .instance_init
= msf2_sysreg_init
,
158 static void msf2_sysreg_register_types(void)
160 type_register_static(&msf2_sysreg_info
);
163 type_init(msf2_sysreg_register_types
)