xref: /openbmc/qemu/hw/ppc/rs6000_mc.c (revision a53b931645183bd0c15dd19ae0708fc3c81ecf1d)
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