Files
86Box/src/chipset/sis_5513_ide.c
2024-05-10 00:29:35 +02:00

506 lines
16 KiB
C

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Implementation of the SiS 5513 IDE controller.
*
* Authors: Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2024 Miran Grca.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/io.h>
#include <86box/timer.h>
#include <86box/dma.h>
#include <86box/mem.h>
#include <86box/nvr.h>
#include <86box/hdd.h>
#include <86box/hdc.h>
#include <86box/hdc_ide.h>
#include <86box/hdc_ide_sff8038i.h>
#include <86box/pci.h>
#include <86box/pic.h>
#include <86box/pit.h>
#include <86box/pit_fast.h>
#include <86box/plat.h>
#include <86box/plat_unused.h>
#include <86box/port_92.h>
#include <86box/smram.h>
#include <86box/spd.h>
#include <86box/apm.h>
#include <86box/ddma.h>
#include <86box/acpi.h>
#include <86box/smbus.h>
#include <86box/sis_55xx.h>
#include <86box/chipset.h>
#ifdef ENABLE_SIS_5513_IDE_LOG
int sis_5513_ide_do_log = ENABLE_SIS_5513_IDE_LOG;
static void
sis_5513_ide_log(const char *fmt, ...)
{
va_list ap;
if (sis_5513_ide_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define sis_5513_ide_log(fmt, ...)
#endif
typedef struct sis_5513_ide_t {
uint8_t rev;
uint8_t pci_conf[256];
sis_55xx_common_t *sis;
} sis_5513_ide_t;
static void
sis_5513_ide_irq_handler(sis_5513_ide_t *dev)
{
if (dev->pci_conf[0x09] & 0x01) {
/* Primary IDE is native. */
sis_5513_ide_log("Primary IDE IRQ mode: Native, Native\n");
sff_set_irq_mode(dev->sis->bm[0], IRQ_MODE_SIS_551X);
} else {
/* Primary IDE is legacy. */
sis_5513_ide_log("Primary IDE IRQ mode: IRQ14, IRQ15\n");
sff_set_irq_mode(dev->sis->bm[0], IRQ_MODE_LEGACY);
}
if (dev->pci_conf[0x09] & 0x04) {
/* Secondary IDE is native. */
sis_5513_ide_log("Secondary IDE IRQ mode: Native, Native\n");
sff_set_irq_mode(dev->sis->bm[1], IRQ_MODE_SIS_551X);
} else {
/* Secondary IDE is legacy. */
sis_5513_ide_log("Secondary IDE IRQ mode: IRQ14, IRQ15\n");
sff_set_irq_mode(dev->sis->bm[1], IRQ_MODE_LEGACY);
}
}
static void
sis_5513_ide_handler(sis_5513_ide_t *dev)
{
uint8_t ide_io_on = dev->pci_conf[0x04] & 0x01;
uint16_t native_base_pri_addr = (dev->pci_conf[0x11] | dev->pci_conf[0x10] << 8) & 0xfffe;
uint16_t native_side_pri_addr = (dev->pci_conf[0x15] | dev->pci_conf[0x14] << 8) & 0xfffe;
uint16_t native_base_sec_addr = (dev->pci_conf[0x19] | dev->pci_conf[0x18] << 8) & 0xfffe;
uint16_t native_side_sec_addr = (dev->pci_conf[0x1c] | dev->pci_conf[0x1b] << 8) & 0xfffe;
uint16_t current_pri_base;
uint16_t current_pri_side;
uint16_t current_sec_base;
uint16_t current_sec_side;
/* Primary Channel Programming */
current_pri_base = (!(dev->pci_conf[0x09] & 1)) ? 0x01f0 : native_base_pri_addr;
current_pri_side = (!(dev->pci_conf[0x09] & 1)) ? 0x03f6 : native_side_pri_addr;
/* Secondary Channel Programming */
current_sec_base = (!(dev->pci_conf[0x09] & 4)) ? 0x0170 : native_base_sec_addr;
current_sec_side = (!(dev->pci_conf[0x09] & 4)) ? 0x0376 : native_side_sec_addr;
sis_5513_ide_log("sis_5513_ide_handler(): Disabling primary IDE...\n");
ide_pri_disable();
sis_5513_ide_log("sis_5513_ide_handler(): Disabling secondary IDE...\n");
ide_sec_disable();
if (ide_io_on) {
/* Primary Channel Setup */
if (dev->pci_conf[0x4a] & 0x02) {
sis_5513_ide_log("sis_5513_ide_handler(): Primary IDE base now %04X...\n", current_pri_base);
ide_set_base(0, current_pri_base);
sis_5513_ide_log("sis_5513_ide_handler(): Primary IDE side now %04X...\n", current_pri_side);
ide_set_side(0, current_pri_side);
sis_5513_ide_log("sis_5513_ide_handler(): Enabling primary IDE...\n");
ide_pri_enable();
sis_5513_ide_log("SiS 5513 PRI: BASE %04x SIDE %04x\n", current_pri_base, current_pri_side);
}
/* Secondary Channel Setup */
if (dev->pci_conf[0x4a] & 0x04) {
sis_5513_ide_log("sis_5513_ide_handler(): Secondary IDE base now %04X...\n", current_sec_base);
ide_set_base(1, current_sec_base);
sis_5513_ide_log("sis_5513_ide_handler(): Secondary IDE side now %04X...\n", current_sec_side);
ide_set_side(1, current_sec_side);
sis_5513_ide_log("sis_5513_ide_handler(): Enabling secondary IDE...\n");
ide_sec_enable();
sis_5513_ide_log("SiS 5513: BASE %04x SIDE %04x\n", current_sec_base, current_sec_side);
}
}
sff_bus_master_handler(dev->sis->bm[0], ide_io_on,
((dev->pci_conf[0x20] & 0xf0) | (dev->pci_conf[0x21] << 8)) + 0);
sff_bus_master_handler(dev->sis->bm[1], ide_io_on,
((dev->pci_conf[0x20] & 0xf0) | (dev->pci_conf[0x21] << 8)) + 8);
}
void
sis_5513_ide_write(int addr, uint8_t val, void *priv)
{
sis_5513_ide_t *dev = (sis_5513_ide_t *) priv;
sis_5513_ide_log("SiS 5513 IDE: [W] dev->pci_conf[%02X] = %02X\n", addr, val);
switch (addr) {
case 0x04: /* Command low byte */
dev->pci_conf[addr] = val & 0x05;
sis_5513_ide_handler(dev);
break;
case 0x06: /* Status low byte */
dev->pci_conf[addr] = val & 0x20;
break;
case 0x07: /* Status high byte */
dev->pci_conf[addr] = (dev->pci_conf[addr] & 0x06) & ~(val & 0x38);
break;
case 0x09: /* Programming Interface Byte */
switch (dev->rev) {
case 0xd0:
if (dev->sis->ide_bits_1_3_writable)
val |= 0x0a;
fallthrough;
case 0x00:
case 0xd1:
val &= 0xbf;
fallthrough;
case 0xc0:
switch (val & 0x0a) {
case 0x00:
dev->pci_conf[addr] = (dev->pci_conf[addr] & 0x85) | (val & 0x4a);
break;
case 0x02:
dev->pci_conf[addr] = (dev->pci_conf[addr] & 0x84) | (val & 0x4b);
break;
case 0x08:
dev->pci_conf[addr] = (dev->pci_conf[addr] & 0x81) | (val & 0x4e);
break;
case 0x0a:
dev->pci_conf[addr] = (dev->pci_conf[addr] & 0x80) | (val & 0x4f);
break;
}
break;
}
sis_5513_ide_irq_handler(dev);
sis_5513_ide_handler(dev);
break;
case 0x0d: /* Latency Timer */
dev->pci_conf[addr] = val;
break;
/* Primary Base Address */
case 0x10 ... 0x11:
case 0x14 ... 0x15:
fallthrough;
/* Secondary Base Address */
case 0x18 ... 0x19:
case 0x1c ... 0x1d:
fallthrough;
/* Bus Mastering Base Address */
case 0x20 ... 0x21:
if (addr == 0x20)
dev->pci_conf[addr] = (val & 0xe0) | 0x01;
else if ((addr & 0x07) == 0x00)
dev->pci_conf[addr] = (val & 0xf8) | 0x01;
else if ((addr & 0x07) == 0x04)
dev->pci_conf[addr] = (val & 0xfc) | 0x01;
else
dev->pci_conf[addr] = val;
sis_5513_ide_handler(dev);
break;
case 0x2c ... 0x2f:
if (dev->rev >= 0xd0)
dev->pci_conf[addr] = val;
break;
case 0x30 ... 0x33: /* Expansion ROM Base Address */
#ifdef DATASHEET
dev->pci_conf[addr] = val;
#else
if (dev->rev == 0x00)
dev->pci_conf[addr] = val;
#endif
break;
case 0x40: /* IDE Primary Channel/Master Drive Data Recovery Time Control */
if (dev->rev >= 0xd0)
dev->pci_conf[addr] = val & 0xcf;
else
dev->pci_conf[addr] = val & 0x0f;
break;
case 0x42: /* IDE Primary Channel/Slave Drive Data Recovery Time Control */
case 0x44: /* IDE Secondary Channel/Master Drive Data Recovery Time Control */
case 0x46: /* IDE Secondary Channel/Slave Drive Data Recovery Time Control */
case 0x48: /* IDE Command Recovery Time Control */
dev->pci_conf[addr] = val & 0x0f;
break;
case 0x41: /* IDE Primary Channel/Master Drive DataActive Time Control */
case 0x43: /* IDE Primary Channel/Slave Drive Data Active Time Control */
case 0x45: /* IDE Secondary Channel/Master Drive Data Active Time Control */
case 0x47: /* IDE Secondary Channel/Slave Drive Data Active Time Control */
if (dev->rev >= 0xd0)
dev->pci_conf[addr] = val & 0xe7;
else
dev->pci_conf[addr] = val & 0x07;
break;
case 0x49: /* IDE Command Active Time Control */
dev->pci_conf[addr] = val & 0x07;
break;
case 0x4a: /* IDE General Control Register 0 */
switch (dev->rev) {
case 0x00:
dev->pci_conf[addr] = val & 0x9e;
break;
case 0xc0:
dev->pci_conf[addr] = val & 0xaf;
break;
case 0xd0:
dev->pci_conf[addr] = val;
break;
}
sis_5513_ide_handler(dev);
break;
case 0x4b: /* IDE General Control Register 1 */
if (dev->rev >= 0xc0)
dev->pci_conf[addr] = val;
else
dev->pci_conf[addr] = val & 0xef;
break;
case 0x4c: /* Prefetch Count of Primary Channel (Low Byte) */
case 0x4d: /* Prefetch Count of Primary Channel (High Byte) */
case 0x4e: /* Prefetch Count of Secondary Channel (Low Byte) */
case 0x4f: /* Prefetch Count of Secondary Channel (High Byte) */
dev->pci_conf[addr] = val;
break;
case 0x50:
case 0x51:
if (dev->rev >= 0xd0)
dev->pci_conf[addr] = val;
break;
case 0x52:
if (dev->rev >= 0xd0)
dev->pci_conf[addr] = val & 0x0f;
break;
default:
break;
}
}
uint8_t
sis_5513_ide_read(int addr, void *priv)
{
const sis_5513_ide_t *dev = (sis_5513_ide_t *) priv;
uint8_t ret = 0xff;
switch (addr) {
default:
ret = dev->pci_conf[addr];
break;
case 0x09:
ret = dev->pci_conf[addr];
if (dev->rev >= 0xc0) {
if (dev->pci_conf[0x09] & 0x40)
ret |= ((dev->pci_conf[0x4a] & 0x06) << 3);
if ((dev->rev == 0xd0) && dev->sis->ide_bits_1_3_writable)
ret |= 0x0a;
}
break;
case 0x3d:
if (dev->rev >= 0xc0)
ret = (dev->pci_conf[0x09] & 0x05) ? PCI_INTA : 0x00;
else
ret = (((dev->pci_conf[0x4b] & 0xc0) == 0xc0) ||
(dev->pci_conf[0x09] & 0x05)) ? PCI_INTA : 0x00;
break;
}
sis_5513_ide_log("SiS 5513 IDE: [R] dev->pci_conf[%02X] = %02X\n", addr, ret);
return ret;
}
static void
sis_5513_ide_reset(void *priv)
{
sis_5513_ide_t *dev = (sis_5513_ide_t *) priv;
dev->pci_conf[0x00] = 0x39;
dev->pci_conf[0x01] = 0x10;
dev->pci_conf[0x02] = 0x13;
dev->pci_conf[0x03] = 0x55;
dev->pci_conf[0x04] = dev->pci_conf[0x05] = 0x00;
dev->pci_conf[0x06] = dev->pci_conf[0x07] = 0x00;
dev->pci_conf[0x08] = (dev->rev == 0xd1) ? 0xd0 : dev->rev;
dev->pci_conf[0x09] = 0x8a;
dev->pci_conf[0x0a] = dev->pci_conf[0x0b] = 0x01;
dev->pci_conf[0x0c] = dev->pci_conf[0x0d] = 0x00;
dev->pci_conf[0x0e] = 0x80;
dev->pci_conf[0x0f] = 0x00;
dev->pci_conf[0x10] = 0xf1;
dev->pci_conf[0x11] = 0x01;
dev->pci_conf[0x14] = 0xf5;
dev->pci_conf[0x15] = 0x03;
dev->pci_conf[0x18] = 0x71;
dev->pci_conf[0x19] = 0x01;
dev->pci_conf[0x1c] = 0x75;
dev->pci_conf[0x1d] = 0x03;
dev->pci_conf[0x20] = 0x01;
dev->pci_conf[0x21] = 0xf0;
dev->pci_conf[0x22] = dev->pci_conf[0x23] = 0x00;
dev->pci_conf[0x24] = dev->pci_conf[0x25] = 0x00;
dev->pci_conf[0x26] = dev->pci_conf[0x27] = 0x00;
dev->pci_conf[0x28] = dev->pci_conf[0x29] = 0x00;
dev->pci_conf[0x2a] = dev->pci_conf[0x2b] = 0x00;
switch (dev->rev) {
case 0x00:
case 0xd0:
case 0xd1:
dev->pci_conf[0x2c] = dev->pci_conf[0x2d] = 0x00;
break;
case 0xc0:
#ifdef DATASHEET
dev->pci_conf[0x2c] = dev->pci_conf[0x2d] = 0x00;
#else
/* The only Linux lspci listing I could find of this chipset,
shows a subsystem of 0058:0000. */
dev->pci_conf[0x2c] = 0x58;
dev->pci_conf[0x2d] = 0x00;
#endif
break;
}
dev->pci_conf[0x2e] = dev->pci_conf[0x2f] = 0x00;
dev->pci_conf[0x30] = dev->pci_conf[0x31] = 0x00;
dev->pci_conf[0x32] = dev->pci_conf[0x33] = 0x00;
dev->pci_conf[0x40] = dev->pci_conf[0x41] = 0x00;
dev->pci_conf[0x42] = dev->pci_conf[0x43] = 0x00;
dev->pci_conf[0x44] = dev->pci_conf[0x45] = 0x00;
dev->pci_conf[0x46] = dev->pci_conf[0x47] = 0x00;
dev->pci_conf[0x48] = dev->pci_conf[0x49] = 0x00;
dev->pci_conf[0x4a] = 0x06;
dev->pci_conf[0x4b] = 0x00;
dev->pci_conf[0x4c] = dev->pci_conf[0x4d] = 0x00;
dev->pci_conf[0x4e] = dev->pci_conf[0x4f] = 0x00;
sis_5513_ide_irq_handler(dev);
sis_5513_ide_handler(dev);
sff_bus_master_reset(dev->sis->bm[0]);
sff_bus_master_reset(dev->sis->bm[1]);
}
static void
sis_5513_ide_close(void *priv)
{
sis_5513_ide_t *dev = (sis_5513_ide_t *) priv;
free(dev);
}
static void *
sis_5513_ide_init(UNUSED(const device_t *info))
{
sis_5513_ide_t *dev = (sis_5513_ide_t *) calloc(1, sizeof(sis_5513_ide_t));
dev->rev = info->local;
dev->sis = device_get_common_priv();
/* SFF IDE */
dev->sis->bm[0] = device_add_inst(&sff8038i_device, 1);
dev->sis->bm[1] = device_add_inst(&sff8038i_device, 2);
sis_5513_ide_reset(dev);
return dev;
}
const device_t sis_5513_ide_device = {
.name = "SiS 5513 IDE controller",
.internal_name = "sis_5513_ide",
.flags = DEVICE_PCI,
.local = 0x00,
.init = sis_5513_ide_init,
.close = sis_5513_ide_close,
.reset = sis_5513_ide_reset,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};
const device_t sis_5572_ide_device = {
.name = "SiS 5572 IDE controller",
.internal_name = "sis_5572_ide",
.flags = DEVICE_PCI,
.local = 0xc0,
.init = sis_5513_ide_init,
.close = sis_5513_ide_close,
.reset = sis_5513_ide_reset,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};
const device_t sis_5582_ide_device = {
.name = "SiS 5582 IDE controller",
.internal_name = "sis_5582_ide",
.flags = DEVICE_PCI,
.local = 0xd0,
.init = sis_5513_ide_init,
.close = sis_5513_ide_close,
.reset = sis_5513_ide_reset,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};
const device_t sis_5591_5600_ide_device = {
.name = "SiS 5591/(5)600 IDE controller",
.internal_name = "sis_5591_5600_ide",
.flags = DEVICE_PCI,
.local = 0xd1, /* D0, but we need to distinguish them. */
.init = sis_5513_ide_init,
.close = sis_5513_ide_close,
.reset = sis_5513_ide_reset,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};