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 "qemu/error-report.h"
23 static inline int msf2_divbits(uint32_t div
)
27 return (div
< 8) ? r
: r
+ 1;
30 static void msf2_sysreg_reset(DeviceState
*d
)
32 MSF2SysregState
*s
= MSF2_SYSREG(d
);
34 s
->regs
[MSSDDR_PLL_STATUS_LOW_CR
] = 0x021A2358;
35 s
->regs
[MSSDDR_PLL_STATUS
] = 0x3;
36 s
->regs
[MSSDDR_FACC1_CR
] = msf2_divbits(s
->apb0div
) << 5 |
37 msf2_divbits(s
->apb1div
) << 2;
40 static uint64_t msf2_sysreg_read(void *opaque
, hwaddr offset
,
43 MSF2SysregState
*s
= opaque
;
47 if (offset
< ARRAY_SIZE(s
->regs
)) {
48 ret
= s
->regs
[offset
];
49 trace_msf2_sysreg_read(offset
<< 2, ret
);
51 qemu_log_mask(LOG_GUEST_ERROR
,
52 "%s: Bad offset 0x%08" HWADDR_PRIx
"\n", __func__
,
59 static void msf2_sysreg_write(void *opaque
, hwaddr offset
,
60 uint64_t val
, unsigned size
)
62 MSF2SysregState
*s
= opaque
;
63 uint32_t newval
= val
;
68 case MSSDDR_PLL_STATUS
:
69 trace_msf2_sysreg_write_pll_status();
74 case ENVM_REMAP_BASE_CR
:
75 if (newval
!= s
->regs
[offset
]) {
76 qemu_log_mask(LOG_UNIMP
,
77 TYPE_MSF2_SYSREG
": remapping not supported\n");
82 if (offset
< ARRAY_SIZE(s
->regs
)) {
83 trace_msf2_sysreg_write(offset
<< 2, newval
, s
->regs
[offset
]);
84 s
->regs
[offset
] = newval
;
86 qemu_log_mask(LOG_GUEST_ERROR
,
87 "%s: Bad offset 0x%08" HWADDR_PRIx
"\n", __func__
,
94 static const MemoryRegionOps sysreg_ops
= {
95 .read
= msf2_sysreg_read
,
96 .write
= msf2_sysreg_write
,
97 .endianness
= DEVICE_NATIVE_ENDIAN
,
100 static void msf2_sysreg_init(Object
*obj
)
102 MSF2SysregState
*s
= MSF2_SYSREG(obj
);
104 memory_region_init_io(&s
->iomem
, obj
, &sysreg_ops
, s
, TYPE_MSF2_SYSREG
,
105 MSF2_SYSREG_MMIO_SIZE
);
106 sysbus_init_mmio(SYS_BUS_DEVICE(obj
), &s
->iomem
);
109 static const VMStateDescription vmstate_msf2_sysreg
= {
110 .name
= TYPE_MSF2_SYSREG
,
112 .minimum_version_id
= 1,
113 .fields
= (VMStateField
[]) {
114 VMSTATE_UINT32_ARRAY(regs
, MSF2SysregState
, MSF2_SYSREG_MMIO_SIZE
/ 4),
115 VMSTATE_END_OF_LIST()
119 static Property msf2_sysreg_properties
[] = {
120 /* default divisors in Libero GUI */
121 DEFINE_PROP_UINT8("apb0divisor", MSF2SysregState
, apb0div
, 2),
122 DEFINE_PROP_UINT8("apb1divisor", MSF2SysregState
, apb1div
, 2),
123 DEFINE_PROP_END_OF_LIST(),
126 static void msf2_sysreg_realize(DeviceState
*dev
, Error
**errp
)
128 MSF2SysregState
*s
= MSF2_SYSREG(dev
);
130 if ((s
->apb0div
> 32 || !is_power_of_2(s
->apb0div
))
131 || (s
->apb1div
> 32 || !is_power_of_2(s
->apb1div
))) {
132 error_setg(errp
, "Invalid apb divisor value");
133 error_append_hint(errp
, "apb divisor must be a power of 2"
134 " and maximum value is 32\n");
138 static void msf2_sysreg_class_init(ObjectClass
*klass
, void *data
)
140 DeviceClass
*dc
= DEVICE_CLASS(klass
);
142 dc
->vmsd
= &vmstate_msf2_sysreg
;
143 dc
->reset
= msf2_sysreg_reset
;
144 dc
->props
= msf2_sysreg_properties
;
145 dc
->realize
= msf2_sysreg_realize
;
148 static const TypeInfo msf2_sysreg_info
= {
149 .name
= TYPE_MSF2_SYSREG
,
150 .parent
= TYPE_SYS_BUS_DEVICE
,
151 .class_init
= msf2_sysreg_class_init
,
152 .instance_size
= sizeof(MSF2SysregState
),
153 .instance_init
= msf2_sysreg_init
,
156 static void msf2_sysreg_register_types(void)
158 type_register_static(&msf2_sysreg_info
);
161 type_init(msf2_sysreg_register_types
)