diff --git a/src/chipset/via_pipc.c b/src/chipset/via_pipc.c index 78b2cf0f2..377f26147 100644 --- a/src/chipset/via_pipc.c +++ b/src/chipset/via_pipc.c @@ -51,6 +51,8 @@ #include <86box/machine.h> #include <86box/smbus_piix4.h> #include <86box/chipset.h> +#include <86box/sio.h> +#include <86box/hwm.h> /* Most revision numbers (PCI-ISA bridge or otherwise) were lifted from PCI device @@ -135,7 +137,7 @@ pipc_reset_hard(void *priv) dev->pci_isa_regs[0x4a] = 0x04; dev->pci_isa_regs[0x4f] = 0x03; - dev->pci_isa_regs[0x50] = (dev->local >= VIA_PIPC_686A) ? 0x2d : 0x24; + dev->pci_isa_regs[0x50] = (dev->local >= VIA_PIPC_686A) ? 0x0e : 0x24; /* 686A/B default value does not line up with default bits */ dev->pci_isa_regs[0x59] = 0x04; if (dev->local >= VIA_PIPC_686A) dev->pci_isa_regs[0x5a] = dev->pci_isa_regs[0x5f] = 0x04; @@ -180,8 +182,7 @@ pipc_reset_hard(void *priv) dev->ide_regs[0x4c] = 0xff; dev->ide_regs[0x4e] = 0xff; dev->ide_regs[0x4f] = 0xff; - dev->ide_regs[0x50] = 0x03; dev->ide_regs[0x51] = 0x03; - dev->ide_regs[0x52] = 0x03; dev->ide_regs[0x53] = 0x03; + dev->ide_regs[0x50] = dev->ide_regs[0x51] = dev->ide_regs[0x52] = dev->ide_regs[0x53] = (dev->local >= VIA_PIPC_686A) ? 0x07 : 0x03; if (dev->local >= VIA_PIPC_596A) dev->ide_regs[0x54] = 0x06; @@ -271,6 +272,9 @@ pipc_reset_hard(void *priv) dev->power_regs[0x42] = 0x40; /* external suspend-related pin, must be set */ dev->power_regs[0x48] = 0x01; + if (dev->local >= VIA_PIPC_686A) + dev->power_regs[0x70] = 0x01; + if (dev->local == VIA_PIPC_596A) dev->power_regs[0x80] = 0x01; else if (dev->local >= VIA_PIPC_596B) @@ -411,8 +415,14 @@ pipc_read(int func, int addr, void *priv) ret = dev->ide_regs[addr]; else if ((func < pm_func) && !((func == 2) ? (dev->pci_isa_regs[0x48] & 0x04) : (dev->pci_isa_regs[0x85] & 0x10))) /* USB */ ret = dev->usb_regs[func - 2][addr]; - else if (func == pm_func) /* Power */ + else if (func == pm_func) { /* Power */ ret = dev->power_regs[addr]; + if (addr == 0x42) { + ret &= ~0x10; + if (dev->nvr->regs[0x0d] & 0x80) + ret |= 0x10; + } + } else if ((func <= (pm_func + 2)) && !(dev->pci_isa_regs[0x85] & ((func == (pm_func + 1)) ? 0x04 : 0x08))) /* AC97 / MC97 */ ret = dev->ac97_regs[func - pm_func - 1][addr]; @@ -446,6 +456,7 @@ pipc_write(int func, int addr, uint8_t val, void *priv) pipc_t *dev = (pipc_t *) priv; int c; uint8_t pm_func = dev->usb[1] ? 4 : 3; + void *subdev; if (func > dev->max_func) return; @@ -488,6 +499,13 @@ pipc_write(int func, int addr, uint8_t val, void *priv) nvr_update_io_mapping(dev); break; + case 0x50: case 0x51: case 0x52: case 0x85: + dev->pci_isa_regs[addr] = val; + /* Forward Super I/O-related registers to sio_vt82c686.c */ + if ((subdev = device_get_priv(&via_vt82c686_sio_device))) + vt82c686_sio_write(addr, val, subdev); + break; + case 0x54: pci_set_irq_level(PCI_INTA, !(val & 8)); pci_set_irq_level(PCI_INTB, !(val & 4)); @@ -551,10 +569,6 @@ pipc_write(int func, int addr, uint8_t val, void *priv) dev->pci_isa_regs[addr] &= ~(val); break; - case 0x85: - dev->pci_isa_regs[addr] = val; - break; - default: dev->pci_isa_regs[addr] = val; break; @@ -707,10 +721,21 @@ pipc_write(int func, int addr, uint8_t val, void *priv) acpi_update_io_mapping(dev->acpi, c, dev->power_regs[0x41] & 0x80); break; + case 0x42: + dev->power_regs[addr] = val & 0x0f; + break; + case 0x61: case 0x62: case 0x63: dev->power_regs[(addr - 0x58)] = val; break; + case 0x70: case 0x71: case 0x74: + dev->power_regs[addr] = val; + /* Forward hardware monitor-related registers to hwm_vt82c686.c */ + if ((subdev = device_get_priv(&via_vt82c686_hwm_device))) + vt82c686_hwm_write(addr, val, subdev); + break; + case 0x80: case 0x81: case 0x84: /* 596(A) has the SMBus I/O base here instead. Enable bit is assumed. */ dev->power_regs[addr] = val; smbus_piix4_remap(dev->smbus, (dev->power_regs[0x81] << 8) | (dev->power_regs[0x80] & 0xf0), dev->power_regs[0x84] & 0x01); @@ -804,14 +829,15 @@ pipc_init(const device_t *info) return dev; } + static void pipc_close(void *p) { - pipc_t *pipc = (pipc_t *) p; + pipc_t *dev = (pipc_t *) p; pipc_log("PIPC: close()\n"); - free(pipc); + free(dev); } @@ -857,3 +883,33 @@ const device_t via_vt82c596b_device = NULL, NULL }; + + +const device_t via_vt82c686a_device = +{ + "VIA VT82C686A", + DEVICE_PCI, + VIA_PIPC_686A, + pipc_init, + pipc_close, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + +const device_t via_vt82c686b_device = +{ + "VIA VT82C686B", + DEVICE_PCI, + VIA_PIPC_686B, + pipc_init, + pipc_close, + NULL, + NULL, + NULL, + NULL, + NULL +}; diff --git a/src/device/hwm_vt82c686.c b/src/device/hwm_vt82c686.c new file mode 100644 index 000000000..475162eef --- /dev/null +++ b/src/device/hwm_vt82c686.c @@ -0,0 +1,180 @@ +/* + * 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. + * + * Emulation of the VIA VT82C686A/B integrated hardware monitor. + * + * + * + * Author: RichardG, + * + * Copyright 2020 RichardG. + */ +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/io.h> +#include "cpu.h" +#include <86box/smbus.h> +#include <86box/hwm.h> + + +#define CLAMP(a, min, max) (((a) < (min)) ? (min) : (((a) > (max)) ? (max) : (a))) +#define VT82C686_RPM_TO_REG(r, d) ((r) ? CLAMP(1350000 / (r * d), 1, 255) : 0) +/* Temperature formula from source comments in Linux's via686a.c driver */ +#define VT82C686_TEMP_TO_REG(t) (-1.160370e-10*(t*t*t*t*t*t) + 3.193693e-08*(t*t*t*t*t) - 1.464447e-06*(t*t*t*t) - 2.525453e-04*(t*t*t) + 1.424593e-02*(t*t) + 2.148941e+00*t + 7.275808e+01) +#define VT82C686_VOLTAGE_TO_REG(v) ((v) >> 4) + + +typedef struct { + hwm_values_t *values; + device_t *lm75[2]; + + uint8_t enable; + uint16_t io_base; + uint8_t regs[80]; +} vt82c686_t; + + +static void vt82c686_reset(vt82c686_t *dev, uint8_t initialization); + + +static uint8_t +vt82c686_read(uint16_t addr, void *priv) +{ + vt82c686_t *dev = (vt82c686_t *) priv; + return dev->regs[addr - dev->io_base]; +} + + +static void +vt82c686_write(uint16_t port, uint8_t val, void *priv) +{ + vt82c686_t *dev = (vt82c686_t *) priv; + uint8_t reg = port - dev->io_base; + + if ((reg == 0x41) || (reg == 0x42) || (reg == 0x45) || (reg == 0x46) || (reg == 0x48) || (reg == 0x4a) || (reg >= 0x4c)) + return; + + switch (reg) { + case 0x40: + if (val & 0x80) + vt82c686_reset(dev, 1); + break; + + case 0x47: + val &= 0xf0; + /* update FAN1/FAN2 values to match the new divisor */ + dev->regs[0x29] = VT82C686_RPM_TO_REG(dev->values->fans[0], 1 << ((dev->regs[0x47] >> 4) & 0x3)); + dev->regs[0x2a] = VT82C686_RPM_TO_REG(dev->values->fans[1], 1 << ((dev->regs[0x47] >> 6) & 0x3)); + break; + } + + dev->regs[reg] = val; +} + + +/* Writes to hardware monitor-related configuration space registers + of the VT82C686 power management function are sent here by via_pipc.c */ +void +vt82c686_hwm_write(uint8_t addr, uint8_t val, void *priv) +{ + vt82c686_t *dev = (vt82c686_t *) priv; + + if (dev->io_base) + io_removehandler(dev->io_base, 0x0050, + vt82c686_read, NULL, NULL, vt82c686_write, NULL, NULL, dev); + + switch (addr) { + case 0x70: + dev->io_base &= 0xff00; + dev->io_base |= val & 0x80; + break; + + case 0x71: + dev->io_base &= 0x00ff; + dev->io_base |= val << 8; + break; + + case 0x74: + dev->enable = val & 0x01; + break; + } + + if (dev->enable && dev->io_base) + io_sethandler(dev->io_base, 0x0050, + vt82c686_read, NULL, NULL, vt82c686_write, NULL, NULL, dev); +} + + +static void +vt82c686_reset(vt82c686_t *dev, uint8_t initialization) +{ + memset(dev->regs, 0, 80); + + dev->regs[0x1f] = VT82C686_TEMP_TO_REG(dev->values->temperatures[2]); + dev->regs[0x20] = VT82C686_TEMP_TO_REG(dev->values->temperatures[0]); + dev->regs[0x21] = VT82C686_TEMP_TO_REG(dev->values->temperatures[1]); + + dev->regs[0x22] = VT82C686_VOLTAGE_TO_REG(dev->values->voltages[0]); + dev->regs[0x23] = VT82C686_VOLTAGE_TO_REG(dev->values->voltages[1]); + dev->regs[0x24] = VT82C686_VOLTAGE_TO_REG(dev->values->voltages[2]); + dev->regs[0x25] = VT82C686_VOLTAGE_TO_REG(dev->values->voltages[3]); + dev->regs[0x26] = VT82C686_VOLTAGE_TO_REG(dev->values->voltages[4]); + + dev->regs[0x29] = VT82C686_RPM_TO_REG(dev->values->fans[0], 2); + dev->regs[0x2a] = VT82C686_RPM_TO_REG(dev->values->fans[1], 2); + + dev->regs[0x40] = 0x08; + dev->regs[0x47] = 0x50; + dev->regs[0x49] = (dev->values->temperatures[2] & 0x3) << 6; + dev->regs[0x49] |= (dev->values->temperatures[1] & 0x3) << 4; + dev->regs[0x4b] = (dev->values->temperatures[0] & 0x3) << 6; + dev->regs[0x4b] |= 0x15; + + if (!initialization) + vt82c686_hwm_write(0x85, 0x00, dev); +} + + +static void +vt82c686_close(void *priv) +{ + vt82c686_t *dev = (vt82c686_t *) priv; + + free(dev); +} + + +static void * +vt82c686_init(const device_t *info) +{ + vt82c686_t *dev = (vt82c686_t *) malloc(sizeof(vt82c686_t)); + memset(dev, 0, sizeof(vt82c686_t)); + + dev->values = hwm_get_values(); + + vt82c686_reset(dev, 0); + + return dev; +} + + +const device_t via_vt82c686_hwm_device = { + "VIA VT82C686 Integrated Hardware Monitor", + DEVICE_ISA, + 0, + vt82c686_init, vt82c686_close, NULL, + NULL, NULL, NULL, + NULL +}; diff --git a/src/include/86box/chipset.h b/src/include/86box/chipset.h index 2d263740f..604d3c073 100644 --- a/src/include/86box/chipset.h +++ b/src/include/86box/chipset.h @@ -123,6 +123,8 @@ extern const device_t via_apro_device; extern const device_t via_vt82c586b_device; extern const device_t via_vt82c596_device; extern const device_t via_vt82c596b_device; +extern const device_t via_vt82c686a_device; +extern const device_t via_vt82c686b_device; /* VLSI */ extern const device_t vl82c480_device; diff --git a/src/include/86box/hwm.h b/src/include/86box/hwm.h index 53d69500b..70d835f01 100644 --- a/src/include/86box/hwm.h +++ b/src/include/86box/hwm.h @@ -47,6 +47,8 @@ extern void lm75_remap(lm75_t *dev, uint8_t addr); extern uint8_t lm75_read(lm75_t *dev, uint8_t reg); extern uint8_t lm75_write(lm75_t *dev, uint8_t reg, uint8_t val); +extern void vt82c686_hwm_write(uint8_t addr, uint8_t val, void *priv); + extern const device_t lm75_1_4a_device; extern const device_t lm75_w83781d_device; @@ -59,5 +61,7 @@ extern const device_t as99127f_rev2_device; extern const device_t gl518sm_2c_device; extern const device_t gl518sm_2d_device; +extern const device_t via_vt82c686_hwm_device; + #endif /*EMU_HWM_H*/ diff --git a/src/include/86box/sio.h b/src/include/86box/sio.h index ee155f3fa..51fe2f19e 100644 --- a/src/include/86box/sio.h +++ b/src/include/86box/sio.h @@ -15,6 +15,9 @@ # define EMU_SIO_H +extern void vt82c686_sio_write(uint8_t addr, uint8_t val, void *priv); + + extern const device_t acc3221_device; extern const device_t f82c710_device; extern const device_t fdc37c661_device; @@ -39,6 +42,7 @@ extern const device_t pc97307_device; extern const device_t ps1_m2133_sio; extern const device_t sio_detect_device; extern const device_t um8669f_device; +extern const device_t via_vt82c686_sio_device; extern const device_t w83787f_device; extern const device_t w83877f_device; extern const device_t w83877f_president_device; diff --git a/src/sio/sio_vt82c686.c b/src/sio/sio_vt82c686.c new file mode 100644 index 000000000..c38dc692c --- /dev/null +++ b/src/sio/sio_vt82c686.c @@ -0,0 +1,272 @@ +/* + * 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. + * + * Emulation of the VIA VT82C686A/B integrated Super I/O. + * + * + * + * Author: RichardG, + * Copyright 2020 RichardG. + */ +#include +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/io.h> +#include <86box/timer.h> +#include <86box/pci.h> +#include <86box/mem.h> +#include <86box/rom.h> +#include <86box/lpt.h> +#include <86box/serial.h> +#include <86box/fdd.h> +#include <86box/fdc.h> +#include <86box/sio.h> + + +typedef struct { + uint8_t config_enable, cur_reg, regs[32], + fdc_dma, fdc_irq, uart_irq[2], lpt_dma, lpt_irq; + fdc_t *fdc; + serial_t *uart[2]; +} vt82c686_t; + + +static uint8_t +get_lpt_length(vt82c686_t *dev) +{ + uint8_t length = 4; + + if ((dev->regs[0x02] & 0x03) == 0x2) + length = 8; + + return length; +} + + +static void +vt82c686_fdc_handler(vt82c686_t *dev) +{ + uint16_t io_base = (dev->regs[0x03] & 0xfc) << 2; + + fdc_remove(dev->fdc); + + if (dev->regs[0x02] & 0x10) + fdc_set_base(dev->fdc, io_base); + + fdc_set_irq(dev->fdc, dev->fdc_irq); +} + + +static void +vt82c686_lpt_handler(vt82c686_t *dev) +{ + uint16_t io_mask, io_base = dev->regs[0x06] << 2; + int io_len = get_lpt_length(dev); + io_base &= (0xff8 | io_len); + io_mask = 0x3fc; + if (io_len == 8) + io_mask = 0x3f8; + + lpt1_remove(); + + if (((dev->regs[0x02] & 0x03) != 0x03) && (io_base >= 0x100) && (io_base <= io_mask)) + lpt1_init(io_base); + + lpt1_irq(dev->lpt_irq); +} + + +static void +vt82c686_serial_handler(vt82c686_t *dev, int uart) +{ + serial_remove(dev->uart[uart]); + + if (dev->regs[0x02] & (uart ? 0x08 : 0x04)) + serial_setup(dev->uart[uart], (dev->regs[0x07 + uart] & 0xfe) << 2, dev->uart_irq[uart]); +} + + +static void +vt82c686_write(uint16_t port, uint8_t val, void *priv) +{ + vt82c686_t *dev = (vt82c686_t *) priv; + + if (!dev->config_enable) + return; + + if (!(port & 1)) { + /* Registers start at 0xE0 but we cut them down to start at 0x00. */ + dev->cur_reg = (val & 0x1f); + return; + } + + /* Read-only registers */ + if ((dev->cur_reg < 0x02) || (dev->cur_reg == 0x04) || (dev->cur_reg == 0x05) || ((dev->cur_reg >= 0xe9) && (dev->cur_reg < 0xee)) || + (dev->cur_reg == 0xf3) || (dev->cur_reg == 0xf5) || (dev->cur_reg == 0xf7) || (dev->cur_reg >= 0xf9)) + return; + + switch (dev->cur_reg) { + case 0x02: + dev->regs[dev->cur_reg] = val; + vt82c686_lpt_handler(dev); + vt82c686_serial_handler(dev, 0); + vt82c686_serial_handler(dev, 1); + vt82c686_fdc_handler(dev); + break; + + case 0x03: + dev->regs[dev->cur_reg] = val; + vt82c686_fdc_handler(dev); + break; + + case 0x06: + dev->regs[dev->cur_reg] = val; + vt82c686_lpt_handler(dev); + break; + + case 0x07: + dev->regs[dev->cur_reg] = val; + vt82c686_serial_handler(dev, 0); + break; + + case 0x08: + dev->regs[dev->cur_reg] = val; + vt82c686_serial_handler(dev, 1); + break; + + default: + dev->regs[dev->cur_reg] = val; + break; + } +} + + +static uint8_t +vt82c686_read(uint16_t port, void *priv) +{ + vt82c686_t *dev = (vt82c686_t *) priv; + uint8_t ret = 0xff; + + if (!(port & 1)) + ret = dev->cur_reg | 0xe0; + else + ret = dev->regs[dev->cur_reg]; + + return ret; +} + + +/* Writes to Super I/O-related configuration space registers + of the VT82C686 PCI-ISA bridge are sent here by via_pipc.c */ +void +vt82c686_sio_write(uint8_t addr, uint8_t val, void *priv) +{ + vt82c686_t *dev = (vt82c686_t *) priv; + + switch (addr) { + case 0x50: + dev->fdc_dma = val & 0x03; + vt82c686_fdc_handler(dev); + dev->lpt_dma = (val >> 2) & 0x03; + vt82c686_lpt_handler(dev); + break; + + case 0x51: + dev->fdc_irq = val & 0x0f; + vt82c686_fdc_handler(dev); + dev->lpt_irq = val >> 4; + vt82c686_lpt_handler(dev); + break; + + case 0x52: + dev->uart_irq[0] = val & 0x0f; + vt82c686_serial_handler(dev, 0); + dev->uart_irq[1] = val >> 4; + vt82c686_serial_handler(dev, 1); + break; + + case 0x85: + io_removehandler(0x3f0, 0x0002, + vt82c686_read, NULL, NULL, vt82c686_write, NULL, NULL, dev); + if (val & 0x01) + io_sethandler(0x3f0, 0x0002, + vt82c686_read, NULL, NULL, vt82c686_write, NULL, NULL, dev); + + dev->config_enable = val & 0x02; + break; + } +} + + +static void +vt82c686_reset(vt82c686_t *dev) +{ + memset(dev->regs, 0, 20); + + dev->regs[0x00] = 0x3c; + dev->regs[0x02] = 0x03; + dev->regs[0x03] = 0xfc; + dev->regs[0x06] = 0xde; + dev->regs[0x07] = 0xfe; + dev->regs[0x08] = 0xbe; + + fdc_reset(dev->fdc); + + serial_setup(dev->uart[0], SERIAL1_ADDR, SERIAL1_IRQ); + serial_setup(dev->uart[1], SERIAL2_ADDR, SERIAL2_IRQ); + + vt82c686_lpt_handler(dev); + vt82c686_serial_handler(dev, 0); + vt82c686_serial_handler(dev, 1); + vt82c686_fdc_handler(dev); + + vt82c686_sio_write(0x85, 0x00, dev); +} + + +static void +vt82c686_close(void *priv) +{ + vt82c686_t *dev = (vt82c686_t *) priv; + + free(dev); +} + + +static void * +vt82c686_init(const device_t *info) +{ + vt82c686_t *dev = (vt82c686_t *) malloc(sizeof(vt82c686_t)); + memset(dev, 0, sizeof(vt82c686_t)); + + dev->fdc = device_add(&fdc_at_smc_device); + dev->fdc_dma = 2; + + dev->uart[0] = device_add_inst(&ns16550_device, 1); + dev->uart[1] = device_add_inst(&ns16550_device, 2); + + dev->lpt_dma = 3; + + vt82c686_reset(dev); + + return dev; +} + + +const device_t via_vt82c686_sio_device = { + "VIA VT82C686 Integrated Super I/O", + 0, + 0, + vt82c686_init, vt82c686_close, NULL, + NULL, NULL, NULL, + NULL +}; diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 0e2fd0cbe..97cdf15ac 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -660,7 +660,7 @@ MCHOBJ := machine.o machine_table.o \ m_at_socket8.o m_at_slot1.o m_at_slot2.o m_at_socket370.o \ m_at_misc.o -DEVOBJ := bugger.o hwm.o hwm_lm75.o hwm_lm78.o hwm_gl518sm.o ibm_5161.o isamem.o isartc.o lpt.o pci_bridge.o postcard.o serial.o vpc2007.o \ +DEVOBJ := bugger.o hwm.o hwm_lm75.o hwm_lm78.o hwm_gl518sm.o hwm_vt82c686.o ibm_5161.o isamem.o isartc.o lpt.o pci_bridge.o postcard.o serial.o vpc2007.o \ smbus.o smbus_piix4.o \ keyboard.o \ keyboard_xt.o keyboard_at.o \ @@ -675,7 +675,8 @@ SIOOBJ := sio_acc3221.o \ sio_pc87306.o sio_pc87307.o sio_pc87309.o sio_pc87332.o \ sio_w83787f.o \ sio_w83877f.o sio_w83977f.o \ - sio_um8669f.o + sio_um8669f.o \ + sio_vt82c686.o FDDOBJ := fdd.o fdc.o fdc_pii15xb.o \ fdi2raw.o \