From a25eeed22850fc1ffcec8c8b95b4030be535b3a2 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 3 Aug 2021 13:03:33 -0300 Subject: [PATCH] Improve VIA 686 Super I/O and hardware monitor to match probed hardware behavior --- src/device/hwm_vt82c686.c | 42 +++++++++++----- src/sio/sio_vt82c686.c | 102 +++++++++++++++++++++++++++----------- 2 files changed, 104 insertions(+), 40 deletions(-) diff --git a/src/device/hwm_vt82c686.c b/src/device/hwm_vt82c686.c index 70a01a6ac..1790cd483 100644 --- a/src/device/hwm_vt82c686.c +++ b/src/device/hwm_vt82c686.c @@ -29,18 +29,17 @@ #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/voltage formulas and factors derived from Linux's via686a.c driver */ +/* Temperature/voltage formulas and factors derived from 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, f) CLAMP((((v) * (2.628 / (f))) - 120.5) / 25, 0, 255) typedef struct { hwm_values_t *values; - device_t *lm75[2]; uint8_t enable; uint16_t io_base; - uint8_t regs[80]; + uint8_t regs[128]; } vt82c686_t; @@ -59,6 +58,11 @@ vt82c686_read(uint16_t addr, void *priv) addr -= dev->io_base; switch (addr) { + case 0x00 ... 0x0f: case 0x50 ... 0x7f: /* undefined registers */ + /* Real 686B returns the contents of 0x40. */ + ret = dev->regs[0x40]; + break; + case 0x1f: case 0x20: case 0x21: /* temperatures */ ret = VT82C686_TEMP_TO_REG(dev->values->temperatures[(addr == 0x1f) ? 2 : (addr & 1)]); break; @@ -84,14 +88,26 @@ 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; + uint8_t reg = port & 0x7f; - if ((reg == 0x41) || (reg == 0x42) || (reg == 0x45) || (reg == 0x46) || (reg == 0x48) || (reg == 0x4a) || (reg >= 0x4c)) - return; + switch (reg) { + case 0x00 ... 0x0f: + case 0x3f: case 0x41: case 0x42: case 0x4a: + case 0x4c ... 0x7f: + /* Read-only registers. */ + return; - if ((reg == 0x40) && (val & 0x80)) { - val &= 0x7f; - vt82c686_reset(dev, 1); + case 0x40: + /* Reset if requested. */ + if (val & 0x80) { + vt82c686_reset(dev, 1); + return; + } + break; + + case 0x48: + val &= 0x7f; + break; } dev->regs[reg] = val; @@ -106,7 +122,7 @@ 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, + io_removehandler(dev->io_base, 128, vt82c686_read, NULL, NULL, vt82c686_write, NULL, NULL, dev); switch (addr) { @@ -126,7 +142,7 @@ vt82c686_hwm_write(uint8_t addr, uint8_t val, void *priv) } if (dev->enable && dev->io_base) - io_sethandler(dev->io_base, 0x0050, + io_sethandler(dev->io_base, 128, vt82c686_read, NULL, NULL, vt82c686_write, NULL, NULL, dev); } @@ -134,8 +150,10 @@ vt82c686_hwm_write(uint8_t addr, uint8_t val, void *priv) static void vt82c686_reset(vt82c686_t *dev, uint8_t initialization) { - memset(dev->regs, 0, 80); + memset(dev->regs, 0, sizeof(dev->regs)); + dev->regs[0x17] = 0x80; + dev->regs[0x3f] = 0xa2; dev->regs[0x40] = 0x08; dev->regs[0x47] = 0x50; dev->regs[0x4b] = 0x15; diff --git a/src/sio/sio_vt82c686.c b/src/sio/sio_vt82c686.c index 3c887fce0..007ae6d93 100644 --- a/src/sio/sio_vt82c686.c +++ b/src/sio/sio_vt82c686.c @@ -34,7 +34,8 @@ typedef struct { - uint8_t cur_reg, regs[32], fdc_dma, fdc_irq, uart_irq[2], lpt_dma, lpt_irq; + uint8_t cur_reg, last_val, regs[25], + fdc_dma, fdc_irq, uart_irq[2], lpt_dma, lpt_irq; fdc_t *fdc; serial_t *uart[2]; } vt82c686_t; @@ -43,10 +44,10 @@ typedef struct { static uint8_t get_lpt_length(vt82c686_t *dev) { - uint8_t length = 4; + uint8_t length = 4; /* non-EPP */ - if ((dev->regs[0x02] & 0x03) == 0x2) - length = 8; + if ((dev->regs[0x02] & 0x03) == 0x02) + length = 8; /* EPP */ return length; } @@ -64,6 +65,7 @@ vt82c686_fdc_handler(vt82c686_t *dev) fdc_set_dma_ch(dev->fdc, dev->fdc_dma); fdc_set_irq(dev->fdc, dev->fdc_irq); + fdc_set_swap(dev->fdc, dev->regs[0x16] & 0x01); } @@ -73,16 +75,20 @@ 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; + io_mask = 0x3fc; /* non-EPP */ if (io_len == 8) - io_mask = 0x3f8; + io_mask = 0x3f8; /* EPP */ lpt1_remove(); if (((dev->regs[0x02] & 0x03) != 0x03) && (io_base >= 0x100) && (io_base <= io_mask)) lpt1_init(io_base); - lpt1_irq(dev->lpt_irq); + if (dev->lpt_irq) { + lpt1_irq(dev->lpt_irq); + } else { + lpt1_irq(0xff); + } } @@ -91,8 +97,8 @@ 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]); + if (dev->regs[0x02] & (0x04 << uart)) + serial_setup(dev->uart[uart], dev->regs[0x07 + uart] << 2, dev->uart_irq[uart]); } @@ -101,25 +107,31 @@ vt82c686_write(uint16_t port, uint8_t val, void *priv) { vt82c686_t *dev = (vt82c686_t *) priv; + /* Store last written value for echo (see comment on read). */ + dev->last_val = val; + + /* Write current register index on port 0. */ if (!(port & 1)) { dev->cur_reg = val; return; } - /* NOTE: Registers are [0xE0:0xFF] but we store them as [0x00:0x1F]. */ - if (dev->cur_reg < 0xe0) - return; + /* NOTE: Registers are [0xE0:0xF8] but we store them as [0x00:0x18]. */ + if ((dev->cur_reg < 0xe0) || (dev->cur_reg > 0xf8)) + return; uint8_t reg = dev->cur_reg & 0x1f; - /* Read-only registers */ - if ((reg < 0x02) || (reg == 0x04) || (reg == 0x05) || ((reg >= 0x09) && (reg < 0x0e)) || - (reg == 0x13) || (reg == 0x15) || (reg == 0x17) || (reg >= 0x19)) + /* Read-only registers. */ + if ((reg < 0x02) || (reg == 0x0c)) return; + /* Write current register value on port 1. */ dev->regs[reg] = val; + /* Update device state. */ switch (reg) { case 0x02: + dev->regs[reg] &= 0xbf; vt82c686_lpt_handler(dev); vt82c686_serial_handler(dev, 0); vt82c686_serial_handler(dev, 1); @@ -127,19 +139,54 @@ vt82c686_write(uint16_t port, uint8_t val, void *priv) break; case 0x03: + dev->regs[reg] &= 0xfc; vt82c686_fdc_handler(dev); break; + case 0x04: + dev->regs[reg] &= 0xfc; + break; + + case 0x05: + dev->regs[reg] |= 0x03; + break; + case 0x06: vt82c686_lpt_handler(dev); break; - case 0x07: - vt82c686_serial_handler(dev, 0); + case 0x07: case 0x08: + dev->regs[reg] &= 0xfe; + vt82c686_serial_handler(dev, reg == 0x08); break; - case 0x08: - vt82c686_serial_handler(dev, 1); + case 0x0d: + dev->regs[reg] &= 0x0f; + break; + + case 0x0f: + dev->regs[reg] &= 0x7f; + break; + + case 0x10: + dev->regs[reg] &= 0xf4; + break; + + case 0x11: + dev->regs[reg] &= 0x3f; + break; + + case 0x13: + dev->regs[reg] &= 0xfb; + break; + + case 0x14: case 0x17: + dev->regs[reg] &= 0xfe; + break; + + case 0x16: + dev->regs[reg] &= 0xf7; + vt82c686_fdc_handler(dev); break; } } @@ -149,17 +196,16 @@ static uint8_t vt82c686_read(uint16_t port, void *priv) { vt82c686_t *dev = (vt82c686_t *) priv; - uint8_t ret = 0xff; - /* NOTE: Registers are [0xE0:0xFF] but we store them as [0x00:0x1F]. */ + /* NOTE: Registers are [0xE0:0xF8] but we store them as [0x00:0x18]. + Real 686B echoes the last read/written value when reading from + registers outside that range. */ if (!(port & 1)) - ret = dev->cur_reg; - else if (dev->cur_reg < 0xe0) - ret = 0xff; - else - ret = dev->regs[dev->cur_reg & 0x1f]; + dev->last_val = dev->cur_reg; + else if ((dev->cur_reg >= 0xe0) && (dev->cur_reg <= 0xf8)) + dev->last_val = dev->regs[dev->cur_reg & 0x1f]; - return ret; + return dev->last_val; } @@ -205,7 +251,7 @@ static void vt82c686_reset(vt82c686_t *dev) { memset(dev->regs, 0, 20); - + dev->regs[0x00] = 0x3c; dev->regs[0x02] = 0x03; dev->regs[0x03] = 0xfc;