Browse Source
Signed-off-by: Hervé Poussineau <hpoussin@reactos.org> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> [dwg: Added CONFIG_RS6000_MC to ppc64 or it breaks testcases] Signed-off-by: David Gibson <david@gibson.dropbear.id.au>pull/45/merge
committed by
David Gibson
5 changed files with 242 additions and 0 deletions
@ -0,0 +1,232 @@ |
|||
/*
|
|||
* QEMU RS/6000 memory controller |
|||
* |
|||
* Copyright (c) 2017 Hervé Poussineau |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 2 of the License, or |
|||
* (at your option) version 3 or any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
#include "qemu/osdep.h" |
|||
#include "hw/isa/isa.h" |
|||
#include "exec/address-spaces.h" |
|||
#include "hw/boards.h" |
|||
#include "qapi/error.h" |
|||
#include "trace.h" |
|||
|
|||
#define TYPE_RS6000MC "rs6000-mc" |
|||
#define RS6000MC_DEVICE(obj) \ |
|||
OBJECT_CHECK(RS6000MCState, (obj), TYPE_RS6000MC) |
|||
|
|||
typedef struct RS6000MCState { |
|||
ISADevice parent_obj; |
|||
/* see US patent 5,684,979 for details (expired 2001-11-04) */ |
|||
uint32_t ram_size; |
|||
bool autoconfigure; |
|||
MemoryRegion simm[6]; |
|||
unsigned int simm_size[6]; |
|||
uint32_t end_address[8]; |
|||
uint8_t port0820_index; |
|||
PortioList portio; |
|||
} RS6000MCState; |
|||
|
|||
/* P0RT 0803 -- SIMM ID Register (32/8 MB) (Read Only) */ |
|||
|
|||
static uint32_t rs6000mc_port0803_read(void *opaque, uint32_t addr) |
|||
{ |
|||
RS6000MCState *s = opaque; |
|||
uint32_t val = 0; |
|||
int socket; |
|||
|
|||
/* (1 << socket) indicates 32 MB SIMM at given socket */ |
|||
for (socket = 0; socket < 6; socket++) { |
|||
if (s->simm_size[socket] == 32) { |
|||
val |= (1 << socket); |
|||
} |
|||
} |
|||
|
|||
trace_rs6000mc_id_read(addr, val); |
|||
return val; |
|||
} |
|||
|
|||
/* PORT 0804 -- SIMM Presence Register (Read Only) */ |
|||
|
|||
static uint32_t rs6000mc_port0804_read(void *opaque, uint32_t addr) |
|||
{ |
|||
RS6000MCState *s = opaque; |
|||
uint32_t val = 0xff; |
|||
int socket; |
|||
|
|||
/* (1 << socket) indicates SIMM absence at given socket */ |
|||
for (socket = 0; socket < 6; socket++) { |
|||
if (s->simm_size[socket]) { |
|||
val &= ~(1 << socket); |
|||
} |
|||
} |
|||
s->port0820_index = 0; |
|||
|
|||
trace_rs6000mc_presence_read(addr, val); |
|||
return val; |
|||
} |
|||
|
|||
/* Memory Controller Size Programming Register */ |
|||
|
|||
static uint32_t rs6000mc_port0820_read(void *opaque, uint32_t addr) |
|||
{ |
|||
RS6000MCState *s = opaque; |
|||
uint32_t val = s->end_address[s->port0820_index] & 0x1f; |
|||
s->port0820_index = (s->port0820_index + 1) & 7; |
|||
trace_rs6000mc_size_read(addr, val); |
|||
return val; |
|||
} |
|||
|
|||
static void rs6000mc_port0820_write(void *opaque, uint32_t addr, uint32_t val) |
|||
{ |
|||
RS6000MCState *s = opaque; |
|||
uint8_t socket = val >> 5; |
|||
uint32_t end_address = val & 0x1f; |
|||
|
|||
trace_rs6000mc_size_write(addr, val); |
|||
s->end_address[socket] = end_address; |
|||
if (socket > 0 && socket < 7) { |
|||
if (s->simm_size[socket - 1]) { |
|||
uint32_t size; |
|||
uint32_t start_address = 0; |
|||
if (socket > 1) { |
|||
start_address = s->end_address[socket - 1]; |
|||
} |
|||
|
|||
size = end_address - start_address; |
|||
memory_region_set_enabled(&s->simm[socket - 1], size != 0); |
|||
memory_region_set_address(&s->simm[socket - 1], |
|||
start_address * 8 * 1024 * 1024); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Read Memory Parity Error */ |
|||
|
|||
enum { |
|||
PORT0841_NO_ERROR_DETECTED = 0x01, |
|||
}; |
|||
|
|||
static uint32_t rs6000mc_port0841_read(void *opaque, uint32_t addr) |
|||
{ |
|||
uint32_t val = PORT0841_NO_ERROR_DETECTED; |
|||
trace_rs6000mc_parity_read(addr, val); |
|||
return val; |
|||
} |
|||
|
|||
static const MemoryRegionPortio rs6000mc_port_list[] = { |
|||
{ 0x803, 1, 1, .read = rs6000mc_port0803_read }, |
|||
{ 0x804, 1, 1, .read = rs6000mc_port0804_read }, |
|||
{ 0x820, 1, 1, .read = rs6000mc_port0820_read, |
|||
.write = rs6000mc_port0820_write, }, |
|||
{ 0x841, 1, 1, .read = rs6000mc_port0841_read }, |
|||
PORTIO_END_OF_LIST() |
|||
}; |
|||
|
|||
static void rs6000mc_realize(DeviceState *dev, Error **errp) |
|||
{ |
|||
RS6000MCState *s = RS6000MC_DEVICE(dev); |
|||
int socket = 0; |
|||
unsigned int ram_size = s->ram_size / (1024 * 1024); |
|||
|
|||
while (socket < 6) { |
|||
if (ram_size >= 64) { |
|||
s->simm_size[socket] = 32; |
|||
s->simm_size[socket + 1] = 32; |
|||
ram_size -= 64; |
|||
} else if (ram_size >= 16) { |
|||
s->simm_size[socket] = 8; |
|||
s->simm_size[socket + 1] = 8; |
|||
ram_size -= 16; |
|||
} else { |
|||
/* Not enough memory */ |
|||
break; |
|||
} |
|||
socket += 2; |
|||
} |
|||
|
|||
for (socket = 0; socket < 6; socket++) { |
|||
if (s->simm_size[socket]) { |
|||
char name[] = "simm.?"; |
|||
name[5] = socket + '0'; |
|||
memory_region_allocate_system_memory(&s->simm[socket], OBJECT(dev), |
|||
name, s->simm_size[socket] |
|||
* 1024 * 1024); |
|||
memory_region_add_subregion_overlap(get_system_memory(), 0, |
|||
&s->simm[socket], socket); |
|||
} |
|||
} |
|||
if (ram_size) { |
|||
/* unable to push all requested RAM in SIMMs */ |
|||
error_setg(errp, "RAM size incompatible with this board. " |
|||
"Try again with something else, like %d MB", |
|||
s->ram_size / 1024 / 1024 - ram_size); |
|||
return; |
|||
} |
|||
|
|||
if (s->autoconfigure) { |
|||
uint32_t start_address = 0; |
|||
for (socket = 0; socket < 6; socket++) { |
|||
if (s->simm_size[socket]) { |
|||
memory_region_set_enabled(&s->simm[socket], true); |
|||
memory_region_set_address(&s->simm[socket], start_address); |
|||
start_address += memory_region_size(&s->simm[socket]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
isa_register_portio_list(ISA_DEVICE(dev), &s->portio, 0x0, |
|||
rs6000mc_port_list, s, "rs6000mc"); |
|||
} |
|||
|
|||
static const VMStateDescription vmstate_rs6000mc = { |
|||
.name = "rs6000-mc", |
|||
.version_id = 1, |
|||
.minimum_version_id = 1, |
|||
.fields = (VMStateField[]) { |
|||
VMSTATE_UINT8(port0820_index, RS6000MCState), |
|||
VMSTATE_END_OF_LIST() |
|||
}, |
|||
}; |
|||
|
|||
static Property rs6000mc_properties[] = { |
|||
DEFINE_PROP_UINT32("ram-size", RS6000MCState, ram_size, 0), |
|||
DEFINE_PROP_BOOL("auto-configure", RS6000MCState, autoconfigure, true), |
|||
DEFINE_PROP_END_OF_LIST() |
|||
}; |
|||
|
|||
static void rs6000mc_class_initfn(ObjectClass *klass, void *data) |
|||
{ |
|||
DeviceClass *dc = DEVICE_CLASS(klass); |
|||
|
|||
dc->realize = rs6000mc_realize; |
|||
dc->vmsd = &vmstate_rs6000mc; |
|||
dc->props = rs6000mc_properties; |
|||
} |
|||
|
|||
static const TypeInfo rs6000mc_info = { |
|||
.name = TYPE_RS6000MC, |
|||
.parent = TYPE_ISA_DEVICE, |
|||
.instance_size = sizeof(RS6000MCState), |
|||
.class_init = rs6000mc_class_initfn, |
|||
}; |
|||
|
|||
static void rs6000mc_types(void) |
|||
{ |
|||
type_register_static(&rs6000mc_info); |
|||
} |
|||
|
|||
type_init(rs6000mc_types) |
|||
Loading…
Reference in new issue