From 5539159657c2992dbe3e710120b8bd7341a6d8ac Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 31 Mar 2020 19:08:01 -0300 Subject: [PATCH] Move the PIIX4 SMBus interface to its own file --- src/include/86box/smbus_piix4.h | 43 ++++++ src/intel_piix.c | 182 +---------------------- src/smbus_piix4.c | 253 ++++++++++++++++++++++++++++++++ src/win/Makefile.mingw | 2 +- src/win/Makefile_ndr.mingw | 2 +- 5 files changed, 304 insertions(+), 178 deletions(-) create mode 100644 src/include/86box/smbus_piix4.h create mode 100644 src/smbus_piix4.c diff --git a/src/include/86box/smbus_piix4.h b/src/include/86box/smbus_piix4.h new file mode 100644 index 000000000..b9f776a5a --- /dev/null +++ b/src/include/86box/smbus_piix4.h @@ -0,0 +1,43 @@ +/* + * 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 generic PIIX4-compatible SMBus host controller. + * + * + * + * Authors: RichardG, + * + * Copyright 2020 RichardG. + */ +#ifndef EMU_SMBUS_PIIX4_H +# define EMU_SMBUS_PIIX4_H + + +#define SMBUS_PIIX4_BLOCK_DATA_SIZE 32 + + +typedef struct +{ + uint16_t io_base; + uint8_t stat, next_stat, ctl, cmd, addr, + data0, data1, + index, + data[SMBUS_PIIX4_BLOCK_DATA_SIZE]; + pc_timer_t response_timer; +} smbus_piix4_t; + + +extern void smbus_piix4_remap(smbus_piix4_t *dev, uint16_t new_io_base, uint8_t enable); + + +#ifdef EMU_DEVICE_H +extern const device_t piix4_smbus_device; +#endif + + +#endif /*EMU_SMBUS_PIIX4_H*/ diff --git a/src/intel_piix.c b/src/intel_piix.c index fdf6b6bc3..df2cf70e0 100644 --- a/src/intel_piix.c +++ b/src/intel_piix.c @@ -47,7 +47,7 @@ #include <86box/hdc_ide_sff8038i.h> #include <86box/zip.h> #include <86box/machine.h> -#include <86box/smbus.h> +#include <86box/smbus_piix4.h> #include <86box/piix.h> @@ -87,16 +87,6 @@ typedef struct } power_t; -typedef struct -{ - uint8_t stat, next_stat, ctl, cmd, addr, - data0, data1, - index, - data[32]; - pc_timer_t command_timer; -} piix_smbus_t; - - typedef struct { uint8_t cur_readout_reg, rev, @@ -105,12 +95,11 @@ typedef struct regs[4][256], readout_regs[256], board_config[2]; uint16_t func0_id, - usb_io_base, power_io_base, - smbus_io_base; + usb_io_base, power_io_base; sff8038i_t *bm[2]; ddma_t ddma[2]; power_t power; - piix_smbus_t smbus; + smbus_piix4_t * smbus; apm_t * apm; nvr_t * nvr; } piix_t; @@ -466,159 +455,10 @@ power_update_io_mapping(piix_t *dev) } -static uint8_t -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; - } - - piix_log("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 *priv) -{ - piix_t *dev = (piix_t *) priv; - uint8_t smbus_addr; - uint8_t smbus_read; - uint16_t temp; - - piix_log("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; -} - - static void smbus_update_io_mapping(piix_t *dev) { - if (dev->smbus_io_base != 0x0000) - io_removehandler(dev->smbus_io_base, 0x10, smbus_reg_read, NULL, NULL, smbus_reg_write, NULL, NULL, dev); - - dev->smbus_io_base = (dev->regs[3][0x91] << 8) | (dev->regs[3][0x90] & 0xf0); - - if ((dev->regs[3][PCI_REG_COMMAND] & PCI_COMMAND_IO) && (dev->regs[3][0xd2] & 0x01) && (dev->smbus_io_base != 0x0000)) - io_sethandler(dev->smbus_io_base, 0x10, smbus_reg_read, NULL, NULL, smbus_reg_write, NULL, NULL, dev); + smbus_piix4_remap(dev->smbus, (dev->regs[3][0x91] << 8) | (dev->regs[3][0x90] & 0xf0), (dev->regs[3][PCI_REG_COMMAND] & PCI_COMMAND_IO) && (dev->regs[3][0xd2] & 0x01)); } @@ -1259,6 +1099,8 @@ static void piix_reset_hard(dev); + dev->smbus = device_add(&piix4_smbus_device); + dev->apm = device_add(&apm_device); device_add(&port_92_pci_device); @@ -1331,18 +1173,6 @@ static void else dev->board_config[1] |= 0x10; /* TODO: how are the overdrive processors configured? */ - 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; - 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/smbus_piix4.c b/src/smbus_piix4.c new file mode 100644 index 000000000..8a1a0a762 --- /dev/null +++ b/src/smbus_piix4.c @@ -0,0 +1,253 @@ +/* + * 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 a generic PIIX4-compatible SMBus host controller. + * + * + * + * Authors: RichardG, + * + * Copyright 2020 RichardG. + */ +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/io.h> +#include <86box/device.h> +#include <86box/timer.h> +#include <86box/smbus.h> +#include <86box/smbus_piix4.h> + + +#ifdef ENABLE_SMBUS_PIIX4_LOG +int smbus_piix4_do_log = ENABLE_SMBUS_PIIX4_LOG; + + +static void +smbus_piix4_log(const char *fmt, ...) +{ + va_list ap; + + if (smbus_piix4_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +#define smbus_piix4_log(fmt, ...) +#endif + + +static uint8_t +smbus_piix4_read(uint16_t addr, void *priv) +{ + smbus_piix4_t *dev = (smbus_piix4_t *) priv; + uint8_t ret = 0x00; + + switch (addr - dev->io_base) { + case 0x00: + ret = dev->stat; + break; + case 0x02: + dev->index = 0; /* reading from this resets the block data index */ + ret = dev->ctl; + break; + case 0x03: + ret = dev->cmd; + break; + case 0x04: + ret = dev->addr; + break; + case 0x05: + ret = dev->data0; + break; + case 0x06: + ret = dev->data1; + break; + case 0x07: + ret = dev->data[dev->index++]; + if (dev->index >= SMBUS_PIIX4_BLOCK_DATA_SIZE) + dev->index = 0; + break; + } + + smbus_piix4_log("SMBus PIIX4: read(%02x) = %02x\n", addr, ret); + + return ret; +} + + +static void +smbus_piix4_write(uint16_t addr, uint8_t val, void *priv) +{ + smbus_piix4_t *dev = (smbus_piix4_t *) priv; + uint8_t smbus_addr, smbus_read, prev_stat; + uint16_t temp; + + smbus_piix4_log("SMBus PIIX4: write(%02x, %02x)\n", addr, val); + + prev_stat = dev->next_stat; + dev->next_stat = 0; + switch (addr - dev->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->stat = dev->stat & ~smbus_addr; + } + break; + case 0x02: + dev->ctl = val & ~(0x40); /* START always reads 0 */ + if (val & 0x02) { /* cancel an in-progress command if KILL is set */ + /* cancel only if a command is in progress */ + if (prev_stat) { + dev->stat = 0x10; /* raise FAILED */ + timer_disable(&dev->response_timer); + } + } + if (val & 0x40) { /* dispatch command if START is set */ + smbus_addr = (dev->addr >> 1); + if (!smbus_has_device(smbus_addr)) { + /* raise DEV_ERR if no device is at this address */ + dev->next_stat = 0x4; + break; + } + smbus_read = (dev->addr & 0x01); + + /* decode the 3-bit command protocol */ + switch ((val >> 2) & 0x7) { + case 0x0: /* quick R/W */ + dev->next_stat = 0x2; + break; + case 0x1: /* byte R/W */ + if (smbus_read) + dev->data0 = smbus_read_byte(smbus_addr); + else + smbus_write_byte(smbus_addr, dev->data0); + dev->next_stat = 0x2; + break; + case 0x2: /* byte data R/W */ + if (smbus_read) + dev->data0 = smbus_read_byte_cmd(smbus_addr, dev->cmd); + else + smbus_write_byte_cmd(smbus_addr, dev->cmd, dev->data0); + dev->next_stat = 0x2; + break; + case 0x3: /* word data R/W */ + if (smbus_read) { + temp = smbus_read_word_cmd(smbus_addr, dev->cmd); + dev->data0 = (temp & 0xFF); + dev->data1 = (temp >> 8); + } else { + temp = (dev->data1 << 8) | dev->data0; + smbus_write_word_cmd(smbus_addr, dev->cmd, temp); + } + dev->next_stat = 0x2; + break; + case 0x5: /* block R/W */ + if (smbus_read) + dev->data0 = smbus_read_block_cmd(smbus_addr, dev->cmd, dev->data); + else + smbus_write_block_cmd(smbus_addr, dev->cmd, dev->data, dev->data0); + dev->next_stat = 0x2; + break; + default: + /* other command protocols have undefined behavior, but raise DEV_ERR to be safe */ + dev->next_stat = 0x4; + break; + } + } + break; + case 0x03: + dev->cmd = val; + break; + case 0x04: + dev->addr = val; + break; + case 0x05: + dev->data0 = val; + break; + case 0x06: + dev->data1 = val; + break; + case 0x07: + dev->data[dev->index++] = val; + if (dev->index >= SMBUS_PIIX4_BLOCK_DATA_SIZE) + dev->index = 0; + break; + } + + /* if a status register update was given, dispatch it after 10ms to ensure nothing breaks */ + if (dev->next_stat) { + dev->stat = 0x1; /* raise HOST_BUSY while waiting */ + timer_disable(&dev->response_timer); + timer_set_delay_u64(&dev->response_timer, 10 * TIMER_USEC); + } +} + + +static void +smbus_piix4_response(void *priv) +{ + smbus_piix4_t *dev = (smbus_piix4_t *) priv; + + /* dispatch the status register update */ + dev->stat = dev->next_stat; +} + + +void +smbus_piix4_remap(smbus_piix4_t *dev, uint16_t new_io_base, uint8_t enable) +{ + if (dev->io_base != 0x0000) + io_removehandler(dev->io_base, 0x10, smbus_piix4_read, NULL, NULL, smbus_piix4_write, NULL, NULL, dev); + + dev->io_base = new_io_base; + smbus_piix4_log("SMBus PIIX4: remap to %04Xh\n", dev->io_base); + + if (enable && (dev->io_base != 0x0000)) + io_sethandler(dev->io_base, 0x10, smbus_piix4_read, NULL, NULL, smbus_piix4_write, NULL, NULL, dev); +} + + +static void * +smbus_piix4_init(const device_t *info) +{ + smbus_piix4_t *dev = (smbus_piix4_t *) malloc(sizeof(smbus_piix4_t)); + memset(dev, 0, sizeof(smbus_piix4_t)); + + smbus_init(); + timer_add(&dev->response_timer, smbus_piix4_response, dev, 0); + + return dev; +} + + +static void +smbus_piix4_close(void *priv) +{ + smbus_piix4_t *dev = (smbus_piix4_t *) priv; + + free(dev); +} + + +const device_t piix4_smbus_device = { + "PIIX4-compatible SMBus Host Controller", + DEVICE_AT, + 0, + smbus_piix4_init, smbus_piix4_close, NULL, + NULL, NULL, NULL, + NULL +}; diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index d67b33764..3602a3f8d 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -593,7 +593,7 @@ DEVOBJ := bugger.o hwm.o hwm_w83781d.o ibm_5161.o isamem.o isartc.o lpt.o postc sio_w83787f.o \ sio_w83877f.o sio_w83977f.o \ sio_um8669f.o \ - smbus.o spd.o \ + smbus.o smbus_piix4.o spd.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 efab536cb..dc8c7c000 100644 --- a/src/win/Makefile_ndr.mingw +++ b/src/win/Makefile_ndr.mingw @@ -598,7 +598,7 @@ DEVOBJ := bugger.o hwm.o hwm_w83781d.o ibm_5161.o isamem.o isartc.o lpt.o postc sio_w83787f.o \ sio_w83877f.o sio_w83977f.o \ sio_um8669f.o \ - smbus.o spd.o \ + smbus.o smbus_piix4.o spd.o \ keyboard.o \ keyboard_xt.o keyboard_at.o \ gameport.o \