179623312SHervé Poussineau /*
279623312SHervé Poussineau * QEMU RS/6000 memory controller
379623312SHervé Poussineau *
479623312SHervé Poussineau * Copyright (c) 2017 Hervé Poussineau
579623312SHervé Poussineau *
6*dc86dd55SPhilippe Mathieu-Daudé * SPDX-License-Identifier: GPL-2.0-or-later
7*dc86dd55SPhilippe Mathieu-Daudé *
879623312SHervé Poussineau * This program is free software: you can redistribute it and/or modify
979623312SHervé Poussineau * it under the terms of the GNU General Public License as published by
1079623312SHervé Poussineau * the Free Software Foundation, either version 2 of the License, or
11*dc86dd55SPhilippe Mathieu-Daudé * (at your option) any later version.
1279623312SHervé Poussineau *
1379623312SHervé Poussineau * This program is distributed in the hope that it will be useful,
1479623312SHervé Poussineau * but WITHOUT ANY WARRANTY; without even the implied warranty of
1579623312SHervé Poussineau * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1679623312SHervé Poussineau * GNU General Public License for more details.
1779623312SHervé Poussineau *
1879623312SHervé Poussineau * You should have received a copy of the GNU General Public License
1979623312SHervé Poussineau * along with this program. If not, see <http://www.gnu.org/licenses/>.
2079623312SHervé Poussineau */
2179623312SHervé Poussineau
2279623312SHervé Poussineau #include "qemu/osdep.h"
23ab3dd749SPhilippe Mathieu-Daudé #include "qemu/units.h"
2479623312SHervé Poussineau #include "hw/isa/isa.h"
25a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
26d6454270SMarkus Armbruster #include "migration/vmstate.h"
2779623312SHervé Poussineau #include "exec/address-spaces.h"
2879623312SHervé Poussineau #include "qapi/error.h"
2979623312SHervé Poussineau #include "trace.h"
30db1015e9SEduardo Habkost #include "qom/object.h"
3179623312SHervé Poussineau
3279623312SHervé Poussineau #define TYPE_RS6000MC "rs6000-mc"
338063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(RS6000MCState, RS6000MC)
3479623312SHervé Poussineau
35db1015e9SEduardo Habkost struct RS6000MCState {
3679623312SHervé Poussineau ISADevice parent_obj;
3779623312SHervé Poussineau /* see US patent 5,684,979 for details (expired 2001-11-04) */
3879623312SHervé Poussineau uint32_t ram_size;
3979623312SHervé Poussineau bool autoconfigure;
4079623312SHervé Poussineau MemoryRegion simm[6];
4179623312SHervé Poussineau unsigned int simm_size[6];
4279623312SHervé Poussineau uint32_t end_address[8];
4379623312SHervé Poussineau uint8_t port0820_index;
4479623312SHervé Poussineau PortioList portio;
45db1015e9SEduardo Habkost };
4679623312SHervé Poussineau
4779623312SHervé Poussineau /* P0RT 0803 -- SIMM ID Register (32/8 MB) (Read Only) */
4879623312SHervé Poussineau
rs6000mc_port0803_read(void * opaque,uint32_t addr)4979623312SHervé Poussineau static uint32_t rs6000mc_port0803_read(void *opaque, uint32_t addr)
5079623312SHervé Poussineau {
5179623312SHervé Poussineau RS6000MCState *s = opaque;
5279623312SHervé Poussineau uint32_t val = 0;
5379623312SHervé Poussineau int socket;
5479623312SHervé Poussineau
5579623312SHervé Poussineau /* (1 << socket) indicates 32 MB SIMM at given socket */
5679623312SHervé Poussineau for (socket = 0; socket < 6; socket++) {
5779623312SHervé Poussineau if (s->simm_size[socket] == 32) {
5879623312SHervé Poussineau val |= (1 << socket);
5979623312SHervé Poussineau }
6079623312SHervé Poussineau }
6179623312SHervé Poussineau
6279623312SHervé Poussineau trace_rs6000mc_id_read(addr, val);
6379623312SHervé Poussineau return val;
6479623312SHervé Poussineau }
6579623312SHervé Poussineau
6679623312SHervé Poussineau /* PORT 0804 -- SIMM Presence Register (Read Only) */
6779623312SHervé Poussineau
rs6000mc_port0804_read(void * opaque,uint32_t addr)6879623312SHervé Poussineau static uint32_t rs6000mc_port0804_read(void *opaque, uint32_t addr)
6979623312SHervé Poussineau {
7079623312SHervé Poussineau RS6000MCState *s = opaque;
7179623312SHervé Poussineau uint32_t val = 0xff;
7279623312SHervé Poussineau int socket;
7379623312SHervé Poussineau
7479623312SHervé Poussineau /* (1 << socket) indicates SIMM absence at given socket */
7579623312SHervé Poussineau for (socket = 0; socket < 6; socket++) {
7679623312SHervé Poussineau if (s->simm_size[socket]) {
7779623312SHervé Poussineau val &= ~(1 << socket);
7879623312SHervé Poussineau }
7979623312SHervé Poussineau }
8079623312SHervé Poussineau s->port0820_index = 0;
8179623312SHervé Poussineau
8279623312SHervé Poussineau trace_rs6000mc_presence_read(addr, val);
8379623312SHervé Poussineau return val;
8479623312SHervé Poussineau }
8579623312SHervé Poussineau
8679623312SHervé Poussineau /* Memory Controller Size Programming Register */
8779623312SHervé Poussineau
rs6000mc_port0820_read(void * opaque,uint32_t addr)8879623312SHervé Poussineau static uint32_t rs6000mc_port0820_read(void *opaque, uint32_t addr)
8979623312SHervé Poussineau {
9079623312SHervé Poussineau RS6000MCState *s = opaque;
9179623312SHervé Poussineau uint32_t val = s->end_address[s->port0820_index] & 0x1f;
9279623312SHervé Poussineau s->port0820_index = (s->port0820_index + 1) & 7;
9379623312SHervé Poussineau trace_rs6000mc_size_read(addr, val);
9479623312SHervé Poussineau return val;
9579623312SHervé Poussineau }
9679623312SHervé Poussineau
rs6000mc_port0820_write(void * opaque,uint32_t addr,uint32_t val)9779623312SHervé Poussineau static void rs6000mc_port0820_write(void *opaque, uint32_t addr, uint32_t val)
9879623312SHervé Poussineau {
9979623312SHervé Poussineau RS6000MCState *s = opaque;
10079623312SHervé Poussineau uint8_t socket = val >> 5;
10179623312SHervé Poussineau uint32_t end_address = val & 0x1f;
10279623312SHervé Poussineau
10379623312SHervé Poussineau trace_rs6000mc_size_write(addr, val);
10479623312SHervé Poussineau s->end_address[socket] = end_address;
10579623312SHervé Poussineau if (socket > 0 && socket < 7) {
10679623312SHervé Poussineau if (s->simm_size[socket - 1]) {
10779623312SHervé Poussineau uint32_t size;
10879623312SHervé Poussineau uint32_t start_address = 0;
10979623312SHervé Poussineau if (socket > 1) {
11079623312SHervé Poussineau start_address = s->end_address[socket - 1];
11179623312SHervé Poussineau }
11279623312SHervé Poussineau
11379623312SHervé Poussineau size = end_address - start_address;
11479623312SHervé Poussineau memory_region_set_enabled(&s->simm[socket - 1], size != 0);
11579623312SHervé Poussineau memory_region_set_address(&s->simm[socket - 1],
116ab3dd749SPhilippe Mathieu-Daudé start_address * 8 * MiB);
11779623312SHervé Poussineau }
11879623312SHervé Poussineau }
11979623312SHervé Poussineau }
12079623312SHervé Poussineau
12179623312SHervé Poussineau /* Read Memory Parity Error */
12279623312SHervé Poussineau
12379623312SHervé Poussineau enum {
12479623312SHervé Poussineau PORT0841_NO_ERROR_DETECTED = 0x01,
12579623312SHervé Poussineau };
12679623312SHervé Poussineau
rs6000mc_port0841_read(void * opaque,uint32_t addr)12779623312SHervé Poussineau static uint32_t rs6000mc_port0841_read(void *opaque, uint32_t addr)
12879623312SHervé Poussineau {
12979623312SHervé Poussineau uint32_t val = PORT0841_NO_ERROR_DETECTED;
13079623312SHervé Poussineau trace_rs6000mc_parity_read(addr, val);
13179623312SHervé Poussineau return val;
13279623312SHervé Poussineau }
13379623312SHervé Poussineau
13479623312SHervé Poussineau static const MemoryRegionPortio rs6000mc_port_list[] = {
13579623312SHervé Poussineau { 0x803, 1, 1, .read = rs6000mc_port0803_read },
13679623312SHervé Poussineau { 0x804, 1, 1, .read = rs6000mc_port0804_read },
13779623312SHervé Poussineau { 0x820, 1, 1, .read = rs6000mc_port0820_read,
13879623312SHervé Poussineau .write = rs6000mc_port0820_write, },
13979623312SHervé Poussineau { 0x841, 1, 1, .read = rs6000mc_port0841_read },
14079623312SHervé Poussineau PORTIO_END_OF_LIST()
14179623312SHervé Poussineau };
14279623312SHervé Poussineau
rs6000mc_realize(DeviceState * dev,Error ** errp)14379623312SHervé Poussineau static void rs6000mc_realize(DeviceState *dev, Error **errp)
14479623312SHervé Poussineau {
1455182f175SEduardo Habkost RS6000MCState *s = RS6000MC(dev);
14679623312SHervé Poussineau int socket = 0;
147ab3dd749SPhilippe Mathieu-Daudé unsigned int ram_size = s->ram_size / MiB;
14879623312SHervé Poussineau
14979623312SHervé Poussineau while (socket < 6) {
15079623312SHervé Poussineau if (ram_size >= 64) {
15179623312SHervé Poussineau s->simm_size[socket] = 32;
15279623312SHervé Poussineau s->simm_size[socket + 1] = 32;
15379623312SHervé Poussineau ram_size -= 64;
15479623312SHervé Poussineau } else if (ram_size >= 16) {
15579623312SHervé Poussineau s->simm_size[socket] = 8;
15679623312SHervé Poussineau s->simm_size[socket + 1] = 8;
15779623312SHervé Poussineau ram_size -= 16;
15879623312SHervé Poussineau } else {
15979623312SHervé Poussineau /* Not enough memory */
16079623312SHervé Poussineau break;
16179623312SHervé Poussineau }
16279623312SHervé Poussineau socket += 2;
16379623312SHervé Poussineau }
16479623312SHervé Poussineau
16579623312SHervé Poussineau for (socket = 0; socket < 6; socket++) {
16679623312SHervé Poussineau if (s->simm_size[socket]) {
16779623312SHervé Poussineau char name[] = "simm.?";
16879623312SHervé Poussineau name[5] = socket + '0';
1692198f5f0SPhilippe Mathieu-Daudé if (!memory_region_init_ram(&s->simm[socket], OBJECT(dev), name,
1702198f5f0SPhilippe Mathieu-Daudé s->simm_size[socket] * MiB, errp)) {
171dcfe4805SMarkus Armbruster return;
1722def24f1SIgor Mammedov }
17379623312SHervé Poussineau memory_region_add_subregion_overlap(get_system_memory(), 0,
17479623312SHervé Poussineau &s->simm[socket], socket);
17579623312SHervé Poussineau }
17679623312SHervé Poussineau }
17779623312SHervé Poussineau if (ram_size) {
17879623312SHervé Poussineau /* unable to push all requested RAM in SIMMs */
179dcfe4805SMarkus Armbruster error_setg(errp, "RAM size incompatible with this board. "
180ab3dd749SPhilippe Mathieu-Daudé "Try again with something else, like %" PRId64 " MB",
181ab3dd749SPhilippe Mathieu-Daudé s->ram_size / MiB - ram_size);
182dcfe4805SMarkus Armbruster return;
18379623312SHervé Poussineau }
18479623312SHervé Poussineau
18579623312SHervé Poussineau if (s->autoconfigure) {
18679623312SHervé Poussineau uint32_t start_address = 0;
18779623312SHervé Poussineau for (socket = 0; socket < 6; socket++) {
18879623312SHervé Poussineau if (s->simm_size[socket]) {
18979623312SHervé Poussineau memory_region_set_enabled(&s->simm[socket], true);
19079623312SHervé Poussineau memory_region_set_address(&s->simm[socket], start_address);
19179623312SHervé Poussineau start_address += memory_region_size(&s->simm[socket]);
19279623312SHervé Poussineau }
19379623312SHervé Poussineau }
19479623312SHervé Poussineau }
19579623312SHervé Poussineau
19679623312SHervé Poussineau isa_register_portio_list(ISA_DEVICE(dev), &s->portio, 0x0,
19779623312SHervé Poussineau rs6000mc_port_list, s, "rs6000mc");
19879623312SHervé Poussineau }
19979623312SHervé Poussineau
20079623312SHervé Poussineau static const VMStateDescription vmstate_rs6000mc = {
20179623312SHervé Poussineau .name = "rs6000-mc",
20279623312SHervé Poussineau .version_id = 1,
20379623312SHervé Poussineau .minimum_version_id = 1,
204078ddbc9SRichard Henderson .fields = (const VMStateField[]) {
20579623312SHervé Poussineau VMSTATE_UINT8(port0820_index, RS6000MCState),
20679623312SHervé Poussineau VMSTATE_END_OF_LIST()
20779623312SHervé Poussineau },
20879623312SHervé Poussineau };
20979623312SHervé Poussineau
21079623312SHervé Poussineau static Property rs6000mc_properties[] = {
21179623312SHervé Poussineau DEFINE_PROP_UINT32("ram-size", RS6000MCState, ram_size, 0),
21279623312SHervé Poussineau DEFINE_PROP_BOOL("auto-configure", RS6000MCState, autoconfigure, true),
21379623312SHervé Poussineau DEFINE_PROP_END_OF_LIST()
21479623312SHervé Poussineau };
21579623312SHervé Poussineau
rs6000mc_class_initfn(ObjectClass * klass,void * data)21679623312SHervé Poussineau static void rs6000mc_class_initfn(ObjectClass *klass, void *data)
21779623312SHervé Poussineau {
21879623312SHervé Poussineau DeviceClass *dc = DEVICE_CLASS(klass);
21979623312SHervé Poussineau
22079623312SHervé Poussineau dc->realize = rs6000mc_realize;
22179623312SHervé Poussineau dc->vmsd = &vmstate_rs6000mc;
2224f67d30bSMarc-André Lureau device_class_set_props(dc, rs6000mc_properties);
22379623312SHervé Poussineau }
22479623312SHervé Poussineau
22579623312SHervé Poussineau static const TypeInfo rs6000mc_info = {
22679623312SHervé Poussineau .name = TYPE_RS6000MC,
22779623312SHervé Poussineau .parent = TYPE_ISA_DEVICE,
22879623312SHervé Poussineau .instance_size = sizeof(RS6000MCState),
22979623312SHervé Poussineau .class_init = rs6000mc_class_initfn,
23079623312SHervé Poussineau };
23179623312SHervé Poussineau
rs6000mc_types(void)23279623312SHervé Poussineau static void rs6000mc_types(void)
23379623312SHervé Poussineau {
23479623312SHervé Poussineau type_register_static(&rs6000mc_info);
23579623312SHervé Poussineau }
23679623312SHervé Poussineau
23779623312SHervé Poussineau type_init(rs6000mc_types)
238