diff --git a/src/hwm.c b/src/hwm.c new file mode 100644 index 000000000..b3142c7a4 --- /dev/null +++ b/src/hwm.c @@ -0,0 +1,36 @@ +/* + * 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. + * + * Common functions for hardware monitor chips. + * + * Version: @(#)hwm.c 1.0.0 2020/03/22 + * + * Author: RichardG, + * Copyright 2020 RichardG. + */ + +#include +#include "device.h" +#include "hwm.h" + + +hwm_values_t hwm_values; + + +void +hwm_set_values(hwm_values_t new_values) +{ + hwm_values = new_values; +} + + +hwm_values_t* +hwm_get_values() +{ + return &hwm_values; +} diff --git a/src/hwm.h b/src/hwm.h new file mode 100644 index 000000000..c5d482fb7 --- /dev/null +++ b/src/hwm.h @@ -0,0 +1,38 @@ +/* + * 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. + * + * Definitions for the hardware monitor chips. + * + * Version: @(#)hwm.h 1.0.0 2020/03/21 + * + * Author: RichardG, + * Copyright 2020 RichardG. + */ +#ifndef EMU_HWM_H +# define EMU_HWM_H + + +#define VDIV(v, r1, r2) (((v) * (r2)) / ((r1) + (r2))) + + +typedef struct _hwm_values_ { + uint16_t fans[4]; + uint8_t temperatures[4]; + uint8_t voltages[8]; +} hwm_values_t; + + +extern void hwm_set_values(hwm_values_t new_values); +extern hwm_values_t* hwm_get_values(); + + +extern const device_t w83781d_device; +extern const device_t as99127f_device; + + +#endif /*EMU_HWM_H*/ diff --git a/src/hwm_w83781d.c b/src/hwm_w83781d.c new file mode 100644 index 000000000..1f6b3ab26 --- /dev/null +++ b/src/hwm_w83781d.c @@ -0,0 +1,482 @@ +/* + * 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 Winbond W83781D hardware monitoring chip. + * + * Version: @(#)hwm_w83781d.c 1.0.0 2020/03/21 + * + * Author: RichardG, + * Copyright 2020 RichardG. + */ +#include +#include +#include +#include +#include +#include "86box.h" +#include "device.h" +#include "86box_io.h" +#include "smbus.h" +#include "hwm.h" + + +#define W83781D_SMBUS 0x10000 +#define W83781D_AS99127F 0x20000 +#define W83781D_VENDOR_ID ((dev->local & W83781D_AS99127F) ? 0x12C3 : 0x5CA3) + +#define CLAMP(a, min, max) (((a) < (min)) ? (min) : (((a) > (max)) ? (max) : (a))) +#define W83781D_RPM_TO_REG(r, d) CLAMP(1350000 / (r * d), 1, 255) +#define W83781D_TEMP_TO_REG(t) ((t) * 8) << 5 + + +typedef struct { + uint32_t local; + hwm_values_t* values; + + uint8_t regs[64]; + uint8_t regs_bank1[6]; + uint8_t regs_bank2[6]; + uint8_t addr_register; + uint8_t data_register; + + uint8_t smbus_addr_main; + uint8_t smbus_addr_temp2; + uint8_t smbus_addr_temp3; + uint8_t hbacs; + uint8_t active_bank; +} w83781d_t; + + +static uint8_t w83781d_isa_read(uint16_t port, void *priv); +static uint8_t w83781d_smbus_read_byte(uint8_t addr, void *priv); +static uint8_t w83781d_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv); +static uint16_t w83781d_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv); +static uint8_t w83781d_read(w83781d_t *dev, uint8_t reg, uint8_t bank); +static void w83781d_isa_write(uint16_t port, uint8_t val, void *priv); +static void w83781d_smbus_write_byte(uint8_t addr, uint8_t val, void *priv); +static void w83781d_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv); +static void w83781d_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv); +static uint8_t w83781d_write(w83781d_t *dev, uint8_t reg, uint8_t val, uint8_t bank); +static void w83781d_reset(w83781d_t *dev, uint8_t initialization); + + +static void +w83781d_remap(w83781d_t *dev) +{ + if (!(dev->local & W83781D_SMBUS)) return; + + smbus_removehandler(0x00, 0x80, + w83781d_smbus_read_byte, w83781d_smbus_read_byte_cmd, w83781d_smbus_read_word_cmd, NULL, + w83781d_smbus_write_byte, w83781d_smbus_write_byte_cmd, w83781d_smbus_write_word_cmd, NULL, + dev); + + if (dev->smbus_addr_main) smbus_sethandler(dev->smbus_addr_main, 1, + w83781d_smbus_read_byte, w83781d_smbus_read_byte_cmd, w83781d_smbus_read_word_cmd, NULL, + w83781d_smbus_write_byte, w83781d_smbus_write_byte_cmd, w83781d_smbus_write_word_cmd, NULL, + dev); + + if (dev->smbus_addr_temp2) smbus_sethandler(dev->smbus_addr_temp2, 1, + w83781d_smbus_read_byte, w83781d_smbus_read_byte_cmd, w83781d_smbus_read_word_cmd, NULL, + w83781d_smbus_write_byte, w83781d_smbus_write_byte_cmd, w83781d_smbus_write_word_cmd, NULL, + dev); + + if (dev->smbus_addr_temp3) smbus_sethandler(dev->smbus_addr_temp3, 1, + w83781d_smbus_read_byte, w83781d_smbus_read_byte_cmd, w83781d_smbus_read_word_cmd, NULL, + w83781d_smbus_write_byte, w83781d_smbus_write_byte_cmd, w83781d_smbus_write_word_cmd, NULL, + dev); +} + + +static uint8_t +w83781d_isa_read(uint16_t port, void *priv) +{ + w83781d_t *dev = (w83781d_t *) priv; + uint8_t ret = 0xFF; + + switch (port - (dev->local & 0xFFFF)) { + case 0x0: + ret = dev->addr_register & 0x7F; + break; + case 0x1: + ret = w83781d_read(dev, dev->addr_register, dev->active_bank); + + if (dev->active_bank == 0 && + (dev->addr_register == 0x41 || dev->addr_register == 0x43 || dev->addr_register == 0x45 || dev->addr_register == 0x56 || + (dev->addr_register >= 0x60 && dev->addr_register < 0x7F))) { + /* auto-increment registers */ + dev->addr_register++; + } + break; + } + + return ret; +} + + +static uint8_t +w83781d_smbus_read_byte(uint8_t addr, void *priv) +{ + w83781d_t *dev = (w83781d_t *) priv; + + return w83781d_read(dev, dev->addr_register, 0); +} + + +static uint8_t +w83781d_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv) +{ + w83781d_t *dev = (w83781d_t *) priv; + + return w83781d_read(dev, cmd, 0); +} + + +static uint16_t +w83781d_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv) +{ + w83781d_t *dev = (w83781d_t *) priv; + uint8_t rethi = 0; + uint8_t retlo = 0; + uint8_t bank = 0; + + if (addr == dev->smbus_addr_temp2 || addr == dev->smbus_addr_temp3) { + if (addr == dev->smbus_addr_temp2) + bank = 2; + else + bank = 3; + + switch (cmd & 0x3) { + case 0x0: + rethi = w83781d_read(dev, 0x50, bank); + retlo = w83781d_read(dev, 0x51, bank); + break; + case 0x1: + rethi = retlo = w83781d_read(dev, 0x52, bank); + break; + case 0x2: + rethi = w83781d_read(dev, 0x53, bank); + retlo = w83781d_read(dev, 0x54, bank); + break; + case 0x3: + rethi = w83781d_read(dev, 0x55, bank); + retlo = w83781d_read(dev, 0x56, bank); + break; + } + + return (retlo << 8) | rethi; /* byte-swapped for some reason */ + } + + return w83781d_read(dev, cmd, bank); +} + + +static uint8_t +w83781d_read(w83781d_t *dev, uint8_t reg, uint8_t bank) +{ + uint8_t ret = 0; + + if ((reg >> 4) == 0x5 && bank != 0) { + /* bank-switched temperature registers */ + if (bank == 1) + ret = dev->regs_bank1[reg - 0x50]; + else + ret = dev->regs_bank2[reg - 0x50]; + } else { + /* regular registers */ + if (reg == 0x4F) /* special case for two-byte vendor ID register */ + ret = dev->hbacs ? (W83781D_VENDOR_ID >> 8) : (W83781D_VENDOR_ID & 0xFF); + else if (reg >= 0x60) /* read auto-increment value RAM registers from their non-auto-increment locations */ + ret = dev->regs[reg - 0x40]; + else + ret = dev->regs[reg - 0x20]; + } + + return ret; +} + + +static void +w83781d_isa_write(uint16_t port, uint8_t val, void *priv) +{ + w83781d_t *dev = (w83781d_t *) priv; + + switch (port - (dev->local & 0xFFFF)) { + case 0x0: + dev->addr_register = val & 0x7F; + break; + case 0x1: + w83781d_write(dev, dev->addr_register, val, dev->active_bank); + + if (dev->active_bank == 0 && + (dev->addr_register == 0x41 || dev->addr_register == 0x43 || dev->addr_register == 0x45 || dev->addr_register == 0x56 || + (dev->addr_register >= 0x60 && dev->addr_register < 0x7F))) { + /* auto-increment registers */ + dev->addr_register++; + } + break; + } +} + + +static void +w83781d_smbus_write_byte(uint8_t addr, uint8_t val, void *priv) +{ + w83781d_t *dev = (w83781d_t *) priv; + + dev->addr_register = val; +} + + +static void +w83781d_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv) +{ + w83781d_t *dev = (w83781d_t *) priv; + + w83781d_write(dev, cmd, val, 0); +} + + +static void +w83781d_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv) +{ + w83781d_t *dev = (w83781d_t *) priv; + uint8_t valhi = (val >> 8); + uint8_t vallo = (val & 0xFF); + uint8_t bank = 0; + + if (addr == dev->smbus_addr_temp2 || addr == dev->smbus_addr_temp3) { + if (addr == dev->smbus_addr_temp2) + bank = 2; + else + bank = 3; + + switch (cmd & 0x3) { + case 0x0: + w83781d_write(dev, 0x50, valhi, bank); + w83781d_write(dev, 0x51, vallo, bank); + break; + case 0x1: + w83781d_write(dev, 0x52, vallo, bank); + break; + case 0x2: + w83781d_write(dev, 0x53, valhi, bank); + w83781d_write(dev, 0x54, vallo, bank); + break; + case 0x3: + w83781d_write(dev, 0x55, valhi, bank); + w83781d_write(dev, 0x56, vallo, bank); + break; + break; + } + return; + } + + w83781d_write(dev, cmd, vallo, bank); +} + + +static uint8_t +w83781d_write(w83781d_t *dev, uint8_t reg, uint8_t val, uint8_t bank) +{ + uint8_t remap = 0; + + if ((reg >> 4) == 0x5 && bank != 0) { + /* bank-switched temperature registers */ + switch (reg) { + case 0x50: case 0x51: + /* read-only registers */ + return 0; + } + + if (bank == 1) + dev->regs_bank1[reg - 0x50] = val; + else + dev->regs_bank2[reg - 0x50] = val; + + return 1; + } + + /* regular registers */ + switch (reg) { + case 0x41: case 0x42: case 0x4F: case 0x58: + case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2A: + case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A: + /* read-only registers */ + return 0; + } + + if (reg >= 0x60) /* write auto-increment value RAM registers to their non-auto-increment locations */ + dev->regs[reg - 0x40] = val; + else + dev->regs[reg - 0x20] = val; + + switch (reg) { + case 0x40: + if (val >> 7) { + /* INITIALIZATION bit set: reset all registers except main SMBus address */ + w83781d_reset(dev, 1); + } + break; + case 0x47: + /* update FAN1/FAN2 values to match the new divisor */ + dev->regs[0x08] = W83781D_RPM_TO_REG(dev->values->fans[0], 1 << ((dev->regs[0x27] >> 4) & 0x3)); + dev->regs[0x09] = W83781D_RPM_TO_REG(dev->values->fans[1], 1 << ((dev->regs[0x27] >> 6) & 0x3)); + break; + case 0x48: + if (dev->local & W83781D_SMBUS) { + dev->smbus_addr_main = (dev->regs[0x28] & 0x7F); + remap = 1; + } + break; + case 0x4A: + if (dev->local & W83781D_SMBUS) { + /* DIS_T2 and DIS_T3 bits can disable those interfaces */ + if ((dev->regs[0x2A] >> 3) & 0x1) + dev->smbus_addr_temp2 = 0x00; + else + dev->smbus_addr_temp2 = 0x48 + (dev->regs[0x2A] & 0x7); + if (dev->regs[0x2A] >> 7) + dev->smbus_addr_temp3 = 0x00; + else + dev->smbus_addr_temp3 = 0x48 + ((dev->regs[0x2A] >> 4) & 0x7); + remap = 1; + } + break; + case 0x4B: + /* update FAN3 value to match the new divisor */ + dev->regs[0x0A] = W83781D_RPM_TO_REG(dev->values->fans[2], 1 << ((dev->regs[0x2B] >> 6) & 0x3)); + break; + case 0x4E: + dev->hbacs = (dev->regs[0x2E] & 0x80); + /* FIXME: Winbond's datasheet does not specify how BANKSEL[0:2] work */ + if (dev->regs[0x2E] & 0x1) + dev->active_bank = 0; + else if (dev->regs[0x2E] & 0x2) + dev->active_bank = 1; + else if (dev->regs[0x2E] & 0x4) + dev->active_bank = 2; + break; + } + + if (remap) + w83781d_remap(dev); + + return 1; +} + + +static void +w83781d_reset(w83781d_t *dev, uint8_t initialization) +{ + memset(dev->regs, 0, 64); + memset(dev->regs_bank1, 0, 6); + memset(dev->regs_bank2, 0, 6); + + /* WARNING: Array elements are register - 0x20. */ + uint8_t i; + for (i = 0; i <= 6; i++) + dev->regs[i] = dev->values->voltages[i]; + dev->regs[0x07] = dev->values->temperatures[0]; + for (i = 0; i <= 2; i++) + dev->regs[0x08 + i] = W83781D_RPM_TO_REG(dev->values->fans[i], 2); + dev->regs[0x20] = 0x01; + dev->regs[0x26] = 0x40; + dev->regs[0x27] = 0x50; + if (dev->local & W83781D_SMBUS) { + if (!initialization) /* don't reset main SMBus address if the reset was triggered by the INITIALIZATION bit */ + dev->smbus_addr_main = 0x2D; + dev->regs[0x28] = dev->smbus_addr_main; + dev->regs[0x2A] = 0x01; + dev->smbus_addr_temp2 = 0x48 + (dev->regs[0x2A] & 0x7); + dev->smbus_addr_temp3 = 0x48 + ((dev->regs[0x2A] >> 4) & 0x7); + } else { + dev->regs[0x28] = 0x00; + dev->regs[0x2A] = 0x88; + dev->smbus_addr_temp2 = dev->smbus_addr_temp3 = 0x00; + } + dev->regs[0x29] = 0x02; + dev->regs[0x2B] = 0x44; + dev->regs[0x2C] = 0x01; + dev->regs[0x2D] = 0x15; + dev->regs[0x2E] = 0x80; + dev->hbacs = (dev->regs[0x2E] & 0x80); + dev->regs[0x2F] = W83781D_VENDOR_ID >> 8; + dev->regs[0x37] = 0x80; + dev->regs[0x38] = (dev->local & W83781D_AS99127F) ? 0x31 : 0x10; + + /* WARNING: Array elements are register - 0x50. */ + uint16_t temp; + temp = W83781D_TEMP_TO_REG(dev->values->temperatures[1]); + dev->regs_bank1[0x0] = temp >> 8; + dev->regs_bank1[0x1] = temp & 0xFF; + dev->regs_bank1[0x3] = 0x4B; + dev->regs_bank1[0x5] = 0x50; + temp = W83781D_TEMP_TO_REG(dev->values->temperatures[2]); + dev->regs_bank2[0x0] = temp >> 8; + dev->regs_bank2[0x1] = temp & 0xFF; + dev->regs_bank2[0x3] = 0x4B; + dev->regs_bank2[0x5] = 0x50; + + w83781d_remap(dev); +} + + +static void +w83781d_close(void *priv) +{ + w83781d_t *dev = (w83781d_t *) priv; + + uint16_t isa_io = dev->local & 0xFFFF; + if (isa_io) + io_removehandler(isa_io, 2, w83781d_isa_read, NULL, NULL, w83781d_isa_write, NULL, NULL, dev); + + free(dev); +} + + +static void * +w83781d_init(const device_t *info) +{ + w83781d_t *dev = (w83781d_t *) malloc(sizeof(w83781d_t)); + memset(dev, 0, sizeof(w83781d_t)); + + dev->local = info->local; + dev->values = hwm_get_values(); + w83781d_reset(dev, 0); + + uint16_t isa_io = dev->local & 0xFFFF; + if (isa_io) + io_sethandler(isa_io, 2, w83781d_isa_read, NULL, NULL, w83781d_isa_write, NULL, NULL, dev); + + return dev; +} + + +const device_t w83781d_device = { + "Winbond W83781D Hardware Monitor", + DEVICE_ISA, + 0x295 | W83781D_SMBUS, + w83781d_init, w83781d_close, NULL, + NULL, NULL, NULL, + NULL +}; + + +/* + * ASUS rebadged version of the W83781D. + * Some claim it's SMBus-only, yet the BIOS clearly reads most values over ISA, + * except TEMP3 (CPU Temperature) which is read over SMBus. + */ +const device_t as99127f_device = { + "ASUS AS99127F Hardware Monitor", + DEVICE_ISA, + 0x295 | W83781D_SMBUS | W83781D_AS99127F, + w83781d_init, w83781d_close, NULL, + NULL, NULL, NULL, + NULL +}; diff --git a/src/intel_piix.c b/src/intel_piix.c index dd504cf25..3e2abeda2 100644 --- a/src/intel_piix.c +++ b/src/intel_piix.c @@ -46,6 +46,7 @@ #include "hdc_ide_sff8038i.h" #include "zip.h" #include "machine.h" +#include "smbus.h" #include "piix.h" @@ -87,11 +88,12 @@ typedef struct typedef struct { - uint8_t stat, ctl, cmd, addr, + uint8_t stat, next_stat, ctl, cmd, addr, data0, data1, index, data[32]; -} smbus_t; + pc_timer_t command_timer; +} piix_smbus_t; typedef struct @@ -107,7 +109,7 @@ typedef struct sff8038i_t *bm[2]; ddma_t ddma[2]; power_t power; - smbus_t smbus; + piix_smbus_t smbus; nvr_t * nvr; } piix_t; @@ -457,17 +459,145 @@ power_update_io_mapping(piix_t *dev) static uint8_t -smbus_reg_read(uint16_t addr, void *p) +smbus_reg_read(uint16_t addr, void *priv) { + piix_t *dev = (piix_t *) priv; uint8_t ret = 0x00; + switch (addr - dev->smbus_io_base) { + case 0x00: + ret = dev->smbus.stat; + break; + case 0x02: + dev->smbus.index = 0; + ret = dev->smbus.ctl; + break; + case 0x03: + ret = dev->smbus.cmd; + break; + case 0x04: + ret = dev->smbus.addr; + break; + case 0x05: + ret = dev->smbus.data0; + break; + case 0x06: + ret = dev->smbus.data1; + break; + case 0x07: + ret = dev->smbus.data[dev->smbus.index++]; + if (dev->smbus.index > 31) + dev->smbus.index = 0; + break; + } + + // pclog("smbus_reg_read %02x %02x\n", addr - dev->smbus_io_base, ret); + return ret; } static void -smbus_reg_write(uint16_t addr, uint8_t val, void *p) +smbus_reg_write(uint16_t addr, uint8_t val, void *priv) { + piix_t *dev = (piix_t *) priv; + uint8_t smbus_addr; + uint8_t smbus_read; + uint16_t temp; + + // pclog("smbus_reg_write %02x %02x\n", addr - dev->smbus_io_base, val); + + dev->smbus.next_stat = 0; + switch (addr - dev->smbus_io_base) { + case 0x00: + /* some status bits are reset by writing 1 to them */ + for (smbus_addr = 0x02; smbus_addr <= 0x10; smbus_addr = smbus_addr << 1) { + if (val & smbus_addr) + dev->smbus.stat = dev->smbus.stat & ~smbus_addr; + } + break; + case 0x02: + dev->smbus.ctl = val & ~(0x40); /* START always reads 0 */ + if (val & 0x40) { /* dispatch command if START is set */ + smbus_addr = (dev->smbus.addr >> 1); + if (!smbus_has_device(smbus_addr)) { + /* raise DEV_ERR if no device is at this address */ + dev->smbus.next_stat = 0x4; + break; + } + smbus_read = (dev->smbus.addr & 0x01); + + switch ((val >> 2) & 0x7) { + case 0x0: /* quick R/W */ + dev->smbus.next_stat = 0x2; + break; + case 0x1: /* byte R/W */ + if (smbus_read) + dev->smbus.data0 = smbus_read_byte(smbus_addr); + else + smbus_write_byte(smbus_addr, dev->smbus.data0); + dev->smbus.next_stat = 0x2; + break; + case 0x2: /* byte data R/W */ + if (smbus_read) + dev->smbus.data0 = smbus_read_byte_cmd(smbus_addr, dev->smbus.cmd); + else + smbus_write_byte_cmd(smbus_addr, dev->smbus.cmd, dev->smbus.data0); + dev->smbus.next_stat = 0x2; + break; + case 0x3: /* word data R/W */ + if (smbus_read) { + temp = smbus_read_word_cmd(smbus_addr, dev->smbus.cmd); + dev->smbus.data0 = (temp & 0xFF); + dev->smbus.data1 = (temp >> 8); + } else { + temp = (dev->smbus.data1 << 8) | dev->smbus.data0; + smbus_write_word_cmd(smbus_addr, dev->smbus.cmd, temp); + } + dev->smbus.next_stat = 0x2; + break; + case 0x5: /* block R/W */ + if (smbus_read) + dev->smbus.data0 = smbus_read_block_cmd(smbus_addr, dev->smbus.cmd, dev->smbus.data); + else + smbus_write_block_cmd(smbus_addr, dev->smbus.cmd, dev->smbus.data, dev->smbus.data0); + dev->smbus.next_stat = 0x2; + break; + } + } + break; + case 0x03: + dev->smbus.cmd = val; + break; + case 0x04: + dev->smbus.addr = val; + break; + case 0x05: + dev->smbus.data0 = val; + break; + case 0x06: + dev->smbus.data1 = val; + break; + case 0x07: + dev->smbus.data[dev->smbus.index++] = val; + if (dev->smbus.index > 31) + dev->smbus.index = 0; + break; + } + + if (dev->smbus.next_stat) { + dev->smbus.stat = 0x1; + timer_disable(&dev->smbus.command_timer); + timer_set_delay_u64(&dev->smbus.command_timer, 10 * TIMER_USEC); + } +} + + +static void +smbus_inter(void *priv) +{ + piix_t *dev = (piix_t *) priv; + dev->smbus.stat = dev->smbus.next_stat; } @@ -1174,6 +1304,19 @@ static void /* Bit 2: 0 = On-board audio absent, 1 = On-board audio present. */ dev->board_config[1] = 0x64; + smbus_init(); + dev->smbus.stat = 0; + dev->smbus.ctl = 0; + dev->smbus.cmd = 0; + dev->smbus.addr = 0; + dev->smbus.data0 = 0; + dev->smbus.data1 = 0; + dev->smbus.index = 0; + int i; + for (i = 0; i < 32; i++) + dev->smbus.data[i] = 0; + timer_add(&dev->smbus.command_timer, smbus_inter, dev, 0); + return dev; } diff --git a/src/machine/m_at_socket8.c b/src/machine/m_at_socket8.c index b53f8d623..a86bc9419 100644 --- a/src/machine/m_at_socket8.c +++ b/src/machine/m_at_socket8.c @@ -34,7 +34,9 @@ #include "piix.h" #include "sio.h" #include "sst_flash.h" +#include "hwm.h" #include "video.h" +#include "cpu.h" #include "machine.h" @@ -166,6 +168,33 @@ machine_at_p2bls_init(const machine_t *model) device_add(&w83977tf_device); device_add(&sst_flash_39sf020_device); + hwm_values_t machine_hwm = { + { /* fan speeds */ + 3000, /* Chassis */ + 3000, /* CPU */ + 3000, /* Power */ + 0 + }, { /* temperatures */ + 30, /* MB */ + 0, /* unused */ + 27, /* CPU */ + 0 + }, { /* voltages (divisors other than 16 = unclear how that number was achieved) */ + 2800 / 16, /* VCORE (2.8V by default) */ + 0, /* unused */ + 3300 / 16, /* +3.3V */ + 5000 / 27, /* +5V */ + VDIV(12000, 28, 10) / 16, /* +12V (with 28K/10K resistor divider suggested in the W83781D datasheet) */ + 12000 / 55, /* -12V */ + 5000 / 24, /* -5V */ + 0 + } + }; + if (model->cpu[cpu_manufacturer].cpus[cpu_effective].cpu_type == CPU_PENTIUM2D) + machine_hwm.voltages[0] = 2050 / 16; /* set lower VCORE (2.05V) for Deschutes */ + hwm_set_values(machine_hwm); + device_add(&as99127f_device); + return ret; } diff --git a/src/smbus.c b/src/smbus.c new file mode 100644 index 000000000..171d4fad5 --- /dev/null +++ b/src/smbus.c @@ -0,0 +1,410 @@ +/* + * 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. + * + * Implement SMBus (System Management Bus) and its operations. + * + * Version: @(#)smbus.c 1.0.0 2020/03/21 + * + * Authors: RichardG, + * + * Copyright 2020 RichardG. + */ +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include "86box.h" +#include "smbus.h" + + +#define NADDRS 128 /* SMBus supports 128 addresses */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + + +typedef struct _smbus_ { + uint8_t (*read_byte)(uint8_t addr, void *priv); + uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv); + uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv); + uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, void *priv); + + void (*write_byte)(uint8_t addr, uint8_t val, void *priv); + void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv); + void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv); + void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv); + + void *priv; + + struct _smbus_ *prev, *next; +} smbus_t; + +int smbus_initialized = 0; +smbus_t *smbus[NADDRS], *smbus_last[NADDRS]; + +#define ENABLE_SMBUS_LOG 1 +#ifdef ENABLE_SMBUS_LOG +int smbus_do_log = ENABLE_SMBUS_LOG; + + +static void +smbus_log(const char *fmt, ...) +{ + va_list ap; + + if (smbus_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +#define smbus_log(fmt, ...) +#endif + + +#ifdef ENABLE_SMBUS_LOG +static uint8_t smbus_null_read_byte(uint8_t addr, void *priv) { smbus_log("SMBus: read_byte(%02x)\n", addr); return(0xff); } +static uint8_t smbus_null_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv) { smbus_log("SMBus: read_byte_cmd(%02x, %02x)\n", addr, cmd); return(0xff); } +static uint16_t smbus_null_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv) { smbus_log("SMBus: read_word_cmd(%02x, %02x)\n", addr, cmd); return(0xffff); } +static uint8_t smbus_null_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, void *priv) { smbus_log("SMBus: read_block_cmd(%02x, %02x)\n", addr, cmd); return(0x00); }; +static void smbus_null_write_byte(uint8_t addr, uint8_t val, void *priv) { smbus_log("SMBus: write_byte(%02x, %02x)\n", addr, val); } +static void smbus_null_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv) { smbus_log("SMBus: write_byte_cmd(%02x, %02x, %02x)\n", addr, cmd, val); } +static void smbus_null_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv) { smbus_log("SMBus: write_word_cmd(%02x, %02x, %04x)\n", addr, cmd, val); } +static void smbus_null_write_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv) { smbus_log("SMBus: write_block_cmd(%02x, %02x, %02x)\n", addr, cmd, len); } +#endif + + +void +smbus_init(void) +{ + int c; + smbus_t *p, *q; + + if (!smbus_initialized) { + for (c=0; cprev; + free(p); + p = q; + } + p = NULL; + } + +#ifdef ENABLE_SMBUS_LOG + /* smbus[c] should be the only handler, pointing at the NULL catch handler. */ + p = (smbus_t *) malloc(sizeof(smbus_t)); + memset(p, 0, sizeof(smbus_t)); + smbus[c] = smbus_last[c] = p; + p->next = NULL; + p->prev = NULL; + p->read_byte = smbus_null_read_byte; + p->read_byte_cmd = smbus_null_read_byte_cmd; + p->read_word_cmd = smbus_null_read_word_cmd; + p->read_block_cmd = smbus_null_read_block_cmd; + p->write_byte = smbus_null_write_byte; + p->write_byte_cmd = smbus_null_write_byte_cmd; + p->write_word_cmd = smbus_null_write_word_cmd; + p->write_block_cmd = smbus_null_write_block_cmd; + p->priv = NULL; +#else + /* smbus[c] should be NULL. */ + smbus[c] = smbus_last[c] = NULL; +#endif + } +} + + +void +smbus_sethandler(uint8_t base, int size, + uint8_t (*read_byte)(uint8_t addr, void *priv), + uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, void *priv), + void (*write_byte)(uint8_t addr, uint8_t val, void *priv), + void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv), + void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv), + void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv), + void *priv) +{ + int c; + smbus_t *p, *q = NULL; + + for (c = 0; c < size; c++) { + p = smbus_last[base + c]; + q = (smbus_t *) malloc(sizeof(smbus_t)); + memset(q, 0, sizeof(smbus_t)); + if (p) { + p->next = q; + q->prev = p; + } else { + smbus[base + c] = q; + q->prev = NULL; + } + + q->read_byte = read_byte; + q->read_byte_cmd = read_byte_cmd; + q->read_word_cmd = read_word_cmd; + q->read_block_cmd = read_block_cmd; + + q->write_byte = write_byte; + q->write_byte_cmd = write_byte_cmd; + q->write_word_cmd = write_word_cmd; + q->write_block_cmd = write_block_cmd; + + q->priv = priv; + q->next = NULL; + + smbus_last[base + c] = q; + } +} + + +void +smbus_removehandler(uint8_t base, int size, + uint8_t (*read_byte)(uint8_t addr, void *priv), + uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, void *priv), + void (*write_byte)(uint8_t addr, uint8_t val, void *priv), + void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv), + void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv), + void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv), + void *priv) +{ + int c; + smbus_t *p; + + for (c = 0; c < size; c++) { + p = smbus[base + c]; + if (!p) + continue; + while(p) { + if ((p->read_byte == read_byte) && (p->read_byte_cmd == read_byte_cmd) && + (p->read_word_cmd == read_word_cmd) && (p->read_block_cmd == read_block_cmd) && + (p->write_byte == write_byte) && (p->write_byte_cmd == write_byte_cmd) && + (p->write_word_cmd == write_word_cmd) && (p->write_block_cmd == write_block_cmd) && + (p->priv == priv)) { + if (p->prev) + p->prev->next = p->next; + else + smbus[base + c] = p->next; + if (p->next) + p->next->prev = p->prev; + else + smbus_last[base + c] = p->prev; + free(p); + p = NULL; + break; + } + p = p->next; + } + } +} + + +void +smbus_handler(int set, uint8_t base, int size, + uint8_t (*read_byte)(uint8_t addr, void *priv), + uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, void *priv), + void (*write_byte)(uint8_t addr, uint8_t val, void *priv), + void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv), + void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv), + void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv), + void *priv) +{ + if (set) + smbus_sethandler(base, size, read_byte, read_byte_cmd, read_word_cmd, read_block_cmd, write_byte, write_byte_cmd, write_word_cmd, write_block_cmd, priv); + else + smbus_removehandler(base, size, read_byte, read_byte_cmd, read_word_cmd, read_block_cmd, write_byte, write_byte_cmd, write_word_cmd, write_block_cmd, priv); +} + + +uint8_t +smbus_has_device(uint8_t addr) +{ + return(!!smbus[addr]); +} + + +uint8_t +smbus_read_byte(uint8_t addr) +{ + uint8_t ret = 0xff; + smbus_t *p; + int found = 0; + + p = smbus[addr]; + if (p) { + while(p) { + if (p->read_byte) { + ret &= p->read_byte(addr, p->priv); + found++; + } + p = p->next; + } + } + + return(ret); +} + +uint8_t +smbus_read_byte_cmd(uint8_t addr, uint8_t cmd) +{ + uint8_t ret = 0xff; + smbus_t *p; + int found = 0; + + p = smbus[addr]; + if (p) { + while(p) { + if (p->read_byte_cmd) { + ret &= p->read_byte_cmd(addr, cmd, p->priv); + found++; + } + p = p->next; + } + } + + return(ret); +} + +uint16_t +smbus_read_word_cmd(uint8_t addr, uint8_t cmd) +{ + uint16_t ret = 0xffff; + smbus_t *p; + int found = 0; + + p = smbus[addr]; + if (p) { + while(p) { + if (p->read_word_cmd) { + ret &= p->read_word_cmd(addr, cmd, p->priv); + found++; + } + p = p->next; + } + } + + return(ret); +} + +uint8_t +smbus_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data) +{ + uint8_t ret = 0; + smbus_t *p; + int found = 0; + + p = smbus[addr]; + if (p) { + while(p) { + if (p->read_block_cmd) { + ret = MAX(ret, p->read_block_cmd(addr, cmd, data, p->priv)); + found++; + } + p = p->next; + } + } + + return(ret); +} + + +void +smbus_write_byte(uint8_t addr, uint8_t val) +{ + smbus_t *p; + int found = 0; + + if (smbus[addr]) { + p = smbus[addr]; + while(p) { + if (p->write_byte) { + p->write_byte(addr, val, p->priv); + found++; + } + p = p->next; + } + } + + return; +} + +void +smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val) +{ + smbus_t *p; + int found = 0; + + if (smbus[addr]) { + p = smbus[addr]; + while(p) { + if (p->write_byte_cmd) { + p->write_byte_cmd(addr, cmd, val, p->priv); + found++; + } + p = p->next; + } + } + + return; +} + +void +smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val) +{ + smbus_t *p; + int found = 0; + + if (smbus[addr]) { + p = smbus[addr]; + while(p) { + if (p->write_word_cmd) { + p->write_word_cmd(addr, cmd, val, p->priv); + found++; + } + p = p->next; + } + } + + return; +} + +void +smbus_write_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len) +{ + smbus_t *p; + int found = 0; + + p = smbus[addr]; + if (p) { + while(p) { + if (p->write_block_cmd) { + p->write_block_cmd(addr, cmd, data, len, p->priv); + found++; + } + p = p->next; + } + } + + return; +} diff --git a/src/smbus.h b/src/smbus.h new file mode 100644 index 000000000..a4fec4a5c --- /dev/null +++ b/src/smbus.h @@ -0,0 +1,67 @@ +/* + * 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. + * + * Definitions for the SMBus handler. + * + * Version: @(#)smbus.h 1.0.0 2020/03/21 + * + * Authors: RichardG, + * + * Copyright 2020 RichardG. + */ +#ifndef EMU_SMBUS_H +# define EMU_SMBUS_H + + +extern void smbus_init(void); + +extern void smbus_sethandler(uint8_t base, int size, + uint8_t (*read_byte)(uint8_t addr, void *priv), + uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, void *priv), + void (*write_byte)(uint8_t addr, uint8_t val, void *priv), + void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv), + void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv), + void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv), + void *priv); + +extern void smbus_removehandler(uint8_t base, int size, + uint8_t (*read_byte)(uint8_t addr, void *priv), + uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, void *priv), + void (*write_byte)(uint8_t addr, uint8_t val, void *priv), + void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv), + void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv), + void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv), + void *priv); + +extern void smbus_handler(int set, uint8_t base, int size, + uint8_t (*read_byte)(uint8_t addr, void *priv), + uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv), + uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, void *priv), + void (*write_byte)(uint8_t addr, uint8_t val, void *priv), + void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv), + void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv), + void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv), + void *priv); + +extern uint8_t smbus_has_device(uint8_t addr); +extern uint8_t smbus_read_byte(uint8_t addr); +extern uint8_t smbus_read_byte_cmd(uint8_t addr, uint8_t cmd); +extern uint16_t smbus_read_word_cmd(uint8_t addr, uint8_t cmd); +extern uint8_t smbus_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data); +extern void smbus_write_byte(uint8_t addr, uint8_t val); +extern void smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val); +extern void smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val); +extern void smbus_write_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len); + + +#endif /*EMU_SMBUS_H*/ diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index d4d7ecc0b..6e4587712 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -597,7 +597,7 @@ MCHOBJ := machine.o machine_table.o \ m_at_286_386sx.o m_at_386dx_486.o \ m_at_socket4_5.o m_at_socket7_s7.o -DEVOBJ := bugger.o ibm_5161.o isamem.o isartc.o lpt.o $(SERIAL) \ +DEVOBJ := bugger.o hwm.o hwm_w83781d.o ibm_5161.o isamem.o isartc.o lpt.o $(SERIAL) \ sio_acc3221.o \ sio_fdc37c66x.o sio_fdc37c669.o \ sio_fdc37c93x.o \ @@ -605,6 +605,7 @@ DEVOBJ := bugger.o ibm_5161.o isamem.o isartc.o lpt.o $(SERIAL) \ sio_w83787f.o \ sio_w83877f.o sio_w83977f.o \ sio_um8669f.o \ + smbus.o \ keyboard.o \ keyboard_xt.o keyboard_at.o \ gameport.o \ diff --git a/src/win/Makefile_ndr.mingw b/src/win/Makefile_ndr.mingw index 27588540a..ca7bed3ee 100644 --- a/src/win/Makefile_ndr.mingw +++ b/src/win/Makefile_ndr.mingw @@ -602,7 +602,7 @@ MCHOBJ := machine.o machine_table.o \ m_at_286_386sx.o m_at_386dx_486.o \ m_at_socket4_5.o m_at_socket7_s7.o -DEVOBJ := bugger.o ibm_5161.o isamem.o isartc.o lpt.o $(SERIAL) \ +DEVOBJ := bugger.o hwm.o hwm_w83781d.o ibm_5161.o isamem.o isartc.o lpt.o $(SERIAL) \ sio_acc3221.o \ sio_fdc37c66x.o sio_fdc37c669.o \ sio_fdc37c93x.o \ @@ -610,6 +610,7 @@ DEVOBJ := bugger.o ibm_5161.o isamem.o isartc.o lpt.o $(SERIAL) \ sio_w83787f.o \ sio_w83877f.o sio_w83977f.o \ sio_um8669f.o \ + smbus.o \ keyboard.o \ keyboard_xt.o keyboard_at.o \ gameport.o \