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 "hw/misc/msf2-sysreg.h"
19 #include "qemu/error-report.h"
22 static inline int msf2_divbits(uint32_t div
)
26 return (div
< 8) ? r
: r
+ 1;
29 static void msf2_sysreg_reset(DeviceState
*d
)
31 MSF2SysregState
*s
= MSF2_SYSREG(d
);
33 s
->regs
[MSSDDR_PLL_STATUS_LOW_CR
] = 0x021A2358;
34 s
->regs
[MSSDDR_PLL_STATUS
] = 0x3;
35 s
->regs
[MSSDDR_FACC1_CR
] = msf2_divbits(s
->apb0div
) << 5 |
36 msf2_divbits(s
->apb1div
) << 2;
39 static uint64_t msf2_sysreg_read(void *opaque
, hwaddr offset
,
42 MSF2SysregState
*s
= opaque
;
46 if (offset
< ARRAY_SIZE(s
->regs
)) {
47 ret
= s
->regs
[offset
];
48 trace_msf2_sysreg_read(offset
<< 2, ret
);
50 qemu_log_mask(LOG_GUEST_ERROR
,
51 "%s: Bad offset 0x%08" HWADDR_PRIx
"\n", __func__
,
58 static void msf2_sysreg_write(void *opaque
, hwaddr offset
,
59 uint64_t val
, unsigned size
)
61 MSF2SysregState
*s
= opaque
;
62 uint32_t newval
= val
;
67 case MSSDDR_PLL_STATUS
:
68 trace_msf2_sysreg_write_pll_status();
73 case ENVM_REMAP_BASE_CR
:
74 if (newval
!= s
->regs
[offset
]) {
75 qemu_log_mask(LOG_UNIMP
,
76 TYPE_MSF2_SYSREG
": remapping not supported\n");
81 if (offset
< ARRAY_SIZE(s
->regs
)) {
82 trace_msf2_sysreg_write(offset
<< 2, newval
, s
->regs
[offset
]);
83 s
->regs
[offset
] = newval
;
85 qemu_log_mask(LOG_GUEST_ERROR
,
86 "%s: Bad offset 0x%08" HWADDR_PRIx
"\n", __func__
,
93 static const MemoryRegionOps sysreg_ops
= {
94 .read
= msf2_sysreg_read
,
95 .write
= msf2_sysreg_write
,
96 .endianness
= DEVICE_NATIVE_ENDIAN
,
99 static void msf2_sysreg_init(Object
*obj
)
101 MSF2SysregState
*s
= MSF2_SYSREG(obj
);
103 memory_region_init_io(&s
->iomem
, obj
, &sysreg_ops
, s
, TYPE_MSF2_SYSREG
,
104 MSF2_SYSREG_MMIO_SIZE
);
105 sysbus_init_mmio(SYS_BUS_DEVICE(obj
), &s
->iomem
);
108 static const VMStateDescription vmstate_msf2_sysreg
= {
109 .name
= TYPE_MSF2_SYSREG
,
111 .minimum_version_id
= 1,
112 .fields
= (VMStateField
[]) {
113 VMSTATE_UINT32_ARRAY(regs
, MSF2SysregState
, MSF2_SYSREG_MMIO_SIZE
/ 4),
114 VMSTATE_END_OF_LIST()
118 static Property msf2_sysreg_properties
[] = {
119 /* default divisors in Libero GUI */
120 DEFINE_PROP_UINT8("apb0divisor", MSF2SysregState
, apb0div
, 2),
121 DEFINE_PROP_UINT8("apb1divisor", MSF2SysregState
, apb1div
, 2),
122 DEFINE_PROP_END_OF_LIST(),
125 static void msf2_sysreg_realize(DeviceState
*dev
, Error
**errp
)
127 MSF2SysregState
*s
= MSF2_SYSREG(dev
);
129 if ((s
->apb0div
> 32 || !is_power_of_2(s
->apb0div
))
130 || (s
->apb1div
> 32 || !is_power_of_2(s
->apb1div
))) {
131 error_setg(errp
, "Invalid apb divisor value");
132 error_append_hint(errp
, "apb divisor must be a power of 2"
133 " and maximum value is 32\n");
137 static void msf2_sysreg_class_init(ObjectClass
*klass
, void *data
)
139 DeviceClass
*dc
= DEVICE_CLASS(klass
);
141 dc
->vmsd
= &vmstate_msf2_sysreg
;
142 dc
->reset
= msf2_sysreg_reset
;
143 dc
->props
= msf2_sysreg_properties
;
144 dc
->realize
= msf2_sysreg_realize
;
147 static const TypeInfo msf2_sysreg_info
= {
148 .name
= TYPE_MSF2_SYSREG
,
149 .parent
= TYPE_SYS_BUS_DEVICE
,
150 .class_init
= msf2_sysreg_class_init
,
151 .instance_size
= sizeof(MSF2SysregState
),
152 .instance_init
= msf2_sysreg_init
,
155 static void msf2_sysreg_register_types(void)
157 type_register_static(&msf2_sysreg_info
);
160 type_init(msf2_sysreg_register_types
)