// license:BSD-3-Clause
// copyright-holders:Ted Green
#include "emu.h"
#include "pci-ide.h"
DEFINE_DEVICE_TYPE(IDE_PCI, ide_pci_device, "ide_pci", "PCI IDE interface")
ide_pci_device::ide_pci_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
pci_device(mconfig, IDE_PCI, tag, owner, clock),
m_ide(*this, "ide"),
m_ide2(*this, "ide2"),
m_irq_handler(*this),
m_legacy_top(0x000),
m_pif(0x8a)
{
}
void ide_pci_device::config_map(address_map &map)
{
pci_device::config_map(map);
map(0x09, 0x09).w(FUNC(ide_pci_device::prog_if_w));
map(0x10, 0x1f).rw(FUNC(ide_pci_device::address_base_r), FUNC(ide_pci_device::address_base_w));
map(0x2c, 0x2f).w(FUNC(ide_pci_device::subsystem_id_w));
map(0x40, 0x5f).rw(FUNC(ide_pci_device::pcictrl_r), FUNC(ide_pci_device::pcictrl_w));
map(0x70, 0x77).rw("ide", FUNC(bus_master_ide_controller_device::bmdma_r), FUNC(bus_master_ide_controller_device::bmdma_w)); // PCI646
map(0x78, 0x7f).rw("ide2", FUNC(bus_master_ide_controller_device::bmdma_r), FUNC(bus_master_ide_controller_device::bmdma_w)); // PCI646
}
void ide_pci_device::chan1_data_command_map(address_map &map)
{
map(0x0, 0x7).rw("ide", FUNC(bus_master_ide_controller_device::cs0_r), FUNC(bus_master_ide_controller_device::cs0_w));
}
void ide_pci_device::chan1_control_map(address_map &map)
{
map(0x0, 0x3).rw(FUNC(ide_pci_device::ide_read_cs1), FUNC(ide_pci_device::ide_write_cs1));
}
void ide_pci_device::chan2_data_command_map(address_map &map)
{
map(0x0, 0x7).rw("ide2", FUNC(bus_master_ide_controller_device::cs0_r), FUNC(bus_master_ide_controller_device::cs0_w));
}
void ide_pci_device::chan2_control_map(address_map &map)
{
map(0x0, 0x3).rw(FUNC(ide_pci_device::ide2_read_cs1), FUNC(ide_pci_device::ide2_write_cs1));
}
void ide_pci_device::bus_master_map(address_map &map)
{
map(0x0, 0x7).rw("ide", FUNC(bus_master_ide_controller_device::bmdma_r), FUNC(bus_master_ide_controller_device::bmdma_w));
map(0x8, 0xf).rw("ide2", FUNC(bus_master_ide_controller_device::bmdma_r), FUNC(bus_master_ide_controller_device::bmdma_w));
}
MACHINE_CONFIG_START(ide_pci_device::device_add_mconfig)
MCFG_BUS_MASTER_IDE_CONTROLLER_ADD("ide", ata_devices, "hdd", "cdrom", true)
MCFG_ATA_INTERFACE_IRQ_HANDLER(WRITELINE(*this, ide_pci_device, ide_interrupt))
//MCFG_BUS_MASTER_IDE_CONTROLLER_SPACE(":maincpu", AS_PROGRAM)
MCFG_BUS_MASTER_IDE_CONTROLLER_SPACE(":pci:00.0", AS_DATA)
MCFG_BUS_MASTER_IDE_CONTROLLER_ADD("ide2", ata_devices, "hdd", "cdrom", true)
MCFG_ATA_INTERFACE_IRQ_HANDLER(WRITELINE(*this, ide_pci_device, ide_interrupt))
//MCFG_BUS_MASTER_IDE_CONTROLLER_SPACE(":maincpu", AS_PROGRAM)
MCFG_BUS_MASTER_IDE_CONTROLLER_SPACE(":pci:00.0", AS_DATA)
MACHINE_CONFIG_END
void ide_pci_device::device_start()
{
pci_device::device_start();
add_map(8, M_IO, FUNC(ide_pci_device::chan1_data_command_map));
add_map(4, M_IO, FUNC(ide_pci_device::chan1_control_map));
add_map(8, M_IO, FUNC(ide_pci_device::chan2_data_command_map));
add_map(4, M_IO, FUNC(ide_pci_device::chan2_control_map));
add_map(16, M_IO, FUNC(ide_pci_device::bus_master_map));
// Setup stored BARs
pci_bar[0] = 0x1f0;
pci_bar[1] = 0x3f4;
pci_bar[2] = 0x170;
pci_bar[3] = 0x374;
pci_bar[4] = 0xf00;
m_irq_handler.resolve_safe();
intr_pin = 0x1;
intr_line = 0xe;
// Save states
save_item(NAME(pci_bar));
save_item(NAME(m_config_data));
}
void ide_pci_device::device_reset()
{
pci_device::device_reset();
bank_infos[0].adr = (m_legacy_top << 20) | 0x1f0;
bank_infos[1].adr = (m_legacy_top << 20) | 0x3f4;
bank_infos[2].adr = (m_legacy_top << 20) | 0x170;
bank_infos[3].adr = (m_legacy_top << 20) | 0x374;
bank_infos[4].adr = 0xf00;
pclass = 0x010100 | m_pif;
remap_cb();
// PCI0646U allow BAR
if (main_id == 0x10950646)
m_config_data[0x10 / 4] |= 0x0C40;
m_ide->reset();
m_ide2->reset();
}
READ32_MEMBER(ide_pci_device::ide_read_cs1)
{
// PCI offset starts at 0x3f4, idectrl expects 0x3f0
uint32_t data;
data = m_ide->read_cs1(1, mem_mask);
if (0)
logerror("%s:ide_read_cs1 offset=%08X data=%08X mask=%08X\n", machine().describe_context(), offset, data, mem_mask);
return data;
}
WRITE32_MEMBER(ide_pci_device::ide_write_cs1)
{
// PCI offset starts at 0x3f4, idectrl expects 0x3f0
m_ide->write_cs1(1, data, mem_mask);
}
READ32_MEMBER(ide_pci_device::ide2_read_cs1)
{
// PCI offset starts at 0x374, idectrl expects 0x370
uint32_t data;
data = m_ide2->read_cs1(1, mem_mask);
return data;
}
WRITE32_MEMBER(ide_pci_device::ide2_write_cs1)
{
// PCI offset starts at 0x374, idectrl expects 0x370
m_ide2->write_cs1(1, data, mem_mask);
}
WRITE_LINE_MEMBER(ide_pci_device::ide_interrupt)
{
// Call the callback
m_irq_handler(state);
// PCI646U2 Offset 0x50 is interrupt status
if (main_id == 0x10950646) {
if (state)
m_config_data[0x10 / 4] |= 0x4;
else
m_config_data[0x10 / 4] &= ~0x4;
}
if (0)
logerror("%s:ide_interrupt set to %i\n", machine().describe_context(), state);
}
WRITE8_MEMBER(ide_pci_device::prog_if_w)
{
uint32_t oldVal = pclass;
pclass = (pclass & ~(0xff)) | (data & 0xff);
// Check for switch to/from compatibility (legacy) mode from/to pci mode
if ((oldVal ^ pclass) & 0x5) {
// Map Primary IDE Channel
if (pclass & 0x1) {
// PCI Mode
pci_device::address_base_w(space, 0, pci_bar[0]);
pci_device::address_base_w(space, 1, pci_bar[1]);
} else {
// Legacy Mode
pci_device::address_base_w(space, 0, (m_legacy_top << 20) | 0x1f0);
pci_device::address_base_w(space, 1, (m_legacy_top << 20) | 0x3f4);
}
// Map Primary IDE Channel
if (pclass & 0x4) {
// PCI Mode
pci_device::address_base_w(space, 2, pci_bar[2]);
pci_device::address_base_w(space, 3, pci_bar[3]);
}
else {
// Legacy Mode
pci_device::address_base_w(space, 2, (m_legacy_top << 20) | 0x170);
pci_device::address_base_w(space, 3, (m_legacy_top << 20) | 0x374);
}
}
if (1)
logerror("%s:prog_if_w pclass = %06X\n", machine().describe_context(), pclass);
}
READ32_MEMBER(ide_pci_device::pcictrl_r)
{
return m_config_data[offset];
}
WRITE32_MEMBER(ide_pci_device::pcictrl_w)
{
COMBINE_DATA(&m_config_data[offset]);
// PCI646U2 Offset 0x50 is interrupt status
if (main_id == 0x10950646 && offset == (0x10 / 4) && (data & 0x4)) {
m_config_data[0x10 / 4] &= ~0x4;
if (0)
logerror("%s:ide_pci_device::pcictrl_w Clearing interrupt status\n", machine().describe_context());
}
}
READ32_MEMBER(ide_pci_device::address_base_r)
{
if (bank_reg_infos[offset].bank == -1)
return 0;
int bid = bank_reg_infos[offset].bank;
if (bank_reg_infos[offset].hi)
return bank_infos[bid].adr >> 32;
int flags = bank_infos[bid].flags;
return (pci_bar[offset] & ~(bank_infos[bid].size - 1)) | (flags & M_IO ? 1 : 0) | (flags & M_64A ? 4 : 0) | (flags & M_PREF ? 8 : 0);
}
WRITE32_MEMBER(ide_pci_device::address_base_w)
{
// Save local copy of BAR
pci_bar[offset] = data;
// Bits 0 (primary) and 2 (secondary) control if the mapping is legacy or BAR
switch (offset) {
case 0: case 1:
if (pclass & 0x1)
pci_device::address_base_w(space, offset, data);
break;
case 2: case 3:
if (pclass & 0x4)
pci_device::address_base_w(space, offset, data);
break;
default:
// Only the first 4 bars are controlled by pif
pci_device::address_base_w(space, offset, data);
}
logerror("Mapping bar[%i] = %08x\n", offset, data);
}
WRITE32_MEMBER(ide_pci_device::subsystem_id_w)
{
// Config register 0x4f enables subsystem id writing for CMD646
if (m_config_data[0xc / 4] & 0x01000000)
subsystem_id = (data << 16) | (data >> 16);
}