diff --git a/src/acpi.c b/src/acpi.c index 48ff9b510..350021e00 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -36,6 +36,7 @@ #include <86box/apm.h> #include <86box/acpi.h> #include <86box/machine.h> +#include <86box/i2c.h> #ifdef ENABLE_ACPI_LOG @@ -92,7 +93,7 @@ acpi_raise_smi(void *priv) if ((!dev->regs.smi_lock || !dev->regs.smi_active)) { smi_line = 1; dev->regs.smi_active = 1; - } + } } else if (dev->vendor == VEN_INTEL) { smi_line = 1; /* Clear bit 16 of GLBCTL. */ @@ -311,9 +312,18 @@ acpi_reg_read_via(int size, uint16_t addr, void *p) ret = dev->regs.gpio_val & 0xff; break; case 0x44: - /* GPIO port Output Value */ - if (size == 1) + /* GPIO port Input Value */ + if (size == 1) { ret = dev->regs.extsmi_val & 0xff; + + if (dev->i2c) { + ret &= 0xf9; + if (!(dev->regs.gpio_dir & 0x02) && i2c_gpio_get_scl(dev->i2c)) + ret |= 0x02; + if (!(dev->regs.gpio_dir & 0x04) && i2c_gpio_get_sda(dev->i2c)) + ret |= 0x04; + } + } break; case 0x46: case 0x47: /* GPO Port Output Value */ @@ -639,6 +649,14 @@ acpi_reg_write_via_common(int size, uint16_t addr, uint8_t val, void *p) } +static void +acpi_i2c_set(acpi_t *dev) +{ + if (dev->i2c) + i2c_gpio_set(dev->i2c, !(dev->regs.gpio_dir & 0x02) || (dev->regs.gpio_val & 0x02), !(dev->regs.gpio_dir & 0x04) || (dev->regs.gpio_val & 0x04)); +} + + static void acpi_reg_write_via(int size, uint16_t addr, uint8_t val, void *p) { @@ -652,13 +670,17 @@ acpi_reg_write_via(int size, uint16_t addr, uint8_t val, void *p) switch (addr) { case 0x40: /* GPIO Direction Control */ - if (size == 1) - dev->regs.gpio_dir = val & 0xff; + if (size == 1) { + dev->regs.gpio_dir = val & 0x7f; + acpi_i2c_set(dev); + } break; case 0x42: /* GPIO port Output Value */ - if (size == 1) - dev->regs.gpio_val = val & 0xff; + if (size == 1) { + dev->regs.gpio_val = val & 0x1f; + acpi_i2c_set(dev); + } break; case 0x46: case 0x47: /* GPO Port Output Value */ @@ -1184,6 +1206,12 @@ acpi_close(void *priv) { acpi_t *dev = (acpi_t *) priv; + if (dev->i2c) { + if (i2c_smbus == i2c_gpio_get_bus(dev->i2c)) + i2c_smbus = NULL; + i2c_gpio_close(dev->i2c); + } + timer_disable(&dev->timer); free(dev); @@ -1206,6 +1234,9 @@ acpi_init(const device_t *info) if (dev->vendor == VEN_INTEL) { dev->apm = device_add(&apm_pci_acpi_device); io_sethandler(0x00b2, 0x0002, acpi_apm_in, NULL, NULL, acpi_apm_out, NULL, NULL, dev); + } else if (dev->vendor == VEN_VIA) { + dev->i2c = i2c_gpio_init("smbus_vt82c586b"); + i2c_smbus = i2c_gpio_get_bus(dev->i2c); } timer_add(&dev->timer, acpi_timer_count, dev, 0); diff --git a/src/chipset/ali6117.c b/src/chipset/ali6117.c index d9d2444df..e5dd880f2 100644 --- a/src/chipset/ali6117.c +++ b/src/chipset/ali6117.c @@ -277,7 +277,7 @@ ali6117_reset(void *priv) dev->regs[0x31] = 0x01; dev->regs[0x34] = 0x04; /* enable internal RTC */ dev->regs[0x35] = 0x20; /* enable internal KBC */ - dev->regs[0x36] = (dev->local & 0x4); /* M6117D ID */ + dev->regs[0x36] = dev->local & 0x4; /* M6117D ID */ } diff --git a/src/chipset/stpc.c b/src/chipset/stpc.c index 3d5ceee0f..4d83a3c8b 100644 --- a/src/chipset/stpc.c +++ b/src/chipset/stpc.c @@ -41,11 +41,10 @@ #include <86box/chipset.h> -#define STPC_NB_CLIENT 0x01 -#define STPC_ISAB_CLIENT 0x02 -#define STPC_ISAB_CONSUMER2 0x04 -#define STPC_IDE_ATLAS 0x08 -#define STPC_USB 0x10 +#define STPC_CONSUMER2 0x104a020b +#define STPC_ATLAS 0x104a0210 +#define STPC_ELITE 0x104a021a +#define STPC_CLIENT 0x100e55cc typedef struct stpc_t @@ -119,7 +118,7 @@ stpc_recalcmapping(stpc_t *dev) shadowbios_write = 0; for (reg = 0; reg <= 3; reg++) { - for (bitpair = 0; bitpair <= (reg == 3 ? 0 : 3); bitpair++) { + for (bitpair = 0; bitpair <= ((reg == 3) ? 0 : 3); bitpair++) { if (reg == 3) { size = 0x10000; base = 0xf0000; @@ -166,7 +165,7 @@ stpc_host_write(uint16_t addr, uint8_t val, void *priv) if (addr == dev->host_base) dev->host_offset = val; - else if (addr == dev->host_base + 4) + else if (addr == (dev->host_base + 4)) dev->host_regs[dev->host_offset] = val; } @@ -179,7 +178,7 @@ stpc_host_read(uint16_t addr, void *priv) if (addr == dev->host_base) ret = dev->host_offset; - else if (addr == dev->host_base + 4) + else if (addr == (dev->host_base + 4)) ret = dev->host_regs[dev->host_offset]; else ret = 0xff; @@ -198,7 +197,7 @@ stpc_localbus_write(uint16_t addr, uint8_t val, void *priv) if (addr == dev->localbus_base) dev->localbus_offset = val; - else if (addr == dev->localbus_base + 4) + else if (addr == (dev->localbus_base + 4)) dev->localbus_regs[addr] = val; } @@ -211,7 +210,7 @@ stpc_localbus_read(uint16_t addr, void *priv) if (addr == dev->localbus_base) ret = dev->localbus_offset; - else if (addr == dev->localbus_base + 4) + else if (addr == (dev->localbus_base + 4)) ret = dev->localbus_regs[dev->localbus_offset]; else ret = 0xff; @@ -329,8 +328,8 @@ stpc_ide_bm_handlers(stpc_t *dev) { uint16_t base = (dev->pci_conf[2][0x20] & 0xf0) | (dev->pci_conf[2][0x21] << 8); - sff_bus_master_handler(dev->bm[0], (dev->pci_conf[2][0x04] & 1), base); - sff_bus_master_handler(dev->bm[1], (dev->pci_conf[2][0x04] & 1), base + 8); + sff_bus_master_handler(dev->bm[0], dev->pci_conf[2][0x04] & 1, base); + sff_bus_master_handler(dev->bm[1], dev->pci_conf[2][0x04] & 1, base + 8); } @@ -352,7 +351,7 @@ stpc_ide_write(int func, int addr, uint8_t val, void *priv) break; case 0x05: - dev->pci_conf[2][addr] = (val & 0x01); + dev->pci_conf[2][addr] = val & 0x01; break; case 0x07: @@ -441,13 +440,13 @@ stpc_ide_read(int func, int addr, void *priv) uint8_t ret; if (func > 0) - ret = 0xff; + ret = 0xff; else { ret = dev->pci_conf[2][addr]; if (addr == 0x48) { ret &= 0xfc; - ret |= (!!(dev->bm[0]->status & 0x04)); - ret |= ((!!(dev->bm[1]->status & 0x04)) << 1); + ret |= !!(dev->bm[0]->status & 0x04); + ret |= (!!(dev->bm[1]->status & 0x04)) << 1; } } @@ -461,9 +460,9 @@ stpc_isab_write(int func, int addr, uint8_t val, void *priv) { stpc_t *dev = (stpc_t *) priv; - if (func == 1 && !(dev->local & STPC_IDE_ATLAS)) { - stpc_ide_write(0, addr, val, priv); - return; + if ((func == 1) && (dev->local != STPC_ATLAS)) { + stpc_ide_write(0, addr, val, priv); + return; } stpc_log("STPC: isab_write(%d, %02X, %02X)\n", func, addr, val); @@ -492,12 +491,12 @@ stpc_isab_read(int func, int addr, void *priv) stpc_t *dev = (stpc_t *) priv; uint8_t ret; - if ((func == 1) && !(dev->local & STPC_IDE_ATLAS)) - ret = stpc_ide_read(0, addr, priv); + if ((func == 1) && (dev->local != STPC_ATLAS)) + ret = stpc_ide_read(0, addr, priv); else if (func > 0) - ret = 0xff; + ret = 0xff; else - ret = dev->pci_conf[1][addr]; + ret = dev->pci_conf[1][addr]; stpc_log("STPC: isab_read(%d, %02X) = %02X\n", func, addr, ret); return ret; @@ -547,9 +546,9 @@ stpc_usb_read(int func, int addr, void *priv) uint8_t ret; if (func > 0) - ret = 0xff; + ret = 0xff; else - ret = dev->pci_conf[3][addr]; + ret = dev->pci_conf[3][addr]; stpc_log("STPC: usb_read(%d, %02X) = %02X\n", func, addr, ret); return ret; @@ -589,34 +588,35 @@ stpc_remap_localbus(stpc_t *dev, uint16_t localbus_base) static uint8_t stpc_serial_handlers(uint8_t val) { - stpc_serial_t *dev; - if (!(dev = device_get_priv(&stpc_serial_device))) { - stpc_log("STPC: Not remapping UARTs, disabled by strap (raw %02X)\n", val); - return 0; + stpc_serial_t *dev = device_get_priv(&stpc_serial_device); + if (!dev) { + stpc_log("STPC: Not remapping UARTs, disabled by strap (raw %02X)\n", val); + return 0; } - uint16_t uart0_io = 0x3f8, uart0_irq = 4, uart1_io = 0x3f8, uart1_irq = 3; + uint16_t uart0_io = 0x3f8, uart1_io = 0x3f8; + uint8_t uart0_irq = 4, uart1_irq = 3; if (val & 0x10) - uart1_io -= 0x100; + uart1_io &= 0xfeff; if (val & 0x20) - uart1_io -= 0x10; + uart1_io &= 0xffef; if (val & 0x40) - uart0_io -= 0x100; + uart0_io &= 0xfeff; if (val & 0x80) - uart0_io -= 0x10; + uart0_io &= 0xffef; if (uart0_io == uart1_io) { - /* Apply defaults if both UARTs are set to the same address. */ - stpc_log("STPC: Both UARTs set to %02X, resetting to defaults\n", uart0_io); - uart0_io = 0x3f8; - uart1_io = 0x2f8; + /* Apply defaults if both UARTs are set to the same address. */ + stpc_log("STPC: Both UARTs set to %02X, resetting to defaults\n", uart0_io); + uart0_io = 0x3f8; + uart1_io = 0x2f8; } - if (uart0_io < 0x300) { - /* The address for UART0 defines the IRQs for both ports. */ - uart0_irq = 3; - uart1_irq = 4; + if (!(uart0_io & 0x100)) { + /* The address for UART0 establishes the IRQs for both ports. */ + uart0_irq = 3; + uart1_irq = 4; } stpc_log("STPC: Remapping UART0 to %04X %d and UART1 to %04X %d (raw %02X)\n", uart0_io, uart0_irq, uart1_io, uart1_irq, val); @@ -714,9 +714,9 @@ stpc_reg_read(uint16_t addr, void *priv) if (addr == 0x22) ret = dev->reg_offset; else if (dev->reg_offset >= 0xc0) - return 0xff; /* Cyrix CPU registers: let the CPU code handle these */ + return 0xff; /* let the CPU code handle Cyrix CPU registers */ else if ((dev->reg_offset == 0x56) || (dev->reg_offset == 0x57)) { - /* ELCR is in here, not in port 4D0h. */ + /* ELCR registers. */ ret = pic_elcr_read(dev->reg_offset, (dev->reg_offset & 1) ? &pic2 : &pic); if (dev->reg_offset == 0x57) ret |= (dev->regs[dev->reg_offset] & 0x01); @@ -738,9 +738,9 @@ stpc_reset(void *priv) memset(dev->regs, 0, sizeof(dev->regs)); dev->regs[0x7b] = 0xff; if (device_get_priv(&stpc_lpt_device)) - dev->regs[0x4c] |= 0x80; /* LPT strap */ + dev->regs[0x4c] |= 0x80; /* LPT strap */ if (stpc_serial_handlers(0x00)) - dev->regs[0x4c] |= 0x03; /* UART straps */ + dev->regs[0x4c] |= 0x03; /* UART straps */ } @@ -754,14 +754,12 @@ stpc_setup(stpc_t *dev) stpc_reg_read, NULL, NULL, stpc_reg_write, NULL, NULL, dev); /* Northbridge */ - if (dev->local & STPC_NB_CLIENT) { - /* Client */ + if (dev->local & STPC_CLIENT) { dev->pci_conf[0][0x00] = 0x0e; dev->pci_conf[0][0x01] = 0x10; dev->pci_conf[0][0x02] = 0x64; dev->pci_conf[0][0x03] = 0x05; } else { - /* Atlas, Elite, Consumer II */ dev->pci_conf[0][0x00] = 0x4a; dev->pci_conf[0][0x01] = 0x10; dev->pci_conf[0][0x02] = 0x0a; @@ -776,31 +774,10 @@ stpc_setup(stpc_t *dev) dev->pci_conf[0][0x0b] = 0x06; /* ISA Bridge */ - if (dev->local & STPC_ISAB_CLIENT) { - /* Client */ - dev->pci_conf[1][0x00] = 0x0e; - dev->pci_conf[1][0x01] = 0x10; - dev->pci_conf[1][0x02] = 0xcc; - dev->pci_conf[1][0x03] = 0x55; - } else if (dev->local & STPC_ISAB_CONSUMER2) { - /* Consumer II */ - dev->pci_conf[1][0x00] = 0x4a; - dev->pci_conf[1][0x01] = 0x10; - dev->pci_conf[1][0x02] = 0x0b; - dev->pci_conf[1][0x03] = 0x02; - } else if (dev->local & STPC_IDE_ATLAS) { - /* Atlas */ - dev->pci_conf[1][0x00] = 0x4a; - dev->pci_conf[1][0x01] = 0x10; - dev->pci_conf[1][0x02] = 0x10; - dev->pci_conf[1][0x03] = 0x02; - } else { - /* Elite */ - dev->pci_conf[1][0x00] = 0x4a; - dev->pci_conf[1][0x01] = 0x10; - dev->pci_conf[1][0x02] = 0x1a; - dev->pci_conf[1][0x03] = 0x02; - } + dev->pci_conf[1][0x00] = dev->local >> 16; + dev->pci_conf[1][0x01] = dev->local >> 24; + dev->pci_conf[1][0x02] = dev->local; + dev->pci_conf[1][0x03] = dev->local >> 8; dev->pci_conf[1][0x04] = 0x0f; @@ -812,24 +789,19 @@ stpc_setup(stpc_t *dev) /* NOTE: This is an erratum in the STPC Atlas programming manual, the programming manuals for the other STPC chipsets say 0x80, which is indeed multi-function (as the STPC Atlas programming manual - indicates as well, and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */ + indicates as well), and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */ dev->pci_conf[1][0x0e] = /*0x40*/ 0x80; /* IDE */ - if (dev->local & STPC_ISAB_CLIENT) { - dev->pci_conf[2][0x00] = 0x0e; - dev->pci_conf[2][0x01] = 0x10; - } else { - dev->pci_conf[2][0x00] = 0x4a; - dev->pci_conf[2][0x01] = 0x10; - } + dev->pci_conf[2][0x00] = dev->local >> 16; + dev->pci_conf[2][0x01] = dev->local >> 24; - if (dev->local & STPC_IDE_ATLAS) { - dev->pci_conf[2][0x02] = 0x28; - dev->pci_conf[2][0x03] = 0x02; + if (dev->local == STPC_ATLAS) { + dev->pci_conf[2][0x02] = 0x28; + dev->pci_conf[2][0x03] = 0x02; } else { - dev->pci_conf[2][0x02] = dev->pci_conf[1][0x02]; - dev->pci_conf[2][0x03] = dev->pci_conf[1][0x03]; + dev->pci_conf[2][0x02] = dev->pci_conf[1][0x02]; + dev->pci_conf[2][0x03] = dev->pci_conf[1][0x03]; } dev->pci_conf[2][0x06] = 0x80; @@ -841,7 +813,7 @@ stpc_setup(stpc_t *dev) /* NOTE: This is an erratum in the STPC Atlas programming manual, the programming manuals for the other STPC chipsets say 0x80, which is indeed multi-function (as the STPC Atlas programming manual - indicates as well, and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */ + indicates as well), and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */ dev->pci_conf[2][0x0e] = /*0x40*/ 0x80; dev->pci_conf[2][0x10] = 0x01; @@ -861,22 +833,22 @@ stpc_setup(stpc_t *dev) /* USB */ if (dev->usb) { - dev->pci_conf[3][0x00] = 0x4a; - dev->pci_conf[3][0x01] = 0x10; - dev->pci_conf[3][0x02] = 0x30; - dev->pci_conf[3][0x03] = 0x02; + dev->pci_conf[3][0x00] = dev->local >> 16; + dev->pci_conf[3][0x01] = dev->local >> 24; + dev->pci_conf[3][0x02] = 0x30; + dev->pci_conf[3][0x03] = 0x02; - dev->pci_conf[3][0x06] = 0x80; - dev->pci_conf[3][0x07] = 0x02; + dev->pci_conf[3][0x06] = 0x80; + dev->pci_conf[3][0x07] = 0x02; - dev->pci_conf[3][0x09] = 0x10; - dev->pci_conf[3][0x0a] = 0x03; - dev->pci_conf[3][0x0b] = 0x0c; + dev->pci_conf[3][0x09] = 0x10; + dev->pci_conf[3][0x0a] = 0x03; + dev->pci_conf[3][0x0b] = 0x0c; /* NOTE: This is an erratum in the STPC Atlas programming manual, the programming manuals for the other STPC chipsets say 0x80, which is indeed multi-function (as the STPC Atlas programming manual - indicates as well, and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */ - dev->pci_conf[3][0x0e] = /*0x40*/ 0x80; + indicates as well), and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */ + dev->pci_conf[3][0x0e] = /*0x40*/ 0x80; } /* PCI setup */ @@ -912,11 +884,10 @@ stpc_init(const device_t *info) pci_add_card(PCI_ADD_NORTHBRIDGE, stpc_nb_read, stpc_nb_write, dev); dev->ide_slot = pci_add_card(PCI_ADD_SOUTHBRIDGE, stpc_isab_read, stpc_isab_write, dev); - if (dev->local & STPC_IDE_ATLAS) - dev->ide_slot = pci_add_card(PCI_ADD_SOUTHBRIDGE, stpc_ide_read, stpc_ide_write, dev); - if (dev->local & STPC_USB) { - dev->usb = device_add(&usb_device); - pci_add_card(PCI_ADD_SOUTHBRIDGE, stpc_usb_read, stpc_usb_write, dev); + if (dev->local == STPC_ATLAS) { + dev->ide_slot = pci_add_card(PCI_ADD_SOUTHBRIDGE, stpc_ide_read, stpc_ide_write, dev); + dev->usb = device_add(&usb_device); + pci_add_card(PCI_ADD_SOUTHBRIDGE, stpc_usb_read, stpc_usb_write, dev); } dev->bm[0] = device_add_inst(&sff8038i_device, 1); @@ -978,43 +949,43 @@ stpc_lpt_handlers(stpc_lpt_t *dev, uint8_t val) uint8_t old_addr = (dev->reg1 & 0x03), new_addr = (val & 0x03); switch (old_addr) { - case 0x1: - lpt3_remove(); - break; + case 0x1: + lpt3_remove(); + break; - case 0x2: - lpt1_remove(); - break; + case 0x2: + lpt1_remove(); + break; - case 0x3: - lpt2_remove(); - break; + case 0x3: + lpt2_remove(); + break; } switch (new_addr) { - case 0x1: - stpc_log("STPC: Remapping parallel port to LPT3\n"); - lpt3_init(0x3bc); - break; + case 0x1: + stpc_log("STPC: Remapping parallel port to LPT3\n"); + lpt3_init(0x3bc); + break; - case 0x2: - stpc_log("STPC: Remapping parallel port to LPT1\n"); - lpt1_init(0x378); - break; + case 0x2: + stpc_log("STPC: Remapping parallel port to LPT1\n"); + lpt1_init(0x378); + break; - case 0x3: - stpc_log("STPC: Remapping parallel port to LPT2\n"); - lpt2_init(0x278); - break; + case 0x3: + stpc_log("STPC: Remapping parallel port to LPT2\n"); + lpt2_init(0x278); + break; - default: - stpc_log("STPC: Disabling parallel port\n"); - break; + default: + stpc_log("STPC: Disabling parallel port\n"); + break; } dev->reg1 = (val & 0x08); dev->reg1 |= new_addr; - dev->reg1 |= 0x84; /* reserved bits that default to 1 - hardwired? */ + dev->reg1 |= 0x84; /* reserved bits that default to 1; hardwired? */ } @@ -1024,22 +995,22 @@ stpc_lpt_write(uint16_t addr, uint8_t val, void *priv) stpc_lpt_t *dev = (stpc_lpt_t *) priv; if (dev->unlocked < 2) { - /* Cheat a little bit: in reality, any write to any - I/O port is supposed to reset the unlock counter. */ - if ((addr == 0x3f0) && (val == 0x55)) - dev->unlocked++; - else - dev->unlocked = 0; + /* Cheat a little bit: in reality, any write to any + I/O port is supposed to reset the unlock counter. */ + if ((addr == 0x3f0) && (val == 0x55)) + dev->unlocked++; + else + dev->unlocked = 0; } else if (addr == 0x3f0) { - if (val == 0xaa) - dev->unlocked = 0; - else - dev->offset = val; + if (val == 0xaa) + dev->unlocked = 0; + else + dev->offset = val; } else if (dev->offset == 1) { - /* dev->reg1 is set by stpc_lpt_handlers */ - stpc_lpt_handlers(dev, val); + /* dev->reg1 is set by stpc_lpt_handlers */ + stpc_lpt_handlers(dev, val); } else if (dev->offset == 4) { - dev->reg4 = (val & 0x03); + dev->reg4 = (val & 0x03); } } @@ -1092,7 +1063,7 @@ const device_t stpc_client_device = { "STPC Client", DEVICE_PCI, - STPC_NB_CLIENT | STPC_ISAB_CLIENT, + STPC_CLIENT, stpc_init, stpc_close, stpc_reset, @@ -1106,7 +1077,7 @@ const device_t stpc_consumer2_device = { "STPC Consumer-II", DEVICE_PCI, - STPC_ISAB_CONSUMER2, + STPC_CONSUMER2, stpc_init, stpc_close, stpc_reset, @@ -1120,7 +1091,7 @@ const device_t stpc_elite_device = { "STPC Elite", DEVICE_PCI, - 0, + STPC_ELITE, stpc_init, stpc_close, stpc_reset, @@ -1134,7 +1105,7 @@ const device_t stpc_atlas_device = { "STPC Atlas", DEVICE_PCI, - STPC_IDE_ATLAS | STPC_USB, + STPC_ATLAS, stpc_init, stpc_close, stpc_reset, diff --git a/src/chipset/via_pipc.c b/src/chipset/via_pipc.c index bbf82e083..b11ac4548 100644 --- a/src/chipset/via_pipc.c +++ b/src/chipset/via_pipc.c @@ -423,7 +423,7 @@ pipc_read(int func, int addr, void *priv) ret |= 0x10; } } - else if ((func <= (pm_func + 2)) && !(dev->pci_isa_regs[0x85] & ((func == (pm_func + 1)) ? 0x04 : 0x08))) /* AC97 / MC97 */ + else if ((func <= (pm_func + 2)) && !(dev->pci_isa_regs[0x85] & ((func == (pm_func + 1)) ? 0x04 : 0x08)) && 0) /* AC97 / MC97; temporarily disabled while unimplemented */ ret = dev->ac97_regs[func - pm_func - 1][addr]; pipc_log("PIPC: read(%d, %02X) = %02X\n", func, addr, ret); @@ -805,6 +805,9 @@ pipc_write(int func, int addr, uint8_t val, void *priv) if ((func == (pm_func + 2)) && ((addr == 0x4a) || (addr == 0x4b) || (dev->pci_isa_regs[0x85] & 0x08))) return; + if (1) /* temporarily disabled while unimplemented */ + return; + switch (addr) { default: dev->ac97_regs[func - pm_func - 1][addr] = val; @@ -868,7 +871,9 @@ pipc_init(const device_t *info) dev->nvr = device_add(&via_nvr_device); - if (dev->local >= VIA_PIPC_596A) + if (dev->local >= VIA_PIPC_686B) + dev->smbus = device_add(&via_smbus_device); + else if (dev->local >= VIA_PIPC_596A) dev->smbus = device_add(&piix4_smbus_device); if (dev->local >= VIA_PIPC_596A) diff --git a/src/config.c b/src/config.c index 64641bef4..85c9c8d65 100644 --- a/src/config.c +++ b/src/config.c @@ -626,6 +626,7 @@ load_machine(void) } } cpu_s = (CPU *) &cpu_f->cpus[cpu]; + cpu_override = config_get_int(cat, "cpu_override", 0); cpu_waitstates = config_get_int(cat, "cpu_waitstates", 0); @@ -1798,6 +1799,10 @@ save_machine(void) config_set_string(cat, "cpu_family", (char *) cpu_f->internal_name); config_set_int(cat, "cpu_speed", cpu_f->cpus[cpu].rspeed); config_set_double(cat, "cpu_multi", cpu_f->cpus[cpu].multi); + if (cpu_override) + config_set_int(cat, "cpu_override", cpu_override); + else + config_delete_var(cat, "cpu_override"); /* Forwards compatibility with the previous CPU model system. */ config_delete_var(cat, "cpu_manufacturer"); diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index a54bd0b9e..5cb6f2c31 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -151,6 +151,7 @@ uint32_t smbase = 0x30000; cpu_family_t *cpu_f; CPU *cpu_s; +int cpu_override; int cpu_effective; int cpu_multi; double cpu_dmulti; @@ -331,6 +332,9 @@ cpu_get_family(const char *internal_name) uint8_t cpu_is_eligible(const cpu_family_t *cpu_family, int cpu, int machine) { + if (cpu_override > 1) /* full override */ + return 1; + /* Get machine. */ const machine_t *machine_s = &machines[machine]; @@ -344,6 +348,9 @@ cpu_is_eligible(const cpu_family_t *cpu_family, int cpu, int machine) if (!(cpu_family->package & packages)) /* package type */ return 0; + if (cpu_override) /* partial override */ + return 1; + const CPU *cpu_s = &cpu_family->cpus[cpu]; if (machine_s->cpu_block & cpu_s->cpu_type) /* CPU type blocklist */ diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index bb89d53ca..73f9524ad 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -375,6 +375,7 @@ extern const cpu_family_t cpu_families[]; extern const cpu_legacy_machine_t cpu_legacy_table[]; extern cpu_family_t *cpu_f; extern CPU *cpu_s; +extern int cpu_override; extern int cpu_isintel; extern int cpu_iscyrix; diff --git a/src/cpu/cpu_table.c b/src/cpu/cpu_table.c index 92ceb1bb8..585f85831 100644 --- a/src/cpu/cpu_table.c +++ b/src/cpu/cpu_table.c @@ -976,9 +976,9 @@ static const cpu_legacy_table_t cpus_pcjr[] = { }; static const cpu_legacy_table_t cpus_europc[] = { - {"8088", 4772728, 1}, - {"8088", 7159092, 1}, - {"8088", 9545456, 1}, + {"8088_europc", 4772728, 1}, + {"8088_europc", 7159092, 1}, + {"8088_europc", 9545456, 1}, {NULL, 0, 0} }; diff --git a/src/device/hwm_gl518sm.c b/src/device/hwm_gl518sm.c index ce347655e..f56b803ea 100644 --- a/src/device/hwm_gl518sm.c +++ b/src/device/hwm_gl518sm.c @@ -24,7 +24,7 @@ #include <86box/86box.h> #include <86box/device.h> #include <86box/io.h> -#include <86box/smbus.h> +#include <86box/i2c.h> #include <86box/hwm.h> @@ -41,17 +41,14 @@ typedef struct { uint16_t regs[32]; uint8_t addr_register; - uint8_t smbus_addr; + uint8_t i2c_addr, i2c_state; } gl518sm_t; -static uint8_t gl518sm_smbus_read_byte(uint8_t addr, void *priv); -static uint8_t gl518sm_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv); -static uint16_t gl518sm_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv); +static uint8_t gl518sm_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv); +static uint8_t gl518sm_i2c_read(void *bus, uint8_t addr, void *priv); static uint16_t gl518sm_read(gl518sm_t *dev, uint8_t reg); -static void gl518sm_smbus_write_byte(uint8_t addr, uint8_t val, void *priv); -static void gl518sm_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv); -static void gl518sm_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv); +static uint8_t gl518sm_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv); static uint8_t gl518sm_write(gl518sm_t *dev, uint8_t reg, uint16_t val); static void gl518sm_reset(gl518sm_t *dev); @@ -81,41 +78,45 @@ gl518sm_remap(gl518sm_t *dev, uint8_t addr) { gl518sm_log("GL518SM: remapping to SMBus %02Xh\n", addr); - smbus_removehandler(dev->smbus_addr, 1, - gl518sm_smbus_read_byte, gl518sm_smbus_read_byte_cmd, gl518sm_smbus_read_word_cmd, NULL, - gl518sm_smbus_write_byte, gl518sm_smbus_write_byte_cmd, gl518sm_smbus_write_word_cmd, NULL, - dev); + i2c_removehandler(i2c_smbus, dev->i2c_addr, 1, gl518sm_i2c_start, gl518sm_i2c_read, gl518sm_i2c_write, NULL, dev); - if (addr < 0x80) smbus_sethandler(addr, 1, - gl518sm_smbus_read_byte, gl518sm_smbus_read_byte_cmd, gl518sm_smbus_read_word_cmd, NULL, - gl518sm_smbus_write_byte, gl518sm_smbus_write_byte_cmd, gl518sm_smbus_write_word_cmd, NULL, - dev); + if (addr < 0x80) + i2c_sethandler(i2c_smbus, addr, 1, gl518sm_i2c_start, gl518sm_i2c_read, gl518sm_i2c_write, NULL, dev); - dev->smbus_addr = addr; + dev->i2c_addr = addr; } static uint8_t -gl518sm_smbus_read_byte(uint8_t addr, void *priv) +gl518sm_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv) { gl518sm_t *dev = (gl518sm_t *) priv; - return gl518sm_read(dev, dev->addr_register); + + dev->i2c_state = 0; + + return 1; } static uint8_t -gl518sm_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv) +gl518sm_i2c_read(void *bus, uint8_t addr, void *priv) { gl518sm_t *dev = (gl518sm_t *) priv; - return gl518sm_read(dev, cmd); -} + uint16_t read = gl518sm_read(dev, dev->addr_register); + uint8_t ret = 0; + if (dev->i2c_state == 0) + dev->i2c_state = 1; -static uint16_t -gl518sm_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv) -{ - gl518sm_t *dev = (gl518sm_t *) priv; - return gl518sm_read(dev, cmd); + if ((dev->i2c_state == 1) && (dev->addr_register >= 0x07) && (dev->addr_register <= 0x0c)) { /* two-byte registers: read MSB first */ + dev->i2c_state = 2; + ret = read >> 8; + } else { + ret = read; + dev->addr_register++; + } + + return ret; } @@ -157,37 +158,36 @@ gl518sm_read(gl518sm_t *dev, uint8_t reg) break; } - /* Duplicate the low byte to the high byte on single-byte registers, although real hardware behavior is undefined. */ - if ((reg < 0x07) || (reg > 0x0c)) - ret |= ret << 8; - gl518sm_log("GL518SM: read(%02X) = %04X\n", reg, ret); return ret; } -static void -gl518sm_smbus_write_byte(uint8_t addr, uint8_t val, void *priv) +static uint8_t +gl518sm_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv) { gl518sm_t *dev = (gl518sm_t *) priv; - dev->addr_register = val; -} + switch (dev->i2c_state++) { + case 0: + dev->addr_register = data; + break; -static void -gl518sm_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv) -{ - gl518sm_t *dev = (gl518sm_t *) priv; - gl518sm_write(dev, cmd, val); -} + case 1: + gl518sm_write(dev, dev->addr_register, (gl518sm_read(dev, dev->addr_register) & 0xff00) | data); + break; + case 2: + gl518sm_write(dev, dev->addr_register, (gl518sm_read(dev, dev->addr_register) << 8) | data); + break; -static void -gl518sm_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv) -{ - gl518sm_t *dev = (gl518sm_t *) priv; - gl518sm_write(dev, cmd, val); + default: + dev->i2c_state = 3; + return 0; + } + + return 1; } diff --git a/src/device/hwm_lm75.c b/src/device/hwm_lm75.c index 9f48bfb78..19aefe314 100644 --- a/src/device/hwm_lm75.c +++ b/src/device/hwm_lm75.c @@ -23,22 +23,19 @@ #include #include <86box/86box.h> #include <86box/device.h> -#include <86box/smbus.h> +#include <86box/i2c.h> #include <86box/hwm.h> #define LM75_TEMP_TO_REG(t) ((t) << 8) -static uint8_t lm75_smbus_read_byte(uint8_t addr, void *priv); -static uint8_t lm75_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv); -static uint16_t lm75_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv); -static void lm75_smbus_write_byte(uint8_t addr, uint8_t val, void *priv); -static void lm75_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv); -static void lm75_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv); +static uint8_t lm75_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv); +static uint8_t lm75_i2c_read(void *bus, uint8_t addr, void *priv); +static uint8_t lm75_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv); static void lm75_reset(lm75_t *dev); - +#define ENABLE_LM75_LOG 1 #ifdef ENABLE_LM75_LOG int lm75_do_log = ENABLE_LM75_LOG; @@ -64,61 +61,67 @@ lm75_remap(lm75_t *dev, uint8_t addr) { lm75_log("LM75: remapping to SMBus %02Xh\n", addr); - if (dev->smbus_addr < 0x80) smbus_removehandler(dev->smbus_addr, 1, - lm75_smbus_read_byte, lm75_smbus_read_byte_cmd, lm75_smbus_read_word_cmd, NULL, - lm75_smbus_write_byte, lm75_smbus_write_byte_cmd, lm75_smbus_write_word_cmd, NULL, - dev); + if (dev->i2c_addr < 0x80) + i2c_removehandler(i2c_smbus, dev->i2c_addr, 1, lm75_i2c_start, lm75_i2c_read, lm75_i2c_write, NULL, dev); - if (addr < 0x80) smbus_sethandler(addr, 1, - lm75_smbus_read_byte, lm75_smbus_read_byte_cmd, lm75_smbus_read_word_cmd, NULL, - lm75_smbus_write_byte, lm75_smbus_write_byte_cmd, lm75_smbus_write_word_cmd, NULL, - dev); + if (addr < 0x80) + i2c_sethandler(i2c_smbus, addr, 1, lm75_i2c_start, lm75_i2c_read, lm75_i2c_write, NULL, dev); - dev->smbus_addr = addr; + dev->i2c_addr = addr; } static uint8_t -lm75_smbus_read_byte(uint8_t addr, void *priv) +lm75_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv) { lm75_t *dev = (lm75_t *) priv; - return lm75_read(dev, dev->addr_register); + + dev->i2c_state = 0; + + return 1; } static uint8_t -lm75_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv) +lm75_i2c_read(void *bus, uint8_t addr, void *priv) { lm75_t *dev = (lm75_t *) priv; - return lm75_read(dev, cmd); -} + uint8_t ret = 0; -static uint16_t -lm75_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv) -{ - lm75_t *dev = (lm75_t *) priv; - uint8_t rethi = 0; - uint8_t retlo = 0; + if (dev->i2c_state == 0) + dev->i2c_state = 1; - switch (cmd & 0x3) { - case 0x0: /* temperature */ - rethi = lm75_read(dev, 0x0); - retlo = lm75_read(dev, 0x1); - break; - case 0x1: /* configuration */ - rethi = retlo = lm75_read(dev, 0x2); - break; - case 0x2: /* Thyst */ - rethi = lm75_read(dev, 0x3); - retlo = lm75_read(dev, 0x4); - break; - case 0x3: /* Tos */ - rethi = lm75_read(dev, 0x5); - retlo = lm75_read(dev, 0x6); - break; + /* The AS99127F hardware monitor uses the addresses of its LM75 devices + to access some of its proprietary registers. Pass this operation on to + the main monitor address through an internal I2C call, if necessary. */ + if ((dev->addr_register > 0x7) && ((dev->addr_register & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80)) { + i2c_start(i2c_smbus, dev->as99127f_i2c_addr, 1); + i2c_write(i2c_smbus, dev->as99127f_i2c_addr, dev->addr_register); + ret = i2c_read(i2c_smbus, dev->as99127f_i2c_addr); + i2c_stop(i2c_smbus, dev->as99127f_i2c_addr); + } else { + switch (dev->addr_register & 0x3) { + case 0x0: /* temperature */ + ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x0 : 0x1); + break; + + case 0x1: /* configuration */ + ret = lm75_read(dev, 0x2); + break; + + case 0x2: /* Thyst */ + ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x3 : 0x4); + break; + case 0x3: /* Tos */ + ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x5 : 0x6); + break; + } } - return (retlo << 8) | rethi; /* byte-swapped for some reason */ + if (++dev->i2c_state > 2) + dev->i2c_state = 2; + + return ret; } @@ -127,12 +130,7 @@ lm75_read(lm75_t *dev, uint8_t reg) { uint8_t ret; - /* The AS99127F hardware monitor uses the addresses of its LM75 devices - to access some of its proprietary registers. Pass this operation on to - the main monitor address through an internal SMBus call, if necessary. */ - if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_smbus_addr < 0x80)) - ret = smbus_read_byte_cmd(dev->as99127f_smbus_addr, reg); - else if ((reg & 0x7) == 0x0) /* temperature high byte */ + if ((reg & 0x7) == 0x0) /* temperature high byte */ ret = LM75_TEMP_TO_REG(dev->values->temperatures[dev->local >> 8]) >> 8; else if ((reg & 0x7) == 0x1) /* temperature low byte */ ret = LM75_TEMP_TO_REG(dev->values->temperatures[dev->local >> 8]); @@ -145,47 +143,54 @@ lm75_read(lm75_t *dev, uint8_t reg) } -static void -lm75_smbus_write_byte(uint8_t addr, uint8_t val, void *priv) +static uint8_t +lm75_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv) { lm75_t *dev = (lm75_t *) priv; - dev->addr_register = val; -} - -static void -lm75_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv) -{ - lm75_t *dev = (lm75_t *) priv; - lm75_write(dev, cmd, val); -} - - -static void -lm75_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv) -{ - lm75_t *dev = (lm75_t *) priv; - uint8_t valhi = (val >> 8); - uint8_t vallo = (val & 0xff); - - switch (cmd & 0x3) { - case 0x0: /* temperature */ - lm75_write(dev, 0x0, valhi); - lm75_write(dev, 0x1, vallo); - break; - case 0x1: /* configuration */ - lm75_write(dev, 0x2, vallo); - break; - case 0x2: /* Thyst */ - lm75_write(dev, 0x3, valhi); - lm75_write(dev, 0x4, vallo); - break; - case 0x3: /* Tos */ - lm75_write(dev, 0x5, valhi); - lm75_write(dev, 0x6, vallo); - break; - break; + if ((dev->i2c_state > 2) || ((dev->i2c_state == 2) && ((dev->addr_register & 0x3) == 0x1))) { + return 0; + } else if (dev->i2c_state == 0) { + dev->i2c_state = 1; + /* Linux lm75.c driver relies on the address register not changing if bit 2 is set. */ + if ((dev->as99127f_i2c_addr < 0x80) || !(data & 0x04)) + dev->addr_register = data; + return 1; } + + /* The AS99127F hardware monitor uses the addresses of its LM75 devices + to access some of its proprietary registers. Pass this operation on to + the main monitor address through an internal I2C call, if necessary. */ + if ((dev->addr_register > 0x7) && ((dev->addr_register & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80)) { + i2c_start(i2c_smbus, dev->as99127f_i2c_addr, 0); + i2c_write(i2c_smbus, dev->as99127f_i2c_addr, dev->addr_register); + i2c_write(i2c_smbus, dev->as99127f_i2c_addr, data); + i2c_stop(i2c_smbus, dev->as99127f_i2c_addr); + return 1; + } else { + switch (dev->addr_register & 0x3) { + case 0x0: /* temperature */ + lm75_write(dev, (dev->i2c_state == 1) ? 0x0 : 0x1, data); + break; + + case 0x1: /* configuration */ + lm75_write(dev, 0x2, data); + break; + + case 0x2: /* Thyst */ + lm75_write(dev, (dev->i2c_state == 1) ? 0x3 : 0x4, data); + break; + + case 0x3: /* Tos */ + lm75_write(dev, (dev->i2c_state == 1) ? 0x5 : 0x6, data); + break; + } + } + + if (dev->i2c_state == 1) + dev->i2c_state = 2; + + return 1; } @@ -194,14 +199,6 @@ lm75_write(lm75_t *dev, uint8_t reg, uint8_t val) { lm75_log("LM75: write(%02X, %02X)\n", reg, val); - /* The AS99127F hardware monitor uses the addresses of its LM75 devices - to access some of its proprietary registers. Pass this operation on to - the main monitor address through an internal SMBus call, if necessary. */ - if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_smbus_addr < 0x80)) { - smbus_write_byte_cmd(dev->as99127f_smbus_addr, reg, val); - return 1; - } - uint8_t reg_idx = (reg & 0x7); if ((reg_idx <= 0x1) || (reg_idx == 0x7)) @@ -247,7 +244,7 @@ lm75_init(const device_t *info) hwm_values.temperatures[dev->local >> 8] = 30; dev->values = &hwm_values; - dev->as99127f_smbus_addr = 0x80; + dev->as99127f_i2c_addr = 0x80; lm75_reset(dev); diff --git a/src/device/hwm_lm78.c b/src/device/hwm_lm78.c index 8e133ffbd..ad4cb7425 100644 --- a/src/device/hwm_lm78.c +++ b/src/device/hwm_lm78.c @@ -25,11 +25,11 @@ #include <86box/device.h> #include <86box/io.h> #include "cpu.h" -#include <86box/smbus.h> +#include <86box/i2c.h> #include <86box/hwm.h> -#define LM78_SMBUS 0x010000 +#define LM78_I2C 0x010000 #define LM78_W83781D 0x020000 #define LM78_AS99127F_REV1 0x040000 #define LM78_AS99127F_REV2 0x080000 @@ -56,19 +56,16 @@ typedef struct { uint8_t addr_register; uint8_t data_register; - uint8_t smbus_addr; + uint8_t i2c_addr, i2c_state; } lm78_t; +static uint8_t lm78_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv); static uint8_t lm78_isa_read(uint16_t port, void *priv); -static uint8_t lm78_smbus_read_byte(uint8_t addr, void *priv); -static uint8_t lm78_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv); -static uint16_t lm78_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv); +static uint8_t lm78_i2c_read(void *bus, uint8_t addr, void *priv); static uint8_t lm78_read(lm78_t *dev, uint8_t reg, uint8_t bank); static void lm78_isa_write(uint16_t port, uint8_t val, void *priv); -static void lm78_smbus_write_byte(uint8_t addr, uint8_t val, void *priv); -static void lm78_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv); -static void lm78_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv); +static uint8_t lm78_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv); static uint8_t lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank); static void lm78_reset(lm78_t *dev, uint8_t initialization); @@ -98,34 +95,40 @@ lm78_remap(lm78_t *dev, uint8_t addr) { lm75_t *lm75; - if (!(dev->local & LM78_SMBUS)) return; + if (!(dev->local & LM78_I2C)) return; lm78_log("LM78: remapping to SMBus %02Xh\n", addr); - smbus_removehandler(dev->smbus_addr, 1, - lm78_smbus_read_byte, lm78_smbus_read_byte_cmd, lm78_smbus_read_word_cmd, NULL, - lm78_smbus_write_byte, lm78_smbus_write_byte_cmd, lm78_smbus_write_word_cmd, NULL, - dev); + i2c_removehandler(i2c_smbus, dev->i2c_addr, 1, lm78_i2c_start, lm78_i2c_read, lm78_i2c_write, NULL, dev); - if (addr < 0x80) smbus_sethandler(addr, 1, - lm78_smbus_read_byte, lm78_smbus_read_byte_cmd, lm78_smbus_read_word_cmd, NULL, - lm78_smbus_write_byte, lm78_smbus_write_byte_cmd, lm78_smbus_write_word_cmd, NULL, - dev); + if (addr < 0x80) + i2c_sethandler(i2c_smbus, addr, 1, lm78_i2c_start, lm78_i2c_read, lm78_i2c_write, NULL, dev); - dev->smbus_addr = addr; + dev->i2c_addr = addr; if (dev->local & LM78_AS99127F) { - /* Store the main SMBus address on the LM75 devices to ensure reads/writes + /* Store the main I2C address on the LM75 devices to ensure reads/writes to the AS99127F's proprietary registers are passed through to this side. */ for (uint8_t i = 0; i <= 1; i++) { lm75 = device_get_priv(dev->lm75[i]); if (lm75) - lm75->as99127f_smbus_addr = dev->smbus_addr; + lm75->as99127f_i2c_addr = dev->i2c_addr; } } } +static uint8_t +lm78_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv) +{ + lm78_t *dev = (lm78_t *) priv; + + dev->i2c_state = 0; + + return 1; +} + + static uint8_t lm78_isa_read(uint16_t port, void *priv) { @@ -159,26 +162,11 @@ lm78_isa_read(uint16_t port, void *priv) static uint8_t -lm78_smbus_read_byte(uint8_t addr, void *priv) +lm78_i2c_read(void *bus, uint8_t addr, void *priv) { lm78_t *dev = (lm78_t *) priv; - return lm78_read(dev, dev->addr_register, LM78_WINBOND_BANK); -} - -static uint8_t -lm78_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv) -{ - lm78_t *dev = (lm78_t *) priv; - return lm78_read(dev, cmd, LM78_WINBOND_BANK); -} - - -static uint16_t -lm78_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv) -{ - lm78_t *dev = (lm78_t *) priv; - return (lm78_read(dev, cmd, LM78_WINBOND_BANK) << 8) | lm78_read(dev, cmd, LM78_WINBOND_BANK); + return lm78_read(dev, dev->addr_register++, LM78_WINBOND_BANK); } @@ -264,27 +252,18 @@ lm78_isa_write(uint16_t port, uint8_t val, void *priv) } -static void -lm78_smbus_write_byte(uint8_t addr, uint8_t val, void *priv) +static uint8_t +lm78_i2c_write(void *bus, uint8_t addr, uint8_t val, void *priv) { lm78_t *dev = (lm78_t *) priv; - dev->addr_register = val; -} + if (dev->i2c_state == 0) { + dev->i2c_state = 1; + dev->addr_register = val; + } else + lm78_write(dev, dev->addr_register++, val, LM78_WINBOND_BANK); -static void -lm78_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv) -{ - lm78_t *dev = (lm78_t *) priv; - lm78_write(dev, cmd, val, LM78_WINBOND_BANK); -} - - -static void -lm78_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv) -{ - lm78_t *dev = (lm78_t *) priv; - lm78_write(dev, cmd, val, LM78_WINBOND_BANK); + return 1; } @@ -354,13 +333,13 @@ lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank) switch (reg) { case 0x40: - if (val & 0x80) /* INITIALIZATION bit resets all registers except main SMBus address */ + if (val & 0x80) /* INITIALIZATION bit resets all registers except main I2C address */ lm78_reset(dev, 1); break; case 0x48: - /* set main SMBus address */ - if (dev->local & LM78_SMBUS) + /* set main I2C address */ + if (dev->local & LM78_I2C) lm78_remap(dev, dev->regs[0x48] & 0x7f); break; @@ -376,8 +355,8 @@ lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank) break; case 0x4a: - /* set LM75 SMBus addresses (Winbond only) */ - if (dev->local & LM78_SMBUS) { + /* set LM75 I2C addresses (Winbond only) */ + if (dev->local & LM78_I2C) { for (uint8_t i = 0; i <= 1; i++) { lm75 = device_get_priv(dev->lm75[i]); if (!lm75) @@ -412,10 +391,10 @@ lm78_reset(lm78_t *dev, uint8_t initialization) dev->regs[0x40] = 0x08; dev->regs[0x46] = 0x40; dev->regs[0x47] = 0x50; - if (dev->local & LM78_SMBUS) { - if (!initialization) /* don't reset main SMBus address if the reset was triggered by the INITIALIZATION bit */ - dev->smbus_addr = 0x2d; - dev->regs[0x48] = dev->smbus_addr; + if (dev->local & LM78_I2C) { + if (!initialization) /* don't reset main I2C address if the reset was triggered by the INITIALIZATION bit */ + dev->i2c_addr = 0x2d; + dev->regs[0x48] = dev->i2c_addr; if (dev->local & LM78_WINBOND) dev->regs[0x4a] = 0x01; } else { @@ -469,7 +448,7 @@ lm78_reset(lm78_t *dev, uint8_t initialization) dev->regs[0x49] = 0x40; } - lm78_remap(dev, dev->smbus_addr); + lm78_remap(dev, dev->i2c_addr); } @@ -519,7 +498,7 @@ lm78_init(const device_t *info) /* Set chip-specific default values. */ if (dev->local & LM78_AS99127F) { - /* AS99127: different -12V Rin value (bruteforced) */ + /* AS99127: different -12V Rin value (bruteforced) */ defaults.voltages[5] = LM78_NEG_VOLTAGE(12000, 2400); } else if (dev->local & LM78_W83782D) { /* W83782D: different negative voltage formula */ @@ -536,7 +515,7 @@ lm78_init(const device_t *info) dev->lm75[i] = (device_t *) malloc(sizeof(device_t)); memcpy(dev->lm75[i], &lm75_w83781d_device, sizeof(device_t)); dev->lm75[i]->local = (i + 1) << 8; - if (dev->local & LM78_SMBUS) + if (dev->local & LM78_I2C) dev->lm75[i]->local |= 0x48 + i; device_add(dev->lm75[i]); } else { @@ -558,7 +537,7 @@ lm78_init(const device_t *info) const device_t lm78_device = { "National Semiconductor LM78 Hardware Monitor", DEVICE_ISA, - 0x290 | LM78_SMBUS, + 0x290 | LM78_I2C, lm78_init, lm78_close, NULL, { NULL }, NULL, NULL, NULL @@ -569,19 +548,19 @@ const device_t lm78_device = { const device_t w83781d_device = { "Winbond W83781D Hardware Monitor", DEVICE_ISA, - 0x290 | LM78_SMBUS | LM78_W83781D, + 0x290 | LM78_I2C | LM78_W83781D, lm78_init, lm78_close, NULL, { NULL }, NULL, NULL, NULL }; -/* The ASUS AS99127F is a customized W83781D with no ISA interface (SMBus +/* The ASUS AS99127F is a customized W83781D with no ISA interface (I2C only), added proprietary registers and different chip/vendor IDs. */ const device_t as99127f_device = { "ASUS AS99127F Rev. 1 Hardware Monitor", DEVICE_ISA, - LM78_SMBUS | LM78_AS99127F_REV1, + LM78_I2C | LM78_AS99127F_REV1, lm78_init, lm78_close, NULL, { NULL }, NULL, NULL, NULL @@ -592,7 +571,7 @@ const device_t as99127f_device = { const device_t as99127f_rev2_device = { "ASUS AS99127F Rev. 2 Hardware Monitor", DEVICE_ISA, - LM78_SMBUS | LM78_AS99127F_REV2, + LM78_I2C | LM78_AS99127F_REV2, lm78_init, lm78_close, NULL, { NULL }, NULL, NULL, NULL @@ -603,7 +582,7 @@ const device_t as99127f_rev2_device = { const device_t w83782d_device = { "Winbond W83782D Hardware Monitor", DEVICE_ISA, - 0x290 | LM78_SMBUS | LM78_W83782D, + 0x290 | LM78_I2C | LM78_W83782D, lm78_init, lm78_close, NULL, { NULL }, NULL, NULL, NULL diff --git a/src/device/hwm_vt82c686.c b/src/device/hwm_vt82c686.c index d91c0aab7..70a01a6ac 100644 --- a/src/device/hwm_vt82c686.c +++ b/src/device/hwm_vt82c686.c @@ -24,7 +24,6 @@ #include <86box/86box.h> #include <86box/device.h> #include <86box/io.h> -#include <86box/smbus.h> #include <86box/hwm.h> diff --git a/src/device/i2c.c b/src/device/i2c.c new file mode 100644 index 000000000..c17f3487c --- /dev/null +++ b/src/device/i2c.c @@ -0,0 +1,329 @@ +/* + * 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 I2C bus and its operations. + * + * + * + * Authors: RichardG, + * + * Copyright 2020 RichardG. + */ +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/i2c.h> + + +#define NADDRS 128 /* I2C supports 128 addresses */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + + +typedef struct _i2c_ { + uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv); + uint8_t (*read)(void *bus, uint8_t addr, void *priv); + uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv); + void (*stop)(void *bus, uint8_t addr, void *priv); + + void *priv; + + struct _i2c_ *prev, *next; +} i2c_t; + +typedef struct { + char *name; + i2c_t *devices[NADDRS], *last[NADDRS]; +} i2c_bus_t; + + +void *i2c_smbus; + + +#ifdef ENABLE_I2C_LOG +int i2c_do_log = ENABLE_I2C_LOG; + + +static void +i2c_log(const char *fmt, ...) +{ + va_list ap; + + if (i2c_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +#define i2c_log(fmt, ...) +#endif + + +void * +i2c_addbus(char *name) +{ + i2c_bus_t *bus = (i2c_bus_t *) malloc(sizeof(i2c_bus_t)); + memset(bus, 0, sizeof(i2c_bus_t)); + + bus->name = name; + + return bus; +} + + +void +i2c_removebus(void *bus_handle) +{ + int c; + i2c_t *p, *q; + i2c_bus_t *bus = (i2c_bus_t *) bus_handle; + + if (!bus_handle) + return; + + for (c = 0; c < NADDRS; c++) { + p = bus->devices[c]; + if (!p) + continue; + while(p) { + q = p->next; + free(p); + p = q; + } + } + + free(bus); +} + + +char * +i2c_getbusname(void *bus_handle) +{ + i2c_bus_t *bus = (i2c_bus_t *) bus_handle; + + if (!bus_handle) + return(NULL); + + return(bus->name); +} + + +void +i2c_sethandler(void *bus_handle, uint8_t base, int size, + uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv), + uint8_t (*read)(void *bus, uint8_t addr, void *priv), + uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv), + void (*stop)(void *bus, uint8_t addr, void *priv), + void *priv) +{ + int c; + i2c_t *p, *q = NULL; + i2c_bus_t *bus = (i2c_bus_t *) bus_handle; + + if (!bus_handle || ((base + size) > NADDRS)) + return; + + for (c = 0; c < size; c++) { + p = bus->last[base + c]; + q = (i2c_t *) malloc(sizeof(i2c_t)); + memset(q, 0, sizeof(i2c_t)); + if (p) { + p->next = q; + q->prev = p; + } else { + bus->devices[base + c] = q; + q->prev = NULL; + } + + q->start = start; + q->read = read; + q->write = write; + q->stop = stop; + + q->priv = priv; + q->next = NULL; + + bus->last[base + c] = q; + } +} + + +void +i2c_removehandler(void *bus_handle, uint8_t base, int size, + uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv), + uint8_t (*read)(void *bus, uint8_t addr, void *priv), + uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv), + void (*stop)(void *bus, uint8_t addr, void *priv), + void *priv) +{ + int c; + i2c_t *p, *q; + i2c_bus_t *bus = (i2c_bus_t *) bus_handle; + + if (!bus_handle || ((base + size) > NADDRS)) + return; + + for (c = 0; c < size; c++) { + p = bus->devices[base + c]; + if (!p) + continue; + while(p) { + q = p->next; + if ((p->start == start) && (p->read == read) && (p->write == write) && (p->stop == stop) && (p->priv == priv)) { + if (p->prev) + p->prev->next = p->next; + else + bus->devices[base + c] = p->next; + if (p->next) + p->next->prev = p->prev; + else + bus->last[base + c] = p->prev; + free(p); + p = NULL; + break; + } + p = q; + } + } +} + + +void +i2c_handler(int set, void *bus_handle, uint8_t base, int size, + uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv), + uint8_t (*read)(void *bus, uint8_t addr, void *priv), + uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv), + void (*stop)(void *bus, uint8_t addr, void *priv), + void *priv) +{ + if (set) + i2c_sethandler(bus_handle, base, size, start, read, write, stop, priv); + else + i2c_removehandler(bus_handle, base, size, start, read, write, stop, priv); +} + + +uint8_t +i2c_has_device(void *bus_handle, uint8_t addr) +{ + i2c_bus_t *bus = (i2c_bus_t *) bus_handle; + + if (!bus) + return 0; + + i2c_log("I2C: has_device(%s, %02X) = %d\n", bus->name, addr, !!bus->devices[addr]); + + return(!!bus->devices[addr]); +} + + +uint8_t +i2c_start(void *bus_handle, uint8_t addr, uint8_t read) +{ + uint8_t ret = 0; + i2c_bus_t *bus = (i2c_bus_t *) bus_handle; + i2c_t *p; + + if (!bus) + return(ret); + + p = bus->devices[addr]; + if (p) { + while(p) { + if (p->start) { + ret |= p->start(bus_handle, addr, read, p->priv); + } + p = p->next; + } + } + + i2c_log("I2C: start(%s, %02X)\n", bus->name, addr); + + return(ret); +} + + +uint8_t +i2c_read(void *bus_handle, uint8_t addr) +{ + uint8_t ret = 0; + i2c_bus_t *bus = (i2c_bus_t *) bus_handle; + i2c_t *p; + + if (!bus) + return(ret); + + p = bus->devices[addr]; + if (p) { + while(p) { + if (p->read) { + ret = p->read(bus_handle, addr, p->priv); + break; + } + p = p->next; + } + } + + i2c_log("I2C: read(%s, %02X) = %02X\n", bus->name, addr, ret); + + return(ret); +} + + +uint8_t +i2c_write(void *bus_handle, uint8_t addr, uint8_t data) +{ + uint8_t ret = 0; + i2c_t *p; + i2c_bus_t *bus = (i2c_bus_t *) bus_handle; + + if (!bus) + return(ret); + + p = bus->devices[addr]; + if (p) { + while(p) { + if (p->write) { + ret |= p->write(bus_handle, addr, data, p->priv); + } + p = p->next; + } + } + + i2c_log("I2C: write(%s, %02X, %02X) = %d\n", bus->name, addr, data, ret); + + return(ret); +} + + +void +i2c_stop(void *bus_handle, uint8_t addr) +{ + i2c_bus_t *bus = (i2c_bus_t *) bus_handle; + i2c_t *p; + + if (!bus) + return; + + p = bus->devices[addr]; + if (p) { + while(p) { + if (p->stop) { + p->stop(bus_handle, addr, p->priv); + } + p = p->next; + } + } + + i2c_log("I2C: stop(%s, %02X)\n", bus->name, addr); +} diff --git a/src/device/i2c_eeprom.c b/src/device/i2c_eeprom.c new file mode 100644 index 000000000..a27403de8 --- /dev/null +++ b/src/device/i2c_eeprom.c @@ -0,0 +1,145 @@ +/* + * 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 24Cxx series of I2C EEPROMs. + * + * + * + * Authors: RichardG, + * + * Copyright 2020 RichardG. + */ +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include +#include <86box/86box.h> +#include <86box/i2c.h> + + +typedef struct { + void *i2c; + uint8_t addr, *data, writable; + + uint32_t addr_mask, addr_register; + uint8_t addr_len, addr_pos; +} i2c_eeprom_t; + + +#ifdef ENABLE_I2C_EEPROM_LOG +int i2c_eeprom_do_log = ENABLE_I2C_EEPROM_LOG; + + +static void +i2c_eeprom_log(const char *fmt, ...) +{ + va_list ap; + + if (i2c_eeprom_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +#define i2c_eeprom_log(fmt, ...) +#endif + + +uint8_t +i2c_eeprom_start(void *bus, uint8_t addr, uint8_t read, void *priv) +{ + i2c_eeprom_t *dev = (i2c_eeprom_t *) priv; + + i2c_eeprom_log("I2C EEPROM %s %02X: start()\n", i2c_getbusname(dev->i2c), dev->addr); + + dev->addr_pos = 0; + dev->addr_register = (addr << dev->addr_len) & dev->addr_mask; + + return 1; +} + + +uint8_t +i2c_eeprom_read(void *bus, uint8_t addr, void *priv) +{ + i2c_eeprom_t *dev = (i2c_eeprom_t *) priv; + uint8_t ret = dev->data[dev->addr_register]; + + i2c_eeprom_log("I2C EEPROM %s %02X: read(%06X) = %02X\n", i2c_getbusname(dev->i2c), dev->addr, dev->addr_register, ret); + if (++dev->addr_register > dev->addr_mask) /* roll-over */ + dev->addr_register = 0; + + return ret; +} + + +uint8_t +i2c_eeprom_write(void *bus, uint8_t addr, uint8_t data, void *priv) +{ + i2c_eeprom_t *dev = (i2c_eeprom_t *) priv; + + if (dev->addr_pos < dev->addr_len) { + dev->addr_register <<= 8; + dev->addr_register |= data; + dev->addr_register &= (1 << dev->addr_len) - 1; + dev->addr_register |= addr << dev->addr_len; + dev->addr_register &= dev->addr_mask; + i2c_eeprom_log("I2C EEPROM %s %02X: write(address, %04X)\n", i2c_getbusname(dev->i2c), dev->addr, dev->addr_register); + dev->addr_pos += 8; + } else { + i2c_eeprom_log("I2C EEPROM %s %02X: write(%06X, %02X) = %d\n", i2c_getbusname(dev->i2c), dev->addr, dev->addr_register, data, !!dev->writable); + if (dev->writable) + dev->data[dev->addr_register] = data; + if (++dev->addr_register > dev->addr_mask) /* roll-over */ + dev->addr_register = 0; + return dev->writable; + } + + return 1; +} + + +void * +i2c_eeprom_init(void *i2c, uint8_t addr, uint8_t *data, uint32_t size, uint8_t writable) +{ + i2c_eeprom_t *dev = (i2c_eeprom_t *) malloc(sizeof(i2c_eeprom_t)); + memset(dev, 0, sizeof(i2c_eeprom_t)); + + size &= 0x7fffff; /* address space limit of 8 MB = 7 bits from I2C address + 16 bits */ + + i2c_eeprom_log("I2C EEPROM %s %02X: init(%d, %d)\n", i2c_getbusname(i2c), addr, size, writable); + + dev->i2c = i2c; + dev->addr = addr; + dev->data = data; + dev->writable = writable; + + dev->addr_len = (size >= 4096) ? 16 : 8; /* use 16-bit addresses on 24C32 and above */ + dev->addr_mask = size - 1; + + i2c_sethandler(dev->i2c, dev->addr & ~(dev->addr_mask >> dev->addr_len), (dev->addr_mask >> dev->addr_len) + 1, i2c_eeprom_start, i2c_eeprom_read, i2c_eeprom_write, NULL, dev); + + return dev; +} + + +void +i2c_eeprom_close(void *dev_handle) +{ + i2c_eeprom_t *dev = (i2c_eeprom_t *) dev_handle; + + i2c_eeprom_log("I2C EEPROM %s %02X: close()\n", i2c_getbusname(dev->i2c), dev->addr); + + i2c_removehandler(dev->i2c, dev->addr & ~(dev->addr_mask >> dev->addr_len), (dev->addr_mask >> dev->addr_len) + 1, i2c_eeprom_start, i2c_eeprom_read, i2c_eeprom_write, NULL, dev); + + free(dev); +} diff --git a/src/device/i2c_gpio.c b/src/device/i2c_gpio.c new file mode 100644 index 000000000..08096123d --- /dev/null +++ b/src/device/i2c_gpio.c @@ -0,0 +1,347 @@ +/* + * 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 a GPIO-based I2C device. + * + * + * + * Authors: Sarah Walker, + * RichardG, + * + * Copyright 2008-2020 Sarah Walker. + * Copyright 2020 RichardG. + */ +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include +#include <86box/86box.h> +#include <86box/i2c.h> + + +enum { + TRANSMITTER_SLAVE = 1, + TRANSMITTER_MASTER = 2 +}; + +enum { + I2C_IDLE = 0, + I2C_RECEIVE, + I2C_RECEIVE_WAIT, + I2C_TRANSMIT_START, + I2C_TRANSMIT, + I2C_ACKNOWLEDGE, + I2C_NOTACKNOWLEDGE, + I2C_TRANSACKNOWLEDGE, + I2C_TRANSMIT_WAIT +}; + +enum { + SLAVE_IDLE = 0, + SLAVE_RECEIVEADDR, + SLAVE_RECEIVEDATA, + SLAVE_SENDDATA, + SLAVE_INVALID +}; + +typedef struct { + char *bus_name; + void *i2c; + uint8_t scl, sda, receive_wait_sda, state, slave_state, slave_addr, + slave_read, last_sda, pos, transmit, byte; +} i2c_gpio_t; + + +#ifdef ENABLE_I2C_GPIO_LOG +int i2c_gpio_do_log = ENABLE_I2C_GPIO_LOG; + + +static void +i2c_gpio_log(int level, const char *fmt, ...) +{ + va_list ap; + + if (i2c_gpio_do_log >= level) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +#define i2c_gpio_log(fmt, ...) +#endif + + +void * +i2c_gpio_init(char *bus_name) +{ + i2c_gpio_t *dev = (i2c_gpio_t *) malloc(sizeof(i2c_gpio_t)); + memset(dev, 0, sizeof(i2c_gpio_t)); + + i2c_gpio_log(1, "I2C GPIO %s: init()\n", bus_name); + + dev->bus_name = bus_name; + dev->i2c = i2c_addbus(dev->bus_name); + dev->scl = dev->sda = 1; + dev->slave_addr = 0xff; + + return dev; +} + + +void +i2c_gpio_close(void *dev_handle) +{ + i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle; + + i2c_gpio_log(1, "I2C GPIO %s: close()\n", dev->bus_name); + + i2c_removebus(dev->i2c); + + free(dev); +} + + +void +i2c_gpio_next_byte(i2c_gpio_t *dev) +{ + dev->byte = i2c_read(dev->i2c, dev->slave_addr); + i2c_gpio_log(1, "I2C GPIO %s: Transmitting data %02X\n", dev->bus_name, dev->byte); +} + + +uint8_t +i2c_gpio_write(i2c_gpio_t *dev) +{ + uint8_t i; + + switch (dev->slave_state) { + case SLAVE_IDLE: + i = dev->slave_addr; + dev->slave_addr = dev->byte >> 1; + dev->slave_read = dev->byte & 1; + + i2c_gpio_log(1, "I2C GPIO %s: Initiating %s address %02X\n", dev->bus_name, dev->slave_read ? "read from" : "write to", dev->slave_addr); + + if (!i2c_has_device(dev->i2c, dev->slave_addr) || + ((i == 0xff) && !i2c_start(dev->i2c, dev->slave_addr, dev->slave_read))) { /* start only once per transfer */ + dev->slave_state = SLAVE_INVALID; + dev->slave_addr = 0xff; + return I2C_NOTACKNOWLEDGE; + } + + if (dev->slave_read) { + dev->slave_state = SLAVE_SENDDATA; + dev->transmit = TRANSMITTER_SLAVE; + dev->byte = i2c_read(dev->i2c, dev->slave_addr); + } else { + dev->slave_state = SLAVE_RECEIVEADDR; + dev->transmit = TRANSMITTER_MASTER; + } + break; + + case SLAVE_RECEIVEADDR: + i2c_gpio_log(1, "I2C GPIO %s: Receiving address %02X\n", dev->bus_name, dev->byte); + dev->slave_state = dev->slave_read ? SLAVE_SENDDATA : SLAVE_RECEIVEDATA; + if (!i2c_write(dev->i2c, dev->slave_addr, dev->byte)) + return I2C_NOTACKNOWLEDGE; + break; + + case SLAVE_RECEIVEDATA: + i2c_gpio_log(1, "I2C GPIO %s: Receiving data %02X\n", dev->bus_name, dev->byte); + if (!i2c_write(dev->i2c, dev->slave_addr, dev->byte)) + return I2C_NOTACKNOWLEDGE; + break; + + case SLAVE_INVALID: + return I2C_NOTACKNOWLEDGE; + } + + return I2C_ACKNOWLEDGE; +} + + +void +i2c_gpio_stop(i2c_gpio_t *dev) +{ + i2c_gpio_log(1, "I2C GPIO %s: Stopping transfer\n", dev->bus_name); + + if (dev->slave_addr != 0xff) /* don't stop if no transfer was in progress */ + i2c_stop(dev->i2c, dev->slave_addr); + + dev->slave_addr = 0xff; + dev->slave_state = SLAVE_IDLE; + dev->transmit = TRANSMITTER_MASTER; +} + + +void +i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda) +{ + i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle; + + i2c_gpio_log(3, "I2C GPIO %s: scl=%d->%d sda=%d->%d last_valid_sda=%d state=%d\n", dev->bus_name, dev->scl, scl, dev->last_sda, sda, dev->sda, dev->state); + + switch (dev->state) { + case I2C_IDLE: + if (scl && dev->last_sda && !sda) { /* start condition; dev->scl check breaks NCR SDMS */ + i2c_gpio_log(2, "I2C GPIO %s: Start condition received (from IDLE)\n", dev->bus_name); + dev->state = I2C_RECEIVE; + dev->pos = 0; + } + break; + + case I2C_RECEIVE_WAIT: + if (!dev->scl && scl) + dev->state = I2C_RECEIVE; + else if (!dev->scl && !scl && dev->last_sda && sda) /* workaround for repeated start condition on Windows XP DDC */ + dev->receive_wait_sda = 1; + /* fall-through */ + + case I2C_RECEIVE: + if (!dev->scl && scl) { + dev->byte <<= 1; + if (sda) + dev->byte |= 1; + else + dev->byte &= 0xfe; + if (++dev->pos == 8) + dev->state = i2c_gpio_write(dev); + } else if (dev->scl && scl) { + if (sda && !dev->last_sda) { /* stop condition */ + i2c_gpio_log(2, "I2C GPIO %s: Stop condition received (from RECEIVE)\n", dev->bus_name); + dev->state = I2C_IDLE; + i2c_gpio_stop(dev); + } else if (!sda && dev->last_sda) { /* start condition */ + i2c_gpio_log(2, "I2C GPIO %s: Start condition received (from RECEIVE)\n", dev->bus_name); + dev->pos = 0; + dev->slave_state = SLAVE_IDLE; + } + } + break; + + case I2C_ACKNOWLEDGE: + if (!dev->scl && scl) { + i2c_gpio_log(2, "I2C GPIO %s: Acknowledging transfer to %02X\n", dev->bus_name, dev->slave_addr); + sda = 0; + dev->receive_wait_sda = 0; /* ack */ + dev->pos = 0; + dev->state = (dev->transmit == TRANSMITTER_MASTER) ? I2C_RECEIVE_WAIT : I2C_TRANSMIT; + } + break; + + case I2C_NOTACKNOWLEDGE: + if (!dev->scl && scl) { + i2c_gpio_log(2, "I2C GPIO %s: Not acknowledging transfer\n", dev->bus_name); + sda = 1; + dev->pos = 0; + dev->state = I2C_IDLE; + dev->slave_state = SLAVE_IDLE; + } + break; + + case I2C_TRANSACKNOWLEDGE: + if (!dev->scl && scl) { + if (sda) { /* not acknowledged; must be end of transfer */ + i2c_gpio_log(2, "I2C GPIO %s: End of transfer\n", dev->bus_name); + dev->state = I2C_IDLE; + i2c_gpio_stop(dev); + } else { /* next byte to transfer */ + dev->state = I2C_TRANSMIT_START; + i2c_gpio_next_byte(dev); + dev->pos = 0; + i2c_gpio_log(2, "I2C GPIO %s: Next byte = %02X\n", dev->bus_name, dev->byte); + } + } + break; + + case I2C_TRANSMIT_WAIT: + if (dev->scl && scl) { + if (dev->last_sda && !sda) { /* start condition */ + i2c_gpio_next_byte(dev); + dev->pos = 0; + i2c_gpio_log(2, "I2C GPIO %s: Next byte = %02X\n", dev->bus_name, dev->byte); + } + if (!dev->last_sda && sda) { /* stop condition */ + i2c_gpio_log(2, "I2C GPIO %s: Stop condition received (from TRANSMIT_WAIT)\n", dev->bus_name); + dev->state = I2C_IDLE; + i2c_gpio_stop(dev); + } + } + break; + + case I2C_TRANSMIT_START: + if (!dev->scl && scl) + dev->state = I2C_TRANSMIT; + if (dev->scl && scl && !dev->last_sda && sda) { /* stop condition */ + i2c_gpio_log(2, "I2C GPIO %s: Stop condition received (from TRANSMIT_START)\n", dev->bus_name); + dev->state = I2C_IDLE; + i2c_gpio_stop(dev); + } + /* fall-through */ + + case I2C_TRANSMIT: + if (!dev->scl && scl) { + dev->scl = scl; + if (!dev->pos) + i2c_gpio_log(2, "I2C GPIO %s: Transmit byte %02X\n", dev->bus_name, dev->byte); + dev->sda = sda = dev->byte & 0x80; + i2c_gpio_log(2, "I2C GPIO %s: Transmit bit %02X %d\n", dev->bus_name, dev->byte, dev->pos); + dev->byte <<= 1; + dev->pos++; + return; + } + if (dev->scl && !scl && (dev->pos == 8)) { + dev->state = I2C_TRANSACKNOWLEDGE; + i2c_gpio_log(2, "I2C GPIO %s: Acknowledge mode\n", dev->bus_name); + } + break; + } + + if (!dev->scl && scl) + dev->sda = sda; + dev->last_sda = sda; + dev->scl = scl; +} + + +uint8_t +i2c_gpio_get_scl(void *dev_handle) +{ + i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle; + return dev->scl; +} + + +uint8_t +i2c_gpio_get_sda(void *dev_handle) +{ + i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle; + switch (dev->state) { + case I2C_TRANSMIT: + case I2C_ACKNOWLEDGE: + return dev->sda; + + case I2C_RECEIVE_WAIT: + return dev->receive_wait_sda; + + default: + return 1; + } +} + + +void * +i2c_gpio_get_bus(void *dev_handle) +{ + i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle; + return dev->i2c; +} diff --git a/src/device/smbus.c b/src/device/smbus.c deleted file mode 100644 index dfd89a95e..000000000 --- a/src/device/smbus.c +++ /dev/null @@ -1,403 +0,0 @@ -/* - * 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. - * - * - * - * Authors: RichardG, - * - * Copyright 2020 RichardG. - */ -#include -#include -#include -#include -#include -#include -#define HAVE_STDARG_H -#include <86box/86box.h> -#include <86box/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, uint8_t len, 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]; - - -#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 - - -void -smbus_init(void) -{ - int c; - smbus_t *p, *q; - - if (!smbus_initialized) { - for (c=0; cprev; - free(p); - p = q; - } - p = NULL; - } - - /* smbus[c] should be NULL. */ - smbus[c] = smbus_last[c] = NULL; - } -} - - -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, uint8_t len, 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; - - if ((base + size) >= NADDRS) - return; - - 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, uint8_t len, 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; - - if ((base + size) >= NADDRS) - return; - - for (c = 0; c < size; c++) { - p = smbus[base + c]; - if (!p) - continue; - while(p) { - q = p->next; - 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 = q; - } - } -} - - -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, uint8_t len, 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; - } - } - - smbus_log("SMBus: read_byte(%02X) = %02X\n", addr, ret); - - 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; - } - } - - smbus_log("SMBus: read_byte_cmd(%02X, %02X) = %02X\n", addr, cmd, ret); - - 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; - } - } - - smbus_log("SMBus: read_word_cmd(%02X, %02X) = %04X\n", addr, cmd, ret); - - return(ret); -} - -uint8_t -smbus_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len) -{ - 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, len, p->priv)); - found++; - } - p = p->next; - } - } - - smbus_log("SMBus: read_block_cmd(%02X, %02X) = %02X\n", addr, cmd, len); - - 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; - } - } - - smbus_log("SMBus: write_byte(%02X, %02X)\n", addr, val); - - 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; - } - } - - smbus_log("SMBus: write_byte_cmd(%02X, %02X, %02X)\n", addr, cmd, val); - - 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; - } - } - - smbus_log("SMBus: write_word_cmd(%02X, %02X, %04X)\n", addr, cmd, val); - - 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; - } - } - - smbus_log("SMBus: write_block_cmd(%02X, %02X, %02X)\n", addr, cmd, len); - - return; -} diff --git a/src/device/smbus_piix4.c b/src/device/smbus_piix4.c index f9b68f6d8..8d457719f 100644 --- a/src/device/smbus_piix4.c +++ b/src/device/smbus_piix4.c @@ -25,7 +25,7 @@ #include <86box/io.h> #include <86box/device.h> #include <86box/timer.h> -#include <86box/smbus.h> +#include <86box/i2c.h> #include <86box/smbus_piix4.h> @@ -59,22 +59,28 @@ smbus_piix4_read(uint16_t addr, void *priv) 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) @@ -82,7 +88,7 @@ smbus_piix4_read(uint16_t addr, void *priv) break; } - smbus_piix4_log("SMBus PIIX4: read(%02x) = %02x\n", addr, ret); + smbus_piix4_log("SMBus PIIX4: read(%02X) = %02x\n", addr, ret); return ret; } @@ -92,96 +98,210 @@ 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; + uint8_t smbus_addr, cmd, read, block_len, prev_stat; + uint16_t timer_bytes = 0, i; - smbus_piix4_log("SMBus PIIX4: write(%02x, %02x)\n", addr, val); + smbus_piix4_log("SMBus PIIX4: write(%02X, %02X)\n", addr, val); prev_stat = dev->next_stat; - dev->next_stat = 0; + dev->next_stat = 0x00; 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 <<= 1) { + for (smbus_addr = 0x02; smbus_addr <= 0x10; smbus_addr <<= 1) { /* handle clearable bits */ if (val & smbus_addr) dev->stat &= ~smbus_addr; } break; + case 0x02: - dev->ctl = val & ~(0x40); /* START always reads 0 */ + dev->ctl = val & ((dev->local == SMBUS_VIA) ? 0x3f : 0x1f); 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 */ + if (prev_stat) { /* cancel only if a command is in progress */ timer_disable(&dev->response_timer); + dev->stat = 0x10; /* raise FAILED */ } } if (val & 0x40) { /* dispatch command if START is set */ + timer_bytes++; /* address */ + 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; + read = dev->addr & 0x01; + + /* Raise DEV_ERR if no device is at this address, or if the device returned NAK when starting the transfer. */ + if (!i2c_has_device(i2c_smbus, smbus_addr) || !i2c_start(i2c_smbus, smbus_addr, read)) { + dev->next_stat = 0x04; break; } - smbus_read = (dev->addr & 0x01); - /* decode the 3-bit command protocol */ - dev->next_stat = 0x2; /* raise INTER (command completed) by default */ - switch ((val >> 2) & 0x7) { + dev->next_stat = 0x02; /* raise INTER (command completed) by default */ + + /* Decode the command protocol. + VIA-specific modes (0x4 and [0x6:0xf]) are undocumented and required real hardware research. */ + cmd = (val >> 2) & 0xf; + smbus_piix4_log("SMBus PIIX4: protocol=%X cmd=%02X data0=%02X data1=%02X\n", cmd, dev->cmd, dev->data0, dev->data1); + switch (cmd) { case 0x0: /* quick R/W */ break; case 0x1: /* byte R/W */ - if (smbus_read) - dev->data0 = smbus_read_byte(smbus_addr); - else - smbus_write_byte(smbus_addr, dev->data0); + if (read) /* byte read */ + dev->data0 = i2c_read(i2c_smbus, smbus_addr); + else /* byte write */ + i2c_write(i2c_smbus, smbus_addr, dev->data0); + timer_bytes++; + 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); + /* command write */ + i2c_write(i2c_smbus, smbus_addr, dev->cmd); + timer_bytes++; + + if (read) /* byte read */ + dev->data0 = i2c_read(i2c_smbus, smbus_addr); + else /* byte write */ + i2c_write(i2c_smbus, smbus_addr, dev->data0); + timer_bytes++; + 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); + /* command write */ + i2c_write(i2c_smbus, smbus_addr, dev->cmd); + timer_bytes++; + + if (read) { /* word read */ + dev->data0 = i2c_read(i2c_smbus, smbus_addr); + dev->data1 = i2c_read(i2c_smbus, smbus_addr); + } else { /* word write */ + i2c_write(i2c_smbus, smbus_addr, dev->data0); + i2c_write(i2c_smbus, smbus_addr, dev->data1); } + timer_bytes += 2; + + break; + + case 0x4: /* process call */ + if (dev->local != SMBUS_VIA) /* VIA only */ + goto unknown_protocol; + + if (!read) { /* command write (only when writing) */ + i2c_write(i2c_smbus, smbus_addr, dev->cmd); + timer_bytes++; + } + + /* fall-through */ + + case 0xc: /* I2C process call */ + if (!read) { /* word write (only when writing) */ + i2c_write(i2c_smbus, smbus_addr, dev->data0); + i2c_write(i2c_smbus, smbus_addr, dev->data1); + timer_bytes += 2; + } + + /* word read */ + dev->data0 = i2c_read(i2c_smbus, smbus_addr); + dev->data1 = i2c_read(i2c_smbus, smbus_addr); + timer_bytes += 2; + break; case 0x5: /* block R/W */ - if (smbus_read) - dev->data0 = smbus_read_block_cmd(smbus_addr, dev->cmd, dev->data, SMBUS_PIIX4_BLOCK_DATA_SIZE); - else - smbus_write_block_cmd(smbus_addr, dev->cmd, dev->data, dev->data0); + timer_bytes++; /* count the SMBus length byte now */ + + /* fall-through */ + + case 0xd: /* I2C block R/W */ + i2c_write(i2c_smbus, smbus_addr, dev->cmd); + timer_bytes++; + + if (read) { + /* block read [data0] (I2C) or [first byte] (SMBus) bytes */ + block_len = (cmd == 0x5) ? i2c_read(i2c_smbus, smbus_addr) : dev->data0; + for (i = 0; i < block_len; i++) + dev->data[i & SMBUS_PIIX4_BLOCK_DATA_MASK] = i2c_read(i2c_smbus, smbus_addr); + } else { + block_len = dev->data0; + if (cmd == 0x5) /* send length [data0] as first byte on SMBus */ + i2c_write(i2c_smbus, smbus_addr, block_len); + /* block write [data0] bytes */ + for (i = 0; i < block_len; i++) { + if (!i2c_write(i2c_smbus, smbus_addr, dev->data[i & SMBUS_PIIX4_BLOCK_DATA_MASK])) + break; + } + } + timer_bytes += i; + break; - default: - /* other command protocols have undefined behavior, but raise DEV_ERR to be safe */ - dev->next_stat = 0x4; + case 0x6: /* I2C with 10-bit address */ + if (dev->local != SMBUS_VIA) /* VIA only */ + goto unknown_protocol; + + /* command write */ + i2c_write(i2c_smbus, smbus_addr, dev->cmd); + timer_bytes += 1; + + /* fall-through */ + + case 0xe: /* I2C with 7-bit address */ + if (!read) { /* word write (only when writing) */ + i2c_write(i2c_smbus, smbus_addr, dev->data0); + i2c_write(i2c_smbus, smbus_addr, dev->data1); + timer_bytes += 2; + } + + /* block read [first byte] bytes */ + block_len = dev->data[0]; + for (i = 0; i < block_len; i++) + dev->data[i & SMBUS_PIIX4_BLOCK_DATA_MASK] = i2c_read(i2c_smbus, smbus_addr); + timer_bytes += i; + + break; + + case 0xf: /* universal */ + /* block write [data0] bytes */ + for (i = 0; i < dev->data0; i++) { + if (!i2c_write(i2c_smbus, smbus_addr, dev->data[i & SMBUS_PIIX4_BLOCK_DATA_MASK])) + break; + } + timer_bytes += i; + + /* block read [data1] bytes */ + for (i = 0; i < dev->data1; i++) + dev->data[i & SMBUS_PIIX4_BLOCK_DATA_MASK] = i2c_read(i2c_smbus, smbus_addr); + timer_bytes += i; + + break; + + default: /* unknown */ +unknown_protocol: + dev->next_stat = 0x04; /* raise DEV_ERR */ + timer_bytes = 0; break; } + + /* Finish transfer. */ + i2c_stop(i2c_smbus, smbus_addr); } 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) @@ -189,11 +309,11 @@ smbus_piix4_write(uint16_t addr, uint8_t val, void *priv) 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 */ + if (dev->next_stat) { /* schedule dispatch of any pending status register update */ + dev->stat = 0x01; /* raise HOST_BUSY while waiting */ timer_disable(&dev->response_timer); - timer_set_delay_u64(&dev->response_timer, 10 * TIMER_USEC); + /* delay = ((half clock for start + half clock for stop) + (bytes * (8 bits + ack))) * 60us period measured on real VIA 686B */ + timer_set_delay_u64(&dev->response_timer, (1 + (timer_bytes * 9)) * 60 * TIMER_USEC); } } @@ -203,7 +323,7 @@ smbus_piix4_response(void *priv) { smbus_piix4_t *dev = (smbus_piix4_t *) priv; - /* dispatch the status register update */ + /* Dispatch the status register update. */ dev->stat = dev->next_stat; } @@ -228,7 +348,9 @@ 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(); + dev->local = info->local; + i2c_smbus = dev->i2c = i2c_addbus((dev->local == SMBUS_VIA) ? "smbus_vt82c686b" : "smbus_piix4"); + timer_add(&dev->response_timer, smbus_piix4_response, dev, 0); return dev; @@ -240,6 +362,10 @@ smbus_piix4_close(void *priv) { smbus_piix4_t *dev = (smbus_piix4_t *) priv; + if (i2c_smbus == dev->i2c) + i2c_smbus = NULL; + i2c_removebus(dev->i2c); + free(dev); } @@ -247,7 +373,16 @@ smbus_piix4_close(void *priv) const device_t piix4_smbus_device = { "PIIX4-compatible SMBus Host Controller", DEVICE_AT, - 0, + SMBUS_PIIX4, + smbus_piix4_init, smbus_piix4_close, NULL, + { NULL }, NULL, NULL, + NULL +}; + +const device_t via_smbus_device = { + "VIA VT82C686B SMBus Host Controller", + DEVICE_AT, + SMBUS_VIA, smbus_piix4_init, smbus_piix4_close, NULL, { NULL }, NULL, NULL, NULL diff --git a/src/include/86box/acpi.h b/src/include/86box/acpi.h index ab5c1e9f2..0e5e688ef 100644 --- a/src/include/86box/acpi.h +++ b/src/include/86box/acpi.h @@ -84,6 +84,7 @@ typedef struct pc_timer_t timer; nvr_t *nvr; apm_t *apm; + void *i2c; } acpi_t; diff --git a/src/include/86box/hwm.h b/src/include/86box/hwm.h index 27cb4a921..95d14cf6a 100644 --- a/src/include/86box/hwm.h +++ b/src/include/86box/hwm.h @@ -35,9 +35,9 @@ typedef struct { uint8_t regs[8]; uint8_t addr_register; uint8_t temp_idx; - uint8_t smbus_addr; + uint8_t i2c_addr, i2c_state; - uint8_t as99127f_smbus_addr; + uint8_t as99127f_i2c_addr; } lm75_t; diff --git a/src/include/86box/i2c.h b/src/include/86box/i2c.h new file mode 100644 index 000000000..e2135225c --- /dev/null +++ b/src/include/86box/i2c.h @@ -0,0 +1,70 @@ +/* + * 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 I2C handler. + * + * + * + * Authors: RichardG, + * + * Copyright 2020 RichardG. + */ +#ifndef EMU_I2C_H +# define EMU_I2C_H + + +/* i2c.c */ +extern void *i2c_smbus; + + +/* i2c.c */ +extern void *i2c_addbus(char *name); +extern void i2c_removebus(void *bus_handle); +extern char *i2c_getbusname(void *bus_handle); + +extern void i2c_sethandler(void *bus_handle, uint8_t base, int size, + uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv), + uint8_t (*read)(void *bus, uint8_t addr, void *priv), + uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv), + void (*stop)(void *bus, uint8_t addr, void *priv), + void *priv); + +extern void i2c_removehandler(void *bus_handle, uint8_t base, int size, + uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv), + uint8_t (*read)(void *bus, uint8_t addr, void *priv), + uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv), + void (*stop)(void *bus, uint8_t addr, void *priv), + void *priv); + +extern void i2c_handler(int set, void *bus_handle, uint8_t base, int size, + uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv), + uint8_t (*read)(void *bus, uint8_t addr, void *priv), + uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv), + void (*stop)(void *bus, uint8_t addr, void *priv), + void *priv); + +extern uint8_t i2c_has_device(void *bus_handle, uint8_t addr); +extern uint8_t i2c_start(void *bus_handle, uint8_t addr, uint8_t read); +extern uint8_t i2c_read(void *bus_handle, uint8_t addr); +extern uint8_t i2c_write(void *bus_handle, uint8_t addr, uint8_t data); +extern void i2c_stop(void *bus_handle, uint8_t addr); + +/* i2c_eeprom.c */ +extern void *i2c_eeprom_init(void *i2c, uint8_t addr, uint8_t *data, uint32_t size, uint8_t writable); +extern void i2c_eeprom_close(void *dev_handle); + +/* i2c_gpio.c */ +extern void *i2c_gpio_init(char *bus_name); +extern void i2c_gpio_close(void *dev_handle); +extern void i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda); +extern uint8_t i2c_gpio_get_scl(void *dev_handle); +extern uint8_t i2c_gpio_get_sda(void *dev_handle); +extern void *i2c_gpio_get_bus(); + + +#endif /*EMU_I2C_H*/ diff --git a/src/include/86box/smbus.h b/src/include/86box/smbus.h deleted file mode 100644 index 6923a7edb..000000000 --- a/src/include/86box/smbus.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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. - * - * - * - * 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, uint8_t len, 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, uint8_t len, 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, uint8_t len, 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, uint8_t len); -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/include/86box/smbus_piix4.h b/src/include/86box/smbus_piix4.h index b9f776a5a..3173ead4e 100644 --- a/src/include/86box/smbus_piix4.h +++ b/src/include/86box/smbus_piix4.h @@ -19,16 +19,22 @@ #define SMBUS_PIIX4_BLOCK_DATA_SIZE 32 +#define SMBUS_PIIX4_BLOCK_DATA_MASK (SMBUS_PIIX4_BLOCK_DATA_SIZE - 1) -typedef struct -{ +enum { + SMBUS_PIIX4 = 0, + SMBUS_VIA +}; + +typedef struct { + uint32_t local; uint16_t io_base; uint8_t stat, next_stat, ctl, cmd, addr, data0, data1, - index, - data[SMBUS_PIIX4_BLOCK_DATA_SIZE]; + index, data[SMBUS_PIIX4_BLOCK_DATA_SIZE]; pc_timer_t response_timer; + void *i2c; } smbus_piix4_t; @@ -37,6 +43,7 @@ extern void smbus_piix4_remap(smbus_piix4_t *dev, uint16_t new_io_base, uint8_t #ifdef EMU_DEVICE_H extern const device_t piix4_smbus_device; +extern const device_t via_smbus_device; #endif diff --git a/src/include/86box/spd.h b/src/include/86box/spd.h index 4acce679d..3b716028f 100644 --- a/src/include/86box/spd.h +++ b/src/include/86box/spd.h @@ -47,17 +47,17 @@ #define SPD_SDR_ATTR_VCC_HI_5 0x20 -typedef struct _spd_ { - const device_t *info; - uint8_t slot; - uint16_t size; - uint16_t row1; - uint16_t row2; +typedef struct { + uint8_t slot; + uint16_t size; + uint16_t row1; + uint16_t row2; - uint8_t addr_register; + uint8_t data[SPD_DATA_SIZE]; + void *eeprom; } spd_t; -typedef struct _spd_edo_ { +typedef struct { uint8_t bytes_used, spd_size, mem_type, row_bits, col_bits, banks, data_width_lsb, data_width_msb, @@ -75,7 +75,7 @@ typedef struct _spd_edo_ { checksum2; } spd_edo_t; -typedef struct _spd_sdram_ { +typedef struct { uint8_t bytes_used, spd_size, mem_type, row_bits, col_bits, rows, data_width_lsb, data_width_msb, diff --git a/src/include/86box/vid_ddc.h b/src/include/86box/vid_ddc.h index 7b8f8bd29..223861436 100644 --- a/src/include/86box/vid_ddc.h +++ b/src/include/86box/vid_ddc.h @@ -1,4 +1,25 @@ -void ddc_init(void); -void ddc_i2c_change(int new_clock, int new_data); -int ddc_read_clock(void); -int ddc_read_data(void); +/* + * 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. + * + * DDC monitor emulation definitions. + * + * + * + * Authors: Sarah Walker, + * RichardG, + * + * Copyright 2008-2020 Sarah Walker. + * Copyright 2020 RichardG. + */ +#ifndef EMU_VID_DDC_H +# define EMU_VID_DDC_H + +extern void *ddc_init(void *i2c); +extern void ddc_close(void *eeprom); + +#endif /*EMU_VID_DDC_H*/ diff --git a/src/mem/spd.c b/src/mem/spd.c index 0268c1d5a..36aa5f34f 100644 --- a/src/mem/spd.c +++ b/src/mem/spd.c @@ -23,7 +23,7 @@ #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/device.h> -#include <86box/smbus.h> +#include <86box/i2c.h> #include <86box/spd.h> #include <86box/version.h> #include <86box/machine.h> @@ -33,13 +33,9 @@ int spd_present = 0; -spd_t *spd_devices[SPD_MAX_SLOTS]; -uint8_t spd_data[SPD_MAX_SLOTS][SPD_DATA_SIZE]; +spd_t *spd_modules[SPD_MAX_SLOTS]; - -static uint8_t spd_read_byte(uint8_t addr, void *priv); -static uint8_t spd_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv); -static void spd_write_byte(uint8_t addr, uint8_t val, void *priv); +static const device_t spd_device; #ifdef ENABLE_SPD_LOG @@ -52,9 +48,9 @@ spd_log(const char *fmt, ...) va_list ap; if (spd_do_log) { - va_start(ap, fmt); - pclog_ex(fmt, ap); - va_end(ap); + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); } } #else @@ -62,83 +58,33 @@ spd_log(const char *fmt, ...) #endif -uint8_t -spd_read_byte(uint8_t addr, void *priv) -{ - spd_t *dev = (spd_t *) priv; - return spd_read_byte_cmd(addr, dev->addr_register, priv); -} - - -uint8_t -spd_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv) -{ - spd_t *dev = (spd_t *) priv; - uint8_t ret = *(spd_data[dev->slot] + cmd); - spd_log("SPD: read(%02X, %02X) = %02X\n", addr, cmd, ret); - return ret; -} - - -uint16_t -spd_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv) -{ - return (spd_read_byte_cmd(addr, cmd + 1, priv) << 8) | spd_read_byte_cmd(addr, cmd, priv); -} - - -uint8_t -spd_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv) -{ - uint8_t read = 0; - for (uint8_t i = cmd; i < len && i < SPD_DATA_SIZE; i++) { - data[read++] = spd_read_byte_cmd(addr, i, priv); - } - return read; -} - - -void -spd_write_byte(uint8_t addr, uint8_t val, void *priv) -{ - spd_t *dev = (spd_t *) priv; - dev->addr_register = val; -} - - static void spd_close(void *priv) { - spd_t *dev = (spd_t *) priv; + spd_log("SPD: close()\n"); - spd_log("SPD: closing slot %d (SMBus %02Xh)\n", dev->slot, SPD_BASE_ADDR + dev->slot); - - smbus_removehandler(SPD_BASE_ADDR + dev->slot, 1, - spd_read_byte, spd_read_byte_cmd, spd_read_word_cmd, spd_read_block_cmd, - spd_write_byte, NULL, NULL, NULL, - dev); + for (uint8_t i = 0; i < SPD_MAX_SLOTS; i++) { + if (spd_modules[i]) + i2c_eeprom_close(spd_modules[i]->eeprom); + } spd_present = 0; - - free(dev); } static void * spd_init(const device_t *info) { - spd_t *dev = spd_devices[info->local]; + spd_log("SPD: init()\n"); - spd_log("SPD: initializing slot %d (SMBus %02Xh)\n", dev->slot, SPD_BASE_ADDR + dev->slot); - - smbus_sethandler(SPD_BASE_ADDR + dev->slot, 1, - spd_read_byte, spd_read_byte_cmd, spd_read_word_cmd, spd_read_block_cmd, - spd_write_byte, NULL, NULL, NULL, - dev); + for (uint8_t i = 0; i < SPD_MAX_SLOTS; i++) { + if (spd_modules[i]) + spd_modules[i]->eeprom = i2c_eeprom_init(i2c_smbus, SPD_BASE_ADDR + i, spd_modules[i]->data, sizeof(spd_modules[i]->data), 0); + } spd_present = 1; - return dev; + return &spd_modules; } @@ -147,7 +93,7 @@ log2_ui16(uint16_t i) { uint8_t ret = 0; while ((i >>= 1)) - ret++; + ret++; return ret; } @@ -162,71 +108,71 @@ comp_ui16_rev(const void *elem1, const void *elem2) void -spd_populate(uint16_t *vslots, uint8_t slot_count, uint16_t total_size, uint16_t min_module_size, uint16_t max_module_size, uint8_t enable_asym) +spd_populate(uint16_t *rows, uint8_t slot_count, uint16_t total_size, uint16_t min_module_size, uint16_t max_module_size, uint8_t enable_asym) { - uint8_t vslot, next_empty_vslot, split, i; + uint8_t row, next_empty_row, split, i; uint16_t asym; - /* populate vslots with modules in power-of-2 capacities */ - memset(vslots, 0x00, SPD_MAX_SLOTS << 1); - for (vslot = 0; vslot < slot_count && total_size; vslot++) { - /* populate slot */ - vslots[vslot] = (1 << log2_ui16(MIN(total_size, max_module_size))); - if (total_size >= vslots[vslot]) { - spd_log("SPD: initial vslot %d = %d MB\n", vslot, vslots[vslot]); - total_size -= vslots[vslot]; - } else { - vslots[vslot] = 0; - break; - } + /* Populate rows with modules in power-of-2 capacities. */ + memset(rows, 0, SPD_MAX_SLOTS << 1); + for (row = 0; row < slot_count && total_size; row++) { + /* populate slot */ + rows[row] = 1 << log2_ui16(MIN(total_size, max_module_size)); + if (total_size >= rows[row]) { + spd_log("SPD: Initial row %d = %d MB\n", row, rows[row]); + total_size -= rows[row]; + } else { + rows[row] = 0; + break; + } } - /* did we populate all the RAM? */ + /* Did we populate all the RAM? */ if (total_size) { - /* work backwards to add the missing RAM as asymmetric modules if possible */ - if (enable_asym) { - vslot = slot_count - 1; - do { - asym = (1 << log2_ui16(MIN(total_size, vslots[vslot]))); - if (vslots[vslot] + asym <= max_module_size) { - vslots[vslot] += asym; - total_size -= asym; - } - } while ((vslot-- > 0) && total_size); - } + /* Work backwards to add the missing RAM as asymmetric modules if possible. */ + if (enable_asym) { + row = slot_count - 1; + do { + asym = (1 << log2_ui16(MIN(total_size, rows[row]))); + if (rows[row] + asym <= max_module_size) { + rows[row] += asym; + total_size -= asym; + } + } while ((row-- > 0) && total_size); + } - if (total_size) /* still not enough */ - spd_log("SPD: not enough RAM slots (%d) to cover memory (%d MB short)\n", slot_count, total_size); + if (total_size) /* still not enough */ + spd_log("SPD: Not enough RAM slots (%d) to cover memory (%d MB short)\n", slot_count, total_size); } - /* populate empty vslots by splitting modules... */ - split = (total_size == 0); /* ...if possible */ + /* Populate empty rows by splitting modules... */ + split = (total_size == 0); /* ...if possible. */ while (split) { - /* look for a module to split */ - split = 0; - for (vslot = 0; vslot < slot_count; vslot++) { - if ((vslots[vslot] < (min_module_size << 1)) || (vslots[vslot] != (1 << log2_ui16(vslots[vslot])))) - continue; /* no module here, module is too small to be split, or asymmetric module */ + /* Look for a module to split. */ + split = 0; + for (row = 0; row < slot_count; row++) { + if ((rows[row] < (min_module_size << 1)) || (rows[row] != (1 << log2_ui16(rows[row])))) + continue; /* no module here, module is too small to be split, or asymmetric module */ - /* find next empty vslot */ - next_empty_vslot = 0; - for (i = vslot + 1; i < slot_count && !next_empty_vslot; i++) { - if (!vslots[i]) - next_empty_vslot = i; - } - if (!next_empty_vslot) - break; /* no empty vslots left */ + /* Find next empty row. */ + next_empty_row = 0; + for (i = row + 1; i < slot_count && !next_empty_row; i++) { + if (!rows[i]) + next_empty_row = i; + } + if (!next_empty_row) + break; /* no empty rows left */ - /* split the module into its own vslot and the next empty vslot */ - spd_log("SPD: splitting vslot %d (%d MB) into %d and %d (%d MB each)\n", vslot, vslots[vslot], vslot, next_empty_vslot, (vslots[vslot] >> 1)); - vslots[vslot] = vslots[next_empty_vslot] = (vslots[vslot] >> 1); - split = 1; - break; - } + /* Split the module into its own row and the next empty row. */ + spd_log("SPD: splitting row %d (%d MB) into %d and %d (%d MB each)\n", row, rows[row], row, next_empty_row, rows[row] >> 1); + rows[row] = rows[next_empty_row] = rows[row] >> 1; + split = 1; + break; + } - /* sort vslots by descending capacity if any were split */ - if (split) - qsort(vslots, slot_count, sizeof(uint16_t), comp_ui16_rev); + /* Sort rows by descending capacity if any were split. */ + if (split) + qsort(rows, slot_count, sizeof(uint16_t), comp_ui16_rev); } } @@ -234,172 +180,163 @@ spd_populate(uint16_t *vslots, uint8_t slot_count, uint16_t total_size, uint16_t void spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size) { - uint8_t slot, slot_count, vslot, i; - uint16_t min_module_size, vslots[SPD_MAX_SLOTS], asym; - device_t *info; + uint8_t slot, slot_count, row, i; + uint16_t min_module_size, rows[SPD_MAX_SLOTS], asym; spd_edo_t *edo_data; spd_sdram_t *sdram_data; - /* determine the minimum module size for this RAM type */ + /* Determine the minimum module size for this RAM type. */ switch (ram_type) { - case SPD_TYPE_FPM: - case SPD_TYPE_EDO: - min_module_size = SPD_MIN_SIZE_EDO; - break; + case SPD_TYPE_FPM: + case SPD_TYPE_EDO: + min_module_size = SPD_MIN_SIZE_EDO; + break; - case SPD_TYPE_SDRAM: - min_module_size = SPD_MIN_SIZE_SDRAM; - break; + case SPD_TYPE_SDRAM: + min_module_size = SPD_MIN_SIZE_SDRAM; + break; default: - spd_log("SPD: unknown RAM type 0x%02X\n", ram_type); - return; + spd_log("SPD: unknown RAM type %02X\n", ram_type); + return; } - /* count how many (real) slots are enabled */ + /* Count how many slots are enabled. */ slot_count = 0; for (slot = 0; slot < SPD_MAX_SLOTS; slot++) { - vslots[slot] = 0; - if (slot_mask & (1 << slot)) { - slot_count++; - } + rows[slot] = 0; + if (slot_mask & (1 << slot)) + slot_count++; } - /* populate vslots */ - spd_populate(vslots, slot_count, (mem_size >> 10), min_module_size, max_module_size, 1); + /* Populate rows. */ + spd_populate(rows, slot_count, (mem_size >> 10), min_module_size, max_module_size, 1); - /* register SPD devices and populate their data according to the vslots */ - vslot = 0; - for (slot = 0; slot < SPD_MAX_SLOTS && vslots[vslot]; slot++) { - if (!(slot_mask & (1 << slot))) - continue; /* slot disabled */ + /* Register SPD devices and populate their data according to the rows. */ + row = 0; + for (slot = 0; slot < SPD_MAX_SLOTS && rows[row]; slot++) { + if (!(slot_mask & (1 << slot))) + continue; /* slot disabled */ - info = (device_t *) malloc(sizeof(device_t)); - memset(info, 0, sizeof(device_t)); - info->name = "Serial Presence Detect ROM"; - info->local = slot; - info->init = spd_init; - info->close = spd_close; + spd_modules[slot] = (spd_t *) malloc(sizeof(spd_t)); + memset(spd_modules[slot], 0, sizeof(spd_t)); + spd_modules[slot]->slot = slot; + spd_modules[slot]->size = rows[row]; - spd_devices[slot] = (spd_t *) malloc(sizeof(spd_t)); - memset(spd_devices[slot], 0, sizeof(spd_t)); - spd_devices[slot]->info = info; - spd_devices[slot]->slot = slot; - spd_devices[slot]->size = vslots[vslot]; + /* Determine the second row size, from which the first row size can be obtained. */ + asym = rows[row] - (1 << log2_ui16(rows[row])); /* separate the powers of 2 */ + if (!asym) /* is the module asymmetric? */ + asym = rows[row] >> 1; /* symmetric, therefore divide by 2 */ - /* determine the second row size, from which the first row size can be obtained */ - asym = (vslots[vslot] - (1 << log2_ui16(vslots[vslot]))); /* separate the powers of 2 */ - if (!asym) /* is the module asymmetric? */ - asym = (vslots[vslot] >> 1); /* symmetric, therefore divide by 2 */ + spd_modules[slot]->row1 = rows[row] - asym; + spd_modules[slot]->row2 = asym; - spd_devices[slot]->row1 = (vslots[vslot] - asym); - spd_devices[slot]->row2 = asym; + spd_log("SPD: Registering slot %d = row %d = %d MB (%d/%d)\n", slot, row, rows[row], spd_modules[slot]->row1, spd_modules[slot]->row2); - spd_log("SPD: registering slot %d = vslot %d = %d MB (%d/%d)\n", slot, vslot, vslots[vslot], spd_devices[slot]->row1, spd_devices[slot]->row2); + switch (ram_type) { + case SPD_TYPE_FPM: + case SPD_TYPE_EDO: + edo_data = (spd_edo_t *) &spd_modules[slot]->data; + memset(edo_data, 0, sizeof(spd_edo_t)); - switch (ram_type) { - case SPD_TYPE_FPM: - case SPD_TYPE_EDO: - edo_data = (spd_edo_t *) &spd_data[slot]; - memset(edo_data, 0, sizeof(spd_edo_t)); + /* EDO SPD is specified by JEDEC and present in some modules, but + most utilities cannot interpret it correctly. SIV32 at least gets + the module capacities right, so it was used as a reference here. */ + edo_data->bytes_used = 0x80; + edo_data->spd_size = 0x08; + edo_data->mem_type = ram_type; + edo_data->row_bits = SPD_ROLLUP(7 + log2_ui16(spd_modules[slot]->row1)); /* first row */ + edo_data->col_bits = 9; + if (spd_modules[slot]->row1 != spd_modules[slot]->row2) { /* the upper 4 bits of row_bits/col_bits should be 0 on a symmetric module */ + edo_data->row_bits |= SPD_ROLLUP(7 + log2_ui16(spd_modules[slot]->row2)) << 4; /* second row, if different from first */ + edo_data->col_bits |= 9 << 4; /* same as first row, but just in case */ + } + edo_data->banks = 2; + edo_data->data_width_lsb = 64; + edo_data->signal_level = SPD_SIGNAL_LVTTL; + edo_data->trac = 50; + edo_data->tcac = 13; + edo_data->refresh_rate = SPD_REFRESH_NORMAL; + edo_data->dram_width = 8; - /* EDO SPD is specified by JEDEC and present in some modules, but - most utilities cannot interpret it correctly. SIV32 at least gets - the module capacities right, so it was used as a reference here. */ - edo_data->bytes_used = 0x80; - edo_data->spd_size = 0x08; - edo_data->mem_type = ram_type; - edo_data->row_bits = SPD_ROLLUP(7 + log2_ui16(spd_devices[slot]->row1)); /* first row */ - edo_data->col_bits = 9; - if (spd_devices[slot]->row1 != spd_devices[slot]->row2) { /* the upper 4 bits of row_bits/col_bits should be 0 on a symmetric module */ - edo_data->row_bits |= (SPD_ROLLUP(7 + log2_ui16(spd_devices[slot]->row2)) << 4); /* second row, if different from first */ - edo_data->col_bits |= (9 << 4); /* same as first row, but just in case */ - } - edo_data->banks = 2; - edo_data->data_width_lsb = 64; - edo_data->signal_level = SPD_SIGNAL_LVTTL; - edo_data->trac = 50; - edo_data->tcac = 13; - edo_data->refresh_rate = SPD_REFRESH_NORMAL; - edo_data->dram_width = 8; + edo_data->spd_rev = 0x12; + sprintf(edo_data->part_no, EMU_NAME "-%s-%03dM", (ram_type == SPD_TYPE_FPM) ? "FPM" : "EDO", rows[row]); + for (i = strlen(edo_data->part_no); i < sizeof(edo_data->part_no); i++) + edo_data->part_no[i] = ' '; /* part number should be space-padded */ + edo_data->rev_code[0] = BCD8(EMU_VERSION_MAJ); + edo_data->rev_code[1] = BCD8(EMU_VERSION_MIN); + edo_data->mfg_year = 20; + edo_data->mfg_week = 17; - edo_data->spd_rev = 0x12; - sprintf(edo_data->part_no, EMU_NAME "-%s-%03dM", (ram_type == SPD_TYPE_FPM) ? "FPM" : "EDO", vslots[vslot]); - for (i = strlen(edo_data->part_no); i < sizeof(edo_data->part_no); i++) - edo_data->part_no[i] = ' '; /* part number should be space-padded */ - edo_data->rev_code[0] = BCD8(EMU_VERSION_MAJ); - edo_data->rev_code[1] = BCD8(EMU_VERSION_MIN); - edo_data->mfg_year = 20; - edo_data->mfg_week = 17; + for (i = 0; i < 63; i++) + edo_data->checksum += spd_modules[slot]->data[i]; + for (i = 0; i < 129; i++) + edo_data->checksum2 += spd_modules[slot]->data[i]; + break; - for (i = 0; i < 63; i++) - edo_data->checksum += spd_data[slot][i]; - for (i = 0; i < 129; i++) - edo_data->checksum2 += spd_data[slot][i]; - break; + case SPD_TYPE_SDRAM: + sdram_data = (spd_sdram_t *) &spd_modules[slot]->data; + memset(sdram_data, 0, sizeof(spd_sdram_t)); - case SPD_TYPE_SDRAM: - sdram_data = (spd_sdram_t *) &spd_data[slot]; - memset(sdram_data, 0, sizeof(spd_sdram_t)); + sdram_data->bytes_used = 0x80; + sdram_data->spd_size = 0x08; + sdram_data->mem_type = ram_type; + sdram_data->row_bits = SPD_ROLLUP(6 + log2_ui16(spd_modules[slot]->row1)); /* first row */ + sdram_data->col_bits = 9; + if (spd_modules[slot]->row1 != spd_modules[slot]->row2) { /* the upper 4 bits of row_bits/col_bits should be 0 on a symmetric module */ + sdram_data->row_bits |= SPD_ROLLUP(6 + log2_ui16(spd_modules[slot]->row2)) << 4; /* second row, if different from first */ + sdram_data->col_bits |= 9 << 4; /* same as first row, but just in case */ + } + sdram_data->rows = 2; + sdram_data->data_width_lsb = 64; + sdram_data->signal_level = SPD_SIGNAL_LVTTL; + sdram_data->tclk = 0x75; /* 7.5 ns = 133.3 MHz */ + sdram_data->tac = 0x10; + sdram_data->refresh_rate = SPD_SDR_REFRESH_SELF | SPD_REFRESH_NORMAL; + sdram_data->sdram_width = 8; + sdram_data->tccd = 1; + sdram_data->burst = SPD_SDR_BURST_PAGE | 1 | 2 | 4 | 8; + sdram_data->banks = 4; + sdram_data->cas = 0x1c; /* CAS 5/4/3 supported */ + sdram_data->cslat = sdram_data->we = 0x7f; + sdram_data->dev_attr = SPD_SDR_ATTR_EARLY_RAS | SPD_SDR_ATTR_AUTO_PC | SPD_SDR_ATTR_PC_ALL | SPD_SDR_ATTR_W1R_BURST; + sdram_data->tclk2 = 0xA0; /* 10 ns = 100 MHz */ + sdram_data->tclk3 = 0xF0; /* 15 ns = 66.7 MHz */ + sdram_data->tac2 = sdram_data->tac3 = 0x10; + sdram_data->trp = sdram_data->trrd = sdram_data->trcd = sdram_data->tras = 1; + if (spd_modules[slot]->row1 != spd_modules[slot]->row2) { + /* Utilities interpret bank_density a bit differently on asymmetric modules. */ + sdram_data->bank_density = 1 << (log2_ui16(spd_modules[slot]->row1 >> 1) - 2); /* first row */ + sdram_data->bank_density |= 1 << (log2_ui16(spd_modules[slot]->row2 >> 1) - 2); /* second row */ + } else { + sdram_data->bank_density = 1 << (log2_ui16(spd_modules[slot]->row1 >> 1) - 1); /* symmetric module = only one bit is set */ + } + sdram_data->ca_setup = sdram_data->data_setup = 0x15; + sdram_data->ca_hold = sdram_data->data_hold = 0x08; - sdram_data->bytes_used = 0x80; - sdram_data->spd_size = 0x08; - sdram_data->mem_type = ram_type; - sdram_data->row_bits = SPD_ROLLUP(6 + log2_ui16(spd_devices[slot]->row1)); /* first row */ - sdram_data->col_bits = 9; - if (spd_devices[slot]->row1 != spd_devices[slot]->row2) { /* the upper 4 bits of row_bits/col_bits should be 0 on a symmetric module */ - sdram_data->row_bits |= (SPD_ROLLUP(6 + log2_ui16(spd_devices[slot]->row2)) << 4); /* second row, if different from first */ - sdram_data->col_bits |= (9 << 4); /* same as first row, but just in case */ - } - sdram_data->rows = 2; - sdram_data->data_width_lsb = 64; - sdram_data->signal_level = SPD_SIGNAL_LVTTL; - sdram_data->tclk = 0x75; /* 7.5 ns = 133.3 MHz */ - sdram_data->tac = 0x10; - sdram_data->refresh_rate = SPD_SDR_REFRESH_SELF | SPD_REFRESH_NORMAL; - sdram_data->sdram_width = 8; - sdram_data->tccd = 1; - sdram_data->burst = SPD_SDR_BURST_PAGE | 1 | 2 | 4 | 8; - sdram_data->banks = 4; - sdram_data->cas = 0x1c; /* CAS 5/4/3 supported */ - sdram_data->cslat = sdram_data->we = 0x7f; - sdram_data->dev_attr = SPD_SDR_ATTR_EARLY_RAS | SPD_SDR_ATTR_AUTO_PC | SPD_SDR_ATTR_PC_ALL | SPD_SDR_ATTR_W1R_BURST; - sdram_data->tclk2 = 0xA0; /* 10 ns = 100 MHz */ - sdram_data->tclk3 = 0xF0; /* 15 ns = 66.7 MHz */ - sdram_data->tac2 = sdram_data->tac3 = 0x10; - sdram_data->trp = sdram_data->trrd = sdram_data->trcd = sdram_data->tras = 1; - if (spd_devices[slot]->row1 != spd_devices[slot]->row2) { - /* Utilities interpret bank_density a bit differently on asymmetric modules. */ - sdram_data->bank_density = (1 << (log2_ui16(spd_devices[slot]->row1 >> 1) - 2)); /* first row */ - sdram_data->bank_density |= (1 << (log2_ui16(spd_devices[slot]->row2 >> 1) - 2)); /* second row */ - } else { - sdram_data->bank_density = (1 << (log2_ui16(spd_devices[slot]->row1 >> 1) - 1)); /* symmetric module = only one bit is set */ - } - sdram_data->ca_setup = sdram_data->data_setup = 0x15; - sdram_data->ca_hold = sdram_data->data_hold = 0x08; + sdram_data->spd_rev = 0x12; + sprintf(sdram_data->part_no, EMU_NAME "-SDR-%03dM", rows[row]); + for (i = strlen(sdram_data->part_no); i < sizeof(sdram_data->part_no); i++) + sdram_data->part_no[i] = ' '; /* part number should be space-padded */ + sdram_data->rev_code[0] = BCD8(EMU_VERSION_MAJ); + sdram_data->rev_code[1] = BCD8(EMU_VERSION_MIN); + sdram_data->mfg_year = 20; + sdram_data->mfg_week = 13; - sdram_data->spd_rev = 0x12; - sprintf(sdram_data->part_no, EMU_NAME "-SDR-%03dM", vslots[vslot]); - for (i = strlen(sdram_data->part_no); i < sizeof(sdram_data->part_no); i++) - sdram_data->part_no[i] = ' '; /* part number should be space-padded */ - sdram_data->rev_code[0] = BCD8(EMU_VERSION_MAJ); - sdram_data->rev_code[1] = BCD8(EMU_VERSION_MIN); - sdram_data->mfg_year = 20; - sdram_data->mfg_week = 13; + sdram_data->freq = 100; + sdram_data->features = 0xFF; - sdram_data->freq = 100; - sdram_data->features = 0xFF; + for (i = 0; i < 63; i++) + sdram_data->checksum += spd_modules[slot]->data[i]; + for (i = 0; i < 129; i++) + sdram_data->checksum2 += spd_modules[slot]->data[i]; + break; + } - for (i = 0; i < 63; i++) - sdram_data->checksum += spd_data[slot][i]; - for (i = 0; i < 129; i++) - sdram_data->checksum2 += spd_data[slot][i]; - break; - } - - device_add(info); - vslot++; + row++; } + + device_add(&spd_device); } @@ -407,53 +344,63 @@ void spd_write_drbs(uint8_t *regs, uint8_t reg_min, uint8_t reg_max, uint8_t drb_unit) { uint8_t row, dimm, drb, apollo = 0; - uint16_t size, vslots[SPD_MAX_SLOTS]; + uint16_t size, rows[SPD_MAX_SLOTS]; /* Special case for VIA Apollo Pro family, which jumps from 5F to 56. */ if (reg_max < reg_min) { - apollo = reg_max; - reg_max = reg_min + 7; + apollo = reg_max; + reg_max = reg_min + 7; } /* No SPD: split SIMMs into pairs as if they were "DIMM"s. */ if (!spd_present) { - dimm = ((reg_max - reg_min) + 1) >> 1; /* amount of "DIMM"s, also used to determine the maximum "DIMM" size */ - spd_populate(vslots, dimm, (mem_size >> 10), drb_unit, 1 << (log2_ui16(machines[machine].max_ram / dimm)), 0); + dimm = ((reg_max - reg_min) + 1) >> 1; /* amount of "DIMM"s, also used to determine the maximum "DIMM" size */ + spd_populate(rows, dimm, mem_size >> 10, drb_unit, 1 << (log2_ui16(machines[machine].max_ram / dimm)), 0); } /* Write DRBs for each row. */ - spd_log("Writing DRBs... regs=[%02X:%02X] unit=%d\n", reg_min, reg_max, drb_unit); + spd_log("SPD: Writing DRBs... regs=[%02X:%02X] unit=%d\n", reg_min, reg_max, drb_unit); for (row = 0; row <= (reg_max - reg_min); row++) { - dimm = (row >> 1); - size = 0; + dimm = (row >> 1); + size = 0; - if (spd_present) { - /* SPD enabled: use SPD info for this slot, if present. */ - if (spd_devices[dimm]) { - if (spd_devices[dimm]->row1 < drb_unit) /* hack within a hack: turn a double-sided DIMM that is too small into a single-sided one */ - size = ((row & 1) ? 0 : drb_unit); - else - size = ((row & 1) ? spd_devices[dimm]->row2 : spd_devices[dimm]->row1); - } - } else { - /* No SPD: use the values calculated above. */ - size = (vslots[dimm] >> 1); - } + if (spd_present) { + /* SPD enabled: use SPD info for this slot, if present. */ + if (spd_modules[dimm]) { + if (spd_modules[dimm]->row1 < drb_unit) /* hack within a hack: turn a double-sided DIMM that is too small into a single-sided one */ + size = (row & 1) ? 0 : drb_unit; + else + size = (row & 1) ? spd_modules[dimm]->row2 : spd_modules[dimm]->row1; + } + } else { + /* No SPD: use the values calculated above. */ + size = (rows[dimm] >> 1); + } - /* Determine the DRB register to write. */ - drb = reg_min + row; - if ((apollo) && ((drb & 0xf) < 0xa)) - drb = apollo + (drb & 0xf); + /* Determine the DRB register to write. */ + drb = reg_min + row; + if (apollo && ((drb & 0xf) < 0xa)) + drb = apollo + (drb & 0xf); - /* Write DRB register, adding the previous DRB's value. */ - if (row == 0) - regs[drb] = 0; - else if ((apollo) && (drb == apollo)) - regs[drb] = regs[drb | 0xf]; /* 5F comes before 56 */ - else - regs[drb] = regs[drb - 1]; - if (size) - regs[drb] += (size / drb_unit); /* this will intentionally overflow on 440GX with 2 GB */ - spd_log("DRB[%d] = %d MB (%02Xh raw)\n", row, size, regs[drb]); + /* Write DRB register, adding the previous DRB's value. */ + if (row == 0) + regs[drb] = 0; + else if ((apollo) && (drb == apollo)) + regs[drb] = regs[drb | 0xf]; /* 5F comes before 56 */ + else + regs[drb] = regs[drb - 1]; + if (size) + regs[drb] += size / drb_unit; /* this will intentionally overflow on 440GX with 2 GB */ + spd_log("SPD: DRB[%d] = %d MB (%02Xh raw)\n", row, size, regs[drb]); } } + + +static const device_t spd_device = { + "Serial Presence Detect ROMs", + DEVICE_ISA, + 0, + spd_init, spd_close, NULL, + { NULL }, NULL, NULL, + NULL +}; diff --git a/src/scsi/scsi_ncr53c8xx.c b/src/scsi/scsi_ncr53c8xx.c index bb467781f..ca0273cc8 100644 --- a/src/scsi/scsi_ncr53c8xx.c +++ b/src/scsi/scsi_ncr53c8xx.c @@ -43,6 +43,7 @@ #include <86box/device.h> #include <86box/nvr.h> #include <86box/plat.h> +#include <86box/i2c.h> #include <86box/scsi.h> #include <86box/scsi_device.h> #include <86box/scsi_ncr53c8xx.h> @@ -185,6 +186,7 @@ /* Flag set if this is a tagged command. */ #define NCR_TAG_VALID (1 << 16) +#define NCR_NVRAM_SIZE 2048 #define NCR_BUF_SIZE 4096 typedef struct ncr53c8xx_request { @@ -225,15 +227,9 @@ typedef struct { int msg_action; int msg_len; uint8_t msg[NCR_MAX_MSGIN_LEN]; -#ifdef USE_NVRAM - uint16_t nvram; - uint8_t nvram_t; - uint8_t nvram_op; - uint8_t nvram_param; - uint8_t nvram_start; - uint8_t nvram_index; -#endif - uint8_t ram[NCR_BUF_SIZE]; /* NCR 53c875 RAM (4 kB). */ + uint8_t nvram[NCR_NVRAM_SIZE]; /* 24C16 EEPROM (16 Kbit) */ + void *i2c, *eeprom; + uint8_t ram[NCR_BUF_SIZE]; /* NCR 53C875 RAM (4 KB) */ /* 0 if SCRIPTS are running or stopped. * 1 if a Wait Reselect instruction has been issued. * 2 if processing DMA from ncr53c8xx_execute_script. @@ -434,10 +430,7 @@ ncr53c8xx_soft_reset(ncr53c8xx_t *dev) dev->gpreg = 0; dev->slpar = 0; dev->sstop = 1; - dev->gpcntl = 0x0f; -#ifdef USE_NVRAM - dev->nvram_t = dev->nvram_index = 0; -#endif + dev->gpcntl = 0x03; if (dev->chip >= CHIP_825) { /* This *IS* a wide SCSI controller, so reset all SCSI @@ -474,7 +467,7 @@ ncr53c8xx_read(ncr53c8xx_t *dev, uint32_t addr, uint8_t *buf, uint32_t len) buf[i] = inb((uint16_t) (addr + i)); } else { ncr53c8xx_log("NCR 810: Reading from memory address %08X\n", addr); - dma_bm_read(addr, buf, len, 4); + dma_bm_read(addr, buf, len, 4); } } @@ -492,7 +485,7 @@ ncr53c8xx_write(ncr53c8xx_t *dev, uint32_t addr, uint8_t *buf, uint32_t len) outb((uint16_t) (addr + i), buf[i]); } else { ncr53c8xx_log("NCR 810: Writing to memory address %08X\n", addr); - dma_bm_write(addr, buf, len, 4); + dma_bm_write(addr, buf, len, 4); } } @@ -511,10 +504,10 @@ static void do_irq(ncr53c8xx_t *dev, int level) { if (level) { - pci_set_irq(dev->pci_slot, PCI_INTA); + pci_set_irq(dev->pci_slot, PCI_INTA); ncr53c8xx_log("Raising IRQ...\n"); } else { - pci_clear_irq(dev->pci_slot, PCI_INTA); + pci_clear_irq(dev->pci_slot, PCI_INTA); ncr53c8xx_log("Lowering IRQ...\n"); } } @@ -852,8 +845,8 @@ ncr53c8xx_do_msgin(ncr53c8xx_t *dev) switch to PHASE_MO. */ switch (dev->msg_action) { case 0: - ncr53c8xx_set_phase(dev, PHASE_CMD); - break; + ncr53c8xx_set_phase(dev, PHASE_CMD); + break; case 1: ncr53c8xx_disconnect(dev); break; @@ -1444,7 +1437,7 @@ ncr53c8xx_callback(void *p) if (dev->waiting) timer_on_auto(&dev->timer, 40.0); else - ncr53c8xx_process_script(dev); + ncr53c8xx_process_script(dev); } if (dev->sstop) @@ -1452,146 +1445,22 @@ ncr53c8xx_callback(void *p) } -#ifdef USE_NVRAM static void -ncr53c8xx_eeprom(ncr53c8xx_t *dev, int save) +ncr53c8xx_eeprom(ncr53c8xx_t *dev, uint8_t save) { FILE *f; - if (save) - f = nvr_fopen(dev->nvr_path, L"wb"); - else - f = nvr_fopen(dev->nvr_path, L"rb"); - if (f) - { - if (save) { - fwrite((uint8_t *) &dev->nvram, 1, 1, f); - fwrite(((uint8_t *) &dev->nvram) + 1, 1, 1, f); - } else { - fread((uint8_t *) &dev->nvram, 1, 1, f); - fread(((uint8_t *) &dev->nvram) + 1, 1, 1, f); - } + f = nvr_fopen(dev->nvr_path, save ? L"wb": L"rb"); + if (f) { + if (save) + fwrite(&dev->nvram, sizeof(dev->nvram), 1, f); + else + fread(&dev->nvram, sizeof(dev->nvram), 1, f); fclose(f); - f = NULL; } } -#define ADDRESS_LENGTH 9 -#define ADDRESS_END (ADDRESS_LENGTH + 2) -#define ADDRESS_SHIFT (ADDRESS_LENGTH - 2) -#define ADDRESS_MASK 0x3f -#define DATA_LENGTH 8 -#define DATA_START (ADDRESS_END + 1) -#define DATA_END (ADDRESS_END + DATA_LENGTH) - - -static void -ncr53c8xx_serial_eeprom_write(ncr53c8xx_t *dev, uint8_t val) -{ - uint8_t temp, old = dev->nvram_t; - dev->nvram_t = val & 0x03; - - if (val & 0x02) { - if (!dev->nvram_index) { - /* First signal clocked in after clock is high, start bit. */ - dev->nvram_start = 1; - ncr53c8xx_log("[W] Serial EEPROM: Start bit\n"); - dev->nvram_op = 0; - } else if ((dev->nvram_index == 1) || (dev->nvram_index == 2)) { - if (!dev->nvram_start) - return; - - dev->nvram_op = (val & 0x01) << (dev->nvram_index - 1); - if (dev->nvram_index == 2) { - // ncr53c8xx_log("[W] Serial EEPROM: Opcode: %01X\n", dev->nvram_op); - dev->nvram_param = 0; - } - } else if ((dev->nvram_index >= 3) && (dev->nvram_index <= ADDRESS_END)) { - if (!dev->nvram_start) - return; - - dev->nvram_param = (val & 0x01) << (dev->nvram_index - 3); - if (dev->nvram_index < ADDRESS_END) { - dev->nvram_index++; - return; - } - - switch (dev->nvram_op) { - case 0x00: - temp = dev->nvram_param >> ADDRESS_SHIFT; - switch(temp) { - case 0x00: - ncr53c8xx_log("[W] Serial EEPROM: EWDS\n"); - break; - case 0x01: - ncr53c8xx_log("[W] Serial EEPROM: WRAL\n"); - break; - case 0x02: - ncr53c8xx_log("[W] Serial EEPROM: ERAL\n"); - break; - case 0x03: - ncr53c8xx_log("[W] Serial EEPROM: EWEN\n"); - break; - default: - ncr53c8xx_log("[W] Serial EEPROM: UNKNOWN 00\n"); - break; - } - dev->nvram_start = dev->nvram_index = dev->nvram_op = dev->nvram_param = 0; - return; - case 0x01: - ncr53c8xx_log("[W] Serial EEPROM: WRITE\n"); - break; - case 0x02: - ncr53c8xx_log("[W] Serial EEPROM: READ\n"); - break; - case 0x03: - ncr53c8xx_log("[W] Serial EEPROM: ERASE\n"); - break; - default: - ncr53c8xx_log("[W] Serial EEPROM: UNKNOWN\n"); - break; - } - } else if ((dev->nvram_index >= DATA_START) && (dev->nvram_index <= DATA_END)) { - if (!dev->nvram_start) - return; - - if (dev->nvram_index == DATA_END) { - ncr53c8xx_log("[W] Serial EEPROM: Data end\n"); - dev->nvram_start = dev->nvram_index = dev->nvram_op = dev->nvram_param = 0; - return; - } - } - dev->nvram_index++; - } -} - - -static uint8_t -ncr53c8xx_serial_eeprom_read(ncr53c8xx_t *dev) -{ - uint8_t temp = 0; - - if (dev->gpreg & 0x02) { - if ((dev->nvram_index >= DATA_START) && (dev->nvram_index <= DATA_END)) { - if (!dev->nvram_start) - return temp; - - dev->nvram_index++; - - if (dev->nvram_index == DATA_END) { - ncr53c8xx_log("[R] Serial EEPROM: Data end\n"); - dev->nvram_start = dev->nvram_index = dev->nvram_op = dev->nvram_param = 0; - return temp; - } - } - } - - return temp; -} -#endif - - static void ncr53c8xx_reg_writeb(ncr53c8xx_t *dev, uint32_t offset, uint8_t val) { @@ -1659,12 +1528,8 @@ ncr53c8xx_reg_writeb(ncr53c8xx_t *dev, uint32_t offset, uint8_t val) break; case 0x07: /* GPREG */ ncr53c8xx_log("NCR 810: GPREG write %02X\n", val); - tmp = dev->gpreg; - dev->gpreg = val & 0xfe; -#ifdef USE_NVRAM - if ((dev->gpcntl & 0xc3) == 0x00) - ncr53c8xx_serial_eeprom_write(dev, val & 0x03); -#endif + dev->gpreg = val; + i2c_gpio_set(dev->i2c, !!(dev->gpreg & 0x02), !!(dev->gpreg & 0x01)); break; case 0x08: /* SFBR */ /* The CPU is not allowed to write to this register. However the @@ -1893,13 +1758,18 @@ ncr53c8xx_reg_readb(ncr53c8xx_t *dev, uint32_t offset) ncr53c8xx_log("NCR 810: Read SDID %02X\n", dev->sdid); return dev->sdid; case 0x07: /* GPREG */ -#ifdef USE_NVRAM - tmp = (dev->gpreg & (dev->gpcntl ^ 0x1f)) & 0x1e; - if ((dev->gpcntl & 0xc3) == 0x01) - tmp |= ncr53c8xx_serial_eeprom_read(dev); -#else - tmp = ((dev->gpreg & (dev->gpcntl ^ 0x1f)) & 0x1e) | 0x01; -#endif + tmp = (dev->gpreg & (dev->gpcntl ^ 0x1f)) & 0x1f; + if ((dev->gpcntl & 0x41) == 0x01) { + tmp &= 0xfe; + if (i2c_gpio_get_sda(dev->i2c)) + tmp |= 0x01; + } + if ((dev->gpcntl & 0x82) == 0x02) { + tmp &= 0xfd; + if (i2c_gpio_get_scl(dev->i2c)) + tmp |= 0x02; + } + ncr53c8xx_log("NCR 810: Read GPREG %02X\n", tmp); return tmp; case 0x08: /* Revision ID */ @@ -2677,8 +2547,8 @@ ncr53c8xx_init(const device_t *info) dev->chip_rev = 0x26; dev->nvr_path = L"ncr53c825a.nvr"; } - ncr53c8xx_pci_bar[2].addr_regs[0] = 0; - ncr53c8xx_pci_bar[3].addr = 0xffff0000; + ncr53c8xx_pci_bar[2].addr_regs[0] = 0; + ncr53c8xx_pci_bar[3].addr = 0xffff0000; /* Need to make it align on a 16k boundary as that's this emulator's memory mapping granularity. */ ncr53c8xx_ram_init(dev, 0x0fffc000); @@ -2695,10 +2565,11 @@ ncr53c8xx_init(const device_t *info) dev->nvr_path = L"ncr53c810.nvr"; } -#ifdef USE_NVRAM + dev->i2c = i2c_gpio_init("nvr_ncr53c8xx"); + dev->eeprom = i2c_eeprom_init(i2c_gpio_get_bus(dev->i2c), 0x50, dev->nvram, sizeof(dev->nvram), 1); + /* Load the serial EEPROM. */ ncr53c8xx_eeprom(dev, 0); -#endif ncr53c8xx_soft_reset(dev); @@ -2714,6 +2585,15 @@ ncr53c8xx_close(void *priv) ncr53c8xx_t *dev = (ncr53c8xx_t *)priv; if (dev) { + if (dev->eeprom) + i2c_eeprom_close(dev->eeprom); + + if (dev->i2c) + i2c_gpio_close(dev->i2c); + + /* Save the serial EEPROM. */ + ncr53c8xx_eeprom(dev, 1); + free(dev); dev = NULL; } @@ -2732,7 +2612,7 @@ static const device_config_t ncr53c8xx_pci_config[] = { const device_t ncr53c810_pci_device = { - "NCR 53c810", + "NCR 53C810", DEVICE_PCI, 0x01, ncr53c8xx_init, ncr53c8xx_close, NULL, @@ -2742,7 +2622,7 @@ const device_t ncr53c810_pci_device = const device_t ncr53c810_onboard_pci_device = { - "NCR 53c810 On-Board", + "NCR 53C810 On-Board", DEVICE_PCI, 0x8001, ncr53c8xx_init, ncr53c8xx_close, NULL, @@ -2752,7 +2632,7 @@ const device_t ncr53c810_onboard_pci_device = const device_t ncr53c825a_pci_device = { - "NCR 53c825A", + "NCR 53C825A", DEVICE_PCI, CHIP_825, ncr53c8xx_init, ncr53c8xx_close, NULL, @@ -2762,7 +2642,7 @@ const device_t ncr53c825a_pci_device = const device_t ncr53c860_pci_device = { - "NCR 53c860", + "NCR 53C860", DEVICE_PCI, CHIP_860, ncr53c8xx_init, ncr53c8xx_close, NULL, @@ -2772,7 +2652,7 @@ const device_t ncr53c860_pci_device = const device_t ncr53c875_pci_device = { - "NCR 53c875", + "NCR 53C875", DEVICE_PCI, CHIP_875, ncr53c8xx_init, ncr53c8xx_close, NULL, diff --git a/src/video/vid_ati_mach64.c b/src/video/vid_ati_mach64.c index 64a36583a..52e125af2 100644 --- a/src/video/vid_ati_mach64.c +++ b/src/video/vid_ati_mach64.c @@ -32,6 +32,7 @@ #include <86box/rom.h> #include <86box/plat.h> #include <86box/video.h> +#include <86box/i2c.h> #include <86box/vid_ddc.h> #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> @@ -251,6 +252,8 @@ typedef struct mach64_t uint32_t buf_offset[2], buf_pitch[2]; int overlay_v_acc; + + void *i2c, *ddc; } mach64_t; static video_timings_t timing_mach64_isa = {VIDEO_ISA, 3, 3, 6, 5, 5, 10}; @@ -1879,11 +1882,11 @@ uint8_t mach64_ext_readb(uint32_t addr, void *p) if ((ret & (1 << 4)) && !(ret & (1 << 1))) gpio_state &= ~(1 << 1); - if (!(ret & (1 << 4)) && !ddc_read_data()) + if (!(ret & (1 << 4)) && !i2c_gpio_get_sda(mach64->i2c)) gpio_state &= ~(1 << 1); if ((ret & (1 << 5)) && !(ret & (1 << 2))) gpio_state &= ~(1 << 2); - if (!(ret & (1 << 5)) && !ddc_read_clock()) + if (!(ret & (1 << 5)) && !i2c_gpio_get_scl(mach64->i2c)) gpio_state &= ~(1 << 2); ret = (ret & ~6) | gpio_state; @@ -2380,8 +2383,8 @@ void mach64_ext_writeb(uint32_t addr, uint8_t val, void *p) svga_set_ramdac_type(svga, (mach64->dac_cntl & 0x100) ? RAMDAC_8BIT : RAMDAC_6BIT); ati68860_set_ramdac_type(mach64->svga.ramdac, (mach64->dac_cntl & 0x100) ? RAMDAC_8BIT : RAMDAC_6BIT); data = (val & (1 << 4)) ? ((val & (1 << 1)) ? 1 : 0) : 1; - clk = (val & (1 << 5)) ? ((val & (1 << 2)) ? 1 : 0) : 1; - ddc_i2c_change(clk, data); + clk = (val & (1 << 5)) ? ((val & (1 << 2)) ? 1 : 0) : 1; + i2c_gpio_set(mach64->i2c, clk, data); break; case 0xd0: case 0xd1: case 0xd2: case 0xd3: @@ -3368,7 +3371,8 @@ static void *mach64_common_init(const device_t *info) mach64->fifo_not_full_event = thread_create_event(); mach64->fifo_thread = thread_create(fifo_thread, mach64); - ddc_init(); + mach64->i2c = i2c_gpio_init("ddc_ati_mach64"); + mach64->ddc = ddc_init(i2c_gpio_get_bus(mach64->i2c)); return mach64; } @@ -3460,6 +3464,9 @@ void mach64_close(void *p) thread_destroy_event(mach64->wake_fifo_thread); thread_destroy_event(mach64->fifo_not_full_event); + ddc_close(mach64->ddc); + i2c_gpio_close(mach64->i2c); + free(mach64); } diff --git a/src/video/vid_cl54xx.c b/src/video/vid_cl54xx.c index 6b2fd4aaa..bb808e1dc 100644 --- a/src/video/vid_cl54xx.c +++ b/src/video/vid_cl54xx.c @@ -33,6 +33,8 @@ #include <86box/device.h> #include <86box/timer.h> #include <86box/video.h> +#include <86box/i2c.h> +#include <86box/vid_ddc.h> #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> @@ -208,6 +210,8 @@ typedef struct gd54xx_t uint32_t extpallook[256]; PALETTE extpal; + + void *i2c, *ddc; } gd54xx_t; @@ -355,6 +359,10 @@ gd54xx_out(uint16_t addr, uint8_t val, void *p) if (svga->crtc[0x27] < CIRRUS_ID_CLGD5429) gd54xx->unlocked = (svga->seqregs[6] == 0x12); break; + case 0x08: + if (gd54xx->i2c) + i2c_gpio_set(gd54xx->i2c, !!(val & 0x01), !!(val & 0x02)); + break; case 0x0b: case 0x0c: case 0x0d: case 0x0e: /* VCLK stuff */ gd54xx->vclk_n[svga->seqaddr-0x0b] = val; break; @@ -697,6 +705,15 @@ gd54xx_in(uint16_t addr, void *p) case 6: ret = svga->seqregs[6]; break; + case 0x08: + if (gd54xx->i2c) { + ret &= 0x7b; + if (i2c_gpio_get_scl(gd54xx->i2c)) + ret |= 0x04; + if (i2c_gpio_get_sda(gd54xx->i2c)) + ret |= 0x80; + } + break; case 0x0b: case 0x0c: case 0x0d: case 0x0e: ret = gd54xx->vclk_n[svga->seqaddr-0x0b]; break; @@ -3252,6 +3269,11 @@ static void mca_add(gd5428_mca_read, gd5428_mca_write, gd5428_mca_feedb, NULL, gd54xx); } + if (gd54xx_is_5434(svga)) { + gd54xx->i2c = i2c_gpio_init("ddc_cl54xx"); + gd54xx->ddc = ddc_init(i2c_gpio_get_bus(gd54xx->i2c)); + } + return gd54xx; } @@ -3359,6 +3381,11 @@ gd54xx_close(void *p) gd54xx_t *gd54xx = (gd54xx_t *)p; svga_close(&gd54xx->svga); + + if (gd54xx->i2c) { + ddc_close(gd54xx->ddc); + i2c_gpio_close(gd54xx->i2c); + } free(gd54xx); } diff --git a/src/video/vid_ddc.c b/src/video/vid_ddc.c index 4c4aab881..afe80b9ef 100644 --- a/src/video/vid_ddc.c +++ b/src/video/vid_ddc.c @@ -1,6 +1,3 @@ -#include -#include -#include /* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM @@ -13,322 +10,192 @@ * * * - * Authors: Sarah Walker, + * Authors: RichardG, * - * Copyright 2008-2020 Sarah Walker. + * Copyright 2020 RichardG. */ +#include +#include +#include #include #include #include #include #include <86box/86box.h> -#include "cpu.h" -#include <86box/vid_ddc.h> +#include <86box/i2c.h> -static uint8_t edid_data[128] = -{ - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, /*Fixed header pattern*/ - 0x09, 0xf8, /*Manufacturer "BOX" - apparently unassigned by UEFI - and it has to be big endian*/ - 0x00, 0x00, /*Product code*/ - 0x12, 0x34, 0x56, 0x78, /*Serial number*/ - 0x01, 9, /*Manufacturer week and year*/ - 0x01, 0x03, /*EDID version (1.3)*/ +#define STD_TIMING(idx, width, aspect_ratio) do { \ + edid->standard_timings[idx].horiz_pixels = ((width) >> 3) - 31; \ + edid->standard_timings[idx].aspect_ratio_refresh_rate = (aspect_ratio) << 6; /* 60 Hz */ \ + } while (0) - 0x08, /*Analogue input, separate sync*/ - 34, 0, /*Landscape, 4:3*/ - 0, /*Gamma*/ - 0x08, /*RGB colour*/ - 0x81, 0xf1, 0xa3, 0x57, 0x53, 0x9f, 0x27, 0x0a, 0x50, /*Chromaticity*/ - - 0xff, 0xff, 0xff, /*Established timing bitmap*/ - 0x00, 0x00, /*Standard timing information*/ - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - - /*Detailed mode descriptions*/ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, /*No extensions*/ - 0x00 +enum { + STD_ASPECT_16_10 = 0x0, + STD_ASPECT_4_3, + STD_ASPECT_5_4, + STD_ASPECT_16_9 }; -/*This should probably be split off into a separate I2C module*/ -enum -{ - TRANSMITTER_MONITOR = 1, - TRANSMITTER_HOST = -1 -}; +typedef struct { + uint8_t horiz_pixels, aspect_ratio_refresh_rate; +} edid_standard_timing_t; -enum -{ - I2C_IDLE = 0, - I2C_RECEIVE, - I2C_RECEIVE_WAIT, - I2C_TRANSMIT_START, - I2C_TRANSMIT, - I2C_ACKNOWLEDGE, - I2C_TRANSACKNOWLEDGE, - I2C_TRANSMIT_WAIT -}; +typedef struct { + uint8_t pixel_clock_lsb, pixel_clock_msb, h_active_lsb, h_blank_lsb, + h_active_blank_msb, v_active_lsb, v_blank_lsb, v_active_blank_msb, + h_front_porch_lsb, h_sync_pulse_lsb, v_front_porch_sync_pulse_lsb, + hv_front_porch_sync_pulse_msb, h_size_lsb, v_size_lsb, hv_size_msb, + h_border, v_border, features; +} edid_detailed_timing_t; -enum -{ - PROM_IDLE = 0, - PROM_RECEIVEADDR, - PROM_RECEIVEDATA, - PROM_SENDDATA, - PROM_INVALID -}; +typedef struct { + uint8_t magic[2], reserved, tag, range_limit_offsets; + union { + char ascii[13]; + struct { + uint8_t min_v_field, max_v_field, min_h_line, max_h_line, max_pixel_clock, + timing_type; + union { + uint8_t padding[7]; + struct { + uint8_t reserved, gtf_start_freq, gtf_c, gtf_m_lsb, gtf_m_msb, + gtf_k, gtf_j; + }; + struct { + uint8_t cvt_version, add_clock_precision, max_active_pixels, + aspect_ratios, aspect_ratio_pref, scaling_support, + refresh_pref; + }; + }; + } range_limits; + struct { + edid_standard_timing_t timings[6]; + uint8_t padding; + } ext_standard_timings; + struct { + uint8_t version; + struct { + uint8_t lines_lsb, lines_msb_aspect_ratio, refresh_rate; + } timings[4]; + } cvt_timings; + struct { + uint8_t version, timings[6], reserved[6]; + } established_timings3; + }; +} edid_descriptor_t; -static struct -{ - int clock, data; - int state; - int last_data; - int pos; - int transmit; - uint8_t byte; -} i2c; +typedef struct { + uint8_t magic[8], mfg[2], mfg_product[2], serial[4], mfg_week, mfg_year, + edid_version, edid_rev; + uint8_t input_params, horiz_size, vert_size, gamma, features; + uint8_t red_green_lsb, blue_white_lsb, red_x_msb, red_y_msb, green_x_msb, + green_y_msb, blue_x_msb, blue_y_msb, white_x_msb, white_y_msb; + uint8_t established_timings[3]; + edid_standard_timing_t standard_timings[8]; + union { + edid_detailed_timing_t detailed_timings[4]; + edid_descriptor_t descriptors[4]; + }; + uint8_t extensions, checksum; -static struct -{ - int state; - int addr; - int rw; -} prom; + uint8_t ext_tag, ext_rev, ext_dtd_offset, ext_native_dtds; + union { + edid_detailed_timing_t ext_detailed_timings[6]; + edid_descriptor_t ext_descriptors[6]; + }; + uint8_t padding[15], checksum2; +} edid_t; -static void prom_stop(void) + +void * +ddc_init(void *i2c) { -// pclog("prom_stop()\n"); - prom.state = PROM_IDLE; - i2c.transmit = TRANSMITTER_HOST; + edid_t *edid = malloc(sizeof(edid_t)); + memset(edid, 0, sizeof(edid_t)); + + memset(&edid->magic[1], 0xff, sizeof(edid->magic) - 2); + + edid->mfg[0] = 0x09; /* manufacturer "BOX" (apparently unassigned by UEFI) */ + edid->mfg[1] = 0xf8; + edid->mfg_week = 48; + edid->mfg_year = 2020 - 1990; + edid->edid_version = 0x01; + edid->edid_rev = 0x04; /* EDID 1.4 */ + + edid->input_params = 0x0e; /* analog input; separate sync; composite sync; sync on green */ + edid->horiz_size = ((4.0 / 3.0) * 100) - 99; /* landscape 4:3 */ + edid->features = 0x09; /* RGB color; GTF/CVT */ + + edid->red_green_lsb = 0x81; + edid->blue_white_lsb = 0xf1; + edid->red_x_msb = 0xa3; + edid->red_y_msb = 0x57; + edid->green_x_msb = 0x53; + edid->green_y_msb = 0x9f; + edid->blue_x_msb = 0x27; + edid->blue_y_msb = 0x0a; + edid->white_x_msb = 0x50; + edid->white_y_msb = 0x00; + + memset(&edid->established_timings, 0xff, sizeof(edid->established_timings)); /* all enabled */ + +#if 0 + memset(&edid->standard_timings, 0x01, sizeof(edid->standard_timings)); /* pad unused entries */ +#endif + STD_TIMING(0, 1280, STD_ASPECT_16_9); /* 1280x720 */ + STD_TIMING(1, 1280, STD_ASPECT_16_10); /* 1280x800 */ + STD_TIMING(2, 1366, STD_ASPECT_16_9); /* 1360x768 (closest to 1366x768) */ + STD_TIMING(3, 1440, STD_ASPECT_16_10); /* 1440x900 */ + STD_TIMING(4, 1600, STD_ASPECT_16_9); /* 1600x900 */ + STD_TIMING(5, 1680, STD_ASPECT_16_10); /* 1680x1050 */ + STD_TIMING(6, 1920, STD_ASPECT_16_9); /* 1920x1080 */ + STD_TIMING(7, 2048, STD_ASPECT_4_3); /* 2048x1536 */ + + /* Detailed timings for the preferred mode of 800x600 @ 60 Hz */ + edid->detailed_timings[0].pixel_clock_lsb = 4000 & 0xff; /* 40.000 MHz */ + edid->detailed_timings[0].pixel_clock_msb = 4000 >> 8; + edid->detailed_timings[0].h_active_lsb = 800 & 0xff; + edid->detailed_timings[0].h_blank_lsb = 256 & 0xff; + edid->detailed_timings[0].h_active_blank_msb = ((800 >> 4) & 0xf0) | ((256 >> 8) & 0x0f); + edid->detailed_timings[0].v_active_lsb = 600 & 0xff; + edid->detailed_timings[0].v_blank_lsb = 28; + edid->detailed_timings[0].v_active_blank_msb = (600 >> 4) & 0xf0; + edid->detailed_timings[0].h_front_porch_lsb = 40; + edid->detailed_timings[0].h_sync_pulse_lsb = 128; + edid->detailed_timings[0].v_front_porch_sync_pulse_lsb = (1 << 4) | 4; + + edid->descriptors[1].tag = 0xf7; /* established timings 3 */ + edid->descriptors[1].established_timings3.version = 0x0a; + memset(&edid->descriptors[1].established_timings3.timings, 0xff, sizeof(edid->descriptors[1].established_timings3.timings)); /* all enabled */ + + edid->descriptors[2].tag = 0xfc; /* display name */ + memcpy(&edid->descriptors[2].ascii, "86Box Monitor", 13); /* exactly 13 characters (would otherwise require LF termination and space padding) */ + + edid->descriptors[3].tag = 0xfd; /* range limits */ + edid->descriptors[3].range_limits.min_v_field = 1; + edid->descriptors[3].range_limits.max_v_field = -1; + edid->descriptors[3].range_limits.min_h_line = 1; + edid->descriptors[3].range_limits.max_h_line = -1; + edid->descriptors[3].range_limits.max_pixel_clock = -1; + edid->descriptors[3].range_limits.timing_type = 0x00; /* default GTF */ + edid->descriptors[3].range_limits.padding[0] = 0x0a; + memset(&edid->descriptors[3].range_limits.padding[1], 0x20, sizeof(edid->descriptors[3].range_limits.padding) - 1); + + uint8_t *edid_bytes = (uint8_t *) edid; + for (uint8_t c = 0; c < 127; c++) + edid->checksum += edid_bytes[c]; + edid->checksum = 256 - edid->checksum; + for (uint8_t c = 128; c < 255; c++) + edid->checksum2 += edid_bytes[c]; + edid->checksum2 = 256 - edid->checksum2; + + return i2c_eeprom_init(i2c, 0x50, edid_bytes, sizeof(edid_t), 0); } -static void prom_next_byte(void) + +void +ddc_close(void *eeprom) { -// pclog("prom_next_byte(%d)\n", prom.addr); - i2c.byte = edid_data[(prom.addr++) & 0x7F]; -} - -static void prom_write(uint8_t byte) -{ -// pclog("prom_write: byte=%02x\n", byte); - switch (prom.state) - { - case PROM_IDLE: - if ((byte & 0xfe) != 0xa0) - { -// pclog("I2C address not PROM\n"); - prom.state = PROM_INVALID; - break; - } - prom.rw = byte & 1; - if (prom.rw) - { - prom.state = PROM_SENDDATA; - i2c.transmit = TRANSMITTER_MONITOR; - i2c.byte = edid_data[(prom.addr++) & 0x7F]; -// pclog("PROM - %02X from %02X\n",i2c.byte, prom.addr-1); -// pclog("Transmitter now PROM\n"); - } - else - { - prom.state = PROM_RECEIVEADDR; - i2c.transmit = TRANSMITTER_HOST; - } -// pclog("PROM R/W=%i\n",promrw); - return; - - case PROM_RECEIVEADDR: -// pclog("PROM addr=%02X\n",byte); - prom.addr = byte; - if (prom.rw) - prom.state = PROM_SENDDATA; - else - prom.state = PROM_RECEIVEDATA; - break; - - case PROM_RECEIVEDATA: -// pclog("PROM write %02X %02X\n",promaddr,byte); - break; - - case PROM_SENDDATA: - break; - } -} - -void ddc_i2c_change(int new_clock, int new_data) -{ -// pclog("I2C update clock %i->%i data %i->%i state %i\n",i2c.clock,new_clock,i2c.last_data,new_data,i2c.state); - switch (i2c.state) - { - case I2C_IDLE: - if (i2c.clock && new_clock) - { - if (i2c.last_data && !new_data) /*Start bit*/ - { -// pclog("Start bit received\n"); - i2c.state = I2C_RECEIVE; - i2c.pos = 0; - } - } - break; - - case I2C_RECEIVE_WAIT: - if (!i2c.clock && new_clock) - i2c.state = I2C_RECEIVE; - case I2C_RECEIVE: - if (!i2c.clock && new_clock) - { - i2c.byte <<= 1; - if (new_data) - i2c.byte |= 1; - else - i2c.byte &= 0xFE; - i2c.pos++; - if (i2c.pos == 8) - { - prom_write(i2c.byte); - i2c.state = I2C_ACKNOWLEDGE; - } - } - else if (i2c.clock && new_clock && new_data && !i2c.last_data) /*Stop bit*/ - { -// pclog("Stop bit received\n"); - i2c.state = I2C_IDLE; - prom_stop(); - } - else if (i2c.clock && new_clock && !new_data && i2c.last_data) /*Start bit*/ - { -// pclog("Start bit received\n"); - i2c.pos = 0; - prom.state = PROM_IDLE; - } - break; - - case I2C_ACKNOWLEDGE: - if (!i2c.clock && new_clock) - { -// pclog("Acknowledging transfer\n"); - new_data = 0; - i2c.pos = 0; - if (i2c.transmit == TRANSMITTER_HOST) - i2c.state = I2C_RECEIVE_WAIT; - else - i2c.state = I2C_TRANSMIT; - } - break; - - case I2C_TRANSACKNOWLEDGE: - if (!i2c.clock && new_clock) - { - if (new_data) /*It's not acknowledged - must be end of transfer*/ - { -// pclog("End of transfer\n"); - i2c.state = I2C_IDLE; - prom_stop(); - } - else /*Next byte to transfer*/ - { - i2c.state = I2C_TRANSMIT_START; - prom_next_byte(); - i2c.pos = 0; -// pclog("Next byte - %02X\n",i2c.byte); - } - } - break; - - case I2C_TRANSMIT_WAIT: - if (i2c.clock && new_clock) - { - if (i2c.last_data && !new_data) /*Start bit*/ - { - prom_next_byte(); - i2c.pos = 0; -// pclog("Next byte - %02X\n",i2c.byte); - } - if (!i2c.last_data && new_data) /*Stop bit*/ - { -// pclog("Stop bit received\n"); - i2c.state = I2C_IDLE; - prom_stop(); - } - } - break; - - case I2C_TRANSMIT_START: - if (!i2c.clock && new_clock) - i2c.state = I2C_TRANSMIT; - if (i2c.clock && new_clock && !i2c.last_data && new_data) /*Stop bit*/ - { -// pclog("Stop bit received\n"); - i2c.state = I2C_IDLE; - prom_stop(); - } - case I2C_TRANSMIT: - if (!i2c.clock && new_clock) - { - i2c.clock = new_clock; -// if (!i2c.pos) -// pclog("Transmit byte %02x\n", i2c.byte); - i2c.data = new_data = i2c.byte & 0x80; -// pclog("Transmit bit %i %i\n", i2c.byte, i2c.pos); - i2c.byte <<= 1; - i2c.pos++; - return; - } - if (i2c.clock && !new_clock && i2c.pos == 8) - { - i2c.state = I2C_TRANSACKNOWLEDGE; -// pclog("Acknowledge mode\n"); - } - break; - - } - if (!i2c.clock && new_clock) - i2c.data = new_data; - i2c.last_data = new_data; - i2c.clock = new_clock; -} - -int ddc_read_clock(void) -{ - return i2c.clock; -} -int ddc_read_data(void) -{ - if (i2c.state == I2C_TRANSMIT || i2c.state == I2C_ACKNOWLEDGE) - return i2c.data; - if (i2c.state == I2C_RECEIVE_WAIT) - return 0; /*ACK*/ - return 1; -} - -void ddc_init(void) -{ - int c; - uint8_t checksum = 0; - - for (c = 0; c < 127; c++) - checksum += edid_data[c]; - edid_data[127] = 256 - checksum; - - i2c.clock = 1; - i2c.data = 1; + i2c_eeprom_close(eeprom); } diff --git a/src/video/vid_mga.c b/src/video/vid_mga.c index ef3a0dbe5..136531457 100644 --- a/src/video/vid_mga.c +++ b/src/video/vid_mga.c @@ -28,6 +28,8 @@ #include <86box/dma.h> #include <86box/plat.h> #include <86box/video.h> +#include <86box/i2c.h> +#include <86box/vid_ddc.h> #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> @@ -499,6 +501,8 @@ typedef struct mystique_t mutex_t *lock; } dma; + + void *i2c, *i2c_ddc, *ddc; } mystique_t; @@ -1019,7 +1023,15 @@ mystique_read_xreg(mystique_t *mystique, int reg) ret = mystique->xgenioctrl; break; case XREG_XGENIODATA: - ret = mystique->xgeniodata; + ret = mystique->xgeniodata & 0xf0; + if (i2c_gpio_get_scl(mystique->i2c_ddc)) + ret |= 0x08; + if (i2c_gpio_get_scl(mystique->i2c)) + ret |= 0x04; + if (i2c_gpio_get_sda(mystique->i2c_ddc)) + ret |= 0x02; + if (i2c_gpio_get_sda(mystique->i2c)) + ret |= 0x01; break; case XREG_XSYSPLLM: @@ -1154,6 +1166,8 @@ mystique_write_xreg(mystique_t *mystique, int reg, uint8_t val) case XREG_XGENIOCTRL: mystique->xgenioctrl = val; + i2c_gpio_set(mystique->i2c_ddc, !(mystique->xgenioctrl & 0x08) || (mystique->xgeniodata & 0x08), !(mystique->xgenioctrl & 0x02) || (mystique->xgeniodata & 0x02)); + i2c_gpio_set(mystique->i2c, !(mystique->xgenioctrl & 0x04) || (mystique->xgeniodata & 0x04), !(mystique->xgenioctrl & 0x01) || (mystique->xgeniodata & 0x01)); break; case XREG_XGENIODATA: mystique->xgeniodata = val; @@ -5009,6 +5023,10 @@ mystique_init(const device_t *info) mystique->svga.vsync_callback = mystique_vsync_callback; + mystique->i2c = i2c_gpio_init("i2c_mga"); + mystique->i2c_ddc = i2c_gpio_init("ddc_mga"); + mystique->ddc = ddc_init(i2c_gpio_get_bus(mystique->i2c_ddc)); + return mystique; } @@ -5025,6 +5043,10 @@ mystique_close(void *p) svga_close(&mystique->svga); + ddc_close(mystique->ddc); + i2c_gpio_close(mystique->i2c_ddc); + i2c_gpio_close(mystique->i2c); + free(mystique); } diff --git a/src/video/vid_s3.c b/src/video/vid_s3.c index a134ccee3..4d61f9424 100644 --- a/src/video/vid_s3.c +++ b/src/video/vid_s3.c @@ -30,6 +30,8 @@ #include <86box/rom.h> #include <86box/plat.h> #include <86box/video.h> +#include <86box/i2c.h> +#include <86box/vid_ddc.h> #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> #include "cpu.h" @@ -266,6 +268,9 @@ typedef struct s3_t int translate; int enable_8514; volatile int busy, force_busy; + + uint8_t serialport; + void *i2c, *ddc; } s3_t; #define INT_VSY (1 << 0) @@ -274,6 +279,11 @@ typedef struct s3_t #define INT_FIFO_EMP (1 << 3) #define INT_MASK 0xf +#define SERIAL_PORT_SCW (1 << 0) +#define SERIAL_PORT_SDW (1 << 1) +#define SERIAL_PORT_SCR (1 << 2) +#define SERIAL_PORT_SDR (1 << 3) + static void s3_updatemapping(s3_t *s3); static void s3_accel_write(uint32_t addr, uint8_t val, void *p); @@ -1051,6 +1061,10 @@ s3_accel_write_fifo(s3_t *s3, uint32_t addr, uint8_t val) s3->accel.advfunc_cntl = val; s3_updatemapping(s3); break; + case 0xff20: + s3->serialport = val; + i2c_gpio_set(s3->i2c, !!(val & SERIAL_PORT_SCW), !!(val & SERIAL_PORT_SDW)); + break; default: s3_accel_out_fifo(s3, addr & 0xffff, val); break; @@ -1084,7 +1098,10 @@ s3_accel_write_fifo_w(s3_t *s3, uint32_t addr, uint16_t val) default: s3_accel_write_fifo(s3, addr, val); s3_accel_write_fifo(s3, addr + 1, val >> 8); - break; + break; + case 0xff20: + s3_accel_write_fifo(s3, addr, val); + break; } } } else { @@ -1233,6 +1250,10 @@ s3_accel_write_fifo_l(s3_t *s3, uint32_t addr, uint32_t val) s3_updatemapping(s3); break; + case 0xff20: + s3_accel_write_fifo(s3, addr, val); + break; + default: s3_accel_write_fifo(s3, addr, val); s3_accel_write_fifo(s3, addr + 1, val >> 8); @@ -2900,6 +2921,14 @@ s3_accel_in(uint16_t port, void *p) else if ((s3->accel.cmd & 0x600) == 0x000 && (s3->accel.cmd & 0x100)) s3_accel_start(1, 1, 0xffffffff, 0xff, s3); return temp; + + case 0xff20: case 0xff21: + temp = s3->serialport & ~(SERIAL_PORT_SCR | SERIAL_PORT_SDR); + if ((s3->serialport & SERIAL_PORT_SCW) && i2c_gpio_get_scl(s3->i2c)) + temp |= SERIAL_PORT_SCR; + if ((s3->serialport & SERIAL_PORT_SDW) && i2c_gpio_get_sda(s3->i2c)) + temp |= SERIAL_PORT_SDR; + return temp; } return 0xff; @@ -2976,7 +3005,7 @@ s3_accel_read(uint32_t addr, void *p) case 0x8504: return s3->subsys_stat; case 0x8505: - return s3->subsys_cntl;; + return s3->subsys_cntl; default: return s3_accel_in(addr & 0xffff, p); } @@ -4643,6 +4672,9 @@ static void *s3_init(const device_t *info) return NULL; } + s3->i2c = i2c_gpio_init("ddc_s3"); + s3->ddc = ddc_init(i2c_gpio_get_bus(s3->i2c)); + return s3; } @@ -4736,6 +4768,9 @@ static void s3_close(void *p) thread_destroy_event(s3->wake_fifo_thread); thread_destroy_event(s3->fifo_not_full_event); + ddc_close(s3->ddc); + i2c_gpio_close(s3->i2c); + free(s3); } diff --git a/src/video/vid_s3_virge.c b/src/video/vid_s3_virge.c index 81c3b2d34..db04f8e8b 100644 --- a/src/video/vid_s3_virge.c +++ b/src/video/vid_s3_virge.c @@ -32,6 +32,7 @@ #include <86box/device.h> #include <86box/plat.h> #include <86box/video.h> +#include <86box/i2c.h> #include <86box/vid_ddc.h> #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> @@ -284,8 +285,10 @@ typedef struct virge_t int virge_busy; uint8_t subsys_stat, subsys_cntl, advfunc_cntl; - + uint8_t serialport; + + void *i2c, *ddc; } virge_t; static video_timings_t timing_diamond_stealth3d_2000_vlb = {VIDEO_BUS, 2, 2, 3, 28, 28, 45}; @@ -444,7 +447,11 @@ static void s3_virge_out(uint16_t addr, uint8_t val, void *p) return; if ((svga->crtcreg == 0x36) && (svga->crtc[0x39] != 0xa5)) return; - if (svga->crtcreg >= 0x80) + if ((svga->crtcreg >= 0x80) +#if defined(DEV_BRANCH) && defined(USE_S3TRIO3D2X) + && !((virge->chip == S3_TRIO3D2X) && (svga->crtcreg == 0xaa)) +#endif + ) return; old = svga->crtc[svga->crtcreg]; svga->crtc[svga->crtcreg] = val; @@ -547,6 +554,12 @@ static void s3_virge_out(uint16_t addr, uint8_t val, void *p) default: svga->bpp = 8; break; } break; + +#if defined(DEV_BRANCH) && defined(USE_S3TRIO3D2X) + case 0xaa: + i2c_gpio_set(virge->i2c, !!(val & SERIAL_PORT_SCW), !!(val & SERIAL_PORT_SDW)); + break; +#endif } if (old != val) { @@ -604,6 +617,18 @@ static uint8_t s3_virge_in(uint16_t addr, void *p) case 0x51: ret = (svga->crtc[0x51] & 0xf0) | ((virge->bank >> 2) & 0xc) | ((virge->ma_ext >> 2) & 3); break; case 0x69: ret = virge->ma_ext; break; case 0x6a: ret = virge->bank; break; +#if defined(DEV_BRANCH) && defined(USE_S3TRIO3D2X) + case 0xaa: /* Trio3D DDC */ + if (virge->chip == S3_TRIO3D2X) { + ret = svga->crtc[0xaa] & ~(SERIAL_PORT_SCR | SERIAL_PORT_SDR); + if ((svga->crtc[0xaa] & SERIAL_PORT_SCW) && i2c_gpio_get_scl(virge->i2c)) + ret |= SERIAL_PORT_SCR; + if ((svga->crtc[0xaa] & SERIAL_PORT_SDW) && i2c_gpio_get_sda(virge->i2c)) + ret |= SERIAL_PORT_SDR; + break; + } + /* fall-through */ +#endif default: ret = svga->crtc[svga->crtcreg]; break; } break; @@ -894,11 +919,11 @@ s3_virge_mmio_read(uint32_t addr, void *p) case 0xff20: case 0xff21: ret = virge->serialport & ~(SERIAL_PORT_SCR | SERIAL_PORT_SDR); - if ((virge->serialport & SERIAL_PORT_SCW) && ddc_read_clock()) + if ((virge->serialport & SERIAL_PORT_SCW) && i2c_gpio_get_scl(virge->i2c)) ret |= SERIAL_PORT_SCR; - if ((virge->serialport & SERIAL_PORT_SDW) && ddc_read_data()) + if ((virge->serialport & SERIAL_PORT_SDW) && i2c_gpio_get_sda(virge->i2c)) ret |= SERIAL_PORT_SDR; - return ret; + return ret; } return 0xff; } @@ -1180,7 +1205,7 @@ static void s3_virge_mmio_write(uint32_t addr, uint8_t val, void *p) case 0xff20: virge->serialport = val; - ddc_i2c_change((val & SERIAL_PORT_SCW) ? 1 : 0, (val & SERIAL_PORT_SDW) ? 1 : 0); + i2c_gpio_set(virge->i2c, !!(val & SERIAL_PORT_SCW), !!(val & SERIAL_PORT_SDW)); break; } } @@ -3845,7 +3870,8 @@ static void *s3_virge_init(const device_t *info) virge->fifo_not_full_event = thread_create_event(); virge->fifo_thread = thread_create(fifo_thread, virge); - ddc_init(); + virge->i2c = i2c_gpio_init("ddc_s3_virge"); + virge->ddc = ddc_init(i2c_gpio_get_bus(virge->i2c)); return virge; } @@ -3864,6 +3890,9 @@ static void s3_virge_close(void *p) thread_destroy_event(virge->fifo_not_full_event); svga_close(&virge->svga); + + ddc_close(virge->ddc); + i2c_gpio_close(virge->i2c); free(virge); } diff --git a/src/video/vid_table.c b/src/video/vid_table.c index a1c7af27d..611be2524 100644 --- a/src/video/vid_table.c +++ b/src/video/vid_table.c @@ -149,9 +149,9 @@ video_cards[] = { { "virge375_vbe20_pci", &s3_virge_375_4_pci_device }, { "cl_gd5446_stb_pci", &gd5446_stb_pci_device }, { "tgui9440_pci", &tgui9440_pci_device }, + { "voodoo_banshee_pci", &voodoo_banshee_device }, { "voodoo3_2k_pci", &voodoo_3_2000_device }, { "voodoo3_3k_pci", &voodoo_3_3000_device }, - { "voodoo_banshee_pci", &voodoo_banshee_device }, { "mach64gx_vlb", &mach64gx_vlb_device }, { "et4000w32p_vlb", &et4000w32p_cardex_vlb_device }, #if defined(DEV_BRANCH) && defined(USE_CL5422) diff --git a/src/video/vid_voodoo_banshee.c b/src/video/vid_voodoo_banshee.c index d7f78bd05..ba4964a09 100644 --- a/src/video/vid_voodoo_banshee.c +++ b/src/video/vid_voodoo_banshee.c @@ -33,6 +33,7 @@ #include <86box/device.h> #include <86box/plat.h> #include <86box/video.h> +#include <86box/i2c.h> #include <86box/vid_ddc.h> #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> @@ -116,6 +117,8 @@ typedef struct banshee_t uint32_t desktop_stride_tiled; int type; + + void *i2c, *i2c_ddc, *ddc; } banshee_t; enum @@ -684,7 +687,8 @@ static void banshee_ext_outl(uint16_t addr, uint32_t val, void *p) case Video_vidSerialParallelPort: banshee->vidSerialParallelPort = val; // banshee_log("vidSerialParallelPort: write %08x %08x %04x(%08x):%08x\n", val, val & (VIDSERIAL_DDC_DCK_W | VIDSERIAL_DDC_DDA_W), CS,cs,cpu_state.pc); - ddc_i2c_change((val & VIDSERIAL_DDC_DCK_W) ? 1 : 0, (val & VIDSERIAL_DDC_DDA_W) ? 1 : 0); + i2c_gpio_set(banshee->i2c_ddc, !!(val & VIDSERIAL_DDC_DCK_W), !!(val & VIDSERIAL_DDC_DDA_W)); + i2c_gpio_set(banshee->i2c, !!(val & VIDSERIAL_I2C_SCK_W), !!(val & VIDSERIAL_I2C_SDA_W)); break; case Video_vidScreenSize: @@ -917,14 +921,14 @@ static uint32_t banshee_ext_inl(uint16_t addr, void *p) case Video_vidSerialParallelPort: ret = banshee->vidSerialParallelPort & ~(VIDSERIAL_DDC_DCK_R | VIDSERIAL_DDC_DDA_R); - if ((banshee->vidSerialParallelPort & VIDSERIAL_DDC_DCK_W) && ddc_read_clock()) + if (!(banshee->vidSerialParallelPort & VIDSERIAL_DDC_DCK_W) || i2c_gpio_get_scl(banshee->i2c_ddc)) ret |= VIDSERIAL_DDC_DCK_R; - if ((banshee->vidSerialParallelPort & VIDSERIAL_DDC_DDA_W) && ddc_read_data()) + if (!(banshee->vidSerialParallelPort & VIDSERIAL_DDC_DDA_W) || i2c_gpio_get_sda(banshee->i2c_ddc)) ret |= VIDSERIAL_DDC_DDA_R; ret = ret & ~(VIDSERIAL_I2C_SCK_R | VIDSERIAL_I2C_SDA_R); - if (banshee->vidSerialParallelPort & VIDSERIAL_I2C_SCK_W) + if (!(banshee->vidSerialParallelPort & VIDSERIAL_I2C_SCK_W) || i2c_gpio_get_scl(banshee->i2c)) ret |= VIDSERIAL_I2C_SCK_R; - if (banshee->vidSerialParallelPort & VIDSERIAL_I2C_SDA_W) + if (!(banshee->vidSerialParallelPort & VIDSERIAL_I2C_SDA_W) || i2c_gpio_get_sda(banshee->i2c)) ret |= VIDSERIAL_I2C_SDA_R; // banshee_log("vidSerialParallelPort: read %08x %08x %04x(%08x):%08x\n", ret, ret & (VIDSERIAL_DDC_DCK_R | VIDSERIAL_DDC_DDA_R), CS,cs,cpu_state.pc); break; @@ -2640,7 +2644,9 @@ static void *banshee_init_common(const device_t *info, wchar_t *fn, int has_sgra banshee->vidSerialParallelPort = VIDSERIAL_DDC_DCK_W | VIDSERIAL_DDC_DDA_W; - ddc_init(); + banshee->i2c = i2c_gpio_init("i2c_voodoo_banshee"); + banshee->i2c_ddc = i2c_gpio_init("ddc_voodoo_banshee"); + banshee->ddc = ddc_init(i2c_gpio_get_bus(banshee->i2c_ddc)); video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_banshee); @@ -2687,6 +2693,9 @@ static void banshee_close(void *p) voodoo_card_close(banshee->voodoo); svga_close(&banshee->svga); + ddc_close(banshee->ddc); + i2c_gpio_close(banshee->i2c_ddc); + i2c_gpio_close(banshee->i2c); free(banshee); } @@ -2708,7 +2717,7 @@ static void banshee_force_redraw(void *p) const device_t voodoo_banshee_device = { - "Voodoo Banshee PCI (reference)", + "3dfx Voodoo Banshee", DEVICE_PCI, 0, banshee_init, @@ -2722,7 +2731,7 @@ const device_t voodoo_banshee_device = const device_t creative_voodoo_banshee_device = { - "Creative Labs 3D Blaster Banshee PCI", + "Creative 3D Blaster Banshee", DEVICE_PCI, 0, creative_banshee_init, @@ -2736,7 +2745,7 @@ const device_t creative_voodoo_banshee_device = const device_t voodoo_3_2000_device = { - "Voodoo 3 2000 PCI", + "3dfx Voodoo3 2000", DEVICE_PCI, 0, v3_2000_init, @@ -2750,7 +2759,7 @@ const device_t voodoo_3_2000_device = const device_t voodoo_3_3000_device = { - "Voodoo 3 3000 PCI", + "3dfx Voodoo3 3000", DEVICE_PCI, 0, v3_3000_init, diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 2be35c1b5..f35641d58 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -654,7 +654,7 @@ MCHOBJ := machine.o machine_table.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 \ + i2c.o i2c_eeprom.o i2c_gpio.o smbus_piix4.o \ keyboard.o \ keyboard_xt.o keyboard_at.o \ mouse.o \