2 * QEMU RS/6000 memory controller
4 * Copyright (c) 2017 Hervé Poussineau
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) version 3 or any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "qemu/osdep.h"
21 #include "qemu/units.h"
22 #include "hw/isa/isa.h"
23 #include "hw/qdev-properties.h"
24 #include "migration/vmstate.h"
25 #include "exec/address-spaces.h"
26 #include "hw/boards.h"
27 #include "qapi/error.h"
29 #include "qom/object.h"
31 #define TYPE_RS6000MC "rs6000-mc"
32 typedef struct RS6000MCState RS6000MCState
;
33 DECLARE_INSTANCE_CHECKER(RS6000MCState
, RS6000MC
,
36 struct RS6000MCState
{
38 /* see US patent 5,684,979 for details (expired 2001-11-04) */
42 unsigned int simm_size
[6];
43 uint32_t end_address
[8];
44 uint8_t port0820_index
;
48 /* P0RT 0803 -- SIMM ID Register (32/8 MB) (Read Only) */
50 static uint32_t rs6000mc_port0803_read(void *opaque
, uint32_t addr
)
52 RS6000MCState
*s
= opaque
;
56 /* (1 << socket) indicates 32 MB SIMM at given socket */
57 for (socket
= 0; socket
< 6; socket
++) {
58 if (s
->simm_size
[socket
] == 32) {
63 trace_rs6000mc_id_read(addr
, val
);
67 /* PORT 0804 -- SIMM Presence Register (Read Only) */
69 static uint32_t rs6000mc_port0804_read(void *opaque
, uint32_t addr
)
71 RS6000MCState
*s
= opaque
;
75 /* (1 << socket) indicates SIMM absence at given socket */
76 for (socket
= 0; socket
< 6; socket
++) {
77 if (s
->simm_size
[socket
]) {
78 val
&= ~(1 << socket
);
81 s
->port0820_index
= 0;
83 trace_rs6000mc_presence_read(addr
, val
);
87 /* Memory Controller Size Programming Register */
89 static uint32_t rs6000mc_port0820_read(void *opaque
, uint32_t addr
)
91 RS6000MCState
*s
= opaque
;
92 uint32_t val
= s
->end_address
[s
->port0820_index
] & 0x1f;
93 s
->port0820_index
= (s
->port0820_index
+ 1) & 7;
94 trace_rs6000mc_size_read(addr
, val
);
98 static void rs6000mc_port0820_write(void *opaque
, uint32_t addr
, uint32_t val
)
100 RS6000MCState
*s
= opaque
;
101 uint8_t socket
= val
>> 5;
102 uint32_t end_address
= val
& 0x1f;
104 trace_rs6000mc_size_write(addr
, val
);
105 s
->end_address
[socket
] = end_address
;
106 if (socket
> 0 && socket
< 7) {
107 if (s
->simm_size
[socket
- 1]) {
109 uint32_t start_address
= 0;
111 start_address
= s
->end_address
[socket
- 1];
114 size
= end_address
- start_address
;
115 memory_region_set_enabled(&s
->simm
[socket
- 1], size
!= 0);
116 memory_region_set_address(&s
->simm
[socket
- 1],
117 start_address
* 8 * MiB
);
122 /* Read Memory Parity Error */
125 PORT0841_NO_ERROR_DETECTED
= 0x01,
128 static uint32_t rs6000mc_port0841_read(void *opaque
, uint32_t addr
)
130 uint32_t val
= PORT0841_NO_ERROR_DETECTED
;
131 trace_rs6000mc_parity_read(addr
, val
);
135 static const MemoryRegionPortio rs6000mc_port_list
[] = {
136 { 0x803, 1, 1, .read
= rs6000mc_port0803_read
},
137 { 0x804, 1, 1, .read
= rs6000mc_port0804_read
},
138 { 0x820, 1, 1, .read
= rs6000mc_port0820_read
,
139 .write
= rs6000mc_port0820_write
, },
140 { 0x841, 1, 1, .read
= rs6000mc_port0841_read
},
144 static void rs6000mc_realize(DeviceState
*dev
, Error
**errp
)
146 RS6000MCState
*s
= RS6000MC(dev
);
148 unsigned int ram_size
= s
->ram_size
/ MiB
;
149 Error
*local_err
= NULL
;
152 if (ram_size
>= 64) {
153 s
->simm_size
[socket
] = 32;
154 s
->simm_size
[socket
+ 1] = 32;
156 } else if (ram_size
>= 16) {
157 s
->simm_size
[socket
] = 8;
158 s
->simm_size
[socket
+ 1] = 8;
161 /* Not enough memory */
167 for (socket
= 0; socket
< 6; socket
++) {
168 if (s
->simm_size
[socket
]) {
169 char name
[] = "simm.?";
170 name
[5] = socket
+ '0';
171 memory_region_init_ram(&s
->simm
[socket
], OBJECT(dev
), name
,
172 s
->simm_size
[socket
] * MiB
, &local_err
);
174 error_propagate(errp
, local_err
);
177 memory_region_add_subregion_overlap(get_system_memory(), 0,
178 &s
->simm
[socket
], socket
);
182 /* unable to push all requested RAM in SIMMs */
183 error_setg(errp
, "RAM size incompatible with this board. "
184 "Try again with something else, like %" PRId64
" MB",
185 s
->ram_size
/ MiB
- ram_size
);
189 if (s
->autoconfigure
) {
190 uint32_t start_address
= 0;
191 for (socket
= 0; socket
< 6; socket
++) {
192 if (s
->simm_size
[socket
]) {
193 memory_region_set_enabled(&s
->simm
[socket
], true);
194 memory_region_set_address(&s
->simm
[socket
], start_address
);
195 start_address
+= memory_region_size(&s
->simm
[socket
]);
200 isa_register_portio_list(ISA_DEVICE(dev
), &s
->portio
, 0x0,
201 rs6000mc_port_list
, s
, "rs6000mc");
204 static const VMStateDescription vmstate_rs6000mc
= {
207 .minimum_version_id
= 1,
208 .fields
= (VMStateField
[]) {
209 VMSTATE_UINT8(port0820_index
, RS6000MCState
),
210 VMSTATE_END_OF_LIST()
214 static Property rs6000mc_properties
[] = {
215 DEFINE_PROP_UINT32("ram-size", RS6000MCState
, ram_size
, 0),
216 DEFINE_PROP_BOOL("auto-configure", RS6000MCState
, autoconfigure
, true),
217 DEFINE_PROP_END_OF_LIST()
220 static void rs6000mc_class_initfn(ObjectClass
*klass
, void *data
)
222 DeviceClass
*dc
= DEVICE_CLASS(klass
);
224 dc
->realize
= rs6000mc_realize
;
225 dc
->vmsd
= &vmstate_rs6000mc
;
226 device_class_set_props(dc
, rs6000mc_properties
);
229 static const TypeInfo rs6000mc_info
= {
230 .name
= TYPE_RS6000MC
,
231 .parent
= TYPE_ISA_DEVICE
,
232 .instance_size
= sizeof(RS6000MCState
),
233 .class_init
= rs6000mc_class_initfn
,
236 static void rs6000mc_types(void)
238 type_register_static(&rs6000mc_info
);
241 type_init(rs6000mc_types
)