mirror of
https://github.com/dingusdev/dingusppc.git
synced 2025-01-27 01:31:21 +00:00
Common code for handling PCI config space.
This commit is contained in:
parent
6d004f0bf8
commit
3bce7bb1ea
164
devices/common/pci/pcidevice.cpp
Normal file
164
devices/common/pci/pcidevice.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-22 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/>.
|
||||
*/
|
||||
|
||||
#include <devices/common/pci/pcidevice.h>
|
||||
#include <devices/common/viacuda.h>
|
||||
#include <endianswap.h>
|
||||
#include <loguru.hpp>
|
||||
#include <memaccess.h>
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
PCIDevice::PCIDevice(std::string name)
|
||||
{
|
||||
this->pci_name = name;
|
||||
|
||||
this->pci_rd_stat = []() { return 0; };
|
||||
this->pci_rd_cmd = [this]() { return this->command; };
|
||||
this->pci_rd_bist = []() { return 0; };
|
||||
this->pci_rd_lat_timer = [this]() { return this->lat_timer; };
|
||||
this->pci_rd_cache_lnsz = [this]() { return this->cache_ln_sz; };
|
||||
|
||||
this->pci_wr_stat = [](uint16_t val) {};
|
||||
this->pci_wr_cmd = [this](uint16_t cmd) { this->command = cmd; };
|
||||
this->pci_wr_bist = [](uint8_t val) {};
|
||||
this->pci_wr_lat_timer = [this](uint8_t val) { this->lat_timer = val; };
|
||||
this->pci_wr_cache_lnsz = [this](uint8_t val) { this->cache_ln_sz = val; };
|
||||
|
||||
this->pci_notify_bar_change = [](int bar_num) {};
|
||||
};
|
||||
|
||||
uint32_t PCIDevice::pci_cfg_read(uint32_t reg_offs, uint32_t size)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
switch (reg_offs) {
|
||||
case PCI_CFG_DEV_ID:
|
||||
result = (this->device_id << 16) | (this->vendor_id);
|
||||
break;
|
||||
case PCI_CFG_STAT_CMD:
|
||||
result = (this->pci_rd_stat() << 16) | (this->pci_rd_cmd());
|
||||
break;
|
||||
case PCI_CFG_CLASS_REV:
|
||||
result = this->class_rev;
|
||||
break;
|
||||
case PCI_CFG_DWORD_3:
|
||||
result = (pci_rd_bist() << 24) | (this->hdr_type << 16) |
|
||||
(pci_rd_lat_timer() << 8) | pci_rd_cache_lnsz();
|
||||
break;
|
||||
case PCI_CFG_BAR0:
|
||||
case PCI_CFG_BAR1:
|
||||
case PCI_CFG_BAR2:
|
||||
case PCI_CFG_BAR3:
|
||||
case PCI_CFG_BAR4:
|
||||
case PCI_CFG_BAR5:
|
||||
result = this->bars[(reg_offs - 0x10) >> 2];
|
||||
break;
|
||||
case PCI_CFG_SUBSYS_ID:
|
||||
result = (this->subsys_id << 16) | (this->subsys_vndr);
|
||||
break;
|
||||
case PCI_CFG_ROM_BAR:
|
||||
result = this->exp_rom_bar;
|
||||
break;
|
||||
case PCI_CFG_DWORD_15:
|
||||
result = (max_lat << 24) | (min_gnt << 16) | (irq_pin << 8) | irq_line;
|
||||
break;
|
||||
default:
|
||||
LOG_F(WARNING, "%s: attempt to read from reserved/unimplemented register %d",
|
||||
this->pci_name.c_str(), reg_offs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (size == 4) {
|
||||
return BYTESWAP_32(result);
|
||||
} else {
|
||||
return read_mem_rev(((uint8_t *)&result) + (reg_offs & 3), size);
|
||||
}
|
||||
}
|
||||
|
||||
void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size)
|
||||
{
|
||||
uint32_t data;
|
||||
|
||||
if (size == 4) {
|
||||
data = BYTESWAP_32(value);
|
||||
} else {
|
||||
// get current register content as DWORD and update it partially
|
||||
data = BYTESWAP_32(this->pci_cfg_read(reg_offs, 4));
|
||||
write_mem_rev(((uint8_t *)&data) + (reg_offs & 3), value, size);
|
||||
}
|
||||
|
||||
switch (reg_offs) {
|
||||
case PCI_CFG_STAT_CMD:
|
||||
this->pci_wr_stat(data >> 16);
|
||||
this->pci_wr_cmd(data & 0xFFFFU);
|
||||
break;
|
||||
case PCI_CFG_DWORD_3:
|
||||
this->pci_wr_bist(data >> 24);
|
||||
this->pci_wr_lat_timer((data >> 8) & 0xFF);
|
||||
this->pci_wr_cache_lnsz(data & 0xFF);
|
||||
break;
|
||||
case PCI_CFG_BAR0:
|
||||
case PCI_CFG_BAR1:
|
||||
case PCI_CFG_BAR2:
|
||||
case PCI_CFG_BAR3:
|
||||
case PCI_CFG_BAR4:
|
||||
case PCI_CFG_BAR5:
|
||||
if (data == 0xFFFFFFFFUL) {
|
||||
this->do_bar_sizing((reg_offs - 0x10) >> 2);
|
||||
} else {
|
||||
this->set_bar_value((reg_offs - 0x10) >> 2, data);
|
||||
}
|
||||
break;
|
||||
case PCI_CFG_ROM_BAR:
|
||||
if (data == 0xFFFFF800UL) {
|
||||
this->exp_rom_bar = this->exp_bar_cfg;
|
||||
} else {
|
||||
this->exp_rom_bar = (data & 0xFFFFF800UL) | (this->exp_bar_cfg & 1);
|
||||
}
|
||||
break;
|
||||
case PCI_CFG_DWORD_15:
|
||||
this->irq_line = data >> 24;
|
||||
break;
|
||||
default:
|
||||
LOG_F(WARNING, "%s: attempt to write to reserved/unimplemented register %d",
|
||||
this->pci_name.c_str(), reg_offs);
|
||||
}
|
||||
}
|
||||
|
||||
void PCIDevice::do_bar_sizing(int bar_num)
|
||||
{
|
||||
this->bars[bar_num] = this->bars_cfg[bar_num];
|
||||
}
|
||||
|
||||
void PCIDevice::set_bar_value(int bar_num, uint32_t value)
|
||||
{
|
||||
uint32_t bar_cfg = this->bars_cfg[bar_num];
|
||||
if (bar_cfg & 1) {
|
||||
this->bars[bar_num] = (value & 0xFFFFFFFCUL) | 1;
|
||||
} else {
|
||||
if (bar_cfg & 6) {
|
||||
ABORT_F("Invalid or unsupported PCI space type: %d", (bar_cfg >> 1) & 3);
|
||||
}
|
||||
this->bars[bar_num] = (value & 0xFFFFFFF0UL) | (bar_cfg & 0xF);
|
||||
}
|
||||
this->pci_notify_bar_change(bar_num);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-21 divingkatae and maximum
|
||||
Copyright (C) 2018-22 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
@ -26,32 +26,43 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <devices/common/pci/pcihost.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
/* convert little-endian DWORD to big-endian DWORD */
|
||||
#define LE2BE(x) (x >> 24) | ((x & 0x00FF0000) >> 8) | ((x & 0x0000FF00) << 8) | (x << 24)
|
||||
|
||||
/** PCI configuration space registers offsets */
|
||||
enum {
|
||||
CFG_REG_CMD = 0x04, // command/status register
|
||||
CFG_REG_BAR0 = 0x10, // base address register 0
|
||||
CFG_REG_BAR1 = 0x14, // base address register 1
|
||||
CFG_REG_BAR2 = 0x18, // base address register 2
|
||||
CFG_REG_BAR3 = 0x1C, // base address register 3
|
||||
CFG_REG_BAR4 = 0x20, // base address register 4
|
||||
CFG_REG_BAR5 = 0x24, // base address register 5
|
||||
CFG_EXP_BASE = 0x30, // expansion ROM base
|
||||
PCI_CFG_DEV_ID = 0x00, // device and vendor IDs
|
||||
PCI_CFG_STAT_CMD = 0x04, // command/status register
|
||||
PCI_CFG_CLASS_REV = 0x08, // class code and revision ID
|
||||
PCI_CFG_DWORD_3 = 0x0C, // BIST, HeaderType, Lat_Timer and Cache_Line_Size
|
||||
PCI_CFG_BAR0 = 0x10, // base address register 0
|
||||
PCI_CFG_BAR1 = 0x14, // base address register 1
|
||||
PCI_CFG_BAR2 = 0x18, // base address register 2
|
||||
PCI_CFG_BAR3 = 0x1C, // base address register 3
|
||||
PCI_CFG_BAR4 = 0x20, // base address register 4
|
||||
PCI_CFG_BAR5 = 0x24, // base address register 5
|
||||
PCI_CFG_CIS_PTR = 0x28, // Cardbus CIS Pointer
|
||||
PCI_CFG_SUBSYS_ID = 0x2C, // Subsysten IDs
|
||||
PCI_CFG_ROM_BAR = 0x30, // expansion ROM base address
|
||||
PCI_CFG_DWORD_15 = 0x3C, // Max_Lat, Min_Gnt, Int_Pin and Int_Line registers
|
||||
};
|
||||
|
||||
/** PCI Vendor IDs for devices used in Power Macintosh computers. */
|
||||
enum {
|
||||
PCI_VENDOR_ATI = 0x1002,
|
||||
PCI_VENDOR_MOTOROLA = 0x1057,
|
||||
PCI_VENDOR_APPLE = 0x106B,
|
||||
};
|
||||
|
||||
|
||||
class PCIDevice : public MMIODevice {
|
||||
public:
|
||||
PCIDevice(std::string name) {
|
||||
this->pci_name = name;
|
||||
};
|
||||
PCIDevice(std::string name);
|
||||
virtual ~PCIDevice() = default;
|
||||
|
||||
virtual bool supports_io_space(void) = 0;
|
||||
virtual bool supports_io_space() {
|
||||
return false;
|
||||
};
|
||||
|
||||
/* I/O space access methods */
|
||||
virtual bool pci_io_read(uint32_t offset, uint32_t size, uint32_t* res) {
|
||||
@ -62,18 +73,55 @@ public:
|
||||
return false;
|
||||
};
|
||||
|
||||
/* configuration space access methods */
|
||||
virtual uint32_t pci_cfg_read(uint32_t reg_offs, uint32_t size) = 0;
|
||||
virtual void pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size) = 0;
|
||||
// configuration space access methods
|
||||
virtual uint32_t pci_cfg_read(uint32_t reg_offs, uint32_t size);
|
||||
virtual void pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size);
|
||||
|
||||
// plugin interface for using in the derived classes
|
||||
std::function<uint16_t()> pci_rd_stat;
|
||||
std::function<void(uint16_t)> pci_wr_stat;
|
||||
std::function<uint16_t()> pci_rd_cmd;
|
||||
std::function<void(uint16_t)> pci_wr_cmd;
|
||||
std::function<uint8_t()> pci_rd_bist;
|
||||
std::function<void(uint8_t)> pci_wr_bist;
|
||||
std::function<uint8_t()> pci_rd_lat_timer;
|
||||
std::function<void(uint8_t)> pci_wr_lat_timer;
|
||||
std::function<uint8_t()> pci_rd_cache_lnsz;
|
||||
std::function<void(uint8_t)> pci_wr_cache_lnsz;
|
||||
|
||||
std::function<void(int)> pci_notify_bar_change;
|
||||
|
||||
virtual void set_host(PCIHost* host_instance) {
|
||||
this->host_instance = host_instance;
|
||||
};
|
||||
|
||||
protected:
|
||||
void do_bar_sizing(int bar_num);
|
||||
void set_bar_value(int bar_num, uint32_t value);
|
||||
|
||||
std::string pci_name; // human-readable device name
|
||||
PCIHost* host_instance; // host bridge instance to call back
|
||||
uint32_t base_addr; // base address register 0
|
||||
|
||||
// PCI configuration space state
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint32_t class_rev; // class code and revision id
|
||||
uint16_t status = 0;
|
||||
uint16_t command = 0;
|
||||
uint8_t hdr_type = 0; // header type
|
||||
uint8_t lat_timer = 0; // latency timer
|
||||
uint8_t cache_ln_sz = 0; // cache line size
|
||||
uint16_t subsys_id = 0;
|
||||
uint16_t subsys_vndr = 0;
|
||||
uint8_t max_lat = 0;
|
||||
uint8_t min_gnt = 0;
|
||||
uint8_t irq_pin = 0;
|
||||
uint8_t irq_line = 0;
|
||||
|
||||
uint32_t bars[6] = { 0 }; // base address registers
|
||||
uint32_t bars_cfg[6] = { 0 }; // configuration values for base address registers
|
||||
uint32_t exp_rom_bar = 0; // expansion ROM base address
|
||||
uint32_t exp_bar_cfg = 0;
|
||||
};
|
||||
|
||||
#endif /* PCI_DEVICE_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user