2020-02-28 16:04:28 +00:00
|
|
|
/*
|
|
|
|
DingusPPC - The Experimental PowerPC Macintosh emulator
|
|
|
|
Copyright (C) 2018-20 divingkatae and maximum
|
|
|
|
(theweirdo) spatium
|
|
|
|
|
|
|
|
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
|
|
|
|
|
|
|
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 3 of the License, or
|
|
|
|
(at your option) 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 <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2019-07-02 02:15:33 +00:00
|
|
|
|
2019-08-21 06:33:01 +00:00
|
|
|
/** MPC106 (Grackle) emulation
|
|
|
|
|
|
|
|
Author: Max Poliakovski
|
|
|
|
*/
|
2019-07-02 02:15:33 +00:00
|
|
|
|
|
|
|
#include <cinttypes>
|
2020-05-12 18:55:45 +00:00
|
|
|
#include <cstring>
|
|
|
|
#include <iostream>
|
|
|
|
#include <thirdparty/loguru/loguru.hpp>
|
2019-08-21 06:33:01 +00:00
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
#include "hwcomponent.h"
|
2019-08-21 06:33:01 +00:00
|
|
|
#include "memctrlbase.h"
|
2020-05-12 18:55:45 +00:00
|
|
|
#include "memreadwrite.h"
|
2019-08-21 06:33:01 +00:00
|
|
|
#include "mmiodevice.h"
|
2019-07-02 02:15:33 +00:00
|
|
|
#include "mpc106.h"
|
|
|
|
|
2019-07-15 00:05:10 +00:00
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
MPC106::MPC106() : MemCtrlBase(), PCIDevice("Grackle PCI host bridge") {
|
2020-03-14 13:23:46 +00:00
|
|
|
this->name = "Grackle";
|
|
|
|
|
2020-03-31 19:12:06 +00:00
|
|
|
/* add PCI/ISA I/O space, 64K for now */
|
|
|
|
add_mmio_region(0xFE000000, 0x10000, this);
|
|
|
|
|
2019-08-21 06:33:01 +00:00
|
|
|
/* add memory mapped I/O region for MPC106 registers */
|
|
|
|
add_mmio_region(0xFEC00000, 0x300000, this);
|
2019-08-23 19:30:30 +00:00
|
|
|
|
|
|
|
this->pci_0_bus.clear();
|
2020-03-31 19:12:06 +00:00
|
|
|
this->io_space_devs.clear();
|
2019-07-02 02:15:33 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
MPC106::~MPC106() {
|
2019-08-23 19:30:30 +00:00
|
|
|
this->pci_0_bus.clear();
|
2019-08-21 06:33:01 +00:00
|
|
|
}
|
2019-07-12 05:27:14 +00:00
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
bool MPC106::supports_type(HWCompType type) {
|
2020-03-14 13:23:46 +00:00
|
|
|
if (type == HWCompType::MEM_CTRL || type == HWCompType::MMIO_DEV ||
|
|
|
|
type == HWCompType::PCI_HOST || type == HWCompType::PCI_DEV) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
uint32_t MPC106::read(uint32_t reg_start, uint32_t offset, int size) {
|
2020-03-31 19:12:06 +00:00
|
|
|
uint32_t result;
|
|
|
|
|
|
|
|
if (reg_start == 0xFE000000) {
|
|
|
|
/* broadcast I/O request to devices that support I/O space
|
|
|
|
until a device returns true that means "request accepted" */
|
|
|
|
for (auto& dev : this->io_space_devs) {
|
|
|
|
if (dev->pci_io_read(offset, size, &result)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LOG_F(ERROR, "Attempt to read from unmapped PCI I/O space, offset=0x%X", offset);
|
2020-05-12 18:55:45 +00:00
|
|
|
} else {
|
2020-03-31 19:12:06 +00:00
|
|
|
if (offset >= 0x200000) {
|
2020-05-12 18:55:45 +00:00
|
|
|
if (this->config_addr & 0x80) // process only if bit E (enable) is set
|
2020-03-31 19:12:06 +00:00
|
|
|
return pci_read(size);
|
|
|
|
}
|
2019-07-07 06:10:32 +00:00
|
|
|
}
|
|
|
|
|
2019-08-21 06:33:01 +00:00
|
|
|
/* FIXME: reading from CONFIG_ADDR is ignored for now */
|
|
|
|
|
2019-07-07 06:10:32 +00:00
|
|
|
return 0;
|
2019-07-02 02:15:33 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
void MPC106::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size) {
|
2020-03-31 19:12:06 +00:00
|
|
|
if (reg_start == 0xFE000000) {
|
|
|
|
/* broadcast I/O request to devices that support I/O space
|
|
|
|
until a device returns true that means "request accepted" */
|
|
|
|
for (auto& dev : this->io_space_devs) {
|
|
|
|
if (dev->pci_io_write(offset, value, size)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LOG_F(ERROR, "Attempt to write to unmapped PCI I/O space, offset=0x%X", offset);
|
2020-05-12 18:55:45 +00:00
|
|
|
} else {
|
2020-03-31 19:12:06 +00:00
|
|
|
if (offset < 0x200000) {
|
|
|
|
this->config_addr = value;
|
2020-05-12 18:55:45 +00:00
|
|
|
} else {
|
|
|
|
if (this->config_addr & 0x80) // process only if bit E (enable) is set
|
2020-03-31 19:12:06 +00:00
|
|
|
return pci_write(value, size);
|
|
|
|
}
|
2019-07-07 06:10:32 +00:00
|
|
|
}
|
2019-07-15 00:05:10 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
uint32_t MPC106::pci_read(uint32_t size) {
|
2019-08-23 19:30:30 +00:00
|
|
|
int bus_num, dev_num, fun_num, reg_offs;
|
2019-07-15 00:05:10 +00:00
|
|
|
|
2019-08-21 06:33:01 +00:00
|
|
|
bus_num = (this->config_addr >> 8) & 0xFF;
|
|
|
|
if (bus_num) {
|
2020-05-12 18:55:45 +00:00
|
|
|
LOG_F(
|
|
|
|
ERROR,
|
|
|
|
"%s err: read attempt from non-local PCI bus, config_addr = %x \n",
|
|
|
|
this->name.c_str(),
|
|
|
|
this->config_addr);
|
2019-08-21 06:33:01 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2019-07-15 00:05:10 +00:00
|
|
|
|
2019-08-23 19:30:30 +00:00
|
|
|
dev_num = (this->config_addr >> 19) & 0x1F;
|
|
|
|
fun_num = (this->config_addr >> 16) & 0x07;
|
|
|
|
reg_offs = (this->config_addr >> 24) & 0xFC;
|
2019-07-15 00:05:10 +00:00
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
if (dev_num == 0 && fun_num == 0) { // dev_num 0 is assigned to myself
|
2019-08-23 19:30:30 +00:00
|
|
|
return this->pci_cfg_read(reg_offs, size);
|
2019-08-21 06:33:01 +00:00
|
|
|
} else {
|
2019-08-23 19:30:30 +00:00
|
|
|
if (this->pci_0_bus.count(dev_num)) {
|
|
|
|
return this->pci_0_bus[dev_num]->pci_cfg_read(reg_offs, size);
|
|
|
|
} else {
|
2020-05-12 18:55:45 +00:00
|
|
|
LOG_F(
|
|
|
|
ERROR,
|
|
|
|
"%s err: read attempt from non-existing PCI device %d \n",
|
|
|
|
this->name.c_str(),
|
|
|
|
dev_num);
|
2019-08-23 19:30:30 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2019-08-21 06:33:01 +00:00
|
|
|
}
|
2019-07-15 00:05:10 +00:00
|
|
|
|
2019-08-21 06:33:01 +00:00
|
|
|
return 0;
|
2019-07-15 00:05:10 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
void MPC106::pci_write(uint32_t value, uint32_t size) {
|
2019-08-23 19:30:30 +00:00
|
|
|
int bus_num, dev_num, fun_num, reg_offs;
|
2019-07-15 00:05:10 +00:00
|
|
|
|
2019-08-21 06:33:01 +00:00
|
|
|
bus_num = (this->config_addr >> 8) & 0xFF;
|
|
|
|
if (bus_num) {
|
2020-05-12 18:55:45 +00:00
|
|
|
LOG_F(
|
|
|
|
ERROR,
|
|
|
|
"%s err: write attempt to non-local PCI bus, config_addr = %x \n",
|
|
|
|
this->name.c_str(),
|
|
|
|
this->config_addr);
|
2019-08-21 06:33:01 +00:00
|
|
|
return;
|
2019-07-15 00:05:10 +00:00
|
|
|
}
|
2019-07-02 02:15:33 +00:00
|
|
|
|
2019-08-23 19:30:30 +00:00
|
|
|
dev_num = (this->config_addr >> 19) & 0x1F;
|
|
|
|
fun_num = (this->config_addr >> 16) & 0x07;
|
|
|
|
reg_offs = (this->config_addr >> 24) & 0xFC;
|
2019-07-02 02:15:33 +00:00
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
if (dev_num == 0 && fun_num == 0) { // dev_num 0 is assigned to myself
|
2019-08-23 19:30:30 +00:00
|
|
|
this->pci_cfg_write(reg_offs, value, size);
|
2019-08-21 06:33:01 +00:00
|
|
|
} else {
|
2019-08-23 19:30:30 +00:00
|
|
|
if (this->pci_0_bus.count(dev_num)) {
|
|
|
|
this->pci_0_bus[dev_num]->pci_cfg_write(reg_offs, value, size);
|
|
|
|
} else {
|
2020-05-12 18:55:45 +00:00
|
|
|
LOG_F(
|
|
|
|
ERROR,
|
|
|
|
"%s err: write attempt to non-existing PCI device %d \n",
|
|
|
|
this->name.c_str(),
|
|
|
|
dev_num);
|
2019-08-23 19:30:30 +00:00
|
|
|
}
|
2019-08-21 06:33:01 +00:00
|
|
|
}
|
2019-07-02 02:15:33 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
uint32_t MPC106::pci_cfg_read(uint32_t reg_offs, uint32_t size) {
|
2019-08-21 06:33:01 +00:00
|
|
|
#ifdef MPC106_DEBUG
|
2020-02-27 02:51:07 +00:00
|
|
|
LOG_F(9, "read from Grackle register %08X\n", reg_offs);
|
2019-08-21 06:33:01 +00:00
|
|
|
#endif
|
2019-07-02 02:15:33 +00:00
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
switch (size) {
|
2019-08-21 06:33:01 +00:00
|
|
|
case 1:
|
2019-08-23 19:30:30 +00:00
|
|
|
return this->my_pci_cfg_hdr[reg_offs];
|
2019-08-21 06:33:01 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2020-01-13 02:04:06 +00:00
|
|
|
return READ_WORD_BE_A(&this->my_pci_cfg_hdr[reg_offs]);
|
2019-08-21 06:33:01 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
2020-01-13 02:04:06 +00:00
|
|
|
return READ_DWORD_BE_A(&this->my_pci_cfg_hdr[reg_offs]);
|
2019-08-21 06:33:01 +00:00
|
|
|
break;
|
|
|
|
default:
|
2020-02-24 14:54:29 +00:00
|
|
|
LOG_F(ERROR, "MPC106 read error: invalid size parameter %d \n", size);
|
2019-07-15 00:05:10 +00:00
|
|
|
}
|
|
|
|
|
2019-08-21 06:33:01 +00:00
|
|
|
return 0;
|
2019-07-15 00:05:10 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
void MPC106::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size) {
|
2019-08-21 06:33:01 +00:00
|
|
|
#ifdef MPC106_DEBUG
|
2020-02-27 02:51:07 +00:00
|
|
|
LOG_F(9, "write %08X to Grackle register %08X\n", value, reg_offs);
|
2019-08-21 06:33:01 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// FIXME: implement write-protection for read-only registers
|
2020-05-12 18:55:45 +00:00
|
|
|
switch (size) {
|
2019-08-21 06:33:01 +00:00
|
|
|
case 1:
|
2020-05-12 18:55:45 +00:00
|
|
|
this->my_pci_cfg_hdr[reg_offs] = value & 0xFF;
|
2019-08-21 06:33:01 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2020-05-12 18:55:45 +00:00
|
|
|
this->my_pci_cfg_hdr[reg_offs] = (value >> 8) & 0xFF;
|
|
|
|
this->my_pci_cfg_hdr[reg_offs + 1] = value & 0xFF;
|
2019-08-21 06:33:01 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
2020-05-12 18:55:45 +00:00
|
|
|
this->my_pci_cfg_hdr[reg_offs] = (value >> 24) & 0xFF;
|
|
|
|
this->my_pci_cfg_hdr[reg_offs + 1] = (value >> 16) & 0xFF;
|
|
|
|
this->my_pci_cfg_hdr[reg_offs + 2] = (value >> 8) & 0xFF;
|
|
|
|
this->my_pci_cfg_hdr[reg_offs + 3] = value & 0xFF;
|
2019-08-21 06:33:01 +00:00
|
|
|
break;
|
|
|
|
default:
|
2020-02-24 14:54:29 +00:00
|
|
|
LOG_F(ERROR, "MPC106 read error: invalid size parameter %d \n", size);
|
2019-07-02 02:15:33 +00:00
|
|
|
}
|
2019-10-07 01:21:01 +00:00
|
|
|
|
|
|
|
if (this->my_pci_cfg_hdr[0xF2] & 8) {
|
|
|
|
#ifdef MPC106_DEBUG
|
2020-02-27 02:51:07 +00:00
|
|
|
LOG_F(9, "MPC106: MCCR1[MEMGO] was set! \n");
|
2019-10-07 01:21:01 +00:00
|
|
|
#endif
|
|
|
|
setup_ram();
|
|
|
|
}
|
2019-07-02 02:15:33 +00:00
|
|
|
}
|
2019-08-23 19:30:30 +00:00
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
bool MPC106::pci_register_device(int dev_num, PCIDevice* dev_instance) {
|
|
|
|
if (this->pci_0_bus.count(dev_num)) // is dev_num already registered?
|
2019-08-23 19:30:30 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
this->pci_0_bus[dev_num] = dev_instance;
|
|
|
|
|
|
|
|
dev_instance->set_host(this);
|
|
|
|
|
2020-03-31 19:12:06 +00:00
|
|
|
if (dev_instance->supports_io_space()) {
|
|
|
|
this->io_space_devs.push_back(dev_instance);
|
|
|
|
}
|
|
|
|
|
2019-08-23 19:30:30 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
bool MPC106::pci_register_mmio_region(uint32_t start_addr, uint32_t size, PCIDevice* obj) {
|
2019-08-23 19:30:30 +00:00
|
|
|
// FIXME: add sanity checks!
|
|
|
|
return this->add_mmio_region(start_addr, size, obj);
|
|
|
|
}
|
2019-10-07 01:21:01 +00:00
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
void MPC106::setup_ram() {
|
2019-10-07 01:21:01 +00:00
|
|
|
uint32_t mem_start, mem_end, ext_mem_start, ext_mem_end, bank_start, bank_end;
|
|
|
|
uint32_t ram_size = 0;
|
|
|
|
|
2020-05-12 18:55:45 +00:00
|
|
|
uint8_t bank_en = this->my_pci_cfg_hdr[0xA0];
|
2019-10-07 01:21:01 +00:00
|
|
|
|
|
|
|
for (int bank = 0; bank < 8; bank++) {
|
|
|
|
if (bank_en & (1 << bank)) {
|
|
|
|
if (bank < 4) {
|
2020-05-12 18:55:45 +00:00
|
|
|
mem_start = READ_DWORD_LE_A(&this->my_pci_cfg_hdr[0x80]);
|
2020-01-13 02:04:06 +00:00
|
|
|
ext_mem_start = READ_DWORD_LE_A(&this->my_pci_cfg_hdr[0x88]);
|
2020-05-12 18:55:45 +00:00
|
|
|
mem_end = READ_DWORD_LE_A(&this->my_pci_cfg_hdr[0x90]);
|
|
|
|
ext_mem_end = READ_DWORD_LE_A(&this->my_pci_cfg_hdr[0x98]);
|
2019-10-07 01:21:01 +00:00
|
|
|
} else {
|
2020-05-12 18:55:45 +00:00
|
|
|
mem_start = READ_DWORD_LE_A(&this->my_pci_cfg_hdr[0x84]);
|
2020-01-13 02:04:06 +00:00
|
|
|
ext_mem_start = READ_DWORD_LE_A(&this->my_pci_cfg_hdr[0x8C]);
|
2020-05-12 18:55:45 +00:00
|
|
|
mem_end = READ_DWORD_LE_A(&this->my_pci_cfg_hdr[0x94]);
|
|
|
|
ext_mem_end = READ_DWORD_LE_A(&this->my_pci_cfg_hdr[0x9C]);
|
2019-10-07 01:21:01 +00:00
|
|
|
}
|
|
|
|
bank_start = (((ext_mem_start >> bank * 8) & 3) << 30) |
|
|
|
|
(((mem_start >> bank * 8) & 0xFF) << 20);
|
|
|
|
bank_end = (((ext_mem_end >> bank * 8) & 3) << 30) |
|
|
|
|
(((mem_end >> bank * 8) & 0xFF) << 20) | 0xFFFFFUL;
|
|
|
|
if (bank && bank_start != ram_size)
|
2020-02-24 14:54:29 +00:00
|
|
|
LOG_F(ERROR, "MPC106 error: RAM not contiguous! \n");
|
2019-10-07 01:21:01 +00:00
|
|
|
ram_size += bank_end + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this->add_ram_region(0, ram_size)) {
|
2020-02-24 14:54:29 +00:00
|
|
|
LOG_F(ERROR, "MPC106 RAM allocation failed! \n");
|
2019-10-07 01:21:01 +00:00
|
|
|
}
|
|
|
|
}
|